Merged latest fixes and improvements from 1.2 branch.
[kdbg.git] / kdbg / ktreeview.cpp
blob19359cc0f16ff6eea88e5ebfed62ed75c83f0320
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 <assert.h>
33 * -------------------------------------------------------------------
35 * KTreeViewItem
37 * -------------------------------------------------------------------
40 // constructor
41 KTreeViewItem::KTreeViewItem(const QString& theText) :
42 owner(0),
43 numChildren(0),
44 doExpandButton(true),
45 expanded(false),
46 delayedExpanding(false),
47 doTree(true),
48 doText(true),
49 child(0),
50 parent(0),
51 sibling(0),
52 deleteChildren(false)
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),
70 deleteChildren(false)
72 text = theText;
73 pixmap = thePixmap;
76 // destructor
77 KTreeViewItem::~KTreeViewItem()
79 if (deleteChildren) {
80 // remove the children
81 KTreeViewItem* i = child;
82 while (i) {
83 KTreeViewItem* d = i;
84 i = i->getSibling();
85 delete d;
90 // appends a direct child
91 void KTreeViewItem::appendChild(KTreeViewItem* newChild)
93 newChild->parent = this;
94 newChild->setOwner(owner);
95 if (!getChild()) {
96 child = newChild;
98 else {
99 KTreeViewItem* lastChild = getChild();
100 while (lastChild->hasSibling()) {
101 lastChild = lastChild->getSibling();
103 lastChild->sibling = newChild;
105 newChild->sibling = 0;
106 numChildren++;
109 // returns the bounding rectangle of the item content (not including indent
110 // and branches) for hit testing
111 QRect KTreeViewItem::boundingRect(int indent) const
113 const QFontMetrics& fm = owner->fontMetrics();
114 int rectX = indent;
115 int rectY = 0;
116 int rectW = width(indent, fm) - rectX;
117 int rectH = height(fm);
118 return QRect(rectX, rectY, rectW, rectH);
121 // returns the child item at the specified index
122 KTreeViewItem* KTreeViewItem::childAt(int index) const
124 if (!hasChild())
125 return 0;
126 KTreeViewItem* item = getChild();
127 while (index > 0 && item != 0) {
128 item = item->getSibling();
129 index--;
131 return item;
134 // returns the number of children this item has
135 uint KTreeViewItem::childCount() const
137 return numChildren;
140 /* returns the index of the given child item in this item's branch */
141 int KTreeViewItem::childIndex(KTreeViewItem* searched) const
143 assert(searched != 0);
144 int index = 0;
145 KTreeViewItem* item = getChild();
146 while (item != 0 && item != searched) {
147 item = item->getSibling();
148 index++;
150 return item == 0 ? -1 : index;
153 // indicates whether mouse press is in expand button
154 inline bool KTreeViewItem::expandButtonClicked(const QPoint& coord) const
156 return expandButton.contains(coord);
159 // returns a pointer to first child item
160 KTreeViewItem* KTreeViewItem::getChild() const
162 return child;
165 // returns the parent of this item
166 KTreeViewItem* KTreeViewItem::getParent() const
168 return parent;
171 // returns reference to the item pixmap
172 const QPixmap& KTreeViewItem::getPixmap() const
174 return pixmap;
177 // returns the sibling below this item
178 KTreeViewItem* KTreeViewItem::getSibling() const
180 return sibling;
183 // returns a pointer to the item text
184 const QString& KTreeViewItem::getText() const
186 return text;
189 // indicates whether this item has any children
190 bool KTreeViewItem::hasChild() const
192 return child != 0;
195 // indicates whether this item has a parent
196 bool KTreeViewItem::hasParent() const
198 return parent != 0;
201 // indicates whether this item has a sibling below it
202 bool KTreeViewItem::hasSibling() const
204 return sibling != 0;
207 int KTreeViewItem::height() const
209 assert(owner != 0);
210 return height(owner->fontMetrics());
213 // returns the maximum height of text and pixmap including margins
214 int KTreeViewItem::height(const QFontMetrics& fm) const
216 int maxHeight = pixmap.height();
217 int textHeight = fm.height();
218 maxHeight = textHeight > maxHeight ? textHeight : maxHeight;
219 return maxHeight + 4;
222 // inserts child item at specified index in branch
223 void KTreeViewItem::insertChild(int index, KTreeViewItem* newChild)
225 if (index < 0) {
226 appendChild(newChild);
227 return;
229 newChild->parent = this;
230 newChild->setOwner(owner);
231 if (index == 0) {
232 newChild->sibling = getChild();
233 child = newChild;
235 else {
236 KTreeViewItem* prevItem = getChild();
237 KTreeViewItem* nextItem = prevItem->getSibling();
238 while (--index > 0 && nextItem) {
239 prevItem = nextItem;
240 nextItem = prevItem->getSibling();
242 prevItem->sibling = newChild;
243 newChild->sibling = nextItem;
245 numChildren++;
248 // indicates whether this item is displayed expanded
249 // NOTE: a TRUE response does not necessarily indicate the item
250 // has any children
251 bool KTreeViewItem::isExpanded() const
253 return expanded;
256 // returns true if all ancestors are expanded
257 bool KTreeViewItem::isVisible() const
259 for (KTreeViewItem* p = getParent(); p != 0; p = p->getParent()) {
260 if (!p->isExpanded())
261 return false;
263 return true;
266 bool KTreeViewItem::keyEvent(QKeyEvent* /*ev*/)
268 return false; /* not handled */
271 bool KTreeViewItem::mouseEvent(QMouseEvent* /*ev*/, const QPoint& /*itemCoord*/)
273 return false; /* not handled */
276 // paint this item, including tree branches and expand button
277 void KTreeViewItem::paint(QPainter* p, int indent, const QColorGroup& cg,
278 bool highlighted) const
280 assert(getParent() != 0); /* can't paint root item */
281 assert(owner != 0);
283 p->save();
285 int cellHeight = height(p->fontMetrics());
287 if (doTree) {
288 paintTree(p, indent, cellHeight, cg);
292 * If this item has at least one child and expand button drawing is
293 * enabled, set the rect for the expand button for later mouse press
294 * bounds checking, and draw the button.
296 if (doExpandButton && (child || delayedExpanding)) {
297 paintExpandButton(p, indent, cellHeight, cg);
300 // now draw the item pixmap and text, if applicable
301 int pixmapX = indent;
302 int pixmapY = (cellHeight - pixmap.height()) / 2;
303 p->drawPixmap(pixmapX, pixmapY, pixmap);
305 if (doText) {
306 paintText(p, indent, cellHeight, cg, highlighted);
308 p->restore();
311 void KTreeViewItem::paintExpandButton(QPainter* p, int indent, int cellHeight,
312 const QColorGroup& cg) const
314 int parentLeaderX = indent - (owner->itemIndent / 2);
315 int cellCenterY = cellHeight / 2;
317 QRect paintRect(parentLeaderX - 4, cellCenterY - 4, 9, 9);
318 p->setBrush(cg.base());
319 p->setPen(cg.foreground());
320 p->drawRect(paintRect);
321 if (expanded) {
322 p->drawLine(parentLeaderX - 2, cellCenterY,
323 parentLeaderX + 2, cellCenterY);
324 } else {
325 p->drawLine(parentLeaderX - 2, cellCenterY,
326 parentLeaderX + 2, cellCenterY);
327 p->drawLine(parentLeaderX, cellCenterY - 2,
328 parentLeaderX, cellCenterY + 2);
330 p->setBrush(NoBrush);
333 * The area that the user can click to collapse and expand the tree is
334 * somewhat larger than the painted expand button.
336 expandButton.setRect(indent - owner->itemIndent, 0,
337 owner->itemIndent, cellHeight);
340 // paint the highlight
341 void KTreeViewItem::paintHighlight(QPainter* p, int indent, const QColorGroup& colorGroup,
342 bool hasFocus) const
344 QColor fc;
345 fc = colorGroup.text();
346 QRect textRect = textBoundingRect(indent);
347 int t,l,b,r;
348 textRect.coords(&l, &t, &r, &b);
349 QRect outerRect;
350 outerRect.setCoords(l - 2, t - 2, r + 2, b + 2);
351 if (hasFocus) {
352 p->fillRect(textRect, fc); /* highlight background */
353 p->setPen(fc);
354 p->drawRect(outerRect);
355 } else {
356 p->fillRect(outerRect, fc); /* highlight background */
360 // draw the text, highlighted if requested
361 void KTreeViewItem::paintText(QPainter* p, int indent, int cellHeight,
362 const QColorGroup& cg, bool highlighted) const
364 int textX = indent + pixmap.width() + 3;
365 int textY = (cellHeight - p->fontMetrics().height()) / 2 +
366 p->fontMetrics().ascent();
368 if (highlighted) {
369 paintHighlight(p, indent, cg, owner->hasFocus());
370 p->setPen(cg.base());
371 p->setBackgroundColor(cg.text());
373 else {
374 p->setPen(cg.text());
375 p->setBackgroundColor(cg.base());
377 p->drawText(textX, textY, text);
380 // paint the tree structure
381 void KTreeViewItem::paintTree(QPainter* p, int indent, int cellHeight,
382 const QColorGroup& cg) const
384 int parentLeaderX = indent - (owner->itemIndent / 2);
385 int cellCenterY = cellHeight / 2;
386 int cellBottomY = cellHeight - 1;
387 int itemLeaderX = indent - 1;
389 p->setPen(cg.background());
392 * If this is not the first item in the tree draw the line up
393 * towards parent or sibling.
395 if (parent->parent != 0 || parent->getChild() != this)
396 p->drawLine(parentLeaderX, 0, parentLeaderX, cellCenterY);
398 // draw the line down towards sibling
399 if (sibling)
400 p->drawLine(parentLeaderX, cellCenterY, parentLeaderX, cellBottomY);
403 * If this item has children or siblings in the tree or is a child of
404 * an item other than the root item then draw the little line from the
405 * item out to the left.
407 if (sibling || (doExpandButton && (child || delayedExpanding)) ||
408 parent->parent != 0 ||
410 * The following handles the case of an item at the end of the
411 * topmost level which doesn't have children.
413 parent->getChild() != this)
415 p->drawLine(parentLeaderX, cellCenterY, itemLeaderX, cellCenterY);
419 * If there are siblings of ancestors below, draw our portion of the
420 * branches that extend through this cell.
422 KTreeViewItem* prevRoot = parent;
423 while (prevRoot->getParent() != 0) { /* while not root item */
424 assert(prevRoot->owner == owner);
425 if (prevRoot->hasSibling()) {
426 int sibLeaderX = owner->indentation(prevRoot) - (owner->itemIndent / 2);
427 p->drawLine(sibLeaderX, 0, sibLeaderX, cellBottomY);
429 prevRoot = prevRoot->getParent();
433 // removes the given (direct) child from the branch
434 bool KTreeViewItem::removeChild(KTreeViewItem* theChild)
436 // search item in list of children
437 KTreeViewItem* prevItem = 0;
438 KTreeViewItem* toRemove = getChild();
439 while (toRemove && toRemove != theChild) {
440 prevItem = toRemove;
441 toRemove = toRemove->getSibling();
444 if (toRemove) {
445 // found it!
446 if (prevItem == 0) {
447 child = toRemove->getSibling();
448 } else {
449 prevItem->sibling = toRemove->getSibling();
451 numChildren--;
452 toRemove->setOwner(0);
454 assert((numChildren == 0) == (child == 0));
456 return toRemove != 0;
459 // sets the delayed-expanding flag
460 void KTreeViewItem::setDelayedExpanding(bool flag)
462 delayedExpanding = flag;
465 // tells the item whether it shall delete child items
466 void KTreeViewItem::setDeleteChildren(bool flag)
468 deleteChildren = flag;
471 // sets the draw expand button flag of this item
472 void KTreeViewItem::setDrawExpandButton(bool doit)
474 doExpandButton = doit;
477 // sets the draw text flag of this item
478 void KTreeViewItem::setDrawText(bool doit)
480 doText = doit;
483 // sets the draw tree branch flag of this item
484 void KTreeViewItem::setDrawTree(bool doit)
486 doTree = doit;
489 // sets the expanded flag of this item
490 void KTreeViewItem::setExpanded(bool is)
492 expanded = is;
495 // sets the owner of this item and its children siblings
496 void KTreeViewItem::setOwner(KTreeView* newOwner, bool includeSiblings)
498 /* Note: set owner of children's siblings! */
499 owner = newOwner;
500 if (getChild())
501 getChild()->setOwner(newOwner, true);
502 if (includeSiblings && getSibling())
503 getSibling()->setOwner(newOwner, true);
506 // sets the item pixmap to the given pixmap
507 void KTreeViewItem::setPixmap(const QPixmap& pm)
509 pixmap = pm;
512 // sets the item text to the given string
513 void KTreeViewItem::setText(const QString& t)
515 text = t;
518 // counts the child items and stores the result in numChildren
519 void KTreeViewItem::synchNumChildren()
521 numChildren = 0;
522 KTreeViewItem* item = getChild();
523 while (item != 0) {
524 numChildren++;
525 item = item->getSibling();
530 * returns the bounding rect of the item text in cell coordinates couldn't
531 * get QFontMetrics::boundingRect() to work right so I made my own
533 QRect KTreeViewItem::textBoundingRect(int indent) const
535 const QFontMetrics& fm = owner->fontMetrics();
536 int cellHeight = height(fm);
537 int rectX = indent + pixmap.width() + 2;
538 int rectY = (cellHeight - fm.height()) / 2;
539 int rectW = fm.width(text) + 2;
540 int rectH = fm.height();
541 return QRect(rectX, rectY, rectW, rectH);
544 // returns the total width of text and pixmap, including margins, spacing
545 // and indent, or -1 if empty -- SHOULD NEVER BE -1!
546 int KTreeViewItem::width(int indent) const
548 return width(indent, owner->fontMetrics());
551 // The width of an item is composed of the following:
552 // - indentation (indent with times level)
553 // - pixmap width
554 // - 2 pixels for highlight border
555 // - 1 pixel margin
556 // - text width
557 // - 1 pixel margin
558 // - 2 pixels for highlight border
559 int KTreeViewItem::width(int indent, const QFontMetrics& fm) const
561 return indent + pixmap.width() + fm.width(text) + 6;
566 * -------------------------------------------------------------------
568 * KTreeView
570 * -------------------------------------------------------------------
573 // constructor
574 KTreeView::KTreeView(QWidget *parent,
575 const char *name,
576 WFlags f) :
577 TableView(parent, name, f),
578 clearing(false),
579 current(-1),
580 drawExpandButton(true),
581 drawTree(true),
582 expansion(0),
583 goingDown(false),
584 itemIndent(18),
585 showText(true),
586 moveCurrentToSibling(false),
587 itemCapacity(500),
588 visibleItems(0),
589 rubberband_mode(false)
591 setNumRows(0);
592 setNumCols(1);
593 setBackgroundColor(colorGroup().base());
595 treeRoot = new KTreeViewItem;
596 treeRoot->setExpanded(true);
597 treeRoot->owner = this;
599 visibleItems = new KTreeViewItem*[itemCapacity];
600 // clear those pointers
601 for (int j = itemCapacity-1; j >= 0; j--) {
602 visibleItems[j] = 0;
606 // destructor
607 KTreeView::~KTreeView()
609 goingDown = true;
610 clear();
611 delete[] visibleItems;
612 delete treeRoot;
615 // appends a child to the item at the given index with the given text
616 // and pixmap
617 void KTreeView::appendChildItem(const char* theText, const QPixmap& thePixmap,
618 int index)
620 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
621 item->setDeleteChildren(true);
622 appendChildItem(item, index);
625 // appends a child to the item at the end of the given path with
626 // the given text and pixmap
627 void KTreeView::appendChildItem(const char* theText, const QPixmap& thePixmap,
628 const KPath& thePath)
630 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
631 item->setDeleteChildren(true);
632 appendChildItem(item, thePath);
635 // appends the given item to the children of the item at the given index
636 void KTreeView::appendChildItem(KTreeViewItem* newItem, int index)
638 /* find parent item and append new item to parent's sub tree */
639 KTreeViewItem* parentItem = itemAt(index);
640 if (!parentItem)
641 return;
642 appendChildItem(parentItem, newItem);
645 // appends the given item to the children of the item at the end of the
646 // given path
647 void KTreeView::appendChildItem(KTreeViewItem* newItem, const KPath& thePath)
649 /* find parent item and append new item to parent's sub tree */
650 KTreeViewItem* parentItem = itemAt(thePath);
651 if (!parentItem)
652 return;
653 appendChildItem(parentItem, newItem);
656 // translate mouse coord to cell coord
657 QPoint KTreeView::cellCoords(int row, const QPoint& widgetCoord)
659 int cellX = 0, cellY = 0;
660 colXPos(0, &cellX);
661 rowYPos(row, &cellY);
662 return QPoint(widgetCoord.x() - cellX, widgetCoord.y() - cellY);
665 // find item at specified index and change pixmap and/or text
666 void KTreeView::changeItem(const char *newText,
667 const QPixmap *newPixmap,
668 int index)
670 KTreeViewItem *item = itemAt(index);
671 if(item)
672 changeItem(item, index, newText, newPixmap);
675 // find item at end of specified path, and change pixmap and/or text
676 void KTreeView::changeItem(const char* newText,
677 const QPixmap* newPixmap,
678 const KPath& thePath)
680 KTreeViewItem* item = itemAt(thePath);
681 if (item) {
682 int index = itemRow(item);
683 changeItem(item, index, newText, newPixmap);
687 // clear all items from list and erase display
688 void KTreeView::clear()
690 setCurrentItem(-1);
692 /* somewhat of a hack for takeItem so it doesn't update the current item... */
693 clearing = TRUE;
695 bool autoU = autoUpdate();
696 setAutoUpdate(FALSE);
697 QStack<KTreeViewItem> stack;
698 stack.push(treeRoot);
699 while(!stack.isEmpty()) {
700 KTreeViewItem *item = stack.pop();
701 if(item->hasChild()) {
702 stack.push(item);
703 stack.push(item->getChild());
705 else if(item->hasSibling()) {
706 stack.push(item);
707 stack.push(item->getSibling());
709 else if(item->getParent() != 0) {
710 takeItem(item);
711 delete item;
714 clearing = FALSE;
715 if(goingDown || QApplication::closingDown())
716 return;
717 setAutoUpdate(autoU);
718 if(autoU && isVisible())
719 repaint();
722 // return a count of all the items in the tree, whether visible or not
723 uint KTreeView::count()
725 int total = 0;
726 forEveryItem(&KTreeView::countItem, (void *)&total);
727 return total;
730 // returns the index of the current (highlighted) item
731 int KTreeView::currentItem() const
733 return current;
736 // collapses the item at the specified row index.
737 void KTreeView::collapseItem(int index, bool emitSignal)
739 KTreeViewItem* item = itemAt(index);
740 if (item)
741 collapseSubTree(item, emitSignal);
744 // expands the item at the specified row indes.
745 void KTreeView::expandItem(int index, bool emitSignal)
747 KTreeViewItem* item = itemAt(index);
748 if (item)
749 expandSubTree(item, emitSignal);
752 // returns the depth the tree is automatically expanded to when
753 // items are added
754 int KTreeView::expandLevel() const
756 return expansion;
759 // visits every item in the tree, visible or not and applies
760 // the user supplied function with the item and user data passed as parameters
761 // if user supplied function returns true, traversal ends and function returns
762 bool KTreeView::forEveryItem(KForEveryFunc func, void* user, KTreeViewItem* item)
764 if (item == 0) {
765 item = treeRoot;
767 assert(item->owner == this);
768 item = item->getChild();
770 while (item != 0) {
771 // visit the siblings
772 if ((*func)(item, user)) {
773 return true;
775 // visit the children (recursively)
776 if (item->hasChild()) {
777 if (forEveryItem(func, user, item))
778 return true;
780 item = item->getSibling();
782 return false;
785 // visits every visible item in the tree in order and applies the
786 // user supplied function with the item and user data passed as parameters
787 // if user supplied function returns TRUE, traversal ends and function
788 // returns
789 bool KTreeView::forEveryVisibleItem(KForEveryFunc func, void *user,
790 KTreeViewItem* item)
792 if (item == 0) {
793 item = treeRoot;
794 } else {
795 // children are invisible (hence, nothing to do)
796 // if item is invisible or collapsed
797 if (!item->isVisible() || !item->isExpanded()) {
798 return false;
801 assert(item->owner == this);
802 item = item->getChild();
804 while (item != 0) {
805 // visit the siblings
806 if ((*func)(item, user)) {
807 return true;
809 // visit the children (recursively)
810 if (item->hasChild() && item->isExpanded()) {
811 if (forEveryVisibleItem(func, user, item))
812 return true;
814 item = item->getSibling();
816 return false;
819 // returns a pointer to the KTreeViewItem at the current index
820 // or 0 if no current item
821 KTreeViewItem *KTreeView::getCurrentItem()
823 if(current == -1) return 0;
824 return itemAt(current);
827 // returns the current indent spacing
828 int KTreeView::indentSpacing()
830 return itemIndent;
833 // inserts a new item with the specified text and pixmap before
834 // or after the item at the given index, depending on the value
835 // of prefix
836 // if index is negative, appends item to tree at root level
837 bool KTreeView::insertItem(const char* theText, const QPixmap& thePixmap,
838 int row, bool prefix)
840 KTreeViewItem* refItem = itemAt(row);
842 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
843 item->setDeleteChildren(true);
845 bool success = insertItem(refItem, item, prefix);
846 if (!success)
847 delete item;
848 return success;
851 // inserts a new item with the specified text and pixmap before
852 // or after the item at the end of the given path, depending on the value
853 // of prefix
854 bool KTreeView::insertItem(const char* theText, const QPixmap& thePixmap,
855 const KPath& path, bool prefix)
857 KTreeViewItem* refItem = itemAt(path);
859 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
860 item->setDeleteChildren(true);
862 bool success = insertItem(refItem, item, prefix);
863 if (!success)
864 delete item;
865 return success;
868 // inserts the given item or derived object into the tree before
869 // or after the item at the given index, depending on the value
870 // of prefix
871 // if index is negative, appends item to tree at root level
872 bool KTreeView::insertItem(KTreeViewItem* newItem,
873 int index, bool prefix)
875 // find the item currently at the index, if there is one
876 KTreeViewItem* refItem = itemAt(index);
878 // insert new item at the appropriate place
879 return insertItem(refItem, newItem, prefix);
882 // inserts the given item or derived object into the tree before
883 // or after the item at the end of the given path, depending on the value
884 // of prefix
885 bool KTreeView::insertItem(KTreeViewItem* newItem,
886 const KPath& thePath, bool prefix)
888 // find the item currently at the end of the path, if there is one
889 KTreeViewItem* refItem = itemAt(thePath);
891 // insert new item at appropriate place
892 return insertItem(refItem, newItem, prefix);
896 * returns pointer to KTreeViewItem at the specifed row or 0 if row is out
897 * of limits.
899 KTreeViewItem* KTreeView::itemAt(int row) const
901 if (row < 0 || row >= numRows()) {
902 return 0;
904 else {
905 // lookup the item in the list of visible items
906 assert(row < itemCapacity);
907 KTreeViewItem* i = visibleItems[row];
908 assert(i != 0);
909 return i;
913 // returns pointer to KTreeViewItem at the end of the
914 // path or 0 if not found
915 KTreeViewItem* KTreeView::itemAt(const KPath& path)
917 if (path.isEmpty())
918 return 0;
920 // need a copy of the path because recursiveFind will destroy it
921 KPath pathCopy;
922 pathCopy.setAutoDelete(false);
923 pathCopy = path;
925 return recursiveFind(pathCopy);
928 // computes the path of the item at the specified index
929 // if index is invalid, nothing is done.
930 void KTreeView::itemPath(int row, KPath& path)
932 KTreeViewItem* item = itemAt(row);
933 if (item != 0) {
934 itemPath(item, path);
938 // returns the row in the visible tree of the given item or
939 // -1 if not found
940 int KTreeView::itemRow(KTreeViewItem* item)
942 if (item->owner == this) {
943 // search in list of visible items
944 for (int i = numRows()-1; i >= 0; i--) {
945 if (visibleItems[i] == item) {
946 return i;
950 // not found
951 return -1;
955 * move the subtree at the specified index up one branch level (make root
956 * item a sibling of its current parent)
958 void KTreeView::join(int index)
960 KTreeViewItem *item = itemAt(index);
961 if(item)
962 join(item);
966 * move the subtree at the specified index up one branch level (make root
967 * item a sibling of it's current parent)
969 void KTreeView::join(const KPath& path)
971 KTreeViewItem *item = itemAt(path);
972 if (item)
973 join(item);
976 /* move item at specified index one slot down in its parent's sub tree */
977 void KTreeView::lowerItem(int index)
979 KTreeViewItem *item = itemAt(index);
980 if(item)
981 lowerItem(item);
984 /* move item at specified path one slot down in its parent's sub tree */
985 void KTreeView::lowerItem(const KPath& path)
987 KTreeViewItem* item = itemAt(path);
988 if (item)
989 lowerItem(item);
992 /* move item at specified index one slot up in its parent's sub tree */
993 void KTreeView::raiseItem(int index)
995 KTreeViewItem* item = itemAt(index);
996 if (item)
997 raiseItem(item);
1000 /* move item at specified path one slot up in its parent's sub tree */
1001 void KTreeView::raiseItem(const KPath& path)
1003 KTreeViewItem* item = itemAt(path);
1004 if (item)
1005 raiseItem(item);
1008 // remove the item at the specified index and delete it
1009 void KTreeView::removeItem(int index)
1011 KTreeViewItem *item = itemAt(index);
1012 if(item) {
1013 takeItem(item);
1014 delete item;
1018 // remove the item at the end of the specified path and delete it
1019 void KTreeView::removeItem(const KPath& thePath)
1021 KTreeViewItem* item = itemAt(thePath);
1022 if (item) {
1023 takeItem(item);
1024 delete item;
1028 void KTreeView::scrollVisible(KTreeViewItem* item, bool children)
1030 if (item == 0)
1031 return;
1032 int row = itemRow(item);
1033 if (row < 0)
1034 return; /* do nothing if invisible */
1036 if (children && item->isExpanded()) {
1037 // we are concerned about children
1038 if (!rowIsVisible(row)) {
1039 // just move to the top
1040 setTopCell(row);
1041 } else {
1042 // this is the complicated part
1043 // count the visible children (including grandchildren)
1044 int numVisible = 0;
1045 forEveryVisibleItem(countItem, &numVisible, item);
1046 // if the last child is visible, do nothing
1047 if (rowIsVisible(row + numVisible))
1048 return;
1050 * Basically, item will become the top cell; but if there are
1051 * more visible rows in the widget than we have children, then
1052 * we don't move that far.
1054 int remain = lastRowVisible()-topCell()-numVisible;
1055 if (remain <= 0) {
1056 setTopCell(row);
1057 } else {
1058 setTopCell(QMAX(0,row-remain));
1061 } else {
1062 // we are not concerned about children
1063 if (rowIsVisible(row))
1064 return;
1065 // just move the item to the top
1066 setTopCell(row);
1070 // sets the current item and hightlights it, emitting signals
1071 void KTreeView::setCurrentItem(int row)
1073 if (row == current)
1074 return;
1075 int numVisible = numRows();
1076 if (row > numVisible)
1077 return;
1078 int oldCurrent = current;
1079 current = row;
1080 if (oldCurrent < numVisible)
1081 updateCell(oldCurrent, 0);
1082 if (current > -1) {
1083 updateCell(current, 0, false);
1084 emit highlighted(current);
1088 // enables/disables drawing of expand button
1089 void KTreeView::setExpandButtonDrawing(bool enable)
1091 if (drawExpandButton == enable)
1092 return;
1093 drawExpandButton = enable;
1095 // the user parameter is cast to a bool in setItemExpandButtonDrawing
1096 forEveryItem(&KTreeView::setItemExpandButtonDrawing, enable ? &enable : 0);
1098 if (autoUpdate() && isVisible())
1099 repaint();
1102 // sets depth to which subtrees are automatically expanded, and
1103 // redraws tree if auto update enabled
1104 void KTreeView::setExpandLevel(int level)
1106 if (expansion == level)
1107 return;
1108 expansion = level;
1109 KTreeViewItem* item = getCurrentItem();
1110 forEveryItem(&KTreeView::setItemExpandLevel, 0);
1111 while (item != 0) {
1112 if (item->getParent()->isExpanded())
1113 break;
1114 item = item->getParent();
1116 if (item != 0)
1117 setCurrentItem(itemRow(item));
1118 if (autoUpdate() && isVisible())
1119 repaint();
1122 // sets the indent margin for all branches and repaints if auto update enabled
1123 void KTreeView::setIndentSpacing(int spacing)
1125 if (itemIndent == spacing)
1126 return;
1127 itemIndent = spacing;
1128 updateCellWidth();
1129 if (autoUpdate() && isVisible())
1130 repaint();
1133 void KTreeView::setMoveCurrentToSibling(bool m)
1135 moveCurrentToSibling = m;
1138 // enables/disables display of item text (keys)
1139 void KTreeView::setShowItemText(bool enable)
1141 if (showText == enable)
1142 return;
1143 showText = enable;
1145 // the user parameter is cast to a bool in setItemShowText
1146 forEveryItem(&KTreeView::setItemShowText, enable ? &enable : 0);
1148 if (autoUpdate() && isVisible())
1149 repaint();
1152 // enables/disables tree branch drawing
1153 void KTreeView::setTreeDrawing(bool enable)
1155 if (drawTree == enable)
1156 return;
1157 drawTree = enable;
1159 // the user parameter is cast to a bool in setItemTreeDrawing
1160 forEveryItem(&KTreeView::setItemTreeDrawing, enable ? &enable : 0);
1162 if (autoUpdate() && isVisible())
1163 repaint();
1166 // indicates whether item text keys are displayed
1167 bool KTreeView::showItemText() const
1169 return showText;
1172 // indents the item at the given index, splitting the tree into
1173 // a new branch
1174 void KTreeView::split(int index)
1176 KTreeViewItem *item = itemAt(index);
1177 if(item)
1178 split(item);
1181 // indents the item at the given path, splitting the tree into
1182 // a new branch
1183 void KTreeView::split(const KPath& path)
1185 KTreeViewItem* item = itemAt(path);
1186 if (item)
1187 split(item);
1190 // removes item at specified index from tree but does not delete it
1191 // returns pointer to the item or 0 if not succesful
1192 KTreeViewItem *KTreeView::takeItem(int index)
1194 KTreeViewItem *item = itemAt(index);
1195 if(item)
1196 takeItem(item);
1197 return item;
1200 // removes item at specified path from tree but does not delete it
1201 // returns pointer to the item or 0 if not successful
1202 KTreeViewItem* KTreeView::takeItem(const KPath& path)
1204 KTreeViewItem* item = itemAt(path);
1205 if (item)
1206 takeItem(item);
1207 return item;
1210 // indicates whether tree branches are drawn
1211 bool KTreeView::treeDrawing() const
1213 return drawTree;
1217 // appends a child to the specified parent item (note: a child, not a sibling, is added!)
1218 void KTreeView::appendChildItem(KTreeViewItem* theParent,
1219 KTreeViewItem* newItem)
1221 theParent->appendChild(newItem);
1223 // set item state
1224 newItem->setDrawExpandButton(drawExpandButton);
1225 newItem->setDrawTree(drawTree);
1226 newItem->setDrawText(showText);
1227 if (level(newItem) < expansion) {
1228 newItem->setExpanded(true);
1231 // fix up branch levels of any children that the new item may already have
1232 if(newItem->hasChild()) {
1233 fixChildren(newItem);
1236 // if necessary, adjust cell width, number of rows and repaint
1237 if (newItem->isVisible() || theParent->childCount() == 1) {
1238 bool autoU = autoUpdate();
1239 setAutoUpdate(false);
1240 updateVisibleItems();
1241 setAutoUpdate(autoU);
1242 if (autoU && isVisible())
1243 repaint();
1247 // returns the height of the cell(row) at the specified row (index)
1248 int KTreeView::cellHeight(int row) const
1250 return itemAt(row)->height(fontMetrics());
1253 // returns the width of the cells. Note: this is mostly for derived classes
1254 // which have more than 1 column
1255 int KTreeView::cellWidth(int /*col*/) const
1257 return maxItemWidth;
1260 // changes the given item with the new text and/or pixmap
1261 void KTreeView::changeItem(KTreeViewItem* toChange, int itemRow,
1262 const char* newText, const QPixmap* newPixmap)
1264 int indent = indentation(toChange);
1265 int oldWidth = toChange->width(indent);
1266 if(newText)
1267 toChange->setText(newText);
1268 if (newPixmap)
1269 toChange->setPixmap(*newPixmap);
1270 if(oldWidth != toChange->width(indent))
1271 updateCellWidth();
1272 if(itemRow == -1)
1273 return;
1274 if(autoUpdate() && rowIsVisible(itemRow))
1275 updateCell(itemRow, 0);
1278 // collapses the subtree at the specified item
1279 void KTreeView::collapseSubTree(KTreeViewItem* subRoot, bool emitSignal)
1281 assert(subRoot->owner == this);
1282 if (!subRoot->isExpanded())
1283 return;
1285 // must move the current item if it is visible
1286 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1288 subRoot->setExpanded(false);
1289 if (subRoot->isVisible()) {
1290 bool autoU = autoUpdate();
1291 setAutoUpdate(false);
1292 updateVisibleItems();
1293 // re-seat current item
1294 if (cur != 0) {
1295 setCurrentItem(itemRow(cur));
1297 if (emitSignal) {
1298 emit collapsed(itemRow(subRoot));
1300 setAutoUpdate(autoU);
1301 if (autoU && isVisible())
1302 repaint();
1306 // used by count() with forEach() function to count total number
1307 // of items in the tree
1308 bool KTreeView::countItem(KTreeViewItem*, void* total)
1310 int* t = static_cast<int*>(total);
1311 (*t)++;
1312 return false;
1315 // expands the subtree at the given item
1316 void KTreeView::expandSubTree(KTreeViewItem* subRoot, bool emitSignal)
1318 assert(subRoot->owner == this);
1319 if (subRoot->isExpanded())
1320 return;
1322 // must move the current item if it is visible
1323 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1325 bool allow = true;
1327 if (subRoot->delayedExpanding) {
1328 emit expanding(subRoot, allow);
1330 if (!allow)
1331 return;
1333 subRoot->setExpanded(true);
1335 if (subRoot->isVisible()) {
1336 bool autoU = autoUpdate();
1337 setAutoUpdate(false);
1338 updateVisibleItems();
1339 // re-seat current item
1340 if (cur != 0) {
1341 setCurrentItem(itemRow(cur));
1343 if (emitSignal) {
1344 emit expanded(itemRow(subRoot));
1346 setAutoUpdate(autoU);
1347 if (autoU && isVisible())
1348 repaint();
1352 // fix up branch levels out of whack from split/join operations on the tree
1353 void KTreeView::fixChildren(KTreeViewItem *parentItem)
1355 KTreeViewItem* childItem = 0;
1356 KTreeViewItem* siblingItem = 0;
1357 // int childBranch = parentItem->getBranch() + 1;
1358 if(parentItem->hasChild()) {
1359 childItem = parentItem->getChild();
1360 // childItem->setBranch(childBranch);
1361 childItem->owner = parentItem->owner;
1362 fixChildren(childItem);
1364 while(childItem && childItem->hasSibling()) {
1365 siblingItem = childItem->getSibling();
1366 // siblingItem->setBranch(childBranch);
1367 siblingItem->owner = parentItem->owner;
1368 fixChildren(siblingItem);
1369 childItem = siblingItem;
1374 * Handle QFocusEvent processing by setting current item to top row if
1375 * there is no current item, and updates cell to add or delete the focus
1376 * rectangle on the highlight bar. The base class is not called because it
1377 * does a repaint() which causes flicker.
1379 void KTreeView::focusInEvent(QFocusEvent *)
1381 if (current < 0 && numRows() > 0)
1382 setCurrentItem(topCell());
1383 updateCell(current, 0);
1386 void KTreeView::focusOutEvent(QFocusEvent *)
1388 updateCell(current, 0);
1391 // called by updateCellWidth() for each item in the visible list
1392 bool KTreeView::getMaxItemWidth(KTreeViewItem* item, void* user)
1394 assert(item->owner != 0);
1395 int indent = item->owner->indentation(item);
1396 int* maxW = static_cast<int*>(user);
1397 int w = item->width(indent);
1398 if (w > *maxW)
1399 *maxW = w;
1400 return false;
1403 int KTreeView::indentation(KTreeViewItem* item) const
1405 return level(item) * itemIndent + itemIndent + 3;
1408 // inserts the new item before or after the reference item, depending
1409 // on the value of prefix
1410 bool KTreeView::insertItem(KTreeViewItem* referenceItem,
1411 KTreeViewItem* newItem,
1412 bool prefix)
1414 assert(newItem != 0);
1415 assert(referenceItem == 0 || referenceItem->owner == this);
1417 /* set the new item's state */
1418 newItem->setDrawExpandButton(drawExpandButton);
1419 newItem->setDrawTree(drawTree);
1420 newItem->setDrawText(showText);
1421 KTreeViewItem* parentItem;
1422 if (referenceItem) {
1423 parentItem = referenceItem->getParent();
1424 int insertIndex = parentItem->childIndex(referenceItem);
1425 if (!prefix)
1426 insertIndex++;
1427 parentItem->insertChild(insertIndex, newItem);
1429 else {
1430 // no reference item, append at end of visible tree
1431 // need to repaint
1432 parentItem = treeRoot;
1433 parentItem->appendChild(newItem);
1436 // set item expansion
1437 if (level(newItem) < expansion)
1438 newItem->setExpanded(true);
1440 // fix up branch levels of any children
1441 if (newItem->hasChild())
1442 fixChildren(newItem);
1444 // if repaint necessary, do it if visible and auto update
1445 // enabled
1446 if (newItem->isVisible() || parentItem->childCount() == 1) {
1447 bool autoU = autoUpdate();
1448 setAutoUpdate(FALSE);
1449 updateVisibleItems();
1450 setAutoUpdate(autoU);
1451 if (autoU && isVisible())
1452 repaint();
1454 return true;
1458 * returns pointer to item's path
1460 void KTreeView::itemPath(KTreeViewItem* item, KPath& path) const
1462 assert(item != 0);
1463 assert(item->owner == this);
1464 if (item != treeRoot) {
1465 itemPath(item->getParent(), path);
1466 path.push(new QString(item->getText()));
1471 * joins the item's branch into the tree, making the item a sibling of its
1472 * parent
1474 void KTreeView::join(KTreeViewItem *item)
1476 KTreeViewItem *itemParent = item->getParent();
1477 if(itemParent->hasParent()) {
1478 bool autoU = autoUpdate();
1479 setAutoUpdate(FALSE);
1480 takeItem(item);
1481 insertItem(itemParent, item, FALSE);
1482 setAutoUpdate(autoU);
1483 if(autoU && isVisible())
1484 repaint();
1488 // handles keyboard interface
1489 void KTreeView::keyPressEvent(QKeyEvent* e)
1491 if (numRows() == 0)
1492 return; /* nothing to do */
1494 /* if there's no current item, make the top item current */
1495 if (currentItem() < 0)
1496 setCurrentItem(topCell());
1497 assert(currentItem() >= 0); /* we need a current item */
1498 assert(itemAt(currentItem()) != 0); /* we really need a current item */
1500 // give currentItem a chance to handle the event
1501 if (itemAt(currentItem())->keyEvent(e))
1502 return; /* handled */
1504 int pageSize, delta;
1505 KTreeViewItem* item;
1506 int key = e->key();
1507 repeat:
1508 switch (key) {
1509 case Key_Up:
1510 // make previous item current, scroll up if necessary
1511 if (currentItem() > 0) {
1512 setCurrentItem(currentItem() - 1);
1513 scrollVisible(itemAt(currentItem()), false);
1515 break;
1516 case Key_Down:
1517 // make next item current, scroll down if necessary
1518 if (currentItem() < numRows() - 1) {
1519 setCurrentItem(currentItem() + 1);
1520 if (currentItem() > lastRowVisible()) {
1521 // scrollVisible is not feasible here because
1522 // it scrolls the item to the top
1523 setTopCell(topCell() + currentItem() - lastRowVisible());
1524 } else if (currentItem() < topCell()) {
1525 setTopCell(currentItem());
1528 break;
1529 case Key_Next:
1530 // move highlight one page down and scroll down
1531 delta = currentItem() - topCell();
1532 pageSize = lastRowVisible() - topCell();
1533 setTopCell(QMIN(topCell() + pageSize, numRows() - 1));
1534 setCurrentItem(QMIN(topCell() + delta, numRows() - 1));
1535 break;
1536 case Key_Prior:
1537 // move highlight one page up and scroll up
1538 delta = currentItem() - topCell();
1539 pageSize = lastRowVisible() - topCell();
1540 setTopCell(QMAX(topCell() - pageSize, 0));
1541 setCurrentItem(QMAX(topCell() + delta, 0));
1542 break;
1543 case Key_Plus:
1544 case Key_Right:
1545 // if current item has subtree and is collapsed, expand it
1546 item = itemAt(currentItem());
1547 if (item->isExpanded() && item->hasChild() && key == Key_Right) {
1548 // going right on an expanded item is like going down
1549 key = Key_Down;
1550 goto repeat;
1551 } else {
1552 expandSubTree(item, true);
1553 scrollVisible(item, true);
1555 break;
1556 case Key_Minus:
1557 case Key_Left:
1558 // if current item has subtree and is expanded, collapse it
1559 item = itemAt(currentItem());
1560 if ((!item->isExpanded() || !item->hasChild()) && key == Key_Left) {
1561 // going left on a collapsed item goes to its parent
1562 item = item->getParent();
1563 if (item == treeRoot)
1564 break; /* we're already at the top */
1565 assert(item->isVisible());
1566 setCurrentItem(itemRow(item));
1567 } else {
1568 collapseSubTree(item, true);
1570 scrollVisible(item, false);
1571 break;
1572 case Key_Return:
1573 case Key_Enter:
1574 // select the current item
1575 if (currentItem() >= 0)
1576 emit selected(currentItem());
1577 break;
1578 default:
1579 break;
1583 // handles keyboard interface
1584 void KTreeView::keyReleaseEvent(QKeyEvent* e)
1586 if (currentItem() >= 0 && itemAt(currentItem()) != 0)
1587 itemAt(currentItem())->keyEvent(e);
1590 int KTreeView::level(KTreeViewItem* item) const
1592 assert(item != 0);
1593 assert(item->owner == this);
1594 assert(item != treeRoot);
1595 int l = 0;
1596 item = item->parent->parent; /* since item != treeRoot, there is a parent */
1597 while (item != 0) {
1598 item = item->parent;
1599 l++;
1601 return l;
1604 /* move specified item down one slot in parent's subtree */
1605 void KTreeView::lowerItem(KTreeViewItem *item)
1607 KTreeViewItem *itemParent = item->getParent();
1608 uint itemChildIndex = itemParent->childIndex(item);
1609 if(itemChildIndex < itemParent->childCount() - 1) {
1610 bool autoU = autoUpdate();
1611 setAutoUpdate(FALSE);
1612 takeItem(item);
1613 insertItem(itemParent->childAt(itemChildIndex), item, FALSE);
1614 setAutoUpdate(autoU);
1615 if(autoU && isVisible())
1616 repaint();
1620 // handle mouse double click events by selecting the clicked item
1621 // and emitting the signal
1622 void KTreeView::mouseDoubleClickEvent(QMouseEvent* e)
1624 // find out which row has been clicked
1625 int itemClicked = findRow(e->y());
1627 if (itemClicked < 0)
1628 return; /* invalid row, do nothing */
1630 KTreeViewItem* item = itemAt(itemClicked);
1631 if (item == 0)
1632 return;
1634 // translate mouse coord to cell coord
1635 QPoint cellCoord = cellCoords(itemClicked, e->pos());
1637 // first ask item
1638 if (item->mouseEvent(e, cellCoord))
1639 return;
1641 // hit test item
1642 int indent = indentation(item);
1643 if (item->boundingRect(indent).contains(cellCoord))
1644 emit selected(itemClicked);
1647 // handle mouse movement events
1648 void KTreeView::mouseMoveEvent(QMouseEvent* e)
1650 // in rubberband_mode we actually scroll the window now
1651 if (rubberband_mode) {
1652 move_rubberband(e->pos());
1653 } else {
1654 // else forward to current item
1655 int current = currentItem();
1656 if (current >= 0 && itemAt(current))
1657 itemAt(current)->mouseEvent(e, cellCoords(current, e->pos()));
1662 // handle single mouse presses
1663 void KTreeView::mousePressEvent(QMouseEvent* e)
1665 /* first: cancel rubberbanding if it's on */
1666 if (rubberband_mode)
1668 // another button was pressed while rubberbanding, stop the move.
1669 // RB: if we allow other buttons while rubberbanding the tree can expand
1670 // while rubberbanding - we then need to recalculate and resize the
1671 // rubberband rect and show the new size
1672 end_rubberband();
1673 return;
1676 // find out which row has been clicked
1677 int itemClicked = findRow(e->y());
1679 // nothing to do if not on valid row
1680 if (itemClicked < 0)
1681 return;
1682 KTreeViewItem* item = itemAt(itemClicked);
1683 if (!item)
1684 return;
1686 // translate mouse coord to cell coord
1687 QPoint cellCoord = cellCoords(itemClicked, e->pos());
1689 // give the item a crack
1690 if (item->mouseEvent(e, cellCoord))
1691 return; /* event eaten by item */
1693 // check for rubberbanding
1694 if (e->button() == MidButton)
1696 // RB: the MMB is hardcoded to the "rubberband" scroll mode
1697 if (!rubberband_mode) {
1698 start_rubberband(e->pos());
1700 return;
1703 if (e->button() == RightButton) {
1704 emit rightPressed(itemClicked, e->pos());
1706 /* hit test expand button (doesn't set currentItem) */
1707 else if (item->expandButtonClicked(cellCoord)) {
1708 if (item->isExpanded()) {
1709 collapseSubTree(item, true);
1710 } else {
1711 expandSubTree(item, true);
1712 scrollVisible(item, true); /* make children visible */
1715 // hit test item
1716 else if (item->boundingRect(indentation(item)).contains(cellCoord)) {
1717 setCurrentItem(itemClicked);
1721 // handle mouse release events
1722 void KTreeView::mouseReleaseEvent(QMouseEvent *e)
1724 /* if it's the MMB end rubberbanding */
1725 if (rubberband_mode) {
1726 if (e->button() == MidButton)
1727 end_rubberband();
1728 return;
1730 // forward to current item
1731 int current = currentItem();
1732 if (current >= 0 && itemAt(current))
1733 itemAt(current)->mouseEvent(e, cellCoords(current, e->pos()));
1736 // rubberband move: draw the rubberband
1737 void KTreeView::draw_rubberband()
1740 * RB: I'm using a white pen because of the XorROP mode. I would prefer
1741 * to draw the rectangle in red but I don't now how to get a pen which
1742 * draws red in XorROP mode (this depends on the background). In fact
1743 * the color should be configurable.
1746 if (!rubberband_mode) return;
1747 QPainter paint(this);
1748 paint.setPen(white);
1749 paint.setRasterOp(XorROP);
1750 paint.drawRect(xOffset()*viewWidth()/totalWidth(),
1751 yOffset()*viewHeight()/totalHeight(),
1752 rubber_width+1, rubber_height+1);
1753 paint.end();
1756 // rubberband move: start move
1757 void KTreeView::start_rubberband(const QPoint& where)
1759 if (rubberband_mode) { // Oops!
1760 end_rubberband();
1762 /* RB: Don't now, if this check is necessary */
1763 if (!viewWidth() || !viewHeight()) return;
1764 if (!totalWidth() || !totalHeight()) return;
1766 // calculate the size of the rubberband rectangle
1767 rubber_width = viewWidth()*viewWidth()/totalWidth();
1768 if (rubber_width > viewWidth()) rubber_width = viewWidth();
1769 rubber_height = viewHeight()*viewHeight()/totalHeight();
1770 if (rubber_height > viewHeight()) rubber_height = viewHeight();
1772 // remember the cursor position and the actual offset
1773 rubber_startMouse = where;
1774 rubber_startX = xOffset();
1775 rubber_startY = yOffset();
1776 rubberband_mode=TRUE;
1777 draw_rubberband();
1780 // rubberband move: end move
1781 void KTreeView::end_rubberband()
1783 if (!rubberband_mode) return;
1784 draw_rubberband();
1785 rubberband_mode = FALSE;
1788 // rubberband move: hanlde mouse moves
1789 void KTreeView::move_rubberband(const QPoint& where)
1791 if (!rubberband_mode) return;
1793 // look how much the mouse moved and calc the new scroll position
1794 QPoint delta = where - rubber_startMouse;
1795 int nx = rubber_startX + delta.x() * totalWidth() / viewWidth();
1796 int ny = rubber_startY + delta.y() * totalHeight() / viewHeight();
1798 // check the new position (and make it valid)
1799 if (nx < 0) nx = 0;
1800 else if (nx > maxXOffset()) nx = maxXOffset();
1801 if (ny < 0) ny = 0;
1802 else if (ny > maxYOffset()) ny = maxYOffset();
1804 // redraw the rubberband at the new position
1805 draw_rubberband();
1806 setOffset(nx,ny);
1807 draw_rubberband();
1811 // paints the cell at the specified row and col
1812 // col is ignored for now since there is only one
1813 void KTreeView::paintCell(QPainter* p, int row, int)
1815 KTreeViewItem* item = itemAt(row);
1816 if (item == 0)
1817 return;
1819 QColorGroup cg = colorGroup();
1820 int indent = indentation(item);
1821 item->paint(p, indent, cg,
1822 current == row); /* highlighted */
1826 /* This is needed to make the kcontrol's color setup working (Marcin Dalecki) */
1827 void KTreeView::paletteChange(const QPalette &)
1829 setBackgroundColor(colorGroup().base());
1830 repaint(true);
1834 /* raise the specified item up one slot in parent's subtree */
1835 void KTreeView::raiseItem(KTreeViewItem *item)
1837 KTreeViewItem *itemParent = item->getParent();
1838 int itemChildIndex = itemParent->childIndex(item);
1839 if(itemChildIndex > 0) {
1840 bool autoU = autoUpdate();
1841 setAutoUpdate(FALSE);
1842 takeItem(item);
1843 insertItem(itemParent->childAt(--itemChildIndex), item, TRUE);
1844 setAutoUpdate(autoU);
1845 if(autoU && isVisible())
1846 repaint();
1850 // find the item at the path
1851 KTreeViewItem* KTreeView::recursiveFind(KPath& path)
1853 if (path.isEmpty())
1854 return treeRoot;
1856 // get the next key
1857 QString* searchString = path.pop();
1859 // find the parent item
1860 KTreeViewItem* parent = recursiveFind(path);
1861 if (parent == 0)
1862 return 0;
1865 * Iterate through the parent's children searching for searchString.
1867 KTreeViewItem* sibling = parent->getChild();
1868 while (sibling != 0) {
1869 if (*searchString == sibling->getText()) {
1870 break; /* found it! */
1872 sibling = sibling->getSibling();
1874 return sibling;
1877 void KTreeView::setItemExpanded(KTreeViewItem* item)
1879 if (level(item) < expansion) {
1880 expandSubTree(item, true);
1881 } else {
1882 collapseSubTree(item, true);
1886 // called by setExpandLevel for each item in tree
1887 bool KTreeView::setItemExpandLevel(KTreeViewItem* item, void*)
1889 assert(item->owner != 0);
1890 item->owner->setItemExpanded(item);
1891 return false;
1894 // called by setExpandButtonDrawing for every item in tree
1895 // the parameter drawButton is used as (and implicitly cast to) a bool
1896 bool KTreeView::setItemExpandButtonDrawing(KTreeViewItem* item,
1897 void* drawButton)
1899 item->setDrawExpandButton(drawButton);
1900 return false;
1903 // called by setShowItemText for every item in tree
1904 // the parameter newDrawText is used as (and implicitly cast to) a bool
1905 bool KTreeView::setItemShowText(KTreeViewItem* item,
1906 void* newDrawText)
1908 item->setDrawText(newDrawText);
1909 return false;
1912 // called by setTreeDrawing for every item in tree
1913 // the parameter drawTree is used as (and implicitly cast to) a bool
1914 bool KTreeView::setItemTreeDrawing(KTreeViewItem* item, void* drawTree)
1916 item->setDrawTree(drawTree);
1917 return false;
1920 // makes the item a child of the item above it, splitting
1921 // the tree into a new branch
1922 void KTreeView::split(KTreeViewItem *item)
1924 KTreeViewItem *itemParent = item->getParent();
1925 int itemChildIndex = itemParent->childIndex(item);
1926 if(itemChildIndex == 0)
1927 return;
1928 bool autoU = autoUpdate();
1929 setAutoUpdate(FALSE);
1930 takeItem(item);
1931 appendChildItem(itemParent->childAt(--itemChildIndex), item);
1932 setAutoUpdate(autoU);
1933 if(autoU && isVisible())
1934 repaint();
1937 // removes the item from the tree without deleting it
1938 void KTreeView::takeItem(KTreeViewItem* item)
1940 assert(item->owner == this);
1942 // TODO: go over this function again
1944 bool wasVisible = item->isVisible();
1946 * If we have a current item, make sure that it is not in the subtree
1947 * that we are about to remove. If the current item is in the part
1948 * below the taken-out subtree, we must move it up a number of rows if
1949 * the taken-out subtree is at least partially visible.
1951 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1952 KTreeViewItem* oldCurrent = cur;
1953 if (wasVisible && cur != 0) {
1954 KTreeViewItem* c = cur;
1955 while (c != 0 && c != item) {
1956 c = c->getParent();
1958 if (c != 0) {
1959 // move current item to parent
1960 cur = item->getParent();
1961 if (cur == treeRoot) {
1962 cur = 0;
1963 // move it to next or previous sibling
1964 if (moveCurrentToSibling) {
1965 if (item->getSibling() != 0) {
1966 cur = item->getSibling();
1967 } else if (treeRoot->getChild() != item) {
1968 cur = treeRoot->getChild();
1969 while (cur != 0 && cur->getSibling() != item)
1970 cur = cur->getSibling();
1976 KTreeViewItem* parentItem = item->getParent();
1977 parentItem->removeChild(item);
1978 item->sibling = 0;
1979 if (wasVisible || parentItem->childCount() == 0) {
1980 bool autoU = autoUpdate();
1981 setAutoUpdate(FALSE);
1982 updateVisibleItems();
1983 setAutoUpdate(autoU);
1984 if (autoU && isVisible())
1985 repaint();
1988 // re-seat the current item
1989 // row changes if cur is below the taken item
1990 current = cur != 0 ? itemRow(cur) : -1;
1991 // signal must be emitted only if item really changed
1992 if (cur != oldCurrent) {
1993 updateCell(current, 0, false);
1994 emit highlighted(current);
1998 // visits each item, calculates the maximum width
1999 // and updates TableView
2000 void KTreeView::updateCellWidth()
2002 // make cells at least 1 pixel wide to avoid singularities (division by zero)
2003 int maxW = 1;
2004 forEveryVisibleItem(&KTreeView::getMaxItemWidth, &maxW);
2005 maxItemWidth = maxW;
2006 updateTableSize();
2008 // correct offsets
2009 int xoff = xOffset();
2010 int yoff = yOffset();
2011 if (xoff > maxXOffset()) xoff = maxXOffset();
2012 if (yoff > maxYOffset()) yoff = maxYOffset();
2014 setOffset(xoff, yoff);
2017 void KTreeView::updateVisibleItems()
2019 int index = 0;
2020 int count = 0;
2021 updateVisibleItemRec(treeRoot, index, count);
2022 assert(index == count);
2023 setNumRows(count);
2024 updateCellWidth();
2027 void KTreeView::updateVisibleItemRec(KTreeViewItem* item, int& index, int& count)
2029 if (!item->isExpanded()) {
2030 // no visible items if not expanded
2031 return;
2035 * Record the children of item in the list of visible items.
2037 * Don't register item itself, it's already in the list. Also only
2038 * allocate new space for children.
2040 count += item->childCount();
2041 if (count > itemCapacity) {
2042 // must reallocate
2043 int newCapacity = itemCapacity;
2044 do {
2045 newCapacity += newCapacity;
2046 } while (newCapacity < count);
2047 KTreeViewItem** newItems = new KTreeViewItem*[newCapacity];
2048 // clear the unneeded space
2049 for (int i = index; i < newCapacity; i++) {
2050 newItems[i] = 0;
2052 // move already accumulated items over
2053 for (int i = index-1; i >= 0; i--) {
2054 newItems[i] = visibleItems[i];
2056 delete[] visibleItems;
2057 visibleItems = newItems;
2058 itemCapacity = newCapacity;
2060 // insert children
2061 for (KTreeViewItem* i = item->getChild(); i != 0; i = i->getSibling()) {
2062 visibleItems[index++] = i;
2063 updateVisibleItemRec(i, index, count);