/* 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.
*/

/*
 * Call Map View
 */

#include <tqwhatsthis.h>
#include <tqpopupmenu.h>

#include <tdelocale.h>
#include <kiconloader.h>
#include <tdeapplication.h>
#include <tdeconfig.h>

#include "callmapview.h"
#include "configuration.h"
#include "listutils.h"
#include "toplevel.h"

//
// CallMapView
//


// defaults
#define DEFAULT_SPLITMODE    "Rows"
#define DEFAULT_DRAWNAME     true
#define DEFAULT_DRAWCOST     true
#define DEFAULT_DRAWLOCATION false
#define DEFAULT_DRAWCALLS    false
#define DEFAULT_FORCESTRINGS false
#define DEFAULT_ROTATION     true
#define DEFAULT_SHADING      true
#define DEFAULT_MAXAREA      100


CallMapView::CallMapView(bool showCallers, TraceItemView* parentView,
                         TQWidget* parent, const char* name)
  : TreeMapWidget(new CallMapBaseItem(), parent, name),  TraceItemView(parentView)
{
  _showCallers = showCallers;

  setFieldType(0, i18n( "Name" ));
  setFieldType(1, i18n( "Cost" ));
  setFieldType(2, i18n( "Location" ));
  setFieldPosition(2, TreeMapItem::TopLeft);
  setFieldType(3, i18n( "Calls" ));
  setFieldPosition(3, TreeMapItem::TopRight);

  setSplitMode(DEFAULT_SPLITMODE);
  setFieldVisible(0, DEFAULT_DRAWNAME);
  setFieldVisible(1, DEFAULT_DRAWCOST);
  setFieldVisible(2, DEFAULT_DRAWLOCATION);
  setFieldVisible(3, DEFAULT_DRAWCALLS);
  setFieldForced(0, DEFAULT_FORCESTRINGS);
  setFieldForced(1, DEFAULT_FORCESTRINGS);
  setFieldForced(2, DEFAULT_FORCESTRINGS);
  setFieldForced(3, DEFAULT_FORCESTRINGS);
  setAllowRotation(DEFAULT_ROTATION);
  setShadingEnabled(DEFAULT_SHADING);
  setMinimalArea(DEFAULT_MAXAREA);

  connect(this,
          TQ_SIGNAL(doubleClicked(TreeMapItem*)),
          TQ_SLOT(activatedSlot(TreeMapItem*)));
  connect(this,
          TQ_SIGNAL(returnPressed(TreeMapItem*)),
          TQ_SLOT(activatedSlot(TreeMapItem*)));
  connect(this,
          TQ_SIGNAL(currentChanged(TreeMapItem*, bool)),
          TQ_SLOT(selectedSlot(TreeMapItem*, bool)));
  connect(this,
          TQ_SIGNAL(contextMenuRequested(TreeMapItem*,const TQPoint &)),
          TQ_SLOT(context(TreeMapItem*,const TQPoint &)));

  TQWhatsThis::add( this, whatsThis());
}

TQString CallMapView::whatsThis() const
{
  TQString s = _showCallers ?
              i18n( "<b>Caller Map</b>"
                    "<p>This graph shows the nested hierarchy of "
                    "all callers of the current activated function. "
                    "Each colored rectangle represents a function; "
                    "its size tries to be proportional to the cost spent "
                    "therein while the active function is running "
                    "(however, there are drawing constrains).</p>") :
              i18n("<b>Call Map</b>"
                   "<p>This graph shows the nested hierarchy of "
                   "all callees of the current activated function. "
                   "Each colored rectangle represents a function; "
                   "its size tries to be proportional to the cost spent "
                   "therein while the active function is running "
                   "(however, there are drawing constrains).</p>");

  s += i18n( "<p>Appearance options can be found in the "
             "in the context menu. To get exact size proportions, "
             "choose 'Hide incorrect borders'. As this mode can be "
             "<em>very</em> time consuming, you may want to limit "
             "the maximum drawn nesting level before. "
             "'Best' determinates the split direction for children "
             "from the aspect ratio of the parent. "
             "'Always Best' decides on remaining space for each "
             "sibling. "
             "'Ignore Proportions' takes space for function name "
             "drawing <em>before</em> drawing children. Note that "
             "size proportions can get <em>heavily</em> wrong.</p>"

             "<p>This is a <em>TreeMap</em> widget. "
             "Keyboard navigation is available with the left/right arrow "
             "keys for traversing siblings, and up/down arrow keys "
             "to go a nesting level up/down. "
             "<em>Return</em> activates the current item.</p>");

  return s;
}

