diff --git a/kcachegrind/configdlg.cpp b/kcachegrind/configdlg.cpp index 987cdf1..01b4a5d 100644 --- a/kcachegrind/configdlg.cpp +++ b/kcachegrind/configdlg.cpp @@ -1,372 +1,372 @@ /* This file is part of KCachegrind. Copyright (C) 2002-2016 Josef Weidendorfer KCachegrind is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * Configuration Dialog for KCachegrind */ #include "configdlg.h" #include #include #include #include -#include +#include #include "tracedata.h" #include "globalguiconfig.h" ConfigDlg::ConfigDlg(GlobalGUIConfig* c, TraceData* data, QWidget* parent) :ConfigDlgBase(parent) { _config = c; _data = data; _objectCS = nullptr; _classCS = nullptr; _fileCS = nullptr; connect(objectCombo, SIGNAL(activated(QString)), this, SLOT(objectActivated(QString))); connect(objectCombo, SIGNAL(editTextChanged(QString)), this, SLOT(objectActivated(QString))); connect(objectCheck, &QAbstractButton::toggled, this, &ConfigDlg::objectCheckChanged); connect(objectColor, &KColorButton::changed, this, &ConfigDlg::objectColorChanged); connect(classCombo, SIGNAL(activated(QString)), this, SLOT(classActivated(QString))); connect(classCombo, SIGNAL(editTextChanged(QString)), this, SLOT(classActivated(QString))); connect(classCheck, &QAbstractButton::toggled, this, &ConfigDlg::classCheckChanged); connect(classColor, &KColorButton::changed, this, &ConfigDlg::classColorChanged); connect(fileCombo, SIGNAL(activated(QString)), this, SLOT(fileActivated(QString))); connect(fileCombo, SIGNAL(editTextChanged(QString)), this, SLOT(fileActivated(QString))); connect(fileCheck, &QAbstractButton::toggled, this, &ConfigDlg::fileCheckChanged); connect(fileColor, &KColorButton::changed, this, &ConfigDlg::fileColorChanged); connect(buttonBox, &QDialogButtonBox::accepted,this, &QDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected,this, &QDialog::reject); QString objectPrefix = ProfileContext::typeName(ProfileContext::Object); QString classPrefix = ProfileContext::typeName(ProfileContext::Class); QString filePrefix = ProfileContext::typeName(ProfileContext::File); objectCombo->setDuplicatesEnabled(false); classCombo->setDuplicatesEnabled(false); fileCombo->setDuplicatesEnabled(false); objectCombo->setAutoCompletion(true); classCombo->setAutoCompletion(true); fileCombo->setAutoCompletion(true); // first unspecified cost items from data TraceObjectMap::Iterator oit; QStringList oList; if (data) { for ( oit = data->objectMap().begin(); oit != data->objectMap().end(); ++oit ) oList.append((*oit).prettyName()); } TraceClassMap::Iterator cit; QStringList cList; if (data) { for ( cit = data->classMap().begin(); cit != data->classMap().end(); ++cit ) cList.append((*cit).prettyName()); } TraceFileMap::Iterator fit; QStringList fList; if (data) { for ( fit = data->fileMap().begin(); fit != data->fileMap().end(); ++fit ) fList.append((*fit).prettyName()); } // then already defined colors (have to check for duplicates!) foreach(ConfigColorSetting* cs, c->_colors) { if (cs->_automatic) continue; QString n = cs->_name; if (n.startsWith(objectPrefix)) { n = n.remove(0, objectPrefix.length()+1); if (oList.indexOf(n) == -1) oList.append(n); } else if (n.startsWith(classPrefix)) { n = n.remove(0, classPrefix.length()+1); if (cList.indexOf(n) == -1) cList.append(n); } else if (n.startsWith(filePrefix)) { n = n.remove(0, filePrefix.length()+1); if (fList.indexOf(n) == -1) fList.append(n); } } oList.sort(); cList.sort(); fList.sort(); objectCombo->addItems(oList); classCombo->addItems(cList); fileCombo->addItems(fList); objectActivated(objectCombo->currentText()); classActivated(classCombo->currentText()); fileActivated(fileCombo->currentText()); maxListEdit->setValue(c->_maxListCount); QTreeWidgetItem* i = new QTreeWidgetItem(dirList); i->setText(0, i18n("(always)")); i->setExpanded(true); QTreeWidgetItem* root = i; QStringList::const_iterator sit = c->_generalSourceDirs.constBegin(); for(; sit != c->_generalSourceDirs.constEnd(); ++sit ) { QTreeWidgetItem *item = new QTreeWidgetItem(i); item->setText(0, sit->isEmpty() ? QDir::rootPath() : *sit); } if (data) { for ( oit = data->objectMap().begin(); oit != data->objectMap().end(); ++oit ) { const QString n = (*oit).name(); if (n.isEmpty()) continue; i = new QTreeWidgetItem(dirList); i->setText(0, n); i->setExpanded(true); const QStringList dirs = c->_objectSourceDirs[n]; sit = dirs.constBegin(); for(; sit != dirs.constEnd(); ++sit ) { QTreeWidgetItem *item = new QTreeWidgetItem(i); item->setText(0, sit->isEmpty() ? QDir::rootPath() : *sit); } } } connect(dirList, &QTreeWidget::itemSelectionChanged, this, &ConfigDlg::dirsItemChanged); connect(addDirButton, &QAbstractButton::clicked, this, &ConfigDlg::dirsAddPressed); connect(deleteDirButton, &QAbstractButton::clicked, this, &ConfigDlg::dirsDeletePressed); dirList->setCurrentItem(root); symbolCount->setValue(c->_maxSymbolCount); symbolLength->setValue(c->_maxSymbolLength); precisionEdit->setValue(c->_percentPrecision); contextEdit->setValue(c->_context); } ConfigDlg::~ConfigDlg() { } bool ConfigDlg::configure(GlobalGUIConfig* c, TraceData* d, QWidget* p) { ConfigDlg dlg(c, d, p); if (dlg.exec()) { // max 499 per definition c->_maxListCount = dlg.maxListEdit->value(); c->_maxSymbolCount = dlg.symbolCount->value(); c->_maxSymbolLength = dlg.symbolLength->value(); c->_percentPrecision = dlg.precisionEdit->value(); c->_context = dlg.contextEdit->value(); return true; } return false; } void ConfigDlg::objectActivated(const QString & s) { // qDebug("objectActivated: %s", s.ascii()); if (s.isEmpty()) { _objectCS=nullptr; return; } QString n = ProfileContext::typeName(ProfileContext::Object) + '-' + s; ConfigColorSetting* cs = _config->_colors[n]; if (!cs) cs = GlobalGUIConfig::colorSetting(n); // else // qDebug("found color %s", n.ascii()); _objectCS = cs; objectCheck->setChecked(cs->_automatic); objectColor->setColor(cs->_color); /* qDebug("Found Color %s, automatic to %s", _objectCS->name.ascii(), _objectCS->automatic ? "true":"false"); */ } void ConfigDlg::objectCheckChanged(bool b) { if (_objectCS) { _objectCS->_automatic = b; /* qDebug("Set Color %s automatic to %s", _objectCS->name.ascii(), _objectCS->automatic ? "true":"false"); */ } } void ConfigDlg::objectColorChanged(const QColor & c) { if (_objectCS) _objectCS->_color = c; } void ConfigDlg::classActivated(const QString & s) { // qDebug("classActivated: %s", s.ascii()); if (s.isEmpty()) { _classCS=nullptr; return; } QString n = ProfileContext::typeName(ProfileContext::Class) + '-' + s; ConfigColorSetting* cs = _config->_colors[n]; if (!cs) cs = GlobalGUIConfig::colorSetting(n); _classCS = cs; classCheck->setChecked(cs->_automatic); classColor->setColor(cs->_color); } void ConfigDlg::classCheckChanged(bool b) { if (_classCS) _classCS->_automatic = b; } void ConfigDlg::classColorChanged(const QColor & c) { if (_classCS) _classCS->_color = c; } void ConfigDlg::fileActivated(const QString & s) { // qDebug("fileActivated: %s", s.ascii()); if (s.isEmpty()) { _fileCS=nullptr; return; } QString n = ProfileContext::typeName(ProfileContext::File) + '-' + s; ConfigColorSetting* cs = _config->_colors[n]; if (!cs) cs = GlobalGUIConfig::colorSetting(n); _fileCS = cs; fileCheck->setChecked(cs->_automatic); fileColor->setColor(cs->_color); } void ConfigDlg::fileCheckChanged(bool b) { if (_fileCS) _fileCS->_automatic = b; } void ConfigDlg::fileColorChanged(const QColor & c) { if (_fileCS) _fileCS->_color = c; } QTreeWidgetItem *ConfigDlg::getSelectedDirItem() { const QList selectedItems = dirList->selectedItems(); return selectedItems.count() ? selectedItems[0] : nullptr; } void ConfigDlg::dirsItemChanged() { QTreeWidgetItem *dirItem = getSelectedDirItem(); deleteDirButton->setEnabled(dirItem && dirItem->parent() != nullptr); addDirButton->setEnabled(dirItem && dirItem->parent() == nullptr); } void ConfigDlg::dirsDeletePressed() { QTreeWidgetItem *dirItem = getSelectedDirItem(); if (!dirItem || (dirItem->parent() == nullptr)) return; QTreeWidgetItem* p = dirItem->parent(); if (!p) return; QString objName = p->text(0); QStringList* dirs; if (objName == i18n("(always)")) dirs = &(_config->_generalSourceDirs); else dirs = &(_config->_objectSourceDirs[objName]); dirs->removeAll(dirItem->text(0)); delete dirItem; deleteDirButton->setEnabled(false); } void ConfigDlg::dirsAddPressed() { QTreeWidgetItem *dirItem = getSelectedDirItem(); if (!dirItem || (dirItem->parent() != nullptr)) return; QString objName = dirItem->text(0); QStringList* dirs; if (objName == i18n("(always)")) dirs = &(_config->_generalSourceDirs); else dirs = &(_config->_objectSourceDirs[objName]); QString newDir; newDir = QFileDialog::getExistingDirectory(this, i18n("Choose Source Folder")); if (newDir.isEmpty()) return; // even for '/', we strip the tailing slash if (newDir.endsWith(QLatin1Char('/'))) newDir = newDir.left(newDir.length()-1); if (dirs->indexOf(newDir)>=0) return; dirs->append(newDir); if (newDir.isEmpty()) newDir = QDir::rootPath(); QTreeWidgetItem *item = new QTreeWidgetItem(dirItem); item->setText(0, newDir); } diff --git a/kcachegrind/kdeconfig.cpp b/kcachegrind/kdeconfig.cpp index 2aaca02..eaef9c8 100644 --- a/kcachegrind/kdeconfig.cpp +++ b/kcachegrind/kdeconfig.cpp @@ -1,146 +1,146 @@ /* This file is part of KCachegrind. Copyright (C) 2002-2016 Josef Weidendorfer KCachegrind is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * Configuration for KCachegrind */ #include "kdeconfig.h" -#include -#include +#include +#include #include "tracedata.h" #include "traceitemview.h" #include "ui_configdlgbase.h" // // KDEConfigGroup // KDEConfigGroup::KDEConfigGroup(KConfigGroup* group, bool readOnly) { _kgroup = group; _readOnly = readOnly; } KDEConfigGroup::~KDEConfigGroup() { delete _kgroup; } void KDEConfigGroup::setValue(const QString& key, const QVariant& value, const QVariant& defaultValue) { if ((_kgroup == nullptr) || _readOnly) return; if (value == defaultValue) { _kgroup->deleteEntry(key); return; } switch(value.type()) { case QVariant::Bool: _kgroup->writeEntry(key, value.toBool()); break; case QVariant::Int: _kgroup->writeEntry(key, value.toInt()); break; case QVariant::Double: _kgroup->writeEntry(key, value.toDouble()); break; case QVariant::String: _kgroup->writeEntry(key, value.toString()); break; case QVariant::StringList: _kgroup->writeEntry(key, value.toStringList()); break; case QVariant::Color: _kgroup->writeEntry(key, value.value()); break; default: qFatal("KDEConfigGroup::setValue - QVariant type %s not supported", value.typeName()); } } QVariant KDEConfigGroup::value(const QString& key, const QVariant& defaultValue) const { if (_kgroup == nullptr) return defaultValue; switch(defaultValue.type()) { case QVariant::Bool: return QVariant(_kgroup->readEntry(key, defaultValue.toBool())); case QVariant::Int: return QVariant(_kgroup->readEntry(key, defaultValue.toInt())); case QVariant::Double: return QVariant(_kgroup->readEntry(key, defaultValue.toDouble())); case QVariant::String: return QVariant(_kgroup->readEntry(key, defaultValue.toString())); case QVariant::StringList: return QVariant(_kgroup->readEntry(key, defaultValue.toStringList())); case QVariant::Color: return QVariant(_kgroup->readEntry(key, defaultValue.value())); default: qFatal("KDEConfigGroup::value - QVariant type %s not supported", defaultValue.typeName()); } return defaultValue; } // // KDEConfigStorage // KDEConfigStorage::KDEConfigStorage(KConfig* kconfig) { _kconfig = kconfig; } ConfigGroup* KDEConfigStorage::getGroup(const QString& group, const QString& optSuffix) { KConfigGroup* g; bool readOnly; if (!optSuffix.isEmpty()) { readOnly = true; QStringList gList = _kconfig->groupList(); if (gList.contains(group+optSuffix)) g = new KConfigGroup(_kconfig, group+optSuffix); else if (gList.contains(group)) g = new KConfigGroup(_kconfig, group); else g = nullptr; } else { readOnly = false; g = new KConfigGroup(_kconfig, group); } return new KDEConfigGroup(g, readOnly); } diff --git a/kcachegrind/toplevel.cpp b/kcachegrind/toplevel.cpp index e5dd506..6a33af2 100644 --- a/kcachegrind/toplevel.cpp +++ b/kcachegrind/toplevel.cpp @@ -1,2392 +1,2392 @@ /* This file is part of KCachegrind. Copyright (C) 2002-2016 Josef Weidendorfer KCachegrind is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * KCachegrind top level window */ #define TRACE_UPDATES 0 #define ENABLE_DUMPDOCK 0 #include "toplevel.h" #include // for system() #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #if ENABLE_DUMPDOCK #include "dumpselection.h" #endif #include "partselection.h" #include "functionselection.h" #include "stackselection.h" #include "stackbrowser.h" #include "tracedata.h" #include "globalguiconfig.h" #include "config.h" #include "configdlg.h" #include "multiview.h" #include "callgraphview.h" TopLevel::TopLevel() : KXmlGuiWindow(nullptr) { QDBusConnection::sessionBus().registerObject(QStringLiteral("/KCachegrind"), this, QDBusConnection::ExportScriptableSlots); _progressBar = nullptr; _statusbar = statusBar(); _statusLabel = new QLabel(_statusbar); _statusbar->addWidget(_statusLabel, 1); _ccProcess = nullptr; _layoutCount = 1; _layoutCurrent = 0; resetState(); KConfig *kconfig = KSharedConfig::openConfig().data(); GlobalGUIConfig::config()->readOptions(); createDocks(); _multiView = new MultiView(this, this ); _multiView->setObjectName(QStringLiteral("MultiView")); setCentralWidget(_multiView); createActions(); _partDockShown->setChecked(!_partDock->isHidden()); _stackDockShown->setChecked(!_stackDock->isHidden()); _functionDockShown->setChecked(!_functionDock->isHidden()); connect(_partDock, &QDockWidget::visibilityChanged, this, &TopLevel::partVisibilityChanged); connect(_stackDock, &QDockWidget::visibilityChanged, this, &TopLevel::stackVisibilityChanged); connect(_functionDock, &QDockWidget::visibilityChanged, this, &TopLevel::functionVisibilityChanged); #if ENABLE_DUMPDOCK _dumpDockShown->setChecked(!_dumpDock->isHidden()); connect(_dumpDock, SIGNAL(visibilityChanged(bool)), this, SLOT(dumpVisibilityChanged(bool))); #endif // set toggle after reading configuration _showPercentage = GlobalConfig::showPercentage(); _showExpanded = GlobalConfig::showExpanded(); _showCycles = GlobalConfig::showCycles(); _hideTemplates = GlobalConfig::hideTemplates(); _taPercentage->setChecked(_showPercentage); _taExpanded->setChecked(_showExpanded); _taCycles->setChecked(_showCycles); _taHideTemplates->setChecked(_hideTemplates); setupPartSelection(_partSelection); // KCachegrind for KDE 3.0.x does not allow to hide toolbars... setStandardToolBarMenuEnabled(true); _openRecent->loadEntries( KConfigGroup( kconfig, "" ) ); // QT dock windows are created before (using QT position restoring) createGUI(); setAutoSaveSettings(); // restore current state settings (not configuration options) restoreCurrentState(QString()); // if this is the first toplevel, show tip of day if (memberList().count() == 1) QTimer::singleShot( 200, this, &TopLevel::slotShowTipOnStart ); } void TopLevel::resetState() { _activeParts.clear(); _hiddenParts.clear(); _data = nullptr; _function = nullptr; _eventType = nullptr; _eventType2 = nullptr; _groupType = ProfileContext::InvalidType; _group = nullptr; // for delayed slots _traceItemDelayed = nullptr; _eventTypeDelayed = nullptr; _eventType2Delayed = nullptr; _groupTypeDelayed = ProfileContext::InvalidType; _groupDelayed = nullptr; _directionDelayed = TraceItemView::None; _lastSender = nullptr; } /** * Setup the part selection widget. * Statusbar has to be created before. */ void TopLevel::setupPartSelection(PartSelection* ps) { // setup connections from the part selection widget connect(ps, &PartSelection::partsHideSelected, this, &TopLevel::partsHideSelectedSlotDelayed); connect(ps, &PartSelection::partsUnhideAll, this, &TopLevel::partsUnhideAllSlotDelayed); } /** * This saves the current state of the main window and * sub widgets. * * No positions are saved. These is done automatically for * KToolbar, and manually in queryClose() for Qt docks. */ void TopLevel::saveCurrentState(const QString& postfix) { QString eventType = _eventType ? _eventType->name() : QStringLiteral("?"); QString eventType2 = _eventType2 ? _eventType2->name() : QStringLiteral("?"); ConfigGroup* stateConfig = ConfigStorage::group(QStringLiteral("CurrentState") + postfix); stateConfig->setValue(QStringLiteral("EventType"), eventType); stateConfig->setValue(QStringLiteral("EventType2"), eventType2); stateConfig->setValue(QStringLiteral("GroupType"), ProfileContext::typeName(_groupType)); delete stateConfig; _partSelection->saveOptions(QStringLiteral("PartOverview"), postfix); _multiView->saveLayout(QStringLiteral("MainView"), postfix); _multiView->saveOptions(QStringLiteral("MainView"), postfix); } /** * This function is called when a trace is closed. * Save browsing position for later restoring */ void TopLevel::saveTraceSettings() { QString key = traceKey(); ConfigGroup* lConfig = ConfigStorage::group(QStringLiteral("Layouts")); lConfig->setValue(QStringLiteral("Count%1").arg(key), _layoutCount); lConfig->setValue(QStringLiteral("Current%1").arg(key), _layoutCurrent); delete lConfig; ConfigGroup* pConfig = ConfigStorage::group(QStringLiteral("TracePositions")); if (_eventType) pConfig->setValue(QStringLiteral("EventType%1").arg(key), _eventType->name()); if (_eventType2) pConfig->setValue(QStringLiteral("EventType2%1").arg(key), _eventType2->name()); if (_groupType != ProfileContext::InvalidType) pConfig->setValue(QStringLiteral("GroupType%1").arg(key), ProfileContext::typeName(_groupType)); if (_data) { if (_group) pConfig->setValue(QStringLiteral("Group%1").arg(key), _group->name()); saveCurrentState(key); } delete pConfig; } /** * This restores the current state of the main window and * sub widgets. * * This does NOT restore any positions. This is done automatically for * KToolbar, and manually in the createDocks() for QT docks.. */ void TopLevel::restoreCurrentState(const QString& postfix) { _partSelection->restoreOptions(QStringLiteral("PartOverview"), postfix); _multiView->restoreLayout(QStringLiteral("MainView"), postfix); _multiView->restoreOptions(QStringLiteral("MainView"), postfix); _taSplit->setChecked(_multiView->childCount()>1); _taSplitDir->setEnabled(_multiView->childCount()>1); _taSplitDir->setChecked(_multiView->orientation() == Qt::Horizontal); } void TopLevel::createDocks() { _partDock = new QDockWidget(this); _partDock->setObjectName(QStringLiteral("part dock")); _partDock->setWindowTitle(i18n("Parts Overview")); _partSelection = new PartSelection(this, _partDock); _partDock->setWidget(_partSelection); _stackDock = new QDockWidget(this); _stackDock->setObjectName(QStringLiteral("stack dock")); // Why is the caption only correct with a close button? _stackSelection = new StackSelection(_stackDock); _stackDock->setWidget(_stackSelection); _stackDock->setWindowTitle(i18n("Top Cost Call Stack")); _stackSelection->setWhatsThis( i18n( "The Top Cost Call Stack" "

