File indexing completed on 2024-04-28 17:02:19
0001 /* 0002 This file is part of Massif Visualizer 0003 0004 Copyright 2014 Milian Wolff <mail@milianw.de> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License as 0008 published by the Free Software Foundation; either version 2 of 0009 the License or (at your option) version 3 or any later version 0010 accepted by the membership of KDE e.V. (or its successor approved 0011 by the membership of KDE e.V.), which shall act as a proxy 0012 defined in Section 14 of version 3 of the license. 0013 0014 This program is distributed in the hope that it will be useful, 0015 but WITHOUT ANY WARRANTY; without even the implied warranty of 0016 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0017 GNU General Public License for more details. 0018 0019 You should have received a copy of the GNU General Public License 0020 along with this program. If not, see <http://www.gnu.org/licenses/>. 0021 */ 0022 0023 #include "charttab.h" 0024 0025 #include "KChartChart" 0026 #include "KChartGridAttributes" 0027 #include "KChartHeaderFooter" 0028 #include "KChartCartesianCoordinatePlane" 0029 #include "KChartPlotter" 0030 #include "KChartLegend" 0031 #include "KChartDataValueAttributes" 0032 #include "KChartBackgroundAttributes" 0033 #include <KChartFrameAttributes.h> 0034 0035 #include "visualizer/totalcostmodel.h" 0036 #include "visualizer/detailedcostmodel.h" 0037 #include "visualizer/datatreemodel.h" 0038 #include "visualizer/filtereddatatreemodel.h" 0039 0040 #include "massifdata/util.h" 0041 #include "massifdata/snapshotitem.h" 0042 #include "massifdata/treeleafitem.h" 0043 #include "massifdata/filedata.h" 0044 0045 #include "massif-visualizer-settings.h" 0046 0047 #include <QDebug> 0048 #include <QVBoxLayout> 0049 #include <QPrinter> 0050 #include <QPrintPreviewDialog> 0051 #include <QLabel> 0052 #include <QMenu> 0053 #include <QApplication> 0054 #include <QDesktopWidget> 0055 #include <QSvgGenerator> 0056 #include <QWidgetAction> 0057 #include <QFileDialog> 0058 0059 #include <KColorScheme> 0060 #include <KLocalizedString> 0061 #include <KStandardAction> 0062 #include <KActionCollection> 0063 #include <KMessageBox> 0064 #include <KFormat> 0065 0066 using namespace KChart; 0067 using namespace Massif; 0068 0069 namespace { 0070 0071 class TimeAxis : public CartesianAxis 0072 { 0073 Q_OBJECT 0074 public: 0075 explicit TimeAxis(AbstractCartesianDiagram* diagram = 0) 0076 : CartesianAxis(diagram) 0077 {} 0078 0079 virtual const QString customizedLabel(const QString& label) const 0080 { 0081 // squeeze large numbers here 0082 // TODO: when the unit is 'b' also use prettyCost() here 0083 return QString::number(label.toDouble()); 0084 } 0085 }; 0086 0087 class SizeAxis : public CartesianAxis 0088 { 0089 Q_OBJECT 0090 public: 0091 explicit SizeAxis(AbstractCartesianDiagram* diagram = 0) 0092 : CartesianAxis(diagram) 0093 {} 0094 0095 virtual const QString customizedLabel(const QString& label) const 0096 { 0097 // TODO: change distance between labels to 1024 and simply use prettyCost() here 0098 KFormat format(QLocale::system()); 0099 return format.formatByteSize(label.toDouble(), 1, KFormat::MetricBinaryDialect); 0100 } 0101 }; 0102 0103 void markPeak(Plotter* p, const QModelIndex& peak, quint64 cost, const KColorScheme& scheme) 0104 { 0105 QBrush brush = p->model()->data(peak, DatasetBrushRole).value<QBrush>(); 0106 0107 QColor outline = brush.color(); 0108 QColor foreground = scheme.foreground().color(); 0109 QBrush background = scheme.background(); 0110 0111 DataValueAttributes dataAttributes = p->dataValueAttributes(peak); 0112 dataAttributes.setDataLabel(prettyCost(cost)); 0113 dataAttributes.setVisible(true); 0114 dataAttributes.setShowRepetitiveDataLabels(true); 0115 dataAttributes.setShowOverlappingDataLabels(false); 0116 0117 FrameAttributes frameAttrs = dataAttributes.frameAttributes(); 0118 QPen framePen(outline); 0119 framePen.setWidth(2); 0120 frameAttrs.setPen(framePen); 0121 frameAttrs.setVisible(true); 0122 dataAttributes.setFrameAttributes(frameAttrs); 0123 0124 MarkerAttributes a = dataAttributes.markerAttributes(); 0125 a.setMarkerSize(QSizeF(7, 7)); 0126 a.setPen(outline); 0127 a.setMarkerStyle(KChart::MarkerAttributes::MarkerDiamond); 0128 a.setVisible(true); 0129 dataAttributes.setMarkerAttributes(a); 0130 0131 TextAttributes txtAttrs = dataAttributes.textAttributes(); 0132 txtAttrs.setPen(foreground); 0133 txtAttrs.setFontSize(Measure(12)); 0134 dataAttributes.setTextAttributes(txtAttrs); 0135 0136 BackgroundAttributes bkgAtt = dataAttributes.backgroundAttributes(); 0137 0138 bkgAtt.setBrush(background); 0139 bkgAtt.setVisible(true); 0140 dataAttributes.setBackgroundAttributes(bkgAtt); 0141 0142 p->setDataValueAttributes(peak, dataAttributes); 0143 } 0144 0145 } 0146 0147 ChartTab::ChartTab(const FileData* data, 0148 KXMLGUIClient* guiParent, QWidget* parent) 0149 : DocumentTabInterface(data, guiParent, parent) 0150 , m_chart(new Chart(this)) 0151 , m_header(new QLabel(this)) 0152 , m_totalDiagram(0) 0153 , m_totalCostModel(new TotalCostModel(m_chart)) 0154 , m_detailedDiagram(0) 0155 , m_detailedCostModel(new DetailedCostModel(m_chart)) 0156 , m_legend(new Legend(m_chart)) 0157 , m_print(0) 0158 , m_saveAs(0) 0159 , m_toggleTotal(0) 0160 , m_toggleDetailed(0) 0161 , m_hideFunction(0) 0162 , m_hideOtherFunctions(0) 0163 , m_box(new QSpinBox(this)) 0164 , m_settingSelection(false) 0165 { 0166 setXMLFile(QStringLiteral("charttabui.rc"), true); 0167 setupActions(); 0168 0169 setLayout(new QVBoxLayout(this)); 0170 0171 setupGui(); 0172 } 0173 0174 ChartTab::~ChartTab() 0175 { 0176 } 0177 0178 void ChartTab::setupActions() 0179 { 0180 m_print = KStandardAction::print(this, SLOT(showPrintPreviewDialog()), actionCollection()); 0181 actionCollection()->addAction(QStringLiteral("file_print"), m_print); 0182 0183 m_saveAs = KStandardAction::saveAs(this, SLOT(saveCurrentDocument()), actionCollection()); 0184 actionCollection()->addAction(QStringLiteral("file_save_as"), m_saveAs); 0185 0186 m_toggleTotal = new QAction(QIcon::fromTheme(QStringLiteral("office-chart-area")), i18n("Toggle total cost graph"), actionCollection()); 0187 m_toggleTotal->setCheckable(true); 0188 m_toggleTotal->setChecked(true); 0189 connect(m_toggleTotal, &QAction::toggled, this, &ChartTab::showTotalGraph); 0190 actionCollection()->addAction(QStringLiteral("toggle_total"), m_toggleTotal); 0191 0192 m_toggleDetailed = new QAction(QIcon::fromTheme(QStringLiteral("office-chart-area-stacked")), i18n("Toggle detailed cost graph"), actionCollection()); 0193 m_toggleDetailed->setCheckable(true); 0194 m_toggleDetailed->setChecked(true); 0195 connect(m_toggleDetailed, &QAction::toggled, this, &ChartTab::showDetailedGraph); 0196 actionCollection()->addAction(QStringLiteral("toggle_detailed"), m_toggleDetailed); 0197 0198 QWidgetAction* stackNumAction = new QWidgetAction(actionCollection()); 0199 actionCollection()->addAction(QStringLiteral("stackNum"), stackNumAction); 0200 stackNumAction->setText(i18n("Stacked diagrams")); 0201 QWidget *stackNumWidget = new QWidget; 0202 QHBoxLayout* stackNumLayout = new QHBoxLayout; 0203 stackNumLayout->addWidget(new QLabel(i18n("Stacked diagrams:"))); 0204 m_box->setMinimum(0); 0205 m_box->setMaximum(50); 0206 m_box->setValue(10); 0207 connect(m_box, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, &ChartTab::setStackNum); 0208 stackNumLayout->addWidget(m_box); 0209 stackNumWidget->setLayout(stackNumLayout); 0210 stackNumAction->setDefaultWidget(stackNumWidget); 0211 0212 m_hideFunction = new QAction(i18n("hide function"), this); 0213 connect(m_hideFunction, &QAction::triggered, 0214 this, &ChartTab::slotHideFunction); 0215 m_hideOtherFunctions = new QAction(i18n("hide other functions"), this); 0216 connect(m_hideOtherFunctions, &QAction::triggered, 0217 this, &ChartTab::slotHideOtherFunctions); 0218 } 0219 0220 void ChartTab::setupGui() 0221 { 0222 layout()->addWidget(m_header); 0223 layout()->addWidget(m_chart); 0224 0225 // HACK: otherwise the legend becomes _really_ large and might even crash X... 0226 // to visualize the issue, try: m_chart->setMaximumSize(QSize(10000, 10000)); 0227 m_chart->setMaximumSize(qApp->desktop()->size()); 0228 0229 // for axis labels to fit 0230 m_chart->setGlobalLeadingRight(10); 0231 m_chart->setGlobalLeadingLeft(10); 0232 m_chart->setGlobalLeadingTop(20); 0233 m_chart->setContextMenuPolicy(Qt::CustomContextMenu); 0234 0235 updateLegendPosition(); 0236 m_legend->setTitleText(QString()); 0237 m_legend->setSortOrder(Qt::DescendingOrder); 0238 0239 m_chart->addLegend(m_legend); 0240 0241 //NOTE: this has to be set _after_ the legend was added to the chart... 0242 updateLegendFont(); 0243 m_legend->setTextAlignment(Qt::AlignLeft); 0244 m_legend->hide(); 0245 0246 connect(m_chart, &Chart::customContextMenuRequested, 0247 this, &ChartTab::chartContextMenuRequested); 0248 0249 //BEGIN KChart 0250 KColorScheme scheme(QPalette::Active, KColorScheme::Window); 0251 QPen foreground(scheme.foreground().color()); 0252 0253 //Begin Legend 0254 BackgroundAttributes bkgAtt = m_legend->backgroundAttributes(); 0255 QColor background = scheme.background(KColorScheme::AlternateBackground).color(); 0256 background.setAlpha(200); 0257 bkgAtt.setBrush(QBrush(background)); 0258 bkgAtt.setVisible(true); 0259 m_legend->setBackgroundAttributes(bkgAtt); 0260 TextAttributes txtAttrs = m_legend->textAttributes(); 0261 txtAttrs.setPen(foreground); 0262 m_legend->setTextAttributes(txtAttrs); 0263 0264 m_header->setAlignment(Qt::AlignCenter); 0265 updateHeader(); 0266 0267 //BEGIN TotalDiagram 0268 m_totalDiagram = new Plotter(this); 0269 m_totalDiagram->setAntiAliasing(true); 0270 0271 CartesianAxis* bottomAxis = new TimeAxis(m_totalDiagram); 0272 TextAttributes axisTextAttributes = bottomAxis->textAttributes(); 0273 axisTextAttributes.setPen(foreground); 0274 axisTextAttributes.setFontSize(Measure(10)); 0275 bottomAxis->setTextAttributes(axisTextAttributes); 0276 TextAttributes axisTitleTextAttributes = bottomAxis->titleTextAttributes(); 0277 axisTitleTextAttributes.setPen(foreground); 0278 axisTitleTextAttributes.setFontSize(Measure(12)); 0279 bottomAxis->setTitleTextAttributes(axisTitleTextAttributes); 0280 bottomAxis->setTitleText(i18n("time in %1", m_data->timeUnit())); 0281 bottomAxis->setPosition ( CartesianAxis::Bottom ); 0282 m_totalDiagram->addAxis(bottomAxis); 0283 0284 CartesianAxis* rightAxis = new SizeAxis(m_totalDiagram); 0285 rightAxis->setTextAttributes(axisTextAttributes); 0286 rightAxis->setTitleTextAttributes(axisTitleTextAttributes); 0287 rightAxis->setTitleText(i18n("memory heap size")); 0288 rightAxis->setPosition ( CartesianAxis::Right ); 0289 m_totalDiagram->addAxis(rightAxis); 0290 0291 m_totalCostModel->setSource(m_data); 0292 m_totalDiagram->setModel(m_totalCostModel); 0293 0294 CartesianCoordinatePlane* coordinatePlane = dynamic_cast<CartesianCoordinatePlane*>(m_chart->coordinatePlane()); 0295 Q_ASSERT(coordinatePlane); 0296 coordinatePlane->addDiagram(m_totalDiagram); 0297 0298 GridAttributes gridAttributes = coordinatePlane->gridAttributes(Qt::Horizontal); 0299 gridAttributes.setAdjustBoundsToGrid(false, false); 0300 coordinatePlane->setGridAttributes(Qt::Horizontal, gridAttributes); 0301 0302 m_legend->addDiagram(m_totalDiagram); 0303 0304 m_detailedDiagram = new Plotter; 0305 m_detailedDiagram->setAntiAliasing(true); 0306 m_detailedDiagram->setType(KChart::Plotter::Stacked); 0307 0308 m_detailedCostModel->setSource(m_data); 0309 m_detailedDiagram->setModel(m_detailedCostModel); 0310 0311 updatePeaks(); 0312 0313 m_chart->coordinatePlane()->addDiagram(m_detailedDiagram); 0314 0315 m_legend->addDiagram(m_detailedDiagram); 0316 m_legend->show(); 0317 0318 m_box->setValue(m_detailedCostModel->maximumDatasetCount()); 0319 0320 connect(m_totalDiagram, &Plotter::clicked, 0321 this, &ChartTab::totalItemClicked); 0322 connect(m_detailedDiagram, &Plotter::clicked, 0323 this, &ChartTab::detailedItemClicked); 0324 } 0325 0326 void ChartTab::settingsChanged() 0327 { 0328 updateHeader(); 0329 updatePeaks(); 0330 updateLegendPosition(); 0331 updateLegendFont(); 0332 } 0333 0334 void ChartTab::setDetailedDiagramHidden(bool hidden) 0335 { 0336 m_detailedDiagram->setHidden(hidden); 0337 } 0338 0339 void ChartTab::setDetailedDiagramVisible(bool visible) 0340 { 0341 m_detailedDiagram->setVisible(visible); 0342 } 0343 0344 void ChartTab::setTotalDiagramHidden(bool hidden) 0345 { 0346 m_totalDiagram->setHidden(hidden); 0347 } 0348 0349 void ChartTab::setTotalDiagramVisible(bool visible) 0350 { 0351 m_totalDiagram->setVisible(visible); 0352 } 0353 0354 void ChartTab::updatePeaks() 0355 { 0356 KColorScheme scheme(QPalette::Active, KColorScheme::Window); 0357 0358 if (m_data->peak()) { 0359 const QModelIndex peak = m_totalCostModel->peak(); 0360 Q_ASSERT(peak.isValid()); 0361 markPeak(m_totalDiagram, peak, m_data->peak()->cost(), scheme); 0362 } 0363 updateDetailedPeaks(); 0364 } 0365 0366 void ChartTab::updateLegendPosition() 0367 { 0368 KChartEnums::PositionValue pos; 0369 switch (Settings::self()->legendPosition()) { 0370 case Settings::EnumLegendPosition::North: 0371 pos = KChartEnums::PositionNorth; 0372 break; 0373 case Settings::EnumLegendPosition::South: 0374 pos = KChartEnums::PositionSouth; 0375 break; 0376 case Settings::EnumLegendPosition::East: 0377 pos = KChartEnums::PositionEast; 0378 break; 0379 case Settings::EnumLegendPosition::West: 0380 pos = KChartEnums::PositionWest; 0381 break; 0382 case Settings::EnumLegendPosition::Floating: 0383 pos = KChartEnums::PositionFloating; 0384 break; 0385 default: 0386 pos = KChartEnums::PositionFloating; 0387 qDebug() << "invalid legend position"; 0388 } 0389 m_legend->setPosition(Position(pos)); 0390 0391 Qt::Alignment align; 0392 switch (Settings::self()->legendAlignment()) { 0393 case Settings::EnumLegendAlignment::Left: 0394 align = Qt::AlignLeft; 0395 break; 0396 case Settings::EnumLegendAlignment::Center: 0397 align = Qt::AlignHCenter | Qt::AlignVCenter; 0398 break; 0399 case Settings::EnumLegendAlignment::Right: 0400 align = Qt::AlignRight; 0401 break; 0402 case Settings::EnumLegendAlignment::Top: 0403 align = Qt::AlignTop; 0404 break; 0405 case Settings::EnumLegendAlignment::Bottom: 0406 align = Qt::AlignBottom; 0407 break; 0408 default: 0409 align = Qt::AlignHCenter | Qt::AlignVCenter; 0410 qDebug() << "invalid legend alignmemnt"; 0411 } 0412 0413 // do something reasonable since top,bottom have no effect 0414 // when used with north,south, same for left,right used with 0415 // east,west 0416 if ((((pos == KChartEnums::PositionNorth) || (pos == KChartEnums::PositionSouth)) 0417 && ((align == Qt::AlignTop) || (align == Qt::AlignBottom))) 0418 || (((pos == KChartEnums::PositionEast) || (pos == KChartEnums::PositionWest)) 0419 && ((align == Qt::AlignLeft) || (align == Qt::AlignRight)))) { 0420 0421 align = Qt::AlignHCenter | Qt::AlignVCenter; 0422 } 0423 0424 m_legend->setAlignment(align); 0425 } 0426 0427 void ChartTab::updateLegendFont() 0428 { 0429 TextAttributes att = m_legend->textAttributes(); 0430 att.setAutoShrink(true); 0431 att.setFontSize(Measure(Settings::self()->legendFontSize())); 0432 QFont font(QStringLiteral("monospace")); 0433 font.setStyleHint(QFont::TypeWriter); 0434 att.setFont(font); 0435 m_legend->setTextAttributes(att); 0436 } 0437 0438 void ChartTab::updateDetailedPeaks() 0439 { 0440 KColorScheme scheme(QPalette::Active, KColorScheme::Window); 0441 0442 const DetailedCostModel::Peaks& peaks = m_detailedCostModel->peaks(); 0443 DetailedCostModel::Peaks::const_iterator it = peaks.constBegin(); 0444 while (it != peaks.constEnd()) { 0445 const QModelIndex peak = it.key(); 0446 Q_ASSERT(peak.isValid()); 0447 markPeak(m_detailedDiagram, peak, it.value()->cost(), scheme); 0448 ++it; 0449 } 0450 } 0451 0452 void ChartTab::updateHeader() 0453 { 0454 const QString app = m_data->cmd().split(QLatin1Char(' '), QString::SkipEmptyParts).first(); 0455 0456 m_header->setText(QString::fromLatin1("<b>%1</b><br /><i>%2</i>") 0457 .arg(i18n("Memory consumption of %1", app)) 0458 .arg(i18n("Peak of %1 at snapshot #%2", prettyCost(m_data->peak()->cost()), m_data->peak()->number())) 0459 ); 0460 m_header->setToolTip(i18n("Command: %1\nValgrind Options: %2", m_data->cmd(), m_data->description())); 0461 } 0462 0463 void ChartTab::saveCurrentDocument() 0464 { 0465 const auto saveFilename = QFileDialog::getSaveFileName(this, i18n("Save Current Visualization"), QString(), 0466 i18n("Images (*.png *.jpg *.tiff *.svg)")); 0467 0468 if (!saveFilename.isEmpty()) { 0469 0470 // TODO: implement a dialog to expose more options to the user. 0471 // for example we could expose dpi, size, and various format 0472 // dependent options such as compressions settings. 0473 0474 // Vector graphic format 0475 if (QFileInfo(saveFilename).suffix().compare(QLatin1String("svg")) == 0) { 0476 QSvgGenerator generator; 0477 generator.setFileName(saveFilename); 0478 generator.setSize(m_chart->size()); 0479 generator.setViewBox(m_chart->rect()); 0480 0481 QPainter painter; 0482 painter.begin(&generator); 0483 m_chart->paint(&painter, m_chart->rect()); 0484 painter.end(); 0485 } 0486 0487 // Other format 0488 else if (!QPixmap::grabWidget(m_chart).save(saveFilename)) { 0489 0490 KMessageBox::sorry(this, QString( 0491 i18n("Error, failed to save the image to %1.") 0492 ).arg(saveFilename)); 0493 } 0494 } 0495 } 0496 0497 void ChartTab::showPrintPreviewDialog() 0498 { 0499 QPrinter printer; 0500 QPrintPreviewDialog *ppd = new QPrintPreviewDialog(&printer, this); 0501 ppd->setAttribute(Qt::WA_DeleteOnClose); 0502 connect(ppd, &QPrintPreviewDialog::paintRequested, this, &ChartTab::printFile); 0503 ppd->setWindowTitle(i18n("Massif Chart Print Preview")); 0504 ppd->resize(800, 600); 0505 ppd->exec(); 0506 } 0507 0508 void ChartTab::printFile(QPrinter *printer) 0509 { 0510 QPainter painter; 0511 painter.begin(printer); 0512 m_chart->paint(&painter, printer->pageRect()); 0513 painter.end(); 0514 } 0515 0516 void ChartTab::showDetailedGraph(bool show) 0517 { 0518 m_detailedDiagram->setHidden(!show); 0519 m_toggleDetailed->setChecked(show); 0520 m_chart->update(); 0521 } 0522 0523 void ChartTab::showTotalGraph(bool show) 0524 { 0525 m_totalDiagram->setHidden(!show); 0526 m_toggleTotal->setChecked(show); 0527 m_chart->update(); 0528 } 0529 0530 void ChartTab::slotHideFunction() 0531 { 0532 m_detailedCostModel->hideFunction(m_hideFunction->data().value<const TreeLeafItem*>()); 0533 } 0534 0535 void ChartTab::slotHideOtherFunctions() 0536 { 0537 m_detailedCostModel->hideOtherFunctions(m_hideOtherFunctions->data().value<const TreeLeafItem*>()); 0538 } 0539 0540 void ChartTab::chartContextMenuRequested(const QPoint& pos) 0541 { 0542 const QPoint dPos = m_detailedDiagram->mapFromGlobal(m_chart->mapToGlobal(pos)); 0543 0544 const QModelIndex idx = m_detailedDiagram->indexAt(dPos); 0545 if (!idx.isValid()) { 0546 return; 0547 } 0548 // hack: the ToolTip will only be queried by KChart and that one uses the 0549 // left index, but we want it to query the right one 0550 const QModelIndex _idx = m_detailedCostModel->index(idx.row() + 1, idx.column(), idx.parent()); 0551 ModelItem item = m_detailedCostModel->itemForIndex(_idx); 0552 0553 if (!item.first) { 0554 return; 0555 } 0556 0557 QMenu menu; 0558 0559 m_hideFunction->setData(QVariant::fromValue(item.first)); 0560 menu.addAction(m_hideFunction); 0561 0562 m_hideOtherFunctions->setData(QVariant::fromValue(item.first)); 0563 menu.addAction(m_hideOtherFunctions); 0564 0565 menu.addSeparator(); 0566 0567 emit contextMenuRequested(item, &menu); 0568 0569 menu.exec(m_detailedDiagram->mapToGlobal(dPos)); 0570 } 0571 0572 void ChartTab::setStackNum(int num) 0573 { 0574 m_detailedCostModel->setMaximumDatasetCount(num); 0575 updatePeaks(); 0576 } 0577 0578 void ChartTab::detailedItemClicked(const QModelIndex& idx) 0579 { 0580 m_detailedCostModel->setSelection(idx); 0581 m_totalCostModel->setSelection(QModelIndex()); 0582 m_chart->update(); 0583 0584 // hack: the ToolTip will only be queried by KChart and that one uses the 0585 // left index, but we want it to query the right one 0586 m_settingSelection = true; 0587 const QModelIndex _idx = m_detailedCostModel->index(idx.row() + 1, idx.column(), idx.parent()); 0588 emit modelItemSelected(m_detailedCostModel->itemForIndex(_idx)); 0589 m_settingSelection = false; 0590 } 0591 0592 void ChartTab::totalItemClicked(const QModelIndex& idx) 0593 { 0594 const QModelIndex _idx = m_totalCostModel->index(idx.row() + 1, idx.column(), idx.parent()); 0595 m_totalCostModel->setSelection(_idx); 0596 m_detailedCostModel->setSelection(QModelIndex()); 0597 m_chart->update(); 0598 0599 m_settingSelection = true; 0600 emit modelItemSelected(m_totalCostModel->itemForIndex(_idx)); 0601 m_settingSelection = false; 0602 } 0603 0604 void ChartTab::selectModelItem(const ModelItem& item) 0605 { 0606 if (m_settingSelection) { 0607 return; 0608 } 0609 0610 if (item.first) { 0611 m_detailedCostModel->setSelection(m_detailedCostModel->indexForItem(item)); 0612 m_totalCostModel->setSelection(QModelIndex()); 0613 } else { 0614 m_totalCostModel->setSelection(m_totalCostModel->indexForItem(item)); 0615 m_detailedCostModel->setSelection(QModelIndex()); 0616 } 0617 0618 m_chart->update(); 0619 } 0620 0621 #include "charttab.moc"