void CallMapView::setData(TraceData* d)
{
  TraceItemView::setData(d);

  ((CallMapBaseItem*)base())->setFunction(0);
}

void CallMapView::context(TreeMapItem* i,const TQPoint & p)
{
  if (!i) return;

  TQPopupMenu popup;
  TQPopupMenu fpopup; // select function subpopup
  TQPopupMenu vpopup; // visualisation subpopup
  TQPopupMenu dpopup; // split direction
  TQPopupMenu bpopup; // border subpopup
  TQPopupMenu l1popup; // depth limit subpopup
  TQPopupMenu l2popup; // function limit subpopup
  TQPopupMenu l3popup; // area limit subpopup

  TreeMapItem* item = i;
  int count;

  TQString shortCurrentName;
  if (i) {
    shortCurrentName = i->text(0);
    if ((int)shortCurrentName.length() > Configuration::maxSymbolLength())
      shortCurrentName =
        shortCurrentName.left(Configuration::maxSymbolLength()) + "...";
  }

  if (item) {
    popup.insertItem(i18n("Go To"), &fpopup, 100);
    count = 0;
    while (count<Configuration::maxSymbolCount() && item) {
      TQString name = item->text(0);
      if ((int)name.length()>Configuration::maxSymbolLength())
        name = name.left(Configuration::maxSymbolLength()) + "...";
      fpopup.insertItem(name, 101+count);
      item = item->parent();
      count++;
    }
    popup.insertSeparator();
  }

  addGoMenu(&popup);
  popup.insertSeparator();

  l1popup.setCheckable(true);
  popup.insertItem(i18n("Stop at Depth"), &l1popup, 12);

  int maxDepth = maxDrawingDepth();
  l1popup.insertItem(i18n("No Depth Limit"), 50);
  l1popup.setItemChecked(50, maxDepth==-1);
  l1popup.insertSeparator();
  l1popup.insertItem(i18n("Depth 10"), 51);
  l1popup.setItemChecked(51, maxDepth==10);
  l1popup.insertItem(i18n("Depth 15"), 52);
  l1popup.setItemChecked(52, maxDepth==15);
  l1popup.insertItem(i18n("Depth 20"), 53);
  l1popup.setItemChecked(53, maxDepth==20);
  if (i) {
    l1popup.insertSeparator();
    l1popup.insertItem(i18n("Depth of '%1' (%2)")
                       .arg(shortCurrentName).arg(i->depth()), 55);
    l1popup.setItemChecked(55, maxDepth == i->depth());
  }
  if (maxDepth>0) {
    l1popup.insertSeparator();
    l1popup.insertItem(i18n("Decrement Depth (to %1)").arg(maxDepth-1), 56);
    l1popup.insertItem(i18n("Increment Depth (to %1)").arg(maxDepth+1), 57);
  }

  l2popup.setCheckable(true);
  popup.insertItem(i18n("Stop at Function"), &l2popup, 13);
  l2popup.insertItem(i18n("No Function Limit"), 200);
  l2popup.setItemChecked(200, fieldStop(0).isEmpty());
  bool foundStopName = false;
  item = i;
  if (i) {
    l2popup.insertSeparator();
    count = 0;
    while (count<Configuration::maxSymbolCount() && item) {
      TQString name = item->text(0);
      if ((int)name.length()>Configuration::maxSymbolLength())
        name = name.left(Configuration::maxSymbolLength()) + "...";
      l2popup.insertItem(name, 201+count);
      if (item->text(0) == fieldStop(0)) {
        l2popup.setItemChecked(201+count, true);
        foundStopName = true;
      }
      item = item->parent();
      count++;
    }
  }
  if (!foundStopName && !fieldStop(0).isEmpty()) {
    l2popup.insertSeparator();
    TQString name = fieldStop(0);
    if ((int)name.length()>Configuration::maxSymbolLength())
      name = name.left(Configuration::maxSymbolLength()) + "...";
    l2popup.insertItem(name, 199);
    l2popup.setItemChecked(199, true);
  }

  l3popup.setCheckable(true);
  popup.insertItem(i18n("Stop at Area"), &l3popup, 14);

  int mArea = minimalArea();
  l3popup.insertItem(i18n("No Area Limit"), 60);
  l3popup.setItemChecked(60, mArea ==-1);
  l3popup.insertSeparator();
  l3popup.insertItem(i18n("50 Pixels"), 63);
  l3popup.setItemChecked(63, mArea==50);
  l3popup.insertItem(i18n("100 Pixels"), 64);
  l3popup.setItemChecked(64, mArea==100);
  l3popup.insertItem(i18n("200 Pixels"), 65);
  l3popup.setItemChecked(65, mArea==200);
  l3popup.insertItem(i18n("500 Pixels"), 66);
  l3popup.setItemChecked(66, mArea==500);
  int currentArea = 0;
  if (i) {
    currentArea = i->width() * i->height();
    l3popup.insertSeparator();
    l3popup.insertItem(i18n("Area of '%1' (%2)")
                       .arg(shortCurrentName).arg(currentArea), 67);
    l3popup.setItemChecked(67, mArea == currentArea);
  }
  if (mArea>0) {
    l3popup.insertSeparator();
    l3popup.insertItem(i18n("Double Area Limit (to %1)")
                       .arg(mArea*2), 68);
    l3popup.insertItem(i18n("Half Area Limit (to %1)")
                       .arg(mArea/2), 69);
  }

  popup.insertSeparator();

  vpopup.setCheckable(true);
  popup.insertItem(i18n("Visualisation"), &vpopup, 10);

  TQPopupMenu splitpopup;
  addSplitDirectionItems(&splitpopup, 1001);
  vpopup.insertItem(i18n("Split Direction"), &splitpopup, 1000);

  vpopup.insertItem(i18n("Skip Incorrect Borders"), 40);
  vpopup.setItemEnabled(40, !_showCallers);
  vpopup.setItemChecked(40, skipIncorrectBorder());

  bpopup.setCheckable(true);
  vpopup.insertItem(i18n("Border Width"), &bpopup, 41);
  bpopup.insertItem(i18n("Border 0"), 42);
  bpopup.setItemEnabled(42, !_showCallers);
  bpopup.setItemChecked(42, borderWidth()==0);
  bpopup.insertItem(i18n("Border 1"), 43);
  bpopup.setItemChecked(43, borderWidth()==1);
  bpopup.insertItem(i18n("Border 2"), 44);
  bpopup.setItemChecked(44, borderWidth()==2);
  bpopup.insertItem(i18n("Border 3"), 45);
  bpopup.setItemChecked(45, borderWidth()==3);

  vpopup.insertSeparator();

  vpopup.insertItem(i18n("Draw Symbol Names"), 20);
  vpopup.insertItem(i18n("Draw Cost"), 21);
  vpopup.insertItem(i18n("Draw Location"), 22);
  vpopup.insertItem(i18n("Draw Calls"), 23);
  vpopup.insertSeparator();

  vpopup.insertItem(i18n("Ignore Proportions"), 24);
  vpopup.insertItem(i18n("Allow Rotation"), 25);
  if (!fieldVisible(0) &&
      !fieldVisible(1) &&
      !fieldVisible(2) &&
      !fieldVisible(3)) {
    vpopup.setItemEnabled(24, false);
    vpopup.setItemEnabled(25, false);
  }
  else {
    vpopup.setItemChecked(20,fieldVisible(0));
    vpopup.setItemChecked(21,fieldVisible(1));
    vpopup.setItemChecked(22,fieldVisible(2));
    vpopup.setItemChecked(23,fieldVisible(3));
    vpopup.setItemChecked(24,fieldForced(0));
    vpopup.setItemChecked(25,allowRotation());
  }

  vpopup.insertItem(i18n("Shading"), 26);
  vpopup.setItemChecked(26,isShadingEnabled());

  int r = popup.exec(mapToGlobal(p));

  if (r>100 && r<150) {
    r -= 100;
    while (i && (r>1)) {
      i=i->parent();
      r--;
    }
    activatedSlot(i);
    return;
  }

  if (r>200 && r<250) {
    r -= 200;
    while (i && (r>1)) {
      i=i->parent();
      r--;
    }
    if (i)
      setFieldStop(0, i->text(0));

    return;
  }

  switch(r) {
  case 20:
    setFieldVisible(0, !vpopup.isItemChecked(20));
    break;

  case 21:
    setFieldVisible(1, !vpopup.isItemChecked(21));
    break;

  case 22:
    setFieldVisible(2, !vpopup.isItemChecked(22));
    break;

  case 23:
    setFieldVisible(3, !vpopup.isItemChecked(23));
    break;

  case 24:
    setFieldForced(0, !vpopup.isItemChecked(24));
    setFieldForced(1, !vpopup.isItemChecked(24));
    setFieldForced(2, !vpopup.isItemChecked(24));
    setFieldForced(3, !vpopup.isItemChecked(24));
    break;

  case 25: setAllowRotation(!vpopup.isItemChecked(25)); break;
  case 26: setShadingEnabled(!vpopup.isItemChecked(26)); break;

  case 40:
    setSkipIncorrectBorder(!vpopup.isItemChecked(40));
    break;

  case 42: setBorderWidth(0); break;
  case 43: setBorderWidth(1); break;
  case 44: setBorderWidth(2); break;
  case 45: setBorderWidth(3); break;

  case 50: setMaxDrawingDepth(-1); break;
  case 51: setMaxDrawingDepth(10); break;
  case 52: setMaxDrawingDepth(15); break;
  case 53: setMaxDrawingDepth(20); break;
  case 55: setMaxDrawingDepth(i->depth()); break;
  case 56: setMaxDrawingDepth(maxDepth-1); break;
  case 57: setMaxDrawingDepth(maxDepth+1); break;

  case 200: setFieldStop(0, TQString()); break;

  case 60: setMinimalArea(-1); break;
  case 61: setMinimalArea(10); break;
  case 62: setMinimalArea(20); break;
  case 63: setMinimalArea(50); break;
  case 64: setMinimalArea(100); break;
  case 65: setMinimalArea(200); break;
  case 66: setMinimalArea(500); break;
  case 67: setMinimalArea(currentArea); break;
  case 68: setMinimalArea(mArea*2); break;
  case 69: setMinimalArea(mArea/2); break;
  }
}

