When an item having the current item is taken out from the tree,
[kdbg.git] / kdbg / ktreeview.cpp
bloba6ead681c2a40d5c1835530f740d6898a7dd3e7b
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 setFocusPolicy(StrongFocus);
628 treeRoot = new KTreeViewItem;
629 treeRoot->setExpanded(true);
630 treeRoot->owner = this;
632 visibleItems = new KTreeViewItem*[itemCapacity];
633 // clear those pointers
634 for (int j = itemCapacity-1; j >= 0; j--) {
635 visibleItems[j] = 0;
639 // destructor
640 KTreeView::~KTreeView()
642 goingDown = true;
643 clear();
644 delete[] visibleItems;
645 delete treeRoot;
648 // appends a child to the item at the given index with the given text
649 // and pixmap
650 void KTreeView::appendChildItem(const char* theText, const QPixmap& thePixmap,
651 int index)
653 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
654 item->setDeleteChildren(true);
655 appendChildItem(item, index);
658 // appends a child to the item at the end of the given path with
659 // the given text and pixmap
660 void KTreeView::appendChildItem(const char* theText, const QPixmap& thePixmap,
661 const KPath& thePath)
663 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
664 item->setDeleteChildren(true);
665 appendChildItem(item, thePath);
668 // appends the given item to the children of the item at the given index
669 void KTreeView::appendChildItem(KTreeViewItem* newItem, int index)
671 /* find parent item and append new item to parent's sub tree */
672 KTreeViewItem* parentItem = itemAt(index);
673 if (!parentItem)
674 return;
675 appendChildItem(parentItem, newItem);
678 // appends the given item to the children of the item at the end of the
679 // given path
680 void KTreeView::appendChildItem(KTreeViewItem* newItem, const KPath& thePath)
682 /* find parent item and append new item to parent's sub tree */
683 KTreeViewItem* parentItem = itemAt(thePath);
684 if (!parentItem)
685 return;
686 appendChildItem(parentItem, newItem);
689 // indicates whether horizontal scrollbar appears only when needed
690 bool KTreeView::autoBottomScrollBar() const
692 return testTableFlags(Tbl_autoHScrollBar);
695 // indicates whether vertical scrollbar appears only when needed
696 bool KTreeView::autoScrollBar() const
698 return testTableFlags(Tbl_autoVScrollBar);
701 // indicates whether display updates automatically on changes
702 bool KTreeView::autoUpdate() const
704 return QTableView::autoUpdate();
707 // indicates whether horizontal scrollbar is present
708 bool KTreeView::bottomScrollBar() const
710 return testTableFlags(Tbl_hScrollBar);
713 // translate mouse coord to cell coord
714 QPoint KTreeView::cellCoords(int row, const QPoint& widgetCoord)
716 int cellX = 0, cellY = 0;
717 colXPos(0, &cellX);
718 rowYPos(row, &cellY);
719 return QPoint(widgetCoord.x() - cellX, widgetCoord.y() - cellY);
722 // find item at specified index and change pixmap and/or text
723 void KTreeView::changeItem(const char *newText,
724 const QPixmap *newPixmap,
725 int index)
727 KTreeViewItem *item = itemAt(index);
728 if(item)
729 changeItem(item, index, newText, newPixmap);
732 // find item at end of specified path, and change pixmap and/or text
733 void KTreeView::changeItem(const char* newText,
734 const QPixmap* newPixmap,
735 const KPath& thePath)
737 KTreeViewItem* item = itemAt(thePath);
738 if (item) {
739 int index = itemRow(item);
740 changeItem(item, index, newText, newPixmap);
744 // clear all items from list and erase display
745 void KTreeView::clear()
747 setCurrentItem(-1);
749 /* somewhat of a hack for takeItem so it doesn't update the current item... */
750 clearing = TRUE;
752 bool autoU = autoUpdate();
753 setAutoUpdate(FALSE);
754 QStack<KTreeViewItem> stack;
755 stack.push(treeRoot);
756 while(!stack.isEmpty()) {
757 KTreeViewItem *item = stack.pop();
758 if(item->hasChild()) {
759 stack.push(item);
760 stack.push(item->getChild());
762 else if(item->hasSibling()) {
763 stack.push(item);
764 stack.push(item->getSibling());
766 else if(item->getParent() != 0) {
767 takeItem(item);
768 delete item;
771 clearing = FALSE;
772 if(goingDown || QApplication::closingDown())
773 return;
774 setAutoUpdate(autoU);
775 if(autoU && isVisible())
776 repaint();
779 // return a count of all the items in the tree, whether visible or not
780 uint KTreeView::count()
782 int total = 0;
783 forEveryItem(&KTreeView::countItem, (void *)&total);
784 return total;
787 // returns the index of the current (highlighted) item
788 int KTreeView::currentItem() const
790 return current;
793 // collapses the item at the specified row index.
794 void KTreeView::collapseItem(int index, bool emitSignal)
796 KTreeViewItem* item = itemAt(index);
797 if (item)
798 collapseSubTree(item, emitSignal);
801 // expands the item at the specified row indes.
802 void KTreeView::expandItem(int index, bool emitSignal)
804 KTreeViewItem* item = itemAt(index);
805 if (item)
806 expandSubTree(item, emitSignal);
809 // returns the depth the tree is automatically expanded to when
810 // items are added
811 int KTreeView::expandLevel() const
813 return expansion;
816 // visits every item in the tree, visible or not and applies
817 // the user supplied function with the item and user data passed as parameters
818 // if user supplied function returns true, traversal ends and function returns
819 bool KTreeView::forEveryItem(KForEveryFunc func, void* user, KTreeViewItem* item)
821 if (item == 0) {
822 item = treeRoot;
824 assert(item->owner == this);
825 item = item->getChild();
827 while (item != 0) {
828 // visit the siblings
829 if ((*func)(item, user)) {
830 return true;
832 // visit the children (recursively)
833 if (item->hasChild()) {
834 if (forEveryItem(func, user, item))
835 return true;
837 item = item->getSibling();
839 return false;
842 // visits every visible item in the tree in order and applies the
843 // user supplied function with the item and user data passed as parameters
844 // if user supplied function returns TRUE, traversal ends and function
845 // returns
846 bool KTreeView::forEveryVisibleItem(KForEveryFunc func, void *user,
847 KTreeViewItem* item)
849 if (item == 0) {
850 item = treeRoot;
851 } else {
852 // children are invisible (hence, nothing to do)
853 // if item is invisible or collapsed
854 if (!item->isVisible() || !item->isExpanded()) {
855 return false;
858 assert(item->owner == this);
859 item = item->getChild();
861 while (item != 0) {
862 // visit the siblings
863 if ((*func)(item, user)) {
864 return true;
866 // visit the children (recursively)
867 if (item->hasChild() && item->isExpanded()) {
868 if (forEveryVisibleItem(func, user, item))
869 return true;
871 item = item->getSibling();
873 return false;
876 // returns a pointer to the KTreeViewItem at the current index
877 // or 0 if no current item
878 KTreeViewItem *KTreeView::getCurrentItem()
880 if(current == -1) return 0;
881 return itemAt(current);
884 // returns the current indent spacing
885 int KTreeView::indentSpacing()
887 return itemIndent;
890 // inserts a new item with the specified text and pixmap before
891 // or after the item at the given index, depending on the value
892 // of prefix
893 // if index is negative, appends item to tree at root level
894 bool KTreeView::insertItem(const char* theText, const QPixmap& thePixmap,
895 int row, bool prefix)
897 KTreeViewItem* refItem = itemAt(row);
899 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
900 item->setDeleteChildren(true);
902 bool success = insertItem(refItem, item, prefix);
903 if (!success)
904 delete item;
905 return success;
908 // inserts a new item with the specified text and pixmap before
909 // or after the item at the end of the given path, depending on the value
910 // of prefix
911 bool KTreeView::insertItem(const char* theText, const QPixmap& thePixmap,
912 const KPath& path, bool prefix)
914 KTreeViewItem* refItem = itemAt(path);
916 KTreeViewItem* item = new KTreeViewItem(theText, thePixmap);
917 item->setDeleteChildren(true);
919 bool success = insertItem(refItem, item, prefix);
920 if (!success)
921 delete item;
922 return success;
925 // inserts the given item or derived object into the tree before
926 // or after the item at the given index, depending on the value
927 // of prefix
928 // if index is negative, appends item to tree at root level
929 bool KTreeView::insertItem(KTreeViewItem* newItem,
930 int index, bool prefix)
932 // find the item currently at the index, if there is one
933 KTreeViewItem* refItem = itemAt(index);
935 // insert new item at the appropriate place
936 return insertItem(refItem, newItem, prefix);
939 // inserts the given item or derived object into the tree before
940 // or after the item at the end of the given path, depending on the value
941 // of prefix
942 bool KTreeView::insertItem(KTreeViewItem* newItem,
943 const KPath& thePath, bool prefix)
945 // find the item currently at the end of the path, if there is one
946 KTreeViewItem* refItem = itemAt(thePath);
948 // insert new item at appropriate place
949 return insertItem(refItem, newItem, prefix);
953 * returns pointer to KTreeViewItem at the specifed row or 0 if row is out
954 * of limits.
956 KTreeViewItem* KTreeView::itemAt(int row)
958 if (row < 0 || row >= numRows()) {
959 return 0;
961 else {
962 // lookup the item in the list of visible items
963 assert(row < itemCapacity);
964 KTreeViewItem* i = visibleItems[row];
965 assert(i != 0);
966 return i;
970 // returns pointer to KTreeViewItem at the end of the
971 // path or 0 if not found
972 KTreeViewItem* KTreeView::itemAt(const KPath& path)
974 if (path.isEmpty())
975 return 0;
977 // need a copy of the path because recursiveFind will destroy it
978 KPath pathCopy;
979 pathCopy.setAutoDelete(false);
980 pathCopy = path;
982 return recursiveFind(pathCopy);
985 // computes the path of the item at the specified index
986 // if index is invalid, nothing is done.
987 void KTreeView::itemPath(int row, KPath& path)
989 KTreeViewItem* item = itemAt(row);
990 if (item != 0) {
991 itemPath(item, path);
995 // returns the row in the visible tree of the given item or
996 // -1 if not found
997 int KTreeView::itemRow(KTreeViewItem* item)
999 if (item->owner == this) {
1000 // search in list of visible items
1001 for (int i = numRows()-1; i >= 0; i--) {
1002 if (visibleItems[i] == item) {
1003 return i;
1007 // not found
1008 return -1;
1012 * move the subtree at the specified index up one branch level (make root
1013 * item a sibling of its current parent)
1015 void KTreeView::join(int index)
1017 KTreeViewItem *item = itemAt(index);
1018 if(item)
1019 join(item);
1023 * move the subtree at the specified index up one branch level (make root
1024 * item a sibling of it's current parent)
1026 void KTreeView::join(const KPath& path)
1028 KTreeViewItem *item = itemAt(path);
1029 if (item)
1030 join(item);
1033 /* move item at specified index one slot down in its parent's sub tree */
1034 void KTreeView::lowerItem(int index)
1036 KTreeViewItem *item = itemAt(index);
1037 if(item)
1038 lowerItem(item);
1041 /* move item at specified path one slot down in its parent's sub tree */
1042 void KTreeView::lowerItem(const KPath& path)
1044 KTreeViewItem* item = itemAt(path);
1045 if (item)
1046 lowerItem(item);
1049 /* move item at specified index one slot up in its parent's sub tree */
1050 void KTreeView::raiseItem(int index)
1052 KTreeViewItem* item = itemAt(index);
1053 if (item)
1054 raiseItem(item);
1057 /* move item at specified path one slot up in its parent's sub tree */
1058 void KTreeView::raiseItem(const KPath& path)
1060 KTreeViewItem* item = itemAt(path);
1061 if (item)
1062 raiseItem(item);
1065 // remove the item at the specified index and delete it
1066 void KTreeView::removeItem(int index)
1068 KTreeViewItem *item = itemAt(index);
1069 if(item) {
1070 takeItem(item);
1071 delete item;
1075 // remove the item at the end of the specified path and delete it
1076 void KTreeView::removeItem(const KPath& thePath)
1078 KTreeViewItem* item = itemAt(thePath);
1079 if (item) {
1080 takeItem(item);
1081 delete item;
1085 // indicates whether vertical scrollbar is present
1086 bool KTreeView::scrollBar() const
1088 return testTableFlags(Tbl_vScrollBar);
1091 void KTreeView::scrollVisible(KTreeViewItem* item, bool children)
1093 if (item == 0)
1094 return;
1095 int row = itemRow(item);
1096 if (row < 0)
1097 return; /* do nothing if invisible */
1099 if (children && item->isExpanded()) {
1100 // we are concerned about children
1101 if (!rowIsVisible(row)) {
1102 // just move to the top
1103 setTopCell(row);
1104 } else {
1105 // this is the complicated part
1106 // count the visible children (including grandchildren)
1107 int numVisible = 0;
1108 forEveryVisibleItem(countItem, &numVisible, item);
1109 // if the last child is visible, do nothing
1110 if (rowIsVisible(row + numVisible))
1111 return;
1113 * Basically, item will become the top cell; but if there are
1114 * more visible rows in the widget than we have children, then
1115 * we don't move that far.
1117 int remain = lastRowVisible()-topCell()-numVisible;
1118 if (remain <= 0) {
1119 setTopCell(row);
1120 } else {
1121 setTopCell(QMAX(0,row-remain));
1124 } else {
1125 // we are not concerned about children
1126 if (rowIsVisible(row))
1127 return;
1128 // just move the item to the top
1129 setTopCell(row);
1133 // enables/disables auto update of display
1134 void KTreeView::setAutoUpdate(bool enable)
1136 QTableView::setAutoUpdate(enable);
1139 // enables/disables horizontal scrollbar
1140 void KTreeView::setBottomScrollBar(bool enable)
1142 enable ? setTableFlags(Tbl_hScrollBar) :
1143 clearTableFlags(Tbl_hScrollBar);
1146 // sets the current item and hightlights it, emitting signals
1147 void KTreeView::setCurrentItem(int row)
1149 if (row == current)
1150 return;
1151 int numVisible = numRows();
1152 if (row > numVisible)
1153 return;
1154 int oldCurrent = current;
1155 current = row;
1156 if (oldCurrent < numVisible)
1157 updateCell(oldCurrent, 0);
1158 if (current > -1) {
1159 updateCell(current, 0, false);
1160 emit highlighted(current);
1164 // enables/disables drawing of expand button
1165 void KTreeView::setExpandButtonDrawing(bool enable)
1167 if (drawExpandButton == enable)
1168 return;
1169 drawExpandButton = enable;
1171 // the user parameter is cast to a bool in setItemExpandButtonDrawing
1172 forEveryItem(&KTreeView::setItemExpandButtonDrawing, enable ? &enable : 0);
1174 if (autoUpdate() && isVisible())
1175 repaint();
1178 // sets depth to which subtrees are automatically expanded, and
1179 // redraws tree if auto update enabled
1180 void KTreeView::setExpandLevel(int level)
1182 if (expansion == level)
1183 return;
1184 expansion = level;
1185 KTreeViewItem* item = getCurrentItem();
1186 forEveryItem(&KTreeView::setItemExpandLevel, 0);
1187 while (item != 0) {
1188 if (item->getParent()->isExpanded())
1189 break;
1190 item = item->getParent();
1192 if (item != 0)
1193 setCurrentItem(itemRow(item));
1194 if (autoUpdate() && isVisible())
1195 repaint();
1198 // sets the indent margin for all branches and repaints if auto update enabled
1199 void KTreeView::setIndentSpacing(int spacing)
1201 if (itemIndent == spacing)
1202 return;
1203 itemIndent = spacing;
1204 updateCellWidth();
1205 if (autoUpdate() && isVisible())
1206 repaint();
1209 void KTreeView::setMoveCurrentToSibling(bool m)
1211 moveCurrentToSibling = m;
1214 // enables/disables vertical scrollbar
1215 void KTreeView::setScrollBar(bool enable)
1217 enable ? setTableFlags(Tbl_vScrollBar) :
1218 clearTableFlags(Tbl_vScrollBar);
1221 // enables/disables display of item text (keys)
1222 void KTreeView::setShowItemText(bool enable)
1224 if (showText == enable)
1225 return;
1226 showText = enable;
1228 // the user parameter is cast to a bool in setItemShowText
1229 forEveryItem(&KTreeView::setItemShowText, enable ? &enable : 0);
1231 if (autoUpdate() && isVisible())
1232 repaint();
1235 // indicates whether vertical scrolling is by pixel or row
1236 void KTreeView::setSmoothScrolling(bool enable)
1238 enable ? setTableFlags(Tbl_smoothVScrolling) :
1239 clearTableFlags(Tbl_smoothVScrolling);
1242 // enables/disables tree branch drawing
1243 void KTreeView::setTreeDrawing(bool enable)
1245 if (drawTree == enable)
1246 return;
1247 drawTree = enable;
1249 // the user parameter is cast to a bool in setItemTreeDrawing
1250 forEveryItem(&KTreeView::setItemTreeDrawing, enable ? &enable : 0);
1252 if (autoUpdate() && isVisible())
1253 repaint();
1256 // indicates whether item text keys are displayed
1257 bool KTreeView::showItemText() const
1259 return showText;
1262 // indicates whether scrolling is by pixel or row
1263 bool KTreeView::smoothScrolling() const
1265 return testTableFlags(Tbl_smoothVScrolling);
1268 // indents the item at the given index, splitting the tree into
1269 // a new branch
1270 void KTreeView::split(int index)
1272 KTreeViewItem *item = itemAt(index);
1273 if(item)
1274 split(item);
1277 // indents the item at the given path, splitting the tree into
1278 // a new branch
1279 void KTreeView::split(const KPath& path)
1281 KTreeViewItem* item = itemAt(path);
1282 if (item)
1283 split(item);
1286 // removes item at specified index from tree but does not delete it
1287 // returns pointer to the item or 0 if not succesful
1288 KTreeViewItem *KTreeView::takeItem(int index)
1290 KTreeViewItem *item = itemAt(index);
1291 if(item)
1292 takeItem(item);
1293 return item;
1296 // removes item at specified path from tree but does not delete it
1297 // returns pointer to the item or 0 if not successful
1298 KTreeViewItem* KTreeView::takeItem(const KPath& path)
1300 KTreeViewItem* item = itemAt(path);
1301 if (item)
1302 takeItem(item);
1303 return item;
1306 // indicates whether tree branches are drawn
1307 bool KTreeView::treeDrawing() const
1309 return drawTree;
1313 // appends a child to the specified parent item (note: a child, not a sibling, is added!)
1314 void KTreeView::appendChildItem(KTreeViewItem* theParent,
1315 KTreeViewItem* newItem)
1317 theParent->appendChild(newItem);
1319 // set item state
1320 newItem->setDrawExpandButton(drawExpandButton);
1321 newItem->setDrawTree(drawTree);
1322 newItem->setDrawText(showText);
1323 if (level(newItem) < expansion) {
1324 newItem->setExpanded(true);
1327 // fix up branch levels of any children that the new item may already have
1328 if(newItem->hasChild()) {
1329 fixChildren(newItem);
1332 // if necessary, adjust cell width, number of rows and repaint
1333 if (newItem->isVisible() || theParent->childCount() == 1) {
1334 bool autoU = autoUpdate();
1335 setAutoUpdate(false);
1336 updateVisibleItems();
1337 setAutoUpdate(autoU);
1338 if (autoU && isVisible())
1339 repaint();
1343 // returns the height of the cell(row) at the specified row (index)
1344 int KTreeView::cellHeight(int row)
1346 return itemAt(row)->height(fontMetrics());
1349 // returns the width of the cells. Note: this is mostly for derived classes
1350 // which have more than 1 column
1351 int KTreeView::cellWidth(int /*col*/)
1353 return maxItemWidth;
1356 // changes the given item with the new text and/or pixmap
1357 void KTreeView::changeItem(KTreeViewItem* toChange, int itemRow,
1358 const char* newText, const QPixmap* newPixmap)
1360 int indent = indentation(toChange);
1361 int oldWidth = toChange->width(indent);
1362 if(newText)
1363 toChange->setText(newText);
1364 if (newPixmap)
1365 toChange->setPixmap(*newPixmap);
1366 if(oldWidth != toChange->width(indent))
1367 updateCellWidth();
1368 if(itemRow == -1)
1369 return;
1370 if(autoUpdate() && rowIsVisible(itemRow))
1371 updateCell(itemRow, 0);
1374 // collapses the subtree at the specified item
1375 void KTreeView::collapseSubTree(KTreeViewItem* subRoot, bool emitSignal)
1377 assert(subRoot->owner == this);
1378 if (!subRoot->isExpanded())
1379 return;
1381 // must move the current item if it is visible
1382 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1384 subRoot->setExpanded(false);
1385 if (subRoot->isVisible()) {
1386 bool autoU = autoUpdate();
1387 setAutoUpdate(false);
1388 updateVisibleItems();
1389 // re-seat current item
1390 if (cur != 0) {
1391 setCurrentItem(itemRow(cur));
1393 if (emitSignal) {
1394 emit collapsed(itemRow(subRoot));
1396 setAutoUpdate(autoU);
1397 if (autoU && isVisible())
1398 repaint();
1402 // used by count() with forEach() function to count total number
1403 // of items in the tree
1404 bool KTreeView::countItem(KTreeViewItem*, void* total)
1406 int* t = static_cast<int*>(total);
1407 (*t)++;
1408 return false;
1411 // expands the subtree at the given item
1412 void KTreeView::expandSubTree(KTreeViewItem* subRoot, bool emitSignal)
1414 assert(subRoot->owner == this);
1415 if (subRoot->isExpanded())
1416 return;
1418 // must move the current item if it is visible
1419 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
1421 bool allow = true;
1423 if (subRoot->delayedExpanding) {
1424 emit expanding(subRoot, allow);
1426 if (!allow)
1427 return;
1429 subRoot->setExpanded(true);
1431 if (subRoot->isVisible()) {
1432 bool autoU = autoUpdate();
1433 setAutoUpdate(false);
1434 updateVisibleItems();
1435 // re-seat current item
1436 if (cur != 0) {
1437 setCurrentItem(itemRow(cur));
1439 if (emitSignal) {
1440 emit expanded(itemRow(subRoot));
1442 setAutoUpdate(autoU);
1443 if (autoU && isVisible())
1444 repaint();
1448 // fix up branch levels out of whack from split/join operations on the tree
1449 void KTreeView::fixChildren(KTreeViewItem *parentItem)
1451 KTreeViewItem* childItem = 0;
1452 KTreeViewItem* siblingItem = 0;
1453 // int childBranch = parentItem->getBranch() + 1;
1454 if(parentItem->hasChild()) {
1455 childItem = parentItem->getChild();
1456 // childItem->setBranch(childBranch);
1457 childItem->owner = parentItem->owner;
1458 fixChildren(childItem);
1460 while(childItem && childItem->hasSibling()) {
1461 siblingItem = childItem->getSibling();
1462 // siblingItem->setBranch(childBranch);
1463 siblingItem->owner = parentItem->owner;
1464 fixChildren(siblingItem);
1465 childItem = siblingItem;
1470 * Handle QFocusEvent processing by setting current item to top row if
1471 * there is no current item, and updates cell to add or delete the focus
1472 * rectangle on the highlight bar. The base class is not called because it
1473 * does a repaint() which causes flicker.
1475 void KTreeView::focusInEvent(QFocusEvent *)
1477 if (current < 0 && numRows() > 0)
1478 setCurrentItem(topCell());
1479 updateCell(current, 0);
1482 void KTreeView::focusOutEvent(QFocusEvent *)
1484 updateCell(current, 0);
1487 // called by updateCellWidth() for each item in the visible list
1488 bool KTreeView::getMaxItemWidth(KTreeViewItem* item, void* user)
1490 assert(item->owner != 0);
1491 int indent = item->owner->indentation(item);
1492 int* maxW = static_cast<int*>(user);
1493 int w = item->width(indent);
1494 if (w > *maxW)
1495 *maxW = w;
1496 return false;
1499 int KTreeView::indentation(KTreeViewItem* item) const
1501 return level(item) * itemIndent + itemIndent + 3;
1504 // inserts the new item before or after the reference item, depending
1505 // on the value of prefix
1506 bool KTreeView::insertItem(KTreeViewItem* referenceItem,
1507 KTreeViewItem* newItem,
1508 bool prefix)
1510 assert(newItem != 0);
1511 assert(referenceItem == 0 || referenceItem->owner == this);
1513 /* set the new item's state */
1514 newItem->setDrawExpandButton(drawExpandButton);
1515 newItem->setDrawTree(drawTree);
1516 newItem->setDrawText(showText);
1517 KTreeViewItem* parentItem;
1518 if (referenceItem) {
1519 parentItem = referenceItem->getParent();
1520 int insertIndex = parentItem->childIndex(referenceItem);
1521 if (!prefix)
1522 insertIndex++;
1523 parentItem->insertChild(insertIndex, newItem);
1525 else {
1526 // no reference item, append at end of visible tree
1527 // need to repaint
1528 parentItem = treeRoot;
1529 parentItem->appendChild(newItem);
1532 // set item expansion
1533 if (level(newItem) < expansion)
1534 newItem->setExpanded(true);
1536 // fix up branch levels of any children
1537 if (newItem->hasChild())
1538 fixChildren(newItem);
1540 // if repaint necessary, do it if visible and auto update
1541 // enabled
1542 if (newItem->isVisible() || parentItem->childCount() == 1) {
1543 bool autoU = autoUpdate();
1544 setAutoUpdate(FALSE);
1545 updateVisibleItems();
1546 setAutoUpdate(autoU);
1547 if (autoU && isVisible())
1548 repaint();
1550 return true;
1554 * returns pointer to item's path
1556 void KTreeView::itemPath(KTreeViewItem* item, KPath& path) const
1558 assert(item != 0);
1559 assert(item->owner == this);
1560 if (item != treeRoot) {
1561 itemPath(item->getParent(), path);
1562 path.push(new QString(item->getText()));
1567 * joins the item's branch into the tree, making the item a sibling of its
1568 * parent
1570 void KTreeView::join(KTreeViewItem *item)
1572 KTreeViewItem *itemParent = item->getParent();
1573 if(itemParent->hasParent()) {
1574 bool autoU = autoUpdate();
1575 setAutoUpdate(FALSE);
1576 takeItem(item);
1577 insertItem(itemParent, item, FALSE);
1578 setAutoUpdate(autoU);
1579 if(autoU && isVisible())
1580 repaint();
1584 // handles keyboard interface
1585 void KTreeView::keyPressEvent(QKeyEvent* e)
1587 if (numRows() == 0)
1588 return; /* nothing to do */
1590 /* if there's no current item, make the top item current */
1591 if (currentItem() < 0)
1592 setCurrentItem(topCell());
1593 assert(currentItem() >= 0); /* we need a current item */
1594 assert(itemAt(currentItem()) != 0); /* we really need a current item */
1596 // give currentItem a chance to handle the event
1597 if (itemAt(currentItem())->keyEvent(e))
1598 return; /* handled */
1600 int pageSize, delta;
1601 KTreeViewItem* item;
1602 int key = e->key();
1603 repeat:
1604 switch (key) {
1605 case Key_Up:
1606 // make previous item current, scroll up if necessary
1607 if (currentItem() > 0) {
1608 setCurrentItem(currentItem() - 1);
1609 scrollVisible(itemAt(currentItem()), false);
1611 break;
1612 case Key_Down:
1613 // make next item current, scroll down if necessary
1614 if (currentItem() < numRows() - 1) {
1615 setCurrentItem(currentItem() + 1);
1616 if (currentItem() > lastRowVisible()) {
1617 // scrollVisible is not feasible here because
1618 // it scrolls the item to the top
1619 setTopCell(topCell() + currentItem() - lastRowVisible());
1620 } else if (currentItem() < topCell()) {
1621 setTopCell(currentItem());
1624 break;
1625 case Key_Next:
1626 // move highlight one page down and scroll down
1627 delta = currentItem() - topCell();
1628 pageSize = lastRowVisible() - topCell();
1629 setTopCell(QMIN(topCell() + pageSize, numRows() - 1));
1630 setCurrentItem(QMIN(topCell() + delta, numRows() - 1));
1631 break;
1632 case Key_Prior:
1633 // move highlight one page up and scroll up
1634 delta = currentItem() - topCell();
1635 pageSize = lastRowVisible() - topCell();
1636 setTopCell(QMAX(topCell() - pageSize, 0));
1637 setCurrentItem(QMAX(topCell() + delta, 0));
1638 break;
1639 case Key_Plus:
1640 case Key_Right:
1641 // if current item has subtree and is collapsed, expand it
1642 item = itemAt(currentItem());
1643 if (item->isExpanded() && item->hasChild() && key == Key_Right) {
1644 // going right on an expanded item is like going down
1645 key = Key_Down;
1646 goto repeat;
1647 } else {
1648 expandSubTree(item, true);
1649 scrollVisible(item, true);
1651 break;
1652 case Key_Minus:
1653 case Key_Left:
1654 // if current item has subtree and is expanded, collapse it
1655 item = itemAt(currentItem());
1656 if ((!item->isExpanded() || !item->hasChild()) && key == Key_Left) {
1657 // going left on a collapsed item goes to its parent
1658 item = item->getParent();
1659 if (item == treeRoot)
1660 break; /* we're already at the top */
1661 assert(item->isVisible());
1662 setCurrentItem(itemRow(item));
1663 } else {
1664 collapseSubTree(item, true);
1666 scrollVisible(item, false);
1667 break;
1668 case Key_Return:
1669 case Key_Enter:
1670 // select the current item
1671 if (currentItem() >= 0)
1672 emit selected(currentItem());
1673 break;
1674 default:
1675 break;
1679 // handles keyboard interface
1680 void KTreeView::keyReleaseEvent(QKeyEvent* e)
1682 if (currentItem() >= 0 && itemAt(currentItem()) != 0)
1683 itemAt(currentItem())->keyEvent(e);
1686 int KTreeView::level(KTreeViewItem* item) const
1688 assert(item != 0);
1689 assert(item->owner == this);
1690 assert(item != treeRoot);
1691 int l = 0;
1692 item = item->parent->parent; /* since item != treeRoot, there is a parent */
1693 while (item != 0) {
1694 item = item->parent;
1695 l++;
1697 return l;
1700 /* move specified item down one slot in parent's subtree */
1701 void KTreeView::lowerItem(KTreeViewItem *item)
1703 KTreeViewItem *itemParent = item->getParent();
1704 uint itemChildIndex = itemParent->childIndex(item);
1705 if(itemChildIndex < itemParent->childCount() - 1) {
1706 bool autoU = autoUpdate();
1707 setAutoUpdate(FALSE);
1708 takeItem(item);
1709 insertItem(itemParent->childAt(itemChildIndex), item, FALSE);
1710 setAutoUpdate(autoU);
1711 if(autoU && isVisible())
1712 repaint();
1716 // handle mouse double click events by selecting the clicked item
1717 // and emitting the signal
1718 void KTreeView::mouseDoubleClickEvent(QMouseEvent* e)
1720 // find out which row has been clicked
1721 int itemClicked = findRow(e->y());
1723 if (itemClicked < 0)
1724 return; /* invalid row, do nothing */
1726 KTreeViewItem* item = itemAt(itemClicked);
1727 if (item == 0)
1728 return;
1730 // translate mouse coord to cell coord
1731 QPoint cellCoord = cellCoords(itemClicked, e->pos());
1733 // first ask item
1734 if (item->mouseEvent(e, cellCoord))
1735 return;
1737 // hit test item
1738 int indent = indentation(item);
1739 if (item->boundingRect(indent).contains(cellCoord))
1740 emit selected(itemClicked);
1743 // handle mouse movement events
1744 void KTreeView::mouseMoveEvent(QMouseEvent* e)
1746 // in rubberband_mode we actually scroll the window now
1747 if (rubberband_mode) {
1748 move_rubberband(e->pos());
1749 } else {
1750 // else forward to current item
1751 int current = currentItem();
1752 if (current >= 0 && itemAt(current))
1753 itemAt(current)->mouseEvent(e, cellCoords(current, e->pos()));
1758 // handle single mouse presses
1759 void KTreeView::mousePressEvent(QMouseEvent* e)
1761 /* first: cancel rubberbanding if it's on */
1762 if (rubberband_mode)
1764 // another button was pressed while rubberbanding, stop the move.
1765 // RB: if we allow other buttons while rubberbanding the tree can expand
1766 // while rubberbanding - we then need to recalculate and resize the
1767 // rubberband rect and show the new size
1768 end_rubberband();
1769 return;
1772 // find out which row has been clicked
1773 int itemClicked = findRow(e->y());
1775 // nothing to do if not on valid row
1776 if (itemClicked < 0)
1777 return;
1778 KTreeViewItem* item = itemAt(itemClicked);
1779 if (!item)
1780 return;
1782 // translate mouse coord to cell coord
1783 QPoint cellCoord = cellCoords(itemClicked, e->pos());
1785 // give the item a crack
1786 if (item->mouseEvent(e, cellCoord))
1787 return; /* event eaten by item */
1789 // check for rubberbanding
1790 if (e->button() == MidButton)
1792 // RB: the MMB is hardcoded to the "rubberband" scroll mode
1793 if (!rubberband_mode) {
1794 start_rubberband(e->pos());
1796 return;
1799 if (e->button() == RightButton) {
1800 emit rightPressed(itemClicked, e->pos());
1802 /* hit test expand button (doesn't set currentItem) */
1803 else if (item->expandButtonClicked(cellCoord)) {
1804 if (item->isExpanded()) {
1805 collapseSubTree(item, true);
1806 } else {
1807 expandSubTree(item, true);
1808 scrollVisible(item, true); /* make children visible */
1811 // hit test item
1812 else if (item->boundingRect(indentation(item)).contains(cellCoord)) {
1813 setCurrentItem(itemClicked);
1817 // handle mouse release events
1818 void KTreeView::mouseReleaseEvent(QMouseEvent *e)
1820 /* if it's the MMB end rubberbanding */
1821 if (rubberband_mode) {
1822 if (e->button() == MidButton)
1823 end_rubberband();
1824 return;
1826 // forward to current item
1827 int current = currentItem();
1828 if (current >= 0 && itemAt(current))
1829 itemAt(current)->mouseEvent(e, cellCoords(current, e->pos()));
1832 // rubberband move: draw the rubberband
1833 void KTreeView::draw_rubberband()
1836 * RB: I'm using a white pen because of the XorROP mode. I would prefer
1837 * to draw the rectangle in red but I don't now how to get a pen which
1838 * draws red in XorROP mode (this depends on the background). In fact
1839 * the color should be configurable.
1842 if (!rubberband_mode) return;
1843 QPainter paint(this);
1844 paint.setPen(white);
1845 paint.setRasterOp(XorROP);
1846 paint.drawRect(xOffset()*viewWidth()/totalWidth(),
1847 yOffset()*viewHeight()/totalHeight(),
1848 rubber_width+1, rubber_height+1);
1849 paint.end();
1852 // rubberband move: start move
1853 void KTreeView::start_rubberband(const QPoint& where)
1855 if (rubberband_mode) { // Oops!
1856 end_rubberband();
1858 /* RB: Don't now, if this check is necessary */
1859 if (!viewWidth() || !viewHeight()) return;
1860 if (!totalWidth() || !totalHeight()) return;
1862 // calculate the size of the rubberband rectangle
1863 rubber_width = viewWidth()*viewWidth()/totalWidth();
1864 if (rubber_width > viewWidth()) rubber_width = viewWidth();
1865 rubber_height = viewHeight()*viewHeight()/totalHeight();
1866 if (rubber_height > viewHeight()) rubber_height = viewHeight();
1868 // remember the cursor position and the actual offset
1869 rubber_startMouse = where;
1870 rubber_startX = xOffset();
1871 rubber_startY = yOffset();
1872 rubberband_mode=TRUE;
1873 draw_rubberband();
1876 // rubberband move: end move
1877 void KTreeView::end_rubberband()
1879 if (!rubberband_mode) return;
1880 draw_rubberband();
1881 rubberband_mode = FALSE;
1884 // rubberband move: hanlde mouse moves
1885 void KTreeView::move_rubberband(const QPoint& where)
1887 if (!rubberband_mode) return;
1889 // look how much the mouse moved and calc the new scroll position
1890 QPoint delta = where - rubber_startMouse;
1891 int nx = rubber_startX + delta.x() * totalWidth() / viewWidth();
1892 int ny = rubber_startY + delta.y() * totalHeight() / viewHeight();
1894 // check the new position (and make it valid)
1895 if (nx < 0) nx = 0;
1896 else if (nx > maxXOffset()) nx = maxXOffset();
1897 if (ny < 0) ny = 0;
1898 else if (ny > maxYOffset()) ny = maxYOffset();
1900 // redraw the rubberband at the new position
1901 draw_rubberband();
1902 setOffset(nx,ny);
1903 draw_rubberband();
1907 // paints the cell at the specified row and col
1908 // col is ignored for now since there is only one
1909 void KTreeView::paintCell(QPainter* p, int row, int)
1911 KTreeViewItem* item = itemAt(row);
1912 if (item == 0)
1913 return;
1915 QColorGroup cg = colorGroup();
1916 int indent = indentation(item);
1917 item->paint(p, indent, cg,
1918 current == row); /* highlighted */
1922 /* This is needed to make the kcontrol's color setup working (Marcin Dalecki) */
1923 void KTreeView::paletteChange(const QPalette &)
1925 setBackgroundColor(colorGroup().base());
1926 repaint(true);
1930 /* raise the specified item up one slot in parent's subtree */
1931 void KTreeView::raiseItem(KTreeViewItem *item)
1933 KTreeViewItem *itemParent = item->getParent();
1934 int itemChildIndex = itemParent->childIndex(item);
1935 if(itemChildIndex > 0) {
1936 bool autoU = autoUpdate();
1937 setAutoUpdate(FALSE);
1938 takeItem(item);
1939 insertItem(itemParent->childAt(--itemChildIndex), item, TRUE);
1940 setAutoUpdate(autoU);
1941 if(autoU && isVisible())
1942 repaint();
1946 // find the item at the path
1947 KTreeViewItem* KTreeView::recursiveFind(KPath& path)
1949 if (path.isEmpty())
1950 return treeRoot;
1952 // get the next key
1953 QString* searchString = path.pop();
1955 // find the parent item
1956 KTreeViewItem* parent = recursiveFind(path);
1957 if (parent == 0)
1958 return 0;
1961 * Iterate through the parent's children searching for searchString.
1963 KTreeViewItem* sibling = parent->getChild();
1964 while (sibling != 0) {
1965 if (*searchString == sibling->getText()) {
1966 break; /* found it! */
1968 sibling = sibling->getSibling();
1970 return sibling;
1973 void KTreeView::setItemExpanded(KTreeViewItem* item)
1975 if (level(item) < expansion) {
1976 expandSubTree(item, true);
1977 } else {
1978 collapseSubTree(item, true);
1982 // called by setExpandLevel for each item in tree
1983 bool KTreeView::setItemExpandLevel(KTreeViewItem* item, void*)
1985 assert(item->owner != 0);
1986 item->owner->setItemExpanded(item);
1987 return false;
1990 // called by setExpandButtonDrawing for every item in tree
1991 // the parameter drawButton is used as (and implicitly cast to) a bool
1992 bool KTreeView::setItemExpandButtonDrawing(KTreeViewItem* item,
1993 void* drawButton)
1995 item->setDrawExpandButton(drawButton);
1996 return false;
1999 // called by setShowItemText for every item in tree
2000 // the parameter newDrawText is used as (and implicitly cast to) a bool
2001 bool KTreeView::setItemShowText(KTreeViewItem* item,
2002 void* newDrawText)
2004 item->setDrawText(newDrawText);
2005 return false;
2008 // called by setTreeDrawing for every item in tree
2009 // the parameter drawTree is used as (and implicitly cast to) a bool
2010 bool KTreeView::setItemTreeDrawing(KTreeViewItem* item, void* drawTree)
2012 item->setDrawTree(drawTree);
2013 return false;
2016 // makes the item a child of the item above it, splitting
2017 // the tree into a new branch
2018 void KTreeView::split(KTreeViewItem *item)
2020 KTreeViewItem *itemParent = item->getParent();
2021 int itemChildIndex = itemParent->childIndex(item);
2022 if(itemChildIndex == 0)
2023 return;
2024 bool autoU = autoUpdate();
2025 setAutoUpdate(FALSE);
2026 takeItem(item);
2027 appendChildItem(itemParent->childAt(--itemChildIndex), item);
2028 setAutoUpdate(autoU);
2029 if(autoU && isVisible())
2030 repaint();
2033 // removes the item from the tree without deleting it
2034 void KTreeView::takeItem(KTreeViewItem* item)
2036 assert(item->owner == this);
2038 // TODO: go over this function again
2040 bool wasVisible = item->isVisible();
2042 * If we have a current item, make sure that it is not in the subtree
2043 * that we are about to remove. If the current item is in the part
2044 * below the taken-out subtree, we must move it up a number of rows if
2045 * the taken-out subtree is at least partially visible.
2047 KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0;
2048 KTreeViewItem* oldCurrent = cur;
2049 if (wasVisible && cur != 0) {
2050 KTreeViewItem* c = cur;
2051 while (c != 0 && c != item) {
2052 c = c->getParent();
2054 if (c != 0) {
2055 // move current item to parent
2056 cur = item->getParent();
2057 if (cur == treeRoot) {
2058 cur = 0;
2059 // move it to next or previous sibling
2060 if (moveCurrentToSibling) {
2061 if (item->getSibling() != 0) {
2062 cur = item->getSibling();
2063 } else if (treeRoot->getChild() != item) {
2064 cur = treeRoot->getChild();
2065 while (cur != 0 && cur->getSibling() != item)
2066 cur = cur->getSibling();
2072 KTreeViewItem* parentItem = item->getParent();
2073 parentItem->removeChild(item);
2074 item->sibling = 0;
2075 if (wasVisible || parentItem->childCount() == 0) {
2076 bool autoU = autoUpdate();
2077 setAutoUpdate(FALSE);
2078 updateVisibleItems();
2079 setAutoUpdate(autoU);
2080 if (autoU && isVisible())
2081 repaint();
2084 // re-seat the current item
2085 // row changes if cur is below the taken item
2086 current = cur != 0 ? itemRow(cur) : -1;
2087 // signal must be emitted only if item really changed
2088 if (cur != oldCurrent) {
2089 updateCell(current, 0, false);
2090 emit highlighted(current);
2094 // visits each item, calculates the maximum width
2095 // and updates QTableView
2096 void KTreeView::updateCellWidth()
2098 // make cells at least 1 pixel wide to avoid singularities (division by zero)
2099 int maxW = 1;
2100 forEveryVisibleItem(&KTreeView::getMaxItemWidth, &maxW);
2101 maxItemWidth = maxW;
2102 updateTableSize();
2104 // correct offsets
2105 int xoff = xOffset();
2106 int yoff = yOffset();
2107 if (xoff > maxXOffset()) xoff = maxXOffset();
2108 if (yoff > maxYOffset()) yoff = maxYOffset();
2110 setOffset(xoff, yoff);
2113 void KTreeView::updateVisibleItems()
2115 int index = 0;
2116 int count = 0;
2117 updateVisibleItemRec(treeRoot, index, count);
2118 assert(index == count);
2119 setNumRows(count);
2120 updateCellWidth();
2123 void KTreeView::updateVisibleItemRec(KTreeViewItem* item, int& index, int& count)
2125 if (!item->isExpanded()) {
2126 // no visible items if not expanded
2127 return;
2131 * Record the children of item in the list of visible items.
2133 * Don't register item itself, it's already in the list. Also only
2134 * allocate new space for children.
2136 count += item->childCount();
2137 if (count > itemCapacity) {
2138 // must reallocate
2139 int newCapacity = itemCapacity;
2140 do {
2141 newCapacity += newCapacity;
2142 } while (newCapacity < count);
2143 KTreeViewItem** newItems = new KTreeViewItem*[newCapacity];
2144 // clear the unneeded space
2145 for (int i = index; i < newCapacity; i++) {
2146 newItems[i] = 0;
2148 // move already accumulated items over
2149 for (int i = index-1; i >= 0; i--) {
2150 newItems[i] = visibleItems[i];
2152 delete[] visibleItems;
2153 visibleItems = newItems;
2154 itemCapacity = newCapacity;
2156 // insert children
2157 for (KTreeViewItem* i = item->getChild(); i != 0; i = i->getSibling()) {
2158 visibleItems[index++] = i;
2159 updateVisibleItemRec(i, index, count);