qcgtoplevel.cpp 65.4 KB
Newer Older
1
/* This file is part of KCachegrind.
Josef Weidendorfer's avatar
Josef Weidendorfer committed
2
   Copyright (c) 2002-2016 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

   KCachegrind is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public
   License as published by the Free Software Foundation, version 2.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; see the file COPYING.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

/*
 * QCachegrind top level window
 */

#define TRACE_UPDATES 0

25
#include "qcgtoplevel.h"
26 27 28

#include <stdlib.h> // for system()

29
#include <QApplication>
30 31 32 33 34 35 36 37 38 39
#include <QDebug>
#include <QDockWidget>
#include <QTimer>
#include <QByteArray>
#include <QLabel>
#include <QMenuBar>
#include <QProgressBar>
#include <QFile>
#include <QFileDialog>
#include <QEventLoop>
40
#include <QToolBar>
41
#include <QComboBox>
42
#include <QMessageBox>
Laurent Montel's avatar
Laurent Montel committed
43
#include <QStatusBar>
44
#include <QWhatsThis>
Calvin Buckley's avatar
Calvin Buckley committed
45
#include <QWindow>
46

47
#ifdef QT_DBUS_SUPPORT
Yuri Chornoivan's avatar
Yuri Chornoivan committed
48
#include <QDBusConnection>
49 50
#endif

51
#include "partselection.h"
52
#include "functionselection.h"
53 54
#include "stackselection.h"
#include "stackbrowser.h"
55
#include "tracedata.h"
56
#include "config.h"
57
#include "globalguiconfig.h"
58 59
#include "multiview.h"
#include "callgraphview.h"
60
#include "configdialog.h"
61

62
QCGTopLevel::QCGTopLevel()
63
{
64
#ifdef QT_DBUS_SUPPORT
Josef Weidendorfer's avatar
Josef Weidendorfer committed
65 66
    QDBusConnection con = QDBusConnection::sessionBus();
    con.registerObject("/QCachegrind", this,
67
                       QDBusConnection::ExportScriptableSlots);
Josef Weidendorfer's avatar
Josef Weidendorfer committed
68
#endif
69

Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
70
    _progressBar = nullptr;
71 72 73
    _statusbar = statusBar();
    _statusLabel = new QLabel(_statusbar);
    _statusbar->addWidget(_statusLabel, 1);
74

75 76 77
    _layoutCount = 1;
    _layoutCurrent = 0;

78
    resetState();
79

80
    GlobalGUIConfig::config()->readOptions();
81

82
    createActions();
83 84
    createDocks();
    createToolbar();
85
    createMenu();
86

87
    _multiView = new MultiView(this, this);
88
    _multiView->setObjectName(QStringLiteral("MultiView"));
89
    setCentralWidget(_multiView);
90

91
    // restore current state settings (not configuration options)
92
    restoreCurrentState(QString());
93

94 95
    // restore docks & toolbars from config
    QByteArray state, geometry;
96 97 98 99
    ConfigGroup* topConfig = ConfigStorage::group(QStringLiteral("TopWindow"));
    _forcePartDock = topConfig->value(QStringLiteral("ForcePartDockVisible"), false).toBool();
    state = topConfig->value(QStringLiteral("State"), QByteArray()).toByteArray();
    geometry = topConfig->value(QStringLiteral("Geometry"), QByteArray()).toByteArray();
100
    delete topConfig;
101

102
    if (!geometry.isEmpty())
103
        restoreGeometry(geometry);
104
    if (!state.isEmpty())
105
        restoreState(state);
106

107
    setWindowIcon(QIcon(QStringLiteral(":/app.png")));
108
    setAttribute(Qt::WA_DeleteOnClose);
109 110
}

111
QCGTopLevel::~QCGTopLevel()
112 113 114
{
    delete _data;
}
115 116

// reset the visualization state, e.g. before loading new data
117
void QCGTopLevel::resetState()
118
{
119 120
    _activeParts.clear();
    _hiddenParts.clear();
121

Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
122 123 124 125
    _data = nullptr;
    _function = nullptr;
    _eventType = nullptr;
    _eventType2 = nullptr;
126
    _groupType = ProfileContext::InvalidType;
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
127
    _group = nullptr;
128

129
    // for delayed slots
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
130 131 132
    _traceItemDelayed = nullptr;
    _eventTypeDelayed = nullptr;
    _eventType2Delayed = nullptr;
133
    _groupTypeDelayed = ProfileContext::InvalidType;
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
134
    _groupDelayed = nullptr;
135
    _directionDelayed = TraceItemView::None;
Friedrich W. H. Kossebau's avatar
Friedrich W. H. Kossebau committed
136
    _lastSender = nullptr;
137 138
}