void CallMapView::activatedSlot(TreeMapItem* item)
{
  if (!item) return;

  if (item->rtti() == 1) {
    CallMapBaseItem* bi = (CallMapBaseItem*)item;
    activated(bi->function());
  }
  else if (item->rtti() == 2) {
    CallMapCallingItem* ci = (CallMapCallingItem*)item;
    activated(ci->function());
  }
  else if (item->rtti() == 3) {
    CallMapCallerItem* ci = (CallMapCallerItem*)item;
    activated(ci->function());
  }
}

void CallMapView::selectedSlot(TreeMapItem* item, bool kbd)
{
  if (!item) return;
  if (item->text(0).isEmpty()) return;

  if (kbd) {
      TQString msg = i18n("Call Map: Current is '%1'").arg(item->text(0));
      if (_topLevel)
	  _topLevel->showMessage(msg, 5000);
  }

  TraceFunction* f = 0;

  if (item->rtti() == 1) {
    CallMapBaseItem* bi = (CallMapBaseItem*)item;
    f = bi->function();
  }
  else if (item->rtti() == 2) {
    CallMapCallingItem* ci = (CallMapCallingItem*)item;
    f = ci->function();
  }
  else if (item->rtti() == 3) {
    CallMapCallerItem* ci = (CallMapCallerItem*)item;
    f = ci->function();
  }
  if (f) {
      // this avoids marking
      _selectedItem = f;
      selected(f);
  }
}

