You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeadmin/kdat/ktreeview.cpp

2075 lines
52 KiB

/*
* $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 <tqapplication.h> /* used for TQApplication::closingDown() */
#include <tqkeycode.h> /* used for keyboard interface */
#include <tqpainter.h> /* used to paint items */
#include <tqscrollbar.h>
#include <tqstyle.h>
#include <assert.h>
/*
* -------------------------------------------------------------------
*
* KTreeViewItem
*
* -------------------------------------------------------------------
*/
// constructor
KTreeViewItem::KTreeViewItem(const TQString& 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 TQString& theText,
const TQPixmap& 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
TQRect KTreeViewItem::boundingRect(int indent) const
{
const TQFontMetrics& fm = owner->fontMetrics();
int rectX = indent;
int rectY = 1;
int rectW = width(indent, fm) - rectX;
int rectH = height(fm) - 2;
return TQRect(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 TQPoint& coord) const
{
return expandButton.contains(coord);
}
bool KTreeViewItem::mousePressEvent( const TQPoint& )
{
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 TQPixmap& 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 TQString& 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 TQFontMetrics& 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(TQPainter* p, int indent, const TQColorGroup& 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(TQPainter* 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(TQt::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(TQt::NoBrush);
}
// paint the highlight
void KTreeViewItem::paintHighlight(TQPainter* p, int indent, const TQColorGroup& colorGroup,
bool hasFocus, TQt::GUIStyle style) const
{
TQColor fc;
if (style == TQt::WindowsStyle)
fc = TQt::darkBlue; /* hardcoded in TQt */
else
fc = colorGroup.text();
TQRect 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 == TQt::WindowsStyle) { /* draw Windows style highlight */
textRect.setCoords(l - 1, t - 1, r + 1, b + 1);
p->setPen(TQPen(TQt::yellow, 0, TQt::DotLine));
p->setBackgroundColor(fc);
p->setBackgroundMode(TQt::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(TQPainter* p, int indent, int cellHeight,
const TQColorGroup& 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(),
(TQt::GUIStyle)owner->style().styleHint(TQStyle::SH_GUIStyle)); // TQt3 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(TQPainter* 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 TQPixmap& pm)
{
pixmap = pm;
if ( owner ) {
owner->updateVisibleItems();
owner->update();
}
}
// sets the item text to the given string
void KTreeViewItem::setText(const TQString& 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 TQFontMetrics::boundingRect() to work right so I made my own
*/
TQRect KTreeViewItem::textBoundingRect(int indent) const
{
const TQFontMetrics& 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 TQRect(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 TQFontMetrics& fm) const
{
int maxWidth = pixmap.width();
maxWidth += (4 + fm.width(text));
return maxWidth == 0 ? -1 : indent + maxWidth + 3;
}
/*
* -------------------------------------------------------------------
*
* KTreeView
*
* -------------------------------------------------------------------
*/
// constructor
KTreeView::KTreeView(TQWidget *parent,
const char *name,
WFlags f) :
TQGridView(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(TQFrame::WinPanel | TQFrame::Sunken);
setBackgroundColor(colorGroup().base());
//break;
/*default:
setFrameStyle(TQFrame::Panel | TQFrame::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 TQString & theText, const TQPixmap& 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 TQString & theText, const TQPixmap& 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 TQString & newText,
const TQPixmap *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 TQString & newText,
const TQPixmap* 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);
TQPtrStack<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 || TQApplication::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 TQString & theText, const TQPixmap& 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 TQString & theText, const TQPixmap& 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 TQString & newText, const TQPixmap* 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 TQFocusEvent 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(TQFocusEvent *)
{
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();
TQPtrStack<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)
{
TQPtrStack<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 TQString(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(TQKeyEvent *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 = TQMAX(1, visibleHeight() / cellHeight());
setCurrentItem(TQMIN(currentItem() + delta, numRows() - 1));
ensureCellVisible(currentItem(), 0);
break;
case Key_Prior:
// move highlight one page up and scroll up
delta = TQMAX(1, visibleHeight() / cellHeight());
setCurrentItem(TQMAX(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(TQMouseEvent *e)
{
// find out which row has been clicked
TQPoint 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;
TQPoint 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(TQMouseEvent *e)
{
// in rubberband_mode we actually scroll the window now
if (rubberband_mode)
{
move_rubberband(e->pos());
}
}
// handle single mouse presses
void KTreeView::mousePressEvent(TQMouseEvent *e)
{
// first: check which button was pressed
if (e->button() == TQt::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() != TQt::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
TQPoint 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;
TQPoint 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() == TQt::RightButton ) {
emit popupMenu( itemClicked, mapToGlobal( TQPoint( e->pos().x(), e->pos().y() ) ) );
}
}
}
// handle mouse release events
void KTreeView::mouseReleaseEvent(TQMouseEvent *e)
{
/* if it's the MMB end rubberbanding */
if (rubberband_mode && e->button()==TQt::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;
TQPainter 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 TQPoint& 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 TQPoint& where)
{
#if 0
if (!rubberband_mode) return;
// look how much the mouse moved and calc the new scroll position
TQPoint 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(TQPainter* p, int row, int)
{
KTreeViewItem* item = itemAt(row);
if (item == 0)
return;
p->setClipRect(cellRect(), TQPainter::CoordPainter );
TQColorGroup 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
TQString* 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 TQGridView
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);
}
}