qcgtoplevel.cpp 59.3 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 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 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 289 290 291
    connect(_partSelection, &PartSelection::partsHideSelected,
	    this, &QCGTopLevel::partsHideSelectedSlotDelayed);
    connect(_partSelection, &PartSelection::partsUnhideAll,
	    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 309
    connect(_stackSelection, SIGNAL(functionSelected(CostItem*)),
	    this, SLOT(setTraceItemDelayed(CostItem*)));
310
    // actions are already created
311 312 313 314 315 316
    connect(_upAction, &QAction::triggered,
	    _stackSelection, &StackSelection::browserUp );
    connect(_backAction, &QAction::triggered,
	    _stackSelection, &StackSelection::browserBack );
    connect(_forwardAction, &QAction::triggered,
	    _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 326
    connect(_functionDock, &QDockWidget::visibilityChanged,
	    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 367 368 369
    connect(_recentFilesMenuAction->menu(), &QMenu::aboutToShow,
	     this, &QCGTopLevel::recentFilesMenuAboutToShow);
    connect(_recentFilesMenuAction->menu(), &QMenu::triggered,
	    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 381 382 383 384 385 386 387 388 389 390

    _primaryMenuAction = new QAction(tr( "Primary Event Type" ), this );
    _primaryMenuAction->setMenu(new QMenu(this));
    connect(_primaryMenuAction->menu(), &QMenu::aboutToShow,
	    this, &QCGTopLevel::primaryAboutToShow );
    _secondaryMenuAction = new QAction(tr( "Secondary Event Type" ), this );
    _secondaryMenuAction->setMenu(new QMenu(this));
    connect(_secondaryMenuAction->menu(), &QMenu::aboutToShow,
	    this, &QCGTopLevel::secondaryAboutToShow );
    _groupingMenuAction = new QAction(tr( "Grouping" ), this );
    _groupingMenuAction->setMenu(new QMenu(this));
    connect(_groupingMenuAction->menu(), &QMenu::aboutToShow,
	    this, &QCGTopLevel::groupingAboutToShow );

391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
    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);
410 411
    connect(_cyclesToggleAction, &QAction::triggered,
	    this, &QCGTopLevel::toggleCycles);
412 413
    _cyclesToggleAction->setChecked(GlobalConfig::showCycles());

414
    _percentageToggleAction = new QAction(QIcon(QStringLiteral(":/percent.png")),
415 416 417
					  tr("Relative Cost"), this);
    _percentageToggleAction->setCheckable(true);
    _percentageToggleAction->setStatusTip(tr("Show Relative Costs"));
418 419
    connect(_percentageToggleAction, &QAction::triggered,
	    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 443 444 445 446 447 448 449
    _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>"
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 458 459
              "<p>(*) Only if function grouping is switched on "
	      "(e.g. ELF object grouping).</p>");
    _expandedToggleAction->setWhatsThis( hint );
460 461
    connect(_expandedToggleAction, &QAction::triggered,
	    this, &QCGTopLevel::toggleExpanded);
462 463 464 465 466 467
    _expandedToggleAction->setChecked(GlobalConfig::showExpanded());

    _splittedToggleAction = new QAction(tr("Splitted Visualization"), this);
    _splittedToggleAction->setCheckable(true);
    _splittedToggleAction->setStatusTip(
	tr("Show visualization of two cost items"));
468 469
    connect(_splittedToggleAction, &QAction::triggered,
	    this, &QCGTopLevel::toggleSplitted);
470 471 472 473 474

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

    _sidebarMenuAction = new QAction(tr("Sidebars"), this);
    _sidebarMenuAction->setMenu(new QMenu(this));
480 481
    connect( _sidebarMenuAction->menu(), &QMenu::aboutToShow,
	     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 517 518 519
    connect(_upAction->menu(), &QMenu::aboutToShow,
	    this, &QCGTopLevel::upAboutToShow );
    connect(_upAction->menu(), &QMenu::triggered,
	    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 529 530 531
    connect(_backAction->menu(), &QMenu::aboutToShow,
           this, &QCGTopLevel::backAboutToShow );
    connect(_backAction->menu(), &QMenu::triggered,
	    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 541 542 543
    connect(_forwardAction->menu(), &QMenu::aboutToShow,
	    this, &QCGTopLevel::forwardAboutToShow );
    connect(_forwardAction->menu(), &QMenu::triggered,
	    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 565 566 567 568
    // 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&)));
}

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
	      "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 "
658
	      "<a href=\"https://kcachegrind.github.io\">homepage</a> of the "
659
	      "KCachegrind project.</p>"
660
              "Main author and maintainer: "
661
	      "<a href=\"mailto:Josef.Weidendorfer@gmx.de\">"
662
              "Josef Weidendorfer</a><br>"
663
              "(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 670
    static QString lastPage = QString::null;

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 676 677 678
    ConfigDialog d(_data, this, s);

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

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

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

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

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

711

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

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

722
void QCGTopLevel::setPercentage(bool show)
723
{
724 725 726 727 728
    if (GlobalConfig::showPercentage() == show) return;
    if (_percentageToggleAction->isChecked() != show)
	_percentageToggleAction->setChecked(show);
    _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 781
    if (v)
	_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
					tr("Open Callgrind Data"),
797
					_lastFile,
798
					tr("Callgrind Files (callgrind.*);;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
	// In new window
810
	QCGTopLevel* t = new QCGTopLevel();
811
	t->show();
812
	t->loadDelayed(files, addToRecentFiles);
813 814 815 816 817
	return;
    }

    // this constructor enables progress bar callbacks
    TraceData* d = new TraceData(this);
818
    int filesLoaded = d->load(files);
819 820
    if (filesLoaded >0)
	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();
829 830 831 832 833 834 835
    foreach(QString file, files) {
        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
					tr("Add Callgrind Data"),
846
					_lastFile,
847
					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 863
	// GUI update for added data
	configChanged();
	return;
    }
864

Josef Weidendorfer's avatar