This is a purely fictional 'most probable' call stack. " "It is built up by starting with the current selected " "function and adds the callers/callees with highest cost " "at the top and to bottom.

" "

The Cost and Calls columns show the " "cost used for all calls from the function in the line " "above.

")); connect(_stackSelection, SIGNAL(functionSelected(CostItem*)), this, SLOT(setTraceItemDelayed(CostItem*))); _functionDock = new QDockWidget(this); _functionDock->setObjectName(QStringLiteral("function dock")); _functionDock->setWindowTitle(i18n("Flat Profile")); _functionSelection = new FunctionSelection(this, _functionDock); _functionDock->setWidget(_functionSelection); #if ENABLE_DUMPDOCK _dumpDock = new QDockWidget(this); _dumpDock->setWindowTitle(i18n("Profile Dumps")); _dumpSelection = new DumpSelection(this, _dumpDock, "dumpSelection"); _dumpSelection->setTopLevel(this); _dumpDock->setWidget(_dumpSelection); _dumpSelection->setWhatsThis( i18n( "Profile Dumps" "

This dockable shows in the top part the list of " "loadable profile dumps in all subdirectories of: " "

  • current working directory of KCachegrind, " "i.e. where it was started from, and
  • " "
  • the default profile dump directory given in the " "configuration.
