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 <> " 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"