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"