qcgtoplevel.cpp 57.8 KB
Newer Older
1
/* This file is part of KCachegrind.
Josef Weidendorfer's avatar
Josef Weidendorfer committed
2
   Copyright (c) 2002-2015 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 47 48 49
#ifdef QT_DBUS_SUPPORT
#include <QtDBus/QDBusConnection>
#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 66 67
    QDBusConnection con = QDBusConnection::sessionBus();
    con.registerObject("/QCachegrind", this,
		       QDBusConnection::ExportScriptableSlots);
#endif
68

69 70 71 72
    _progressBar = 0;
    _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 91
    // restore current state settings (not configuration options)
    restoreCurrentState(QString::null);
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 102 103 104
    if (!geometry.isEmpty())
	restoreGeometry(geometry);
    if (!state.isEmpty())
	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

121 122 123 124 125 126
    _data = 0;
    _function = 0;
    _eventType = 0;
    _eventType2 = 0;
    _groupType = ProfileContext::InvalidType;
    _group = 0;
127

128 129 130 131 132 133 134 135
    // for delayed slots
    _traceItemDelayed = 0;
    _eventTypeDelayed = 0;
    _eventType2Delayed = 0;
    _groupTypeDelayed = ProfileContext::InvalidType;
    _groupDelayed = 0;
    _directionDelayed = TraceItemView::None;
    _lastSender = 0;
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 153 154 155
    ConfigGroup* stateConfig = ConfigStorage::group(QStringLiteral("CurrentState") + postfix);
    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
	pConfig->setValue(QStringLiteral("GroupType%1").arg(key),
184 185 186 187
			  ProfileContext::typeName(_groupType));

    if (_data) {
	if (_group)
188
	    pConfig->setValue(QStringLiteral("Group%1").arg(key), _group->name());
189 190 191
	saveCurrentState(key);
    }
    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 207
    _splittedToggleAction->setChecked(_multiView->childCount()>1);
    _splitDirectionToggleAction->setEnabled(_multiView->childCount()>1);
    _splitDirectionToggleAction->setChecked(_multiView->orientation() ==
					    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 243 244 245 246 247
				       QStringList()).toStringList();
    delete generalConfig;

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

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

261
void QCGTopLevel::createDocks()
262
{
263 264
    // part visualization/selection side bar
    _partDock = new QDockWidget(this);
265
    _partDock->setObjectName(QStringLiteral("part-dock"));
266 267 268 269
    _partDock->setWindowTitle(tr("Parts Overview"));
    _partSelection = new PartSelection(this, _partDock);
    _partDock->setWidget(_partSelection);

270 271 272 273
    connect(_partSelection, &PartSelection::partsHideSelected,
	    this, &QCGTopLevel::partsHideSelectedSlotDelayed);
    connect(_partSelection, &PartSelection::partsUnhideAll,
	    this, &QCGTopLevel::partsUnhideAllSlotDelayed);
274 275 276

    // stack selection side bar
    _stackDock = new QDockWidget(this);
277
    _stackDock->setObjectName(QStringLiteral("stack-dock"));
278 279 280 281
    _stackSelection = new StackSelection(_stackDock);
    _stackDock->setWidget(_stackSelection);
    _stackDock->setWindowTitle(tr("Top Cost Call Stack"));
    _stackSelection->setWhatsThis( tr(
282 283 284 285 286 287 288 289
                   "<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>"));
290 291
    connect(_stackSelection, SIGNAL(functionSelected(CostItem*)),
	    this, SLOT(setTraceItemDelayed(CostItem*)));
292
    // actions are already created
293 294 295 296 297 298
    connect(_upAction, &QAction::triggered,
	    _stackSelection, &StackSelection::browserUp );
    connect(_backAction, &QAction::triggered,
	    _stackSelection, &StackSelection::browserBack );
    connect(_forwardAction, &QAction::triggered,
	    _stackSelection, &StackSelection::browserForward);
299 300 301

    // flat function profile side bar
    _functionDock = new QDockWidget(this);
302
    _functionDock->setObjectName(QStringLiteral("function-dock"));
303 304 305 306
    _functionDock->setWindowTitle(tr("Flat Profile"));
    _functionSelection = new FunctionSelection(this, _functionDock);
    _functionDock->setWidget(_functionSelection);
    // functionDock needs call to updateView() when getting visible
307 308
    connect(_functionDock, &QDockWidget::visibilityChanged,
	    this, &QCGTopLevel::functionVisibilityChanged);
309

310
    // defaults (later to be adjusted from stored state in config)
Josef Weidendorfer's avatar
Cleanup  
Josef Weidendorfer committed
311 312 313 314
    addDockWidget(Qt::LeftDockWidgetArea, _partDock );
    addDockWidget(Qt::LeftDockWidgetArea, _stackDock );
    addDockWidget(Qt::LeftDockWidgetArea, _functionDock );
    _stackDock->hide();
315
    _partDock->hide();
316 317 318 319 320
}




321
void QCGTopLevel::createActions()
322
{
323 324 325 326 327 328 329
    QString hint;
    QIcon icon;

    // file menu actions
    _newAction = new QAction(tr("&New"), this);
    _newAction->setShortcuts(QKeySequence::New);
    _newAction->setStatusTip(tr("Open new empty window"));
330
    connect(_newAction, &QAction::triggered, this, &QCGTopLevel::newWindow);
331 332 333 334 335

    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"));
336
    connect(_openAction, SIGNAL(triggered()), this, SLOT(load()));
337 338 339

    _addAction = new QAction(tr( "&Add..." ), this);
    _addAction->setStatusTip(tr("Add profile data to current window"));
340
    connect(_addAction, SIGNAL(triggered(bool)), SLOT(add()));
341 342 343

    _exportAction = new QAction(tr("Export Graph"), this);
    _exportAction->setStatusTip(tr("Generate GraphViz file 'callgraph.dot'"));
344
    connect(_exportAction, &QAction::triggered, this, &QCGTopLevel::exportGraph);
345 346 347

    _recentFilesMenuAction = new QAction(tr("Open &Recent"), this);
    _recentFilesMenuAction->setMenu(new QMenu(this));
348 349 350 351
    connect(_recentFilesMenuAction->menu(), &QMenu::aboutToShow,
	     this, &QCGTopLevel::recentFilesMenuAboutToShow);
    connect(_recentFilesMenuAction->menu(), &QMenu::triggered,
	    this, &QCGTopLevel::recentFilesTriggered);
352 353 354 355

    _exitAction = new QAction(tr("E&xit"), this);
    _exitAction->setShortcut(tr("Ctrl+Q"));
    _exitAction->setStatusTip(tr("Exit the application"));
356
    connect(_exitAction, &QAction::triggered, this, &QWidget::close);
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377

    // view menu actions
    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 "
	      "the recursion ad infinitum. Note that "
              "the size of black areas often will be wrong, as inside "
	      "recursive cycles the cost of calls cannot be determined; "
	      "the error is small, "
              "however, for false cycles (see documentation).</p>"
              "<p>The correct handling for cycles is to detect them and "
	      "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 "
              "lead to huge false cycles, making the analysis impossible; "
	      "therefore, there is the option to switch this off.</p>");
    _cyclesToggleAction->setWhatsThis(hint);
378 379
    connect(_cyclesToggleAction, &QAction::triggered,
	    this, &QCGTopLevel::toggleCycles);
380 381
    _cyclesToggleAction->setChecked(GlobalConfig::showCycles());

382
    _percentageToggleAction = new QAction(QIcon(QStringLiteral(":/percent.png")),
383 384 385
					  tr("Relative Cost"), this);
    _percentageToggleAction->setCheckable(true);
    _percentageToggleAction->setStatusTip(tr("Show Relative Costs"));
386 387
    connect(_percentageToggleAction, &QAction::triggered,
	    this, &QCGTopLevel::togglePercentage);
388 389
    _percentageToggleAction->setChecked(GlobalConfig::showPercentage());

390
    _hideTemplatesToggleAction = new QAction(QIcon(QStringLiteral(":/hidetemplates.png")),
391 392 393 394
                                             tr("Shorten Templates"), this);
    _hideTemplatesToggleAction->setCheckable(true);
    _hideTemplatesToggleAction->setStatusTip(tr("Hide Template Parameters "
                                                "in C++ Symbols"));
395 396
    connect(_hideTemplatesToggleAction, &QAction::triggered,
            this, &QCGTopLevel::toggleHideTemplates);
397 398 399 400 401 402 403 404 405 406
    _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);

407
    _expandedToggleAction = new QAction(QIcon(QStringLiteral(":/move.png")),
408
					tr("Relative to Parent"), this);
409 410 411 412 413 414 415 416 417
    _expandedToggleAction->setCheckable(true);
    _expandedToggleAction->setStatusTip(
	tr("Show Percentage relative to Parent"));
    hint = tr("<b>Show percentage costs relative to parent</b>"
              "<p>If this is switched off, percentage costs are always "
	      "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>"
418 419
              "<ul><table>"
              "<tr><td><b>Cost Type</b></td><td><b>Parent Cost</b></td></tr>"
420 421
              "<tr><td>Function Inclusive</td><td>Total</td></tr>"
              "<tr><td>Function Self</td><td>Function Group (*)/Total</td></tr>"
422 423 424
              "<tr><td>Call</td><td>Function Inclusive</td></tr>"
              "<tr><td>Source Line</td><td>Function Inclusive</td></tr>"
              "</table></ul>"
425 426 427
              "<p>(*) Only if function grouping is switched on "
	      "(e.g. ELF object grouping).</p>");
    _expandedToggleAction->setWhatsThis( hint );
428 429
    connect(_expandedToggleAction, &QAction::triggered,
	    this, &QCGTopLevel::toggleExpanded);
430 431 432 433 434 435
    _expandedToggleAction->setChecked(GlobalConfig::showExpanded());

    _splittedToggleAction = new QAction(tr("Splitted Visualization"), this);
    _splittedToggleAction->setCheckable(true);
    _splittedToggleAction->setStatusTip(
	tr("Show visualization of two cost items"));
436 437
    connect(_splittedToggleAction, &QAction::triggered,
	    this, &QCGTopLevel::toggleSplitted);
438 439 440 441 442

    _splitDirectionToggleAction = new QAction(tr("Split Horizontal"), this);
    _splitDirectionToggleAction->setCheckable(true);
    _splitDirectionToggleAction->setStatusTip(
	tr("Split visualization area horizontally"));
443 444
    connect(_splitDirectionToggleAction, &QAction::triggered,
	    this, &QCGTopLevel::toggleSplitDirection);
445 446 447

    _sidebarMenuAction = new QAction(tr("Sidebars"), this);
    _sidebarMenuAction->setMenu(new QMenu(this));
448 449
    connect( _sidebarMenuAction->menu(), &QMenu::aboutToShow,
	     this, &QCGTopLevel::sidebarMenuAboutToShow);
450

451
    _layoutDup = new QAction(tr("&Duplicate"), this);
452
    connect(_layoutDup, &QAction::triggered, this, &QCGTopLevel::layoutDuplicate);
453 454
    _layoutDup->setShortcut(Qt::CTRL + Qt::Key_Plus);
    _layoutDup->setStatusTip(tr("Duplicate current layout"));
455

456
    _layoutRemove = new QAction(tr("&Remove"), this);
457
    connect(_layoutRemove, &QAction::triggered, this, &QCGTopLevel::layoutRemove);
458
    _layoutRemove->setStatusTip(tr("Remove current layout"));
459

460
    _layoutNext = new QAction(tr("Go to &Next"), this);
461
    connect(_layoutNext, &QAction::triggered, this, &QCGTopLevel::layoutNext);
462 463
    _layoutNext->setShortcut(Qt::CTRL + Qt::Key_Right);
    _layoutNext->setStatusTip(tr("Switch to next layout"));
464

465
    _layoutPrev = new QAction(tr("Go to &Previous"), this);
466
    connect(_layoutPrev, &QAction::triggered, this, &QCGTopLevel::layoutPrevious);
467 468 469 470
    _layoutPrev->setShortcut(Qt::CTRL + Qt::Key_Left);
    _layoutPrev->setStatusTip(tr("Switch to previous layout"));

    _layoutRestore = new QAction(tr("&Restore to Default"), this);
471
    connect(_layoutRestore, &QAction::triggered, this, &QCGTopLevel::layoutRestore);
472 473 474
    _layoutRestore->setStatusTip(tr("Restore layouts to default"));

    _layoutSave = new QAction(tr("&Save as Default"), this);
475
    connect(_layoutSave, &QAction::triggered, this, &QCGTopLevel::layoutSave);
476 477 478 479 480 481 482 483
    _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));
484 485 486 487
    connect(_upAction->menu(), &QMenu::aboutToShow,
	    this, &QCGTopLevel::upAboutToShow );
    connect(_upAction->menu(), &QMenu::triggered,
	    this, &QCGTopLevel::upTriggered );
488 489 490 491 492 493 494 495
    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));
496 497 498 499
    connect(_backAction->menu(), &QMenu::aboutToShow,
           this, &QCGTopLevel::backAboutToShow );
    connect(_backAction->menu(), &QMenu::triggered,
	    this, &QCGTopLevel::backTriggered );
500 501 502 503 504 505 506 507
    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));
508 509 510 511
    connect(_forwardAction->menu(), &QMenu::aboutToShow,
	    this, &QCGTopLevel::forwardAboutToShow );
    connect(_forwardAction->menu(), &QMenu::triggered,
	    this, &QCGTopLevel::forwardTriggered );
512 513 514
    hint = tr("Go forward in function selection history");
    _forwardAction->setToolTip( hint );

515
    // settings menu actions
516
    _configureAction = new QAction(tr("&Configure..."), this);
517 518 519
    _configureAction->setStatusTip(tr("Configure QCachegrind"));
    connect(_configureAction, SIGNAL(triggered()), this, SLOT(configure()));

520
    // help menu actions
521
    _aboutAction = new QAction(tr("&About QCachegrind..."), this);
522
    _aboutAction->setStatusTip(tr("Show the application's About box"));
523
    connect(_aboutAction, &QAction::triggered, this, &QCGTopLevel::about);
524

525
    _aboutQtAction = new QAction(tr("About Qt..."), this);
526
    connect(_aboutQtAction, &QAction::triggered, qApp, &QApplication::aboutQt);
527

528 529 530 531 532 533 534 535 536
    // toolbar actions
    _eventTypeBox = new QComboBox(this);
    _eventTypeBox->setMinimumContentsLength(25);
    hint = tr("Select primary event type of costs");
    _eventTypeBox->setToolTip( hint );
    connect( _eventTypeBox, SIGNAL(activated(const QString&)),
	     this, SLOT(eventTypeSelected(const QString&)));
}

537
void QCGTopLevel::createMenu()
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564
{
    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"));
    viewMenu->addAction(_cyclesToggleAction);
    viewMenu->addAction(_percentageToggleAction);
    viewMenu->addAction(_expandedToggleAction);
565
    viewMenu->addAction(_hideTemplatesToggleAction);
566 567 568 569 570
    viewMenu->addSeparator();
    viewMenu->addAction(_splittedToggleAction);
    viewMenu->addAction(_splitDirectionToggleAction);
    viewMenu->addMenu(layoutMenu);

571
    QMenu* goMenu = mBar->addMenu(tr("&Go"));
572 573 574 575
    goMenu->addAction(_backAction);
    goMenu->addAction(_forwardAction);
    goMenu->addAction(_upAction);

576 577
    QMenu* settingsMenu = mBar->addMenu(tr("&Settings"));
    settingsMenu->addAction(_sidebarMenuAction);
578 579
    settingsMenu->addSeparator();
    settingsMenu->addAction(_configureAction);
580 581

    QMenu* helpMenu = mBar->addMenu(tr("&Help"));
582 583
    helpMenu->addAction(QWhatsThis::createAction(this));
    helpMenu->addSeparator();
584
    helpMenu->addAction(_aboutAction);
585
    helpMenu->addAction(_aboutQtAction);
586 587
}

588
void QCGTopLevel::createToolbar()
589 590
{
    QToolBar* tb = new QToolBar(tr("Main Toolbar"), this);
591
    tb->setObjectName(QStringLiteral("main-toolbar"));
592 593 594 595 596 597 598 599
    addToolBar(Qt::TopToolBarArea, tb);

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

    tb->addAction(_cyclesToggleAction);
    tb->addAction(_percentageToggleAction);
    tb->addAction(_expandedToggleAction);
600
    tb->addAction(_hideTemplatesToggleAction);
601 602 603 604 605 606 607 608
    tb->addSeparator();

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

    tb->addWidget(_eventTypeBox);
609 610
}

611

612
void QCGTopLevel::about()
613
{
614
    QString text, version;
615 616
    version = QStringLiteral("0.7.4kde");
    text = QStringLiteral("<h3>QCachegrind %1</h3>").arg(version);
617
    text += tr("<p>QCachegrind is a graphical user interface for analysing "
618 619 620 621
	      "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 "
622
	      "<a href=\"https://kcachegrind.github.io\">homepage</a> of the "
623
	      "KCachegrind project.</p>"
624
              "Main author and maintainer: "
625
	      "<a href=\"mailto:Josef.Weidendorfer@gmx.de\">"
626
              "Josef Weidendorfer</a><br>"
627
              "(with lots of bug fixes/porting help by the KDE community)");
628 629 630
    QMessageBox::about(this, tr("About QCachegrind"), text);
}

631 632
void QCGTopLevel::configure(QString s)
{
633 634
    static QString lastPage = QString::null;

Yuri Chornoivan's avatar
Yuri Chornoivan committed
635
    // if no specific config item should be focused, use last page
636
    if (s.isEmpty()) s = lastPage;
637 638 639 640 641 642
    ConfigDialog d(_data, this, s);

    if (d.exec() == QDialog::Accepted) {
	GlobalConfig::config()->saveOptions();
	configChanged();
    }
643
    lastPage = d.currentPage();
644
}
645

646
void QCGTopLevel::togglePartDock()
647 648 649 650 651 652 653
{
  if (!_partDock->isVisible())
    _partDock->show();
  else
    _partDock->hide();
}

654
void QCGTopLevel::toggleStackDock()
655 656 657 658 659 660 661
{
  if (!_stackDock->isVisible())
    _stackDock->show();
  else
    _stackDock->hide();
}

662
void QCGTopLevel::toggleFunctionDock()
663 664 665 666 667 668 669
{
  if (!_functionDock->isVisible())
    _functionDock->show();
  else
    _functionDock->hide();
}

670
void QCGTopLevel::togglePercentage()
671
{
672
  setPercentage(_percentageToggleAction->isChecked());
673 674
}

675

676
void QCGTopLevel::setAbsoluteCost()
677 678 679 680
{
  setPercentage(false);
}

681
void QCGTopLevel::setRelativeCost()
682 683 684 685
{
  setPercentage(true);
}

686
void QCGTopLevel::setPercentage(bool show)
687
{
688 689 690 691 692
    if (GlobalConfig::showPercentage() == show) return;
    if (_percentageToggleAction->isChecked() != show)
	_percentageToggleAction->setChecked(show);
    _expandedToggleAction->setEnabled(show);
    GlobalConfig::setShowPercentage(show);
693

694 695 696 697
    _partSelection->notifyChange(TraceItemView::configChanged);
    _stackSelection->refresh();
    _functionSelection->notifyChange(TraceItemView::configChanged);
    _multiView->notifyChange(TraceItemView::configChanged);
698 699
}

700 701 702 703 704 705 706 707 708 709 710 711
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);
}

712
void QCGTopLevel::toggleExpanded()
713
{
714 715 716
    bool show = _expandedToggleAction->isChecked();
    if (GlobalConfig::showExpanded() == show) return;
    GlobalConfig::setShowExpanded(show);
717

718 719 720 721
    _partSelection->notifyChange(TraceItemView::configChanged);
    _stackSelection->refresh();
    _functionSelection->notifyChange(TraceItemView::configChanged);
    _multiView->notifyChange(TraceItemView::configChanged);
722 723
}

724
void QCGTopLevel::toggleCycles()
725
{
726 727 728
    bool show = _cyclesToggleAction->isChecked();
    if (GlobalConfig::showCycles() == show) return;
    GlobalConfig::setShowCycles(show);
729

730
    if (!_data) return;
731

732 733
    _data->invalidateDynamicCost();
    _data->updateFunctionCycles();
734

735 736 737 738
    _partSelection->notifyChange(TraceItemView::configChanged);
    _stackSelection->rebuildStackList();
    _functionSelection->notifyChange(TraceItemView::configChanged);
    _multiView->notifyChange(TraceItemView::configChanged);
739 740 741
}


742
void QCGTopLevel::functionVisibilityChanged(bool v)
743
{
744 745
    if (v)
	_functionSelection->updateView();
746 747 748
}


749
void QCGTopLevel::newWindow()
750
{
751
    QCGTopLevel* t = new QCGTopLevel();
752
    t->show();
753 754 755
}


756
void QCGTopLevel::load()
757
{
758 759
    QStringList files;
    files = QFileDialog::getOpenFileNames(this,
760
					tr("Open Callgrind Data"),
761
					_lastFile,
762
					tr("Callgrind Files (callgrind.*);;All Files (*)"));
763
    load(files);
764 765
}

766
void QCGTopLevel::load(QStringList files, bool addToRecentFiles)
767
{
768 769
    if (files.isEmpty()) return;
    _lastFile = files[0];
770

771
    if (_data && _data->parts().count()>0) {
772

773
	// In new window
774
	QCGTopLevel* t = new QCGTopLevel();
775
	t->show();
776
	t->loadDelayed(files, addToRecentFiles);
777 778 779 780 781
	return;
    }

    // this constructor enables progress bar callbacks
    TraceData* d = new TraceData(this);
782
    int filesLoaded = d->load(files);
783 784
    if (filesLoaded >0)
	setData(d);
785

786 787 788 789
    if (!addToRecentFiles) return;

    // add to recent file list in config
    QStringList recentFiles;
790 791
    ConfigGroup* generalConfig = ConfigStorage::group(QStringLiteral("GeneralSettings"));
    recentFiles = generalConfig->value(QStringLiteral("RecentFiles"),
792
				       QStringList()).toStringList();
793 794 795 796 797 798 799
    foreach(QString file, files) {
        recentFiles.removeAll(file);
        if (filesLoaded >0)
            recentFiles.prepend(file);
        if (recentFiles.count() >5)
            recentFiles.removeLast();
    }
800
    generalConfig->setValue(QStringLiteral("RecentFiles"), recentFiles);
801
    delete generalConfig;
802 803 804
}


805
void QCGTopLevel::add()
806
{
807 808
    QStringList files;
    files = QFileDialog::getOpenFileNames(this,
809
					tr("Add Callgrind Data"),
810
					_lastFile,
811
					tr("Callgrind Files (callgrind.*);;All Files (*)"));
812
    add(files);
813 814 815
}


816
void QCGTopLevel::add(QStringList files)
817
{
818 819
    if (files.isEmpty()) return;
    _lastFile = files[0];
820

821
    if (_data) {
822
        _data->load(files);
823

824 825 826 827
	// GUI update for added data
	configChanged();
	return;
    }
828

829 830
    // this constructor enables progress bar callbacks
    TraceData* d = new TraceData(this);
831
    int filesLoaded = d->load(files);
832 833
    if (filesLoaded >0)
	setData(d);
834 835
}

836 837 838
void QCGTopLevel::loadDelayed(QString file, bool addToRecentFiles)
{
    _loadFilesDelayed << file;
839

840
    _addToRecentFiles = addToRecentFiles;
841
    QTimer::singleShot(0, this, &QCGTopLevel::loadFilesDelayed);
842
}
843

844
void QCGTopLevel::loadDelayed(QStringList files, bool addToRecentFiles)
845
{