qcgtoplevel.cpp 63 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>
45

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

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

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

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

74 75 76
    _layoutCount = 1;
    _layoutCurrent = 0;

77
    resetState();
78

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

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

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

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

93 94
    // restore docks & toolbars from config
    QByteArray state, geometry;
95 96 97 98
    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();
99
    delete topConfig;
100

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

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

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

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

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

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

138

139 140 141 142 143 144 145
/**
 * 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.
 */
146
void QCGTopLevel::saveCurrentState(const QString& postfix)
147
{
148 149 150
    QString eventType, eventType2;
    if (_eventType) eventType = _eventType->name();
    if (_eventType2) eventType2 = _eventType2->name();
151

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

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

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

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

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

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

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

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

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

    popup->clear();

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

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

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

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

    popup->clear();

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

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

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

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

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

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



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

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

    // stack selection side bar
    _stackDock = new QDockWidget(this);
295
    _stackDock->setObjectName(QStringLiteral("stack-dock"));
296 297 298 299
    _stackSelection = new StackSelection(_stackDock);
    _stackDock->setWidget(_stackSelection);
    _stackDock->setWindowTitle(tr("Top Cost Call Stack"));
    _stackSelection->setWhatsThis( tr(
300 301 302 303 304 305 306 307
                                       "<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>"));
308
    connect(_stackSelection, SIGNAL(functionSelected(CostItem*)),
309
            this, SLOT(setTraceItemDelayed(CostItem*)));
310
    // actions are already created
311
    connect(_upAction, &QAction::triggered,
312
            _stackSelection, &StackSelection::browserUp );
313
    connect(_backAction, &QAction::triggered,
314
            _stackSelection, &StackSelection::browserBack );
315
    connect(_forwardAction, &QAction::triggered,
316
            _stackSelection, &StackSelection::browserForward);
317 318 319

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

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




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

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

    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"));
354
    connect(_openAction, SIGNAL(triggered()), this, SLOT(load()));
355

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

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

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

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

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

    // view menu actions
383 384 385 386

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

397 398 399 400 401 402 403
    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 "
404
              "the recursion ad infinitum. Note that "
405
              "the size of black areas often will be wrong, as inside "
406 407
              "recursive cycles the cost of calls cannot be determined; "
              "the error is small, "
408 409
              "however, for false cycles (see documentation).</p>"
              "<p>The correct handling for cycles is to detect them and "
410 411 412
              "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 "
413
              "lead to huge false cycles, making the analysis impossible; "
414
              "therefore, there is the option to switch this off.</p>");
415
    _cyclesToggleAction->setWhatsThis(hint);
416
    connect(_cyclesToggleAction, &QAction::triggered,
417
            this, &QCGTopLevel::toggleCycles);
418 419
    _cyclesToggleAction->setChecked(GlobalConfig::showCycles());

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

428
    _hideTemplatesToggleAction = new QAction(QIcon(QStringLiteral(":/hidetemplates.png")),
429 430 431 432
                                             tr("Shorten Templates"), this);
    _hideTemplatesToggleAction->setCheckable(true);
    _hideTemplatesToggleAction->setStatusTip(tr("Hide Template Parameters "
                                                "in C++ Symbols"));
433 434
    connect(_hideTemplatesToggleAction, &QAction::triggered,
            this, &QCGTopLevel::toggleHideTemplates);
435 436 437 438 439 440 441 442 443 444
    _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);

445
    _expandedToggleAction = new QAction(QIcon(QStringLiteral(":/move.png")),
446
                                        tr("Relative to Parent"), this);
447 448
    _expandedToggleAction->setCheckable(true);
    _expandedToggleAction->setStatusTip(
449
                tr("Show Percentage relative to Parent"));