139

140 141 142 143 144 145 146
/**
 * This saves the current state of the main window and
 * sub widgets.
 *
 * No positions are saved. These is done automatically for
 * KToolbar, and manually in queryExit() for QT docks.
 */
147
void QCGTopLevel::saveCurrentState(const QString& postfix)
148
{
149 150 151
    QString eventType, eventType2;
    if (_eventType) eventType = _eventType->name();
    if (_eventType2) eventType2 = _eventType2->name();
152

153
    ConfigGroup* stateConfig = ConfigStorage::group(QLatin1String("CurrentState") + postfix);
154 155 156
    stateConfig->setValue(QStringLiteral("EventType"), eventType);
    stateConfig->setValue(QStringLiteral("EventType2"), eventType2);
    stateConfig->setValue(QStringLiteral("GroupType"), ProfileContext::typeName(_groupType));
157
    delete stateConfig;
158

159 160 161
    _partSelection->saveOptions(QStringLiteral("PartOverview"), postfix);
    _multiView->saveLayout(QStringLiteral("MainView"), postfix);
    _multiView->saveOptions(QStringLiteral("MainView"), postfix);
162 163 164 165 166 167
}

/**
 * This function is called when a trace is closed.
 * Save browsing position for later restoring
 */
168
void QCGTopLevel::saveTraceSettings()
169
{
170
    QString key = traceKey();
171

172 173 174
    ConfigGroup* lConfig = ConfigStorage::group(QStringLiteral("Layouts"));
    lConfig->setValue(QStringLiteral("Count%1").arg(key), _layoutCount);
    lConfig->setValue(QStringLiteral("Current%1").arg(key), _layoutCurrent);
175 176
    delete lConfig;

177
    ConfigGroup* pConfig = ConfigStorage::group(QStringLiteral("TracePositions"));
178 179 180
    QString eventType, eventType2;
    if (_eventType) eventType = _eventType->name();
    if (_eventType2) eventType2 = _eventType2->name();
181 182
    pConfig->setValue(QStringLiteral("EventType%1").arg(key), eventType);
    pConfig->setValue(QStringLiteral("EventType2%1").arg(key), eventType2);
183
    if (_groupType != ProfileContext::InvalidType)
184 185
        pConfig->setValue(QStringLiteral("GroupType%1").arg(key),
                          ProfileContext::typeName(_groupType));
186 187

    if (_data) {
188 189 190
        if (_group)
            pConfig->setValue(QStringLiteral("Group%1").arg(key), _group->name());
        saveCurrentState(key);
191 192
    }
    delete pConfig;
193 194 195
}

/**
196 197
 * This restores the current visualization state of the main window and
 * of the profile views.
198
 */
199
void QCGTopLevel::restoreCurrentState(const QString& postfix)
200
{
201 202 203
    _partSelection->restoreOptions(QStringLiteral("PartOverview"), postfix);
    _multiView->restoreLayout(QStringLiteral("MainView"), postfix);
    _multiView->restoreOptions(QStringLiteral("MainView"), postfix);
204

205 206 207
    _splittedToggleAction->setChecked(_multiView->childCount()>1);
    _splitDirectionToggleAction->setEnabled(_multiView->childCount()>1);
    _splitDirectionToggleAction->setChecked(_multiView->orientation() ==
208
                                            Qt::Horizontal);
209 210
}

211
void QCGTopLevel::sidebarMenuAboutToShow()
212 213 214 215 216 217 218 219 220
{
    QAction* action;
    QMenu *popup = _sidebarMenuAction->menu();

    popup->clear();

    action = popup->addAction(tr("Parts Overview"));
    action->setCheckable(true);
    action->setChecked(_partDock->isVisible());
221
    connect(action, &QAction::triggered, this, &QCGTopLevel::togglePartDock);
222 223 224 225

    action = popup->addAction(tr("Top Cost Call Stack"));
    action->setCheckable(true);
    action->setChecked(_stackDock->isVisible());
226
    connect(action, &QAction::triggered, this, &QCGTopLevel::toggleStackDock);
227 228 229 230

    action = popup->addAction(tr("Flat Profile"));
    action->setCheckable(true);
    action->setChecked(_functionDock->isVisible());
231
    connect(action, &QAction::triggered, this, &QCGTopLevel::toggleFunctionDock);
232 233
}

