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

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  * QCachegrind top level window
0011  */
0012 
0013 #define TRACE_UPDATES 0
0014 
0015 #include "qcgtoplevel.h"
0016 
0017 #include <stdlib.h> // for system()
0018 
0019 #include <QApplication>
0020 #include <QDebug>
0021 #include <QDockWidget>
0022 #include <QTimer>
0023 #include <QByteArray>
0024 #include <QLabel>
0025 #include <QMenuBar>
0026 #include <QProgressBar>
0027 #include <QFile>
0028 #include <QFileDialog>
0029 #include <QEventLoop>
0030 #include <QToolBar>
0031 #include <QComboBox>
0032 #include <QMessageBox>
0033 #include <QStatusBar>
0034 #include <QWhatsThis>
0035 #include <QWindow>
0036 
0037 #ifdef QT_DBUS_SUPPORT
0038 #include <QDBusConnection>
0039 #endif
0040 
0041 #include "partselection.h"
0042 #include "functionselection.h"
0043 #include "stackselection.h"
0044 #include "stackbrowser.h"
0045 #include "tracedata.h"
0046 #include "config.h"
0047 #include "globalguiconfig.h"
0048 #include "multiview.h"
0049 #include "callgraphview.h"
0050 #include "configdialog.h"
0051 
0052 QCGTopLevel::QCGTopLevel()
0053 {
0054 #ifdef QT_DBUS_SUPPORT
0055     QDBusConnection con = QDBusConnection::sessionBus();
0056     con.registerObject("/QCachegrind", this,
0057                        QDBusConnection::ExportScriptableSlots);
0058 #endif
0059 
0060     _progressBar = nullptr;
0061     _statusbar = statusBar();
0062     _statusLabel = new QLabel(_statusbar);
0063     _statusbar->addWidget(_statusLabel, 1);
0064 
0065     _layoutCount = 1;
0066     _layoutCurrent = 0;
0067 
0068     resetState();
0069 
0070     GlobalGUIConfig::config()->readOptions();
0071 
0072     createActions();
0073     createDocks();
0074     createToolbar();
0075     createMenu();
0076 
0077     _multiView = new MultiView(this, this);
0078     _multiView->setObjectName(QStringLiteral("MultiView"));
0079     setCentralWidget(_multiView);
0080 
0081     // restore current state settings (not configuration options)
0082     restoreCurrentState(QString());
0083 
0084     // restore docks & toolbars from config
0085     QByteArray state, geometry;
0086     ConfigGroup* topConfig = ConfigStorage::group(QStringLiteral("TopWindow"));
0087     _forcePartDock = topConfig->value(QStringLiteral("ForcePartDockVisible"), false).toBool();
0088     state = topConfig->value(QStringLiteral("State"), QByteArray()).toByteArray();
0089     geometry = topConfig->value(QStringLiteral("Geometry"), QByteArray()).toByteArray();
0090     delete topConfig;
0091 
0092     if (!geometry.isEmpty())
0093         restoreGeometry(geometry);
0094     if (!state.isEmpty())
0095         restoreState(state);
0096 
0097     setWindowIcon(QIcon(QStringLiteral(":/app.png")));
0098     setAttribute(Qt::WA_DeleteOnClose);
0099 }
0100 
0101 QCGTopLevel::~QCGTopLevel()
0102 {
0103 #ifdef Q_OS_MAC
0104     // hacky way to reinstall the dock, since each toplevel has a dock menu,
0105     // we don't do any global menu stuff on mac beyond what qt gives us, and
0106     // when the last window that installed the dock menu dies, it goes away.
0107     // reinstall it into another window if we can.
0108     auto windowList = QApplication::topLevelWidgets();
0109     for (int i = 0; i < windowList.size(); i++) {
0110         QWidget *topLevelRaw = windowList[i];
0111         if (QCGTopLevel *topLevel = qobject_cast<QCGTopLevel*>(topLevelRaw)) {
0112             if (topLevel != this) {
0113                 topLevel->reinstallMacDock();
0114                 break;
0115             }
0116         }
0117     }
0118 #endif
0119     delete _data;
0120 }
0121 
0122 void QCGTopLevel::reinstallMacDock()
0123 {
0124 #ifdef Q_OS_MAC
0125     if (macDockMenu != nullptr) {
0126         this->macDockMenu->setAsDockMenu();
0127     }
0128 #endif
0129 }
0130 
0131 // reset the visualization state, e.g. before loading new data
0132 void QCGTopLevel::resetState()
0133 {
0134     _activeParts.clear();
0135     _hiddenParts.clear();
0136 
0137     _data = nullptr;
0138     _function = nullptr;
0139     _eventType = nullptr;
0140     _eventType2 = nullptr;
0141     _groupType = ProfileContext::InvalidType;
0142     _group = nullptr;
0143 
0144     // for delayed slots
0145     _traceItemDelayed = nullptr;
0146     _eventTypeDelayed = nullptr;
0147     _eventType2Delayed = nullptr;
0148     _groupTypeDelayed = ProfileContext::InvalidType;
0149     _groupDelayed = nullptr;
0150     _directionDelayed = TraceItemView::None;
0151     _lastSender = nullptr;
0152 }
0153 
0154 
0155 /**
0156  * This saves the current state of the main window and
0157  * sub widgets.
0158  *
0159  * No positions are saved. These is done automatically for
0160  * KToolbar, and manually in queryExit() for QT docks.
0161  */
0162 void QCGTopLevel::saveCurrentState(const QString& postfix)
0163 {
0164     QString eventType, eventType2;
0165     if (_eventType) eventType = _eventType->name();
0166     if (_eventType2) eventType2 = _eventType2->name();
0167 
0168     ConfigGroup* stateConfig = ConfigStorage::group(QLatin1String("CurrentState") + postfix);
0169     stateConfig->setValue(QStringLiteral("EventType"), eventType);
0170     stateConfig->setValue(QStringLiteral("EventType2"), eventType2);
0171     stateConfig->setValue(QStringLiteral("GroupType"), ProfileContext::typeName(_groupType));
0172     delete stateConfig;
0173 
0174     _partSelection->saveOptions(QStringLiteral("PartOverview"), postfix);
0175     _multiView->saveLayout(QStringLiteral("MainView"), postfix);
0176     _multiView->saveOptions(QStringLiteral("MainView"), postfix);
0177 }
0178 
0179 /**
0180  * This function is called when a trace is closed.
0181  * Save browsing position for later restoring
0182  */
0183 void QCGTopLevel::saveTraceSettings()
0184 {
0185     QString key = traceKey();
0186 
0187     ConfigGroup* lConfig = ConfigStorage::group(QStringLiteral("Layouts"));
0188     lConfig->setValue(QStringLiteral("Count%1").arg(key), _layoutCount);
0189     lConfig->setValue(QStringLiteral("Current%1").arg(key), _layoutCurrent);
0190     delete lConfig;
0191 
0192     ConfigGroup* pConfig = ConfigStorage::group(QStringLiteral("TracePositions"));
0193     QString eventType, eventType2;
0194     if (_eventType) eventType = _eventType->name();
0195     if (_eventType2) eventType2 = _eventType2->name();
0196     pConfig->setValue(QStringLiteral("EventType%1").arg(key), eventType);
0197     pConfig->setValue(QStringLiteral("EventType2%1").arg(key), eventType2);
0198     if (_groupType != ProfileContext::InvalidType)
0199         pConfig->setValue(QStringLiteral("GroupType%1").arg(key),
0200                           ProfileContext::typeName(_groupType));
0201 
0202     if (_data) {
0203         if (_group)
0204             pConfig->setValue(QStringLiteral("Group%1").arg(key), _group->name());
0205         saveCurrentState(key);
0206     }
0207     delete pConfig;
0208 }
0209 
0210 /**
0211  * This restores the current visualization state of the main window and
0212  * of the profile views.
0213  */
0214 void QCGTopLevel::restoreCurrentState(const QString& postfix)
0215 {
0216     _partSelection->restoreOptions(QStringLiteral("PartOverview"), postfix);
0217     _multiView->restoreLayout(QStringLiteral("MainView"), postfix);
0218     _multiView->restoreOptions(QStringLiteral("MainView"), postfix);
0219 
0220     _splittedToggleAction->setChecked(_multiView->childCount()>1);
0221     _splitDirectionToggleAction->setEnabled(_multiView->childCount()>1);
0222     _splitDirectionToggleAction->setChecked(_multiView->orientation() ==
0223                                             Qt::Horizontal);
0224 }
0225 
0226 void QCGTopLevel::sidebarMenuAboutToShow()
0227 {
0228     QAction* action;
0229     QMenu *popup = _sidebarMenuAction->menu();
0230 
0231     popup->clear();
0232 
0233     action = popup->addAction(tr("Parts Overview"));
0234     action->setCheckable(true);
0235     action->setChecked(_partDock->isVisible());
0236     connect(action, &QAction::triggered, this, &QCGTopLevel::togglePartDock);
0237 
0238     action = popup->addAction(tr("Top Cost Call Stack"));
0239     action->setCheckable(true);
0240     action->setChecked(_stackDock->isVisible());
0241     connect(action, &QAction::triggered, this, &QCGTopLevel::toggleStackDock);
0242 
0243     action = popup->addAction(tr("Flat Profile"));
0244     action->setCheckable(true);
0245     action->setChecked(_functionDock->isVisible());
0246     connect(action, &QAction::triggered, this, &QCGTopLevel::toggleFunctionDock);
0247 }
0248 
0249 void QCGTopLevel::recentFilesMenuAboutToShow()
0250 {
0251     QStringList recentFiles;
0252     QMenu *popup = _recentFilesMenuAction->menu();
0253 
0254     popup->clear();
0255 
0256     ConfigGroup* generalConfig = ConfigStorage::group(QStringLiteral("GeneralSettings"));
0257     recentFiles = generalConfig->value(QStringLiteral("RecentFiles"),
0258                                        QStringList()).toStringList();
0259     delete generalConfig;
0260 
0261     if (recentFiles.isEmpty())
0262         popup->addAction(tr("(No recent files)"));
0263     else {
0264         foreach(const QString& file, recentFiles) {
0265             // paths shown to user should use OS-native separators
0266             popup->addAction(QDir::toNativeSeparators(file));
0267         }
0268     }
0269 }
0270 
0271 void QCGTopLevel::recentFilesTriggered(QAction* action)
0272 {
0273     if (action)
0274         load(QStringList(QDir::fromNativeSeparators(action->text())));
0275 }
0276 
0277 void QCGTopLevel::primaryAboutToShow()
0278 {
0279     updateEventTypeMenu(_primaryMenuAction->menu(), false);
0280 }
0281 
0282 void QCGTopLevel::secondaryAboutToShow()
0283 {
0284     updateEventTypeMenu(_secondaryMenuAction->menu(), true);
0285 }
0286 
0287 void QCGTopLevel::groupingAboutToShow()
0288 {
0289     if (!_functionSelection) return;
0290     _functionSelection->updateGroupingMenu(_groupingMenuAction->menu());
0291 }
0292 
0293 
0294 
0295 void QCGTopLevel::createDocks()
0296 {
0297     // part visualization/selection side bar
0298     _partDock = new QDockWidget(this);
0299     _partDock->setObjectName(QStringLiteral("part-dock"));
0300     _partDock->setWindowTitle(tr("Parts Overview"));
0301     _partSelection = new PartSelection(this, _partDock);
0302     _partDock->setWidget(_partSelection);
0303 
0304     connect(_partSelection, &PartSelection::partsHideSelected,
0305             this, &QCGTopLevel::partsHideSelectedSlotDelayed);
0306     connect(_partSelection, &PartSelection::partsUnhideAll,
0307             this, &QCGTopLevel::partsUnhideAllSlotDelayed);
0308 
0309     // stack selection side bar
0310     _stackDock = new QDockWidget(this);
0311     _stackDock->setObjectName(QStringLiteral("stack-dock"));
0312     _stackSelection = new StackSelection(_stackDock);
0313     _stackDock->setWidget(_stackSelection);
0314     _stackDock->setWindowTitle(tr("Top Cost Call Stack"));
0315     _stackSelection->setWhatsThis( tr(
0316                                        "<b>The Top Cost Call Stack</b>"
0317                                        "<p>This is a purely fictional 'most probable' call stack. "
0318                                        "It is built up by starting with the current selected "
0319                                        "function and adds the callers/callees with highest cost "
0320                                        "at the top and to bottom.</p>"
0321                                        "<p>The <b>Cost</b> and <b>Calls</b> columns show the "
0322                                        "cost used for all calls from the function in the line "
0323                                        "above.</p>"));
0324     connect(_stackSelection, SIGNAL(functionSelected(CostItem*)),
0325             this, SLOT(setTraceItemDelayed(CostItem*)));
0326     // actions are already created
0327     connect(_upAction, &QAction::triggered,
0328             _stackSelection, &StackSelection::browserUp );
0329     connect(_backAction, &QAction::triggered,
0330             _stackSelection, &StackSelection::browserBack );
0331     connect(_forwardAction, &QAction::triggered,
0332             _stackSelection, &StackSelection::browserForward);
0333 
0334     // flat function profile side bar
0335     _functionDock = new QDockWidget(this);
0336     _functionDock->setObjectName(QStringLiteral("function-dock"));
0337     _functionDock->setWindowTitle(tr("Flat Profile"));
0338     _functionSelection = new FunctionSelection(this, _functionDock);
0339     _functionDock->setWidget(_functionSelection);
0340     // functionDock needs call to updateView() when getting visible
0341     connect(_functionDock, &QDockWidget::visibilityChanged,
0342             this, &QCGTopLevel::functionVisibilityChanged);
0343 
0344     // defaults (later to be adjusted from stored state in config)
0345     addDockWidget(Qt::LeftDockWidgetArea, _partDock );
0346     addDockWidget(Qt::LeftDockWidgetArea, _stackDock );
0347     addDockWidget(Qt::LeftDockWidgetArea, _functionDock );
0348     _stackDock->hide();
0349     _partDock->hide();
0350 }
0351 
0352 
0353 
0354 
0355 void QCGTopLevel::createActions()
0356 {
0357     QString hint;
0358     QIcon icon;
0359 
0360     // file menu actions
0361     _newAction = new QAction(tr("&New"), this);
0362     _newAction->setShortcuts(QKeySequence::New);
0363     _newAction->setStatusTip(tr("Open new empty window"));
0364     connect(_newAction, &QAction::triggered, this, &QCGTopLevel::newWindow);
0365 
0366     icon = QApplication::style()->standardIcon(QStyle::SP_DialogOpenButton);
0367     _openAction = new QAction(icon, tr("&Open..."), this);
0368     _openAction->setShortcuts(QKeySequence::Open);
0369     _openAction->setStatusTip(tr("Open profile data file"));
0370     connect(_openAction, SIGNAL(triggered()), this, SLOT(load()));
0371 
0372     _closeAction = new QAction(tr("&Close"), this);
0373     _closeAction->setShortcuts(QKeySequence::Close);
0374     _closeAction->setStatusTip(tr("Close the current window"));
0375     connect(_closeAction, SIGNAL(triggered()), this, SLOT(close()));
0376 
0377     _addAction = new QAction(tr( "&Add..." ), this);
0378     _addAction->setStatusTip(tr("Add profile data to current window"));
0379     connect(_addAction, SIGNAL(triggered(bool)), SLOT(add()));
0380 
0381     _exportAction = new QAction(tr("Export Graph"), this);
0382     _exportAction->setStatusTip(tr("Generate GraphViz file 'callgraph.dot'"));
0383     connect(_exportAction, &QAction::triggered, this, &QCGTopLevel::exportGraph);
0384 
0385     _recentFilesMenuAction = new QAction(tr("Open &Recent"), this);
0386     _recentFilesMenuAction->setMenu(new QMenu(this));
0387     connect(_recentFilesMenuAction->menu(), &QMenu::aboutToShow,
0388             this, &QCGTopLevel::recentFilesMenuAboutToShow);
0389     connect(_recentFilesMenuAction->menu(), &QMenu::triggered,
0390             this, &QCGTopLevel::recentFilesTriggered);
0391 
0392     _exitAction = new QAction(tr("E&xit"), this);
0393     _exitAction->setMenuRole(QAction::QuitRole);
0394     _exitAction->setShortcut(tr("Ctrl+Q"));
0395     _exitAction->setStatusTip(tr("Exit the application"));
0396     connect(_exitAction, &QAction::triggered, this, &QApplication::closeAllWindows);
0397 
0398     // view menu actions
0399 
0400     _primaryMenuAction = new QAction(tr( "Primary Event Type" ), this );
0401     _primaryMenuAction->setMenu(new QMenu(this));
0402     connect(_primaryMenuAction->menu(), &QMenu::aboutToShow,
0403             this, &QCGTopLevel::primaryAboutToShow );
0404     _secondaryMenuAction = new QAction(tr( "Secondary Event Type" ), this );
0405     _secondaryMenuAction->setMenu(new QMenu(this));
0406     connect(_secondaryMenuAction->menu(), &QMenu::aboutToShow,
0407             this, &QCGTopLevel::secondaryAboutToShow );
0408     _groupingMenuAction = new QAction(tr( "Grouping" ), this );
0409     _groupingMenuAction->setMenu(new QMenu(this));
0410     connect(_groupingMenuAction->menu(), &QMenu::aboutToShow,
0411             this, &QCGTopLevel::groupingAboutToShow );
0412 
0413     icon = QApplication::style()->standardIcon(QStyle::SP_BrowserReload);
0414     _cyclesToggleAction = new QAction(icon, tr("Detect Cycles"), this);
0415     _cyclesToggleAction->setCheckable(true);
0416     _cyclesToggleAction->setStatusTip(tr("Do Cycle Detection"));
0417     hint = tr("<b>Detect recursive cycles</b>"
0418               "<p>If this is switched off, the treemap drawing will show "
0419               "black areas when a recursive call is made instead of drawing "
0420               "the recursion ad infinitum. Note that "
0421               "the size of black areas often will be wrong, as inside "
0422               "recursive cycles the cost of calls cannot be determined; "
0423               "the error is small, "
0424               "however, for false cycles (see documentation).</p>"
0425               "<p>The correct handling for cycles is to detect them and "
0426               "collapse all functions of a cycle into an artificial "
0427               "function, which is done when this option is selected. "
0428               "Unfortunately, with GUI applications, this often will "
0429               "lead to huge false cycles, making the analysis impossible; "
0430               "therefore, there is the option to switch this off.</p>");
0431     _cyclesToggleAction->setWhatsThis(hint);
0432     connect(_cyclesToggleAction, &QAction::triggered,
0433             this, &QCGTopLevel::toggleCycles);
0434     _cyclesToggleAction->setChecked(GlobalConfig::showCycles());
0435 
0436     _percentageToggleAction = new QAction(QIcon(QStringLiteral(":/percent.png")),
0437                                           tr("Relative Cost"), this);
0438     _percentageToggleAction->setCheckable(true);
0439     _percentageToggleAction->setStatusTip(tr("Show Relative Costs"));
0440     connect(_percentageToggleAction, &QAction::triggered,
0441             this, &QCGTopLevel::togglePercentage);
0442     _percentageToggleAction->setChecked(GlobalConfig::showPercentage());
0443 
0444     _hideTemplatesToggleAction = new QAction(QIcon(QStringLiteral(":/hidetemplates.png")),
0445                                              tr("Shorten Templates"), this);
0446     _hideTemplatesToggleAction->setCheckable(true);
0447     _hideTemplatesToggleAction->setStatusTip(tr("Hide Template Parameters "
0448                                                 "in C++ Symbols"));
0449     connect(_hideTemplatesToggleAction, &QAction::triggered,
0450             this, &QCGTopLevel::toggleHideTemplates);
0451     _hideTemplatesToggleAction->setChecked(GlobalConfig::hideTemplates());
0452     hint = tr("<b>Hide Template Parameters in C++ Symbols</b>"
0453               "<p>If this is switched on, every symbol displayed will have "
0454               "any C++ template parameters hidden, just showing &lt;&gt; "
0455               "instead of a potentially nested template parameter.</p>"
0456               "<p>In this mode, you can hover the mouse pointer over the "
0457               "activated symbol label to show a tooltip with the "
0458               "unabbreviated symbol.</p>");
0459     _hideTemplatesToggleAction->setWhatsThis(hint);
0460 
0461     _expandedToggleAction = new QAction(QIcon(QStringLiteral(":/move.png")),
0462                                         tr("Relative to Parent"), this);
0463     _expandedToggleAction->setCheckable(true);
0464     _expandedToggleAction->setStatusTip(
0465                 tr("Show Percentage relative to Parent"));
0466     hint = tr("<b>Show percentage costs relative to parent</b>"
0467               "<p>If this is switched off, percentage costs are always "
0468               "shown relative to the total cost of the profile part(s) "
0469               "that are currently browsed. By turning on this option, "
0470               "percentage cost of shown cost items will be relative "
0471               "to the parent cost item.</p>"
0472               "<ul><table>"
0473               "<tr><td><b>Cost Type</b></td><td><b>Parent Cost</b></td></tr>"
0474               "<tr><td>Function Inclusive</td><td>Total</td></tr>"
0475               "<tr><td>Function Self</td><td>Function Group (*)/Total</td></tr>"
0476               "<tr><td>Call</td><td>Function Inclusive</td></tr>"
0477               "<tr><td>Source Line</td><td>Function Inclusive</td></tr>"
0478               "</table></ul>"
0479               "<p>(*) Only if function grouping is switched on "
0480               "(e.g. ELF object grouping).</p>");
0481     _expandedToggleAction->setWhatsThis( hint );
0482     connect(_expandedToggleAction, &QAction::triggered,
0483             this, &QCGTopLevel::toggleExpanded);
0484     _expandedToggleAction->setChecked(GlobalConfig::showExpanded());
0485 
0486     _splittedToggleAction = new QAction(tr("Split Visualization"), this);
0487     _splittedToggleAction->setCheckable(true);
0488     _splittedToggleAction->setStatusTip(
0489                 tr("Show visualization of two cost items"));
0490     connect(_splittedToggleAction, &QAction::triggered,
0491             this, &QCGTopLevel::toggleSplitted);
0492 
0493     _splitDirectionToggleAction = new QAction(tr("Split Horizontal"), this);
0494     _splitDirectionToggleAction->setCheckable(true);
0495     _splitDirectionToggleAction->setStatusTip(
0496                 tr("Split visualization area horizontally"));
0497     connect(_splitDirectionToggleAction, &QAction::triggered,
0498             this, &QCGTopLevel::toggleSplitDirection);
0499 
0500     _sidebarMenuAction = new QAction(tr("Sidebars"), this);
0501     _sidebarMenuAction->setMenu(new QMenu(this));
0502     connect( _sidebarMenuAction->menu(), &QMenu::aboutToShow,
0503              this, &QCGTopLevel::sidebarMenuAboutToShow);
0504 
0505     _layoutDup = new QAction(tr("&Duplicate"), this);
0506     connect(_layoutDup, &QAction::triggered, this, &QCGTopLevel::layoutDuplicate);
0507     _layoutDup->setShortcut(Qt::CTRL + Qt::Key_Plus);
0508     _layoutDup->setStatusTip(tr("Duplicate current layout"));
0509 
0510     _layoutRemove = new QAction(tr("&Remove"), this);
0511     connect(_layoutRemove, &QAction::triggered, this, &QCGTopLevel::layoutRemove);
0512     _layoutRemove->setStatusTip(tr("Remove current layout"));
0513 
0514     _layoutNext = new QAction(tr("Go to &Next"), this);
0515     connect(_layoutNext, &QAction::triggered, this, &QCGTopLevel::layoutNext);
0516     _layoutNext->setShortcut(Qt::CTRL + Qt::Key_Right);
0517     _layoutNext->setStatusTip(tr("Switch to next layout"));
0518 
0519     _layoutPrev = new QAction(tr("Go to &Previous"), this);
0520     connect(_layoutPrev, &QAction::triggered, this, &QCGTopLevel::layoutPrevious);
0521     _layoutPrev->setShortcut(Qt::CTRL + Qt::Key_Left);
0522     _layoutPrev->setStatusTip(tr("Switch to previous layout"));
0523 
0524     _layoutRestore = new QAction(tr("&Restore to Default"), this);
0525     connect(_layoutRestore, &QAction::triggered, this, &QCGTopLevel::layoutRestore);
0526     _layoutRestore->setStatusTip(tr("Restore layouts to default"));
0527 
0528     _layoutSave = new QAction(tr("&Save as Default"), this);
0529     connect(_layoutSave, &QAction::triggered, this, &QCGTopLevel::layoutSave);
0530     _layoutSave->setStatusTip(tr("Save layouts as default"));
0531 
0532     // go menu actions
0533     icon = QApplication::style()->standardIcon(QStyle::SP_ArrowUp);
0534     _upAction = new QAction(icon, tr( "Up" ), this );
0535     _upAction->setShortcut( QKeySequence(Qt::ALT+Qt::Key_Up) );
0536     _upAction->setStatusTip(tr("Go Up in Call Stack"));
0537     _upAction->setMenu(new QMenu(this));
0538     connect(_upAction->menu(), &QMenu::aboutToShow,
0539             this, &QCGTopLevel::upAboutToShow );
0540     connect(_upAction->menu(), &QMenu::triggered,
0541             this, &QCGTopLevel::upTriggered );
0542     hint = tr("Go to last selected caller of current function");
0543     _upAction->setToolTip(hint);
0544 
0545     icon = QApplication::style()->standardIcon(QStyle::SP_ArrowBack);
0546     _backAction = new QAction(icon, tr("Back"), this);
0547     _backAction->setShortcut( QKeySequence(Qt::ALT+Qt::Key_Left) );
0548     _backAction->setStatusTip(tr("Go Back"));
0549     _backAction->setMenu(new QMenu(this));
0550     connect(_backAction->menu(), &QMenu::aboutToShow,
0551             this, &QCGTopLevel::backAboutToShow );
0552     connect(_backAction->menu(), &QMenu::triggered,
0553             this, &QCGTopLevel::backTriggered );
0554     hint = tr("Go back in function selection history");
0555     _backAction->setToolTip(hint);
0556 
0557     icon = QApplication::style()->standardIcon(QStyle::SP_ArrowForward);
0558     _forwardAction = new QAction(icon, tr("Forward"), this);
0559     _forwardAction->setShortcut( QKeySequence(Qt::ALT+Qt::Key_Right) );
0560     _forwardAction->setStatusTip(tr("Go Forward"));
0561     _forwardAction->setMenu(new QMenu(this));
0562     connect(_forwardAction->menu(), &QMenu::aboutToShow,
0563             this, &QCGTopLevel::forwardAboutToShow );
0564     connect(_forwardAction->menu(), &QMenu::triggered,
0565             this, &QCGTopLevel::forwardTriggered );
0566     hint = tr("Go forward in function selection history");
0567     _forwardAction->setToolTip( hint );
0568 
0569     // settings menu actions
0570     _configureAction = new QAction(tr("&Configure..."), this);
0571     _configureAction->setMenuRole(QAction::PreferencesRole);
0572     _configureAction->setStatusTip(tr("Configure QCachegrind"));
0573     connect(_configureAction, SIGNAL(triggered()), this, SLOT(configure()));
0574 
0575     // window menu actions
0576     _minimizeAction = new QAction(tr("&Minimize"), this);
0577     _minimizeAction->setShortcut(Qt::CTRL + Qt::Key_M);
0578     connect(_minimizeAction, &QAction::triggered, this, &QWidget::showMinimized);
0579 
0580     _zoomAction = new QAction(tr("&Zoom"), this);
0581     // on macOS, zoom doesn't have exactly the same semantics as maximize
0582     // (it creates the best fit), but maximize is the usual copout answer
0583     connect(_zoomAction, &QAction::triggered, this, &QWidget::showMaximized);
0584 
0585     // help menu actions
0586     _aboutAction = new QAction(tr("&About QCachegrind..."), this);
0587     _aboutAction->setMenuRole(QAction::AboutRole);
0588     _aboutAction->setStatusTip(tr("Show the application's About box"));
0589     connect(_aboutAction, &QAction::triggered, this, &QCGTopLevel::about);
0590 
0591     _aboutQtAction = new QAction(tr("About Qt..."), this);
0592     _aboutQtAction->setMenuRole(QAction::AboutQtRole);
0593     connect(_aboutQtAction, &QAction::triggered, qApp, &QApplication::aboutQt);
0594 
0595     // toolbar actions
0596     _eventTypeBox = new QComboBox(this);
0597     _eventTypeBox->setMinimumContentsLength(25);
0598     hint = tr("Select primary event type of costs");
0599     _eventTypeBox->setToolTip( hint );
0600     connect( _eventTypeBox, SIGNAL(activated(QString)),
0601              this, SLOT(eventTypeSelected(QString)));
0602 }
0603 
0604 void QCGTopLevel::createMenu()
0605 {
0606     QMenuBar* mBar = menuBar();
0607 
0608     QMenu* fileMenu = mBar->addMenu(tr("&File"));
0609     fileMenu->addAction(_newAction);
0610     fileMenu->addAction(_openAction);
0611     fileMenu->addAction(_recentFilesMenuAction);
0612     fileMenu->addAction(_addAction);
0613     fileMenu->addSeparator();
0614     fileMenu->addAction(_exportAction);
0615     fileMenu->addSeparator();
0616     fileMenu->addAction(_closeAction);
0617     fileMenu->addSeparator();
0618     fileMenu->addAction(_exitAction);
0619 
0620     QMenu* layoutMenu = new QMenu(tr("&Layout"), this);
0621     layoutMenu->addAction(_layoutDup);
0622     layoutMenu->addAction(_layoutRemove);
0623     layoutMenu->addSeparator();
0624     layoutMenu->addAction(_layoutPrev);
0625     layoutMenu->addAction(_layoutNext);
0626     layoutMenu->addSeparator();
0627     layoutMenu->addAction(_layoutSave);
0628     layoutMenu->addAction(_layoutRestore);
0629 
0630     QMenu* viewMenu = mBar->addMenu(tr("&View"));
0631     viewMenu->addAction(_primaryMenuAction);
0632     viewMenu->addAction(_secondaryMenuAction);
0633     viewMenu->addAction(_groupingMenuAction);
0634     viewMenu->addSeparator();
0635     viewMenu->addAction(tb->toggleViewAction());
0636     viewMenu->addMenu(layoutMenu);
0637     viewMenu->addAction(_sidebarMenuAction);
0638     viewMenu->addAction(_splittedToggleAction);
0639     viewMenu->addAction(_splitDirectionToggleAction);
0640     viewMenu->addSeparator();
0641     viewMenu->addAction(_cyclesToggleAction);
0642     viewMenu->addAction(_percentageToggleAction);
0643     viewMenu->addAction(_expandedToggleAction);
0644     viewMenu->addAction(_hideTemplatesToggleAction);
0645     viewMenu->addSeparator();
0646     viewMenu->addAction(_configureAction);
0647 
0648     QMenu* goMenu = mBar->addMenu(tr("&Go"));
0649     goMenu->addAction(_backAction);
0650     goMenu->addAction(_forwardAction);
0651     goMenu->addAction(_upAction);
0652     fileMenu->addAction(_exitAction);
0653 
0654 #ifdef Q_OS_MAC
0655     // class level for ease of manipulation
0656     this->windowMenu = mBar->addMenu(tr("&Window"));
0657     this->windowMenu->addAction(_minimizeAction);
0658     this->windowMenu->addAction(_zoomAction);
0659     connect(this->windowMenu, &QMenu::aboutToShow, this, &QCGTopLevel::windowListAboutToShow);
0660     connect(this->windowMenu, &QMenu::triggered, this, &QCGTopLevel::windowListTriggered);
0661 
0662     // right-clicking the dock icon should be a window list
0663     this->macDockMenu = new QMenu(this);
0664     connect(this->macDockMenu, &QMenu::aboutToShow, this, &QCGTopLevel::macDockMenuAboutToShow);
0665     // it can reuse the same events, it just needs a diff menu structure
0666     connect(this->macDockMenu, &QMenu::triggered, this, &QCGTopLevel::windowListTriggered);
0667     reinstallMacDock();
0668 #endif
0669 
0670     QMenu* helpMenu = mBar->addMenu(tr("&Help"));
0671     helpMenu->addAction(QWhatsThis::createAction(this));
0672     helpMenu->addSeparator();
0673     helpMenu->addAction(_aboutAction);
0674     helpMenu->addAction(_aboutQtAction);
0675 }
0676 
0677 void QCGTopLevel::createToolbar()
0678 {
0679     tb = new QToolBar(tr("Main Toolbar"), this);
0680     tb->setObjectName(QStringLiteral("main-toolbar"));
0681     addToolBar(Qt::TopToolBarArea, tb);
0682 
0683     tb->addAction(_openAction);
0684     tb->addSeparator();
0685 
0686     tb->addAction(_cyclesToggleAction);
0687     tb->addAction(_percentageToggleAction);
0688     tb->addAction(_expandedToggleAction);
0689     tb->addAction(_hideTemplatesToggleAction);
0690     tb->addSeparator();
0691 
0692     tb->addAction(_backAction);
0693     tb->addAction(_forwardAction);
0694     tb->addAction(_upAction);
0695     tb->addSeparator();
0696 
0697     tb->addWidget(_eventTypeBox);
0698 }
0699 
0700 
0701 void QCGTopLevel::about()
0702 {
0703     QString text, version;
0704     version = QStringLiteral("0.8.0kde");
0705     text = QStringLiteral("<h3>QCachegrind %1</h3>").arg(version);
0706     text += tr("<p>QCachegrind is a graphical user interface for analysing "
0707                "profiling data, which helps in the performance optimization "
0708                "phase of developing a computer program. "
0709                "QCachegrind is open-source, and it is distributed under the "
0710                "terms of the GPL v2. For details and source code, see the "
0711                "<a href=\"https://kcachegrind.github.io\">homepage</a> of the "
0712                "KCachegrind project.</p>"
0713                "Main author and maintainer: "
0714                "<a href=\"mailto:Josef.Weidendorfer@gmx.de\">"
0715                "Josef Weidendorfer</a><br>"
0716                "(with lots of bug fixes/porting help by the KDE community)");
0717     QMessageBox::about(this, tr("About QCachegrind"), text);
0718 }
0719 
0720 void QCGTopLevel::configure(QString s)
0721 {
0722     static QString lastPage;
0723 
0724     // if no specific config item should be focused, use last page
0725     if (s.isEmpty()) s = lastPage;
0726     ConfigDialog d(_data, this, s);
0727 
0728     if (d.exec() == QDialog::Accepted) {
0729         GlobalConfig::config()->saveOptions();
0730         configChanged();
0731     }
0732     lastPage = d.currentPage();
0733 }
0734 
0735 void QCGTopLevel::togglePartDock()
0736 {
0737     if (!_partDock->isVisible())
0738         _partDock->show();
0739     else
0740         _partDock->hide();
0741 }
0742 
0743 void QCGTopLevel::toggleStackDock()
0744 {
0745     if (!_stackDock->isVisible())
0746         _stackDock->show();
0747     else
0748         _stackDock->hide();
0749 }
0750 
0751 void QCGTopLevel::toggleFunctionDock()
0752 {
0753     if (!_functionDock->isVisible())
0754         _functionDock->show();
0755     else
0756         _functionDock->hide();
0757 }
0758 
0759 void QCGTopLevel::togglePercentage()
0760 {
0761     setPercentage(_percentageToggleAction->isChecked());
0762 }
0763 
0764 
0765 void QCGTopLevel::setAbsoluteCost()
0766 {
0767     setPercentage(false);
0768 }
0769 
0770 void QCGTopLevel::setRelativeCost()
0771 {
0772     setPercentage(true);
0773 }
0774 
0775 void QCGTopLevel::setPercentage(bool show)
0776 {
0777     if (GlobalConfig::showPercentage() == show) return;
0778     if (_percentageToggleAction->isChecked() != show)
0779         _percentageToggleAction->setChecked(show);
0780     _expandedToggleAction->setEnabled(show);
0781     GlobalConfig::setShowPercentage(show);
0782 
0783     _partSelection->notifyChange(TraceItemView::configChanged);
0784     _stackSelection->refresh();
0785     _functionSelection->notifyChange(TraceItemView::configChanged);
0786     _multiView->notifyChange(TraceItemView::configChanged);
0787 }
0788 
0789 void QCGTopLevel::toggleHideTemplates()
0790 {
0791     bool show = _hideTemplatesToggleAction->isChecked();
0792     if (GlobalConfig::hideTemplates() == show) return;
0793     GlobalConfig::setHideTemplates(show);
0794 
0795     _partSelection->notifyChange(TraceItemView::configChanged);
0796     _stackSelection->refresh();
0797     _functionSelection->notifyChange(TraceItemView::configChanged);
0798     _multiView->notifyChange(TraceItemView::configChanged);
0799 }
0800 
0801 void QCGTopLevel::toggleExpanded()
0802 {
0803     bool show = _expandedToggleAction->isChecked();
0804     if (GlobalConfig::showExpanded() == show) return;
0805     GlobalConfig::setShowExpanded(show);
0806 
0807     _partSelection->notifyChange(TraceItemView::configChanged);
0808     _stackSelection->refresh();
0809     _functionSelection->notifyChange(TraceItemView::configChanged);
0810     _multiView->notifyChange(TraceItemView::configChanged);
0811 }
0812 
0813 void QCGTopLevel::toggleCycles()
0814 {
0815     bool show = _cyclesToggleAction->isChecked();
0816     if (GlobalConfig::showCycles() == show) return;
0817     GlobalConfig::setShowCycles(show);
0818 
0819     if (!_data) return;
0820 
0821     _data->invalidateDynamicCost();
0822     _data->updateFunctionCycles();
0823 
0824     _partSelection->notifyChange(TraceItemView::configChanged);
0825     _stackSelection->rebuildStackList();
0826     _functionSelection->notifyChange(TraceItemView::configChanged);
0827     _multiView->notifyChange(TraceItemView::configChanged);
0828 }
0829 
0830 
0831 void QCGTopLevel::functionVisibilityChanged(bool v)
0832 {
0833     if (v)
0834         _functionSelection->updateView();
0835 }
0836 
0837 
0838 void QCGTopLevel::newWindow()
0839 {
0840     QCGTopLevel* t = new QCGTopLevel();
0841     t->show();
0842 }
0843 
0844 
0845 void QCGTopLevel::load()
0846 {
0847     QStringList files;
0848     files = QFileDialog::getOpenFileNames(this,
0849                                           tr("Open Callgrind Data"),
0850                                           _lastFile,
0851                                           tr("Callgrind Files (callgrind.* cachegrind.*);;All Files (*)"));
0852     load(files);
0853 }
0854 
0855 void QCGTopLevel::load(QStringList files, bool addToRecentFiles)
0856 {
0857     if (files.isEmpty()) return;
0858     _lastFile = files[0];
0859 
0860     if (_data && _data->parts().count()>0) {
0861 
0862         // In new window
0863         QCGTopLevel* t = new QCGTopLevel();
0864         t->show();
0865         t->loadDelayed(files, addToRecentFiles);
0866         return;
0867     }
0868 
0869     // this constructor enables progress bar callbacks
0870     TraceData* d = new TraceData(this);
0871     int filesLoaded = d->load(files);
0872     if (filesLoaded >0)
0873         setData(d);
0874 
0875     if (!addToRecentFiles) return;
0876 
0877     // add to recent file list in config
0878     QStringList recentFiles;
0879     ConfigGroup* generalConfig = ConfigStorage::group(QStringLiteral("GeneralSettings"));
0880     recentFiles = generalConfig->value(QStringLiteral("RecentFiles"),
0881                                        QStringList()).toStringList();
0882     foreach(const QString& file, files) {
0883         recentFiles.removeAll(file);
0884         if (filesLoaded >0)
0885             recentFiles.prepend(file);
0886         if (recentFiles.count() >5)
0887             recentFiles.removeLast();
0888     }
0889     generalConfig->setValue(QStringLiteral("RecentFiles"), recentFiles);
0890     delete generalConfig;
0891 }
0892 
0893 
0894 void QCGTopLevel::add()
0895 {
0896     QStringList files;
0897     files = QFileDialog::getOpenFileNames(this,
0898                                           tr("Add Callgrind Data"),
0899                                           _lastFile,
0900                                           tr("Callgrind Files (callgrind.*);;All Files (*)"));
0901     add(files);
0902 }
0903 
0904 
0905 void QCGTopLevel::add(QStringList files)
0906 {
0907     if (files.isEmpty()) return;
0908     _lastFile = files[0];
0909 
0910     if (_data) {
0911         _data->load(files);
0912 
0913         // GUI update for added data
0914         configChanged();
0915         return;
0916     }
0917 
0918     // this constructor enables progress bar callbacks
0919     TraceData* d = new TraceData(this);
0920     int filesLoaded = d->load(files);
0921     if (filesLoaded >0)
0922         setData(d);
0923 }
0924 
0925 void QCGTopLevel::loadDelayed(QString file, bool addToRecentFiles)
0926 {
0927     _loadFilesDelayed << file;
0928 
0929     _addToRecentFiles = addToRecentFiles;
0930     QTimer::singleShot(0, this, &QCGTopLevel::loadFilesDelayed);
0931 }
0932 
0933 void QCGTopLevel::loadDelayed(QStringList files, bool addToRecentFiles)
0934 {
0935     _loadFilesDelayed << files;
0936 
0937     _addToRecentFiles = addToRecentFiles;
0938     QTimer::singleShot(0, this, &QCGTopLevel::loadFilesDelayed);
0939 }
0940 
0941 void QCGTopLevel::loadFilesDelayed()
0942 {
0943     if (_loadFilesDelayed.isEmpty()) return;
0944 
0945     load(_loadFilesDelayed, _addToRecentFiles);
0946     _loadFilesDelayed.clear();
0947 }
0948 
0949 
0950 void QCGTopLevel::exportGraph()
0951 {
0952     if (!_data || !_function) return;
0953 
0954     GraphExporter::savePrompt(this, _data, _function, _eventType, _groupType, nullptr);
0955 }
0956 
0957 
0958 bool QCGTopLevel::setEventType(QString s)
0959 {
0960     EventType* ct;
0961 
0962     ct = (_data) ? _data->eventTypes()->type(s) : nullptr;
0963 
0964     // if costtype with given name not found, use first available
0965     if (!ct && _data) ct = _data->eventTypes()->type(0);
0966 
0967     return setEventType(ct);
0968 }
0969 
0970 bool QCGTopLevel::setEventType2(QString s)
0971 {
0972     EventType* ct;
0973 
0974     // Special type tr("(Hidden)") gives 0
0975     ct = (_data) ? _data->eventTypes()->type(s) : nullptr;
0976 
0977     return setEventType2(ct);
0978 }
0979 
0980 void QCGTopLevel::eventTypeSelected(const QString& s)
0981 {
0982     EventType* ct;
0983 
0984     ct = (_data) ? _data->eventTypes()->typeForLong(s) : nullptr;
0985     setEventType(ct);
0986 }
0987 
0988 void QCGTopLevel::eventType2Selected(const QString& s)
0989 {
0990     EventType* ct;
0991 
0992     ct = (_data) ? _data->eventTypes()->typeForLong(s) : nullptr;
0993     setEventType2(ct);
0994 }
0995 
0996 bool QCGTopLevel::setEventType(EventType* ct)
0997 {
0998     if (_eventType == ct) return false;
0999     _eventType = ct;
1000 
1001     if (ct) {
1002         int idx = _eventTypeBox->findText(ct->longName());
1003         if (idx >=0) _eventTypeBox->setCurrentIndex(idx);
1004     }
1005 
1006     _partSelection->setEventType(_eventType);
1007     _stackSelection->setEventType(_eventType);
1008     _functionSelection->setEventType(_eventType);
1009     _multiView->setEventType(_eventType);
1010 
1011     updateStatusBar();
1012 
1013     return true;
1014 }
1015 
1016 bool QCGTopLevel::setEventType2(EventType* ct)
1017 {
1018     if (_eventType2 == ct) return false;
1019     _eventType2 = ct;
1020 
1021     _partSelection->setEventType2(_eventType2);
1022     _stackSelection->setEventType2(_eventType2);
1023     _functionSelection->setEventType2(_eventType2);
1024     _multiView->setEventType2(_eventType2);
1025 
1026     updateStatusBar();
1027 
1028     return true;
1029 }
1030 
1031 
1032 void QCGTopLevel::groupTypeSelected(int cg)
1033 {
1034     switch(cg) {
1035     case 0: setGroupType( ProfileContext::Function ); break;
1036     case 1: setGroupType( ProfileContext::Object ); break;
1037     case 2: setGroupType( ProfileContext::File ); break;
1038     case 3: setGroupType( ProfileContext::Class ); break;
1039     case 4: setGroupType( ProfileContext::FunctionCycle ); break;
1040     default: break;
1041     }
1042 }
1043 
1044 bool QCGTopLevel::setGroupType(QString s)
1045 {
1046     ProfileContext::Type gt;
1047 
1048     gt = ProfileContext::type(s);
1049     // only allow Function/Object/File/Class as grouptype
1050     switch(gt) {
1051     case ProfileContext::Object:
1052     case ProfileContext::File:
1053     case ProfileContext::Class:
1054     case ProfileContext::FunctionCycle:
1055         break;
1056     default:
1057         gt = ProfileContext::Function;
1058     }
1059 
1060     return setGroupType(gt);
1061 }
1062 
1063 bool QCGTopLevel::setGroupType(ProfileContext::Type gt)
1064 {
1065     if (_groupType == gt) return false;
1066     _groupType = gt;
1067 
1068     int idx = -1;
1069     switch(gt) {
1070     case ProfileContext::Function: idx = 0; break;
1071     case ProfileContext::Object: idx = 1; break;
1072     case ProfileContext::File: idx = 2; break;
1073     case ProfileContext::Class: idx = 3; break;
1074     case ProfileContext::FunctionCycle: idx = 4; break;
1075     default:
1076         break;
1077     }
1078 
1079     if (idx==-1) return false;
1080 
1081 #if 0
1082     if (saGroup->currentItem() != idx)
1083         saGroup->setCurrentItem(idx);
1084 #endif
1085 
1086     _stackSelection->setGroupType(_groupType);
1087 
1088     _partSelection->set(_groupType);
1089     _functionSelection->set(_groupType);
1090     _multiView->set(_groupType);
1091 
1092     updateStatusBar();
1093 
1094     return true;
1095 }
1096 
1097 bool QCGTopLevel::setGroup(QString s)
1098 {
1099     TraceCostItem* ci = _functionSelection->group(s);
1100     if (!ci)
1101         return false;
1102 
1103     return setGroup(ci);
1104 }
1105 
1106 
1107 bool QCGTopLevel::setGroup(TraceCostItem* g)
1108 {
1109     if (_group == g) return false;
1110     _group = g;
1111 
1112     _functionSelection->setGroup(g);
1113     updateStatusBar();
1114 
1115     return true;
1116 }
1117 
1118 bool QCGTopLevel::setFunction(QString s)
1119 {
1120     if (!_data) return false;
1121 
1122     ProfileCostArray* f = _data->search(ProfileContext::Function, s, _eventType);
1123     if (!f) return false;
1124 
1125     return setFunction((TraceFunction*)f);
1126 }
1127 
1128 bool QCGTopLevel::setFunction(TraceFunction* f)
1129 {
1130     if (_function == f) return false;
1131     _function = f;
1132 
1133     _multiView->activate(f);
1134     _functionSelection->activate(f);
1135     _partSelection->activate(f);
1136     _stackSelection->setFunction(_function);
1137 
1138     StackBrowser* b = _stackSelection->browser();
1139     if (b) {
1140         // do not disable up: a press forces stack-up extending...
1141         _forwardAction->setEnabled(b->canGoForward());
1142         _backAction->setEnabled(b->canGoBack());
1143     }
1144 
1145 #if TRACE_UPDATES
1146     qDebug("QCGTopLevel::setFunction(%s), lastSender %s",
1147            f ? f->prettyName().toAscii() : "0",
1148            _lastSender ? _lastSender->name() :"0" );
1149 #endif
1150 
1151     return true;
1152 }
1153 
1154 
1155 /**
1156  * Delayed versions.
1157  * We always have a pair of slots: One receiver to start the
1158  * delay with a singleShot Timer. It stores the parameter into a
1159  * temporary variable. And one parameterless slot for
1160  * forwarding, using this temporary.
1161  */
1162 void QCGTopLevel::setEventTypeDelayed(EventType* ct)
1163 {
1164     _eventTypeDelayed = ct;
1165     QTimer::singleShot (0, this, SLOT(setEventTypeDelayed()));
1166 }
1167 
1168 void QCGTopLevel::setEventType2Delayed(EventType* ct)
1169 {
1170     _eventType2Delayed = ct;
1171     QTimer::singleShot (0, this, SLOT(setEventType2Delayed()));
1172 }
1173 
1174 void QCGTopLevel::setEventTypeDelayed()
1175 {
1176     setEventType(_eventTypeDelayed);
1177 }
1178 
1179 void QCGTopLevel::setEventType2Delayed()
1180 {
1181     setEventType2(_eventType2Delayed);
1182 }
1183 
1184 void QCGTopLevel::setGroupTypeDelayed(ProfileContext::Type gt)
1185 {
1186     _groupTypeDelayed = gt;
1187     QTimer::singleShot (0, this, SLOT(setGroupTypeDelayed()));
1188 }
1189 
1190 void QCGTopLevel::setGroupTypeDelayed()
1191 {
1192     setGroupType(_groupTypeDelayed);
1193 }
1194 
1195 void QCGTopLevel::setGroupDelayed(TraceCostItem* g)
1196 {
1197 #if TRACE_UPDATES
1198     qDebug("QCGTopLevel::setGroupDelayed(%s), sender %s",
1199            g ? g->prettyName().toAscii() : "0",
1200            _lastSender ? _lastSender->name() :"0" );
1201 #endif
1202 
1203     _groupDelayed = g;
1204     QTimer::singleShot (0, this, SLOT(setGroupDelayed()));
1205 }
1206 
1207 void QCGTopLevel::setGroupDelayed()
1208 {
1209     setGroup(_groupDelayed);
1210 }
1211 
1212 void QCGTopLevel::setDirectionDelayed(TraceItemView::Direction d)
1213 {
1214     _directionDelayed = d;
1215     QTimer::singleShot (0, this, SLOT(setDirectionDelayed()));
1216 }
1217 
1218 void QCGTopLevel::setDirectionDelayed()
1219 {
1220     switch(_directionDelayed) {
1221     case TraceItemView::Back:
1222         _stackSelection->browserBack();
1223         break;
1224 
1225     case TraceItemView::Forward:
1226         _stackSelection->browserForward();
1227         break;
1228 
1229     case TraceItemView::Up:
1230     {
1231         StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr;
1232         HistoryItem* hi = b ? b->current() : nullptr;
1233         TraceFunction* f = hi ? hi->function() : nullptr;
1234 
1235         if (!f) break;
1236         f = hi->stack()->caller(f, false);
1237         if (f) setFunction(f);
1238     }
1239         break;
1240 
1241     default: break;
1242     }
1243 
1244     _directionDelayed = TraceItemView::None;
1245 }
1246 
1247 
1248 void QCGTopLevel::setTraceItemDelayed(CostItem* i)
1249 {
1250     // no need to select same item a 2nd time...
1251     if (_traceItemDelayed == i) return;
1252     _traceItemDelayed = i;
1253     _lastSender = sender();
1254 
1255     qDebug() << "Selected " << (i ? i->fullName() : QStringLiteral("(none)"));
1256 
1257 #if TRACE_UPDATES
1258     qDebug("QCGTopLevel::setTraceItemDelayed(%s), sender %s",
1259            i ? i->prettyName().toAscii() : "0",
1260            _lastSender ? _lastSender->name() :"0" );
1261 #endif
1262 
1263     QTimer::singleShot (0, this, SLOT(setTraceItemDelayed()));
1264 }
1265 
1266 void QCGTopLevel::setTraceItemDelayed()
1267 {
1268     if (!_traceItemDelayed) return;
1269 
1270     switch(_traceItemDelayed->type()) {
1271     case ProfileContext::Function:
1272     case ProfileContext::FunctionCycle:
1273         setFunction((TraceFunction*)_traceItemDelayed);
1274         break;
1275 
1276     case ProfileContext::Object:
1277     case ProfileContext::File:
1278     case ProfileContext::Class:
1279         _multiView->activate(_traceItemDelayed);
1280         break;
1281 
1282     case ProfileContext::Instr:
1283     case ProfileContext::Line:
1284         // only for multiview
1285         _multiView->activate(_traceItemDelayed);
1286         break;
1287 
1288     default: break;
1289     }
1290 
1291     _traceItemDelayed = nullptr;
1292     _lastSender = nullptr;
1293 }
1294 
1295 /**
1296  * A TraceData object cannot be viewed many times in different
1297  * toplevel windows. Thus, this toplevel window takes ownership
1298  * of the TraceData object: on closing the window or opening
1299  * another trace, the object is destroyed.
1300  */
1301 void QCGTopLevel::setData(TraceData* data)
1302 {
1303     if (data == _data) return;
1304 
1305     _lastSender = nullptr;
1306 
1307     saveTraceSettings();
1308 
1309     if (_data) {
1310         _partSelection->setData(nullptr);
1311         _stackSelection->setData(nullptr);
1312         _functionSelection->setData(nullptr);
1313         _multiView->setData(nullptr);
1314         _multiView->updateView(true);
1315 
1316         // we are the owner...
1317         delete _data;
1318     }
1319 
1320     // reset members
1321     resetState();
1322 
1323     _data = data;
1324 
1325     // fill cost type list
1326     QStringList types;
1327 
1328     if (_data) {
1329         /* add all supported virtual types */
1330         EventTypeSet* m = _data->eventTypes();
1331         m->addKnownDerivedTypes();
1332 
1333         /* first, fill selection list with available cost types */
1334         for (int i=0;i<m->realCount();i++)
1335             types << m->realType(i)->longName();
1336         for (int i=0;i<m->derivedCount();i++)
1337             types << m->derivedType(i)->longName();
1338     }
1339     _eventTypes = types;
1340     _eventTypeBox->addItems(types);
1341 
1342     _stackSelection->setData(_data);
1343     _partSelection->setData(_data);
1344     _functionSelection->setData(_data);
1345     _multiView->setData(_data);
1346     // Force update of _data in all children of _multiView
1347     // This is needed to make restoring of activeItem work!
1348     _multiView->updateView(true);
1349 
1350     /* this is needed to let the other widgets know the types */
1351     restoreTraceTypes();
1352 
1353     restoreTraceSettings();
1354 
1355     QString caption;
1356     if (_data) {
1357         caption = QDir::toNativeSeparators(_data->traceName());
1358         if (!_data->command().isEmpty())
1359             caption += " [" + _data->command() + ']';
1360     }
1361     setWindowFilePath(QDir::toNativeSeparators(_data->traceName()));
1362     setWindowTitle(caption);
1363 
1364     if (!_data || (!_forcePartDock && _data->parts().count()<2))
1365         _partDock->hide();
1366     else
1367         _partDock->show();
1368 
1369     updateStatusBar();
1370 }
1371 
1372 // Clears and repopulates the given menu with dynamic items for event types.
1373 // Menu item handlers for setting the types are installed.
1374 void QCGTopLevel::updateEventTypeMenu(QMenu* m, bool secondary)
1375 {
1376     QAction* action;
1377 
1378     if (!m) return;
1379     m->clear();
1380 
1381     if (!_data) {
1382         // no data loaded yet
1383         m->addAction(tr("(None)"));
1384         return;
1385     }
1386 
1387     if (secondary) {
1388         connect(m, SIGNAL(triggered(QAction*)),
1389                 this, SLOT(setEventType2(QAction*)), Qt::UniqueConnection);
1390 
1391         if (_eventType2 != nullptr) {
1392             action = m->addAction(tr("Hide"));
1393             action->setData(199);
1394             m->addSeparator();
1395         }
1396     }
1397     else {
1398         connect(m, SIGNAL(triggered(QAction*)),
1399                 this, SLOT(setEventType(QAction*)), Qt::UniqueConnection);
1400     }
1401 
1402     EventTypeSet* ets = _data->eventTypes();
1403     EventType* et;
1404     EventType* selected = secondary ? _eventType2 : _eventType;
1405     for (int i = 0; i < ets->realCount(); i++) {
1406         et = ets->realType(i);
1407 
1408         action = m->addAction(et->longName());
1409         action->setCheckable(true);
1410         action->setData(100+i);
1411         if (et == selected) action->setChecked(true);
1412     }
1413     for (int i = 0; i < ets->derivedCount(); i++) {
1414         et = ets->derivedType(i);
1415 
1416         action = m->addAction(et->longName());
1417         action->setCheckable(true);
1418         action->setData(200+i);
1419         if (et == selected) action->setChecked(true);
1420     }
1421 }
1422 
1423 void QCGTopLevel::addEventTypeMenu(QMenu* popup, bool withCost2)
1424 {
1425     if (_data) {
1426         QMenu* menu = popup->addMenu(tr("Primary Event Type"));
1427         updateEventTypeMenu(menu, false);
1428 
1429         if (withCost2) {
1430             QMenu* menu = popup->addMenu(tr("Secondary Event Type"));
1431             updateEventTypeMenu(menu, true);
1432         }
1433     }
1434 
1435     if (GlobalConfig::showPercentage())
1436         popup->addAction(tr("Show Absolute Cost"),
1437                          this, SLOT(setAbsoluteCost()));
1438     else
1439         popup->addAction(tr("Show Relative Cost"),
1440                          this, SLOT(setRelativeCost()));
1441 }
1442 
1443 bool QCGTopLevel::setEventType(QAction* action)
1444 {
1445     if (!_data) return false;
1446     int id = action->data().toInt(nullptr);
1447 
1448     EventTypeSet* m = _data->eventTypes();
1449     EventType* ct=nullptr;
1450     if (id >=100 && id<199) ct = m->realType(id-100);
1451     if (id >=200 && id<299) ct = m->derivedType(id-200);
1452 
1453     return ct ? setEventType(ct) : false;
1454 }
1455 
1456 bool QCGTopLevel::setEventType2(QAction* action)
1457 {
1458     if (!_data) return false;
1459     int id = action->data().toInt(nullptr);
1460 
1461     EventTypeSet* m = _data->eventTypes();
1462     EventType* ct=nullptr;
1463     if (id >=100 && id<199) ct = m->realType(id-100);
1464     if (id >=200 && id<299) ct = m->derivedType(id-200);
1465 
1466     return setEventType2(ct);
1467 }
1468 
1469 void QCGTopLevel::addGoMenu(QMenu* popup)
1470 {
1471     StackBrowser* b = _stackSelection->browser();
1472     if (b) {
1473         if (b->canGoBack())
1474             popup->addAction(tr("Go Back"), this, SLOT(goBack()));
1475         if (b->canGoForward())
1476             popup->addAction(tr("Go Forward"), this, SLOT(goForward()));
1477     }
1478     // do not disable up: a press forces stack-up extending...
1479     popup->addAction(tr("Go Up"), this, SLOT(goUp()));
1480 }
1481 
1482 void QCGTopLevel::goBack()
1483 {
1484     setDirectionDelayed(TraceItemView::Back);
1485 }
1486 
1487 void QCGTopLevel::goForward()
1488 {
1489     setDirectionDelayed(TraceItemView::Forward);
1490 }
1491 
1492 void QCGTopLevel::goUp()
1493 {
1494     setDirectionDelayed(TraceItemView::Up);
1495 }
1496 
1497 QString QCGTopLevel::traceKey()
1498 {
1499     if (!_data || _data->command().isEmpty()) return QString();
1500 
1501     QString name = _data->command();
1502     QString key;
1503     for (int l=0;l<name.length();l++)
1504         if (name[l].isLetterOrNumber()) key += name[l];
1505 
1506     return QStringLiteral("-") + key;
1507 }
1508 
1509 
1510 void QCGTopLevel::restoreTraceTypes()
1511 {
1512     QString key = traceKey();
1513     QString groupType, eventType, eventType2;
1514 
1515     ConfigGroup* pConfig = ConfigStorage::group(QStringLiteral("TracePositions"));
1516     groupType = pConfig->value(QStringLiteral("GroupType%1").arg(key),QString()).toString();
1517     eventType = pConfig->value(QStringLiteral("EventType%1").arg(key),QString()).toString();
1518     eventType2 = pConfig->value(QStringLiteral("EventType2%1").arg(key),QString()).toString();
1519     delete pConfig;
1520 
1521     ConfigGroup* cConfig = ConfigStorage::group(QStringLiteral("CurrentState"));
1522     if (groupType.isEmpty())
1523         groupType = cConfig->value(QStringLiteral("GroupType"),QString()).toString();
1524     if (eventType.isEmpty())
1525         eventType = cConfig->value(QStringLiteral("EventType"),QString()).toString();
1526     if (eventType2.isEmpty())
1527         eventType2 = cConfig->value(QStringLiteral("EventType2"),QString()).toString();
1528     delete cConfig;
1529 
1530     setGroupType(groupType);
1531     setEventType(eventType);
1532     setEventType2(eventType2);
1533 
1534     // if still no event type set, use first available
1535     if (!_eventType && !_eventTypes.isEmpty())
1536         eventTypeSelected(_eventTypes.first());
1537 
1538     ConfigGroup* aConfig = ConfigStorage::group(QStringLiteral("Layouts"));
1539     _layoutCount = aConfig->value(QStringLiteral("Count%1").arg(key), 0).toInt();
1540     _layoutCurrent = aConfig->value(QStringLiteral("Current%1").arg(key), 0).toInt();
1541     delete aConfig;
1542 
1543     if (_layoutCount == 0) layoutRestore();
1544     updateLayoutActions();
1545 }
1546 
1547 
1548 /**
1549  * This must be called after setting group/cost types in the function
1550  * selection widget, because the group/function choosing depends on
1551  * filled lists in the function selection widget
1552  */
1553 void QCGTopLevel::restoreTraceSettings()
1554 {
1555     if (!_data) return;
1556 
1557     QString key = traceKey();
1558 
1559     restoreCurrentState(key);
1560 
1561     ConfigGroup* pConfig = ConfigStorage::group(QStringLiteral("TracePositions"));
1562     QString group = pConfig->value(QStringLiteral("Group%1").arg(key),QString()).toString();
1563     delete pConfig;
1564     if (!group.isEmpty()) setGroup(group);
1565 
1566     // restoreCurrentState() usually leads to a call to setTraceItemDelayed()
1567     // to restore last active item...
1568     if (!_traceItemDelayed) {
1569         // function not available any more.. try with "main"
1570         if (!setFunction(QStringLiteral("main"))) {
1571 #if 1
1572             _functionSelection->selectTopFunction();
1573 #else
1574             HighestCostList hc;
1575             hc.clear(50);
1576             TraceFunctionMap::Iterator it;
1577             for ( it = _data->functionMap().begin();
1578                   it != _data->functionMap().end(); ++it )
1579                 hc.addCost(&(*it), (*it).inclusive()->subCost(_eventType));
1580 
1581             setFunction( (TraceFunction*) hc[0]);
1582 #endif
1583         }
1584     }
1585 }
1586 
1587 
1588 /* Layout */
1589 
1590 void QCGTopLevel::layoutDuplicate()
1591 {
1592     // save current and allocate a new slot
1593     _multiView->saveLayout(QStringLiteral("Layout%1-MainView").arg(_layoutCurrent),
1594                            traceKey());
1595     _layoutCurrent = _layoutCount;
1596     _layoutCount++;
1597 
1598     updateLayoutActions();
1599 
1600     qDebug() << "QCGTopLevel::layoutDuplicate: count " << _layoutCount;
1601 }
1602 
1603 void QCGTopLevel::layoutRemove()
1604 {
1605     if (_layoutCount <2) return;
1606 
1607     int from = _layoutCount-1;
1608     if (_layoutCurrent == from) { _layoutCurrent--; from--; }
1609 
1610     // restore from last and decrement count
1611     _multiView->restoreLayout(QStringLiteral("Layout%1-MainView").arg(from),
1612                               traceKey());
1613     _layoutCount--;
1614 
1615     updateLayoutActions();
1616 
1617     qDebug() << "QCGTopLevel::layoutRemove: count " << _layoutCount;
1618 }
1619 
1620 void QCGTopLevel::layoutNext()
1621 {
1622     if (_layoutCount <2) return;
1623 
1624     QString key = traceKey();
1625     QString layoutPrefix = QStringLiteral("Layout%1-MainView");
1626 
1627     _multiView->saveLayout(layoutPrefix.arg(_layoutCurrent), key);
1628     _layoutCurrent++;
1629     if (_layoutCurrent == _layoutCount) _layoutCurrent = 0;
1630     _multiView->restoreLayout(layoutPrefix.arg(_layoutCurrent), key);
1631 
1632     qDebug() << "QCGTopLevel::layoutNext: current " << _layoutCurrent;
1633 }
1634 
1635 void QCGTopLevel::layoutPrevious()
1636 {
1637     if (_layoutCount <2) return;
1638 
1639     QString key = traceKey();
1640     QString layoutPrefix = QStringLiteral("Layout%1-MainView");
1641 
1642     _multiView->saveLayout(layoutPrefix.arg(_layoutCurrent), key);
1643     _layoutCurrent--;
1644     if (_layoutCurrent <0) _layoutCurrent = _layoutCount-1;
1645     _multiView->restoreLayout(layoutPrefix.arg(_layoutCurrent), key);
1646 
1647     qDebug() << "QCGTopLevel::layoutPrevious: current " << _layoutCurrent;
1648 }
1649 
1650 void QCGTopLevel::layoutSave()
1651 {
1652     QString key = traceKey();
1653     QString layoutPrefix = QStringLiteral("Layout%1-MainView");
1654 
1655     _multiView->saveLayout(layoutPrefix.arg(_layoutCurrent), key);
1656 
1657     // save all layouts as defaults (ie. without any group name postfix)
1658     for(int i=0;i<_layoutCount;i++) {
1659         _multiView->restoreLayout(layoutPrefix.arg(i), key);
1660         _multiView->saveLayout(layoutPrefix.arg(i), QString());
1661     }
1662     // restore the previously saved current layout
1663     _multiView->restoreLayout(layoutPrefix.arg(_layoutCurrent), key);
1664 
1665     ConfigGroup* layoutConfig = ConfigStorage::group(QStringLiteral("Layouts"));
1666     layoutConfig->setValue(QStringLiteral("DefaultCount"), _layoutCount);
1667     layoutConfig->setValue(QStringLiteral("DefaultCurrent"), _layoutCurrent);
1668     delete layoutConfig;
1669 }
1670 
1671 void QCGTopLevel::layoutRestore()
1672 {
1673     ConfigGroup* layoutConfig = ConfigStorage::group(QStringLiteral("Layouts"));
1674     _layoutCount = layoutConfig->value(QStringLiteral("DefaultCount"), 0).toInt();
1675     _layoutCurrent = layoutConfig->value(QStringLiteral("DefaultCurrent"), 0).toInt();
1676     delete layoutConfig;
1677 
1678     if (_layoutCount == 0) {
1679         _layoutCount++;
1680         return;
1681     }
1682 
1683     QString layoutPrefix = QStringLiteral("Layout%1-MainView");
1684     _multiView->restoreLayout( layoutPrefix.arg(_layoutCurrent), traceKey());
1685 
1686     updateLayoutActions();
1687 }
1688 
1689 
1690 void QCGTopLevel::updateLayoutActions()
1691 {
1692     if (_layoutNext)
1693         _layoutNext->setEnabled(_layoutCount>1);
1694 
1695     if (_layoutPrev)
1696         _layoutPrev->setEnabled(_layoutCount>1);
1697 
1698     if (_layoutRemove)
1699         _layoutRemove->setEnabled(_layoutCount>1);
1700 
1701     if (_statusbar)
1702         _statusbar->showMessage(tr("Layout Count: %1").arg(_layoutCount),
1703                                 1000);
1704 }
1705 
1706 
1707 void QCGTopLevel::updateStatusBar()
1708 {
1709     if (!_data || _data->parts().isEmpty()) {
1710         _statusLabel->setText(tr("No profile data file loaded."));
1711         return;
1712     }
1713 
1714     QString status = QStringLiteral("%1 [%2] - ")
1715                      .arg(_data->shortTraceName())
1716                      .arg(_data->activePartRange());
1717 
1718     if (_eventType) {
1719         status += tr("Total %1 Cost: %2")
1720                   .arg(_eventType->longName())
1721                   .arg(_data->prettySubCost(_eventType));
1722 
1723         /* this gets too long...
1724     if (_eventType2 && (_eventType2 != _eventType))
1725       status += tr(", %1 Cost: %2")
1726     .arg(_eventType2->longName())
1727     .arg(_data->prettySubCost(_eventType2));
1728     */
1729     }
1730     else
1731         status += tr("No event type selected");
1732 
1733     /* Not working... should give group of selected function
1734 
1735   if (_groupType != ProfileContext::Function) {
1736     status += QString(" - %1 '%2'")
1737               .arg(ProfileContext::trTypeName(_groupType))
1738               .arg(_group ? _group->prettyName() : tr("(None)"));
1739   }
1740   */
1741 
1742     _statusLabel->setText(status);
1743 }
1744 
1745 
1746 void QCGTopLevel::closeEvent(QCloseEvent* event)
1747 {
1748     GlobalConfig::config()->saveOptions();
1749 
1750     saveTraceSettings();
1751     saveCurrentState(QString());
1752 
1753     // if part dock was chosen visible even for only 1 part loaded,
1754     // keep this choice...
1755     _forcePartDock = false;
1756     if (_data && (_data->parts().count()<2) && _partDock->isVisible())
1757         _forcePartDock=true;
1758 
1759     ConfigGroup* topConfig = ConfigStorage::group(QStringLiteral("TopWindow"));
1760     topConfig->setValue(QStringLiteral("ForcePartDockVisible"), _forcePartDock, false);
1761     topConfig->setValue(QStringLiteral("State"), saveState());
1762     topConfig->setValue(QStringLiteral("Geometry"), saveGeometry());
1763     delete topConfig;
1764 
1765     event->accept();
1766 }
1767 
1768 
1769 void QCGTopLevel::toggleSplitted()
1770 {
1771     int count = _multiView->childCount();
1772     if (count<1) count = 1;
1773     if (count>2) count = 2;
1774     count = 3-count;
1775     _multiView->setChildCount(count);
1776 
1777     _splittedToggleAction->setChecked(count>1);
1778     _splitDirectionToggleAction->setEnabled(count>1);
1779     _splitDirectionToggleAction->setChecked(_multiView->orientation() ==
1780                                             Qt::Horizontal);
1781 }
1782 
1783 void QCGTopLevel::toggleSplitDirection()
1784 {
1785     _multiView->setOrientation( _splitDirectionToggleAction->isChecked() ?
1786                                     Qt::Horizontal : Qt::Vertical );
1787 }
1788 
1789 
1790 
1791 // this is called after a config change in the dialog
1792 void QCGTopLevel::configChanged()
1793 {
1794     // invalidate found/cached dirs of source files
1795     if (_data)
1796         _data->resetSourceDirs();
1797 
1798     _partSelection->notifyChange(TraceItemView::configChanged);
1799     _stackSelection->refresh();
1800     _functionSelection->notifyChange(TraceItemView::configChanged);
1801     _multiView->notifyChange(TraceItemView::configChanged);
1802 }
1803 
1804 
1805 
1806 void QCGTopLevel::activePartsChangedSlot(const TracePartList& list)
1807 {
1808     if (!_data) return;
1809 
1810     if (!_data->activateParts(list)) {
1811         //    qDebug("QCGTopLevel::activePartsChangedSlot: No Change!");
1812         return;
1813     }
1814     _activeParts = list;
1815 
1816     _partSelection->set(list);
1817     _stackSelection->refresh();
1818     _functionSelection->set(list);
1819     _multiView->set(list);
1820 
1821     updateStatusBar();
1822 }
1823 
1824 void QCGTopLevel::partsHideSelectedSlotDelayed()
1825 {
1826     QTimer::singleShot( 0, this, &QCGTopLevel::partsHideSelectedSlot );
1827 }
1828 
1829 // this puts selected parts into hidden list,
1830 // deselects them and makes the remaining parts selected
1831 void QCGTopLevel::partsHideSelectedSlot()
1832 {
1833     if (!_data) return;
1834 
1835     TracePartList newHidden, newActive;
1836     foreach(TracePart* part, _data->parts()) {
1837         if (_activeParts.contains(part) ||
1838             _hiddenParts.contains(part))
1839             newHidden.append(part);
1840         else
1841             newActive.append(part);
1842     }
1843 
1844     _hiddenParts = newHidden;
1845     _partSelection->hiddenPartsChangedSlot(_hiddenParts);
1846 
1847 #if 0
1848     _mainWidget1->hiddenPartsChangedSlot(_hiddenParts);
1849     _mainWidget2->hiddenPartsChangedSlot(_hiddenParts);
1850 #endif
1851 
1852     activePartsChangedSlot(newActive);
1853 }
1854 
1855 void QCGTopLevel::partsUnhideAllSlotDelayed()
1856 {
1857     QTimer::singleShot( 0, this, &QCGTopLevel::partsUnhideAllSlot );
1858 }
1859 
1860 // this unhides all hidden parts. Does NOT change selection
1861 void QCGTopLevel::partsUnhideAllSlot()
1862 {
1863     if (!_data) return;
1864 
1865     _hiddenParts.clear();
1866     _partSelection->hiddenPartsChangedSlot(_hiddenParts);
1867 
1868 #if 0
1869     _mainWidget1->hiddenPartsChangedSlot(_hiddenParts);
1870     _mainWidget2->hiddenPartsChangedSlot(_hiddenParts);
1871 #endif
1872 }
1873 
1874 void QCGTopLevel::insertWindowList(QMenu* menu)
1875 {
1876     auto windowList = QApplication::topLevelWidgets();
1877     auto activeWindow = QApplication::activeWindow();
1878     // dock menu this corresponds to (IIRC) last window that installed one,
1879     // so using it unless we have to is a bad idea
1880     if (activeWindow == nullptr) {
1881         activeWindow = this;
1882     }
1883     for (int i = 0; i < windowList.size(); i++) {
1884         QWidget *topLevelRaw = windowList[i];
1885         if (QCGTopLevel *topLevel = qobject_cast<QCGTopLevel*>(topLevelRaw)) {
1886             QString windowTitle = topLevel->windowTitle();
1887             QAction *windowItem = menu->addAction(windowTitle);
1888             windowItem->setData(QVariant::fromValue(topLevel));
1889             if (topLevel == activeWindow) {
1890                 windowItem->setCheckable(true);
1891                 windowItem->setChecked(true);
1892             }
1893         }
1894     }
1895 }
1896 
1897 void QCGTopLevel::windowListAboutToShow()
1898 {
1899     windowMenu->clear();
1900 
1901     windowMenu->addAction(_minimizeAction);
1902     windowMenu->addAction(_zoomAction);
1903     windowMenu->addSeparator();
1904 
1905     insertWindowList(windowMenu);
1906 }
1907 
1908 void QCGTopLevel::macDockMenuAboutToShow()
1909 {
1910     macDockMenu-> clear();
1911 
1912     insertWindowList(macDockMenu);
1913 
1914     macDockMenu->addSeparator();
1915     macDockMenu->addAction(_newAction);
1916 }
1917 
1918 void QCGTopLevel::forwardAboutToShow()
1919 {
1920     QMenu *popup = _forwardAction->menu();
1921 
1922     popup->clear();
1923     StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr;
1924     HistoryItem* hi = b ? b->current() : nullptr;
1925     TraceFunction* f;
1926     QAction* action;
1927 
1928     if (!hi) {
1929         popup->addAction(tr("(No Stack)"));
1930         return;
1931     }
1932 
1933     hi = hi->next();
1934     if (!hi) {
1935         popup->addAction(tr("(No next function)"));
1936         return;
1937     }
1938 
1939     int count = 1;
1940     while (count<GlobalConfig::maxSymbolCount() && hi) {
1941         f = hi->function();
1942         if (!f) break;
1943 
1944         QString name = GlobalConfig::shortenSymbol(f->prettyName());
1945 
1946         //qDebug("forward: Adding %s", name.toAscii());
1947         action = popup->addAction(name);
1948         action->setData(count);
1949 
1950         hi = hi->next();
1951         count++;
1952     }
1953 }
1954 
1955 void QCGTopLevel::backAboutToShow()
1956 {
1957     QMenu *popup = _backAction->menu();
1958 
1959     popup->clear();
1960     StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr;
1961     HistoryItem* hi = b ? b->current() : nullptr;
1962     TraceFunction* f;
1963     QAction* action;
1964 
1965     if (!hi) {
1966         popup->addAction(tr("(No Stack)"));
1967         return;
1968     }
1969 
1970     hi = hi->last();
1971     if (!hi) {
1972         popup->addAction(tr("(No previous function)"));
1973         return;
1974     }
1975 
1976     int count = 1;
1977     while (count<GlobalConfig::maxSymbolCount() && hi) {
1978         f = hi->function();
1979         if (!f) break;
1980 
1981         QString name = GlobalConfig::shortenSymbol(f->prettyName());
1982 
1983         //qDebug("back: Adding %s", name.toAscii());
1984         action = popup->addAction(name);
1985         action->setData(count);
1986 
1987         hi = hi->last();
1988         count++;
1989     }
1990 }
1991 
1992 void QCGTopLevel::upAboutToShow()
1993 {
1994     QMenu *popup = _upAction->menu();
1995 
1996     popup->clear();
1997     StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr;
1998     HistoryItem* hi = b ? b->current() : nullptr;
1999     TraceFunction* f = hi ? hi->function() : nullptr;
2000     QAction* action;
2001 
2002     if (!f) {
2003         popup->addAction(tr("(No Stack)"));
2004         return;
2005     }
2006     f = hi->stack()->caller(f, false);
2007     if (!f) {
2008         popup->addAction(tr("(No Function Up)"));
2009         return;
2010     }
2011 
2012     int count = 1;
2013     while (count<GlobalConfig::maxSymbolCount() && f) {
2014         QString name = GlobalConfig::shortenSymbol(f->prettyName());
2015 
2016         action = popup->addAction(name);
2017         action->setData(count);
2018 
2019         f = hi->stack()->caller(f, false);
2020         count++;
2021     }
2022 }
2023 
2024 void QCGTopLevel::windowListTriggered(QAction* action)
2025 {
2026     if (action == _minimizeAction || action == _zoomAction) {
2027         // these are always in the menu for macOS
2028         return;
2029     }
2030 
2031     QVariant data = action->data();
2032     if (QCGTopLevel* tl = qvariant_cast<QCGTopLevel*>(data)) {
2033         tl->activateWindow();
2034         tl->raise();
2035     }
2036 }
2037 
2038 void QCGTopLevel::forwardTriggered(QAction* action)
2039 {
2040     int count = action->data().toInt(nullptr);
2041     //qDebug("forwardTriggered: %d", count);
2042     if( count <= 0)
2043         return;
2044 
2045     StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr;
2046     if (!b) return;
2047 
2048     while (count>1) {
2049         b->goForward();
2050         count--;
2051     }
2052     _stackSelection->browserForward();
2053 }
2054 
2055 void QCGTopLevel::backTriggered(QAction* action)
2056 {
2057     int count = action->data().toInt(nullptr);
2058     //qDebug("backTriggered: %d", count);
2059     if( count <= 0)
2060         return;
2061 
2062     StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr;
2063     if (!b) return;
2064 
2065     while (count>1) {
2066         b->goBack();
2067         count--;
2068     }
2069     _stackSelection->browserBack();
2070 }
2071 
2072 void QCGTopLevel::upTriggered(QAction* action)
2073 {
2074     int count = action->data().toInt(nullptr);
2075     //qDebug("upTriggered: %d", count);
2076     if( count <= 0)
2077         return;
2078 
2079     StackBrowser* b = _stackSelection ? _stackSelection->browser() : nullptr;
2080     HistoryItem* hi = b ? b->current() : nullptr;
2081     if (!hi) return;
2082 
2083     TraceFunction* f = hi->function();
2084 
2085     while (count>0 && f) {
2086         f = hi->stack()->caller(f, false);
2087         count--;
2088     }
2089 
2090     //qDebug("upActivated: %s", f ? f->prettyName().toAscii() : "??" );
2091     if (f)
2092         setFunction(f);
2093 }
2094 
2095 void QCGTopLevel::showMessage(const QString& msg, int ms)
2096 {
2097     if (_statusbar)
2098         _statusbar->showMessage(msg, ms);
2099 }
2100 
2101 void QCGTopLevel::showStatus(const QString& msg, int progress)
2102 {
2103     static bool msgUpdateNeeded = true;
2104 
2105     if (!_statusbar) return;
2106 
2107     if (msg.isEmpty()) {
2108         //reset status
2109         if (_progressBar) {
2110             _statusbar->removeWidget(_progressBar);
2111             delete _progressBar;
2112             _progressBar = nullptr;
2113         }
2114         _statusbar->clearMessage();
2115         _progressMsg = msg;
2116         return;
2117     }
2118 
2119     if (_progressMsg.isEmpty())
2120         _progressStart.start();
2121 
2122     if (msg != _progressMsg) {
2123         _progressMsg = msg;
2124         msgUpdateNeeded = true;
2125     }
2126 
2127     // do nothing if last change was less than 0.5 seconds ago
2128     if (_progressStart.elapsed() < 500)
2129         return;
2130 
2131     if (!_progressBar) {
2132         _progressBar = new QProgressBar(_statusbar);
2133         _progressBar->setMaximumSize(200, _statusbar->height()-4);
2134         _statusbar->addPermanentWidget(_progressBar, 1);
2135         _progressBar->show();
2136         msgUpdateNeeded = true;
2137     }
2138 
2139     _progressStart.restart();
2140 
2141     if (msgUpdateNeeded) {
2142         _statusbar->showMessage(msg);
2143         msgUpdateNeeded = false;
2144     }
2145     _progressBar->setValue(progress);
2146 
2147     // let the progress bar update itself
2148     qApp->processEvents(QEventLoop::ExcludeUserInputEvents);
2149 }
2150 
2151 void QCGTopLevel::loadStart(const QString& filename)
2152 {
2153     showStatus(QStringLiteral("Loading %1").arg(filename), 0);
2154     Logger::_filename = filename;
2155 }
2156 
2157 void QCGTopLevel::loadFinished(const QString& msg)
2158 {
2159     showStatus(QString(), 0);
2160     if (!msg.isEmpty())
2161         showMessage(QStringLiteral("Error loading %1: %2").arg(_filename).arg(msg),
2162                     2000);
2163 }
2164 
2165 void QCGTopLevel::loadProgress(int progress)
2166 {
2167     showStatus(QStringLiteral("Loading %1").arg(_filename), progress);
2168 }
2169 
2170 void QCGTopLevel::loadError(int line, const QString& msg)
2171 {
2172     qCritical() << "Loading" << _filename
2173                 << ":" << line << ": " << msg;
2174 }
2175 
2176 void QCGTopLevel::loadWarning(int line, const QString& msg)
2177 {
2178     qWarning() << "Loading" << _filename
2179                << ":" << line << ": " << msg;
2180 }
2181 
2182 #include "moc_qcgtoplevel.cpp"