1 /*
2 This file is part of KCachegrind.
3
4 SPDX-FileCopyrightText: 2002-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
5
6 SPDX-License-Identifier: GPL-2.0-only
7 */
8
9 /*
10 * QCachegrind top level window
11 */
12
13 #define TRACE_UPDATES 0
14
15 #include "qcgtoplevel.h"
16
17 #include <stdlib.h> // for system()
18
19 #include <QApplication>
20 #include <QDebug>
21 #include <QDockWidget>
22 #include <QTimer>
23 #include <QByteArray>
24 #include <QLabel>
25 #include <QMenuBar>
26 #include <QProgressBar>
27 #include <QFile>
28 #include <QFileDialog>
29 #include <QEventLoop>
30 #include <QToolBar>
31 #include <QComboBox>
32 #include <QMessageBox>
33 #include <QStatusBar>
34 #include <QWhatsThis>
35 #include <QWindow>
36
37 #ifdef QT_DBUS_SUPPORT
38 #include <QDBusConnection>
39 #endif
40
41 #include "partselection.h"
42 #include "functionselection.h"
43 #include "stackselection.h"
44 #include "stackbrowser.h"
45 #include "tracedata.h"
46 #include "config.h"
47 #include "globalguiconfig.h"
48 #include "multiview.h"
49 #include "callgraphview.h"
50 #include "configdialog.h"
51
QCGTopLevel()52 QCGTopLevel::QCGTopLevel()
53 {
54 #ifdef QT_DBUS_SUPPORT
55 QDBusConnection con = QDBusConnection::sessionBus();
56 con.registerObject("/QCachegrind", this,
57 QDBusConnection::ExportScriptableSlots);
58 #endif
59
60 _progressBar = nullptr;
61 _statusbar = statusBar();
62 _statusLabel = new QLabel(_statusbar);
63 _statusbar->addWidget(_statusLabel, 1);
64
65 _layoutCount = 1;
66 _layoutCurrent = 0;
67
68 resetState();
69
70 GlobalGUIConfig::config()->readOptions();
71
72 createActions();
73 createDocks();
74 createToolbar();
75 createMenu();
76
77 _multiView = new MultiView(this, this);
78 _multiView->setObjectName(QStringLiteral("MultiView"));
79 setCentralWidget(_multiView);
80
81 // restore current state settings (not configuration options)
82 restoreCurrentState(QString());
83
84 // restore docks & toolbars from config
85 QByteArray state, geometry;
86 ConfigGroup* topConfig = ConfigStorage::group(QStringLiteral("TopWindow"));
87 _forcePartDock = topConfig->value(QStringLiteral("ForcePartDockVisible"), false).toBool();
88 state = topConfig->value(QStringLiteral("State"), QByteArray()).toByteArray();
89 geometry = topConfig->value(QStringLiteral("Geometry"), QByteArray()).toByteArray();
90 delete topConfig;
91
92 if (!geometry.isEmpty())
93 restoreGeometry(geometry);
94 if (!state.isEmpty())
95 restoreState(state);
96
97 setWindowIcon(QIcon(QStringLiteral(":/app.png")));
98 setAttribute(Qt::WA_DeleteOnClose);
99 }
100
~QCGTopLevel()101 QCGTopLevel::~QCGTopLevel()
102 {
103 #ifdef Q_OS_MAC
104 // hacky way to reinstall the dock, since each toplevel has a dock menu,
105 // we don't do any global menu stuff on mac beyond what qt gives us, and
106 // when the last window that installed the dock menu dies, it goes away.
107 // reinstall it into another window if we can.
108 auto windowList = QApplication::topLevelWidgets();
109 for (int i = 0; i < windowList.size(); i++) {
110 QWidget *topLevelRaw = windowList[i];
111 if (QCGTopLevel *topLevel = qobject_cast<QCGTopLevel*>(topLevelRaw)) {
112 if (topLevel != this) {
113 topLevel->reinstallMacDock();
114 break;
115 }
116 }
117 }
118 #endif
119 delete _data;
120 }
121
reinstallMacDock()122 void QCGTopLevel::reinstallMacDock()
123 {
124 #ifdef Q_OS_MAC
125 if (macDockMenu != nullptr) {
126 this->macDockMenu->setAsDockMenu();
127 }
128 #endif
129 }
130
131 // reset the visualization state, e.g. before loading new data
resetState()132 void QCGTopLevel::resetState()
133 {
134 _activeParts.clear();
135 _hiddenParts.clear();
136
137 _data = nullptr;
138 _function = nullptr;
139 _eventType = nullptr;
140 _eventType2 = nullptr;
141 _groupType = ProfileContext::InvalidType;
142 _group = nullptr;
143
144 // for delayed slots
145 _traceItemDelayed = nullptr;
146 _eventTypeDelayed = nullptr;
147 _eventType2Delayed = nullptr;
148 _groupTypeDelayed = ProfileContext::InvalidType;
149 _groupDelayed = nullptr;
150 _directionDelayed = TraceItemView::None;
151 _lastSender = nullptr;
152 }
153
154
155 /**
156 * This saves the current state of the main window and
157 * sub widgets.
158 *
159 * No positions are saved. These is done automatically for
160 * KToolbar, and manually in queryExit() for QT docks.
161 */
saveCurrentState(const QString & postfix)162 void QCGTopLevel::saveCurrentState(const QString& postfix)
163 {
164 QString eventType, eventType2;
165 if (_eventType) eventType = _eventType->name();
166 if (_eventType2) eventType2 = _eventType2->name();
167
168 ConfigGroup* stateConfig = ConfigStorage::group(QLatin1String("CurrentState") + postfix);
169 stateConfig->setValue(QStringLiteral("EventType"), eventType);
170 stateConfig->setValue(QStringLiteral("EventType2"), eventType2);
171 stateConfig->setValue(QStringLiteral("GroupType"), ProfileContext::typeName(_groupType));
172 delete stateConfig;
173
174 _partSelection->saveOptions(QStringLiteral("PartOverview"), postfix);
175 _multiView->saveLayout(QStringLiteral("MainView"), postfix);
176 _multiView->saveOptions(QStringLiteral("MainView"), postfix);
177 }
178
179 /**
180 * This function is called when a trace is closed.
181 * Save browsing position for later restoring
182 */
saveTraceSettings()183 void QCGTopLevel::saveTraceSettings()
184 {
185 QString key = traceKey();
186
187 ConfigGroup* lConfig = ConfigStorage::group(QStringLiteral("Layouts"));
188 lConfig->setValue(QStringLiteral("Count%1").arg(key), _layoutCount);
189 lConfig->setValue(QStringLiteral("Current%1").arg(key), _layoutCurrent);
190 delete lConfig;
191
192 ConfigGroup* pConfig = ConfigStorage::group(QStringLiteral("TracePositions"));
193 QString eventType, eventType2;
194 if (_eventType) eventType = _eventType->name();
195 if (_eventType2) eventType2 = _eventType2->name();
196 pConfig->setValue(QStringLiteral("EventType%1").arg(key), eventType);
197 pConfig->setValue(QStringLiteral("EventType2%1").arg(key), eventType2);
198 if (_groupType != ProfileContext::InvalidType)
199 pConfig->setValue(QStringLiteral("GroupType%1").arg(key),
200 ProfileContext::typeName(_groupType));
201
202 if (_data) {
203 if (_group)
204 pConfig->setValue(QStringLiteral("Group%1").arg(key), _group->name());
205 saveCurrentState(key);
206 }
207 delete pConfig;
208 }
209
210 /**
211 * This restores the current visualization state of the main window and
212 * of the profile views.
213 */
restoreCurrentState(const QString & postfix)214 void QCGTopLevel::restoreCurrentState(const QString& postfix)
215 {
216 _partSelection->restoreOptions(QStringLiteral("PartOverview"), postfix);
217 _multiView->restoreLayout(QStringLiteral("MainView"), postfix);
218 _multiView->restoreOptions(QStringLiteral("MainView"), postfix);
219
220 _splittedToggleAction->setChecked(_multiView->childCount()>1);
221 _splitDirectionToggleAction->setEnabled(_multiView->childCount()>1);
222 _splitDirectionToggleAction->setChecked(_multiView->orientation() ==
223 Qt::Horizontal);
224 }
225
sidebarMenuAboutToShow()226 void QCGTopLevel::sidebarMenuAboutToShow()
227 {
228 QAction* action;
229 QMenu *popup = _sidebarMenuAction->menu();
230
231 popup->clear();
232
233 action = popup->addAction(tr("Parts Overview"));
234 action->setCheckable(true);
235 action->setChecked(_partDock->isVisible());
236 connect(action, &QAction::triggered, this, &QCGTopLevel::togglePartDock);
237
238 action = popup->addAction(tr("Top Cost Call Stack"));
239 action->setCheckable(true);
240 action->setChecked(_stackDock->isVisible());
241 connect(action, &QAction::triggered, this, &QCGTopLevel::toggleStackDock);
242
243 action = popup->addAction(tr("Flat Profile"));
244 action->setCheckable(true);
245 action->setChecked(_functionDock->isVisible());
246 connect(action, &QAction::triggered, this, &QCGTopLevel::toggleFunctionDock);
247 }
248
recentFilesMenuAboutToShow()249 void QCGTopLevel::recentFilesMenuAboutToShow()
250 {
251 QStringList recentFiles;
252 QMenu *popup = _recentFilesMenuAction->menu();
253
254 popup->clear();
255
256 ConfigGroup* generalConfig = ConfigStorage::group(QStringLiteral("GeneralSettings"));
257 recentFiles = generalConfig->value(QStringLiteral("RecentFiles"),
258 QStringList()).toStringList();
259 delete generalConfig;
260
261 if (recentFiles.isEmpty())
262 popup->addAction(tr("(No recent files)"));
263 else {
264 foreach(const QString& file, recentFiles) {
265 // paths shown to user should use OS-native separators
266 popup->addAction(QDir::toNativeSeparators(file));
267 }
268 }
269 }
270
recentFilesTriggered(QAction * action)271 void QCGTopLevel::recentFilesTriggered(QAction* action)
272 {
273 if (action)
274 load(QStringList(QDir::fromNativeSeparators(action->text())));
275 }
276
primaryAboutToShow()277 void QCGTopLevel::primaryAboutToShow()
278 {
279 updateEventTypeMenu(_primaryMenuAction->menu(), false);
280 }
281
secondaryAboutToShow()282 void QCGTopLevel::secondaryAboutToShow()
283 {
284 updateEventTypeMenu(_secondaryMenuAction->menu(), true);
285 }
286
groupingAboutToShow()287 void QCGTopLevel::groupingAboutToShow()
288 {
289 if (!_functionSelection) return;
290 _functionSelection->updateGroupingMenu(_groupingMenuAction->menu());
291 }
292
293
294
createDocks()295 void QCGTopLevel::createDocks()
296 {
297 // part visualization/selection side bar
298 _partDock = new QDockWidget(this);
299 _partDock->setObjectName(QStringLiteral("part-dock"));
300 _partDock->setWindowTitle(tr("Parts Overview"));
301 _partSelection = new PartSelection(this, _partDock);
302 _partDock->setWidget(_partSelection);
303
304 connect(_partSelection, &PartSelection::partsHideSelected,
305 this, &QCGTopLevel::partsHideSelectedSlotDelayed);
306 connect(_partSelection, &PartSelection::partsUnhideAll,
307 this, &QCGTopLevel::partsUnhideAllSlotDelayed);
308
309 // stack selection side bar
310 _stackDock = new QDockWidget(this);
311 _stackDock->setObjectName(QStringLiteral("stack-dock"));
312 _stackSelection = new StackSelection(_stackDock);
313 _stackDock->setWidget(_stackSelection);
314 _stackDock->setWindowTitle(tr("Top Cost Call Stack"));
315 _stackSelection->setWhatsThis( tr(
316 "<b>The Top Cost Call Stack</b>"
317 "<p>This is a purely fictional 'most probable' call stack. "
318 "It is built up by starting with the current selected "
319 "function and adds the callers/callees with highest cost "
320 "at the top and to bottom.</p>"
321 "<p>The <b>Cost</b> and <b>Calls</b> columns show the "
322 "cost used for all calls from the function in the line "
323 "above.</p>"));
324 connect(_stackSelection, SIGNAL(functionSelected(CostItem*)),
325 this, SLOT(setTraceItemDelayed(CostItem*)));
326 // actions are already created
327 connect(_upAction, &QAction::triggered,
328 _stackSelection, &StackSelection::browserUp );
329 connect(_backAction, &QAction::triggered,
330 _stackSelection, &StackSelection::browserBack );
331 connect(_forwardAction, &QAction::triggered,
332 _stackSelection, &StackSelection::browserForward);
333
334 // flat function profile side bar
335 _functionDock = new QDockWidget(this);
336 _functionDock->setObjectName(QStringLiteral("function-dock"));
337 _functionDock->setWindowTitle(tr("Flat Profile"));
338 _functionSelection = new FunctionSelection(this, _functionDock);
339 _functionDock->setWidget(_functionSelection);
340 // functionDock needs call to updateView() when getting visible
341 connect(_functionDock, &QDockWidget::visibilityChanged,
342 this, &QCGTopLevel::functionVisibilityChanged);
343
344 // defaults (later to be adjusted from stored state in config)
345 addDockWidget(Qt::LeftDockWidgetArea, _partDock );
346 addDockWidget(Qt::LeftDockWidgetArea, _stackDock );
347 addDockWidget(Qt::LeftDockWidgetArea, _functionDock );
348 _stackDock->hide();
349 _partDock->hide();
350 }
351
352
353
354
createActions()355 void QCGTopLevel::createActions()
356 {
357 QString hint;
358 QIcon icon;
359
360 // file menu actions
361 _newAction = new QAction(tr("&New"), this);
362 _newAction->setShortcuts(QKeySequence::New);
363 _newAction->setStatusTip(tr("Open new empty window"));
364 connect(_newAction, &QAction::triggered, this, &QCGTopLevel::newWindow);
365
366 icon = QApplication::style()->standardIcon(QStyle::SP_DialogOpenButton);
367 _openAction = new QAction(icon, tr("&Open..."), this);
368 _openAction->setShortcuts(QKeySequence::Open);
369 _openAction->setStatusTip(tr("Open profile data file"));
370 connect(_openAction, SIGNAL(triggered()), this, SLOT(load()));
371
372 _closeAction = new QAction(tr("&Close"), this);
373 _closeAction->setShortcuts(QKeySequence::Close);
374 _closeAction->setStatusTip(tr("Close the current window"));
375 connect(_closeAction, SIGNAL(triggered()), this, SLOT(close()));
376
377 _addAction = new QAction(tr( "&Add..." ), this);
378 _addAction->setStatusTip(tr("Add profile data to current window"));
379 connect(_addAction, SIGNAL(triggered(bool)), SLOT(add()));
380
381 _exportAction = new QAction(tr("Export Graph"), this);
382 _exportAction->setStatusTip(tr("Generate GraphViz file 'callgraph.dot'"));
383 connect(_exportAction, &QAction::triggered, this, &QCGTopLevel::exportGraph);
384
385 _recentFilesMenuAction = new QAction(tr("Open &Recent"), this);
386 _recentFilesMenuAction->setMenu(new QMenu(this));
387 connect(_recentFilesMenuAction->menu(), &QMenu::aboutToShow,
388 this, &QCGTopLevel::recentFilesMenuAboutToShow);
389 connect(_recentFilesMenuAction->menu(), &QMenu::triggered,
390 this, &QCGTopLevel::recentFilesTriggered);
391
392 _exitAction = new QAction(tr("E&xit"), this);
393 _exitAction->setMenuRole(QAction::QuitRole);
394 _exitAction->setShortcut(tr("Ctrl+Q"));
395 _exitAction->setStatusTip(tr("Exit the application"));
396 connect(_exitAction, &QAction::triggered, this, &QApplication::closeAllWindows);
397
398 // view menu actions
399
400 _primaryMenuAction = new QAction(tr( "Primary Event Type" ), this );
401 _primaryMenuAction->setMenu(new QMenu(this));
402 connect(_primaryMenuAction->menu(), &QMenu::aboutToShow,
403 this, &QCGTopLevel::primaryAboutToShow );
404 _secondaryMenuAction = new QAction(tr( "Secondary Event Type" ), this );
405 _secondaryMenuAction->setMenu(new QMenu(this));
406 connect(_secondaryMenuAction->menu(), &QMenu::aboutToShow,
407 this, &QCGTopLevel::secondaryAboutToShow );
408 _groupingMenuAction = new QAction(tr( "Grouping" ), this );
409 _groupingMenuAction->setMenu(new QMenu(this));
410 connect(_groupingMenuAction->menu(), &QMenu::aboutToShow,
411 this, &QCGTopLevel::groupingAboutToShow );
412
413 icon = QApplication::style()->standardIcon(QStyle::SP_BrowserReload);
414 _cyclesToggleAction = new QAction(icon, tr("Detect Cycles"), this);
415 _cyclesToggleAction->setCheckable(true);
416 _cyclesToggleAction->setStatusTip(tr("Do Cycle Detection"));
417 hint = tr("<b>Detect recursive cycles</b>"
418 "<p>If this is switched off, the treemap drawing will show "
419 "black areas when a recursive call is made instead of drawing "
420 "the recursion ad infinitum. Note that "
421 "the size of black areas often will be wrong, as inside "
422 "recursive cycles the cost of calls cannot be determined; "
423 "the error is small, "
424 "however, for false cycles (see documentation).</p>"
425 "<p>The correct handling for cycles is to detect them and "
426 "collapse all functions of a cycle into an artificial "
427 "function, which is done when this option is selected. "
428 "Unfortunately, with GUI applications, this often will "
429 "lead to huge false cycles, making the analysis impossible; "
430 "therefore, there is the option to switch this off.</p>");
431 _cyclesToggleAction->setWhatsThis(hint);
432 connect(_cyclesToggleAction, &QAction::triggered,
433 this, &QCGTopLevel::toggleCycles);
434 _cyclesToggleAction->setChecked(GlobalConfig::showCycles());
435
436 _percentageToggleAction = new QAction(QIcon(QStringLiteral(":/percent.png")),
437 tr("Relative Cost"), this);
438 _percentageToggleAction->setCheckable(true);
439 _percentageToggleAction->setStatusTip(tr("Show Relative Costs"));
440 connect(_percentageToggleAction, &QAction::triggered,
441 this, &QCGTopLevel::togglePercentage);
442 _percentageToggleAction->setChecked(GlobalConfig::showPercentage());
443
444 _hideTemplatesToggleAction = new QAction(QIcon(QStringLiteral(":/hidetemplates.png")),
445 tr("Shorten Templates"), this);
446 _hideTemplatesToggleAction->setCheckable(true);
447 _hideTemplatesToggleAction->setStatusTip(tr("Hide Template Parameters "
448 "in C++ Symbols"));
449 connect(_hideTemplatesToggleAction, &QAction::triggered,
450 this, &QCGTopLevel::toggleHideTemplates);
451 _hideTemplatesToggleAction->setChecked(GlobalConfig::hideTemplates());
452 hint = tr("<b>Hide Template Parameters in C++ Symbols</b>"
453 "<p>If this is switched on, every symbol displayed will have "
454 "any C++ template parameters hidden, just showing <> "
455 "instead of a potentially nested template parameter.</p>"
456 "<p>In this mode, you can hover the mouse pointer over the "
457 "activated symbol label to show a tooltip with the "
458 "unabbreviated symbol.</p>");
459 _hideTemplatesToggleAction->setWhatsThis(hint);
460
461 _expandedToggleAction = new QAction(QIcon(QStringLiteral(":/move.png")),
462 tr("Relative to Parent"), this);
463 _expandedToggleAction->setCheckable(true);
464 _expandedToggleAction->setStatusTip(
465 tr("Show Percentage relative to Parent"));
466 hint = tr("<b>Show percentage costs relative to parent</b>"
467 "<p>If this is switched off, percentage costs are always "
468 "shown relative to the total cost of the profile part(s) "
469 "that are currently browsed. By turning on this option, "
470 "percentage cost of shown cost items will be relative "
471 "to the parent cost item.</p>"
472 "<ul><table>"
473 "<tr><td><b>Cost Type</b></td><td><b>Parent Cost</b></td></tr>"
474 "<tr><td>Function Inclusive</td><td>Total</td></tr>"
475 "<tr><td>Function Self</td><td>Function Group (*)/Total</td></tr>"
476 "<tr><td>Call</td><td>Function Inclusive</td></tr>"
477 "<tr><td>Source Line</td><td>Function Inclusive</td></tr>"
478 "</table></ul>"
479 "<p>(*) Only if function grouping is switched on "
480 "(e.g. ELF object grouping).</p>");
481 _expandedToggleAction->setWhatsThis( hint );
482 connect(_expandedToggleAction, &QAction::triggered,
483 this, &QCGTopLevel::toggleExpanded);
484 _expandedToggleAction->setChecked(GlobalConfig::showExpanded());
485
486 _splittedToggleAction = new QAction(tr("Split Visualization"), this);
487 _splittedToggleAction->setCheckable(true);
488 _splittedToggleAction->setStatusTip(
489 tr("Show visualization of two cost items"));
490 connect(_splittedToggleAction, &QAction::triggered,
491 this, &QCGTopLevel::toggleSplitted);
492
493 _splitDirectionToggleAction = new QAction(tr("Split Horizontal"), this);
494 _splitDirectionToggleAction->setCheckable(true);
495 _splitDirectionToggleAction->setStatusTip(
496 tr("Split visualization area horizontally"));
497 connect(_splitDirectionToggleAction, &QAction::triggered,
498 this, &QCGTopLevel::toggleSplitDirection);
499
500 _sidebarMenuAction = new QAction(tr("Sidebars"), this);
501 _sidebarMenuAction->setMenu(new QMenu(this));
502 connect( _sidebarMenuAction->menu(), &QMenu::aboutToShow,
503 this, &QCGTopLevel::sidebarMenuAboutToShow);
504
505 _layoutDup = new QAction(tr("&Duplicate"), this);
506 connect(_layoutDup, &QAction::triggered, this, &QCGTopLevel::layoutDuplicate);
507 _layoutDup->setShortcut(Qt::CTRL + Qt::Key_Plus);
508 _layoutDup->setStatusTip(tr("Duplicate current layout"));
509
510 _layoutRemove = new QAction(tr("&Remove"), this);
511 connect(_layoutRemove, &QAction::triggered, this, &QCGTopLevel::layoutRemove);
512 _layoutRemove->setStatusTip(tr("Remove current layout"));
513
514 _layoutNext = new QAction(tr("Go to &Next"), this);
515 connect(_layoutNext, &QAction::triggered, this, &QCGTopLevel::layoutNext);
516 _layoutNext->setShortcut(Qt::CTRL + Qt::Key_Right);
517 _layoutNext->setStatusTip(tr("Switch to next layout"));
518
519 _layoutPrev = new QAction(tr("Go to &Previous"), this);
520 connect(_layoutPrev, &QAction::triggered, this, &QCGTopLevel::layoutPrevious);
521 _layoutPrev->setShortcut(Qt::CTRL + Qt::Key_Left);
522 _layoutPrev->setStatusTip(tr("Switch to previous layout"));
523
524 _layoutRestore = new QAction(tr("&Restore to Default"), this);
525 connect(_layoutRestore, &QAction::triggered, this, &QCGTopLevel::layoutRestore);
526 _layoutRestore->setStatusTip(tr("Restore layouts to default"));
527
528 _layoutSave = new QAction(tr("&Save as Default"), this);
529 connect(_layoutSave, &QAction::triggered, this, &QCGTopLevel::layoutSave);
530 _layoutSave->setStatusTip(tr("Save layouts as default"));
531
532 // go menu actions
533 icon = QApplication::style()->standardIcon(QStyle::SP_ArrowUp);
534 _upAction = new QAction(icon, tr( "Up" ), this );
535 _upAction->setShortcut( QKeySequence(Qt::ALT+Qt::Key_Up) );
536 _upAction->setStatusTip(tr("Go Up in Call Stack"));
537 _upAction->setMenu(new QMenu(this));
538 connect(_upAction->menu(), &QMenu::aboutToShow,
539 this, &QCGTopLevel::upAboutToShow );
540 connect(_upAction->menu(), &QMenu::triggered,
541 this, &QCGTopLevel::upTriggered );
542 hint = tr("Go to last selected caller of current function");
543 _upAction->setToolTip(hint);
544
545 icon = QApplication::style()->standardIcon(QStyle::SP_ArrowBack);
546 _backAction = new QAction(icon, tr("Back"), this);
547 _backAction->setShortcut( QKeySequence(Qt::ALT+Qt::Key_Left) );
548 _backAction->setStatusTip(tr("Go Back"));
549 _backAction->setMenu(new QMenu(this));
550 connect(_backAction->menu(), &QMenu::aboutToShow,
551 this, &QCGTopLevel::backAboutToShow );
552 connect(_backAction->menu(), &QMenu::triggered,
553 this, &QCGTopLevel::backTriggered );
554 hint = tr("Go back in function selection history");
555 _backAction->setToolTip(hint);
556
557 icon = QApplication::style()->standardIcon(QStyle::SP_ArrowForward);
558 _forwardAction = new QAction(icon, tr("Forward"), this);
559 _forwardAction->setShortcut( QKeySequence(Qt::ALT+Qt::Key_Right) );
560 _forwardAction->setStatusTip(tr("Go Forward"));
561 _forwardAction->setMenu(new QMenu(this));
562 connect(_forwardAction->menu(), &QMenu::aboutToShow,
563 this, &QCGTopLevel::forwardAboutToShow );
564 connect(_forwardAction->menu(), &QMenu::triggered,
565 this, &QCGTopLevel::forwardTriggered );
566 hint = tr("Go forward in function selection history");
567 _forwardAction->setToolTip( hint );
568
569 // settings menu actions
570 _configureAction = new QAction(tr("&Configure..."), this);
571 _configureAction->setMenuRole(QAction::PreferencesRole);
572 _configureAction->setStatusTip(tr("Configure QCachegrind"));
573 connect(_configureAction, SIGNAL(triggered()), this, SLOT(configure()));
574
575 // window menu actions
576 _minimizeAction = new QAction(tr("&Minimize"), this);
577 _minimizeAction->setShortcut(Qt::CTRL + Qt::Key_M);
578 connect(_minimizeAction, &QAction::triggered, this, &QWidget::showMinimized);
579
580 _zoomAction = new QAction(tr("&Zoom"), this);
581 // on macOS, zoom doesn't have exactly the same semantics as maximize
582 // (it creates the best fit), but maximize is the usual copout answer
583 connect(_zoomAction, &QAction::triggered, this, &QWidget::showMaximized);
584
585 // help menu actions
586 _aboutAction = new QAction(tr("&About QCachegrind..."), this);
587 _aboutAction->setMenuRole(QAction::AboutRole);
588 _aboutAction->setStatusTip(tr("Show the application's About box"));
589 connect(_aboutAction, &QAction::triggered, this, &QCGTopLevel::about);
590
591 _aboutQtAction = new QAction(tr("About Qt..."), this);
592 _aboutQtAction->setMenuRole(QAction::AboutQtRole);
593 connect(_aboutQtAction, &QAction::triggered, qApp, &QApplication::aboutQt);
594
595 // toolbar actions
596 _eventTypeBox = new QComboBox(this);
597 _eventTypeBox->setMinimumContentsLength(25);
598 hint = tr("Select primary event type of costs");
599 _eventTypeBox->setToolTip( hint );
600 connect( _eventTypeBox, SIGNAL(activated(QString)),
601 this, SLOT(eventTypeSelected(QString)));
602 }
603
createMenu()604 void QCGTopLevel::createMenu()
605 {
606 QMenuBar* mBar = menuBar();
607
608 QMenu* fileMenu = mBar->addMenu(tr("&File"));
609 fileMenu->addAction(_newAction);
610 fileMenu->addAction(_openAction);
611 fileMenu->addAction(_recentFilesMenuAction);
612 fileMenu->addAction(_addAction);
613 fileMenu->addSeparator();
614 fileMenu->addAction(_exportAction);
615 fileMenu->addSeparator();
616 fileMenu->addAction(_closeAction);
617 fileMenu->addSeparator();
618 fileMenu->addAction(_exitAction);
619
620 QMenu* layoutMenu = new QMenu(tr("&Layout"), this);
621 layoutMenu->addAction(_layoutDup);
622 layoutMenu->addAction(_layoutRemove);
623 layoutMenu->addSeparator();
624 layoutMenu->addAction(_layoutPrev);
625 layoutMenu->addAction(_layoutNext);
626 layoutMenu->addSeparator();
627 layoutMenu->addAction(_layoutSave);
628 layoutMenu->addAction(_layoutRestore);
629
630 QMenu* viewMenu = mBar->addMenu(tr("&View"));
631 viewMenu->addAction(_primaryMenuAction);
632 viewMenu->addAction(_secondaryMenuAction);
633 viewMenu->addAction(_groupingMenuAction);
634 viewMenu->addSeparator();
635 viewMenu->addAction(tb->toggleViewAction());
636 viewMenu->addMenu(layoutMenu);
637 viewMenu->addAction(_sidebarMenuAction);
638 viewMenu->addAction(_splittedToggleAction);
639 viewMenu->addAction(_splitDirectionToggleAction);
640 viewMenu->addSeparator();
641 viewMenu->addAction(_cyclesToggleAction);
642 viewMenu->addAction(_percentageToggleAction);
643 viewMenu->addAction(_expandedToggleAction);
644 viewMenu->addAction(_hideTemplatesToggleAction);
645 viewMenu->addSeparator();
646 viewMenu->addAction(_configureAction);
647
648 QMenu* goMenu = mBar->addMenu(tr("&Go"));
649 goMenu->addAction(_backAction);
650 goMenu->addAction(_forwardAction);
651 goMenu->addAction(_upAction);
652 fileMenu->addAction(_exitAction);
653
654 #ifdef Q_OS_MAC
655 // class level for ease of manipulation
656 this->windowMenu = mBar->addMenu(tr("&Window"));
657 this->windowMenu->addAction(_minimizeAction);
658 this->windowMenu->addAction(_zoomAction);
659 connect(this->windowMenu, &QMenu::aboutToShow, this, &QCGTopLevel::windowListAboutToShow);
660 connect(this->windowMenu, &QMenu::triggered, this, &QCGTopLevel::windowListTriggered);
661
662 // right-clicking the dock icon should be a window list
663 this->macDockMenu = new QMenu(this);
664 connect(this->macDockMenu, &QMenu::aboutToShow, this, &QCGTopLevel::macDockMenuAboutToShow);
665 // it can reuse the same events, it just needs a diff menu structure
666 connect(this->macDockMenu, &QMenu::triggered, this, &QCGTopLevel::windowListTriggered);
667 reinstallMacDock();
668 #endif
669
670 QMenu* helpMenu = mBar->addMenu(tr("&Help"));
671 helpMenu->addAction(QWhatsThis::createAction(this));
672 helpMenu->addSeparator();
673 helpMenu->addAction(_aboutAction);
674 helpMenu->addAction(_aboutQtAction);
675 }
676
createToolbar()677 void QCGTopLevel::createToolbar()
678 {
679 tb = new QToolBar(tr("Main Toolbar"), this);
680 tb->setObjectName(QStringLiteral("main-toolbar"));
681 addToolBar(Qt::TopToolBarArea, tb);
682
683 tb->addAction(_openAction);
684 tb->addSeparator();
685
686 tb->addAction(_cyclesToggleAction);
687 tb->addAction(_percentageToggleAction);
688 tb->addAction(_expandedToggleAction);
689 tb->addAction(_hideTemplatesToggleAction);
690 tb->addSeparator();
691
692 tb->addAction(_backAction);
693 tb->addAction(_forwardAction);
694 tb->addAction(_upAction);
695 tb->addSeparator();
696
697 tb->addWidget(_eventTypeBox);
698 }
699
700
about()701 void QCGTopLevel::about()
702 {
703 QString text, version;
704 version = QStringLiteral("0.8.0kde");
705 text = QStringLiteral("<h3>QCachegrind %1</h3>").arg(version);
706 text += tr("<p>QCachegrind is a graphical user interface for analysing "
707 "profiling data, which helps in the performance optimization "
708 "phase of developing a computer program. "
709 "QCachegrind is open-source, and it is distributed under the "
710 "terms of the GPL v2. For details and source code, see the "
711 "<a href=\"https://kcachegrind.github.io\">homepage</a> of the "
712 "KCachegrind project.</p>"
713 "Main author and maintainer: "
714 "<a href=\"mailto:Josef.Weidendorfer@gmx.de\">"
715 "Josef Weidendorfer</a><br>"
716 "(with lots of bug fixes/porting help by the KDE community)");
717 QMessageBox::about(this, tr("About QCachegrind"), text);
718 }
719
configure(QString s)720 void QCGTopLevel::configure(QString s)
721 {
722 static QString lastPage;
723
724 // if no specific config item should be focused, use last page
725 if (s.isEmpty()) s = lastPage;
726 ConfigDialog d(_data, this, s);
727
728 if (d.exec() == QDialog::Accepted) {
729 GlobalConfig::config()->saveOptions();
730 configChanged();
731 }
732 lastPage = d.currentPage();
733 }
734
togglePartDock()735 void QCGTopLevel::togglePartDock()
736 {
737 if (!_partDock->isVisible())
738 _partDock->show();
739 else
740 _partDock->hide();
741 }
742
toggleStackDock()743 void QCGTopLevel::toggleStackDock()
744 {
745 if (!_stackDock->isVisible())
746 _stackDock->show();
747 else
748 _stackDock->hide();
749 }
750
toggleFunctionDock()751 void QCGTopLevel::toggleFunctionDock()
752 {
753 if (!_functionDock->isVisible())
754 _functionDock->show();
755 else
756 _functionDock->hide();
757 }
758
togglePercentage()759 void QCGTopLevel::togglePercentage()
760 {
761 setPercentage(_percentageToggleAction->isChecked());
762 }
763
764
setAbsoluteCost()765 void QCGTopLevel::setAbsoluteCost()
766 {
767 setPercentage(false);
768 }
769
setRelativeCost()770 void QCGTopLevel::setRelativeCost()
771 {
772 setPercentage(true);
773 }
774
setPercentage(bool show)775 void QCGTopLevel::setPercentage(bool show)
776 {
777 if (GlobalConfig::showPercentage() == show) return;
778 if (_percentageToggleAction->isChecked() != show)
779 _percentageToggleAction->setChecked(show);
780 _expandedToggleAction->setEnabled(show);
781 GlobalConfig::setShowPercentage(show);
782
783 _partSelection->notifyChange(TraceItemView::configChanged);
784 _stackSelection->refresh();
785 _functionSelection->notifyChange(TraceItemView::configChanged);
786 _multiView->notifyChange(TraceItemView::configChanged);
787 }
788
toggleHideTemplates()789 void QCGTopLevel::toggleHideTemplates()
790 {
791 bool show = _hideTemplatesToggleAction->isChecked();
792 if (GlobalConfig::hideTemplates() == show) return;
793 GlobalConfig::setHideTemplates(show);
794
795 _partSelection->notifyChange(TraceItemView::configChanged);
796 _stackSelection->refresh();
797 _functionSelection->notifyChange(TraceItemView::configChanged);
798 _multiView->notifyChange(TraceItemView::configChanged);
799 }
800
toggleExpanded()801 void QCGTopLevel::toggleExpanded()
802 {
803 bool show = _expandedToggleAction->isChecked();
804 if (GlobalConfig::showExpanded() == show) return;
805 GlobalConfig::setShowExpanded(show);
806
807 _partSelection->notifyChange(TraceItemView::configChanged);
808 _stackSelection->refresh();
809 _functionSelection->notifyChange(TraceItemView::configChanged);
810 _multiView->notifyChange(TraceItemView::configChanged);
811 }
812
toggleCycles()813 void QCGTopLevel::toggleCycles()
814 {
815 bool show = _cyclesToggleAction->isChecked();
816 if (GlobalConfig::showCycles() == show) return;
817 GlobalConfig::setShowCycles(show);
818
819 if (!_data) return;
820
821 _data->invalidateDynamicCost();
822 _data->updateFunctionCycles();
823
824 _partSelection->notifyChange(TraceItemView::configChanged);
825 _stackSelection->rebuildStackList();
826 _functionSelection->notifyChange(TraceItemView::configChanged);
827 _multiView->notifyChange(TraceItemView::configChanged);
828 }
829
830
functionVisibilityChanged(bool v)831 void QCGTopLevel::functionVisibilityChanged(bool v)
832 {
833 if (v)
834 _functionSelection->updateView();
835 }
836
837
newWindow()838 void QCGTopLevel::newWindow()
839 {
840 QCGTopLevel* t = new QCGTopLevel();
841 t->show();
842 }
843
844
load()845 void QCGTopLevel::load()
846 {
847 QStringList files;
848 files = QFileDialog::getOpenFileNames(this,
849 tr("Open Callgrind Data"),
850 _lastFile,
851 tr("Callgrind Files (callgrind.* cachegrind.*);;All Files (*)"));
852 load(files);
853 }
854
load(QStringList files,bool addToRecentFiles)855 void QCGTopLevel::load(QStringList files, bool addToRecentFiles)
856 {
857 if (files.isEmpty()) return;
858 _lastFile = files[0];
859
860 if (_data && _data->parts().count()>0) {
861
862 // In new window
863 QCGTopLevel* t = new QCGTopLevel();
864 t->show();
865 t->loadDelayed(files, addToRecentFiles);
866 return;
867 }
868
869 // this constructor enables progress bar callbacks
870 TraceData* d = new TraceData(this);
871 int filesLoaded = d->load(files);
872 if (filesLoaded >0)
873 setData(d);
874
875 if (!addToRecentFiles) return;
876
877 // add to recent file list in config
878 QStringList recentFiles;
879 ConfigGroup* generalConfig = ConfigStorage::group(QStringLiteral("GeneralSettings"));
880 recentFiles = generalConfig->value(QStringLiteral("RecentFiles"),
881 QStringList()).toStringList();
882 foreach(const QString& file, files) {
883 recentFiles.removeAll(file);
884 if (filesLoaded >0)
885 recentFiles.prepend(file);
886 if (recentFiles.count() >5)
887 recentFiles.removeLast();
888 }
889 generalConfig->setValue(QStringLiteral("RecentFiles"), recentFiles);
890 delete generalConfig;
891 }
892
893
add()894 void QCGTopLevel::add()
895 {
896 QStringList files;
897 files = QFileDialog::getOpenFileNames(this,
898 tr("Add Callgrind Data"),
899 _lastFile,
900 tr("Callgrind Files (callgrind.*);;All Files (*)"));
901 add(files);
902 }
903
904
add(QStringList files)905 void QCGTopLevel::add(QStringList files)
906 {
907 if (files.isEmpty()) return;
908 _lastFile = files[0];
909
910 if (_data) {
911 _data->load(files);
912
913 // GUI update for added data
914 configChanged();
915 return;
916 }
917
918 // this constructor enables progress bar callbacks
919 TraceData* d = new TraceData(this);
920 int filesLoaded = d->load(files);
921 if (filesLoaded >0)
922 setData(d);
923 }
924
loadDelayed(QString file,bool addToRecentFiles)925 void QCGTopLevel::loadDelayed(QString file, bool addToRecentFiles)
926 {
927 _loadFilesDelayed << file;
928
929 _addToRecentFiles = addToRecentFiles;
930 QTimer::singleShot(0, this, &QCGTopLevel::loadFilesDelayed);
931 }
932
loadDelayed(QStringList files,bool addToRecentFiles)933 void QCGTopLevel::loadDelayed(QStringList files, bool addToRecentFiles)
934 {
935 _loadFilesDelayed << files;
936
937 _addToRecentFiles = addToRecentFiles;
938 QTimer::singleShot(0, this, &QCGTopLevel::loadFilesDelayed);
939 }
940
loadFilesDelayed()941 void QCGTopLevel::loadFilesDelayed()
942 {
943 if (_loadFilesDelayed.isEmpty()) return;
944
945 load(_loadFilesDelayed, _addToRecentFiles);
946 _loadFilesDelayed.clear();
947 }
948
949
exportGraph()950 void QCGTopLevel::exportGraph()
951 {
952 if (!_data || !_function) return;
953
954 GraphExporter::savePrompt(this, _data, _function, _eventType, _groupType, nullptr);
955 }
956
957
setEventType(QString s)958 bool QCGTopLevel::setEventType(QString s)
959 {
960 EventType* ct;
961
962 ct = (_data) ? _data->eventTypes()->type(s) : nullptr;
963
964 // if costtype with given name not found, use first available
965 if (!ct && _data) ct = _data->eventTypes()->type(0);
966
967 return setEventType(ct);
968 }
969
setEventType2(QString s)970 bool QCGTopLevel::setEventType2(QString s)
971 {
972 EventType* ct;
973
974 // Special type tr("(Hidden)") gives 0
975 ct = (_data) ? _data->eventTypes()->type(s) : nullptr;
976
977 return setEventType2(ct);
978 }
979
eventTypeSelected(const QString & s)980 void QCGTopLevel::eventTypeSelected(const QString& s)
981 {
982 EventType* ct;
983
984 ct = (_data) ? _data->eventTypes()->typeForLong(s) : nullptr;
985 setEventType(ct);
986 }
987
eventType2Selected(const QString & s)988 void QCGTopLevel::eventType2Selected(const QString& s)
989 {
990 EventType* ct;
991
992 ct = (_data) ? _data->eventTypes()->typeForLong(s) : nullptr;
993 setEventType2(ct);
994 }
995
setEventType(EventType * ct)996 bool QCGTopLevel::setEventType(EventType* ct)
997 {
998 if (_eventType == ct) return false;
999 _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
setEventType2(EventType * ct)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
groupTypeSelected(int cg)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
setGroupType(QString s)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
setGroupType(ProfileContext::Type gt)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
setGroup(QString s)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
setGroup(TraceCostItem * g)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
setFunction(QString s)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
setFunction(TraceFunction * f)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 */
setEventTypeDelayed(EventType * ct)1162 void QCGTopLevel::setEventTypeDelayed(EventType* ct)
1163 {
1164 _eventTypeDelayed = ct;
1165 QTimer::singleShot (0, this, SLOT(setEventTypeDelayed()));
1166 }
1167
setEventType2Delayed(EventType * ct)1168 void QCGTopLevel::setEventType2Delayed(EventType* ct)
1169 {
1170 _eventType2Delayed = ct;
1171 QTimer::singleShot (0, this, SLOT(setEventType2Delayed()));
1172 }
1173
setEventTypeDelayed()1174 void QCGTopLevel::setEventTypeDelayed()
1175 {
1176 setEventType(_eventTypeDelayed);
1177 }
1178
setEventType2Delayed()1179 void QCGTopLevel::setEventType2Delayed()
1180 {
1181 setEventType2(_eventType2Delayed);
1182 }
1183
setGroupTypeDelayed(ProfileContext::Type gt)1184 void QCGTopLevel::setGroupTypeDelayed(ProfileContext::Type gt)
1185 {
1186 _groupTypeDelayed = gt;
1187 QTimer::singleShot (0, this, SLOT(setGroupTypeDelayed()));
1188 }
1189
setGroupTypeDelayed()1190 void QCGTopLevel::setGroupTypeDelayed()
1191 {
1192 setGroupType(_groupTypeDelayed);
1193 }
1194
setGroupDelayed(TraceCostItem * g)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
setGroupDelayed()1207 void QCGTopLevel::setGroupDelayed()
1208 {
1209 setGroup(_groupDelayed);
1210 }
1211
setDirectionDelayed(TraceItemView::Direction d)1212 void QCGTopLevel::setDirectionDelayed(TraceItemView::Direction d)
1213 {
1214 _directionDelayed = d;
1215 QTimer::singleShot (0, this, SLOT(setDirectionDelayed()));
1216 }
1217
setDirectionDelayed()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
setTraceItemDelayed(CostItem * i)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
setTraceItemDelayed()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 */
setData(TraceData * data)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.
updateEventTypeMenu(QMenu * m,bool secondary)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
addEventTypeMenu(QMenu * popup,bool withCost2)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
setEventType(QAction * action)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
setEventType2(QAction * action)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
addGoMenu(QMenu * popup)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
goBack()1482 void QCGTopLevel::goBack()
1483 {
1484 setDirectionDelayed(TraceItemView::Back);
1485 }
1486
goForward()1487 void QCGTopLevel::goForward()
1488 {
1489 setDirectionDelayed(TraceItemView::Forward);
1490 }
1491
goUp()1492 void QCGTopLevel::goUp()
1493 {
1494 setDirectionDelayed(TraceItemView::Up);
1495 }
1496
traceKey()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
restoreTraceTypes()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 */
restoreTraceSettings()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
layoutDuplicate()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
layoutRemove()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
layoutNext()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
layoutPrevious()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
layoutSave()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
layoutRestore()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
updateLayoutActions()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
updateStatusBar()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
closeEvent(QCloseEvent * event)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
toggleSplitted()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
toggleSplitDirection()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
configChanged()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
activePartsChangedSlot(const TracePartList & list)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
partsHideSelectedSlotDelayed()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
partsHideSelectedSlot()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
partsUnhideAllSlotDelayed()1855 void QCGTopLevel::partsUnhideAllSlotDelayed()
1856 {
1857 QTimer::singleShot( 0, this, &QCGTopLevel::partsUnhideAllSlot );
1858 }
1859
1860 // this unhides all hidden parts. Does NOT change selection
partsUnhideAllSlot()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
insertWindowList(QMenu * menu)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
windowListAboutToShow()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
macDockMenuAboutToShow()1908 void QCGTopLevel::macDockMenuAboutToShow()
1909 {
1910 macDockMenu-> clear();
1911
1912 insertWindowList(macDockMenu);
1913
1914 macDockMenu->addSeparator();
1915 macDockMenu->addAction(_newAction);
1916 }
1917
forwardAboutToShow()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
backAboutToShow()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
upAboutToShow()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
windowListTriggered(QAction * action)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
forwardTriggered(QAction * action)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
backTriggered(QAction * action)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
upTriggered(QAction * action)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
showMessage(const QString & msg,int ms)2095 void QCGTopLevel::showMessage(const QString& msg, int ms)
2096 {
2097 if (_statusbar)
2098 _statusbar->showMessage(msg, ms);
2099 }
2100
showStatus(const QString & msg,int progress)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
loadStart(const QString & filename)2151 void QCGTopLevel::loadStart(const QString& filename)
2152 {
2153 showStatus(QStringLiteral("Loading %1").arg(filename), 0);
2154 Logger::_filename = filename;
2155 }
2156
loadFinished(const QString & msg)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
loadProgress(int progress)2165 void QCGTopLevel::loadProgress(int progress)
2166 {
2167 showStatus(QStringLiteral("Loading %1").arg(_filename), progress);
2168 }
2169
loadError(int line,const QString & msg)2170 void QCGTopLevel::loadError(int line, const QString& msg)
2171 {
2172 qCritical() << "Loading" << _filename
2173 << ":" << line << ": " << msg;
2174 }
2175
loadWarning(int line,const QString & msg)2176 void QCGTopLevel::loadWarning(int line, const QString& msg)
2177 {
2178 qWarning() << "Loading" << _filename
2179 << ":" << line << ": " << msg;
2180 }
2181
2182