TraceItem* CallMapView::canShow(TraceItem* i)
{
  TraceItem::CostType t = i ? i->type() : TraceItem::NoCostType;

  switch(t) {
  case TraceItem::Function:
  case TraceItem::FunctionCycle:
      return i;
  default:
      break;
  }
  return 0;
}

void CallMapView::doUpdate(int changeType)
{
    if (changeType == costType2Changed) return;

    // if there is a selected item, always draw marking...
    if (changeType & selectedItemChanged) {
	TraceFunction* f = 0;

	if (_selectedItem) {
	    switch(_selectedItem->type()) {
	    case TraceItem::Function:
	    case TraceItem::FunctionCycle:
		f = (TraceFunction*)_selectedItem;
		break;
	    default:
		break;
	    }
	}
	// if this is the only change...
	if (changeType == selectedItemChanged) {
	    setMarked(f ? 1:0, true);
	    return;
	}
	setMarked(f ? 1:0, false);
    }


  if (changeType & activeItemChanged) {
      TraceFunction* f = 0;

      if (_activeItem) {
	  switch(_activeItem->type()) {
	  case TraceItem::Function:
	  case TraceItem::FunctionCycle:
	      f = (TraceFunction*)_activeItem;
	      break;
	  default:
	      break;
	  }
      }
      ((CallMapBaseItem*)base())->setFunction(f);
  }
  else if ( ((changeType & partsChanged) && Configuration::showCycles()) ||
            (changeType & dataChanged) ||
            (changeType & configChanged)) {
    /* regenerates the treemap because traceitems were added/removed */
    base()->refresh();
  }
  else if ((changeType & partsChanged) ||
           (changeType & costTypeChanged)) {
    /* we need to do the draw order sorting again as the values change */
    resort();
    redraw();
  }
  else
    redraw();
}



