qcgtoplevel.cpp 65.1 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
85
    createDocks();
    createMenu();
    createToolbar();
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
620
    viewMenu->addAction(_primaryMenuAction);
    viewMenu->addAction(_secondaryMenuAction);
    viewMenu->addAction(_groupingMenuAction);
    viewMenu->addSeparator();
    viewMenu->addMenu(layoutMenu);
621
    viewMenu->addAction(_sidebarMenuAction);
622
623
624
    viewMenu->addAction(_splittedToggleAction);
    viewMenu->addAction(_splitDirectionToggleAction);
    viewMenu->addSeparator();
625
626
627
    viewMenu->addAction(_cyclesToggleAction);
    viewMenu->addAction(_percentageToggleAction);
    viewMenu->addAction(_expandedToggleAction);
628
    viewMenu->addAction(_hideTemplatesToggleAction);
629
630
    viewMenu->addSeparator();
    viewMenu->addAction(_configureAction);
631

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

638
#ifdef Q_OS_MAC
Calvin Buckley's avatar
Calvin Buckley committed
639
640
641
642
643
644
    // 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);
645
646
647
648
649
650
651

    // 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();
652
653
#endif

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

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

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

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

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

    tb->addWidget(_eventTypeBox);
682
683
}

684

685
void QCGTopLevel::about()
686
{
687
    QString text, version;
Josef Weidendorfer's avatar
Josef Weidendorfer committed
688
    version = QStringLiteral("0.8.0kde");
689
    text = QStringLiteral("<h3>QCachegrind %1</h3>").arg(version);
690
    text += tr("<p>QCachegrind is a graphical user interface for analysing "
691
692
693
694
695
696
697
698
699
700
               "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)");
701
702
703
    QMessageBox::about(this, tr("About QCachegrind"), text);
}

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

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

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

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

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

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

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

748

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

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

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

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

773
774
775
776
777
778
779
780
781
782
783
784
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);
}

785
void QCGTopLevel::toggleExpanded()
786
{
787
788
789
    bool show = _expandedToggleAction->isChecked();
    if (GlobalConfig::showExpanded() == show) return;
    GlobalConfig::setShowExpanded(show);
790

791
792
793
794
    _partSelection->notifyChange(TraceItemView::configChanged);
    _stackSelection->refresh();
    _functionSelection->notifyChange(TraceItemView::configChanged);
    _multiView->notifyChange(TraceItemView::configChanged);
795
796
}

797
void QCGTopLevel::toggleCycles()
798
{
799
800
801
    bool show = _cyclesToggleAction->isChecked();
    if (GlobalConfig::showCycles() == show) return;
    GlobalConfig::setShowCycles(show);
802

803
    if (!_data) return;
804

805
806
    _data->invalidateDynamicCost();
    _data->updateFunctionCycles();
807

808
809
810
811
    _partSelection->notifyChange(TraceItemView::configChanged);
    _stackSelection->rebuildStackList();
    _functionSelection->notifyChange(TraceItemView::configChanged);
    _multiView->notifyChange(TraceItemView::configChanged);
812
813
814
}


815
void QCGTopLevel::functionVisibilityChanged(bool v)
816
{
817
    if (v)
818
        _functionSelection->updateView();
819
820
821
}


822
void QCGTopLevel::newWindow()
823
{
824
    QCGTopLevel* t = new QCGTopLevel();
825
    t->show();
826
827
828
}


829
void QCGTopLevel::load()
830
{
831
832
    QStringList files;
    files = QFileDialog::getOpenFileNames(this,
833
834
                                          tr("Open Callgrind Data"),
                                          _lastFile,
835
                                          tr("Callgrind Files (callgrind.* cachegrind.*);;All Files (*)"));
836
    load(files);
837
838
}

839
void QCGTopLevel::load(QStringList files, bool addToRecentFiles)
840
{
841
842
    if (files.isEmpty()) return;
    _lastFile = files[0];
843

844
    if (_data && _data->parts().count()>0) {
845

846
847
848
849
850
        // In new window
        QCGTopLevel* t = new QCGTopLevel();
        t->show();
        t->loadDelayed(files, addToRecentFiles);
        return;
851
852
853
854
    }

    // this constructor enables progress bar callbacks
    TraceData* d = new TraceData(this);
855
    int filesLoaded = d->load(files);
856
    if (filesLoaded >0)
857
        setData(d);
858

859
860
861
862
    if (!addToRecentFiles) return;

    // add to recent file list in config
    QStringList recentFiles;
863
864
    ConfigGroup* generalConfig = ConfigStorage::group(QStringLiteral("GeneralSettings"));
    recentFiles = generalConfig->value(QStringLiteral("RecentFiles"),
865
                                       QStringList()).toStringList();
Christian Ehrlicher's avatar
Christian Ehrlicher committed
866
    foreach(const QString& file, files) {
867
868
869
870
871
872
        recentFiles.removeAll(file);
        if (filesLoaded >0)
            recentFiles.prepend(file);
        if (recentFiles.count() >5)
            recentFiles.removeLast();
    }
873
    generalConfig->setValue(QStringLiteral("RecentFiles"), recentFiles);
874
    delete generalConfig;
875
876
877
}


878
void QCGTopLevel::add()
879
{
880
881
    QStringList files;
    files = QFileDialog::getOpenFileNames(this,
882
883
884
                                          tr("Add Callgrind Data"),
                                          _lastFile,
                                          tr("Callgrind Files (callgrind.*);;All Files (*)"));
885