234
void QCGTopLevel::recentFilesMenuAboutToShow()
235 236 237 238 239 240
{
    QStringList recentFiles;
    QMenu *popup = _recentFilesMenuAction->menu();

    popup->clear();

241 242
    ConfigGroup* generalConfig = ConfigStorage::group(QStringLiteral("GeneralSettings"));
    recentFiles = generalConfig->value(QStringLiteral("RecentFiles"),
243
                                       QStringList()).toStringList();
244 245
    delete generalConfig;

246
    if (recentFiles.isEmpty())
247
        popup->addAction(tr("(No recent files)"));
248
    else {
249 250 251 252
        foreach(const QString& file, recentFiles) {
            // paths shown to user should use OS-native separators
            popup->addAction(QDir::toNativeSeparators(file));
        }
253 254 255
    }
}

256
void QCGTopLevel::recentFilesTriggered(QAction* action)
257 258
{
    if (action)
259
        load(QStringList(QDir::fromNativeSeparators(action->text())));
260
}
261

262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279
void QCGTopLevel::primaryAboutToShow()
{
    updateEventTypeMenu(_primaryMenuAction->menu(), false);
}

void QCGTopLevel::secondaryAboutToShow()
{
    updateEventTypeMenu(_secondaryMenuAction->menu(), true);
}

void QCGTopLevel::groupingAboutToShow()
{
    if (!_functionSelection) return;
    _functionSelection->updateGroupingMenu(_groupingMenuAction->menu());
}



280
void QCGTopLevel::createDocks()
281
{
282 283
    // part visualization/selection side bar
    _partDock = new QDockWidget(this);
284
    _partDock->setObjectName(QStringLiteral("part-dock"));
285 286 287 288
    _partDock->setWindowTitle(tr("Parts Overview"));
    _partSelection = new PartSelection(this, _partDock);
    _partDock->setWidget(_partSelection);

289
    connect(_partSelection, &PartSelection::partsHideSelected,
290
            this, &QCGTopLevel::partsHideSelectedSlotDelayed);
291
    connect(_partSelection, &PartSelection::partsUnhideAll,
292
            this, &QCGTopLevel::partsUnhideAllSlotDelayed);
293 294 295

    // stack selection side bar
    _stackDock = new QDockWidget(this);
296
    _stackDock->setObjectName(QStringLiteral("stack-dock"));
297 298 299 300
    _stackSelection = new StackSelection(_stackDock);
    _stackDock->setWidget(_stackSelection);
    _stackDock->setWindowTitle(tr("Top Cost Call Stack"));
    _stackSelection->setWhatsThis( tr(
301 302 303 304 305 306 307 308
                                       "<b>The Top Cost Call Stack</b>"
                                       "<p>This is a purely fictional 'most probable' call stack. "
                                       "It is built up by starting with the current selected "
                                       "function and adds the callers/callees with highest cost "
                                       "at the top and to bottom.</p>"
                                       "<p>The <b>Cost</b> and <b>Calls</b> columns show the "
                                       "cost used for all calls from the function in the line "
                                       "above.</p>"));
309
    connect(_stackSelection, SIGNAL(functionSelected(CostItem*)),
310
            this, SLOT(setTraceItemDelayed(CostItem*)));
311
    // actions are already created
312
    connect(_upAction, &QAction::triggered,
313
            _stackSelection, &StackSelection::browserUp );
314
    connect(_backAction, &QAction::triggered,
315
            _stackSelection, &StackSelection::browserBack );
316
    connect(_forwardAction, &QAction::triggered,
317
            _stackSelection, &StackSelection::browserForward);
318 319 320

    // flat function profile side bar
    _functionDock = new QDockWidget(this);
321
    _functionDock->setObjectName(QStringLiteral("function-dock"));
322 323 324 325
    _functionDock->setWindowTitle(tr("Flat Profile"));
    _functionSelection = new FunctionSelection(this, _functionDock);
    _functionDock->setWidget(_functionSelection);
    // functionDock needs call to updateView() when getting visible
326
    connect(_functionDock, &QDockWidget::visibilityChanged,
327
            this, &QCGTopLevel::functionVisibilityChanged);
328

329
    // defaults (later to be adjusted from stored state in config)
Josef Weidendorfer's avatar
Cleanup  
Josef Weidendorfer committed
330 331 332 333
    addDockWidget(Qt::LeftDockWidgetArea, _partDock );
    addDockWidget(Qt::LeftDockWidgetArea, _stackDock );
    addDockWidget(Qt::LeftDockWidgetArea, _functionDock );
    _stackDock->hide();
334
    _partDock->hide();
335 336 337 338 339
}