" "The list is sorted according to the target command " "profiled in the corresponding dump.

" "

On selecting a profile dump, information for it " "is shown in the bottom area of the dockable: " "

  • Options allows you to view the profiled " "command and profile options of this dump. By changing " "any item, a new (yet unexisting) profile template " "is created. Press Run Profile to start a " "profile run with these options in the background.
  • " "
  • Info gives detailed info on the selected " "dump like event cost summary and properties of the " "simulated cache.
  • " "
  • State is only available for current happening " "profiles runs. Press Update to see different " "counters of the run, and a stack trace of the current " "position in the program profiled. Check the Every " "option to let KCachegrind regularly poll these data. " "Check the Sync option to let the dockable activate " "the top function in the current loaded dump.

")); #endif // default positions, will be adjusted automatically by stored state in config addDockWidget(Qt::LeftDockWidgetArea, _partDock ); addDockWidget(Qt::LeftDockWidgetArea, _stackDock ); addDockWidget(Qt::LeftDockWidgetArea, _functionDock ); _stackDock->hide(); _partDock->hide(); #if ENABLE_DUMPDOCK addDockWidget( Qt::LeftDockWidgetArea, _dumpDock ); _dumpDock->hide(); #endif KConfigGroup dockConfig(KSharedConfig::openConfig(), "Docks"); _forcePartDock = dockConfig.readEntry("ForcePartDockVisible", false); } TopLevel::~TopLevel() { delete _data; } void TopLevel::saveProperties(KConfigGroup & c) { if ( _data ) c.writeEntry("TraceName", _data->traceName()); } void TopLevel::readProperties(const KConfigGroup &c) { QString traceName = c.readEntry("TraceName"); if (!traceName.isEmpty()) { openDataFile(traceName); } } void TopLevel::createLayoutActions() { QString hint; QAction* action; action = actionCollection()->addAction( QStringLiteral("layout_duplicate") ); action->setText( i18n( "&Duplicate" ) ); connect(action, &QAction::triggered, this, &TopLevel::layoutDuplicate); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Plus)); hint = i18n("Duplicate Current Layout" "

Make a copy of the current layout.

"); action->setWhatsThis( hint ); action = actionCollection()->addAction( QStringLiteral("layout_remove") ); action->setText( i18n( "&Remove" ) ); connect(action, &QAction::triggered, this, &TopLevel::layoutRemove); hint = i18n("Remove Current Layout" "

Delete current layout and make the previous active.

"); action->setWhatsThis( hint ); action = actionCollection()->addAction( QStringLiteral("layout_next") ); action->setText( i18n( "&Go to Next" ) ); connect(action, &QAction::triggered, this, &TopLevel::layoutNext); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Right)); hint = i18n("Go to Next Layout"); action->setWhatsThis( hint ); action = actionCollection()->addAction( QStringLiteral("layout_previous") ); action->setText( i18n( "&Go to Previous" ) ); connect(action, &QAction::triggered, this, &TopLevel::layoutPrevious); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Left)); hint = i18n("Go to Previous Layout"); action->setWhatsThis( hint ); action = actionCollection()->addAction( QStringLiteral("layout_restore") ); action->setText( i18n( "&Restore to Default" ) ); connect(action, &QAction::triggered, this, &TopLevel::layoutRestore); hint = i18n("Restore Layouts to Default"); action->setWhatsThis( hint ); action = actionCollection()->addAction( QStringLiteral("layout_save") ); action->setText( i18n( "&Save as Default" ) ); connect(action, &QAction::triggered, this, &TopLevel::layoutSave); hint = i18n("Save Layouts as Default"); action->setWhatsThis( hint ); } // TODO: split this up... void TopLevel::createMiscActions() { QString hint; QAction* action; action = KStandardAction::openNew(this, SLOT(newWindow()), actionCollection()); hint = i18n("New

Open new empty KCachegrind window.

"); action->setWhatsThis( hint ); action = actionCollection()->addAction( QStringLiteral("file_add") ); action->setText( i18n( "&Add..." ) ); connect(action, SIGNAL(triggered(bool)), SLOT(add())); hint = i18n("Add Profile Data" "

This opens an additional profile data file in the current window.

"); action->setWhatsThis( hint ); action = actionCollection()->addAction( QStringLiteral("reload") ); action->setIcon( QIcon::fromTheme(QStringLiteral("view-refresh")) ); action->setText( i18nc("Reload a document", "&Reload" ) ); connect(action, &QAction::triggered, this, &TopLevel::reload); actionCollection()->setDefaultShortcut(action, KStandardShortcut::Reload); hint = i18n("Reload Profile Data" "

This loads any new created parts, too.

"); action->setWhatsThis( hint ); action = actionCollection()->addAction( QStringLiteral("export") ); action->setText( i18n( "&Export Graph" ) ); connect(action, &QAction::triggered, this, &TopLevel::exportGraph); hint = i18n("Export Call Graph" "

Generates a file with extension .dot for the tools " "of the GraphViz package.

"); action->setWhatsThis( hint ); _taDump = actionCollection()->add( QStringLiteral("dump") ); _taDump->setIcon( QIcon::fromTheme(QStringLiteral("edit-redo")) ); _taDump->setText( i18n( "&Force Dump" ) ); connect(_taDump, &QAction::triggered, this, &TopLevel::forceTrace); actionCollection()->setDefaultShortcut(_taDump, QKeySequence::Undo); hint = i18n("Force Dump" "

This forces a dump for a Callgrind profile run " "in the current directory. This action is checked while " "KCachegrind looks for the dump. If the dump is " "finished, it automatically reloads the current trace. " "If this is the one from the running Callgrind, the new " "created trace part will be loaded, too.

" "

Force dump creates a file 'callgrind.cmd', and " "checks every second for its existence. A running " "Callgrind will detect this file, dump a trace part, " "and delete 'callgrind.cmd'. " "The deletion is detected by KCachegrind, " "and it does a Reload. If there is no Callgrind " "running, press 'Force Dump' again to cancel the dump " "request. This deletes 'callgrind.cmd' itself and " "stops polling for a new dump.

" "

Note: A Callgrind run only detects " "existence of 'callgrind.cmd' when actively running " "a few milliseconds, i.e. " "not sleeping. Tip: For a profiled GUI program, " "you can awake Callgrind e.g. by resizing a window " "of the program.

"); _taDump->setWhatsThis( hint ); action = KStandardAction::open(this, SLOT(load()), actionCollection()); hint = i18n("Open Profile Data" "

This opens a profile data file, with possible multiple parts

"); action->setToolTip( hint ); action->setWhatsThis( hint ); _openRecent = KStandardAction::openRecent(this, SLOT(load(QUrl)), actionCollection()); KStandardAction::showStatusbar(this, SLOT(toggleStatusBar()), actionCollection()); _partDockShown = actionCollection()->add(QStringLiteral("settings_show_partdock")); _partDockShown->setText(i18n("Parts Overview")); connect(_partDockShown, &QAction::triggered, this, &TopLevel::togglePartDock); hint = i18n("Show/Hide the Parts Overview Dockable"); _partDockShown->setToolTip( hint ); _partDockShown->setWhatsThis( hint ); _stackDockShown = actionCollection()->add(QStringLiteral("settings_show_stackdock")); _stackDockShown->setText(i18n("Call Stack")); connect(_stackDockShown, &QAction::triggered, this, &TopLevel::toggleStackDock); hint = i18n("Show/Hide the Call Stack Dockable"); _stackDockShown->setToolTip( hint ); _stackDockShown->setWhatsThis( hint ); _functionDockShown = actionCollection()->add(QStringLiteral("settings_show_profiledock")); _functionDockShown->setText(i18n("Function Profile")); connect(_functionDockShown, &QAction::triggered, this, &TopLevel::toggleFunctionDock); hint = i18n("Show/Hide the Function Profile Dockable"); _functionDockShown->setToolTip( hint ); _functionDockShown->setWhatsThis( hint ); #if ENABLE_DUMPDOCK _dumpDockShown = actionCollection()->add("settings_show_dumpdock", this, SLOT(toggleDumpDock())); _dumpDockShown->setText(i18n("Profile Dumps")); hint = i18n("Show/Hide the Profile Dumps Dockable"); _dumpDockShown->setToolTip( hint ); _dumpDockShown->setWhatsThis( hint ); #endif _taPercentage = actionCollection()->add(QStringLiteral("view_percentage")); _taPercentage->setIcon(QIcon::fromTheme(QStringLiteral("percent"))); _taPercentage->setText(i18n("Relative")); connect(_taPercentage, &QAction::triggered, this, &TopLevel::togglePercentage); hint = i18n("Show relative instead of absolute costs"); _taPercentage->setToolTip( hint ); _taPercentage->setWhatsThis( hint ); _taExpanded = actionCollection()->add(QStringLiteral("view_expanded")); _taExpanded->setIcon(QIcon::fromTheme(QStringLiteral("move"))); _taExpanded->setText(i18n("Relative to Parent")); connect(_taExpanded, &QAction::triggered, this, &TopLevel::toggleExpanded); hint = i18n("Show percentage costs relative to parent"); _taExpanded->setToolTip( hint ); _taExpanded->setWhatsThis( hint ); hint = i18n("Show percentage costs relative to parent" "

If this is switched off, percentage costs are always shown " "relative to the total cost of the profile part(s) that are " "currently browsed. By turning on this option, percentage cost " "of shown cost items will be relative to the parent cost item.