450 451
    hint = tr("<b>Show percentage costs relative to parent</b>"
              "<p>If this is switched off, percentage costs are always "
452 453 454 455
              "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>"
456 457
              "<ul><table>"
              "<tr><td><b>Cost Type</b></td><td><b>Parent Cost</b></td></tr>"
458 459
              "<tr><td>Function Inclusive</td><td>Total</td></tr>"
              "<tr><td>Function Self</td><td>Function Group (*)/Total</td></tr>"
460 461 462
              "<tr><td>Call</td><td>Function Inclusive</td></tr>"
              "<tr><td>Source Line</td><td>Function Inclusive</td></tr>"
              "</table></ul>"
463
              "<p>(*) Only if function grouping is switched on "
464
              "(e.g. ELF object grouping).</p>");
465
    _expandedToggleAction->setWhatsThis( hint );
466
    connect(_expandedToggleAction, &QAction::triggered,
467
            this, &QCGTopLevel::toggleExpanded);
468 469
    _expandedToggleAction->setChecked(GlobalConfig::showExpanded());

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

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

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

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

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

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

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

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

    _layoutSave = new QAction(tr("&Save as Default"), this);
513
    connect(_layoutSave, &QAction::triggered, this, &QCGTopLevel::layoutSave);
514 515 516 517 518 519 520 521
    _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));
522
    connect(_upAction->menu(), &QMenu::aboutToShow,
523
            this, &QCGTopLevel::upAboutToShow );
524
    connect(_upAction->menu(), &QMenu::triggered,
525
            this, &QCGTopLevel::upTriggered );
526 527 528 529 530 531 532 533
    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));
534
    connect(_backAction->menu(), &QMenu::aboutToShow,
535
            this, &QCGTopLevel::backAboutToShow );
536
    connect(_backAction->menu(), &QMenu::triggered,
537
            this, &QCGTopLevel::backTriggered );
538 539 540 541 542 543 544 545
    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));
546
    connect(_forwardAction->menu(), &QMenu::aboutToShow,
547
            this, &QCGTopLevel::forwardAboutToShow );
548
    connect(_forwardAction->menu(), &QMenu::triggered,
549
            this, &QCGTopLevel::forwardTriggered );
550 551 552
    hint = tr("Go forward in function selection history");
    _forwardAction->setToolTip( hint );

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

559 560 561 562 563 564 565 566 567 568
    // 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);

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

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

579 580 581 582 583
    // 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
584 585
    connect( _eventTypeBox, SIGNAL(activated(QString)),
             this, SLOT(eventTypeSelected(QString)));
586 587
}

588
void QCGTopLevel::createMenu()
589 590 591 592 593 594 595 596 597 598 599
{
    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();
600 601
    fileMenu->addAction(_closeAction);
    fileMenu->addSeparator();
602 603 604 605 606 607 608 609 610 611 612 613 614
    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"));
615 616 617 618 619
    viewMenu->addAction(_primaryMenuAction);
    viewMenu->addAction(_secondaryMenuAction);
    viewMenu->addAction(_groupingMenuAction);
    viewMenu->addSeparator();
    viewMenu->addMenu(layoutMenu);
620
    viewMenu->addAction(_sidebarMenuAction);
621 622 623
    viewMenu->addAction(_splittedToggleAction);
    viewMenu->addAction(_splitDirectionToggleAction);
    viewMenu->addSeparator();
624 625 626
    viewMenu->addAction(_cyclesToggleAction);
    viewMenu->addAction(_percentageToggleAction);
    viewMenu->addAction(_expandedToggleAction);
627
    viewMenu->addAction(_hideTemplatesToggleAction);
628 629
    viewMenu->addSeparator();
    viewMenu->addAction(_configureAction);
630

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

637 638 639 640 641 642
#ifdef Q_OS_MAC
    QMenu* windowMenu = mBar->addMenu(tr("&Window"));
    windowMenu->addAction(_minimizeAction);
    windowMenu->addAction(_zoomAction);
#endif

643
    QMenu* helpMenu = mBar->addMenu(tr("&Help"));
644 645
    helpMenu->addAction(QWhatsThis::createAction(this));
    helpMenu->addSeparator();
646
    helpMenu->addAction(_aboutAction);
647
    helpMenu->addAction(_aboutQtAction);
648 649
}

