Updated documentation.
[kdbg.git] / kdbg / ktreeview.cpp
blob43dac1f12c5cc7fe6ab86442dd50ee6723b22398
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, GUIStyle style) const
344 QColor fc;
345 if (style == WindowsStyle)
346 fc = darkBlue; /* hardcoded in Qt */
347 else
348 fc = colorGroup.text();
349 QRect textRect = textBoundingRect(indent);
350 int t,l,b,r;
351 textRect.coords(&l, &t, &r, &b);
352 QRect outerRect;
353 outerRect.setCoords(l - 2, t - 2, r + 2, b + 2);
354 if (style == WindowsStyle) { /* Windows style highlight */
355 if (hasFocus) {
356 p->fillRect(textRect, fc); /* highlight background */
357 textRect.setCoords(l - 1, t - 1, r + 1, b + 1);
358 p->setPen(QPen(yellow, 0, DotLine));
359 p->setBackgroundColor(fc);
360 p->setBackgroundMode(OpaqueMode);
361 p->drawRect(textRect);
362 p->setPen(fc);
363 p->drawRect(outerRect);
364 } else {
365 p->fillRect(outerRect, fc); /* highlight background */
367 } else { /* Motif style highlight */
368 if (hasFocus) {
369 p->fillRect(textRect, fc); /* highlight background */
370 p->setPen(fc);
371 p->drawRect(outerRect);
372 } else {
373 p->fillRect(outerRect, fc); /* highlight background */
378 // draw the text, highlighted if requested
379 void KTreeViewItem::paintText(QPainter* p, int indent, int cellHeight,
380 const QColorGroup& cg, bool highlighted) const
382 int textX = indent + pixmap.width() + 3;
383 int textY = (cellHeight - p->fontMetrics().height()) / 2 +
384 p->fontMetrics().ascent();
386 if (highlighted) {
387 paintHighlight(p, indent, cg,
388 owner->hasFocus(), owner->style());
389 p->setPen(cg.base());
390 p->setBackgroundColor(cg.text());
392 else {
393 p->setPen(cg.text());
394 p->setBackgroundColor(cg.base());
396 p->drawText(textX, textY, text);
399 // paint the tree structure
400 void KTreeViewItem::paintTree(QPainter* p, int indent, int cellHeight,
401 const QColorGroup& cg) const
403 int parentLeaderX = indent - (owner->itemIndent / 2);
404 int cellCenterY = cellHeight / 2;
405 int cellBottomY = cellHeight - 1;
406 int itemLeaderX = indent - 1;
408 p->setPen(cg.background());
411 * If this is not the first item in the tree draw the line up
412 * towards parent or sibling.
414 if (parent->parent != 0 || parent->getChild() != this)
415 p->drawLine(parentLeaderX, 0, parentLeaderX, cellCenterY);
417 // draw the line down towards sibling
418 if (sibling)
419 p->drawLine(parentLeaderX, cellCenterY, parentLeaderX, cellBottomY);
422 * If this item has children or siblings in the tree or is a child of
423 * an item other than the root item then draw the little line from the
424 * item out to the left.
426 if (sibling || (doExpandButton && (child || delayedExpanding)) ||
427 parent->parent != 0 ||
429 * The following handles the case of an item at the end of the
430 * topmost level which doesn't have children.
432 parent->getChild() != this)
434 p->drawLine(parentLeaderX, cellCenterY, itemLeaderX, cellCenterY);
438 * If there are siblings of ancestors below, draw our portion of the
439 * branches that extend through this cell.
441 KTreeViewItem* prevRoot = parent;
442 while (prevRoot->getParent() != 0) { /* while not root item */
443 assert(prevRoot->owner == owner);
444 if (prevRoot->hasSibling()) {
445 int sibLeaderX = owner->indentation(prevRoot) - (owner->itemIndent / 2);
446 p->drawLine(sibLeaderX, 0, sibLeaderX, cellBottomY);
448 prevRoot = prevRoot->getParent();
452 // removes the given (direct) child from the branch
453 bool KTreeViewItem::removeChild(KTreeViewItem* theChild)
455 // search item in list of children
456 KTreeViewItem* prevItem = 0;
457 KTreeViewItem* toRemove = getChild();
458 while (toRemove && toRemove != theChild) {
459 prevItem = toRemove;
460 toRemove = toRemove->getSibling();
463 if (toRemove) {
464 // found it!
465 if (prevItem == 0) {
466 child = toRemove->getSibling();
467 } else {
468 prevItem->sibling = toRemove->getSibling();
470 numChildren--;
471 toRemove->setOwner(0);
473 assert((numChildren == 0) == (child == 0));
475 return toRemove != 0;
478 // sets the delayed-expanding flag
479 void KTreeViewItem::setDelayedExpanding(bool flag)
481 delayedExpanding = flag;
484 // tells the item whether it shall delete child items
485 void KTreeViewItem::setDeleteChildren(bool flag)
487 deleteChildren = flag;
490 // sets the draw expand button flag of this item
491 void KTreeViewItem::setDrawExpandButton(bool doit)
493 doExpandButton = doit;
496 // sets the draw text flag of this item
497 void KTreeViewItem::setDrawText(bool doit)
499 doText = doit;
502 // sets the draw tree branch flag of this item
503 void KTreeViewItem::setDrawTree(bool doit)
505 doTree = doit;
508 // sets the expanded flag of this item
509 void KTreeViewItem::setExpanded(bool is)
511 expanded = is;
514 // sets the owner of this item and its children siblings
515 void KTreeViewItem::setOwner(KTreeView* newOwner, bool includeSiblings)
517 /* Note: set owner of children's siblings! */
518 owner = newOwner;
519 if (getChild())
520 getChild()->setOwner(newOwner, true);
521 if (includeSiblings && getSibling())
522 getSibling()->setOwner(newOwner, true);
525 // sets the item pixmap to the given pixmap
526 void KTreeViewItem::setPixmap(const QPixmap& pm)
528 pixmap = pm;
531 // sets the item text to the given string
532 void KTreeViewItem::setText(const QString& t)
534 text = t;
537 // counts the child items and stores the result in numChildren
538 void KTreeViewItem::synchNumChildren()
540 numChildren = 0;
541 KTreeViewItem* item = getChild();
542 while (item != 0) {
543 numChildren++;
544 item = item->getSibling();
549 * returns the bounding rect of the item text in cell coordinates couldn't
550 * get QFontMetrics::boundingRect() to work right so I made my own
552 QRect KTreeViewItem::textBoundingRect(int indent) const
554 const QFontMetrics& fm = owner->fontMetrics();
555 int cellHeight = height(fm);
556 int rectX = indent + pixmap.width() + 2;
557 int rectY = (cellHeight - fm.height()) / 2;
558 int rectW = fm.width(text) + 2;
559 int rectH = fm.height();
560 return QRect(rectX, rectY, rectW, rectH);
563 // returns the total width of text and pixmap, including margins, spacing
564 // and indent, or -1 if empty -- SHOULD NEVER BE -1!
565 int KTreeViewItem::width(int indent) const
567 return width(indent, owner->fontMetrics());
570 // The width of an item is composed of the following:
571 // - indentation (indent with times level)
572 // - pixmap width
573 // - 2 pixels for highlight border
574 // - 1 pixel margin
575 // - text width
576 // - 1 pixel margin
577 // - 2 pixels for highlight border
578 int KTreeViewItem::width(int indent, const QFontMetrics& fm) const
580 return indent + pixmap.width() + fm.width(text) + 6;
585 * -------------------------------------------------------------------
587 * KTreeView
589 * -------------------------------------------------------------------
592 // constructor
593 KTreeView::KTreeView(QWidget *parent,
594 const char *name,
595 WFlags f) :
596 QTableView(parent, name, f),
597 clearing(false),
598 current(-1),
599 drawExpandButton(true),
600 drawTree(true),
601 expansion(0),
602 goingDown(false),
603 itemIndent(18),
604 showText(true),
605 itemCapacity(500),
606 visibleItems(0),
607 rubberband_mode(false)
609 initMetaObject();
610 setCellHeight(0);
611 setCellWidth(0);
612 setNumRows(0);
613 setNumCols(1);
614 setTableFlags(Tbl_autoScrollBars | Tbl_clipCellPainting | Tbl_snapToVGrid);
615 clearTableFlags(Tbl_scrollLastVCell | Tbl_scrollLastHCell | Tbl_snapToVGrid);
616 switch(style()) {
617 case WindowsStyle:
618 case MotifStyle:
619 setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
620 setBackgroundColor(colorGroup().base());
621 break;
622 default:
623 setFrameStyle(QFrame::Panel | QFrame::Plain);
624 setLineWidth(1);
626 setFocusPolicy(StrongFocus);
627 treeRoot = new KTreeViewItem;
628 treeRoot->setExpanded(true);
629 treeRoot->owner = this;
631 visibleItems = new KTreeViewItem*[itemCapacity];
632 // clear those pointers
633 for (int j = itemCapacity-1; j >= 0; j--) {
634 visibleItems[j] = 0;
638 // destructor
639 KTreeView::~KTreeView()
641 goingDown = true;
642 clear();
643 delete[] visibleItems;
644 delete treeRoot;
647 // appends a child to the item at the given index with the given text
648 // and pixmap
649 void KTreeView::appendChildItem(const char* theText, const QPixmap& thePixmap,
650 int index)
652 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
653 item->setDeleteChildren(true);
654 appendChildItem(item, index);
657 // appends a child to the item at the end of the given path with
658 // the given text and pixmap
659 void KTreeView::appendChildItem(const char* theText, const QPixmap& thePixmap,
660 const KPath& thePath)
662 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
663 item->setDeleteChildren(true);
664 appendChildItem(item, thePath);
667 // appends the given item to the children of the item at the given index
668 void KTreeView::appendChildItem(KTreeViewItem* newItem, int index)
670 /* find parent item and append new item to parent's sub tree */
671 KTreeViewItem* parentItem = itemAt(index);
672 if (!parentItem)
673 return;
674 appendChildItem(parentItem, newItem);
677 // appends the given item to the children of the item at the end of the
678 // given path
679 void KTreeView::appendChildItem(KTreeViewItem* newItem, const KPath& thePath)
681 /* find parent item and append new item to parent's sub tree */
682 KTreeViewItem* parentItem = itemAt(thePath);
683 if (!parentItem)
684 return;
685 appendChildItem(parentItem, newItem);
688 // indicates whether horizontal scrollbar appears only when needed
689 bool KTreeView::autoBottomScrollBar() const
691 return testTableFlags(Tbl_autoHScrollBar);
694 // indicates whether vertical scrollbar appears only when needed
695 bool KTreeView::autoScrollBar() const
697 return testTableFlags(Tbl_autoVScrollBar);
700 // indicates whether display updates automatically on changes
701 bool KTreeView::autoUpdate() const
703 return QTableView::autoUpdate();
706 // indicates whether horizontal scrollbar is present
707 bool KTreeView::bottomScrollBar() const
709 return testTableFlags(Tbl_hScrollBar);
712 // translate mouse coord to cell coord
713 QPoint KTreeView::cellCoords(int row, const QPoint& widgetCoord)
715 int cellX = 0, cellY = 0;
716 colXPos(0, &cellX);
717 rowYPos(row, &cellY);
718 return QPoint(widgetCoord.x() - cellX, widgetCoord.y() - cellY);
721 // find item at specified index and change pixmap and/or text
722 void KTreeView::changeItem(const char *newText,
723 const QPixmap *newPixmap,
724 int index)
726 KTreeViewItem *item = itemAt(index);
727 if(item)
728 changeItem(item, index, newText, newPixmap);
731 // find item at end of specified path, and change pixmap and/or text
732 void KTreeView::changeItem(const char* newText,
733 const QPixmap* newPixmap,
734 const KPath& thePath)
736 KTreeViewItem* item = itemAt(thePath);
737 if (item) {
738 int index = itemRow(item);
739 changeItem(item, index, newText, newPixmap);
743 // clear all items from list and erase display
744 void KTreeView::clear()
746 setCurrentItem(-1);
748 /* somewhat of a hack for takeItem so it doesn't update the current item... */
749 clearing = TRUE;
751 bool autoU = autoUpdate();
752 setAutoUpdate(FALSE);
753 QStack<KTreeViewItem> stack;
754 stack.push(treeRoot);
755 while(!stack.isEmpty()) {
756 KTreeViewItem *item = stack.pop();
757 if(item->hasChild()) {
758 stack.push(item);
759 stack.push(item->getChild());
761 else if(item->hasSibling()) {
762 stack.push(item);
763 stack.push(item->getSibling());
765 else if(item->getParent() != 0) {
766 takeItem(item);
767 delete item;
770 clearing = FALSE;
771 if(goingDown || QApplication::closingDown())
772 return;
773 setAutoUpdate(autoU);
774 if(autoU && isVisible())
775 repaint();
778 // return a count of all the items in the tree, whether visible or not
779 uint KTreeView::count()
781 int total = 0;
782 forEveryItem(&KTreeView::countItem, (void *)&total);
783 return total;
786 // returns the index of the current (highlighted) item
787 int KTreeView::currentItem() const
789 return current;
792 // collapses the item at the specified row index.
793 void KTreeView::collapseItem(int index, bool emitSignal)
795 KTreeViewItem* item = itemAt(index);
796 if (item)
797 collapseSubTree(item, emitSignal);
800 // expands the item at the specified row indes.
801 void KTreeView::expandItem(int index, bool emitSignal)
803 KTreeViewItem* item = itemAt(index);
804 if (item)
805 expandSubTree(item, emitSignal);
808 // returns the depth the tree is automatically expanded to when
809 // items are added
810 int KTreeView::expandLevel() const
812 return expansion;
815 // visits every item in the tree, visible or not and applies
816 // the user supplied function with the item and user data passed as parameters
817 // if user supplied function returns true, traversal ends and function returns
818 bool KTreeView::forEveryItem(KForEveryFunc func, void* user, KTreeViewItem* item)
820 if (item == 0) {
821 item = treeRoot;
823 assert(item->owner == this);
824 item = item->getChild();
826 while (item != 0) {
827 // visit the siblings
828 if ((*func)(item, user)) {
829 return true;
831 // visit the children (recursively)
832 if (item->hasChild()) {
833 if (forEveryItem(func, user, item))
834 return true;
836 item = item->getSibling();
838 return false;
841 // visits every visible item in the tree in order and applies the
842 // user supplied function with the item and user data passed as parameters
843 // if user supplied function returns TRUE, traversal ends and function
844 // returns
845 bool KTreeView::forEveryVisibleItem(KForEveryFunc func, void *user,
846 KTreeViewItem* item)
848 if (item == 0) {
849 item = treeRoot;
850 } else {
851 // children are invisible (hence, nothing to do)
852 // if item is invisible or collapsed
853 if (!item->isVisible() || !item->isExpanded()) {
854 return false;
857 assert(item->owner == this);
858 item = item->getChild();
860 while (item != 0) {
861 // visit the siblings
862 if ((*func)(item, user)) {
863 return true;
865 // visit the children (recursively)
866 if (item->hasChild() && item->isExpanded()) {
867 if (forEveryVisibleItem(func, user, item))
868 return true;
870 item = item->getSibling();
872 return false;
875 // returns a pointer to the KTreeViewItem at the current index
876 // or 0 if no current item
877 KTreeViewItem *KTreeView::getCurrentItem()
879 if(current == -1) return 0;
880 return itemAt(current);
883 // returns the current indent spacing
884 int KTreeView::indentSpacing()
886 return itemIndent;
889 // inserts a new item with the specified text and pixmap before
890 // or after the item at the given index, depending on the value
891 // of prefix
892 // if index is negative, appends item to tree at root level
893 bool KTreeView::insertItem(const char* theText, const QPixmap& thePixmap,
894 int row, bool prefix)
896 KTreeViewItem* refItem = itemAt(row);
898 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
899 item->setDeleteChildren(true);
901 bool success = insertItem(refItem, item, prefix);
902 if (!success)
903 delete item;
904 return success;
907 // inserts a new item with the specified text and pixmap before
908 // or after the item at the end of the given path, depending on the value
909 // of prefix
910 bool KTreeView::insertItem(const char* theText, const QPixmap& thePixmap,
911 const KPath& path, bool prefix)
913 KTreeViewItem* refItem = itemAt(path);
915 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
916 item->setDeleteChildren(true);
918 bool success = insertItem(refItem, item, prefix);
919 if (!success)
920 delete item;
921 return success;
924 // inserts the given item or derived object into the tree before
925 // or after the item at the given index, depending on the value
926 // of prefix
927 // if index is negative, appends item to tree at root level
928 bool KTreeView::insertItem(KTreeViewItem* newItem,
929 int index, bool prefix)
931 // find the item currently at the index, if there is one
932 KTreeViewItem* refItem = itemAt(index);
934 // insert new item at the appropriate place
935 return insertItem(refItem, newItem, prefix);
938 // inserts the given item or derived object into the tree before
939 // or after the item at the end of the given path, depending on the value
940 // of prefix
941 bool KTreeView::insertItem(KTreeViewItem* newItem,
942 const KPath& thePath, bool prefix)
944 // find the item currently at the end of the path, if there is one
945 KTreeViewItem* refItem = itemAt(thePath);
947 // insert new item at appropriate place
948 return insertItem(refItem, newItem, prefix);
952 * returns pointer to KTreeViewItem at the specifed row or 0 if row is out
953 * of limits.
955 KTreeViewItem* KTreeView::itemAt(int row)
957 if (row < 0 || row >= numRows()) {
958 return 0;
960 else {
961 // lookup the item in the list of visible items
962 assert(row < itemCapacity);
963 KTreeViewItem* i = visibleItems[row];
964 assert(i != 0);
965 return i;
969 // returns pointer to KTreeViewItem at the end of the
970 // path or 0 if not found
971 KTreeViewItem* KTreeView::itemAt(const KPath& path)
973 if (path.isEmpty())
974 return 0;
976 // need a copy of the path because recursiveFind will destroy it
977 KPath pathCopy;
978 pathCopy.setAutoDelete(false);
979 pathCopy = path;
981 return recursiveFind(pathCopy);
984 // computes the path of the item at the specified index
985 // if index is invalid, nothing is done.
986 void KTreeView::itemPath(int row, KPath& path)
988 KTreeViewItem* item = itemAt(row);
989 if (item != 0) {
990 itemPath(item, path);
994 // returns the row in the visible tree of the given item or
995 // -1 if not found
996 int KTreeView::itemRow(KTreeViewItem* item)
998 if (item->owner == this) {
999 // search in list of visible items
1000 for (int i = numRows()-1; i >= 0; i--) {
1001 if (visibleItems[i] == item) {
1002 return i;
1006 // not found
1007 return -1;
1011 * move the subtree at the specified index up one branch level (make root
1012 * item a sibling of its current parent)
1014 void KTreeView::join(int index)
1016 KTreeViewItem *item = itemAt(index);
1017 if(item)
1018 join(item);
1022 * move the subtree at the specified index up one branch level (make root
1023 * item a sibling of it's current parent)
1025 void KTreeView::join(const KPath& path)
1027 KTreeViewItem *item = itemAt(path);
1028 if (item)
1029 join(item);
1032 /* move item at specified index one slot down in its parent's sub tree */
1033 void KTreeView::lowerItem(int index)
1035 KTreeViewItem *item = itemAt(index);
1036 if(item)
1037 lowerItem(item);
1040 /* move item at specified path one slot down in its parent's sub tree */
1041 void KTreeView::lowerItem(const KPath& path)
1043 KTreeViewItem* item = itemAt(path);
1044 if (item)
1045 lowerItem(item);
1048 /* move item at specified index one slot up in its parent's sub tree */
1049 void KTreeView::raiseItem(int index)
1051 KTreeViewItem* item = itemAt(index);
1052 if (item)
1053 raiseItem(item);
1056 /* move item at specified path one slot up in its parent's sub tree */
1057 void KTreeView::raiseItem(const KPath& path)
1059 KTreeViewItem* item = itemAt(path);
1060 if (item)
1061 raiseItem(item);
1064 // remove the item at the specified index and delete it
1065 void KTreeView::removeItem(int index)
1067 KTreeViewItem *item = itemAt(index);
1068 if(item) {
1069 takeItem(item);
1070 delete item;
1074 // remove the item at the end of the specified path and delete it
1075 void KTreeView::removeItem(const KPath& thePath)
1077 KTreeViewItem* item = itemAt(thePath);
1078 if (item) {
1079 takeItem(item);
1080 delete item;
1084 // indicates whether vertical scrollbar is present
1085 bool KTreeView::scrollBar() const
1087 return testTableFlags(Tbl_vScrollBar);
1090 void KTreeView::scrollVisible(KTreeViewItem* item, bool children)
1092 if (item == 0)
1093 return;
1094 int row = itemRow(item);
1095 if (row < 0)
1096 return; /* do nothing if invisible */
1098 if (children && item->isExpanded()) {
1099 // we are concerned about children
1100 if (!rowIsVisible(row)) {
1101 // just move to the top
1102 setTopCell(row);
1103 } else {
1104 // this is the complicated part
1105 // count the visible children (including grandchildren)
1106 int numVisible = 0;
1107 forEveryVisibleItem(countItem, &numVisible, item);
1108 // if the last child is visible, do nothing
1109 if (rowIsVisible(row + numVisible))
1110 return;
1112 * Basically, item will become the top cell; but if there are
1113 * more visible rows in the widget than we have children, then
1114 * we don't move that far.
1116 int remain = lastRowVisible()-topCell()-numVisible;
1117 if (remain <= 0) {
1118 setTopCell(row);
1119 } else {
1120 setTopCell(QMAX(0,row-remain));
1123 } else {
1124 // we are not concerned about children
1125 if (rowIsVisible(row))
1126 return;
1127 // just move the item to the top
1128 setTopCell(row);
1132 // enables/disables auto update of display
1133 void KTreeView::setAutoUpdate(bool enable)
1135 QTableView::setAutoUpdate(enable);
1138 // enables/disables horizontal scrollbar
1139 void KTreeView::setBottomScrollBar(bool enable)
1141 enable ? setTableFlags(Tbl_hScrollBar) :
1142 clearTableFlags(Tbl_hScrollBar);
1145 // sets the current item and hightlights it, emitting signals
1146 void KTreeView::setCurrentItem(int row)
1148 if (row == current)
1149 return;
1150 int numVisible = numRows();
1151 if (row > numVisible)
1152 return;
1153 int oldCurrent = current;
1154 current = row;
1155 if (oldCurrent < numVisible)
1156 updateCell(oldCurrent, 0);
1157 if (current > -1) {
1158 updateCell(current, 0, false);
1159 emit highlighted(current);
1163 // enables/disables drawing of expand button
1164 void KTreeView::setExpandButtonDrawing(bool enable)
1166 if (drawExpandButton == enable)
1167 return;
1168 drawExpandButton = enable;
1170 // the user parameter is cast to a bool in setItemExpandButtonDrawing
1171 forEveryItem(&KTreeView::setItemExpandButtonDrawing, enable ? &enable : 0);
1173 if (autoUpdate() && isVisible())
1174 repaint();
1177 // sets depth to which subtrees are automatically expanded, and
1178 // redraws tree if auto update enabled
1179 void KTreeView::setExpandLevel(int level)
1181 if (expansion == level)
1182 return;
1183 expansion = level;
1184 KTreeViewItem* item = getCurrentItem();
1185 forEveryItem(&KTreeView::setItemExpandLevel, 0);
1186 while (item != 0) {
1187 if (item->getParent()->isExpanded())
1188 break;
1189 item = item->getParent();
1191 if (item != 0)
1192 setCurrentItem(itemRow(item));
1193 if (autoUpdate() && isVisible())
1194 repaint();
1197 // sets the indent margin for all branches and repaints if auto update enabled
1198 void KTreeView::setIndentSpacing(int spacing)
1200 if (itemIndent == spacing)
1201 return;
1202 itemIndent = spacing;
1203 updateCellWidth();
1204 if (autoUpdate() && isVisible())
1205 repaint();
1208 // enables/disables vertical scrollbar
1209 void KTreeView::setScrollBar(bool enable)
1211 enable ? setTableFlags(Tbl_vScrollBar) :
1212 clearTableFlags(Tbl_vScrollBar);
1215 // enables/disables display of item text (keys)
1216 void KTreeView::setShowItemText(bool enable)
1218 if (showText == enable)
1219 return;
1220 showText = enable;
1222 // the user parameter is cast to a bool in setItemShowText
1223 forEveryItem(&KTreeView::setItemShowText, enable ? &enable : 0);
1225 if (autoUpdate() && isVisible())
1226 repaint();
1229 // indicates whether vertical scrolling is by pixel or row
1230 void KTreeView::setSmoothScrolling(bool enable)
1232 enable ? setTableFlags(Tbl_smoothVScrolling) :
1233 clearTableFlags(Tbl_smoothVScrolling);
1236 // enables/disables tree branch drawing
1237 void KTreeView::setTreeDrawing(bool enable)
1239 if (drawTree == enable)
1240 return;
1241 drawTree = enable;
1243 // the user parameter is cast to a bool in setItemTreeDrawing
1244 forEveryItem(&KTreeView::setItemTreeDrawing, enable ? &enable : 0);
1246 if (autoUpdate() && isVisible())
1247 repaint();
1250 // indicates whether item text keys are displayed
1251 bool KTreeView::showItemText() const
1253 return showText;
1256 // indicates whether scrolling is by pixel or row
1257 bool KTreeView::smoothScrolling() const
1259 return testTableFlags(Tbl_smoothVScrolling);
1262 // indents the item at the given index, splitting the tree into
1263 // a new branch
1264 void KTreeView::split(int index)
1266 KTreeViewItem *item = itemAt(index);
1267 if(item)
1268 split(item);
1271 // indents the item at the given path, splitting the tree into
1272 // a new branch
1273 void KTreeView::split(const KPath& path)
1275 KTreeViewItem* item = itemAt(path);
1276 if (item)
1277 split(item);
1280 // removes item at specified index from tree but does not delete it
1281 // returns pointer to the item or 0 if not succesful
1282 KTreeViewItem *KTreeView::takeItem(int index)
1284 KTreeViewItem *item = itemAt(index);
1285 if(item)
1286 takeItem(item);
1287 return item;
1290 // removes item at specified path from tree but does not delete it
1291 // returns pointer to the item or 0 if not successful
1292 KTreeViewItem* KTreeView::takeItem(const KPath& path)
1294 KTreeViewItem* item = itemAt(path);
1295 if (item)
1296 takeItem(item);
1297 return item;
1300 // indicates whether tree branches are drawn
1301 bool KTreeView::treeDrawing() const
1303 return drawTree;
1307 // appends a child to the specified parent item (note: a child, not a sibling, is added!)
1308 void KTreeView::appendChildItem(KTreeViewItem* theParent,
1309 KTreeViewItem* newItem)
1311 theParent->appendChild(newItem);
1313 // set item state
1314 newItem->setDrawExpandButton(drawExpandButton);
1315 newItem->setDrawTree(drawTree);
1316 newItem->setDrawText(showText);
1317 if (level(newItem) < expansion) {
1318 newItem->setExpanded(true);
1321 // fix up branch levels of any children that the new item may already have
1322 if(newItem->hasChild()) {
1323 fixChildren(newItem);
1326 // if necessary, adjust cell width, number of rows and repaint
1327 if (newItem->isVisible() || theParent->childCount() == 1) {
1328 bool autoU = autoUpdate();
1329 setAutoUpdate(false);
1330 updateVisibleItems();
1331 setAutoUpdate(autoU);
1332 if (autoU && isVisible())
1333 repaint();
1337 // returns the height of the cell(row) at the specified row (index)
1338 int KTreeView::cellHeight(int row)
1340 return itemAt(row)->height(fontMetrics());
1343 // returns the width of the cells. Note: this is mostly for derived classes
1344 // which have more than 1 column
1345 int KTreeView::cellWidth(int /*col*/)
1347 return maxItemWidth;
1350 // changes the given item with the new text and/or pixmap
1351 void KTreeView::changeItem(KTreeViewItem* toChange, int itemRow,
1352 const char* newText, const QPixmap* newPixmap)
1354 int indent = indentation(toChange);
1355 int oldWidth = toChange->width(indent);
1356 if(newText)
1357 toChange->setText(newText);
1358 if (newPixmap)
1359 toChange->setPixmap(*newPixmap);
1360 if(oldWidth != toChange->width(indent))
1361 updateCellWidth();
1362 if(itemRow == -1)
1363 return;
1364 if(autoUpdate() && rowIsVisible(itemRow))
1365 updateCell(itemRow, 0);
1368 // collapses the subtree at the specified item
1369 void KTreeView::collapseSubTree(KTreeViewItem* subRoot, bool emitSignal)
1371 assert(subRoot->owner == this);
1372 if (!subRoot->isExpanded())
1373 return;
1375 // must move the current item if it is visible
1376 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1378 subRoot->setExpanded(false);
1379 if (subRoot->isVisible()) {
1380 bool autoU = autoUpdate();
1381 setAutoUpdate(false);
1382 updateVisibleItems();
1383 // re-seat current item
1384 if (cur != 0) {
1385 setCurrentItem(itemRow(cur));
1387 if (emitSignal) {
1388 emit collapsed(itemRow(subRoot));
1390 setAutoUpdate(autoU);
1391 if (autoU && isVisible())
1392 repaint();
1396 // used by count() with forEach() function to count total number
1397 // of items in the tree
1398 bool KTreeView::countItem(KTreeViewItem*, void* total)
1400 int* t = static_cast<int*>(total);
1401 (*t)++;
1402 return false;
1405 // expands the subtree at the given item
1406 void KTreeView::expandSubTree(KTreeViewItem* subRoot, bool emitSignal)
1408 assert(subRoot->owner == this);
1409 if (subRoot->isExpanded())
1410 return;
1412 // must move the current item if it is visible
1413 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1415 bool allow = true;
1417 if (subRoot->delayedExpanding) {
1418 emit expanding(subRoot, allow);
1420 if (!allow)
1421 return;
1423 subRoot->setExpanded(true);
1425 if (subRoot->isVisible()) {
1426 bool autoU = autoUpdate();
1427 setAutoUpdate(false);
1428 updateVisibleItems();
1429 // re-seat current item
1430 if (cur != 0) {
1431 setCurrentItem(itemRow(cur));
1433 if (emitSignal) {
1434 emit expanded(itemRow(subRoot));
1436 setAutoUpdate(autoU);
1437 if (autoU && isVisible())
1438 repaint();
1442 // fix up branch levels out of whack from split/join operations on the tree
1443 void KTreeView::fixChildren(KTreeViewItem *parentItem)
1445 KTreeViewItem* childItem = 0;
1446 KTreeViewItem* siblingItem = 0;
1447 // int childBranch = parentItem->getBranch() + 1;
1448 if(parentItem->hasChild()) {
1449 childItem = parentItem->getChild();
1450 // childItem->setBranch(childBranch);
1451 childItem->owner = parentItem->owner;
1452 fixChildren(childItem);
1454 while(childItem && childItem->hasSibling()) {
1455 siblingItem = childItem->getSibling();
1456 // siblingItem->setBranch(childBranch);
1457 siblingItem->owner = parentItem->owner;
1458 fixChildren(siblingItem);
1459 childItem = siblingItem;
1464 * Handle QFocusEvent processing by setting current item to top row if
1465 * there is no current item, and updates cell to add or delete the focus
1466 * rectangle on the highlight bar. The base class is not called because it
1467 * does a repaint() which causes flicker.
1469 void KTreeView::focusInEvent(QFocusEvent *)
1471 if (current < 0 && numRows() > 0)
1472 setCurrentItem(topCell());
1473 updateCell(current, 0);
1476 void KTreeView::focusOutEvent(QFocusEvent *)
1478 updateCell(current, 0);
1481 // called by updateCellWidth() for each item in the visible list
1482 bool KTreeView::getMaxItemWidth(KTreeViewItem* item, void* user)
1484 assert(item->owner != 0);
1485 int indent = item->owner->indentation(item);
1486 int* maxW = static_cast<int*>(user);
1487 int w = item->width(indent);
1488 if (w > *maxW)
1489 *maxW = w;
1490 return false;
1493 int KTreeView::indentation(KTreeViewItem* item) const
1495 return level(item) * itemIndent + itemIndent + 3;
1498 // inserts the new item before or after the reference item, depending
1499 // on the value of prefix
1500 bool KTreeView::insertItem(KTreeViewItem* referenceItem,
1501 KTreeViewItem* newItem,
1502 bool prefix)
1504 assert(newItem != 0);
1505 assert(referenceItem == 0 || referenceItem->owner == this);
1507 /* set the new item's state */
1508 newItem->setDrawExpandButton(drawExpandButton);
1509 newItem->setDrawTree(drawTree);
1510 newItem->setDrawText(showText);
1511 KTreeViewItem* parentItem;
1512 if (referenceItem) {
1513 parentItem = referenceItem->getParent();
1514 int insertIndex = parentItem->childIndex(referenceItem);
1515 if (!prefix)
1516 insertIndex++;
1517 parentItem->insertChild(insertIndex, newItem);
1519 else {
1520 // no reference item, append at end of visible tree
1521 // need to repaint
1522 parentItem = treeRoot;
1523 parentItem->appendChild(newItem);
1526 // set item expansion
1527 if (level(newItem) < expansion)
1528 newItem->setExpanded(true);
1530 // fix up branch levels of any children
1531 if (newItem->hasChild())
1532 fixChildren(newItem);
1534 // if repaint necessary, do it if visible and auto update
1535 // enabled
1536 if (newItem->isVisible() || parentItem->childCount() == 1) {
1537 bool autoU = autoUpdate();
1538 setAutoUpdate(FALSE);
1539 updateVisibleItems();
1540 setAutoUpdate(autoU);
1541 if (autoU && isVisible())
1542 repaint();
1544 return true;
1548 * returns pointer to item's path
1550 void KTreeView::itemPath(KTreeViewItem* item, KPath& path) const
1552 assert(item != 0);
1553 assert(item->owner == this);
1554 if (item != treeRoot) {
1555 itemPath(item->getParent(), path);
1556 path.push(new QString(item->getText()));
1561 * joins the item's branch into the tree, making the item a sibling of its
1562 * parent
1564 void KTreeView::join(KTreeViewItem *item)
1566 KTreeViewItem *itemParent = item->getParent();
1567 if(itemParent->hasParent()) {
1568 bool autoU = autoUpdate();
1569 setAutoUpdate(FALSE);
1570 takeItem(item);
1571 insertItem(itemParent, item, FALSE);
1572 setAutoUpdate(autoU);
1573 if(autoU && isVisible())
1574 repaint();
1578 // handles keyboard interface
1579 void KTreeView::keyPressEvent(QKeyEvent* e)
1581 if (numRows() == 0)
1582 return; /* nothing to do */
1584 /* if there's no current item, make the top item current */
1585 if (currentItem() < 0)
1586 setCurrentItem(topCell());
1587 assert(currentItem() >= 0); /* we need a current item */
1588 assert(itemAt(currentItem()) != 0); /* we really need a current item */
1590 // give currentItem a chance to handle the event
1591 if (itemAt(currentItem())->keyEvent(e))
1592 return; /* handled */
1594 int pageSize, delta;
1595 KTreeViewItem* item;
1596 int key = e->key();
1597 repeat:
1598 switch (key) {
1599 case Key_Up:
1600 // make previous item current, scroll up if necessary
1601 if (currentItem() > 0) {
1602 setCurrentItem(currentItem() - 1);
1603 scrollVisible(itemAt(currentItem()), false);
1605 break;
1606 case Key_Down:
1607 // make next item current, scroll down if necessary
1608 if (currentItem() < numRows() - 1) {
1609 setCurrentItem(currentItem() + 1);
1610 if (currentItem() > lastRowVisible()) {
1611 // scrollVisible is not feasible here because
1612 // it scrolls the item to the top
1613 setTopCell(topCell() + currentItem() - lastRowVisible());
1614 } else if (currentItem() < topCell()) {
1615 setTopCell(currentItem());
1618 break;
1619 case Key_Next:
1620 // move highlight one page down and scroll down
1621 delta = currentItem() - topCell();
1622 pageSize = lastRowVisible() - topCell();
1623 setTopCell(QMIN(topCell() + pageSize, numRows() - 1));
1624 setCurrentItem(QMIN(topCell() + delta, numRows() - 1));
1625 break;
1626 case Key_Prior:
1627 // move highlight one page up and scroll up
1628 delta = currentItem() - topCell();
1629 pageSize = lastRowVisible() - topCell();
1630 setTopCell(QMAX(topCell() - pageSize, 0));
1631 setCurrentItem(QMAX(topCell() + delta, 0));
1632 break;
1633 case Key_Plus:
1634 case Key_Right:
1635 // if current item has subtree and is collapsed, expand it
1636 item = itemAt(currentItem());
1637 if (item->isExpanded() && item->hasChild() && key == Key_Right) {
1638 // going right on an expanded item is like going down
1639 key = Key_Down;
1640 goto repeat;
1641 } else {
1642 expandSubTree(item, true);
1643 scrollVisible(item, true);
1645 break;
1646 case Key_Minus:
1647 case Key_Left:
1648 // if current item has subtree and is expanded, collapse it
1649 item = itemAt(currentItem());
1650 if ((!item->isExpanded() || !item->hasChild()) && key == Key_Left) {
1651 // going left on a collapsed item goes to its parent
1652 item = item->getParent();
1653 if (item == treeRoot)
1654 break; /* we're already at the top */
1655 assert(item->isVisible());
1656 setCurrentItem(itemRow(item));
1657 } else {
1658 collapseSubTree(item, true);
1660 scrollVisible(item, false);
1661 break;
1662 case Key_Return:
1663 case Key_Enter:
1664 // select the current item
1665 if (currentItem() >= 0)
1666 emit selected(currentItem());
1667 break;
1668 default:
1669 break;
1673 // handles keyboard interface
1674 void KTreeView::keyReleaseEvent(QKeyEvent* e)
1676 if (currentItem() >= 0 && itemAt(currentItem()) != 0)
1677 itemAt(currentItem())->keyEvent(e);
1680 int KTreeView::level(KTreeViewItem* item) const
1682 assert(item != 0);
1683 assert(item->owner == this);
1684 assert(item != treeRoot);
1685 int l = 0;
1686 item = item->parent->parent; /* since item != treeRoot, there is a parent */
1687 while (item != 0) {
1688 item = item->parent;
1689 l++;
1691 return l;
1694 /* move specified item down one slot in parent's subtree */
1695 void KTreeView::lowerItem(KTreeViewItem *item)
1697 KTreeViewItem *itemParent = item->getParent();
1698 uint itemChildIndex = itemParent->childIndex(item);
1699 if(itemChildIndex < itemParent->childCount() - 1) {
1700 bool autoU = autoUpdate();
1701 setAutoUpdate(FALSE);
1702 takeItem(item);
1703 insertItem(itemParent->childAt(itemChildIndex), item, FALSE);
1704 setAutoUpdate(autoU);
1705 if(autoU && isVisible())
1706 repaint();
1710 // handle mouse double click events by selecting the clicked item
1711 // and emitting the signal
1712 void KTreeView::mouseDoubleClickEvent(QMouseEvent* e)
1714 // find out which row has been clicked
1715 int itemClicked = findRow(e->y());
1717 if (itemClicked < 0)
1718 return; /* invalid row, do nothing */
1720 KTreeViewItem* item = itemAt(itemClicked);
1721 if (item == 0)
1722 return;
1724 // translate mouse coord to cell coord
1725 QPoint cellCoord = cellCoords(itemClicked, e->pos());
1727 // first ask item
1728 if (item->mouseEvent(e, cellCoord))
1729 return;
1731 // hit test item
1732 int indent = indentation(item);
1733 if (item->boundingRect(indent).contains(cellCoord))
1734 emit selected(itemClicked);
1737 // handle mouse movement events
1738 void KTreeView::mouseMoveEvent(QMouseEvent* e)
1740 // in rubberband_mode we actually scroll the window now
1741 if (rubberband_mode) {
1742 move_rubberband(e->pos());
1743 } else {
1744 // else forward to current item
1745 int current = currentItem();
1746 if (current >= 0 && itemAt(current))
1747 itemAt(current)->mouseEvent(e, cellCoords(current, e->pos()));
1752 // handle single mouse presses
1753 void KTreeView::mousePressEvent(QMouseEvent* e)
1755 /* first: cancel rubberbanding if it's on */
1756 if (rubberband_mode)
1758 // another button was pressed while rubberbanding, stop the move.
1759 // RB: if we allow other buttons while rubberbanding the tree can expand
1760 // while rubberbanding - we then need to recalculate and resize the
1761 // rubberband rect and show the new size
1762 end_rubberband();
1763 return;
1766 // find out which row has been clicked
1767 int itemClicked = findRow(e->y());
1769 // nothing to do if not on valid row
1770 if (itemClicked < 0)
1771 return;
1772 KTreeViewItem* item = itemAt(itemClicked);
1773 if (!item)
1774 return;
1776 // translate mouse coord to cell coord
1777 QPoint cellCoord = cellCoords(itemClicked, e->pos());
1779 // give the item a crack
1780 if (item->mouseEvent(e, cellCoord))
1781 return; /* event eaten by item */
1783 // check for rubberbanding
1784 if (e->button() == MidButton)
1786 // RB: the MMB is hardcoded to the "rubberband" scroll mode
1787 if (!rubberband_mode) {
1788 start_rubberband(e->pos());
1790 return;
1793 if (e->button() == RightButton) {
1794 emit rightPressed(itemClicked, e->pos());
1796 /* hit test expand button (doesn't set currentItem) */
1797 else if (item->expandButtonClicked(cellCoord)) {
1798 if (item->isExpanded()) {
1799 collapseSubTree(item, true);
1800 } else {
1801 expandSubTree(item, true);
1802 scrollVisible(item, true); /* make children visible */
1805 // hit test item
1806 else if (item->boundingRect(indentation(item)).contains(cellCoord)) {
1807 setCurrentItem(itemClicked);
1811 // handle mouse release events
1812 void KTreeView::mouseReleaseEvent(QMouseEvent *e)
1814 /* if it's the MMB end rubberbanding */
1815 if (rubberband_mode) {
1816 if (e->button() == MidButton)
1817 end_rubberband();
1818 return;
1820 // forward to current item
1821 int current = currentItem();
1822 if (current >= 0 && itemAt(current))
1823 itemAt(current)->mouseEvent(e, cellCoords(current, e->pos()));
1826 // rubberband move: draw the rubberband
1827 void KTreeView::draw_rubberband()
1830 * RB: I'm using a white pen because of the XorROP mode. I would prefer
1831 * to draw the rectangle in red but I don't now how to get a pen which
1832 * draws red in XorROP mode (this depends on the background). In fact
1833 * the color should be configurable.
1836 if (!rubberband_mode) return;
1837 QPainter paint(this);
1838 paint.setPen(white);
1839 paint.setRasterOp(XorROP);
1840 paint.drawRect(xOffset()*viewWidth()/totalWidth(),
1841 yOffset()*viewHeight()/totalHeight(),
1842 rubber_width+1, rubber_height+1);
1843 paint.end();
1846 // rubberband move: start move
1847 void KTreeView::start_rubberband(const QPoint& where)
1849 if (rubberband_mode) { // Oops!
1850 end_rubberband();
1852 /* RB: Don't now, if this check is necessary */
1853 if (!viewWidth() || !viewHeight()) return;
1854 if (!totalWidth() || !totalHeight()) return;
1856 // calculate the size of the rubberband rectangle
1857 rubber_width = viewWidth()*viewWidth()/totalWidth();
1858 if (rubber_width > viewWidth()) rubber_width = viewWidth();
1859 rubber_height = viewHeight()*viewHeight()/totalHeight();
1860 if (rubber_height > viewHeight()) rubber_height = viewHeight();
1862 // remember the cursor position and the actual offset
1863 rubber_startMouse = where;
1864 rubber_startX = xOffset();
1865 rubber_startY = yOffset();
1866 rubberband_mode=TRUE;
1867 draw_rubberband();
1870 // rubberband move: end move
1871 void KTreeView::end_rubberband()
1873 if (!rubberband_mode) return;
1874 draw_rubberband();
1875 rubberband_mode = FALSE;
1878 // rubberband move: hanlde mouse moves
1879 void KTreeView::move_rubberband(const QPoint& where)
1881 if (!rubberband_mode) return;
1883 // look how much the mouse moved and calc the new scroll position
1884 QPoint delta = where - rubber_startMouse;
1885 int nx = rubber_startX + delta.x() * totalWidth() / viewWidth();
1886 int ny = rubber_startY + delta.y() * totalHeight() / viewHeight();
1888 // check the new position (and make it valid)
1889 if (nx < 0) nx = 0;
1890 else if (nx > maxXOffset()) nx = maxXOffset();
1891 if (ny < 0) ny = 0;
1892 else if (ny > maxYOffset()) ny = maxYOffset();
1894 // redraw the rubberband at the new position
1895 draw_rubberband();
1896 setOffset(nx,ny);
1897 draw_rubberband();
1901 // paints the cell at the specified row and col
1902 // col is ignored for now since there is only one
1903 void KTreeView::paintCell(QPainter* p, int row, int)
1905 KTreeViewItem* item = itemAt(row);
1906 if (item == 0)
1907 return;
1909 QColorGroup cg = colorGroup();
1910 int indent = indentation(item);
1911 item->paint(p, indent, cg,
1912 current == row); /* highlighted */
1916 /* This is needed to make the kcontrol's color setup working (Marcin Dalecki) */
1917 void KTreeView::paletteChange(const QPalette &)
1919 setBackgroundColor(colorGroup().base());
1920 repaint(true);
1924 /* raise the specified item up one slot in parent's subtree */
1925 void KTreeView::raiseItem(KTreeViewItem *item)
1927 KTreeViewItem *itemParent = item->getParent();
1928 int itemChildIndex = itemParent->childIndex(item);
1929 if(itemChildIndex > 0) {
1930 bool autoU = autoUpdate();
1931 setAutoUpdate(FALSE);
1932 takeItem(item);
1933 insertItem(itemParent->childAt(--itemChildIndex), item, TRUE);
1934 setAutoUpdate(autoU);
1935 if(autoU && isVisible())
1936 repaint();
1940 // find the item at the path
1941 KTreeViewItem* KTreeView::recursiveFind(KPath& path)
1943 if (path.isEmpty())
1944 return treeRoot;
1946 // get the next key
1947 QString* searchString = path.pop();
1949 // find the parent item
1950 KTreeViewItem* parent = recursiveFind(path);
1951 if (parent == 0)
1952 return 0;
1955 * Iterate through the parent's children searching for searchString.
1957 KTreeViewItem* sibling = parent->getChild();
1958 while (sibling != 0) {
1959 if (*searchString == sibling->getText()) {
1960 break; /* found it! */
1962 sibling = sibling->getSibling();
1964 return sibling;
1967 void KTreeView::setItemExpanded(KTreeViewItem* item)
1969 if (level(item) < expansion) {
1970 expandSubTree(item, true);
1971 } else {
1972 collapseSubTree(item, true);
1976 // called by setExpandLevel for each item in tree
1977 bool KTreeView::setItemExpandLevel(KTreeViewItem* item, void*)
1979 assert(item->owner != 0);
1980 item->owner->setItemExpanded(item);
1981 return false;
1984 // called by setExpandButtonDrawing for every item in tree
1985 // the parameter drawButton is used as (and implicitly cast to) a bool
1986 bool KTreeView::setItemExpandButtonDrawing(KTreeViewItem* item,
1987 void* drawButton)
1989 item->setDrawExpandButton(drawButton);
1990 return false;
1993 // called by setShowItemText for every item in tree
1994 // the parameter newDrawText is used as (and implicitly cast to) a bool
1995 bool KTreeView::setItemShowText(KTreeViewItem* item,
1996 void* newDrawText)
1998 item->setDrawText(newDrawText);
1999 return false;
2002 // called by setTreeDrawing for every item in tree
2003 // the parameter drawTree is used as (and implicitly cast to) a bool
2004 bool KTreeView::setItemTreeDrawing(KTreeViewItem* item, void* drawTree)
2006 item->setDrawTree(drawTree);
2007 return false;
2010 // makes the item a child of the item above it, splitting
2011 // the tree into a new branch
2012 void KTreeView::split(KTreeViewItem *item)
2014 KTreeViewItem *itemParent = item->getParent();
2015 int itemChildIndex = itemParent->childIndex(item);
2016 if(itemChildIndex == 0)
2017 return;
2018 bool autoU = autoUpdate();
2019 setAutoUpdate(FALSE);
2020 takeItem(item);
2021 appendChildItem(itemParent->childAt(--itemChildIndex), item);
2022 setAutoUpdate(autoU);
2023 if(autoU && isVisible())
2024 repaint();
2027 // removes the item from the tree without deleting it
2028 void KTreeView::takeItem(KTreeViewItem* item)
2030 assert(item->owner == this);
2032 // TODO: go over this function again
2034 bool wasVisible = item->isVisible();
2036 * If we have a current item, make sure that it is not in the subtree
2037 * that we are about to remove. If the current item is in the part
2038 * below the taken-out subtree, we must move it up a number of rows if
2039 * the taken-out subtree is at least partially visible.
2041 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
2042 if (wasVisible && cur != 0) {
2043 KTreeViewItem* c = cur;
2044 while (c != 0 && c != item) {
2045 c = c->getParent();
2047 if (c != 0) {
2048 // move current item to parent
2049 cur = item->getParent();
2050 if (cur == treeRoot)
2051 cur = 0;
2054 KTreeViewItem* parentItem = item->getParent();
2055 parentItem->removeChild(item);
2056 item->sibling = 0;
2057 if (wasVisible || parentItem->childCount() == 0) {
2058 bool autoU = autoUpdate();
2059 setAutoUpdate(FALSE);
2060 updateVisibleItems();
2061 setAutoUpdate(autoU);
2062 if (autoU && isVisible())
2063 repaint();
2066 // re-seat the current item
2067 setCurrentItem(cur != 0 ? itemRow(cur) : -1);
2070 // visits each item, calculates the maximum width
2071 // and updates QTableView
2072 void KTreeView::updateCellWidth()
2074 // make cells at least 1 pixel wide to avoid singularities (division by zero)
2075 int maxW = 1;
2076 forEveryVisibleItem(&KTreeView::getMaxItemWidth, &maxW);
2077 maxItemWidth = maxW;
2078 updateTableSize();
2080 // correct offsets
2081 int xoff = xOffset();
2082 int yoff = yOffset();
2083 if (xoff > maxXOffset()) xoff = maxXOffset();
2084 if (yoff > maxYOffset()) yoff = maxYOffset();
2086 setOffset(xoff, yoff);
2089 void KTreeView::updateVisibleItems()
2091 int index = 0;
2092 int count = 0;
2093 updateVisibleItemRec(treeRoot, index, count);
2094 assert(index == count);
2095 setNumRows(count);
2096 updateCellWidth();
2099 void KTreeView::updateVisibleItemRec(KTreeViewItem* item, int& index, int& count)
2101 if (!item->isExpanded()) {
2102 // no visible items if not expanded
2103 return;
2107 * Record the children of item in the list of visible items.
2109 * Don't register item itself, it's already in the list. Also only
2110 * allocate new space for children.
2112 count += item->childCount();
2113 if (count > itemCapacity) {
2114 // must reallocate
2115 int newCapacity = itemCapacity;
2116 do {
2117 newCapacity += newCapacity;
2118 } while (newCapacity < count);
2119 KTreeViewItem** newItems = new KTreeViewItem*[newCapacity];
2120 // clear the unneeded space
2121 for (int i = index; i < newCapacity; i++) {
2122 newItems[i] = 0;
2124 // move already accumulated items over
2125 for (int i = index-1; i >= 0; i--) {
2126 newItems[i] = visibleItems[i];
2128 delete[] visibleItems;
2129 visibleItems = newItems;
2130 itemCapacity = newCapacity;
2132 // insert children
2133 for (KTreeViewItem* i = item->getChild(); i != 0; i = i->getSibling()) {
2134 visibleItems[index++] = i;
2135 updateVisibleItemRec(i, index, count);