qcgtoplevel.cpp 62.2 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

    _addAction = new QAction(tr( "&Add..." ), this);
    _addAction->setStatusTip(tr("Add profile data to current window"));
358
    connect(_addAction, SIGNAL(triggered(bool)), SLOT(add()));
359
360
361

    _exportAction = new QAction(tr("Export Graph"), this);
    _exportAction->setStatusTip(tr("Generate GraphViz file 'callgraph.dot'"));
362
    connect(_exportAction, &QAction::triggered, this, &QCGTopLevel::exportGraph);
363
364
365

    _recentFilesMenuAction = new QAction(tr("Open &Recent"), this);
    _recentFilesMenuAction->setMenu(new QMenu(this));
366
    connect(_recentFilesMenuAction->menu(), &QMenu::aboutToShow,
367
            this, &QCGTopLevel::recentFilesMenuAboutToShow);
368
    connect(_recentFilesMenuAction->menu(), &QMenu::triggered,
369
            this, &QCGTopLevel::recentFilesTriggered);
370
371
372
373

    _exitAction = new QAction(tr("E&xit"), this);
    _exitAction->setShortcut(tr("Ctrl+Q"));
    _exitAction->setStatusTip(tr("Exit the application"));
374
    connect(_exitAction, &QAction::triggered, this, &QWidget::close);
375
376

    // view menu actions
377
378
379
380

    _primaryMenuAction = new QAction(tr( "Primary Event Type" ), this );
    _primaryMenuAction->setMenu(new QMenu(this));
    connect(_primaryMenuAction->menu(), &QMenu::aboutToShow,
381
            this, &QCGTopLevel::primaryAboutToShow );
382
383
384
    _secondaryMenuAction = new QAction(tr( "Secondary Event Type" ), this );
    _secondaryMenuAction->setMenu(new QMenu(this));
    connect(_secondaryMenuAction->menu(), &QMenu::aboutToShow,
385
            this, &QCGTopLevel::secondaryAboutToShow );
386
387
388
    _groupingMenuAction = new QAction(tr( "Grouping" ), this );
    _groupingMenuAction->setMenu(new QMenu(this));
    connect(_groupingMenuAction->menu(), &QMenu::aboutToShow,
389
            this, &QCGTopLevel::groupingAboutToShow );
390

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

414
    _percentageToggleAction = new QAction(QIcon(QStringLiteral(":/percent.png")),
415
                                          tr("Relative Cost"), this);
416
417
    _percentageToggleAction->setCheckable(true);
    _percentageToggleAction->setStatusTip(tr("Show Relative Costs"));
418
    connect(_percentageToggleAction, &QAction::triggered,
419
            this, &QCGTopLevel::togglePercentage);
420
421
    _percentageToggleAction->setChecked(GlobalConfig::showPercentage());

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

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

Yuri Chornoivan's avatar
Yuri Chornoivan committed
464
    _splittedToggleAction = new QAction(tr("Split Visualization"), this);
465
466
    _splittedToggleAction->setCheckable(true);
    _splittedToggleAction->setStatusTip(
467
                tr("Show visualization of two cost items"));
468
    connect(_splittedToggleAction, &QAction::triggered,
469
            this, &QCGTopLevel::toggleSplitted);
470
471
472
473

    _splitDirectionToggleAction = new QAction(tr("Split Horizontal"), this);
    _splitDirectionToggleAction->setCheckable(true);
    _splitDirectionToggleAction->setStatusTip(
474
                tr("Split visualization area horizontally"));
475
    connect(_splitDirectionToggleAction, &QAction::triggered,
476
            this, &QCGTopLevel::toggleSplitDirection);
477
478
479

    _sidebarMenuAction = new QAction(tr("Sidebars"), this);
    _sidebarMenuAction->setMenu(new QMenu(this));
480
    connect( _sidebarMenuAction->menu(), &QMenu::aboutToShow,
481
             this, &QCGTopLevel::sidebarMenuAboutToShow);
