File indexing completed on 2024-04-28 05:41:23

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  * KCachegrind top level window
0011  */
0012 
0013 #define TRACE_UPDATES 0
0014 #define ENABLE_DUMPDOCK 0
0015 
0016 #include "toplevel.h"
0017 #include <karchive_version.h>
0018 
0019 #include <stdlib.h> // for system()
0020 
0021 #include <QDebug>
0022 #include <QDockWidget>
0023 #include <QEventLoop>
0024 #include <QFile>
0025 #include <QFileDialog>
0026 #include <QLabel>
0027 #include <QLineEdit>
0028 #include <QMenu>
0029 #include <QMimeDatabase>
0030 #include <QProcess>
0031 #include <QProgressBar>
0032 #include <QStatusBar>
0033 #include <QTemporaryFile>
0034 #include <QTimer>
0035 #include <QUrl>
0036 #include <QDBusConnection>
0037 
0038 #include <KToggleAction>
0039 #include <KToolBarPopupAction>
0040 #include <KActionCollection>
0041 #include <KSelectAction>
0042 #include <KRecentFilesAction>
0043 #include <KToolBar>
0044 #include <KStandardGuiItem>
0045 #include <KStandardShortcut>
0046 #include <KStandardAction>
0047 #include <KIO/Job>
0048 #include <KIO/FileCopyJob>
0049 #include <KJobWidgets>
0050 #include <KEditToolBar>
0051 #include <KShortcutsDialog>
0052 #include <KMessageBox>
0053 #include <KSharedConfig>
0054 #include <KConfigGroup>
0055 #include <KCompressionDevice>
0056 
0057 #if ENABLE_DUMPDOCK
0058 #include "dumpselection.h"
0059 #endif
0060 
0061 #include "partselection.h"
0062 #include "functionselection.h"
0063 #include "stackselection.h"
0064 #include "stackbrowser.h"
0065 #include "tracedata.h"
0066 #include "globalguiconfig.h"
0067 #include "config.h"
0068 #include "configdlg.h"
0069 #include "multiview.h"
0070 #include "callgraphview.h"
0071 
0072 TopLevel::TopLevel()
0073     : KXmlGuiWindow(nullptr)
0074 {
0075     QDBusConnection::sessionBus().registerObject(QStringLiteral("/KCachegrind"), this, QDBusConnection::ExportScriptableSlots);
0076 
0077     _progressBar = nullptr;
0078     _statusbar = statusBar();
0079     _statusLabel = new QLabel(_statusbar);
0080     _statusbar->addWidget(_statusLabel, 1);
0081     _ccProcess = nullptr;
0082 
0083     _layoutCount = 1;
0084     _layoutCurrent = 0;
0085 
0086     resetState();
0087 
0088     KConfig *kconfig = KSharedConfig::openConfig().data();
0089     GlobalGUIConfig::config()->readOptions();
0090 
0091     createDocks();
0092 
0093     _multiView = new MultiView(this, this );
0094     _multiView->setObjectName(QStringLiteral("MultiView"));
0095     setCentralWidget(_multiView);
0096 
0097     createActions();
0098 
0099     _partDockShown->setChecked(!_partDock->isHidden());
0100     _stackDockShown->setChecked(!_stackDock->isHidden());
0101     _functionDockShown->setChecked(!_functionDock->isHidden());
0102 
0103     connect(_partDock, &QDockWidget::visibilityChanged,
0104             this, &TopLevel::partVisibilityChanged);
0105     connect(_stackDock, &QDockWidget::visibilityChanged,
0106             this, &TopLevel::stackVisibilityChanged);
0107     connect(_functionDock, &QDockWidget::visibilityChanged,
0108             this, &TopLevel::functionVisibilityChanged);
0109 
0110 #if ENABLE_DUMPDOCK
0111     _dumpDockShown->setChecked(!_dumpDock->isHidden());
0112     connect(_dumpDock, SIGNAL(visibilityChanged(bool)),
0113             this, SLOT(dumpVisibilityChanged(bool)));
0114 #endif
0115 
0116 
0117     // set toggle after reading configuration
0118     _showPercentage = GlobalConfig::showPercentage();
0119     _showExpanded   = GlobalConfig::showExpanded();
0120     _showCycles     = GlobalConfig::showCycles();
0121     _hideTemplates  = GlobalConfig::hideTemplates();
0122     _taPercentage->setChecked(_showPercentage);
0123     _taExpanded->setChecked(_showExpanded);
0124     _taCycles->setChecked(_showCycles);
0125     _taHideTemplates->setChecked(_hideTemplates);
0126 
0127     setupPartSelection(_partSelection);
0128 
0129     // KCachegrind for KDE 3.0.x does not allow to hide toolbars...
0130     setStandardToolBarMenuEnabled(true);
0131     _openRecent->loadEntries( KConfigGroup( kconfig, "" ) );
0132 
0133     // QT dock windows are created before (using QT position restoring)
0134     createGUI();
0135 
0136     setAutoSaveSettings();
0137 
0138     // restore current state settings (not configuration options)
0139     restoreCurrentState(QString());
0140 }
0141 
0142 void TopLevel::resetState()
0143 {
0144     _activeParts.clear();
0145     _hiddenParts.clear();
0146 
0147     _data = nullptr;
0148     _function = nullptr;
0149     _eventType = nullptr;
0150     _eventType2 = nullptr;
0151     _groupType = ProfileContext::InvalidType;
0152     _group = nullptr;
0153 
0154     // for delayed slots
0155     _traceItemDelayed = nullptr;
0156     _eventTypeDelayed = nullptr;
0157     _eventType2Delayed = nullptr;
0158     _groupTypeDelayed = ProfileContext::InvalidType;
0159     _groupDelayed = nullptr;
0160     _directionDelayed = TraceItemView::None;
0161     _lastSender = nullptr;
0162 }
0163 
0164 
0165 /**
0166  * Setup the part selection widget.
0167  * Statusbar has to be created before.
0168  */
0169 void TopLevel::setupPartSelection(PartSelection* ps)
0170 {
0171     // setup connections from the part selection widget
0172 
0173     connect(ps, &PartSelection::partsHideSelected,
0174             this, &TopLevel::partsHideSelectedSlotDelayed);
0175     connect(ps, &PartSelection::partsUnhideAll,
0176             this, &TopLevel::partsUnhideAllSlotDelayed);
0177 }
0178 
0179 /**
0180  * This saves the current state of the main window and
0181  * sub widgets.
0182  *
0183  * No positions are saved. These is done automatically for
0184  * KToolbar, and manually in queryClose() for Qt docks.
0185  */
0186 void TopLevel::saveCurrentState(const QString& postfix)
0187 {
0188     QString eventType = _eventType ? _eventType->name() : QStringLiteral("?");
0189     QString eventType2 = _eventType2 ? _eventType2->name() : QStringLiteral("?");
0190 
0191     ConfigGroup* stateConfig = ConfigStorage::group(QLatin1String("CurrentState") + postfix);
0192     stateConfig->setValue(QStringLiteral("EventType"), eventType);
0193     stateConfig->setValue(QStringLiteral("EventType2"), eventType2);
0194     stateConfig->setValue(QStringLiteral("GroupType"), ProfileContext::typeName(_groupType));
0195     delete stateConfig;
0196 
0197     _partSelection->saveOptions(QStringLiteral("PartOverview"), postfix);
0198     _multiView->saveLayout(QStringLiteral("MainView"), postfix);
0199     _multiView->saveOptions(QStringLiteral("MainView"), postfix);
0200 }
0201 
0202 /**
0203  * This function is called when a trace is closed.
0204  * Save browsing position for later restoring
0205  */
0206 void TopLevel::saveTraceSettings()
0207 {
0208     QString key = traceKey();
0209 
0210     ConfigGroup* lConfig = ConfigStorage::group(QStringLiteral("Layouts"));
0211     lConfig->setValue(QStringLiteral("Count%1").arg(key), _layoutCount);
0212     lConfig->setValue(QStringLiteral("Current%1").arg(key), _layoutCurrent);
0213     delete lConfig;
0214 
0215     ConfigGroup* pConfig = ConfigStorage::group(QStringLiteral("TracePositions"));
0216     if (_eventType)
0217         pConfig->setValue(QStringLiteral("EventType%1").arg(key), _eventType->name());
0218     if (_eventType2)
0219         pConfig->setValue(QStringLiteral("EventType2%1").arg(key), _eventType2->name());
0220     if (_groupType != ProfileContext::InvalidType)
0221         pConfig->setValue(QStringLiteral("GroupType%1").arg(key),
0222                           ProfileContext::typeName(_groupType));
0223 
0224     if (_data) {
0225         if (_group)
0226             pConfig->setValue(QStringLiteral("Group%1").arg(key), _group->name());
0227         saveCurrentState(key);
0228     }
0229     delete pConfig;
0230 }
0231 
0232 /**
0233  * This restores the current state of the main window and
0234  * sub widgets.
0235  *
0236  * This does NOT restore any positions. This is done automatically for
0237  * KToolbar, and manually in the createDocks() for QT docks..
0238  */
0239 void TopLevel::restoreCurrentState(const QString& postfix)
0240 {
0241     _partSelection->restoreOptions(QStringLiteral("PartOverview"), postfix);
0242     _multiView->restoreLayout(QStringLiteral("MainView"), postfix);
0243     _multiView->restoreOptions(QStringLiteral("MainView"), postfix);
0244 
0245     _taSplit->setChecked(_multiView->childCount()>1);
0246     _taSplitDir->setEnabled(_multiView->childCount()>1);
0247     _taSplitDir->setChecked(_multiView->orientation() == Qt::Horizontal);
0248 }
0249 
0250 
0251 void TopLevel::createDocks()
0252 {
0253     _partDock = new QDockWidget(this);
0254     _partDock->setObjectName(QStringLiteral("part dock"));
0255     _partDock->setWindowTitle(i18n("Parts Overview"));
0256     _partSelection = new PartSelection(this, _partDock);
0257     _partDock->setWidget(_partSelection);
0258 
0259     _stackDock = new QDockWidget(this);
0260     _stackDock->setObjectName(QStringLiteral("stack dock"));
0261     // Why is the caption only correct with a close button?
0262     _stackSelection = new StackSelection(_stackDock);
0263     _stackDock->setWidget(_stackSelection);
0264     _stackDock->setWindowTitle(i18n("Top Cost Call Stack"));
0265     _stackSelection->setWhatsThis( i18n(
0266                                        "<b>The Top Cost Call Stack</b>"
0267                                        "<p>This is a purely fictional 'most probable' call stack. "
0268                                        "It is built up by starting with the current selected "
0269                                        "function and adds the callers/callees with highest cost "
0270                                        "at the top and to bottom.</p>"
0271                                        "<p>The <b>Cost</b> and <b>Calls</b> columns show the "
0272                                        "cost used for all calls from the function in the line "
0273                                        "above.</p>"));
0274 
0275     connect(_stackSelection, SIGNAL(functionSelected(CostItem*)),
0276             this, SLOT(setTraceItemDelayed(CostItem*)));
0277 
0278     _functionDock = new QDockWidget(this);
0279     _functionDock->setObjectName(QStringLiteral("function dock"));
0280     _functionDock->setWindowTitle(i18n("Flat Profile"));
0281     _functionSelection = new FunctionSelection(this, _functionDock);
0282     _functionDock->setWidget(_functionSelection);
0283 
0284 #if ENABLE_DUMPDOCK
0285     _dumpDock = new QDockWidget(this);
0286     _dumpDock->setWindowTitle(i18n("Profile Dumps"));
0287     _dumpSelection = new DumpSelection(this, _dumpDock,
0288                                        "dumpSelection");
0289     _dumpSelection->setTopLevel(this);
0290 
0291     _dumpDock->setWidget(_dumpSelection);
0292     _dumpSelection->setWhatsThis( i18n(
0293                                       "<b>Profile Dumps</b>"
0294                                       "<p>This dockable shows in the top part the list of "
0295                                       "loadable profile dumps in all subdirectories of: "
0296                                       "<ul><li>current working directory of KCachegrind, "
0297                                       "i.e. where it was started from, and </li>"
0298                                       "<li>the default profile dump directory given in the "
0299                                       "configuration.</li></ul> "
0300                                       "The list is sorted according to the target command "
0301                                       "profiled in the corresponding dump.</p>"
0302                                       "<p>On selecting a profile dump, information for it "
0303                                       "is shown in the bottom area of the dockable: "
0304                                       "<ul><li><b>Options</b> allows you to view the profiled "
0305                                       "command and profile options of this dump. By changing "
0306                                       "any item, a new (yet unexisting) profile template "
0307                                       "is created. Press <b>Run Profile</b> to start a "
0308                                       "profile run with these options in the background. </li>"
0309                                       "<li><b>Info</b> gives detailed info on the selected "
0310                                       "dump like event cost summary and properties of the "
0311                                       "simulated cache. </li>"
0312                                       "<li><b>State</b> is only available for current happening "
0313                                       "profiles runs. Press <b>Update</b> to see different "
0314                                       "counters of the run, and a stack trace of the current "
0315                                       "position in the program profiled. Check the <b>Every</b> "
0316                                       "option to let KCachegrind regularly poll these data. "
0317                                       "Check the <b>Sync</b> option to let the dockable activate "
0318                                       "the top function in the current loaded dump.</li></ul></p>"));
0319 #endif
0320 
0321     // default positions, will be adjusted automatically by stored state in config
0322     addDockWidget(Qt::LeftDockWidgetArea, _partDock );
0323     addDockWidget(Qt::LeftDockWidgetArea, _stackDock );
0324     addDockWidget(Qt::LeftDockWidgetArea, _functionDock );
0325     _stackDock->hide();
0326     _partDock->hide();
0327 
0328 #if ENABLE_DUMPDOCK
0329     addDockWidget( Qt::LeftDockWidgetArea, _dumpDock );
0330     _dumpDock->hide();
0331 #endif
0332 
0333     KConfigGroup dockConfig(KSharedConfig::openConfig(), "Docks");
0334     _forcePartDock = dockConfig.readEntry("ForcePartDockVisible", false);
0335 }
0336 
0337 
0338 TopLevel::~TopLevel()
0339 {
0340     delete _data;
0341 }
0342 
0343 
0344 void TopLevel::saveProperties(KConfigGroup & c)
0345 {
0346     if ( _data )
0347         c.writeEntry("TraceName", _data->traceName());
0348 }
0349 
0350 void TopLevel::readProperties(const KConfigGroup &c)
0351 {
0352     QString traceName = c.readEntry("TraceName");
0353     if (!traceName.isEmpty()) {
0354         openDataFile(traceName);
0355     }
0356 }
0357 
0358 void TopLevel::createLayoutActions()
0359 {
0360     QString hint;
0361     QAction* action;
0362 
0363     action = actionCollection()->addAction( QStringLiteral("layout_duplicate") );
0364     action->setText( i18n( "&Duplicate" ) );
0365     connect(action, &QAction::triggered, this, &TopLevel::layoutDuplicate);
0366     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Plus));
0367     hint = i18n("<b>Duplicate Current Layout</b>"
0368                 "<p>Make a copy of the current layout.</p>");
0369     action->setWhatsThis( hint );
0370 
0371     action = actionCollection()->addAction( QStringLiteral("layout_remove") );
0372     action->setText( i18n( "&Remove" ) );
0373     connect(action, &QAction::triggered, this, &TopLevel::layoutRemove);
0374     hint = i18n("<b>Remove Current Layout</b>"
0375                 "<p>Delete current layout and make the previous active.</p>");
0376     action->setWhatsThis( hint );
0377 
0378     action = actionCollection()->addAction( QStringLiteral("layout_next") );
0379     action->setText( i18n( "&Go to Next" ) );
0380     connect(action, &QAction::triggered, this, &TopLevel::layoutNext);
0381     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Right));
0382     hint = i18n("Go to Next Layout");
0383     action->setWhatsThis( hint );
0384 
0385     action = actionCollection()->addAction( QStringLiteral("layout_previous") );
0386     action->setText( i18n( "&Go to Previous" ) );
0387     connect(action, &QAction::triggered, this, &TopLevel::layoutPrevious);
0388     actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL + Qt::Key_Left));
0389     hint = i18n("Go to Previous Layout");
0390     action->setWhatsThis( hint );
0391 
0392     action = actionCollection()->addAction( QStringLiteral("layout_restore") );
0393     action->setText( i18n( "&Restore to Default" ) );
0394     connect(action, &QAction::triggered, this, &TopLevel::layoutRestore);
0395     hint = i18n("Restore Layouts to Default");
0396     action->setWhatsThis( hint );
0397 
0398     action = actionCollection()->addAction( QStringLiteral("layout_save") );
0399     action->setText( i18n( "&Save as Default" ) );
0400     connect(action, &QAction::triggered, this, &TopLevel::layoutSave);
0401     hint = i18n("Save Layouts as Default");
0402     action->setWhatsThis( hint );
0403 }
0404 
0405 // TODO: split this up...
0406 void TopLevel::createMiscActions()
0407 {
0408     QString hint;
0409     QAction* action;
0410 
0411     action = KStandardAction::openNew(this, SLOT(newWindow()), actionCollection());
0412     hint = i18n("<b>New</b><p>Open new empty KCachegrind window.</p>");
0413     action->setWhatsThis( hint );
0414 
0415     action = actionCollection()->addAction( QStringLiteral("file_add") );
0416     action->setText( i18n( "&Add..." ) );
0417     connect(action, SIGNAL(triggered(bool)), SLOT(add()));
0418     hint = i18n("<b>Add Profile Data</b>"
0419                 "<p>This opens an additional profile data file in the current window.</p>");
0420     action->setWhatsThis( hint );
0421 
0422     action = actionCollection()->addAction( QStringLiteral("reload") );
0423     action->setIcon( QIcon::fromTheme(QStringLiteral("view-refresh")) );
0424     action->setText( i18nc("Reload a document", "&Reload" ) );
0425     connect(action, &QAction::triggered, this, &TopLevel::reload);
0426     actionCollection()->setDefaultShortcut(action, KStandardShortcut::Reload);
0427     hint = i18n("<b>Reload Profile Data</b>"
0428                 "<p>This loads any new created parts, too.</p>");
0429     action->setWhatsThis( hint );
0430 
0431     action = actionCollection()->addAction( QStringLiteral("export") );
0432     action->setText( i18n( "&Export Graph" ) );
0433     connect(action, &QAction::triggered, this, &TopLevel::exportGraph);
0434 
0435     hint = i18n("<b>Export Call Graph</b>"
0436                 "<p>Generates a file with extension .dot for the tools "
0437                 "of the GraphViz package.</p>");
0438     action->setWhatsThis( hint );
0439 
0440 
0441     _taDump = actionCollection()->add<KToggleAction>( QStringLiteral("dump") );
0442     _taDump->setIcon( QIcon::fromTheme(QStringLiteral("edit-redo")) );
0443     _taDump->setText( i18n( "&Force Dump" ) );
0444     connect(_taDump, &QAction::triggered, this, &TopLevel::forceTrace);
0445     actionCollection()->setDefaultShortcut(_taDump, QKeySequence::Undo);
0446     hint = i18n("<b>Force Dump</b>"
0447                 "<p>This forces a dump for a Callgrind profile run "
0448                 "in the current directory. This action is checked while "
0449                 "KCachegrind looks for the dump. If the dump is "
0450                 "finished, it automatically reloads the current trace. "
0451                 "If this is the one from the running Callgrind, the new "
0452                 "created trace part will be loaded, too.</p>"
0453                 "<p>Force dump creates a file 'callgrind.cmd', and "
0454                 "checks every second for its existence. A running "
0455                 "Callgrind will detect this file, dump a trace part, "
0456                 "and delete 'callgrind.cmd'. "
0457                 "The deletion is detected by KCachegrind, "
0458                 "and it does a Reload. If there is <em>no</em> Callgrind "
0459                 "running, press 'Force Dump' again to cancel the dump "
0460                 "request. This deletes 'callgrind.cmd' itself and "
0461                 "stops polling for a new dump.</p>"
0462                 "<p>Note: A Callgrind run <em>only</em> detects "
0463                 "existence of 'callgrind.cmd' when actively running "
0464                 "a few milliseconds, i.e. "
0465                 "<em>not</em> sleeping. Tip: For a profiled GUI program, "
0466                 "you can awake Callgrind e.g. by resizing a window "
0467                 "of the program.</p>");
0468     _taDump->setWhatsThis( hint );
0469 
0470     action = KStandardAction::open(this, SLOT(load()), actionCollection());
0471     hint = i18n("<b>Open Profile Data</b>"
0472                 "<p>This opens a profile data file, with possible multiple parts</p>");
0473     action->setToolTip( hint );
0474     action->setWhatsThis( hint );
0475 
0476     _openRecent = KStandardAction::openRecent(this, SLOT(load(QUrl)),
0477                                               actionCollection());
0478 
0479     KStandardAction::showStatusbar(this,
0480                                    SLOT(toggleStatusBar()), actionCollection());
0481 
0482     _partDockShown = actionCollection()->add<KToggleAction>(QStringLiteral("settings_show_partdock"));
0483     _partDockShown->setText(i18n("Parts Overview"));
0484     connect(_partDockShown, &QAction::triggered, this, &TopLevel::togglePartDock);
0485 
0486     hint = i18n("Show/Hide the Parts Overview Dockable");
0487     _partDockShown->setToolTip( hint );
0488     _partDockShown->setWhatsThis( hint );
0489 
0490     _stackDockShown = actionCollection()->add<KToggleAction>(QStringLiteral("settings_show_stackdock"));
0491     _stackDockShown->setText(i18n("Call Stack"));
0492     connect(_stackDockShown, &QAction::triggered, this, &TopLevel::toggleStackDock);
0493 
0494     hint = i18n("Show/Hide the Call Stack Dockable");
0495     _stackDockShown->setToolTip( hint );
0496     _stackDockShown->setWhatsThis( hint );
0497 
0498     _functionDockShown = actionCollection()->add<KToggleAction>(QStringLiteral("settings_show_profiledock"));
0499     _functionDockShown->setText(i18n("Function Profile"));
0500     connect(_functionDockShown, &QAction::triggered, this, &TopLevel::toggleFunctionDock);
0501 
0502     hint = i18n("Show/Hide the Function Profile Dockable");
0503     _functionDockShown->setToolTip( hint );
0504     _functionDockShown->setWhatsThis( hint );
0505 
0506 #if ENABLE_DUMPDOCK
0507     _dumpDockShown = actionCollection()->add<KToggleAction>("settings_show_dumpdock",
0508                                                             this, SLOT(toggleDumpDock()));
0509     _dumpDockShown->setText(i18n("Profile Dumps"));
0510     hint = i18n("Show/Hide the Profile Dumps Dockable");
0511     _dumpDockShown->setToolTip( hint );
0512     _dumpDockShown->setWhatsThis( hint );
0513 #endif
0514 
0515     _taPercentage = actionCollection()->add<KToggleAction>(QStringLiteral("view_percentage"));
0516     _taPercentage->setIcon(QIcon::fromTheme(QStringLiteral("percent")));
0517     _taPercentage->setText(i18n("Relative"));
0518     connect(_taPercentage, &QAction::triggered, this, &TopLevel::togglePercentage);
0519     hint = i18n("Show relative instead of absolute costs");
0520     _taPercentage->setToolTip( hint );
0521     _taPercentage->setWhatsThis( hint );
0522 
0523     _taExpanded = actionCollection()->add<KToggleAction>(QStringLiteral("view_expanded"));
0524     _taExpanded->setIcon(QIcon::fromTheme(QStringLiteral("move")));
0525     _taExpanded->setText(i18n("Relative to Parent"));
0526     connect(_taExpanded, &QAction::triggered, this, &TopLevel::toggleExpanded);
0527 
0528     hint = i18n("Show percentage costs relative to parent");
0529     _taExpanded->setToolTip( hint );
0530     _taExpanded->setWhatsThis( hint );
0531 
0532     hint = i18n("<b>Show percentage costs relative to parent</b>"
0533                 "<p>If this is switched off, percentage costs are always shown "
0534                 "relative to the total cost of the profile part(s) that are "
0535                 "currently browsed. By turning on this option, percentage cost "
0536                 "of shown cost items will be relative to the parent cost item.</p>"
0537                 "<ul><table>"
0538                 "<tr><td><b>Cost Type</b></td><td><b>Parent Cost</b></td></tr>"
0539                 "<tr><td>Function Cumulative</td><td>Total</td></tr>"
0540                 "<tr><td>Function Self</td><td>Function Group (*) / Total</td></tr>"
0541                 "<tr><td>Call</td><td>Function Inclusive</td></tr>"
0542                 "<tr><td>Source Line</td><td>Function Inclusive</td></tr>"
0543                 "</table></ul>"
0544                 "<p>(*) Only if function grouping is switched on (e.g. ELF object grouping).</p>");
0545     _taExpanded->setWhatsThis( hint );
0546 
0547     _taCycles = actionCollection()->add<KToggleAction>(QStringLiteral("view_cycles"));
0548     _taCycles->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo")));
0549     _taCycles->setText(i18n( "Cycle Detection" ));
0550     connect(_taCycles, &QAction::triggered, this, &TopLevel::toggleCycles);
0551 
0552     hint = i18n("<b>Detect recursive cycles</b>"
0553                 "<p>If this is switched off, the treemap drawing will show "
0554                 "black areas when a recursive call is made instead of drawing the "
0555                 "recursion ad infinitum. Note that "
0556                 "the size of black areas often will be wrong, as inside recursive "
0557                 "cycles the cost of calls cannot be determined; the error is small, "
0558                 "however, for false cycles (see documentation).</p>"
0559                 "<p>The correct handling for cycles is to detect them and collapse all "
0560                 "functions of a cycle into an artificial function, which is done when this "
0561                 "option is selected. Unfortunately, with GUI applications, this often will "
0562                 "lead to huge false cycles, making the analysis impossible; therefore, there "
0563                 "is the option to switch this off.</p>");
0564     _taCycles->setWhatsThis( hint );
0565 
0566     _taHideTemplates = actionCollection()->add<KToggleAction>(QStringLiteral("hide_templates"));
0567     _taHideTemplates->setIcon(QIcon::fromTheme(QStringLiteral("hidetemplates")));
0568     _taHideTemplates->setText(i18n( "Shorten Templates" ));
0569     connect(_taHideTemplates, &QAction::triggered, this, &TopLevel::toggleHideTemplates);
0570     _taHideTemplates->setToolTip(i18n( "Hide Template Parameters in C++ Symbols" ));
0571     hint = i18n("<b>Hide Template Parameters in C++ Symbols</b>"
0572                 "<p>If this is switched on, every symbol displayed will have "
0573                 "any C++ template parameters hidden, just showing &lt;&gt; "
0574                 "instead of a potentially nested template parameter.</p>"
0575                 "<p>In this mode, you can hover the mouse pointer over the "
0576                 "activated symbol label to show a tooltip with the "
0577                 "unabbreviated symbol.</p>");
0578     _taHideTemplates->setWhatsThis(hint);
0579 
0580     KStandardAction::quit(this, SLOT(close()), actionCollection());
0581     KStandardAction::preferences(this, SLOT(configure()), actionCollection());
0582     KStandardAction::keyBindings(this, SLOT(configureKeys()), actionCollection());
0583     KStandardAction::configureToolbars(this,SLOT(configureToolbars()),
0584                                        actionCollection());
0585 
0586     _paUp = new KToolBarPopupAction( QIcon::fromTheme( QStringLiteral("go-up") ),
0587                                      i18n( "&Up" ), this );
0588     connect( _paUp, &QAction::triggered,
0589              _stackSelection, &StackSelection::browserUp );
0590     actionCollection()->addAction( QStringLiteral("go_up"), _paUp );
0591     actionCollection()->setDefaultShortcut(_paUp, QKeySequence(Qt::ALT + Qt::Key_Up));
0592     connect( _paUp->popupMenu(), &QMenu::aboutToShow,
0593              this, &TopLevel::upAboutToShow );
0594     connect( _paUp->popupMenu(), &QMenu::triggered,
0595              this, &TopLevel::upTriggered );
0596     hint = i18n("<b>Go Up</b>"
0597                 "<p>Go to last selected caller of current function. "
0598                 "If no caller was visited, use that with highest cost.</p>");
0599     _paUp->setToolTip( hint );
0600     _paUp->setWhatsThis( hint );
0601 
0602     QPair< KGuiItem, KGuiItem > backForward = KStandardGuiItem::backAndForward();
0603     _paBack = new KToolBarPopupAction( backForward.first.icon(),
0604                                        backForward.first.text(), this );
0605     connect( _paBack, &QAction::triggered,
0606              _stackSelection, &StackSelection::browserBack );
0607     actionCollection()->addAction( QStringLiteral("go_back"), _paBack );
0608     actionCollection()->setDefaultShortcut(_paBack, QKeySequence(Qt::ALT + Qt::Key_Left));
0609     connect( _paBack->popupMenu(), &QMenu::aboutToShow,
0610              this, &TopLevel::backAboutToShow );
0611     connect( _paBack->popupMenu(), &QMenu::triggered,
0612              this, &TopLevel::backTriggered );
0613     hint = i18n("Go back in function selection history");
0614     _paBack->setToolTip( hint );
0615     _paBack->setWhatsThis( hint );
0616 
0617     _paForward = new KToolBarPopupAction( backForward.second.icon(),
0618                                           backForward.second.text(), this );
0619     connect( _paForward, &QAction::triggered,
0620              _stackSelection, &StackSelection::browserForward );
0621     actionCollection()->addAction( QStringLiteral("go_forward"), _paForward );
0622     actionCollection()->setDefaultShortcut(_paForward, QKeySequence(Qt::ALT + Qt::Key_Right));
0623     connect( _paForward->popupMenu(), &QMenu::aboutToShow,
0624              this, &TopLevel::forwardAboutToShow );
0625     connect( _paForward->popupMenu(), &QMenu::triggered,
0626              this, &TopLevel::forwardTriggered );
0627     hint = i18n("Go forward in function selection history");
0628     _paForward->setToolTip( hint );
0629     _paForward->setWhatsThis( hint );
0630 
0631     _saCost = actionCollection()->add<KSelectAction>(QStringLiteral("view_cost_type"));
0632     _saCost->setText(i18n("Primary Event Type"));
0633     hint = i18n("Select primary event type of costs");
0634     _saCost->setComboWidth(300);
0635     _saCost->setToolTip( hint );
0636     _saCost->setWhatsThis( hint );
0637 
0638     // This is needed because for KDE4, "_saCost->setComboWidth(300);" seems to
0639     // have no effect. Instead, list box entry widths are used to determine the
0640     // combobox width. However, at KCachegrind startup, we do not have yet
0641     // a list of event types, as this depends on the profile data.
0642     // In KDE 4.2, we used a translatable string, which did not really work as
0643     // the semantic is not known to translators. Instead, we use a
0644     // nontranslatable string now...
0645     QStringList dummyItems;
0646     dummyItems << QStringLiteral("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX");
0647     _saCost->setItems(dummyItems);
0648 
0649     // cost types are dependent on loaded data, thus KSelectAction
0650     // is filled in setData()
0651     connect( _saCost, &KSelectAction::textTriggered,
0652              this, &TopLevel::eventTypeSelected);
0653 
0654     _saCost2 = actionCollection()->add<KSelectAction>(QStringLiteral("view_cost_type2"));
0655     _saCost2->setText(i18n("Secondary Event Type"));
0656     hint = i18n("Select secondary event type for cost e.g. shown in annotations");
0657     _saCost2->setComboWidth(300);
0658     _saCost2->setToolTip( hint );
0659     _saCost2->setWhatsThis( hint );
0660     _saCost2->setItems(dummyItems);
0661     connect( _saCost2, &KSelectAction::textTriggered,
0662              this, &TopLevel::eventTypeSelected);
0663 
0664     saGroup = actionCollection()->add<KSelectAction>(QStringLiteral("view_group_type"));
0665     saGroup->setText(i18n("Grouping"));
0666 
0667     hint = i18n("Select how functions are grouped into higher level cost items");
0668     saGroup->setToolTip( hint );
0669     saGroup->setWhatsThis( hint );
0670 
0671     QStringList args;
0672 
0673     args << i18n("(No Grouping)")
0674          << ProfileContext::i18nTypeName(ProfileContext::Object)
0675          << ProfileContext::i18nTypeName(ProfileContext::File)
0676          << ProfileContext::i18nTypeName(ProfileContext::Class)
0677          << ProfileContext::i18nTypeName(ProfileContext::FunctionCycle);
0678 
0679     saGroup->setItems(args);
0680     connect( saGroup, &KSelectAction::indexTriggered,
0681              this, &TopLevel::groupTypeSelected);
0682 
0683     _taSplit = actionCollection()->add<KToggleAction>(QStringLiteral("view_split"));
0684     _taSplit->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right")));
0685     _taSplit->setText(i18n("Split"));
0686     connect(_taSplit, &QAction::triggered, this, &TopLevel::splitSlot);
0687 
0688     hint = i18n("Show two information panels");
0689     _taSplit->setToolTip( hint );
0690     _taSplit->setWhatsThis( hint );
0691 
0692     _taSplitDir = actionCollection()->add<KToggleAction>(QStringLiteral("view_split_dir"));
0693     _taSplitDir->setIcon(QIcon::fromTheme(QStringLiteral("view-split-left-right")));
0694     _taSplitDir->setText(i18n("Split Horizontal"));
0695     connect(_taSplitDir, &QAction::triggered, this, &TopLevel::splitDirSlot);
0696 
0697     hint = i18n("Change Split Orientation when main window is split.");
0698     _taSplitDir->setToolTip( hint );
0699     _taSplitDir->setWhatsThis( hint );
0700 }
0701 
0702 void TopLevel::createActions()
0703 {
0704     createMiscActions();
0705     createLayoutActions();
0706 }
0707 
0708 void TopLevel::toggleStatusBar()
0709 {
0710     if (statusBar()->isVisible())
0711         statusBar()->hide();
0712     else
0713         statusBar()->show();
0714 }
0715 
0716 void TopLevel::togglePartDock()
0717 {
0718     if (!_partDock->isVisible())
0719         _partDock->show();
0720     else
0721         _partDock->hide();
0722 }
0723 
0724 void TopLevel::toggleStackDock()
0725 {
0726     if (!_stackDock->isVisible())
0727         _stackDock->show();
0728     else
0729         _stackDock->hide();
0730 }
0731 
0732 void TopLevel::toggleDumpDock()
0733 {
0734 #if ENABLE_DUMPDOCK
0735     if (!_dumpDock->isVisible())
0736         _dumpDock->show();
0737     else
0738         _dumpDock->hide();
0739 #endif
0740 }
0741 
0742 void TopLevel::toggleFunctionDock()
0743 {
0744     if (!_functionDock->isVisible())
0745         _functionDock->show();
0746     else
0747         _functionDock->hide();
0748 }
0749 
0750 void TopLevel::togglePercentage()
0751 {
0752     setPercentage(_taPercentage->isChecked());
0753 }
0754 
0755 void TopLevel::setAbsoluteCost()
0756 {
0757     setPercentage(false);
0758 }
0759 
0760 void TopLevel::setRelativeCost()
0761 {
0762     setPercentage(true);
0763 }
0764 
0765 void TopLevel::updateViewsOnChange(int change)
0766 {
0767     _partSelection->notifyChange(change);
0768     _functionSelection->notifyChange(change);
0769     _multiView->notifyChange(change);
0770 }
0771 
0772 void TopLevel::setPercentage(bool show)
0773 {
0774     if (_showPercentage == show) return;
0775     _showPercentage = show;
0776     if (_taPercentage->isChecked() != show)
0777         _taPercentage->setChecked(show);
0778 
0779     GlobalConfig::setShowPercentage(_showPercentage);
0780 
0781     _stackSelection->refresh();
0782 
0783     updateViewsOnChange(TraceItemView::configChanged);
0784 }
0785 
0786 void TopLevel::toggleExpanded()
0787 {
0788     bool show = _taExpanded->isChecked();
0789     if (_showExpanded == show) return;
0790     _showExpanded = show;
0791 
0792     GlobalConfig::setShowExpanded(_showExpanded);
0793 
0794     _stackSelection->refresh();
0795 
0796     updateViewsOnChange(TraceItemView::configChanged);
0797 }
0798 
0799 void TopLevel::toggleCycles()
0800 {
0801     bool show = _taCycles->isChecked();
0802     if (_showCycles == show) return;
0803     _showCycles = show;
0804 
0805     GlobalConfig::setShowCycles(_showCycles);
0806 
0807     if (!_data) return;
0808 
0809     _data->invalidateDynamicCost();
0810     _data->updateFunctionCycles();
0811 
0812     _stackSelection->rebuildStackList();
0813 
0814     updateViewsOnChange(TraceItemView::configChanged);
0815 }
0816 
0817 void TopLevel::toggleHideTemplates()
0818 {
0819     bool b = _taHideTemplates->isChecked();
0820     if (_hideTemplates == b) return;
0821     _hideTemplates = b;
0822 
0823     GlobalConfig::setHideTemplates(_hideTemplates);
0824 
0825     _stackSelection->refresh();
0826 
0827     updateViewsOnChange(TraceItemView::configChanged);
0828 }
0829 
0830 void TopLevel::partVisibilityChanged(bool v)
0831 {
0832     _partDockShown->setChecked(v);
0833 }
0834 
0835 void TopLevel::stackVisibilityChanged(bool v)
0836 {
0837     _stackDockShown->setChecked(v);
0838 }
0839 
0840 #if ENABLE_DUMPDOCK
0841 void TopLevel::dumpVisibilityChanged(bool v)
0842 #else
0843 void TopLevel::dumpVisibilityChanged(bool)
0844 #endif
0845 {
0846 #if ENABLE_DUMPDOCK
0847     _dumpDockShown->setChecked(v);
0848 #endif
0849 }
0850 
0851 void TopLevel::functionVisibilityChanged(bool v)
0852 {
0853     _functionDockShown->setChecked(v);
0854     if (v)
0855         _functionSelection->updateView();
0856 }
0857 
0858 
0859 void TopLevel::querySlot()
0860 {
0861     _functionSelection->query(queryLineEdit->text());
0862 }
0863 
0864 void TopLevel::configureKeys()
0865 {
0866     KShortcutsDialog::showDialog(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this);
0867 }
0868 
0869 
0870 void TopLevel::configureToolbars()
0871 {
0872     KEditToolBar *dlg = new KEditToolBar(guiFactory(), this);
0873 
0874     if (dlg->exec())
0875         createGUI();
0876 
0877     delete dlg;
0878 }
0879 
0880 
0881 void TopLevel::newWindow()
0882 {
0883     TopLevel* t = new TopLevel();
0884     t->show();
0885 }
0886 
0887 
0888 void TopLevel::load()
0889 {
0890     QUrl url = QFileDialog::getOpenFileUrl(this,
0891                                            i18n("Select Callgrind Profile Data"),
0892                                            QUrl(),
0893                                            i18n("Callgrind Profile Data (cachegrind.out* callgrind.out*);;All Files (*)"));
0894 
0895     load(url);
0896 }
0897 
0898 void TopLevel::load(const QUrl& url)
0899 {
0900     if (url.isEmpty()) return;
0901 
0902     QString tmpFileName;
0903     QTemporaryFile tmpFile;
0904     if (url.isLocalFile()) {
0905         tmpFileName = url.toLocalFile();
0906     }
0907     else if (tmpFile.open()){
0908         // network transparency
0909         tmpFileName = tmpFile.fileName();
0910         KIO::FileCopyJob *job = KIO::file_copy(url,
0911                                                QUrl::fromLocalFile(tmpFileName));
0912         KJobWidgets::setWindow(job, this);
0913         job->exec();
0914     }
0915     if (!tmpFileName.isEmpty()) {
0916         _openRecent->addUrl(url);
0917         _openRecent->saveEntries( KConfigGroup( KSharedConfig::openConfig(), QString() ) );
0918 
0919         load(tmpFileName);
0920     } else {
0921         KMessageBox::error(this, i18n("Could not open the file \"%1\". "
0922                                       "Check it exists and you have enough "
0923                                       "permissions to read it.",
0924                                       url.toDisplayString()));
0925     }
0926 }
0927 
0928 /* if file name is ".": load first file found in current directory, but do
0929  * not show an error message if nothing could be loaded
0930  */
0931 void TopLevel::load(QString file)
0932 {
0933     if (file.isEmpty()) return;
0934 
0935     bool showError = true;
0936     if (file == QLatin1Char('.'))
0937         showError = false;
0938 
0939     if (_data && _data->parts().count()>0) {
0940 
0941         // In new window
0942         TopLevel* t = new TopLevel();
0943         t->show();
0944         t->loadDelayed(file);
0945         return;
0946     }
0947 
0948     bool loaded = openDataFile(file);
0949     if (!loaded && showError)
0950         KMessageBox::error(this, i18n("Could not open the file \"%1\". "
0951                                       "Check it exists and you have enough "
0952                                       "permissions to read it.", file));
0953 }
0954 
0955 
0956 void TopLevel::add()
0957 {
0958     QUrl url = QFileDialog::getOpenFileUrl(this,
0959                                            i18n("Add Callgrind Profile Data"),
0960                                            QUrl(),
0961                                            i18n("Callgrind Profile Data (cachegrind.out* callgrind.out*);;All Files (*)"));
0962 
0963     add(url);
0964 }
0965 
0966 void TopLevel::add(const QUrl &url)
0967 {
0968     if (url.isEmpty()) return;
0969 
0970     QString tmpFileName;
0971     QTemporaryFile tmpFile;
0972     if (url.isLocalFile()) {
0973         tmpFileName = url.toLocalFile();
0974     }
0975     else if (tmpFile.open()){
0976         // network transparency
0977         tmpFileName = tmpFile.fileName();
0978         KIO::FileCopyJob *job = KIO::file_copy(url,
0979                                                QUrl::fromLocalFile(tmpFileName));
0980         KJobWidgets::setWindow(job, this);
0981         job->exec();
0982     }
0983     if (!tmpFileName.isEmpty()) {
0984         _openRecent->addUrl(url);
0985         _openRecent->saveEntries( KSharedConfig::openConfig()->group( QString() ) );
0986 
0987         add(tmpFileName);
0988     }
0989 }
0990 
0991 void TopLevel::add(QString file)
0992 {
0993     if (file.isEmpty()) return;
0994 
0995     if (_data) {
0996         _data->load(file);
0997 
0998         // GUI update for added data
0999         configChanged();
1000         return;
1001     }
1002 
1003     openDataFile(file);
1004 }
1005 
1006 
1007 
1008 void TopLevel::loadDelayed(QString file)
1009 {
1010     _loadFilesDelayed << file;
1011     QTimer::singleShot(0, this, &TopLevel::loadTraceDelayed);
1012 }
1013 
1014 void TopLevel::loadDelayed(QStringList files)
1015 {
1016     _loadFilesDelayed << files;
1017     QTimer::singleShot(0, this, &TopLevel::loadTraceDelayed);
1018 }
1019 
1020 void TopLevel::loadTraceDelayed()
1021 {
1022     if (_loadFilesDelayed.isEmpty()) return;
1023 
1024     if (_loadFilesDelayed.count()>1) {
1025         // FIXME: we expect all files to be local and existing
1026         TraceData* d = new TraceData(this);
1027         d->load(_loadFilesDelayed);
1028         setData(d);
1029     }
1030     else {
1031         QString file = _loadFilesDelayed[0];
1032 
1033         // if URL scheme is missing (URL is relative), this is a local file
1034         QUrl u = QUrl::fromUserInput(file, QDir::currentPath(), QUrl::AssumeLocalFile);
1035         if (u.isLocalFile())
1036             load(file); // special case for local file: maybe just prefix
1037         else
1038             load(u);
1039     }
1040     _loadFilesDelayed.clear();
1041 }
1042 
1043 
1044 void TopLevel::reload()
1045 {
1046     QString trace;
1047     if (!_data || _data->parts().isEmpty())
1048         trace = QStringLiteral("."); // open first trace found in dir
1049     else
1050         trace = _data->traceName();
1051 
1052     // this also keeps sure we have the same browsing position...
1053     openDataFile(trace);
1054 }
1055 
1056 void TopLevel::exportGraph()
1057 {
1058     if (!_data || !_function) return;
1059 
1060     GraphExporter::savePrompt(this, _data, _function, _eventType, _groupType, nullptr);
1061 }
1062 
1063 
1064 bool TopLevel::setEventType(QString s)
1065 {
1066     EventType* ct;
1067 
1068     ct = (_data) ? _data->eventTypes()->type(s) : nullptr;
1069 
1070     // if costtype with given name not found, use first available
1071     if (!ct && _data) ct = _data->eventTypes()->type(0);
1072 
1073     return setEventType(ct);
1074 }
1075 
1076 bool TopLevel::setEventType2(QString s)
1077 {
1078     EventType* ct;
1079 
1080     // Special type i18n("(Hidden)") gives 0
1081     ct = (_data) ? _data->eventTypes()->type(s) : nullptr;
1082 
1083     return setEventType2(ct);
1084 }
1085 
1086 void TopLevel::eventTypeSelected(const QString& s)
1087 {
1088     EventType* ct;
1089 
1090     ct = (_data) ? _data->eventTypes()->typeForLong(s) : nullptr;
1091     setEventType(ct);
1092 }
1093 
1094 void TopLevel::eventType2Selected(const QString& s)
1095 {
1096     EventType* ct;
1097 
1098     ct = (_data) ? _data->eventTypes()->typeForLong(s) : nullptr;
1099     setEventType2(ct);
1100 }
1101 
1102 bool TopLevel::setEventType(EventType* ct)
1103 {
1104     if (_eventType == ct) return false;
1105     _eventType = ct;
1106 
1107     if (ct) {
1108         QStringList l = _saCost->items();
1109         int idx = l.indexOf(ct->longName());
1110         if (idx >= 0)
1111             _saCost->setCurrentItem(idx);
1112     }
1113 
1114     _partSelection->setEventType(_eventType);
1115     _stackSelection->setEventType(_eventType);
1116     _functionSelection->setEventType(_eventType);
1117     _multiView->setEventType(_eventType);
1118 
1119     updateStatusBar();
1120 
1121     return true;
1122 }
1123 
1124 bool TopLevel::setEventType2(EventType* ct)
1125 {
1126     if (_eventType2 == ct) return false;
1127     _eventType2 = ct;
1128 
1129     QString longName = ct ? ct->longName() : i18n("(Hidden)");
1130     QStringList l = _saCost2->items();
1131     int idx = l.indexOf(longName);
1132     if (idx >= 0)
1133         _saCost2->setCurrentItem(idx);
1134 
1135     _partSelection->setEventType2(_eventType2);
1136     _stackSelection->setEventType2(_eventType2);
1137     _functionSelection->setEventType2(_eventType2);
1138     _multiView->setEventType2(_eventType2);
1139 
1140     updateStatusBar();
1141 
1142     return true;
1143 }
1144 
1145 
1146 void TopLevel::groupTypeSelected(int cg)
1147 {
1148     switch(cg) {
1149     case 0: setGroupType( ProfileContext::Function ); break;
1150     case 1: setGroupType( ProfileContext::Object ); break;
1151     case 2: setGroupType( ProfileContext::File ); break;
1152     case 3: setGroupType( ProfileContext::Class ); break;
1153     case 4: setGroupType( ProfileContext::FunctionCycle ); break;
1154     default: break;
1155     }
1156 }
1157 
1158 bool TopLevel::setGroupType(QString s)
1159 {
1160     ProfileContext::Type gt;
1161 
1162     gt = ProfileContext::type(s);
1163     // only allow Function/Object/File/Class as grouptype
1164     switch(gt) {
1165     case ProfileContext::Object:
1166     case ProfileContext::File:
1167     case ProfileContext::Class:
1168     case ProfileContext::FunctionCycle:
1169         break;
1170     default:
1171         gt = ProfileContext::Function;
1172     }
1173 
1174     return setGroupType(gt);
1175 }
1176 
1177 bool TopLevel::setGroupType(ProfileContext::Type gt)
1178 {
1179     if (_groupType == gt) return false;
1180     _groupType = gt;
1181 
1182     int idx = -1;
1183     switch(gt) {
1184     case ProfileContext::Function: idx = 0; break;
1185     case ProfileContext::Object: idx = 1; break;
1186     case ProfileContext::File: idx = 2; break;
1187     case ProfileContext::Class: idx = 3; break;
1188     case ProfileContext::FunctionCycle: idx = 4; break;
1189     default:
1190         break;
1191     }
1192 
1193     if (idx==-1) return false;
1194 
1195     if (saGroup->currentItem() != idx)
1196         saGroup->setCurrentItem(idx);
1197 
1198     _stackSelection->setGroupType(_groupType);
1199     _partSelection->set(_groupType);
1200     _functionSelection->set(_groupType);
1201     _multiView->set(_groupType);
1202 
1203     updateStatusBar();
1204 
1205     return true;
1206 }
1207 
1208 bool TopLevel::setGroup(QString s)
1209 {
1210     TraceCostItem* ci = _functionSelection->group(s);
1211     if (!ci)
1212         return false;
1213 
1214     return setGroup(ci);
1215 }
1216 
1217 
1218 bool TopLevel::setGroup(TraceCostItem* g)
1219 {
1220     if (_group == g) return false;
1221     _group = g;
1222 
1223     _functionSelection->setGroup(g);
1224     updateStatusBar();
1225 
1226     return true;
1227 }
1228 
1229 bool TopLevel::setFunction(QString s)
1230 {
1231     if (!_data) return false;
1232 
1233     ProfileCostArray* f = _data->search(ProfileContext::Function, s, _eventType);
1234     if (!f) return false;
1235 
1236     return setFunction((TraceFunction*)f);
1237 }
1238 
1239 bool TopLevel::setFunction(TraceFunction* f)
1240 {
1241     if (_function == f) return false;
1242     _function = f;
1243 
1244     _multiView->activate(f);
1245     _functionSelection->activate(f);
1246     _partSelection->activate(f);
1247     _stackSelection->setFunction(_function);
1248 
1249     StackBrowser* b = _stackSelection->browser();
1250     if (b) {
1251         // do not disable up: a press forces stack-up extending...
1252         _paForward->setEnabled(b->canGoForward());
1253         _paBack->setEnabled(b->canGoBack());
1254     }
1255 
1256 #if TRACE_UPDATES
1257     qDebug("TopLevel::setFunction(%s), lastSender %s",
1258            f ? f->prettyName().ascii() : "0",
1259            _lastSender ? _lastSender->name() :"0" );
1260 #endif
1261 
1262     return true;
1263 }
1264 
1265 
1266 /**
1267  * Delayed versions.
1268  * We always have a pair of slots: One receiver to start the
1269  * delay with a singleShot Timer. It stores the parameter into a
1270  * temporary variable. And one parameterless slot for
1271  * forwarding, using this temporary.
1272  */
1273 void TopLevel::setEventTypeDelayed(EventType* ct)
1274 {
1275     _eventTypeDelayed = ct;
1276     QTimer::singleShot (0, this, SLOT(setEventTypeDelayed()));
1277 }
1278 
1279 void TopLevel::setEventType2Delayed(EventType* ct)
1280 {
1281     _eventType2Delayed = ct;
1282     QTimer::singleShot (0, this, SLOT(setEventType2Delayed()));
1283 }
1284 
1285 void TopLevel::setEventTypeDelayed()
1286 {
1287     setEventType(_eventTypeDelayed);
1288 }
1289 
1290 void TopLevel::setEventType2Delayed()
1291 {
1292     setEventType2(_eventType2Delayed);
1293 }
1294 
1295 void TopLevel::setGroupTypeDelayed(ProfileContext::Type gt)
1296 {
1297     _groupTypeDelayed = gt;
1298     QTimer::singleShot (0, this, SLOT(setGroupTypeDelayed()));
1299 }
1300 
1301 void TopLevel::setGroupTypeDelayed()
1302 {
1303     setGroupType(_groupTypeDelayed);
1304 }
1305 
1306 void TopLevel::setGroupDelayed(TraceCostItem* g)
1307 {
1308 #if TRACE_UPDATES
1309     qDebug("TopLevel::setGroupDelayed(%s), sender %s",
1310            g ? g->prettyName().ascii() : "0",
1311            _lastSender ? _lastSender->name() :"0" );
1312 #endif
1313 
1314     _groupDelayed = g;
1315     QTimer::singleShot (0, this, SLOT(setGroupDelayed()));
1316 }
1317 
1318 void TopLevel::setGroupDelayed()
1319 {
1320     setGroup(_groupDelayed);
1321 }
1322 
1323 void TopLevel::setDirectionDelayed(TraceItemView::Direction d)
1324 {
1325     _directionDelayed = d;
1326     QTimer::singleShot (0, this, SLOT(setDirectionDelayed()));
1327 }
1328 
1329 void TopLevel::setDirectionDelayed()
1330 {
1331     switch(_directionDelayed) {
1332     case TraceItemView::Back:
1333         _stackSelection->browserBack();
1334         break;
1335 
1336     case TraceItemView::Forward:
1337         _stackSelection->browserForward();
1338         break;
1339 
1340     case TraceItemView::Up:
1341     {
1342         StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr;
1343         HistoryItem* hi = b ? b->current() : nullptr;
1344         TraceFunction* f = hi ? hi->function() : nullptr;
1345 
1346         if (!f) break;
1347         f = hi->stack()->caller(f, false);
1348         if (f) setFunction(f);
1349     }
1350         break;
1351 
1352     default: break;
1353     }
1354 
1355     _directionDelayed = TraceItemView::None;
1356 }
1357 
1358 
1359 void TopLevel::setTraceItemDelayed(CostItem* i)
1360 {
1361     // no need to select same item a 2nd time...
1362     if (_traceItemDelayed == i) return;
1363     _traceItemDelayed = i;
1364     _lastSender = sender();
1365 
1366     qDebug() << "Selected " << (i ? i->prettyName() : QStringLiteral("(none)"));
1367 
1368 #if TRACE_UPDATES
1369     qDebug("TopLevel::setTraceItemDelayed(%s), sender %s",
1370            i ? i->prettyName().ascii() : "0",
1371            _lastSender ? _lastSender->name() :"0" );
1372 #endif
1373 
1374     QTimer::singleShot (0, this, SLOT(setTraceItemDelayed()));
1375 }
1376 
1377 void TopLevel::setTraceItemDelayed()
1378 {
1379     if (!_traceItemDelayed) return;
1380 
1381     switch(_traceItemDelayed->type()) {
1382     case ProfileContext::Function:
1383     case ProfileContext::FunctionCycle:
1384         setFunction((TraceFunction*)_traceItemDelayed);
1385         break;
1386 
1387     case ProfileContext::Object:
1388     case ProfileContext::File:
1389     case ProfileContext::Class:
1390         _multiView->activate(_traceItemDelayed);
1391         break;
1392 
1393 #if 0
1394         // this conflicts with the selection policy of InstrView ?!?
1395     case ProfileContext::Instr:
1396     case ProfileContext::Line:
1397         // only for multiview
1398         _multiView->activate(_traceItemDelayed);
1399         break;
1400 #endif
1401 
1402     default: break;
1403     }
1404 
1405     _traceItemDelayed = nullptr;
1406     _lastSender = nullptr;
1407 }
1408 
1409 /**
1410  * A TraceData object cannot be viewed many times in different
1411  * toplevel windows. Thus, this toplevel window takes ownership
1412  * of the TraceData object: on closing the window or opening
1413  * another trace, the object is destroyed.
1414  */
1415 void TopLevel::setData(TraceData* data)
1416 {
1417     if (data == _data) return;
1418 
1419     _lastSender = nullptr;
1420 
1421     saveTraceSettings();
1422 
1423     if (_data) {
1424         _partSelection->setData(nullptr);
1425         _stackSelection->setData(nullptr);
1426         _functionSelection->setData(nullptr);
1427         _multiView->setData(nullptr);
1428         _multiView->updateView(true);
1429 
1430         // we are the owner...
1431         delete _data;
1432     }
1433 
1434     // reset members
1435     resetState();
1436 
1437     _data = data;
1438 
1439     // fill cost type list
1440     QStringList types;
1441 
1442     if (_data) {
1443         /* add all supported virtual types */
1444         EventTypeSet* m = _data->eventTypes();
1445         m->addKnownDerivedTypes();
1446 
1447         /* first, fill selection list with available cost types */
1448         for (int i=0;i<m->realCount();i++)
1449             types << m->realType(i)->longName();
1450         for (int i=0;i<m->derivedCount();i++)
1451             types << m->derivedType(i)->longName();
1452     }
1453     _saCost->setItems(types);
1454     _saCost->setComboWidth(300);
1455 
1456     if (types.count()>0) {
1457         // second type list gets an additional "(Hidden)"
1458         types.prepend(i18n("(Hidden)"));
1459     }
1460     _saCost2->setItems(types);
1461     _saCost2->setComboWidth(300);
1462     // default is hidden
1463     if (types.count()>0)
1464         _saCost2->setCurrentItem(0);
1465 
1466     _partSelection->setData(_data);
1467     _stackSelection->setData(_data);
1468     _functionSelection->setData(_data);
1469     _multiView->setData(_data);
1470     // Force update of _data in all children of _multiView
1471     // This is needed to make restoring of activeItem work!
1472     _multiView->updateView(true);
1473 
1474     /* this is needed to let the other widgets know the types */
1475     restoreTraceTypes();
1476 
1477     restoreTraceSettings();
1478 
1479     QString caption;
1480     if (_data) {
1481         caption = _data->traceName();
1482         if (!_data->command().isEmpty())
1483             caption += " [" + _data->command() + ']';
1484     }
1485     setWindowTitle(caption);
1486 
1487     if (!_data || (!_forcePartDock && _data->parts().count()<2)) {
1488         _partDock->hide();
1489         _partDockShown->setChecked(false);
1490     }
1491     else {
1492         _partDock->show();
1493         _partDockShown->setChecked(true);
1494     }
1495 
1496     updateStatusBar();
1497 }
1498 
1499 void TopLevel::addEventTypeMenu(QMenu* popup, bool withCost2)
1500 {
1501     if (_data) {
1502         QMenu *popup1, *popup2 = nullptr;
1503         QAction* action;
1504 
1505         popup1 = popup->addMenu(i18n("Primary Event Type"));
1506         connect(popup1, SIGNAL(triggered(QAction*)),
1507                 this, SLOT(setEventType(QAction*)));
1508 
1509         if (withCost2) {
1510             popup2 = popup->addMenu(i18n("Secondary Event Type"));
1511             connect(popup2, SIGNAL(triggered(QAction*)),
1512                     this, SLOT(setEventType2(QAction*)));
1513 
1514             if (_eventType2) {
1515                 action = popup2->addAction(i18n("Hide"));
1516                 action->setData(199);
1517                 popup2->addSeparator();
1518             }
1519         }
1520 
1521         EventTypeSet* m = _data->eventTypes();
1522         EventType* ct;
1523         for (int i=0;i<m->realCount();i++) {
1524             ct = m->realType(i);
1525 
1526             action = popup1->addAction(ct->longName());
1527             action->setCheckable(true);
1528             action->setData(100+i);
1529             if (_eventType == ct) action->setChecked(true);
1530 
1531             if (popup2) {
1532                 action = popup2->addAction(ct->longName());
1533                 action->setCheckable(true);
1534                 action->setData(100+i);
1535                 if (_eventType2 == ct) action->setChecked(true);
1536             }
1537         }
1538 
1539         for (int i=0;i<m->derivedCount();i++) {
1540             ct = m->derivedType(i);
1541 
1542             action = popup1->addAction(ct->longName());
1543             action->setCheckable(true);
1544             action->setData(200+i);
1545             if (_eventType == ct) action->setChecked(true);
1546 
1547             if (popup2) {
1548                 action = popup2->addAction(ct->longName());
1549                 action->setCheckable(true);
1550                 action->setData(200+i);
1551                 if (_eventType2 == ct) action->setChecked(true);
1552             }
1553         }
1554     }
1555 
1556     if (_showPercentage)
1557         popup->addAction(i18n("Show Absolute Cost"),
1558                          this, SLOT(setAbsoluteCost()));
1559     else
1560         popup->addAction(i18n("Show Relative Cost"),
1561                          this, SLOT(setRelativeCost()));
1562 }
1563 
1564 bool TopLevel::setEventType(QAction* action)
1565 {
1566     if (!_data) return false;
1567     int id = action->data().toInt(nullptr);
1568 
1569     EventTypeSet* m = _data->eventTypes();
1570     EventType* ct=nullptr;
1571     if (id >=100 && id<199) ct = m->realType(id-100);
1572     if (id >=200 && id<299) ct = m->derivedType(id-200);
1573 
1574     return ct ? setEventType(ct) : false;
1575 }
1576 
1577 bool TopLevel::setEventType2(QAction* action)
1578 {
1579     if (!_data) return false;
1580     int id = action->data().toInt(nullptr);
1581 
1582     EventTypeSet* m = _data->eventTypes();
1583     EventType* ct=nullptr;
1584     if (id >=100 && id<199) ct = m->realType(id-100);
1585     if (id >=200 && id<299) ct = m->derivedType(id-200);
1586 
1587     return setEventType2(ct);
1588 }
1589 
1590 void TopLevel::addGoMenu(QMenu* popup)
1591 {
1592     popup->addAction(i18n("Go Back"), this, SLOT(goBack()));
1593     popup->addAction(i18n("Go Forward"), this, SLOT(goForward()));
1594     popup->addAction(i18n("Go Up"), this, SLOT(goUp()));
1595 }
1596 
1597 void TopLevel::goBack()
1598 {
1599     setDirectionDelayed(TraceItemView::Back);
1600 }
1601 
1602 void TopLevel::goForward()
1603 {
1604     setDirectionDelayed(TraceItemView::Forward);
1605 }
1606 
1607 void TopLevel::goUp()
1608 {
1609     setDirectionDelayed(TraceItemView::Up);
1610 }
1611 
1612 QString TopLevel::traceKey()
1613 {
1614     if (!_data || _data->command().isEmpty()) return QString();
1615 
1616     QString name = _data->command();
1617     QString key;
1618     for (int l=0;l<name.length();l++)
1619         if (name[l].isLetterOrNumber()) key += name[l];
1620 
1621     return QStringLiteral("-") + key;
1622 }
1623 
1624 
1625 void TopLevel::restoreTraceTypes()
1626 {
1627     QString key = traceKey();
1628     QString groupType, eventType, eventType2;
1629 
1630     ConfigGroup* pConfig = ConfigStorage::group(QStringLiteral("TracePositions"));
1631     groupType = pConfig->value(QStringLiteral("GroupType%1").arg(key),QString()).toString();
1632     eventType = pConfig->value(QStringLiteral("EventType%1").arg(key),QString()).toString();
1633     eventType2 = pConfig->value(QStringLiteral("EventType2%1").arg(key),QString()).toString();
1634     delete pConfig;
1635 
1636     ConfigGroup* cConfig = ConfigStorage::group(QStringLiteral("CurrentState"));
1637     if (groupType.isEmpty())
1638         groupType = cConfig->value(QStringLiteral("GroupType"),QString()).toString();
1639     if (eventType.isEmpty())
1640         eventType = cConfig->value(QStringLiteral("EventType"),QString()).toString();
1641     if (eventType2.isEmpty())
1642         eventType2 = cConfig->value(QStringLiteral("EventType2"),QString()).toString();
1643     delete cConfig;
1644 
1645     setGroupType(groupType);
1646     setEventType(eventType);
1647     setEventType2(eventType2);
1648 
1649     // if still no cost type set, use first available
1650     if (!_eventType && !_saCost->items().isEmpty())
1651         eventTypeSelected(_saCost->items().first());
1652 
1653     ConfigGroup* aConfig = ConfigStorage::group(QStringLiteral("Layouts"));
1654     _layoutCount = aConfig->value(QStringLiteral("Count%1").arg(key), 0).toInt();
1655     _layoutCurrent = aConfig->value(QStringLiteral("Current%1").arg(key), 0).toInt();
1656     delete aConfig;
1657 
1658     if (_layoutCount == 0) layoutRestore();
1659     updateLayoutActions();
1660 }
1661 
1662 
1663 /**
1664  * This must be called after setting group/cost types in the function
1665  * selection widget, because the group/function choosing depends on
1666  * filled lists in the function selection widget
1667  */
1668 void TopLevel::restoreTraceSettings()
1669 {
1670     if (!_data) return;
1671 
1672     QString key = traceKey();
1673 
1674     restoreCurrentState(key);
1675 
1676     ConfigGroup* pConfig = ConfigStorage::group(QStringLiteral("TracePositions"));
1677     QString group = pConfig->value(QStringLiteral("Group%1").arg(key),QString()).toString();
1678     delete pConfig;
1679     if (!group.isEmpty()) setGroup(group);
1680 
1681 
1682     // restoreCurrentState() usually leads to a call to setTraceItemDelayed()
1683     // to restore last active item...
1684     if (!_traceItemDelayed) {
1685         // function not available any more.. try with "main"
1686         if (!setFunction(QStringLiteral("main")))
1687             _functionSelection->selectTopFunction();
1688     }
1689 }
1690 
1691 
1692 /* Layout */
1693 
1694 void TopLevel::layoutDuplicate()
1695 {
1696     // save current and allocate a new slot
1697     _multiView->saveLayout(QStringLiteral("Layout%1-MainView").arg(_layoutCurrent),
1698                            traceKey());
1699     _layoutCurrent = _layoutCount;
1700     _layoutCount++;
1701 
1702     updateLayoutActions();
1703 
1704     if (0) qDebug() << "TopLevel::layoutDuplicate: count " << _layoutCount;
1705 }
1706 
1707 void TopLevel::layoutRemove()
1708 {
1709     if (_layoutCount <2) return;
1710 
1711     int from = _layoutCount-1;
1712     if (_layoutCurrent == from) { _layoutCurrent--; from--; }
1713 
1714     // restore from last and decrement count
1715     _multiView->restoreLayout(QStringLiteral("Layout%1-MainView").arg(from),
1716                               traceKey());
1717     _layoutCount--;
1718 
1719     updateLayoutActions();
1720 }
1721 
1722 void TopLevel::layoutNext()
1723 {
1724     if (_layoutCount <2) return;
1725 
1726     QString key = traceKey();
1727     QString layoutPrefix = QStringLiteral("Layout%1-MainView");
1728 
1729     _multiView->saveLayout(layoutPrefix.arg(_layoutCurrent), key);
1730     _layoutCurrent++;
1731     if (_layoutCurrent == _layoutCount) _layoutCurrent = 0;
1732     _multiView->restoreLayout(layoutPrefix.arg(_layoutCurrent), key);
1733 
1734     if (0) qDebug() << "TopLevel::layoutNext: current "
1735                     << _layoutCurrent;
1736 }
1737 
1738 void TopLevel::layoutPrevious()
1739 {
1740     if (_layoutCount <2) return;
1741 
1742     QString key = traceKey();
1743     QString layoutPrefix = QStringLiteral("Layout%1-MainView");
1744 
1745     _multiView->saveLayout(layoutPrefix.arg(_layoutCurrent), key);
1746     _layoutCurrent--;
1747     if (_layoutCurrent <0) _layoutCurrent = _layoutCount-1;
1748     _multiView->restoreLayout(layoutPrefix.arg(_layoutCurrent), key);
1749 
1750     if (0) qDebug() << "TopLevel::layoutPrevious: current "
1751                     << _layoutCurrent;
1752 }
1753 
1754 void TopLevel::layoutSave()
1755 {
1756     QString key = traceKey();
1757     QString layoutPrefix = QStringLiteral("Layout%1-MainView");
1758 
1759     _multiView->saveLayout(layoutPrefix.arg(_layoutCurrent), key);
1760 
1761     // save all layouts as defaults (ie. without any group name postfix)
1762     for(int i=0;i<_layoutCount;i++) {
1763         _multiView->restoreLayout(layoutPrefix.arg(i), key);
1764         _multiView->saveLayout(layoutPrefix.arg(i), QString());
1765     }
1766     // restore the previously saved current layout
1767     _multiView->restoreLayout(layoutPrefix.arg(_layoutCurrent), key);
1768 
1769     ConfigGroup* layoutConfig = ConfigStorage::group(QStringLiteral("Layouts"));
1770     layoutConfig->setValue(QStringLiteral("DefaultCount"), _layoutCount);
1771     layoutConfig->setValue(QStringLiteral("DefaultCurrent"), _layoutCurrent);
1772     delete layoutConfig;
1773 }
1774 
1775 void TopLevel::layoutRestore()
1776 {
1777     KConfig *config = KSharedConfig::openConfig().data();
1778     KConfigGroup aConfig(config, "Layouts");
1779     _layoutCount = aConfig.readEntry("DefaultCount", 0);
1780     _layoutCurrent = aConfig.readEntry("DefaultCurrent", 0);
1781     if (_layoutCount == 0) {
1782         _layoutCount++;
1783         return;
1784     }
1785 
1786     QString layoutPrefix = QStringLiteral("Layout%1-MainView");
1787     _multiView->restoreLayout( layoutPrefix.arg(_layoutCurrent), traceKey());
1788 
1789     updateLayoutActions();
1790 }
1791 
1792 
1793 void TopLevel::updateLayoutActions()
1794 {
1795     QAction* ka;
1796 
1797     ka = actionCollection()->action(QStringLiteral("layout_next"));
1798     if (ka) ka->setEnabled(_layoutCount>1);
1799 
1800     ka = actionCollection()->action(QStringLiteral("layout_previous"));
1801     if (ka) ka->setEnabled(_layoutCount>1);
1802 
1803     ka = actionCollection()->action(QStringLiteral("layout_remove"));
1804     if (ka) ka->setEnabled(_layoutCount>1);
1805 
1806     _statusbar->showMessage(i18n("Layout Count: %1", _layoutCount), 1000);
1807 }
1808 
1809 
1810 void TopLevel::updateStatusBar()
1811 {
1812     if (!_data || _data->parts().isEmpty()) {
1813         _statusLabel->setText(i18n("No profile data file loaded."));
1814         return;
1815     }
1816 
1817     QString status = QStringLiteral("%1 [%2] - ")
1818                      .arg(_data->shortTraceName())
1819                      .arg(_data->activePartRange());
1820 
1821     if (_eventType) {
1822         status += i18n("Total %1 Cost: %2",
1823                        _eventType->longName(),
1824                        _data->prettySubCost(_eventType));
1825 
1826         /* this gets too long...
1827         if (_eventType2 && (_eventType2 != _eventType))
1828           status += i18n(", %1 Cost: %2")
1829         .arg(_eventType2->longName())
1830         .arg(_data->prettySubCost(_eventType2));
1831         */
1832     }
1833     else
1834         status += i18n("No event type selected");
1835 
1836     /* Not working... should give group of selected function
1837 
1838     if (_groupType != ProfileContext::Function) {
1839       status += QString(" - %1 '%2'")
1840                 .arg(ProfileContext::i18nTypeName(_groupType))
1841                 .arg(_group ? _group->prettyName() : i18n("(None)"));
1842     }
1843     */
1844 
1845     _statusLabel->setText(status);
1846 }
1847 
1848 void TopLevel::configure()
1849 {
1850     if (ConfigDlg::configure( GlobalGUIConfig::config(),
1851                               _data, this)) {
1852         GlobalGUIConfig::config()->saveOptions();
1853 
1854         configChanged();
1855     }
1856     else
1857         GlobalGUIConfig::config()->readOptions();
1858 }
1859 
1860 bool TopLevel::queryClose()
1861 {
1862     saveTraceSettings();
1863 
1864     // save current toplevel options as defaults...
1865     GlobalConfig::setShowPercentage(_showPercentage);
1866     GlobalConfig::setShowExpanded(_showExpanded);
1867     GlobalConfig::setShowCycles(_showCycles);
1868     GlobalConfig::setHideTemplates(_hideTemplates);
1869     GlobalGUIConfig::config()->saveOptions();
1870 
1871     saveCurrentState(QString());
1872 
1873     // toolbar and dock positions are automatically stored
1874 
1875     // if part dock was chosen visible even for only 1 part loaded,
1876     // keep this choice...
1877     _forcePartDock = false;
1878     if (_data && (_data->parts().count()<2) && _partDock->isVisible())
1879         _forcePartDock=true;
1880     KConfigGroup dockConfig(KSharedConfig::openConfig(), "Docks");
1881     dockConfig.writeEntry("ForcePartDockVisible", _forcePartDock);
1882 
1883     return true;
1884 }
1885 
1886 
1887 void TopLevel::splitSlot()
1888 {
1889     int count = _multiView->childCount();
1890     if (count<1) count = 1;
1891     if (count>2) count = 2;
1892     count = 3-count;
1893     _multiView->setChildCount(count);
1894 
1895     _taSplit->setChecked(count>1);
1896     _taSplitDir->setEnabled(count>1);
1897     _taSplitDir->setChecked(_multiView->orientation() == Qt::Horizontal);
1898 }
1899 
1900 void TopLevel::splitDirSlot()
1901 {
1902     _multiView->setOrientation( _taSplitDir->isChecked() ?
1903                                     Qt::Horizontal : Qt::Vertical );
1904 }
1905 
1906 
1907 
1908 // this is called after a config change in the dialog
1909 void TopLevel::configChanged()
1910 {
1911     // Invalidate found/cached dirs of source files if we have TraceData loaded.
1912     if (_data) {
1913         _data->resetSourceDirs();
1914     }
1915 
1916     _stackSelection->refresh();
1917 
1918     updateViewsOnChange(TraceItemView::configChanged);
1919 }
1920 
1921 void TopLevel::dummySlot()
1922 {
1923 }
1924 
1925 void TopLevel::activePartsChangedSlot(const TracePartList& list)
1926 {
1927     if (!_data) return;
1928 
1929     if (!_data->activateParts(list)) {
1930         //    qDebug("TopLevel::activePartsChangedSlot: No Change!");
1931         return;
1932     }
1933     _activeParts = list;
1934 
1935     _partSelection->set(list);
1936     _multiView->set(list);
1937     _functionSelection->set(list);
1938     _stackSelection->refresh();
1939 
1940     updateStatusBar();
1941 }
1942 
1943 void TopLevel::partsHideSelectedSlotDelayed()
1944 {
1945     QTimer::singleShot( 0, this, &TopLevel::partsHideSelectedSlot );
1946 }
1947 
1948 // this puts selected parts into hidden list,
1949 // deselects them and makes the remaining parts selected
1950 void TopLevel::partsHideSelectedSlot()
1951 {
1952     if (!_data) return;
1953 
1954     TracePartList newHidden, newActive;
1955     foreach(TracePart* part, _data->parts()) {
1956         if (_activeParts.contains(part) ||
1957             _hiddenParts.contains(part))
1958             newHidden.append(part);
1959         else
1960             newActive.append(part);
1961     }
1962 
1963     _hiddenParts = newHidden;
1964     _partSelection->hiddenPartsChangedSlot(_hiddenParts);
1965 
1966 #if 0
1967     _mainWidget1->hiddenPartsChangedSlot(_hiddenParts);
1968     _mainWidget2->hiddenPartsChangedSlot(_hiddenParts);
1969 #endif
1970 
1971     activePartsChangedSlot(newActive);
1972 }
1973 
1974 void TopLevel::partsUnhideAllSlotDelayed()
1975 {
1976     QTimer::singleShot( 0, this, &TopLevel::partsUnhideAllSlot );
1977 }
1978 
1979 // this unhides all hidden parts. Does NOT change selection
1980 void TopLevel::partsUnhideAllSlot()
1981 {
1982     if (!_data) return;
1983 
1984     _hiddenParts.clear();
1985     _partSelection->hiddenPartsChangedSlot(_hiddenParts);
1986 #if 0
1987     _mainWidget1->hiddenPartsChangedSlot(_hiddenParts);
1988     _mainWidget2->hiddenPartsChangedSlot(_hiddenParts);
1989 #endif
1990 }
1991 
1992 void TopLevel::forceTrace()
1993 {
1994     if (_ccProcess) {
1995         // callgrind_control still running, cancel request
1996         qDebug("TopLevel::forceTrace: killing old callgrind_control");
1997         _ccProcess->kill();
1998         delete _ccProcess;
1999         _ccProcess = nullptr;
2000         _ccOutput = QString();
2001     }
2002     if (!_taDump->isChecked()) return;
2003 
2004     // get PID of first loaded part
2005     int pid = 0;
2006     TracePart* p = nullptr;
2007     TracePartList pl;
2008     if (_data) pl = _data->parts();
2009     if (!pl.isEmpty()) p = pl.first();
2010     if (p) pid = p->processID();
2011     if (pid == 0) {
2012         showMessage(i18n("Cannot determine receiver PID for dump request"),
2013                     5000);
2014         _taDump->setChecked(false);
2015         return;
2016     }
2017 
2018     qDebug("TopLevel::forceTrace: run 'callgrind_control -d %d'", pid);
2019 
2020     _ccProcess = new QProcess(this);
2021     connect(_ccProcess, &QProcess::readyReadStandardOutput,
2022             this, &TopLevel::ccReadOutput);
2023     connect(_ccProcess, SIGNAL(error(QProcess::ProcessError)),
2024             SLOT(ccError(QProcess::ProcessError)));
2025     connect(_ccProcess, SIGNAL(finished(int,QProcess::ExitStatus)),
2026             SLOT(ccExit(int,QProcess::ExitStatus)));
2027 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
2028     _ccProcess->start(QStringLiteral("callgrind_control -d %1").arg(pid),
2029                       QIODevice::ReadOnly);
2030 #else
2031     _ccProcess->startCommand(QStringLiteral("callgrind_control -d %1").arg(pid),
2032                       QIODevice::ReadOnly);
2033 #endif
2034 }
2035 
2036 void TopLevel::ccReadOutput()
2037 {
2038     QProcess* p = qobject_cast<QProcess*>(sender());
2039     qDebug("TopLevel::ccReadOutput: QProcess %p", p);
2040 
2041     // signal from old/uninteresting process?
2042     if (!_ccProcess) return;
2043     if (p != _ccProcess) return;
2044 
2045     _ccOutput.append(QString::fromLocal8Bit(_ccProcess->readAllStandardOutput()));
2046 }
2047 
2048 void TopLevel::ccError(QProcess::ProcessError e)
2049 {
2050     QProcess* p = qobject_cast<QProcess*>(sender());
2051     qDebug("TopLevel::ccError: Got %d from QProcess %p",
2052            e, p);
2053 
2054     // signal from old/uninteresting process?
2055     if (!_ccProcess) return;
2056     if (p != _ccProcess) return;
2057 
2058     showMessage(i18n("Error running callgrind_control"), 5000);
2059 
2060     _ccProcess->deleteLater();
2061     _ccProcess = nullptr;
2062 }
2063 
2064 void TopLevel::ccExit(int exitCode, QProcess::ExitStatus s)
2065 {
2066     QProcess* p = qobject_cast<QProcess*>(sender());
2067     qDebug("TopLevel::ccExit: QProcess %p, exitCode %d",
2068            p, exitCode);
2069 
2070     // signal from old/uninteresting process?
2071     if (!_ccProcess) return;
2072     if (p != _ccProcess) return;
2073     _ccProcess->deleteLater();
2074     _ccProcess = nullptr;
2075     _taDump->setChecked(false);
2076 
2077     // if not successful no need to reload
2078     if ((s == QProcess::CrashExit) || (exitCode != 0))
2079         return;
2080 
2081     // FIXME: Are we sure that file is completely
2082     //        dumped after waiting one second?
2083     QTimer::singleShot( 1000, this, &TopLevel::reload );
2084 }
2085 
2086 void TopLevel::forwardAboutToShow()
2087 {
2088     QMenu *popup = _paForward->menu();
2089 
2090     popup->clear();
2091     StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr;
2092     HistoryItem* hi = b ? b->current() : nullptr;
2093     TraceFunction* f;
2094     QAction* action;
2095 
2096     if (!hi) {
2097         popup->addAction(i18n("(No Stack)"));
2098         return;
2099     }
2100 
2101     hi = hi->next();
2102     if (!hi) {
2103         popup->addAction(i18n("(No next function)"));
2104         return;
2105     }
2106 
2107     int count = 1;
2108     while (count<GlobalConfig::maxSymbolCount() && hi) {
2109         f = hi->function();
2110         if (!f) break;
2111 
2112         QString name = GlobalConfig::shortenSymbol(f->prettyName());
2113 
2114         //qDebug("forward: Adding %s", name.ascii());
2115         action = popup->addAction(name);
2116         action->setData(count);
2117 
2118         hi = hi->next();
2119         count++;
2120     }
2121 }
2122 
2123 void TopLevel::backAboutToShow()
2124 {
2125     QMenu *popup = _paBack->menu();
2126 
2127     popup->clear();
2128     StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr;
2129     HistoryItem* hi = b ? b->current() : nullptr;
2130     TraceFunction* f;
2131     QAction* action;
2132 
2133     if (!hi) {
2134         popup->addAction(i18n("(No Stack)"));
2135         return;
2136     }
2137 
2138     hi = hi->last();
2139     if (!hi) {
2140         popup->addAction(i18n("(No previous function)"));
2141         return;
2142     }
2143 
2144     int count = 1;
2145     while (count<GlobalConfig::maxSymbolCount() && hi) {
2146         f = hi->function();
2147         if (!f) break;
2148 
2149         QString name = GlobalConfig::shortenSymbol(f->prettyName());
2150 
2151         //qDebug("back: Adding %s", name.ascii());
2152         action = popup->addAction(name);
2153         action->setData(count);
2154 
2155         hi = hi->last();
2156         count++;
2157     }
2158 }
2159 
2160 void TopLevel::upAboutToShow()
2161 {
2162     QMenu *popup = _paUp->menu();
2163 
2164     popup->clear();
2165     StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr;
2166     HistoryItem* hi = b ? b->current() : nullptr;
2167     TraceFunction* f = hi ? hi->function() : nullptr;
2168     QAction* action;
2169 
2170     if (!f) {
2171         popup->addAction(i18n("(No Stack)"));
2172         return;
2173     }
2174     f = hi->stack()->caller(f, false);
2175     if (!f) {
2176         popup->addAction(i18n("(No Function Up)"));
2177         return;
2178     }
2179 
2180     int count = 1;
2181     while (count<GlobalConfig::maxSymbolCount() && f) {
2182         QString name = GlobalConfig::shortenSymbol(f->prettyName());
2183 
2184         action = popup->addAction(name);
2185         action->setData(count);
2186 
2187         f = hi->stack()->caller(f, false);
2188         count++;
2189     }
2190 
2191 }
2192 
2193 void TopLevel::forwardTriggered(QAction* action)
2194 {
2195     int count = action->data().toInt(nullptr);
2196     //qDebug("forwardTriggered: %d", count);
2197     if( count <= 0)
2198         return;
2199 
2200     StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr;
2201     if (!b) return;
2202 
2203     while (count>1) {
2204         b->goForward();
2205         count--;
2206     }
2207     _stackSelection->browserForward();
2208 }
2209 
2210 void TopLevel::backTriggered(QAction* action)
2211 {
2212     int count = action->data().toInt(nullptr);
2213     //qDebug("backTriggered: %d", count);
2214     if( count <= 0)
2215         return;
2216 
2217     StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr;
2218     if (!b) return;
2219 
2220     while (count>1) {
2221         b->goBack();
2222         count--;
2223     }
2224     _stackSelection->browserBack();
2225 }
2226 
2227 void TopLevel::upTriggered(QAction* action)
2228 {
2229     int count = action->data().toInt(nullptr);
2230     //qDebug("upTriggered: %d", count);
2231     if( count <= 0)
2232         return;
2233 
2234     StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr;
2235     HistoryItem* hi = b ? b->current() : nullptr;
2236     if (!hi) return;
2237 
2238     TraceFunction* f = hi->function();
2239 
2240     while (count>0 && f) {
2241         f = hi->stack()->caller(f, false);
2242         count--;
2243     }
2244 
2245     //qDebug("upActivated: %s", f ? f->prettyName().ascii() : "??" );
2246     if (f)
2247         setFunction(f);
2248 }
2249 
2250 void TopLevel::showMessage(const QString& msg, int ms)
2251 {
2252     if (_statusbar)
2253         _statusbar->showMessage(msg, ms);
2254 }
2255 
2256 void TopLevel::showStatus(const QString& msg, int progress)
2257 {
2258     static bool msgUpdateNeeded = true;
2259 
2260     if (!_statusbar) return;
2261 
2262     if (msg.isEmpty()) {
2263         //reset status
2264         if (_progressBar) {
2265             _statusbar->removeWidget(_progressBar);
2266             delete _progressBar;
2267             _progressBar = nullptr;
2268         }
2269         _statusbar->clearMessage();
2270         _progressMsg = msg;
2271         return;
2272     }
2273 
2274     if (_progressMsg.isEmpty())
2275         _progressStart.start();
2276 
2277     if (msg != _progressMsg) {
2278         _progressMsg = msg;
2279         msgUpdateNeeded = true;
2280     }
2281 
2282     // do nothing if last change was less than 0.5 seconds ago
2283     if (_progressStart.elapsed() < 500)
2284         return;
2285 
2286     if (!_progressBar) {
2287         _progressBar = new QProgressBar(_statusbar);
2288         _progressBar->setMaximumSize(200, _statusbar->height()-4);
2289         _statusbar->addPermanentWidget(_progressBar, 1);
2290         _progressBar->show();
2291         msgUpdateNeeded = true;
2292     }
2293 
2294     _progressStart.restart();
2295 
2296     if (msgUpdateNeeded) {
2297         _statusbar->showMessage(msg);
2298         msgUpdateNeeded = false;
2299     }
2300     _progressBar->setValue(progress);
2301 
2302     // let the progress bar update itself
2303     qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
2304 }
2305 
2306 void TopLevel::loadStart(const QString& filename)
2307 {
2308     showStatus(i18n("Loading %1", filename), 0);
2309     Logger::_filename = filename;
2310 }
2311 
2312 void TopLevel::loadFinished(const QString& msg)
2313 {
2314     showStatus(QString(), 0);
2315     if (!msg.isEmpty())
2316         showMessage(i18n("Error loading %1: %2", _filename, msg),
2317                     2000);
2318 }
2319 
2320 void TopLevel::loadProgress(int progress)
2321 {
2322     showStatus(i18n("Loading %1", _filename), progress);
2323 }
2324 
2325 void TopLevel::loadError(int line, const QString& msg)
2326 {
2327     qCritical() << "Loading" << _filename << ":" << line << ": " << msg;
2328 }
2329 
2330 void TopLevel::loadWarning(int line, const QString& msg)
2331 {
2332     qWarning() << "Loading" << _filename << ":" << line << ": " << msg;
2333 }
2334 
2335 bool TopLevel::openDataFile(const QString& file)
2336 {
2337     TraceData* d = new TraceData(this);
2338     int filesLoaded;
2339 
2340     // see whether this file is compressed, than take the direct route
2341     QMimeDatabase dataBase;
2342     QString mimeType = dataBase.mimeTypeForFile(file, QMimeDatabase::MatchContent).name();
2343 
2344     auto compressionType = KCompressionDevice::compressionTypeForMimeType(mimeType);
2345     KCompressionDevice* compressed;
2346     compressed = new KCompressionDevice(file,
2347                                         compressionType);
2348     if (compressed &&
2349         (compressed->compressionType() != KCompressionDevice::None)) {
2350         filesLoaded = d->load(compressed, file);
2351     } else {
2352         // else fallback to string based method that can also find multi-part callgrind data.
2353         filesLoaded = d->load(file);
2354     }
2355     if (filesLoaded > 0) {
2356         setData(d);
2357         return true;
2358     } else {
2359         return false;
2360     }
2361 }
2362 
2363 #include "moc_toplevel.cpp"