" "
    " "" "" "" "" "" "
    Cost TypeParent Cost
    Function CumulativeTotal
    Function SelfFunction Group (*) / Total
    CallFunction Inclusive
    Source LineFunction Inclusive
" "

(*) Only if function grouping is switched on (e.g. ELF object grouping).

"); _taExpanded->setWhatsThis( hint ); _taCycles = actionCollection()->add(QStringLiteral("view_cycles")); _taCycles->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo"))); _taCycles->setText(i18n( "Cycle Detection" )); connect(_taCycles, &QAction::triggered, this, &TopLevel::toggleCycles); hint = i18n("Detect recursive cycles" "

If this is switched off, the treemap drawing will show " "black areas when a recursive call is made instead of drawing the " "recursion ad infinitum. Note that " "the size of black areas often will be wrong, as inside recursive " "cycles the cost of calls cannot be determined; the error is small, " "however, for false cycles (see documentation).

" "

The correct handling for cycles is to detect them and collapse all " "functions of a cycle into an artificial function, which is done when this " "option is selected. Unfortunately, with GUI applications, this often will " "lead to huge false cycles, making the analysis impossible; therefore, there " "is the option to switch this off.

"); _taCycles->setWhatsThis( hint ); _taHideTemplates = actionCollection()->add(QStringLiteral("hide_templates")); _taHideTemplates->setIcon(QIcon::fromTheme(QStringLiteral("hidetemplates"))); _taHideTemplates->setText(i18n( "Shorten Templates" )); connect(_taHideTemplates, &QAction::triggered, this, &TopLevel::toggleHideTemplates); _taHideTemplates->setToolTip(i18n( "Hide Template Parameters in C++ Symbols" )); hint = i18n("Hide Template Parameters in C++ Symbols" "

If this is switched on, every symbol displayed will have " "any C++ template parameters hidden, just showing <> " "instead of a potentially nested template parameter.

" "

In this mode, you can hover the mouse pointer over the " "activated symbol label to show a tooltip with the " "unabbreviated symbol.

"); _taHideTemplates->setWhatsThis(hint); KStandardAction::quit(this, SLOT(close()), actionCollection()); KStandardAction::preferences(this, SLOT(configure()), actionCollection()); KStandardAction::keyBindings(this, SLOT(configureKeys()), actionCollection()); KStandardAction::configureToolbars(this,SLOT(configureToolbars()), actionCollection()); _paUp = new KToolBarPopupAction( QIcon::fromTheme( QStringLiteral("go-up") ), i18n( "&Up" ), this ); connect( _paUp, &QAction::triggered, _stackSelection, &StackSelection::browserUp ); actionCollection()->addAction( QStringLiteral("go_up"), _paUp ); actionCollection()->setDefaultShortcut(_paUp, QKeySequence(Qt::ALT + Qt::Key_Up)); connect( _paUp->menu(), &QMenu::aboutToShow, this, &TopLevel::upAboutToShow ); connect( _paUp->menu(), &QMenu::triggered, this, &TopLevel::upTriggered ); hint = i18n("Go Up" "

Go to last selected caller of current function. " "If no caller was visited, use that with highest cost.

"); _paUp->setToolTip( hint ); _paUp->setWhatsThis( hint ); QPair< KGuiItem, KGuiItem > backForward = KStandardGuiItem::backAndForward(); _paBack = new KToolBarPopupAction( backForward.first.icon(), backForward.first.text(), this ); connect( _paBack, &QAction::triggered, _stackSelection, &StackSelection::browserBack ); actionCollection()->addAction( QStringLiteral("go_back"), _paBack ); actionCollection()->setDefaultShortcut(_paBack, QKeySequence(Qt::ALT + Qt::Key_Left)); connect( _paBack->menu(), &QMenu::aboutToShow, this, &TopLevel::backAboutToShow ); connect( _paBack->menu(), &QMenu::triggered, this, &TopLevel::backTriggered ); hint = i18n("Go back in function selection history"); _paBack->setToolTip( hint ); _paBack->setWhatsThis( hint ); _paForward = new KToolBarPopupAction( backForward.second.icon(), backForward.second.text(), this ); connect( _paForward, &QAction::triggered, _stackSelection, &StackSelection::browserForward ); actionCollection()->addAction( QStringLiteral("go_forward"), _paForward ); actionCollection()->setDefaultShortcut(_paForward, QKeySequence(Qt::ALT + Qt::Key_Right)); connect( _paForward->menu(), &QMenu::aboutToShow, this, &TopLevel::forwardAboutToShow ); connect( _paForward->menu(), &QMenu::triggered, this, &TopLevel::forwardTriggered ); hint = i18n("Go forward in function selection history"); _paForward->setToolTip( hint ); _paForward->setWhatsThis( hint ); _saCost = actionCollection()->add(QStringLiteral("view_cost_type")); _saCost->setText(i18n("Primary Event Type")); hint = i18n("Select primary event type of costs"); _saCost->setComboWidth(300); _saCost->setToolTip( hint ); _saCost->setWhatsThis( hint ); // This is needed because for KDE4, "_saCost->setComboWidth(300);" seems to // have no effect. Instead, list box entry widths are used to determine the // combobox width. However, at KCachegrind startup, we do not have yet // a list of event types, as this depends on the profile data. // In KDE 4.2, we used a translatable string, which did not really work as // the semantic is not known to translators. Instead, we use a // nontranslatable string now... QStringList dummyItems; dummyItems << QStringLiteral("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); _saCost->setItems(dummyItems); // cost types are dependent on loaded data, thus KSelectAction // is filled in setData() connect( _saCost, SIGNAL(triggered(QString)), this, SLOT(eventTypeSelected(QString))); _saCost2 = actionCollection()->add(QStringLiteral("view_cost_type2")); _saCost2->setText(i18n("Secondary Event Type")); hint = i18n("Select secondary event type for cost e.g. shown in annotations"); _saCost2->setComboWidth(300); _saCost2->setToolTip( hint ); _saCost2->setWhatsThis( hint ); _saCost2->setItems(dummyItems); connect( _saCost2, SIGNAL(triggered(QString)), this, SLOT(eventType2Selected(QString))); saGroup = actionCollection()->add(QStringLiteral("view_group_type")); saGroup->setText(i18n("Grouping")); hint = i18n("Select how functions are grouped into higher level cost items"); saGroup->setToolTip( hint ); saGroup->setWhatsThis( hint ); QStringList args; args << i18n("(No Grouping)") << ProfileContext::i18nTypeName(ProfileContext::Object) << ProfileContext::i18nTypeName(ProfileContext::File) << ProfileContext::i18nTypeName(ProfileContext::Class) << ProfileContext::i18nTypeName(ProfileContext::FunctionCycle); saGroup->setItems(args); connect( saGroup, SIGNAL(triggered(int)), this, SLOT(groupTypeSelected(int))); _taSplit = actionCollection()->add(QStringLiteral("view_split")); _taSplit->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right"))); _taSplit->setText(i18n("Split")); connect(_taSplit, &QAction::triggered, this, &TopLevel::splitSlot); hint = i18n("Show two information panels"); _taSplit->setToolTip( hint ); _taSplit->setWhatsThis( hint ); _taSplitDir = actionCollection()->add(QStringLiteral("view_split_dir")); _taSplitDir->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right"))); _taSplitDir->setText(i18n("Split Horizontal")); connect(_taSplitDir, &QAction::triggered, this, &TopLevel::splitDirSlot); hint = i18n("Change Split Orientation when main window is split."); _taSplitDir->setToolTip( hint ); _taSplitDir->setWhatsThis( hint ); // copied from KMail... KStandardAction::tipOfDay( this, SLOT(slotShowTip()), actionCollection() ); } void TopLevel::createActions() { createMiscActions(); createLayoutActions(); } void TopLevel::toggleStatusBar() { if (statusBar()->isVisible()) statusBar()->hide(); else statusBar()->show(); } void TopLevel::togglePartDock() { if (!_partDock->isVisible()) _partDock->show(); else _partDock->hide(); } void TopLevel::toggleStackDock() { if (!_stackDock->isVisible()) _stackDock->show(); else _stackDock->hide(); } void TopLevel::toggleDumpDock() { #if ENABLE_DUMPDOCK if (!_dumpDock->isVisible()) _dumpDock->show(); else _dumpDock->hide(); #endif } void TopLevel::toggleFunctionDock() { if (!_functionDock->isVisible()) _functionDock->show(); else _functionDock->hide(); } void TopLevel::togglePercentage() { setPercentage(_taPercentage->isChecked()); } void TopLevel::setAbsoluteCost() { setPercentage(false); } void TopLevel::setRelativeCost() { setPercentage(true); } void TopLevel::updateViewsOnChange(int change) { _partSelection->notifyChange(change); _functionSelection->notifyChange(change); _multiView->notifyChange(change); } void TopLevel::setPercentage(bool show) { if (_showPercentage == show) return; _showPercentage = show; if (_taPercentage->isChecked() != show) _taPercentage->setChecked(show); GlobalConfig::setShowPercentage(_showPercentage); _stackSelection->refresh(); updateViewsOnChange(TraceItemView::configChanged); } void TopLevel::toggleExpanded() { bool show = _taExpanded->isChecked(); if (_showExpanded == show) return; _showExpanded = show; GlobalConfig::setShowExpanded(_showExpanded); _stackSelection->refresh(); updateViewsOnChange(TraceItemView::configChanged); } void TopLevel::toggleCycles() { bool show = _taCycles->isChecked(); if (_showCycles == show) return; _showCycles = show; GlobalConfig::setShowCycles(_showCycles); if (!_data) return; _data->invalidateDynamicCost(); _data->updateFunctionCycles(); _stackSelection->rebuildStackList(); updateViewsOnChange(TraceItemView::configChanged); } void TopLevel::toggleHideTemplates() { bool b = _taHideTemplates->isChecked(); if (_hideTemplates == b) return; _hideTemplates = b; GlobalConfig::setHideTemplates(_hideTemplates); _stackSelection->refresh(); updateViewsOnChange(TraceItemView::configChanged); } void TopLevel::partVisibilityChanged(bool v) { _partDockShown->setChecked(v); } void TopLevel::stackVisibilityChanged(bool v) { _stackDockShown->setChecked(v); } #if ENABLE_DUMPDOCK void TopLevel::dumpVisibilityChanged(bool v) #else void TopLevel::dumpVisibilityChanged(bool) #endif { #if ENABLE_DUMPDOCK _dumpDockShown->setChecked(v); #endif } void TopLevel::functionVisibilityChanged(bool v) { _functionDockShown->setChecked(v); if (v) _functionSelection->updateView(); } void TopLevel::querySlot() { _functionSelection->query(queryLineEdit->text()); } void TopLevel::configureKeys() { KShortcutsDialog::configure(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this); } void TopLevel::configureToolbars() { KEditToolBar *dlg = new KEditToolBar(guiFactory(), this); if (dlg->exec()) createGUI(); delete dlg; } void TopLevel::newWindow() { TopLevel* t = new TopLevel(); t->show(); } void TopLevel::load() { QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Select Callgrind Profile Data"), QUrl(), i18n("Callgrind Profile Data (cachegrind.out* callgrind.out*);;All Files (*)")); load(url); } void TopLevel::load(const QUrl& url) { if (url.isEmpty()) return; QString tmpFileName; QTemporaryFile tmpFile; if (url.isLocalFile()) { tmpFileName = url.toLocalFile(); } else if (tmpFile.open()){ // network transparency tmpFileName = tmpFile.fileName(); KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tmpFileName)); KJobWidgets::setWindow(job, this); job->exec(); } if (!tmpFileName.isEmpty()) { _openRecent->addUrl(url); _openRecent->saveEntries( KConfigGroup( KSharedConfig::openConfig(), QString() ) ); load(tmpFileName); } else { KMessageBox::error(this, i18n("Could not open the file \"%1\". " "Check it exists and you have enough " "permissions to read it.", url.toDisplayString())); } } /* if file name is ".": load first file found in current directory, but do * not show an error message if nothing could be loaded */ void TopLevel::load(QString file) { if (file.isEmpty()) return; bool showError = true; if (file == QStringLiteral(".")) showError = false; if (_data && _data->parts().count()>0) { // In new window TopLevel* t = new TopLevel(); t->show(); t->loadDelayed(file); return; } bool loaded = openDataFile(file); if (!loaded && showError) KMessageBox::error(this, i18n("Could not open the file \"%1\". " "Check it exists and you have enough " "permissions to read it.", file)); } void TopLevel::add() { QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Add Callgrind Profile Data"), QUrl(), i18n("Callgrind Profile Data (cachegrind.out* callgrind.out*);;All Files (*)")); add(url); } void TopLevel::add(const QUrl &url) { if (url.isEmpty()) return; QString tmpFileName; QTemporaryFile tmpFile; if (url.isLocalFile()) { tmpFileName = url.toLocalFile(); } else if (tmpFile.open()){ // network transparency tmpFileName = tmpFile.fileName(); KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tmpFileName)); KJobWidgets::setWindow(job, this); job->exec(); } if (!tmpFileName.isEmpty()) { _openRecent->addUrl(url); _openRecent->saveEntries( KSharedConfig::openConfig()->group( QString() ) ); add(tmpFileName); } } void TopLevel::add(QString file) { if (file.isEmpty()) return; if (_data) { _data->load(file); // GUI update for added data configChanged(); return; } openDataFile(file); } void TopLevel::loadDelayed(QString file) { _loadFilesDelayed << file; QTimer::singleShot(0, this, &TopLevel::loadTraceDelayed); } void TopLevel::loadDelayed(QStringList files) { _loadFilesDelayed << files; QTimer::singleShot(0, this, &TopLevel::loadTraceDelayed); } void TopLevel::loadTraceDelayed() { if (_loadFilesDelayed.isEmpty()) return; if (_loadFilesDelayed.count()>1) { // FIXME: we expect all files to be local and existing TraceData* d = new TraceData(this); d->load(_loadFilesDelayed); setData(d); } else { QString file = _loadFilesDelayed[0]; // if URL scheme is missing (URL is relative), this is a local file QUrl u = QUrl::fromUserInput(file, QString(), QUrl::AssumeLocalFile); if (u.isLocalFile()) load(file); // special case for local file: maybe just prefix else load(u); } _loadFilesDelayed.clear(); } void TopLevel::reload() { QString trace; if (!_data || _data->parts().count()==0) trace = QStringLiteral("."); // open first trace found in dir else trace = _data->traceName(); // this also keeps sure we have the same browsing position... openDataFile(trace); } void TopLevel::exportGraph() { if (!_data || !_function) return; QString n = QStringLiteral("callgraph.dot"); GraphExporter ge(_data, _function, _eventType, _groupType, n); ge.writeDot(); #ifdef Q_OS_UNIX // shell commands only work in UNIX QString cmd = QStringLiteral("(dot %1 -Tps > %2.ps; xdg-open %3.ps)&") .arg(n).arg(n).arg(n); if (::system(QFile::encodeName( cmd ))<0) qDebug() << "TopLevel::exportGraph: can not run " << cmd; #endif } bool TopLevel::setEventType(QString s) { EventType* ct; ct = (_data) ? _data->eventTypes()->type(s) : nullptr; // if costtype with given name not found, use first available if (!ct && _data) ct = _data->eventTypes()->type(0); return setEventType(ct); } bool TopLevel::setEventType2(QString s) { EventType* ct; // Special type i18n("(Hidden)") gives 0 ct = (_data) ? _data->eventTypes()->type(s) : nullptr; return setEventType2(ct); } void TopLevel::eventTypeSelected(const QString& s) { EventType* ct; ct = (_data) ? _data->eventTypes()->typeForLong(s) : nullptr; setEventType(ct); } void TopLevel::eventType2Selected(const QString& s) { EventType* ct; ct = (_data) ? _data->eventTypes()->typeForLong(s) : nullptr; setEventType2(ct); } bool TopLevel::setEventType(EventType* ct) { if (_eventType == ct) return false; _eventType = ct; if (ct) { QStringList l = _saCost->items(); int idx = l.indexOf(ct->longName()); if (idx >= 0) _saCost->setCurrentItem(idx); } _partSelection->setEventType(_eventType); _stackSelection->setEventType(_eventType); _functionSelection->setEventType(_eventType); _multiView->setEventType(_eventType); updateStatusBar(); return true; } bool TopLevel::setEventType2(EventType* ct) { if (_eventType2 == ct) return false; _eventType2 = ct; QString longName = ct ? ct->longName() : i18n("(Hidden)"); QStringList l = _saCost2->items(); int idx = l.indexOf(longName); if (idx >= 0) _saCost2->setCurrentItem(idx); _partSelection->setEventType2(_eventType2); _stackSelection->setEventType2(_eventType2); _functionSelection->setEventType2(_eventType2); _multiView->setEventType2(_eventType2); updateStatusBar(); return true; } void TopLevel::groupTypeSelected(int cg) { switch(cg) { case 0: setGroupType( ProfileContext::Function ); break; case 1: setGroupType( ProfileContext::Object ); break; case 2: setGroupType( ProfileContext::File ); break; case 3: setGroupType( ProfileContext::Class ); break; case 4: setGroupType( ProfileContext::FunctionCycle ); break; default: break; } } bool TopLevel::setGroupType(QString s) { ProfileContext::Type gt; gt = ProfileContext::type(s); // only allow Function/Object/File/Class as grouptype switch(gt) { case ProfileContext::Object: case ProfileContext::File: case ProfileContext::Class: case ProfileContext::FunctionCycle: break; default: gt = ProfileContext::Function; } return setGroupType(gt); } bool TopLevel::setGroupType(ProfileContext::Type gt) { if (_groupType == gt) return false; _groupType = gt; int idx = -1; switch(gt) { case ProfileContext::Function: idx = 0; break; case ProfileContext::Object: idx = 1; break; case ProfileContext::File: idx = 2; break; case ProfileContext::Class: idx = 3; break; case ProfileContext::FunctionCycle: idx = 4; break; default: break; } if (idx==-1) return false; if (saGroup->currentItem() != idx) saGroup->setCurrentItem(idx); _stackSelection->setGroupType(_groupType); _partSelection->set(_groupType); _functionSelection->set(_groupType); _multiView->set(_groupType); updateStatusBar(); return true; } bool TopLevel::setGroup(QString s) { TraceCostItem* ci = _functionSelection->group(s); if (!ci) return false; return setGroup(ci); } bool TopLevel::setGroup(TraceCostItem* g) { if (_group == g) return false; _group = g; _functionSelection->setGroup(g); updateStatusBar(); return true; } bool TopLevel::setFunction(QString s) { if (!_data) return false; ProfileCostArray* f = _data->search(ProfileContext::Function, s, _eventType); if (!f) return false; return setFunction((TraceFunction*)f); } bool TopLevel::setFunction(TraceFunction* f) { if (_function == f) return false; _function = f; _multiView->activate(f); _functionSelection->activate(f); _partSelection->activate(f); _stackSelection->setFunction(_function); StackBrowser* b = _stackSelection->browser(); if (b) { // do not disable up: a press forces stack-up extending... _paForward->setEnabled(b->canGoForward()); _paBack->setEnabled(b->canGoBack()); } #if TRACE_UPDATES qDebug("TopLevel::setFunction(%s), lastSender %s", f ? f->prettyName().ascii() : "0", _lastSender ? _lastSender->name() :"0" ); #endif return true; } /** * Delayed versions. * We always have a pair of slots: One receiver to start the * delay with a singleShot Timer. It stores the parameter into a * temporary variable. And one parameterless slot for * forwarding, using this temporary. */ void TopLevel::setEventTypeDelayed(EventType* ct) { _eventTypeDelayed = ct; QTimer::singleShot (0, this, SLOT(setEventTypeDelayed())); } void TopLevel::setEventType2Delayed(EventType* ct) { _eventType2Delayed = ct; QTimer::singleShot (0, this, SLOT(setEventType2Delayed())); } void TopLevel::setEventTypeDelayed() { setEventType(_eventTypeDelayed); } void TopLevel::setEventType2Delayed() { setEventType2(_eventType2Delayed); } void TopLevel::setGroupTypeDelayed(ProfileContext::Type gt) { _groupTypeDelayed = gt; QTimer::singleShot (0, this, SLOT(setGroupTypeDelayed())); } void TopLevel::setGroupTypeDelayed() { setGroupType(_groupTypeDelayed); } void TopLevel::setGroupDelayed(TraceCostItem* g) { #if TRACE_UPDATES qDebug("TopLevel::setGroupDelayed(%s), sender %s", g ? g->prettyName().ascii() : "0", _lastSender ? _lastSender->name() :"0" ); #endif _groupDelayed = g; QTimer::singleShot (0, this, SLOT(setGroupDelayed())); } void TopLevel::setGroupDelayed() { setGroup(_groupDelayed); } void TopLevel::setDirectionDelayed(TraceItemView::Direction d) { _directionDelayed = d; QTimer::singleShot (0, this, SLOT(setDirectionDelayed())); } void TopLevel::setDirectionDelayed() { switch(_directionDelayed) { case TraceItemView::Back: _stackSelection->browserBack(); break; case TraceItemView::Forward: _stackSelection->browserForward(); break; case TraceItemView::Up: { StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr; HistoryItem* hi = b ? b->current() : nullptr; TraceFunction* f = hi ? hi->function() : nullptr; if (!f) break; f = hi->stack()->caller(f, false); if (f) setFunction(f); } break; default: break; } _directionDelayed = TraceItemView::None; } void TopLevel::setTraceItemDelayed(CostItem* i) { // no need to select same item a 2nd time... if (_traceItemDelayed == i) return; _traceItemDelayed = i; _lastSender = sender(); qDebug() << "Selected " << (i ? i->prettyName() : QStringLiteral("(none)")); #if TRACE_UPDATES qDebug("TopLevel::setTraceItemDelayed(%s), sender %s", i ? i->prettyName().ascii() : "0", _lastSender ? _lastSender->name() :"0" ); #endif QTimer::singleShot (0, this, SLOT(setTraceItemDelayed())); } void TopLevel::setTraceItemDelayed() { if (!_traceItemDelayed) return; switch(_traceItemDelayed->type()) { case ProfileContext::Function: case ProfileContext::FunctionCycle: setFunction((TraceFunction*)_traceItemDelayed); break; case ProfileContext::Object: case ProfileContext::File: case ProfileContext::Class: _multiView->activate(_traceItemDelayed); break; #if 0 // this conflicts with the selection policy of InstrView ?!? case ProfileContext::Instr: case ProfileContext::Line: // only for multiview _multiView->activate(_traceItemDelayed); break; #endif default: break; } _traceItemDelayed = nullptr; _lastSender = nullptr; } /** * A TraceData object cannot be viewed many times in different * toplevel windows. Thus, this toplevel window takes ownership * of the TraceData object: on closing the window or opening * another trace, the object is destroyed. */ void TopLevel::setData(TraceData* data) { if (data == _data) return; _lastSender = nullptr; saveTraceSettings(); if (_data) { _partSelection->setData(nullptr); _stackSelection->setData(nullptr); _functionSelection->setData(nullptr); _multiView->setData(nullptr); _multiView->updateView(true); // we are the owner... delete _data; } // reset members resetState(); _data = data; // fill cost type list QStringList types; if (_data) { /* add all supported virtual types */ EventTypeSet* m = _data->eventTypes(); m->addKnownDerivedTypes(); /* first, fill selection list with available cost types */ for (int i=0;irealCount();i++) types << m->realType(i)->longName(); for (int i=0;iderivedCount();i++) types << m->derivedType(i)->longName(); } _saCost->setItems(types); _saCost->setComboWidth(300); if (types.count()>0) { // second type list gets an additional "(Hidden)" types.prepend(i18n("(Hidden)")); } _saCost2->setItems(types); _saCost2->setComboWidth(300); // default is hidden if (types.count()>0) _saCost2->setCurrentItem(0); _partSelection->setData(_data); _stackSelection->setData(_data); _functionSelection->setData(_data); _multiView->setData(_data); // Force update of _data in all children of _multiView // This is needed to make restoring of activeItem work! _multiView->updateView(true); /* this is needed to let the other widgets know the types */ restoreTraceTypes(); restoreTraceSettings(); QString caption; if (_data) { caption = _data->traceName(); if (!_data->command().isEmpty()) caption += " [" + _data->command() + ']'; } setWindowTitle(caption); if (!_data || (!_forcePartDock && _data->parts().count()<2)) { _partDock->hide(); _partDockShown->setChecked(false); } else { _partDock->show(); _partDockShown->setChecked(true); } updateStatusBar(); } void TopLevel::addEventTypeMenu(QMenu* popup, bool withCost2) { if (_data) { QMenu *popup1, *popup2 = nullptr; QAction* action; popup1 = popup->addMenu(i18n("Primary Event Type")); connect(popup1, SIGNAL(triggered(QAction*)), this, SLOT(setEventType(QAction*))); if (withCost2) { popup2 = popup->addMenu(i18n("Secondary Event Type")); connect(popup2, SIGNAL(triggered(QAction*)), this, SLOT(setEventType2(QAction*))); if (_eventType2) { action = popup2->addAction(i18n("Hide")); action->setData(199); popup2->addSeparator(); } } EventTypeSet* m = _data->eventTypes(); EventType* ct; for (int i=0;irealCount();i++) { ct = m->realType(i); action = popup1->addAction(ct->longName()); action->setCheckable(true); action->setData(100+i); if (_eventType == ct) action->setChecked(true); if (popup2) { action = popup2->addAction(ct->longName()); action->setCheckable(true); action->setData(100+i); if (_eventType2 == ct) action->setChecked(true); } } for (int i=0;iderivedCount();i++) { ct = m->derivedType(i); action = popup1->addAction(ct->longName()); action->setCheckable(true); action->setData(200+i); if (_eventType == ct) action->setChecked(true); if (popup2) { action = popup2->addAction(ct->longName()); action->setCheckable(true); action->setData(200+i); if (_eventType2 == ct) action->setChecked(true); } } } if (_showPercentage) popup->addAction(i18n("Show Absolute Cost"), this, SLOT(setAbsoluteCost())); else popup->addAction(i18n("Show Relative Cost"), this, SLOT(setRelativeCost())); } bool TopLevel::setEventType(QAction* action) { if (!_data) return false; int id = action->data().toInt(nullptr); EventTypeSet* m = _data->eventTypes(); EventType* ct=nullptr; if (id >=100 && id<199) ct = m->realType(id-100); if (id >=200 && id<299) ct = m->derivedType(id-200); return ct ? setEventType(ct) : false; } bool TopLevel::setEventType2(QAction* action) { if (!_data) return false; int id = action->data().toInt(nullptr); EventTypeSet* m = _data->eventTypes(); EventType* ct=nullptr; if (id >=100 && id<199) ct = m->realType(id-100); if (id >=200 && id<299) ct = m->derivedType(id-200); return setEventType2(ct); } void TopLevel::addGoMenu(QMenu* popup) { popup->addAction(i18n("Go Back"), this, SLOT(goBack())); popup->addAction(i18n("Go Forward"), this, SLOT(goForward())); popup->addAction(i18n("Go Up"), this, SLOT(goUp())); } void TopLevel::goBack() { setDirectionDelayed(TraceItemView::Back); } void TopLevel::goForward() { setDirectionDelayed(TraceItemView::Forward); } void TopLevel::goUp() { setDirectionDelayed(TraceItemView::Up); } QString TopLevel::traceKey() { if (!_data || _data->command().isEmpty()) return QString(); QString name = _data->command(); QString key; for (int l=0;lvalue(QStringLiteral("GroupType%1").arg(key),QString()).toString(); eventType = pConfig->value(QStringLiteral("EventType%1").arg(key),QString()).toString(); eventType2 = pConfig->value(QStringLiteral("EventType2%1").arg(key),QString()).toString(); delete pConfig; ConfigGroup* cConfig = ConfigStorage::group(QStringLiteral("CurrentState")); if (groupType.isEmpty()) groupType = cConfig->value(QStringLiteral("GroupType"),QString()).toString(); if (eventType.isEmpty()) eventType = cConfig->value(QStringLiteral("EventType"),QString()).toString(); if (eventType2.isEmpty()) eventType2 = cConfig->value(QStringLiteral("EventType2"),QString()).toString(); delete cConfig; setGroupType(groupType); setEventType(eventType); setEventType2(eventType2); // if still no cost type set, use first available if (!_eventType && !_saCost->items().isEmpty()) eventTypeSelected(_saCost->items().first()); ConfigGroup* aConfig = ConfigStorage::group(QStringLiteral("Layouts")); _layoutCount = aConfig->value(QStringLiteral("Count%1").arg(key), 0).toInt(); _layoutCurrent = aConfig->value(QStringLiteral("Current%1").arg(key), 0).toInt(); delete aConfig; if (_layoutCount == 0) layoutRestore(); updateLayoutActions(); } /** * This must be called after setting group/cost types in the function * selection widget, because the group/function choosing depends on * filled lists in the function selection widget */ void TopLevel::restoreTraceSettings() { if (!_data) return; QString key = traceKey(); restoreCurrentState(key); ConfigGroup* pConfig = ConfigStorage::group(QStringLiteral("TracePositions")); QString group = pConfig->value(QStringLiteral("Group%1").arg(key),QString()).toString(); delete pConfig; if (!group.isEmpty()) setGroup(group); // restoreCurrentState() usually leads to a call to setTraceItemDelayed() // to restore last active item... if (!_traceItemDelayed) { // function not available any more.. try with "main" if (!setFunction(QStringLiteral("main"))) _functionSelection->selectTopFunction(); } } /* Layout */ void TopLevel::layoutDuplicate() { // save current and allocate a new slot _multiView->saveLayout(QStringLiteral("Layout%1-MainView").arg(_layoutCurrent), traceKey()); _layoutCurrent = _layoutCount; _layoutCount++; updateLayoutActions(); if (0) qDebug() << "TopLevel::layoutDuplicate: count " << _layoutCount; } void TopLevel::layoutRemove() { if (_layoutCount <2) return; int from = _layoutCount-1; if (_layoutCurrent == from) { _layoutCurrent--; from--; } // restore from last and decrement count _multiView->restoreLayout(QStringLiteral("Layout%1-MainView").arg(from), traceKey()); _layoutCount--; updateLayoutActions(); } void TopLevel::layoutNext() { if (_layoutCount <2) return; QString key = traceKey(); QString layoutPrefix = QStringLiteral("Layout%1-MainView"); _multiView->saveLayout(layoutPrefix.arg(_layoutCurrent), key); _layoutCurrent++; if (_layoutCurrent == _layoutCount) _layoutCurrent = 0; _multiView->restoreLayout(layoutPrefix.arg(_layoutCurrent), key); if (0) qDebug() << "TopLevel::layoutNext: current " << _layoutCurrent << endl; } void TopLevel::layoutPrevious() { if (_layoutCount <2) return; QString key = traceKey(); QString layoutPrefix = QStringLiteral("Layout%1-MainView"); _multiView->saveLayout(layoutPrefix.arg(_layoutCurrent), key); _layoutCurrent--; if (_layoutCurrent <0) _layoutCurrent = _layoutCount-1; _multiView->restoreLayout(layoutPrefix.arg(_layoutCurrent), key); if (0) qDebug() << "TopLevel::layoutPrevious: current " << _layoutCurrent << endl; } void TopLevel::layoutSave() { QString key = traceKey(); QString layoutPrefix = QStringLiteral("Layout%1-MainView"); _multiView->saveLayout(layoutPrefix.arg(_layoutCurrent), key); // save all layouts as defaults (ie. without any group name postfix) for(int i=0;i<_layoutCount;i++) { _multiView->restoreLayout(layoutPrefix.arg(i), key); _multiView->saveLayout(layoutPrefix.arg(i), QString()); } // restore the previously saved current layout _multiView->restoreLayout(layoutPrefix.arg(_layoutCurrent), key); ConfigGroup* layoutConfig = ConfigStorage::group(QStringLiteral("Layouts")); layoutConfig->setValue(QStringLiteral("DefaultCount"), _layoutCount); layoutConfig->setValue(QStringLiteral("DefaultCurrent"), _layoutCurrent); delete layoutConfig; } void TopLevel::layoutRestore() { KConfig *config = KSharedConfig::openConfig().data(); KConfigGroup aConfig(config, "Layouts"); _layoutCount = aConfig.readEntry("DefaultCount", 0); _layoutCurrent = aConfig.readEntry("DefaultCurrent", 0); if (_layoutCount == 0) { _layoutCount++; return; } QString layoutPrefix = QStringLiteral("Layout%1-MainView"); _multiView->restoreLayout( layoutPrefix.arg(_layoutCurrent), traceKey()); updateLayoutActions(); } void TopLevel::updateLayoutActions() { QAction* ka; ka = actionCollection()->action(QStringLiteral("layout_next")); if (ka) ka->setEnabled(_layoutCount>1); ka = actionCollection()->action(QStringLiteral("layout_previous")); if (ka) ka->setEnabled(_layoutCount>1); ka = actionCollection()->action(QStringLiteral("layout_remove")); if (ka) ka->setEnabled(_layoutCount>1); _statusbar->showMessage(i18n("Layout Count: %1", _layoutCount), 1000); } void TopLevel::updateStatusBar() { if (!_data || _data->parts().count()==0) { _statusLabel->setText(i18n("No profile data file loaded.")); return; } QString status = QStringLiteral("%1 [%2] - ") .arg(_data->shortTraceName()) .arg(_data->activePartRange()); if (_eventType) { status += i18n("Total %1 Cost: %2", _eventType->longName(), _data->prettySubCost(_eventType)); /* this gets too long... if (_eventType2 && (_eventType2 != _eventType)) status += i18n(", %1 Cost: %2") .arg(_eventType2->longName()) .arg(_data->prettySubCost(_eventType2)); */ } else status += i18n("No event type selected"); /* Not working... should give group of selected function if (_groupType != ProfileContext::Function) { status += QString(" - %1 '%2'") .arg(ProfileContext::i18nTypeName(_groupType)) .arg(_group ? _group->prettyName() : i18n("(None)")); } */ _statusLabel->setText(status); } void TopLevel::configure() { if (ConfigDlg::configure( GlobalGUIConfig::config(), _data, this)) { GlobalGUIConfig::config()->saveOptions(); configChanged(); } else GlobalGUIConfig::config()->readOptions(); } bool TopLevel::queryClose() { saveTraceSettings(); // save current toplevel options as defaults... GlobalConfig::setShowPercentage(_showPercentage); GlobalConfig::setShowExpanded(_showExpanded); GlobalConfig::setShowCycles(_showCycles); GlobalConfig::setHideTemplates(_hideTemplates); GlobalGUIConfig::config()->saveOptions(); saveCurrentState(QString()); // toolbar and dock positions are automatically stored // if part dock was chosen visible even for only 1 part loaded, // keep this choice... _forcePartDock = false; if (_data && (_data->parts().count()<2) && _partDock->isVisible()) _forcePartDock=true; KConfigGroup dockConfig(KSharedConfig::openConfig(), "Docks"); dockConfig.writeEntry("ForcePartDockVisible", _forcePartDock); return true; } void TopLevel::splitSlot() { int count = _multiView->childCount(); if (count<1) count = 1; if (count>2) count = 2; count = 3-count; _multiView->setChildCount(count); _taSplit->setChecked(count>1); _taSplitDir->setEnabled(count>1); _taSplitDir->setChecked(_multiView->orientation() == Qt::Horizontal); } void TopLevel::splitDirSlot() { _multiView->setOrientation( _taSplitDir->isChecked() ? Qt::Horizontal : Qt::Vertical ); } // this is called after a config change in the dialog void TopLevel::configChanged() { // Invalidate found/cached dirs of source files if we have TraceData loaded. if (_data) { _data->resetSourceDirs(); } _stackSelection->refresh(); updateViewsOnChange(TraceItemView::configChanged); } void TopLevel::slotShowTipOnStart() { KTipDialog::showTip(this); } void TopLevel::slotShowTip() { KTipDialog::showTip( this, QString(), true ); } void TopLevel::dummySlot() { } void TopLevel::activePartsChangedSlot(const TracePartList& list) { if (!_data) return; if (!_data->activateParts(list)) { // qDebug("TopLevel::activePartsChangedSlot: No Change!"); return; } _activeParts = list; _partSelection->set(list); _multiView->set(list); _functionSelection->set(list); _stackSelection->refresh(); updateStatusBar(); } void TopLevel::partsHideSelectedSlotDelayed() { QTimer::singleShot( 0, this, &TopLevel::partsHideSelectedSlot ); } // this puts selected parts into hidden list, // deselects them and makes the remaining parts selected void TopLevel::partsHideSelectedSlot() { if (!_data) return; TracePartList newHidden, newActive; foreach(TracePart* part, _data->parts()) { if (_activeParts.contains(part) || _hiddenParts.contains(part)) newHidden.append(part); else newActive.append(part); } _hiddenParts = newHidden; _partSelection->hiddenPartsChangedSlot(_hiddenParts); #if 0 _mainWidget1->hiddenPartsChangedSlot(_hiddenParts); _mainWidget2->hiddenPartsChangedSlot(_hiddenParts); #endif activePartsChangedSlot(newActive); } void TopLevel::partsUnhideAllSlotDelayed() { QTimer::singleShot( 0, this, &TopLevel::partsUnhideAllSlot ); } // this unhides all hidden parts. Does NOT change selection void TopLevel::partsUnhideAllSlot() { if (!_data) return; _hiddenParts.clear(); _partSelection->hiddenPartsChangedSlot(_hiddenParts); #if 0 _mainWidget1->hiddenPartsChangedSlot(_hiddenParts); _mainWidget2->hiddenPartsChangedSlot(_hiddenParts); #endif } void TopLevel::forceTrace() { if (_ccProcess) { // callgrind_control still running, cancel request qDebug("TopLevel::forceTrace: killing old callgrind_control"); _ccProcess->kill(); delete _ccProcess; _ccProcess = nullptr; _ccOutput = QString(); } if (!_taDump->isChecked()) return; // get PID of first loaded part int pid = 0; TracePart* p = nullptr; TracePartList pl; if (_data) pl = _data->parts(); if (!pl.isEmpty()) p = pl.first(); if (p) pid = p->processID(); if (pid == 0) { showMessage(i18n("Cannot determine receiver PID for dump request"), 5000); _taDump->setChecked(false); return; } qDebug("TopLevel::forceTrace: run 'callgrind_control -d %d'", pid); _ccProcess = new QProcess(this); connect(_ccProcess, &QProcess::readyReadStandardOutput, this, &TopLevel::ccReadOutput); connect(_ccProcess, SIGNAL(error(QProcess::ProcessError)), SLOT(ccError(QProcess::ProcessError))); connect(_ccProcess, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(ccExit(int,QProcess::ExitStatus))); _ccProcess->start(QStringLiteral("callgrind_control -d %1").arg(pid), QIODevice::ReadOnly); } void TopLevel::ccReadOutput() { QProcess* p = qobject_cast(sender()); qDebug("TopLevel::ccReadOutput: QProcess %p", p); // signal from old/uninteresting process? if (!_ccProcess) return; if (p != _ccProcess) return; _ccOutput.append(QString::fromLocal8Bit(_ccProcess->readAllStandardOutput())); } void TopLevel::ccError(QProcess::ProcessError e) { QProcess* p = qobject_cast(sender()); qDebug("TopLevel::ccError: Got %d from QProcess %p", e, p); // signal from old/uninteresting process? if (!_ccProcess) return; if (p != _ccProcess) return; showMessage(i18n("Error running callgrind_control"), 5000); _ccProcess->deleteLater(); _ccProcess = nullptr; } void TopLevel::ccExit(int exitCode, QProcess::ExitStatus s) { QProcess* p = qobject_cast(sender()); qDebug("TopLevel::ccExit: QProcess %p, exitCode %d", p, exitCode); // signal from old/uninteresting process? if (!_ccProcess) return; if (p != _ccProcess) return; _ccProcess->deleteLater(); _ccProcess = nullptr; _taDump->setChecked(false); // if not successful no need to reload if ((s == QProcess::CrashExit) || (exitCode != 0)) return; // FIXME: Are we sure that file is completely // dumped after waiting one second? QTimer::singleShot( 1000, this, &TopLevel::reload ); } void TopLevel::forwardAboutToShow() { QMenu *popup = _paForward->menu(); popup->clear(); StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr; HistoryItem* hi = b ? b->current() : nullptr; TraceFunction* f; QAction* action; if (!hi) { popup->addAction(i18n("(No Stack)")); return; } hi = hi->next(); if (!hi) { popup->addAction(i18n("(No next function)")); return; } int count = 1; while (countfunction(); if (!f) break; QString name = GlobalConfig::shortenSymbol(f->prettyName()); //qDebug("forward: Adding %s", name.ascii()); action = popup->addAction(name); action->setData(count); hi = hi->next(); count++; } } void TopLevel::backAboutToShow() { QMenu *popup = _paBack->menu(); popup->clear(); StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr; HistoryItem* hi = b ? b->current() : nullptr; TraceFunction* f; QAction* action; if (!hi) { popup->addAction(i18n("(No Stack)")); return; } hi = hi->last(); if (!hi) { popup->addAction(i18n("(No previous function)")); return; } int count = 1; while (countfunction(); if (!f) break; QString name = GlobalConfig::shortenSymbol(f->prettyName()); //qDebug("back: Adding %s", name.ascii()); action = popup->addAction(name); action->setData(count); hi = hi->last(); count++; } } void TopLevel::upAboutToShow() { QMenu *popup = _paUp->menu(); popup->clear(); StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr; HistoryItem* hi = b ? b->current() : nullptr; TraceFunction* f = hi ? hi->function() : nullptr; QAction* action; if (!f) { popup->addAction(i18n("(No Stack)")); return; } f = hi->stack()->caller(f, false); if (!f) { popup->addAction(i18n("(No Function Up)")); return; } int count = 1; while (countprettyName()); action = popup->addAction(name); action->setData(count); f = hi->stack()->caller(f, false); count++; } } void TopLevel::forwardTriggered(QAction* action) { int count = action->data().toInt(nullptr); //qDebug("forwardTriggered: %d", count); if( count <= 0) return; StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr; if (!b) return; while (count>1) { b->goForward(); count--; } _stackSelection->browserForward(); } void TopLevel::backTriggered(QAction* action) { int count = action->data().toInt(nullptr); //qDebug("backTriggered: %d", count); if( count <= 0) return; StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr; if (!b) return; while (count>1) { b->goBack(); count--; } _stackSelection->browserBack(); } void TopLevel::upTriggered(QAction* action) { int count = action->data().toInt(nullptr); //qDebug("upTriggered: %d", count); if( count <= 0) return; StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr; HistoryItem* hi = b ? b->current() : nullptr; if (!hi) return; TraceFunction* f = hi->function(); while (count>0 && f) { f = hi->stack()->caller(f, false); count--; } //qDebug("upActivated: %s", f ? f->prettyName().ascii() : "??" ); if (f) setFunction(f); } void TopLevel::showMessage(const QString& msg, int ms) { if (_statusbar) _statusbar->showMessage(msg, ms); } void TopLevel::showStatus(const QString& msg, int progress) { static bool msgUpdateNeeded = true; if (!_statusbar) return; if (msg.isEmpty()) { //reset status if (_progressBar) { _statusbar->removeWidget(_progressBar); delete _progressBar; _progressBar = nullptr; } _statusbar->clearMessage(); _progressMsg = msg; return; } if (_progressMsg.isEmpty()) _progressStart.start(); if (msg != _progressMsg) { _progressMsg = msg; msgUpdateNeeded = true; } // do nothing if last change was less than 0.5 seconds ago if (_progressStart.elapsed() < 500) return; if (!_progressBar) { _progressBar = new QProgressBar(_statusbar); _progressBar->setMaximumSize(200, _statusbar->height()-4); _statusbar->addPermanentWidget(_progressBar, 1); _progressBar->show(); msgUpdateNeeded = true; } _progressStart.restart(); if (msgUpdateNeeded) { _statusbar->showMessage(msg); msgUpdateNeeded = false; } _progressBar->setValue(progress); // let the progress bar update itself qApp->processEvents(QEventLoop::ExcludeUserInputEvents); } void TopLevel::loadStart(const QString& filename) { showStatus(i18n("Loading %1", filename), 0); Logger::_filename = filename; } void TopLevel::loadFinished(const QString& msg) { showStatus(QString(), 0); if (!msg.isEmpty()) showMessage(i18n("Error loading %1: %2", _filename, msg), 2000); } void TopLevel::loadProgress(int progress) { showStatus(i18n("Loading %1", _filename), progress); } void TopLevel::loadError(int line, const QString& msg) { qCritical() << "Loading" << _filename << ":" << line << ": " << msg; } void TopLevel::loadWarning(int line, const QString& msg) { qWarning() << "Loading" << _filename << ":" << line << ": " << msg; } bool TopLevel::openDataFile(const QString& file) { TraceData* d = new TraceData(this); int filesLoaded; // see whether this file is compressed, than take the direct route QMimeDatabase dataBase; QString mimeType = dataBase.mimeTypeForFile(file, QMimeDatabase::MatchContent).name(); KCompressionDevice* compressed; compressed = new KCompressionDevice(file, KFilterDev::compressionTypeForMimeType(mimeType)); if (compressed && (compressed->compressionType() != KCompressionDevice::None)) { filesLoaded = d->load(compressed, file); } else { // else fallback to string based method that can also find multi-part callgrind data. filesLoaded = d->load(file); } if (filesLoaded > 0) { setData(d); return true; } else { return false; } } diff --git a/kcachegrind/toplevel.h b/kcachegrind/toplevel.h index 1ee8ee9..db41317 100644 --- a/kcachegrind/toplevel.h +++ b/kcachegrind/toplevel.h @@ -1,298 +1,298 @@ /* This file is part of KCachegrind. Copyright (C) 2002-2016 Josef Weidendorfer KCachegrind is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * KCachegrind top level window */ #ifndef TOPLEVEL_H #define TOPLEVEL_H #include #include #include #include -#include +#include #include "logger.h" #include "traceitemview.h" #include "tracedata.h" #include "toplevelbase.h" class MultiView; class QLineEdit; class QDockWidget; class QLabel; class QProgressBar; class QMenu; class QUrl; class KSelectAction; class KToggleAction; class KToolBarPopupAction; class KStatusBar; class TraceData; class KRecentFilesAction; class MainWidget; class PartSelection; class FunctionSelection; class DumpSelection; class StackSelection; class TraceFunction; class TopLevel : public KXmlGuiWindow, public Logger, public TopLevelBase { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kcachegrind") public: TopLevel(); ~TopLevel() override; TraceData* data() { return _data; } void setData(TraceData*); void saveProperties(KConfigGroup &) override; void readProperties(const KConfigGroup &) override; void createActions(); void createDocks(); ProfileContext::Type groupType() { return _groupType; } EventType* eventType() { return _eventType; } EventType* eventType2() { return _eventType2; } TracePartList activeParts() { return _activeParts; } TracePartList hiddenParts() override { return _hiddenParts; } // current config bool showPercentage() const { return _showPercentage; } bool showExpanded() const { return _showExpanded; } bool showCycles() const { return _showCycles; } /* convenience functions for often used context menu items */ void addEventTypeMenu(QMenu*,bool) override; void addGoMenu(QMenu*) override; // Logger overwrites: notifications for file loading void loadStart(const QString& filename) override; void loadProgress(int progress) override; // 0 - 100 void loadWarning(int line, const QString& msg) override; void loadError(int line, const QString& msg) override; void loadFinished(const QString& msg) override; // msg could be error public Q_SLOTS: void load(); void load(const QUrl&); void load(QString); void add(); void add(const QUrl&); void add(QString); // for quickly showing the main window... void loadDelayed(QString); void loadDelayed(QStringList); void reload(); void exportGraph(); void newWindow(); void configure(); void querySlot(); void dummySlot(); // layouts void layoutDuplicate(); void layoutRemove(); void layoutNext(); void layoutPrevious(); void layoutSave(); void layoutRestore(); void updateLayoutActions(); void updateStatusBar(); void eventTypeSelected(const QString&); void eventType2Selected(const QString&); void groupTypeSelected(int); void splitSlot(); void splitDirSlot(); void configureToolbars() override; void configureKeys(); bool queryClose() override; void togglePartDock(); void toggleStackDock(); void toggleFunctionDock(); void toggleDumpDock(); void toggleStatusBar(); void partVisibilityChanged(bool); void dumpVisibilityChanged(bool); void stackVisibilityChanged(bool); void functionVisibilityChanged(bool); void togglePercentage(); void setPercentage(bool); void setAbsoluteCost(); void setRelativeCost(); void toggleExpanded(); void toggleCycles(); void toggleHideTemplates(); void forceTrace(); void forwardAboutToShow(); void forwardTriggered(QAction*); void backAboutToShow(); void backTriggered(QAction*); void upAboutToShow(); void upTriggered(QAction*); bool setEventType(EventType*); bool setEventType2(EventType*); bool setEventType(QString); bool setEventType2(QString); bool setEventType(QAction*); bool setEventType2(QAction*); bool setGroupType(ProfileContext::Type); bool setGroupType(QString); bool setGroup(TraceCostItem*); bool setGroup(QString); bool setFunction(TraceFunction*); bool setFunction(QString); void activePartsChangedSlot(const TracePartList& list) override; void partsHideSelectedSlot(); void partsUnhideAllSlot(); /* These go back to mainloop first by using a timer. * So they can be called from event handlers that * are not allowed to delete list entries. */ void setEventTypeDelayed(EventType*) override; void setEventType2Delayed(EventType*) override; void setGroupTypeDelayed(ProfileContext::Type) override; void setGroupDelayed(TraceCostItem*) override; void setTraceItemDelayed(CostItem*) override; void partsHideSelectedSlotDelayed(); void partsUnhideAllSlotDelayed(); void goBack(); void goForward(); void goUp(); void setDirectionDelayed(TraceItemView::Direction) override; /* SingleShot Slots (without parameters) for the delayed versions */ void setEventTypeDelayed(); void setEventType2Delayed(); void setGroupTypeDelayed(); void setGroupDelayed(); void setTraceItemDelayed(); void loadTraceDelayed(); void setDirectionDelayed(); // configuration has changed void configChanged() override; //void refresh(); void slotShowTipOnStart(); void slotShowTip(); // progress in status bar, empty message disables progress display void showStatus(const QString& msg, int progress); void showMessage(const QString&, int msec) override; // for running callgrind_control in the background void ccReadOutput(); void ccError(QProcess::ProcessError); void ccExit(int,QProcess::ExitStatus); private: void resetState(); void createLayoutActions(); void createMiscActions(); void setupMainWidget(MainWidget*); void setupPartSelection(PartSelection*); void restoreCurrentState(const QString& postfix); void saveCurrentState(const QString& postfix); void saveTraceSettings(); QString traceKey(); void restoreTraceTypes(); void restoreTraceSettings(); void updateViewsOnChange(int); /// open @p file, might be compressed /// @return true when the file could be opened, false otherwise. bool openDataFile(const QString& file); QStatusBar* _statusbar; QLabel* _statusLabel; KRecentFilesAction* _openRecent; bool _twoMainWidgets; Qt::Orientation _spOrientation; MultiView* _multiView; FunctionSelection* _functionSelection; DumpSelection* _dumpSelection; PartSelection* _partSelection; StackSelection* _stackSelection; QLineEdit* queryLineEdit; QDockWidget *_partDock, *_stackDock, *_functionDock, *_dumpDock; bool _forcePartDock; KSelectAction *_saCost, *_saCost2, *saGroup; KToggleAction *_partDockShown, *_stackDockShown; KToggleAction *_functionDockShown, *_dumpDockShown; KToggleAction *_taPercentage, *_taExpanded, *_taCycles, *_taHideTemplates; KToggleAction *_taDump, *_taSplit, *_taSplitDir; KToolBarPopupAction *_paForward, *_paBack, *_paUp; TraceFunction* _function; const QObject* _lastSender; // trace data shown in this window TraceData* _data; // subcost types used for visualization EventType* _eventType; EventType* _eventType2; // grouping of function list ProfileContext::Type _groupType; // selected group TraceCostItem* _group; // selected parts TracePartList _activeParts; // hidden parts TracePartList _hiddenParts; // layouts int _layoutCurrent, _layoutCount; // for delayed slots EventType* _eventTypeDelayed; EventType* _eventType2Delayed; ProfileContext::Type _groupTypeDelayed; TraceCostItem* _groupDelayed; CostItem* _traceItemDelayed; QStringList _loadFilesDelayed; TraceItemView::Direction _directionDelayed; // for status progress display QString _progressMsg; QTime _progressStart; QProgressBar* _progressBar; // toplevel configuration options bool _showPercentage, _showExpanded, _showCycles, _hideTemplates; // for running callgrind_control in the background QProcess* _ccProcess; QString _ccOutput; }; #endif