summaryrefslogtreecommitdiffstats
path: root/konq-plugins/fsview/treemap.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'konq-plugins/fsview/treemap.cpp')
-rw-r--r--konq-plugins/fsview/treemap.cpp3199
1 files changed, 3199 insertions, 0 deletions
diff --git a/konq-plugins/fsview/treemap.cpp b/konq-plugins/fsview/treemap.cpp
new file mode 100644
index 0000000..8de5c01
--- /dev/null
+++ b/konq-plugins/fsview/treemap.cpp
@@ -0,0 +1,3199 @@
+/* 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.
+*/
+
+/*
+ * A Widget for visualizing hierarchical metrics as areas.
+ * The API is similar to QListView.
+ */
+
+#include <math.h>
+
+#include <qpainter.h>
+#include <qtooltip.h>
+#include <qregexp.h>
+#include <qstyle.h>
+#include <qpopupmenu.h>
+
+#include <klocale.h>
+#include <kconfig.h>
+#include <kdebug.h>
+
+#include "treemap.h"
+
+
+// set this to 1 to enable debug output
+#define DEBUG_DRAWING 0
+#define MAX_FIELD 12
+
+
+//
+// StoredDrawParams
+//
+StoredDrawParams::StoredDrawParams()
+{
+ _selected = false;
+ _current = false;
+ _shaded = true;
+ _rotated = false;
+
+ _backColor = Qt::white;
+
+ // field array has size 0
+}
+
+StoredDrawParams::StoredDrawParams(QColor c,
+ bool selected, bool current)
+{
+ _backColor = c;
+
+ _selected = selected;
+ _current = current;
+ _shaded = true;
+ _rotated = false;
+
+ // field array has size 0
+}
+
+QString StoredDrawParams::text(int f) const
+{
+ if ((f<0) || (f >= (int)_field.size()))
+ return QString::null;
+
+ return _field[f].text;
+}
+
+QPixmap StoredDrawParams::pixmap(int f) const
+{
+ if ((f<0) || (f >= (int)_field.size()))
+ return QPixmap();
+
+ return _field[f].pix;
+}
+
+DrawParams::Position StoredDrawParams::position(int f) const
+{
+ if ((f<0) || (f >= (int)_field.size()))
+ return Default;
+
+ return _field[f].pos;
+}
+
+int StoredDrawParams::maxLines(int f) const
+{
+ if ((f<0) || (f >= (int)_field.size()))
+ return 0;
+
+ return _field[f].maxLines;
+}
+
+const QFont& StoredDrawParams::font() const
+{
+ static QFont* f = 0;
+ if (!f) f = new QFont(QApplication::font());
+
+ return *f;
+}
+
+void StoredDrawParams::ensureField(int f)
+{
+ static Field* def = 0;
+ if (!def) {
+ def = new Field();
+ def->pos = Default;
+ def->maxLines = 0;
+ }
+
+ if (f<0 || f>=MAX_FIELD) return;
+
+ if ((int)_field.size() < f+1) _field.resize(f+1, *def);
+}
+
+
+void StoredDrawParams::setField(int f, const QString& t, QPixmap pm,
+ Position p, int maxLines)
+{
+ if (f<0 || f>=MAX_FIELD) return;
+ ensureField(f);
+
+ _field[f].text = t;
+ _field[f].pix = pm;
+ _field[f].pos = p;
+ _field[f].maxLines = maxLines;
+}
+
+void StoredDrawParams::setText(int f, const QString& t)
+{
+ if (f<0 || f>=MAX_FIELD) return;
+ ensureField(f);
+
+ _field[f].text = t;
+}
+
+void StoredDrawParams::setPixmap(int f, const QPixmap& pm)
+{
+ if (f<0 || f>=MAX_FIELD) return;
+ ensureField(f);
+
+ _field[f].pix = pm;
+}
+
+void StoredDrawParams::setPosition(int f, Position p)
+{
+ if (f<0 || f>=MAX_FIELD) return;
+ ensureField(f);
+
+ _field[f].pos = p;
+}
+
+void StoredDrawParams::setMaxLines(int f, int m)
+{
+ if (f<0 || f>=MAX_FIELD) return;
+ ensureField(f);
+
+ _field[f].maxLines = m;
+}
+
+
+
+//
+// RectDrawing
+//
+
+RectDrawing::RectDrawing(QRect r)
+{
+ _fm = 0;
+ _dp = 0;
+ setRect(r);
+}
+
+
+RectDrawing::~RectDrawing()
+{
+ delete _fm;
+ delete _dp;
+}
+
+DrawParams* RectDrawing::drawParams()
+{
+ if (!_dp)
+ _dp = new StoredDrawParams();
+
+ return _dp;
+}
+
+
+void RectDrawing::setDrawParams(DrawParams* dp)
+{
+ if (_dp) delete _dp;
+ _dp = dp;
+}
+
+void RectDrawing::setRect(QRect r)
+{
+ _rect = r;
+
+ _usedTopLeft = 0;
+ _usedTopCenter = 0;
+ _usedTopRight = 0;
+ _usedBottomLeft = 0;
+ _usedBottomCenter = 0;
+ _usedBottomRight = 0;
+
+ _fontHeight = 0;
+}
+
+QRect RectDrawing::remainingRect(DrawParams* dp)
+{
+ if (!dp) dp = drawParams();
+
+ if ((_usedTopLeft >0) ||
+ (_usedTopCenter >0) ||
+ (_usedTopRight >0)) {
+ if (dp->rotated())
+ _rect.setLeft(_rect.left() + _fontHeight);
+ else
+ _rect.setTop(_rect.top() + _fontHeight);
+ }
+
+ if ((_usedBottomLeft >0) ||
+ (_usedBottomCenter >0) ||
+ (_usedBottomRight >0)) {
+ if (dp->rotated())
+ _rect.setRight(_rect.right() - _fontHeight);
+ else
+ _rect.setBottom(_rect.bottom() - _fontHeight);
+ }
+ return _rect;
+}
+
+
+void RectDrawing::drawBack(QPainter* p, DrawParams* dp)
+{
+ if (!dp) dp = drawParams();
+ if (_rect.width()<=0 || _rect.height()<=0) return;
+
+ QRect r = _rect;
+ QColor normal = dp->backColor();
+ if (dp->selected()) normal = normal.light();
+ bool isCurrent = dp->current();
+
+ // 3D raised/sunken frame effect...
+ QColor high = normal.light();
+ QColor low = normal.dark();
+ p->setPen( isCurrent ? low:high);
+ p->drawLine(r.left(), r.top(), r.right(), r.top());
+ p->drawLine(r.left(), r.top(), r.left(), r.bottom());
+ p->setPen( isCurrent ? high:low);
+ p->drawLine(r.right(), r.top(), r.right(), r.bottom());
+ p->drawLine(r.left(), r.bottom(), r.right(), r.bottom());
+ r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2);
+ if (r.width()<=0 || r.height()<=0) return;
+
+ if (dp->shaded()) {
+ // some shading
+ bool goDark = qGray(normal.rgb())>128;
+ int rBase, gBase, bBase;
+ normal.rgb(&rBase, &gBase, &bBase);
+ p->setBrush(QBrush::NoBrush);
+
+ // shade parameters:
+ int d = 7;
+ float factor = 0.1, forth=0.7, back1 =0.9, toBack2 = .7, back2 = 0.97;
+
+ // coefficient corrections because of rectangle size
+ int s = r.width();
+ if (s > r.height()) s = r.height();
+ if (s<100) {
+ forth -= .3 * (100-s)/100;
+ back1 -= .2 * (100-s)/100;
+ back2 -= .02 * (100-s)/100;
+ }
+
+
+ // maximal color difference
+ int rDiff = goDark ? -rBase/d : (255-rBase)/d;
+ int gDiff = goDark ? -gBase/d : (255-gBase)/d;
+ int bDiff = goDark ? -bBase/d : (255-bBase)/d;
+
+ QColor shadeColor;
+ while (factor<.95) {
+ shadeColor.setRgb((int)(rBase+factor*rDiff+.5),
+ (int)(gBase+factor*gDiff+.5),
+ (int)(bBase+factor*bDiff+.5));
+ p->setPen(shadeColor);
+ p->drawRect(r);
+ r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2);
+ if (r.width()<=0 || r.height()<=0) return;
+ factor = 1.0 - ((1.0 - factor) * forth);
+ }
+
+ // and back (1st half)
+ while (factor>toBack2) {
+ shadeColor.setRgb((int)(rBase+factor*rDiff+.5),
+ (int)(gBase+factor*gDiff+.5),
+ (int)(bBase+factor*bDiff+.5));
+ p->setPen(shadeColor);
+ p->drawRect(r);
+ r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2);
+ if (r.width()<=0 || r.height()<=0) return;
+ factor = 1.0 - ((1.0 - factor) / back1);
+ }
+
+ // and back (2nd half)
+ while ( factor>.01) {
+ shadeColor.setRgb((int)(rBase+factor*rDiff+.5),
+ (int)(gBase+factor*gDiff+.5),
+ (int)(bBase+factor*bDiff+.5));
+ p->setPen(shadeColor);
+ p->drawRect(r);
+ r.setRect(r.x()+1, r.y()+1, r.width()-2, r.height()-2);
+ if (r.width()<=0 || r.height()<=0) return;
+
+ factor = factor * back2;
+ }
+ }
+
+ // fill inside
+ p->setPen(QPen::NoPen);
+ p->setBrush(normal);
+ p->drawRect(r);
+}
+
+
+bool RectDrawing::drawField(QPainter* p, int f, DrawParams* dp)
+{
+ if (!dp) dp = drawParams();
+
+ if (!_fm) {
+ _fm = new QFontMetrics(dp->font());
+ _fontHeight = _fm->height();
+ }
+
+ QRect r = _rect;
+
+ if (0) kdDebug(90100) << "DrawField: Rect " << r.x() << "/" << r.y()
+ << " - " << r.width() << "x" << r.height() << endl;
+
+ int h = _fontHeight;
+ bool rotate = dp->rotated();
+ int width = (rotate ? r.height() : r.width()) -4;
+ int height = (rotate ? r.width() : r.height());
+ int lines = height / h;
+
+ // stop if we have no space available
+ if (lines<1) return false;
+
+ // calculate free space in first line (<unused>)
+ int pos = dp->position(f);
+ if (pos == DrawParams::Default) {
+ switch(f%4) {
+ case 0: pos = DrawParams::TopLeft; break;
+ case 1: pos = DrawParams::TopRight; break;
+ case 2: pos = DrawParams::BottomRight; break;
+ case 3: pos = DrawParams::BottomLeft; break;
+ }
+ }
+
+ int unused = 0;
+ bool isBottom = false;
+ bool isCenter = false;
+ bool isRight = false;
+ int* used = 0;
+ switch(pos) {
+ case DrawParams::TopLeft:
+ used = &_usedTopLeft;
+ if (_usedTopLeft == 0) {
+ if (_usedTopCenter)
+ unused = (width - _usedTopCenter)/2;
+ else
+ unused = width - _usedTopRight;
+ }
+ break;
+
+ case DrawParams::TopCenter:
+ isCenter = true;
+ used = &_usedTopCenter;
+ if (_usedTopCenter == 0) {
+ if (_usedTopLeft > _usedTopRight)
+ unused = width - 2 * _usedTopLeft;
+ else
+ unused = width - 2 * _usedTopRight;
+ }
+ break;
+
+ case DrawParams::TopRight:
+ isRight = true;
+ used = &_usedTopRight;
+ if (_usedTopRight == 0) {
+ if (_usedTopCenter)
+ unused = (width - _usedTopCenter)/2;
+ else
+ unused = width - _usedTopLeft;
+ }
+ break;
+
+ case DrawParams::BottomLeft:
+ isBottom = true;
+ used = &_usedBottomLeft;
+ if (_usedBottomLeft == 0) {
+ if (_usedBottomCenter)
+ unused = (width - _usedBottomCenter)/2;
+ else
+ unused = width - _usedBottomRight;
+ }
+ break;
+
+ case DrawParams::BottomCenter:
+ isCenter = true;
+ isBottom = true;
+ used = &_usedBottomCenter;
+ if (_usedBottomCenter == 0) {
+ if (_usedBottomLeft > _usedBottomRight)
+ unused = width - 2 * _usedBottomLeft;
+ else
+ unused = width - 2 * _usedBottomRight;
+ }
+ break;
+
+ case DrawParams::BottomRight:
+ isRight = true;
+ isBottom = true;
+ used = &_usedBottomRight;
+ if (_usedBottomRight == 0) {
+ if (_usedBottomCenter)
+ unused = (width - _usedBottomCenter)/2;
+ else
+ unused = width - _usedBottomLeft;
+ }
+ break;
+ }
+
+ if (isBottom) {
+ if ((_usedTopLeft >0) ||
+ (_usedTopCenter >0) ||
+ (_usedTopRight >0))
+ lines--;
+ }
+ else if (!isBottom) {
+ if ((_usedBottomLeft >0) ||
+ (_usedBottomCenter >0) ||
+ (_usedBottomRight >0))
+ lines--;
+ }
+ if (lines<1) return false;
+
+
+ int y = isBottom ? height - h : 0;
+
+ if (unused < 0) unused = 0;
+ if (unused == 0) {
+ // no space available in last line at this position
+ y = isBottom ? (y-h) : (y+h);
+ lines--;
+
+ if (lines<1) return false;
+
+ // new line: reset used space
+ if (isBottom)
+ _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0;
+ else
+ _usedTopLeft = _usedTopCenter = _usedTopRight = 0;
+
+ unused = width;
+ }
+
+ // stop as soon as possible when there's no space for "..."
+ static int dotW = 0;
+ if (!dotW) dotW = _fm->width("...");
+ if (width < dotW) return false;
+
+ // get text and pixmap now, only if we need to, because it is possible
+ // that they are calculated on demand (and this can take some time)
+ QString name = dp->text(f);
+ if (name.isEmpty()) return 0;
+ QPixmap pix = dp->pixmap(f);
+
+ // check if pixmap can be drawn
+ int pixW = pix.width();
+ int pixH = pix.height();
+ int pixY = 0;
+ bool pixDrawn = true;
+ if (pixW>0) {
+ pixW += 2; // X distance from pix
+ if ((width < pixW + dotW) || (height < pixH)) {
+ // don't draw
+ pixW = 0;
+ }
+ else
+ pixDrawn = false;
+ }
+
+ // width of text and pixmap to be drawn
+ int w = pixW + _fm->width(name);
+
+ if (0) kdDebug(90100) << " For '" << name << "': Unused " << unused
+ << ", StrW " << w << ", Width " << width << endl;
+
+ // if we have limited space at 1st line:
+ // use it only if whole name does fit in last line...
+ if ((unused < width) && (w > unused)) {
+ y = isBottom ? (y-h) : (y+h);
+ lines--;
+
+ if (lines<1) return false;
+
+ // new line: reset used space
+ if (isBottom)
+ _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0;
+ else
+ _usedTopLeft = _usedTopCenter = _usedTopRight = 0;
+ }
+
+ p->save();
+ p->setPen( (qGray(dp->backColor().rgb())>100) ? Qt::black : Qt::white);
+ p->setFont(dp->font());
+ if (rotate) {
+ //p->translate(r.x()+2, r.y()+r.height());
+ p->translate(r.x(), r.y()+r.height()-2);
+ p->rotate(270);
+ }
+ else
+ p->translate(r.x()+2, r.y());
+
+
+ // adjust available lines according to maxLines
+ int max = dp->maxLines(f);
+ if ((max > 0) && (lines>max)) lines = max;
+
+ /* loop over name parts to break up string depending on available width.
+ * every char category change is supposed a possible break,
+ * with the exception Uppercase=>Lowercase.
+ * It's good enough for numbers, Symbols...
+ *
+ * If the text is to be written at the bottom, we start with the
+ * end of the string (so everything is reverted)
+ */
+ QString remaining;
+ int origLines = lines;
+ while (lines>0) {
+
+ if (w>width && lines>1) {
+ int lastBreakPos = name.length(), lastWidth = w;
+ int len = name.length();
+ QChar::Category caOld, ca;
+
+ if (!isBottom) {
+ // start with comparing categories of last 2 chars
+ caOld = name[len-1].category();
+ while (len>2) {
+ len--;
+ ca = name[len-1].category();
+ if (ca != caOld) {
+ // "Aa" has no break between...
+ if (ca == QChar::Letter_Uppercase &&
+ caOld == QChar::Letter_Lowercase) {
+ caOld = ca;
+ continue;
+ }
+ caOld = ca;
+ lastBreakPos = len;
+ w = pixW + _fm->width(name, len);
+ lastWidth = w;
+ if (w <= width) break;
+ }
+ }
+ w = lastWidth;
+ remaining = name.mid(lastBreakPos);
+ // remove space on break point
+ if (name[lastBreakPos-1].category() == QChar::Separator_Space)
+ name = name.left(lastBreakPos-1);
+ else
+ name = name.left(lastBreakPos);
+ }
+ else { // bottom
+ int l = len;
+ caOld = name[l-len].category();
+ while (len>2) {
+ len--;
+ ca = name[l-len].category();
+
+ if (ca != caOld) {
+ // "Aa" has no break between...
+ if (caOld == QChar::Letter_Uppercase &&
+ ca == QChar::Letter_Lowercase) {
+ caOld = ca;
+ continue;
+ }
+ caOld = ca;
+ lastBreakPos = len;
+ w = pixW + _fm->width(name.right(len));
+ lastWidth = w;
+ if (w <= width) break;
+ }
+ }
+ w = lastWidth;
+ remaining = name.left(l-lastBreakPos);
+ // remove space on break point
+ if (name[l-lastBreakPos].category() == QChar::Separator_Space)
+ name = name.right(lastBreakPos-1);
+ else
+ name = name.right(lastBreakPos);
+ }
+ }
+ else
+ remaining = QString::null;
+
+ /* truncate and add ... if needed */
+ if (w>width) {
+ int len = name.length();
+ w += dotW;
+ while (len>2 && (w > width)) {
+ len--;
+ w = pixW + _fm->width(name, len) + dotW;
+ }
+ // stop drawing: we cannot draw 2 chars + "..."
+ if (w>width) break;
+
+ name = name.left(len) + "...";
+ }
+
+ int x = 0;
+ if (isCenter)
+ x = (width - w)/2;
+ else if (isRight)
+ x = width - w;
+
+ if (!pixDrawn) {
+ pixY = y+(h-pixH)/2; // default: center vertically
+ if (pixH > h) pixY = isBottom ? y-(pixH-h) : y;
+
+ p->drawPixmap( x, pixY, pix);
+
+ // for distance to next text
+ pixY = isBottom ? (pixY - h - 2) : (pixY + pixH + 2);
+ pixDrawn = true;
+ }
+
+
+ if (0) kdDebug(90100) << " Drawing '" << name << "' at "
+ << x+pixW << "/" << y << endl;
+
+ p->drawText( x+pixW, y,
+ width - pixW, h,
+ Qt::AlignLeft, name);
+ y = isBottom ? (y-h) : (y+h);
+ lines--;
+
+ if (remaining.isEmpty()) break;
+ name = remaining;
+ w = pixW + _fm->width(name);
+ }
+
+ // make sure the pix stays visible
+ if (pixDrawn && (pixY>0)) {
+ if (isBottom && (pixY<y)) y = pixY;
+ if (!isBottom && (pixY>y)) y = pixY;
+ }
+
+ if (origLines > lines) {
+ // if only 1 line written, don't reset _used* vars
+ if (lines - origLines >1) {
+ if (isBottom)
+ _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0;
+ else
+ _usedTopLeft = _usedTopCenter = _usedTopRight = 0;
+ }
+
+ // take back one line
+ y = isBottom ? (y+h) : (y-h);
+ if (used) *used = w;
+ }
+
+ // update free space
+ if (!isBottom) {
+ if (rotate)
+ _rect.setRect(r.x()+y, r.y(), r.width()-y, r.height());
+ else
+ _rect.setRect(r.x(), r.y()+y, r.width(), r.height()-y);
+ }
+ else {
+ if (rotate)
+ _rect.setRect(r.x(), r.y(), y+h, r.height());
+ else
+ _rect.setRect(r.x(), r.y(), r.width(), y+h);
+ }
+
+ p->restore();
+
+ return true;
+}
+
+
+
+
+
+
+//
+// TreeMapItemList
+//
+
+int TreeMapItemList::compareItems ( Item item1, Item item2 )
+{
+ bool ascending;
+ int result;
+
+ TreeMapItem* parent = ((TreeMapItem*)item1)->parent();
+ // shouldn't happen
+ if (!parent) return 0;
+
+ int textNo = parent->sorting(&ascending);
+
+ if (textNo < 0) {
+ double diff = ((TreeMapItem*)item1)->value() -
+ ((TreeMapItem*)item2)->value();
+ result = (diff < -.9) ? -1 : (diff > .9) ? 1 : 0;
+ }
+ else
+ result = (((TreeMapItem*)item1)->text(textNo) <
+ ((TreeMapItem*)item2)->text(textNo)) ? -1 : 1;
+
+ return ascending ? result : -result;
+}
+
+
+TreeMapItem* TreeMapItemList::commonParent()
+{
+ TreeMapItem* parent, *item;
+ parent = first();
+ if (parent)
+ while( (item = next()) != 0)
+ parent = parent->commonParent(item);
+
+ return parent;
+}
+
+
+// TreeMapItem
+
+TreeMapItem::TreeMapItem(TreeMapItem* parent, double value)
+{
+ _value = value;
+ _parent = parent;
+
+ _sum = 0;
+ _children = 0;
+ _widget = 0;
+ _index = -1;
+ _depth = -1; // not set
+ _unused_self = 0;
+ _freeRects = 0;
+
+ if (_parent) {
+ // take sorting from parent
+ _sortTextNo = _parent->sorting(&_sortAscending);
+ _parent->addItem(this);
+ }
+ else {
+ _sortAscending = false;
+ _sortTextNo = -1; // default: no sorting
+ }
+}
+
+
+TreeMapItem::TreeMapItem(TreeMapItem* parent, double value,
+ QString text1, QString text2,
+ QString text3, QString text4)
+{
+ _value = value;
+ _parent = parent;
+
+ // this resizes the text vector only if needed
+ if (!text4.isEmpty()) setText(3, text4);
+ if (!text3.isEmpty()) setText(2, text3);
+ if (!text2.isEmpty()) setText(1, text2);
+ setText(0, text1);
+
+ _sum = 0;
+ _children = 0;
+ _widget = 0;
+ _index = -1;
+ _depth = -1; // not set
+ _unused_self = 0;
+ _freeRects = 0;
+
+ if (_parent) _parent->addItem(this);
+}
+
+TreeMapItem::~TreeMapItem()
+{
+ if (_children) delete _children;
+ if (_freeRects) delete _freeRects;
+
+ // finally, notify widget about deletion
+ if (_widget) _widget->deletingItem(this);
+}
+
+void TreeMapItem::setParent(TreeMapItem* p)
+{
+ _parent = p;
+ if (p) _widget = p->_widget;
+}
+
+bool TreeMapItem::isChildOf(TreeMapItem* item)
+{
+ if (!item) return false;
+
+ TreeMapItem* i = this;
+ while (i) {
+ if (item == i) return true;
+ i = i->_parent;
+ }
+ return false;
+}
+
+TreeMapItem* TreeMapItem::commonParent(TreeMapItem* item)
+{
+ while (item && !isChildOf(item)) {
+ item = item->parent();
+ }
+ return item;
+}
+
+void TreeMapItem::redraw()
+{
+ if (_widget)
+ _widget->redraw(this);
+}
+
+void TreeMapItem::clear()
+{
+ if (_children) {
+ // delete selected items below this item from selection
+ if (_widget) _widget->clearSelection(this);
+
+ delete _children;
+ _children = 0;
+ }
+}
+
+
+// invalidates current children and forces redraw
+// this is only usefull when children are created on demand in items()
+void TreeMapItem::refresh()
+{
+ clear();
+ redraw();
+}
+
+
+QStringList TreeMapItem::path(int textNo) const
+{
+ QStringList list(text(textNo));
+
+ TreeMapItem* i = _parent;
+ while (i) {
+ QString text = i->text(textNo);
+ if (!text.isEmpty())
+ list.prepend(i->text(textNo));
+ i = i->_parent;
+ }
+ return list;
+}
+
+int TreeMapItem::depth() const
+{
+ if (_depth>0) return _depth;
+
+ if (_parent)
+ return _parent->depth() + 1;
+ return 1;
+}
+
+
+bool TreeMapItem::initialized()
+{
+ if (!_children) {
+ _children = new TreeMapItemList;
+ _children->setAutoDelete(true);
+ return false;
+ }
+ return true;
+}
+
+void TreeMapItem::addItem(TreeMapItem* i)
+{
+ if (!i) return;
+
+ if (!_children) {
+ _children = new TreeMapItemList;
+ _children->setAutoDelete(true);
+ }
+ i->setParent(this);
+
+ if (sorting(0) == -1)
+ _children->append(i); // preserve insertion order
+ else
+ _children->inSort(i);
+}
+
+
+// default implementations of virtual functions
+
+double TreeMapItem::value() const
+{
+ return _value;
+}
+
+double TreeMapItem::sum() const
+{
+ return _sum;
+}
+
+DrawParams::Position TreeMapItem::position(int f) const
+{
+ Position p = StoredDrawParams::position(f);
+ if (_widget && (p == Default))
+ p = _widget->fieldPosition(f);
+
+ return p;
+}
+
+// use widget font
+const QFont& TreeMapItem::font() const
+{
+ return _widget->currentFont();
+}
+
+
+bool TreeMapItem::isMarked(int) const
+{
+ return false;
+}
+
+
+int TreeMapItem::borderWidth() const
+{
+ if (_widget)
+ return _widget->borderWidth();
+
+ return 2;
+}
+
+int TreeMapItem::sorting(bool* ascending) const
+{
+ if (ascending) *ascending = _sortAscending;
+ return _sortTextNo;
+}
+
+// do *not* set sorting recursively
+void TreeMapItem::setSorting(int textNo, bool ascending)
+{
+ if (_sortTextNo == textNo) {
+ if(_sortAscending == ascending) return;
+ if (textNo == -1) {
+ // when no sorting is done, order change doesn't do anything
+ _sortAscending = ascending;
+ return;
+ }
+ }
+ _sortAscending = ascending;
+ _sortTextNo = textNo;
+
+ if (_children && _sortTextNo != -1) _children->sort();
+}
+
+void TreeMapItem::resort(bool recursive)
+{
+ if (!_children) return;
+
+ if (_sortTextNo != -1) _children->sort();
+
+ if (recursive)
+ for (TreeMapItem* i=_children->first(); i; i=_children->next())
+ i->resort(recursive);
+}
+
+
+TreeMapItem::SplitMode TreeMapItem::splitMode() const
+{
+ if (_widget)
+ return _widget->splitMode();
+
+ return Best;
+}
+
+int TreeMapItem::rtti() const
+{
+ return 0;
+}
+
+TreeMapItemList* TreeMapItem::children()
+{
+ if (!_children) {
+ _children = new TreeMapItemList;
+ _children->setAutoDelete(true);
+ }
+ return _children;
+}
+
+void TreeMapItem::clearItemRect()
+{
+ _rect = QRect();
+ clearFreeRects();
+}
+
+void TreeMapItem::clearFreeRects()
+{
+ if (_freeRects) _freeRects->clear();
+}
+
+void TreeMapItem::addFreeRect(const QRect& r)
+{
+ // don't add invalid rects
+ if ((r.width() < 1) || (r.height() < 1)) return;
+
+ if (!_freeRects) {
+ _freeRects = new QPtrList<QRect>;
+ _freeRects->setAutoDelete(true);
+ }
+
+ if (0) kdDebug(90100) << "addFree(" << path(0).join("/") << ", "
+ << r.x() << "/" << r.y() << "-"
+ << r.width() << "x" << r.height() << ")" << endl;
+
+ QRect* last = _freeRects->last();
+ if (!last) {
+ _freeRects->append(new QRect(r));
+ return;
+ }
+
+ // join rect with last rect if possible
+ // this saves memory and doesn't make the tooltip flicker
+
+ bool replaced = false;
+ if ((last->left() == r.left()) && (last->width() == r.width())) {
+ if ((last->bottom()+1 == r.top()) || (r.bottom()+1 == last->top())) {
+ *last |= r;
+ replaced = true;
+ }
+ }
+ else if ((last->top() == r.top()) && (last->height() == r.height())) {
+ if ((last->right()+1 == r.left()) || (r.right()+1 == last->left())) {
+ *last |= r;
+ replaced = true;
+ }
+ }
+
+ if (!replaced) {
+ _freeRects->append(new QRect(r));
+ return;
+ }
+
+ if (0) kdDebug(90100) << " united with last to ("
+ << last->x() << "/" << last->y() << "-"
+ << last->width() << "x" << last->height() << ")" << endl;
+}
+
+
+// Tooltips for TreeMapWidget
+
+class TreeMapTip: public QToolTip
+{
+public:
+ TreeMapTip( QWidget* p ):QToolTip(p) {}
+
+protected:
+ void maybeTip( const QPoint & );
+};
+
+void TreeMapTip::maybeTip( const QPoint& pos )
+{
+ if ( !parentWidget()->inherits( "TreeMapWidget" ) )
+ return;
+
+ TreeMapWidget* p = (TreeMapWidget*)parentWidget();
+ TreeMapItem* i;
+ i = p->item(pos.x(), pos.y());
+ QPtrList<QRect>* rList = i ? i->freeRects() : 0;
+ if (rList) {
+ QRect* r;
+ for(r=rList->first();r;r=rList->next())
+ if (r->contains(pos))
+ tip(*r, p->tipString(i));
+ }
+}
+
+
+
+// TreeMapWidget
+
+TreeMapWidget::TreeMapWidget(TreeMapItem* base,
+ QWidget* parent, const char* name)
+ : QWidget(parent, name)
+{
+ _base = base;
+ _base->setWidget(this);
+
+ _font = font();
+ _fontHeight = fontMetrics().height();
+
+
+ // default behaviour
+ _selectionMode = Single;
+ _splitMode = TreeMapItem::AlwaysBest;
+ _visibleWidth = 2;
+ _reuseSpace = false;
+ _skipIncorrectBorder = false;
+ _drawSeparators = false;
+ _allowRotation = true;
+ _borderWidth = 2;
+ _shading = true; // beautiful is default!
+ _maxSelectDepth = -1; // unlimited
+ _maxDrawingDepth = -1; // unlimited
+ _minimalArea = -1; // unlimited
+ _markNo = 0;
+
+ // _stopAtText will be unset on resizing (per default)
+ // _textVisible will be true on resizing (per default)
+ // _forceText will be false on resizing (per default)
+
+ // start state: _selection is an empty list
+ _current = 0;
+ _oldCurrent = 0;
+ _pressed = 0;
+ _lastOver = 0;
+ _needsRefresh = _base;
+
+ setBackgroundMode(Qt::NoBackground);
+ setFocusPolicy(QWidget::StrongFocus);
+ _tip = new TreeMapTip(this);
+}
+
+TreeMapWidget::~TreeMapWidget()
+{
+}
+
+const QFont& TreeMapWidget::currentFont() const
+{
+ return _font;
+}
+
+void TreeMapWidget::setSplitMode(TreeMapItem::SplitMode m)
+{
+ if (_splitMode == m) return;
+
+ _splitMode = m;
+ redraw();
+}
+
+TreeMapItem::SplitMode TreeMapWidget::splitMode() const
+{
+ return _splitMode;
+}
+
+bool TreeMapWidget::setSplitMode(QString mode)
+{
+ if (mode == "Bisection") setSplitMode(TreeMapItem::Bisection);
+ else if (mode == "Columns") setSplitMode(TreeMapItem::Columns);
+ else if (mode == "Rows") setSplitMode(TreeMapItem::Rows);
+ else if (mode == "AlwaysBest") setSplitMode(TreeMapItem::AlwaysBest);
+ else if (mode == "Best") setSplitMode(TreeMapItem::Best);
+ else if (mode == "HAlternate") setSplitMode(TreeMapItem::HAlternate);
+ else if (mode == "VAlternate") setSplitMode(TreeMapItem::VAlternate);
+ else if (mode == "Horizontal") setSplitMode(TreeMapItem::Horizontal);
+ else if (mode == "Vertical") setSplitMode(TreeMapItem::Vertical);
+ else return false;
+
+ return true;
+}
+
+QString TreeMapWidget::splitModeString() const
+{
+ QString mode;
+ switch(splitMode()) {
+ case TreeMapItem::Bisection: mode = "Bisection"; break;
+ case TreeMapItem::Columns: mode = "Columns"; break;
+ case TreeMapItem::Rows: mode = "Rows"; break;
+ case TreeMapItem::AlwaysBest: mode = "AlwaysBest"; break;
+ case TreeMapItem::Best: mode = "Best"; break;
+ case TreeMapItem::HAlternate: mode = "HAlternate"; break;
+ case TreeMapItem::VAlternate: mode = "VAlternate"; break;
+ case TreeMapItem::Horizontal: mode = "Horizontal"; break;
+ case TreeMapItem::Vertical: mode = "Vertical"; break;
+ default: mode = "Unknown"; break;
+ }
+ return mode;
+}
+
+
+void TreeMapWidget::setShadingEnabled(bool s)
+{
+ if (_shading == s) return;
+
+ _shading = s;
+ redraw();
+}
+
+void TreeMapWidget::setAllowRotation(bool enable)
+{
+ if (_allowRotation == enable) return;
+
+ _allowRotation = enable;
+ redraw();
+}
+
+void TreeMapWidget::setVisibleWidth(int width, bool reuseSpace)
+{
+ if (_visibleWidth == width && _reuseSpace == reuseSpace) return;
+
+ _visibleWidth = width;
+ _reuseSpace = reuseSpace;
+ redraw();
+}
+
+void TreeMapWidget::setSkipIncorrectBorder(bool enable)
+{
+ if (_skipIncorrectBorder == enable) return;
+
+ _skipIncorrectBorder = enable;
+ redraw();
+}
+
+void TreeMapWidget::setBorderWidth(int w)
+{
+ if (_borderWidth == w) return;
+
+ _borderWidth = w;
+ redraw();
+}
+
+void TreeMapWidget::setMaxDrawingDepth(int d)
+{
+ if (_maxDrawingDepth == d) return;
+
+ _maxDrawingDepth = d;
+ redraw();
+}
+
+QString TreeMapWidget::defaultFieldType(int f) const
+{
+ return i18n("Text %1").arg(f+1);
+}
+
+QString TreeMapWidget::defaultFieldStop(int) const
+{
+ return QString();
+}
+
+bool TreeMapWidget::defaultFieldVisible(int f) const
+{
+ return (f<2);
+}
+
+bool TreeMapWidget::defaultFieldForced(int) const
+{
+ return false;
+}
+
+DrawParams::Position TreeMapWidget::defaultFieldPosition(int f) const
+{
+ switch(f%4) {
+ case 0: return DrawParams::TopLeft;
+ case 1: return DrawParams::TopRight;
+ case 2: return DrawParams::BottomRight;
+ case 3: return DrawParams::BottomLeft;
+ default:break;
+ }
+ return DrawParams::TopLeft;
+}
+
+bool TreeMapWidget::resizeAttr(int size)
+{
+ if (size<0 || size>=MAX_FIELD) return false;
+
+ if (size>(int)_attr.size()) {
+ struct FieldAttr a;
+ int oldSize = _attr.size();
+ _attr.resize(size, a);
+ while (oldSize<size) {
+ _attr[oldSize].type = defaultFieldType(oldSize);
+ _attr[oldSize].stop = defaultFieldStop(oldSize);
+ _attr[oldSize].visible = defaultFieldVisible(oldSize);
+ _attr[oldSize].forced = defaultFieldForced(oldSize);
+ _attr[oldSize].pos = defaultFieldPosition(oldSize);
+ oldSize++;
+ }
+ }
+ return true;
+}
+
+void TreeMapWidget::setFieldType(int f, QString type)
+{
+ if (((int)_attr.size() < f+1) &&
+ (type == defaultFieldType(f))) return;
+ if (resizeAttr(f+1)) _attr[f].type = type;
+
+ // no need to redraw: the type string is not visible in the TreeMap
+}
+
+QString TreeMapWidget::fieldType(int f) const
+{
+ if (f<0 || (int)_attr.size()<f+1) return defaultFieldType(f);
+ return _attr[f].type;
+}
+
+void TreeMapWidget::setFieldStop(int f, QString stop)
+{
+ if (((int)_attr.size() < f+1) &&
+ (stop == defaultFieldStop(f))) return;
+ if (resizeAttr(f+1)) {
+ _attr[f].stop = stop;
+ redraw();
+ }
+}
+
+QString TreeMapWidget::fieldStop(int f) const
+{
+ if (f<0 || (int)_attr.size()<f+1) return defaultFieldStop(f);
+ return _attr[f].stop;
+}
+
+void TreeMapWidget::setFieldVisible(int f, bool enable)
+{
+ if (((int)_attr.size() < f+1) &&
+ (enable == defaultFieldVisible(f))) return;
+
+ if (resizeAttr(f+1)) {
+ _attr[f].visible = enable;
+ redraw();
+ }
+}
+
+bool TreeMapWidget::fieldVisible(int f) const
+{
+ if (f<0 || (int)_attr.size()<f+1)
+ return defaultFieldVisible(f);
+
+ return _attr[f].visible;
+}
+
+void TreeMapWidget::setFieldForced(int f, bool enable)
+{
+ if (((int)_attr.size() < f+1) &&
+ (enable == defaultFieldForced(f))) return;
+
+ if (resizeAttr(f+1)) {
+ _attr[f].forced = enable;
+ if (_attr[f].visible) redraw();
+ }
+}
+
+bool TreeMapWidget::fieldForced(int f) const
+{
+ if (f<0 || (int)_attr.size()<f+1)
+ return defaultFieldForced(f);
+
+ return _attr[f].forced;
+}
+
+void TreeMapWidget::setFieldPosition(int f, TreeMapItem::Position pos)
+{
+ if (((int)_attr.size() < f+1) &&
+ (pos == defaultFieldPosition(f))) return;
+
+ if (resizeAttr(f+1)) {
+ _attr[f].pos = pos;
+ if (_attr[f].visible) redraw();
+ }
+}
+
+DrawParams::Position TreeMapWidget::fieldPosition(int f) const
+{
+ if (f<0 || (int)_attr.size()<f+1)
+ return defaultFieldPosition(f);
+
+ return _attr[f].pos;
+}
+
+void TreeMapWidget::setFieldPosition(int f, QString pos)
+{
+ if (pos == "TopLeft")
+ setFieldPosition(f, DrawParams::TopLeft);
+ else if (pos == "TopCenter")
+ setFieldPosition(f, DrawParams::TopCenter);
+ else if (pos == "TopRight")
+ setFieldPosition(f, DrawParams::TopRight);
+ else if (pos == "BottomLeft")
+ setFieldPosition(f, DrawParams::BottomLeft);
+ else if (pos == "BottomCenter")
+ setFieldPosition(f, DrawParams::BottomCenter);
+ else if (pos == "BottomRight")
+ setFieldPosition(f, DrawParams::BottomRight);
+ else if (pos == "Default")
+ setFieldPosition(f, DrawParams::Default);
+}
+
+QString TreeMapWidget::fieldPositionString(int f) const
+{
+ TreeMapItem::Position pos = fieldPosition(f);
+ if (pos == DrawParams::TopLeft) return QString("TopLeft");
+ if (pos == DrawParams::TopCenter) return QString("TopCenter");
+ if (pos == DrawParams::TopRight) return QString("TopRight");
+ if (pos == DrawParams::BottomLeft) return QString("BottomLeft");
+ if (pos == DrawParams::BottomCenter) return QString("BottomCenter");
+ if (pos == DrawParams::BottomRight) return QString("BottomRight");
+ if (pos == DrawParams::Default) return QString("Default");
+ return QString("unknown");
+}
+
+void TreeMapWidget::setMinimalArea(int area)
+{
+ if (_minimalArea == area) return;
+
+ _minimalArea = area;
+ redraw();
+}
+
+
+void TreeMapWidget::deletingItem(TreeMapItem* i)
+{
+ // remove any references to the item to be deleted
+ while(_selection.findRef(i) > -1)
+ _selection.remove();
+
+ while(_tmpSelection.findRef(i) > -1)
+ _tmpSelection.remove();
+
+ if (_current == i) _current = 0;
+ if (_oldCurrent == i) _oldCurrent = 0;
+ if (_pressed == i) _pressed = 0;
+ if (_lastOver == i) _lastOver = 0;
+
+ // don't redraw a deleted item
+ if (_needsRefresh == i) {
+ // we can savely redraw the parent, as deleting order is
+ // from child to parent; i.e. i->parent() is existing.
+ _needsRefresh = i->parent();
+ }
+}
+
+
+QString TreeMapWidget::tipString(TreeMapItem* i) const
+{
+ QString tip, itemTip;
+
+ while (i) {
+ if (!i->text(0).isEmpty()) {
+ itemTip = i->text(0);
+ if (!i->text(1).isEmpty())
+ itemTip += " (" + i->text(1) + ")";
+
+ if (!tip.isEmpty())
+ tip += "\n";
+
+ tip += itemTip;
+ }
+ i = i->parent();
+ }
+ return tip;
+}
+
+TreeMapItem* TreeMapWidget::item(int x, int y) const
+{
+ TreeMapItem* p = _base;
+ TreeMapItem* i;
+
+ if (!rect().contains(x, y)) return 0;
+ if (DEBUG_DRAWING) kdDebug(90100) << "item(" << x << "," << y << "):" << endl;
+
+ while (1) {
+ TreeMapItemList* list = p->children();
+ if (!list)
+ i = 0;
+ else {
+ int idx=0;
+ for (i=list->first();i;i=list->next(),idx++) {
+
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << " Checking " << i->path(0).join("/") << " ("
+ << i->itemRect().x() << "/" << i->itemRect().y()
+ << "-" << i->itemRect().width()
+ << "x" << i->itemRect().height() << ")" << endl;
+
+ if (i->itemRect().contains(x, y)) {
+
+ if (DEBUG_DRAWING) kdDebug(90100) << " .. Got. Index " << idx << endl;
+
+ p->setIndex(idx);
+ break;
+ }
+ }
+ }
+
+ if (!i) {
+ static TreeMapItem* last = 0;
+ if (p != last) {
+ last = p;
+
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << "item(" << x << "," << y << "): Got "
+ << p->path(0).join("/") << " (Size "
+ << p->itemRect().width() << "x" << p->itemRect().height()
+ << ", Val " << p->value() << ")" << endl;
+ }
+
+ return p;
+ }
+ p = i;
+ }
+ return 0;
+}
+
+TreeMapItem* TreeMapWidget::possibleSelection(TreeMapItem* i) const
+{
+ if (i) {
+ if (_maxSelectDepth>=0) {
+ int depth = i->depth();
+ while(i && depth > _maxSelectDepth) {
+ i = i->parent();
+ depth--;
+ }
+ }
+ }
+ return i;
+}
+
+TreeMapItem* TreeMapWidget::visibleItem(TreeMapItem* i) const
+{
+ if (i) {
+ /* Must have a visible area */
+ while(i && ((i->itemRect().width() <1) ||
+ (i->itemRect().height() <1))) {
+ TreeMapItem* p = i->parent();
+ if (!p) break;
+ int idx = p->children()->findRef(i);
+ idx--;
+ if (idx<0)
+ i = p;
+ else
+ i = p->children()->at(idx);
+ }
+ }
+ return i;
+}
+
+void TreeMapWidget::setSelected(TreeMapItem* item, bool selected)
+{
+ item = possibleSelection(item);
+ setCurrent(item);
+
+ TreeMapItem* changed = setTmpSelected(item, selected);
+ if (!changed) return;
+
+ _selection = _tmpSelection;
+ if (_selectionMode == Single)
+ emit selectionChanged(item);
+ emit selectionChanged();
+ redraw(changed);
+
+ if (0) kdDebug(90100) << (selected ? "S":"Des") << "elected Item "
+ << (item ? item->path(0).join("") : QString("(null)"))
+ << " (depth " << (item ? item->depth() : -1)
+ << ")" << endl;
+}
+
+void TreeMapWidget::setMarked(int markNo, bool redrawWidget)
+{
+ // if there's no marking, return
+ if ((_markNo == 0) && (markNo == 0)) return;
+
+ _markNo = markNo;
+ if (!clearSelection() && redrawWidget) redraw();
+}
+
+/* Returns all items which appear only in one of the given lists */
+TreeMapItemList TreeMapWidget::diff(TreeMapItemList& l1,
+ TreeMapItemList& l2)
+{
+ TreeMapItemList l;
+ TreeMapItemListIterator it1(l1), it2(l2);
+
+ TreeMapItem* item;
+ while ( (item = it1.current()) != 0 ) {
+ ++it1;
+ if (l2.containsRef(item) > 0) continue;
+ l.append(item);
+ }
+ while ( (item = it2.current()) != 0 ) {
+ ++it2;
+ if (l1.containsRef(item) > 0) continue;
+ l.append(item);
+ }
+
+ return l;
+}
+
+/* Only modifies _tmpSelection.
+ * Returns 0 when no change happened, otherwise the TreeMapItem that has
+ * to be redrawn for all changes.
+ */
+TreeMapItem* TreeMapWidget::setTmpSelected(TreeMapItem* item, bool selected)
+{
+ if (!item) return 0;
+ if (_selectionMode == NoSelection) return 0;
+
+ TreeMapItemList old = _tmpSelection;
+
+ if (_selectionMode == Single) {
+ _tmpSelection.clear();
+ if (selected) _tmpSelection.append(item);
+ }
+ else {
+ if (selected) {
+ TreeMapItem* i=_tmpSelection.first();
+ while (i) {
+ if (i->isChildOf(item) || item->isChildOf(i)) {
+ _tmpSelection.remove();
+ i = _tmpSelection.current();
+ }
+ else
+ i = _tmpSelection.next();
+ }
+ _tmpSelection.append(item);
+ }
+ else
+ _tmpSelection.removeRef(item);
+ }
+
+ return diff(old, _tmpSelection).commonParent();
+}
+
+
+bool TreeMapWidget::clearSelection(TreeMapItem* parent)
+{
+ TreeMapItemList old = _selection;
+
+ TreeMapItem* i=_selection.first();
+ while (i) {
+ if (i->isChildOf(parent)) {
+ _selection.remove();
+ i = _selection.current();
+ }
+ else
+ i = _selection.next();
+ }
+
+ TreeMapItem* changed = diff(old, _selection).commonParent();
+ if (changed) {
+ changed->redraw();
+ emit selectionChanged();
+ }
+ return (changed != 0);
+}
+
+bool TreeMapWidget::isSelected(TreeMapItem* i) const
+{
+ return _selection.containsRef(i)>0;
+}
+
+bool TreeMapWidget::isTmpSelected(TreeMapItem* i)
+{
+ return _tmpSelection.containsRef(i)>0;
+}
+
+
+void TreeMapWidget::setCurrent(TreeMapItem* i, bool kbd)
+{
+ TreeMapItem* old = _current;
+ _current = i;
+
+ if (_markNo >0) {
+ // remove mark
+ _markNo = 0;
+
+ if (1) kdDebug(90100) << "setCurrent(" << i->path(0).join("/")
+ << ") - mark removed" << endl;
+
+ // always complete redraw needed to remove mark
+ redraw();
+
+ if (old == _current) return;
+ }
+ else {
+ if (old == _current) return;
+
+ if (old) old->redraw();
+ if (i) i->redraw();
+ }
+
+ //kdDebug(90100) << "Current Item " << (i ? i->path().ascii() : "(null)") << endl;
+
+ emit currentChanged(i, kbd);
+}
+
+void TreeMapWidget::setRangeSelection(TreeMapItem* i1,
+ TreeMapItem* i2, bool selected)
+{
+ i1 = possibleSelection(i1);
+ i2 = possibleSelection(i2);
+ setCurrent(i2);
+
+ TreeMapItem* changed = setTmpRangeSelection(i1, i2, selected);
+ if (!changed) return;
+
+ _selection = _tmpSelection;
+ if (_selectionMode == Single)
+ emit selectionChanged(i2);
+ emit selectionChanged();
+ redraw(changed);
+}
+
+TreeMapItem* TreeMapWidget::setTmpRangeSelection(TreeMapItem* i1,
+ TreeMapItem* i2,
+ bool selected)
+{
+ if ((i1 == 0) && (i2 == 0)) return 0;
+ if ((i1 == 0) || i1->isChildOf(i2)) return setTmpSelected(i2, selected);
+ if ((i2 == 0) || i2->isChildOf(i1)) return setTmpSelected(i1, selected);
+
+ TreeMapItem* changed = setTmpSelected(i1, selected);
+ TreeMapItem* changed2 = setTmpSelected(i2, selected);
+ if (changed2) changed = changed2->commonParent(changed);
+
+ TreeMapItem* commonParent = i1;
+ while (commonParent && !i2->isChildOf(commonParent)) {
+ i1 = commonParent;
+ commonParent = commonParent->parent();
+ }
+ if (!commonParent) return changed;
+ while (i2 && i2->parent() != commonParent)
+ i2 = i2->parent();
+ if (!i2) return changed;
+
+ TreeMapItemList* list = commonParent->children();
+ if (!list) return changed;
+
+ TreeMapItem* i = list->first();
+ bool between = false;
+ while (i) {
+ if (between) {
+ if (i==i1 || i==i2) break;
+ changed2 = setTmpSelected(i, selected);
+ if (changed2) changed = changed2->commonParent(changed);
+ }
+ else if (i==i1 || i==i2)
+ between = true;
+ i = list->next();
+ }
+
+ return changed;
+}
+
+void TreeMapWidget::contextMenuEvent( QContextMenuEvent* e )
+{
+ //kdDebug(90100) << "TreeMapWidget::contextMenuEvent" << endl;
+
+ if ( receivers( SIGNAL(contextMenuRequested(TreeMapItem*, const QPoint &)) ) )
+ e->accept();
+
+ if ( e->reason() == QContextMenuEvent::Keyboard ) {
+ QRect r = (_current) ? _current->itemRect() : _base->itemRect();
+ QPoint p = QPoint(r.left() + r.width()/2, r.top() + r.height()/2);
+ emit contextMenuRequested(_current, p);
+ }
+ else {
+ TreeMapItem* i = item(e->x(), e->y());
+ emit contextMenuRequested(i, e->pos());
+ }
+}
+
+
+void TreeMapWidget::mousePressEvent( QMouseEvent* e )
+{
+ //kdDebug(90100) << "TreeMapWidget::mousePressEvent" << endl;
+
+ _oldCurrent = _current;
+
+ TreeMapItem* i = item(e->x(), e->y());
+
+ _pressed = i;
+
+ _inShiftDrag = e->state() & ShiftButton;
+ _inControlDrag = e->state() & ControlButton;
+ _lastOver = _pressed;
+
+ TreeMapItem* changed = 0;
+ TreeMapItem* item = possibleSelection(_pressed);
+
+ switch(_selectionMode) {
+ case Single:
+ changed = setTmpSelected(item, true);
+ break;
+ case Multi:
+ changed = setTmpSelected(item, !isTmpSelected(item));
+ break;
+ case Extended:
+ if (_inControlDrag)
+ changed = setTmpSelected(item, !isTmpSelected(item));
+ else if (_inShiftDrag) {
+ TreeMapItem* sCurrent = possibleSelection(_current);
+ changed = setTmpRangeSelection(sCurrent, item,
+ !isTmpSelected(item));
+ }
+ else {
+ _selectionMode = Single;
+ changed = setTmpSelected(item, true);
+ _selectionMode = Extended;
+ }
+ break;
+ default:
+ break;
+ }
+
+ // item under mouse always selected on right button press
+ if (e->button() == RightButton) {
+ TreeMapItem* changed2 = setTmpSelected(item, true);
+ if (changed2) changed = changed2->commonParent(changed);
+ }
+
+ setCurrent(_pressed);
+
+ if (changed)
+ redraw(changed);
+
+ if (e->button() == RightButton) {
+
+ // emit selection change
+ if (! (_tmpSelection == _selection)) {
+ _selection = _tmpSelection;
+ if (_selectionMode == Single)
+ emit selectionChanged(_lastOver);
+ emit selectionChanged();
+ }
+ _pressed = 0;
+ _lastOver = 0;
+ emit rightButtonPressed(i, e->pos());
+ }
+}
+
+void TreeMapWidget::mouseMoveEvent( QMouseEvent* e )
+{
+ //kdDebug(90100) << "TreeMapWidget::mouseMoveEvent" << endl;
+
+ if (!_pressed) return;
+ TreeMapItem* over = item(e->x(), e->y());
+ if (_lastOver == over) return;
+
+ setCurrent(over);
+ if (over == 0) {
+ _lastOver = 0;
+ return;
+ }
+
+ TreeMapItem* changed = 0;
+ TreeMapItem* item = possibleSelection(over);
+
+ switch(_selectionMode) {
+ case Single:
+ changed = setTmpSelected(item, true);
+ break;
+ case Multi:
+ changed = setTmpSelected(item, !isTmpSelected(item));
+ break;
+ case Extended:
+ if (_inControlDrag)
+ changed = setTmpSelected(item, !isTmpSelected(item));
+ else {
+ TreeMapItem* sLast = possibleSelection(_lastOver);
+ changed = setTmpRangeSelection(sLast, item, true);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ _lastOver = over;
+
+ if (changed)
+ redraw(changed);
+}
+
+void TreeMapWidget::mouseReleaseEvent( QMouseEvent* )
+{
+ //kdDebug(90100) << "TreeMapWidget::mouseReleaseEvent" << endl;
+
+ if (!_pressed) return;
+
+ if (!_lastOver) {
+ // take back
+ setCurrent(_oldCurrent);
+ TreeMapItem* changed = diff(_tmpSelection, _selection).commonParent();
+ _tmpSelection = _selection;
+ if (changed)
+ redraw(changed);
+ }
+ else {
+ if (! (_tmpSelection == _selection)) {
+ _selection = _tmpSelection;
+ if (_selectionMode == Single)
+ emit selectionChanged(_lastOver);
+ emit selectionChanged();
+ }
+ if (!_inControlDrag && !_inShiftDrag && (_pressed == _lastOver))
+ emit clicked(_lastOver);
+ }
+
+ _pressed = 0;
+ _lastOver = 0;
+}
+
+
+void TreeMapWidget::mouseDoubleClickEvent( QMouseEvent* e )
+{
+ TreeMapItem* over = item(e->x(), e->y());
+
+ emit doubleClicked(over);
+}
+
+
+/* returns -1 if nothing visible found */
+int nextVisible(TreeMapItem* i)
+{
+ TreeMapItem* p = i->parent();
+ if (!p || p->itemRect().isEmpty()) return -1;
+
+ int idx = p->children()->findRef(i);
+ if (idx<0) return -1;
+
+ while (idx < (int)p->children()->count()-1) {
+ idx++;
+ QRect r = p->children()->at(idx)->itemRect();
+ if (r.width()>1 && r.height()>1)
+ return idx;
+ }
+ return -1;
+}
+
+/* returns -1 if nothing visible found */
+int prevVisible(TreeMapItem* i)
+{
+ TreeMapItem* p = i->parent();
+ if (!p || p->itemRect().isEmpty()) return -1;
+
+ int idx = p->children()->findRef(i);
+ if (idx<0) return -1;
+
+ while (idx > 0) {
+ idx--;
+ QRect r = p->children()->at(idx)->itemRect();
+ if (r.width()>1 && r.height()>1)
+ return idx;
+ }
+ return -1;
+}
+
+
+
+
+void TreeMapWidget::keyPressEvent( QKeyEvent* e )
+{
+ if (e->key() == Key_Escape && _pressed) {
+
+ // take back
+ if (_oldCurrent != _lastOver)
+ setCurrent(_oldCurrent);
+ if (! (_tmpSelection == _selection)) {
+ TreeMapItem* changed = diff(_tmpSelection, _selection).commonParent();
+ _tmpSelection = _selection;
+ if (changed)
+ redraw(changed);
+ }
+ _pressed = 0;
+ _lastOver = 0;
+ }
+
+ if ((e->key() == Key_Space) ||
+ (e->key() == Key_Return)) {
+
+ switch(_selectionMode) {
+ case NoSelection:
+ break;
+ case Single:
+ setSelected(_current, true);
+ break;
+ case Multi:
+ setSelected(_current, !isSelected(_current));
+ break;
+ case Extended:
+ if ((e->state() & ControlButton) || (e->state() & ShiftButton))
+ setSelected(_current, !isSelected(_current));
+ else {
+ _selectionMode = Single;
+ setSelected(_current, true);
+ _selectionMode = Extended;
+ }
+ }
+
+ if (_current && (e->key() == Key_Return))
+ emit returnPressed(_current);
+
+ return;
+ }
+
+ if (!_current) {
+ if (e->key() == Key_Down) {
+ setCurrent(_base, true);
+ }
+ return;
+ }
+
+ TreeMapItem* old = _current, *newItem;
+ TreeMapItem* p = _current->parent();
+
+ bool goBack;
+ if (_current->sorting(&goBack) == -1) {
+ // noSorting
+ goBack = false;
+ }
+
+
+ if ((e->key() == Key_Backspace) ||
+ (e->key() == Key_Up)) {
+ newItem = visibleItem(p);
+ setCurrent(newItem, true);
+ }
+ else if (e->key() == Key_Left) {
+ int newIdx = goBack ? nextVisible(_current) : prevVisible(_current);
+ if (p && newIdx>=0) {
+ p->setIndex(newIdx);
+ setCurrent(p->children()->at(newIdx), true);
+ }
+ }
+ else if (e->key() == Key_Right) {
+ int newIdx = goBack ? prevVisible(_current) : nextVisible(_current);
+ if (p && newIdx>=0) {
+ p->setIndex(newIdx);
+ setCurrent(p->children()->at(newIdx), true);
+ }
+ }
+ else if (e->key() == Key_Down) {
+ if (_current->children() && _current->children()->count()>0) {
+ int newIdx = _current->index();
+ if (newIdx<0)
+ newIdx = goBack ? (_current->children()->count()-1) : 0;
+ if (newIdx>=(int)_current->children()->count())
+ newIdx = _current->children()->count()-1;
+ newItem = visibleItem(_current->children()->at(newIdx));
+ setCurrent(newItem, true);
+ }
+ }
+
+ if (old == _current) return;
+ if (! (e->state() & ControlButton)) return;
+ if (! (e->state() & ShiftButton)) return;
+
+ switch(_selectionMode) {
+ case NoSelection:
+ break;
+ case Single:
+ setSelected(_current, true);
+ break;
+ case Multi:
+ setSelected(_current, !isSelected(_current));
+ break;
+ case Extended:
+ if (e->state() & ControlButton)
+ setSelected(_current, !isSelected(_current));
+ else
+ setSelected(_current, isSelected(old));
+ }
+}
+
+void TreeMapWidget::fontChange( const QFont& )
+{
+ redraw();
+}
+
+
+void TreeMapWidget::resizeEvent( QResizeEvent * )
+{
+ // this automatically redraws (as size is changed)
+ drawTreeMap();
+}
+
+void TreeMapWidget::paintEvent( QPaintEvent * )
+{
+ drawTreeMap();
+}
+
+void TreeMapWidget::showEvent( QShowEvent * )
+{
+ // refresh only if needed
+ drawTreeMap();
+}
+
+// Updates screen from shadow buffer,
+// but redraws before if needed
+void TreeMapWidget::drawTreeMap()
+{
+ // no need to draw if hidden
+ if (!isVisible()) return;
+
+ if (_pixmap.size() != size())
+ _needsRefresh = _base;
+
+ if (_needsRefresh) {
+
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << "Redrawing " << _needsRefresh->path(0).join("/") << endl;
+
+ if (_needsRefresh == _base) {
+ // redraw whole widget
+ _pixmap = QPixmap(size());
+ _pixmap.fill(backgroundColor());
+ }
+ QPainter p(&_pixmap);
+ if (_needsRefresh == _base) {
+ p.setPen(black);
+ p.drawRect(QRect(2, 2, QWidget::width()-4, QWidget::height()-4));
+ _base->setItemRect(QRect(3, 3, QWidget::width()-6, QWidget::height()-6));
+ }
+ else {
+ // only subitem
+ if (!_needsRefresh->itemRect().isValid()) return;
+ }
+
+ // reset cached font object; it could have been changed
+ _font = font();
+ _fontHeight = fontMetrics().height();
+
+ drawItems(&p, _needsRefresh);
+ _needsRefresh = 0;
+ }
+
+ bitBlt( this, 0, 0, &_pixmap, 0, 0,
+ QWidget::width(), QWidget::height(), CopyROP, true);
+
+ if (hasFocus()) {
+ QPainter p(this);
+ style().drawPrimitive( QStyle::PE_FocusRect, &p,
+ QRect(0, 0, QWidget::width(), QWidget::height()),
+ colorGroup() );
+ }
+}
+
+
+
+void TreeMapWidget::redraw(TreeMapItem* i)
+{
+ if (!i) return;
+
+ if (!_needsRefresh)
+ _needsRefresh = i;
+ else {
+ if (!i->isChildOf(_needsRefresh))
+ _needsRefresh = _needsRefresh->commonParent(i);
+ }
+
+ if (isVisible()) {
+ // delayed drawing if we have multiple redraw requests
+ update();
+ }
+}
+
+void TreeMapWidget::drawItem(QPainter* p,
+ TreeMapItem* item)
+{
+ bool isSelected = false;
+ TreeMapItem* i;
+
+ if (_markNo>0) {
+ for(i = item;i;i=i->parent())
+ if (i->isMarked(_markNo)) break;
+
+ isSelected = (i!=0);
+ }
+ else {
+ for (i=_tmpSelection.first();i;i=_tmpSelection.next())
+ if (item->isChildOf(i)) break;
+
+ isSelected = (i!=0);
+ }
+
+ bool isCurrent = _current && item->isChildOf(_current);
+
+ RectDrawing d(item->itemRect());
+ item->setSelected(isSelected);
+ item->setCurrent(isCurrent);
+ item->setShaded(_shading);
+ d.drawBack(p, item);
+}
+
+
+bool TreeMapWidget::horizontal(TreeMapItem* i, const QRect& r)
+{
+ switch(i->splitMode()) {
+ case TreeMapItem::HAlternate:
+ return (i->depth()%2)==1;
+ case TreeMapItem::VAlternate:
+ return (i->depth()%2)==0;
+ case TreeMapItem::Horizontal:
+ return true;
+ case TreeMapItem::Vertical:
+ return false;
+ default:
+ return r.width() > r.height();
+ }
+ return false;
+}
+
+
+/**
+ * Draw TreeMapItems recursive, starting from item
+ */
+void TreeMapWidget::drawItems(QPainter* p,
+ TreeMapItem* item)
+{
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << "+drawItems(" << item->path(0).join("/") << ", "
+ << item->itemRect().x() << "/" << item->itemRect().y()
+ << "-" << item->itemRect().width() << "x"
+ << item->itemRect().height() << "), Val " << item->value()
+ << ", Sum " << item->sum() << endl;
+
+ drawItem(p, item);
+ item->clearFreeRects();
+
+ QRect origRect = item->itemRect();
+ int bw = item->borderWidth();
+ QRect r = QRect(origRect.x()+bw, origRect.y()+bw,
+ origRect.width()-2*bw, origRect.height()-2*bw);
+
+ TreeMapItemList* list = item->children();
+ TreeMapItem* i;
+
+ bool stopDrawing = false;
+
+ // only subdivide if there are children
+ if (!list || list->count()==0)
+ stopDrawing = true;
+
+ // only subdivide if there is enough space
+ if (!stopDrawing && (r.width()<=0 || r.height()<=0))
+ stopDrawing = true;
+
+ // stop drawing if maximum depth is reached
+ if (!stopDrawing &&
+ (_maxDrawingDepth>=0 && item->depth()>=_maxDrawingDepth))
+ stopDrawing = true;
+
+ // stop drawing if stopAtText is reached
+ if (!stopDrawing)
+ for (int no=0;no<(int)_attr.size();no++) {
+ QString stopAt = fieldStop(no);
+ if (!stopAt.isEmpty() && (item->text(no) == stopAt)) {
+ stopDrawing = true;
+ break;
+ }
+ }
+
+ // area size is checked later...
+#if 0
+ // stop drawing if minimal area size is reached
+ if (!stopDrawing &&
+ (_minimalArea > 0) &&
+ (r.width() * r.height() < _minimalArea)) stopDrawing = true;
+#endif
+
+ if (stopDrawing) {
+ if (list) {
+ // invalidate rects
+ for (i=list->first();i;i=list->next())
+ i->clearItemRect();
+ }
+ // tooltip apears on whole item rect
+ item->addFreeRect(item->itemRect());
+
+ // if we have space for text...
+ if ((r.height() < _fontHeight) || (r.width() < _fontHeight)) return;
+
+ RectDrawing d(r);
+ item->setRotated(_allowRotation && (r.height() > r.width()));
+ for (int no=0;no<(int)_attr.size();no++) {
+ if (!fieldVisible(no)) continue;
+ d.drawField(p, no, item);
+ }
+ r = d.remainingRect(item);
+
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << "-drawItems(" << item->path(0).join("/") << ")" << endl;
+ return;
+ }
+
+ double user_sum, child_sum, self;
+
+ // user supplied sum
+ user_sum = item->sum();
+
+ // own sum
+ child_sum = 0;
+ for (i=list->first();i;i=list->next()) {
+ child_sum += i->value();
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << " child: " << i->text(0) << ", value "
+ << i->value() << endl;
+ }
+
+ QRect orig = r;
+
+ // if we have space for text...
+ if ((r.height() >= _fontHeight) && (r.width() >= _fontHeight)) {
+
+ RectDrawing d(r);
+ item->setRotated(_allowRotation && (r.height() > r.width()));
+ for (int no=0;no<(int)_attr.size();no++) {
+ if (!fieldVisible(no)) continue;
+ if (!fieldForced(no)) continue;
+ d.drawField(p, no, item);
+ }
+ r = d.remainingRect(item);
+ }
+
+ if (orig.x() == r.x()) {
+ // Strings on top
+ item->addFreeRect(QRect(orig.x(), orig.y(),
+ orig.width(), orig.height()-r.height()));
+ }
+ else {
+ // Strings on the left
+ item->addFreeRect(QRect(orig.x(), orig.y(),
+ orig.width()-r.width(), orig.height()));
+ }
+
+ if (user_sum == 0) {
+ // user didn't supply any sum
+ user_sum = child_sum;
+ self = 0;
+ }
+ else {
+ self = user_sum - child_sum;
+
+ if (user_sum < child_sum) {
+ //kdDebug(90100) << "TreeMWidget " <<
+ // item->path() << ": User sum " << user_sum << " < Child Items sum " << child_sum << endl;
+
+ // invalid user supplied sum: ignore and use calculate sum
+ user_sum = child_sum;
+ self = 0.0;
+ }
+ else {
+ // Try to put the border waste in self
+ // percent of wasted space on border...
+ float borderArea = origRect.width() * origRect.height();
+ borderArea = (borderArea - r.width()*r.height())/borderArea;
+ unsigned borderValue = (unsigned)(borderArea * user_sum);
+
+ if (borderValue > self) {
+ if (_skipIncorrectBorder) {
+ r = origRect;
+ // should add my self to nested self and set my self =0
+ }
+ else
+ self = 0.0;
+ }
+ else
+ self -= borderValue;
+
+ user_sum = child_sum + self;
+ }
+ }
+
+ bool rotate = (_allowRotation && (r.height() > r.width()));
+ int self_length = (int)( ((rotate) ? r.width() : r.height()) *
+ self / user_sum + .5);
+ if (self_length > 0) {
+ // take space for self cost
+ QRect sr = r;
+ if (rotate) {
+ sr.setWidth( self_length );
+ r.setRect(r.x()+sr.width(), r.y(), r.width()-sr.width(), r.height());
+ }
+ else {
+ sr.setHeight( self_length );
+ r.setRect(r.x(), r.y()+sr.height(), r.width(), r.height()-sr.height());
+ }
+
+ // set selfRect (not occupied by children) for tooltip
+ item->addFreeRect(sr);
+
+ if (0) kdDebug(90100) << "Item " << item->path(0).join("/") << ": SelfR "
+ << sr.x() << "/" << sr.y() << "-" << sr.width()
+ << "/" << sr.height() << ", self " << self << "/"
+ << user_sum << endl;
+
+ if ((sr.height() >= _fontHeight) && (sr.width() >= _fontHeight)) {
+
+ RectDrawing d(sr);
+ item->setRotated(_allowRotation && (r.height() > r.width()));
+ for (int no=0;no<(int)_attr.size();no++) {
+ if (!fieldVisible(no)) continue;
+ if (fieldForced(no)) continue;
+ d.drawField(p, no, item);
+ }
+ }
+
+ user_sum -= self;
+ }
+
+ bool goBack;
+ if (item->sorting(&goBack) == -1) {
+ // noSorting
+ goBack = false;
+ }
+
+ TreeMapItemListIterator it(*list);
+ if (goBack) it.toLast();
+
+ if (item->splitMode() == TreeMapItem::Columns) {
+ int len = list->count();
+ bool drawDetails = true;
+
+ while (len>0 && user_sum>0) {
+ TreeMapItemListIterator first = it;
+ double valSum = 0;
+ int lenLeft = len;
+ int columns = (int)(sqrt((double)len * r.width()/r.height())+.5);
+ if (columns==0) columns = 1; //should never be needed
+
+ while (lenLeft>0 && ((double)valSum*(len-lenLeft) <
+ (double)len*user_sum/columns/columns)) {
+ valSum += it.current()->value();
+ if (goBack) --it; else ++it;
+ lenLeft--;
+ }
+
+ // we always split horizontally
+ int nextPos = (int)((double)r.width() * valSum / user_sum);
+ QRect firstRect = QRect(r.x(), r.y(), nextPos, r.height());
+
+ if (nextPos < _visibleWidth) {
+ if (item->sorting(0) == -1) {
+ // fill current rect with hash pattern
+ drawFill(item, p, firstRect);
+ }
+ else {
+ // fill rest with hash pattern
+ drawFill(item, p, r, first, len, goBack);
+ break;
+ }
+ }
+ else {
+ drawDetails = drawItemArray(p, item, firstRect,
+ valSum, first, len-lenLeft, goBack);
+ }
+ r.setRect(r.x()+nextPos, r.y(), r.width()-nextPos, r.height());
+ user_sum -= valSum;
+ len = lenLeft;
+
+ if (!drawDetails) {
+ if (item->sorting(0) == -1)
+ drawDetails = true;
+ else {
+ drawFill(item, p, r, it, len, goBack);
+ break;
+ }
+ }
+ }
+ }
+ else if (item->splitMode() == TreeMapItem::Rows) {
+ int len = list->count();
+ bool drawDetails = true;
+
+ while (len>0 && user_sum>0) {
+ TreeMapItemListIterator first = it;
+ double valSum = 0;
+ int lenLeft = len;
+ int rows = (int)(sqrt((double)len * r.height()/r.width())+.5);
+ if (rows==0) rows = 1; //should never be needed
+
+ while (lenLeft>0 && ((double)valSum*(len-lenLeft) <
+ (double)len*user_sum/rows/rows)) {
+ valSum += it.current()->value();
+ if (goBack) --it; else ++it;
+ lenLeft--;
+ }
+
+ // we always split horizontally
+ int nextPos = (int)((double)r.height() * valSum / user_sum);
+ QRect firstRect = QRect(r.x(), r.y(), r.width(), nextPos);
+
+ if (nextPos < _visibleWidth) {
+ if (item->sorting(0) == -1) {
+ drawFill(item, p, firstRect);
+ }
+ else {
+ drawFill(item, p, r, first, len, goBack);
+ break;
+ }
+ }
+ else {
+ drawDetails = drawItemArray(p, item, firstRect,
+ valSum, first, len-lenLeft, goBack);
+ }
+ r.setRect(r.x(), r.y()+nextPos, r.width(), r.height()-nextPos);
+ user_sum -= valSum;
+ len = lenLeft;
+
+ if (!drawDetails) {
+ if (item->sorting(0) == -1)
+ drawDetails = true;
+ else {
+ drawFill(item, p, r, it, len, goBack);
+ break;
+ }
+ }
+ }
+ }
+ else
+ drawItemArray(p, item, r, user_sum, it, list->count(), goBack);
+
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << "-drawItems(" << item->path(0).join("/") << ")" << endl;
+}
+
+// fills area with a pattern if to small to draw children
+void TreeMapWidget::drawFill(TreeMapItem* i, QPainter* p, QRect& r)
+{
+ p->setBrush(Qt::Dense4Pattern);
+ p->setPen(Qt::NoPen);
+ p->drawRect(r);
+ i->addFreeRect(r);
+}
+
+// fills area with a pattern if to small to draw children
+void TreeMapWidget::drawFill(TreeMapItem* i, QPainter* p, QRect& r,
+ TreeMapItemListIterator it, int len, bool goBack)
+{
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << " +drawFill(" << r.x() << "/" << r.y()
+ << "-" << r.width() << "x" << r.height()
+ << ", len " << len << ")" << endl;
+
+ p->setBrush(Qt::Dense4Pattern);
+ p->setPen(Qt::NoPen);
+ p->drawRect(r);
+ i->addFreeRect(r);
+
+ // reset rects
+ while (len>0 && it.current()) {
+
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << " Reset Rect " << (*it)->path(0).join("/") << endl;
+
+ (*it)->clearItemRect();
+ if (goBack) --it; else ++it;
+ len--;
+ }
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << " -drawFill(" << r.x() << "/" << r.y()
+ << "-" << r.width() << "x" << r.height()
+ << ", len " << len << ")" << endl;
+}
+
+// returns false if rect gets to small
+bool TreeMapWidget::drawItemArray(QPainter* p, TreeMapItem* item,
+ QRect& r, double user_sum,
+ TreeMapItemListIterator it, int len,
+ bool goBack)
+{
+ if (user_sum == 0) return false;
+
+ static bool b2t = true;
+
+ // stop recursive bisection for small rectangles
+ if (((r.height() < _visibleWidth) &&
+ (r.width() < _visibleWidth)) ||
+ ((_minimalArea > 0) &&
+ (r.width() * r.height() < _minimalArea))) {
+
+ drawFill(item, p, r, it, len, goBack);
+ return false;
+ }
+
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << " +drawItemArray(" << item->path(0).join("/")
+ << ", " << r.x() << "/" << r.y() << "-" << r.width()
+ << "x" << r.height() << ")" << endl;
+
+ if (len>2 && (item->splitMode() == TreeMapItem::Bisection)) {
+
+ TreeMapItemListIterator first = it;
+ double valSum = 0;
+ int lenLeft = len;
+ //while (lenLeft>0 && valSum<user_sum/2) {
+ while (lenLeft>len/2) {
+ valSum += it.current()->value();
+ if (goBack) --it; else ++it;
+ lenLeft--;
+ }
+
+ // draw first half...
+ bool drawOn;
+
+ if (r.width() > r.height()) {
+ int halfPos = (int)((double)r.width() * valSum / user_sum);
+ QRect firstRect = QRect(r.x(), r.y(), halfPos, r.height());
+ drawOn = drawItemArray(p, item, firstRect,
+ valSum, first, len-lenLeft, goBack);
+ r.setRect(r.x()+halfPos, r.y(), r.width()-halfPos, r.height());
+ }
+ else {
+ int halfPos = (int)((double)r.height() * valSum / user_sum);
+ QRect firstRect = QRect(r.x(), r.y(), r.width(), halfPos);
+ drawOn = drawItemArray(p, item, firstRect,
+ valSum, first, len-lenLeft, goBack);
+ r.setRect(r.x(), r.y()+halfPos, r.width(), r.height()-halfPos);
+ }
+
+ // if no sorting, don't stop drawing
+ if (item->sorting(0) == -1) drawOn = true;
+
+ // second half
+ if (drawOn)
+ drawOn = drawItemArray(p, item, r, user_sum - valSum,
+ it, lenLeft, goBack);
+ else {
+ drawFill(item, p, r, it, len, goBack);
+ }
+
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << " -drawItemArray(" << item->path(0).join("/")
+ << ")" << endl;
+
+ return drawOn;
+ }
+
+ bool hor = horizontal(item,r);
+
+ TreeMapItem* i;
+ while (len>0) {
+ i = it.current();
+ if (user_sum <= 0) {
+
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << "drawItemArray: Reset " << i->path(0).join("/") << endl;
+
+ i->clearItemRect();
+ if (goBack) --it; else ++it;
+ len--;
+ continue;
+ }
+
+ // stop drawing for small rectangles
+ if (((r.height() < _visibleWidth) &&
+ (r.width() < _visibleWidth)) ||
+ ((_minimalArea > 0) &&
+ (r.width() * r.height() < _minimalArea))) {
+
+ drawFill(item, p, r, it, len, goBack);
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << " -drawItemArray(" << item->path(0).join("/")
+ << "): Stop" << endl;
+ return false;
+ }
+
+ if (i->splitMode() == TreeMapItem::AlwaysBest)
+ hor = r.width() > r.height();
+
+ int lastPos = hor ? r.width() : r.height();
+ double val = i->value();
+ int nextPos = (user_sum <= 0.0) ? 0: (int)(lastPos * val / user_sum +.5);
+ if (nextPos>lastPos) nextPos = lastPos;
+
+ if ((item->sorting(0) != -1) && (nextPos < _visibleWidth)) {
+ drawFill(item, p, r, it, len, goBack);
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << " -drawItemArray(" << item->path(0).join("/")
+ << "): Stop" << endl;
+ return false;
+ }
+
+ QRect currRect = r;
+
+ if (hor)
+ currRect.setWidth(nextPos);
+ else {
+ if (b2t)
+ currRect.setRect(r.x(), r.bottom()-nextPos+1, r.width(), nextPos);
+ else
+ currRect.setHeight(nextPos);
+ }
+
+ // don't draw very small rectangles:
+ if (nextPos >= _visibleWidth) {
+ i->setItemRect(currRect);
+ drawItems(p, i);
+ }
+ else {
+ i->clearItemRect();
+ drawFill(item, p, currRect);
+ }
+
+ // draw Separator
+ if (_drawSeparators && (nextPos<lastPos)) {
+ p->setPen(black);
+ if (hor) {
+ if (r.top()<=r.bottom())
+ p->drawLine(r.x() + nextPos, r.top(), r.x() + nextPos, r.bottom());
+ }
+ else {
+ if (r.left()<=r.right())
+ p->drawLine(r.left(), r.y() + nextPos, r.right(), r.y() + nextPos);
+ }
+ nextPos++;
+ }
+
+ if (hor)
+ r.setRect(r.x() + nextPos, r.y(), lastPos-nextPos, r.height());
+ else {
+ if (b2t)
+ r.setRect(r.x(), r.y(), r.width(), lastPos-nextPos);
+ else
+ r.setRect(r.x(), r.y() + nextPos, r.width(), lastPos-nextPos);
+ }
+
+ user_sum -= val;
+ if (goBack) --it; else ++it;
+ len--;
+ }
+
+ if (DEBUG_DRAWING)
+ kdDebug(90100) << " -drawItemArray(" << item->path(0).join("/")
+ << "): Continue" << endl;
+
+ return true;
+}
+
+
+/*----------------------------------------------------------------
+ * Popup menus for option setting
+ */
+
+void TreeMapWidget::splitActivated(int id)
+{
+ if (id == _splitID) setSplitMode(TreeMapItem::Bisection);
+ else if (id == _splitID+1) setSplitMode(TreeMapItem::Columns);
+ else if (id == _splitID+2) setSplitMode(TreeMapItem::Rows);
+ else if (id == _splitID+3) setSplitMode(TreeMapItem::AlwaysBest);
+ else if (id == _splitID+4) setSplitMode(TreeMapItem::Best);
+ else if (id == _splitID+5) setSplitMode(TreeMapItem::VAlternate);
+ else if (id == _splitID+6) setSplitMode(TreeMapItem::HAlternate);
+ else if (id == _splitID+7) setSplitMode(TreeMapItem::Horizontal);
+ else if (id == _splitID+8) setSplitMode(TreeMapItem::Vertical);
+}
+
+
+void TreeMapWidget::addSplitDirectionItems(QPopupMenu* popup, int id)
+{
+ _splitID = id;
+ popup->setCheckable(true);
+
+ connect(popup, SIGNAL(activated(int)),
+ this, SLOT(splitActivated(int)));
+
+ popup->insertItem(i18n("Recursive Bisection"), id);
+ popup->insertItem(i18n("Columns"), id+1);
+ popup->insertItem(i18n("Rows"), id+2);
+ popup->insertItem(i18n("Always Best"), id+3);
+ popup->insertItem(i18n("Best"), id+4);
+ popup->insertItem(i18n("Alternate (V)"), id+5);
+ popup->insertItem(i18n("Alternate (H)"), id+6);
+ popup->insertItem(i18n("Horizontal"), id+7);
+ popup->insertItem(i18n("Vertical"), id+8);
+
+ switch(splitMode()) {
+ case TreeMapItem::Bisection: popup->setItemChecked(id,true); break;
+ case TreeMapItem::Columns: popup->setItemChecked(id+1,true); break;
+ case TreeMapItem::Rows: popup->setItemChecked(id+2,true); break;
+ case TreeMapItem::AlwaysBest: popup->setItemChecked(id+3,true); break;
+ case TreeMapItem::Best: popup->setItemChecked(id+4,true); break;
+ case TreeMapItem::VAlternate: popup->setItemChecked(id+5,true); break;
+ case TreeMapItem::HAlternate: popup->setItemChecked(id+6,true); break;
+ case TreeMapItem::Horizontal: popup->setItemChecked(id+7,true); break;
+ case TreeMapItem::Vertical: popup->setItemChecked(id+8,true); break;
+ default: break;
+ }
+}
+
+void TreeMapWidget::visualizationActivated(int id)
+{
+ if (id == _visID+2) setSkipIncorrectBorder(!skipIncorrectBorder());
+ else if (id == _visID+3) setBorderWidth(0);
+ else if (id == _visID+4) setBorderWidth(1);
+ else if (id == _visID+5) setBorderWidth(2);
+ else if (id == _visID+6) setBorderWidth(3);
+ else if (id == _visID+10) setAllowRotation(!allowRotation());
+ else if (id == _visID+11) setShadingEnabled(!isShadingEnabled());
+ else if (id<_visID+19 || id>_visID+100) return;
+
+ id -= 20+_visID;
+ int f = id/10;
+ if ((id%10) == 1) setFieldVisible(f, !fieldVisible(f));
+ else if ((id%10) == 2) setFieldForced(f, !fieldForced(f));
+ else if ((id%10) == 3) setFieldPosition(f, DrawParams::TopLeft);
+ else if ((id%10) == 4) setFieldPosition(f, DrawParams::TopCenter);
+ else if ((id%10) == 5) setFieldPosition(f, DrawParams::TopRight);
+ else if ((id%10) == 6) setFieldPosition(f, DrawParams::BottomLeft);
+ else if ((id%10) == 7) setFieldPosition(f, DrawParams::BottomCenter);
+ else if ((id%10) == 8) setFieldPosition(f, DrawParams::BottomRight);
+}
+
+void TreeMapWidget::addVisualizationItems(QPopupMenu* popup, int id)
+{
+ _visID = id;
+
+ popup->setCheckable(true);
+
+ QPopupMenu* bpopup = new QPopupMenu();
+ bpopup->setCheckable(true);
+
+ connect(popup, SIGNAL(activated(int)),
+ this, SLOT(visualizationActivated(int)));
+ connect(bpopup, SIGNAL(activated(int)),
+ this, SLOT(visualizationActivated(int)));
+
+ QPopupMenu* spopup = new QPopupMenu();
+ addSplitDirectionItems(spopup, id+100);
+ popup->insertItem(i18n("Nesting"), spopup, id);
+
+ popup->insertItem(i18n("Border"), bpopup, id+1);
+ bpopup->insertItem(i18n("Correct Borders Only"), id+2);
+ bpopup->insertSeparator();
+ bpopup->insertItem(i18n("Width %1").arg(0), id+3);
+ bpopup->insertItem(i18n("Width %1").arg(1), id+4);
+ bpopup->insertItem(i18n("Width %1").arg(2), id+5);
+ bpopup->insertItem(i18n("Width %1").arg(3), id+6);
+ bpopup->setItemChecked(id+2, skipIncorrectBorder());
+ bpopup->setItemChecked(id+3, borderWidth()==0);
+ bpopup->setItemChecked(id+4, borderWidth()==1);
+ bpopup->setItemChecked(id+5, borderWidth()==2);
+ bpopup->setItemChecked(id+6, borderWidth()==3);
+
+ popup->insertItem(i18n("Allow Rotation"), id+10);
+ popup->setItemChecked(id+10,allowRotation());
+ popup->insertItem(i18n("Shading"), id+11);
+ popup->setItemChecked(id+11,isShadingEnabled());
+
+ if (_attr.size() ==0) return;
+
+ popup->insertSeparator();
+ int f;
+ QPopupMenu* tpopup;
+ id += 20;
+ for (f=0;f<(int)_attr.size();f++, id+=10) {
+ tpopup = new QPopupMenu();
+ tpopup->setCheckable(true);
+ popup->insertItem(_attr[f].type, tpopup, id);
+ tpopup->insertItem(i18n("Visible"), id+1);
+ tpopup->insertItem(i18n("Take Space From Children"), id+2);
+ tpopup->insertSeparator();
+ tpopup->insertItem(i18n("Top Left"), id+3);
+ tpopup->insertItem(i18n("Top Center"), id+4);
+ tpopup->insertItem(i18n("Top Right"), id+5);
+ tpopup->insertItem(i18n("Bottom Left"), id+6);
+ tpopup->insertItem(i18n("Bottom Center"), id+7);
+ tpopup->insertItem(i18n("Bottom Right"), id+8);
+
+ tpopup->setItemChecked(id+1,_attr[f].visible);
+ tpopup->setItemEnabled(id+2,_attr[f].visible);
+ tpopup->setItemEnabled(id+3,_attr[f].visible);
+ tpopup->setItemEnabled(id+4,_attr[f].visible);
+ tpopup->setItemEnabled(id+5,_attr[f].visible);
+ tpopup->setItemEnabled(id+6,_attr[f].visible);
+ tpopup->setItemEnabled(id+7,_attr[f].visible);
+ tpopup->setItemEnabled(id+8,_attr[f].visible);
+ tpopup->setItemChecked(id+2,_attr[f].forced);
+ tpopup->setItemChecked(id+3,_attr[f].pos == DrawParams::TopLeft);
+ tpopup->setItemChecked(id+4,_attr[f].pos == DrawParams::TopCenter);
+ tpopup->setItemChecked(id+5,_attr[f].pos == DrawParams::TopRight);
+ tpopup->setItemChecked(id+6,_attr[f].pos == DrawParams::BottomLeft);
+ tpopup->setItemChecked(id+7,_attr[f].pos == DrawParams::BottomCenter);
+ tpopup->setItemChecked(id+8,_attr[f].pos == DrawParams::BottomRight);
+
+ connect(tpopup, SIGNAL(activated(int)),
+ this, SLOT(visualizationActivated(int)));
+ }
+}
+
+void TreeMapWidget::selectionActivated(int id)
+{
+ TreeMapItem* i = _menuItem;
+ id -= _selectionID;
+ while (id>0 && i) {
+ i=i->parent();
+ id--;
+ }
+ if (i)
+ setSelected(i, true);
+}
+
+void TreeMapWidget::addSelectionItems(QPopupMenu* popup,
+ int id, TreeMapItem* i)
+{
+ if (!i) return;
+
+ _selectionID = id;
+ _menuItem = i;
+
+ connect(popup, SIGNAL(activated(int)),
+ this, SLOT(selectionActivated(int)));
+
+ while (i) {
+ QString name = i->text(0);
+ if (name.isEmpty()) break;
+ popup->insertItem(i->text(0), id++);
+ i = i->parent();
+ }
+}
+
+void TreeMapWidget::fieldStopActivated(int id)
+{
+ if (id == _fieldStopID) setFieldStop(0, QString::null);
+ else {
+ TreeMapItem* i = _menuItem;
+ id -= _fieldStopID+1;
+ while (id>0 && i) {
+ i=i->parent();
+ id--;
+ }
+ if (i)
+ setFieldStop(0, i->text(0));
+ }
+}
+
+void TreeMapWidget::addFieldStopItems(QPopupMenu* popup,
+ int id, TreeMapItem* i)
+{
+ _fieldStopID = id;
+
+ connect(popup, SIGNAL(activated(int)),
+ this, SLOT(fieldStopActivated(int)));
+
+ popup->insertItem(i18n("No %1 Limit").arg(fieldType(0)), id);
+ popup->setItemChecked(id, fieldStop(0).isEmpty());
+ _menuItem = i;
+ bool foundFieldStop = false;
+ if (i) {
+ popup->insertSeparator();
+
+ while (i) {
+ id++;
+ QString name = i->text(0);
+ if (name.isEmpty()) break;
+ popup->insertItem(i->text(0), id);
+ if (fieldStop(0) == i->text(0)) {
+ popup->setItemChecked(id, true);
+ foundFieldStop = true;
+ }
+ i = i->parent();
+ }
+ }
+
+ if (!foundFieldStop && !fieldStop(0).isEmpty()) {
+ popup->insertSeparator();
+ popup->insertItem(fieldStop(0), id+1);
+ popup->setItemChecked(id+1, true);
+ }
+}
+
+void TreeMapWidget::areaStopActivated(int id)
+{
+ if (id == _areaStopID) setMinimalArea(-1);
+ else if (id == _areaStopID+1) {
+ int area = _menuItem ? (_menuItem->width() * _menuItem->height()) : -1;
+ setMinimalArea(area);
+ }
+ else if (id == _areaStopID+2) setMinimalArea(100);
+ else if (id == _areaStopID+3) setMinimalArea(400);
+ else if (id == _areaStopID+4) setMinimalArea(1000);
+ else if (id == _areaStopID+5) setMinimalArea(minimalArea()*2);
+ else if (id == _areaStopID+6) setMinimalArea(minimalArea()/2);
+}
+
+void TreeMapWidget::addAreaStopItems(QPopupMenu* popup,
+ int id, TreeMapItem* i)
+{
+ _areaStopID = id;
+ _menuItem = i;
+
+ connect(popup, SIGNAL(activated(int)),
+ this, SLOT(areaStopActivated(int)));
+
+ bool foundArea = false;
+
+ popup->insertItem(i18n("No Area Limit"), id);
+ popup->setItemChecked(id, minimalArea() == -1);
+
+ if (i) {
+ int area = i->width() * i->height();
+ popup->insertSeparator();
+ popup->insertItem(i18n("Area of '%1' (%2)")
+ .arg(i->text(0)).arg(area), id+1);
+ if (area == minimalArea()) {
+ popup->setItemChecked(id+1, true);
+ foundArea = true;
+ }
+ }
+
+ popup->insertSeparator();
+ int area = 100, count;
+ for (count=0;count<3;count++) {
+ popup->insertItem(i18n("1 Pixel", "%n Pixels", area), id+2+count);
+ if (area == minimalArea()) {
+ popup->setItemChecked(id+2+count, true);
+ foundArea = true;
+ }
+ area = (area==100) ? 400 : (area==400) ? 1000 : 4000;
+ }
+
+ if (minimalArea()>0) {
+ popup->insertSeparator();
+ if (!foundArea) {
+ popup->insertItem(i18n("1 Pixel", "%n Pixels", minimalArea()), id+10);
+ popup->setItemChecked(id+10, true);
+ }
+
+ popup->insertItem(i18n("Double Area Limit (to %1)")
+ .arg(minimalArea()*2), id+5);
+ popup->insertItem(i18n("Halve Area Limit (to %1)")
+ .arg(minimalArea()/2), id+6);
+ }
+}
+
+
+void TreeMapWidget::depthStopActivated(int id)
+{
+ if (id == _depthStopID) setMaxDrawingDepth(-1);
+ else if (id == _depthStopID+1) {
+ int d = _menuItem ? _menuItem->depth() : -1;
+ setMaxDrawingDepth(d);
+ }
+ else if (id == _depthStopID+2) setMaxDrawingDepth(maxDrawingDepth()-1);
+ else if (id == _depthStopID+3) setMaxDrawingDepth(maxDrawingDepth()+1);
+ else if (id == _depthStopID+4) setMaxDrawingDepth(2);
+ else if (id == _depthStopID+5) setMaxDrawingDepth(4);
+ else if (id == _depthStopID+6) setMaxDrawingDepth(6);
+}
+
+void TreeMapWidget::addDepthStopItems(QPopupMenu* popup,
+ int id, TreeMapItem* i)
+{
+ _depthStopID = id;
+ _menuItem = i;
+
+ connect(popup, SIGNAL(activated(int)),
+ this, SLOT(depthStopActivated(int)));
+
+ bool foundDepth = false;
+
+ popup->insertItem(i18n("No Depth Limit"), id);
+ popup->setItemChecked(id, maxDrawingDepth() == -1);
+
+ if (i) {
+ int d = i->depth();
+ popup->insertSeparator();
+ popup->insertItem(i18n("Depth of '%1' (%2)")
+ .arg(i->text(0)).arg(d), id+1);
+ if (d == maxDrawingDepth()) {
+ popup->setItemChecked(id+1, true);
+ foundDepth = true;
+ }
+ }
+
+ popup->insertSeparator();
+ int depth = 2, count;
+ for (count=0;count<3;count++) {
+ popup->insertItem(i18n("Depth %1").arg(depth), id+4+count);
+ if (depth == maxDrawingDepth()) {
+ popup->setItemChecked(id+4+count, true);
+ foundDepth = true;
+ }
+ depth = (depth==2) ? 4 : 6;
+ }
+
+ if (maxDrawingDepth()>1) {
+ popup->insertSeparator();
+ if (!foundDepth) {
+ popup->insertItem(i18n("Depth %1").arg(maxDrawingDepth()), id+10);
+ popup->setItemChecked(id+10, true);
+ }
+
+ popup->insertItem(i18n("Decrement (to %1)")
+ .arg(maxDrawingDepth()-1), id+2);
+ popup->insertItem(i18n("Increment (to %1)")
+ .arg(maxDrawingDepth()+1), id+3);
+ }
+}
+
+
+
+/*----------------------------------------------------------------
+ * Option saving/restoring
+ */
+
+void TreeMapWidget::saveOptions(KConfigGroup* config, QString prefix)
+{
+ config->writeEntry(prefix+"Nesting", splitModeString());
+ config->writeEntry(prefix+"AllowRotation", allowRotation());
+ config->writeEntry(prefix+"ShadingEnabled", isShadingEnabled());
+ config->writeEntry(prefix+"OnlyCorrectBorder", skipIncorrectBorder());
+ config->writeEntry(prefix+"BorderWidth", borderWidth());
+ config->writeEntry(prefix+"MaxDepth", maxDrawingDepth());
+ config->writeEntry(prefix+"MinimalArea", minimalArea());
+
+ int f, fCount = _attr.size();
+ config->writeEntry(prefix+"FieldCount", fCount);
+ for (f=0;f<fCount;f++) {
+ config->writeEntry(QString(prefix+"FieldVisible%1").arg(f),
+ _attr[f].visible);
+ config->writeEntry(QString(prefix+"FieldForced%1").arg(f),
+ _attr[f].forced);
+ config->writeEntry(QString(prefix+"FieldStop%1").arg(f),
+ _attr[f].stop);
+ config->writeEntry(QString(prefix+"FieldPosition%1").arg(f),
+ fieldPositionString(f));
+ }
+}
+
+
+void TreeMapWidget::restoreOptions(KConfigGroup* config, QString prefix)
+{
+ bool enabled;
+ int num;
+ QString str;
+
+ str = config->readEntry(prefix+"Nesting");
+ if (!str.isEmpty()) setSplitMode(str);
+
+ if (config->hasKey(prefix+"AllowRotation")) {
+ enabled = config->readBoolEntry(prefix+"AllowRotation", true);
+ setAllowRotation(enabled);
+ }
+
+ if (config->hasKey(prefix+"ShadingEnabled")) {
+ enabled = config->readBoolEntry(prefix+"ShadingEnabled", true);
+ setShadingEnabled(enabled);
+ }
+
+ if (config->hasKey(prefix+"OnlyCorrectBorder")) {
+ enabled = config->readBoolEntry(prefix+"OnlyCorrectBorder", false);
+ setSkipIncorrectBorder(enabled);
+ }
+
+ num = config->readNumEntry(prefix+"BorderWidth", -2);
+ if (num!=-2) setBorderWidth(num);
+
+ num = config->readNumEntry(prefix+"MaxDepth", -2);
+ if (num!=-2) setMaxDrawingDepth(num);
+
+ num = config->readNumEntry(prefix+"MinimalArea", -2);
+ if (num!=-2) setMinimalArea(num);
+
+ num = config->readNumEntry(prefix+"FieldCount", -2);
+ if (num<=0 || num>MAX_FIELD) return;
+
+ int f;
+ for (f=0;f<num;f++) {
+ str = QString(prefix+"FieldVisible%1").arg(f);
+ if (config->hasKey(str))
+ setFieldVisible(f, config->readBoolEntry(str));
+
+ str = QString(prefix+"FieldForced%1").arg(f);
+ if (config->hasKey(str))
+ setFieldForced(f, config->readBoolEntry(str));
+
+ str = config->readEntry(QString(prefix+"FieldStop%1").arg(f));
+ setFieldStop(f, str);
+
+ str = config->readEntry(QString(prefix+"FieldPosition%1").arg(f));
+ if (!str.isEmpty()) setFieldPosition(f, str);
+ }
+}
+
+#include "treemap.moc"