Fix small memory leak by switch from QStack to QValueStack.
[kdbg.git] / kdbg / ktreeview.cpp
blobaccc8df13ffcfdb3c3c84ad63a6819855d195b98
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),
53 deleteChildren(false)
55 text = theText;
58 // constructor that takes a pixmap
59 KTreeViewItem::KTreeViewItem(const QString& theText,
60 const QPixmap& thePixmap) :
61 owner(0),
62 numChildren(0),
63 doExpandButton(true),
64 expanded(false),
65 delayedExpanding(false),
66 doTree(true),
67 doText(true),
68 child(0),
69 parent(0),
70 sibling(0),
71 deleteChildren(false)
73 text = theText;
74 pixmap = thePixmap;
77 // destructor
78 KTreeViewItem::~KTreeViewItem()
80 if (deleteChildren) {
81 // remove the children
82 KTreeViewItem* i = child;
83 while (i) {
84 KTreeViewItem* d = i;
85 i = i->getSibling();
86 delete d;
91 // appends a direct child
92 void KTreeViewItem::appendChild(KTreeViewItem* newChild)
94 newChild->parent = this;
95 newChild->setOwner(owner);
96 if (!getChild()) {
97 child = newChild;
99 else {
100 KTreeViewItem* lastChild = getChild();
101 while (lastChild->hasSibling()) {
102 lastChild = lastChild->getSibling();
104 lastChild->sibling = newChild;
106 newChild->sibling = 0;
107 numChildren++;
110 // returns the bounding rectangle of the item content (not including indent
111 // and branches) for hit testing
112 QRect KTreeViewItem::boundingRect(int indent) const
114 const QFontMetrics& fm = owner->fontMetrics();
115 int rectX = indent;
116 int rectY = 0;
117 int rectW = width(indent, fm) - rectX;
118 int rectH = height(fm);
119 return QRect(rectX, rectY, rectW, rectH);
122 // returns the child item at the specified index
123 KTreeViewItem* KTreeViewItem::childAt(int index) const
125 if (!hasChild())
126 return 0;
127 KTreeViewItem* item = getChild();
128 while (index > 0 && item != 0) {
129 item = item->getSibling();
130 index--;
132 return item;
135 // returns the number of children this item has
136 uint KTreeViewItem::childCount() const
138 return numChildren;
141 /* returns the index of the given child item in this item's branch */
142 int KTreeViewItem::childIndex(KTreeViewItem* searched) const
144 assert(searched != 0);
145 int index = 0;
146 KTreeViewItem* item = getChild();
147 while (item != 0 && item != searched) {
148 item = item->getSibling();
149 index++;
151 return item == 0 ? -1 : index;
154 // indicates whether mouse press is in expand button
155 inline bool KTreeViewItem::expandButtonClicked(const QPoint& coord) const
157 return expandButton.contains(coord);
160 // returns a pointer to first child item
161 KTreeViewItem* KTreeViewItem::getChild() const
163 return child;
166 // returns the parent of this item
167 KTreeViewItem* KTreeViewItem::getParent() const
169 return parent;
172 // returns reference to the item pixmap
173 const QPixmap& KTreeViewItem::getPixmap() const
175 return pixmap;
178 // returns the sibling below this item
179 KTreeViewItem* KTreeViewItem::getSibling() const
181 return sibling;
184 // returns a pointer to the item text
185 const QString& KTreeViewItem::getText() const
187 return text;
190 // indicates whether this item has any children
191 bool KTreeViewItem::hasChild() const
193 return child != 0;
196 // indicates whether this item has a parent
197 bool KTreeViewItem::hasParent() const
199 return parent != 0;
202 // indicates whether this item has a sibling below it
203 bool KTreeViewItem::hasSibling() const
205 return sibling != 0;
208 int KTreeViewItem::height() const
210 assert(owner != 0);
211 return height(owner->fontMetrics());
214 // returns the maximum height of text and pixmap including margins
215 int KTreeViewItem::height(const QFontMetrics& fm) const
217 int maxHeight = pixmap.height();
218 int textHeight = fm.height();
219 maxHeight = textHeight > maxHeight ? textHeight : maxHeight;
220 return maxHeight + 4;
223 // inserts child item at specified index in branch
224 void KTreeViewItem::insertChild(int index, KTreeViewItem* newChild)
226 if (index < 0) {
227 appendChild(newChild);
228 return;
230 newChild->parent = this;
231 newChild->setOwner(owner);
232 if (index == 0) {
233 newChild->sibling = getChild();
234 child = newChild;
236 else {
237 KTreeViewItem* prevItem = getChild();
238 KTreeViewItem* nextItem = prevItem->getSibling();
239 while (--index > 0 && nextItem) {
240 prevItem = nextItem;
241 nextItem = prevItem->getSibling();
243 prevItem->sibling = newChild;
244 newChild->sibling = nextItem;
246 numChildren++;
249 // indicates whether this item is displayed expanded
250 // NOTE: a TRUE response does not necessarily indicate the item
251 // has any children
252 bool KTreeViewItem::isExpanded() const
254 return expanded;
257 // returns true if all ancestors are expanded
258 bool KTreeViewItem::isVisible() const
260 for (KTreeViewItem* p = getParent(); p != 0; p = p->getParent()) {
261 if (!p->isExpanded())
262 return false;
264 return true;
267 bool KTreeViewItem::keyEvent(QKeyEvent* /*ev*/)
269 return false; /* not handled */
272 bool KTreeViewItem::mouseEvent(QMouseEvent* /*ev*/, const QPoint& /*itemCoord*/)
274 return false; /* not handled */
277 // paint this item, including tree branches and expand button
278 void KTreeViewItem::paint(QPainter* p, int indent, const QColorGroup& cg,
279 bool highlighted) const
281 assert(getParent() != 0); /* can't paint root item */
282 assert(owner != 0);
284 p->save();
286 int cellHeight = height(p->fontMetrics());
288 if (doTree) {
289 paintTree(p, indent, cellHeight, cg);
293 * If this item has at least one child and expand button drawing is
294 * enabled, set the rect for the expand button for later mouse press
295 * bounds checking, and draw the button.
297 if (doExpandButton && (child || delayedExpanding)) {
298 paintExpandButton(p, indent, cellHeight, cg);
301 // now draw the item pixmap and text, if applicable
302 int pixmapX = indent;
303 int pixmapY = (cellHeight - pixmap.height()) / 2;
304 p->drawPixmap(pixmapX, pixmapY, pixmap);
306 if (doText) {
307 paintText(p, indent, cellHeight, cg, highlighted);
309 p->restore();
312 void KTreeViewItem::paintExpandButton(QPainter* p, int indent, int cellHeight,
313 const QColorGroup& cg) const
315 int parentLeaderX = indent - (owner->itemIndent / 2);
316 int cellCenterY = cellHeight / 2;
318 QRect paintRect(parentLeaderX - 4, cellCenterY - 4, 9, 9);
319 p->setBrush(cg.base());
320 p->setPen(cg.foreground());
321 p->drawRect(paintRect);
322 if (expanded) {
323 p->drawLine(parentLeaderX - 2, cellCenterY,
324 parentLeaderX + 2, cellCenterY);
325 } else {
326 p->drawLine(parentLeaderX - 2, cellCenterY,
327 parentLeaderX + 2, cellCenterY);
328 p->drawLine(parentLeaderX, cellCenterY - 2,
329 parentLeaderX, cellCenterY + 2);
331 p->setBrush(NoBrush);
334 * The area that the user can click to collapse and expand the tree is
335 * somewhat larger than the painted expand button.
337 expandButton.setRect(indent - owner->itemIndent, 0,
338 owner->itemIndent, cellHeight);
341 // paint the highlight
342 void KTreeViewItem::paintHighlight(QPainter* p, int indent, const QColorGroup& colorGroup,
343 bool hasFocus) const
345 QColor fc;
346 fc = colorGroup.text();
347 QRect textRect = textBoundingRect(indent);
348 int t,l,b,r;
349 textRect.coords(&l, &t, &r, &b);
350 QRect outerRect;
351 outerRect.setCoords(l - 2, t - 2, r + 2, b + 2);
352 if (hasFocus) {
353 p->fillRect(textRect, fc); /* highlight background */
354 p->setPen(fc);
355 p->drawRect(outerRect);
356 } else {
357 p->fillRect(outerRect, fc); /* highlight background */
361 // draw the text, highlighted if requested
362 void KTreeViewItem::paintText(QPainter* p, int indent, int cellHeight,
363 const QColorGroup& cg, bool highlighted) const
365 int textX = indent + pixmap.width() + 3;
366 int textY = (cellHeight - p->fontMetrics().height()) / 2 +
367 p->fontMetrics().ascent();
369 if (highlighted) {
370 paintHighlight(p, indent, cg, owner->hasFocus());
371 p->setPen(cg.base());
372 p->setBackgroundColor(cg.text());
374 else {
375 p->setPen(cg.text());
376 p->setBackgroundColor(cg.base());
378 p->drawText(textX, textY, text);
381 // paint the tree structure
382 void KTreeViewItem::paintTree(QPainter* p, int indent, int cellHeight,
383 const QColorGroup& cg) const
385 int parentLeaderX = indent - (owner->itemIndent / 2);
386 int cellCenterY = cellHeight / 2;
387 int cellBottomY = cellHeight - 1;
388 int itemLeaderX = indent - 1;
390 p->setPen(cg.background());
393 * If this is not the first item in the tree draw the line up
394 * towards parent or sibling.
396 if (parent->parent != 0 || parent->getChild() != this)
397 p->drawLine(parentLeaderX, 0, parentLeaderX, cellCenterY);
399 // draw the line down towards sibling
400 if (sibling)
401 p->drawLine(parentLeaderX, cellCenterY, parentLeaderX, cellBottomY);
404 * If this item has children or siblings in the tree or is a child of
405 * an item other than the root item then draw the little line from the
406 * item out to the left.
408 if (sibling || (doExpandButton && (child || delayedExpanding)) ||
409 parent->parent != 0 ||
411 * The following handles the case of an item at the end of the
412 * topmost level which doesn't have children.
414 parent->getChild() != this)
416 p->drawLine(parentLeaderX, cellCenterY, itemLeaderX, cellCenterY);
420 * If there are siblings of ancestors below, draw our portion of the
421 * branches that extend through this cell.
423 KTreeViewItem* prevRoot = parent;
424 while (prevRoot->getParent() != 0) { /* while not root item */
425 assert(prevRoot->owner == owner);
426 if (prevRoot->hasSibling()) {
427 int sibLeaderX = owner->indentation(prevRoot) - (owner->itemIndent / 2);
428 p->drawLine(sibLeaderX, 0, sibLeaderX, cellBottomY);
430 prevRoot = prevRoot->getParent();
434 // removes the given (direct) child from the branch
435 bool KTreeViewItem::removeChild(KTreeViewItem* theChild)
437 // search item in list of children
438 KTreeViewItem* prevItem = 0;
439 KTreeViewItem* toRemove = getChild();
440 while (toRemove && toRemove != theChild) {
441 prevItem = toRemove;
442 toRemove = toRemove->getSibling();
445 if (toRemove) {
446 // found it!
447 if (prevItem == 0) {
448 child = toRemove->getSibling();
449 } else {
450 prevItem->sibling = toRemove->getSibling();
452 numChildren--;
453 toRemove->setOwner(0);
455 assert((numChildren == 0) == (child == 0));
457 return toRemove != 0;
460 // sets the delayed-expanding flag
461 void KTreeViewItem::setDelayedExpanding(bool flag)
463 delayedExpanding = flag;
466 // tells the item whether it shall delete child items
467 void KTreeViewItem::setDeleteChildren(bool flag)
469 deleteChildren = flag;
472 // sets the draw expand button flag of this item
473 void KTreeViewItem::setDrawExpandButton(bool doit)
475 doExpandButton = doit;
478 // sets the draw text flag of this item
479 void KTreeViewItem::setDrawText(bool doit)
481 doText = doit;
484 // sets the draw tree branch flag of this item
485 void KTreeViewItem::setDrawTree(bool doit)
487 doTree = doit;
490 // sets the expanded flag of this item
491 void KTreeViewItem::setExpanded(bool is)
493 expanded = is;
496 // sets the owner of this item and its children siblings
497 void KTreeViewItem::setOwner(KTreeView* newOwner, bool includeSiblings)
499 /* Note: set owner of children's siblings! */
500 owner = newOwner;
501 if (getChild())
502 getChild()->setOwner(newOwner, true);
503 if (includeSiblings && getSibling())
504 getSibling()->setOwner(newOwner, true);
507 // sets the item pixmap to the given pixmap
508 void KTreeViewItem::setPixmap(const QPixmap& pm)
510 pixmap = pm;
513 // sets the item text to the given string
514 void KTreeViewItem::setText(const QString& t)
516 text = t;
519 // counts the child items and stores the result in numChildren
520 void KTreeViewItem::synchNumChildren()
522 numChildren = 0;
523 KTreeViewItem* item = getChild();
524 while (item != 0) {
525 numChildren++;
526 item = item->getSibling();
531 * returns the bounding rect of the item text in cell coordinates couldn't
532 * get QFontMetrics::boundingRect() to work right so I made my own
534 QRect KTreeViewItem::textBoundingRect(int indent) const
536 const QFontMetrics& fm = owner->fontMetrics();
537 int cellHeight = height(fm);
538 int rectX = indent + pixmap.width() + 2;
539 int rectY = (cellHeight - fm.height()) / 2;
540 int rectW = fm.width(text) + 2;
541 int rectH = fm.height();
542 return QRect(rectX, rectY, rectW, rectH);
545 // returns the total width of text and pixmap, including margins, spacing
546 // and indent, or -1 if empty -- SHOULD NEVER BE -1!
547 int KTreeViewItem::width(int indent) const
549 return width(indent, owner->fontMetrics());
552 // The width of an item is composed of the following:
553 // - indentation (indent with times level)
554 // - pixmap width
555 // - 2 pixels for highlight border
556 // - 1 pixel margin
557 // - text width
558 // - 1 pixel margin
559 // - 2 pixels for highlight border
560 int KTreeViewItem::width(int indent, const QFontMetrics& fm) const
562 return indent + pixmap.width() + fm.width(text) + 6;
567 * -------------------------------------------------------------------
569 * KTreeView
571 * -------------------------------------------------------------------
574 // constructor
575 KTreeView::KTreeView(QWidget *parent,
576 const char *name,
577 WFlags f) :
578 TableView(parent, name, f),
579 clearing(false),
580 current(-1),
581 drawExpandButton(true),
582 drawTree(true),
583 expansion(0),
584 goingDown(false),
585 itemIndent(18),
586 showText(true),
587 moveCurrentToSibling(false),
588 itemCapacity(500),
589 visibleItems(0),
590 rubberband_mode(false)
592 setNumRows(0);
593 setNumCols(1);
594 setBackgroundColor(colorGroup().base());
596 treeRoot = new KTreeViewItem;
597 treeRoot->setExpanded(true);
598 treeRoot->owner = this;
600 visibleItems = new KTreeViewItem*[itemCapacity];
601 // clear those pointers
602 for (int j = itemCapacity-1; j >= 0; j--) {
603 visibleItems[j] = 0;
607 // destructor
608 KTreeView::~KTreeView()
610 goingDown = true;
611 clear();
612 delete[] visibleItems;
613 delete treeRoot;
616 // appends a child to the item at the given index with the given text
617 // and pixmap
618 void KTreeView::appendChildItem(const char* theText, const QPixmap& thePixmap,
619 int index)
621 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
622 item->setDeleteChildren(true);
623 appendChildItem(item, index);
626 // appends a child to the item at the end of the given path with
627 // the given text and pixmap
628 void KTreeView::appendChildItem(const char* theText, const QPixmap& thePixmap,
629 const KPath& thePath)
631 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
632 item->setDeleteChildren(true);
633 appendChildItem(item, thePath);
636 // appends the given item to the children of the item at the given index
637 void KTreeView::appendChildItem(KTreeViewItem* newItem, int index)
639 /* find parent item and append new item to parent's sub tree */
640 KTreeViewItem* parentItem = itemAt(index);
641 if (!parentItem)
642 return;
643 appendChildItem(parentItem, newItem);
646 // appends the given item to the children of the item at the end of the
647 // given path
648 void KTreeView::appendChildItem(KTreeViewItem* newItem, const KPath& thePath)
650 /* find parent item and append new item to parent's sub tree */
651 KTreeViewItem* parentItem = itemAt(thePath);
652 if (!parentItem)
653 return;
654 appendChildItem(parentItem, newItem);
657 // translate mouse coord to cell coord
658 QPoint KTreeView::cellCoords(int row, const QPoint& widgetCoord)
660 int cellX = 0, cellY = 0;
661 colXPos(0, &cellX);
662 rowYPos(row, &cellY);
663 return QPoint(widgetCoord.x() - cellX, widgetCoord.y() - cellY);
666 // find item at specified index and change pixmap and/or text
667 void KTreeView::changeItem(const char *newText,
668 const QPixmap *newPixmap,
669 int index)
671 KTreeViewItem *item = itemAt(index);
672 if(item)
673 changeItem(item, index, newText, newPixmap);
676 // find item at end of specified path, and change pixmap and/or text
677 void KTreeView::changeItem(const char* newText,
678 const QPixmap* newPixmap,
679 const KPath& thePath)
681 KTreeViewItem* item = itemAt(thePath);
682 if (item) {
683 int index = itemRow(item);
684 changeItem(item, index, newText, newPixmap);
688 // clear all items from list and erase display
689 void KTreeView::clear()
691 setCurrentItem(-1);
693 /* somewhat of a hack for takeItem so it doesn't update the current item... */
694 clearing = TRUE;
696 bool autoU = autoUpdate();
697 setAutoUpdate(FALSE);
698 QStack<KTreeViewItem> stack;
699 stack.push(treeRoot);
700 while(!stack.isEmpty()) {
701 KTreeViewItem *item = stack.pop();
702 if(item->hasChild()) {
703 stack.push(item);
704 stack.push(item->getChild());
706 else if(item->hasSibling()) {
707 stack.push(item);
708 stack.push(item->getSibling());
710 else if(item->getParent() != 0) {
711 takeItem(item);
712 delete item;
715 clearing = FALSE;
716 if(goingDown || QApplication::closingDown())
717 return;
718 setAutoUpdate(autoU);
719 if(autoU && isVisible())
720 repaint();
723 // return a count of all the items in the tree, whether visible or not
724 uint KTreeView::count()
726 int total = 0;
727 forEveryItem(&KTreeView::countItem, (void *)&total);
728 return total;
731 // returns the index of the current (highlighted) item
732 int KTreeView::currentItem() const
734 return current;
737 // collapses the item at the specified row index.
738 void KTreeView::collapseItem(int index, bool emitSignal)
740 KTreeViewItem* item = itemAt(index);
741 if (item)
742 collapseSubTree(item, emitSignal);
745 // expands the item at the specified row indes.
746 void KTreeView::expandItem(int index, bool emitSignal)
748 KTreeViewItem* item = itemAt(index);
749 if (item)
750 expandSubTree(item, emitSignal);
753 // returns the depth the tree is automatically expanded to when
754 // items are added
755 int KTreeView::expandLevel() const
757 return expansion;
760 // visits every item in the tree, visible or not and applies
761 // the user supplied function with the item and user data passed as parameters
762 // if user supplied function returns true, traversal ends and function returns
763 bool KTreeView::forEveryItem(KForEveryFunc func, void* user, KTreeViewItem* item)
765 if (item == 0) {
766 item = treeRoot;
768 assert(item->owner == this);
769 item = item->getChild();
771 while (item != 0) {
772 // visit the siblings
773 if ((*func)(item, user)) {
774 return true;
776 // visit the children (recursively)
777 if (item->hasChild()) {
778 if (forEveryItem(func, user, item))
779 return true;
781 item = item->getSibling();
783 return false;
786 // visits every visible item in the tree in order and applies the
787 // user supplied function with the item and user data passed as parameters
788 // if user supplied function returns TRUE, traversal ends and function
789 // returns
790 bool KTreeView::forEveryVisibleItem(KForEveryFunc func, void *user,
791 KTreeViewItem* item)
793 if (item == 0) {
794 item = treeRoot;
795 } else {
796 // children are invisible (hence, nothing to do)
797 // if item is invisible or collapsed
798 if (!item->isVisible() || !item->isExpanded()) {
799 return false;
802 assert(item->owner == this);
803 item = item->getChild();
805 while (item != 0) {
806 // visit the siblings
807 if ((*func)(item, user)) {
808 return true;
810 // visit the children (recursively)
811 if (item->hasChild() && item->isExpanded()) {
812 if (forEveryVisibleItem(func, user, item))
813 return true;
815 item = item->getSibling();
817 return false;
820 // returns a pointer to the KTreeViewItem at the current index
821 // or 0 if no current item
822 KTreeViewItem *KTreeView::getCurrentItem()
824 if(current == -1) return 0;
825 return itemAt(current);
828 // returns the current indent spacing
829 int KTreeView::indentSpacing()
831 return itemIndent;
834 // inserts a new item with the specified text and pixmap before
835 // or after the item at the given index, depending on the value
836 // of prefix
837 // if index is negative, appends item to tree at root level
838 bool KTreeView::insertItem(const char* theText, const QPixmap& thePixmap,
839 int row, bool prefix)
841 KTreeViewItem* refItem = itemAt(row);
843 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
844 item->setDeleteChildren(true);
846 bool success = insertItem(refItem, item, prefix);
847 if (!success)
848 delete item;
849 return success;
852 // inserts a new item with the specified text and pixmap before
853 // or after the item at the end of the given path, depending on the value
854 // of prefix
855 bool KTreeView::insertItem(const char* theText, const QPixmap& thePixmap,
856 const KPath& path, bool prefix)
858 KTreeViewItem* refItem = itemAt(path);
860 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
861 item->setDeleteChildren(true);
863 bool success = insertItem(refItem, item, prefix);
864 if (!success)
865 delete item;
866 return success;
869 // inserts the given item or derived object into the tree before
870 // or after the item at the given index, depending on the value
871 // of prefix
872 // if index is negative, appends item to tree at root level
873 bool KTreeView::insertItem(KTreeViewItem* newItem,
874 int index, bool prefix)
876 // find the item currently at the index, if there is one
877 KTreeViewItem* refItem = itemAt(index);
879 // insert new item at the appropriate place
880 return insertItem(refItem, newItem, prefix);
883 // inserts the given item or derived object into the tree before
884 // or after the item at the end of the given path, depending on the value
885 // of prefix
886 bool KTreeView::insertItem(KTreeViewItem* newItem,
887 const KPath& thePath, bool prefix)
889 // find the item currently at the end of the path, if there is one
890 KTreeViewItem* refItem = itemAt(thePath);
892 // insert new item at appropriate place
893 return insertItem(refItem, newItem, prefix);
897 * returns pointer to KTreeViewItem at the specifed row or 0 if row is out
898 * of limits.
900 KTreeViewItem* KTreeView::itemAt(int row) const
902 if (row < 0 || row >= numRows()) {
903 return 0;
905 else {
906 // lookup the item in the list of visible items
907 assert(row < itemCapacity);
908 KTreeViewItem* i = visibleItems[row];
909 assert(i != 0);
910 return i;
914 // returns pointer to KTreeViewItem at the end of the
915 // path or 0 if not found
916 KTreeViewItem* KTreeView::itemAt(const KPath& path)
918 if (path.isEmpty())
919 return 0;
921 // need a copy of the path because recursiveFind will destroy it
922 KPath pathCopy = path;
924 return recursiveFind(pathCopy);
927 // computes the path of the item at the specified index
928 // if index is invalid, nothing is done.
929 void KTreeView::itemPath(int row, KPath& path)
931 KTreeViewItem* item = itemAt(row);
932 if (item != 0) {
933 itemPath(item, path);
937 // returns the row in the visible tree of the given item or
938 // -1 if not found
939 int KTreeView::itemRow(KTreeViewItem* item)
941 if (item->owner == this) {
942 // search in list of visible items
943 for (int i = numRows()-1; i >= 0; i--) {
944 if (visibleItems[i] == item) {
945 return i;
949 // not found
950 return -1;
954 * move the subtree at the specified index up one branch level (make root
955 * item a sibling of its current parent)
957 void KTreeView::join(int index)
959 KTreeViewItem *item = itemAt(index);
960 if(item)
961 join(item);
965 * move the subtree at the specified index up one branch level (make root
966 * item a sibling of it's current parent)
968 void KTreeView::join(const KPath& path)
970 KTreeViewItem *item = itemAt(path);
971 if (item)
972 join(item);
975 /* move item at specified index one slot down in its parent's sub tree */
976 void KTreeView::lowerItem(int index)
978 KTreeViewItem *item = itemAt(index);
979 if(item)
980 lowerItem(item);
983 /* move item at specified path one slot down in its parent's sub tree */
984 void KTreeView::lowerItem(const KPath& path)
986 KTreeViewItem* item = itemAt(path);
987 if (item)
988 lowerItem(item);
991 /* move item at specified index one slot up in its parent's sub tree */
992 void KTreeView::raiseItem(int index)
994 KTreeViewItem* item = itemAt(index);
995 if (item)
996 raiseItem(item);
999 /* move item at specified path one slot up in its parent's sub tree */
1000 void KTreeView::raiseItem(const KPath& path)
1002 KTreeViewItem* item = itemAt(path);
1003 if (item)
1004 raiseItem(item);
1007 // remove the item at the specified index and delete it
1008 void KTreeView::removeItem(int index)
1010 KTreeViewItem *item = itemAt(index);
1011 if(item) {
1012 takeItem(item);
1013 delete item;
1017 // remove the item at the end of the specified path and delete it
1018 void KTreeView::removeItem(const KPath& thePath)
1020 KTreeViewItem* item = itemAt(thePath);
1021 if (item) {
1022 takeItem(item);
1023 delete item;
1027 void KTreeView::scrollVisible(KTreeViewItem* item, bool children)
1029 if (item == 0)
1030 return;
1031 int row = itemRow(item);
1032 if (row < 0)
1033 return; /* do nothing if invisible */
1035 if (children && item->isExpanded()) {
1036 // we are concerned about children
1037 if (!rowIsVisible(row)) {
1038 // just move to the top
1039 setTopCell(row);
1040 } else {
1041 // this is the complicated part
1042 // count the visible children (including grandchildren)
1043 int numVisible = 0;
1044 forEveryVisibleItem(countItem, &numVisible, item);
1045 // if the last child is visible, do nothing
1046 if (rowIsVisible(row + numVisible))
1047 return;
1049 * Basically, item will become the top cell; but if there are
1050 * more visible rows in the widget than we have children, then
1051 * we don't move that far.
1053 int remain = lastRowVisible()-topCell()-numVisible;
1054 if (remain <= 0) {
1055 setTopCell(row);
1056 } else {
1057 setTopCell(QMAX(0,row-remain));
1060 } else {
1061 // we are not concerned about children
1062 if (rowIsVisible(row))
1063 return;
1064 // just move the item to the top
1065 setTopCell(row);
1069 // sets the current item and hightlights it, emitting signals
1070 void KTreeView::setCurrentItem(int row)
1072 if (row == current)
1073 return;
1074 int numVisible = numRows();
1075 if (row > numVisible)
1076 return;
1077 int oldCurrent = current;
1078 current = row;
1079 if (oldCurrent < numVisible)
1080 updateCell(oldCurrent, 0);
1081 if (current > -1) {
1082 updateCell(current, 0, false);
1083 emit highlighted(current);
1087 // enables/disables drawing of expand button
1088 void KTreeView::setExpandButtonDrawing(bool enable)
1090 if (drawExpandButton == enable)
1091 return;
1092 drawExpandButton = enable;
1094 // the user parameter is cast to a bool in setItemExpandButtonDrawing
1095 forEveryItem(&KTreeView::setItemExpandButtonDrawing, enable ? &enable : 0);
1097 if (autoUpdate() && isVisible())
1098 repaint();
1101 // sets depth to which subtrees are automatically expanded, and
1102 // redraws tree if auto update enabled
1103 void KTreeView::setExpandLevel(int level)
1105 if (expansion == level)
1106 return;
1107 expansion = level;
1108 KTreeViewItem* item = getCurrentItem();
1109 forEveryItem(&KTreeView::setItemExpandLevel, 0);
1110 while (item != 0) {
1111 if (item->getParent()->isExpanded())
1112 break;
1113 item = item->getParent();
1115 if (item != 0)
1116 setCurrentItem(itemRow(item));
1117 if (autoUpdate() && isVisible())
1118 repaint();
1121 // sets the indent margin for all branches and repaints if auto update enabled
1122 void KTreeView::setIndentSpacing(int spacing)
1124 if (itemIndent == spacing)
1125 return;
1126 itemIndent = spacing;
1127 updateCellWidth();
1128 if (autoUpdate() && isVisible())
1129 repaint();
1132 void KTreeView::setMoveCurrentToSibling(bool m)
1134 moveCurrentToSibling = m;
1137 // enables/disables display of item text (keys)
1138 void KTreeView::setShowItemText(bool enable)
1140 if (showText == enable)
1141 return;
1142 showText = enable;
1144 // the user parameter is cast to a bool in setItemShowText
1145 forEveryItem(&KTreeView::setItemShowText, enable ? &enable : 0);
1147 if (autoUpdate() && isVisible())
1148 repaint();
1151 // enables/disables tree branch drawing
1152 void KTreeView::setTreeDrawing(bool enable)
1154 if (drawTree == enable)
1155 return;
1156 drawTree = enable;
1158 // the user parameter is cast to a bool in setItemTreeDrawing
1159 forEveryItem(&KTreeView::setItemTreeDrawing, enable ? &enable : 0);
1161 if (autoUpdate() && isVisible())
1162 repaint();
1165 // indicates whether item text keys are displayed
1166 bool KTreeView::showItemText() const
1168 return showText;
1171 // indents the item at the given index, splitting the tree into
1172 // a new branch
1173 void KTreeView::split(int index)
1175 KTreeViewItem *item = itemAt(index);
1176 if(item)
1177 split(item);
1180 // indents the item at the given path, splitting the tree into
1181 // a new branch
1182 void KTreeView::split(const KPath& path)
1184 KTreeViewItem* item = itemAt(path);
1185 if (item)
1186 split(item);
1189 // removes item at specified index from tree but does not delete it
1190 // returns pointer to the item or 0 if not succesful
1191 KTreeViewItem *KTreeView::takeItem(int index)
1193 KTreeViewItem *item = itemAt(index);
1194 if(item)
1195 takeItem(item);
1196 return item;
1199 // removes item at specified path from tree but does not delete it
1200 // returns pointer to the item or 0 if not successful
1201 KTreeViewItem* KTreeView::takeItem(const KPath& path)
1203 KTreeViewItem* item = itemAt(path);
1204 if (item)
1205 takeItem(item);
1206 return item;
1209 // indicates whether tree branches are drawn
1210 bool KTreeView::treeDrawing() const
1212 return drawTree;
1216 // appends a child to the specified parent item (note: a child, not a sibling, is added!)
1217 void KTreeView::appendChildItem(KTreeViewItem* theParent,
1218 KTreeViewItem* newItem)
1220 theParent->appendChild(newItem);
1222 // set item state
1223 newItem->setDrawExpandButton(drawExpandButton);
1224 newItem->setDrawTree(drawTree);
1225 newItem->setDrawText(showText);
1226 if (level(newItem) < expansion) {
1227 newItem->setExpanded(true);
1230 // fix up branch levels of any children that the new item may already have
1231 if(newItem->hasChild()) {
1232 fixChildren(newItem);
1235 // if necessary, adjust cell width, number of rows and repaint
1236 if (newItem->isVisible() || theParent->childCount() == 1) {
1237 bool autoU = autoUpdate();
1238 setAutoUpdate(false);
1239 updateVisibleItems();
1240 setAutoUpdate(autoU);
1241 if (autoU && isVisible())
1242 repaint();
1246 // returns the height of the cell(row) at the specified row (index)
1247 int KTreeView::cellHeight(int row) const
1249 return itemAt(row)->height(fontMetrics());
1252 // returns the width of the cells. Note: this is mostly for derived classes
1253 // which have more than 1 column
1254 int KTreeView::cellWidth(int /*col*/) const
1256 return maxItemWidth;
1259 // changes the given item with the new text and/or pixmap
1260 void KTreeView::changeItem(KTreeViewItem* toChange, int itemRow,
1261 const char* newText, const QPixmap* newPixmap)
1263 int indent = indentation(toChange);
1264 int oldWidth = toChange->width(indent);
1265 if(newText)
1266 toChange->setText(newText);
1267 if (newPixmap)
1268 toChange->setPixmap(*newPixmap);
1269 if(oldWidth != toChange->width(indent))
1270 updateCellWidth();
1271 if(itemRow == -1)
1272 return;
1273 if(autoUpdate() && rowIsVisible(itemRow))
1274 updateCell(itemRow, 0);
1277 // collapses the subtree at the specified item
1278 void KTreeView::collapseSubTree(KTreeViewItem* subRoot, bool emitSignal)
1280 assert(subRoot->owner == this);
1281 if (!subRoot->isExpanded())
1282 return;
1284 // must move the current item if it is visible
1285 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1287 subRoot->setExpanded(false);
1288 if (subRoot->isVisible()) {
1289 bool autoU = autoUpdate();
1290 setAutoUpdate(false);
1291 updateVisibleItems();
1292 // re-seat current item
1293 if (cur != 0) {
1294 setCurrentItem(itemRow(cur));
1296 if (emitSignal) {
1297 emit collapsed(itemRow(subRoot));
1299 setAutoUpdate(autoU);
1300 if (autoU && isVisible())
1301 repaint();
1305 // used by count() with forEach() function to count total number
1306 // of items in the tree
1307 bool KTreeView::countItem(KTreeViewItem*, void* total)
1309 int* t = static_cast<int*>(total);
1310 (*t)++;
1311 return false;
1314 // expands the subtree at the given item
1315 void KTreeView::expandSubTree(KTreeViewItem* subRoot, bool emitSignal)
1317 assert(subRoot->owner == this);
1318 if (subRoot->isExpanded())
1319 return;
1321 // must move the current item if it is visible
1322 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1324 bool allow = true;
1326 if (subRoot->delayedExpanding) {
1327 emit expanding(subRoot, allow);
1329 if (!allow)
1330 return;
1332 subRoot->setExpanded(true);
1334 if (subRoot->isVisible()) {
1335 bool autoU = autoUpdate();
1336 setAutoUpdate(false);
1337 updateVisibleItems();
1338 // re-seat current item
1339 if (cur != 0) {
1340 setCurrentItem(itemRow(cur));
1342 if (emitSignal) {
1343 emit expanded(itemRow(subRoot));
1345 setAutoUpdate(autoU);
1346 if (autoU && isVisible())
1347 repaint();
1351 // fix up branch levels out of whack from split/join operations on the tree
1352 void KTreeView::fixChildren(KTreeViewItem *parentItem)
1354 KTreeViewItem* childItem = 0;
1355 KTreeViewItem* siblingItem = 0;
1356 // int childBranch = parentItem->getBranch() + 1;
1357 if(parentItem->hasChild()) {
1358 childItem = parentItem->getChild();
1359 // childItem->setBranch(childBranch);
1360 childItem->owner = parentItem->owner;
1361 fixChildren(childItem);
1363 while(childItem && childItem->hasSibling()) {
1364 siblingItem = childItem->getSibling();
1365 // siblingItem->setBranch(childBranch);
1366 siblingItem->owner = parentItem->owner;
1367 fixChildren(siblingItem);
1368 childItem = siblingItem;
1373 * Handle QFocusEvent processing by setting current item to top row if
1374 * there is no current item, and updates cell to add or delete the focus
1375 * rectangle on the highlight bar. The base class is not called because it
1376 * does a repaint() which causes flicker.
1378 void KTreeView::focusInEvent(QFocusEvent *)
1380 if (current < 0 && numRows() > 0)
1381 setCurrentItem(topCell());
1382 updateCell(current, 0);
1385 void KTreeView::focusOutEvent(QFocusEvent *)
1387 updateCell(current, 0);
1390 // called by updateCellWidth() for each item in the visible list
1391 bool KTreeView::getMaxItemWidth(KTreeViewItem* item, void* user)
1393 assert(item->owner != 0);
1394 int indent = item->owner->indentation(item);
1395 int* maxW = static_cast<int*>(user);
1396 int w = item->width(indent);
1397 if (w > *maxW)
1398 *maxW = w;
1399 return false;
1402 int KTreeView::indentation(KTreeViewItem* item) const
1404 return level(item) * itemIndent + itemIndent + 3;
1407 // inserts the new item before or after the reference item, depending
1408 // on the value of prefix
1409 bool KTreeView::insertItem(KTreeViewItem* referenceItem,
1410 KTreeViewItem* newItem,
1411 bool prefix)
1413 assert(newItem != 0);
1414 assert(referenceItem == 0 || referenceItem->owner == this);
1416 /* set the new item's state */
1417 newItem->setDrawExpandButton(drawExpandButton);
1418 newItem->setDrawTree(drawTree);
1419 newItem->setDrawText(showText);
1420 KTreeViewItem* parentItem;
1421 if (referenceItem) {
1422 parentItem = referenceItem->getParent();
1423 int insertIndex = parentItem->childIndex(referenceItem);
1424 if (!prefix)
1425 insertIndex++;
1426 parentItem->insertChild(insertIndex, newItem);
1428 else {
1429 // no reference item, append at end of visible tree
1430 // need to repaint
1431 parentItem = treeRoot;
1432 parentItem->appendChild(newItem);
1435 // set item expansion
1436 if (level(newItem) < expansion)
1437 newItem->setExpanded(true);
1439 // fix up branch levels of any children
1440 if (newItem->hasChild())
1441 fixChildren(newItem);
1443 // if repaint necessary, do it if visible and auto update
1444 // enabled
1445 if (newItem->isVisible() || parentItem->childCount() == 1) {
1446 bool autoU = autoUpdate();
1447 setAutoUpdate(FALSE);
1448 updateVisibleItems();
1449 setAutoUpdate(autoU);
1450 if (autoU && isVisible())
1451 repaint();
1453 return true;
1457 * returns pointer to item's path
1459 void KTreeView::itemPath(KTreeViewItem* item, KPath& path) const
1461 assert(item != 0);
1462 assert(item->owner == this);
1463 if (item != treeRoot) {
1464 itemPath(item->getParent(), path);
1465 path.push(item->getText());
1470 * joins the item's branch into the tree, making the item a sibling of its
1471 * parent
1473 void KTreeView::join(KTreeViewItem *item)
1475 KTreeViewItem *itemParent = item->getParent();
1476 if(itemParent->hasParent()) {
1477 bool autoU = autoUpdate();
1478 setAutoUpdate(FALSE);
1479 takeItem(item);
1480 insertItem(itemParent, item, FALSE);
1481 setAutoUpdate(autoU);
1482 if(autoU && isVisible())
1483 repaint();
1487 // handles keyboard interface
1488 void KTreeView::keyPressEvent(QKeyEvent* e)
1490 if (numRows() == 0)
1491 return; /* nothing to do */
1493 /* if there's no current item, make the top item current */
1494 if (currentItem() < 0)
1495 setCurrentItem(topCell());
1496 assert(currentItem() >= 0); /* we need a current item */
1497 assert(itemAt(currentItem()) != 0); /* we really need a current item */
1499 // give currentItem a chance to handle the event
1500 if (itemAt(currentItem())->keyEvent(e))
1501 return; /* handled */
1503 int pageSize, delta;
1504 KTreeViewItem* item;
1505 int key = e->key();
1506 repeat:
1507 switch (key) {
1508 case Key_Up:
1509 // make previous item current, scroll up if necessary
1510 if (currentItem() > 0) {
1511 setCurrentItem(currentItem() - 1);
1512 scrollVisible(itemAt(currentItem()), false);
1514 break;
1515 case Key_Down:
1516 // make next item current, scroll down if necessary
1517 if (currentItem() < numRows() - 1) {
1518 setCurrentItem(currentItem() + 1);
1519 if (currentItem() > lastRowVisible()) {
1520 // scrollVisible is not feasible here because
1521 // it scrolls the item to the top
1522 setTopCell(topCell() + currentItem() - lastRowVisible());
1523 } else if (currentItem() < topCell()) {
1524 setTopCell(currentItem());
1527 break;
1528 case Key_Next:
1529 // move highlight one page down and scroll down
1530 delta = currentItem() - topCell();
1531 pageSize = lastRowVisible() - topCell();
1532 setTopCell(QMIN(topCell() + pageSize, numRows() - 1));
1533 setCurrentItem(QMIN(topCell() + delta, numRows() - 1));
1534 break;
1535 case Key_Prior:
1536 // move highlight one page up and scroll up
1537 delta = currentItem() - topCell();
1538 pageSize = lastRowVisible() - topCell();
1539 setTopCell(QMAX(topCell() - pageSize, 0));
1540 setCurrentItem(QMAX(topCell() + delta, 0));
1541 break;
1542 case Key_Plus:
1543 case Key_Right:
1544 // if current item has subtree and is collapsed, expand it
1545 item = itemAt(currentItem());
1546 if (item->isExpanded() && item->hasChild() && key == Key_Right) {
1547 // going right on an expanded item is like going down
1548 key = Key_Down;
1549 goto repeat;
1550 } else {
1551 expandSubTree(item, true);
1552 scrollVisible(item, true);
1554 break;
1555 case Key_Minus:
1556 case Key_Left:
1557 // if current item has subtree and is expanded, collapse it
1558 item = itemAt(currentItem());
1559 if ((!item->isExpanded() || !item->hasChild()) && key == Key_Left) {
1560 // going left on a collapsed item goes to its parent
1561 item = item->getParent();
1562 if (item == treeRoot)
1563 break; /* we're already at the top */
1564 assert(item->isVisible());
1565 setCurrentItem(itemRow(item));
1566 } else {
1567 collapseSubTree(item, true);
1569 scrollVisible(item, false);
1570 break;
1571 case Key_Return:
1572 case Key_Enter:
1573 // select the current item
1574 if (currentItem() >= 0)
1575 emit selected(currentItem());
1576 break;
1577 default:
1578 break;
1582 // handles keyboard interface
1583 void KTreeView::keyReleaseEvent(QKeyEvent* e)
1585 if (currentItem() >= 0 && itemAt(currentItem()) != 0)
1586 itemAt(currentItem())->keyEvent(e);
1589 int KTreeView::level(KTreeViewItem* item) const
1591 assert(item != 0);
1592 assert(item->owner == this);
1593 assert(item != treeRoot);
1594 int l = 0;
1595 item = item->parent->parent; /* since item != treeRoot, there is a parent */
1596 while (item != 0) {
1597 item = item->parent;
1598 l++;
1600 return l;
1603 /* move specified item down one slot in parent's subtree */
1604 void KTreeView::lowerItem(KTreeViewItem *item)
1606 KTreeViewItem *itemParent = item->getParent();
1607 uint itemChildIndex = itemParent->childIndex(item);
1608 if(itemChildIndex < itemParent->childCount() - 1) {
1609 bool autoU = autoUpdate();
1610 setAutoUpdate(FALSE);
1611 takeItem(item);
1612 insertItem(itemParent->childAt(itemChildIndex), item, FALSE);
1613 setAutoUpdate(autoU);
1614 if(autoU && isVisible())
1615 repaint();
1619 // handle mouse double click events by selecting the clicked item
1620 // and emitting the signal
1621 void KTreeView::mouseDoubleClickEvent(QMouseEvent* e)
1623 // find out which row has been clicked
1624 int itemClicked = findRow(e->y());
1626 if (itemClicked < 0)
1627 return; /* invalid row, do nothing */
1629 KTreeViewItem* item = itemAt(itemClicked);
1630 if (item == 0)
1631 return;
1633 // translate mouse coord to cell coord
1634 QPoint cellCoord = cellCoords(itemClicked, e->pos());
1636 // first ask item
1637 if (item->mouseEvent(e, cellCoord))
1638 return;
1640 // hit test item
1641 int indent = indentation(item);
1642 if (item->boundingRect(indent).contains(cellCoord))
1643 emit selected(itemClicked);
1646 // handle mouse movement events
1647 void KTreeView::mouseMoveEvent(QMouseEvent* e)
1649 // in rubberband_mode we actually scroll the window now
1650 if (rubberband_mode) {
1651 move_rubberband(e->pos());
1652 } else {
1653 // else forward to current item
1654 int current = currentItem();
1655 if (current >= 0 && itemAt(current))
1656 itemAt(current)->mouseEvent(e, cellCoords(current, e->pos()));
1661 // handle single mouse presses
1662 void KTreeView::mousePressEvent(QMouseEvent* e)
1664 /* first: cancel rubberbanding if it's on */
1665 if (rubberband_mode)
1667 // another button was pressed while rubberbanding, stop the move.
1668 // RB: if we allow other buttons while rubberbanding the tree can expand
1669 // while rubberbanding - we then need to recalculate and resize the
1670 // rubberband rect and show the new size
1671 end_rubberband();
1672 return;
1675 // find out which row has been clicked
1676 int itemClicked = findRow(e->y());
1678 // nothing to do if not on valid row
1679 if (itemClicked < 0)
1680 return;
1681 KTreeViewItem* item = itemAt(itemClicked);
1682 if (!item)
1683 return;
1685 // translate mouse coord to cell coord
1686 QPoint cellCoord = cellCoords(itemClicked, e->pos());
1688 // give the item a crack
1689 if (item->mouseEvent(e, cellCoord))
1690 return; /* event eaten by item */
1692 // check for rubberbanding
1693 if (e->button() == MidButton)
1695 // RB: the MMB is hardcoded to the "rubberband" scroll mode
1696 if (!rubberband_mode) {
1697 start_rubberband(e->pos());
1699 return;
1702 if (e->button() == RightButton) {
1703 emit rightPressed(itemClicked, e->pos());
1705 /* hit test expand button (doesn't set currentItem) */
1706 else if (item->expandButtonClicked(cellCoord)) {
1707 if (item->isExpanded()) {
1708 collapseSubTree(item, true);
1709 } else {
1710 expandSubTree(item, true);
1711 scrollVisible(item, true); /* make children visible */
1714 // hit test item
1715 else if (item->boundingRect(indentation(item)).contains(cellCoord)) {
1716 setCurrentItem(itemClicked);
1720 // handle mouse release events
1721 void KTreeView::mouseReleaseEvent(QMouseEvent *e)
1723 /* if it's the MMB end rubberbanding */
1724 if (rubberband_mode) {
1725 if (e->button() == MidButton)
1726 end_rubberband();
1727 return;
1729 // forward to current item
1730 int current = currentItem();
1731 if (current >= 0 && itemAt(current))
1732 itemAt(current)->mouseEvent(e, cellCoords(current, e->pos()));
1735 // rubberband move: draw the rubberband
1736 void KTreeView::draw_rubberband()
1739 * RB: I'm using a white pen because of the XorROP mode. I would prefer
1740 * to draw the rectangle in red but I don't now how to get a pen which
1741 * draws red in XorROP mode (this depends on the background). In fact
1742 * the color should be configurable.
1745 if (!rubberband_mode) return;
1746 QPainter paint(this);
1747 paint.setPen(white);
1748 paint.setRasterOp(XorROP);
1749 paint.drawRect(xOffset()*viewWidth()/totalWidth(),
1750 yOffset()*viewHeight()/totalHeight(),
1751 rubber_width+1, rubber_height+1);
1752 paint.end();
1755 // rubberband move: start move
1756 void KTreeView::start_rubberband(const QPoint& where)
1758 if (rubberband_mode) { // Oops!
1759 end_rubberband();
1761 /* RB: Don't now, if this check is necessary */
1762 if (!viewWidth() || !viewHeight()) return;
1763 if (!totalWidth() || !totalHeight()) return;
1765 // calculate the size of the rubberband rectangle
1766 rubber_width = viewWidth()*viewWidth()/totalWidth();
1767 if (rubber_width > viewWidth()) rubber_width = viewWidth();
1768 rubber_height = viewHeight()*viewHeight()/totalHeight();
1769 if (rubber_height > viewHeight()) rubber_height = viewHeight();
1771 // remember the cursor position and the actual offset
1772 rubber_startMouse = where;
1773 rubber_startX = xOffset();
1774 rubber_startY = yOffset();
1775 rubberband_mode=TRUE;
1776 draw_rubberband();
1779 // rubberband move: end move
1780 void KTreeView::end_rubberband()
1782 if (!rubberband_mode) return;
1783 draw_rubberband();
1784 rubberband_mode = FALSE;
1787 // rubberband move: hanlde mouse moves
1788 void KTreeView::move_rubberband(const QPoint& where)
1790 if (!rubberband_mode) return;
1792 // look how much the mouse moved and calc the new scroll position
1793 QPoint delta = where - rubber_startMouse;
1794 int nx = rubber_startX + delta.x() * totalWidth() / viewWidth();
1795 int ny = rubber_startY + delta.y() * totalHeight() / viewHeight();
1797 // check the new position (and make it valid)
1798 if (nx < 0) nx = 0;
1799 else if (nx > maxXOffset()) nx = maxXOffset();
1800 if (ny < 0) ny = 0;
1801 else if (ny > maxYOffset()) ny = maxYOffset();
1803 // redraw the rubberband at the new position
1804 draw_rubberband();
1805 setOffset(nx,ny);
1806 draw_rubberband();
1810 // paints the cell at the specified row and col
1811 // col is ignored for now since there is only one
1812 void KTreeView::paintCell(QPainter* p, int row, int)
1814 KTreeViewItem* item = itemAt(row);
1815 if (item == 0)
1816 return;
1818 QColorGroup cg = colorGroup();
1819 int indent = indentation(item);
1820 item->paint(p, indent, cg,
1821 current == row); /* highlighted */
1825 /* This is needed to make the kcontrol's color setup working (Marcin Dalecki) */
1826 void KTreeView::paletteChange(const QPalette &)
1828 setBackgroundColor(colorGroup().base());
1829 repaint(true);
1833 /* raise the specified item up one slot in parent's subtree */
1834 void KTreeView::raiseItem(KTreeViewItem *item)
1836 KTreeViewItem *itemParent = item->getParent();
1837 int itemChildIndex = itemParent->childIndex(item);
1838 if(itemChildIndex > 0) {
1839 bool autoU = autoUpdate();
1840 setAutoUpdate(FALSE);
1841 takeItem(item);
1842 insertItem(itemParent->childAt(--itemChildIndex), item, TRUE);
1843 setAutoUpdate(autoU);
1844 if(autoU && isVisible())
1845 repaint();
1849 // find the item at the path
1850 KTreeViewItem* KTreeView::recursiveFind(KPath& path)
1852 if (path.isEmpty())
1853 return treeRoot;
1855 // get the next key
1856 QString searchString = path.pop();
1858 // find the parent item
1859 KTreeViewItem* parent = recursiveFind(path);
1860 if (parent == 0)
1861 return 0;
1864 * Iterate through the parent's children searching for searchString.
1866 KTreeViewItem* sibling = parent->getChild();
1867 while (sibling != 0) {
1868 if (searchString == sibling->getText()) {
1869 break; /* found it! */
1871 sibling = sibling->getSibling();
1873 return sibling;
1876 void KTreeView::setItemExpanded(KTreeViewItem* item)
1878 if (level(item) < expansion) {
1879 expandSubTree(item, true);
1880 } else {
1881 collapseSubTree(item, true);
1885 // called by setExpandLevel for each item in tree
1886 bool KTreeView::setItemExpandLevel(KTreeViewItem* item, void*)
1888 assert(item->owner != 0);
1889 item->owner->setItemExpanded(item);
1890 return false;
1893 // called by setExpandButtonDrawing for every item in tree
1894 // the parameter drawButton is used as (and implicitly cast to) a bool
1895 bool KTreeView::setItemExpandButtonDrawing(KTreeViewItem* item,
1896 void* drawButton)
1898 item->setDrawExpandButton(drawButton);
1899 return false;
1902 // called by setShowItemText for every item in tree
1903 // the parameter newDrawText is used as (and implicitly cast to) a bool
1904 bool KTreeView::setItemShowText(KTreeViewItem* item,
1905 void* newDrawText)
1907 item->setDrawText(newDrawText);
1908 return false;
1911 // called by setTreeDrawing for every item in tree
1912 // the parameter drawTree is used as (and implicitly cast to) a bool
1913 bool KTreeView::setItemTreeDrawing(KTreeViewItem* item, void* drawTree)
1915 item->setDrawTree(drawTree);
1916 return false;
1919 // makes the item a child of the item above it, splitting
1920 // the tree into a new branch
1921 void KTreeView::split(KTreeViewItem *item)
1923 KTreeViewItem *itemParent = item->getParent();
1924 int itemChildIndex = itemParent->childIndex(item);
1925 if(itemChildIndex == 0)
1926 return;
1927 bool autoU = autoUpdate();
1928 setAutoUpdate(FALSE);
1929 takeItem(item);
1930 appendChildItem(itemParent->childAt(--itemChildIndex), item);
1931 setAutoUpdate(autoU);
1932 if(autoU && isVisible())
1933 repaint();
1936 // removes the item from the tree without deleting it
1937 void KTreeView::takeItem(KTreeViewItem* item)
1939 assert(item->owner == this);
1941 // TODO: go over this function again
1943 bool wasVisible = item->isVisible();
1945 * If we have a current item, make sure that it is not in the subtree
1946 * that we are about to remove. If the current item is in the part
1947 * below the taken-out subtree, we must move it up a number of rows if
1948 * the taken-out subtree is at least partially visible.
1950 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1951 KTreeViewItem* oldCurrent = cur;
1952 if (wasVisible && cur != 0) {
1953 KTreeViewItem* c = cur;
1954 while (c != 0 && c != item) {
1955 c = c->getParent();
1957 if (c != 0) {
1958 // move current item to parent
1959 cur = item->getParent();
1960 if (cur == treeRoot) {
1961 cur = 0;
1962 // move it to next or previous sibling
1963 if (moveCurrentToSibling) {
1964 if (item->getSibling() != 0) {
1965 cur = item->getSibling();
1966 } else if (treeRoot->getChild() != item) {
1967 cur = treeRoot->getChild();
1968 while (cur != 0 && cur->getSibling() != item)
1969 cur = cur->getSibling();
1975 KTreeViewItem* parentItem = item->getParent();
1976 parentItem->removeChild(item);
1977 item->sibling = 0;
1978 if (wasVisible || parentItem->childCount() == 0) {
1979 bool autoU = autoUpdate();
1980 setAutoUpdate(FALSE);
1981 updateVisibleItems();
1982 setAutoUpdate(autoU);
1983 if (autoU && isVisible())
1984 repaint();
1987 // re-seat the current item
1988 // row changes if cur is below the taken item
1989 current = cur != 0 ? itemRow(cur) : -1;
1990 // signal must be emitted only if item really changed
1991 if (cur != oldCurrent) {
1992 updateCell(current, 0, false);
1993 emit highlighted(current);
1997 // visits each item, calculates the maximum width
1998 // and updates TableView
1999 void KTreeView::updateCellWidth()
2001 // make cells at least 1 pixel wide to avoid singularities (division by zero)
2002 int maxW = 1;
2003 forEveryVisibleItem(&KTreeView::getMaxItemWidth, &maxW);
2004 maxItemWidth = maxW;
2005 updateTableSize();
2007 // correct offsets
2008 int xoff = xOffset();
2009 int yoff = yOffset();
2010 if (xoff > maxXOffset()) xoff = maxXOffset();
2011 if (yoff > maxYOffset()) yoff = maxYOffset();
2013 setOffset(xoff, yoff);
2016 void KTreeView::updateVisibleItems()
2018 int index = 0;
2019 int count = 0;
2020 updateVisibleItemRec(treeRoot, index, count);
2021 assert(index == count);
2022 setNumRows(count);
2023 updateCellWidth();
2026 void KTreeView::updateVisibleItemRec(KTreeViewItem* item, int& index, int& count)
2028 if (!item->isExpanded()) {
2029 // no visible items if not expanded
2030 return;
2034 * Record the children of item in the list of visible items.
2036 * Don't register item itself, it's already in the list. Also only
2037 * allocate new space for children.
2039 count += item->childCount();
2040 if (count > itemCapacity) {
2041 // must reallocate
2042 int newCapacity = itemCapacity;
2043 do {
2044 newCapacity += newCapacity;
2045 } while (newCapacity < count);
2046 KTreeViewItem** newItems = new KTreeViewItem*[newCapacity];
2047 // clear the unneeded space
2048 for (int i = index; i < newCapacity; i++) {
2049 newItems[i] = 0;
2051 // move already accumulated items over
2052 for (int i = index-1; i >= 0; i--) {
2053 newItems[i] = visibleItems[i];
2055 delete[] visibleItems;
2056 visibleItems = newItems;
2057 itemCapacity = newCapacity;
2059 // insert children
2060 for (KTreeViewItem* i = item->getChild(); i != 0; i = i->getSibling()) {
2061 visibleItems[index++] = i;
2062 updateVisibleItemRec(i, index, count);