340
void QCGTopLevel::createActions()
341
{
342 343 344 345 346 347 348
    QString hint;
    QIcon icon;

    // file menu actions
    _newAction = new QAction(tr("&New"), this);
    _newAction->setShortcuts(QKeySequence::New);
    _newAction->setStatusTip(tr("Open new empty window"));
349
    connect(_newAction, &QAction::triggered, this, &QCGTopLevel::newWindow);
350 351 352 353 354

    icon = QApplication::style()->standardIcon(QStyle::SP_DialogOpenButton);
    _openAction = new QAction(icon, tr("&Open..."), this);
    _openAction->setShortcuts(QKeySequence::Open);
    _openAction->setStatusTip(tr("Open profile data file"));
355
    connect(_openAction, SIGNAL(triggered()), this, SLOT(load()));
356

357 358 359 360 361
    _closeAction = new QAction(tr("&Close"), this);
    _closeAction->setShortcuts(QKeySequence::Close);
    _closeAction->setStatusTip(tr("Close the current window"));
    connect(_closeAction, SIGNAL(triggered()), this, SLOT(close()));

362 363
    _addAction = new QAction(tr( "&Add..." ), this);
    _addAction->setStatusTip(tr("Add profile data to current window"));
364
    connect(_addAction, SIGNAL(triggered(bool)), SLOT(add()));
365 366 367

    _exportAction = new QAction(tr("Export Graph"), this);
    _exportAction->setStatusTip(tr("Generate GraphViz file 'callgraph.dot'"));
368
    connect(_exportAction, &QAction::triggered, this, &QCGTopLevel::exportGraph);
369 370 371

    _recentFilesMenuAction = new QAction(tr("Open &Recent"), this);
    _recentFilesMenuAction->setMenu(new QMenu(this));
372
    connect(_recentFilesMenuAction->menu(), &QMenu::aboutToShow,
373
            this, &QCGTopLevel::recentFilesMenuAboutToShow);
374
    connect(_recentFilesMenuAction->menu(), &QMenu::triggered,
375
            this, &QCGTopLevel::recentFilesTriggered);
376 377

    _exitAction = new QAction(tr("E&xit"), this);
378
    _exitAction->setMenuRole(QAction::QuitRole);
379 380
    _exitAction->setShortcut(tr("Ctrl+Q"));
    _exitAction->setStatusTip(tr("Exit the application"));
381
    connect(_exitAction, &QAction::triggered, this, &QApplication::closeAllWindows);
382 383

    // view menu actions
384 385 386 387

    _primaryMenuAction = new QAction(tr( "Primary Event Type" ), this );
    _primaryMenuAction->setMenu(new QMenu(this));
    connect(_primaryMenuAction->menu(), &QMenu::aboutToShow,
388
            this, &QCGTopLevel::primaryAboutToShow );
389 390 391
    _secondaryMenuAction = new QAction(tr( "Secondary Event Type" ), this );
    _secondaryMenuAction->setMenu(new QMenu(this));
    connect(_secondaryMenuAction->menu(), &QMenu::aboutToShow,
392
            this, &QCGTopLevel::secondaryAboutToShow );
393 394 395
    _groupingMenuAction = new QAction(tr( "Grouping" ), this );
    _groupingMenuAction->setMenu(new QMenu(this));
    connect(_groupingMenuAction->menu(), &QMenu::aboutToShow,
396
            this, &QCGTopLevel::groupingAboutToShow );
397

398 399 400 401 402 403 404
    icon = QApplication::style()->standardIcon(QStyle::SP_BrowserReload);
    _cyclesToggleAction = new QAction(icon, tr("Detect Cycles"), this);
    _cyclesToggleAction->setCheckable(true);
    _cyclesToggleAction->setStatusTip(tr("Do Cycle Detection"));
    hint = tr("<b>Detect recursive cycles</b>"
              "<p>If this is switched off, the treemap drawing will show "
              "black areas when a recursive call is made instead of drawing "
405
              "the recursion ad infinitum. Note that "
406
              "the size of black areas often will be wrong, as inside "
407 408
              "recursive cycles the cost of calls cannot be determined; "
              "the error is small, "
409 410
              "however, for false cycles (see documentation).</p>"
              "<p>The correct handling for cycles is to detect them and "
411 412 413
              "collapse all functions of a cycle into an artificial "
              "function, which is done when this option is selected. "
              "Unfortunately, with GUI applications, this often will "
414
              "lead to huge false cycles, making the analysis impossible; "
415
              "therefore, there is the option to switch this off.</p>");
416
    _cyclesToggleAction->setWhatsThis(hint);
417
    connect(_cyclesToggleAction, &QAction::triggered,
418
            this, &QCGTopLevel::toggleCycles);
419 420
    _cyclesToggleAction->setChecked(GlobalConfig::showCycles());

421
    _percentageToggleAction = new QAction(QIcon(QStringLiteral(":/percent.png")),
422
                                          tr("Relative Cost"), this);
423 424
    _percentageToggleAction->setCheckable(true);
    _percentageToggleAction->setStatusTip(tr("Show Relative Costs"));
425
    connect(_percentageToggleAction, &QAction::triggered,
426
            this, &QCGTopLevel::togglePercentage);
427 428
    _percentageToggleAction->setChecked(GlobalConfig::showPercentage());

429
    _hideTemplatesToggleAction = new QAction(QIcon(QStringLiteral(":/hidetemplates.png")),
430 431 432 433
                                             tr("Shorten Templates"), this);
    _hideTemplatesToggleAction->setCheckable(true);
    _hideTemplatesToggleAction->setStatusTip(tr("Hide Template Parameters "
                                                "in C++ Symbols"));
434 435
    connect(_hideTemplatesToggleAction, &QAction::triggered,
            this, &QCGTopLevel::toggleHideTemplates);
436 437 438 439 440 441 442 443 444 445
    _hideTemplatesToggleAction->setChecked(GlobalConfig::hideTemplates());
    hint = tr("<b>Hide Template Parameters in C++ Symbols</b>"
              "<p>If this is switched on, every symbol displayed will have "
              "any C++ template parameters hidden, just showing &lt;&gt; "
              "instead of a potentially nested template parameter.</p>"
              "<p>In this mode, you can hover the mouse pointer over the "
              "activated symbol label to show a tooltip with the "
              "unabbreviated symbol.</p>");
    _hideTemplatesToggleAction->setWhatsThis(hint);

446
    _expandedToggleAction = new QAction(QIcon(QStringLiteral(":/move.png")),
447
                                        tr("Relative to Parent"), this);
448 449
    _expandedToggleAction->setCheckable(true);
    _expandedToggleAction->setStatusTip(
450
                tr("Show Percentage relative to Parent"));
451 452
    hint = tr("<b>Show percentage costs relative to parent</b>"
              "<p>If this is switched off, percentage costs are always "
453 454 455 456
              "shown relative to the total cost of the profile part(s) "
              "that are currently browsed. By turning on this option, "
              "percentage cost of shown cost items will be relative "
              "to the parent cost item.</p>"
457 458
              "<ul><table>"
              "<tr><td><b>Cost Type</b></td><td><b>Parent Cost</b></td></tr>"
459 460
              "<tr><td>Function Inclusive</td><td>Total</td></tr>"
              "<tr><td>Function Self</td><td>Function Group (*)/Total</td></tr>"
461 462 463
              "<tr><td>Call</td><td>Function Inclusive</td></tr>"
              "<tr><td>Source Line</td><td>Function Inclusive</td></tr>"
              "</table></ul>"
464
              "<p>(*) Only if function grouping is switched on "
465
              "(e.g. ELF object grouping).</p>");
466
    _expandedToggleAction->setWhatsThis( hint );
467
    connect(_expandedToggleAction, &QAction::triggered,
468
            this, &QCGTopLevel::toggleExpanded);
469 470
    _expandedToggleAction->setChecked(GlobalConfig::showExpanded());

Yuri Chornoivan's avatar
Yuri Chornoivan committed
471
    _splittedToggleAction = new QAction(tr("Split Visualization"), this);
472 473
    _splittedToggleAction->setCheckable(true);
    _splittedToggleAction->setStatusTip(
474
                tr("Show visualization of two cost items"));
475
    connect(_splittedToggleAction, &QAction::triggered,
476
            this, &QCGTopLevel::toggleSplitted);
477 478 479 480

    _splitDirectionToggleAction = new QAction(tr("Split Horizontal"), this);
    _splitDirectionToggleAction->setCheckable(true);
    _splitDirectionToggleAction->setStatusTip(
481
                tr("Split visualization area horizontally"));
482
    connect(_splitDirectionToggleAction, &QAction::triggered,
483
            this, &QCGTopLevel::toggleSplitDirection);
484 485 486

    _sidebarMenuAction = new QAction(tr("Sidebars"), this);
    _sidebarMenuAction->setMenu(new QMenu(this));
487
    connect( _sidebarMenuAction->menu(), &QMenu::aboutToShow,
488
             this, &QCGTopLevel::sidebarMenuAboutToShow);
489

490
    _layoutDup = new QAction(tr("&Duplicate"), this);
491
    connect(_layoutDup, &QAction::triggered, this, &QCGTopLevel::layoutDuplicate);
492 493
    _layoutDup->setShortcut(Qt::CTRL + Qt::Key_Plus);
    _layoutDup->setStatusTip(tr("Duplicate current layout"));
494

495
    _layoutRemove = new QAction(tr("&Remove"), this);
496
    connect(_layoutRemove, &QAction::triggered, this, &QCGTopLevel::layoutRemove);
497
    _layoutRemove->setStatusTip(tr("Remove current layout"));
498

499
    _layoutNext = new QAction(tr("Go to &Next"), this);
500
    connect(_layoutNext, &QAction::triggered, this, &QCGTopLevel::layoutNext);
501 502
    _layoutNext->setShortcut(Qt::CTRL + Qt::Key_Right);
    _layoutNext->setStatusTip(tr("Switch to next layout"));
503

504
    _layoutPrev = new QAction(tr("Go to &Previous"), this);
505
    connect(_layoutPrev, &QAction::triggered, this, &QCGTopLevel::layoutPrevious);
506 507 508 509
    _layoutPrev->setShortcut(Qt::CTRL + Qt::Key_Left);
    _layoutPrev->setStatusTip(tr("Switch to previous layout"));

    _layoutRestore = new QAction(tr("&Restore to Default"), this);
510
    connect(_layoutRestore, &QAction::triggered, this, &QCGTopLevel::layoutRestore);
511 512 513
    _layoutRestore->setStatusTip(tr("Restore layouts to default"));

    _layoutSave = new QAction(tr("&Save as Default"), this);
514
    connect(_layoutSave, &QAction::triggered, this, &QCGTopLevel::layoutSave);
515 516 517 518 519 520 521 522
    _layoutSave->setStatusTip(tr("Save layouts as default"));

    // go menu actions
    icon = QApplication::style()->standardIcon(QStyle::SP_ArrowUp);
    _upAction = new QAction(icon, tr( "Up" ), this );
    _upAction->setShortcut( QKeySequence(Qt::ALT+Qt::Key_Up) );
    _upAction->setStatusTip(tr("Go Up in Call Stack"));
    _upAction->setMenu(new QMenu(this));
523
    connect(_upAction->menu(), &QMenu::aboutToShow,
524
            this, &QCGTopLevel::upAboutToShow );
525
    connect(_upAction->menu(), &QMenu::triggered,
526
            this, &QCGTopLevel::upTriggered );
527 528 529 530 531 532 533 534
    hint = tr("Go to last selected caller of current function");
    _upAction->setToolTip(hint);

    icon = QApplication::style()->standardIcon(QStyle::SP_ArrowBack);
    _backAction = new QAction(icon, tr("Back"), this);
    _backAction->setShortcut( QKeySequence(Qt::ALT+Qt::Key_Left) );
    _backAction->setStatusTip(tr("Go Back"));
    _backAction->setMenu(new QMenu(this));
535
    connect(_backAction->menu(), &QMenu::aboutToShow,
536
            this, &QCGTopLevel::backAboutToShow );
537
    connect(_backAction->menu(), &QMenu::triggered,
538
            this, &QCGTopLevel::backTriggered );
539 540 541 542 543 544 545 546
    hint = tr("Go back in function selection history");
    _backAction->setToolTip(hint);

    icon = QApplication::style()->standardIcon(QStyle::SP_ArrowForward);
    _forwardAction = new QAction(icon, tr("Forward"), this);
    _forwardAction->setShortcut( QKeySequence(Qt::ALT+Qt::Key_Right) );
    _forwardAction->setStatusTip(tr("Go Forward"));
    _forwardAction->setMenu(new QMenu(this));
547
    connect(_forwardAction->menu(), &QMenu::aboutToShow,
548
            this, &QCGTopLevel::forwardAboutToShow );
549
    connect(_forwardAction->menu(), &QMenu::triggered,
550
            this, &QCGTopLevel::forwardTriggered );
551 552 553
    hint = tr("Go forward in function selection history");
    _forwardAction->setToolTip( hint );

554
    // settings menu actions
555
    _configureAction = new QAction(tr("&Configure..."), this);
556
    _configureAction->setMenuRole(QAction::PreferencesRole);
557 558 559
    _configureAction->setStatusTip(tr("Configure QCachegrind"));
    connect(_configureAction, SIGNAL(triggered()), this, SLOT(configure()));

560 561 562 563 564 565 566 567 568 569
    // window menu actions
    _minimizeAction = new QAction(tr("&Minimize"), this);
    _minimizeAction->setShortcut(Qt::CTRL + Qt::Key_M);
    connect(_minimizeAction, &QAction::triggered, this, &QWidget::showMinimized);

    _zoomAction = new QAction(tr("&Zoom"), this);
    // on macOS, zoom doesn't have exactly the same semantics as maximize
    // (it creates the best fit), but maximize is the usual copout answer
    connect(_zoomAction, &QAction::triggered, this, &QWidget::showMaximized);

570
    // help menu actions
571
    _aboutAction = new QAction(tr("&About QCachegrind..."), this);
572
    _aboutAction->setMenuRole(QAction::AboutRole);
573
    _aboutAction->setStatusTip(tr("Show the application's About box"));
574
    connect(_aboutAction, &QAction::triggered, this, &QCGTopLevel::about);
575

576
    _aboutQtAction = new QAction(tr("About Qt..."), this);
577
    _aboutQtAction->setMenuRole(QAction::AboutQtRole);
578
    connect(_aboutQtAction, &QAction::triggered, qApp, &QApplication::aboutQt);
579

580 581 582 583 584
    // toolbar actions
    _eventTypeBox = new QComboBox(this);
    _eventTypeBox->setMinimumContentsLength(25);
    hint = tr("Select primary event type of costs");
    _eventTypeBox->setToolTip( hint );
Yuri Chornoivan's avatar
Yuri Chornoivan committed
585 586
    connect( _eventTypeBox, SIGNAL(activated(QString)),
             this, SLOT(eventTypeSelected(QString)));
587 588
}

