You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdesdk/tdecachegrind/tdecachegrind/functionselection.cpp

872 lines
23 KiB

/* This file is part of KCachegrind.
Copyright (C) 2002, 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 function selection, to be put into a TQDockWindow
*/
#include <tqtimer.h>
#include <tqlistview.h>
#include <tqlabel.h>
#include <tqpushbutton.h>
#include <tqcombobox.h>
#include <tqlineedit.h>
#include <tqregexp.h>
#include <tqpopupmenu.h>
#include <tdelocale.h>
#include "traceitemview.h"
#include "stackbrowser.h"
#include "functionselection.h"
#include "partgraph.h"
#include "functionitem.h"
#include "costlistitem.h"
#include "configuration.h"
#include "toplevel.h"
FunctionSelection::FunctionSelection( TopLevel* top,
TQWidget* parent, const char* name)
: FunctionSelectionBase(parent, name), TraceItemView(0, top)
{
_group = 0;
_inSetGroup = false;
_inSetFunction = false;
TQStringList args;
args << i18n("(No Grouping)")
<< TraceCost::i18nTypeName(TraceItem::Object)
<< TraceCost::i18nTypeName(TraceItem::File)
<< TraceCost::i18nTypeName(TraceItem::Class)
<< TraceCost::i18nTypeName(TraceItem::FunctionCycle);
groupBox->insertStringList(args);
// this needs same order of grouptype actionlist!
connect(groupBox, TQT_SIGNAL(activated(int)),
top, TQT_SLOT(groupTypeSelected(int)));
// search while typing...
connect(searchEdit, TQT_SIGNAL(textChanged(const TQString&)),
this, TQT_SLOT(searchChanged(const TQString&)));
connect(&_searchTimer, TQT_SIGNAL(timeout()),
this, TQT_SLOT(queryDelayed()));
// select first matching group/function on return
connect(searchEdit, TQT_SIGNAL(returnPressed()),
this, TQT_SLOT(searchReturnPressed()));
searchEdit->setMinimumWidth(50);
// we start with desending cost sorting
functionList->setSorting(0,false);
functionList->setColumnAlignment(0, TQt::AlignRight);
functionList->setColumnAlignment(1, TQt::AlignRight);
functionList->setColumnAlignment(2, TQt::AlignRight);
functionList->setAllColumnsShowFocus(true);
// functionList->setShowSortIndicator(true);
// we can have very long function and location names
functionList->setColumnWidthMode(3, TQListView::Manual);
functionList->setColumnWidth(3, 200);
functionList->setColumnWidthMode(4, TQListView::Manual);
functionList->setColumnWidth(4, 200);
groupList->setSorting(0,false);
groupList->setColumnAlignment(0, TQt::AlignRight);
groupList->setAllColumnsShowFocus(true);
// groupList->setShowSortIndicator(true);
groupList->setResizeMode(TQListView::LastColumn);
#if 0
// single click press activation
connect(functionList, TQT_SIGNAL(selectionChanged(TQListViewItem*)),
this, TQT_SLOT(functionActivated(TQListViewItem*)));
connect(functionList,
TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint &, int)),
this, TQT_SLOT(functionContext(TQListViewItem*, const TQPoint &, int)));
#else
// single click release activation
connect(functionList, TQT_SIGNAL(selectionChanged(TQListViewItem*)),
this, TQT_SLOT(functionSelected(TQListViewItem*)));
connect(functionList, TQT_SIGNAL(clicked(TQListViewItem*)),
this, TQT_SLOT(functionActivated(TQListViewItem*)));
connect(functionList, TQT_SIGNAL(returnPressed(TQListViewItem*)),
this, TQT_SLOT(functionActivated(TQListViewItem*)));
connect(functionList,
TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint &, int)),
this, TQT_SLOT(functionContext(TQListViewItem*, const TQPoint &, int)));
#endif
connect(groupList, TQT_SIGNAL(selectionChanged(TQListViewItem*)),
this, TQT_SLOT(groupSelected(TQListViewItem*)));
connect(groupList, TQT_SIGNAL(doubleClicked(TQListViewItem*)),
this, TQT_SLOT(groupDoubleClicked(TQListViewItem*)));
connect(groupList, TQT_SIGNAL(returnPressed(TQListViewItem*)),
this, TQT_SLOT(groupDoubleClicked(TQListViewItem*)));
connect(groupList,
TQT_SIGNAL(contextMenuRequested(TQListViewItem*, const TQPoint &, int)),
this, TQT_SLOT(groupContext(TQListViewItem*, const TQPoint &, int)));
// start hidden
groupList->hide();
}
FunctionSelection::~FunctionSelection()
{
}
void FunctionSelection::searchReturnPressed()
{
query(searchEdit->text());
TQListViewItem* item;
if (_groupType != TraceItem::Function) {
// if current group not matching, select first matching group
item = groupList->currentItem();
if (!item || !item->isVisible()) {
item = groupList->firstChild();
for (;item;item = item->nextSibling())
if (item->isVisible()) break;
if (!item) return;
setGroup(((CostListItem*)item)->costItem());
return;
}
}
functionActivated(functionList->firstChild());
}
// trigger the query after some delay, dependent on length
void FunctionSelection::searchChanged(const TQString& q)
{
_searchDelayed = q;
int ms = 100;
if (q.length()<5) ms = 200;
if (q.length()<2) ms = 300;
_searchTimer.start(ms,true);
}
void FunctionSelection::queryDelayed()
{
query(_searchDelayed);
}
void FunctionSelection::functionContext(TQListViewItem* i,
const TQPoint & p, int c)
{
TQPopupMenu popup;
TraceFunction* f = 0;
if (i) {
f = ((FunctionItem*) i)->function();
if (f) {
popup.insertItem(i18n("Go to %1").arg(f->prettyName()), 93);
popup.insertSeparator();
}
}
if ((c == 0) || (c == 1)) {
addCostMenu(&popup,false);
popup.insertSeparator();
}
addGroupMenu(&popup);
popup.insertSeparator();
addGoMenu(&popup);
int r = popup.exec(p);
if (r == 93) activated(f);
}
void FunctionSelection::groupContext(TQListViewItem* /*i*/,
const TQPoint & p, int c)
{
TQPopupMenu popup;
#if 0
TraceCostItem* g = 0;
if (i) {
g = ((CostListItem*) i)->costItem();
if (!g) {
popup.insertItem(i18n("Show All Items"), 93);
popup.insertSeparator();
}
}
#endif
if (c == 0) {
addCostMenu(&popup,false);
popup.insertSeparator();
}
addGroupMenu(&popup);
popup.insertSeparator();
addGoMenu(&popup);
popup.exec(p);
}
void FunctionSelection::addGroupMenu(TQPopupMenu* popup)
{
TQPopupMenu *popup1 = new TQPopupMenu(popup);
popup1->setCheckable(true);
if (_groupType != TraceItem::Function) {
popup1->insertItem(i18n("No Grouping"),0);
popup1->insertSeparator();
}
popup1->insertItem(TraceCost::i18nTypeName(TraceItem::Object),1);
popup1->insertItem(TraceCost::i18nTypeName(TraceItem::File),2);
popup1->insertItem(TraceCost::i18nTypeName(TraceItem::Class),3);
popup1->insertItem(TraceCost::i18nTypeName(TraceItem::FunctionCycle),4);
switch(_groupType) {
case TraceItem::Object: popup1->setItemChecked(1, true); break;
case TraceItem::File: popup1->setItemChecked(2, true); break;
case TraceItem::Class: popup1->setItemChecked(3, true); break;
case TraceItem::FunctionCycle: popup1->setItemChecked(4, true); break;
default: break;
}
connect(popup1,TQT_SIGNAL(activated(int)),
_topLevel,TQT_SLOT(groupTypeSelected(int)));
popup->insertItem(i18n("Grouping"), popup1);
}
TraceItem* FunctionSelection::canShow(TraceItem* i)
{
TraceItem::CostType t = i ? i->type() : TraceItem::NoCostType;
switch(t) {
case TraceItem::Function:
case TraceItem::FunctionCycle:
case TraceItem::Object:
case TraceItem::File:
case TraceItem::Class:
break;
case TraceItem::Instr:
i = ((TraceInstr*)i)->function();
break;
case TraceItem::Line:
i = ((TraceLine*)i)->functionSource()->function();
break;
default:
i = 0;
break;
}
return i;
}
void FunctionSelection::doUpdate(int changeType)
{
// Special case ?
if (changeType == selectedItemChanged) return;
// we don't show cost 2 at all...
if (changeType == costType2Changed) return;
if (changeType == activeItemChanged) {
if (_activeItem ==0) {
functionList->clearSelection();
return;
}
switch(_activeItem->type()) {
case TraceItem::Object:
case TraceItem::File:
case TraceItem::Class:
setGroup((TraceCostItem*)_activeItem);
return;
default: break;
}
// active item is a function
TraceFunction* f = (TraceFunction*) _activeItem;
// if already current, nothing to do
TQListViewItem* i = functionList->currentItem();
if (i && (((FunctionItem*)i)->function() == f)) {
functionList->setSelected(i,true);
return;
}
// reset searchEdit (as not activated from this view)
_searchString = TQString();
query(TQString());
// select cost item group of function
switch(_groupType) {
case TraceItem::Object: setGroup(f->object()); break;
case TraceItem::Class: setGroup(f->cls()); break;
case TraceItem::File: setGroup(f->file()); break;
case TraceItem::FunctionCycle: setGroup(f->cycle()); break;
default:
break;
}
TQListViewItem* item = functionList->firstChild();
for (;item;item = item->nextSibling())
if (((FunctionItem*)item)->function() == f)
break;
if (!item)
item = new FunctionItem(functionList, f, _costType, _groupType);
functionList->ensureItemVisible(item);
// prohibit signalling of a function selection
_inSetFunction = true;
functionList->setSelected(item, true);
_inSetFunction = false;
return;
}
if (changeType & groupTypeChanged) {
if (_activeItem && (_activeItem->type() == TraceItem::Function)) {
TraceFunction* f = (TraceFunction*) _activeItem;
// select cost item group of function
switch(_groupType) {
case TraceItem::Object: _group = f->object(); break;
case TraceItem::Class: _group = f->cls(); break;
case TraceItem::File: _group = f->file(); break;
case TraceItem::FunctionCycle: _group = f->cycle(); break;
default:
_group = 0;
break;
}
}
int id;
switch(_groupType) {
case TraceItem::Object: id = 1; break;
case TraceItem::File: id = 2; break;
case TraceItem::Class: id = 3; break;
case TraceItem::FunctionCycle: id = 4; break;
default: id = 0; break;
}
groupBox->setCurrentItem(id);
if (_groupType == TraceItem::Function)
groupList->hide();
else
groupList->show();
}
// reset searchEdit
_searchString = TQString();
query(TQString());
refresh();
}
/*
* This set/selects a group of the set available within the
* current group type
*/
void FunctionSelection::setGroup(TraceCostItem* g)
{
if (!g) return;
if (g->type() != _groupType) return;
if (g == _group) return;
_group = g;
TQListViewItem* item = groupList->firstChild();
for (;item;item = item->nextSibling())
if (((CostListItem*)item)->costItem() == g)
break;
if (item) {
groupList->ensureItemVisible(item);
// prohibit signalling of a group selection
_inSetGroup = true;
groupList->setSelected(item, true);
_inSetGroup = false;
}
else
groupList->clearSelection();
}
void FunctionSelection::refresh()
{
groupList->setUpdatesEnabled(false);
groupList->clear();
// make cost columns as small as possible:
// the new functions make them as wide as needed
groupList->setColumnWidth(0, 50);
groupList->setColumnText(1, TraceItem::i18nTypeName(_groupType));
if (!_data || _data->parts().count()==0) {
functionList->clear();
groupList->setUpdatesEnabled(true);
groupList->repaint();
// this clears all other lists
functionList->setSelected(functionList->firstChild(), true);
return;
}
/*
tqDebug("FunctionSelection::fillLists (%s)",
_data->command().ascii());
*/
TraceObjectMap::Iterator oit;
TraceClassMap::Iterator cit;
TraceFileMap::Iterator fit;
TQListViewItem *i = 0, *item = 0, *fitem = 0;
// Fill up group list.
// Always show group of current function, even if cost below low limit.
//
_hc.clear(Configuration::maxListCount());
TraceCostItem *group;
// update group from _activeItem if possible
if (_activeItem && (_activeItem->type() == _groupType))
_group = (TraceCostItem*) _activeItem;
switch(_groupType) {
case TraceItem::Object:
for ( oit = _data->objectMap().begin();
oit != _data->objectMap().end(); ++oit )
_hc.addCost(&(*oit), (*oit).subCost(_costType));
break;
case TraceItem::Class:
for ( cit = _data->classMap().begin();
cit != _data->classMap().end(); ++cit )
_hc.addCost(&(*cit), (*cit).subCost(_costType));
break;
case TraceItem::File:
for ( fit = _data->fileMap().begin();
fit != _data->fileMap().end(); ++fit )
_hc.addCost(&(*fit), (*fit).subCost(_costType));
break;
case TraceItem::FunctionCycle:
{
// add all cycles
TraceFunctionCycleList l = _data->functionCycles();
for (group=l.first();group;group=l.next())
_hc.addCost(group, group->subCost(_costType));
}
break;
default:
{
TQListViewItem* oldItem = functionList->selectedItem();
TraceFunction* oldFunction = 0;
int oldPos = 0;
if (oldItem) {
oldFunction = ((FunctionItem*)oldItem)->function();
oldPos = oldItem->itemPos();
oldPos -= functionList->contentsY();
if (oldPos < 0 || oldPos > functionList->height())
oldFunction = 0;
}
// switching off TQListView updates is buggy with some QT versions...
//functionList->setUpdatesEnabled(false);
functionList->clear();
setCostColumnWidths();
if (0) tqDebug("Function %s at %d, Item %p",
oldFunction ? oldFunction->name().ascii() : "-",
oldPos, (void*)oldItem);
TraceFunctionMap::Iterator it;
TraceFunction *f;
i = 0;
fitem = 0;
for ( it = _data->functionMap().begin();
it != _data->functionMap().end(); ++it )
_hc.addCost(&(*it), (*it).inclusive()->subCost(_costType));
TraceFunctionCycleList l = _data->functionCycles();
for (f=l.first();f;f=l.next())
_hc.addCost(f, f->inclusive()->subCost(_costType));
if (_activeItem &&
((_activeItem->type() == TraceItem::Function) ||
(_activeItem->type() == TraceItem::FunctionCycle)))
fitem = new FunctionItem(functionList, (TraceFunction*)_activeItem,
_costType, _groupType);
for(int i=0;i<_hc.realCount();i++) {
f = (TraceFunction*)_hc[i];
if (f == _activeItem) continue;
new FunctionItem(functionList, f, _costType, _groupType);
}
if (_hc.hasMore()) {
// a placeholder for all the cost items skipped ...
new FunctionItem(functionList, _hc.count() - _hc.maxSize(),
(TraceFunction*)_hc[_hc.maxSize()-1], _costType);
}
functionList->sort();
if (fitem && oldFunction) {
_inSetFunction = true;
functionList->setSelected(fitem, true);
_inSetFunction = false;
int newPos = functionList->itemPos(fitem) - functionList->contentsY();
functionList->scrollBy(0, newPos-oldPos);
}
else if (fitem) {
functionList->ensureItemVisible(fitem);
_inSetFunction = true;
functionList->setSelected(fitem, true);
_inSetFunction = false;
}
else
functionList->clearSelection();
//functionList->setUpdatesEnabled(true);
//functionList->repaint();
groupList->setUpdatesEnabled(true);
groupList->repaint();
return;
}
}
// we always put group of active item in list, even if
// it would be skipped because of small costs
if (_group)
item = new CostListItem(groupList, _group, _costType);
for(int i=0;i<_hc.realCount();i++) {
group = (TraceCostItem*)_hc[i];
// don't put group of active item twice into list
if (group == _group) continue;
new CostListItem(groupList, group, _costType);
}
if (_hc.hasMore()) {
// a placeholder for all the cost items skipped ...
new CostListItem(groupList, _hc.count() - _hc.maxSize(),
(TraceCostItem*)_hc[_hc.maxSize()-1], _costType);
}
groupList->sort();
if (item) {
groupList->ensureItemVisible(item);
_inSetGroup = true;
groupList->setSelected(item, true);
_inSetGroup = false;
}
else
groupList->clearSelection();
groupList->setUpdatesEnabled(true);
groupList->repaint();
}
void FunctionSelection::groupSelected(TQListViewItem* i)
{
if (!i) return;
if (!_data) return;
TraceCostItem* g = ((CostListItem*) i)->costItem();
if (!g) return;
_group = g;
TraceFunctionList list;
switch(g->type()) {
case TraceItem::Object:
list = ((TraceObject*)g)->functions();
break;
case TraceItem::Class:
list = ((TraceClass*)g)->functions();
break;
case TraceItem::File:
list = ((TraceFile*)g)->functions();
break;
case TraceItem::FunctionCycle:
list = ((TraceFunctionCycle*)g)->members();
break;
default:
return;
}
// switching off TQListView updates is buggy with some QT versions...
//functionList->setUpdatesEnabled(false);
functionList->clear();
setCostColumnWidths();
double total;
if (Configuration::showExpanded())
total = (double) g->subCost(_costType);
else
total = (double) _data->subCost(_costType);
#if 0
if (total == 0.0) {
functionList->setUpdatesEnabled(true);
functionList->repaint();
return;
}
#endif
TQRegExp re(_searchString, false, true);
FunctionItem* fitem = 0;
TraceFunction *f;
_hc.clear(Configuration::maxListCount());
for (f=list.first();f;f=list.next()) {
if (re.search(f->prettyName())<0) continue;
_hc.addCost(f, f->inclusive()->subCost(_costType));
if (_activeItem == f)
fitem = new FunctionItem(functionList, (TraceFunction*)_activeItem,
_costType, _groupType);
}
for(int i=0;i<_hc.realCount();i++) {
if (_activeItem == (TraceFunction*)_hc[i]) continue;
new FunctionItem(functionList, (TraceFunction*)_hc[i],
_costType, _groupType);
}
if (_hc.hasMore()) {
// a placeholder for all the functions skipped ...
new FunctionItem(functionList, _hc.count() - _hc.maxSize(),
(TraceFunction*)_hc[_hc.maxSize()-1], _costType);
}
functionList->sort();
if (fitem) {
functionList->ensureItemVisible(fitem);
_inSetFunction = true;
functionList->setSelected(fitem, true);
_inSetFunction = false;
}
//functionList->setUpdatesEnabled(true);
//functionList->repaint();
// Don't emit signal if cost item was changed programatically
if (!_inSetGroup) {
_selectedItem = g;
selected(g);
}
}
void FunctionSelection::groupDoubleClicked(TQListViewItem* i)
{
if (!i) return;
if (!_data) return;
TraceCostItem* g = ((CostListItem*) i)->costItem();
if (!g) return;
// group must be selected first
if (g != _group) return;
activated(g);
}
TraceCostItem* FunctionSelection::group(TQString s)
{
TQListViewItem *item;
item = groupList->firstChild();
for(;item;item = item->nextSibling())
if (((CostListItem*)item)->costItem()->name() == s)
return ((CostListItem*)item)->costItem();
return 0;
}
void FunctionSelection::functionSelected(TQListViewItem* i)
{
if (!i) return;
if (!_data) return;
TraceFunction* f = ((FunctionItem*) i)->function();
if (!f) return;
//tqDebug("FunctionSelection::functionSelected %s", f->name().ascii());
// Don't emit signal if function was changed programatically
if (!_inSetFunction) {
_selectedItem = f;
selected(f);
}
}
void FunctionSelection::functionActivated(TQListViewItem* i)
{
if (!i) return;
if (!_data) return;
TraceFunction* f = ((FunctionItem*) i)->function();
if (!f) return;
if (!_inSetFunction)
activated(f);
}
void FunctionSelection::updateGroupSizes(bool hideEmpty)
{
TQListViewItem* item = groupList->firstChild();
for (;item;item = item->nextSibling()) {
CostListItem* i = (CostListItem*)item;
int size = (_groupSize.contains(i->costItem())) ?
_groupSize[i->costItem()] : -1;
i->setSize(size);
i->setVisible(!hideEmpty || (size>0));
}
}
void FunctionSelection::query(TQString query)
{
if (searchEdit->text() != query)
searchEdit->setText(query);
if (_searchString == query) {
// when resetting query, get rid of group sizes
if (query.isEmpty()) {
_groupSize.clear();
updateGroupSizes(false);
}
return;
}
_searchString = query;
TQRegExp re(query, false, true);
_groupSize.clear();
TraceFunction* f = 0;
TraceFunctionList list2;
_hc.clear(Configuration::maxListCount());
TraceFunctionMap::Iterator it;
for ( it = _data->functionMap().begin();
it != _data->functionMap().end(); ++it ) {
f = &(*it);
if (re.search(f->prettyName())>=0) {
if (_group) {
if (_groupType==TraceItem::Object) {
if (_groupSize.contains(f->object()))
_groupSize[f->object()]++;
else
_groupSize[f->object()] = 1;
if (f->object() != _group) continue;
}
else if (_groupType==TraceItem::Class) {
if (_groupSize.contains(f->cls()))
_groupSize[f->cls()]++;
else
_groupSize[f->cls()] = 1;
if (f->cls() != _group) continue;
}
else if (_groupType==TraceItem::File) {
if (_groupSize.contains(f->file()))
_groupSize[f->file()]++;
else
_groupSize[f->file()] = 1;
if (f->file() != _group) continue;
}
else if (_groupType==TraceItem::FunctionCycle) {
if (_groupSize.contains(f->cycle()))
_groupSize[f->cycle()]++;
else
_groupSize[f->cycle()] = 1;
if (f->cycle() != _group) continue;
}
}
_hc.addCost(f, f->inclusive()->subCost(_costType));
}
}
updateGroupSizes(true);
FunctionItem *fi, *item = 0;
functionList->clear();
setCostColumnWidths();
for(int i=0;i<_hc.realCount();i++) {
fi = new FunctionItem(functionList, (TraceFunction*)_hc[i],
_costType, _groupType);
if (_activeItem == f) item = fi;
}
if (_hc.hasMore()) {
// a placeholder for all the functions skipped ...
new FunctionItem(functionList, _hc.count() - _hc.maxSize(),
(TraceFunction*)_hc[_hc.maxSize()-1], _costType);
}
functionList->sort();
if (item) {
functionList->ensureItemVisible(item);
_inSetFunction = true;
functionList->setSelected(item, true);
_inSetFunction = false;
}
else {
// this emits a function selection
functionList->setSelected(functionList->firstChild(), true);
}
}
bool FunctionSelection::setTopFunction()
{
TQListViewItem* i = functionList->firstChild();
// this emits a function selection
functionList->setSelected(i, true);
functionActivated(i);
return i!=0;
}
void FunctionSelection::setCostColumnWidths()
{
if (_costType && (_costType->subCost(_data->callMax())>0) ) {
functionList->setColumnWidthMode(0, TQListView::Maximum);
functionList->setColumnWidth(0,50);
functionList->setColumnWidthMode(2, TQListView::Maximum);
functionList->setColumnWidth(2,50);
}
else {
functionList->setColumnWidthMode(0, TQListView::Manual);
functionList->setColumnWidth(0,0);
functionList->setColumnWidthMode(2, TQListView::Manual);
functionList->setColumnWidth(2,0);
}
functionList->setColumnWidth(1, 50);
}
#include "functionselection.moc"