Fixed typo; now wheel mouse should work.
[kdbg.git] / kdbg / ktreeview.cpp
blobe0182618e6740db331049eafddf7a14803abed0f
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 moveCurrentToSibling(false),
606 itemCapacity(500),
607 visibleItems(0),
608 rubberband_mode(false)
610 initMetaObject();
611 setCellHeight(0);
612 setCellWidth(0);
613 setNumRows(0);
614 setNumCols(1);
615 setTableFlags(Tbl_autoScrollBars | Tbl_clipCellPainting | Tbl_snapToVGrid);
616 clearTableFlags(Tbl_scrollLastVCell | Tbl_scrollLastHCell | Tbl_snapToVGrid);
617 switch(style()) {
618 case WindowsStyle:
619 case MotifStyle:
620 setFrameStyle(QFrame::WinPanel | QFrame::Sunken);
621 setBackgroundColor(colorGroup().base());
622 break;
623 default:
624 setFrameStyle(QFrame::Panel | QFrame::Plain);
625 setLineWidth(1);
627 #if QT_VERSION < 200
628 setFocusPolicy(StrongFocus);
629 #else
630 setFocusPolicy(WheelFocus);
631 #endif
632 treeRoot = new KTreeViewItem;
633 treeRoot->setExpanded(true);
634 treeRoot->owner = this;
636 visibleItems = new KTreeViewItem*[itemCapacity];
637 // clear those pointers
638 for (int j = itemCapacity-1; j >= 0; j--) {
639 visibleItems[j] = 0;
643 // destructor
644 KTreeView::~KTreeView()
646 goingDown = true;
647 clear();
648 delete[] visibleItems;
649 delete treeRoot;
652 // appends a child to the item at the given index with the given text
653 // and pixmap
654 void KTreeView::appendChildItem(const char* theText, const QPixmap& thePixmap,
655 int index)
657 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
658 item->setDeleteChildren(true);
659 appendChildItem(item, index);
662 // appends a child to the item at the end of the given path with
663 // the given text and pixmap
664 void KTreeView::appendChildItem(const char* theText, const QPixmap& thePixmap,
665 const KPath& thePath)
667 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
668 item->setDeleteChildren(true);
669 appendChildItem(item, thePath);
672 // appends the given item to the children of the item at the given index
673 void KTreeView::appendChildItem(KTreeViewItem* newItem, int index)
675 /* find parent item and append new item to parent's sub tree */
676 KTreeViewItem* parentItem = itemAt(index);
677 if (!parentItem)
678 return;
679 appendChildItem(parentItem, newItem);
682 // appends the given item to the children of the item at the end of the
683 // given path
684 void KTreeView::appendChildItem(KTreeViewItem* newItem, const KPath& thePath)
686 /* find parent item and append new item to parent's sub tree */
687 KTreeViewItem* parentItem = itemAt(thePath);
688 if (!parentItem)
689 return;
690 appendChildItem(parentItem, newItem);
693 // indicates whether horizontal scrollbar appears only when needed
694 bool KTreeView::autoBottomScrollBar() const
696 return testTableFlags(Tbl_autoHScrollBar);
699 // indicates whether vertical scrollbar appears only when needed
700 bool KTreeView::autoScrollBar() const
702 return testTableFlags(Tbl_autoVScrollBar);
705 // indicates whether display updates automatically on changes
706 bool KTreeView::autoUpdate() const
708 return QTableView::autoUpdate();
711 // indicates whether horizontal scrollbar is present
712 bool KTreeView::bottomScrollBar() const
714 return testTableFlags(Tbl_hScrollBar);
717 // translate mouse coord to cell coord
718 QPoint KTreeView::cellCoords(int row, const QPoint& widgetCoord)
720 int cellX = 0, cellY = 0;
721 colXPos(0, &cellX);
722 rowYPos(row, &cellY);
723 return QPoint(widgetCoord.x() - cellX, widgetCoord.y() - cellY);
726 // find item at specified index and change pixmap and/or text
727 void KTreeView::changeItem(const char *newText,
728 const QPixmap *newPixmap,
729 int index)
731 KTreeViewItem *item = itemAt(index);
732 if(item)
733 changeItem(item, index, newText, newPixmap);
736 // find item at end of specified path, and change pixmap and/or text
737 void KTreeView::changeItem(const char* newText,
738 const QPixmap* newPixmap,
739 const KPath& thePath)
741 KTreeViewItem* item = itemAt(thePath);
742 if (item) {
743 int index = itemRow(item);
744 changeItem(item, index, newText, newPixmap);
748 // clear all items from list and erase display
749 void KTreeView::clear()
751 setCurrentItem(-1);
753 /* somewhat of a hack for takeItem so it doesn't update the current item... */
754 clearing = TRUE;
756 bool autoU = autoUpdate();
757 setAutoUpdate(FALSE);
758 QStack<KTreeViewItem> stack;
759 stack.push(treeRoot);
760 while(!stack.isEmpty()) {
761 KTreeViewItem *item = stack.pop();
762 if(item->hasChild()) {
763 stack.push(item);
764 stack.push(item->getChild());
766 else if(item->hasSibling()) {
767 stack.push(item);
768 stack.push(item->getSibling());
770 else if(item->getParent() != 0) {
771 takeItem(item);
772 delete item;
775 clearing = FALSE;
776 if(goingDown || QApplication::closingDown())
777 return;
778 setAutoUpdate(autoU);
779 if(autoU && isVisible())
780 repaint();
783 // return a count of all the items in the tree, whether visible or not
784 uint KTreeView::count()
786 int total = 0;
787 forEveryItem(&KTreeView::countItem, (void *)&total);
788 return total;
791 // returns the index of the current (highlighted) item
792 int KTreeView::currentItem() const
794 return current;
797 // collapses the item at the specified row index.
798 void KTreeView::collapseItem(int index, bool emitSignal)
800 KTreeViewItem* item = itemAt(index);
801 if (item)
802 collapseSubTree(item, emitSignal);
805 // expands the item at the specified row indes.
806 void KTreeView::expandItem(int index, bool emitSignal)
808 KTreeViewItem* item = itemAt(index);
809 if (item)
810 expandSubTree(item, emitSignal);
813 // returns the depth the tree is automatically expanded to when
814 // items are added
815 int KTreeView::expandLevel() const
817 return expansion;
820 // visits every item in the tree, visible or not and applies
821 // the user supplied function with the item and user data passed as parameters
822 // if user supplied function returns true, traversal ends and function returns
823 bool KTreeView::forEveryItem(KForEveryFunc func, void* user, KTreeViewItem* item)
825 if (item == 0) {
826 item = treeRoot;
828 assert(item->owner == this);
829 item = item->getChild();
831 while (item != 0) {
832 // visit the siblings
833 if ((*func)(item, user)) {
834 return true;
836 // visit the children (recursively)
837 if (item->hasChild()) {
838 if (forEveryItem(func, user, item))
839 return true;
841 item = item->getSibling();
843 return false;
846 // visits every visible item in the tree in order and applies the
847 // user supplied function with the item and user data passed as parameters
848 // if user supplied function returns TRUE, traversal ends and function
849 // returns
850 bool KTreeView::forEveryVisibleItem(KForEveryFunc func, void *user,
851 KTreeViewItem* item)
853 if (item == 0) {
854 item = treeRoot;
855 } else {
856 // children are invisible (hence, nothing to do)
857 // if item is invisible or collapsed
858 if (!item->isVisible() || !item->isExpanded()) {
859 return false;
862 assert(item->owner == this);
863 item = item->getChild();
865 while (item != 0) {
866 // visit the siblings
867 if ((*func)(item, user)) {
868 return true;
870 // visit the children (recursively)
871 if (item->hasChild() && item->isExpanded()) {
872 if (forEveryVisibleItem(func, user, item))
873 return true;
875 item = item->getSibling();
877 return false;
880 // returns a pointer to the KTreeViewItem at the current index
881 // or 0 if no current item
882 KTreeViewItem *KTreeView::getCurrentItem()
884 if(current == -1) return 0;
885 return itemAt(current);
888 // returns the current indent spacing
889 int KTreeView::indentSpacing()
891 return itemIndent;
894 // inserts a new item with the specified text and pixmap before
895 // or after the item at the given index, depending on the value
896 // of prefix
897 // if index is negative, appends item to tree at root level
898 bool KTreeView::insertItem(const char* theText, const QPixmap& thePixmap,
899 int row, bool prefix)
901 KTreeViewItem* refItem = itemAt(row);
903 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
904 item->setDeleteChildren(true);
906 bool success = insertItem(refItem, item, prefix);
907 if (!success)
908 delete item;
909 return success;
912 // inserts a new item with the specified text and pixmap before
913 // or after the item at the end of the given path, depending on the value
914 // of prefix
915 bool KTreeView::insertItem(const char* theText, const QPixmap& thePixmap,
916 const KPath& path, bool prefix)
918 KTreeViewItem* refItem = itemAt(path);
920 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
921 item->setDeleteChildren(true);
923 bool success = insertItem(refItem, item, prefix);
924 if (!success)
925 delete item;
926 return success;
929 // inserts the given item or derived object into the tree before
930 // or after the item at the given index, depending on the value
931 // of prefix
932 // if index is negative, appends item to tree at root level
933 bool KTreeView::insertItem(KTreeViewItem* newItem,
934 int index, bool prefix)
936 // find the item currently at the index, if there is one
937 KTreeViewItem* refItem = itemAt(index);
939 // insert new item at the appropriate place
940 return insertItem(refItem, newItem, prefix);
943 // inserts the given item or derived object into the tree before
944 // or after the item at the end of the given path, depending on the value
945 // of prefix
946 bool KTreeView::insertItem(KTreeViewItem* newItem,
947 const KPath& thePath, bool prefix)
949 // find the item currently at the end of the path, if there is one
950 KTreeViewItem* refItem = itemAt(thePath);
952 // insert new item at appropriate place
953 return insertItem(refItem, newItem, prefix);
957 * returns pointer to KTreeViewItem at the specifed row or 0 if row is out
958 * of limits.
960 KTreeViewItem* KTreeView::itemAt(int row)
962 if (row < 0 || row >= numRows()) {
963 return 0;
965 else {
966 // lookup the item in the list of visible items
967 assert(row < itemCapacity);
968 KTreeViewItem* i = visibleItems[row];
969 assert(i != 0);
970 return i;
974 // returns pointer to KTreeViewItem at the end of the
975 // path or 0 if not found
976 KTreeViewItem* KTreeView::itemAt(const KPath& path)
978 if (path.isEmpty())
979 return 0;
981 // need a copy of the path because recursiveFind will destroy it
982 KPath pathCopy;
983 pathCopy.setAutoDelete(false);
984 pathCopy = path;
986 return recursiveFind(pathCopy);
989 // computes the path of the item at the specified index
990 // if index is invalid, nothing is done.
991 void KTreeView::itemPath(int row, KPath& path)
993 KTreeViewItem* item = itemAt(row);
994 if (item != 0) {
995 itemPath(item, path);
999 // returns the row in the visible tree of the given item or
1000 // -1 if not found
1001 int KTreeView::itemRow(KTreeViewItem* item)
1003 if (item->owner == this) {
1004 // search in list of visible items
1005 for (int i = numRows()-1; i >= 0; i--) {
1006 if (visibleItems[i] == item) {
1007 return i;
1011 // not found
1012 return -1;
1016 * move the subtree at the specified index up one branch level (make root
1017 * item a sibling of its current parent)
1019 void KTreeView::join(int index)
1021 KTreeViewItem *item = itemAt(index);
1022 if(item)
1023 join(item);
1027 * move the subtree at the specified index up one branch level (make root
1028 * item a sibling of it's current parent)
1030 void KTreeView::join(const KPath& path)
1032 KTreeViewItem *item = itemAt(path);
1033 if (item)
1034 join(item);
1037 /* move item at specified index one slot down in its parent's sub tree */
1038 void KTreeView::lowerItem(int index)
1040 KTreeViewItem *item = itemAt(index);
1041 if(item)
1042 lowerItem(item);
1045 /* move item at specified path one slot down in its parent's sub tree */
1046 void KTreeView::lowerItem(const KPath& path)
1048 KTreeViewItem* item = itemAt(path);
1049 if (item)
1050 lowerItem(item);
1053 /* move item at specified index one slot up in its parent's sub tree */
1054 void KTreeView::raiseItem(int index)
1056 KTreeViewItem* item = itemAt(index);
1057 if (item)
1058 raiseItem(item);
1061 /* move item at specified path one slot up in its parent's sub tree */
1062 void KTreeView::raiseItem(const KPath& path)
1064 KTreeViewItem* item = itemAt(path);
1065 if (item)
1066 raiseItem(item);
1069 // remove the item at the specified index and delete it
1070 void KTreeView::removeItem(int index)
1072 KTreeViewItem *item = itemAt(index);
1073 if(item) {
1074 takeItem(item);
1075 delete item;
1079 // remove the item at the end of the specified path and delete it
1080 void KTreeView::removeItem(const KPath& thePath)
1082 KTreeViewItem* item = itemAt(thePath);
1083 if (item) {
1084 takeItem(item);
1085 delete item;
1089 // indicates whether vertical scrollbar is present
1090 bool KTreeView::scrollBar() const
1092 return testTableFlags(Tbl_vScrollBar);
1095 void KTreeView::scrollVisible(KTreeViewItem* item, bool children)
1097 if (item == 0)
1098 return;
1099 int row = itemRow(item);
1100 if (row < 0)
1101 return; /* do nothing if invisible */
1103 if (children && item->isExpanded()) {
1104 // we are concerned about children
1105 if (!rowIsVisible(row)) {
1106 // just move to the top
1107 setTopCell(row);
1108 } else {
1109 // this is the complicated part
1110 // count the visible children (including grandchildren)
1111 int numVisible = 0;
1112 forEveryVisibleItem(countItem, &numVisible, item);
1113 // if the last child is visible, do nothing
1114 if (rowIsVisible(row + numVisible))
1115 return;
1117 * Basically, item will become the top cell; but if there are
1118 * more visible rows in the widget than we have children, then
1119 * we don't move that far.
1121 int remain = lastRowVisible()-topCell()-numVisible;
1122 if (remain <= 0) {
1123 setTopCell(row);
1124 } else {
1125 setTopCell(QMAX(0,row-remain));
1128 } else {
1129 // we are not concerned about children
1130 if (rowIsVisible(row))
1131 return;
1132 // just move the item to the top
1133 setTopCell(row);
1137 // enables/disables auto update of display
1138 void KTreeView::setAutoUpdate(bool enable)
1140 QTableView::setAutoUpdate(enable);
1143 // enables/disables horizontal scrollbar
1144 void KTreeView::setBottomScrollBar(bool enable)
1146 enable ? setTableFlags(Tbl_hScrollBar) :
1147 clearTableFlags(Tbl_hScrollBar);
1150 // sets the current item and hightlights it, emitting signals
1151 void KTreeView::setCurrentItem(int row)
1153 if (row == current)
1154 return;
1155 int numVisible = numRows();
1156 if (row > numVisible)
1157 return;
1158 int oldCurrent = current;
1159 current = row;
1160 if (oldCurrent < numVisible)
1161 updateCell(oldCurrent, 0);
1162 if (current > -1) {
1163 updateCell(current, 0, false);
1164 emit highlighted(current);
1168 // enables/disables drawing of expand button
1169 void KTreeView::setExpandButtonDrawing(bool enable)
1171 if (drawExpandButton == enable)
1172 return;
1173 drawExpandButton = enable;
1175 // the user parameter is cast to a bool in setItemExpandButtonDrawing
1176 forEveryItem(&KTreeView::setItemExpandButtonDrawing, enable ? &enable : 0);
1178 if (autoUpdate() && isVisible())
1179 repaint();
1182 // sets depth to which subtrees are automatically expanded, and
1183 // redraws tree if auto update enabled
1184 void KTreeView::setExpandLevel(int level)
1186 if (expansion == level)
1187 return;
1188 expansion = level;
1189 KTreeViewItem* item = getCurrentItem();
1190 forEveryItem(&KTreeView::setItemExpandLevel, 0);
1191 while (item != 0) {
1192 if (item->getParent()->isExpanded())
1193 break;
1194 item = item->getParent();
1196 if (item != 0)
1197 setCurrentItem(itemRow(item));
1198 if (autoUpdate() && isVisible())
1199 repaint();
1202 // sets the indent margin for all branches and repaints if auto update enabled
1203 void KTreeView::setIndentSpacing(int spacing)
1205 if (itemIndent == spacing)
1206 return;
1207 itemIndent = spacing;
1208 updateCellWidth();
1209 if (autoUpdate() && isVisible())
1210 repaint();
1213 void KTreeView::setMoveCurrentToSibling(bool m)
1215 moveCurrentToSibling = m;
1218 // enables/disables vertical scrollbar
1219 void KTreeView::setScrollBar(bool enable)
1221 enable ? setTableFlags(Tbl_vScrollBar) :
1222 clearTableFlags(Tbl_vScrollBar);
1225 // enables/disables display of item text (keys)
1226 void KTreeView::setShowItemText(bool enable)
1228 if (showText == enable)
1229 return;
1230 showText = enable;
1232 // the user parameter is cast to a bool in setItemShowText
1233 forEveryItem(&KTreeView::setItemShowText, enable ? &enable : 0);
1235 if (autoUpdate() && isVisible())
1236 repaint();
1239 // indicates whether vertical scrolling is by pixel or row
1240 void KTreeView::setSmoothScrolling(bool enable)
1242 enable ? setTableFlags(Tbl_smoothVScrolling) :
1243 clearTableFlags(Tbl_smoothVScrolling);
1246 // enables/disables tree branch drawing
1247 void KTreeView::setTreeDrawing(bool enable)
1249 if (drawTree == enable)
1250 return;
1251 drawTree = enable;
1253 // the user parameter is cast to a bool in setItemTreeDrawing
1254 forEveryItem(&KTreeView::setItemTreeDrawing, enable ? &enable : 0);
1256 if (autoUpdate() && isVisible())
1257 repaint();
1260 // indicates whether item text keys are displayed
1261 bool KTreeView::showItemText() const
1263 return showText;
1266 // indicates whether scrolling is by pixel or row
1267 bool KTreeView::smoothScrolling() const
1269 return testTableFlags(Tbl_smoothVScrolling);
1272 // indents the item at the given index, splitting the tree into
1273 // a new branch
1274 void KTreeView::split(int index)
1276 KTreeViewItem *item = itemAt(index);
1277 if(item)
1278 split(item);
1281 // indents the item at the given path, splitting the tree into
1282 // a new branch
1283 void KTreeView::split(const KPath& path)
1285 KTreeViewItem* item = itemAt(path);
1286 if (item)
1287 split(item);
1290 // removes item at specified index from tree but does not delete it
1291 // returns pointer to the item or 0 if not succesful
1292 KTreeViewItem *KTreeView::takeItem(int index)
1294 KTreeViewItem *item = itemAt(index);
1295 if(item)
1296 takeItem(item);
1297 return item;
1300 // removes item at specified path from tree but does not delete it
1301 // returns pointer to the item or 0 if not successful
1302 KTreeViewItem* KTreeView::takeItem(const KPath& path)
1304 KTreeViewItem* item = itemAt(path);
1305 if (item)
1306 takeItem(item);
1307 return item;
1310 // indicates whether tree branches are drawn
1311 bool KTreeView::treeDrawing() const
1313 return drawTree;
1317 // appends a child to the specified parent item (note: a child, not a sibling, is added!)
1318 void KTreeView::appendChildItem(KTreeViewItem* theParent,
1319 KTreeViewItem* newItem)
1321 theParent->appendChild(newItem);
1323 // set item state
1324 newItem->setDrawExpandButton(drawExpandButton);
1325 newItem->setDrawTree(drawTree);
1326 newItem->setDrawText(showText);
1327 if (level(newItem) < expansion) {
1328 newItem->setExpanded(true);
1331 // fix up branch levels of any children that the new item may already have
1332 if(newItem->hasChild()) {
1333 fixChildren(newItem);
1336 // if necessary, adjust cell width, number of rows and repaint
1337 if (newItem->isVisible() || theParent->childCount() == 1) {
1338 bool autoU = autoUpdate();
1339 setAutoUpdate(false);
1340 updateVisibleItems();
1341 setAutoUpdate(autoU);
1342 if (autoU && isVisible())
1343 repaint();
1347 // returns the height of the cell(row) at the specified row (index)
1348 int KTreeView::cellHeight(int row)
1350 return itemAt(row)->height(fontMetrics());
1353 // returns the width of the cells. Note: this is mostly for derived classes
1354 // which have more than 1 column
1355 int KTreeView::cellWidth(int /*col*/)
1357 return maxItemWidth;
1360 // changes the given item with the new text and/or pixmap
1361 void KTreeView::changeItem(KTreeViewItem* toChange, int itemRow,
1362 const char* newText, const QPixmap* newPixmap)
1364 int indent = indentation(toChange);
1365 int oldWidth = toChange->width(indent);
1366 if(newText)
1367 toChange->setText(newText);
1368 if (newPixmap)
1369 toChange->setPixmap(*newPixmap);
1370 if(oldWidth != toChange->width(indent))
1371 updateCellWidth();
1372 if(itemRow == -1)
1373 return;
1374 if(autoUpdate() && rowIsVisible(itemRow))
1375 updateCell(itemRow, 0);
1378 // collapses the subtree at the specified item
1379 void KTreeView::collapseSubTree(KTreeViewItem* subRoot, bool emitSignal)
1381 assert(subRoot->owner == this);
1382 if (!subRoot->isExpanded())
1383 return;
1385 // must move the current item if it is visible
1386 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1388 subRoot->setExpanded(false);
1389 if (subRoot->isVisible()) {
1390 bool autoU = autoUpdate();
1391 setAutoUpdate(false);
1392 updateVisibleItems();
1393 // re-seat current item
1394 if (cur != 0) {
1395 setCurrentItem(itemRow(cur));
1397 if (emitSignal) {
1398 emit collapsed(itemRow(subRoot));
1400 setAutoUpdate(autoU);
1401 if (autoU && isVisible())
1402 repaint();
1406 // used by count() with forEach() function to count total number
1407 // of items in the tree
1408 bool KTreeView::countItem(KTreeViewItem*, void* total)
1410 int* t = static_cast<int*>(total);
1411 (*t)++;
1412 return false;
1415 // expands the subtree at the given item
1416 void KTreeView::expandSubTree(KTreeViewItem* subRoot, bool emitSignal)
1418 assert(subRoot->owner == this);
1419 if (subRoot->isExpanded())
1420 return;
1422 // must move the current item if it is visible
1423 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1425 bool allow = true;
1427 if (subRoot->delayedExpanding) {
1428 emit expanding(subRoot, allow);
1430 if (!allow)
1431 return;
1433 subRoot->setExpanded(true);
1435 if (subRoot->isVisible()) {
1436 bool autoU = autoUpdate();
1437 setAutoUpdate(false);
1438 updateVisibleItems();
1439 // re-seat current item
1440 if (cur != 0) {
1441 setCurrentItem(itemRow(cur));
1443 if (emitSignal) {
1444 emit expanded(itemRow(subRoot));
1446 setAutoUpdate(autoU);
1447 if (autoU && isVisible())
1448 repaint();
1452 // fix up branch levels out of whack from split/join operations on the tree
1453 void KTreeView::fixChildren(KTreeViewItem *parentItem)
1455 KTreeViewItem* childItem = 0;
1456 KTreeViewItem* siblingItem = 0;
1457 // int childBranch = parentItem->getBranch() + 1;
1458 if(parentItem->hasChild()) {
1459 childItem = parentItem->getChild();
1460 // childItem->setBranch(childBranch);
1461 childItem->owner = parentItem->owner;
1462 fixChildren(childItem);
1464 while(childItem && childItem->hasSibling()) {
1465 siblingItem = childItem->getSibling();
1466 // siblingItem->setBranch(childBranch);
1467 siblingItem->owner = parentItem->owner;
1468 fixChildren(siblingItem);
1469 childItem = siblingItem;
1474 * Handle QFocusEvent processing by setting current item to top row if
1475 * there is no current item, and updates cell to add or delete the focus
1476 * rectangle on the highlight bar. The base class is not called because it
1477 * does a repaint() which causes flicker.
1479 void KTreeView::focusInEvent(QFocusEvent *)
1481 if (current < 0 && numRows() > 0)
1482 setCurrentItem(topCell());
1483 updateCell(current, 0);
1486 void KTreeView::focusOutEvent(QFocusEvent *)
1488 updateCell(current, 0);
1491 // called by updateCellWidth() for each item in the visible list
1492 bool KTreeView::getMaxItemWidth(KTreeViewItem* item, void* user)
1494 assert(item->owner != 0);
1495 int indent = item->owner->indentation(item);
1496 int* maxW = static_cast<int*>(user);
1497 int w = item->width(indent);
1498 if (w > *maxW)
1499 *maxW = w;
1500 return false;
1503 int KTreeView::indentation(KTreeViewItem* item) const
1505 return level(item) * itemIndent + itemIndent + 3;
1508 // inserts the new item before or after the reference item, depending
1509 // on the value of prefix
1510 bool KTreeView::insertItem(KTreeViewItem* referenceItem,
1511 KTreeViewItem* newItem,
1512 bool prefix)
1514 assert(newItem != 0);
1515 assert(referenceItem == 0 || referenceItem->owner == this);
1517 /* set the new item's state */
1518 newItem->setDrawExpandButton(drawExpandButton);
1519 newItem->setDrawTree(drawTree);
1520 newItem->setDrawText(showText);
1521 KTreeViewItem* parentItem;
1522 if (referenceItem) {
1523 parentItem = referenceItem->getParent();
1524 int insertIndex = parentItem->childIndex(referenceItem);
1525 if (!prefix)
1526 insertIndex++;
1527 parentItem->insertChild(insertIndex, newItem);
1529 else {
1530 // no reference item, append at end of visible tree
1531 // need to repaint
1532 parentItem = treeRoot;
1533 parentItem->appendChild(newItem);
1536 // set item expansion
1537 if (level(newItem) < expansion)
1538 newItem->setExpanded(true);
1540 // fix up branch levels of any children
1541 if (newItem->hasChild())
1542 fixChildren(newItem);
1544 // if repaint necessary, do it if visible and auto update
1545 // enabled
1546 if (newItem->isVisible() || parentItem->childCount() == 1) {
1547 bool autoU = autoUpdate();
1548 setAutoUpdate(FALSE);
1549 updateVisibleItems();
1550 setAutoUpdate(autoU);
1551 if (autoU && isVisible())
1552 repaint();
1554 return true;
1558 * returns pointer to item's path
1560 void KTreeView::itemPath(KTreeViewItem* item, KPath& path) const
1562 assert(item != 0);
1563 assert(item->owner == this);
1564 if (item != treeRoot) {
1565 itemPath(item->getParent(), path);
1566 path.push(new QString(item->getText()));
1571 * joins the item's branch into the tree, making the item a sibling of its
1572 * parent
1574 void KTreeView::join(KTreeViewItem *item)
1576 KTreeViewItem *itemParent = item->getParent();
1577 if(itemParent->hasParent()) {
1578 bool autoU = autoUpdate();
1579 setAutoUpdate(FALSE);
1580 takeItem(item);
1581 insertItem(itemParent, item, FALSE);
1582 setAutoUpdate(autoU);
1583 if(autoU && isVisible())
1584 repaint();
1588 // handles keyboard interface
1589 void KTreeView::keyPressEvent(QKeyEvent* e)
1591 if (numRows() == 0)
1592 return; /* nothing to do */
1594 /* if there's no current item, make the top item current */
1595 if (currentItem() < 0)
1596 setCurrentItem(topCell());
1597 assert(currentItem() >= 0); /* we need a current item */
1598 assert(itemAt(currentItem()) != 0); /* we really need a current item */
1600 // give currentItem a chance to handle the event
1601 if (itemAt(currentItem())->keyEvent(e))
1602 return; /* handled */
1604 int pageSize, delta;
1605 KTreeViewItem* item;
1606 int key = e->key();
1607 repeat:
1608 switch (key) {
1609 case Key_Up:
1610 // make previous item current, scroll up if necessary
1611 if (currentItem() > 0) {
1612 setCurrentItem(currentItem() - 1);
1613 scrollVisible(itemAt(currentItem()), false);
1615 break;
1616 case Key_Down:
1617 // make next item current, scroll down if necessary
1618 if (currentItem() < numRows() - 1) {
1619 setCurrentItem(currentItem() + 1);
1620 if (currentItem() > lastRowVisible()) {
1621 // scrollVisible is not feasible here because
1622 // it scrolls the item to the top
1623 setTopCell(topCell() + currentItem() - lastRowVisible());
1624 } else if (currentItem() < topCell()) {
1625 setTopCell(currentItem());
1628 break;
1629 case Key_Next:
1630 // move highlight one page down and scroll down
1631 delta = currentItem() - topCell();
1632 pageSize = lastRowVisible() - topCell();
1633 setTopCell(QMIN(topCell() + pageSize, numRows() - 1));
1634 setCurrentItem(QMIN(topCell() + delta, numRows() - 1));
1635 break;
1636 case Key_Prior:
1637 // move highlight one page up and scroll up
1638 delta = currentItem() - topCell();
1639 pageSize = lastRowVisible() - topCell();
1640 setTopCell(QMAX(topCell() - pageSize, 0));
1641 setCurrentItem(QMAX(topCell() + delta, 0));
1642 break;
1643 case Key_Plus:
1644 case Key_Right:
1645 // if current item has subtree and is collapsed, expand it
1646 item = itemAt(currentItem());
1647 if (item->isExpanded() && item->hasChild() && key == Key_Right) {
1648 // going right on an expanded item is like going down
1649 key = Key_Down;
1650 goto repeat;
1651 } else {
1652 expandSubTree(item, true);
1653 scrollVisible(item, true);
1655 break;
1656 case Key_Minus:
1657 case Key_Left:
1658 // if current item has subtree and is expanded, collapse it
1659 item = itemAt(currentItem());
1660 if ((!item->isExpanded() || !item->hasChild()) && key == Key_Left) {
1661 // going left on a collapsed item goes to its parent
1662 item = item->getParent();
1663 if (item == treeRoot)
1664 break; /* we're already at the top */
1665 assert(item->isVisible());
1666 setCurrentItem(itemRow(item));
1667 } else {
1668 collapseSubTree(item, true);
1670 scrollVisible(item, false);
1671 break;
1672 case Key_Return:
1673 case Key_Enter:
1674 // select the current item
1675 if (currentItem() >= 0)
1676 emit selected(currentItem());
1677 break;
1678 default:
1679 break;
1683 // handles keyboard interface
1684 void KTreeView::keyReleaseEvent(QKeyEvent* e)
1686 if (currentItem() >= 0 && itemAt(currentItem()) != 0)
1687 itemAt(currentItem())->keyEvent(e);
1690 int KTreeView::level(KTreeViewItem* item) const
1692 assert(item != 0);
1693 assert(item->owner == this);
1694 assert(item != treeRoot);
1695 int l = 0;
1696 item = item->parent->parent; /* since item != treeRoot, there is a parent */
1697 while (item != 0) {
1698 item = item->parent;
1699 l++;
1701 return l;
1704 /* move specified item down one slot in parent's subtree */
1705 void KTreeView::lowerItem(KTreeViewItem *item)
1707 KTreeViewItem *itemParent = item->getParent();
1708 uint itemChildIndex = itemParent->childIndex(item);
1709 if(itemChildIndex < itemParent->childCount() - 1) {
1710 bool autoU = autoUpdate();
1711 setAutoUpdate(FALSE);
1712 takeItem(item);
1713 insertItem(itemParent->childAt(itemChildIndex), item, FALSE);
1714 setAutoUpdate(autoU);
1715 if(autoU && isVisible())
1716 repaint();
1720 // handle mouse double click events by selecting the clicked item
1721 // and emitting the signal
1722 void KTreeView::mouseDoubleClickEvent(QMouseEvent* e)
1724 // find out which row has been clicked
1725 int itemClicked = findRow(e->y());
1727 if (itemClicked < 0)
1728 return; /* invalid row, do nothing */
1730 KTreeViewItem* item = itemAt(itemClicked);
1731 if (item == 0)
1732 return;
1734 // translate mouse coord to cell coord
1735 QPoint cellCoord = cellCoords(itemClicked, e->pos());
1737 // first ask item
1738 if (item->mouseEvent(e, cellCoord))
1739 return;
1741 // hit test item
1742 int indent = indentation(item);
1743 if (item->boundingRect(indent).contains(cellCoord))
1744 emit selected(itemClicked);
1747 // handle mouse movement events
1748 void KTreeView::mouseMoveEvent(QMouseEvent* e)
1750 // in rubberband_mode we actually scroll the window now
1751 if (rubberband_mode) {
1752 move_rubberband(e->pos());
1753 } else {
1754 // else forward to current item
1755 int current = currentItem();
1756 if (current >= 0 && itemAt(current))
1757 itemAt(current)->mouseEvent(e, cellCoords(current, e->pos()));
1762 // handle single mouse presses
1763 void KTreeView::mousePressEvent(QMouseEvent* e)
1765 /* first: cancel rubberbanding if it's on */
1766 if (rubberband_mode)
1768 // another button was pressed while rubberbanding, stop the move.
1769 // RB: if we allow other buttons while rubberbanding the tree can expand
1770 // while rubberbanding - we then need to recalculate and resize the
1771 // rubberband rect and show the new size
1772 end_rubberband();
1773 return;
1776 // find out which row has been clicked
1777 int itemClicked = findRow(e->y());
1779 // nothing to do if not on valid row
1780 if (itemClicked < 0)
1781 return;
1782 KTreeViewItem* item = itemAt(itemClicked);
1783 if (!item)
1784 return;
1786 // translate mouse coord to cell coord
1787 QPoint cellCoord = cellCoords(itemClicked, e->pos());
1789 // give the item a crack
1790 if (item->mouseEvent(e, cellCoord))
1791 return; /* event eaten by item */
1793 // check for rubberbanding
1794 if (e->button() == MidButton)
1796 // RB: the MMB is hardcoded to the "rubberband" scroll mode
1797 if (!rubberband_mode) {
1798 start_rubberband(e->pos());
1800 return;
1803 if (e->button() == RightButton) {
1804 emit rightPressed(itemClicked, e->pos());
1806 /* hit test expand button (doesn't set currentItem) */
1807 else if (item->expandButtonClicked(cellCoord)) {
1808 if (item->isExpanded()) {
1809 collapseSubTree(item, true);
1810 } else {
1811 expandSubTree(item, true);
1812 scrollVisible(item, true); /* make children visible */
1815 // hit test item
1816 else if (item->boundingRect(indentation(item)).contains(cellCoord)) {
1817 setCurrentItem(itemClicked);
1821 // handle mouse release events
1822 void KTreeView::mouseReleaseEvent(QMouseEvent *e)
1824 /* if it's the MMB end rubberbanding */
1825 if (rubberband_mode) {
1826 if (e->button() == MidButton)
1827 end_rubberband();
1828 return;
1830 // forward to current item
1831 int current = currentItem();
1832 if (current >= 0 && itemAt(current))
1833 itemAt(current)->mouseEvent(e, cellCoords(current, e->pos()));
1836 // rubberband move: draw the rubberband
1837 void KTreeView::draw_rubberband()
1840 * RB: I'm using a white pen because of the XorROP mode. I would prefer
1841 * to draw the rectangle in red but I don't now how to get a pen which
1842 * draws red in XorROP mode (this depends on the background). In fact
1843 * the color should be configurable.
1846 if (!rubberband_mode) return;
1847 QPainter paint(this);
1848 paint.setPen(white);
1849 paint.setRasterOp(XorROP);
1850 paint.drawRect(xOffset()*viewWidth()/totalWidth(),
1851 yOffset()*viewHeight()/totalHeight(),
1852 rubber_width+1, rubber_height+1);
1853 paint.end();
1856 // rubberband move: start move
1857 void KTreeView::start_rubberband(const QPoint& where)
1859 if (rubberband_mode) { // Oops!
1860 end_rubberband();
1862 /* RB: Don't now, if this check is necessary */
1863 if (!viewWidth() || !viewHeight()) return;
1864 if (!totalWidth() || !totalHeight()) return;
1866 // calculate the size of the rubberband rectangle
1867 rubber_width = viewWidth()*viewWidth()/totalWidth();
1868 if (rubber_width > viewWidth()) rubber_width = viewWidth();
1869 rubber_height = viewHeight()*viewHeight()/totalHeight();
1870 if (rubber_height > viewHeight()) rubber_height = viewHeight();
1872 // remember the cursor position and the actual offset
1873 rubber_startMouse = where;
1874 rubber_startX = xOffset();
1875 rubber_startY = yOffset();
1876 rubberband_mode=TRUE;
1877 draw_rubberband();
1880 // rubberband move: end move
1881 void KTreeView::end_rubberband()
1883 if (!rubberband_mode) return;
1884 draw_rubberband();
1885 rubberband_mode = FALSE;
1888 // rubberband move: hanlde mouse moves
1889 void KTreeView::move_rubberband(const QPoint& where)
1891 if (!rubberband_mode) return;
1893 // look how much the mouse moved and calc the new scroll position
1894 QPoint delta = where - rubber_startMouse;
1895 int nx = rubber_startX + delta.x() * totalWidth() / viewWidth();
1896 int ny = rubber_startY + delta.y() * totalHeight() / viewHeight();
1898 // check the new position (and make it valid)
1899 if (nx < 0) nx = 0;
1900 else if (nx > maxXOffset()) nx = maxXOffset();
1901 if (ny < 0) ny = 0;
1902 else if (ny > maxYOffset()) ny = maxYOffset();
1904 // redraw the rubberband at the new position
1905 draw_rubberband();
1906 setOffset(nx,ny);
1907 draw_rubberband();
1911 // paints the cell at the specified row and col
1912 // col is ignored for now since there is only one
1913 void KTreeView::paintCell(QPainter* p, int row, int)
1915 KTreeViewItem* item = itemAt(row);
1916 if (item == 0)
1917 return;
1919 QColorGroup cg = colorGroup();
1920 int indent = indentation(item);
1921 item->paint(p, indent, cg,
1922 current == row); /* highlighted */
1926 /* This is needed to make the kcontrol's color setup working (Marcin Dalecki) */
1927 void KTreeView::paletteChange(const QPalette &)
1929 setBackgroundColor(colorGroup().base());
1930 repaint(true);
1934 /* raise the specified item up one slot in parent's subtree */
1935 void KTreeView::raiseItem(KTreeViewItem *item)
1937 KTreeViewItem *itemParent = item->getParent();
1938 int itemChildIndex = itemParent->childIndex(item);
1939 if(itemChildIndex > 0) {
1940 bool autoU = autoUpdate();
1941 setAutoUpdate(FALSE);
1942 takeItem(item);
1943 insertItem(itemParent->childAt(--itemChildIndex), item, TRUE);
1944 setAutoUpdate(autoU);
1945 if(autoU && isVisible())
1946 repaint();
1950 // find the item at the path
1951 KTreeViewItem* KTreeView::recursiveFind(KPath& path)
1953 if (path.isEmpty())
1954 return treeRoot;
1956 // get the next key
1957 QString* searchString = path.pop();
1959 // find the parent item
1960 KTreeViewItem* parent = recursiveFind(path);
1961 if (parent == 0)
1962 return 0;
1965 * Iterate through the parent's children searching for searchString.
1967 KTreeViewItem* sibling = parent->getChild();
1968 while (sibling != 0) {
1969 if (*searchString == sibling->getText()) {
1970 break; /* found it! */
1972 sibling = sibling->getSibling();
1974 return sibling;
1977 void KTreeView::setItemExpanded(KTreeViewItem* item)
1979 if (level(item) < expansion) {
1980 expandSubTree(item, true);
1981 } else {
1982 collapseSubTree(item, true);
1986 // called by setExpandLevel for each item in tree
1987 bool KTreeView::setItemExpandLevel(KTreeViewItem* item, void*)
1989 assert(item->owner != 0);
1990 item->owner->setItemExpanded(item);
1991 return false;
1994 // called by setExpandButtonDrawing for every item in tree
1995 // the parameter drawButton is used as (and implicitly cast to) a bool
1996 bool KTreeView::setItemExpandButtonDrawing(KTreeViewItem* item,
1997 void* drawButton)
1999 item->setDrawExpandButton(drawButton);
2000 return false;
2003 // called by setShowItemText for every item in tree
2004 // the parameter newDrawText is used as (and implicitly cast to) a bool
2005 bool KTreeView::setItemShowText(KTreeViewItem* item,
2006 void* newDrawText)
2008 item->setDrawText(newDrawText);
2009 return false;
2012 // called by setTreeDrawing for every item in tree
2013 // the parameter drawTree is used as (and implicitly cast to) a bool
2014 bool KTreeView::setItemTreeDrawing(KTreeViewItem* item, void* drawTree)
2016 item->setDrawTree(drawTree);
2017 return false;
2020 // makes the item a child of the item above it, splitting
2021 // the tree into a new branch
2022 void KTreeView::split(KTreeViewItem *item)
2024 KTreeViewItem *itemParent = item->getParent();
2025 int itemChildIndex = itemParent->childIndex(item);
2026 if(itemChildIndex == 0)
2027 return;
2028 bool autoU = autoUpdate();
2029 setAutoUpdate(FALSE);
2030 takeItem(item);
2031 appendChildItem(itemParent->childAt(--itemChildIndex), item);
2032 setAutoUpdate(autoU);
2033 if(autoU && isVisible())
2034 repaint();
2037 // removes the item from the tree without deleting it
2038 void KTreeView::takeItem(KTreeViewItem* item)
2040 assert(item->owner == this);
2042 // TODO: go over this function again
2044 bool wasVisible = item->isVisible();
2046 * If we have a current item, make sure that it is not in the subtree
2047 * that we are about to remove. If the current item is in the part
2048 * below the taken-out subtree, we must move it up a number of rows if
2049 * the taken-out subtree is at least partially visible.
2051 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
2052 KTreeViewItem* oldCurrent = cur;
2053 if (wasVisible && cur != 0) {
2054 KTreeViewItem* c = cur;
2055 while (c != 0 && c != item) {
2056 c = c->getParent();
2058 if (c != 0) {
2059 // move current item to parent
2060 cur = item->getParent();
2061 if (cur == treeRoot) {
2062 cur = 0;
2063 // move it to next or previous sibling
2064 if (moveCurrentToSibling) {
2065 if (item->getSibling() != 0) {
2066 cur = item->getSibling();
2067 } else if (treeRoot->getChild() != item) {
2068 cur = treeRoot->getChild();
2069 while (cur != 0 && cur->getSibling() != item)
2070 cur = cur->getSibling();
2076 KTreeViewItem* parentItem = item->getParent();
2077 parentItem->removeChild(item);
2078 item->sibling = 0;
2079 if (wasVisible || parentItem->childCount() == 0) {
2080 bool autoU = autoUpdate();
2081 setAutoUpdate(FALSE);
2082 updateVisibleItems();
2083 setAutoUpdate(autoU);
2084 if (autoU && isVisible())
2085 repaint();
2088 // re-seat the current item
2089 // row changes if cur is below the taken item
2090 current = cur != 0 ? itemRow(cur) : -1;
2091 // signal must be emitted only if item really changed
2092 if (cur != oldCurrent) {
2093 updateCell(current, 0, false);
2094 emit highlighted(current);
2098 // visits each item, calculates the maximum width
2099 // and updates QTableView
2100 void KTreeView::updateCellWidth()
2102 // make cells at least 1 pixel wide to avoid singularities (division by zero)
2103 int maxW = 1;
2104 forEveryVisibleItem(&KTreeView::getMaxItemWidth, &maxW);
2105 maxItemWidth = maxW;
2106 updateTableSize();
2108 // correct offsets
2109 int xoff = xOffset();
2110 int yoff = yOffset();
2111 if (xoff > maxXOffset()) xoff = maxXOffset();
2112 if (yoff > maxYOffset()) yoff = maxYOffset();
2114 setOffset(xoff, yoff);
2117 void KTreeView::updateVisibleItems()
2119 int index = 0;
2120 int count = 0;
2121 updateVisibleItemRec(treeRoot, index, count);
2122 assert(index == count);
2123 setNumRows(count);
2124 updateCellWidth();
2127 void KTreeView::updateVisibleItemRec(KTreeViewItem* item, int& index, int& count)
2129 if (!item->isExpanded()) {
2130 // no visible items if not expanded
2131 return;
2135 * Record the children of item in the list of visible items.
2137 * Don't register item itself, it's already in the list. Also only
2138 * allocate new space for children.
2140 count += item->childCount();
2141 if (count > itemCapacity) {
2142 // must reallocate
2143 int newCapacity = itemCapacity;
2144 do {
2145 newCapacity += newCapacity;
2146 } while (newCapacity < count);
2147 KTreeViewItem** newItems = new KTreeViewItem*[newCapacity];
2148 // clear the unneeded space
2149 for (int i = index; i < newCapacity; i++) {
2150 newItems[i] = 0;
2152 // move already accumulated items over
2153 for (int i = index-1; i >= 0; i--) {
2154 newItems[i] = visibleItems[i];
2156 delete[] visibleItems;
2157 visibleItems = newItems;
2158 itemCapacity = newCapacity;
2160 // insert children
2161 for (KTreeViewItem* i = item->getChild(); i != 0; i = i->getSibling()) {
2162 visibleItems[index++] = i;
2163 updateVisibleItemRec(i, index, count);