TQColor CallMapView::groupColor(TraceFunction* f) const
{
  if (!f)
    return colorGroup().button();

  return Configuration::functionColor(_groupType, f);
}


TQString CallMapView::tipString(TreeMapItem* i) const
{
  TQString tip, itemTip;
  int count = 0;

  //tqDebug("CallMapView::tipString for '%s'", i->text(0).ascii());

  // first, SubPartItem's
  while (i && count<Configuration::maxSymbolCount()) {
    itemTip = i->text(0);
    if ((int)itemTip.length()>Configuration::maxSymbolLength())
      itemTip = itemTip.left(Configuration::maxSymbolLength()) + "...";

    if (!i->text(1).isEmpty())
      itemTip += " (" + i->text(1) + ")";

    if (!tip.isEmpty()) tip += "\n";

    tip += itemTip;
    i = i->parent();
    count++;
  }
  if (count == Configuration::maxSymbolCount()) tip += "\n...";

  return tip;
}


TraceCost* CallMapView::totalCost()
{
  TraceFunction* f = ((CallMapBaseItem*)base())->function();
  if (!f) return 0;

  return Configuration::showExpanded() ? f->inclusive() : f->data();
}




// CallMapBaseItem

CallMapBaseItem::CallMapBaseItem()
{
  _f = 0;
}

void CallMapBaseItem::setFunction(TraceFunction* f)
{
  if (f == _f) return;

  _f = f;
  refresh();
}


