Code cleanup: Remove exprStringAt().
[kdbg.git] / kdbg / ktreeview.cpp
blobb42732adf8f56e888067346fd62656ca118f71a7
1 /*
2 * $Id$
4 * KTreeView implementation
5 *
6 * Copyright (C) 1997-1999 Johannes Sixt
7 *
8 * based on KTreeList, which is
9 * Copyright (C) 1996 Keith Brown and KtSoft
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version.
16 * This program is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABLILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details. You should have received a copy
20 * of the GNU General Public License along with this program; if not, write
21 * to the Free Software Foundation, Inc, 675 Mass Ave, Cambridge, MA 02139,
22 * USA.
25 #include <ktreeview.h>
26 #include "ktreeview.moc"
27 #include <qapplication.h> /* used for QApplication::closingDown() */
28 #include <qkeycode.h> /* used for keyboard interface */
29 #include <qpainter.h> /* used to paint items */
30 #include <qptrstack.h>
31 #include <assert.h>
34 * -------------------------------------------------------------------
36 * KTreeViewItem
38 * -------------------------------------------------------------------
41 // constructor
42 KTreeViewItem::KTreeViewItem(const QString& theText) :
43 owner(0),
44 numChildren(0),
45 doExpandButton(true),
46 expanded(false),
47 delayedExpanding(false),
48 doTree(true),
49 doText(true),
50 child(0),
51 parent(0),
52 sibling(0)
54 text = theText;
57 // constructor that takes a pixmap
58 KTreeViewItem::KTreeViewItem(const QString& theText,
59 const QPixmap& thePixmap) :
60 owner(0),
61 numChildren(0),
62 doExpandButton(true),
63 expanded(false),
64 delayedExpanding(false),
65 doTree(true),
66 doText(true),
67 child(0),
68 parent(0),
69 sibling(0)
71 text = theText;
72 pixmap = thePixmap;
75 // destructor
76 KTreeViewItem::~KTreeViewItem()
78 // remove the children
79 KTreeViewItem* i = child;
80 while (i) {
81 KTreeViewItem* d = i;
82 i = i->getSibling();
83 delete d;
87 // appends a direct child
88 void KTreeViewItem::appendChild(KTreeViewItem* newChild)
90 newChild->parent = this;
91 newChild->setOwner(owner);
92 if (!getChild()) {
93 child = newChild;
95 else {
96 KTreeViewItem* lastChild = getChild();
97 while (lastChild->hasSibling()) {
98 lastChild = lastChild->getSibling();
100 lastChild->sibling = newChild;
102 newChild->sibling = 0;
103 numChildren++;
106 // returns the bounding rectangle of the item content (not including indent
107 // and branches) for hit testing
108 QRect KTreeViewItem::boundingRect(int indent) const
110 const QFontMetrics& fm = owner->fontMetrics();
111 int rectX = indent;
112 int rectY = 0;
113 int rectW = width(indent, fm) - rectX;
114 int rectH = height(fm);
115 return QRect(rectX, rectY, rectW, rectH);
118 // returns the child item at the specified index
119 KTreeViewItem* KTreeViewItem::childAt(int index) const
121 if (!hasChild())
122 return 0;
123 KTreeViewItem* item = getChild();
124 while (index > 0 && item != 0) {
125 item = item->getSibling();
126 index--;
128 return item;
131 // returns the number of children this item has
132 uint KTreeViewItem::childCount() const
134 return numChildren;
137 /* returns the index of the given child item in this item's branch */
138 int KTreeViewItem::childIndex(KTreeViewItem* searched) const
140 assert(searched != 0);
141 int index = 0;
142 KTreeViewItem* item = getChild();
143 while (item != 0 && item != searched) {
144 item = item->getSibling();
145 index++;
147 return item == 0 ? -1 : index;
150 // indicates whether mouse press is in expand button
151 inline bool KTreeViewItem::expandButtonClicked(const QPoint& coord) const
153 return expandButton.contains(coord);
156 // returns a pointer to first child item
157 KTreeViewItem* KTreeViewItem::getChild() const
159 return child;
162 // returns the parent of this item
163 KTreeViewItem* KTreeViewItem::getParent() const
165 return parent;
168 // returns reference to the item pixmap
169 const QPixmap& KTreeViewItem::getPixmap() const
171 return pixmap;
174 // returns the sibling below this item
175 KTreeViewItem* KTreeViewItem::getSibling() const
177 return sibling;
180 // returns a pointer to the item text
181 const QString& KTreeViewItem::getText() const
183 return text;
186 // indicates whether this item has any children
187 bool KTreeViewItem::hasChild() const
189 return child != 0;
192 // indicates whether this item has a parent
193 bool KTreeViewItem::hasParent() const
195 return parent != 0;
198 // indicates whether this item has a sibling below it
199 bool KTreeViewItem::hasSibling() const
201 return sibling != 0;
204 int KTreeViewItem::height() const
206 assert(owner != 0);
207 return height(owner->fontMetrics());
210 // returns the maximum height of text and pixmap including margins
211 int KTreeViewItem::height(const QFontMetrics& fm) const
213 int maxHeight = pixmap.height();
214 int textHeight = fm.height();
215 maxHeight = textHeight > maxHeight ? textHeight : maxHeight;
216 return maxHeight + 4;
219 // inserts child item at specified index in branch
220 void KTreeViewItem::insertChild(int index, KTreeViewItem* newChild)
222 if (index < 0) {
223 appendChild(newChild);
224 return;
226 newChild->parent = this;
227 newChild->setOwner(owner);
228 if (index == 0) {
229 newChild->sibling = getChild();
230 child = newChild;
232 else {
233 KTreeViewItem* prevItem = getChild();
234 KTreeViewItem* nextItem = prevItem->getSibling();
235 while (--index > 0 && nextItem) {
236 prevItem = nextItem;
237 nextItem = prevItem->getSibling();
239 prevItem->sibling = newChild;
240 newChild->sibling = nextItem;
242 numChildren++;
245 // indicates whether this item is displayed expanded
246 // NOTE: a TRUE response does not necessarily indicate the item
247 // has any children
248 bool KTreeViewItem::isExpanded() const
250 return expanded;
253 // returns true if all ancestors are expanded
254 bool KTreeViewItem::isVisible() const
256 for (KTreeViewItem* p = getParent(); p != 0; p = p->getParent()) {
257 if (!p->isExpanded())
258 return false;
260 return true;
263 bool KTreeViewItem::keyEvent(QKeyEvent* /*ev*/)
265 return false; /* not handled */
268 bool KTreeViewItem::mouseEvent(QMouseEvent* /*ev*/, const QPoint& /*itemCoord*/)
270 return false; /* not handled */
273 // paint this item, including tree branches and expand button
274 void KTreeViewItem::paint(QPainter* p, int indent, const QColorGroup& cg,
275 bool highlighted) const
277 assert(getParent() != 0); /* can't paint root item */
278 assert(owner != 0);
280 p->save();
282 int cellHeight = height(p->fontMetrics());
284 if (doTree) {
285 paintTree(p, indent, cellHeight, cg);
289 * If this item has at least one child and expand button drawing is
290 * enabled, set the rect for the expand button for later mouse press
291 * bounds checking, and draw the button.
293 if (doExpandButton && (child || delayedExpanding)) {
294 paintExpandButton(p, indent, cellHeight, cg);
297 // now draw the item pixmap and text, if applicable
298 int pixmapX = indent;
299 int pixmapY = (cellHeight - pixmap.height()) / 2;
300 p->drawPixmap(pixmapX, pixmapY, pixmap);
302 if (doText) {
303 paintText(p, indent, cellHeight, cg, highlighted);
305 p->restore();
308 void KTreeViewItem::paintExpandButton(QPainter* p, int indent, int cellHeight,
309 const QColorGroup& cg) const
311 int parentLeaderX = indent - (owner->itemIndent / 2);
312 int cellCenterY = cellHeight / 2;
314 QRect paintRect(parentLeaderX - 4, cellCenterY - 4, 9, 9);
315 p->setBrush(cg.base());
316 p->setPen(cg.foreground());
317 p->drawRect(paintRect);
318 if (expanded) {
319 p->drawLine(parentLeaderX - 2, cellCenterY,
320 parentLeaderX + 2, cellCenterY);
321 } else {
322 p->drawLine(parentLeaderX - 2, cellCenterY,
323 parentLeaderX + 2, cellCenterY);
324 p->drawLine(parentLeaderX, cellCenterY - 2,
325 parentLeaderX, cellCenterY + 2);
327 p->setBrush(NoBrush);
330 * The area that the user can click to collapse and expand the tree is
331 * somewhat larger than the painted expand button.
333 expandButton.setRect(indent - owner->itemIndent, 0,
334 owner->itemIndent, cellHeight);
337 // paint the highlight
338 void KTreeViewItem::paintHighlight(QPainter* p, int indent, const QColorGroup& colorGroup,
339 bool hasFocus) const
341 QColor fc;
342 fc = colorGroup.text();
343 QRect textRect = textBoundingRect(indent);
344 int t,l,b,r;
345 textRect.coords(&l, &t, &r, &b);
346 QRect outerRect;
347 outerRect.setCoords(l - 2, t - 2, r + 2, b + 2);
348 if (hasFocus) {
349 p->fillRect(textRect, fc); /* highlight background */
350 p->setPen(fc);
351 p->drawRect(outerRect);
352 } else {
353 p->fillRect(outerRect, fc); /* highlight background */
357 // draw the text, highlighted if requested
358 void KTreeViewItem::paintText(QPainter* p, int indent, int cellHeight,
359 const QColorGroup& cg, bool highlighted) const
361 int textX = indent + pixmap.width() + 3;
362 int textY = (cellHeight - p->fontMetrics().height()) / 2 +
363 p->fontMetrics().ascent();
365 if (highlighted) {
366 paintHighlight(p, indent, cg, owner->hasFocus());
367 p->setPen(cg.base());
368 p->setBackgroundColor(cg.text());
370 else {
371 p->setPen(cg.text());
372 p->setBackgroundColor(cg.base());
374 p->drawText(textX, textY, text);
377 // paint the tree structure
378 void KTreeViewItem::paintTree(QPainter* p, int indent, int cellHeight,
379 const QColorGroup& cg) const
381 int parentLeaderX = indent - (owner->itemIndent / 2);
382 int cellCenterY = cellHeight / 2;
383 int cellBottomY = cellHeight - 1;
384 int itemLeaderX = indent - 1;
386 p->setPen(cg.background());
389 * If this is not the first item in the tree draw the line up
390 * towards parent or sibling.
392 if (parent->parent != 0 || parent->getChild() != this)
393 p->drawLine(parentLeaderX, 0, parentLeaderX, cellCenterY);
395 // draw the line down towards sibling
396 if (sibling)
397 p->drawLine(parentLeaderX, cellCenterY, parentLeaderX, cellBottomY);
400 * If this item has children or siblings in the tree or is a child of
401 * an item other than the root item then draw the little line from the
402 * item out to the left.
404 if (sibling || (doExpandButton && (child || delayedExpanding)) ||
405 parent->parent != 0 ||
407 * The following handles the case of an item at the end of the
408 * topmost level which doesn't have children.
410 parent->getChild() != this)
412 p->drawLine(parentLeaderX, cellCenterY, itemLeaderX, cellCenterY);
416 * If there are siblings of ancestors below, draw our portion of the
417 * branches that extend through this cell.
419 KTreeViewItem* prevRoot = parent;
420 while (prevRoot->getParent() != 0) { /* while not root item */
421 assert(prevRoot->owner == owner);
422 if (prevRoot->hasSibling()) {
423 int sibLeaderX = owner->indentation(prevRoot) - (owner->itemIndent / 2);
424 p->drawLine(sibLeaderX, 0, sibLeaderX, cellBottomY);
426 prevRoot = prevRoot->getParent();
430 // removes the given (direct) child from the branch
431 bool KTreeViewItem::removeChild(KTreeViewItem* theChild)
433 // search item in list of children
434 KTreeViewItem* prevItem = 0;
435 KTreeViewItem* toRemove = getChild();
436 while (toRemove && toRemove != theChild) {
437 prevItem = toRemove;
438 toRemove = toRemove->getSibling();
441 if (toRemove) {
442 // found it!
443 if (prevItem == 0) {
444 child = toRemove->getSibling();
445 } else {
446 prevItem->sibling = toRemove->getSibling();
448 numChildren--;
449 toRemove->setOwner(0);
451 assert((numChildren == 0) == (child == 0));
453 return toRemove != 0;
456 // sets the delayed-expanding flag
457 void KTreeViewItem::setDelayedExpanding(bool flag)
459 delayedExpanding = flag;
462 // sets the draw expand button flag of this item
463 void KTreeViewItem::setDrawExpandButton(bool doit)
465 doExpandButton = doit;
468 // sets the draw text flag of this item
469 void KTreeViewItem::setDrawText(bool doit)
471 doText = doit;
474 // sets the draw tree branch flag of this item
475 void KTreeViewItem::setDrawTree(bool doit)
477 doTree = doit;
480 // sets the expanded flag of this item
481 void KTreeViewItem::setExpanded(bool is)
483 expanded = is;
486 // sets the owner of this item and its children siblings
487 void KTreeViewItem::setOwner(KTreeView* newOwner, bool includeSiblings)
489 /* Note: set owner of children's siblings! */
490 owner = newOwner;
491 if (getChild())
492 getChild()->setOwner(newOwner, true);
493 if (includeSiblings && getSibling())
494 getSibling()->setOwner(newOwner, true);
497 // sets the item pixmap to the given pixmap
498 void KTreeViewItem::setPixmap(const QPixmap& pm)
500 pixmap = pm;
503 // sets the item text to the given string
504 void KTreeViewItem::setText(const QString& t)
506 text = t;
509 // counts the child items and stores the result in numChildren
510 void KTreeViewItem::synchNumChildren()
512 numChildren = 0;
513 KTreeViewItem* item = getChild();
514 while (item != 0) {
515 numChildren++;
516 item = item->getSibling();
521 * returns the bounding rect of the item text in cell coordinates couldn't
522 * get QFontMetrics::boundingRect() to work right so I made my own
524 QRect KTreeViewItem::textBoundingRect(int indent) const
526 const QFontMetrics& fm = owner->fontMetrics();
527 int cellHeight = height(fm);
528 int rectX = indent + pixmap.width() + 2;
529 int rectY = (cellHeight - fm.height()) / 2;
530 int rectW = fm.width(text) + 2;
531 int rectH = fm.height();
532 return QRect(rectX, rectY, rectW, rectH);
535 // returns the total width of text and pixmap, including margins, spacing
536 // and indent, or -1 if empty -- SHOULD NEVER BE -1!
537 int KTreeViewItem::width(int indent) const
539 return width(indent, owner->fontMetrics());
542 // The width of an item is composed of the following:
543 // - indentation (indent with times level)
544 // - pixmap width
545 // - 2 pixels for highlight border
546 // - 1 pixel margin
547 // - text width
548 // - 1 pixel margin
549 // - 2 pixels for highlight border
550 int KTreeViewItem::width(int indent, const QFontMetrics& fm) const
552 return indent + pixmap.width() + fm.width(text) + 6;
557 * -------------------------------------------------------------------
559 * KTreeView
561 * -------------------------------------------------------------------
564 // constructor
565 KTreeView::KTreeView(QWidget *parent,
566 const char *name,
567 WFlags f) :
568 TableView(parent, name, f),
569 clearing(false),
570 current(-1),
571 drawExpandButton(true),
572 drawTree(true),
573 expansion(0),
574 goingDown(false),
575 itemIndent(18),
576 showText(true),
577 moveCurrentToSibling(false),
578 itemCapacity(500),
579 visibleItems(0),
580 rubberband_mode(false)
582 setNumRows(0);
583 setNumCols(1);
584 setBackgroundColor(colorGroup().base());
586 treeRoot = new KTreeViewItem;
587 treeRoot->setExpanded(true);
588 treeRoot->owner = this;
590 visibleItems = new KTreeViewItem*[itemCapacity];
591 // clear those pointers
592 for (int j = itemCapacity-1; j >= 0; j--) {
593 visibleItems[j] = 0;
597 // destructor
598 KTreeView::~KTreeView()
600 goingDown = true;
601 clear();
602 delete[] visibleItems;
603 delete treeRoot;
606 // appends a child to the item at the given index with the given text
607 // and pixmap
608 void KTreeView::appendChildItem(const char* theText, const QPixmap& thePixmap,
609 int index)
611 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
612 appendChildItem(item, index);
615 // appends a child to the item at the end of the given path with
616 // the given text and pixmap
617 void KTreeView::appendChildItem(const char* theText, const QPixmap& thePixmap,
618 const KPath& thePath)
620 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
621 appendChildItem(item, thePath);
624 // appends the given item to the children of the item at the given index
625 void KTreeView::appendChildItem(KTreeViewItem* newItem, int index)
627 /* find parent item and append new item to parent's sub tree */
628 KTreeViewItem* parentItem = itemAt(index);
629 if (!parentItem)
630 return;
631 appendChildItem(parentItem, newItem);
634 // appends the given item to the children of the item at the end of the
635 // given path
636 void KTreeView::appendChildItem(KTreeViewItem* newItem, const KPath& thePath)
638 /* find parent item and append new item to parent's sub tree */
639 KTreeViewItem* parentItem = itemAt(thePath);
640 if (!parentItem)
641 return;
642 appendChildItem(parentItem, newItem);
645 // translate mouse coord to cell coord
646 QPoint KTreeView::cellCoords(int row, const QPoint& widgetCoord)
648 int cellX = 0, cellY = 0;
649 colXPos(0, &cellX);
650 rowYPos(row, &cellY);
651 return QPoint(widgetCoord.x() - cellX, widgetCoord.y() - cellY);
654 // find item at specified index and change pixmap and/or text
655 void KTreeView::changeItem(const char *newText,
656 const QPixmap *newPixmap,
657 int index)
659 KTreeViewItem *item = itemAt(index);
660 if(item)
661 changeItem(item, index, newText, newPixmap);
664 // find item at end of specified path, and change pixmap and/or text
665 void KTreeView::changeItem(const char* newText,
666 const QPixmap* newPixmap,
667 const KPath& thePath)
669 KTreeViewItem* item = itemAt(thePath);
670 if (item) {
671 int index = itemRow(item);
672 changeItem(item, index, newText, newPixmap);
676 // clear all items from list and erase display
677 void KTreeView::clear()
679 setCurrentItem(-1);
681 /* somewhat of a hack for takeItem so it doesn't update the current item... */
682 clearing = TRUE;
684 bool autoU = autoUpdate();
685 setAutoUpdate(FALSE);
686 QStack<KTreeViewItem> stack;
687 stack.push(treeRoot);
688 while(!stack.isEmpty()) {
689 KTreeViewItem *item = stack.pop();
690 if(item->hasChild()) {
691 stack.push(item);
692 stack.push(item->getChild());
694 else if(item->hasSibling()) {
695 stack.push(item);
696 stack.push(item->getSibling());
698 else if(item->getParent() != 0) {
699 takeItem(item);
700 delete item;
703 clearing = FALSE;
704 if(goingDown || QApplication::closingDown())
705 return;
706 setAutoUpdate(autoU);
707 if(autoU && isVisible())
708 repaint();
711 // return a count of all the items in the tree, whether visible or not
712 uint KTreeView::count()
714 int total = 0;
715 forEveryItem(&KTreeView::countItem, (void *)&total);
716 return total;
719 // returns the index of the current (highlighted) item
720 int KTreeView::currentItem() const
722 return current;
725 // collapses the item at the specified row index.
726 void KTreeView::collapseItem(int index, bool emitSignal)
728 KTreeViewItem* item = itemAt(index);
729 if (item)
730 collapseSubTree(item, emitSignal);
733 // expands the item at the specified row indes.
734 void KTreeView::expandItem(int index, bool emitSignal)
736 KTreeViewItem* item = itemAt(index);
737 if (item)
738 expandSubTree(item, emitSignal);
741 // returns the depth the tree is automatically expanded to when
742 // items are added
743 int KTreeView::expandLevel() const
745 return expansion;
748 // visits every item in the tree, visible or not and applies
749 // the user supplied function with the item and user data passed as parameters
750 // if user supplied function returns true, traversal ends and function returns
751 bool KTreeView::forEveryItem(KForEveryFunc func, void* user, KTreeViewItem* item)
753 if (item == 0) {
754 item = treeRoot;
756 assert(item->owner == this);
757 item = item->getChild();
759 while (item != 0) {
760 // visit the siblings
761 if ((*func)(item, user)) {
762 return true;
764 // visit the children (recursively)
765 if (item->hasChild()) {
766 if (forEveryItem(func, user, item))
767 return true;
769 item = item->getSibling();
771 return false;
774 // visits every visible item in the tree in order and applies the
775 // user supplied function with the item and user data passed as parameters
776 // if user supplied function returns TRUE, traversal ends and function
777 // returns
778 bool KTreeView::forEveryVisibleItem(KForEveryFunc func, void *user,
779 KTreeViewItem* item)
781 if (item == 0) {
782 item = treeRoot;
783 } else {
784 // children are invisible (hence, nothing to do)
785 // if item is invisible or collapsed
786 if (!item->isVisible() || !item->isExpanded()) {
787 return false;
790 assert(item->owner == this);
791 item = item->getChild();
793 while (item != 0) {
794 // visit the siblings
795 if ((*func)(item, user)) {
796 return true;
798 // visit the children (recursively)
799 if (item->hasChild() && item->isExpanded()) {
800 if (forEveryVisibleItem(func, user, item))
801 return true;
803 item = item->getSibling();
805 return false;
808 // returns a pointer to the KTreeViewItem at the current index
809 // or 0 if no current item
810 KTreeViewItem *KTreeView::getCurrentItem() const
812 if(current == -1) return 0;
813 return itemAt(current);
816 // returns the current indent spacing
817 int KTreeView::indentSpacing()
819 return itemIndent;
822 // inserts a new item with the specified text and pixmap before
823 // or after the item at the given index, depending on the value
824 // of prefix
825 // if index is negative, appends item to tree at root level
826 bool KTreeView::insertItem(const char* theText, const QPixmap& thePixmap,
827 int row, bool prefix)
829 KTreeViewItem* refItem = itemAt(row);
831 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
833 bool success = insertItem(refItem, item, prefix);
834 if (!success)
835 delete item;
836 return success;
839 // inserts a new item with the specified text and pixmap before
840 // or after the item at the end of the given path, depending on the value
841 // of prefix
842 bool KTreeView::insertItem(const char* theText, const QPixmap& thePixmap,
843 const KPath& path, bool prefix)
845 KTreeViewItem* refItem = itemAt(path);
847 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
849 bool success = insertItem(refItem, item, prefix);
850 if (!success)
851 delete item;
852 return success;
855 // inserts the given item or derived object into the tree before
856 // or after the item at the given index, depending on the value
857 // of prefix
858 // if index is negative, appends item to tree at root level
859 bool KTreeView::insertItem(KTreeViewItem* newItem,
860 int index, bool prefix)
862 // find the item currently at the index, if there is one
863 KTreeViewItem* refItem = itemAt(index);
865 // insert new item at the appropriate place
866 return insertItem(refItem, newItem, prefix);
869 // inserts the given item or derived object into the tree before
870 // or after the item at the end of the given path, depending on the value
871 // of prefix
872 bool KTreeView::insertItem(KTreeViewItem* newItem,
873 const KPath& thePath, bool prefix)
875 // find the item currently at the end of the path, if there is one
876 KTreeViewItem* refItem = itemAt(thePath);
878 // insert new item at appropriate place
879 return insertItem(refItem, newItem, prefix);
883 * returns pointer to KTreeViewItem at the specifed row or 0 if row is out
884 * of limits.
886 KTreeViewItem* KTreeView::itemAt(int row) const
888 if (row < 0 || row >= numRows()) {
889 return 0;
891 else {
892 // lookup the item in the list of visible items
893 assert(row < itemCapacity);
894 KTreeViewItem* i = visibleItems[row];
895 assert(i != 0);
896 return i;
900 // returns pointer to KTreeViewItem at the end of the
901 // path or 0 if not found
902 KTreeViewItem* KTreeView::itemAt(const KPath& path)
904 if (path.isEmpty())
905 return 0;
907 // need a copy of the path because recursiveFind will destroy it
908 KPath pathCopy = path;
910 return recursiveFind(pathCopy);
913 // computes the path of the item at the specified index
914 // if index is invalid, nothing is done.
915 void KTreeView::itemPath(int row, KPath& path)
917 KTreeViewItem* item = itemAt(row);
918 if (item != 0) {
919 itemPath(item, path);
923 // returns the row in the visible tree of the given item or
924 // -1 if not found
925 int KTreeView::itemRow(KTreeViewItem* item)
927 if (item->owner == this) {
928 // search in list of visible items
929 for (int i = numRows()-1; i >= 0; i--) {
930 if (visibleItems[i] == item) {
931 return i;
935 // not found
936 return -1;
940 * move the subtree at the specified index up one branch level (make root
941 * item a sibling of its current parent)
943 void KTreeView::join(int index)
945 KTreeViewItem *item = itemAt(index);
946 if(item)
947 join(item);
951 * move the subtree at the specified index up one branch level (make root
952 * item a sibling of it's current parent)
954 void KTreeView::join(const KPath& path)
956 KTreeViewItem *item = itemAt(path);
957 if (item)
958 join(item);
961 /* move item at specified index one slot down in its parent's sub tree */
962 void KTreeView::lowerItem(int index)
964 KTreeViewItem *item = itemAt(index);
965 if(item)
966 lowerItem(item);
969 /* move item at specified path one slot down in its parent's sub tree */
970 void KTreeView::lowerItem(const KPath& path)
972 KTreeViewItem* item = itemAt(path);
973 if (item)
974 lowerItem(item);
977 /* move item at specified index one slot up in its parent's sub tree */
978 void KTreeView::raiseItem(int index)
980 KTreeViewItem* item = itemAt(index);
981 if (item)
982 raiseItem(item);
985 /* move item at specified path one slot up in its parent's sub tree */
986 void KTreeView::raiseItem(const KPath& path)
988 KTreeViewItem* item = itemAt(path);
989 if (item)
990 raiseItem(item);
993 // remove the item at the specified index and delete it
994 void KTreeView::removeItem(int index)
996 KTreeViewItem *item = itemAt(index);
997 if(item) {
998 takeItem(item);
999 delete item;
1003 // remove the item at the end of the specified path and delete it
1004 void KTreeView::removeItem(const KPath& thePath)
1006 KTreeViewItem* item = itemAt(thePath);
1007 if (item) {
1008 takeItem(item);
1009 delete item;
1013 void KTreeView::scrollVisible(KTreeViewItem* item, bool children)
1015 if (item == 0)
1016 return;
1017 int row = itemRow(item);
1018 if (row < 0)
1019 return; /* do nothing if invisible */
1021 if (children && item->isExpanded()) {
1022 // we are concerned about children
1023 if (!rowIsVisible(row)) {
1024 // just move to the top
1025 setTopCell(row);
1026 } else {
1027 // this is the complicated part
1028 // count the visible children (including grandchildren)
1029 int numVisible = 0;
1030 forEveryVisibleItem(countItem, &numVisible, item);
1031 // if the last child is visible, do nothing
1032 if (rowIsVisible(row + numVisible))
1033 return;
1035 * Basically, item will become the top cell; but if there are
1036 * more visible rows in the widget than we have children, then
1037 * we don't move that far.
1039 int remain = lastRowVisible()-topCell()-numVisible;
1040 if (remain <= 0) {
1041 setTopCell(row);
1042 } else {
1043 setTopCell(QMAX(0,row-remain));
1046 } else {
1047 // we are not concerned about children
1048 if (rowIsVisible(row))
1049 return;
1050 // just move the item to the top
1051 setTopCell(row);
1055 // sets the current item and hightlights it, emitting signals
1056 void KTreeView::setCurrentItem(int row)
1058 if (row == current)
1059 return;
1060 int numVisible = numRows();
1061 if (row > numVisible)
1062 return;
1063 int oldCurrent = current;
1064 current = row;
1065 if (oldCurrent < numVisible)
1066 updateCell(oldCurrent, 0);
1067 if (current > -1) {
1068 updateCell(current, 0, false);
1069 emit highlighted(current);
1073 // enables/disables drawing of expand button
1074 void KTreeView::setExpandButtonDrawing(bool enable)
1076 if (drawExpandButton == enable)
1077 return;
1078 drawExpandButton = enable;
1080 // the user parameter is cast to a bool in setItemExpandButtonDrawing
1081 forEveryItem(&KTreeView::setItemExpandButtonDrawing, enable ? &enable : 0);
1083 if (autoUpdate() && isVisible())
1084 repaint();
1087 // sets depth to which subtrees are automatically expanded, and
1088 // redraws tree if auto update enabled
1089 void KTreeView::setExpandLevel(int level)
1091 if (expansion == level)
1092 return;
1093 expansion = level;
1094 KTreeViewItem* item = getCurrentItem();
1095 forEveryItem(&KTreeView::setItemExpandLevel, 0);
1096 while (item != 0) {
1097 if (item->getParent()->isExpanded())
1098 break;
1099 item = item->getParent();
1101 if (item != 0)
1102 setCurrentItem(itemRow(item));
1103 if (autoUpdate() && isVisible())
1104 repaint();
1107 // sets the indent margin for all branches and repaints if auto update enabled
1108 void KTreeView::setIndentSpacing(int spacing)
1110 if (itemIndent == spacing)
1111 return;
1112 itemIndent = spacing;
1113 updateCellWidth();
1114 if (autoUpdate() && isVisible())
1115 repaint();
1118 void KTreeView::setMoveCurrentToSibling(bool m)
1120 moveCurrentToSibling = m;
1123 // enables/disables display of item text (keys)
1124 void KTreeView::setShowItemText(bool enable)
1126 if (showText == enable)
1127 return;
1128 showText = enable;
1130 // the user parameter is cast to a bool in setItemShowText
1131 forEveryItem(&KTreeView::setItemShowText, enable ? &enable : 0);
1133 if (autoUpdate() && isVisible())
1134 repaint();
1137 // enables/disables tree branch drawing
1138 void KTreeView::setTreeDrawing(bool enable)
1140 if (drawTree == enable)
1141 return;
1142 drawTree = enable;
1144 // the user parameter is cast to a bool in setItemTreeDrawing
1145 forEveryItem(&KTreeView::setItemTreeDrawing, enable ? &enable : 0);
1147 if (autoUpdate() && isVisible())
1148 repaint();
1151 // indicates whether item text keys are displayed
1152 bool KTreeView::showItemText() const
1154 return showText;
1157 // indents the item at the given index, splitting the tree into
1158 // a new branch
1159 void KTreeView::split(int index)
1161 KTreeViewItem *item = itemAt(index);
1162 if(item)
1163 split(item);
1166 // indents the item at the given path, splitting the tree into
1167 // a new branch
1168 void KTreeView::split(const KPath& path)
1170 KTreeViewItem* item = itemAt(path);
1171 if (item)
1172 split(item);
1175 // removes item at specified index from tree but does not delete it
1176 // returns pointer to the item or 0 if not succesful
1177 KTreeViewItem *KTreeView::takeItem(int index)
1179 KTreeViewItem *item = itemAt(index);
1180 if(item)
1181 takeItem(item);
1182 return item;
1185 // removes item at specified path from tree but does not delete it
1186 // returns pointer to the item or 0 if not successful
1187 KTreeViewItem* KTreeView::takeItem(const KPath& path)
1189 KTreeViewItem* item = itemAt(path);
1190 if (item)
1191 takeItem(item);
1192 return item;
1195 // indicates whether tree branches are drawn
1196 bool KTreeView::treeDrawing() const
1198 return drawTree;
1202 // appends a child to the specified parent item (note: a child, not a sibling, is added!)
1203 void KTreeView::appendChildItem(KTreeViewItem* theParent,
1204 KTreeViewItem* newItem)
1206 theParent->appendChild(newItem);
1208 // set item state
1209 newItem->setDrawExpandButton(drawExpandButton);
1210 newItem->setDrawTree(drawTree);
1211 newItem->setDrawText(showText);
1212 if (level(newItem) < expansion) {
1213 newItem->setExpanded(true);
1216 // fix up branch levels of any children that the new item may already have
1217 if(newItem->hasChild()) {
1218 fixChildren(newItem);
1221 // if necessary, adjust cell width, number of rows and repaint
1222 if (newItem->isVisible() || theParent->childCount() == 1) {
1223 bool autoU = autoUpdate();
1224 setAutoUpdate(false);
1225 updateVisibleItems();
1226 setAutoUpdate(autoU);
1227 if (autoU && isVisible())
1228 repaint();
1232 // returns the height of the cell(row) at the specified row (index)
1233 int KTreeView::cellHeight(int row) const
1235 return itemAt(row)->height(fontMetrics());
1238 // returns the width of the cells. Note: this is mostly for derived classes
1239 // which have more than 1 column
1240 int KTreeView::cellWidth(int /*col*/) const
1242 return maxItemWidth;
1245 // changes the given item with the new text and/or pixmap
1246 void KTreeView::changeItem(KTreeViewItem* toChange, int itemRow,
1247 const char* newText, const QPixmap* newPixmap)
1249 int indent = indentation(toChange);
1250 int oldWidth = toChange->width(indent);
1251 if(newText)
1252 toChange->setText(newText);
1253 if (newPixmap)
1254 toChange->setPixmap(*newPixmap);
1255 if(oldWidth != toChange->width(indent))
1256 updateCellWidth();
1257 if(itemRow == -1)
1258 return;
1259 if(autoUpdate() && rowIsVisible(itemRow))
1260 updateCell(itemRow, 0);
1263 // collapses the subtree at the specified item
1264 void KTreeView::collapseSubTree(KTreeViewItem* subRoot, bool emitSignal)
1266 assert(subRoot->owner == this);
1267 if (!subRoot->isExpanded())
1268 return;
1270 // must move the current item if it is visible
1271 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1273 subRoot->setExpanded(false);
1274 if (subRoot->isVisible()) {
1275 bool autoU = autoUpdate();
1276 setAutoUpdate(false);
1277 updateVisibleItems();
1278 // re-seat current item
1279 if (cur != 0) {
1280 setCurrentItem(itemRow(cur));
1282 if (emitSignal) {
1283 emit collapsed(itemRow(subRoot));
1285 setAutoUpdate(autoU);
1286 if (autoU && isVisible())
1287 repaint();
1291 // used by count() with forEach() function to count total number
1292 // of items in the tree
1293 bool KTreeView::countItem(KTreeViewItem*, void* total)
1295 int* t = static_cast<int*>(total);
1296 (*t)++;
1297 return false;
1300 // expands the subtree at the given item
1301 void KTreeView::expandSubTree(KTreeViewItem* subRoot, bool emitSignal)
1303 assert(subRoot->owner == this);
1304 if (subRoot->isExpanded())
1305 return;
1307 // must move the current item if it is visible
1308 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1310 bool allow = true;
1312 if (subRoot->delayedExpanding) {
1313 emit expanding(subRoot, allow);
1315 if (!allow)
1316 return;
1318 subRoot->setExpanded(true);
1320 if (subRoot->isVisible()) {
1321 bool autoU = autoUpdate();
1322 setAutoUpdate(false);
1323 updateVisibleItems();
1324 // re-seat current item
1325 if (cur != 0) {
1326 setCurrentItem(itemRow(cur));
1328 if (emitSignal) {
1329 emit expanded(itemRow(subRoot));
1331 setAutoUpdate(autoU);
1332 if (autoU && isVisible())
1333 repaint();
1337 // fix up branch levels out of whack from split/join operations on the tree
1338 void KTreeView::fixChildren(KTreeViewItem *parentItem)
1340 KTreeViewItem* childItem = 0;
1341 KTreeViewItem* siblingItem = 0;
1342 // int childBranch = parentItem->getBranch() + 1;
1343 if(parentItem->hasChild()) {
1344 childItem = parentItem->getChild();
1345 // childItem->setBranch(childBranch);
1346 childItem->owner = parentItem->owner;
1347 fixChildren(childItem);
1349 while(childItem && childItem->hasSibling()) {
1350 siblingItem = childItem->getSibling();
1351 // siblingItem->setBranch(childBranch);
1352 siblingItem->owner = parentItem->owner;
1353 fixChildren(siblingItem);
1354 childItem = siblingItem;
1359 * Handle QFocusEvent processing by setting current item to top row if
1360 * there is no current item, and updates cell to add or delete the focus
1361 * rectangle on the highlight bar. The base class is not called because it
1362 * does a repaint() which causes flicker.
1364 void KTreeView::focusInEvent(QFocusEvent *)
1366 if (current < 0 && numRows() > 0)
1367 setCurrentItem(topCell());
1368 updateCell(current, 0);
1371 void KTreeView::focusOutEvent(QFocusEvent *)
1373 updateCell(current, 0);
1376 // called by updateCellWidth() for each item in the visible list
1377 bool KTreeView::getMaxItemWidth(KTreeViewItem* item, void* user)
1379 assert(item->owner != 0);
1380 int indent = item->owner->indentation(item);
1381 int* maxW = static_cast<int*>(user);
1382 int w = item->width(indent);
1383 if (w > *maxW)
1384 *maxW = w;
1385 return false;
1388 int KTreeView::indentation(KTreeViewItem* item) const
1390 return level(item) * itemIndent + itemIndent + 3;
1393 // inserts the new item before or after the reference item, depending
1394 // on the value of prefix
1395 bool KTreeView::insertItem(KTreeViewItem* referenceItem,
1396 KTreeViewItem* newItem,
1397 bool prefix)
1399 assert(newItem != 0);
1400 assert(referenceItem == 0 || referenceItem->owner == this);
1402 /* set the new item's state */
1403 newItem->setDrawExpandButton(drawExpandButton);
1404 newItem->setDrawTree(drawTree);
1405 newItem->setDrawText(showText);
1406 KTreeViewItem* parentItem;
1407 if (referenceItem) {
1408 parentItem = referenceItem->getParent();
1409 int insertIndex = parentItem->childIndex(referenceItem);
1410 if (!prefix)
1411 insertIndex++;
1412 parentItem->insertChild(insertIndex, newItem);
1414 else {
1415 // no reference item, append at end of visible tree
1416 // need to repaint
1417 parentItem = treeRoot;
1418 parentItem->appendChild(newItem);
1421 // set item expansion
1422 if (level(newItem) < expansion)
1423 newItem->setExpanded(true);
1425 // fix up branch levels of any children
1426 if (newItem->hasChild())
1427 fixChildren(newItem);
1429 // if repaint necessary, do it if visible and auto update
1430 // enabled
1431 if (newItem->isVisible() || parentItem->childCount() == 1) {
1432 bool autoU = autoUpdate();
1433 setAutoUpdate(FALSE);
1434 updateVisibleItems();
1435 setAutoUpdate(autoU);
1436 if (autoU && isVisible())
1437 repaint();
1439 return true;
1443 * returns pointer to item's path
1445 void KTreeView::itemPath(KTreeViewItem* item, KPath& path) const
1447 assert(item != 0);
1448 assert(item->owner == this);
1449 if (item != treeRoot) {
1450 itemPath(item->getParent(), path);
1451 path.push(item->getText());
1456 * joins the item's branch into the tree, making the item a sibling of its
1457 * parent
1459 void KTreeView::join(KTreeViewItem *item)
1461 KTreeViewItem *itemParent = item->getParent();
1462 if(itemParent->hasParent()) {
1463 bool autoU = autoUpdate();
1464 setAutoUpdate(FALSE);
1465 takeItem(item);
1466 insertItem(itemParent, item, FALSE);
1467 setAutoUpdate(autoU);
1468 if(autoU && isVisible())
1469 repaint();
1473 // handles keyboard interface
1474 void KTreeView::keyPressEvent(QKeyEvent* e)
1476 if (numRows() == 0)
1477 return; /* nothing to do */
1479 /* if there's no current item, make the top item current */
1480 if (currentItem() < 0)
1481 setCurrentItem(topCell());
1482 assert(currentItem() >= 0); /* we need a current item */
1483 assert(itemAt(currentItem()) != 0); /* we really need a current item */
1485 // give currentItem a chance to handle the event
1486 if (itemAt(currentItem())->keyEvent(e))
1487 return; /* handled */
1489 int pageSize, delta;
1490 KTreeViewItem* item;
1491 int key = e->key();
1492 repeat:
1493 switch (key) {
1494 case Key_Up:
1495 // make previous item current, scroll up if necessary
1496 if (currentItem() > 0) {
1497 setCurrentItem(currentItem() - 1);
1498 scrollVisible(itemAt(currentItem()), false);
1500 break;
1501 case Key_Down:
1502 // make next item current, scroll down if necessary
1503 if (currentItem() < numRows() - 1) {
1504 setCurrentItem(currentItem() + 1);
1505 if (currentItem() > lastRowVisible()) {
1506 // scrollVisible is not feasible here because
1507 // it scrolls the item to the top
1508 setTopCell(topCell() + currentItem() - lastRowVisible());
1509 } else if (currentItem() < topCell()) {
1510 setTopCell(currentItem());
1513 break;
1514 case Key_Next:
1515 // move highlight one page down and scroll down
1516 delta = currentItem() - topCell();
1517 pageSize = lastRowVisible() - topCell();
1518 setTopCell(QMIN(topCell() + pageSize, numRows() - 1));
1519 setCurrentItem(QMIN(topCell() + delta, numRows() - 1));
1520 break;
1521 case Key_Prior:
1522 // move highlight one page up and scroll up
1523 delta = currentItem() - topCell();
1524 pageSize = lastRowVisible() - topCell();
1525 setTopCell(QMAX(topCell() - pageSize, 0));
1526 setCurrentItem(QMAX(topCell() + delta, 0));
1527 break;
1528 case Key_Plus:
1529 case Key_Right:
1530 // if current item has subtree and is collapsed, expand it
1531 item = itemAt(currentItem());
1532 if (item->isExpanded() && item->hasChild() && key == Key_Right) {
1533 // going right on an expanded item is like going down
1534 key = Key_Down;
1535 goto repeat;
1536 } else {
1537 expandSubTree(item, true);
1538 scrollVisible(item, true);
1540 break;
1541 case Key_Minus:
1542 case Key_Left:
1543 // if current item has subtree and is expanded, collapse it
1544 item = itemAt(currentItem());
1545 if ((!item->isExpanded() || !item->hasChild()) && key == Key_Left) {
1546 // going left on a collapsed item goes to its parent
1547 item = item->getParent();
1548 if (item == treeRoot)
1549 break; /* we're already at the top */
1550 assert(item->isVisible());
1551 setCurrentItem(itemRow(item));
1552 } else {
1553 collapseSubTree(item, true);
1555 scrollVisible(item, false);
1556 break;
1557 case Key_Return:
1558 case Key_Enter:
1559 // select the current item
1560 if (currentItem() >= 0)
1561 emit selected(currentItem());
1562 break;
1563 default:
1564 break;
1568 // handles keyboard interface
1569 void KTreeView::keyReleaseEvent(QKeyEvent* e)
1571 if (currentItem() >= 0 && itemAt(currentItem()) != 0)
1572 itemAt(currentItem())->keyEvent(e);
1575 int KTreeView::level(KTreeViewItem* item) const
1577 assert(item != 0);
1578 assert(item->owner == this);
1579 assert(item != treeRoot);
1580 int l = 0;
1581 item = item->parent->parent; /* since item != treeRoot, there is a parent */
1582 while (item != 0) {
1583 item = item->parent;
1584 l++;
1586 return l;
1589 /* move specified item down one slot in parent's subtree */
1590 void KTreeView::lowerItem(KTreeViewItem *item)
1592 KTreeViewItem *itemParent = item->getParent();
1593 uint itemChildIndex = itemParent->childIndex(item);
1594 if(itemChildIndex < itemParent->childCount() - 1) {
1595 bool autoU = autoUpdate();
1596 setAutoUpdate(FALSE);
1597 takeItem(item);
1598 insertItem(itemParent->childAt(itemChildIndex), item, FALSE);
1599 setAutoUpdate(autoU);
1600 if(autoU && isVisible())
1601 repaint();
1605 // handle mouse double click events by selecting the clicked item
1606 // and emitting the signal
1607 void KTreeView::mouseDoubleClickEvent(QMouseEvent* e)
1609 // find out which row has been clicked
1610 int itemClicked = findRow(e->y());
1612 if (itemClicked < 0)
1613 return; /* invalid row, do nothing */
1615 KTreeViewItem* item = itemAt(itemClicked);
1616 if (item == 0)
1617 return;
1619 // translate mouse coord to cell coord
1620 QPoint cellCoord = cellCoords(itemClicked, e->pos());
1622 // first ask item
1623 if (item->mouseEvent(e, cellCoord))
1624 return;
1626 // hit test item
1627 int indent = indentation(item);
1628 if (item->boundingRect(indent).contains(cellCoord))
1629 emit selected(itemClicked);
1632 // handle mouse movement events
1633 void KTreeView::mouseMoveEvent(QMouseEvent* e)
1635 // in rubberband_mode we actually scroll the window now
1636 if (rubberband_mode) {
1637 move_rubberband(e->pos());
1638 } else {
1639 // else forward to current item
1640 int current = currentItem();
1641 if (current >= 0 && itemAt(current))
1642 itemAt(current)->mouseEvent(e, cellCoords(current, e->pos()));
1647 // handle single mouse presses
1648 void KTreeView::mousePressEvent(QMouseEvent* e)
1650 /* first: cancel rubberbanding if it's on */
1651 if (rubberband_mode)
1653 // another button was pressed while rubberbanding, stop the move.
1654 // RB: if we allow other buttons while rubberbanding the tree can expand
1655 // while rubberbanding - we then need to recalculate and resize the
1656 // rubberband rect and show the new size
1657 end_rubberband();
1658 return;
1661 // find out which row has been clicked
1662 int itemClicked = findRow(e->y());
1664 // nothing to do if not on valid row
1665 if (itemClicked < 0)
1666 return;
1667 KTreeViewItem* item = itemAt(itemClicked);
1668 if (!item)
1669 return;
1671 // translate mouse coord to cell coord
1672 QPoint cellCoord = cellCoords(itemClicked, e->pos());
1674 // give the item a crack
1675 if (item->mouseEvent(e, cellCoord))
1676 return; /* event eaten by item */
1678 // check for rubberbanding
1679 if (e->button() == MidButton)
1681 // RB: the MMB is hardcoded to the "rubberband" scroll mode
1682 if (!rubberband_mode) {
1683 start_rubberband(e->pos());
1685 return;
1688 if (e->button() == RightButton) {
1689 emit rightPressed(itemClicked, e->pos());
1691 /* hit test expand button (doesn't set currentItem) */
1692 else if (item->expandButtonClicked(cellCoord)) {
1693 if (item->isExpanded()) {
1694 collapseSubTree(item, true);
1695 } else {
1696 expandSubTree(item, true);
1697 scrollVisible(item, true); /* make children visible */
1700 // hit test item
1701 else if (item->boundingRect(indentation(item)).contains(cellCoord)) {
1702 setCurrentItem(itemClicked);
1706 // handle mouse release events
1707 void KTreeView::mouseReleaseEvent(QMouseEvent *e)
1709 /* if it's the MMB end rubberbanding */
1710 if (rubberband_mode) {
1711 if (e->button() == MidButton)
1712 end_rubberband();
1713 return;
1715 // forward to current item
1716 int current = currentItem();
1717 if (current >= 0 && itemAt(current))
1718 itemAt(current)->mouseEvent(e, cellCoords(current, e->pos()));
1721 // rubberband move: draw the rubberband
1722 void KTreeView::draw_rubberband()
1725 * RB: I'm using a white pen because of the XorROP mode. I would prefer
1726 * to draw the rectangle in red but I don't now how to get a pen which
1727 * draws red in XorROP mode (this depends on the background). In fact
1728 * the color should be configurable.
1731 if (!rubberband_mode) return;
1732 QPainter paint(this);
1733 paint.setPen(white);
1734 paint.setRasterOp(XorROP);
1735 paint.drawRect(xOffset()*viewWidth()/totalWidth(),
1736 yOffset()*viewHeight()/totalHeight(),
1737 rubber_width+1, rubber_height+1);
1738 paint.end();
1741 // rubberband move: start move
1742 void KTreeView::start_rubberband(const QPoint& where)
1744 if (rubberband_mode) { // Oops!
1745 end_rubberband();
1747 /* RB: Don't now, if this check is necessary */
1748 if (!viewWidth() || !viewHeight()) return;
1749 if (!totalWidth() || !totalHeight()) return;
1751 // calculate the size of the rubberband rectangle
1752 rubber_width = viewWidth()*viewWidth()/totalWidth();
1753 if (rubber_width > viewWidth()) rubber_width = viewWidth();
1754 rubber_height = viewHeight()*viewHeight()/totalHeight();
1755 if (rubber_height > viewHeight()) rubber_height = viewHeight();
1757 // remember the cursor position and the actual offset
1758 rubber_startMouse = where;
1759 rubber_startX = xOffset();
1760 rubber_startY = yOffset();
1761 rubberband_mode=TRUE;
1762 draw_rubberband();
1765 // rubberband move: end move
1766 void KTreeView::end_rubberband()
1768 if (!rubberband_mode) return;
1769 draw_rubberband();
1770 rubberband_mode = FALSE;
1773 // rubberband move: hanlde mouse moves
1774 void KTreeView::move_rubberband(const QPoint& where)
1776 if (!rubberband_mode) return;
1778 // look how much the mouse moved and calc the new scroll position
1779 QPoint delta = where - rubber_startMouse;
1780 int nx = rubber_startX + delta.x() * totalWidth() / viewWidth();
1781 int ny = rubber_startY + delta.y() * totalHeight() / viewHeight();
1783 // check the new position (and make it valid)
1784 if (nx < 0) nx = 0;
1785 else if (nx > maxXOffset()) nx = maxXOffset();
1786 if (ny < 0) ny = 0;
1787 else if (ny > maxYOffset()) ny = maxYOffset();
1789 // redraw the rubberband at the new position
1790 draw_rubberband();
1791 setOffset(nx,ny);
1792 draw_rubberband();
1796 // paints the cell at the specified row and col
1797 // col is ignored for now since there is only one
1798 void KTreeView::paintCell(QPainter* p, int row, int)
1800 KTreeViewItem* item = itemAt(row);
1801 if (item == 0)
1802 return;
1804 QColorGroup cg = colorGroup();
1805 int indent = indentation(item);
1806 item->paint(p, indent, cg,
1807 current == row); /* highlighted */
1811 /* This is needed to make the kcontrol's color setup working (Marcin Dalecki) */
1812 void KTreeView::paletteChange(const QPalette &)
1814 setBackgroundColor(colorGroup().base());
1815 repaint(true);
1819 /* raise the specified item up one slot in parent's subtree */
1820 void KTreeView::raiseItem(KTreeViewItem *item)
1822 KTreeViewItem *itemParent = item->getParent();
1823 int itemChildIndex = itemParent->childIndex(item);
1824 if(itemChildIndex > 0) {
1825 bool autoU = autoUpdate();
1826 setAutoUpdate(FALSE);
1827 takeItem(item);
1828 insertItem(itemParent->childAt(--itemChildIndex), item, TRUE);
1829 setAutoUpdate(autoU);
1830 if(autoU && isVisible())
1831 repaint();
1835 // find the item at the path
1836 KTreeViewItem* KTreeView::recursiveFind(KPath& path)
1838 if (path.isEmpty())
1839 return treeRoot;
1841 // get the next key
1842 QString searchString = path.pop();
1844 // find the parent item
1845 KTreeViewItem* parent = recursiveFind(path);
1846 if (parent == 0)
1847 return 0;
1850 * Iterate through the parent's children searching for searchString.
1852 KTreeViewItem* sibling = parent->getChild();
1853 while (sibling != 0) {
1854 if (searchString == sibling->getText()) {
1855 break; /* found it! */
1857 sibling = sibling->getSibling();
1859 return sibling;
1862 void KTreeView::setItemExpanded(KTreeViewItem* item)
1864 if (level(item) < expansion) {
1865 expandSubTree(item, true);
1866 } else {
1867 collapseSubTree(item, true);
1871 // called by setExpandLevel for each item in tree
1872 bool KTreeView::setItemExpandLevel(KTreeViewItem* item, void*)
1874 assert(item->owner != 0);
1875 item->owner->setItemExpanded(item);
1876 return false;
1879 // called by setExpandButtonDrawing for every item in tree
1880 // the parameter drawButton is used as (and implicitly cast to) a bool
1881 bool KTreeView::setItemExpandButtonDrawing(KTreeViewItem* item,
1882 void* drawButton)
1884 item->setDrawExpandButton(drawButton);
1885 return false;
1888 // called by setShowItemText for every item in tree
1889 // the parameter newDrawText is used as (and implicitly cast to) a bool
1890 bool KTreeView::setItemShowText(KTreeViewItem* item,
1891 void* newDrawText)
1893 item->setDrawText(newDrawText);
1894 return false;
1897 // called by setTreeDrawing for every item in tree
1898 // the parameter drawTree is used as (and implicitly cast to) a bool
1899 bool KTreeView::setItemTreeDrawing(KTreeViewItem* item, void* drawTree)
1901 item->setDrawTree(drawTree);
1902 return false;
1905 // makes the item a child of the item above it, splitting
1906 // the tree into a new branch
1907 void KTreeView::split(KTreeViewItem *item)
1909 KTreeViewItem *itemParent = item->getParent();
1910 int itemChildIndex = itemParent->childIndex(item);
1911 if(itemChildIndex == 0)
1912 return;
1913 bool autoU = autoUpdate();
1914 setAutoUpdate(FALSE);
1915 takeItem(item);
1916 appendChildItem(itemParent->childAt(--itemChildIndex), item);
1917 setAutoUpdate(autoU);
1918 if(autoU && isVisible())
1919 repaint();
1922 // removes the item from the tree without deleting it
1923 void KTreeView::takeItem(KTreeViewItem* item)
1925 assert(item->owner == this);
1927 // TODO: go over this function again
1929 bool wasVisible = item->isVisible();
1931 * If we have a current item, make sure that it is not in the subtree
1932 * that we are about to remove. If the current item is in the part
1933 * below the taken-out subtree, we must move it up a number of rows if
1934 * the taken-out subtree is at least partially visible.
1936 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1937 KTreeViewItem* oldCurrent = cur;
1938 if (wasVisible && cur != 0) {
1939 KTreeViewItem* c = cur;
1940 while (c != 0 && c != item) {
1941 c = c->getParent();
1943 if (c != 0) {
1944 // move current item to parent
1945 cur = item->getParent();
1946 if (cur == treeRoot) {
1947 cur = 0;
1948 // move it to next or previous sibling
1949 if (moveCurrentToSibling) {
1950 if (item->getSibling() != 0) {
1951 cur = item->getSibling();
1952 } else if (treeRoot->getChild() != item) {
1953 cur = treeRoot->getChild();
1954 while (cur != 0 && cur->getSibling() != item)
1955 cur = cur->getSibling();
1961 KTreeViewItem* parentItem = item->getParent();
1962 parentItem->removeChild(item);
1963 item->sibling = 0;
1964 if (wasVisible || parentItem->childCount() == 0) {
1965 bool autoU = autoUpdate();
1966 setAutoUpdate(FALSE);
1967 updateVisibleItems();
1968 setAutoUpdate(autoU);
1969 if (autoU && isVisible())
1970 repaint();
1973 // re-seat the current item
1974 // row changes if cur is below the taken item
1975 current = cur != 0 ? itemRow(cur) : -1;
1976 // signal must be emitted only if item really changed
1977 if (cur != oldCurrent) {
1978 updateCell(current, 0, false);
1979 emit highlighted(current);
1983 // visits each item, calculates the maximum width
1984 // and updates TableView
1985 void KTreeView::updateCellWidth()
1987 // make cells at least 1 pixel wide to avoid singularities (division by zero)
1988 int maxW = 1;
1989 forEveryVisibleItem(&KTreeView::getMaxItemWidth, &maxW);
1990 maxItemWidth = maxW;
1991 updateTableSize();
1993 // correct offsets
1994 int xoff = xOffset();
1995 int yoff = yOffset();
1996 if (xoff > maxXOffset()) xoff = maxXOffset();
1997 if (yoff > maxYOffset()) yoff = maxYOffset();
1999 setOffset(xoff, yoff);
2002 void KTreeView::updateVisibleItems()
2004 int index = 0;
2005 int count = 0;
2006 updateVisibleItemRec(treeRoot, index, count);
2007 assert(index == count);
2008 setNumRows(count);
2009 updateCellWidth();
2012 void KTreeView::updateVisibleItemRec(KTreeViewItem* item, int& index, int& count)
2014 if (!item->isExpanded()) {
2015 // no visible items if not expanded
2016 return;
2020 * Record the children of item in the list of visible items.
2022 * Don't register item itself, it's already in the list. Also only
2023 * allocate new space for children.
2025 count += item->childCount();
2026 if (count > itemCapacity) {
2027 // must reallocate
2028 int newCapacity = itemCapacity;
2029 do {
2030 newCapacity += newCapacity;
2031 } while (newCapacity < count);
2032 KTreeViewItem** newItems = new KTreeViewItem*[newCapacity];
2033 // clear the unneeded space
2034 for (int i = index; i < newCapacity; i++) {
2035 newItems[i] = 0;
2037 // move already accumulated items over
2038 for (int i = index-1; i >= 0; i--) {
2039 newItems[i] = visibleItems[i];
2041 delete[] visibleItems;
2042 visibleItems = newItems;
2043 itemCapacity = newCapacity;
2045 // insert children
2046 for (KTreeViewItem* i = item->getChild(); i != 0; i = i->getSibling()) {
2047 visibleItems[index++] = i;
2048 updateVisibleItemRec(i, index, count);