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