TQString CallMapBaseItem::text(int textNo) const
{
  if (textNo == 0) {
    if (!_f)
      return i18n("(no function)");

    return _f->prettyName();
  }

  if (!_f) return TQString();

  if (textNo == 2) return _f->prettyLocation();
  if (textNo == 3) return _f->calledCount().pretty();
  if (textNo != 1) return TQString();

  TraceCostType* ct = ((CallMapView*)widget())->costType();
  TraceCost* t      = ((CallMapView*)widget())->totalCost();

  if (Configuration::showPercentage()) {
      double sum, total = t->subCost(ct);
      if (total == 0.0)
	  sum = 100.0;
      else
	  sum = 100.0 * _f->inclusive()->subCost(ct) / total;

      return TQString("%1 %")
	  .arg(sum, 0, 'f', Configuration::percentPrecision());
  }
  return _f->inclusive()->prettySubCost(ct);
}

TQPixmap CallMapBaseItem::pixmap(int i) const
{
    if ((i != 1) || !_f) return TQPixmap();

    TraceCostType* ct = ((CallMapView*)widget())->costType();
    TraceCost* t      = ((CallMapView*)widget())->totalCost();

    // colored level meter with frame
    return costPixmap( ct, _f->inclusive(), (double) (t->subCost(ct)), true);
}


double CallMapBaseItem::value() const
{
  if (!_f) return 0.0;

  TraceCostType* ct;
  ct = ((CallMapView*)widget())->costType();
  return (double) _f->inclusive()->subCost(ct);
}


double CallMapBaseItem::sum() const
{
  if (!_f) return 0.0;

  CallMapView* w = (CallMapView*)widget();

  if (w->showCallers())
    return 0.0;
  else
    return (double) _f->inclusive()->subCost(w->costType());
}


bool CallMapBaseItem::isMarked(int) const
{
    return ((CallMapView*)widget())->selectedItem() == _f;
}

TreeMapItemList* CallMapBaseItem::children()
{
  if (_f && !initialized()) {
    CallMapView* w = (CallMapView*)widget();

    if (0) tqDebug("Create Function %s (%s)",
		  w->showCallers() ? "Callers":"Callees",
		  text(0).ascii());

    TraceCall* call;

    setSorting(-1);
    if (w->showCallers()) {
      TraceCallList l = _f->callers();
      for (call=l.first();call;call=l.next()) {

        // don't show calls inside of a cycle
        if (call->inCycle()>0) continue;
        if (call->isRecursion()) continue;

        addItem(new CallMapCallerItem(1.0, call));
      }

      setSum(0);
    }
    else {
      TraceCallList l = _f->callings();
      for (call=l.first();call;call=l.next()) {

        // don't show calls inside of a cycle
        if (call->inCycle()>0) continue;
        if (call->isRecursion()) continue;

        CallMapCallingItem* i = new CallMapCallingItem(1.0, call);
        i->init();
        addItem(i);
      }

      setSum(_f->inclusive()->subCost(w->costType()));
    }
    setSorting(-2, false);
  }

  return _children;
}

TQColor CallMapBaseItem::backColor() const
{
  return ((CallMapView*)widget())->groupColor(_f);
}



// CallMapCallingItems

CallMapCallingItem::CallMapCallingItem(double factor, TraceCall* c)
{
  _factor = factor;
  _c = c;
}

void CallMapCallingItem::init()
{
#if 0
  // create assoziation: if not possible, i.e. an ass. already exists
  // for the function, we need to draw the recursive version
  _recursive = !setFunction(_c->called());
  _valid = true;
#endif
}

TQString CallMapCallingItem::text(int textNo) const
{
  if (textNo == 0) {
    if (!_c)
      return i18n("(no call)");

    return _c->calledName();
  }

  if (textNo == 2) return _c->called()->prettyLocation();
  if (textNo == 3) return SubCost(_factor * _c->callCount()).pretty();
  if (textNo != 1) return TQString();

  TraceCostType* ct;
  ct = ((CallMapView*)widget())->costType();

  SubCost val = SubCost(_factor * _c->subCost(ct));
  if (Configuration::showPercentage()) {
    // percentage relative to function cost
    TraceCost* t = ((CallMapView*)widget())->totalCost();
    double p  = 100.0 * _factor * _c->subCost(ct) / t->subCost(ct);
    return TQString("%1 %")
      .arg(p, 0, 'f', Configuration::percentPrecision());
  }
  return val.pretty();
}

