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 Views
0011  */
0012 
0013 
0014 #include "callview.h"
0015 
0016 #include <QAction>
0017 #include <QMenu>
0018 #include <QTreeWidget>
0019 #include <QHeaderView>
0020 #include <QKeyEvent>
0021 
0022 #include "globalconfig.h"
0023 #include "callitem.h"
0024 
0025 
0026 //
0027 // CallView
0028 //
0029 
0030 
0031 CallView::CallView(bool showCallers, TraceItemView* parentView, QWidget* parent)
0032     : QTreeWidget(parent), TraceItemView(parentView)
0033 {
0034     _showCallers = showCallers;
0035 
0036     QStringList headerLabels;
0037     headerLabels << tr( "Cost" )
0038                  << tr( "Cost per call" )
0039                  << tr( "Cost 2" )
0040                  << tr( "Cost 2 per call" )
0041                  << tr( "Count" )
0042                  << ((_showCallers) ? tr( "Caller" ) : tr( "Callee" ));
0043     setHeaderLabels(headerLabels);
0044 
0045     // forbid scaling icon pixmaps to smaller size
0046     setIconSize(QSize(99,99));
0047     setAllColumnsShowFocus(true);
0048     setRootIsDecorated(false);
0049     setUniformRowHeights(true);
0050     // sorting will be enabled after refresh()
0051     sortByColumn(0, Qt::DescendingOrder);
0052     setMinimumHeight(50);
0053 
0054     this->setWhatsThis( whatsThis() );
0055 
0056     connect( this,
0057              &QTreeWidget::currentItemChanged,
0058              this, &CallView::selectedSlot );
0059 
0060     setContextMenuPolicy(Qt::CustomContextMenu);
0061     connect( this,
0062              &QWidget::customContextMenuRequested,
0063              this, &CallView::context);
0064 
0065     connect(this,
0066             &QTreeWidget::itemDoubleClicked,
0067             this, &CallView::activatedSlot);
0068 
0069     connect(header(), &QHeaderView::sectionClicked,
0070             this, &CallView::headerClicked);
0071 }
0072 
0073 QString CallView::whatsThis() const
0074 {
0075     return _showCallers ?
0076                 tr( "<b>List of direct Callers</b>"
0077                     "<p>This list shows all functions calling the "
0078                     "current selected one directly, together with "
0079                     "a call count and the cost spent in the current "
0080                     "selected function while being called from the "
0081                     "function from the list.</p>"
0082                     "<p>An icon instead of an inclusive cost specifies "
0083                     "that this is a call inside of a recursive cycle. "
0084                     "An inclusive cost makes no sense here.</p>"
0085                     "<p>Selecting a function makes it the current selected "
0086                     "one of this information panel. "
0087                     "If there are two panels (Split mode), the "
0088                     "function of the other panel is changed instead.</p>") :
0089                 tr( "<b>List of direct Callees</b>"
0090                     "<p>This list shows all functions called by the "
0091                     "current selected one directly, together with "
0092                     "a call count and the cost spent in this function "
0093                     "while being called from the selected function.</p>"
0094                     "<p>Selecting a function makes it the current selected "
0095                     "one of this information panel. "
0096                     "If there are two panels (Split mode), the "
0097                     "function of the other panel is changed instead.</p>");
0098 }
0099 
0100 
0101 void CallView::context(const QPoint & p)
0102 {
0103     QMenu popup;
0104 
0105     // p is in local coordinates
0106     int col = columnAt(p.x());
0107     QTreeWidgetItem* i = itemAt(p);
0108     TraceCall* c = i ? ((CallItem*) i)->call() : nullptr;
0109     TraceFunction *f = nullptr, *cycle = nullptr;
0110 
0111     QAction* activateFunctionAction = nullptr;
0112     QAction* activateCycleAction = nullptr;
0113     if (c) {
0114         QString name  = _showCallers ? c->callerName(true) : c->calledName(true);
0115         f = _showCallers ? c->caller(true) : c->called(true);
0116         cycle = f->cycle();
0117 
0118         QString menuText = tr("Go to '%1'").arg(GlobalConfig::shortenSymbol(name));
0119         activateFunctionAction = popup.addAction(menuText);
0120 
0121         if (cycle) {
0122             name = GlobalConfig::shortenSymbol(cycle->prettyName());
0123             QString menuText = tr("Go to '%1'").arg(name);
0124             activateCycleAction = popup.addAction(menuText);
0125         }
0126 
0127         popup.addSeparator();
0128     }
0129 
0130     // add menu items to select event type if column displays cost for a type
0131     if (col < 4) {
0132         addEventTypeMenu(&popup);
0133         popup.addSeparator();
0134     }
0135     addGoMenu(&popup);
0136 
0137     QAction* a = popup.exec(mapToGlobal(p + QPoint(0,header()->height())));
0138     if (a == activateFunctionAction)
0139         TraceItemView::activated(f);
0140     else if (a == activateCycleAction)
0141         TraceItemView::activated(cycle);
0142 }
0143 
0144 void CallView::selectedSlot(QTreeWidgetItem * i, QTreeWidgetItem *)
0145 {
0146     if (!i) return;
0147     TraceCall* c = ((CallItem*) i)->call();
0148     // Should we skip cycles here?
0149     CostItem* f = _showCallers ? c->caller(false) : c->called(false);
0150 
0151     _selectedItem = f;
0152     selected(f);
0153 }
0154 
0155 void CallView::activatedSlot(QTreeWidgetItem* i,int)
0156 {
0157     if (!i) return;
0158 
0159     TraceCall* c = ((CallItem*) i)->call();
0160     // skip cycles: use the context menu to get to the cycle...
0161     CostItem* f = _showCallers ? c->caller(true) : c->called(true);
0162 
0163     TraceItemView::activated(f);
0164 }
0165 
0166 void CallView::headerClicked(int col)
0167 {
0168     // name columns should be sortable in both ways
0169     if (col == 5) return;
0170 
0171     // all others only descending
0172     sortByColumn(col, Qt::DescendingOrder);
0173 }
0174 
0175 void CallView::keyPressEvent(QKeyEvent* event)
0176 {
0177     QTreeWidgetItem *item = currentItem();
0178     if (item && ((event->key() == Qt::Key_Return) ||
0179                  (event->key() == Qt::Key_Space)))
0180     {
0181         TraceCall* c = ((CallItem*) item)->call();
0182         CostItem* f = _showCallers ? c->caller(false) : c->called(false);
0183 
0184         TraceItemView::activated(f);
0185     }
0186     QTreeView::keyPressEvent(event);
0187 }
0188 
0189 CostItem* CallView::canShow(CostItem* i)
0190 {
0191     ProfileContext::Type t = i ? i->type() : ProfileContext::InvalidType;
0192 
0193     switch(t) {
0194     case ProfileContext::Function:
0195     case ProfileContext::FunctionCycle:
0196         return i;
0197     default:
0198         break;
0199     }
0200     return nullptr;
0201 }
0202 
0203 void CallView::doUpdate(int changeType, bool)
0204 {
0205     // Special case ?
0206     if (changeType == selectedItemChanged) {
0207 
0208         if (!_selectedItem) {
0209             clearSelection();
0210             return;
0211         }
0212 
0213         CallItem* ci = (CallItem*) currentItem();
0214         TraceCall* c;
0215         CostItem* ti;
0216         if (ci) {
0217             c = ci->call();
0218             ti = _showCallers ? c->caller() : c->called();
0219             if (ti == _selectedItem) return;
0220         }
0221 
0222         QTreeWidgetItem *item = nullptr;
0223         for (int i=0; i<topLevelItemCount();i++) {
0224             item = topLevelItem(i);
0225             c = ((CallItem*) item)->call();
0226             ti = _showCallers ? c->caller() : c->called();
0227             if (ti == _selectedItem) {
0228                 scrollToItem(item);
0229                 setCurrentItem(item);
0230                 break;
0231             }
0232         }
0233         if (!item && ci) clearSelection();
0234         return;
0235     }
0236     
0237     if (changeType == groupTypeChanged) {
0238         QTreeWidgetItem *item;
0239         for (int i=0; i<topLevelItemCount(); i++){
0240             item = topLevelItem(i);
0241             ((CallItem*)item)->updateGroup();
0242         }
0243         return;
0244     }
0245 
0246     refresh();
0247 }
0248 
0249 void CallView::refresh()
0250 {
0251     clear();
0252     setColumnHidden(2, (_eventType2 == nullptr));
0253     setColumnHidden(3, (_eventType2 == nullptr));
0254 
0255     if (_eventType) {
0256         headerItem()->setText(0, _eventType->name());
0257         headerItem()->setText(1, tr("%1 per call").arg(_eventType->name()));
0258     }
0259     if (_eventType2) {
0260         headerItem()->setText(2, _eventType2->name());
0261         headerItem()->setText(3, tr("%1 per call").arg(_eventType2->name()));
0262     }
0263 
0264     if (!_data || !_activeItem) return;
0265 
0266     TraceFunction* f = activeFunction();
0267     if (!f) return;
0268 
0269     // In the call lists, we skip cycles to show the real call relations
0270     TraceCallList l = _showCallers ? f->callers(true) : f->callings(true);
0271 
0272     QList<QTreeWidgetItem*> items;
0273     foreach(TraceCall* call, l)
0274         if (call->subCost(_eventType)>0)
0275             items.append(new CallItem(this, nullptr, call));
0276 
0277     // when inserting, switch off sorting for performance reason
0278     setSortingEnabled(false);
0279     addTopLevelItems(items);
0280     setSortingEnabled(true);
0281     // enabling sorting switches on the indicator, but we want it off
0282     header()->setSortIndicatorShown(false);
0283     // resize to content now (section size still can be interactively changed)
0284     setCostColumnWidths();
0285 }
0286 
0287 void CallView::setCostColumnWidths()
0288 {
0289     // first columns 0 + 2
0290     resizeColumnToContents(0);
0291     if (_eventType2) {
0292         setColumnHidden(2, false);
0293         resizeColumnToContents(2);
0294     }
0295     else {
0296         setColumnHidden(2, true);
0297     }
0298 
0299     // then "per call" columns
0300     if (_data->maxCallCount() == 0) {
0301         // hide "per call" columns and call count column
0302         setColumnHidden(1, true);
0303         setColumnHidden(3, true);
0304         setColumnHidden(4, true);
0305     }
0306     else {
0307         setColumnHidden(1, false);
0308         resizeColumnToContents(1);
0309         if (_eventType2) {
0310             setColumnHidden(3, false);
0311             resizeColumnToContents(3);
0312         }
0313         else
0314             setColumnHidden(3, true);
0315 
0316         setColumnHidden(4, false);
0317         resizeColumnToContents(4);
0318     }
0319 }
0320 
0321 #include "moc_callview.cpp"