482

483
    _layoutDup = new QAction(tr("&Duplicate"), this);
484
    connect(_layoutDup, &QAction::triggered, this, &QCGTopLevel::layoutDuplicate);
485
486
    _layoutDup->setShortcut(Qt::CTRL + Qt::Key_Plus);
    _layoutDup->setStatusTip(tr("Duplicate current layout"));
487

488
    _layoutRemove = new QAction(tr("&Remove"), this);
489
    connect(_layoutRemove, &QAction::triggered, this, &QCGTopLevel::layoutRemove);
490
    _layoutRemove->setStatusTip(tr("Remove current layout"));
491

492
    _layoutNext = new QAction(tr("Go to &Next"), this);
493
    connect(_layoutNext, &QAction::triggered, this, &QCGTopLevel::layoutNext);
494
495
    _layoutNext->setShortcut(Qt::CTRL + Qt::Key_Right);
    _layoutNext->setStatusTip(tr("Switch to next layout"));
496

497
    _layoutPrev = new QAction(tr("Go to &Previous"), this);
498
    connect(_layoutPrev, &QAction::triggered, this, &QCGTopLevel::layoutPrevious);
499
500
501
502
    _layoutPrev->setShortcut(Qt::CTRL + Qt::Key_Left);
    _layoutPrev->setStatusTip(tr("Switch to previous layout"));

    _layoutRestore = new QAction(tr("&Restore to Default"), this);
503
    connect(_layoutRestore, &QAction::triggered, this, &QCGTopLevel::layoutRestore);
504
505
506
    _layoutRestore->setStatusTip(tr("Restore layouts to default"));

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

547
    // settings menu actions
548
    _configureAction = new QAction(tr("&Configure..."), this);
549
550
551
    _configureAction->setStatusTip(tr("Configure QCachegrind"));
    connect(_configureAction, SIGNAL(triggered()), this, SLOT(configure()));

552
    // help menu actions
553
    _aboutAction = new QAction(tr("&About QCachegrind..."), this);
554
    _aboutAction->setStatusTip(tr("Show the application's About box"));
555
    connect(_aboutAction, &QAction::triggered, this, &QCGTopLevel::about);
556

557
    _aboutQtAction = new QAction(tr("About Qt..."), this);
558
    connect(_aboutQtAction, &QAction::triggered, qApp, &QApplication::aboutQt);
559

560
561
562
563
564
    // 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
565
566
    connect( _eventTypeBox, SIGNAL(activated(QString)),
             this, SLOT(eventTypeSelected(QString)));
567
568
}

569
void QCGTopLevel::createMenu()
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
{
    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();
    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"));
594
595
596
597
598
599
600
601
    viewMenu->addAction(_primaryMenuAction);
    viewMenu->addAction(_secondaryMenuAction);
    viewMenu->addAction(_groupingMenuAction);
    viewMenu->addSeparator();
    viewMenu->addMenu(layoutMenu);
    viewMenu->addAction(_splittedToggleAction);
    viewMenu->addAction(_splitDirectionToggleAction);
    viewMenu->addSeparator();
602
603
604
    viewMenu->addAction(_cyclesToggleAction);
    viewMenu->addAction(_percentageToggleAction);
    viewMenu->addAction(_expandedToggleAction);
605
    viewMenu->addAction(_hideTemplatesToggleAction);
606

607
    QMenu* goMenu = mBar->addMenu(tr("&Go"));
608
609
610
611
    goMenu->addAction(_backAction);
    goMenu->addAction(_forwardAction);
    goMenu->addAction(_upAction);

612
613
    QMenu* settingsMenu = mBar->addMenu(tr("&Settings"));
    settingsMenu->addAction(_sidebarMenuAction);
614
615
    settingsMenu->addSeparator();
    settingsMenu->addAction(_configureAction);
616
617

    QMenu* helpMenu = mBar->addMenu(tr("&Help"));
618
619
    helpMenu->addAction(QWhatsThis::createAction(this));
    helpMenu->addSeparator();
620
    helpMenu->addAction(_aboutAction);
621
    helpMenu->addAction(_aboutQtAction);
622
623
}

