File indexing completed on 2024-04-28 05:41:33
0001 /* 0002 This file is part of KCachegrind. 0003 0004 SPDX-FileCopyrightText: 2002-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de> 0005 0006 SPDX-License-Identifier: GPL-2.0-only 0007 */ 0008 0009 /* 0010 * For function selection, to be put into a QDockWindow 0011 */ 0012 0013 #include "functionselection.h" 0014 0015 #include <QLabel> 0016 #include <QPushButton> 0017 #include <QComboBox> 0018 #include <QLineEdit> 0019 #include <QRegularExpression> 0020 #include <QVBoxLayout> 0021 #include <QHBoxLayout> 0022 #include <QAction> 0023 #include <QMenu> 0024 #include <QDebug> 0025 #include <QTreeView> 0026 #include <QHeaderView> 0027 #include <QToolTip> 0028 #include <QHelpEvent> 0029 0030 #include "traceitemview.h" 0031 #include "stackbrowser.h" 0032 #include "costlistitem.h" 0033 #include "globalconfig.h" 0034 #include "functionlistmodel.h" 0035 0036 0037 // custom item delegate for function list 0038 AutoToolTipDelegate::AutoToolTipDelegate(QObject* parent) 0039 : QStyledItemDelegate(parent) 0040 {} 0041 0042 AutoToolTipDelegate::~AutoToolTipDelegate() 0043 {} 0044 0045 bool AutoToolTipDelegate::helpEvent(QHelpEvent* e, QAbstractItemView* view, 0046 const QStyleOptionViewItem& option, 0047 const QModelIndex& index) 0048 { 0049 if (!e || !view) 0050 return false; 0051 0052 if ( e->type() != QEvent::ToolTip ) 0053 return QStyledItemDelegate::helpEvent(e, view, option, index); 0054 0055 QRect rect = view->visualRect(index); 0056 QSize size = sizeHint(option, index); 0057 if ( rect.width() < size.width() ) { 0058 QVariant tooltip = index.data(Qt::DisplayRole); 0059 if ( tooltip.canConvert<QString>() ) { 0060 QToolTip::showText(e->globalPos(), tooltip.toString(), view ); 0061 return true; 0062 } 0063 } 0064 0065 if ( !QStyledItemDelegate::helpEvent( e, view, option, index ) ) 0066 QToolTip::hideText(); 0067 0068 return true; 0069 } 0070 0071 0072 0073 0074 // 0075 // FunctionSelection 0076 // 0077 0078 FunctionSelection::FunctionSelection( TopLevelBase* top, 0079 QWidget* parent) 0080 : QWidget(parent), TraceItemView(nullptr, top) 0081 { 0082 _group = nullptr; 0083 _inSetGroup = false; 0084 _inSetFunction = false; 0085 _functionListSortOrder = Qt::DescendingOrder; 0086 0087 setTitle(tr("Function Profile")); 0088 0089 // first row with search label and group type combo 0090 QHBoxLayout* hboxLayout = new QHBoxLayout(); 0091 hboxLayout->setSpacing(6); 0092 hboxLayout->setContentsMargins(0, 0, 0, 0); 0093 0094 searchLabel = new QLabel(this); 0095 searchLabel->setText(tr("&Search:")); 0096 searchLabel->setWordWrap(false); 0097 hboxLayout->addWidget(searchLabel); 0098 0099 searchEdit = new QLineEdit(this); 0100 searchEdit->setClearButtonEnabled(true); 0101 searchEdit->setPlaceholderText(tr("Search Query")); 0102 searchLabel->setBuddy(searchEdit); 0103 hboxLayout->addWidget(searchEdit); 0104 0105 groupBox = new QComboBox(this); 0106 hboxLayout->addWidget(groupBox); 0107 0108 // vertical layout: first row, group list, function list 0109 QVBoxLayout* vboxLayout = new QVBoxLayout(this); 0110 vboxLayout->setSpacing(6); 0111 vboxLayout->setContentsMargins(3, 3, 3, 3); 0112 vboxLayout->addLayout(hboxLayout); 0113 0114 groupList = new QTreeWidget(this); 0115 QStringList groupHeader; 0116 groupHeader << tr("Self") << tr("Group"); 0117 groupList->setHeaderLabels(groupHeader); 0118 0119 #if QT_VERSION >= 0x050000 0120 groupList->header()->setSectionsClickable(true); 0121 #else 0122 groupList->header()->setClickable(true); 0123 #endif 0124 groupList->header()->setSortIndicatorShown(false); 0125 groupList->header()->stretchLastSection(); 0126 groupList->setIconSize(QSize(99,99)); 0127 groupList->setMaximumHeight(150); 0128 groupList->setRootIsDecorated(false); 0129 groupList->setUniformRowHeights(true); 0130 groupList->sortByColumn(0, Qt::AscendingOrder); 0131 vboxLayout->addWidget(groupList); 0132 0133 functionListModel = new FunctionListModel(); 0134 functionListModel->setMaxCount(GlobalConfig::maxListCount()); 0135 0136 functionList = new QTreeView(this); 0137 functionList->setRootIsDecorated(false); 0138 functionList->setAllColumnsShowFocus(true); 0139 functionList->setAutoScroll(false); 0140 functionList->setContextMenuPolicy(Qt::CustomContextMenu); 0141 functionList->setUniformRowHeights(true); 0142 #if QT_VERSION >= 0x050000 0143 functionList->header()->setSectionsClickable(true); 0144 functionList->header()->setSectionResizeMode(QHeaderView::Interactive); 0145 #else 0146 functionList->header()->setClickable(true); 0147 functionList->header()->setResizeMode(QHeaderView::Interactive); 0148 #endif 0149 functionList->header()->setSortIndicatorShown(false); 0150 functionList->header()->setSortIndicator(0, Qt::DescendingOrder); 0151 // for columns 3 and 4 (all others get resized) 0152 functionList->header()->setDefaultSectionSize(200); 0153 functionList->setModel(functionListModel); 0154 functionList->setItemDelegate(new AutoToolTipDelegate(functionList)); 0155 vboxLayout->addWidget(functionList); 0156 0157 // only to adjust size, will be repopulated on data change 0158 QStringList args; 0159 args << tr("(No Grouping)") 0160 << ProfileContext::i18nTypeName(ProfileContext::Object) 0161 << ProfileContext::i18nTypeName(ProfileContext::File) 0162 << ProfileContext::i18nTypeName(ProfileContext::Class) 0163 << ProfileContext::i18nTypeName(ProfileContext::FunctionCycle); 0164 groupBox->addItems(args); 0165 connect(groupBox, SIGNAL(activated(int)), 0166 this, SLOT(groupTypeSelected(int))); 0167 0168 // search while typing... 0169 connect(searchEdit, &QLineEdit::textChanged, 0170 this, &FunctionSelection::searchChanged); 0171 connect(&_searchTimer, &QTimer::timeout, 0172 this, &FunctionSelection::queryDelayed); 0173 // select first matching group/function on return 0174 connect(searchEdit, &QLineEdit::returnPressed, 0175 this, &FunctionSelection::searchReturnPressed); 0176 searchEdit->setMinimumWidth(50); 0177 0178 // single click release activation 0179 connect(functionList, &QAbstractItemView::clicked, 0180 this, &FunctionSelection::functionActivated); 0181 connect(functionList, &QAbstractItemView::activated, 0182 this, &FunctionSelection::functionActivated); 0183 connect(functionList, &QWidget::customContextMenuRequested, 0184 this, &FunctionSelection::functionContext); 0185 connect(functionList->header(), &QHeaderView::sectionClicked, 0186 this, &FunctionSelection::functionHeaderClicked); 0187 0188 connect(groupList, 0189 &QTreeWidget::currentItemChanged, 0190 this, &FunctionSelection::groupSelected ); 0191 connect(groupList, &QTreeWidget::itemDoubleClicked, 0192 this, &FunctionSelection::groupDoubleClicked); 0193 0194 groupList->setContextMenuPolicy(Qt::CustomContextMenu); 0195 connect(groupList, 0196 &QWidget::customContextMenuRequested, 0197 this, &FunctionSelection::groupContext); 0198 connect(groupList->header(), 0199 &QHeaderView::sectionClicked, this, &FunctionSelection::groupHeaderClicked); 0200 0201 // start hidden 0202 groupList->hide(); 0203 0204 setWhatsThis(whatsThis()); 0205 } 0206 0207 QString FunctionSelection::whatsThis() const 0208 { 0209 return tr( 0210 // xgettext: no-c-format 0211 "<b>The Flat Profile</b>" 0212 "<p>The flat profile contains a group and a function " 0213 "selection list. The group list contains all groups " 0214 "where costs " 0215 "are spent in, depending on the chosen group type. " 0216 "The group list is hidden when group type 'Function' " 0217 "is selected.</p>" 0218 "<p>The function list contains the functions of the " 0219 "selected group (or all for 'Function' group type), " 0220 "ordered by the costs spent therein. Functions with " 0221 "costs less than 1% are hidden on default.</p>"); 0222 } 0223 0224 void FunctionSelection::setData(TraceData* d) 0225 { 0226 TraceItemView::setData(d); 0227 0228 _group = nullptr; 0229 _groupSize.clear(); 0230 _hc.clear(GlobalConfig::maxListCount()); 0231 groupList->clear(); 0232 functionListModel->resetModelData(d, nullptr, QString(), nullptr); 0233 } 0234 0235 void FunctionSelection::searchReturnPressed() 0236 { 0237 query(searchEdit->text()); 0238 0239 QTreeWidgetItem* item; 0240 if (_groupType != ProfileContext::Function) { 0241 // if current group not matching, select first matching group 0242 item = groupList->currentItem(); 0243 if (!item || item->isHidden()) { 0244 int i; 0245 item = nullptr; 0246 for (i=0; i<groupList->topLevelItemCount(); i++) { 0247 item = groupList->topLevelItem(i); 0248 if (!item->isHidden()) break; 0249 } 0250 if (!item) return; 0251 0252 setGroup(((CostListItem*)item)->costItem()); 0253 return; 0254 } 0255 } 0256 // activate top function in functionList 0257 selectTopFunction(); 0258 functionList->setFocus(); 0259 } 0260 0261 // trigger the query after some delay, dependent on length 0262 void FunctionSelection::searchChanged(const QString& q) 0263 { 0264 _searchDelayed = q; 0265 int ms = 100; 0266 if (q.length()<5) ms = 200; 0267 if (q.length()<2) ms = 300; 0268 _searchTimer.setSingleShot(true); 0269 _searchTimer.start(ms); 0270 } 0271 0272 void FunctionSelection::queryDelayed() 0273 { 0274 query(_searchDelayed); 0275 } 0276 0277 void FunctionSelection::functionContext(const QPoint & p) 0278 { 0279 QMenu popup; 0280 TraceFunction* f = nullptr; 0281 0282 QAction* activateFunctionAction = nullptr; 0283 QModelIndex i = functionList->indexAt(p); 0284 if (i.isValid()) { 0285 f = functionListModel->function(i); 0286 if (f) { 0287 QString menuText = tr("Go to '%1'").arg(GlobalConfig::shortenSymbol(f->prettyName())); 0288 activateFunctionAction = popup.addAction(menuText); 0289 popup.addSeparator(); 0290 } 0291 if ((i.column() == 0) || (i.column() == 1)) { 0292 addEventTypeMenu(&popup,false); 0293 popup.addSeparator(); 0294 } 0295 } 0296 0297 QMenu* m = popup.addMenu(tr("Grouping")); 0298 updateGroupingMenu(m); 0299 popup.addSeparator(); 0300 addGoMenu(&popup); 0301 0302 QPoint pDiff = QPoint(0, functionList->header()->height()); 0303 QAction* a = popup.exec(functionList->mapToGlobal(p + pDiff)); 0304 if (a == activateFunctionAction) 0305 activated(f); 0306 } 0307 0308 void FunctionSelection::groupContext(const QPoint & p) 0309 { 0310 QMenu popup; 0311 0312 int c = groupList->columnAt(p.x()); 0313 if (c == 0) { 0314 addEventTypeMenu(&popup,false); 0315 popup.addSeparator(); 0316 } 0317 QMenu* m = popup.addMenu(tr("Grouping")); 0318 updateGroupingMenu(m); 0319 popup.addSeparator(); 0320 addGoMenu(&popup); 0321 0322 QPoint headerSize = QPoint(0, groupList->header()->height()); 0323 popup.exec(groupList->mapToGlobal(p + headerSize)); 0324 } 0325 0326 void FunctionSelection::addGroupAction(QMenu* m, 0327 ProfileContext::Type v, 0328 const QString& s) 0329 { 0330 QAction* a; 0331 if (s.isEmpty()) 0332 a = m->addAction(ProfileContext::i18nTypeName(v)); 0333 else 0334 a = m->addAction(s); 0335 a->setData((int)v); 0336 a->setCheckable(true); 0337 a->setChecked(_groupType == v); 0338 } 0339 0340 // Clears and repopulates the given menu with dynamic items for grouping. 0341 // Menu item handlers for setting the grouping are installed. 0342 void FunctionSelection::updateGroupingMenu(QMenu* m) 0343 { 0344 if (!m) return; 0345 m->clear(); 0346 0347 // use a unique connection, as we may be called multiple times 0348 // for same menu (e.g. for repopulating a menu related to a QAction) 0349 connect(m, SIGNAL(triggered(QAction*)), 0350 this, SLOT(groupTypeSelected(QAction*)), Qt::UniqueConnection); 0351 0352 if (_groupType != ProfileContext::Function) { 0353 addGroupAction(m, ProfileContext::Function, tr("No Grouping")); 0354 m->addSeparator(); 0355 } 0356 if (_data && _data->objectMap().count()>1) 0357 addGroupAction(m, ProfileContext::Object); 0358 if (_data && _data->fileMap().count()>1) 0359 addGroupAction(m, ProfileContext::File); 0360 if (_data && _data->classMap().count()>1) 0361 addGroupAction(m, ProfileContext::Class); 0362 addGroupAction(m, ProfileContext::FunctionCycle); 0363 } 0364 0365 0366 void FunctionSelection::groupTypeSelected(QAction* a) 0367 { 0368 selectedGroupType( (ProfileContext::Type) a->data().toInt() ); 0369 } 0370 0371 void FunctionSelection::groupTypeSelected(int cg) 0372 { 0373 int t = groupBox->itemData(cg).toInt(); 0374 if (t == 0) 0375 t = ProfileContext::Function; // always works 0376 0377 selectedGroupType((ProfileContext::Type) t); 0378 } 0379 0380 CostItem* FunctionSelection::canShow(CostItem* i) 0381 { 0382 ProfileContext::Type t = i ? i->type() : ProfileContext::InvalidType; 0383 0384 switch(t) { 0385 case ProfileContext::Function: 0386 case ProfileContext::FunctionCycle: 0387 case ProfileContext::Object: 0388 case ProfileContext::File: 0389 case ProfileContext::Class: 0390 break; 0391 0392 case ProfileContext::Instr: 0393 i = ((TraceInstr*)i)->function(); 0394 break; 0395 0396 case ProfileContext::Line: 0397 i = ((TraceLine*)i)->functionSource()->function(); 0398 break; 0399 0400 default: 0401 i = nullptr; 0402 break; 0403 } 0404 return i; 0405 } 0406 0407 0408 void FunctionSelection::selectFunction(TraceFunction* f, 0409 bool ensureVisible) 0410 { 0411 QModelIndex i = functionListModel->indexForFunction(f, true); 0412 if (!i.isValid()) return; 0413 0414 if (ensureVisible) 0415 functionList->scrollTo(i, QAbstractItemView::EnsureVisible); 0416 0417 _inSetFunction = true; 0418 QModelIndex last = functionListModel->index(i.row(), 4); 0419 QItemSelection s(i, last); 0420 functionList->selectionModel()->select(s, QItemSelectionModel::ClearAndSelect); 0421 _inSetFunction = false; 0422 } 0423 0424 void FunctionSelection::doUpdate(int changeType, bool) 0425 { 0426 // Special case ? 0427 if (changeType == selectedItemChanged) return; 0428 0429 // we do not show cost 2 at all... 0430 if (changeType == eventType2Changed) return; 0431 0432 if (changeType == eventTypeChanged) { 0433 int i; 0434 0435 #if QT_VERSION >= 0x050000 0436 groupList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); 0437 #else 0438 groupList->header()->setResizeMode(0, QHeaderView::ResizeToContents); 0439 #endif 0440 // need to disable sorting! Otherwise each change of shown cost 0441 // reorders list and changes order returned by topLevelItem() 0442 groupList->setSortingEnabled(false); 0443 for (i=0; i<groupList->topLevelItemCount(); ++i) { 0444 CostListItem* item = (CostListItem*) groupList->topLevelItem(i); 0445 item->setEventType(_eventType); 0446 } 0447 #if QT_VERSION >= 0x050000 0448 groupList->header()->setSectionResizeMode(0, QHeaderView::Interactive); 0449 #else 0450 groupList->header()->setResizeMode(0, QHeaderView::Interactive); 0451 #endif 0452 groupList->setSortingEnabled(true); 0453 groupList->header()->setSortIndicatorShown(false); 0454 0455 functionListModel->setEventType(_eventType); 0456 // previous line resets the model: reselect active item 0457 selectFunction(dynamic_cast<TraceFunction*>(_activeItem)); 0458 setCostColumnWidths(); 0459 return; 0460 } 0461 0462 if (changeType == activeItemChanged) { 0463 if (_activeItem ==nullptr) { 0464 functionList->clearSelection(); 0465 return; 0466 } 0467 switch(_activeItem->type()) { 0468 case ProfileContext::Object: 0469 case ProfileContext::File: 0470 case ProfileContext::Class: 0471 setGroup((TraceCostItem*)_activeItem); 0472 return; 0473 default: break; 0474 } 0475 0476 // active item is a function 0477 TraceFunction* f = (TraceFunction*) _activeItem; 0478 0479 // if already current, nothing to do 0480 QModelIndex i = functionList->currentIndex(); 0481 if (functionListModel->function(i) == f) { 0482 return; 0483 } 0484 0485 // reset search (as not activated from this view) 0486 query(QString()); 0487 0488 // select cost item group of function 0489 switch(_groupType) { 0490 case ProfileContext::Object: setGroup(f->object()); break; 0491 case ProfileContext::Class: setGroup(f->cls()); break; 0492 case ProfileContext::File: setGroup(f->file()); break; 0493 case ProfileContext::FunctionCycle: setGroup(f->cycle()); break; 0494 default: 0495 break; 0496 } 0497 0498 selectFunction(f); 0499 return; 0500 } 0501 0502 if (changeType & dataChanged) { 0503 groupBox->clear(); 0504 groupBox->addItem(tr("(No Grouping)"), ProfileContext::Function); 0505 if (_data) { 0506 if (_data->objectMap().count()>1) 0507 groupBox->addItem(ProfileContext::i18nTypeName(ProfileContext::Object), 0508 ProfileContext::Object); 0509 if (_data->fileMap().count()>1) 0510 groupBox->addItem(ProfileContext::i18nTypeName(ProfileContext::File), 0511 ProfileContext::File); 0512 if (_data->classMap().count()>1) 0513 groupBox->addItem(ProfileContext::i18nTypeName(ProfileContext::Class), 0514 ProfileContext::Class); 0515 groupBox->addItem(ProfileContext::i18nTypeName(ProfileContext::FunctionCycle), 0516 ProfileContext::FunctionCycle); 0517 } 0518 } 0519 0520 if (changeType & groupTypeChanged) { 0521 if (_activeItem && (_activeItem->type() == ProfileContext::Function)) { 0522 TraceFunction* f = (TraceFunction*) _activeItem; 0523 0524 // select cost item group of function 0525 switch(_groupType) { 0526 case ProfileContext::Object: _group = f->object(); break; 0527 case ProfileContext::Class: _group = f->cls(); break; 0528 case ProfileContext::File: _group = f->file(); break; 0529 case ProfileContext::FunctionCycle: _group = f->cycle(); break; 0530 default: 0531 _group = nullptr; 0532 break; 0533 } 0534 } 0535 0536 int id = groupBox->findData(_groupType); 0537 if (id < 0) id = 0; // if not found, default to first entry 0538 groupBox->setCurrentIndex(id); 0539 0540 if (_groupType == ProfileContext::Function) 0541 groupList->hide(); 0542 else 0543 groupList->show(); 0544 } 0545 0546 // reset searchEdit 0547 _searchString.clear(); 0548 query(QString()); 0549 0550 refresh(); 0551 } 0552 0553 0554 /* 0555 * This set/selects a group of the set available within the 0556 * current group type 0557 */ 0558 void FunctionSelection::setGroup(TraceCostItem* g) 0559 { 0560 if (!g) return; 0561 if (g->type() != _groupType) return; 0562 if (g == _group) return; 0563 _group = g; 0564 0565 QTreeWidgetItem* item = nullptr; 0566 int i; 0567 for (i=0; i < groupList->topLevelItemCount(); i++) { 0568 item = groupList->topLevelItem(i); 0569 if (((CostListItem*)item)->costItem() == g) 0570 break; 0571 } 0572 0573 if (item) { 0574 groupList->scrollToItem(item); 0575 // prohibit signalling of a group selection 0576 _inSetGroup = true; 0577 _group = nullptr; 0578 groupList->setCurrentItem(item); 0579 _inSetGroup = false; 0580 } 0581 else 0582 groupList->clearSelection(); 0583 } 0584 0585 0586 void FunctionSelection::refresh() 0587 { 0588 groupList->clear(); 0589 0590 // make cost columns as small as possible: 0591 // the new functions make them as wide as needed 0592 groupList->setColumnWidth(0, 50); 0593 groupList->headerItem()->setText(1, ProfileContext::i18nTypeName(_groupType)); 0594 0595 functionListModel->setMaxCount(GlobalConfig::maxListCount()); 0596 0597 if (!_data || _data->parts().isEmpty()) { 0598 functionListModel->resetModelData(nullptr, nullptr, QString(), nullptr); 0599 selectTopFunction(); 0600 return; 0601 } 0602 0603 0604 TraceObjectMap::Iterator oit; 0605 TraceClassMap::Iterator cit; 0606 TraceFileMap::Iterator fit; 0607 0608 // Fill up group list. 0609 // Always show group of current function, even if cost below low limit. 0610 // 0611 0612 _hc.clear(GlobalConfig::maxListCount()); 0613 0614 switch(_groupType) { 0615 case ProfileContext::Object: 0616 0617 for ( oit = _data->objectMap().begin(); 0618 oit != _data->objectMap().end(); ++oit ) 0619 _hc.addCost(&(*oit), (*oit).subCost(_eventType)); 0620 break; 0621 0622 case ProfileContext::Class: 0623 0624 for ( cit = _data->classMap().begin(); 0625 cit != _data->classMap().end(); ++cit ) 0626 _hc.addCost(&(*cit), (*cit).subCost(_eventType)); 0627 break; 0628 0629 case ProfileContext::File: 0630 0631 for ( fit = _data->fileMap().begin(); 0632 fit != _data->fileMap().end(); ++fit ) 0633 _hc.addCost(&(*fit), (*fit).subCost(_eventType)); 0634 break; 0635 0636 case ProfileContext::FunctionCycle: 0637 { 0638 // add all cycles 0639 foreach(TraceCostItem *group, _data->functionCycles()) 0640 _hc.addCost(group, group->subCost(_eventType)); 0641 } 0642 0643 break; 0644 0645 default: 0646 { 0647 _group = nullptr; 0648 functionListModel->resetModelData(_data, _group, _searchString, _eventType); 0649 selectFunction(dynamic_cast<TraceFunction*>(_activeItem)); 0650 setCostColumnWidths(); 0651 return; 0652 } 0653 } 0654 0655 // update group from _activeItem if possible 0656 if (_activeItem && (_activeItem->type() == _groupType)) 0657 _group = (TraceCostItem*) _activeItem; 0658 0659 QTreeWidgetItem *item = nullptr, *activeItem = nullptr; 0660 QList<QTreeWidgetItem*> items; 0661 0662 // we always put group of active item in list, even if 0663 // it would be skipped because of small costs 0664 if (_group) { 0665 activeItem = new CostListItem(groupList, _group, _eventType); 0666 items.append(activeItem); 0667 } 0668 0669 for(int i=0; i<_hc.realCount(); i++) { 0670 TraceCostItem *group = (TraceCostItem*)_hc[i]; 0671 // do not put group of active item twice into list 0672 if (group == _group) continue; 0673 item = new CostListItem(groupList, group, _eventType); 0674 items.append(item); 0675 } 0676 if (_hc.hasMore()) { 0677 // a placeholder for all the cost items skipped ... 0678 item = new CostListItem(groupList, _hc.count() - _hc.maxSize(), 0679 (TraceCostItem*)_hc[_hc.maxSize()-1], _eventType); 0680 items.append(item); 0681 } 0682 0683 #if QT_VERSION >= 0x050000 0684 groupList->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); 0685 #else 0686 groupList->header()->setResizeMode(0, QHeaderView::ResizeToContents); 0687 #endif 0688 groupList->setSortingEnabled(false); 0689 groupList->addTopLevelItems(items); 0690 groupList->setSortingEnabled(true); 0691 // always reset to cost sorting 0692 groupList->sortByColumn(0, Qt::DescendingOrder); 0693 groupList->header()->setSortIndicatorShown(false); 0694 #if QT_VERSION >= 0x050000 0695 groupList->header()->setSectionResizeMode(0, QHeaderView::Interactive); 0696 #else 0697 groupList->header()->setResizeMode(0, QHeaderView::Interactive); 0698 #endif 0699 0700 if (activeItem) { 0701 groupList->scrollToItem(activeItem); 0702 _inSetGroup = true; 0703 _group = nullptr; 0704 groupList->setCurrentItem(activeItem); 0705 _inSetGroup = false; 0706 } 0707 else 0708 groupList->clearSelection(); 0709 } 0710 0711 0712 void FunctionSelection::groupSelected(QTreeWidgetItem* i, QTreeWidgetItem*) 0713 { 0714 if (!i) return; 0715 if (!_data) return; 0716 0717 TraceCostItem* g = ((CostListItem*) i)->costItem(); 0718 if (!g) return; 0719 if (g == _group) return; 0720 _group = g; 0721 0722 functionListModel->resetModelData(_data, g, _searchString, _eventType); 0723 selectFunction(dynamic_cast<TraceFunction*>(_activeItem)); 0724 setCostColumnWidths(); 0725 0726 // Do not emit signal if cost item was changed programatically 0727 if (!_inSetGroup) { 0728 if (_topLevel) 0729 _topLevel->setGroupDelayed(g); 0730 } 0731 } 0732 0733 void FunctionSelection::groupDoubleClicked(QTreeWidgetItem* i, int) 0734 { 0735 if (!i) return; 0736 if (!_data) return; 0737 TraceCostItem* g = ((CostListItem*) i)->costItem(); 0738 0739 if (!g) return; 0740 // group must be selected first 0741 if (g != _group) return; 0742 0743 activated(g); 0744 } 0745 0746 void FunctionSelection::groupHeaderClicked(int col) 0747 { 0748 groupList->sortByColumn(col, Qt::DescendingOrder); 0749 } 0750 0751 TraceCostItem* FunctionSelection::group(QString s) 0752 { 0753 int i; 0754 for (i=0; i<groupList->topLevelItemCount(); i++) { 0755 CostListItem* item = (CostListItem*) groupList->topLevelItem(i); 0756 if (item->costItem()->name() == s) 0757 return item->costItem(); 0758 } 0759 0760 return nullptr; 0761 } 0762 0763 0764 void FunctionSelection::functionActivated(const QModelIndex& i) 0765 { 0766 if (!_data) return; 0767 0768 TraceFunction* f = functionListModel->function(i); 0769 if (!f) return; 0770 0771 if (!_inSetFunction) 0772 activated(f); 0773 } 0774 0775 void FunctionSelection::updateGroupSizes(bool hideEmpty) 0776 { 0777 int i; 0778 for (i=0; i<groupList->topLevelItemCount(); i++) { 0779 CostListItem* item = (CostListItem*) groupList->topLevelItem(i); 0780 int size = (_groupSize.contains(item->costItem())) ? 0781 _groupSize[item->costItem()] : -1; 0782 item->setSize(size); 0783 item->setHidden(hideEmpty && (size<0)); 0784 } 0785 } 0786 0787 void FunctionSelection::query(QString query) 0788 { 0789 if(!_data) 0790 return; 0791 if (searchEdit->text() != query) 0792 searchEdit->setText(query); 0793 if (_searchString == query) { 0794 // when resetting query, get rid of group sizes 0795 if (query.isEmpty()) { 0796 _groupSize.clear(); 0797 updateGroupSizes(false); 0798 } 0799 return; 0800 } 0801 _searchString = query; 0802 0803 QRegularExpression re(glob2Regex(query), 0804 QRegularExpression::CaseInsensitiveOption); 0805 _groupSize.clear(); 0806 0807 TraceFunction* f = nullptr; 0808 TraceFunctionList list2; 0809 0810 _hc.clear(GlobalConfig::maxListCount()); 0811 0812 TraceFunctionMap::Iterator it; 0813 for ( it = _data->functionMap().begin(); 0814 it != _data->functionMap().end(); ++it ) { 0815 f = &(*it); 0816 if (re.isValid() && f->prettyName().contains(re)) { 0817 if (_group) { 0818 if (_groupType==ProfileContext::Object) { 0819 if (_groupSize.contains(f->object())) 0820 _groupSize[f->object()]++; 0821 else 0822 _groupSize[f->object()] = 1; 0823 if (f->object() != _group) continue; 0824 } 0825 else if (_groupType==ProfileContext::Class) { 0826 if (_groupSize.contains(f->cls())) 0827 _groupSize[f->cls()]++; 0828 else 0829 _groupSize[f->cls()] = 1; 0830 if (f->cls() != _group) continue; 0831 } 0832 else if (_groupType==ProfileContext::File) { 0833 if (_groupSize.contains(f->file())) 0834 _groupSize[f->file()]++; 0835 else 0836 _groupSize[f->file()] = 1; 0837 if (f->file() != _group) continue; 0838 } 0839 else if (_groupType==ProfileContext::FunctionCycle) { 0840 if (_groupSize.contains(f->cycle())) 0841 _groupSize[f->cycle()]++; 0842 else 0843 _groupSize[f->cycle()] = 1; 0844 if (f->cycle() != _group) continue; 0845 } 0846 } 0847 _hc.addCost(f, f->inclusive()->subCost(_eventType)); 0848 } 0849 } 0850 updateGroupSizes(true); 0851 0852 functionListModel->resetModelData(_data, _group, _searchString, _eventType); 0853 selectFunction(dynamic_cast<TraceFunction*>(_activeItem)); 0854 setCostColumnWidths(); 0855 } 0856 0857 bool FunctionSelection::selectTopFunction() 0858 { 0859 QModelIndex i = functionListModel->index(0,0); 0860 TraceFunction* f = functionListModel->function(i); 0861 0862 // pre-select before activation to not trigger a refresh of this view 0863 _activeItem = f; 0864 selectFunction(f); 0865 0866 functionActivated(i); 0867 0868 return (f!=nullptr); 0869 } 0870 0871 void FunctionSelection::setCostColumnWidths() 0872 { 0873 functionList->resizeColumnToContents(1); 0874 0875 // hide call count column if all call counts given are zero 0876 if (_data->maxCallCount() > 0) 0877 functionList->resizeColumnToContents(2); 0878 else 0879 functionList->header()->resizeSection(2, 0); 0880 0881 // hide inclusive costs if all inclusive costs given are zero 0882 if (_eventType && (_eventType->subCost(_data->callMax())>0) ) 0883 functionList->resizeColumnToContents(0); 0884 else 0885 functionList->header()->resizeSection(0, 0); 0886 } 0887 0888 void FunctionSelection::functionHeaderClicked(int col) 0889 { 0890 if ((_functionListSortOrder== Qt::AscendingOrder) || (col<3)) 0891 _functionListSortOrder = Qt::DescendingOrder; 0892 else 0893 _functionListSortOrder = Qt::AscendingOrder; 0894 0895 functionList->sortByColumn(col, _functionListSortOrder); 0896 selectFunction(dynamic_cast<TraceFunction*>(_activeItem), false); 0897 setCostColumnWidths(); 0898 } 0899 0900 #include "moc_functionselection.cpp"