diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | 37333bf25ad9a4c538250f5af2f9f1d666362883 (patch) | |
tree | c45e8df5b9efbffe07eb3d9340df7811c7e16943 /kdat/ktreeview.cpp | |
download | tdeadmin-37333bf25ad9a4c538250f5af2f9f1d666362883.tar.gz tdeadmin-37333bf25ad9a4c538250f5af2f9f1d666362883.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdeadmin@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kdat/ktreeview.cpp')
-rw-r--r-- | kdat/ktreeview.cpp | 2074 |
1 files changed, 2074 insertions, 0 deletions
diff --git a/kdat/ktreeview.cpp b/kdat/ktreeview.cpp new file mode 100644 index 0000000..334a834 --- /dev/null +++ b/kdat/ktreeview.cpp @@ -0,0 +1,2074 @@ +/* + * $Id$ + * + * KTreeView implementation + * + * Copyright (C) 1997 Johannes Sixt + * + * based on KTreeList, which is + * Copyright (C) 1996 Keith Brown and KtSoft + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABLILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. You should have received a copy + * of the GNU General Public License along with this program; if not, write + * to the Free Software Foundation, Inc, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include <ktreeview.h> +#include "ktreeview.moc" +#include <qapplication.h> /* used for QApplication::closingDown() */ +#include <qkeycode.h> /* used for keyboard interface */ +#include <qpainter.h> /* used to paint items */ +#include <qscrollbar.h> +#include <qstyle.h> +#include <assert.h> + +/* + * ------------------------------------------------------------------- + * + * KTreeViewItem + * + * ------------------------------------------------------------------- + */ + +// constructor +KTreeViewItem::KTreeViewItem(const QString& theText) : + owner(0), + numChildren(0), + doExpandButton(true), + expanded(false), + delayedExpanding(false), + doTree(true), + doText(true), + child(0), + parent(0), + sibling(0), + deleteChildren(false) +{ + text = theText; +} + +// constructor that takes a pixmap +KTreeViewItem::KTreeViewItem(const QString& theText, + const QPixmap& thePixmap) : + owner(0), + numChildren(0), + doExpandButton(true), + expanded(false), + delayedExpanding(false), + doTree(true), + doText(true), + child(0), + parent(0), + sibling(0), + deleteChildren(false) +{ + text = theText; + pixmap = thePixmap; +} + +// destructor +KTreeViewItem::~KTreeViewItem() +{ + if (deleteChildren) { + // remove the children + KTreeViewItem* i = child; + while (i) { + KTreeViewItem* d = i; + i = i->getSibling(); + delete d; + } + } +} + +// appends a direct child +void KTreeViewItem::appendChild(KTreeViewItem* newChild) +{ + newChild->parent = this; + newChild->owner = owner; + if (!getChild()) { + child = newChild; + } + else { + KTreeViewItem* lastChild = getChild(); + while (lastChild->hasSibling()) { + lastChild = lastChild->getSibling(); + } + lastChild->sibling = newChild; + } + newChild->sibling = 0; + numChildren++; +} + +// returns the bounding rectangle of the item content (not including indent +// and branches) for hit testing +QRect KTreeViewItem::boundingRect(int indent) const +{ + const QFontMetrics& fm = owner->fontMetrics(); + int rectX = indent; + int rectY = 1; + int rectW = width(indent, fm) - rectX; + int rectH = height(fm) - 2; + return QRect(rectX, rectY, rectW, rectH); +} + +// returns the child item at the specified index +KTreeViewItem* KTreeViewItem::childAt(int index) const +{ + if (!hasChild()) + return 0; + KTreeViewItem* item = getChild(); + while (index > 0 && item != 0) { + item = item->getSibling(); + index--; + } + return item; +} + +// returns the number of children this item has +uint KTreeViewItem::childCount() const +{ + return numChildren; +} + +/* returns the index of the given child item in this item's branch */ +int KTreeViewItem::childIndex(KTreeViewItem* searched) const +{ + assert(searched != 0); + int index = 0; + KTreeViewItem* item = getChild(); + while (item != 0 && item != searched) { + item = item->getSibling(); + index++; + } + return item == 0 ? -1 : index; +} + +// indicates whether mouse press is in expand button +inline bool KTreeViewItem::expandButtonClicked(const QPoint& coord) const +{ + return expandButton.contains(coord); +} + +bool KTreeViewItem::mousePressEvent( const QPoint& ) +{ + return FALSE; +} + +// returns a pointer to first child item +KTreeViewItem* KTreeViewItem::getChild() const +{ + return child; +} + +// returns the parent of this item +KTreeViewItem* KTreeViewItem::getParent() const +{ + return parent; +} + +// returns reference to the item pixmap +const QPixmap& KTreeViewItem::getPixmap() const +{ + return pixmap; +} + +// returns the sibling below this item +KTreeViewItem* KTreeViewItem::getSibling() const +{ + return sibling; +} + +// returns a pointer to the item text +const QString& KTreeViewItem::getText() const +{ + return text; +} + +// indicates whether this item has any children +bool KTreeViewItem::hasChild() const +{ + return child != 0; +} + +// indicates whether this item has a parent +bool KTreeViewItem::hasParent() const +{ + return parent != 0; +} + +// indicates whether this item has a sibling below it +bool KTreeViewItem::hasSibling() const +{ + return sibling != 0; +} + +int KTreeViewItem::height() const +{ + assert(owner != 0); + return height(owner->fontMetrics()); +} + +// returns the maximum height of text and pixmap including margins +// or -1 if item is empty -- SHOULD NEVER BE -1! +int KTreeViewItem::height(const QFontMetrics& fm) const +{ + int maxHeight = pixmap.height(); + int textHeight = fm.ascent() + fm.leading(); + maxHeight = textHeight > maxHeight ? textHeight : maxHeight; + return maxHeight == 0 ? -1 : maxHeight + 8; +} + +// inserts child item at specified index in branch +void KTreeViewItem::insertChild(int index, KTreeViewItem* newChild) +{ + if (index < 0) { + appendChild(newChild); + return; + } + newChild->parent = this; + newChild->owner = owner; + if (index == 0) { + newChild->sibling = getChild(); + child = newChild; + } + else { + KTreeViewItem* prevItem = getChild(); + KTreeViewItem* nextItem = prevItem->getSibling(); + while (--index > 0 && nextItem) { + prevItem = nextItem; + nextItem = prevItem->getSibling(); + } + prevItem->sibling = newChild; + newChild->sibling = nextItem; + } + numChildren++; + + if ( owner ) { + owner->updateVisibleItems(); + owner->update(); + } +} + +// indicates whether this item is displayed expanded +// NOTE: a TRUE response does not necessarily indicate the item +// has any children +bool KTreeViewItem::isExpanded() const +{ + return expanded; +} + +// returns true if all ancestors are expanded +bool KTreeViewItem::isVisible() const +{ + for (KTreeViewItem* p = getParent(); p != 0; p = p->getParent()) { + if (!p->isExpanded()) + return false; + } + return true; +} + +// paint this item, including tree branches and expand button +void KTreeViewItem::paint(QPainter* p, int indent, const QColorGroup& cg, + bool highlighted) const +{ + assert(getParent() != 0); /* can't paint root item */ + + p->save(); + p->setPen(cg.text()); + p->setBackgroundColor(cg.base()); + + int cellHeight = height(p->fontMetrics()); + + if (doTree) { + paintTree(p, indent, cellHeight); + } + + /* + * If this item has at least one child and expand button drawing is + * enabled, set the rect for the expand button for later mouse press + * bounds checking, and draw the button. + */ + if (doExpandButton && (child || delayedExpanding)) { + paintExpandButton(p, indent, cellHeight); + } + + // now draw the item pixmap and text, if applicable + int pixmapX = indent; + int pixmapY = (cellHeight - pixmap.height()) / 2; + p->drawPixmap(pixmapX, pixmapY, pixmap); + + if (doText) { + paintText(p, indent, cellHeight, cg, highlighted); + } + p->restore(); +} + +void KTreeViewItem::paintExpandButton(QPainter* p, int indent, int cellHeight) const +{ + int parentLeaderX = indent - (22 / 2); + int cellCenterY = cellHeight / 2; + + expandButton.setRect(parentLeaderX - 4, cellCenterY - 4, 9, 9); + p->setBrush(Qt::white); + p->drawRect(expandButton); + if (expanded) { + p->drawLine(parentLeaderX - 2, cellCenterY, + parentLeaderX + 2, cellCenterY); + } else { + p->drawLine(parentLeaderX - 2, cellCenterY, + parentLeaderX + 2, cellCenterY); + p->drawLine(parentLeaderX, cellCenterY - 2, + parentLeaderX, cellCenterY + 2); + } + p->setBrush(Qt::NoBrush); +} + +// paint the highlight +void KTreeViewItem::paintHighlight(QPainter* p, int indent, const QColorGroup& colorGroup, + bool hasFocus, Qt::GUIStyle style) const +{ + QColor fc; + if (style == Qt::WindowsStyle) + fc = Qt::darkBlue; /* hardcoded in Qt */ + else + fc = colorGroup.text(); + QRect textRect = textBoundingRect(indent); + int t,l,b,r; + textRect.coords(&l, &t, &r, &b); + p->fillRect(textRect, fc); /* draw highlight background */ + if (hasFocus) { + if(style == Qt::WindowsStyle) { /* draw Windows style highlight */ + textRect.setCoords(l - 1, t - 1, r + 1, b + 1); + p->setPen(QPen(Qt::yellow, 0, Qt::DotLine)); + p->setBackgroundColor(fc); + p->setBackgroundMode(Qt::OpaqueMode); + p->drawRect(textRect); + textRect.setCoords(l - 2, t - 2, r + 2, b + 2); + p->setPen(fc); + p->drawRect(textRect); + } + else { /* draw Motif style highlight */ + textRect.setCoords(l - 2, t - 2, r + 2, b + 2); + p->drawRect(textRect); + } + } +} + +// draw the text, highlighted if requested +void KTreeViewItem::paintText(QPainter* p, int indent, int cellHeight, + const QColorGroup& cg, bool highlighted) const +{ + int textX = indent + pixmap.width() + 4; + int textY = cellHeight - ((cellHeight - p->fontMetrics().ascent() - + p->fontMetrics().leading()) / 2); + if (highlighted) { + paintHighlight(p, indent, cg, owner->hasFocus(), + (Qt::GUIStyle)owner->style().styleHint(QStyle::SH_GUIStyle)); // Qt3 doesn't make this easy ;) + p->setPen(cg.base()); + p->setBackgroundColor(cg.text()); + } + else { + p->setPen(cg.text()); + p->setBackgroundColor(cg.base()); + } + p->drawText(textX, textY, text); +} + +// paint the tree structure +void KTreeViewItem::paintTree(QPainter* p, int indent, int cellHeight) const +{ + int parentLeaderX = indent - (22 / 2); + int cellCenterY = cellHeight / 2; + int cellBottomY = cellHeight - 1; + int itemLeaderX = indent - 3; + + /* + * If this is not the first item in the tree draw the line up + * towards parent or sibling. + */ + if (parent->parent != 0 || parent->getChild() != this) + p->drawLine(parentLeaderX, 0, parentLeaderX, cellCenterY); + + // draw the line down towards sibling + if (sibling) + p->drawLine(parentLeaderX, cellCenterY, parentLeaderX, cellBottomY); + + /* + * If this item has children or siblings in the tree or is a child of + * an item other than the root item then draw the little line from the + * item out to the left. + */ + if (sibling || (doExpandButton && (child || delayedExpanding)) || + parent->parent != 0 || + /* + * The following handles the case of an item at the end of the + * topmost level which doesn't have children. + */ + parent->getChild() != this) + { + p->drawLine(parentLeaderX, cellCenterY, itemLeaderX, cellCenterY); + } + + /* + * If there are siblings of ancestors below, draw our portion of the + * branches that extend through this cell. + */ + KTreeViewItem* prevRoot = parent; + while (prevRoot->getParent() != 0) { /* while not root item */ + if (prevRoot->hasSibling()) { + int sibLeaderX = owner->indentation(prevRoot) - (22 / 2); + p->drawLine(sibLeaderX, 0, sibLeaderX, cellBottomY); + } + prevRoot = prevRoot->getParent(); + } +} + +// removes the given (direct) child from the branch +bool KTreeViewItem::removeChild(KTreeViewItem* theChild) +{ + // search item in list of children + KTreeViewItem* prevItem = 0; + KTreeViewItem* toRemove = getChild(); + while (toRemove && toRemove != theChild) { + prevItem = toRemove; + toRemove = toRemove->getSibling(); + } + + if (toRemove) { + // found it! + if (prevItem == 0) { + child = toRemove->getSibling(); + } else { + prevItem->sibling = toRemove->getSibling(); + } + numChildren--; + toRemove->owner = 0; + } + assert((numChildren == 0) == (child == 0)); + + if ( owner ) { + owner->updateVisibleItems(); + owner->update(); + } + + return toRemove != 0; +} + +// sets the delayed-expanding flag +void KTreeViewItem::setDelayedExpanding(bool flag) +{ + delayedExpanding = flag; +} + +// tells the item whether it shall delete child items +void KTreeViewItem::setDeleteChildren(bool flag) +{ + deleteChildren = flag; +} + +// sets the draw expand button flag of this item +void KTreeViewItem::setDrawExpandButton(bool doit) +{ + doExpandButton = doit; +} + +// sets the draw text flag of this item +void KTreeViewItem::setDrawText(bool doit) +{ + doText = doit; +} + +// sets the draw tree branch flag of this item +void KTreeViewItem::setDrawTree(bool doit) +{ + doTree = doit; +} + +// sets the expanded flag of this item +void KTreeViewItem::setExpanded(bool is) +{ + expanded = is; + if ( owner ) { + owner->updateVisibleItems(); + owner->update(); + } +} + +// sets the item pixmap to the given pixmap +void KTreeViewItem::setPixmap(const QPixmap& pm) +{ + pixmap = pm; + if ( owner ) { + owner->updateVisibleItems(); + owner->update(); + } +} + +// sets the item text to the given string +void KTreeViewItem::setText(const QString& t) +{ + text = t; + if ( owner ) { + owner->updateVisibleItems(); + owner->update(); + } +} + +// counts the child items and stores the result in numChildren +void KTreeViewItem::synchNumChildren() +{ + numChildren = 0; + KTreeViewItem* item = getChild(); + while (item != 0) { + numChildren++; + item = item->getSibling(); + } +} + +/* + * returns the bounding rect of the item text in cell coordinates couldn't + * get QFontMetrics::boundingRect() to work right so I made my own + */ +QRect KTreeViewItem::textBoundingRect(int indent) const +{ + const QFontMetrics& fm = owner->fontMetrics(); + int cellHeight = height(fm); + int rectX = indent + pixmap.width() + 3; + int rectY = (cellHeight - fm.ascent() - fm.leading()) / 2 + 2; + int rectW = fm.width(text) + 1; + int rectH = fm.ascent() + fm.leading(); + return QRect(rectX, rectY, rectW, rectH); +} + +// returns the total width of text and pixmap, including margins, spacing +// and indent, or -1 if empty -- SHOULD NEVER BE -1! +int KTreeViewItem::width(int indent) const +{ + return width(indent, owner->fontMetrics()); +} + +int KTreeViewItem::width(int indent, const QFontMetrics& fm) const +{ + int maxWidth = pixmap.width(); + maxWidth += (4 + fm.width(text)); + return maxWidth == 0 ? -1 : indent + maxWidth + 3; +} + + +/* + * ------------------------------------------------------------------- + * + * KTreeView + * + * ------------------------------------------------------------------- + */ + +// constructor +KTreeView::KTreeView(QWidget *parent, + const char *name, + WFlags f) : + QGridView(parent, name, f), + clearing(false), + current(-1), + drawExpandButton(true), + drawTree(true), + expansion(0), + goingDown(false), + itemIndent(22), + showText(true), + itemCapacity(500), + visibleItems(0), + rubberband_mode(false) +{ + setCellHeight(0); +// setCellWidth(0); + setNumRows(0); + setNumCols(1); + setVScrollBarMode(Auto); + setHScrollBarMode(Auto); + //switch(style().guiStyle()) { + //case WindowsStyle: + //case MotifStyle: + setFrameStyle(QFrame::WinPanel | QFrame::Sunken); + setBackgroundColor(colorGroup().base()); + //break; + /*default: + setFrameStyle(QFrame::Panel | QFrame::Plain); + setLineWidth(1); + }*/ + // setAcceptFocus(true); + treeRoot = new KTreeViewItem; + treeRoot->setExpanded(true); + treeRoot->owner = this; + + visibleItems = new KTreeViewItem*[itemCapacity]; + // clear those pointers + for (int j = itemCapacity-1; j >= 0; j--) { + visibleItems[j] = 0; + } +} + +// destructor +KTreeView::~KTreeView() +{ + goingDown = true; + clear(); + delete[] visibleItems; + delete treeRoot; +} + +// appends a child to the item at the given index with the given text +// and pixmap +void KTreeView::appendChildItem(const QString & theText, const QPixmap& thePixmap, + int index) +{ + KTreeViewItem* item = new KTreeViewItem(theText, thePixmap); + item->setDeleteChildren(true); + appendChildItem(item, index); +} + +// appends a child to the item at the end of the given path with +// the given text and pixmap +void KTreeView::appendChildItem(const QString & theText, const QPixmap& thePixmap, + const KPath& thePath) +{ + KTreeViewItem* item = new KTreeViewItem(theText, thePixmap); + item->setDeleteChildren(true); + appendChildItem(item, thePath); +} + +// appends the given item to the children of the item at the given index +void KTreeView::appendChildItem(KTreeViewItem* newItem, int index) +{ + /* find parent item and append new item to parent's sub tree */ + KTreeViewItem* parentItem = itemAt(index); + if (!parentItem) + return; + appendChildItem(parentItem, newItem); +} + +// appends the given item to the children of the item at the end of the +// given path +void KTreeView::appendChildItem(KTreeViewItem* newItem, const KPath& thePath) +{ + /* find parent item and append new item to parent's sub tree */ + KTreeViewItem* parentItem = itemAt(thePath); + if (!parentItem) + return; + appendChildItem(parentItem, newItem); +} + +// indicates whether horizontal scrollbar appears only when needed +bool KTreeView::autoBottomScrollBar() const +{ + return (hScrollBarMode() == Auto); +} + +// indicates whether vertical scrollbar appears only when needed +bool KTreeView::autoScrollBar() const +{ + return (vScrollBarMode() == Auto); +} + +// indicates whether display updates automatically on changes +bool KTreeView::autoUpdate() const +{ + return isUpdatesEnabled(); +} + +// indicates whether horizontal scrollbar is present +bool KTreeView::bottomScrollBar() const +{ + return !(horizontalScrollBar()->isHidden()); +} + +// find item at specified index and change pixmap and/or text +void KTreeView::changeItem(const QString & newText, + const QPixmap *newPixmap, + int index) +{ + KTreeViewItem *item = itemAt(index); + if(item) + changeItem(item, index, newText, newPixmap); +} + +// find item at end of specified path, and change pixmap and/or text +void KTreeView::changeItem(const QString & newText, + const QPixmap* newPixmap, + const KPath& thePath) +{ + KTreeViewItem* item = itemAt(thePath); + if (item) { + int index = itemRow(item); + changeItem(item, index, newText, newPixmap); + } +} + +// clear all items from list and erase display +void KTreeView::clear() +{ + setCurrentItem(-1); + + /* somewhat of a hack for takeItem so it doesn't update the current item... */ + clearing = TRUE; + + bool autoU = autoUpdate(); + setAutoUpdate(FALSE); + QPtrStack<KTreeViewItem> stack; + stack.push(treeRoot); + while(!stack.isEmpty()) { + KTreeViewItem *item = stack.pop(); + if(item->hasChild()) { + stack.push(item); + stack.push(item->getChild()); + } + else if(item->hasSibling()) { + stack.push(item); + stack.push(item->getSibling()); + } + else if(item->getParent() != 0) { + takeItem(item); + delete item; + } + } + clearing = FALSE; + if(goingDown || QApplication::closingDown()) + return; + if(autoU && isVisible()) + repaint(); + setAutoUpdate(autoU); +} + +// return a count of all the items in the tree, whether visible or not +uint KTreeView::count() +{ + int total = 0; + forEveryItem(&KTreeView::countItem, (void *)&total); + return total; +} + +// returns the index of the current (highlighted) item +int KTreeView::currentItem() const +{ + return current; +} + +// only collapses the item if it is expanded. If not expanded, or +// index invalid, does nothing +void KTreeView::collapseItem(int index) +{ + KTreeViewItem *item = itemAt(index); + if(item && item->isExpanded()) + expandOrCollapse(item); +} + +// only expands the item if it is collapsed. If not collapsed, or +// index invalid, does nothing +void KTreeView::expandItem(int index) +{ + KTreeViewItem *item = itemAt(index); + if(item && !item->isExpanded()) + expandOrCollapse(item); +} + +// returns the depth the tree is automatically expanded to when +// items are added +int KTreeView::expandLevel() const +{ + return expansion; +} + +// expands or collapses the subtree rooted at the given item, +// as approptiate +// if item has no children, does nothing +void KTreeView::expandOrCollapseItem(int index) +{ + KTreeViewItem *item = itemAt(index); + if(item) + expandOrCollapse(item); +} + +// visits every item in the tree, visible or not and applies +// the user supplied function with the item and user data passed as parameters +// if user supplied function returns true, traversal ends and function returns +bool KTreeView::forEveryItem(KForEvery func, void* user, KTreeViewItem* item) +{ + if (item == 0) { + item = treeRoot; + } + assert(item->owner == this); + item = item->getChild(); + + while (item != 0) { + // visit the siblings + if ((*func)(item, user)) { + return true; + } + // visit the children (recursively) + if (item->hasChild()) { + if (forEveryItem(func, user, item)) + return true; + } + item = item->getSibling(); + } + return false; +} + +// visits every visible item in the tree in order and applies the +// user supplied function with the item and user data passed as parameters +// if user supplied function returns TRUE, traversal ends and function +// returns +bool KTreeView::forEveryVisibleItem(KForEvery func, void *user, + KTreeViewItem* item) +{ + if (item == 0) { + item = treeRoot; + } else { + // children are invisible (hence, nothing to do) + // if item is invisible or collapsed + if (!item->isVisible() || !item->isExpanded()) { + return false; + } + } + assert(item->owner == this); + item = item->getChild(); + + while (item != 0) { + // visit the siblings + if ((*func)(item, user)) { + return true; + } + // visit the children (recursively) + if (item->hasChild() && item->isExpanded()) { + if (forEveryVisibleItem(func, user, item)) + return true; + } + item = item->getSibling(); + } + return false; +} + +// returns a pointer to the KTreeViewItem at the current index +// or 0 if no current item +KTreeViewItem *KTreeView::getCurrentItem() +{ + if(current == -1) return 0; + return itemAt(current); +} + +// returns the current indent spacing +int KTreeView::indentSpacing() +{ + return itemIndent; +} + +// inserts a new item with the specified text and pixmap before +// or after the item at the given index, depending on the value +// of prefix +// if index is negative, appends item to tree at root level +bool KTreeView::insertItem(const QString & theText, const QPixmap& thePixmap, + int row, bool prefix) +{ + KTreeViewItem* refItem = itemAt(row); + + KTreeViewItem* item = new KTreeViewItem(theText, thePixmap); + item->setDeleteChildren(true); + + bool success = insertItem(refItem, item, prefix); + if (!success) + delete item; + return success; +} + +// inserts a new item with the specified text and pixmap before +// or after the item at the end of the given path, depending on the value +// of prefix +bool KTreeView::insertItem(const QString & theText, const QPixmap& thePixmap, + const KPath& path, bool prefix) +{ + KTreeViewItem* refItem = itemAt(path); + + KTreeViewItem* item = new KTreeViewItem(theText, thePixmap); + item->setDeleteChildren(true); + + bool success = insertItem(refItem, item, prefix); + if (!success) + delete item; + return success; +} + +// inserts the given item or derived object into the tree before +// or after the item at the given index, depending on the value +// of prefix +// if index is negative, appends item to tree at root level +bool KTreeView::insertItem(KTreeViewItem* newItem, + int index, bool prefix) +{ + // find the item currently at the index, if there is one + KTreeViewItem* refItem = itemAt(index); + + // insert new item at the appropriate place + return insertItem(refItem, newItem, prefix); +} + +// inserts the given item or derived object into the tree before +// or after the item at the end of the given path, depending on the value +// of prefix +bool KTreeView::insertItem(KTreeViewItem* newItem, + const KPath& thePath, bool prefix) +{ + // find the item currently at the end of the path, if there is one + KTreeViewItem* refItem = itemAt(thePath); + + // insert new item at appropriate place + return insertItem(refItem, newItem, prefix); +} + +/* + * returns pointer to KTreeViewItem at the specifed row or 0 if row is out + * of limits. + */ +KTreeViewItem* KTreeView::itemAt(int row) +{ + if (row < 0 || row >= numRows()) { + return 0; + } + else { + // lookup the item in the list of visible items + assert(row < itemCapacity); + KTreeViewItem* i = visibleItems[row]; + assert(i != 0); + return i; + } +} + +// returns pointer to KTreeViewItem at the end of the +// path or 0 if not found +KTreeViewItem* KTreeView::itemAt(const KPath& path) +{ + if (path.isEmpty()) + return 0; + + // need a copy of the path because recursiveFind will destroy it + KPath pathCopy; + pathCopy.setAutoDelete(false); + pathCopy = path; + + return recursiveFind(pathCopy); +} + +// computes the path of the item at the specified index +// if index is invalid, nothing is done. +void KTreeView::itemPath(int row, KPath& path) +{ + KTreeViewItem* item = itemAt(row); + if (item != 0) { + itemPath(item, path); + } +} + +// returns the row in the visible tree of the given item or +// -1 if not found +int KTreeView::itemRow(KTreeViewItem* item) +{ + if (item->owner == this) { + // search in list of visible items + for (int i = numRows()-1; i >= 0; i--) { + if (visibleItems[i] == item) { + return i; + } + } + } + // not found + return -1; +} + +/* + * move the subtree at the specified index up one branch level (make root + * item a sibling of its current parent) + */ +void KTreeView::join(int index) +{ + KTreeViewItem *item = itemAt(index); + if(item) + join(item); +} + +/* + * move the subtree at the specified index up one branch level (make root + * item a sibling of it's current parent) + */ +void KTreeView::join(const KPath& path) +{ + KTreeViewItem *item = itemAt(path); + if (item) + join(item); +} + +/* move item at specified index one slot down in its parent's sub tree */ +void KTreeView::lowerItem(int index) +{ + KTreeViewItem *item = itemAt(index); + if(item) + lowerItem(item); +} + +/* move item at specified path one slot down in its parent's sub tree */ +void KTreeView::lowerItem(const KPath& path) +{ + KTreeViewItem* item = itemAt(path); + if (item) + lowerItem(item); +} + +/* move item at specified index one slot up in its parent's sub tree */ +void KTreeView::raiseItem(int index) +{ + KTreeViewItem* item = itemAt(index); + if (item) + raiseItem(item); +} + +/* move item at specified path one slot up in its parent's sub tree */ +void KTreeView::raiseItem(const KPath& path) +{ + KTreeViewItem* item = itemAt(path); + if (item) + raiseItem(item); +} + +// remove the item at the specified index and delete it +void KTreeView::removeItem(int index) +{ + KTreeViewItem *item = itemAt(index); + if(item) { + takeItem(item); + delete item; + } +} + +// remove the item at the end of the specified path and delete it +void KTreeView::removeItem(const KPath& thePath) +{ + KTreeViewItem* item = itemAt(thePath); + if (item) { + takeItem(item); + delete item; + } +} + +// indicates whether vertical scrollbar is present +bool KTreeView::scrollBar() const +{ + return !(verticalScrollBar()->isHidden()); +} + +// enables/disables auto update of display +void KTreeView::setAutoUpdate(bool enable) +{ + setUpdatesEnabled(enable); +} + +// enables/disables horizontal scrollbar +void KTreeView::setBottomScrollBar(bool enable) +{ + setHScrollBarMode(enable ? AlwaysOn : AlwaysOff); +} + +// sets the current item and hightlights it, emitting signals +void KTreeView::setCurrentItem(int row) +{ + if (row == current) + return; + int numVisible = numRows(); + if (row > numVisible) { + emit highlighted( current ); + return; + } + int oldCurrent = current; + current = row; + if (oldCurrent < numVisible) + updateCell(oldCurrent, 0); + if (current > -1) { + updateCell(current, 0); + } + + emit highlighted( current ); +} + +// enables/disables drawing of expand button +void KTreeView::setExpandButtonDrawing(bool enable) +{ + if(drawExpandButton == enable) + return; + drawExpandButton = enable; + forEveryItem(&KTreeView::setItemExpandButtonDrawing, 0); + if(autoUpdate() && isVisible()) + repaint(); +} + +// sets depth to which subtrees are automatically expanded, and +// redraws tree if auto update enabled +void KTreeView::setExpandLevel(int level) +{ + if(expansion == level) + return; + expansion = level; + KTreeViewItem *item = getCurrentItem(); + forEveryItem(&KTreeView::setItemExpanded, 0); + while(item) { + if(item->getParent()->isExpanded()) + break; + item = item->getParent(); + } + setCurrentItem(itemRow(item)); + if(autoUpdate() && isVisible()) + repaint(); +} + +// sets the indent margin for all branches and repaints if auto update enabled +void KTreeView::setIndentSpacing(int spacing) +{ + if (itemIndent == spacing) + return; + itemIndent = spacing; + updateCellWidth(); + if (autoUpdate() && isVisible()) + repaint(); +} + +// enables/disables vertical scrollbar +void KTreeView::setScrollBar(bool enable) +{ + setVScrollBarMode(enable? AlwaysOn : AlwaysOff ); +} + +// enables/disables display of item text (keys) +void KTreeView::setShowItemText(bool enable) +{ + if(showText == enable) + return; + showText = enable; + forEveryItem(&KTreeView::setItemShowText, 0); + if(autoUpdate() && isVisible()) + repaint(); +} + +// indicates whether vertical scrolling is by pixel or row +void KTreeView::setSmoothScrolling(bool enable) +{ + verticalScrollBar()->setLineStep(enable ? 1 : cellHeight()); +} + +// enables/disables tree branch drawing +void KTreeView::setTreeDrawing(bool enable) +{ + if(drawTree == enable) + return; + drawTree = enable; + forEveryItem(&KTreeView::setItemTreeDrawing, 0); + if(autoUpdate() && isVisible()) + repaint(); +} + +// indicates whether item text keys are displayed +bool KTreeView::showItemText() const +{ + return showText; +} + +// indicates whether scrolling is by pixel or row +bool KTreeView::smoothScrolling() const +{ + return (verticalScrollBar()->lineStep() == 1); +} + +// indents the item at the given index, splitting the tree into +// a new branch +void KTreeView::split(int index) +{ + KTreeViewItem *item = itemAt(index); + if(item) + split(item); +} + +// indents the item at the given path, splitting the tree into +// a new branch +void KTreeView::split(const KPath& path) +{ + KTreeViewItem* item = itemAt(path); + if (item) + split(item); +} + +// removes item at specified index from tree but does not delete it +// returns pointer to the item or 0 if not succesful +KTreeViewItem *KTreeView::takeItem(int index) +{ + KTreeViewItem *item = itemAt(index); + if(item) + takeItem(item); + return item; +} + +// removes item at specified path from tree but does not delete it +// returns pointer to the item or 0 if not successful +KTreeViewItem* KTreeView::takeItem(const KPath& path) +{ + KTreeViewItem* item = itemAt(path); + if (item) + takeItem(item); + return item; +} + +// indicates whether tree branches are drawn +bool KTreeView::treeDrawing() const +{ + return drawTree; +} + + +// appends a child to the specified parent item (note: a child, not a sibling, is added!) +void KTreeView::appendChildItem(KTreeViewItem* theParent, + KTreeViewItem* newItem) +{ + theParent->appendChild(newItem); + + // set item state + newItem->setDrawExpandButton(drawExpandButton); + newItem->setDrawTree(drawTree); + newItem->setDrawText(showText); + if (level(newItem) < expansion) { + newItem->setExpanded(true); + } + + // fix up branch levels of any children that the new item may already have + if(newItem->hasChild()) { + fixChildren(newItem); + } + + // if necessary, adjust cell width, number of rows and repaint + if (newItem->isVisible() || theParent->childCount() == 1) { + bool autoU = autoUpdate(); + setAutoUpdate(false); + updateVisibleItems(); + if(autoU && isVisible()) + repaint(); + setAutoUpdate(autoU); + } +} + +// changes the given item with the new text and/or pixmap +void KTreeView::changeItem(KTreeViewItem* toChange, int itemRow, + const QString & newText, const QPixmap* newPixmap) +{ + int indent = indentation(toChange); + int oldWidth = toChange->width(indent); + if(!newText.isNull()) + toChange->setText(newText); + if (newPixmap) + toChange->setPixmap(*newPixmap); + if(oldWidth != toChange->width(indent)) + updateCellWidth(); + if(itemRow == -1) + return; + if(autoUpdate()) + updateCell(itemRow, 0); +} + +// collapses the subtree at the specified item +void KTreeView::collapseSubTree(KTreeViewItem* subRoot) +{ + assert(subRoot->owner == this); + + // must move the current item if it is visible + KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0; + + subRoot->setExpanded(false); + if (subRoot->isVisible()) { + updateVisibleItems(); + // re-seat current item + if (cur != 0) { + setCurrentItem(itemRow(cur)); + } + } + // roland + repaint(); + setAutoUpdate(TRUE); + // roland +} + +// used by count() with forEach() function to count total number +// of items in the tree +bool KTreeView::countItem(KTreeViewItem*, void* total) +{ + int* t = static_cast<int*>(total); + (*t)++; + return false; +} + +// if item is expanded, collapses it or vice-versa +void KTreeView::expandOrCollapse(KTreeViewItem* parent) +{ + bool autoU = autoUpdate(); + setAutoUpdate(false); + int parentIndex = itemRow(parent); + if (parent->isExpanded()) { + collapseSubTree(parent); + emit collapsed(parentIndex); + } + else { + expandSubTree(parent); + emit expanded(parentIndex); + } + if (autoU && isVisible()) + repaint(); + setAutoUpdate(autoU); +} + +// expands the subtree at the given item +void KTreeView::expandSubTree(KTreeViewItem* subRoot) +{ + assert(subRoot->owner == this); + + // must move the current item if it is visible + KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0; + + bool allow = true; + + if (subRoot->delayedExpanding) { + emit expanding(subRoot, allow); + } + if (!allow) + return; + + subRoot->setExpanded(true); + + if (subRoot->isVisible()) { + updateVisibleItems(); + // re-seat current item + if (cur != 0) { + setCurrentItem(itemRow(cur)); + } + } + // roland + repaint(); + setAutoUpdate(TRUE); + // roland + +} + +// fix up branch levels out of whack from split/join operations on the tree +void KTreeView::fixChildren(KTreeViewItem *parentItem) +{ + KTreeViewItem* childItem = 0; + KTreeViewItem* siblingItem = 0; +// int childBranch = parentItem->getBranch() + 1; + if(parentItem->hasChild()) { + childItem = parentItem->getChild(); +// childItem->setBranch(childBranch); + childItem->owner = parentItem->owner; + fixChildren(childItem); + } + while(childItem && childItem->hasSibling()) { + siblingItem = childItem->getSibling(); +// siblingItem->setBranch(childBranch); + siblingItem->owner = parentItem->owner; + fixChildren(siblingItem); + childItem = siblingItem; + } +} + +// handles QFocusEvent processing by setting current item to top +// row if there is no current item, and updates cell to add or +// delete the focus rectangle on the highlight bar +void KTreeView::focusInEvent(QFocusEvent *) +{ + if(current < 0 && numRows() > 0) + setCurrentItem(rowAt(contentsY())); + updateCell(current, 0); +} + +// visits every item in the tree, visible or not and applies the user +// supplied member function with the item and user data passed as parameters +// if the user supplied member function returns TRUE, traversal +// ends and the function returns +void KTreeView::forEveryItem(KForEveryM func, + void *user) +{ + KTreeViewItem *item = treeRoot->getChild(); + QPtrStack<KTreeViewItem> stack; + while(item) { + stack.push(item); + while(!stack.isEmpty()) { + KTreeViewItem *poppedItem = stack.pop(); + if((this->*func)(poppedItem, user)) + return; + if(poppedItem->hasChild()) { + KTreeViewItem *childItem = poppedItem->getChild(); + while(childItem) { + stack.push(childItem); + childItem = childItem->getSibling(); + } + } + } + item = item->getSibling(); + } +} + +// visits every visible item in the tree in order and applies the user +// supplied member function with the item and user data passed as parameters +// if user supplied function returns TRUE, traversal ends and function +// returns +void KTreeView::forEveryVisibleItem(KForEveryM func, + void *user) +{ + QPtrStack<KTreeViewItem> stack; + KTreeViewItem *item = treeRoot->getChild(); + do { + while(item) { + if((this->*func)(item, user)) return; + if(item->hasChild() && item->isExpanded()) { + stack.push(item); + item = item->getChild(); + } + else + item = item->getSibling(); + } + if(stack.isEmpty()) + break; + item = stack.pop()->getSibling(); + } while(TRUE); +} + +// called by updateCellWidth() for each item in the visible list +bool KTreeView::getMaxItemWidth(KTreeViewItem *item, void *user) +{ + int indent = indentation(item); + int* maxW = static_cast<int*>(user); + int w = item->width(indent); + if (w > *maxW) + *maxW = w; + return false; +} + +int KTreeView::indentation(KTreeViewItem* item) const +{ + return level(item) * itemIndent + itemIndent + 3; +} + +// inserts the new item before or after the reference item, depending +// on the value of prefix +bool KTreeView::insertItem(KTreeViewItem* referenceItem, + KTreeViewItem* newItem, + bool prefix) +{ + assert(newItem != 0); + assert(referenceItem == 0 || referenceItem->owner == this); + + /* set the new item's state */ + newItem->setDrawExpandButton(drawExpandButton); + newItem->setDrawTree(drawTree); + newItem->setDrawText(showText); + if (cellHeight() == 0) + { + setCellHeight(newItem->height(fontMetrics())); + } + KTreeViewItem* parentItem; + if (referenceItem) { + parentItem = referenceItem->getParent(); + int insertIndex = parentItem->childIndex(referenceItem); + if (!prefix) + insertIndex++; + parentItem->insertChild(insertIndex, newItem); + } + else { + // no reference item, append at end of visible tree + // need to repaint + parentItem = treeRoot; + parentItem->appendChild(newItem); + } + + // set item expansion + if (level(newItem) < expansion) + newItem->setExpanded(true); + + // fix up branch levels of any children + if (newItem->hasChild()) + fixChildren(newItem); + + // if repaint necessary, do it if visible and auto update + // enabled + if (newItem->isVisible() || parentItem->childCount() == 1) { + bool autoU = autoUpdate(); + setAutoUpdate(FALSE); + updateVisibleItems(); + if(autoU && isVisible()) + repaint(); + setAutoUpdate(autoU); + } + return true; +} + +/* + * returns pointer to item's path + */ +void KTreeView::itemPath(KTreeViewItem* item, KPath& path) const +{ + assert(item != 0); + assert(item->owner == this); + if (item != treeRoot) { + itemPath(item->getParent(), path); + path.push(new QString(item->getText())); + } +} + +/* + * joins the item's branch into the tree, making the item a sibling of its + * parent + */ +void KTreeView::join(KTreeViewItem *item) +{ + KTreeViewItem *itemParent = item->getParent(); + if(itemParent->hasParent()) { + bool autoU = autoUpdate(); + setAutoUpdate(FALSE); + takeItem(item); + insertItem(itemParent, item, FALSE); + if(autoU && isVisible()) + repaint(); + setAutoUpdate(autoU); + } +} + +// handles keyboard interface to tree list +void KTreeView::keyPressEvent(QKeyEvent *e) +{ + if(numRows() == 0) + + // nothing to be done + + return; + if(currentItem() < 0) + + // if no current item, make the top item current + + setCurrentItem(rowAt(contentsY())); + int pageSize, delta; + switch(e->key()) { + case Key_Up: + + // make previous item current, scroll up if necessary + + if(currentItem() > 0) { + setCurrentItem(currentItem() - 1); + ensureCellVisible(currentItem(), 0); + } + break; + case Key_Down: + + // make next item current, scroll down if necessary + + if (currentItem() < numRows() - 1) { + setCurrentItem(currentItem() + 1); + ensureCellVisible(currentItem(), 0); + } + break; + case Key_Next: + + // move highlight one page down and scroll down + + delta = QMAX(1, visibleHeight() / cellHeight()); + setCurrentItem(QMIN(currentItem() + delta, numRows() - 1)); + ensureCellVisible(currentItem(), 0); + break; + case Key_Prior: + + // move highlight one page up and scroll up + + delta = QMAX(1, visibleHeight() / cellHeight()); + setCurrentItem(QMAX(currentItem() - delta, 0)); + ensureCellVisible(currentItem(), 0); + break; + case Key_Plus: + + // if current item has subtree and is collapsed, expand it + + if(currentItem() >= 0) + expandItem(currentItem()); + break; + case Key_Minus: + + // if current item has subtree and is expanded, collapse it + + if(currentItem() >= 0) + collapseItem(currentItem()); + break; + case Key_Return: + case Key_Enter: + + // select the current item + + if(currentItem() >= 0) + emit selected(currentItem()); + break; + default: + break; + } +} + +int KTreeView::level(KTreeViewItem* item) const +{ + assert(item != 0); + assert(item->owner == this); + assert(item != treeRoot); + int l = 0; + item = item->parent->parent; /* since item != treeRoot, there is a parent */ + while (item != 0) { + item = item->parent; + l++; + } + return l; +} + +/* move specified item down one slot in parent's subtree */ +void KTreeView::lowerItem(KTreeViewItem *item) +{ + KTreeViewItem *itemParent = item->getParent(); + uint itemChildIndex = itemParent->childIndex(item); + if(itemChildIndex < itemParent->childCount() - 1) { + bool autoU = autoUpdate(); + setAutoUpdate(FALSE); + takeItem(item); + insertItem(itemParent->childAt(itemChildIndex), item, FALSE); + if(autoU && isVisible()) + repaint(); + setAutoUpdate(autoU); + } +} + +// handle mouse double click events by selecting the clicked item +// and emitting the signal +void KTreeView::mouseDoubleClickEvent(QMouseEvent *e) +{ + // find out which row has been clicked + + QPoint mouseCoord = viewportToContents(e->pos()); + int itemClicked = rowAt(mouseCoord.y()); + + // if a valid row was not clicked, do nothing + + if(itemClicked == -1) + return; + + KTreeViewItem *item = itemAt(itemClicked); + if(!item) return; + + // translate mouse coord to cell coord + + int cellY = cellHeight()*itemClicked; + int cellX = 0; + QPoint cellCoord(mouseCoord.x() - cellX, mouseCoord.y() - cellY); + + // hit test item + int indent = indentation(item); + if(item->boundingRect(indent).contains(cellCoord)) + emit selected(itemClicked); +} + +// handle mouse movement events +void KTreeView::mouseMoveEvent(QMouseEvent *e) +{ + // in rubberband_mode we actually scroll the window now + if (rubberband_mode) + { + move_rubberband(e->pos()); + } +} + + +// handle single mouse presses +void KTreeView::mousePressEvent(QMouseEvent *e) +{ + // first: check which button was pressed + + if (e->button() == MidButton) + { + // RB: the MMB is hardcoded to the "rubberband" scroll mode + if (!rubberband_mode) { + start_rubberband(e->pos()); + } + return; + } + else if ( ( rubberband_mode ) && ( e->button() != RightButton ) ) + { + // another button was pressed while rubberbanding, stop the move. + // RB: if we allow other buttons while rubberbanding the tree can expand + // while rubberbanding - we then need to reclaculate and resize the + // rubberband rect and show the new size + end_rubberband(); + return; // should we process the button press? + } + + // find out which row has been clicked + QPoint mouseCoord = viewportToContents(e->pos()); + int itemClicked = rowAt(mouseCoord.y()); + + // nothing to do if not on valid row + if (itemClicked == -1) + return; + + KTreeViewItem* item = itemAt(itemClicked); + if (!item) + return; + + // translate mouse coord to cell coord + int cellX = 0; + int cellY = cellHeight()*itemClicked; + QPoint cellCoord(mouseCoord.x() - cellX, mouseCoord.y() - cellY); + + /* hit expand button (doesn't set currentItem) */ + if(item->expandButtonClicked(cellCoord)) { + expandOrCollapse(item); + } + // hit select button (to select/deselect the item for backup) + // 2002-01-20 LEW: I'm adding the emit() here so that the screen gets updated, + // as in KTreeView::mouseDoubleClickEvent(). KTreeViewItem::mousePressEvent() + // returns false, so I guess some other function is being called here. + else if ( item->mousePressEvent( cellCoord ) ) { + // Item processed the button press in localSelected(itemClicked) + emit selected(itemClicked); + } + // hit item (to show info on the file/dir label clicked) + else if (item->boundingRect(indentation(item)).contains(cellCoord)) { + setCurrentItem(itemClicked); // highlight item + if ( e->button() == RightButton ) { + emit popupMenu( itemClicked, mapToGlobal( QPoint( e->pos().x(), e->pos().y() ) ) ); + } + } +} + +// handle mouse release events +void KTreeView::mouseReleaseEvent(QMouseEvent *e) +{ + /* if it's the MMB end rubberbanding */ + if (rubberband_mode && e->button()==MidButton) + { + end_rubberband(); + } +} + +// rubberband move: draw the rubberband +void KTreeView::draw_rubberband() +{ +#if 0 + /* + * RB: I'm using a white pen because of the XorROP mode. I would prefer + * to draw the rectangle in red but I don't now how to get a pen which + * draws red in XorROP mode (this depends on the background). In fact + * the color should be configurable. + */ + + if (!rubberband_mode) return; + QPainter paint(this); + paint.setPen(white); + paint.setRasterOp(XorROP); + paint.drawRect(xOffset()*viewWidth()/totalWidth(), + yOffset()*viewHeight()/totalHeight(), + rubber_width+1, rubber_height+1); + paint.end(); +#endif +} + +// rubberband move: start move +void KTreeView::start_rubberband(const QPoint& where) +{ +#if 0 + if (rubberband_mode) { // Oops! + end_rubberband(); + } + /* RB: Don't now, if this check is necessary */ + if (!viewWidth() || !viewHeight()) return; + if (!totalWidth() || !totalHeight()) return; + + // calculate the size of the rubberband rectangle + rubber_width = viewWidth()*viewWidth()/totalWidth(); + if (rubber_width > viewWidth()) rubber_width = viewWidth(); + rubber_height = viewHeight()*viewHeight()/totalHeight(); + if (rubber_height > viewHeight()) rubber_height = viewHeight(); + + // remember the cursor position and the actual offset + rubber_startMouse = where; + rubber_startX = xOffset(); + rubber_startY = yOffset(); + rubberband_mode=TRUE; + draw_rubberband(); +#endif +} + +// rubberband move: end move +void KTreeView::end_rubberband() +{ +#if 0 + if (!rubberband_mode) return; + draw_rubberband(); + rubberband_mode = FALSE; +#endif +} + +// rubberband move: handle mouse moves +void KTreeView::move_rubberband(const QPoint& where) +{ +#if 0 + if (!rubberband_mode) return; + + // look how much the mouse moved and calc the new scroll position + QPoint delta = where - rubber_startMouse; + int nx = rubber_startX + delta.x() * totalWidth() / viewWidth(); + int ny = rubber_startY + delta.y() * totalHeight() / viewHeight(); + + // check the new position (and make it valid) + if (nx < 0) nx = 0; + else if (nx > maxXOffset()) nx = maxXOffset(); + if (ny < 0) ny = 0; + else if (ny > maxYOffset()) ny = maxYOffset(); + + // redraw the rubberband at the new position + draw_rubberband(); + setOffset(nx,ny); + draw_rubberband(); +#endif +} + + +// paints the cell at the specified row and col +// col is ignored for now since there is only one +void KTreeView::paintCell(QPainter* p, int row, int) +{ + KTreeViewItem* item = itemAt(row); + if (item == 0) + return; + + p->setClipRect(cellRect(), QPainter::CoordPainter ); + QColorGroup cg = colorGroup(); + int indent = indentation(item); + item->paint(p, indent, cg, + current == row); /* highlighted */ + p->setClipping(false); +} + +/* raise the specified item up one slot in parent's subtree */ +void KTreeView::raiseItem(KTreeViewItem *item) +{ + KTreeViewItem *itemParent = item->getParent(); + int itemChildIndex = itemParent->childIndex(item); + if(itemChildIndex > 0) { + bool autoU = autoUpdate(); + setAutoUpdate(FALSE); + takeItem(item); + insertItem(itemParent->childAt(--itemChildIndex), item, TRUE); + if(autoU && isVisible()) + repaint(); + setAutoUpdate(autoU); + } +} + +// find the item at the path +KTreeViewItem* KTreeView::recursiveFind(KPath& path) +{ + if (path.isEmpty()) + return treeRoot; + + // get the next key + QString* searchString = path.pop(); + + // find the parent item + KTreeViewItem* parent = recursiveFind(path); + if (parent == 0) + return 0; + + /* + * Iterate through the parent's children searching for searchString. + */ + KTreeViewItem* sibling = parent->getChild(); + while (sibling != 0) { + if (*searchString == sibling->getText()) { + break; /* found it! */ + } + sibling = sibling->getSibling(); + } + return sibling; +} + +// called by setExpandLevel for each item in tree +bool KTreeView::setItemExpanded(KTreeViewItem *item, void *) +{ + if (level(item) < expansion) { + if(item->hasChild() && !item->isExpanded()) + expandSubTree(item); + else + item->setExpanded(TRUE); + } + else { + if (item->hasChild() && item->isExpanded()) + collapseSubTree(item); + else + item->setExpanded(FALSE); + } + return FALSE; +} + +// called by setExpandButtonDrawing for every item in tree +bool KTreeView::setItemExpandButtonDrawing(KTreeViewItem *item, + void *) +{ + item->setDrawExpandButton(drawExpandButton); + return FALSE; +} + +// called by setShowItemText for every item in tree +bool KTreeView::setItemShowText(KTreeViewItem *item, + void *) +{ + item->setDrawText(showText); + return FALSE; +} + +// called by setTreeDrawing for every item in tree +bool KTreeView::setItemTreeDrawing(KTreeViewItem *item, void *) +{ + item->setDrawTree(drawTree); + return FALSE; +} + +// makes the item a child of the item above it, splitting +// the tree into a new branch +void KTreeView::split(KTreeViewItem *item) +{ + KTreeViewItem *itemParent = item->getParent(); + int itemChildIndex = itemParent->childIndex(item); + if(itemChildIndex == 0) + return; + bool autoU = autoUpdate(); + setAutoUpdate(FALSE); + takeItem(item); + appendChildItem(itemParent->childAt(--itemChildIndex), item); + if(autoU && isVisible()) + repaint(); + setAutoUpdate(autoU); +} + +// removes the item from the tree without deleting it +void KTreeView::takeItem(KTreeViewItem* item) +{ + assert(item->owner == this); + + // TODO: go over this function again + + bool wasVisible = item->isVisible(); + /* + * If we have a current item, make sure that it is not in the subtree + * that we are about to remove. If the current item is in the part + * below the taken-out subtree, we must move it up a number of rows if + * the taken-out subtree is at least partially visible. + */ + KTreeViewItem* cur = current >= 0 ? itemAt(current) : 0; + if (wasVisible && cur != 0) { + KTreeViewItem* c = cur; + while (c != 0 && c != item) { + c = c->getParent(); + } + if (c != 0) { + // move current item to parent + cur = item->getParent(); + if (cur == treeRoot) + cur = 0; + } + } + KTreeViewItem* parentItem = item->getParent(); + parentItem->removeChild(item); + item->sibling = 0; + if (wasVisible || parentItem->childCount() == 0) { + bool autoU = autoUpdate(); + setAutoUpdate(FALSE); + updateVisibleItems(); + + if (autoU && isVisible()) + repaint(); + setAutoUpdate(autoU); + } + + // re-seat the current item + setCurrentItem(cur != 0 ? itemRow(cur) : -1); +} + +// visits each item, calculates the maximum width +// and updates QGridView +void KTreeView::updateCellWidth() +{ + // make cells at least 1 pixel wide to avoid singularities (division by zero) + int maxW = 1; + forEveryVisibleItem(&KTreeView::getMaxItemWidth, &maxW); + maxItemWidth = maxW; + setCellWidth(maxW); + update(); +} + +void KTreeView::updateVisibleItems() +{ + int index = 0; + int count = 0; + updateVisibleItemRec(treeRoot, index, count); + assert(index == count); + setNumRows(count); + updateCellWidth(); +} + +void KTreeView::updateVisibleItemRec(KTreeViewItem* item, int& index, int& count) +{ + if (!item->isExpanded()) { + // no visible items if not expanded + return; + } + + /* + * Record the children of item in the list of visible items. + * + * Don't register item itself, it's already in the list. Also only + * allocate new space for children. + */ + count += item->childCount(); + if (count > itemCapacity) { + // must reallocate + int newCapacity = itemCapacity; + do { + newCapacity += newCapacity; + } while (newCapacity < count); + KTreeViewItem** newItems = new KTreeViewItem*[newCapacity]; + // clear the unneeded space + for (int i = index; i < newCapacity; i++) { + newItems[i] = 0; + } + // move already accumulated items over + for (int i = index-1; i >= 0; i--) { + newItems[i] = visibleItems[i]; + } + delete[] visibleItems; + visibleItems = newItems; + itemCapacity = newCapacity; + } + // insert children + for (KTreeViewItem* i = item->getChild(); i != 0; i = i->getSibling()) { + visibleItems[index++] = i; + updateVisibleItemRec(i, index, count); + } +} |