589
void QCGTopLevel::createMenu()
590 591 592 593 594 595 596 597 598 599 600
{
    QMenuBar* mBar = menuBar();

    QMenu* fileMenu = mBar->addMenu(tr("&File"));
    fileMenu->addAction(_newAction);
    fileMenu->addAction(_openAction);
    fileMenu->addAction(_recentFilesMenuAction);
    fileMenu->addAction(_addAction);
    fileMenu->addSeparator();
    fileMenu->addAction(_exportAction);
    fileMenu->addSeparator();
601 602
    fileMenu->addAction(_closeAction);
    fileMenu->addSeparator();
603 604 605 606 607 608 609 610 611 612 613 614 615
    fileMenu->addAction(_exitAction);

    QMenu* layoutMenu = new QMenu(tr("&Layout"), this);
    layoutMenu->addAction(_layoutDup);
    layoutMenu->addAction(_layoutRemove);
    layoutMenu->addSeparator();
    layoutMenu->addAction(_layoutPrev);
    layoutMenu->addAction(_layoutNext);
    layoutMenu->addSeparator();
    layoutMenu->addAction(_layoutSave);
    layoutMenu->addAction(_layoutRestore);

    QMenu* viewMenu = mBar->addMenu(tr("&View"));
616 617 618 619
    viewMenu->addAction(_primaryMenuAction);
    viewMenu->addAction(_secondaryMenuAction);
    viewMenu->addAction(_groupingMenuAction);
    viewMenu->addSeparator();
620
    viewMenu->addAction(tb->toggleViewAction());
621
    viewMenu->addMenu(layoutMenu);
622
    viewMenu->addAction(_sidebarMenuAction);
623 624 625
    viewMenu->addAction(_splittedToggleAction);
    viewMenu->addAction(_splitDirectionToggleAction);
    viewMenu->addSeparator();
626 627 628
    viewMenu->addAction(_cyclesToggleAction);
    viewMenu->addAction(_percentageToggleAction);
    viewMenu->addAction(_expandedToggleAction);
629
    viewMenu->addAction(_hideTemplatesToggleAction);
630 631
    viewMenu->addSeparator();
    viewMenu->addAction(_configureAction);
632

633
    QMenu* goMenu = mBar->addMenu(tr("&Go"));
634 635 636
    goMenu->addAction(_backAction);
    goMenu->addAction(_forwardAction);
    goMenu->addAction(_upAction);
637
    fileMenu->addAction(_exitAction);
638

639
#ifdef Q_OS_MAC
Calvin Buckley's avatar
Calvin Buckley committed
640 641 642 643 644 645
    // class level for ease of manipulation
    this->windowMenu = mBar->addMenu(tr("&Window"));
    this->windowMenu->addAction(_minimizeAction);
    this->windowMenu->addAction(_zoomAction);
    connect(this->windowMenu, &QMenu::aboutToShow, this, &QCGTopLevel::windowListAboutToShow);
    connect(this->windowMenu, &QMenu::triggered, this, &QCGTopLevel::windowListTriggered);
646 647 648 649 650 651 652

    // right-clicking the dock icon should be a window list
    this->macDockMenu = new QMenu(this);
    connect(this->macDockMenu, &QMenu::aboutToShow, this, &QCGTopLevel::macDockMenuAboutToShow);
    // it can reuse the same events, it just needs a diff menu structure
    connect(this->macDockMenu, &QMenu::triggered, this, &QCGTopLevel::windowListTriggered);
    this->macDockMenu->setAsDockMenu();
653 654
#endif

655
    QMenu* helpMenu = mBar->addMenu(tr("&Help"));
656 657
    helpMenu->addAction(QWhatsThis::createAction(this));
    helpMenu->addSeparator();
658
    helpMenu->addAction(_aboutAction);
659
    helpMenu->addAction(_aboutQtAction);
660 661
}