650
void QCGTopLevel::createToolbar()
651 652
{
    QToolBar* tb = new QToolBar(tr("Main Toolbar"), this);
653
    tb->setObjectName(QStringLiteral("main-toolbar"));
654 655 656 657 658 659 660 661
    addToolBar(Qt::TopToolBarArea, tb);

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

    tb->addAction(_cyclesToggleAction);
    tb->addAction(_percentageToggleAction);
    tb->addAction(_expandedToggleAction);
662
    tb->addAction(_hideTemplatesToggleAction);
663 664 665 666 667 668 669 670
    tb->addSeparator();

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

    tb->addWidget(_eventTypeBox);
671 672
}

673

674
void QCGTopLevel::about()
675
{
676
    QString text, version;
Josef Weidendorfer's avatar
Josef Weidendorfer committed
677
    version = QStringLiteral("0.8.0kde");
678
    text = QStringLiteral("<h3>QCachegrind %1</h3>").arg(version);
679
    text += tr("<p>QCachegrind is a graphical user interface for analysing "
680 681 682 683 684 685 686 687 688 689
               "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)");
690 691 692
    QMessageBox::about(this, tr("About QCachegrind"), text);
}

693 694
void QCGTopLevel::configure(QString s)
{
695
    static QString lastPage;
696

Yuri Chornoivan's avatar
Yuri Chornoivan committed
697
    // if no specific config item should be focused, use last page
698
    if (s.isEmpty()) s = lastPage;
699 700 701
    ConfigDialog d(_data, this, s);

    if (d.exec() == QDialog::Accepted) {
702 703
        GlobalConfig::config()->saveOptions();
        configChanged();
704
    }
705
    lastPage = d.currentPage();
706
}
707

708
void QCGTopLevel::togglePartDock()
709
{
710 711 712 713
    if (!_partDock->isVisible())
        _partDock->show();
    else
        _partDock->hide();
714 715
}

716
void QCGTopLevel::toggleStackDock()
717
{
718 719 720 721
    if (!_stackDock->isVisible())
        _stackDock->show();
    else
        _stackDock->hide();
722 723
}

724
void QCGTopLevel::toggleFunctionDock()
725
{
726 727 728 729
    if (!_functionDock->isVisible())
        _functionDock->show();
    else
        _functionDock->hide();
730 731
}

732
void QCGTopLevel::togglePercentage()
733
{
734
    setPercentage(_percentageToggleAction->isChecked());
735 736
}

737

738
void QCGTopLevel::setAbsoluteCost()
739
{
740
    setPercentage(false);
741 742
}

743
void QCGTopLevel::setRelativeCost()
744
{
745
    setPercentage(true);
746 747
}

748
void QCGTopLevel::setPercentage(bool show)
749
{
750 751
    if (GlobalConfig::showPercentage() == show) return;
    if (_percentageToggleAction->isChecked() != show)
752
        _percentageToggleAction->setChecked(show);
753 754
    _expandedToggleAction->setEnabled(show);
    GlobalConfig::setShowPercentage(show);
755

756 757 758 759
    _partSelection->notifyChange(TraceItemView::configChanged);
    _stackSelection->refresh();
    _functionSelection->notifyChange(TraceItemView::configChanged);
    _multiView->notifyChange(TraceItemView::configChanged);
760 761
}

762 763 764 765 766 767 768 769 770 771 772 773
void QCGTopLevel::toggleHideTemplates()
{
    bool show = _hideTemplatesToggleAction->isChecked();
    if (GlobalConfig::hideTemplates() == show) return;
    GlobalConfig::setHideTemplates(show);

    _partSelection->notifyChange(TraceItemView::configChanged);
    _stackSelection->refresh();
    _functionSelection->notifyChange(TraceItemView::configChanged);
    _multiView->notifyChange(TraceItemView::configChanged);
}

774
void QCGTopLevel::toggleExpanded()