TQPixmap CallMapCallingItem::pixmap(int i) const
{
    if (i != 1) return TQPixmap();

    // Cost pixmap
    TraceCostType* ct = ((CallMapView*)widget())->costType();
    TraceCost* t      = ((CallMapView*)widget())->totalCost();

    // colored level meter with frame
    return costPixmap( ct, _c, t->subCost(ct) / _factor, true);
}


double CallMapCallingItem::value() const
{
  TraceCostType* ct;
  ct = ((CallMapView*)widget())->costType();
  return _factor * _c->subCost(ct);
}

double CallMapCallingItem::sum() const
{
  return value();
}

bool CallMapCallingItem::isMarked(int) const
{
    return ((CallMapView*)widget())->selectedItem() == _c->called();
}


TreeMapItemList* CallMapCallingItem::children()
{
  if (!initialized()) {
    if (0) tqDebug("Create Calling subitems (%s)", path(0).join("/").ascii());

    TraceCostType* ct;
    ct = ((CallMapView*)widget())->costType();

    // same as sum()
    SubCost s = _c->called()->inclusive()->subCost(ct);
    SubCost v = _c->subCost(ct);
    if (v>s) {
      tqDebug("Warning: CallingItem subVal %u > Sum %u (%s)",
             (unsigned)v, (unsigned)s, _c->called()->prettyName().ascii());
      v = s;
    }
    double newFactor = _factor * v / s;

#if 0
    tqDebug("CallingItem: Subitems of %s => %s, factor %f * %d/%d => %f",
               _c->caller()->prettyName().ascii(),
               _c->called()->prettyName().ascii(),
               _factor, v, s, newFactor);
#endif
    setSorting(-1);
    TraceCall* call;
    TraceCallList l = _c->called()->callings();
    for (call=l.first();call;call=l.next()) {

      // don't show calls inside of a cycle
      if (call->inCycle()>0) continue;
      if (call->isRecursion()) continue;

      CallMapCallingItem* i = new CallMapCallingItem(newFactor, call);
      i->init();
      addItem(i);
    }
    setSorting(-2, false);
  }

  return _children;
}


TQColor CallMapCallingItem::backColor() const
{
  CallMapView* w = (CallMapView*)widget();
  return w->groupColor(_c->called());
}


// CallMapCallerItem

CallMapCallerItem::CallMapCallerItem(double factor, TraceCall* c)
{
  _factor = factor;
  _c = c;
}

TQString CallMapCallerItem::text(int textNo) const
{
  if (textNo == 0) {
    if (!_c)
      return i18n("(no call)");

    return _c->callerName();
  }

  if (textNo == 2) return _c->caller()->prettyLocation();
  if (textNo == 3) return SubCost(_factor * _c->callCount()).pretty();
  if (textNo != 1) return TQString();

  TraceCostType* ct;
  ct = ((CallMapView*)widget())->costType();

  SubCost val = SubCost(_factor * _c->subCost(ct));
  if (Configuration::showPercentage()) {
    TraceCost* t = ((CallMapView*)widget())->totalCost();
    double p  = 100.0 * _factor * _c->subCost(ct) / t->subCost(ct);
    return TQString("%1 %")
      .arg(p, 0, 'f', Configuration::percentPrecision());
  }
  return val.pretty();
}


TQPixmap CallMapCallerItem::pixmap(int i) const
{
    if (i != 1) return TQPixmap();

    // Cost pixmap
    TraceCostType* ct = ((CallMapView*)widget())->costType();
    TraceCost* t      = ((CallMapView*)widget())->totalCost();

    // colored level meter with frame
    return costPixmap( ct, _c, t->subCost(ct) / _factor, true );
}


double CallMapCallerItem::value() const
{
  TraceCostType* ct;
  ct = ((CallMapView*)widget())->costType();
  return (double) _c->subCost(ct);
}

bool CallMapCallerItem::isMarked(int) const
{
    return ((CallMapView*)widget())->selectedItem() == _c->caller();
}