662
void QCGTopLevel::createToolbar()
663
{
664
    tb = new QToolBar(tr("Main Toolbar"), this);
665
    tb->setObjectName(QStringLiteral("main-toolbar"));
666 667 668 669 670 671 672 673
    addToolBar(Qt::TopToolBarArea, tb);

    tb->addAction(_openAction);
    tb->addSeparator();

    tb->addAction(_cyclesToggleAction);
    tb->addAction(_percentageToggleAction);
    tb->addAction(_expandedToggleAction);
674
    tb->addAction(_hideTemplatesToggleAction);
675 676 677 678 679 680 681 682
    tb->addSeparator();

    tb->addAction(_backAction);
    tb->addAction(_forwardAction);
    tb->addAction(_upAction);
    tb->addSeparator();

    tb->addWidget(_eventTypeBox);
683 684
}

685

686
void QCGTopLevel::about()
687
{
688
    QString text, version;
Josef Weidendorfer's avatar
Josef Weidendorfer committed
689
    version = QStringLiteral("0.8.0kde");
690
    text = QStringLiteral("<h3>QCachegrind %1</h3>").arg(version);
691
    text += tr("<p>QCachegrind is a graphical user interface for analysing "
692 693 694 695 696 697 698 699 700 701
               "profiling data, which helps in the performance optimization "
               "phase of developing a computer program. "
               "QCachegrind is open-source, and it is distributed under the "
               "terms of the GPL v2. For details and source code, see the "
               "<a href=\"https://kcachegrind.github.io\">homepage</a> of the "
               "KCachegrind project.</p>"
               "Main author and maintainer: "
               "<a href=\"mailto:Josef.Weidendorfer@gmx.de\">"
               "Josef Weidendorfer</a><br>"
               "(with lots of bug fixes/porting help by the KDE community)");
702 703 704
    QMessageBox::about(this, tr("About QCachegrind"), text);
}

