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.
2075 lines
52 KiB
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(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(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() == Qt::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() != Qt::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() == Qt::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()==Qt::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);
|
|
}
|
|
}
|