File indexing completed on 2024-04-28 05:41:31

0001 /*
0002     This file is part of KCachegrind.
0003 
0004     SPDX-FileCopyrightText: 2003-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
0005 
0006     SPDX-License-Identifier: GPL-2.0-only
0007 */
0008 
0009 /*
0010  * Call Map View
0011  */
0012 
0013 
0014 #include "callmapview.h"
0015 
0016 #include <QPixmap>
0017 #include <QAction>
0018 #include <QMenu>
0019 
0020 #include "config.h"
0021 #include "globalguiconfig.h"
0022 #include "listutils.h"
0023 #include "toplevelbase.h"
0024 
0025 
0026 //
0027 // CallMapView
0028 //
0029 
0030 
0031 // defaults
0032 #define DEFAULT_SPLITMODE    "Rows"
0033 #define DEFAULT_DRAWNAME     true
0034 #define DEFAULT_DRAWCOST     true
0035 #define DEFAULT_DRAWLOCATION false
0036 #define DEFAULT_DRAWCALLS    false
0037 #define DEFAULT_FORCESTRINGS false
0038 #define DEFAULT_ROTATION     true
0039 #define DEFAULT_SHADING      true
0040 #define DEFAULT_STOPNAME     ""
0041 #define DEFAULT_MAXDEPTH     -1
0042 #define DEFAULT_MAXAREA      100
0043 
0044 
0045 CallMapView::CallMapView(bool showCallers, TraceItemView* parentView,
0046                          QWidget* parent, const QString& name)
0047     : TreeMapWidget(new CallMapRootItem(), parent),  TraceItemView(parentView)
0048 {
0049     setObjectName(name);
0050     _showCallers = showCallers;
0051 
0052     setFieldType(0, tr("A thing's name", "Name" ));
0053     setFieldType(1, tr( "Cost" ));
0054     setFieldType(2, tr( "Location" ));
0055     setFieldPosition(2, TreeMapItem::TopLeft);
0056     setFieldType(3, tr( "Calls" ));
0057     setFieldPosition(3, TreeMapItem::TopRight);
0058 
0059     setSplitMode(DEFAULT_SPLITMODE);
0060     setFieldVisible(0, DEFAULT_DRAWNAME);
0061     setFieldVisible(1, DEFAULT_DRAWCOST);
0062     setFieldVisible(2, DEFAULT_DRAWLOCATION);
0063     setFieldVisible(3, DEFAULT_DRAWCALLS);
0064     setFieldForced(0, DEFAULT_FORCESTRINGS);
0065     setFieldForced(1, DEFAULT_FORCESTRINGS);
0066     setFieldForced(2, DEFAULT_FORCESTRINGS);
0067     setFieldForced(3, DEFAULT_FORCESTRINGS);
0068     setAllowRotation(DEFAULT_ROTATION);
0069     setShadingEnabled(DEFAULT_SHADING);
0070     setMinimalArea(DEFAULT_MAXAREA);
0071 
0072     connect(this,
0073             &TreeMapWidget::doubleClicked,
0074             this, &CallMapView::activatedSlot);
0075     connect(this,
0076             &TreeMapWidget::returnPressed,
0077             this, &CallMapView::activatedSlot);
0078     connect(this,
0079             &TreeMapWidget::currentChanged,
0080             this, &CallMapView::selectedSlot);
0081     connect(this,
0082             &TreeMapWidget::contextMenuRequested,
0083             this, &CallMapView::context);
0084 
0085     this->setWhatsThis( whatsThis());
0086 }
0087 
0088 QString CallMapView::whatsThis() const
0089 {
0090     QString s = _showCallers ?
0091                     tr( "<b>Caller Map</b>"
0092                         "<p>This graph shows the nested hierarchy of "
0093                         "all callers of the current activated function. "
0094                         "Each colored rectangle represents a function; "
0095                         "its size tries to be proportional to the cost spent "
0096                         "therein while the active function is running "
0097                         "(however, there are drawing constraints).</p>") :
0098                     tr("<b>Call Map</b>"
0099                        "<p>This graph shows the nested hierarchy of "
0100                        "all callees of the current activated function. "
0101                        "Each colored rectangle represents a function; "
0102                        "its size tries to be proportional to the cost spent "
0103                        "therein while the active function is running "
0104                        "(however, there are drawing constraints).</p>");
0105 
0106     s += tr( "<p>Appearance options can be found in the "
0107              "context menu. To get exact size proportions, "
0108              "choose 'Hide incorrect borders'. As this mode can be "
0109              "<em>very</em> time consuming, you may want to limit "
0110              "the maximum drawn nesting level before. "
0111              "'Best' determinates the split direction for children "
0112              "from the aspect ratio of the parent. "
0113              "'Always Best' decides on remaining space for each "
0114              "sibling. "
0115              "'Ignore Proportions' takes space for function name "
0116              "drawing <em>before</em> drawing children. Note that "
0117              "size proportions can get <em>heavily</em> wrong.</p>"
0118 
0119              "<p>This is a <em>TreeMap</em> widget. "
0120              "Keyboard navigation is available with the left/right arrow "
0121              "keys for traversing siblings, and up/down arrow keys "
0122              "to go a nesting level up/down. "
0123              "<em>Return</em> activates the current item.</p>");
0124 
0125     return s;
0126 }
0127 
0128 void CallMapView::setData(TraceData* d)
0129 {
0130     TraceItemView::setData(d);
0131 
0132     ((CallMapRootItem*)base())->setFunction(nullptr);
0133 }
0134 
0135 void CallMapView::addItemListMenu(QMenu* menu, TreeMapItem* item)
0136 {
0137     QAction* a;
0138 
0139     QMenu* m = menu->addMenu(tr("Go To"));
0140     int count = 0;
0141     while (count<GlobalConfig::maxSymbolCount() && item) {
0142         QString name = item->text(IDX_FUNCNAME);
0143         a = m->addAction(GlobalConfig::shortenSymbol(name));
0144         a->setData(QVariant::fromValue( (void*)item ));
0145         item = item->parent();
0146         count++;
0147     }
0148     connect(m, &QMenu::triggered,
0149             this, &CallMapView::mapItemTriggered );
0150 }
0151 
0152 void CallMapView::mapItemTriggered(QAction* a)
0153 {
0154     activatedSlot( (TreeMapItem*) a->data().value<void*>() );
0155 }
0156 
0157 QAction* CallMapView::addDrawingDepthAction(QMenu* m,
0158                                             const QString& s, int d)
0159 {
0160     QAction* a = m->addAction(s);
0161     a->setData(d);
0162     a->setCheckable(true);
0163     a->setChecked(maxDrawingDepth() == d);
0164     return a;
0165 }
0166 
0167 void CallMapView::addDrawingDepthMenu(QMenu* menu,
0168                                       TreeMapItem* i, const QString& name)
0169 {
0170     QMenu* m = menu->addMenu(tr("Stop at Depth"));
0171     addDrawingDepthAction(m, tr("No Depth Limit"), -1);
0172     m->addSeparator();
0173     addDrawingDepthAction(m, tr("Depth 10"), 10);
0174     addDrawingDepthAction(m, tr("Depth 15"), 15);
0175     addDrawingDepthAction(m, tr("Depth 20"), 20);
0176     if (i) {
0177         m->addSeparator();
0178         addDrawingDepthAction(m, tr("Depth of '%1' (%2)")
0179                               .arg(name).arg(i->depth()),
0180                               i->depth());
0181     }
0182     int maxDepth = maxDrawingDepth();
0183     if (maxDepth>0) {
0184         m->addSeparator();
0185         addDrawingDepthAction(m, tr("Decrement Depth (to %1)").arg(maxDepth-1),
0186                               maxDepth-1);
0187         addDrawingDepthAction(m, tr("Increment Depth (to %1)").arg(maxDepth+1),
0188                               maxDepth+1);
0189     }
0190 
0191     connect(m, &QMenu::triggered,
0192             this, &CallMapView::drawingDepthTriggered );
0193 }
0194 
0195 void CallMapView::drawingDepthTriggered(QAction* a)
0196 {
0197     setMaxDrawingDepth(a->data().toInt());
0198 }
0199 
0200 QAction* CallMapView::addStopFunctionAction(QMenu* m,
0201                                             const QString& s,
0202                                             const QString& v)
0203 {
0204     QAction* a = m->addAction(s);
0205     a->setData(v);
0206     a->setCheckable(true);
0207     a->setChecked(fieldStop(IDX_FUNCNAME) == v);
0208     return a;
0209 }
0210 
0211 void CallMapView::addStopFunctionMenu(QMenu* menu, TreeMapItem* item)
0212 {
0213     QMenu* m = menu->addMenu(tr("Stop at Function"));
0214     addStopFunctionAction(m, tr("No Function Limit"), QString());
0215 
0216     bool foundStopName = false;
0217     QAction* a;
0218     if (item) {
0219         m->addSeparator();
0220         int count = 0;
0221         while (count<GlobalConfig::maxSymbolCount() && item) {
0222             QString name = GlobalConfig::shortenSymbol(item->text(IDX_FUNCNAME));
0223             a = addStopFunctionAction(m, name, item->text(IDX_FUNCNAME));
0224             if (a->isChecked()) foundStopName = true;
0225             item = item->parent();
0226             count++;
0227         }
0228     }
0229     if (!foundStopName && !fieldStop(IDX_FUNCNAME).isEmpty()) {
0230         m->addSeparator();
0231         QString name = GlobalConfig::shortenSymbol(fieldStop(IDX_FUNCNAME));
0232         addStopFunctionAction(m, name, fieldStop(IDX_FUNCNAME));
0233     }
0234 
0235     connect(m, &QMenu::triggered,
0236             this, &CallMapView::stopFunctionTriggered );
0237 }
0238 
0239 void CallMapView::stopFunctionTriggered(QAction* a)
0240 {
0241     setFieldStop(IDX_FUNCNAME, a->data().toString());
0242 }
0243 
0244 QAction* CallMapView::addAreaLimitAction(QMenu* m,
0245                                          const QString& s, int v)
0246 {
0247     QAction* a = m->addAction(s);
0248     a->setData(v);
0249     a->setCheckable(true);
0250     a->setChecked(minimalArea() == v);
0251     return a;
0252 }
0253 
0254 void CallMapView::addAreaLimitMenu(QMenu* menu, TreeMapItem* i,
0255                                    const QString& name)
0256 {
0257     QMenu* m = menu->addMenu(tr("Stop at Area"));
0258     addAreaLimitAction(m, tr("No Area Limit"), -1);
0259     m->addSeparator();
0260     addAreaLimitAction(m, tr("100 Pixels"), 100);
0261     addAreaLimitAction(m, tr("200 Pixels"), 200);
0262     addAreaLimitAction(m, tr("500 Pixels"), 500);
0263     addAreaLimitAction(m, tr("1000 Pixels"), 1000);
0264 
0265     int currentArea = 0;
0266     if (i) {
0267         currentArea = i->width() * i->height();
0268         m->addSeparator();
0269         addAreaLimitAction(m, tr("Area of '%1' (%2)")
0270                            .arg(name).arg(currentArea), currentArea);
0271     }
0272     int mArea = minimalArea();
0273     if (mArea>0) {
0274         m->addSeparator();
0275         addAreaLimitAction(m, tr("Double Area Limit (to %1)")
0276                            .arg(mArea*2), mArea*2);
0277         addAreaLimitAction(m, tr("Half Area Limit (to %1)")
0278                            .arg(mArea/2), mArea/2);
0279     }
0280 
0281     connect(m, &QMenu::triggered,
0282             this, &CallMapView::areaLimitTriggered );
0283 }
0284 
0285 void CallMapView::areaLimitTriggered(QAction* a)
0286 {
0287     setMinimalArea(a->data().toInt());
0288 }
0289 
0290 QAction* CallMapView::addBorderWidthAction(QMenu* m, const QString& s, int v)
0291 {
0292     QAction* a = m->addAction(s);
0293     a->setData(v);
0294     a->setCheckable(true);
0295     a->setChecked(borderWidth() == v);
0296     return a;
0297 }
0298 
0299 void CallMapView::borderWidthTriggered(QAction* a)
0300 {
0301     setBorderWidth(a->data().toInt());
0302 }
0303 
0304 void CallMapView::context(TreeMapItem* i,const QPoint & p)
0305 {
0306     if (!i) return;
0307 
0308     QMenu popup;
0309     QAction* a;
0310 
0311     QString shortCurrentName;
0312     if (i) {
0313         shortCurrentName = GlobalConfig::shortenSymbol(i->text(IDX_FUNCNAME));
0314     }
0315 
0316     if (i) {
0317         addItemListMenu(&popup, i);
0318         popup.addSeparator();
0319     }
0320     addGoMenu(&popup);
0321     popup.addSeparator();
0322     addDrawingDepthMenu(&popup, i, shortCurrentName);
0323     addStopFunctionMenu(&popup, i);
0324     addAreaLimitMenu(&popup, i, shortCurrentName);
0325     popup.addSeparator();
0326 
0327     QMenu* vpopup = popup.addMenu(tr("Visualization"));
0328     QMenu* spopup = vpopup->addMenu(tr("Split Direction"));
0329     addSplitDirectionItems(spopup);
0330 
0331     QAction* skipBorderAction = vpopup->addAction(tr("Skip Incorrect Borders"));
0332     skipBorderAction->setEnabled(!_showCallers);
0333     skipBorderAction->setCheckable(true);
0334     skipBorderAction->setChecked(skipIncorrectBorder());
0335 
0336     QMenu* bpopup = vpopup->addMenu(tr("Border Width"));
0337     a = addBorderWidthAction(bpopup, tr("Border 0"), 0);
0338     a->setEnabled(!_showCallers);
0339     addBorderWidthAction(bpopup, tr("Border 1"), 1);
0340     addBorderWidthAction(bpopup, tr("Border 2"), 2);
0341     addBorderWidthAction(bpopup, tr("Border 3"), 3);
0342     connect(bpopup, &QMenu::triggered,
0343             this, &CallMapView::borderWidthTriggered );
0344     vpopup->addSeparator();
0345 
0346     QAction* drawNamesAction = vpopup->addAction(tr("Draw Symbol Names"));
0347     drawNamesAction->setCheckable(true);
0348     QAction* drawCostAction = vpopup->addAction(tr("Draw Cost"));
0349     drawCostAction->setCheckable(true);
0350     QAction* drawLocationAction = vpopup->addAction(tr("Draw Location"));
0351     drawLocationAction->setCheckable(true);
0352     QAction* drawCallsAction = vpopup->addAction(tr("Draw Calls"));
0353     drawCallsAction->setCheckable(true);
0354     vpopup->addSeparator();
0355 
0356     QAction* ignorePropAction = vpopup->addAction(tr("Ignore Proportions"));
0357     ignorePropAction->setCheckable(true);
0358     QAction* allowRotationAction = vpopup->addAction(tr("Allow Rotation"));
0359     allowRotationAction->setCheckable(true);
0360     if (!fieldVisible(0) &&
0361         !fieldVisible(1) &&
0362         !fieldVisible(2) &&
0363         !fieldVisible(3)) {
0364         ignorePropAction->setEnabled(false);
0365         allowRotationAction->setEnabled(false);
0366     }
0367     else {
0368         drawNamesAction->setChecked(fieldVisible(IDX_FUNCNAME));
0369         drawCostAction->setChecked(fieldVisible(IDX_COST));
0370         drawLocationAction->setChecked(fieldVisible(IDX_LOCATION));
0371         drawCallsAction->setChecked(fieldVisible(IDX_CALLCOUNT));
0372         ignorePropAction->setChecked(fieldForced(0));
0373         allowRotationAction->setChecked(allowRotation());
0374     }
0375 
0376     QAction* drawShadingAction = vpopup->addAction(tr("Shading"));
0377     drawShadingAction->setCheckable(true);
0378     drawShadingAction->setChecked(isShadingEnabled());
0379 
0380     a = popup.exec(mapToGlobal(p));
0381     if (a == drawNamesAction)
0382         setFieldVisible(IDX_FUNCNAME, !fieldVisible(IDX_FUNCNAME));
0383     else if (a == drawCostAction)
0384         setFieldVisible(IDX_COST, !fieldVisible(IDX_COST));
0385     else if (a == drawLocationAction)
0386         setFieldVisible(IDX_LOCATION, !fieldVisible(IDX_LOCATION));
0387     else if (a == drawCallsAction)
0388         setFieldVisible(IDX_CALLCOUNT, !fieldVisible(IDX_CALLCOUNT));
0389     else if (a == ignorePropAction) {
0390         bool newSetting = !fieldForced(0);
0391         setFieldForced(0, newSetting);
0392         setFieldForced(1, newSetting);
0393         setFieldForced(2, newSetting);
0394         setFieldForced(3, newSetting);
0395     }
0396     else if (a == allowRotationAction)
0397         setAllowRotation(!allowRotation());
0398     else if (a == drawShadingAction)
0399         setShadingEnabled(!isShadingEnabled());
0400     else if (a == skipBorderAction)
0401         setSkipIncorrectBorder(!skipIncorrectBorder());
0402 }
0403 
0404 void CallMapView::activatedSlot(TreeMapItem* item)
0405 {
0406     if (!item) return;
0407 
0408     if (item->rtti() == 1) {
0409         CallMapRootItem* bi = (CallMapRootItem*)item;
0410         activated(bi->function());
0411     }
0412     else if (item->rtti() == 2) {
0413         CallMapCallingItem* ci = (CallMapCallingItem*)item;
0414         activated(ci->function());
0415     }
0416     else if (item->rtti() == 3) {
0417         CallMapCallerItem* ci = (CallMapCallerItem*)item;
0418         activated(ci->function());
0419     }
0420 }
0421 
0422 void CallMapView::selectedSlot(TreeMapItem* item, bool kbd)
0423 {
0424     if (!item) return;
0425     if (item->text(IDX_FUNCNAME).isEmpty()) return;
0426 
0427     if (kbd) {
0428         QString msg = tr("Call Map: Current is '%1'").arg(item->text(IDX_FUNCNAME));
0429         if (_topLevel)
0430             _topLevel->showMessage(msg, 5000);
0431     }
0432 
0433     TraceFunction* f = nullptr;
0434 
0435     if (item->rtti() == 1) {
0436         CallMapRootItem* bi = (CallMapRootItem*)item;
0437         f = bi->function();
0438     }
0439     else if (item->rtti() == 2) {
0440         CallMapCallingItem* ci = (CallMapCallingItem*)item;
0441         f = ci->function();
0442     }
0443     else if (item->rtti() == 3) {
0444         CallMapCallerItem* ci = (CallMapCallerItem*)item;
0445         f = ci->function();
0446     }
0447     if (f) {
0448         // this avoids marking
0449         _selectedItem = f;
0450         selected(f);
0451     }
0452 }
0453 
0454 CostItem* CallMapView::canShow(CostItem* i)
0455 {
0456     ProfileContext::Type t = i ? i->type() : ProfileContext::InvalidType;
0457 
0458     switch(t) {
0459     case ProfileContext::Function:
0460     case ProfileContext::FunctionCycle:
0461         return i;
0462     default:
0463         break;
0464     }
0465     return nullptr;
0466 }
0467 
0468 void CallMapView::doUpdate(int changeType, bool)
0469 {
0470     if (changeType == eventType2Changed) return;
0471 
0472     // if there is a selected item, always draw marking...
0473     if (changeType & selectedItemChanged) {
0474         TraceFunction* f = nullptr;
0475 
0476         if (_selectedItem) {
0477             switch(_selectedItem->type()) {
0478             case ProfileContext::Function:
0479             case ProfileContext::FunctionCycle:
0480                 f = (TraceFunction*)_selectedItem;
0481                 break;
0482             default:
0483                 break;
0484             }
0485         }
0486         // if this is the only change...
0487         if (changeType == selectedItemChanged) {
0488             setMarked(f ? 1:0, true);
0489             return;
0490         }
0491         setMarked(f ? 1:0, false);
0492     }
0493 
0494 
0495     if (changeType & activeItemChanged) {
0496         TraceFunction* f = nullptr;
0497 
0498         if (_activeItem) {
0499             switch(_activeItem->type()) {
0500             case ProfileContext::Function:
0501             case ProfileContext::FunctionCycle:
0502                 f = (TraceFunction*)_activeItem;
0503                 break;
0504             default:
0505                 break;
0506             }
0507         }
0508         ((CallMapRootItem*)base())->setFunction(f);
0509     }
0510     else if ( ((changeType & partsChanged) && GlobalConfig::showCycles()) ||
0511               (changeType & dataChanged) ||
0512               (changeType & configChanged)) {
0513         /* regenerates the treemap because traceitems were added/removed */
0514         base()->refresh();
0515     }
0516     else if ((changeType & partsChanged) ||
0517              (changeType & eventTypeChanged)) {
0518         /* we need to do the draw order sorting again as the values change */
0519         resort();
0520         redraw();
0521     }
0522     else
0523         redraw();
0524 }
0525 
0526 
0527 
0528 QColor CallMapView::groupColor(TraceFunction* f) const
0529 {
0530     if (!f)
0531         return palette().color( QPalette::Button );
0532 
0533     return GlobalGUIConfig::functionColor(_groupType, f);
0534 }
0535 
0536 
0537 QString CallMapView::tipString(TreeMapItem* i) const
0538 {
0539     QString tip, itemTip;
0540     int count = 0;
0541 
0542     //qDebug("CallMapView::tipString for '%s'", i->text(0).toAscii());
0543 
0544     // first, SubPartItem's
0545     while (i && count<GlobalConfig::maxSymbolCount()) {
0546         itemTip = GlobalConfig::shortenSymbol(i->text(IDX_FUNCNAME));
0547 
0548         if (!i->text(IDX_COST).isEmpty())
0549             itemTip += " (" + i->text(IDX_COST) + ')';
0550 
0551         if (!tip.isEmpty()) tip += '\n';
0552 
0553         tip += itemTip;
0554         i = i->parent();
0555         count++;
0556     }
0557     if (count == GlobalConfig::maxSymbolCount()) tip += QLatin1String("\n...");
0558 
0559     return tip;
0560 }
0561 
0562 
0563 ProfileCostArray* CallMapView::totalCost()
0564 {
0565     TraceFunction* f = ((CallMapRootItem*)base())->function();
0566     if (!f) return nullptr;
0567 
0568     return GlobalConfig::showExpanded() ? f->inclusive() : f->data();
0569 }
0570 
0571 
0572 
0573 // CallMapItemBase
0574 
0575 int CallMapItemBase::maxLines(int i) const
0576 {
0577     if ((i == IDX_FUNCNAME) || (i == IDX_LOCATION)) return 1;
0578     return 0;
0579 }
0580 
0581 bool CallMapItemBase::allowBreak(int i) const
0582 {
0583     if ((i == IDX_COST) || (i == IDX_CALLCOUNT)) return false;
0584     return true;
0585 }
0586 
0587 bool CallMapItemBase::allowTruncation(int i) const
0588 {
0589     if ((i == IDX_COST) || (i == IDX_CALLCOUNT)) return false;
0590     return true;
0591 }
0592 
0593 DrawParams::Position CallMapItemBase::position(int i) const
0594 {
0595     if ((i == IDX_FUNCNAME) || (i == IDX_LOCATION)) return TopLeft;
0596     return TopRight;
0597 }
0598 
0599 
0600 
0601 // CallMapRootItem
0602 
0603 CallMapRootItem::CallMapRootItem()
0604 {
0605     _f = nullptr;
0606 }
0607 
0608 void CallMapRootItem::setFunction(TraceFunction* f)
0609 {
0610     if (f == _f) return;
0611 
0612     _f = f;
0613     refresh();
0614 }
0615 
0616 
0617 QString CallMapRootItem::text(int i) const
0618 {
0619     if (i == IDX_FUNCNAME) {
0620         if (!_f)
0621             return QObject::tr("(no function)");
0622 
0623         return _f->prettyName();
0624     }
0625 
0626     if (!_f) return QString();
0627 
0628     if (i == IDX_LOCATION)
0629         return _f->prettyLocation();
0630     if (i == IDX_CALLCOUNT)
0631         return QString("%1 x").arg(_f->calledCount().pretty());
0632     if (i != IDX_COST)
0633         return QString();
0634 
0635     EventType* ct = ((CallMapView*)widget())->eventType();
0636     ProfileCostArray* t = ((CallMapView*)widget())->totalCost();
0637 
0638     if (GlobalConfig::showPercentage()) {
0639         double sum, total = t->subCost(ct);
0640         if (total == 0.0)
0641             sum = 100.0;
0642         else
0643             sum = 100.0 * _f->inclusive()->subCost(ct) / total;
0644 
0645         return QStringLiteral("%1 %")
0646                 .arg(sum, 0, 'f', GlobalConfig::percentPrecision());
0647     }
0648     return _f->inclusive()->prettySubCost(ct);
0649 }
0650 
0651 
0652 QPixmap CallMapRootItem::pixmap(int i) const
0653 {
0654     if ((i != IDX_COST) || !_f) return QPixmap();
0655 
0656     EventType* ct = ((CallMapView*)widget())->eventType();
0657     ProfileCostArray* t      = ((CallMapView*)widget())->totalCost();
0658 
0659     // colored level meter with frame
0660     return costPixmap( ct, _f->inclusive(), (double) (t->subCost(ct)), true);
0661 }
0662 
0663 
0664 double CallMapRootItem::value() const
0665 {
0666     if (!_f) return 0.0;
0667 
0668     EventType* ct;
0669     ct = ((CallMapView*)widget())->eventType();
0670     return (double) _f->inclusive()->subCost(ct);
0671 }
0672 
0673 
0674 double CallMapRootItem::sum() const
0675 {
0676     if (!_f) return 0.0;
0677 
0678     CallMapView* w = (CallMapView*)widget();
0679 
0680     if (w->showCallers())
0681         return 0.0;
0682     else
0683         return (double) _f->inclusive()->subCost(w->eventType());
0684 }
0685 
0686 
0687 bool CallMapRootItem::isMarked(int) const
0688 {
0689     return ((CallMapView*)widget())->selectedItem() == _f;
0690 }
0691 
0692 TreeMapItemList* CallMapRootItem::children()
0693 {
0694     if (_f && !initialized()) {
0695         CallMapView* w = (CallMapView*)widget();
0696 
0697         if (0) qDebug("Create Function %s (%s)",
0698                       w->showCallers() ? "Callers":"Callees",
0699                       qPrintable(text(IDX_FUNCNAME)));
0700 
0701         setSorting(-1);
0702         if (w->showCallers()) {
0703             foreach(TraceCall* call, _f->callers()) {
0704                 // do not show calls inside of a cycle
0705                 if (call->inCycle()>0) continue;
0706                 if (call->isRecursion()) continue;
0707 
0708                 addItem(new CallMapCallerItem(1.0, call));
0709             }
0710 
0711             setSum(0);
0712         }
0713         else {
0714             foreach(TraceCall* call, _f->callings()) {
0715                 // do not show calls inside of a cycle
0716                 if (call->inCycle()>0) continue;
0717                 if (call->isRecursion()) continue;
0718 
0719                 CallMapCallingItem* i = new CallMapCallingItem(1.0, call);
0720                 i->init();
0721                 addItem(i);
0722             }
0723 
0724             setSum(_f->inclusive()->subCost(w->eventType()));
0725         }
0726         setSorting(-2, false);
0727     }
0728 
0729     return _children;
0730 }
0731 
0732 QColor CallMapRootItem::backColor() const
0733 {
0734     return ((CallMapView*)widget())->groupColor(_f);
0735 }
0736 
0737 
0738 
0739 // CallMapCallingItems
0740 
0741 CallMapCallingItem::CallMapCallingItem(double factor, TraceCall* c)
0742 {
0743     _factor = factor;
0744     _c = c;
0745 }
0746 
0747 void CallMapCallingItem::init()
0748 {
0749 #if 0
0750     // create association: if not possible, i.e. an ass. already exists
0751     // for the function, we need to draw the recursive version
0752     _recursive = !setFunction(_c->called());
0753     _valid = true;
0754 #endif
0755 }
0756 
0757 QString CallMapCallingItem::text(int textNo) const
0758 {
0759     if (textNo == IDX_FUNCNAME) {
0760         if (!_c)
0761             return QObject::tr("(no call)");
0762 
0763         return _c->calledName();
0764     }
0765 
0766     if (textNo == IDX_LOCATION)
0767         return _c->called()->prettyLocation();
0768     if (textNo == IDX_CALLCOUNT)
0769         return QString("%1 x").arg(SubCost(_factor * _c->callCount()).pretty());
0770     if (textNo != IDX_COST)
0771         return QString();
0772 
0773     EventType* ct;
0774     ct = ((CallMapView*)widget())->eventType();
0775 
0776     SubCost val = SubCost(_factor * _c->subCost(ct));
0777     if (GlobalConfig::showPercentage()) {
0778         // percentage relative to function cost
0779         ProfileCostArray* t = ((CallMapView*)widget())->totalCost();
0780         double p  = 100.0 * _factor * _c->subCost(ct) / t->subCost(ct);
0781         return QStringLiteral("%1 %")
0782                 .arg(p, 0, 'f', GlobalConfig::percentPrecision());
0783     }
0784     return val.pretty();
0785 }
0786 
0787 
0788 QPixmap CallMapCallingItem::pixmap(int i) const
0789 {
0790     if (i != IDX_COST) return QPixmap();
0791 
0792     // Cost pixmap
0793     EventType* ct = ((CallMapView*)widget())->eventType();
0794     ProfileCostArray* t = ((CallMapView*)widget())->totalCost();
0795 
0796     // colored level meter with frame
0797     return costPixmap( ct, _c, t->subCost(ct) / _factor, true);
0798 }
0799 
0800 
0801 double CallMapCallingItem::value() const
0802 {
0803     EventType* ct;
0804     ct = ((CallMapView*)widget())->eventType();
0805     return _factor * _c->subCost(ct);
0806 }
0807 
0808 double CallMapCallingItem::sum() const
0809 {
0810     return value();
0811 }
0812 
0813 bool CallMapCallingItem::isMarked(int) const
0814 {
0815     return ((CallMapView*)widget())->selectedItem() == _c->called();
0816 }
0817 
0818 
0819 TreeMapItemList* CallMapCallingItem::children()
0820 {
0821     if (!initialized()) {
0822         if (0) qDebug("Create Calling subitems (%s)",
0823                       qPrintable(path(0).join("/")));
0824 
0825         EventType* ct;
0826         ct = ((CallMapView*)widget())->eventType();
0827 
0828         // same as sum()
0829         SubCost s = _c->called()->inclusive()->subCost(ct);
0830         SubCost v = _c->subCost(ct);
0831         if (v>s) {
0832             qDebug("Warning: CallingItem subVal %u > Sum %u (%s)",
0833                    (unsigned)v, (unsigned)s, qPrintable(_c->called()->prettyName()));
0834             v = s;
0835         }
0836         double newFactor = _factor * v / s;
0837 
0838 #if 0
0839         qDebug("CallingItem: Subitems of %s => %s, factor %f * %d/%d => %f",
0840                qPrintable(_c->caller()->prettyName()),
0841                qPrintable(_c->called()->prettyName()),
0842                _factor, v, s, newFactor);
0843 #endif
0844         setSorting(-1);
0845         foreach(TraceCall* call, _c->called()->callings()) {
0846             // do not show calls inside of a cycle
0847             if (call->inCycle()>0) continue;
0848             if (call->isRecursion()) continue;
0849 
0850             CallMapCallingItem* i = new CallMapCallingItem(newFactor, call);
0851             i->init();
0852             addItem(i);
0853         }
0854         setSorting(-2, false);
0855     }
0856 
0857     return _children;
0858 }
0859 
0860 
0861 QColor CallMapCallingItem::backColor() const
0862 {
0863     CallMapView* w = (CallMapView*)widget();
0864     return w->groupColor(_c->called());
0865 }
0866 
0867 
0868 // CallMapCallerItem
0869 
0870 CallMapCallerItem::CallMapCallerItem(double factor, TraceCall* c)
0871 {
0872     _factor = factor;
0873     _c = c;
0874 }
0875 
0876 QString CallMapCallerItem::text(int i) const
0877 {
0878     if (i == IDX_FUNCNAME) {
0879         if (!_c)
0880             return QObject::tr("(no call)");
0881 
0882         return _c->callerName();
0883     }
0884 
0885     if (i == IDX_LOCATION)
0886         return _c->caller()->prettyLocation();
0887     if (i == IDX_CALLCOUNT)
0888         return QString("%1 x").arg(SubCost(_factor * _c->callCount()).pretty());
0889     if (i != IDX_COST)
0890         return QString();
0891 
0892     EventType* ct;
0893     ct = ((CallMapView*)widget())->eventType();
0894 
0895     SubCost val = SubCost(_factor * _c->subCost(ct));
0896     if (GlobalConfig::showPercentage()) {
0897         ProfileCostArray* t = ((CallMapView*)widget())->totalCost();
0898         double p  = 100.0 * _factor * _c->subCost(ct) / t->subCost(ct);
0899         return QStringLiteral("%1 %")
0900                 .arg(p, 0, 'f', GlobalConfig::percentPrecision());
0901     }
0902     return val.pretty();
0903 }
0904 
0905 QPixmap CallMapCallerItem::pixmap(int i) const
0906 {
0907     if (i != IDX_COST) return QPixmap();
0908 
0909     // Cost pixmap
0910     EventType* ct = ((CallMapView*)widget())->eventType();
0911     ProfileCostArray* t = ((CallMapView*)widget())->totalCost();
0912 
0913     // colored level meter with frame
0914     return costPixmap( ct, _c, t->subCost(ct) / _factor, true );
0915 }
0916 
0917 
0918 double CallMapCallerItem::value() const
0919 {
0920     EventType* ct;
0921     ct = ((CallMapView*)widget())->eventType();
0922     return (double) _c->subCost(ct);
0923 }
0924 
0925 bool CallMapCallerItem::isMarked(int) const
0926 {
0927     return ((CallMapView*)widget())->selectedItem() == _c->caller();
0928 }
0929 
0930 
0931 TreeMapItemList* CallMapCallerItem::children()
0932 {
0933     if (!initialized()) {
0934         //qDebug("Create Caller subitems (%s)", name().toAscii());
0935 
0936         EventType* ct;
0937         ct = ((CallMapView*)widget())->eventType();
0938 
0939         SubCost s = _c->caller()->inclusive()->subCost(ct);
0940         SubCost v = _c->subCost(ct);
0941         double newFactor = _factor * v / s;
0942 
0943 
0944 #if 0
0945         qDebug("CallerItem: Subitems of %s => %s, factor %f * %d/%d => %f",
0946                qPrintable(_c->caller()->prettyName()),
0947                qPrintable(_c->called()->prettyName()),
0948                _factor, v, s, newFactor);
0949 #endif
0950         setSorting(-1);
0951 
0952         foreach(TraceCall* call, _c->caller()->callers()) {
0953             // do not show calls inside of a cycle
0954             if (call->inCycle()>0) continue;
0955             if (call->isRecursion()) continue;
0956 
0957             TreeMapItem* i = new CallMapCallerItem(newFactor, call);
0958             addItem(i);
0959         }
0960         setSorting(-2, false);
0961     }
0962 
0963     return _children;
0964 }
0965 
0966 QColor CallMapCallerItem::backColor() const
0967 {
0968     CallMapView* w = (CallMapView*)widget();
0969     return w->groupColor(_c->caller());
0970 }
0971 
0972 void CallMapView::restoreOptions(const QString& prefix, const QString& postfix)
0973 {
0974     ConfigGroup* g = ConfigStorage::group(prefix, postfix);
0975 
0976     setSplitMode(g->value(QStringLiteral("SplitMode"), QStringLiteral(DEFAULT_SPLITMODE)).toString());
0977 
0978     setFieldVisible(IDX_FUNCNAME,
0979                     g->value(QStringLiteral("DrawName"), DEFAULT_DRAWNAME).toBool());
0980     setFieldVisible(IDX_COST,
0981                     g->value(QStringLiteral("DrawCost"), DEFAULT_DRAWCOST).toBool());
0982     setFieldVisible(IDX_LOCATION,
0983                     g->value(QStringLiteral("DrawLocation"), DEFAULT_DRAWLOCATION).toBool());
0984     setFieldVisible(IDX_CALLCOUNT,
0985                     g->value(QStringLiteral("DrawCalls"), DEFAULT_DRAWCALLS).toBool());
0986 
0987     bool enable = g->value(QStringLiteral("ForceStrings"), DEFAULT_FORCESTRINGS).toBool();
0988     setFieldForced(0, enable);
0989     setFieldForced(1, enable);
0990     setFieldForced(2, enable);
0991     setFieldForced(3, enable);
0992 
0993     setAllowRotation(g->value(QStringLiteral("AllowRotation"), DEFAULT_ROTATION).toBool());
0994     setShadingEnabled(g->value(QStringLiteral("Shading"), DEFAULT_SHADING).toBool());
0995     setFieldStop(IDX_FUNCNAME,
0996                  g->value(QStringLiteral("StopName"), QLatin1String(DEFAULT_STOPNAME)).toString());
0997     setMaxDrawingDepth(g->value(QStringLiteral("MaxDepth"), DEFAULT_MAXDEPTH).toInt());
0998     setMinimalArea(g->value(QStringLiteral("MaxArea"), DEFAULT_MAXAREA).toInt());
0999 
1000     delete g;
1001 }
1002 
1003 void CallMapView::saveOptions(const QString& prefix, const QString& postfix)
1004 {
1005     ConfigGroup* g = ConfigStorage::group(prefix + postfix);
1006 
1007     g->setValue(QStringLiteral("SplitMode"), splitModeString(), QStringLiteral(DEFAULT_SPLITMODE));
1008     g->setValue(QStringLiteral("DrawName"),
1009                 fieldVisible(IDX_FUNCNAME), DEFAULT_DRAWNAME);
1010     g->setValue(QStringLiteral("DrawCost"),
1011                 fieldVisible(IDX_COST), DEFAULT_DRAWCOST);
1012     g->setValue(QStringLiteral("DrawLocation"),
1013                 fieldVisible(IDX_LOCATION), DEFAULT_DRAWLOCATION);
1014     g->setValue(QStringLiteral("DrawCalls"),
1015                 fieldVisible(IDX_CALLCOUNT), DEFAULT_DRAWCALLS);
1016     // when option for all text (0-3)
1017     g->setValue(QStringLiteral("ForceStrings"), fieldForced(IDX_FUNCNAME), DEFAULT_FORCESTRINGS);
1018 
1019     g->setValue(QStringLiteral("AllowRotation"), allowRotation(), DEFAULT_ROTATION);
1020     g->setValue(QStringLiteral("Shading"), isShadingEnabled(), DEFAULT_SHADING);
1021 
1022     g->setValue(QStringLiteral("StopName"), fieldStop(IDX_FUNCNAME), QLatin1String(DEFAULT_STOPNAME));
1023     g->setValue(QStringLiteral("MaxDepth"), maxDrawingDepth(), DEFAULT_MAXDEPTH);
1024     g->setValue(QStringLiteral("MaxArea"), minimalArea(), DEFAULT_MAXAREA);
1025 
1026     delete g;
1027 }
1028 
1029 #include "moc_callmapview.cpp"