kde-playground/kcachegrind/libviews/partselection.cpp
Ivailo Monev 2e71a21b77 kcachegrind: adjust to Katana and Katie changes
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2021-06-25 17:54:47 +03:00

598 lines
18 KiB
C++

/* This file is part of KCachegrind.
Copyright (C) 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
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.
*/
/*
* For part file selection, to be put into a QDockWindow
*/
#include "partselection.h"
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QAction>
#include <QMenu>
#include "toplevelbase.h"
#include "partgraph.h"
#include "globalconfig.h"
#include "config.h"
//
// PartSelection
//
// defaults
#define DEFAULT_PARTITIONMODE "Inclusive"
#define DEFAULT_DIAGRAMMODE false
#define DEFAULT_DRAWFRAMES true
#define DEFAULT_SHOWINFO false
#define DEFAULT_FUNCTIONZOOM false
#define DEFAULT_CALLEELEVELS 1
#define DEFAULT_DRAWNAME true
#define DEFAULT_DRAWCOST true
#define DEFAULT_FORCESTRINGS false
#define DEFAULT_ALLOWROTATION true
PartSelection::PartSelection( TopLevelBase* top,
QWidget* parent)
: QWidget(parent), TraceItemView(0, top)
{
_inSelectionUpdate = false;
setWindowTitle(tr("Parts Overview"));
QVBoxLayout* vboxLayout = new QVBoxLayout(this);
vboxLayout->setSpacing(6);
vboxLayout->setMargin(6);
_partAreaWidget = new PartAreaWidget(this);
_partAreaWidget->setMinimumHeight(50);
_partAreaWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
_partAreaWidget->setMaxSelectDepth(2);
_partAreaWidget->setSelectionMode(TreeMapWidget::Extended);
_partAreaWidget->setSplitMode(TreeMapItem::HAlternate);
_partAreaWidget->setVisibleWidth(2, true);
_partAreaWidget->setFieldType(0, tr("Name"));
_partAreaWidget->setFieldType(1, tr("Cost"));
vboxLayout->addWidget(_partAreaWidget);
_rangeLabel = new QLabel(this);
_rangeLabel->setWordWrap(false);
_rangeLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
vboxLayout->addWidget(_rangeLabel);
_rangeLabel->setText(tr("(no trace parts)"));
_showInfo = true; // label is currently shown
_diagramMode = DEFAULT_DIAGRAMMODE;
_drawFrames = DEFAULT_DRAWFRAMES;
// sets _showInfo
showInfo(DEFAULT_SHOWINFO);
_partAreaWidget->setAllowRotation(DEFAULT_ALLOWROTATION);
connect(_partAreaWidget, SIGNAL(selectionChanged()),
this, SLOT(selectionChanged()));
connect(_partAreaWidget, SIGNAL(currentChanged(TreeMapItem*, bool)),
this, SLOT(currentChangedSlot(TreeMapItem*, bool)));
connect(_partAreaWidget, SIGNAL(doubleClicked(TreeMapItem*)),
this, SLOT(doubleClicked(TreeMapItem*)));
connect(_partAreaWidget,
SIGNAL(contextMenuRequested(TreeMapItem*,const QPoint &)),
this,
SLOT(contextMenuRequested(TreeMapItem*,const QPoint &)));
setWhatsThis(whatsThis());
}
QString PartSelection::whatsThis() const
{
return tr( "<b>The Parts Overview</b>"
"<p>A trace consists of multiple trace parts when "
"there are several profile data files from one profile run. "
"The Trace Part Overview dockable shows these, "
"horizontally ordered in execution time; "
"the rectangle sizes are proportional to the total "
"cost spent in the parts. You can select one or several "
"parts to constrain all costs shown to these parts only."
"</p>"
"<p>The parts are further subdivided: there is a "
"partitioning and an callee split mode: "
"<ul><li>Partitioning: You see the "
"partitioning into groups for a trace part, according to "
"the group type selected. E.g. if ELF object groups are "
"selected, you see colored rectangles for each "
"used ELF object (shared library or executable), sized "
"according to the cost spent therein.</li>"
"<li>Callee: A rectangle showing the inclusive "
"cost of the current selected function in the trace part "
"is shown. "
"This is split up into smaller rectangles to show the costs of its "
"callees.</li></ul></p>");
}
void PartSelection::setData(TraceData* data)
{
TraceItemView::setData(data);
_partAreaWidget->setData(data);
}
CostItem* PartSelection::canShow(CostItem* i)
{
ProfileContext::Type t = i ? i->type() : ProfileContext::InvalidType;
switch(t) {
case ProfileContext::Function:
case ProfileContext::FunctionCycle:
return i;
default:
break;
}
return 0;
}
/* Helper for doUpdate(), called on partsChanged event.
* This makes the graph selection the same to the parts in the list
*/
void PartSelection::selectParts(const TracePartList& list)
{
_inSelectionUpdate = true;
qDebug("Entering PartSelection::activePartsChangedSlot");
TreeMapItemList l = *_partAreaWidget->base()->children();
// first deselect inactive, then select active (makes current active)
foreach(TreeMapItem* i, l) {
TracePart* part = ((PartItem*)i)->part();
bool active = list.contains(part);
if (!active && _partAreaWidget->isSelected(i)) {
#if 0
qDebug("PartSelection::selectParts: Part %s changed to unselected.",
((PartItem*)i)->part()->shortName());
#endif
_partAreaWidget->setSelected(i, false);
}
}
foreach(TreeMapItem* i, l) {
TracePart* part = ((PartItem*)i)->part();
bool active = list.contains(part);
if (active && !_partAreaWidget->isSelected(i)) {
#if 0
qDebug("PartSelection::selectParts: Part %s changed to selected.",
((PartItem*)i)->part()->shortName()));
#endif
_partAreaWidget->setSelected(i, true);
}
}
_inSelectionUpdate = false;
qDebug("Leaving PartSelection::activePartsChangedSlot");
}
void PartSelection::doUpdate(int changeType, bool)
{
if (changeType == eventType2Changed) return;
if (changeType == selectedItemChanged) return;
if (changeType & eventTypeChanged)
_partAreaWidget->setEventType(_eventType);
if (changeType & groupTypeChanged)
_partAreaWidget->setGroupType(_groupType);
if (changeType & activeItemChanged) {
TraceFunction* f = 0;
if (_activeItem) {
switch(_activeItem->type()) {
case ProfileContext::Function:
case ProfileContext::FunctionCycle:
f = (TraceFunction*)_activeItem;
break;
default:
break;
}
}
_inSelectionUpdate = true;
_partAreaWidget->setFunction(f);
_inSelectionUpdate = false;
}
if (changeType & partsChanged)
selectParts(_partList);
_partAreaWidget->redraw();
fillInfo();
}
void PartSelection::currentChangedSlot(TreeMapItem* i, bool kbd)
{
if (!i) return;
if (!kbd) return;
if (i->text(0).isEmpty()) return;
if (_topLevel) {
QString str = i->text(0);
if (!i->text(1).isEmpty())
str += " (" + i->text(1) + ')';
QString msg = tr("Profile Part Overview: Current is '%1'").arg(str);
_topLevel->showMessage(msg, 5000);
}
if (_showInfo) fillInfo();
}
void PartSelection::doubleClicked(TreeMapItem* i)
{
if (!i || i->rtti() != 3) return;
ProfileCostArray* c = ((SubPartItem*) i)->partCostItem();
TraceCostItem* ci = 0;
switch(c->type()) {
case ProfileContext::PartFunction:
{
TraceFunction* f = ((TracePartFunction*)c)->function();
if (f)
activated(f);
}
return;
case ProfileContext::PartObject:
ci = ((TracePartObject*)c)->object();
break;
case ProfileContext::PartClass:
ci = ((TracePartClass*)c)->cls();
break;
case ProfileContext::PartFile:
ci = ((TracePartFile*)c)->file();
break;
default:
break;
}
if (ci)
activated(ci);
}
void PartSelection::selectionChanged()
{
if (_inSelectionUpdate) return;
qDebug("PartSelection::selectionChanged");
bool something_changed = false;
bool nothingSelected = true;
TracePartList pList;
TracePart* part;
// if nothing is selected, activate all parts
TreeMapItemList* list = _partAreaWidget->base()->children();
if (!list) return;
foreach(TreeMapItem* i, *list)
if (_partAreaWidget->isSelected(i)) {
nothingSelected = false;
break;
}
foreach(TreeMapItem* i, *list) {
part = ((PartItem*)i)->part();
bool active = nothingSelected || _partAreaWidget->isSelected(i);
if (active) {
pList.append(part);
something_changed = true;
}
}
if (something_changed) {
//qDebug("PartSelection: Something changed.");
partsSelected(pList);
}
}
void PartSelection::itemSelected()
{
QAction* a = qobject_cast<QAction*>(sender());
if (a)
doubleClicked( (TreeMapItem*) a->data().value<void*>() );
}
void PartSelection::contextMenuRequested(TreeMapItem* i,
const QPoint & p)
{
if (!i) return;
QMenu popup;
QAction* a;
QString str;
TreeMapItem* s = 0;
QAction* selectPartAction = 0;
QAction* selectAllPartsAction = 0;
QAction* hidePartsAction = 0;
QAction* showPartsAction = 0;
if (_data && (_data->parts().count()>1)) {
s = _partAreaWidget->possibleSelection(i);
if (!s->text(0).isEmpty()) {
if (_partAreaWidget->isSelected(s))
str = tr("Deselect '%1'").arg(s->text(0));
else
str = tr("Select '%1'").arg(s->text(0));
selectPartAction = popup.addAction(str);
}
selectAllPartsAction = popup.addAction(tr("Select All Parts"));
QMenu* ppopup = popup.addMenu(tr("Visible Parts"));
hidePartsAction = ppopup->addAction(tr("Hide Selected Parts"));
showPartsAction = ppopup->addAction(tr("Show Hidden Parts"));
popup.addSeparator();
}
addGoMenu(&popup);
if (i->rtti() == 3) {
TreeMapItem* ni = i;
while (ni && ni->rtti() == 3) {
ProfileCostArray* c = ((SubPartItem*)ni)->partCostItem();
if (c->type() == ProfileContext::PartFunction)
if ( ((TracePartFunction*)c)->function() == _selectedItem) break;
str = tr("Go to '%1'").arg(GlobalConfig::shortenSymbol(ni->text(0)));
a = popup.addAction(str);
a->setData(QVariant::fromValue( (void*)ni ));
connect(a, SIGNAL(triggered()), this, SLOT(itemSelected()));
ni = ni->parent();
}
}
popup.addSeparator();
QMenu* vpopup = popup.addMenu(tr("Visualization"));
QAction* showPartitioningAction = vpopup->addAction(tr("Partitioning Mode"));
showPartitioningAction->setCheckable(true);
QAction* zoomFunctionAction = vpopup->addAction(tr("Zoom Function"));
zoomFunctionAction->setCheckable(true);
QAction* directCallsAction = vpopup->addAction(tr("Show Direct Calls"));
QAction* incCallsAction = vpopup->addAction(tr("Increment Shown Call Levels"));
QAction* showDiagramAction = vpopup->addAction(tr("Diagram Mode"));
showDiagramAction->setCheckable(true);
if (_partAreaWidget->visualization() == PartAreaWidget::Partitioning) {
showPartitioningAction->setChecked(true);
zoomFunctionAction->setEnabled(false);
directCallsAction->setEnabled(false);
incCallsAction->setEnabled(false);
}
else {
zoomFunctionAction->setChecked(_partAreaWidget->zoomFunction());
}
showDiagramAction->setChecked(_diagramMode);
vpopup->addSeparator();
QAction* drawNamesAction = vpopup->addAction(tr("Draw Names"));
drawNamesAction->setCheckable(true);
QAction* drawCostsAction = vpopup->addAction(tr("Draw Costs"));
drawCostsAction->setCheckable(true);
QAction* ignorePropsAction = vpopup->addAction(tr("Ignore Proportions"));
ignorePropsAction->setCheckable(true);
QAction* allowRotationAction = vpopup->addAction(tr("Allow Rotation"));
allowRotationAction->setCheckable(true);
QAction* drawFramesAction = vpopup->addAction(tr("Draw Frames"));
drawFramesAction->setCheckable(true);
if (!_partAreaWidget->fieldVisible(0) &&
!_partAreaWidget->fieldVisible(1)) {
ignorePropsAction->setEnabled(false);
allowRotationAction->setEnabled(false);
}
else {
drawNamesAction->setChecked(_partAreaWidget->fieldVisible(0));
drawCostsAction->setChecked(_partAreaWidget->fieldVisible(1));
ignorePropsAction->setChecked(_partAreaWidget->fieldForced(0));
allowRotationAction->setChecked(_partAreaWidget->allowRotation());
drawFramesAction->setChecked(_drawFrames);
}
QAction* showInfoAction = popup.addAction(_showInfo ? tr("Hide Info"):tr("Show Info"));
a = popup.exec(_partAreaWidget->mapToGlobal(p));
if (a == selectPartAction) {
// select/deselect part under mouse
_partAreaWidget->setSelected(s, !_partAreaWidget->isSelected(s));
}
else if (a == selectAllPartsAction) {
// select all parts
TreeMapItemList list = *_partAreaWidget->base()->children();
_partAreaWidget->setRangeSelection(list.first(), list.last(), true);
}
else if (a == hidePartsAction)
emit partsHideSelected();
else if (a == showPartsAction)
emit partsUnhideAll();
else if (a == drawNamesAction)
_partAreaWidget->setFieldVisible(0, !_partAreaWidget->fieldVisible(0));
else if (a == drawCostsAction)
_partAreaWidget->setFieldVisible(1, !_partAreaWidget->fieldVisible(1));
else if (a == ignorePropsAction) {
_partAreaWidget->setFieldForced(0, !_partAreaWidget->fieldForced(0));
_partAreaWidget->setFieldForced(1, !_partAreaWidget->fieldForced(0));
}
else if (a == allowRotationAction)
_partAreaWidget->setAllowRotation(!_partAreaWidget->allowRotation());
else if (a == drawFramesAction) {
_drawFrames = !_drawFrames;
_partAreaWidget->drawFrame(2,_drawFrames);
_partAreaWidget->drawFrame(3,_drawFrames);
}
else if (a == showInfoAction)
showInfo(!_showInfo);
else if (a == showPartitioningAction)
_partAreaWidget->setVisualization(
(_partAreaWidget->visualization() != PartAreaWidget::Partitioning) ?
PartAreaWidget::Partitioning : PartAreaWidget::Inclusive );
else if (a == zoomFunctionAction) {
// zoom/unzoom function
_partAreaWidget->setZoomFunction(!_partAreaWidget->zoomFunction());
}
else if (a == directCallsAction)
_partAreaWidget->setCallLevels(1);
else if (a == incCallsAction) {
int l = _partAreaWidget->callLevels()+1;
_partAreaWidget->setCallLevels(l);
}
else if (a == showDiagramAction) {
_diagramMode = !_diagramMode;
_partAreaWidget->setTransparent(2,_diagramMode);
}
}
void PartSelection::hiddenPartsChangedSlot(const TracePartList& list)
{
_partAreaWidget->changeHidden(list);
}
void PartSelection::restoreOptions(const QString& prefix, const QString& postfix)
{
ConfigGroup* g = ConfigStorage::group(prefix, postfix);
QString pmode = g->value("PartitionMode",
QString(DEFAULT_PARTITIONMODE)).toString();
if (pmode == "Inclusive")
_partAreaWidget->setVisualization(PartAreaWidget::Inclusive);
else
_partAreaWidget->setVisualization(PartAreaWidget::Partitioning);
_diagramMode = g->value("DiagramMode", DEFAULT_DIAGRAMMODE).toBool();
_partAreaWidget->setTransparent(2,_diagramMode);
_drawFrames = g->value("DrawFrames", DEFAULT_DRAWFRAMES).toBool();
_partAreaWidget->drawFrame(2,_drawFrames);
_partAreaWidget->drawFrame(3,_drawFrames);
showInfo(g->value("ShowInfo", DEFAULT_SHOWINFO).toBool());
bool enable = g->value("FunctionZoom", DEFAULT_FUNCTIONZOOM).toBool();
_partAreaWidget->setZoomFunction(enable);
int levels = g->value("CalleeLevels", DEFAULT_CALLEELEVELS).toInt();
_partAreaWidget->setCallLevels(levels);
enable = g->value("DrawName", DEFAULT_DRAWNAME).toBool();
_partAreaWidget->setFieldVisible(0, enable);
enable = g->value("DrawCost", DEFAULT_DRAWCOST).toBool();
_partAreaWidget->setFieldVisible(1, enable);
enable = g->value("ForceStrings", DEFAULT_FORCESTRINGS).toBool();
_partAreaWidget->setFieldForced(0, enable);
_partAreaWidget->setFieldForced(1, enable);
enable = g->value("AllowRotation", DEFAULT_ALLOWROTATION).toBool();
_partAreaWidget->setAllowRotation(enable);
delete g;
}
void PartSelection::saveOptions(const QString& prefix, const QString& postfix)
{
ConfigGroup* g = ConfigStorage::group(prefix + postfix);
QString mode;
if (_partAreaWidget->visualization() == PartAreaWidget::Inclusive)
mode = "Inclusive";
else
mode = "Partitioning";
g->setValue("PartitionMode", mode, QString(DEFAULT_PARTITIONMODE));
g->setValue("DiagramMode", _diagramMode, DEFAULT_DIAGRAMMODE);
g->setValue("DrawFrames", _drawFrames, DEFAULT_DRAWFRAMES);
g->setValue("ShowInfo", _showInfo, DEFAULT_SHOWINFO);
g->setValue("FunctionZoom",
_partAreaWidget->zoomFunction(), DEFAULT_FUNCTIONZOOM);
g->setValue("CalleeLevels",
_partAreaWidget->callLevels(), DEFAULT_CALLEELEVELS);
g->setValue("DrawName",
_partAreaWidget->fieldVisible(0), DEFAULT_DRAWNAME);
g->setValue("DrawCost",
_partAreaWidget->fieldVisible(1), DEFAULT_DRAWCOST);
g->setValue("ForceStrings",
_partAreaWidget->fieldForced(0), DEFAULT_FORCESTRINGS);
g->setValue("AllowRotation",
_partAreaWidget->allowRotation(), DEFAULT_ALLOWROTATION);
delete g;
}
void PartSelection::showInfo(bool enable)
{
if (_showInfo == enable) return;
_showInfo = enable;
if (enable) {
_rangeLabel->show();
fillInfo();
}
else
_rangeLabel->hide();
}
void PartSelection::fillInfo()
{
if (!_data) {
_rangeLabel->setText(tr("(no trace loaded)"));
return;
}
QString info = _data->activePartRange();
TreeMapItem* i = _partAreaWidget->current();
while (i && i->rtti()!=2) i = i->parent();
if (i) {
TracePart* part = ((PartItem*)i)->part();
//if (!part->trigger().isEmpty()) info += ", " + part->trigger();
if (!part->timeframe().isEmpty())
info += ", Time " + part->timeframe() + " BBs";
}
else {
TracePart* part = _data->parts().first();
if (part && !part->version().isEmpty())
info += ", Cachegrind " + part->version();
}
_rangeLabel->setText(info);
}
#include "moc_partselection.cpp"