705 706
void QCGTopLevel::configure(QString s)
{
707
    static QString lastPage;
708

Yuri Chornoivan's avatar
Yuri Chornoivan committed
709
    // if no specific config item should be focused, use last page
710
    if (s.isEmpty()) s = lastPage;
711 712 713
    ConfigDialog d(_data, this, s);

    if (d.exec() == QDialog::Accepted) {
714 715
        GlobalConfig::config()->saveOptions();
        configChanged();
716
    }
717
    lastPage = d.currentPage();
718
}
719

720
void QCGTopLevel::togglePartDock()
721
{
722 723 724 725
    if (!_partDock->isVisible())
        _partDock->show();
    else
        _partDock->hide();
726 727
}

728
void QCGTopLevel::toggleStackDock()
729
{
730 731 732 733
    if (!_stackDock->isVisible())
        _stackDock->show();
    else
        _stackDock->hide();
734 735
}

736
void QCGTopLevel::toggleFunctionDock()
737
{
738 739 740 741
    if (!_functionDock->isVisible())
        _functionDock->show();
    else
        _functionDock->hide();
742 743
}

744
void QCGTopLevel::togglePercentage()
745
{
746
    setPercentage(_percentageToggleAction->isChecked());
747 748
}

749

750
void QCGTopLevel::setAbsoluteCost()
751
{
752
    setPercentage(false);
753 754
}

755
void QCGTopLevel::setRelativeCost()
756
{
757
    setPercentage(true);
758 759
}

760
void QCGTopLevel::setPercentage(bool show)
761
{
762 763
    if (GlobalConfig::showPercentage() == show) return;
    if (_percentageToggleAction->isChecked() != show)
764
        _percentageToggleAction->setChecked(show);
765 766
    _expandedToggleAction->setEnabled(show);
    GlobalConfig::setShowPercentage(show);
767

768 769 770 771
    _partSelection->notifyChange(TraceItemView::configChanged);
    _stackSelection->refresh();
    _functionSelection->notifyChange(TraceItemView::configChanged);
    _multiView->notifyChange(TraceItemView::configChanged);
772 773
}

774 775 776 777 778 779 780 781 782 783 784 785
void QCGTopLevel::toggleHideTemplates()
{
    bool show = _hideTemplatesToggleAction->isChecked();
    if (GlobalConfig::hideTemplates() == show) return;
    GlobalConfig::setHideTemplates(show);

    _partSelection->notifyChange(TraceItemView::configChanged);