File indexing completed on 2024-12-22 04:17:34

0001 /***************************************************************************
0002  *                                                                         *
0003  *   copyright : (C) 2008 The University of Toronto                        *
0004  *                   <netterfield@astro.utoronto.ca>                       *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  *                                                                         *
0010  ***************************************************************************/
0011 
0012 #include "legenditem.h"
0013 
0014 #include <labelparser.h>
0015 #include "labelrenderer.h"
0016 
0017 #include "debug.h"
0018 #include "plotitem.h"
0019 #include "legenditemdialog.h"
0020 #include "objectstore.h"
0021 #include "dialogdefaults.h"
0022 #include "legendscriptinterface.h"
0023 
0024 #include <QDebug>
0025 #include <QGraphicsItem>
0026 #include <QGraphicsScene>
0027 
0028 #define LEGENDITEMMAXWIDTH 900
0029 #define LEGENDITEMMAXHEIGHT 100
0030 
0031 namespace Kst {
0032 
0033 
0034 LegendItem::LegendItem(PlotItem *parentPlot)
0035   : ViewItem(parentPlot->view()), _plotItem(parentPlot), _auto(true), _verticalDisplay(true) {
0036   setTypeName(tr("Legend", "a legend in a plot"));
0037 
0038   _initializeShortName();
0039 
0040   setFixedSize(true);
0041   setAllowedGripModes(Move /*| Resize*/ /*| Rotate*/ /*| Scale*/);
0042 
0043   setViewRect(0.0, 0.0, 0.0, 0.0);
0044   view()->scene()->addItem(this);
0045   setParentViewItem(_plotItem->renderItem());
0046 
0047   QPointF origin = QPointF(_plotItem->plotRect().width() * 0.15, _plotItem->plotRect().height() * 0.15);
0048   setPos(origin);
0049 
0050   applyDefaults();
0051   applyDialogDefaultsStroke();
0052   applyDialogDefaultsFill();
0053   applyDialogDefaultsLockPosToData();
0054 }
0055 
0056 void LegendItem::_initializeShortName() {
0057   _shortName = 'L'+QString::number(_legendnum);
0058   if (_legendnum>max_legendnum)
0059     max_legendnum = _legendnum;
0060   _legendnum++;
0061 }
0062 
0063 
0064 LegendItem::~LegendItem() {
0065 }
0066 
0067 
0068 void LegendItem::paint(QPainter *painter) {
0069   if (!isVisible()) {
0070     return;
0071   }
0072 
0073   RelationList legendItems;
0074   if (_auto) {
0075     legendItems = plot()->renderItem(PlotRenderItem::Cartesian)->relationList();
0076   } else {
0077     legendItems = _relations;
0078   }
0079 
0080   int count = legendItems.count();
0081   if (count <= 0) { // no legend or box if there are no legend items
0082     return;
0083   }
0084 
0085 
0086   QFont font(_font);
0087   font.setPointSizeF(view()->scaledFontSize(_fontScale, *painter->device()));
0088 
0089   painter->setFont(font);
0090 
0091   // generate string list of relation names
0092   QStringList names;
0093   //bool allAuto = true;
0094   bool sameX = true;
0095   bool sameYUnits = true;
0096 
0097   LabelInfo label_info = legendItems.at(0)->xLabelInfo();
0098   QString yUnits =  legendItems.at(0)->yLabelInfo().units;
0099 
0100   for (int i = 0; i<count; i++) {
0101     RelationPtr relation = legendItems.at(i);
0102     if (relation->descriptiveNameIsManual()) {
0103       //allAuto = false;
0104     }
0105     if (relation->xLabelInfo() != label_info) {
0106       sameX = false;
0107     }
0108     // sameYUnits is false if any non empty units are defined differently.
0109     if (yUnits.isEmpty()) {
0110       yUnits = relation->yLabelInfo().units;
0111     } else if (relation->yLabelInfo().units != yUnits) {
0112       if (!relation->yLabelInfo().units.isEmpty()) {
0113         sameYUnits = false;
0114       }
0115     }
0116   }
0117 
0118 //  if (!allAuto) {
0119   //    for (int i = 0; i<count; i++) {
0120   //      names.append(legendItems.at(i)->descriptiveName());
0121   //    }
0122   //  } else {
0123 
0124   // FIXME: move most of this into a new function, relation->legend_name
0125   //        then create separate [legend_name] and Auto in the relation dialog.
0126   //        show relation->legend_name in dialog when [x] Auto
0127   for (int i = 0; i<count; i++) {
0128     RelationPtr relation = legendItems.at(i);
0129     QString label = relation->legendName(sameX, sameYUnits);
0130 
0131     int i_dup = names.indexOf(label);
0132     if (i_dup<0) {
0133       names.append(label);
0134     } else {
0135       RelationPtr dup_relation = legendItems.at(i_dup);
0136       if (!dup_relation->yLabelInfo().file.isEmpty()) {
0137         names.replace(i_dup, label + " (" + dup_relation->yLabelInfo().escapedFile() + ')');
0138       }
0139       if (!relation->yLabelInfo().file.isEmpty()) {
0140         names.append(label + " (" + relation->yLabelInfo().escapedFile() + ')');
0141       }
0142     }
0143   }
0144 
0145   QSize legendSize(0, 0);
0146   QSize titleSize(0,0);
0147   Label::Parsed *parsed = Label::parse(_title, _color);
0148   int pad = painter->fontMetrics().ascent()/4;
0149   Label::RenderContext rc(painter->font(), painter);
0150   Label::renderLabel(rc, parsed->chunk, false, false);
0151 
0152   if (!_title.isEmpty()) {
0153     titleSize.setWidth(rc.x+3*pad);
0154     titleSize.setHeight(painter->fontMetrics().height()+pad);
0155   }
0156 
0157   QList<QSize> sizes;
0158   int max_w = 0;
0159   int max_h = 0;
0160   for (int i = 0; i<count; i++) {
0161     RelationPtr relation = legendItems.at(i);
0162     QSize size;
0163     painter->save();
0164     size = paintRelation(names.at(i), relation, painter, false);
0165     painter->restore();
0166     sizes.append(size);
0167     max_w = qMax(max_w, size.width());
0168     max_h = qMax(max_h, size.height());
0169   }
0170 
0171   // determine number of rows and number of columns
0172   int n_rows = 0;
0173   int n_cols = 0;
0174   if (_verticalDisplay) {
0175     int h=titleSize.height();
0176     for (int i = 0; i<count; i++) {
0177       h+=sizes.at(i).height();
0178     }
0179     int max_legend_height = _plotItem->plotRect().height()*0.6+1;
0180     n_cols = qMin(count, h / max_legend_height + 1);
0181     n_rows = count / n_cols;
0182     while (n_rows*n_cols<count) {
0183       n_rows++;
0184     }
0185   } else {
0186     int w = 0;
0187     for (int i = 0; i<count; i++) {
0188       w+=sizes.at(i).width();
0189     }
0190     int max_legend_width = _plotItem->plotRect().width()*0.8+1;
0191     n_rows = qMin(count, w / max_legend_width+1);
0192     n_cols = count/n_rows;
0193     while (n_rows*n_cols<count) {
0194       n_cols++;
0195     }
0196   }
0197 
0198   // determine the dimensions of each column
0199   QList<QSize> col_sizes;
0200   for (int i=0; i<n_cols; i++) {
0201     col_sizes.append(QSize(0,0));
0202   }
0203   for (int i = 0; i<count; i++) {
0204     int col = i/n_rows;
0205     col_sizes[col].rheight()+= sizes.at(i).height();
0206     col_sizes[col].setWidth(qMax(sizes.at(i).width(), col_sizes.at(col).width()));
0207   }
0208 
0209   // determine the dimensions of the legend
0210   int w = 0;
0211   int h = 0;
0212   for (int col = 0; col < n_cols; col++) {
0213     w += col_sizes.at(col).width();
0214     h = qMax(h, col_sizes.at(col).height());
0215   }
0216   legendSize.setHeight(h + titleSize.height());
0217   legendSize.setWidth(qMax(titleSize.width(), w));
0218   setViewRect(rect().x(), rect().y(), legendSize.width()+pad, legendSize.height()+pad);
0219 
0220   // Now paint everything
0221   painter->drawRect(rect());
0222 
0223   int x=rect().x();
0224   int y=rect().y();
0225 
0226   if (!_title.isEmpty()) {
0227     rc.y = rect().y() + titleSize.height()-pad;
0228     rc.x = qMax(rect().x()+pad, rect().x() + legendSize.width()/2 - titleSize.width()/2);
0229     Label::renderLabel(rc, parsed->chunk, false, true);
0230     y+= titleSize.height();
0231   }
0232 
0233   legendSize.setWidth(0);
0234   legendSize.setHeight(0);
0235   for (int i = 0; i<count; i++) {
0236     RelationPtr relation = legendItems.at(i);
0237     painter->save();
0238     painter->translate(x,y);
0239     paintRelation(names.at(i), relation, painter, true);
0240     painter->restore();
0241 
0242     int col = i/n_rows;
0243     int row = i%n_rows;
0244     if (row == n_rows-1) { // end of a column
0245       x += col_sizes.at(col).width();
0246       y = rect().y() + titleSize.height();
0247     } else {
0248       y += sizes.at(i).height();
0249     }
0250   }
0251   delete parsed;
0252 }
0253 
0254 
0255 QSize LegendItem::paintRelation(QString name, RelationPtr relation, QPainter *painter, bool draw) {
0256   Label::Parsed *parsed = Label::parse(name, _color);
0257 
0258   int fontHeight = painter->fontMetrics().height();
0259   int fontAscent = painter->fontMetrics().ascent();
0260 
0261   QSize symbol_size = relation->legendSymbolSize(painter);
0262   int label_width = 0;
0263   int paddingValue = fontHeight / 4;
0264 
0265   if (relation->symbolLabelOnTop()) {
0266     Label::RenderContext tmprc(painter->font(), painter);
0267     Label::renderLabel(tmprc, parsed->chunk, false, false);
0268     label_width = tmprc.x;
0269     painter->translate(paddingValue, fontHeight+paddingValue / 2);
0270     symbol_size.setWidth(qMax(label_width, symbol_size.width()));
0271   } else {
0272     painter->translate(paddingValue, paddingValue / 2);
0273   }
0274 
0275   if (draw) {
0276     relation->paintLegendSymbol(painter, symbol_size);
0277   }
0278 
0279   if (relation->symbolLabelOnTop()) {
0280     painter->translate((symbol_size.width()-label_width)/2, fontAscent - fontHeight);
0281   } else {
0282     painter->translate(symbol_size.width() + paddingValue, 0);
0283   }
0284   Label::RenderContext rc(painter->font(), painter);
0285   if (relation->symbolLabelOnTop()) {
0286     rc.y = 0;
0287   } else {
0288     rc.y = (symbol_size.height()+painter->fontMetrics().boundingRect('M').height())/2;
0289   }
0290   if (parsed) {
0291     Label::renderLabel(rc, parsed->chunk, false, draw);
0292 
0293     delete parsed;
0294     parsed = 0;
0295   }
0296 
0297   double h = symbol_size.height() + paddingValue;
0298   if (relation->symbolLabelOnTop()) {
0299     h += fontHeight;
0300   }
0301   if (relation->symbolLabelOnTop()) {
0302     return QSize(qMax(rc.x,(symbol_size.width())) + (paddingValue * 2), h);
0303   } else {
0304     return QSize((symbol_size.width()) + (paddingValue * 3) + rc.x, h);
0305   }
0306 
0307 }
0308 
0309 
0310 void LegendItem::save(QXmlStreamWriter &xml) {
0311   Q_UNUSED(xml);
0312 }
0313 
0314 QColor LegendItem::legendColor() const {
0315   return _color;
0316 }
0317 
0318 
0319 void LegendItem::setLegendColor(const QColor &color) {
0320   _color = color;
0321 }
0322 
0323 
0324 void LegendItem::applyDefaults() {
0325   _auto = dialogDefaults().value(defaultsGroupName()+"/auto",true).toBool();
0326 
0327   _color = dialogDefaults().value(defaultsGroupName()+"/color",QColor(Qt::black)).value<QColor>();
0328 
0329   QFont font;
0330   font.fromString(dialogDefaults().value(defaultsGroupName()+"/font",font.toString()).toString());
0331   setLegendFont(font);
0332 
0333   setFontScale(dialogDefaults().value(defaultsGroupName()+"/fontScale", 12.0).toDouble());
0334   _verticalDisplay = dialogDefaults().value(defaultsGroupName()+"/verticalDisplay",true).toBool();
0335 }
0336 
0337 void LegendItem::setFont(const QFont &f, const QColor &c) {
0338   setLegendColor(c);
0339   setLegendFont(f);
0340   setFontScale(f.pointSize());
0341 }
0342 
0343 void LegendItem::saveAsDialogDefaults() const {
0344   dialogDefaults().setValue(defaultsGroupName()+"/auto",_auto);
0345   dialogDefaults().setValue(defaultsGroupName()+"/title", _title);
0346   dialogDefaults().setValue(defaultsGroupName()+"/verticalDisplay", _verticalDisplay);
0347 
0348   QFont F = _font;
0349   F.setPointSize(_fontScale);
0350   saveDialogDefaultsFont(F, _color);
0351   saveDialogDefaultsPen(defaultsGroupName(), pen());
0352   saveDialogDefaultsBrush(defaultsGroupName(), brush());
0353 }
0354 
0355 void LegendItem::saveDialogDefaultsFont(const QFont &F, const QColor &C) {
0356   dialogDefaults().setValue(staticDefaultsGroupName()+"/font", QVariant(F).toString());
0357   dialogDefaults().setValue(staticDefaultsGroupName()+"/fontScale",F.pointSize());
0358   dialogDefaults().setValue(staticDefaultsGroupName()+"/color", C.name());
0359 }
0360 
0361 void LegendItem::saveInPlot(QXmlStreamWriter &xml) {
0362   xml.writeStartElement("legend");
0363   xml.writeAttribute("auto", QVariant(_auto).toString());
0364   xml.writeAttribute("title", QVariant(_title).toString());
0365   xml.writeAttribute("font", QVariant(_font).toString());
0366   xml.writeAttribute("fontscale", QVariant(_fontScale).toString());
0367   xml.writeAttribute("color", QVariant(_color).toString());
0368   xml.writeAttribute("verticaldisplay", QVariant(_verticalDisplay).toString());
0369   ViewItem::save(xml);
0370   foreach (const RelationPtr &relation, _relations) {
0371     xml.writeStartElement("relation");
0372     xml.writeAttribute("tag", relation->Name());
0373     xml.writeEndElement();
0374   }
0375   xml.writeEndElement();
0376 }
0377 
0378 
0379 bool LegendItem::configureFromXml(QXmlStreamReader &xml, ObjectStore *store) {
0380   bool validTag = true;
0381 
0382   QString primaryTag = xml.name().toString();
0383   QXmlStreamAttributes attrs = xml.attributes();
0384   QStringRef av;
0385   av = attrs.value("auto");
0386   if (!av.isNull()) {
0387     setAutoContents(QVariant(av.toString()).toBool());
0388   }
0389   av = attrs.value("title");
0390   if (!av.isNull()) {
0391     setTitle(av.toString());
0392   }
0393   av = attrs.value("font");
0394   if (!av.isNull()) {
0395     QFont font;
0396     font.fromString(av.toString());
0397     setLegendFont(font);
0398   }
0399   av = attrs.value("fontscale");
0400   if (!av.isNull()) {
0401     setFontScale(QVariant(av.toString()).toDouble());
0402   }
0403   av = attrs.value("color");
0404   if (!av.isNull()) {
0405     setLegendColor(QColor(av.toString()));
0406   }
0407   av = attrs.value("verticaldisplay");
0408   if (!av.isNull()) {
0409     setVerticalDisplay(QVariant(av.toString()).toBool());
0410   }
0411   QString expectedEnd;
0412   while (!(xml.isEndElement() && (xml.name().toString() == primaryTag))) {
0413    if (xml.isStartElement()) {
0414     if (xml.name().toString() == "relation") {
0415       expectedEnd = xml.name().toString();
0416       attrs = xml.attributes();
0417       QString tagName = attrs.value("tag").toString();
0418       RelationPtr relation = kst_cast<Relation>(store->retrieveObject(tagName));
0419       if (relation) {
0420         _relations.append(relation);
0421       }
0422     } else {
0423       parse(xml, validTag);
0424     }
0425    } else if (xml.isEndElement()) {
0426       if (xml.name().toString() != expectedEnd) {
0427         validTag = false;
0428         break;
0429       }
0430     }
0431     xml.readNext();
0432   }
0433 
0434   return validTag;
0435 }
0436 
0437 
0438 void LegendItem::edit() {
0439   LegendItemDialog *editDialog = new LegendItemDialog(this);
0440   editDialog->show();
0441 }
0442 
0443 
0444 void LegendItem::remove() {
0445   if (_plotItem) {
0446     _plotItem->setShowLegend(false);
0447   }
0448   ViewItem::remove();
0449 }
0450 
0451 
0452 void LegendItem::setAutoContents(const bool autoContents) {
0453   _auto = autoContents;
0454 }
0455 
0456 
0457 bool LegendItem::autoContents() const {
0458   return _auto;
0459 }
0460 
0461 
0462 void LegendItem::setVerticalDisplay(const bool vertical) {
0463   _verticalDisplay = vertical;
0464 }
0465 
0466 
0467 bool LegendItem::verticalDisplay() const {
0468   return _verticalDisplay;
0469 }
0470 
0471 
0472 void LegendItem::setTitle(const QString &title) {
0473   _title = title;
0474 }
0475 
0476 
0477 QString LegendItem::title() const {
0478   return _title;
0479 }
0480 
0481 
0482 QFont LegendItem::font() const {
0483   return _font;
0484 }
0485 
0486 
0487 void LegendItem::setLegendFont(const QFont &font) {
0488   _font = font;
0489 }
0490 
0491 
0492 qreal LegendItem::fontScale() const {
0493   return _fontScale;
0494 }
0495 
0496 
0497 void LegendItem::setFontScale(const qreal scale) {
0498   _fontScale = scale;
0499 }
0500 
0501 QString LegendItem::_automaticDescriptiveName() const {
0502 
0503   QString name = tr("Empty Legend");
0504   if (_auto) {
0505     name = _plotItem->descriptiveName();
0506   } else if (_relations.size()>0) {
0507     name = _relations.at(0)->descriptiveName();
0508     if (_relations.size()>1) {
0509       name += ", ...";
0510     }
0511   }
0512   //qDebug() << "a desc name called: " << name << " relation.length: " << _relations.length();
0513   return name;
0514 }
0515 
0516 QString LegendItem::descriptionTip() const {
0517   QString contents;
0518   foreach (const RelationPtr &relation, _relations) {
0519     contents += QString("  %1\n").arg(relation->Name());
0520   }
0521 
0522   return tr("Plot: %1 \nContents:\n %2").arg(Name()).arg(contents);
0523 }
0524 
0525 ScriptInterface* LegendItem::createScriptInterface() {
0526   return new LegendSI(this);
0527 }
0528 
0529 
0530 }
0531 
0532 // vim: ts=2 sw=2 et