TreeMapItemList* CallMapCallerItem::children()
{
  if (!initialized()) {
    //tqDebug("Create Caller subitems (%s)", name().ascii());

    TraceCostType* ct;
    ct = ((CallMapView*)widget())->costType();

    SubCost s = _c->caller()->inclusive()->subCost(ct);
    SubCost v = _c->subCost(ct);
    double newFactor = _factor * v / s;


#if 0
    tqDebug("CallerItem: Subitems of %s => %s, factor %f * %d/%d => %f",
           _c->caller()->prettyName().ascii(),
           _c->called()->prettyName().ascii(),
           _factor, v, s, newFactor);
#endif
    setSorting(-1);

    TraceCall* call;
    TraceCallList l = _c->caller()->callers();
    for (call=l.first();call;call=l.next()) {

      // don't show calls inside of a cycle
      if (call->inCycle()>0) continue;
      if (call->isRecursion()) continue;

      TreeMapItem* i = new CallMapCallerItem(newFactor, call);
      addItem(i);
    }
    setSorting(-2, false);
  }

  return _children;
}

TQColor CallMapCallerItem::backColor() const
{
  CallMapView* w = (CallMapView*)widget();
  return w->groupColor(_c->caller());
}

void CallMapView::readViewConfig(TDEConfig* c,
				 TQString prefix, TQString postfix, bool)
{
    TDEConfigGroup* g = configGroup(c, prefix, postfix);

    setSplitMode(g->readEntry("SplitMode", DEFAULT_SPLITMODE));

    setFieldVisible(0, g->readBoolEntry("DrawName", DEFAULT_DRAWNAME));
    setFieldVisible(1, g->readBoolEntry("DrawCost", DEFAULT_DRAWCOST));
    setFieldVisible(2, g->readBoolEntry("DrawLocation", DEFAULT_DRAWLOCATION));
    setFieldVisible(3, g->readBoolEntry("DrawCalls", DEFAULT_DRAWCALLS));

    bool enable = g->readBoolEntry("ForceStrings", DEFAULT_FORCESTRINGS);
    setFieldForced(0, enable);
    setFieldForced(1, enable);
    setFieldForced(2, enable);
    setFieldForced(3, enable);

    setAllowRotation(g->readBoolEntry("AllowRotation", DEFAULT_ROTATION));
    setShadingEnabled(g->readBoolEntry("Shading", DEFAULT_SHADING));
    setFieldStop(0, g->readEntry("StopName"));
    setMaxDrawingDepth(g->readNumEntry("MaxDepth", -1));
    setMinimalArea(g->readNumEntry("MaxArea", DEFAULT_MAXAREA));

    delete g;
}

void CallMapView::saveViewConfig(TDEConfig* c, 
				 TQString prefix, TQString postfix, bool)
{
    TDEConfigGroup g(c, (prefix+postfix).ascii());

    writeConfigEntry(&g, "SplitMode", splitModeString(), DEFAULT_SPLITMODE);
    writeConfigEntry(&g, "DrawName", fieldVisible(0), DEFAULT_DRAWNAME);
    writeConfigEntry(&g, "DrawCost", fieldVisible(1), DEFAULT_DRAWCOST);
    writeConfigEntry(&g, "DrawLocation", fieldVisible(2), DEFAULT_DRAWLOCATION);
    writeConfigEntry(&g, "DrawCalls", fieldVisible(3), DEFAULT_DRAWCALLS);
    // when option for all text (0-3)
    writeConfigEntry(&g, "ForceStrings", fieldForced(0), DEFAULT_FORCESTRINGS);

    writeConfigEntry(&g, "AllowRotation", allowRotation(), DEFAULT_ROTATION);
    writeConfigEntry(&g, "Shading", isShadingEnabled(), DEFAULT_SHADING);

    writeConfigEntry(&g, "StopName", fieldStop(0), "");
    writeConfigEntry(&g, "MaxDepth", maxDrawingDepth(), -1);
    writeConfigEntry(&g, "MaxArea", minimalArea(), DEFAULT_MAXAREA);
}

#include "callmapview.moc"