624
void QCGTopLevel::createToolbar()
625
626
{
    QToolBar* tb = new QToolBar(tr("Main Toolbar"), this);
627
    tb->setObjectName(QStringLiteral("main-toolbar"));
628
629
630
631
632
633
634
635
    addToolBar(Qt::TopToolBarArea, tb);

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

    tb->addAction(_cyclesToggleAction);
    tb->addAction(_percentageToggleAction);
    tb->addAction(_expandedToggleAction);
636
    tb->addAction(_hideTemplatesToggleAction);
637
638
639
640
641
642
643
644
    tb->addSeparator();

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

    tb->addWidget(_eventTypeBox);
645
646
}

647

648
void QCGTopLevel::about()
649
{
650
    QString text, version;
Josef Weidendorfer's avatar
Josef Weidendorfer committed
651
    version = QStringLiteral("0.8.0kde");
652
    text = QStringLiteral("<h3>QCachegrind %1</h3>").arg(version);
653
    text += tr("<p>QCachegrind is a graphical user interface for analysing "
654
655
656
657
658
659
660
661
662
663
               "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)");
664
665
666
    QMessageBox::about(this, tr("About QCachegrind"), text);
}

667
668
void QCGTopLevel::configure(QString s)
{
669
    static QString lastPage;
670

Yuri Chornoivan's avatar
Yuri Chornoivan committed
671
    // if no specific config item should be focused, use last page
672
    if (s.isEmpty()) s = lastPage;
673
674
675
    ConfigDialog d(_data, this, s);

    if (d.exec() == QDialog::Accepted) {
676
677
        GlobalConfig::config()->saveOptions();
        configChanged();
678
    }
679
    lastPage = d.currentPage();
680
}
681

682
void QCGTopLevel::togglePartDock()
683
{
684
685
686
687
    if (!_partDock->isVisible())
        _partDock->show();
    else
        _partDock->hide();
688
689
}

690
void QCGTopLevel::toggleStackDock()
691
{
692
693
694
695
    if (!_stackDock->isVisible())
        _stackDock->show();
    else
        _stackDock->hide();
696
697
}

698
void QCGTopLevel::toggleFunctionDock()
699
{
700
701
702
703
    if (!_functionDock->isVisible())
        _functionDock->show();
    else
        _functionDock->hide();
704
705
}

706
void QCGTopLevel::togglePercentage()
707
{
708
    setPercentage(_percentageToggleAction->isChecked());
709
710
}

711

712
void QCGTopLevel::setAbsoluteCost()
713
{
714
    setPercentage(false);
715
716
}

717
void QCGTopLevel::setRelativeCost()
718
{
719
    setPercentage(true);
720
721
}

722
void QCGTopLevel::setPercentage(bool show)
723
{
724
725
    if (GlobalConfig::showPercentage() == show) return;
    if (_percentageToggleAction->isChecked() != show)
726
        _percentageToggleAction->setChecked(show);
727
728
    _expandedToggleAction->setEnabled(show);
    GlobalConfig::setShowPercentage(show);
729

730
731
732
733
    _partSelection->notifyChange(TraceItemView::configChanged);
    _stackSelection->refresh();
    _functionSelection->notifyChange(TraceItemView::configChanged);
    _multiView->notifyChange(TraceItemView::configChanged);
734
735
}

736
737
738
739
740
741
742
743
744
745
746
747
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);
}

