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"