aboutsummaryrefslogtreecommitdiff
path: root/Src/Wasabi/api/wnd/wndclass/treewnd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Wasabi/api/wnd/wndclass/treewnd.cpp')
-rw-r--r--Src/Wasabi/api/wnd/wndclass/treewnd.cpp1645
1 files changed, 1645 insertions, 0 deletions
diff --git a/Src/Wasabi/api/wnd/wndclass/treewnd.cpp b/Src/Wasabi/api/wnd/wndclass/treewnd.cpp
new file mode 100644
index 00000000..99b03c85
--- /dev/null
+++ b/Src/Wasabi/api/wnd/wndclass/treewnd.cpp
@@ -0,0 +1,1645 @@
+#include "precomp.h"
+
+#include "treewnd.h"
+
+#include <tataki/canvas/ifc_canvas.h>
+#include <bfc/stack.h>
+#include <api/wnd/wndclass/scrollbar.h>
+#include <tataki/color/skinclr.h>
+#include <api/wnd/notifmsg.h>
+#include <api/wnd/accessible.h>
+#include <api/wnd/PaintCanvas.h>
+
+#define DEF_TEXT_SIZE 14
+#define CHILD_INDENT itemHeight
+#define X_SHIFT 2
+#define Y_SHIFT 2
+#define DRAG_THRESHOLD 4
+
+#define TIMER_EDIT_DELAY 1000
+#define TIMER_EDIT_ID 1249
+
+///////////////////////////////////////////////////////////////////////////////
+// TreeWnd
+///////////////////////////////////////////////////////////////////////////////
+
+static SkinColor textcolor(L"wasabi.tree.text");
+static SkinColor drophilitecolor(L"wasabi.tree.hiliteddrop");
+static SkinColor selectedcolor(L"wasabi.tree.selected");
+
+int CompareTreeItem::compareItem(TreeItem *p1, TreeItem *p2) {
+ return p1->getTree()->compareItem(p1, p2);
+}
+
+TreeWnd::TreeWnd() {
+ tabClosed = NULL;
+ tabOpen = NULL;
+ linkTopBottom = NULL;
+ linkTopRight = NULL;
+ linkTopRightBottom = NULL;
+ linkTabTopBottom = NULL;
+ linkTabTopRight = NULL;
+ linkTabTopRightBottom = NULL;
+ curSelected = NULL;
+ mousedown_item = NULL;
+ hitItem = NULL;
+ draggedItem = NULL;
+ tipitem = NULL;
+ edited = NULL;
+ editwnd = NULL;
+ metrics_ok = FALSE;
+ setSorted(TRUE);
+ setFontSize(DEF_TEXT_SIZE);
+ redraw = TRUE;
+ prevbdownitem = NULL;
+ autoedit=0;
+ autocollapse=1;
+
+ tabClosed = L"wasabi.tree.tab.closed";
+ tabOpen = L"wasabi.tree.tab.open";
+ linkTopBottom = L"wasabi.tree.link.top.bottom";
+ linkTopRight = L"wasabi.tree.link.top.right";
+ linkTopRightBottom = L"wasabi.tree.link.top.rightBottom";
+ linkTabTopBottom = L"wasabi.tree.link.tab.top.bottom";
+ linkTabTopRight = L"wasabi.tree.link.tab.top.right";
+ linkTabTopRightBottom = L"wasabi.tree.link.tab.top.rightBottom";
+}
+
+TreeWnd::~TreeWnd() {
+ // delete all root items
+ deleteAllItems();
+ drawList.removeAll();
+}
+
+int TreeWnd::onInit() {
+ TREEWND_PARENT::onInit();
+
+ setBgBitmap(L"wasabi.tree.background");
+ setLineHeight(itemHeight);
+
+ return 1;
+}
+
+void TreeWnd::setRedraw(bool r) {
+ int old = redraw;
+ redraw = r;
+ if (!old && redraw)
+ invalidate();
+}
+
+int TreeWnd::onPaint(Canvas *canvas) {
+
+ PaintCanvas paintcanvas;
+ PaintBltCanvas paintbcanvas;
+
+ if (canvas == NULL) {
+ if (needDoubleBuffer()) {
+ if (!paintbcanvas.beginPaintNC(this)) return 0;
+ canvas = &paintbcanvas;
+ } else {
+ if (!paintcanvas.beginPaint(this)) return 0;
+ canvas = &paintcanvas;
+ }
+ }
+ TREEWND_PARENT::onPaint(canvas);
+
+/* uncomment if you add columns or anything that should be not be drawn over by onPaint in which case you'll have to clip->subtract(your_region)
+ api_region *clip = new RegionI();
+ canvas->getClipRgn(clip); */
+
+ /*RECT r;
+ getNonClientRect(&r);
+
+ int y = -getScrollY()+Y_SHIFT+r.top;
+ int x = -getScrollX()+X_SHIFT;*/
+
+ Wasabi::FontInfo fontInfo;
+ fontInfo.color = textcolor;
+ fontInfo.opaque=false;
+ fontInfo.pointSize = getFontSize();
+
+ firstItemVisible = NULL;
+ lastItemVisible = NULL;
+
+ ensureMetricsValid();
+
+ //drawSubItems(canvas, x, &y, items, r.top, r.bottom, 0);
+ drawItems(canvas, &fontInfo);
+
+ canvas->selectClipRgn(NULL); // reset cliping region - NEEDED;
+
+// delete clip; uncomment if necessary
+
+ return 1;
+}
+
+void TreeWnd::drawItems(Canvas *canvas, const Wasabi::FontInfo *fontInfo)
+{
+ RECT r, c, ir;
+ RegionI *orig=NULL;
+ getClientRect(&r);
+ if (!canvas->getClipBox(&c)) {
+ getClientRect(&c);
+ orig = new RegionI(&c);
+ } else
+ orig = new RegionI(canvas);
+
+ int first = ((c.top-r.top) + getScrollY() - Y_SHIFT) / itemHeight;
+ int last = ((c.bottom-r.top) + getScrollY() - Y_SHIFT) / itemHeight + 1;
+ POINT pt;
+ TreeItem *item;
+ bool hastab;
+
+ for (int i=first;i<=last;i++)
+ {
+
+ if (i >= drawList.getNumItems()) break;
+
+ item = drawList[i];
+ if (!item) continue;
+ item->getCurRect(&ir);
+ pt.x = r.left + X_SHIFT+item->getIndent()*itemHeight - getScrollX();//ir.left;
+ pt.y = ir.top;
+
+ // if we need the +/- icon and any of the link lines, draw them
+ if (item->needTab()) {
+// pt.x += itemHeight;
+ RECT _r={pt.x-itemHeight, pt.y, pt.x, pt.y+itemHeight};
+ (item->isCollapsed() ? tabClosed : tabOpen).stretchToRectAlpha(canvas, &_r);
+ hastab=TRUE;
+ } else hastab = FALSE;
+
+ int indent = item->getIndent();
+
+ for (int j=0;j<indent;j++)
+ {
+ RECT _r={pt.x-itemHeight*(j+1), pt.y, pt.x-itemHeight*j, pt.y+itemHeight};
+ int l = getLinkLine(item, j);
+ if (l == (LINK_RIGHT | LINK_TOP)) {
+ ((hastab && j == 0) ? linkTabTopRight : linkTopRight).stretchToRectAlpha(canvas, &_r);
+ }
+ if (l == (LINK_RIGHT | LINK_TOP | LINK_BOTTOM)) {
+ ((hastab && j == 0) ? linkTabTopRightBottom : linkTopRightBottom).stretchToRectAlpha(canvas, &_r);
+ }
+ if (l == (LINK_BOTTOM | LINK_TOP)) {
+ ((hastab && j == 0) ? linkTabTopBottom : linkTopBottom).stretchToRectAlpha(canvas, &_r);
+ }
+ }
+
+ item->customDraw(canvas, pt, itemHeight, (pt.x+getScrollX())-r.left-X_SHIFT, r, fontInfo);
+ }
+
+ delete orig;
+}
+
+TreeItem *TreeWnd::hitTest(int x, int y) {
+ POINT pt={x,y};
+ return hitTest(pt);
+}
+
+TreeItem *TreeWnd::hitTest(POINT pt) {
+ RECT r, ir;
+ getClientRect(&r);
+
+ int first = (getScrollY() - Y_SHIFT) / itemHeight;
+ int last = ((r.bottom-r.top) + getScrollY() - Y_SHIFT) / itemHeight + 1;
+
+ for (int i=first;i<=last;i++) {
+
+ if (i >= drawList.getNumItems()) break;
+
+ TreeItem *item = drawList.enumItem(i);
+
+ if (item) {
+ item->getCurRect(&ir);
+ if (Wasabi::Std::pointInRect(ir, pt) && item->isHitTestable())
+ return item;
+ }
+ }
+
+ return NULL;
+}
+
+void TreeWnd::getMetrics(int *numItemsShown, int *mWidth) {
+ *mWidth=0;
+ *numItemsShown=0;
+ drawList.removeAll();
+ countSubItems(drawList, &items, X_SHIFT, numItemsShown, mWidth, 0);
+}
+
+void TreeWnd::countSubItems(PtrList<TreeItem> &drawlist, TreeItemList *_list, int indent, int *count, int *maxwidth, int z) {
+
+ TreeItemList &list = *_list;
+
+ for (int i=0;i<list.getNumItems();i++) {
+
+ TreeItem *nextitem = list[i];
+
+ int w = nextitem->getItemWidth(itemHeight, indent-X_SHIFT);
+ if (indent+w > *maxwidth) *maxwidth = w+indent;
+
+ int j = indent-(nextitem->needTab() ? itemHeight : 0);
+ int k;
+ k = indent + w;
+
+ nextitem->setCurRect(j, Y_SHIFT+(*count * itemHeight), k, Y_SHIFT+((*count+1) * itemHeight), z);
+ (*count)++;
+
+ drawlist.addItem(nextitem);
+
+ if (nextitem->isExpanded())
+ countSubItems(drawlist, &nextitem->subitems, indent+CHILD_INDENT, count, maxwidth, z+1);
+ }
+}
+
+void TreeWnd::timerCallback(int c) {
+ switch (c) {
+ case TIMER_EDIT_ID:
+ prevbdownitem = NULL;
+ killTimer(TIMER_EDIT_ID);
+ break;
+ default:
+ TREEWND_PARENT::timerCallback(c);
+ }
+}
+
+int TreeWnd::onLeftButtonDown(int x, int y) {
+
+ if (edited)
+ {
+ delete editwnd; editwnd = NULL;
+ endEditLabel(editbuffer);
+ }
+
+ POINT pt={x,y};
+ TreeItem *item = hitTest(pt);
+
+ if (item) {
+ mousedown_item = item;
+ mousedown_anchor.x = pt.x;
+ mousedown_anchor.y = pt.y;
+ mousedown_dragdone = FALSE;
+ // only do expand/collapse if was already selected
+ setCurItem(item, autocollapse?(curSelected == item):0, FALSE);
+ beginCapture();
+ }
+
+ return 1;
+}
+
+int TreeWnd::onLeftButtonUp(int x, int y) {
+ if (getCapture())
+ endCapture();
+ TREEWND_PARENT::onLeftButtonUp(x, y);
+ POINT pt={x,y};
+ TreeItem *item = hitTest(pt);
+ if (autoedit && item == mousedown_item && item == prevbdownitem)
+ setCurItem(item, FALSE, TRUE);
+ else
+ if (autoedit) {
+ prevbdownitem = getCurItem();
+ setTimer(TIMER_EDIT_ID, TIMER_EDIT_DELAY);
+ }
+
+ mousedown_item = NULL;
+ return 1;
+}
+
+int TreeWnd::onRightButtonUp(int x, int y){
+ TREEWND_PARENT::onRightButtonUp(x, y);
+ POINT pos={x,y};
+
+ TreeItem *ti = hitTest(pos);
+ if (ti != NULL) {
+ selectItem(ti);
+ if (onPreItemContextMenu(ti, x, y) == 0) {
+ int ret = ti->onContextMenu(x, y);
+ onPostItemContextMenu(ti, x, y, ret);
+ return ret;
+ }
+ return 1;
+ } else {
+ return onContextMenu(x, y);
+ }
+}
+
+int TreeWnd::onMouseMove(int x, int y) {
+
+ TREEWND_PARENT::onMouseMove(x, y);
+
+ POINT pt={x,y};
+
+ if (mousedown_item) {
+ if (!mousedown_dragdone && (ABS(pt.x - mousedown_anchor.x) > DRAG_THRESHOLD || ABS(pt.y - mousedown_anchor.y) > DRAG_THRESHOLD)) {
+ mousedown_dragdone = TRUE;
+ if (getCapture())
+ endCapture();
+ onBeginDrag(mousedown_item);
+ }
+ }
+ else
+ {
+ TreeItem *item = hitTest(pt);
+ if (item) {
+ if (tipitem != item) {
+ tipitem = item;
+ RECT r;
+ RECT c;
+ getClientRect(&c);
+ item->getCurRect(&r);
+ const wchar_t *tt = item->getTip();
+ if (tt != NULL && *tt != '\0')
+ setLiveTip(tt);
+ else if (r.right > c.right || r.bottom > c.bottom || r.top < c.top || r.left < c.left)
+ setLiveTip(item->getLabel());
+ else
+ setLiveTip(NULL);
+ }
+ } else {
+ setLiveTip(NULL);
+ }
+ }
+
+ return 1;
+}
+
+int TreeWnd::onLeftButtonDblClk(int x, int y) {
+ TreeItem *item = hitTest(x, y);
+ if (item == NULL) return 0;
+ return item->onLeftDoubleClick();
+}
+
+int TreeWnd::onRightButtonDblClk(int x, int y) {
+ TreeItem *item = hitTest(x, y);
+ if (item == NULL) return 0;
+ return item->onRightDoubleClick();
+}
+
+void TreeWnd::setLiveTip(const wchar_t *tip)
+{
+ if (!tip)
+ {
+ setTip(oldtip);
+ oldtip = L"";
+ return;
+ }
+ oldtip = TREEWND_PARENT::getTip();
+ setTip(tip);
+}
+
+int TreeWnd::onBeginDrag(TreeItem *treeitem)
+{
+ wchar_t title[WA_MAX_PATH]=L"";
+ // item calls addDragItem()
+ if (!treeitem->onBeginDrag(title)) return 0;
+ ASSERT(draggedItem == NULL);
+ draggedItem = treeitem;
+ if (*title != 0) setSuggestedDropTitle(title);
+ handleDrag();
+ return 1;
+}
+
+int TreeWnd::dragEnter(ifc_window *sourceWnd) {
+ // uh... we don't know yet, but we can accept drops in general
+ hitItem = NULL;
+ return 1;
+}
+
+int TreeWnd::dragOver(int x, int y, ifc_window *sourceWnd) {
+ POINT pos={x,y};
+ screenToClient(&pos);
+ TreeItem *prevItem;
+
+ prevItem = hitItem;
+ hitItem = hitTest(pos);
+
+ // no dropping on yourself! :)
+ if (hitItem == draggedItem) hitItem = NULL;
+
+ // unselect previous item
+ if (prevItem != hitItem && prevItem != NULL) {
+ unhiliteDropItem(prevItem);
+ repaint(); // commit invalidation of unhilited item so no trouble with scrolling
+ prevItem->dragLeave(sourceWnd);
+ }
+
+
+ RECT r;
+ getClientRect(&r);
+ if (pos.y < r.top + 16) {
+ if (getScrollY() >= 0) {
+ scrollToY(MAX(0, getScrollY()-itemHeight));
+ }
+ } else if (pos.y > r.bottom - 16) {
+ if (getScrollY() < getMaxScrollY()) {
+ scrollToY(MIN(getMaxScrollY(), getScrollY()+itemHeight));
+ }
+ }
+
+ if (hitItem != NULL) {
+ // hilight it
+ if (prevItem != hitItem) {
+ hiliteDropItem(hitItem);
+ repaint(); // commit invalidation of hilited so no trouble with scrolling
+ }
+ }
+
+ if (hitItem == NULL) return defaultDragOver(x, y, sourceWnd);
+
+ // ask the item if it can really accept such a drop
+ return hitItem->dragOver(sourceWnd);
+}
+
+int TreeWnd::dragLeave(ifc_window *sourceWnd) {
+ if (hitItem != NULL) {
+ unhiliteDropItem(hitItem);
+ hitItem->dragLeave(sourceWnd);
+ }
+ hitItem = NULL;
+ return 1;
+}
+
+int TreeWnd::dragDrop(ifc_window *sourceWnd, int x, int y) {
+ int res;
+ if (hitItem == NULL) return defaultDragDrop(sourceWnd, x, y);
+ // unhilite the dest
+ unhiliteDropItem(hitItem);
+ // the actual drop
+ res = hitItem->dragDrop(sourceWnd);
+ if (res) {
+ onItemRecvDrop(hitItem);
+ }
+ hitItem = NULL;
+ return res;
+}
+
+int TreeWnd::dragComplete(int success) {
+ int ret;
+ ASSERT(draggedItem != NULL);
+ ret = draggedItem->dragComplete(success);
+ draggedItem = NULL;
+ return ret;
+}
+
+void TreeItem::setTip(const wchar_t *tip)
+{
+ tooltip = tip;
+}
+
+const wchar_t *TreeItem::getTip()
+{
+ return tooltip;
+}
+
+void TreeWnd::hiliteDropItem(TreeItem *item) {
+ if (item)
+ item->setHilitedDrop(TRUE);
+}
+
+void TreeWnd::hiliteItem(TreeItem *item) {
+ if (item)
+ item->setHilited(TRUE);
+}
+
+void TreeWnd::selectItem(TreeItem *item) {
+ setCurItem(item, FALSE);
+}
+
+void TreeWnd::selectItemDeferred(TreeItem *item) {
+ postDeferredCallback(DC_SETITEM, (intptr_t)item);
+}
+
+void TreeWnd::delItemDeferred(TreeItem *item) {
+ postDeferredCallback(DC_DELITEM, (intptr_t)item);
+}
+
+void TreeWnd::unhiliteItem(TreeItem *item) {
+ if (item)
+ item->setHilited(FALSE);
+}
+
+void TreeWnd::unhiliteDropItem(TreeItem *item) {
+ if (item)
+ item->setHilitedDrop(FALSE);
+}
+
+void TreeWnd::setCurItem(TreeItem *item, bool expandCollapse, bool editifselected) {
+ if (curSelected && curSelected != item) {
+ onDeselectItem(curSelected);
+ curSelected->setSelected(FALSE);
+ }
+ if (item) {
+ curSelected = item;
+ onSelectItem(curSelected);
+ item->setSelected(TRUE, expandCollapse, editifselected);
+ setSlidersPosition();
+ }
+}
+
+// Returns the current tree width in pixels
+int TreeWnd::getContentsWidth() {
+ ensureMetricsValid();
+ return maxWidth;
+}
+
+// Returns the current tree height in pixels
+int TreeWnd::getContentsHeight() {
+ ensureMetricsValid();
+ return maxHeight;
+}
+
+void TreeWnd::ensureMetricsValid() {
+ if (metrics_ok) return;
+ int n;
+ getMetrics(&n, &maxWidth);
+ maxWidth += X_SHIFT*2;
+ maxHeight = n*itemHeight+Y_SHIFT*2;
+ metrics_ok = TRUE;
+ setSlidersPosition();
+}
+
+// Gets notification from sliders
+int TreeWnd::childNotify(ifc_window *child, int msg, intptr_t param1, intptr_t param2) {
+ switch (msg) {
+ case ChildNotify::EDITWND_ENTER_PRESSED:
+ if (child == editwnd && editwnd != NULL) {
+ endEditLabel(editbuffer);
+ return 1;
+ }
+ break;
+ case ChildNotify::EDITWND_CANCEL_PRESSED:
+ if (child == editwnd && editwnd != NULL) {
+ cancelEditLabel();
+ return 1;
+ }
+ break;
+ case ChildNotify::EDITWND_DATA_MODIFIED:
+ if (child == editwnd && editwnd != NULL) {
+ editUpdate();
+ return 1;
+ }
+ break;
+ }
+
+ return TREEWND_PARENT::childNotify(child, msg, param1, param2);
+}
+
+void TreeWnd::editUpdate() {
+ ASSERT(edited != NULL && editwnd != NULL);
+ if (!edited || !editwnd) return;
+ int w = editwnd->getTextLength()+16;
+ RECT i, r, e;
+ edited->getCurRect(&i);
+ getClientRect(&r);
+ editwnd->getClientRect(&e);
+ e.left += i.left;
+ e.right += i.left;
+ e.top += i.top;
+ e.bottom += i.top;
+ e.right = i.left+w;
+ e.right = MIN<int>(r.right - X_SHIFT, e.right);
+ editwnd->resize(&e);
+ editwnd->invalidate();
+}
+
+TreeItem *TreeWnd::addTreeItem(TreeItem *item, TreeItem *par, int _sorted, int haschildtab) {
+
+ ASSERT(item != NULL);
+ ASSERTPR(item->getTree() == NULL, "can't transplant TreeItems");
+ ASSERTPR(item->getLabel() != NULL, "tree items must have a label to be inserted");
+
+ item->setSorted(_sorted);
+ item->setChildTab(haschildtab ? TAB_AUTO : TAB_NO/*&& par != NULL*/);
+ item->setTree(this);
+ item->linkTo(par);
+
+ if (par == NULL)
+ items.addItem(item);
+
+ all_items.addItem(item);
+
+ metrics_ok = FALSE;
+
+ if (redraw)
+ invalidate();
+
+ item->onTreeAdd();
+
+ return item;
+}
+
+int TreeWnd::removeTreeItem(TreeItem *item) {
+ ASSERT(item != NULL);
+ ASSERT(item->getTree() == this);
+ if (item->isSelected()) item->setSelected(FALSE);
+ if (curSelected == item) curSelected = NULL;
+//CUT item->deleteSubitems();
+ TreeItem *par = item->getParent();
+ if (!par) { // is root item ?
+ ASSERT(items.haveItem(item));
+ items.removeItem(item);
+ } else {
+ if (!par->removeSubitem(item))
+ return 0;
+ }
+ all_items.removeItem(item);
+ metrics_ok = FALSE;
+ drawList.removeItem(item);
+ if (redraw)
+ invalidate();
+
+ item->setTree(NULL);
+ item->onTreeRemove();
+
+ if (par != NULL) par->onChildItemRemove(item);
+
+ return 1;
+}
+
+void TreeWnd::moveTreeItem(TreeItem *item, TreeItem *newparent) {
+ ASSERT(item != NULL);
+ ASSERTPR(item->getTree() == this, "can't move between trees (fucks up Freelist)");
+ removeTreeItem(item);
+ addTreeItem(item, newparent, item->subitems.getAutoSort(), item->childTab);
+}
+
+void TreeWnd::deleteAllItems() {
+ bool save_redraw = redraw;
+ setRedraw(FALSE);
+
+ TreeItem *item;
+ while ((item = enumRootItem(0)) != NULL)
+ delete item;
+
+ setRedraw(save_redraw);
+}
+
+void TreeWnd::setSorted(bool dosort) {
+ items.setAutoSort(dosort);
+}
+
+bool TreeWnd::getSorted() {
+ return items.getAutoSort();
+}
+
+void TreeWnd::sortTreeItems() {
+ items.sort(TRUE);
+ metrics_ok = FALSE;
+ if (redraw)
+ invalidate();
+}
+
+TreeItem *TreeWnd::getSibling(TreeItem *item) {
+ for (int i=0;i<items.getNumItems();i++) {
+ if (items[i] == item) {
+ if (i == items.getNumItems()-1) return NULL;
+ return items[i+1];
+ }
+ }
+ return NULL;
+}
+
+void TreeWnd::setAutoCollapse(bool doautocollase) {
+ autocollapse=doautocollase;
+}
+
+int TreeWnd::onContextMenu(int x, int y) {
+ POINT pos={x,y};
+ screenToClient(&pos);
+ TreeItem *ti = hitTest(pos);
+ if (ti != NULL) {
+ selectItem(ti);
+ return ti->onContextMenu(x, y);
+ }
+ return 0;
+}
+
+int TreeWnd::onDeferredCallback(intptr_t param1, intptr_t param2) {
+ switch (param1) {
+ case DC_SETITEM:
+ setCurItem((TreeItem *)param2, FALSE);
+ return 1;
+ case DC_DELITEM:
+ delete (TreeItem *)param2;
+ return 1;
+ case DC_EXPAND:
+ expandItem((TreeItem *)param2);
+ return 1;
+ case DC_COLLAPSE:
+ collapseItem((TreeItem *)param2);
+ return 1;
+ }
+ return 0;
+}
+
+int TreeWnd::getNumRootItems() {
+ return items.getNumItems();
+}
+
+TreeItem *TreeWnd::enumRootItem(int which) {
+ return items[which];
+}
+
+void TreeWnd::invalidateMetrics() {
+ metrics_ok = FALSE;
+}
+
+int TreeWnd::getLinkLine(TreeItem *item, int level) {
+
+ ASSERT(item != NULL);
+
+ int l = 0;
+ int r = 0;
+
+ if (item->parent == NULL)
+ return 0;
+
+ TreeItem *cur=item;
+
+ while (cur->getParent() && l < level) {
+ cur = cur->getParent();
+ l++;
+ }
+
+ if (cur->getSibling()) r |= LINK_BOTTOM | LINK_TOP;
+ if (level == 0) r |= LINK_RIGHT;
+ if (level == 0 && cur->getParent()) r |= LINK_TOP;
+
+ return r;
+
+}
+
+int TreeWnd::onMouseWheelDown(int clicked, int lines) {
+ if (!clicked)
+ scrollToY(MIN(getMaxScrollY(), getScrollY()+itemHeight));
+ else
+ scrollToX(MIN(getMaxScrollX(), getScrollX()+itemHeight));
+ return 1;
+}
+
+int TreeWnd::onMouseWheelUp(int clicked, int lines) {
+ if (!clicked)
+ scrollToY(MAX(0, getScrollY()-itemHeight));
+ else
+ scrollToX(MAX(0, getScrollX()-itemHeight));
+ return 1;
+}
+
+int TreeWnd::expandItem(TreeItem *item) {
+ ASSERT(item != NULL);
+
+ return item->expand();
+}
+
+void TreeWnd::expandItemDeferred(TreeItem *item) {
+ postDeferredCallback(DC_EXPAND, (intptr_t)item);
+}
+
+int TreeWnd::collapseItem(TreeItem *item) {
+ ASSERT(item != NULL);
+
+ return item->collapse();
+}
+
+void TreeWnd::collapseItemDeferred(TreeItem *item) {
+ postDeferredCallback(DC_COLLAPSE, (intptr_t)item);
+}
+
+TreeItem *TreeWnd::getCurItem() {
+ return curSelected;
+}
+
+int TreeWnd::getItemRect(TreeItem *item, RECT *r) {
+ ASSERT(item != NULL);
+
+ return item->getCurRect(r);
+}
+
+void TreeWnd::editItemLabel(TreeItem *item) {
+
+ if (edited) {
+ edited->setEdition(FALSE);
+ edited->invalidate();
+ }
+
+
+ ASSERT(item != NULL);
+ if (item == NULL) return;
+
+ if (item->onBeginLabelEdit()) return;
+ item->setEdition(TRUE);
+ edited = item;
+
+ editwnd = new EditWnd();
+ editwnd->setModal(TRUE);
+ editwnd->setAutoSelect(TRUE);
+ editwnd->setStartHidden(TRUE);
+ editwnd->init(getOsModuleHandle(), getOsWindowHandle());
+ editwnd->setParent(this);
+ RECT r;
+ edited->getCurRect(&r);
+ RECT cr;
+ getClientRect(&cr);
+ r.right = cr.right;
+ if (r.bottom - r.top < 24) r.bottom = r.top + 24;
+ editwnd->resize(&r);
+ wcsncpy(editbuffer, edited->getLabel(), 256);
+ editwnd->setBuffer(editbuffer, 255);
+ editUpdate();
+ editwnd->setVisible(TRUE);
+}
+
+void TreeWnd::endEditLabel(const wchar_t *newlabel)
+{
+ editwnd = NULL; // editwnd self destructs
+ if (edited->onEndLabelEdit(newlabel))
+ edited->setLabel(newlabel);
+ edited->setEdition(FALSE);
+ edited->invalidate();
+ onLabelChange(edited);
+ edited = NULL;
+ invalidateMetrics();
+ setSlidersPosition();
+}
+
+void TreeWnd::cancelEditLabel(int destroyit) {
+ ASSERT(edited != NULL);
+ if (!edited) return;
+
+ if (destroyit)
+ delete editwnd;
+
+ editwnd = NULL; // editwnd self destructs (update> except if destroyit for cancelling from treewnd)
+ edited->setEdition(FALSE);
+ edited->invalidate();
+ edited = NULL;
+}
+
+void TreeWnd::setAutoEdit(int ae) {
+ autoedit = ae;
+}
+
+int TreeWnd::getAutoEdit() {
+ return autoedit;
+}
+
+TreeItem *TreeWnd::getByLabel(TreeItem *item, const wchar_t *name)
+{
+ TreeItem *ti;
+ // handle root-level searching
+ if (item == NULL) {
+ int n = getNumRootItems();
+ for (int i = 0; i < n; i++) {
+ ti = enumRootItem(i);
+ if (!wcscmp(name, ti->getLabel())) return ti;
+ ti = getByLabel(ti, name);
+ if (ti) return ti;
+ }
+ return NULL;
+ }
+
+ // check the given item
+ if (!wcscmp(name, item->getLabel())) return item;
+
+ // depth first search
+ ti = item->getChild();
+ if (ti != NULL) {
+ ti = getByLabel(ti, name);
+ if (ti != NULL) return ti;
+ }
+
+ // recursively check siblings
+ ti = item->getSibling();
+ if (ti != NULL) ti = getByLabel(ti, name);
+
+ return ti;
+}
+
+int TreeWnd::onGetFocus() {
+ int r = TREEWND_PARENT::onGetFocus();
+
+#if 0
+DebugString("yay got focus");
+ TreeItem *ti = getCurItem();
+ if (ti != NULL) {
+ ti->setSelected(FALSE);
+ selectItemDeferred(ti);
+ }
+#endif
+
+ return r;
+}
+
+int TreeWnd::onKillFocus() {
+
+ TREEWND_PARENT::onKillFocus();
+ mousedown_item=NULL;
+/* if (edited)
+ cancelEditLabel();*/
+#if 0
+DebugString("no mo focus");
+#endif
+
+ return 1;
+}
+
+int TreeWnd::onChar(unsigned int c)
+{
+ int r = 0;
+
+ if (c == 27) {
+ if (edited)
+ cancelEditLabel(1);
+ }
+
+ if (curSelected != NULL && (r = curSelected->onChar(c)) != 0) return r;
+
+ wchar_t b = TOUPPERW(c);
+ if (b >= 'A' && b <= 'Z')
+ {
+ jumpToNext(b);
+ r = 1;
+ }
+
+ return r ? r : TREEWND_PARENT::onChar(c);
+}
+
+int TreeWnd::getNumVisibleChildItems(TreeItem *c) {
+ int nb=0;
+ for(int i=0;i<c->getNumChildren();i++) {
+ TreeItem *t=c->getNthChild(i);
+ if(t->hasSubItems() && t->isExpanded())
+ nb+=getNumVisibleChildItems(t);
+ nb++;
+ }
+ return nb;
+}
+
+int TreeWnd::getNumVisibleItems() {
+ int nb=0;
+ for(int i=0;i<items.getNumItems();i++) {
+ TreeItem *t=items.enumItem(i);
+ if(t->hasSubItems() && t->isExpanded())
+ nb+=getNumVisibleChildItems(t);
+ nb++;
+ }
+ return nb;
+}
+
+TreeItem *TreeWnd::enumVisibleChildItems(TreeItem *c, int n) {
+ int nb=0;
+ for(int i=0;i<c->getNumChildren();i++) {
+ TreeItem *t=c->getNthChild(i);
+ if(nb==n) return t;
+ if(t->hasSubItems() && t->isExpanded()) {
+ TreeItem *t2=enumVisibleChildItems(t, n-nb-1);
+ if(t2) return t2;
+ nb+=getNumVisibleChildItems(t);
+ }
+ nb++;
+ }
+ return NULL;
+}
+
+TreeItem *TreeWnd::enumVisibleItems(int n) {
+ int nb=0;
+ for(int i=0;i<items.getNumItems();i++) {
+ TreeItem *t=items.enumItem(i);
+ if(nb==n) return t;
+ if(t->hasSubItems() && t->isExpanded()) {
+ TreeItem *t2=enumVisibleChildItems(t, n-nb-1);
+ if(t2) return t2;
+ nb+=getNumVisibleChildItems(t);
+ }
+ nb++;
+ }
+ return NULL;
+}
+
+int TreeWnd::findChildItem(TreeItem *c, TreeItem *i, int *nb) {
+ for(int j=0;j<c->getNumChildren();j++) {
+ TreeItem *t=c->getNthChild(j); (*nb)++;
+ if (t == i) return *nb;
+ if(t->hasSubItems() && t->isExpanded()) {
+ int n = findChildItem(t, i, nb);
+ if (n != -1) return *nb;
+ }
+ }
+ return -1;
+}
+
+int TreeWnd::findItem(TreeItem *i) {
+ int nb=-1;
+ for(int j=0;j<items.getNumItems();j++) {
+ TreeItem *t=items.enumItem(j); nb++;
+ if (t == i) return nb;
+ if(t->hasSubItems() && t->isExpanded()) {
+ int n = findChildItem(t, i, &nb);
+ if (n != -1) return nb;
+ }
+ }
+ return -1;
+}
+
+
+TreeItem *TreeWnd::enumAllItems(int n) {
+ return all_items[n];
+}
+
+int TreeWnd::onKeyDown(int keycode)
+{
+ switch(keycode)
+ {
+ case 113: {
+ TreeItem *item = getCurItem();
+ if (item)
+ item->editLabel();
+ return 1;
+ }
+ case STDKEY_UP: {
+ TreeItem *t=getCurItem();
+ int l=getNumVisibleItems();
+ if (t == NULL) {
+ if (l > 0) setCurItem(enumVisibleItems(getNumVisibleItems()-1), FALSE, FALSE);
+ } else {
+ for(int i=0;i<l;i++)
+ if(enumVisibleItems(i)==t) {
+ if(i-1>=0) {
+ TreeItem *t2=enumVisibleItems(i-1);
+ if(t2) setCurItem(t2,FALSE,FALSE);
+ }
+ }
+ }
+ return 1;
+ }
+ case STDKEY_DOWN: {
+ TreeItem *t=getCurItem();
+ int l=getNumVisibleItems();
+ if (t == NULL) {
+ if (l > 0) setCurItem(enumVisibleItems(0), FALSE, FALSE);
+ } else {
+ for(int i=0;i<l;i++)
+ if(enumVisibleItems(i)==t) {
+ TreeItem *t2=enumVisibleItems(i+1);
+ if(t2) setCurItem(t2,FALSE,FALSE);
+ }
+ }
+ return 1;
+ }
+ case VK_PRIOR: {
+ TreeItem *t=getCurItem();
+ int l=getNumVisibleItems();
+ for(int i=0;i<l;i++)
+ if(enumVisibleItems(i)==t) {
+ int a=MAX(i-5,0);
+ TreeItem *t2=enumVisibleItems(a);
+ if(t2) setCurItem(t2,FALSE,FALSE);
+ }
+ return 1;
+ }
+ case VK_NEXT: {
+ TreeItem *t=getCurItem();
+ int l=getNumVisibleItems();
+ for(int i=0;i<l;i++)
+ if(enumVisibleItems(i)==t) {
+ int a=MIN(i+5,l-1);
+ TreeItem *t2=enumVisibleItems(a);
+ if(t2) setCurItem(t2,FALSE,FALSE);
+ }
+ return 1;
+ }
+ case STDKEY_HOME: {
+ TreeItem *t=enumVisibleItems(0);
+ if(t) setCurItem(t,FALSE,FALSE);
+ return 1;
+ }
+ case STDKEY_END: {
+ TreeItem *t=enumVisibleItems(getNumVisibleItems()-1);
+ if(t) setCurItem(t,FALSE,FALSE);
+ return 1;
+ }
+ case STDKEY_LEFT: {
+ TreeItem *t=getCurItem();
+ if(t) t->collapse();
+ return 1;
+ }
+ case STDKEY_RIGHT: {
+ TreeItem *t=getCurItem();
+ if(t) t->expand();
+ return 1;
+ }
+ }
+
+ return TREEWND_PARENT::onKeyDown(keycode);
+}
+
+void TreeWnd::jumpToNext(wchar_t c) {
+ firstFound=FALSE;
+ if (jumpToNextSubItems(&items, c)) return;
+ firstFound=TRUE;
+ jumpToNextSubItems(&items, c);
+}
+
+int TreeWnd::jumpToNextSubItems(TreeItemList *list, wchar_t c) {
+
+ for (int i=0;i<list->getNumItems();i++) {
+
+ TreeItem *nextitem = list->enumItem(i);
+ const wchar_t *l = nextitem->getLabel();
+ wchar_t b = l ? TOUPPERW(*l) : 0;
+ if (b == c && firstFound)
+ {
+ selectItem(nextitem);
+ nextitem->ensureVisible();
+ return 1;
+ }
+
+ if (nextitem->isSelected()) firstFound = TRUE;
+
+ if (nextitem->isExpanded())
+ if (jumpToNextSubItems(&nextitem->subitems, c)) return 1;
+ }
+ return 0;
+}
+
+void TreeWnd::ensureItemVisible(TreeItem *item) {
+ ASSERT(item != NULL);
+
+ // walk the parent tree to make sure item is visible
+ for (TreeItem *cur = item->getParent(); cur; cur = cur->getParent()) {
+ if (cur->isCollapsed()) cur->expand();
+ }
+
+ RECT r;
+ RECT c;
+ item->getCurRect(&r);
+ getClientRect(&c);
+ if (r.top < c.top || r.bottom > c.bottom) {
+ if (r.top + (c.bottom - c.top) <= getContentsHeight())
+ scrollToY(r.top);
+ else {
+ scrollToY(getContentsHeight()-(c.bottom-c.top));
+ }
+ }
+}
+
+void TreeWnd::setHilitedColor(const wchar_t *colorname) {
+ // we have to store it in a String because SkinColor does not make a copy
+ hilitedColorName = colorname;
+ hilitedColor = hilitedColorName;
+}
+
+ARGB32 TreeWnd::getHilitedColor() {
+ return hilitedColor;
+}
+
+int TreeWnd::compareItem(TreeItem *p1, TreeItem *p2)
+{
+ int r = wcscmp(p1->getLabel(), p2->getLabel());
+ if (r == 0) return CMP3(p1, p2);
+ return r;
+}
+
+int TreeWnd::setFontSize(int newsize)
+{
+ TREEWND_PARENT::setFontSize(newsize);
+ if (newsize >= 0) textsize = newsize;
+ TextInfoCanvas c(this);
+ Wasabi::FontInfo fontInfo;
+ fontInfo.pointSize = getFontSize();
+ itemHeight = c.getTextHeight(&fontInfo);
+ redraw = 1;
+ metrics_ok = 0;
+ invalidate();
+ return 1;
+}
+
+int TreeWnd::getFontSize() {
+#ifndef WASABINOMAINAPI
+ return textsize + api->metrics_getDelta();
+#else
+ //MULTIAPI-FIXME: not handling delta
+ return textsize;
+#endif
+}
+
+
+void TreeWnd::onSelectItem(TreeItem *i) {
+ Accessible *a = getAccessibleObject();
+ if (a != NULL)
+ a->onGetFocus(findItem(i));
+}
+
+void TreeWnd::onDeselectItem(TreeItem *i) {
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////
+// TreeItem
+////////////////////////////////////////////////////////////////////////////////////
+
+TreeItem::TreeItem(const wchar_t *label) {
+ parent=NULL;
+ MEMZERO(&curRect, sizeof(RECT));
+ childTab = TAB_AUTO;
+ tree = NULL;
+ expandStatus = STATUS_COLLAPSED;
+ icon = NULL;
+ _z = 0;
+
+ if (label != NULL)
+ setLabel(label);
+
+ selected = FALSE;
+ hilitedDrop = FALSE;
+ hilited = FALSE;
+ being_edited = FALSE;
+
+ setSorted(TRUE);
+}
+
+TreeItem::~TreeItem() {
+ // the subitem will call parent tree which will remove item from our list
+ deleteSubitems();
+
+ // remove from parent tree
+ if (tree) tree->removeTreeItem(this);
+
+ delete icon;
+}
+
+void TreeItem::deleteSubitems() {
+ while (subitems.getNumItems() > 0) {
+ delete subitems.enumItem(0);
+ }
+}
+
+void TreeItem::setSorted(int issorted) {
+ subitems.setAutoSort(!!issorted);
+}
+
+void TreeItem::setChildTab(int haschildtab) {
+ childTab = haschildtab;
+}
+
+void TreeItem::linkTo(TreeItem *par) {
+ parent = par;
+
+ if (par == NULL) return;
+
+ par->addSubItem(this);
+}
+
+void TreeItem::addSubItem(TreeItem *item) {
+ subitems.addItem(item);
+}
+
+int TreeItem::removeSubitem(TreeItem *item) {
+ if (subitems.searchItem(item) == -1) return 0;
+ subitems.removeItem(item);
+ if (tree->redraw)
+ tree->invalidate();
+ return 1;
+}
+
+TreeItem *TreeItem::getChild() {
+ return subitems.getFirst();
+}
+
+TreeItem *TreeItem::getChildSibling(TreeItem *item) { // locate item in children and return its sibling
+ for (int i=0;i<subitems.getNumItems();i++) {
+ if (subitems.enumItem(i) == item) {
+ if (i == subitems.getNumItems()-1) return NULL;
+ return subitems.enumItem(i+1);
+ }
+ }
+return NULL;
+}
+
+TreeItem *TreeItem::getSibling() { // returns next item
+ if (!parent)
+ return tree->getSibling(this);
+ else
+ return parent->getChildSibling(this);
+}
+
+void TreeItem::setTree(TreeWnd *newtree) {
+ tree = newtree;
+ // recursively reset tree for children, if any
+ for (int i = 0; ; i++) {
+ TreeItem *item = getNthChild(i);
+ if (item == NULL) break;
+ item->setTree(tree);
+ }
+}
+
+void TreeItem::ensureVisible()
+{
+ if (tree) tree->ensureItemVisible(this);
+}
+
+const wchar_t *TreeItem::getLabel()
+{
+ return label;
+}
+
+void TreeItem::setLabel(const wchar_t *newlabel)
+{
+ label = newlabel;
+ if (newlabel) {
+ if (tree)
+ tree->invalidateMetrics();
+ invalidate();
+ }
+}
+
+int TreeItem::customDraw(Canvas *canvas, const POINT &pt, int txtHeight, int indentation, const RECT &clientRect, const Wasabi::FontInfo *fontInfo)
+{
+ if (being_edited) return 0;
+
+ SkinBitmap *icon = getIcon();
+
+ int cw = clientRect.right - clientRect.left;
+
+ int iconw = MIN(icon ? icon->getWidth() : 0, cw);
+
+ if (isSelected() || isHilitedDrop())
+ {
+ RECT r;
+ r.left = pt.x;
+ r.top = pt.y;
+ //r.right = r.left + canvas->getTextWidth(label)+2+(icon ? txtHeight : 0);
+ r.right = r.left + canvas->getTextWidth(label, fontInfo)+2+iconw;
+ r.bottom = r.top + txtHeight;
+ canvas->fillRect(&r, isHilitedDrop() ? drophilitecolor : selectedcolor);
+ }
+
+ if (isHilited())
+ {
+ RECT r;
+ r.left = pt.x;
+ r.top = pt.y;
+ //r.right = r.left + canvas->getTextWidth(label)+2+(icon ? txtHeight : 0);
+ r.right = r.left + canvas->getTextWidth(label, fontInfo)+2+iconw;
+ r.bottom = r.top + txtHeight;
+ canvas->drawRect(&r, 1, tree->getHilitedColor());
+ }
+
+ POINT d=pt;
+
+ if (icon) {
+ RECT i;
+ i.left = pt.x+1;
+ i.right = i.left + iconw;
+// i.top = pt.y+1;
+ int lh = MIN(icon->getHeight(), txtHeight);
+ i.top = pt.y + (txtHeight - lh) / 2;
+// i.bottom = i.top + txtHeight-2;
+ i.bottom = i.top + lh;
+ icon->stretchToRectAlpha(canvas, &i);
+ //d.x += txtHeight;
+ d.x += icon->getWidth();
+ }
+
+ canvas->textOut(d.x+1, d.y, label, fontInfo);
+
+ return canvas->getTextWidth(label, fontInfo)+2+iconw;
+}
+
+int TreeItem::getItemWidth(int txtHeight, int indentation) {
+ SkinBitmap *icon = getIcon();
+ if (!label) return (icon ? txtHeight : 0);
+ TextInfoCanvas c(tree);
+ Wasabi::FontInfo fontInfo;
+ fontInfo.pointSize = getTree()->getFontSize();
+ int width = c.getTextWidth(label, &fontInfo)+2;
+ width += (icon ? txtHeight : 0);
+ return width;
+}
+
+int TreeItem::getNumChildren() {
+ return subitems.getNumItems();
+}
+
+TreeItem *TreeItem::getNthChild(int nth) {
+ if (nth >= subitems.getNumItems()) return NULL;
+ return subitems.enumItem(nth);
+}
+
+void TreeItem::setCurRect(int x1, int y1, int x2, int y2, int z) {
+ curRect.left = x1;
+ curRect.right = x2;
+ curRect.top = y1;
+ curRect.bottom = y2;
+ _z = z;
+}
+
+int TreeItem::getIndent() {
+ return _z;
+}
+
+bool TreeItem::hasSubItems() {
+ return subitems.getNumItems()>0;
+}
+
+bool TreeItem::needTab() {
+ return (childTab == TAB_YES || (childTab == TAB_AUTO && hasSubItems()));
+}
+
+bool TreeItem::isExpanded() {
+ if (!hasSubItems()) return FALSE;
+ return expandStatus == STATUS_EXPANDED;
+}
+
+bool TreeItem::isCollapsed() {
+ if (!hasSubItems()) return TRUE;
+ return expandStatus == STATUS_COLLAPSED;
+}
+
+int TreeItem::getCurRect(RECT *r) {
+ r->left = curRect.left-tree->getScrollX();
+ r->top = curRect.top-tree->getScrollY();
+ r->right = curRect.right-tree->getScrollX();
+ r->bottom = curRect.bottom-tree->getScrollY();
+ RECT c;
+ tree->getClientRect(&c);
+ r->top += c.top;
+ r->bottom += c.top;
+ r->left += c.left;
+ r->right += c.left;
+ return 1;
+}
+
+TreeWnd *TreeItem::getTree() const {
+ return tree;
+}
+
+void TreeItem::setSelected(bool isSelected, bool expandCollapse, bool editifselected) {
+ bool wasselected = selected;
+ selected = !!isSelected;
+
+ if (selected != wasselected) {
+ invalidate();
+ tree->repaint();
+ if (selected) {
+ onSelect();
+ ASSERT(tree != NULL);
+ tree->onItemSelected(this);
+ } else {
+ onDeselect();
+ ASSERT(tree != NULL);
+ tree->onItemDeselected(this);
+ }
+ } else {
+ if (selected && editifselected) {
+ editLabel();
+ }
+ }
+
+ if (expandCollapse) {
+ if (isCollapsed())
+ expand();
+ else
+ collapse();
+ }
+}
+
+void TreeItem::invalidate() {
+ if (tree) {
+ RECT r;
+ getCurRect(&r);
+ tree->invalidateRect(&r);
+ }
+}
+
+bool TreeItem::isSelected() {
+ return selected;
+}
+
+int TreeItem::collapse() {
+ int old = expandStatus;
+
+ if (hasSubItems()) {
+ if (expandStatus == STATUS_COLLAPSED)
+ return 0;
+ expandStatus = STATUS_COLLAPSED;
+ RECT c;
+ tree->getClientRect(&c);
+ RECT r;
+ getCurRect(&r);
+ r.bottom = c.bottom;
+ r.left = c.left;
+ r.right = c.right;
+ if (tree) {
+ tree->invalidateRect(&r);
+ tree->invalidateMetrics();
+ }
+ }
+
+ onCollapse();
+
+ return old != expandStatus;
+}
+
+int TreeItem::expand() {
+ int old = expandStatus;
+
+ if (hasSubItems()) {
+ if (expandStatus == STATUS_EXPANDED)
+ return 0;
+ expandStatus = STATUS_EXPANDED;
+ RECT c;
+ tree->getClientRect(&c);
+ RECT r;
+ getCurRect(&r);
+ r.bottom = c.bottom;
+ r.left = c.left;
+ r.right = c.right;
+ if (tree) {
+ tree->invalidateRect(&r);
+ tree->invalidateMetrics();
+ }
+ }
+
+ onExpand();
+
+ return old != expandStatus;
+}
+
+int TreeItem::onContextMenu(int x, int y) {
+ return 0;
+}
+
+void TreeItem::setHilitedDrop(bool ishilitedDrop) {
+ bool washilighted = hilitedDrop;
+ hilitedDrop = !!ishilitedDrop;
+
+ if (washilighted != hilitedDrop)
+ invalidate();
+}
+
+void TreeItem::setHilited(bool ishilited) {
+ bool washilighted = hilited;
+ hilited = !!ishilited;
+
+ if (washilighted != hilited)
+ invalidate();
+}
+
+TreeItem *TreeItem::getParent() {
+ return parent;
+}
+
+bool TreeItem::isHilitedDrop() {
+ return hilitedDrop;
+}
+
+bool TreeItem::isHilited() {
+ return hilited;
+}
+
+void TreeItem::setIcon(SkinBitmap *newicon) {
+ if (icon) {
+ delete icon;
+ icon = NULL;
+ }
+ icon = newicon;
+ invalidate();
+}
+
+SkinBitmap *TreeItem::getIcon() {
+ return icon;
+}
+
+void TreeItem::sortItems() {
+ subitems.sort();
+}
+
+void TreeItem::editLabel() {
+ if (!tree) return;
+ tree->editItemLabel(this);
+}
+
+int TreeItem::onBeginLabelEdit() {
+ return 1; // disable editing by default
+}
+
+int TreeItem::onEndLabelEdit(const wchar_t *newlabel)
+{
+ return 1; // accept new label by default
+}
+
+void TreeItem::setEdition(bool isedited) {
+ being_edited = !!isedited;
+ invalidate();
+}
+
+bool TreeItem::getEdition() {
+ return being_edited;
+}
+
+bool TreeItem::isSorted()
+{
+ return subitems.getAutoSort();
+}
+