748
void QCGTopLevel::toggleExpanded()
749
{
750
751
752
    bool show = _expandedToggleAction->isChecked();
    if (GlobalConfig::showExpanded() == show) return;
    GlobalConfig::setShowExpanded(show);
753

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

760
void QCGTopLevel::toggleCycles()
761
{
762
763
764
    bool show = _cyclesToggleAction->isChecked();
    if (GlobalConfig::showCycles() == show) return;
    GlobalConfig::setShowCycles(show);
765

766
    if (!_data) return;
767

768
769
    _data->invalidateDynamicCost();
    _data->updateFunctionCycles();
770

771
772
773
774
    _partSelection->notifyChange(TraceItemView::configChanged);
    _stackSelection->rebuildStackList();
    _functionSelection->notifyChange(TraceItemView::configChanged);
    _multiView->notifyChange(TraceItemView::configChanged);
775
776
777
}


778
void QCGTopLevel::functionVisibilityChanged(bool v)
779
{
780
    if (v)
781
        _functionSelection->updateView();
782
783
784
}


785
void QCGTopLevel::newWindow()
786
{
787
    QCGTopLevel* t = new QCGTopLevel();
788
    t->show();
789
790
791
}


792
void QCGTopLevel::load()
793
{
794
795
    QStringList files;
    files = QFileDialog::getOpenFileNames(this,
796
797
                                          tr("Open Callgrind Data"),
                                          _lastFile,
798
                                          tr("Callgrind Files (callgrind.* cachegrind.*);;All Files (*)"));
799
    load(files);
800
801
}

802
void QCGTopLevel::load(QStringList files, bool addToRecentFiles)
803
{
804
805
    if (files.isEmpty()) return;
    _lastFile = files[0];
806

807
    if (_data && _data->parts().count()>0) {
808

809
810
811
812
813
        // In new window
        QCGTopLevel* t = new QCGTopLevel();
        t->show();
        t->loadDelayed(files, addToRecentFiles);
        return;
814
815
816
817
    }

    // this constructor enables progress bar callbacks
    TraceData* d = new TraceData(this);
818
    int filesLoaded = d->load(files);
819
    if (filesLoaded >0)
820
        setData(d);
821

822
823
824
825
    if (!addToRecentFiles) return;

    // add to recent file list in config
    QStringList recentFiles;
826
827
    ConfigGroup* generalConfig = ConfigStorage::group(QStringLiteral("GeneralSettings"));
    recentFiles = generalConfig->value(QStringLiteral("RecentFiles"),
828
                                       QStringList()).toStringList();
Christian Ehrlicher's avatar
Christian Ehrlicher committed
829
    foreach(const QString& file, files) {
830
831
832
833
834
835
        recentFiles.removeAll(file);
        if (filesLoaded >0)
            recentFiles.prepend(file);
        if (recentFiles.count() >5)
            recentFiles.removeLast();
    }
836
    generalConfig->setValue(QStringLiteral("RecentFiles"), recentFiles);
837
    delete generalConfig;
838
839
840
}


841
void QCGTopLevel::add()
842
{
843
844
    QStringList files;
    files = QFileDialog::getOpenFileNames(this,
845
846
847
                                          tr("Add Callgrind Data"),
                                          _lastFile,
                                          tr("Callgrind Files (callgrind.*);;All Files (*)"));
848
    add(files);
849
850
851
}


852
void QCGTopLevel::add(QStringList files)
853
{
854
855
    if (files.isEmpty()) return;
    _lastFile = files[0];
856

857
    if (_data) {
858
        _data->load(files);
859

860
861
862
        // GUI update for added data
        configChanged();
        return;
863
    }
864

865
866
    // this constructor enables progress bar callbacks
    TraceData* d = new TraceData(this);
867
    int filesLoaded = d->load(files);
868
    if (filesLoaded >0)
869
        setData(d);
870
871
}

872
873
874
void QCGTopLevel::loadDelayed(QString file, bool addToRecentFiles)
{
    _loadFilesDelayed << file;
875

876
    _addToRecentFiles = addToRecentFiles;
877
    QTimer::singleShot(0, this, &QCGTopLevel::loadFilesDelayed);
878
}
879

880
void QCGTopLevel::loadDelayed(QStringList files, bool addToRecentFiles)