#include "tkTreeCtrl.h"
typedef
struct
TreeItem_ TreeItem_;
typedef
struct
TreeItemColumn_ TreeItemColumn_;
struct
TreeItemColumn_ {
int
cstate;
int
span;
TreeStyle style;
TreeHeaderColumn headerColumn;
TreeItemColumn next;
};
struct
TreeItem_ {
int
id;
int
depth;
int
fixedHeight;
int
numChildren;
int
index;
int
indexVis;
int
state;
TreeItem parent;
TreeItem firstChild;
TreeItem lastChild;
TreeItem prevSibling;
TreeItem nextSibling;
TreeItemDInfo dInfo;
TreeItemRInfo rInfo;
TreeItemColumn columns;
int
*spans;
int
spanAlloc;
#define ITEM_FLAG_DELETED 0x0001 /* Item is being deleted */
#define ITEM_FLAG_SPANS_SIMPLE 0x0002 /* All spans are 1 */
#define ITEM_FLAG_SPANS_VALID 0x0004 /* Some spans are > 1, but we don't
* need to redo them. Also indicates
* we have an entry in
* TreeCtrl.itemSpansHash. */
#define ITEM_FLAG_BUTTON 0x0008 /* -button true */
#define ITEM_FLAG_BUTTON_AUTO 0x0010 /* -button auto */
#define ITEM_FLAG_VISIBLE 0x0020 /* -visible */
#define ITEM_FLAG_WRAP 0x0040 /* -wrap */
#define ITEM_FLAG_BUTTONSTATE_ACTIVE 0x0080 /* buttonstate "active" */
#define ITEM_FLAG_BUTTONSTATE_PRESSED 0x0100 /* buttonstate "pressed" */
int
flags;
TagInfo *tagInfo;
TreeHeader header;
};
#define ITEM_FLAGS_BUTTONSTATE (ITEM_FLAG_BUTTONSTATE_ACTIVE | \
ITEM_FLAG_BUTTONSTATE_PRESSED)
#ifdef ALLOC_HAX
static
CONST
char
*ItemUid =
"Item"
, *ItemColumnUid =
"ItemColumn"
;
#endif
#define IS_ROOT(i) ((i)->depth == -1)
#define IS_ALL(i) ((i) == ITEM_ALL)
#define IS_DELETED(i) (((i)->flags & ITEM_FLAG_DELETED) != 0)
#define IS_VISIBLE(i) (((i)->flags & ITEM_FLAG_VISIBLE) != 0)
#define IS_WRAP(i) (((i)->flags & ITEM_FLAG_WRAP) != 0)
#define ITEM_CONF_BUTTON 0x0001
#define ITEM_CONF_SIZE 0x0002
#define ITEM_CONF_VISIBLE 0x0004
#define ITEM_CONF_WRAP 0x0008
static
Tk_OptionSpec itemOptionSpecs[] = {
{TK_OPTION_CUSTOM,
"-button"
, (
char
*) NULL, (
char
*) NULL,
"0"
, -1, Tk_Offset(TreeItem_, flags),
0, (ClientData) NULL, ITEM_CONF_BUTTON},
{TK_OPTION_PIXELS,
"-height"
, (
char
*) NULL, (
char
*) NULL,
(
char
*) NULL, -1, Tk_Offset(TreeItem_, fixedHeight),
TK_OPTION_NULL_OK, (ClientData) NULL, ITEM_CONF_SIZE},
{TK_OPTION_CUSTOM,
"-tags"
, (
char
*) NULL, (
char
*) NULL,
(
char
*) NULL, -1, Tk_Offset(TreeItem_, tagInfo),
TK_OPTION_NULL_OK, (ClientData) &TreeCtrlCO_tagInfo, 0},
{TK_OPTION_CUSTOM,
"-visible"
, (
char
*) NULL, (
char
*) NULL,
"1"
, -1, Tk_Offset(TreeItem_, flags),
0, (ClientData) NULL, ITEM_CONF_VISIBLE},
{TK_OPTION_CUSTOM,
"-wrap"
, (
char
*) NULL, (
char
*) NULL,
"0"
, -1, Tk_Offset(TreeItem_, flags),
0, (ClientData) NULL, ITEM_CONF_WRAP},
{TK_OPTION_END, (
char
*) NULL, (
char
*) NULL, (
char
*) NULL,
(
char
*) NULL, 0, -1, 0, 0, 0}
};
static
TreeItemColumn
Column_Alloc(
TreeCtrl *tree,
TreeItem item
)
{
#ifdef ALLOC_HAX
TreeItemColumn column = (TreeItemColumn) TreeAlloc_Alloc(tree->allocData, ItemColumnUid,
sizeof
(TreeItemColumn_));
#else
TreeItemColumn column = (TreeItemColumn) ckalloc(
sizeof
(TreeItemColumn_));
#endif
memset
(column,
'\0'
,
sizeof
(TreeItemColumn_));
column->span = 1;
if
(item->header != NULL) {
column->headerColumn = TreeHeaderColumn_CreateWithItemColumn(
item->header, column);
#if TREECTRL_DEBUG
if
(column->headerColumn == NULL)
panic(
"TreeHeaderColumn_CreateWithItemColumn failed"
);
#endif
column->cstate = STATE_HEADER_NORMAL;
}
return
column;
}
void
TreeItemColumn_InvalidateSize(
TreeCtrl *tree,
TreeItemColumn column_
)
{
}
int
TreeItemColumn_NeededWidth(
TreeCtrl *tree,
TreeItem item,
TreeItemColumn column
)
{
if
(column->style != NULL)
return
TreeStyle_NeededWidth(tree, column->style,
item->state | column->cstate);
return
0;
}
void
TreeItems_RequestWidthInColumns(
TreeCtrl *tree,
TreeColumn columnMin,
TreeColumn columnMax
)
{
TreeItem item = tree->root;
if
(!TreeItem_ReallyVisible(tree, item))
item = TreeItem_NextVisible(tree, item);
while
(item != NULL) {
TreeItem_RequestWidthInColumns(tree, item, columnMin, columnMax);
item = TreeItem_NextVisible(tree, item);
}
}
TreeStyle
TreeItemColumn_GetStyle(
TreeCtrl *tree,
TreeItemColumn column
)
{
return
column->style;
}
int
TreeItemColumn_Index(
TreeCtrl *tree,
TreeItem item,
TreeItemColumn column
)
{
TreeItemColumn walk;
int
i = 0;
walk = item->columns;
while
((walk != NULL) && (walk != column)) {
i++;
walk = walk->next;
}
if
(walk == NULL)
panic(
"TreeItemColumn_Index: couldn't find the column\n"
);
return
i;
}
void
TreeItemColumn_ForgetStyle(
TreeCtrl *tree,
TreeItemColumn column
)
{
if
(column->style != NULL) {
TreeStyle_FreeResources(tree, column->style);
column->style = NULL;
}
}
void
TreeItemColumn_SetStyle(
TreeCtrl *tree,
TreeItemColumn column,
TreeStyle style
)
{
if
(column->style != NULL) {
TreeStyle_FreeResources(tree, column->style);
}
column->style = style;
}
TreeItemColumn
TreeItemColumn_GetNext(
TreeCtrl *tree,
TreeItemColumn column
)
{
return
column->next;
}
static
TreeItemColumn
Column_FreeResources(
TreeCtrl *tree,
TreeItemColumn self
)
{
TreeItemColumn next = self->next;
if
(self->style != NULL)
TreeStyle_FreeResources(tree, self->style);
if
(self->headerColumn != NULL)
TreeHeaderColumn_FreeResources(tree, self->headerColumn);
#ifdef ALLOC_HAX
TreeAlloc_Free(tree->allocData, ItemColumnUid, (
char
*) self,
sizeof
(TreeItemColumn_));
#else
WFREE(self, TreeItemColumn_);
#endif
return
next;
}
static
void
Item_UpdateIndex(TreeCtrl *tree,
TreeItem item,
int
*index,
int
*indexVis
)
{
TreeItem child, parent = item->parent;
int
parentVis, parentOpen;
if
(parent != NULL)
item->depth = parent->depth + 1;
else
item->depth = 0;
if
(item->depth > tree->depth)
tree->depth = item->depth;
item->index = (*index)++;
item->indexVis = -1;
if
(parent != NULL) {
parentOpen = (parent->state & STATE_ITEM_OPEN) != 0;
parentVis = parent->indexVis != -1;
if
(IS_ROOT(parent) && !tree->showRoot) {
parentOpen = TRUE;
parentVis = IS_VISIBLE(parent);
}
if
(parentVis && parentOpen && IS_VISIBLE(item)) {
item->indexVis = (*indexVis)++;
if
(IS_WRAP(item))
tree->itemWrapCount++;
}
}
child = item->firstChild;
while
(child != NULL) {
Item_UpdateIndex(tree, child, index, indexVis);
child = child->nextSibling;
}
}
void
Tree_UpdateItemIndex(
TreeCtrl *tree
)
{
TreeItem item = tree->root;
int
index = 1, indexVis = 0;
if
(!tree->updateIndex)
return
;
if
(tree->debug.enable && tree->debug.data)
dbwin(
"Tree_UpdateItemIndex %s\n"
, Tk_PathName(tree->tkwin));
tree->depth = -1;
tree->itemWrapCount = 0;
item->index = 0;
item->indexVis = -1;
if
(tree->showRoot && IS_VISIBLE(item)) {
item->indexVis = indexVis++;
if
(IS_WRAP(item))
tree->itemWrapCount++;
}
item = item->firstChild;
while
(item != NULL) {
Item_UpdateIndex(tree, item, &index, &indexVis);
item = item->nextSibling;
}
tree->itemVisCount = indexVis;
tree->updateIndex = 0;
}
static
TreeItem
Item_Alloc(
TreeCtrl *tree,
int
isHeader
)
{
#ifdef ALLOC_HAX
TreeItem item = (TreeItem) TreeAlloc_Alloc(tree->allocData, ItemUid,
sizeof
(TreeItem_));
#else
TreeItem item = (TreeItem) ckalloc(
sizeof
(TreeItem_));
#endif
memset
(item,
'\0'
,
sizeof
(TreeItem_));
if
(Tk_InitOptions(tree->interp, (
char
*) item,
tree->itemOptionTable, tree->tkwin) != TCL_OK)
panic(
"Tk_InitOptions() failed in Item_Alloc()"
);
if
(isHeader) {
if
(tree->gotFocus)
item->state |= STATE_HEADER_FOCUS;
#if MAC_OSX_TK
if
(!tree->isActive)
item->state |= STATE_HEADER_BG;
#endif
}
else
{
item->state =
STATE_ITEM_OPEN |
STATE_ITEM_ENABLED;
if
(tree->gotFocus)
item->state |= STATE_ITEM_FOCUS;
}
item->indexVis = -1;
item->flags |= ITEM_FLAG_SPANS_SIMPLE;
if
(isHeader)
Tree_AddHeader(tree, item);
else
Tree_AddItem(tree, item);
return
item;
}
static
TreeItem
Item_AllocRoot(
TreeCtrl *tree
)
{
TreeItem item;
item = Item_Alloc(tree, FALSE);
item->depth = -1;
item->state |= STATE_ITEM_ACTIVE;
return
item;
}
TreeItemColumn
TreeItem_GetFirstColumn(
TreeCtrl *tree,
TreeItem item
)
{
return
item->columns;
}
int
TreeItem_GetState(
TreeCtrl *tree,
TreeItem item
)
{
return
item->state;
}
int
TreeItemColumn_GetState(
TreeCtrl *tree,
TreeItemColumn column
)
{
return
column->cstate;
}
int
TreeItemColumn_ChangeState(
TreeCtrl *tree,
TreeItem item,
TreeItemColumn column,
TreeColumn treeColumn,
int
stateOff,
int
stateOn
)
{
int
cstate, state;
int
sMask, iMask = 0;
cstate = column->cstate;
cstate &= ~stateOff;
cstate |= stateOn;
if
(cstate == column->cstate)
return
0;
state = item->state | column->cstate;
state &= ~stateOff;
state |= stateOn;
if
(column->style != NULL) {
sMask = TreeStyle_ChangeState(tree, column->style,
item->state | column->cstate, state);
if
(sMask) {
if
((sMask & CS_LAYOUT)
)
TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
iMask |= sMask;
}
if
(iMask & CS_LAYOUT) {
TreeItem_InvalidateHeight(tree, item);
TreeItemColumn_InvalidateSize(tree, column);
Tree_FreeItemDInfo(tree, item, NULL);
if
(item->header == NULL)
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
}
else
if
(iMask & CS_DISPLAY) {
Tree_InvalidateItemDInfo(tree, treeColumn, item, NULL);
}
}
if
((iMask & CS_LAYOUT)
)
TreeColumns_InvalidateWidth(tree);
column->cstate = cstate;
return
iMask;
}
int
TreeItem_ChangeState(
TreeCtrl *tree,
TreeItem item,
int
stateOff,
int
stateOn
)
{
TreeItemColumn column;
TreeColumn treeColumn;
int
columnIndex = 0, state, cstate;
int
sMask, iMask = 0;
int
tailOK = item->header != NULL;
state = item->state;
state &= ~stateOff;
state |= stateOn;
if
(state == item->state)
return
0;
treeColumn = Tree_FirstColumn(tree, -1, tailOK);
column = item->columns;
while
(column != NULL) {
if
(column->style != NULL) {
cstate = item->state | column->cstate;
cstate &= ~stateOff;
cstate |= stateOn;
sMask = TreeStyle_ChangeState(tree, column->style,
item->state | column->cstate, cstate);
if
(sMask) {
if
(sMask & CS_LAYOUT) {
TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
TreeItemColumn_InvalidateSize(tree, column);
}
else
if
(sMask & CS_DISPLAY) {
Tree_InvalidateItemDInfo(tree, treeColumn, item, NULL);
}
iMask |= sMask;
}
}
columnIndex++;
column = column->next;
treeColumn = Tree_ColumnToTheRight(treeColumn, FALSE, tailOK);
}
if
(TreeItem_HasButton(tree, item)) {
Tk_Image image1, image2;
Pixmap bitmap1, bitmap2;
static
int
butOpen, butClosed;
static
int
themeOpen, themeClosed;
int
w1, h1, w2, h2;
void
*ptr1 = NULL, *ptr2 = NULL;
image1 = PerStateImage_ForState(tree, &tree->buttonImage, item->state, NULL);
if
(image1 != NULL) {
Tk_SizeOfImage(image1, &w1, &h1);
ptr1 = image1;
}
if
(ptr1 == NULL) {
bitmap1 = PerStateBitmap_ForState(tree, &tree->buttonBitmap, item->state, NULL);
if
(bitmap1 != None) {
Tk_SizeOfBitmap(tree->display, bitmap1, &w1, &h1);
ptr1 = (
void
*) bitmap1;
}
}
if
(ptr1 == NULL) {
if
(tree->useTheme &&
TreeTheme_GetButtonSize(tree, Tk_WindowId(tree->tkwin),
(item->state & STATE_ITEM_OPEN) != 0, &w1, &h1) == TCL_OK) {
ptr1 = (item->state & STATE_ITEM_OPEN) ? &themeOpen : &themeClosed;
}
}
if
(ptr1 == NULL) {
w1 = h1 = tree->buttonSize;
ptr1 = (item->state & STATE_ITEM_OPEN) ? &butOpen : &butClosed;
}
image2 = PerStateImage_ForState(tree, &tree->buttonImage, state, NULL);
if
(image2 != NULL) {
Tk_SizeOfImage(image2, &w2, &h2);
ptr2 = image2;
}
if
(ptr2 == NULL) {
bitmap2 = PerStateBitmap_ForState(tree, &tree->buttonBitmap, state, NULL);
if
(bitmap2 != None) {
Tk_SizeOfBitmap(tree->display, bitmap2, &w2, &h2);
ptr2 = (
void
*) bitmap2;
}
}
if
(ptr2 == NULL) {
if
(tree->useTheme &&
TreeTheme_GetButtonSize(tree, Tk_WindowId(tree->tkwin),
(state & STATE_ITEM_OPEN) != 0, &w2, &h2) == TCL_OK) {
ptr2 = (state & STATE_ITEM_OPEN) ? &themeOpen : &themeClosed;
}
}
if
(ptr2 == NULL) {
w2 = h2 = tree->buttonSize;
ptr2 = (state & STATE_ITEM_OPEN) ? &butOpen : &butClosed;
}
if
((w1 != w2) || (h1 != h2)) {
iMask |= CS_LAYOUT | CS_DISPLAY;
}
else
if
(ptr1 != ptr2) {
iMask |= CS_DISPLAY;
if
(tree->columnTree != NULL)
Tree_InvalidateItemDInfo(tree, tree->columnTree, item, NULL);
}
}
if
(iMask & CS_LAYOUT) {
TreeItem_InvalidateHeight(tree, item);
Tree_FreeItemDInfo(tree, item, NULL);
if
(item->header == NULL)
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
else
TreeColumns_InvalidateWidth(tree);
}
item->state = state;
return
iMask;
}
void
TreeItem_UndefineState(
TreeCtrl *tree,
TreeItem item,
int
state
)
{
TreeItemColumn column = item->columns;
while
(column != NULL) {
column->cstate &= ~state;
column = column->next;
}
item->state &= ~state;
}
int
TreeItem_HasButton(
TreeCtrl *tree,
TreeItem item
)
{
if
(!tree->showButtons || (IS_ROOT(item) && !tree->showRootButton))
return
0;
if
(item->parent == tree->root && !tree->showRootChildButtons)
return
0;
if
(item->flags & ITEM_FLAG_BUTTON)
return
1;
if
(item->flags & ITEM_FLAG_BUTTON_AUTO) {
TreeItem child = item->firstChild;
while
(child != NULL) {
if
(IS_VISIBLE(child))
return
1;
child = child->nextSibling;
}
}
return
0;
}
int
TreeItem_GetButtonBbox(
TreeCtrl *tree,
TreeItem item,
TreeRectangle *tr
)
{
TreeItemColumn itemColumn;
TreeStyle style = NULL;
int
indent, buttonY = -1;
if
(!tree->columnTreeVis)
return
0;
if
(!TreeItem_HasButton(tree, item))
return
0;
if
(TreeItem_GetRects(tree, item, tree->columnTree, 0, NULL, tr) == 0)
return
0;
itemColumn = TreeItem_FindColumn(tree, item,
TreeColumn_Index(tree->columnTree));
if
(itemColumn != NULL)
style = TreeItemColumn_GetStyle(tree, itemColumn);
indent = TreeItem_Indent(tree, tree->columnTree, item);
if
(style != NULL)
buttonY = TreeStyle_GetButtonY(tree, style);
tr->x = indent - tree->useIndent;
tr->width = tree->useIndent;
if
(buttonY < 0)
tr->y = (tr->height - tree->buttonHeightMax) / 2;
else
tr->y = buttonY;
tr->height = tree->buttonHeightMax;
return
1;
}
int
TreeItem_IsPointInButton(
TreeCtrl *tree,
TreeItem item,
int
x,
int
y
)
{
TreeRectangle tr;
#define BUTTON_HITTEST_SLOP 11
int
centerY, slop = MAX(tree->buttonHeightMax / 2, BUTTON_HITTEST_SLOP);
if
(!TreeItem_GetButtonBbox(tree, item, &tr))
return
0;
centerY = TreeRect_Top(tr) + TreeRect_Height(tr) / 2;
if
((y < centerY - slop) ||
(y >= centerY + slop + (tree->buttonHeightMax % 2)))
return
0;
return
1;
}
int
TreeItem_GetDepth(
TreeCtrl *tree,
TreeItem item
)
{
#if 0
Tree_UpdateItemIndex(tree);
#endif
return
item->depth;
}
int
TreeItem_GetID(
TreeCtrl *tree,
TreeItem item
)
{
return
item->id;
}
int
TreeItem_SetID(
TreeCtrl *tree,
TreeItem item,
int
id
)
{
return
item->id = id;
}
int
TreeItem_GetEnabled(
TreeCtrl *tree,
TreeItem item
)
{
return
(item->state & STATE_ITEM_ENABLED) != 0;
}
int
TreeItem_GetSelected(
TreeCtrl *tree,
TreeItem item
)
{
return
(item->state & STATE_ITEM_SELECTED) != 0;
}
int
TreeItem_CanAddToSelection(
TreeCtrl *tree,
TreeItem item
)
{
if
(item->header != NULL)
return
FALSE;
if
(TreeItem_GetSelected(tree, item))
return
FALSE;
if
(!TreeItem_GetEnabled(tree, item))
return
FALSE;
#ifdef SELECTION_VISIBLE
if
(!TreeItem_ReallyVisible(tree, item))
return
FALSE;
#endif
return
TRUE;
}
TreeItem
TreeItem_GetParent(
TreeCtrl *tree,
TreeItem item
)
{
return
item->parent;
}
int
TreeItem_GetWrap(
TreeCtrl *tree,
TreeItem item
)
{
return
(item->flags & ITEM_FLAG_WRAP) != 0;
}
TreeItem
TreeItem_GetNextSibling(
TreeCtrl *tree,
TreeItem item
)
{
return
item->nextSibling;
}
TreeItem
TreeItem_NextSiblingVisible(
TreeCtrl *tree,
TreeItem item
)
{
item = TreeItem_GetNextSibling(tree, item);
while
(item != NULL) {
if
(TreeItem_ReallyVisible(tree, item))
return
item;
item = TreeItem_GetNextSibling(tree, item);
}
return
NULL;
}
void
TreeItem_SetDInfo(
TreeCtrl *tree,
TreeItem item,
TreeItemDInfo dInfo
)
{
item->dInfo = dInfo;
}
TreeItemDInfo
TreeItem_GetDInfo(
TreeCtrl *tree,
TreeItem item
)
{
return
item->dInfo;
}
void
TreeItem_SetRInfo(
TreeCtrl *tree,
TreeItem item,
TreeItemRInfo rInfo
)
{
item->rInfo = rInfo;
}
TreeItemRInfo
TreeItem_GetRInfo(
TreeCtrl *tree,
TreeItem item
)
{
return
item->rInfo;
}
TreeItem
TreeItem_Next(
TreeCtrl *tree,
TreeItem item
)
{
if
(item->firstChild != NULL)
return
item->firstChild;
if
(item->nextSibling != NULL)
return
item->nextSibling;
while
(1) {
item = item->parent;
if
(item == NULL)
break
;
if
(item->nextSibling != NULL)
return
item->nextSibling;
}
return
NULL;
}
TreeItem
TreeItem_NextVisible(
TreeCtrl *tree,
TreeItem item
)
{
item = TreeItem_Next(tree, item);
while
(item != NULL) {
if
(TreeItem_ReallyVisible(tree, item))
return
item;
item = TreeItem_Next(tree, item);
}
return
NULL;
}
TreeItem
TreeItem_Prev(
TreeCtrl *tree,
TreeItem item
)
{
TreeItem walk;
if
(item->parent == NULL)
return
NULL;
walk = item->parent;
if
(item->prevSibling) {
walk = item->prevSibling;
while
(walk->lastChild != NULL)
walk = walk->lastChild;
}
return
walk;
}
TreeItem
TreeItem_PrevVisible(
TreeCtrl *tree,
TreeItem item
)
{
item = TreeItem_Prev(tree, item);
while
(item != NULL) {
if
(TreeItem_ReallyVisible(tree, item))
return
item;
item = TreeItem_Prev(tree, item);
}
return
NULL;
}
void
TreeItem_ToIndex(
TreeCtrl *tree,
TreeItem item,
int
*index,
int
*indexVis
)
{
Tree_UpdateItemIndex(tree);
if
(index != NULL) (*index) = item->index;
if
(indexVis != NULL) (*indexVis) = item->indexVis;
}
int
TreeItem_HasTag(
TreeItem item,
Tk_Uid tag
)
{
TagInfo *tagInfo = item->tagInfo;
Tk_Uid *tagPtr;
int
count;
if
(tagInfo == NULL)
return
0;
for
(tagPtr = tagInfo->tagPtr, count = tagInfo->numTags;
count > 0; tagPtr++, count--) {
if
(*tagPtr == tag) {
return
1;
}
}
return
0;
}
typedef
struct
Qualifiers {
TreeCtrl *tree;
int
visible;
int
states[3];
TagExpr expr;
int
exprOK;
int
depth;
Tk_Uid tag;
} Qualifiers;
static
void
Qualifiers_Init(
TreeCtrl *tree,
Qualifiers *q
)
{
q->tree = tree;
q->visible = -1;
q->states[0] = q->states[1] = q->states[2] = 0;
q->exprOK = FALSE;
q->depth = -1;
q->tag = NULL;
}
static
int
Qualifiers_Scan(
Qualifiers *q,
int
objc,
Tcl_Obj **objv,
int
startIndex,
int
*argsUsed
)
{
TreeCtrl *tree = q->tree;
Tcl_Interp *interp = tree->interp;
int
qual, j = startIndex;
static
CONST
char
*qualifiers[] = {
"depth"
,
"state"
,
"tag"
,
"visible"
,
"!visible"
, NULL
};
enum
qualEnum {
QUAL_DEPTH, QUAL_STATE, QUAL_TAG, QUAL_VISIBLE, QUAL_NOT_VISIBLE
};
static
int
qualArgs[] = {
2, 2, 2, 1, 1
};
*argsUsed = 0;
for
(; j < objc; ) {
if
(Tcl_GetIndexFromObj(NULL, objv[j], qualifiers, NULL, 0,
&qual) != TCL_OK)
break
;
if
(objc - j < qualArgs[qual]) {
Tcl_AppendResult(interp,
"missing arguments to \""
,
Tcl_GetString(objv[j]),
"\" qualifier"
, NULL);
goto
errorExit;
}
switch
((
enum
qualEnum) qual) {
case
QUAL_DEPTH: {
if
(Tcl_GetIntFromObj(interp, objv[j + 1], &q->depth) != TCL_OK)
goto
errorExit;
break
;
}
case
QUAL_STATE: {
if
(Tree_StateFromListObj(tree, STATE_DOMAIN_ITEM, objv[j + 1], q->states,
SFO_NOT_TOGGLE) != TCL_OK)
goto
errorExit;
break
;
}
case
QUAL_TAG: {
if
(tree->itemTagExpr) {
if
(q->exprOK)
TagExpr_Free(&q->expr);
if
(TagExpr_Init(tree, objv[j + 1], &q->expr) != TCL_OK)
return
TCL_ERROR;
q->exprOK = TRUE;
}
else
{
q->tag = Tk_GetUid(Tcl_GetString(objv[j + 1]));
}
break
;
}
case
QUAL_VISIBLE: {
q->visible = 1;
break
;
}
case
QUAL_NOT_VISIBLE: {
q->visible = 0;
break
;
}
}
*argsUsed += qualArgs[qual];
j += qualArgs[qual];
}
return
TCL_OK;
errorExit:
if
(q->exprOK)
TagExpr_Free(&q->expr);
return
TCL_ERROR;
}
static
int
Qualifies(
Qualifiers *q,
TreeItem item
)
{
TreeCtrl *tree = q->tree;
if
(item == NULL)
return
1;
if
((q->visible == 1) && !TreeItem_ReallyVisible(tree, item))
return
0;
else
if
((q->visible == 0) && TreeItem_ReallyVisible(tree, (TreeItem) item))
return
0;
if
(q->states[STATE_OP_OFF] & item->state)
return
0;
if
((q->states[STATE_OP_ON] & item->state) != q->states[STATE_OP_ON])
return
0;
if
(q->exprOK && !TagExpr_Eval(&q->expr, item->tagInfo))
return
0;
if
((q->depth >= 0) && (item->depth + 1 != q->depth))
return
0;
if
((q->tag != NULL) && !TreeItem_HasTag(item, q->tag))
return
0;
return
1;
}
static
void
Qualifiers_Free(
Qualifiers *q
)
{
if
(q->exprOK)
TagExpr_Free(&q->expr);
}
static
void
NotManyMsg(
TreeCtrl *tree,
int
doHeaders
)
{
FormatResult(tree->interp,
"can't specify > 1 %s for this command"
,
doHeaders ?
"header"
:
"item"
);
}
int
TreeItemList_FromObj(
TreeCtrl *tree,
Tcl_Obj *objPtr,
TreeItemList *items,
int
flags
)
{
Tcl_Interp *interp = tree->interp;
int
i, objc, index, listIndex, id;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
Tcl_Obj **objv, *elemPtr;
TreeItem item = NULL;
Qualifiers q;
int
qualArgsTotal;
static
CONST
char
*indexName[] = {
"active"
,
"all"
,
"anchor"
,
"end"
,
"first"
,
"last"
,
"list"
,
"nearest"
,
"range"
,
"rnc"
,
"root"
, (
char
*) NULL
};
enum
indexEnum {
INDEX_ACTIVE, INDEX_ALL, INDEX_ANCHOR, INDEX_END, INDEX_FIRST,
INDEX_LAST, INDEX_LIST, INDEX_NEAREST, INDEX_RANGE, INDEX_RNC,
INDEX_ROOT
};
static
int
indexArgs[] = {
1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1
};
static
int
indexQual[] = {
0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1
};
static
CONST
char
*modifiers[] = {
"above"
,
"ancestors"
,
"below"
,
"bottom"
,
"child"
,
"children"
,
"descendants"
,
"firstchild"
,
"lastchild"
,
"left"
,
"leftmost"
,
"next"
,
"nextsibling"
,
"parent"
,
"prev"
,
"prevsibling"
,
"right"
,
"rightmost"
,
"sibling"
,
"top"
, (
char
*) NULL
};
enum
modEnum {
TMOD_ABOVE, TMOD_ANCESTORS, TMOD_BELOW, TMOD_BOTTOM, TMOD_CHILD,
TMOD_CHILDREN, TMOD_DESCENDANTS, TMOD_FIRSTCHILD, TMOD_LASTCHILD,
TMOD_LEFT, TMOD_LEFTMOST, TMOD_NEXT, TMOD_NEXTSIBLING, TMOD_PARENT,
TMOD_PREV, TMOD_PREVSIBLING, TMOD_RIGHT, TMOD_RIGHTMOST, TMOD_SIBLING,
TMOD_TOP
};
static
int
modArgs[] = {
1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1
};
static
int
modQual[] = {
0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0
};
TreeItemList_Init(tree, items, 0);
Qualifiers_Init(tree, &q);
if
(Tcl_ListObjGetElements(NULL, objPtr, &objc, &objv) != TCL_OK)
goto
baditem;
if
(objc == 0)
goto
baditem;
listIndex = 0;
elemPtr = objv[listIndex];
if
(Tcl_GetIndexFromObj(NULL, elemPtr, indexName, NULL, 0, &index)
== TCL_OK) {
if
(objc - listIndex < indexArgs[index]) {
Tcl_AppendResult(interp,
"missing arguments to \""
,
Tcl_GetString(elemPtr),
"\" keyword"
, NULL);
goto
errorExit;
}
qualArgsTotal = 0;
if
(indexQual[index]) {
if
(Qualifiers_Scan(&q, objc, objv, listIndex + indexArgs[index],
&qualArgsTotal) != TCL_OK) {
goto
errorExit;
}
}
switch
((
enum
indexEnum) index) {
case
INDEX_ACTIVE: {
item = tree->activeItem;
break
;
}
case
INDEX_ALL: {
if
(qualArgsTotal) {
hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
while
(hPtr != NULL) {
item = (TreeItem) Tcl_GetHashValue(hPtr);
if
(Qualifies(&q, item)) {
TreeItemList_Append(items, (TreeItem) item);
}
hPtr = Tcl_NextHashEntry(&search);
}
item = NULL;
}
else
if
(flags & IFO_LIST_ALL) {
hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
while
(hPtr != NULL) {
item = (TreeItem) Tcl_GetHashValue(hPtr);
TreeItemList_Append(items, item);
hPtr = Tcl_NextHashEntry(&search);
}
item = NULL;
}
else
{
item = ITEM_ALL;
}
break
;
}
case
INDEX_ANCHOR: {
item = tree->anchorItem;
break
;
}
case
INDEX_FIRST: {
item = tree->root;
while
(!Qualifies(&q, item))
item = TreeItem_Next(tree, item);
break
;
}
case
INDEX_END:
case
INDEX_LAST: {
item = tree->root;
while
(item->lastChild) {
item = item->lastChild;
}
while
(!Qualifies(&q, item))
item = TreeItem_Prev(tree, item);
break
;
}
case
INDEX_LIST: {
int
listObjc;
Tcl_Obj **listObjv;
int
count;
if
(Tcl_ListObjGetElements(interp, objv[listIndex + 1],
&listObjc, &listObjv) != TCL_OK) {
goto
errorExit;
}
for
(i = 0; i < listObjc; i++) {
TreeItemList item2s;
if
(TreeItemList_FromObj(tree, listObjv[i], &item2s, flags)
!= TCL_OK)
goto
errorExit;
TreeItemList_Concat(items, &item2s);
TreeItemList_Free(&item2s);
}
count = TreeItemList_Count(items);
for
(i = 0; i < count; i++) {
TreeItem item2 = TreeItemList_Nth(items, i);
if
(IS_ALL(item2))
break
;
}
if
(i < count) {
TreeItemList_Free(items);
item = ITEM_ALL;
}
else
item = NULL;
break
;
}
case
INDEX_NEAREST: {
int
x, y;
if
(Tk_GetPixelsFromObj(interp, tree->tkwin,
objv[listIndex + 1], &x) != TCL_OK) {
goto
errorExit;
}
if
(Tk_GetPixelsFromObj(interp, tree->tkwin,
objv[listIndex + 2], &y) != TCL_OK) {
goto
errorExit;
}
item = Tree_ItemUnderPoint(tree, &x, &y, NULL, TRUE);
break
;
}
case
INDEX_RANGE: {
TreeItem itemFirst, itemLast;
if
(TreeItem_FromObj(tree, objv[listIndex + 1], &itemFirst,
IFO_NOT_NULL) != TCL_OK)
goto
errorExit;
if
(TreeItem_FromObj(tree, objv[listIndex + 2], &itemLast,
IFO_NOT_NULL) != TCL_OK)
goto
errorExit;
if
(TreeItem_FirstAndLast(tree, &itemFirst, &itemLast) == 0)
goto
errorExit;
while
(1) {
if
(Qualifies(&q, itemFirst)) {
TreeItemList_Append(items, itemFirst);
}
if
(itemFirst == itemLast)
break
;
itemFirst = TreeItem_Next(tree, itemFirst);
}
item = NULL;
break
;
}
case
INDEX_RNC: {
int
row, col;
if
(Tcl_GetIntFromObj(interp, objv[listIndex + 1], &row) != TCL_OK)
goto
errorExit;
if
(Tcl_GetIntFromObj(interp, objv[listIndex + 2], &col) != TCL_OK)
goto
errorExit;
item = Tree_RNCToItem(tree, row, col);
break
;
}
case
INDEX_ROOT: {
item = tree->root;
break
;
}
}
listIndex += indexArgs[index] + qualArgsTotal;
}
else
{
int
gotId = FALSE;
TagExpr expr;
if
(tree->itemPrefixLen) {
char
*end, *t = Tcl_GetString(elemPtr);
if
(
strncmp
(t, tree->itemPrefix, tree->itemPrefixLen) == 0) {
t += tree->itemPrefixLen;
id =
strtoul
(t, &end, 10);
if
((end != t) && (*end ==
'\0'
))
gotId = TRUE;
}
}
else
if
(Tcl_GetIntFromObj(NULL, elemPtr, &id) == TCL_OK) {
gotId = TRUE;
}
if
(gotId) {
hPtr = Tcl_FindHashEntry(&tree->itemHash, (
char
*) INT2PTR(id));
if
(hPtr != NULL) {
item = (TreeItem) Tcl_GetHashValue(hPtr);
}
else
{
item = NULL;
}
listIndex++;
goto
gotFirstPart;
}
if
(Qualifiers_Scan(&q, objc, objv, listIndex, &qualArgsTotal)
!= TCL_OK) {
goto
errorExit;
}
if
(qualArgsTotal) {
hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
while
(hPtr != NULL) {
item = (TreeItem) Tcl_GetHashValue(hPtr);
if
(Qualifies(&q, item)) {
TreeItemList_Append(items, item);
}
hPtr = Tcl_NextHashEntry(&search);
}
item = NULL;
listIndex += qualArgsTotal;
goto
gotFirstPart;
}
if
(objc > 1) {
if
(Qualifiers_Scan(&q, objc, objv, listIndex + 1,
&qualArgsTotal) != TCL_OK) {
goto
errorExit;
}
}
if
(tree->itemTagExpr) {
if
(TagExpr_Init(tree, elemPtr, &expr) != TCL_OK)
goto
errorExit;
hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
while
(hPtr != NULL) {
item = (TreeItem) Tcl_GetHashValue(hPtr);
if
(TagExpr_Eval(&expr, item->tagInfo) && Qualifies(&q, item)) {
TreeItemList_Append(items, item);
}
hPtr = Tcl_NextHashEntry(&search);
}
TagExpr_Free(&expr);
}
else
{
Tk_Uid tag = Tk_GetUid(Tcl_GetString(elemPtr));
hPtr = Tcl_FirstHashEntry(&tree->itemHash, &search);
while
(hPtr != NULL) {
item = (TreeItem) Tcl_GetHashValue(hPtr);
if
(TreeItem_HasTag(item, tag) && Qualifies(&q, item)) {
TreeItemList_Append(items, item);
}
hPtr = Tcl_NextHashEntry(&search);
}
}
item = NULL;
listIndex += 1 + qualArgsTotal;
}
gotFirstPart:
if
(TreeItemList_Count(items) == 1) {
item = TreeItemList_Nth(items, 0);
items->count = 0;
}
if
(IS_ALL(item) && (tree->itemCount == 1) && !(flags & IFO_NOT_ROOT)) {
item = tree->root;
}
if
((TreeItemList_Count(items) > 1) || IS_ALL(item)) {
if
(listIndex < objc) {
Tcl_AppendResult(interp,
"unexpected arguments after \""
,
(
char
*) NULL);
for
(i = 0; i < listIndex; i++) {
Tcl_AppendResult(interp, Tcl_GetString(objv[i]), (
char
*) NULL);
if
(i != listIndex - 1)
Tcl_AppendResult(interp,
" "
, (
char
*) NULL);
}
Tcl_AppendResult(interp,
"\""
, (
char
*) NULL);
goto
errorExit;
}
}
if
((TreeItemList_Count(items) == 0) && (item == NULL)) {
if
(flags & IFO_NOT_NULL)
goto
noitem;
goto
goodExit;
}
for
(; listIndex < objc;
) {
elemPtr = objv[listIndex];
if
(Tcl_GetIndexFromObj(interp, elemPtr, modifiers,
"modifier"
, 0,
&index) != TCL_OK) {
goto
errorExit;
}
if
(objc - listIndex < modArgs[index]) {
Tcl_AppendResult(interp,
"missing arguments to \""
,
Tcl_GetString(elemPtr),
"\" modifier"
, NULL);
goto
errorExit;
}
qualArgsTotal = 0;
if
(modQual[index]) {
Qualifiers_Free(&q);
Qualifiers_Init(tree, &q);
if
(Qualifiers_Scan(&q, objc, objv, listIndex + modArgs[index],
&qualArgsTotal) != TCL_OK) {
goto
errorExit;
}
}
switch
((
enum
modEnum) index) {
case
TMOD_ABOVE: {
item = Tree_ItemAbove(tree, item);
break
;
}
case
TMOD_ANCESTORS: {
item = item->parent;
while
(item != NULL) {
if
(Qualifies(&q, item)) {
TreeItemList_Append(items, item);
}
item = item->parent;
}
item = NULL;
break
;
}
case
TMOD_BELOW: {
item = Tree_ItemBelow(tree, item);
break
;
}
case
TMOD_BOTTOM: {
item = Tree_ItemBottom(tree, item);
break
;
}
case
TMOD_CHILD: {
int
n, endRelative;
if
(Tree_GetIntForIndex(tree, objv[listIndex + 1], &n,
&endRelative) != TCL_OK) {
goto
errorExit;
}
if
(endRelative) {
item = item->lastChild;
while
(item != NULL) {
if
(Qualifies(&q, item))
if
(n-- <= 0)
break
;
item = item->prevSibling;
}
}
else
{
item = item->firstChild;
while
(item != NULL) {
if
(Qualifies(&q, item))
if
(n-- <= 0)
break
;
item = item->nextSibling;
}
}
break
;
}
case
TMOD_CHILDREN: {
item = item->firstChild;
while
(item != NULL) {
if
(Qualifies(&q, item)) {
TreeItemList_Append(items, item);
}
item = item->nextSibling;
}
item = NULL;
break
;
}
case
TMOD_DESCENDANTS: {
TreeItem last = item;
while
(last->lastChild != NULL)
last = last->lastChild;
item = item->firstChild;
while
(item != NULL) {
if
(Qualifies(&q, item)) {
TreeItemList_Append(items, item);
}
if
(item == last)
break
;
item = TreeItem_Next(tree, item);
}
item = NULL;
break
;
}
case
TMOD_FIRSTCHILD: {
item = item->firstChild;
while
(!Qualifies(&q, item))
item = item->nextSibling;
break
;
}
case
TMOD_LASTCHILD: {
item = item->lastChild;
while
(!Qualifies(&q, item))
item = item->prevSibling;
break
;
}
case
TMOD_LEFT: {
item = Tree_ItemLeft(tree, item);
break
;
}
case
TMOD_LEFTMOST: {
item = Tree_ItemLeftMost(tree, item);
break
;
}
case
TMOD_NEXT: {
item = TreeItem_Next(tree, item);
while
(!Qualifies(&q, item))
item = TreeItem_Next(tree, item);
break
;
}
case
TMOD_NEXTSIBLING: {
item = item->nextSibling;
while
(!Qualifies(&q, item))
item = item->nextSibling;
break
;
}
case
TMOD_PARENT: {
item = item->parent;
break
;
}
case
TMOD_PREV: {
item = TreeItem_Prev(tree, item);
while
(!Qualifies(&q, item))
item = TreeItem_Prev(tree, item);
break
;
}
case
TMOD_PREVSIBLING: {
item = item->prevSibling;
while
(!Qualifies(&q, item))
item = item->prevSibling;
break
;
}
case
TMOD_RIGHT: {
item = Tree_ItemRight(tree, item);
break
;
}
case
TMOD_RIGHTMOST: {
item = Tree_ItemRightMost(tree, item);
break
;
}
case
TMOD_SIBLING: {
int
n, endRelative;
if
(Tree_GetIntForIndex(tree, objv[listIndex + 1], &n,
&endRelative) != TCL_OK) {
goto
errorExit;
}
item = item->parent;
if
(item == NULL)
break
;
if
(endRelative) {
item = item->lastChild;
while
(item != NULL) {
if
(Qualifies(&q, item))
if
(n-- <= 0)
break
;
item = item->prevSibling;
}
}
else
{
item = item->firstChild;
while
(item != NULL) {
if
(Qualifies(&q, item))
if
(n-- <= 0)
break
;
item = item->nextSibling;
}
}
break
;
}
case
TMOD_TOP: {
item = Tree_ItemTop(tree, item);
break
;
}
}
if
((TreeItemList_Count(items) > 1) || IS_ALL(item)) {
int
end = listIndex + modArgs[index] + qualArgsTotal;
if
(end < objc) {
Tcl_AppendResult(interp,
"unexpected arguments after \""
,
(
char
*) NULL);
for
(i = 0; i < end; i++) {
Tcl_AppendResult(interp, Tcl_GetString(objv[i]), (
char
*) NULL);
if
(i != end - 1)
Tcl_AppendResult(interp,
" "
, (
char
*) NULL);
}
Tcl_AppendResult(interp,
"\""
, (
char
*) NULL);
goto
errorExit;
}
}
if
((TreeItemList_Count(items) == 0) && (item == NULL)) {
if
(flags & IFO_NOT_NULL)
goto
noitem;
goto
goodExit;
}
listIndex += modArgs[index] + qualArgsTotal;
}
if
((flags & IFO_NOT_MANY) && (IS_ALL(item) ||
(TreeItemList_Count(items) > 1))) {
NotManyMsg(tree, FALSE);
goto
errorExit;
}
if
(TreeItemList_Count(items)) {
if
(flags & (IFO_NOT_ROOT | IFO_NOT_ORPHAN)) {
int
i;
for
(i = 0; i < TreeItemList_Count(items); i++) {
item = TreeItemList_Nth(items, i);
if
(IS_ROOT(item) && (flags & IFO_NOT_ROOT))
goto
notRoot;
if
((item->parent == NULL) && (flags & IFO_NOT_ORPHAN))
goto
notOrphan;
}
}
}
else
if
(IS_ALL(item)) {
TreeItemList_Append(items, ITEM_ALL);
}
else
{
if
(IS_ROOT(item) && (flags & IFO_NOT_ROOT)) {
notRoot:
FormatResult(interp,
"can't specify \"root\" for this command"
);
goto
errorExit;
}
if
((item->parent == NULL) && (flags & IFO_NOT_ORPHAN)) {
notOrphan:
FormatResult(interp,
"item \"%s\" has no parent"
,
Tcl_GetString(objPtr));
goto
errorExit;
}
TreeItemList_Append(items, item);
}
goodExit:
Qualifiers_Free(&q);
return
TCL_OK;
baditem:
Tcl_AppendResult(interp,
"bad item description \""
, Tcl_GetString(objPtr),
"\""
, NULL);
goto
errorExit;
noitem:
Tcl_AppendResult(interp,
"item \""
, Tcl_GetString(objPtr),
"\" doesn't exist"
, NULL);
errorExit:
Qualifiers_Free(&q);
TreeItemList_Free(items);
return
TCL_ERROR;
}
int
TreeItem_FromObj(
TreeCtrl *tree,
Tcl_Obj *objPtr,
TreeItem *itemPtr,
int
flags
)
{
TreeItemList items;
if
(TreeItemList_FromObj(tree, objPtr, &items, flags | IFO_NOT_MANY) != TCL_OK)
return
TCL_ERROR;
(*itemPtr) = TreeItemList_Nth(&items, 0);
TreeItemList_Free(&items);
return
TCL_OK;
}
TreeItem
TreeItemForEach_Start(
TreeItemList *items,
TreeItemList *item2s,
ItemForEach *iter
)
{
TreeCtrl *tree = items->tree;
TreeItem item, item2 = NULL;
item = TreeItemList_Nth(items, 0);
if
(item2s)
item2 = TreeItemList_Nth(item2s, 0);
iter->tree = tree;
iter->all = FALSE;
iter->error = 0;
iter->items = NULL;
if
(IS_ALL(item) || IS_ALL(item2)) {
Tcl_HashEntry *hPtr = Tcl_FirstHashEntry(&tree->itemHash, &iter->search);
iter->all = TRUE;
return
iter->item = (TreeItem) Tcl_GetHashValue(hPtr);
}
if
(item2 != NULL) {
if
(TreeItem_FirstAndLast(tree, &item, &item2) == 0) {
iter->error = 1;
return
NULL;
}
iter->last = item2;
return
iter->item = item;
}
iter->items = items;
iter->index = 0;
return
iter->item = item;
}
TreeItem
TreeItemForEach_Next(
ItemForEach *iter
)
{
TreeCtrl *tree = iter->tree;
if
(iter->all) {
Tcl_HashEntry *hPtr = Tcl_NextHashEntry(&iter->search);
if
(hPtr == NULL)
return
iter->item = NULL;
return
iter->item = (TreeItem) Tcl_GetHashValue(hPtr);
}
if
(iter->items != NULL) {
if
(iter->index >= TreeItemList_Count(iter->items))
return
iter->item = NULL;
return
iter->item = TreeItemList_Nth(iter->items, ++iter->index);
}
if
(iter->item == iter->last)
return
iter->item = NULL;
return
iter->item = TreeItem_Next(tree, iter->item);
}
static
void
Item_ToggleOpen(
TreeCtrl *tree,
TreeItem item,
int
stateOff,
int
stateOn
)
{
TreeItem_ChangeState(tree, item, stateOff, stateOn);
if
(IS_ROOT(item) && !tree->showRoot)
return
;
#if 0
if
(!TreeItem_ReallyVisible(tree, item))
return
;
Tree_InvalidateItemDInfo(tree, item, NULL);
#endif
if
(item->numChildren > 0) {
tree->updateIndex = 1;
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
TreeColumns_InvalidateWidthOfItems(tree, NULL);
TreeColumns_InvalidateSpans(tree);
}
Tree_EventuallyRedraw(tree);
}
void
TreeItem_OpenClose(
TreeCtrl *tree,
TreeItem item,
int
mode
)
{
int
stateOff = 0, stateOn = 0;
if
(IS_DELETED(item))
return
;
if
(mode == -1) {
if
(item->state & STATE_ITEM_OPEN)
stateOff = STATE_ITEM_OPEN;
else
stateOn = STATE_ITEM_OPEN;
}
else
if
(!mode && (item->state & STATE_ITEM_OPEN))
stateOff = STATE_ITEM_OPEN;
else
if
(mode && !(item->state & STATE_ITEM_OPEN))
stateOn = STATE_ITEM_OPEN;
if
(stateOff != stateOn) {
TreeNotify_OpenClose(tree, item, stateOn, TRUE);
if
(IS_DELETED(item))
return
;
Item_ToggleOpen(tree, item, stateOff, stateOn);
TreeNotify_OpenClose(tree, item, stateOn, FALSE);
}
}
void
TreeItem_Delete(
TreeCtrl *tree,
TreeItem item
)
{
while
(item->numChildren > 0)
TreeItem_Delete(tree, item->firstChild);
if
(item->header != NULL) {
if
(item != tree->headerItems) {
item->prevSibling->nextSibling = item->nextSibling;
if
(item->nextSibling != NULL)
item->nextSibling->prevSibling = item->prevSibling;
}
else
{
tree->headerItems = item->nextSibling;
if
(item->nextSibling != NULL)
item->nextSibling->prevSibling = NULL;
}
item->prevSibling = item->nextSibling = NULL;
}
TreeItem_RemoveFromParent(tree, item);
TreeDisplay_ItemDeleted(tree, item);
TreeGradient_ItemDeleted(tree, item);
TreeTheme_ItemDeleted(tree, item);
if
(item->header != NULL)
Tree_RemoveHeader(tree, item);
else
Tree_RemoveItem(tree, item);
TreeItem_FreeResources(tree, item);
if
(tree->activeItem == item) {
tree->activeItem = tree->root;
TreeItem_ChangeState(tree, tree->activeItem, 0, STATE_ITEM_ACTIVE);
}
if
(tree->anchorItem == item)
tree->anchorItem = tree->root;
if
(tree->debug.enable && tree->debug.data)
Tree_Debug(tree);
}
int
TreeItem_Deleted(
TreeCtrl *tree,
TreeItem item
)
{
return
IS_DELETED(item);
}
int
TreeItem_FirstAndLast(
TreeCtrl *tree,
TreeItem *first,
TreeItem *last
)
{
int
indexFirst, indexLast, index;
if
(TreeItem_RootAncestor(tree, *first) !=
TreeItem_RootAncestor(tree, *last)) {
FormatResult(tree->interp,
"item %s%d and item %s%d don't share a common ancestor"
,
tree->itemPrefix, TreeItem_GetID(tree, *first),
tree->itemPrefix, TreeItem_GetID(tree, *last));
return
0;
}
TreeItem_ToIndex(tree, *first, &indexFirst, NULL);
TreeItem_ToIndex(tree, *last, &indexLast, NULL);
if
(indexFirst > indexLast) {
TreeItem item = *first;
*first = *last;
*last = item;
index = indexFirst;
indexFirst = indexLast;
indexLast = index;
}
return
indexLast - indexFirst + 1;
}
void
TreeItem_ListDescendants(
TreeCtrl *tree,
TreeItem item,
TreeItemList *items
)
{
TreeItem last;
if
(item->firstChild == NULL)
return
;
last = item;
while
(last->lastChild != NULL)
last = last->lastChild;
item = item->firstChild;
while
(1) {
TreeItemList_Append(items, item);
if
(item == last)
break
;
item = TreeItem_Next(tree, item);
}
}
void
TreeItem_UpdateDepth(
TreeCtrl *tree,
TreeItem item
)
{
TreeItem child;
if
(IS_ROOT(item))
return
;
if
(item->parent != NULL)
item->depth = item->parent->depth + 1;
else
item->depth = 0;
child = item->firstChild;
while
(child != NULL) {
TreeItem_UpdateDepth(tree, child);
child = child->nextSibling;
}
}
void
TreeItem_AddToParent(
TreeCtrl *tree,
TreeItem item
)
{
TreeItem last, parent = item->parent;
if
((item->prevSibling != NULL) &&
(item->nextSibling == NULL) &&
tree->showLines && (tree->columnTree != NULL)) {
last = item->prevSibling;
while
(last->lastChild != NULL)
last = last->lastChild;
Tree_InvalidateItemDInfo(tree, tree->columnTree,
item->prevSibling, last);
}
if
(IS_VISIBLE(item) && (parent->flags & ITEM_FLAG_BUTTON_AUTO) &&
tree->showButtons && (tree->columnTree != NULL)) {
Tree_InvalidateItemDInfo(tree, tree->columnTree, parent,
NULL);
}
tree->updateIndex = 1;
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
TreeItem_UpdateDepth(tree, item);
TreeColumns_InvalidateWidthOfItems(tree, NULL);
TreeColumns_InvalidateSpans(tree);
if
(tree->debug.enable && tree->debug.data)
Tree_Debug(tree);
}
static
void
RemoveFromParentAux(
TreeCtrl *tree,
TreeItem item,
int
*index
)
{
TreeItem child;
if
(item->dInfo != NULL)
Tree_InvalidateItemDInfo(tree, NULL, item, NULL);
if
(item->parent != NULL)
item->depth = item->parent->depth + 1;
else
item->depth = 0;
item->index = (*index)++;
item->indexVis = -1;
child = item->firstChild;
while
(child != NULL) {
RemoveFromParentAux(tree, child, index);
child = child->nextSibling;
}
}
void
TreeItem_RemoveFromParent(
TreeCtrl *tree,
TreeItem item
)
{
TreeItem parent = item->parent;
TreeItem last;
int
index = 0;
if
(parent == NULL)
return
;
if
((item->prevSibling != NULL) &&
(item->nextSibling == NULL) &&
tree->showLines && (tree->columnTree != NULL)) {
last = item->prevSibling;
while
(last->lastChild != NULL)
last = last->lastChild;
Tree_InvalidateItemDInfo(tree, tree->columnTree,
item->prevSibling, last);
}
if
(IS_VISIBLE(item) && (parent->flags & ITEM_FLAG_BUTTON_AUTO) &&
tree->showButtons && (tree->columnTree != NULL)) {
Tree_InvalidateItemDInfo(tree, tree->columnTree, parent,
NULL);
}
tree->updateIndex = 1;
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
if
(item->prevSibling)
item->prevSibling->nextSibling = item->nextSibling;
if
(item->nextSibling)
item->nextSibling->prevSibling = item->prevSibling;
if
(parent->firstChild == item) {
parent->firstChild = item->nextSibling;
if
(!parent->firstChild)
parent->lastChild = NULL;
}
if
(parent->lastChild == item)
parent->lastChild = item->prevSibling;
item->prevSibling = item->nextSibling = NULL;
item->parent = NULL;
parent->numChildren--;
RemoveFromParentAux(tree, item, &index);
}
void
TreeItem_RemoveColumns(
TreeCtrl *tree,
TreeItem item,
int
first,
int
last
)
{
TreeItemColumn column = item->columns;
TreeItemColumn prev = NULL, next = NULL;
int
i = 0;
while
(column != NULL) {
next = column->next;
if
(i == first - 1)
prev = column;
else
if
(i >= first)
Column_FreeResources(tree, column);
if
(i == last)
break
;
++i;
column = next;
}
if
(prev != NULL)
prev->next = next;
else
if
(first == 0)
item->columns = next;
}
void
TreeItem_RemoveAllColumns(
TreeCtrl *tree,
TreeItem item
)
{
TreeItemColumn column = item->columns;
while
(column != NULL) {
TreeItemColumn next = column->next;
if
(item->header != NULL && next == NULL) {
item->columns = column;
return
;
}
Column_FreeResources(tree, column);
column = next;
}
item->columns = NULL;
}
static
TreeItemColumn
Item_CreateColumn(
TreeCtrl *tree,
TreeItem item,
int
columnIndex,
int
*isNew
)
{
TreeItemColumn column;
int
i;
#ifdef TREECTRL_DEBUG
if
(columnIndex < 0 || columnIndex >= tree->columnCount + (item->header ? 1 : 0)) {
panic(
"Item_CreateColumn with index %d, must be from 0-%d"
, columnIndex, tree->columnCount + (item->header ? 1 : 0) - 1);
}
#endif
if
(isNew != NULL) (*isNew) = FALSE;
column = item->columns;
if
(column == NULL) {
column = Column_Alloc(tree, item);
item->columns = column;
if
(isNew != NULL) (*isNew) = TRUE;
}
for
(i = 0; i < columnIndex; i++) {
if
(column->next == NULL) {
column->next = Column_Alloc(tree, item);
if
(isNew != NULL) (*isNew) = TRUE;
}
column = column->next;
}
if
(item->header != NULL && columnIndex == TreeColumn_Index(tree->columnTail) + 1) {
TreeItem_MoveColumn(tree, item, columnIndex, columnIndex - 1);
}
return
column;
}
void
TreeItem_MoveColumn(
TreeCtrl *tree,
TreeItem item,
int
columnIndex,
int
beforeIndex
)
{
TreeItemColumn before = NULL, move = NULL;
TreeItemColumn prevM = NULL, prevB = NULL;
TreeItemColumn last = NULL, prev, walk;
int
index = 0;
prev = NULL;
walk = item->columns;
while
(walk != NULL) {
if
(index == columnIndex) {
prevM = prev;
move = walk;
}
if
(index == beforeIndex) {
prevB = prev;
before = walk;
}
prev = walk;
if
(walk->next == NULL)
last = walk;
index++;
walk = walk->next;
}
if
(move == NULL && before == NULL)
return
;
if
(move == NULL)
move = Column_Alloc(tree, item);
else
{
if
(before == NULL) {
prevB = Item_CreateColumn(tree, item, beforeIndex - 1, NULL);
last = prevB;
}
if
(prevM == NULL)
item->columns = move->next;
else
prevM->next = move->next;
}
if
(before == NULL) {
last->next = move;
move->next = NULL;
}
else
{
if
(prevB == NULL)
item->columns = move;
else
prevB->next = move;
move->next = before;
}
}
void
TreeItem_FreeResources(
TreeCtrl *tree,
TreeItem item
)
{
TreeItemColumn column;
column = item->columns;
while
(column != NULL)
column = Column_FreeResources(tree, column);
if
(item->dInfo != NULL)
Tree_FreeItemDInfo(tree, item, NULL);
if
(item->rInfo != NULL)
Tree_FreeItemRInfo(tree, item);
if
(item->spans != NULL)
ckfree((
char
*) item->spans);
if
(item->header != NULL)
TreeHeader_FreeResources(item->header);
Tk_FreeConfigOptions((
char
*) item, tree->itemOptionTable, tree->tkwin);
TreeItemList_Append(&tree->preserveItemList, item);
}
void
TreeItem_Release(
TreeCtrl *tree,
TreeItem item
)
{
#ifdef ALLOC_HAX
TreeAlloc_Free(tree->allocData, ItemUid, (
char
*) item,
sizeof
(TreeItem_));
#else
WFREE(item, TreeItem_);
#endif
}
static
int
Item_HeightOfStyles(
TreeCtrl *tree,
TreeItem item
)
{
TreeItemColumn column = item->columns;
int
*spans = TreeItem_GetSpans(tree, item);
int
tailOK = item->header != NULL;
TreeColumn treeColumn = Tree_FirstColumn(tree, -1, tailOK);
StyleDrawArgs drawArgs;
int
height = 0, hasHeaderElem = FALSE;
drawArgs.tree = tree;
if
(spans == NULL) {
while
(column != NULL) {
if
(TreeColumn_Visible(treeColumn) && (column->style != NULL)) {
drawArgs.state = item->state | column->cstate;
drawArgs.style = column->style;
drawArgs.indent = TreeItem_Indent(tree, treeColumn, item);
if
(treeColumn == tree->columnTail) {
drawArgs.width = -1;
}
else
{
drawArgs.width = TreeColumn_UseWidth(treeColumn);
if
(item->header != NULL)
drawArgs.width += drawArgs.indent;
}
height = MAX(height, TreeStyle_UseHeight(&drawArgs));
if
(!hasHeaderElem && (item->header != NULL) &&
TreeStyle_HasHeaderElement(tree, column->style))
hasHeaderElem = TRUE;
}
treeColumn = Tree_ColumnToTheRight(treeColumn, FALSE, tailOK);
column = column->next;
}
}
else
{
while
(column != NULL) {
if
(TreeColumn_Visible(treeColumn)) {
int
columnIndex = TreeColumn_Index(treeColumn);
int
columnIndex2 = columnIndex;
TreeColumn treeColumn2 = treeColumn;
drawArgs.width = 0;
#if defined(TREECTRL_DEBUG)
if
(TreeColumn_Index(treeColumn) != columnIndex) BreakIntoDebugger();
if
(TreeItemColumn_Index(tree, item, column) != columnIndex) BreakIntoDebugger();
if
(spans[columnIndex] != columnIndex) BreakIntoDebugger();
#endif
while
(spans[columnIndex2] == columnIndex) {
if
(!TreeColumn_Visible(treeColumn2)) {
}
else
if
(treeColumn2 == tree->columnTail) {
drawArgs.width = -1;
}
else
{
drawArgs.width += TreeColumn_UseWidth(treeColumn2);
}
treeColumn2 = Tree_ColumnToTheRight(treeColumn2, FALSE, tailOK);
if
(treeColumn2 == NULL)
break
;
columnIndex2++;
}
if
(column->style != NULL) {
drawArgs.indent = TreeItem_Indent(tree, treeColumn, item);
if
(item->header != NULL)
drawArgs.width += drawArgs.indent;
drawArgs.state = item->state | column->cstate;
drawArgs.style = column->style;
height = MAX(height, TreeStyle_UseHeight(&drawArgs));
if
(!hasHeaderElem && (item->header != NULL) &&
TreeStyle_HasHeaderElement(tree, column->style))
hasHeaderElem = TRUE;
}
treeColumn = treeColumn2;
if
(treeColumn == NULL)
break
;
while
((column != NULL) && (columnIndex < columnIndex2)) {
column = column->next;
columnIndex++;
}
continue
;
}
treeColumn = Tree_ColumnToTheRight(treeColumn, FALSE, tailOK);
column = column->next;
}
}
if
(hasHeaderElem && tree->useTheme) {
if
(tree->themeHeaderHeight > 0)
return
tree->themeHeaderHeight;
}
return
height;
}
int
TreeItem_Height(
TreeCtrl *tree,
TreeItem item
)
{
int
buttonHeight = 0;
int
useHeight;
if
(!TreeItem_ReallyVisible(tree, item))
return
0;
if
(item->header != NULL) {
if
(item->fixedHeight > 0)
return
item->fixedHeight;
return
Item_HeightOfStyles(tree, item);
}
useHeight = Item_HeightOfStyles(tree, item);
if
(TreeItem_HasButton(tree, item)) {
buttonHeight = Tree_ButtonHeight(tree, item->state);
}
if
(item->fixedHeight > 0)
return
MAX(item->fixedHeight, buttonHeight);
if
(tree->itemHeight > 0)
return
MAX(tree->itemHeight, buttonHeight);
if
(tree->minItemHeight > 0)
useHeight = MAX(useHeight, tree->minItemHeight);
return
MAX(useHeight, buttonHeight);
}
void
TreeItem_InvalidateHeight(
TreeCtrl *tree,
TreeItem item
)
{
}
TreeItemColumn
TreeItem_FindColumn(
TreeCtrl *tree,
TreeItem item,
int
columnIndex
)
{
TreeItemColumn column;
int
i = 0;
column = item->columns;
if
(!column)
return
NULL;
while
(column != NULL && i < columnIndex) {
column = column->next;
i++;
}
return
column;
}
int
TreeItem_ColumnFromObj(
TreeCtrl *tree,
TreeItem item,
Tcl_Obj *obj,
TreeItemColumn *columnPtr,
TreeColumn *treeColumnPtr,
int
*indexPtr,
int
flags
)
{
TreeColumn treeColumn;
int
columnIndex;
if
(TreeColumn_FromObj(tree, obj, &treeColumn, flags) != TCL_OK)
return
TCL_ERROR;
columnIndex = TreeColumn_Index(treeColumn);
(*columnPtr) = TreeItem_FindColumn(tree, item, columnIndex);
if
(treeColumnPtr != NULL)
(*treeColumnPtr) = treeColumn;
if
(indexPtr != NULL)
(*indexPtr) = columnIndex;
return
TCL_OK;
}
int
TreeItem_Indent(
TreeCtrl *tree,
TreeColumn treeColumn,
TreeItem item
)
{
int
depth;
if
(item->header != NULL) {
if
((TreeColumn_Lock(treeColumn) == COLUMN_LOCK_NONE) &&
(TreeColumn_VisIndex(treeColumn) == 0)) {
return
tree->canvasPadX[PAD_TOP_LEFT];
}
return
0;
}
if
(treeColumn != tree->columnTree)
return
0;
if
(IS_ROOT(item))
return
(tree->showRoot && tree->showButtons && tree->showRootButton)
? tree->useIndent : 0;
Tree_UpdateItemIndex(tree);
depth = item->depth;
if
(tree->showRoot)
{
depth += 1;
if
(tree->showButtons && tree->showRootButton)
depth += 1;
}
else
if
(tree->showButtons && tree->showRootChildButtons)
depth += 1;
else
if
(tree->showLines && tree->showRootLines)
depth += 1;
return
tree->useIndent * depth;
}
static
void
ItemDrawBackground(
TreeCtrl *tree,
TreeColumn treeColumn,
TreeItem item,
TreeItemColumn column,
TreeDrawable td,
int
x,
int
y,
int
width,
int
height,
int
index
)
{
TreeColor *tc;
TreeClip clip, *clipPtr = &clip;
TreeRectangle tr;
#if USE_ITEM_PIXMAP == 0
clip.type = TREE_CLIP_AREA;
switch
(TreeColumn_Lock(treeColumn)) {
case
COLUMN_LOCK_LEFT:
clip.area = TREE_AREA_LEFT;
break
;
case
COLUMN_LOCK_NONE:
clip.area = TREE_AREA_CONTENT;
break
;
case
COLUMN_LOCK_RIGHT:
clip.area = TREE_AREA_RIGHT;
break
;
}
#else
clipPtr = NULL;
#endif
TreeRect_SetXYWH(tr, x, y, width, height);
if
(!Tree_IsBgImageOpaque(tree)) {
tc = TreeColumn_BackgroundColor(treeColumn, index);
if
(tc != NULL) {
TreeRectangle trBrush;
TreeColor_GetBrushBounds(tree, tc, tr,
tree->drawableXOrigin, tree->drawableYOrigin,
treeColumn, item, &trBrush);
if
(!TreeColor_IsOpaque(tree, tc)
|| (trBrush.width <= 0) || (trBrush.height <= 0)) {
GC gc = Tk_3DBorderGC(tree->tkwin, tree->border, TK_3D_FLAT_GC);
Tree_FillRectangle(tree, td, clipPtr, gc, tr);
}
TreeColor_FillRect(tree, td, clipPtr, tc, trBrush, tr);
}
else
{
GC gc = Tk_3DBorderGC(tree->tkwin, tree->border, TK_3D_FLAT_GC);
Tree_FillRectangle(tree, td, clipPtr, gc, tr);
}
}
if
(tree->backgroundImage != NULL) {
Tree_DrawBgImage(tree, td, tr, tree->drawableXOrigin,
tree->drawableYOrigin);
}
}
void
TreeItem_SpansInvalidate(
TreeCtrl *tree,
TreeItem item
)
{
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
int
count = 0;
if
(item == NULL) {
hPtr = Tcl_FirstHashEntry(&tree->itemSpansHash, &search);
while
(hPtr != NULL) {
item = (TreeItem) Tcl_GetHashKey(&tree->itemSpansHash, hPtr);
item->flags &= ~ITEM_FLAG_SPANS_VALID;
count++;
hPtr = Tcl_NextHashEntry(&search);
}
if
(count) {
Tcl_DeleteHashTable(&tree->itemSpansHash);
Tcl_InitHashTable(&tree->itemSpansHash, TCL_ONE_WORD_KEYS);
}
}
else
if
(item->flags & ITEM_FLAG_SPANS_VALID) {
hPtr = Tcl_FindHashEntry(&tree->itemSpansHash, (
char
*) item);
Tcl_DeleteHashEntry(hPtr);
item->flags &= ~ITEM_FLAG_SPANS_VALID;
count++;
}
if
(count && tree->debug.enable && tree->debug.span)
dbwin(
"TreeItem_SpansInvalidate forgot %d items\n"
, count);
TreeColumns_InvalidateSpans(tree);
}
int
TreeItem_SpansRedo(
TreeCtrl *tree,
TreeItem item
)
{
TreeColumn treeColumn = tree->columns;
TreeItemColumn itemColumn = item->columns;
int
columnCount = tree->columnCount + (item->header ? 1 : 0);
int
columnIndex = 0, spanner = 0, span = 1, simple = TRUE;
int
lock = TreeColumn_Lock(treeColumn);
if
(tree->debug.enable && tree->debug.span)
dbwin(
"TreeItem_SpansRedo %s %d\n"
, item->header ?
"header"
:
"item"
,
item->id);
if
(item->spans == NULL) {
item->spans = (
int
*) ckalloc(
sizeof
(
int
) * columnCount);
item->spanAlloc = columnCount;
}
else
if
(item->spanAlloc < columnCount) {
item->spans = (
int
*) ckrealloc((
char
*) item->spans,
sizeof
(
int
) * columnCount);
item->spanAlloc = columnCount;
}
while
(treeColumn != NULL) {
if
(TreeColumn_Lock(treeColumn) != lock) {
lock = TreeColumn_Lock(treeColumn);
span = 1;
}
if
(--span == 0) {
if
(TreeColumn_Visible(treeColumn))
span = itemColumn ? itemColumn->span : 1;
else
span = 1;
spanner = columnIndex;
}
if
((itemColumn != NULL) && (itemColumn->span > 1))
simple = FALSE;
item->spans[columnIndex] = spanner;
columnIndex++;
treeColumn = TreeColumn_Next(treeColumn);
if
(itemColumn != NULL)
itemColumn = itemColumn->next;
}
if
(item->header != NULL) {
item->spans[columnCount - 1] = columnCount - 1;
}
return
simple;
}
void
TreeItem_SpansRedoIfNeeded(
TreeCtrl *tree,
TreeItem item
)
{
if
(item->flags & ITEM_FLAG_SPANS_SIMPLE)
return
;
if
(item->flags & ITEM_FLAG_SPANS_VALID)
return
;
if
(TreeItem_SpansRedo(tree, item)) {
item->flags |= ITEM_FLAG_SPANS_SIMPLE;
}
else
{
int
isNew;
Tcl_HashEntry *hPtr;
hPtr = Tcl_CreateHashEntry(&tree->itemSpansHash, (
char
*) item, &isNew);
Tcl_SetHashValue(hPtr, (ClientData) item);
item->flags |= ITEM_FLAG_SPANS_VALID;
}
}
int
*
TreeItem_GetSpans(
TreeCtrl *tree,
TreeItem item
)
{
TreeItem_SpansRedoIfNeeded(tree, item);
if
(item->flags & ITEM_FLAG_SPANS_SIMPLE)
return
NULL;
return
item->spans;
}
typedef
struct
SpanInfo {
TreeColumn treeColumn;
TreeItemColumn itemColumn;
int
span;
int
width;
int
visIndex;
int
isDragColumn;
} SpanInfo;
typedef
struct
ColumnColumn {
TreeColumn treeColumn;
TreeItemColumn itemColumn;
int
isDragColumn;
} ColumnColumn;
typedef
struct
SpanInfoStack SpanInfoStack;
struct
SpanInfoStack
{
int
spanCount;
SpanInfo *spans;
int
columnCount;
ColumnColumn *columns;
int
inUse;
SpanInfoStack *next;
};
static
int
Item_GetSpans(
TreeCtrl *tree,
TreeItem item,
TreeColumn firstColumn,
TreeColumn lastColumn,
int
columnCount,
SpanInfo spans[],
#define WALKSPAN_IGNORE_DND 0 /* Ignore header drag-and-drop positions. */
#define WALKSPAN_ONLY_DRAGGED 0x01 /* Calculate the bounds of dragged headers
* only considering -imageoffset. */
#define WALKSPAN_DRAG_ORDER 0x02 /* Calculate the bounds of all headers in
* their current drag positions. */
#define WALKSPAN_IGNORE_DRAGGED 0x04 /* Don't call the callback routine for
* dragged headers. */
int
dragPosition
)
{
SpanInfoStack *siStack = tree->itemSpanPriv;
ColumnColumn *columns = siStack->columns;
TreeColumn treeColumn = firstColumn;
int
columnIndex = TreeColumn_Index(firstColumn);
TreeItemColumn column = TreeItem_FindColumn(tree, item, columnIndex);
int
spanCount = 0, span = 1;
SpanInfo *spanPtr = NULL;
int
i, isDragColumn;
if
((item->header == NULL) && (dragPosition & WALKSPAN_ONLY_DRAGGED))
return
0;
if
((columns == NULL) || (siStack->columnCount < tree->columnCount + 1)) {
columns = (ColumnColumn *) ckrealloc((
char
*) columns,
sizeof
(ColumnColumn) * (tree->columnCount + 1));
siStack->columnCount = tree->columnCount + 1;
siStack->columns = columns;
}
#ifdef TREECTRL_DEBUG
for
(i = 0; i < tree->columnCount + 1; i++) {
columns[i].treeColumn = NULL;
columns[i].itemColumn = NULL;
}
#endif
columnCount = 0;
while
(treeColumn != NULL) {
if
(TreeColumn_Lock(treeColumn) != TreeColumn_Lock(firstColumn))
break
;
columnIndex = columnCount;
isDragColumn = 0;
if
((item->header != NULL) && (dragPosition != WALKSPAN_IGNORE_DND)) {
if
(dragPosition & WALKSPAN_DRAG_ORDER) {
isDragColumn = TreeHeader_IsDraggedColumn(item->header,
treeColumn);
columnIndex = TreeHeader_ColumnDragOrder(item->header,
treeColumn, columnIndex);
}
if
(dragPosition & WALKSPAN_ONLY_DRAGGED)
isDragColumn = 1;
}
#ifdef TREECTRL_DEBUG
if
(columnIndex < 0 || columnIndex >= siStack->columnCount) panic(
"Item_GetSpans columnIndex %d columnCount %d"
, columnIndex, siStack->columnCount);
#endif
columns[columnIndex].treeColumn = treeColumn;
columns[columnIndex].itemColumn = column;
columns[columnIndex].isDragColumn = isDragColumn;
columnCount++;
if
(treeColumn == lastColumn)
break
;
treeColumn = Tree_ColumnToTheRight(treeColumn, TRUE,
item->header != NULL);
if
(column != NULL)
column = column->next;
if
(treeColumn == tree->columnTail) {
while
(column != NULL && column->next != NULL)
column = column->next;
}
}
isDragColumn = columns[0].isDragColumn;
for
(i = 0; i < columnCount; i++) {
treeColumn = columns[i].treeColumn;
column = columns[i].itemColumn;
if
(isDragColumn != columns[i].isDragColumn)
span = 1;
isDragColumn = columns[i].isDragColumn;
if
(treeColumn == tree->columnTail) {
span = 1;
}
if
(--span == 0) {
if
((treeColumn == tree->columnTail) || TreeColumn_Visible(treeColumn)) {
span = column ? column->span : 1;
if
(spanPtr == NULL)
spanPtr = spans;
else
spanPtr++;
spanPtr->treeColumn = treeColumn;
spanPtr->itemColumn = column;
spanPtr->span = 0;
spanPtr->width = 0;
if
(!(dragPosition & WALKSPAN_ONLY_DRAGGED) &&
(item->header != NULL) &&
(spanCount == 0) &&
(TreeColumn_Lock(treeColumn) == COLUMN_LOCK_NONE)) {
spanPtr->width += tree->canvasPadX[PAD_TOP_LEFT];
}
spanPtr->visIndex = spanCount;
spanPtr->isDragColumn = isDragColumn;
spanCount++;
}
else
{
span = 1;
continue
;
}
}
spanPtr->span++;
if
(treeColumn == tree->columnTail) {
spanPtr->width = 100;
}
else
{
spanPtr->width += TreeColumn_UseWidth(treeColumn);
}
}
return
spanCount;
}
typedef
int
(*TreeItemWalkSpansProc)(
TreeCtrl *tree,
TreeItem item,
SpanInfo *spanPtr,
StyleDrawArgs *drawArgs,
ClientData clientData
);
static
void
TreeItem_WalkSpans(
TreeCtrl *tree,
TreeItem item,
int
lock,
int
x,
int
y,
int
width,
int
height,
int
dragPosition,
TreeItemWalkSpansProc proc,
ClientData clientData
)
{
SpanInfoStack *siStack = tree->itemSpanPriv;
int
columnWidth, totalWidth;
TreeItemColumn itemColumn;
StyleDrawArgs drawArgs;
TreeColumn treeColumn = tree->columnLockNone, treeColumnLast = NULL;
int
spanCount, spanIndex, columnCount = tree->columnCountVis;
SpanInfo *spans;
int
area = TREE_AREA_CONTENT;
switch
(lock) {
case
COLUMN_LOCK_LEFT:
treeColumn = tree->columnLockLeft;
columnCount = tree->columnCountVisLeft;
area = TREE_AREA_LEFT;
break
;
case
COLUMN_LOCK_NONE:
break
;
case
COLUMN_LOCK_RIGHT:
treeColumn = tree->columnLockRight;
columnCount = tree->columnCountVisRight;
area = TREE_AREA_RIGHT;
break
;
}
if
(item->header != NULL) {
switch
(lock) {
case
COLUMN_LOCK_LEFT:
area = TREE_AREA_HEADER_LEFT;
break
;
case
COLUMN_LOCK_NONE:
area = TREE_AREA_HEADER_NONE;
if
(treeColumn == NULL)
treeColumn = tree->columnTail;
columnCount += 1;
break
;
case
COLUMN_LOCK_RIGHT:
area = TREE_AREA_HEADER_RIGHT;
break
;
}
if
(dragPosition & WALKSPAN_ONLY_DRAGGED) {
columnCount = TreeHeader_GetDraggedColumns(item->header, lock,
&treeColumn, &treeColumnLast);
if
(columnCount == 0)
return
;
}
}
if
(columnCount <= 0)
return
;
if
(!Tree_AreaBbox(tree, area, &drawArgs.bounds)) {
TreeRect_SetXYWH(drawArgs.bounds, 0, 0, 0, 0);
}
if
(siStack == NULL) {
siStack = (SpanInfoStack *) ckalloc(
sizeof
(SpanInfoStack));
memset
(siStack,
'\0'
,
sizeof
(SpanInfoStack));
tree->itemSpanPriv = siStack;
}
while
(siStack->inUse) {
if
(siStack->next == NULL) {
siStack->next = (SpanInfoStack *) ckalloc(
sizeof
(SpanInfoStack));
memset
(siStack->next,
'\0'
,
sizeof
(SpanInfoStack));
siStack = siStack->next;
break
;
}
siStack = siStack->next;
}
if
(siStack->spanCount < columnCount) {
siStack->spans = (SpanInfo *) ckrealloc((
char
*) siStack->spans,
sizeof
(SpanInfo) * columnCount);
siStack->spanCount = columnCount;
}
spans = siStack->spans;
spanCount = Item_GetSpans(tree, item, treeColumn, treeColumnLast,
columnCount, spans, dragPosition);
if
(spanCount <= 0)
return
;
#ifdef TREECTRL_DEBUG
if
(siStack->inUse) panic(
"TreeItem_WalkSpans stack is in use"
);
#endif
siStack->inUse = 1;
drawArgs.tree = tree;
drawArgs.item = item;
drawArgs.td.drawable = None;
totalWidth = 0;
if
(dragPosition & WALKSPAN_ONLY_DRAGGED) {
#ifdef TREECTRL_DEBUG
if
(item->header == NULL) panic(
"TreeItem_WalkSpans header == NULL"
);
#endif
treeColumn = spans[0].treeColumn;
totalWidth = TreeColumn_Offset(treeColumn);
}
for
(spanIndex = 0; spanIndex < spanCount; spanIndex++) {
treeColumn = spans[spanIndex].treeColumn;
itemColumn = spans[spanIndex].itemColumn;
if
(treeColumn == tree->columnTail) {
spans[spanIndex].width = MAX(0, MAX(Tree_ContentWidth(tree),
Tree_FakeCanvasWidth(tree)) - totalWidth) + tree->tailExtend;
}
if
(item->header != NULL) {
columnWidth = spans[spanIndex].width;
}
else
if
((tree->columnCountVis == 1) && (treeColumn == tree->columnVis)) {
columnWidth = width;
}
else
{
columnWidth = spans[spanIndex].width;
}
if
(columnWidth <= 0)
continue
;
if
((dragPosition & WALKSPAN_IGNORE_DRAGGED) &&
spans[spanIndex].isDragColumn)
goto
next;
if
(itemColumn != NULL) {
drawArgs.state = item->state | itemColumn->cstate;
drawArgs.style = itemColumn->style;
}
else
{
drawArgs.state = item->state;
drawArgs.style = NULL;
}
if
((dragPosition & WALKSPAN_DRAG_ORDER) && (item->header != NULL)) {
if
((spanIndex == 0) && (TreeColumn_Lock(treeColumn) == COLUMN_LOCK_NONE))
drawArgs.indent = tree->canvasPadX[PAD_TOP_LEFT];
else
drawArgs.indent = 0;
}
else
drawArgs.indent = TreeItem_Indent(tree, treeColumn, item);
drawArgs.x = x + totalWidth;
if
(dragPosition & WALKSPAN_ONLY_DRAGGED) {
#ifdef TREECTRL_DEBUG
if
(item->header == NULL) panic(
"TreeItem_WalkSpans header == NULL"
);
#endif
drawArgs.x += tree->columnDrag.offset;
drawArgs.indent = 0;
}
drawArgs.y = y;
drawArgs.width = columnWidth;
drawArgs.height = height;
drawArgs.spanIndex = spanIndex;
if
(item->header != NULL)
drawArgs.justify = TreeHeaderColumn_Justify(item->header,
itemColumn->headerColumn);
else
drawArgs.justify = TreeColumn_ItemJustify(treeColumn);
drawArgs.column = treeColumn;
if
((*proc)(tree, item, &spans[spanIndex], &drawArgs, clientData))
break
;
next:
totalWidth += columnWidth;
}
siStack->inUse = 0;
}
static
int
SpanWalkProc_Draw(
TreeCtrl *tree,
TreeItem item,
SpanInfo *spanPtr,
StyleDrawArgs *drawArgs,
ClientData clientData
)
{
TreeColumn treeColumn = spanPtr->treeColumn;
TreeItemColumn itemColumn = spanPtr->itemColumn;
#if COLUMNGRID == 1
TreeColor *leftColor, *rightColor;
int
leftWidth, rightWidth;
#endif
int
i, x;
struct
{
TreeDrawable td;
int
minX;
int
maxX;
int
index;
int
dragPosition;
} *data = clientData;
if
((drawArgs->x >= data->maxX) ||
(drawArgs->x + drawArgs->width <= data->minX))
return
0;
drawArgs->td = data->td;
if
(item->header != NULL) {
TreeHeaderColumn_Draw(item->header,
itemColumn ? itemColumn->headerColumn : NULL,
spanPtr->visIndex, drawArgs, data->dragPosition);
return
drawArgs->x + drawArgs->width >= data->maxX;
}
if
(spanPtr->span == 1) {
ItemDrawBackground(tree, treeColumn, item, itemColumn,
drawArgs->td, drawArgs->x, drawArgs->y,
drawArgs->width, drawArgs->height, data->index);
}
else
{
x = drawArgs->x;
for
(i = 0; i < spanPtr->span; i++) {
int
columnWidth = TreeColumn_UseWidth(treeColumn);
if
((columnWidth > 0) && (x < data->maxX) &&
(x + columnWidth > data->minX)) {
ItemDrawBackground(tree, treeColumn, item, itemColumn,
drawArgs->td, x, drawArgs->y,
columnWidth, drawArgs->height, data->index);
}
x += columnWidth;
treeColumn = TreeColumn_Next(treeColumn);
}
}
if
(drawArgs->style != NULL) {
StyleDrawArgs drawArgsCopy = *drawArgs;
TreeStyle_Draw(&drawArgsCopy);
}
#if COLUMNGRID == 1
if
(TreeColumn_GridColors(spanPtr->treeColumn, &leftColor, &rightColor,
&leftWidth, &rightWidth) != 0) {
TreeRectangle tr, trBrush;
if
(leftColor != NULL && leftWidth > 0) {
TreeRect_SetXYWH(tr, drawArgs->x, drawArgs->y, leftWidth,
drawArgs->height);
TreeColor_GetBrushBounds(tree, leftColor, tr,
tree->drawableXOrigin, tree->drawableYOrigin,
spanPtr->treeColumn, item, &trBrush);
TreeColor_FillRect(tree, data->td, NULL, leftColor, trBrush, tr);
}
if
(rightColor != NULL && rightWidth > 0) {
TreeRect_SetXYWH(tr, drawArgs->x + drawArgs->width - rightWidth,
drawArgs->y, rightWidth, drawArgs->height);
TreeColor_GetBrushBounds(tree, rightColor, tr,
tree->drawableXOrigin, tree->drawableYOrigin,
spanPtr->treeColumn, item, &trBrush);
TreeColor_FillRect(tree, data->td, NULL, rightColor, trBrush, tr);
}
}
#endif
if
(spanPtr->treeColumn == tree->columnTree) {
if
(tree->showLines)
TreeItem_DrawLines(tree, item, drawArgs->x, drawArgs->y,
drawArgs->width, drawArgs->height, data->td,
drawArgs->style);
if
(tree->showButtons)
TreeItem_DrawButton(tree, item, drawArgs->x, drawArgs->y,
drawArgs->width, drawArgs->height, data->td,
drawArgs->style);
}
return
drawArgs->x + drawArgs->width >= data->maxX;
}
void
TreeItem_Draw(
TreeCtrl *tree,
TreeItem item,
int
lock,
int
x,
int
y,
int
width,
int
height,
TreeDrawable td,
int
minX,
int
maxX,
int
index
)
{
struct
{
TreeDrawable td;
int
minX;
int
maxX;
int
index;
int
dragPosition;
} clientData;
clientData.td = td;
clientData.minX = minX;
clientData.maxX = maxX;
clientData.index = index;
clientData.dragPosition = FALSE;
TreeItem_WalkSpans(tree, item, lock,
x, y, width, height,
WALKSPAN_DRAG_ORDER,
SpanWalkProc_Draw, (ClientData) &clientData);
if
(item->header != NULL) {
clientData.dragPosition = TRUE;
TreeItem_WalkSpans(tree, item, lock,
x, y, width, height,
WALKSPAN_ONLY_DRAGGED,
SpanWalkProc_Draw, (ClientData) &clientData);
}
}
void
TreeItem_DrawLines(
TreeCtrl *tree,
TreeItem item,
int
x,
int
y,
int
width,
int
height,
TreeDrawable td,
TreeStyle style
)
{
TreeItem parent, walk;
int
buttonY = -1;
int
indent, left, lineLeft, lineTop;
int
hasPrev, hasNext;
int
i, vert = 0;
indent = TreeItem_Indent(tree, tree->columnTree, item);
if
(style != NULL)
buttonY = TreeStyle_GetButtonY(tree, style);
left = x
+ indent - tree->useIndent;
lineLeft = left + (tree->useIndent - tree->lineThickness) / 2;
if
(buttonY < 0)
lineTop = y + (height - tree->lineThickness) / 2;
else
lineTop = y + buttonY + (tree->buttonHeightMax - tree->lineThickness) / 2;
walk = item->prevSibling;
while
((walk != NULL) && !IS_VISIBLE(walk))
walk = walk->prevSibling;
hasPrev = (walk != NULL);
if
((item->parent != NULL) && (!IS_ROOT(item->parent) || tree->showRoot))
hasPrev = TRUE;
walk = item->nextSibling;
while
((walk != NULL) && !IS_VISIBLE(walk))
walk = walk->nextSibling;
hasNext = (walk != NULL);
if
((item->parent != NULL) && IS_ROOT(item->parent) && !tree->showRootLines)
hasPrev = hasNext = FALSE;
if
(hasPrev || hasNext) {
int
top = y, bottom = y + height;
if
(!hasPrev)
top = lineTop;
if
(!hasNext)
bottom = lineTop + tree->lineThickness;
if
(tree->lineStyle == LINE_STYLE_DOT) {
for
(i = 0; i < tree->lineThickness; i++) {
Tree_VDotLine(tree, td.drawable,
lineLeft + i,
top,
bottom);
}
}
else
{
XFillRectangle(tree->display, td.drawable, tree->lineGC[0],
lineLeft,
top,
tree->lineThickness,
bottom - top);
}
vert = tree->lineThickness;
}
if
(hasPrev || hasNext) {
if
(tree->lineStyle == LINE_STYLE_DOT) {
for
(i = 0; i < tree->lineThickness; i++) {
Tree_HDotLine(tree, td.drawable,
lineLeft + vert,
lineTop + i,
x
+ indent);
}
}
else
{
XFillRectangle(tree->display, td.drawable, tree->lineGC[0],
lineLeft + vert,
lineTop,
left + tree->useIndent - (lineLeft + vert),
tree->lineThickness);
}
}
for
(parent = item->parent;
parent != NULL;
parent = parent->parent) {
lineLeft -= tree->useIndent;
if
((parent->parent != NULL) && IS_ROOT(parent->parent) && !tree->showRootLines)
continue
;
item = parent->nextSibling;
while
((item != NULL) && !IS_VISIBLE(item))
item = item->nextSibling;
if
(item != NULL) {
if
(tree->lineStyle == LINE_STYLE_DOT) {
for
(i = 0; i < tree->lineThickness; i++) {
Tree_VDotLine(tree, td.drawable,
lineLeft + i,
y,
y + height);
}
}
else
{
XFillRectangle(tree->display, td.drawable, tree->lineGC[0],
lineLeft,
y,
tree->lineThickness,
height);
}
}
}
}
void
TreeItem_DrawButton(
TreeCtrl *tree,
TreeItem item,
int
x,
int
y,
int
width,
int
height,
TreeDrawable td,
TreeStyle style
)
{
int
indent, left, lineLeft, lineTop;
int
buttonLeft, buttonTop, buttonY = -1, w1;
Tk_Image image;
Pixmap bitmap;
if
(!TreeItem_HasButton(tree, item))
return
;
indent = TreeItem_Indent(tree, tree->columnTree, item);
if
(style != NULL)
buttonY = TreeStyle_GetButtonY(tree, style);
left = x
+ indent - tree->useIndent;
image = PerStateImage_ForState(tree, &tree->buttonImage, item->state, NULL);
if
(image != NULL) {
int
imgW, imgH;
Tk_SizeOfImage(image, &imgW, &imgH);
if
(buttonY < 0)
buttonY = (height - imgH) / 2;
Tree_RedrawImage(image, 0, 0, imgW, imgH, td,
left + (tree->useIndent - imgW) / 2,
y + buttonY);
return
;
}
bitmap = PerStateBitmap_ForState(tree, &tree->buttonBitmap, item->state, NULL);
if
(bitmap != None) {
int
bmpW, bmpH;
int
bx, by;
Tk_SizeOfBitmap(tree->display, bitmap, &bmpW, &bmpH);
if
(buttonY < 0)
buttonY = (height - bmpH) / 2;
bx = left + (tree->useIndent - bmpW) / 2;
by = y + buttonY;
Tree_DrawBitmap(tree, bitmap, td.drawable, NULL, NULL,
0, 0, (unsigned
int
) bmpW, (unsigned
int
) bmpH,
bx, by);
return
;
}
if
(tree->useTheme) {
int
bw, bh;
int
buttonState = item->state;
buttonState &= ~ITEM_FLAGS_BUTTONSTATE;
if
(item->flags & ITEM_FLAG_BUTTONSTATE_ACTIVE)
buttonState |= BUTTON_STATE_ACTIVE;
if
(item->flags & ITEM_FLAG_BUTTONSTATE_PRESSED)
buttonState |= BUTTON_STATE_PRESSED;
if
(TreeTheme_GetButtonSize(tree, td.drawable,
(buttonState & STATE_ITEM_OPEN) != 0, &bw, &bh) == TCL_OK) {
if
(buttonY < 0)
buttonY = (height - bh) / 2;
if
(TreeTheme_DrawButton(tree, td, item, buttonState,
left + (tree->useIndent - bw) / 2, y + buttonY,
bw, bh) == TCL_OK) {
return
;
}
}
}
w1 = tree->buttonThickness / 2;
lineLeft = left + (tree->useIndent - tree->buttonThickness) / 2;
if
(buttonY < 0)
lineTop = y + (height - tree->lineThickness) / 2;
else
lineTop = y + buttonY + (tree->buttonHeightMax - tree->lineThickness) / 2;
buttonLeft = left + (tree->useIndent - tree->buttonSize) / 2;
if
(buttonY < 0)
buttonTop = y + (height - tree->buttonSize) / 2;
else
buttonTop = y + buttonY + (tree->buttonHeightMax - tree->buttonSize) / 2;
XFillRectangle(tree->display, td.drawable,
Tk_3DBorderGC(tree->tkwin, tree->border, TK_3D_FLAT_GC),
buttonLeft + tree->buttonThickness,
buttonTop + tree->buttonThickness,
tree->buttonSize - tree->buttonThickness,
tree->buttonSize - tree->buttonThickness);
XDrawRectangle(tree->display, td.drawable, tree->buttonGC,
buttonLeft + w1,
buttonTop + w1,
tree->buttonSize - tree->buttonThickness,
tree->buttonSize - tree->buttonThickness);
XFillRectangle(tree->display, td.drawable, tree->buttonGC,
buttonLeft + tree->buttonThickness * 2,
lineTop,
tree->buttonSize - tree->buttonThickness * 4,
tree->buttonThickness);
if
(!(item->state & STATE_ITEM_OPEN)) {
XFillRectangle(tree->display, td.drawable, tree->buttonGC,
lineLeft,
buttonTop + tree->buttonThickness * 2,
tree->buttonThickness,
tree->buttonSize - tree->buttonThickness * 4);
}
}
static
int
SpanWalkProc_UpdateWindowPositions(
TreeCtrl *tree,
TreeItem item,
SpanInfo *spanPtr,
StyleDrawArgs *drawArgs,
ClientData clientData
)
{
StyleDrawArgs drawArgsCopy;
int
requests;
if
((drawArgs->x >= TreeRect_Right(drawArgs->bounds)) ||
(drawArgs->x + drawArgs->width <= TreeRect_Left(drawArgs->bounds)) ||
(drawArgs->style == NULL))
return
0;
TreeDisplay_GetReadyForTrouble(tree, &requests);
drawArgsCopy = *drawArgs;
TreeStyle_UpdateWindowPositions(&drawArgsCopy);
if
(TreeDisplay_WasThereTrouble(tree, requests))
return
1;
return
drawArgs->x + drawArgs->width >= TreeRect_Right(drawArgs->bounds);
}
void
TreeItem_UpdateWindowPositions(
TreeCtrl *tree,
TreeItem item,
int
lock,
int
x,
int
y,
int
width,
int
height
)
{
TreeItem_WalkSpans(tree, item, lock,
x, y, width, height,
WALKSPAN_DRAG_ORDER | WALKSPAN_IGNORE_DRAGGED,
SpanWalkProc_UpdateWindowPositions, (ClientData) NULL);
if
(item->header != NULL) {
TreeItem_WalkSpans(tree, item, lock,
x, y, width, height,
WALKSPAN_ONLY_DRAGGED,
SpanWalkProc_UpdateWindowPositions, (ClientData) NULL);
}
}
static
int
SpanWalkProc_GetOnScreenColumns(
TreeCtrl *tree,
TreeItem item,
SpanInfo *spanPtr,
StyleDrawArgs *drawArgs,
ClientData clientData
)
{
TreeColumnList *columns = clientData;
if
((drawArgs->x >= TreeRect_Right(drawArgs->bounds)) ||
(drawArgs->x + drawArgs->width <= TreeRect_Left(drawArgs->bounds)))
return
0;
TreeColumnList_Append(columns, drawArgs->column);
return
drawArgs->x + drawArgs->width >= TreeRect_Right(drawArgs->bounds);
}
void
TreeItem_GetOnScreenColumns(
TreeCtrl *tree,
TreeItem item,
int
lock,
int
x,
int
y,
int
width,
int
height,
TreeColumnList *columns
)
{
TreeItem_WalkSpans(tree, item, lock,
x, y, width, height,
WALKSPAN_DRAG_ORDER | WALKSPAN_IGNORE_DRAGGED,
SpanWalkProc_GetOnScreenColumns, (ClientData) columns);
if
(item->header != NULL) {
TreeItem_WalkSpans(tree, item, lock,
x, y, width, height,
WALKSPAN_ONLY_DRAGGED,
SpanWalkProc_GetOnScreenColumns, (ClientData) columns);
}
}
void
TreeItem_OnScreen(
TreeCtrl *tree,
TreeItem item,
int
onScreen
)
{
#if 0
TreeItemColumn column = item->columns;
while
(column != NULL) {
if
(column->style != NULL) {
TreeStyle_OnScreen(tree, column->style, onScreen);
}
column = column->next;
}
#endif
}
int
TreeItem_ReallyVisible(
TreeCtrl *tree,
TreeItem item
)
{
TreeItem parent = item->parent;
if
(item->header != NULL) {
if
(!tree->showHeader || !IS_VISIBLE(item))
return
0;
(
void
) TreeColumns_UpdateCounts(tree);
if
(tree->columnCountVis +
tree->columnCountVisLeft +
tree->columnCountVisRight == 0)
return
0;
return
1;
}
if
(!tree->updateIndex)
return
item->indexVis != -1;
if
(!IS_VISIBLE(item))
return
0;
if
(parent == NULL)
return
IS_ROOT(item) ? tree->showRoot : 0;
if
(IS_ROOT(parent)) {
if
(!IS_VISIBLE(parent))
return
0;
if
(!tree->showRoot)
return
1;
if
(!(parent->state & STATE_ITEM_OPEN))
return
0;
}
if
(!IS_VISIBLE(parent) || !(parent->state & STATE_ITEM_OPEN))
return
0;
return
TreeItem_ReallyVisible(tree, parent);
}
TreeItem
TreeItem_RootAncestor(
TreeCtrl *tree,
TreeItem item
)
{
while
(item->parent != NULL)
item = item->parent;
return
item;
}
int
TreeItem_IsAncestor(
TreeCtrl *tree,
TreeItem item1,
TreeItem item2
)
{
if
(item1 == item2)
return
0;
while
(item2 && item2 != item1)
item2 = item2->parent;
return
item2 != NULL;
}
Tcl_Obj *
TreeItem_ToObj(
TreeCtrl *tree,
TreeItem item
)
{
if
(tree->itemPrefixLen) {
char
buf[100 + TCL_INTEGER_SPACE];
(
void
)
sprintf
(buf,
"%s%d"
, tree->itemPrefix, item->id);
return
Tcl_NewStringObj(buf, -1);
}
return
Tcl_NewIntObj(item->id);
}
static
int
Item_Configure(
TreeCtrl *tree,
TreeItem item,
int
objc,
Tcl_Obj *CONST objv[]
)
{
Tk_SavedOptions savedOptions;
int
error;
Tcl_Obj *errorResult = NULL;
int
mask;
int
lastVisible = IS_VISIBLE(item);
int
lastWrap = IS_WRAP(item);
for
(error = 0; error <= 1; error++) {
if
(error == 0) {
if
(Tree_SetOptions(tree, STATE_DOMAIN_ITEM, item, tree->itemOptionTable,
objc, objv, &savedOptions, &mask) != TCL_OK) {
mask = 0;
continue
;
}
Tk_FreeSavedOptions(&savedOptions);
break
;
}
else
{
errorResult = Tcl_GetObjResult(tree->interp);
Tcl_IncrRefCount(errorResult);
Tk_RestoreSavedOptions(&savedOptions);
Tcl_SetObjResult(tree->interp, errorResult);
Tcl_DecrRefCount(errorResult);
return
TCL_ERROR;
}
}
if
(mask & ITEM_CONF_SIZE) {
Tree_FreeItemDInfo(tree, item, NULL);
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
}
if
(mask & ITEM_CONF_BUTTON) {
if
(tree->columnTree != NULL)
Tree_InvalidateItemDInfo(tree, tree->columnTree, item, NULL);
}
if
((mask & ITEM_CONF_VISIBLE) && (IS_VISIBLE(item) != lastVisible)) {
TreeColumns_InvalidateWidthOfItems(tree, NULL);
TreeColumns_InvalidateSpans(tree);
if
((item->prevSibling != NULL) &&
(item->nextSibling == NULL) &&
tree->showLines && (tree->columnTree != NULL)) {
TreeItem last = item->prevSibling;
while
(last->lastChild != NULL)
last = last->lastChild;
Tree_InvalidateItemDInfo(tree, tree->columnTree,
item->prevSibling,
last);
}
if
((item->parent != NULL) &&
(item->parent->flags & ITEM_FLAG_BUTTON_AUTO) &&
tree->showButtons && (tree->columnTree != NULL)) {
Tree_InvalidateItemDInfo(tree, tree->columnTree, item->parent,
NULL);
}
tree->updateIndex = 1;
Tree_DInfoChanged(tree, DINFO_REDO_RANGES | DINFO_REDO_SELECTION);
}
if
((mask & ITEM_CONF_WRAP) && (IS_WRAP(item) != lastWrap)) {
tree->updateIndex = 1;
TreeColumns_InvalidateWidthOfItems(tree, NULL);
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
}
return
TCL_OK;
}
static
void
NoStyleMsg(
TreeCtrl *tree,
TreeItem item,
int
columnIndex
)
{
FormatResult(tree->interp,
"%s %s%d column %s%d has no style"
,
item->header ?
"header"
:
"item"
,
item->header ?
""
: tree->itemPrefix, item->id,
tree->columnPrefix,
TreeColumn_GetID(Tree_FindColumn(tree, columnIndex)));
}
static
void
StateDomainErrMsg(
TreeCtrl *tree,
TreeItem item,
TreeStyle style
)
{
FormatResult(tree->interp,
"state domain conflict between %s \"%s%d\" and style \"%s\""
,
item->header ?
"header"
:
"item"
,
item->header ?
""
: tree->itemPrefix, item->id,
TreeStyle_GetName(tree, style));
}
int
TreeItemCmd_Bbox(
TreeCtrl *tree,
int
objc,
Tcl_Obj *CONST objv[],
int
doHeaders
)
{
Tcl_Interp *interp = tree->interp;
TreeItem item;
int
count;
TreeColumn treeColumn;
TreeRectangle rect;
if
(objc < 4 || objc > 6) {
Tcl_WrongNumArgs(interp, 3, objv,
doHeaders ?
"header ?column? ?element?"
:
"item ?column? ?element?"
);
return
TCL_ERROR;
}
if
(doHeaders) {
TreeHeader header;
if
(TreeHeader_FromObj(tree, objv[3], &header) != TCL_OK)
return
TCL_ERROR;
item = TreeHeader_GetItem(header);
}
else
{
if
(TreeItem_FromObj(tree, objv[3], &item, IFO_NOT_NULL) != TCL_OK)
return
TCL_ERROR;
}
(
void
) Tree_GetOriginX(tree);
(
void
) Tree_GetOriginY(tree);
if
(objc == 4) {
if
(Tree_ItemBbox(tree, item, COLUMN_LOCK_NONE, &rect) < 0)
return
TCL_OK;
if
(doHeaders)
rect.width -= tree->tailExtend;
}
else
{
if
(TreeColumn_FromObj(tree, objv[4], &treeColumn,
CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK)
return
TCL_ERROR;
if
(objc == 5) {
objc = 0;
objv = NULL;
}
else
{
TreeItemColumn column;
TreeElement elem;
objc -= 5;
objv += 5;
column = TreeItem_FindColumn(tree, item, TreeColumn_Index(treeColumn));
if
(column == NULL || column->style == NULL || TreeStyle_IsHeaderStyle(tree, column->style)) {
NoStyleMsg(tree, item, TreeColumn_Index(treeColumn));
return
TCL_ERROR;
}
if
(TreeElement_FromObj(tree, objv[0], &elem) != TCL_OK)
return
TCL_ERROR;
if
(TreeStyle_FindElement(tree, column->style, elem, NULL) != TCL_OK)
return
TCL_ERROR;
}
count = TreeItem_GetRects(tree, item, treeColumn, objc, objv, &rect);
if
(count == 0)
return
TCL_OK;
if
(count == -1)
return
TCL_ERROR;
}
FormatResult(interp,
"%d %d %d %d"
,
TreeRect_Left(rect) - tree->xOrigin,
TreeRect_Top(rect) - tree->yOrigin,
TreeRect_Left(rect) - tree->xOrigin + TreeRect_Width(rect),
TreeRect_Top(rect) - tree->yOrigin + TreeRect_Height(rect));
return
TCL_OK;
}
static
int
ItemBboxCmd(
ClientData clientData,
Tcl_Interp *interp,
int
objc,
Tcl_Obj *CONST objv[]
)
{
TreeCtrl *tree = clientData;
return
TreeItemCmd_Bbox(tree, objc, objv, FALSE);
}
static
int
ItemCreateCmd(
ClientData clientData,
Tcl_Interp *interp,
int
objc,
Tcl_Obj *CONST objv[]
)
{
TreeCtrl *tree = clientData;
static
CONST
char
*optionNames[] = {
"-button"
,
"-count"
,
"-enabled"
,
"-height"
,
"-nextsibling"
,
"-open"
,
"-parent"
,
"-prevsibling"
,
"-returnid"
,
"-tags"
,
"-visible"
,
"-wrap"
,
(
char
*) NULL };
enum
{ OPT_BUTTON, OPT_COUNT, OPT_ENABLED, OPT_HEIGHT, OPT_NEXTSIBLING,
OPT_OPEN, OPT_PARENT, OPT_PREVSIBLING, OPT_RETURNID, OPT_TAGS,
OPT_VISIBLE, OPT_WRAP };
int
index, i, count = 1, button = 0, returnId = 1, open = 1, visible = 1;
int
enabled = 1, wrap = 0, height = 0;
TreeItem item, parent = NULL, prevSibling = NULL, nextSibling = NULL;
TreeItem head = NULL, tail = NULL;
Tcl_Obj *listObj = NULL, *tagsObj = NULL;
TagInfo *tagInfo = NULL;
TreeColumn treeColumn;
for
(i = 3; i < objc; i += 2) {
if
(Tcl_GetIndexFromObj(interp, objv[i], optionNames,
"option"
, 0,
&index) != TCL_OK) {
return
TCL_ERROR;
}
if
(i + 1 == objc) {
FormatResult(interp,
"missing value for \"%s\" option"
,
optionNames[index]);
return
TCL_ERROR;
}
switch
(index) {
case
OPT_BUTTON: {
int
length;
char
*s = Tcl_GetStringFromObj(objv[i + 1], &length);
if
(s[0] ==
'a'
&&
strncmp
(s,
"auto"
, length) == 0) {
button = ITEM_FLAG_BUTTON_AUTO;
}
else
{
if
(Tcl_GetBooleanFromObj(interp, objv[i + 1], &button) != TCL_OK) {
FormatResult(interp,
"expected boolean or auto but got \"%s\""
, s);
return
TCL_ERROR;
}
if
(button) {
button = ITEM_FLAG_BUTTON;
}
}
break
;
}
case
OPT_COUNT:
if
(Tcl_GetIntFromObj(interp, objv[i + 1], &count) != TCL_OK)
return
TCL_ERROR;
if
(count <= 0) {
FormatResult(interp,
"bad count \"%d\": must be > 0"
,
count);
return
TCL_ERROR;
}
break
;
case
OPT_ENABLED:
if
(Tcl_GetBooleanFromObj(interp, objv[i + 1], &enabled)
!= TCL_OK) {
return
TCL_ERROR;
}
break
;
case
OPT_HEIGHT:
if
(Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1],
&height) != TCL_OK)
return
TCL_ERROR;
if
(height < 0) {
FormatResult(interp,
"bad screen distance \"%s\": must be > 0"
,
Tcl_GetString(objv[i + 1]));
return
TCL_ERROR;
}
break
;
case
OPT_NEXTSIBLING:
if
(TreeItem_FromObj(tree, objv[i + 1], &nextSibling,
IFO_NOT_NULL | IFO_NOT_ROOT | IFO_NOT_ORPHAN) != TCL_OK) {
return
TCL_ERROR;
}
parent = prevSibling = NULL;
break
;
case
OPT_OPEN:
if
(Tcl_GetBooleanFromObj(interp, objv[i + 1], &open)
!= TCL_OK) {
return
TCL_ERROR;
}
break
;
case
OPT_PARENT:
if
(TreeItem_FromObj(tree, objv[i + 1], &parent, IFO_NOT_NULL) != TCL_OK) {
return
TCL_ERROR;
}
prevSibling = nextSibling = NULL;
break
;
case
OPT_PREVSIBLING:
if
(TreeItem_FromObj(tree, objv[i + 1], &prevSibling,
IFO_NOT_NULL | IFO_NOT_ROOT | IFO_NOT_ORPHAN) != TCL_OK) {
return
TCL_ERROR;
}
parent = nextSibling = NULL;
break
;
case
OPT_RETURNID:
if
(Tcl_GetBooleanFromObj(interp, objv[i + 1], &returnId)
!= TCL_OK) {
return
TCL_ERROR;
}
break
;
case
OPT_TAGS:
tagsObj = objv[i + 1];
break
;
case
OPT_VISIBLE:
if
(Tcl_GetBooleanFromObj(interp, objv[i + 1], &visible)
!= TCL_OK) {
return
TCL_ERROR;
}
break
;
case
OPT_WRAP:
if
(Tcl_GetBooleanFromObj(interp, objv[i + 1], &wrap)
!= TCL_OK) {
return
TCL_ERROR;
}
break
;
}
}
if
(tagsObj != NULL) {
if
(TagInfo_FromObj(tree, tagsObj, &tagInfo) != TCL_OK)
return
TCL_ERROR;
}
if
(returnId)
listObj = Tcl_NewListObj(0, NULL);
if
((parent && IS_DELETED(parent)) ||
(prevSibling && IS_DELETED(prevSibling->parent)) ||
(nextSibling && IS_DELETED(nextSibling->parent)))
parent = prevSibling = nextSibling = NULL;
for
(i = 0; i < count; i++) {
item = Item_Alloc(tree, FALSE);
item->flags &= ~(ITEM_FLAG_BUTTON | ITEM_FLAG_BUTTON_AUTO);
item->flags |= button;
if
(enabled) item->state |= STATE_ITEM_ENABLED;
else
item->state &= ~STATE_ITEM_ENABLED;
if
(open) item->state |= STATE_ITEM_OPEN;
else
item->state &= ~STATE_ITEM_OPEN;
if
(visible) item->flags |= ITEM_FLAG_VISIBLE;
else
item->flags &= ~ITEM_FLAG_VISIBLE;
if
(wrap) item->flags |= ITEM_FLAG_WRAP;
else
item->flags &= ~ITEM_FLAG_WRAP;
item->fixedHeight = height;
for
(treeColumn = tree->columns; treeColumn != NULL;
treeColumn = TreeColumn_Next(treeColumn)) {
TreeStyle style = TreeColumn_ItemStyle(treeColumn);
if
(style != NULL) {
TreeItemColumn column = Item_CreateColumn(tree, item,
TreeColumn_Index(treeColumn), NULL);
column->style = TreeStyle_NewInstance(tree, style);
}
}
#ifdef DEPRECATED
if
(tree->defaultStyle.numStyles) {
int
i, n = MIN(tree->columnCount, tree->defaultStyle.numStyles);
for
(i = 0; i < n; i++) {
TreeItemColumn column = Item_CreateColumn(tree, item, i, NULL);
if
(column->style != NULL)
continue
;
if
(tree->defaultStyle.styles[i] != NULL) {
column->style = TreeStyle_NewInstance(tree,
tree->defaultStyle.styles[i]);
}
}
}
#endif /* DEPRECATED */
if
(tagInfo != NULL) {
if
(count == 1) {
item->tagInfo = tagInfo;
tagInfo = NULL;
}
else
{
item->tagInfo = TagInfo_Copy(tree, tagInfo);
}
}
if
(parent || prevSibling || nextSibling) {
if
(head == NULL)
head = item;
if
(tail != NULL) {
tail->nextSibling = item;
item->prevSibling = tail;
}
tail = item;
}
if
(returnId)
Tcl_ListObjAppendElement(interp, listObj, TreeItem_ToObj(tree,
item));
}
if
(parent != NULL) {
head->prevSibling = parent->lastChild;
if
(parent->lastChild != NULL)
parent->lastChild->nextSibling = head;
else
parent->firstChild = head;
parent->lastChild = tail;
}
else
if
(prevSibling != NULL) {
parent = prevSibling->parent;
if
(prevSibling->nextSibling != NULL)
prevSibling->nextSibling->prevSibling = tail;
else
parent->lastChild = tail;
head->prevSibling = prevSibling;
tail->nextSibling = prevSibling->nextSibling;
prevSibling->nextSibling = head;
}
else
if
(nextSibling != NULL) {
parent = nextSibling->parent;
if
(nextSibling->prevSibling != NULL)
nextSibling->prevSibling->nextSibling = head;
else
parent->firstChild = head;
head->prevSibling = nextSibling->prevSibling;
tail->nextSibling = nextSibling;
nextSibling->prevSibling = tail;
}
if
(parent != NULL) {
for
(item = head; item != NULL; item = item->nextSibling) {
item->parent = parent;
item->depth = parent->depth + 1;
}
parent->numChildren += count;
TreeItem_AddToParent(tree, head);
}
TagInfo_Free(tree, tagInfo);
if
(returnId)
Tcl_SetObjResult(interp, listObj);
return
TCL_OK;
}
int
TreeItemCmd_Element(
TreeCtrl *tree,
int
objc,
Tcl_Obj *CONST objv[],
int
doHeaders
)
{
Tcl_Interp *interp = tree->interp;
static
CONST
char
*commandNames[] = {
#ifdef DEPRECATED
"actual"
,
#endif
"cget"
,
"configure"
,
"perstate"
, (
char
*) NULL };
enum
{
#ifdef DEPRECATED
COMMAND_ACTUAL,
#endif
COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_PERSTATE };
int
index;
int
columnIndex;
TreeItemColumn column;
TreeItemList itemList;
TreeItem item;
int
flags = IFO_NOT_NULL;
int
result = TCL_OK;
int
tailFlag = doHeaders ? 0 : CFO_NOT_TAIL;
int
domain = doHeaders ? STATE_DOMAIN_HEADER : STATE_DOMAIN_ITEM;
if
(objc < 7) {
Tcl_WrongNumArgs(interp, 3, objv,
doHeaders ?
"command header column element ?arg ...?"
:
"command item column element ?arg ...?"
);
return
TCL_ERROR;
}
if
(Tcl_GetIndexFromObj(interp, objv[3], commandNames,
"command"
, 0,
&index) != TCL_OK)
return
TCL_ERROR;
if
((index != COMMAND_CONFIGURE) || (objc < 9))
flags |= IFO_NOT_MANY;
if
(doHeaders) {
if
(TreeHeaderList_FromObj(tree, objv[4], &itemList, flags) != TCL_OK) {
return
TCL_ERROR;
}
}
else
{
if
(TreeItemList_FromObj(tree, objv[4], &itemList, flags) != TCL_OK)
return
TCL_ERROR;
}
item = TreeItemList_Nth(&itemList, 0);
switch
(index) {
#ifdef DEPRECATED
case
COMMAND_ACTUAL:
#endif
case
COMMAND_PERSTATE: {
int
state;
if
(objc < 8 || objc > 9) {
Tcl_WrongNumArgs(tree->interp, 4, objv,
doHeaders ?
"header column element option ?stateList?"
:
"item column element option ?stateList?"
);
result = TCL_ERROR;
break
;
}
if
(TreeItem_ColumnFromObj(tree, item, objv[5], &column,
NULL, &columnIndex, CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
result = TCL_ERROR;
break
;
}
if
((column == NULL) || (column->style == NULL) || TreeStyle_IsHeaderStyle(tree, column->style)) {
NoStyleMsg(tree, item, columnIndex);
result = TCL_ERROR;
break
;
}
state = item->state | column->cstate;
if
(objc == 9) {
int
states[3];
if
(Tree_StateFromListObj(tree, domain, objv[8], states,
SFO_NOT_OFF | SFO_NOT_TOGGLE) != TCL_OK) {
result = TCL_ERROR;
break
;
}
state = states[STATE_OP_ON];
}
result = TreeStyle_ElementActual(tree, column->style,
state, objv[6], objv[7]);
break
;
}
case
COMMAND_CGET: {
if
(objc != 8) {
Tcl_WrongNumArgs(tree->interp, 4, objv,
doHeaders ?
"header column element option"
:
"item column element option"
);
result = TCL_ERROR;
break
;
}
if
(TreeItem_ColumnFromObj(tree, item, objv[5], &column,
NULL, &columnIndex, CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
result = TCL_ERROR;
break
;
}
if
((column == NULL) || (column->style == NULL) || TreeStyle_IsHeaderStyle(tree, column->style)) {
NoStyleMsg(tree, item, columnIndex);
result = TCL_ERROR;
break
;
}
result = TreeStyle_ElementCget(tree, item,
column, column->style, objv[6], objv[7]);
break
;
}
case
COMMAND_CONFIGURE: {
struct
columnObj {
TreeColumnList columns;
int
isColumn;
int
numArgs;
} staticCO[STATIC_SIZE], *co = staticCO;
int
i, index, indexElem, prevColumn;
ItemForEach iter;
flags = CFO_NOT_NULL | tailFlag;
if
(objc < 9)
flags |= CFO_NOT_MANY;
STATIC_ALLOC(co,
struct
columnObj, objc);
for
(i = 5; i < objc; i++) {
co[i].isColumn = FALSE;
co[i].numArgs = -1;
}
indexElem = 6;
i = indexElem - 1;
if
(TreeColumnList_FromObj(tree, objv[i], &co[i].columns,
flags) != TCL_OK) {
result = TCL_ERROR;
break
;
}
co[i].isColumn = TRUE;
prevColumn = i;
while
(1) {
int
numArgs = 0;
char
breakChar =
'\0'
;
for
(index = indexElem + 1; index < objc; index++) {
if
(numArgs % 2 == 0) {
int
length;
char
*s = Tcl_GetStringFromObj(objv[index], &length);
if
((length == 1) && ((s[0] ==
'+'
) || (s[0] ==
','
))) {
breakChar = s[0];
break
;
}
}
numArgs++;
}
if
((breakChar || indexElem != 6) && (numArgs < 2)) {
FormatResult(interp,
"missing option-value pair after element \"%s\""
,
Tcl_GetString(objv[indexElem]));
result = TCL_ERROR;
goto
doneCONF;
}
co[indexElem].numArgs = numArgs;
if
(!breakChar)
break
;
if
(index == objc - 1) {
FormatResult(interp,
"missing %s after \"%c\""
,
(breakChar ==
'+'
) ?
"element name"
:
"column"
,
breakChar);
result = TCL_ERROR;
goto
doneCONF;
}
if
(breakChar ==
'+'
) {
indexElem = index + 1;
}
else
if
(breakChar ==
','
) {
co[prevColumn].numArgs = index - prevColumn;
if
(TreeColumnList_FromObj(tree, objv[index + 1],
&co[index + 1].columns, CFO_NOT_NULL |
tailFlag) != TCL_OK) {
result = TCL_ERROR;
goto
doneCONF;
}
co[index + 1].isColumn = TRUE;
prevColumn = index + 1;
indexElem = index + 2;
if
(indexElem == objc) {
FormatResult(interp,
"missing element name after column \"%s\""
,
Tcl_GetString(objv[index + 1]));
result = TCL_ERROR;
goto
doneCONF;
}
}
}
co[prevColumn].numArgs = index - prevColumn;
ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
int
iMask = 0;
for
(index = 5; index < objc; index += co[index].numArgs + 1) {
ColumnForEach citer;
TreeColumn treeColumn;
#ifdef TREECTRL_DEBUG
if
(!co[index].isColumn) panic(
"isColumn == FALSE"
);
#endif
COLUMN_FOR_EACH(treeColumn, &co[index].columns, NULL, &citer) {
int
columnIndex, cMask = 0;
columnIndex = TreeColumn_Index(treeColumn);
column = TreeItem_FindColumn(tree, item, columnIndex);
if
((column == NULL) || (column->style == NULL) || TreeStyle_IsHeaderStyle(tree, column->style)) {
NoStyleMsg(tree, item, columnIndex);
result = TCL_ERROR;
break
;
}
indexElem = index + 1;
while
(1) {
int
eMask, index2;
#ifdef TREECTRL_DEBUG
if
(co[indexElem].numArgs == -1) panic(
"indexElem=%d (%s) objc=%d numArgs == -1"
, indexElem, Tcl_GetString(objv[indexElem]), objc);
#endif
result = TreeStyle_ElementConfigureFromObj(tree, item,
column, column->style, objv[indexElem],
co[indexElem].numArgs, (Tcl_Obj **) objv + indexElem + 1, &eMask);
if
(result != TCL_OK)
break
;
cMask |= eMask;
index2 = indexElem + co[indexElem].numArgs;
if
(index2 == objc - 1)
break
;
index2 += 2;
if
(co[index2].isColumn)
break
;
indexElem = index2;
}
if
(cMask & CS_LAYOUT) {
TreeItemColumn_InvalidateSize(tree, column);
TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
}
else
if
(cMask & CS_DISPLAY) {
Tree_InvalidateItemDInfo(tree, treeColumn, item, NULL);
}
iMask |= cMask;
if
(result != TCL_OK)
break
;
}
if
(result != TCL_OK)
break
;
}
if
(iMask & CS_LAYOUT) {
TreeItem_InvalidateHeight(tree, item);
Tree_FreeItemDInfo(tree, item, NULL);
if
(item->header == NULL)
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
}
else
if
(iMask & CS_DISPLAY) {
}
if
(result != TCL_OK)
break
;
}
doneCONF:
for
(i = 5; i < objc; i++) {
if
(co[i].isColumn)
TreeColumnList_Free(&co[i].columns);
}
STATIC_FREE(co,
struct
columnObj, objc);
break
;
}
}
TreeItemList_Free(&itemList);
return
result;
}
static
int
ItemElementCmd(
ClientData clientData,
Tcl_Interp *interp,
int
objc,
Tcl_Obj *CONST objv[]
)
{
TreeCtrl *tree = clientData;
return
TreeItemCmd_Element(tree, objc, objv, FALSE);
}
int
TreeItemCmd_Style(
TreeCtrl *tree,
int
objc,
Tcl_Obj *CONST objv[],
int
doHeaders
)
{
Tcl_Interp *interp = tree->interp;
int
domain = doHeaders ? STATE_DOMAIN_HEADER : STATE_DOMAIN_ITEM;
int
index;
TreeItemList itemList;
TreeItem item;
int
flags = IFO_NOT_NULL;
int
result = TCL_OK;
int
tailFlag = doHeaders ? 0 : CFO_NOT_TAIL;
static
CONST
char
*commandNames[] = {
"elements"
,
"map"
,
"set"
, (
char
*) NULL
};
enum
{ COMMAND_ELEMENTS, COMMAND_MAP, COMMAND_SET };
if
(objc < 5) {
Tcl_WrongNumArgs(interp, 3, objv,
doHeaders ?
"command header ?arg ...?"
:
"command item ?arg ...?"
);
return
TCL_ERROR;
}
if
(Tcl_GetIndexFromObj(interp, objv[3], commandNames,
"command"
, 0,
&index) != TCL_OK) {
return
TCL_ERROR;
}
if
((index == COMMAND_ELEMENTS) || (index == COMMAND_SET && objc < 7))
flags |= IFO_NOT_MANY;
if
(doHeaders) {
if
(TreeHeaderList_FromObj(tree, objv[4], &itemList, flags) != TCL_OK) {
return
TCL_ERROR;
}
}
else
{
if
(TreeItemList_FromObj(tree, objv[4], &itemList, flags) != TCL_OK) {
return
TCL_ERROR;
}
}
item = TreeItemList_Nth(&itemList, 0);
switch
(index) {
case
COMMAND_ELEMENTS: {
TreeItemColumn column;
int
columnIndex;
if
(objc != 6) {
Tcl_WrongNumArgs(interp, 4, objv,
doHeaders ?
"header column"
:
"item column"
);
result = TCL_ERROR;
break
;
}
if
(TreeItem_ColumnFromObj(tree, item, objv[5], &column,
NULL, &columnIndex, CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
result = TCL_ERROR;
break
;
}
if
((column == NULL) || (column->style == NULL) || TreeStyle_IsHeaderStyle(tree, column->style)) {
NoStyleMsg(tree, item, columnIndex);
result = TCL_ERROR;
break
;
}
TreeStyle_ListElements(tree, column->style);
break
;
}
case
COMMAND_MAP: {
TreeStyle style;
TreeColumnList columns;
TreeColumn treeColumn;
TreeItemColumn column;
int
columnIndex;
int
objcM;
Tcl_Obj **objvM;
ItemForEach iter;
ColumnForEach citer;
int
redoRanges = !doHeaders;
if
(objc != 8) {
Tcl_WrongNumArgs(interp, 4, objv,
doHeaders ?
"header column style map"
:
"item column style map"
);
return
TCL_ERROR;
}
if
(TreeColumnList_FromObj(tree, objv[5], &columns,
CFO_NOT_NULL | tailFlag) != TCL_OK) {
result = TCL_ERROR;
break
;
}
if
(TreeStyle_FromObj(tree, objv[6], &style) != TCL_OK) {
result = TCL_ERROR;
goto
doneMAP;
}
if
(TreeStyle_GetStateDomain(tree, style) != domain) {
StateDomainErrMsg(tree, item, style);
result = TCL_ERROR;
goto
doneMAP;
}
if
(Tcl_ListObjGetElements(interp, objv[7], &objcM, &objvM)
!= TCL_OK) {
result = TCL_ERROR;
goto
doneMAP;
}
if
(objcM & 1) {
FormatResult(interp,
"list must contain even number of elements"
);
result = TCL_ERROR;
goto
doneMAP;
}
ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
COLUMN_FOR_EACH(treeColumn, &columns, NULL, &citer) {
columnIndex = TreeColumn_Index(treeColumn);
column = Item_CreateColumn(tree, item, columnIndex, NULL);
if
((column->style != NULL) && !TreeStyle_IsHeaderStyle(tree, column->style)) {
if
(TreeStyle_Remap(tree, column->style, style, objcM,
objvM) != TCL_OK) {
result = TCL_ERROR;
break
;
}
}
else
{
TreeItemColumn_ForgetStyle(tree, column);
column->style = TreeStyle_NewInstance(tree, style);
}
TreeItemColumn_InvalidateSize(tree, column);
TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
}
TreeItem_InvalidateHeight(tree, item);
Tree_FreeItemDInfo(tree, item, NULL);
if
(result != TCL_OK)
break
;
}
if
(redoRanges)
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
doneMAP:
TreeColumnList_Free(&columns);
break
;
}
case
COMMAND_SET: {
struct
columnStyle {
TreeColumnList columns;
TreeStyle style;
};
struct
columnStyle staticCS[STATIC_SIZE], *cs = staticCS;
TreeColumn treeColumn;
TreeItemColumn column;
int
i, count = 0, length, changed = FALSE, changedI;
ItemForEach iter;
ColumnForEach citer;
if
(objc < 5) {
Tcl_WrongNumArgs(interp, 4, objv,
doHeaders ?
"header ?column? ?style? ?column style ...?"
:
"item ?column? ?style? ?column style ...?"
);
return
TCL_ERROR;
}
if
(objc == 5) {
Tcl_Obj *listObj = Tcl_NewListObj(0, NULL);
int
tailOK = item->header != NULL;
treeColumn = Tree_FirstColumn(tree, -1, tailOK);
column = item->columns;
while
(treeColumn != NULL) {
if
((column != NULL) && (column->style != NULL) && !TreeStyle_IsHeaderStyle(tree, column->style))
Tcl_ListObjAppendElement(interp, listObj,
TreeStyle_ToObj(TreeStyle_GetMaster(
tree, column->style)));
else
Tcl_ListObjAppendElement(interp, listObj,
Tcl_NewObj());
treeColumn = Tree_ColumnToTheRight(treeColumn, FALSE, tailOK);
if
(column != NULL)
column = column->next;
}
Tcl_SetObjResult(interp, listObj);
break
;
}
if
(objc == 6) {
if
(TreeItem_ColumnFromObj(tree, item, objv[5], &column,
NULL, NULL, CFO_NOT_NULL | tailFlag) != TCL_OK)
return
TCL_ERROR;
if
((column != NULL) && (column->style != NULL) && !TreeStyle_IsHeaderStyle(tree, column->style))
Tcl_SetObjResult(interp, TreeStyle_ToObj(
TreeStyle_GetMaster(tree, column->style)));
break
;
}
STATIC_ALLOC(cs,
struct
columnStyle, objc / 2);
for
(i = 5; i < objc; i += 2) {
if
(TreeColumnList_FromObj(tree, objv[i], &cs[count].columns,
CFO_NOT_NULL | tailFlag) != TCL_OK) {
result = TCL_ERROR;
goto
doneSET;
}
if
(i + 1 == objc) {
FormatResult(interp,
"missing style for column \"%s\""
,
Tcl_GetString(objv[i]));
result = TCL_ERROR;
goto
doneSET;
}
(
void
) Tcl_GetStringFromObj(objv[i + 1], &length);
if
(length == 0) {
cs[count].style = NULL;
}
else
{
if
(TreeStyle_FromObj(tree, objv[i + 1], &cs[count].style)
!= TCL_OK) {
result = TCL_ERROR;
goto
doneSET;
}
if
(TreeStyle_GetStateDomain(tree, cs[count].style) != domain) {
StateDomainErrMsg(tree, item, cs[count].style);
result = TCL_ERROR;
goto
doneSET;
}
}
count++;
}
ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
changedI = FALSE;
for
(i = 0; i < count; i++) {
COLUMN_FOR_EACH(treeColumn, &cs[i].columns, NULL, &citer) {
if
(cs[i].style == NULL) {
column = TreeItem_FindColumn(tree, item,
TreeColumn_Index(treeColumn));
if
(column == NULL || column->style == NULL ||
TreeStyle_IsHeaderStyle(tree, column->style))
continue
;
TreeItemColumn_ForgetStyle(tree, column);
if
(doHeaders) TreeHeaderColumn_EnsureStyleExists(item->header, column->headerColumn, treeColumn);
}
else
{
column = Item_CreateColumn(tree, item,
TreeColumn_Index(treeColumn), NULL);
if
(column->style != NULL) {
if
(TreeStyle_GetMaster(tree, column->style)
== cs[i].style)
continue
;
TreeItemColumn_ForgetStyle(tree, column);
}
column->style = TreeStyle_NewInstance(tree,
cs[i].style);
}
TreeItemColumn_InvalidateSize(tree, column);
TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
changedI = TRUE;
}
if
(changedI) {
TreeItem_InvalidateHeight(tree, item);
Tree_FreeItemDInfo(tree, item, NULL);
changed = TRUE;
}
}
}
if
(changed && !doHeaders)
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
doneSET:
for
(i = 0; i < count; i++) {
TreeColumnList_Free(&cs[i].columns);
}
STATIC_FREE(cs,
struct
columnStyle, objc / 2);
break
;
}
}
TreeItemList_Free(&itemList);
return
result;
}
static
int
ItemStyleCmd(
ClientData clientData,
Tcl_Interp *interp,
int
objc,
Tcl_Obj *CONST objv[]
)
{
TreeCtrl *tree = clientData;
return
TreeItemCmd_Style(tree, objc, objv, FALSE);
}
int
TreeItemCmd_ImageOrText(
TreeCtrl *tree,
int
objc,
Tcl_Obj *CONST objv[],
int
doImage,
int
doHeaders
)
{
Tcl_Interp *interp = tree->interp;
TreeColumn treeColumn = tree->columns;
TreeItemList itemList;
TreeItem item;
TreeElement elem = NULL;
TreeItemColumn column;
Tcl_Obj *objPtr;
int
isImage = doImage;
struct
columnObj {
TreeColumnList columns;
Tcl_Obj *obj;
} staticCO[STATIC_SIZE], *co = staticCO;
int
i, count = 0, changed = FALSE, columnIndex;
ItemForEach iter;
ColumnForEach citer;
int
flags = 0, result = TCL_OK;
int
tailFlag =
CFO_NOT_TAIL;
if
(objc < 4) {
Tcl_WrongNumArgs(interp, 3, objv,
doHeaders ?
"header ?column? ?text? ?column text ...?"
:
"item ?column? ?text? ?column text ...?"
);
return
TCL_ERROR;
}
if
(objc < 6)
flags = IFO_NOT_NULL | IFO_NOT_MANY;
if
(doHeaders) {
if
(TreeHeaderList_FromObj(tree, objv[3], &itemList, flags)
!= TCL_OK)
return
TCL_ERROR;
}
else
{
if
(TreeItemList_FromObj(tree, objv[3], &itemList, flags)
!= TCL_OK)
return
TCL_ERROR;
}
item = TreeItemList_Nth(&itemList, 0);
if
(objc == 4) {
Tcl_Obj *listObj = Tcl_NewListObj(0, NULL);
column = item->columns;
while
(treeColumn != NULL) {
if
((column != NULL) && (column->style != NULL) &&
!TreeStyle_IsHeaderStyle(tree, column->style)) {
objPtr = isImage ?
TreeStyle_GetImage(tree, column->style, &elem) :
TreeStyle_GetText(tree, column->style, &elem);
}
else
objPtr = NULL;
if
(doHeaders && elem == NULL)
objPtr = TreeHeaderColumn_GetImageOrText(item->header,
column->headerColumn, isImage);
if
(objPtr == NULL)
objPtr = Tcl_NewObj();
Tcl_ListObjAppendElement(interp, listObj, objPtr);
treeColumn = TreeColumn_Next(treeColumn);
if
(column != NULL)
column = column->next;
}
Tcl_SetObjResult(interp, listObj);
goto
okExit;
}
if
(objc == 5) {
if
(TreeItem_ColumnFromObj(tree, item, objv[4], &column, NULL, NULL,
CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
goto
errorExit;
}
if
((column != NULL) && (column->style != NULL) &&
!TreeStyle_IsHeaderStyle(tree, column->style)) {
objPtr = isImage ?
TreeStyle_GetImage(tree, column->style, &elem) :
TreeStyle_GetText(tree, column->style, &elem);
}
else
objPtr = NULL;
if
(doHeaders && elem == NULL)
objPtr = TreeHeaderColumn_GetImageOrText(item->header,
column->headerColumn, isImage);
if
(objPtr != NULL)
Tcl_SetObjResult(interp, objPtr);
goto
okExit;
}
if
((objc - 4) & 1) {
FormatResult(interp,
"missing argument after column \"%s\""
,
Tcl_GetString(objv[objc - 1]));
goto
errorExit;
}
STATIC_ALLOC(co,
struct
columnObj, objc / 2);
for
(i = 4; i < objc; i += 2) {
if
(TreeColumnList_FromObj(tree, objv[i], &co[count].columns,
CFO_NOT_NULL | tailFlag) != TCL_OK) {
result = TCL_ERROR;
goto
doneTEXT;
}
co[count].obj = objv[i + 1];
count++;
}
ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
int
changedI = FALSE;
for
(i = 0; i < count; i++) {
COLUMN_FOR_EACH(treeColumn, &co[i].columns, NULL, &citer) {
columnIndex = TreeColumn_Index(treeColumn);
column = TreeItem_FindColumn(tree, item, columnIndex);
if
((column == NULL) || (column->style == NULL) ||
TreeStyle_IsHeaderStyle(tree, column->style)) {
if
(doHeaders) {
result = TreeHeaderColumn_SetImageOrText(item->header,
column->headerColumn, treeColumn, co[i].obj, isImage);
if
(result != TCL_OK)
goto
doneTEXT;
continue
;
}
NoStyleMsg(tree, item, columnIndex);
result = TCL_ERROR;
goto
doneTEXT;
}
result = isImage ?
TreeStyle_SetImage(tree, item, column, column->style,
co[i].obj, &elem) :
TreeStyle_SetText(tree, item, column, column->style,
co[i].obj, &elem);
if
(result != TCL_OK)
goto
doneTEXT;
if
(elem == NULL) {
if
(doHeaders) {
result = TreeHeaderColumn_SetImageOrText(item->header,
column->headerColumn, treeColumn, co[i].obj, isImage);
if
(result != TCL_OK)
goto
doneTEXT;
}
}
else
{
TreeItemColumn_InvalidateSize(tree, column);
TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
changedI = TRUE;
}
}
}
if
(changedI) {
TreeItem_InvalidateHeight(tree, item);
Tree_FreeItemDInfo(tree, item, NULL);
changed = TRUE;
}
}
if
(changed && !doHeaders)
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
doneTEXT:
for
(i = 0; i < count; i++) {
TreeColumnList_Free(&co[i].columns);
}
STATIC_FREE(co,
struct
columnObj, objc / 2);
TreeItemList_Free(&itemList);
return
result;
okExit:
TreeItemList_Free(&itemList);
return
TCL_OK;
errorExit:
TreeItemList_Free(&itemList);
return
TCL_ERROR;
}
static
int
ItemImageCmd(
ClientData clientData,
Tcl_Interp *interp,
int
objc,
Tcl_Obj *CONST objv[]
)
{
TreeCtrl *tree = clientData;
return
TreeItemCmd_ImageOrText(tree, objc, objv, TRUE, FALSE);
}
static
int
ItemTextCmd(
ClientData clientData,
Tcl_Interp *interp,
int
objc,
Tcl_Obj *CONST objv[]
)
{
TreeCtrl *tree = clientData;
return
TreeItemCmd_ImageOrText(tree, objc, objv, FALSE, FALSE);
}
#define STABLE_SORT
struct
SortItem1
{
long
longValue;
double
doubleValue;
char
*string;
};
struct
SortItem
{
TreeItem item;
struct
SortItem1 *item1;
Tcl_Obj *obj;
#ifdef STABLE_SORT
int
index;
#endif
};
typedef
struct
SortData SortData;
struct
SortElement
{
TreeStyle style;
TreeElement elem;
int
elemIndex;
};
struct
SortColumn
{
int
(*proc)(SortData *,
struct
SortItem *,
struct
SortItem *,
int
);
int
sortBy;
int
column;
int
order;
Tcl_Obj *command;
struct
SortElement elems[20];
int
elemCount;
};
struct
SortData
{
TreeCtrl *tree;
struct
SortItem *items;
struct
SortItem1 *item1s;
#define MAX_SORT_COLUMNS 40
struct
SortColumn columns[MAX_SORT_COLUMNS];
int
columnCount;
int
result;
};
static
int
DictionaryCompare(
char
*left,
char
*right
)
{
Tcl_UniChar uniLeft, uniRight, uniLeftLower, uniRightLower;
int
diff, zeros;
int
secondaryDiff = 0;
while
(1) {
if
(
isdigit
(
UCHAR
(*right)) &&
isdigit
(
UCHAR
(*left))) {
zeros = 0;
while
((*right ==
'0'
) && (
isdigit
(
UCHAR
(right[1])))) {
right++;
zeros--;
}
while
((*left ==
'0'
) && (
isdigit
(
UCHAR
(left[1])))) {
left++;
zeros++;
}
if
(secondaryDiff == 0) {
secondaryDiff = zeros;
}
diff = 0;
while
(1) {
if
(diff == 0) {
diff =
UCHAR
(*left) -
UCHAR
(*right);
}
right++;
left++;
if
(!
isdigit
(
UCHAR
(*right))) {
if
(
isdigit
(
UCHAR
(*left))) {
return
1;
}
else
{
if
(diff != 0) {
return
diff;
}
break
;
}
}
else
if
(!
isdigit
(
UCHAR
(*left))) {
return
-1;
}
}
continue
;
}
if
((*left !=
'\0'
) && (*right !=
'\0'
)) {
left += Tcl_UtfToUniChar(left, &uniLeft);
right += Tcl_UtfToUniChar(right, &uniRight);
uniLeftLower = Tcl_UniCharToLower(uniLeft);
uniRightLower = Tcl_UniCharToLower(uniRight);
}
else
{
diff =
UCHAR
(*left) -
UCHAR
(*right);
break
;
}
diff = uniLeftLower - uniRightLower;
if
(diff) {
return
diff;
}
else
if
(secondaryDiff == 0) {
if
(Tcl_UniCharIsUpper(uniLeft) &&
Tcl_UniCharIsLower(uniRight)) {
secondaryDiff = -1;
}
else
if
(Tcl_UniCharIsUpper(uniRight) &&
Tcl_UniCharIsLower(uniLeft)) {
secondaryDiff = 1;
}
}
}
if
(diff == 0) {
diff = secondaryDiff;
}
return
diff;
}
static
int
CompareAscii(
SortData *sortData,
struct
SortItem *a,
struct
SortItem *b,
int
n
)
{
char
*left = a->item1[n].string;
char
*right = b->item1[n].string;
if
(left == NULL) {
return
((right == NULL) ? 0 : (0 -
UCHAR
(*right)));
}
else
if
(right == NULL) {
return
UCHAR
(*left);
}
else
{
return
strcmp
(left, right);
}
}
static
int
CompareDict(
SortData *sortData,
struct
SortItem *a,
struct
SortItem *b,
int
n
)
{
char
*left = a->item1[n].string;
char
*right = b->item1[n].string;
if
(left == NULL) {
return
((right == NULL) ? 0 : (0 -
UCHAR
(*right)));
}
else
if
(right == NULL) {
return
UCHAR
(*left);
}
else
{
return
DictionaryCompare(left, right);
}
}
static
int
CompareDouble(
SortData *sortData,
struct
SortItem *a,
struct
SortItem *b,
int
n
)
{
return
(a->item1[n].doubleValue < b->item1[n].doubleValue) ? -1 :
((a->item1[n].doubleValue == b->item1[n].doubleValue) ? 0 : 1);
}
static
int
CompareLong(
SortData *sortData,
struct
SortItem *a,
struct
SortItem *b,
int
n
)
{
return
(a->item1[n].longValue < b->item1[n].longValue) ? -1 :
((a->item1[n].longValue == b->item1[n].longValue) ? 0 : 1);
}
static
int
CompareCmd(
SortData *sortData,
struct
SortItem *a,
struct
SortItem *b,
int
n
)
{
Tcl_Interp *interp = sortData->tree->interp;
Tcl_Obj **objv, *paramObjv[2];
int
objc, v;
paramObjv[0] = a->obj;
paramObjv[1] = b->obj;
Tcl_ListObjLength(interp, sortData->columns[n].command, &objc);
Tcl_ListObjReplace(interp, sortData->columns[n].command, objc - 2,
2, 2, paramObjv);
Tcl_ListObjGetElements(interp, sortData->columns[n].command,
&objc, &objv);
sortData->result = Tcl_EvalObjv(interp, objc, objv, 0);
if
(sortData->result != TCL_OK) {
Tcl_AddErrorInfo(interp,
"\n (evaluating item sort -command)"
);
return
0;
}
sortData->result = Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &v);
if
(sortData->result != TCL_OK) {
Tcl_ResetResult(interp);
Tcl_AppendToObj(Tcl_GetObjResult(interp),
"-command returned non-numeric result"
, -1);
return
0;
}
return
v;
}
static
int
CompareProc(
SortData *sortData,
struct
SortItem *a,
struct
SortItem *b
)
{
int
i, v;
if
(a->item == b->item)
return
0;
for
(i = 0; i < sortData->columnCount; i++) {
v = (*sortData->columns[i].proc)(sortData, a, b, i);
if
(sortData->result != TCL_OK)
return
0;
if
(v != 0) {
if
(i && (sortData->columns[i].order != sortData->columns[0].order))
v *= -1;
return
v;
}
}
#ifdef STABLE_SORT
return
((a->index < b->index) == sortData->columns[0].order) ? -1 : 1;
#else
return
0;
#endif
}
static
int
find_pivot(
SortData *sortData,
struct
SortItem *left,
struct
SortItem *right,
struct
SortItem *pivot
)
{
struct
SortItem *a, *b, *c, *p, *tmp;
int
v;
a = left;
b = (left + (right - left) / 2);
c = right;
v = CompareProc(sortData, a, b);
if
(sortData->result != TCL_OK)
return
0;
if
(v > 0) { tmp = a; a = b; b = tmp; }
v = CompareProc(sortData, a, c);
if
(sortData->result != TCL_OK)
return
0;
if
(v > 0) { tmp = a; a = c; c = tmp; }
v = CompareProc(sortData, b, c);
if
(sortData->result != TCL_OK)
return
0;
if
(v > 0) { tmp = b; b = c; c = tmp; }
v = CompareProc(sortData, a, b);
if
(sortData->result != TCL_OK)
return
0;
if
(v < 0) {
(*pivot) = *b;
return
1;
}
v = CompareProc(sortData, b, c);
if
(sortData->result != TCL_OK)
return
0;
if
(v < 0) {
(*pivot) = *c;
return
1;
}
for
(p = left + 1; p <= right; p++) {
int
v = CompareProc(sortData, p, left);
if
(sortData->result != TCL_OK)
return
0;
if
(v != 0) {
(*pivot) = (v < 0) ? *left : *p;
return
1;
}
}
return
0;
}
#define BUGGY_COMMAND
static
struct
SortItem *
partition(
SortData *sortData,
struct
SortItem *left,
struct
SortItem *right,
struct
SortItem *pivot
)
{
int
v;
#ifdef BUGGY_COMMAND
struct
SortItem *min = left, *max = right;
#endif
while
(left <= right) {
while
(1) {
v = CompareProc(sortData, left, pivot);
if
(sortData->result != TCL_OK)
return
NULL;
if
(v >= 0)
break
;
#ifdef BUGGY_COMMAND
if
(left == max)
goto
buggy;
#endif
left++;
}
while
(1) {
v = CompareProc(sortData, right, pivot);
if
(sortData->result != TCL_OK)
return
NULL;
if
(v < 0)
break
;
#ifdef BUGGY_COMMAND
if
(right == min)
goto
buggy;
#endif
right--;
}
if
(left < right) {
struct
SortItem tmp = *left;
*left = *right;
*right = tmp;
left++;
right--;
}
}
return
left;
#ifdef BUGGY_COMMAND
buggy:
FormatResult(sortData->tree->interp,
"buggy item sort -command detected"
);
sortData->result = TCL_ERROR;
return
NULL;
#endif
}
static
void
quicksort(
SortData *sortData,
struct
SortItem *left,
struct
SortItem *right
)
{
struct
SortItem *p, pivot;
if
(sortData->result != TCL_OK)
return
;
if
(left == right)
return
;
if
(find_pivot(sortData, left, right, &pivot) == 1) {
p = partition(sortData, left, right, &pivot);
quicksort(sortData, left, p - 1);
quicksort(sortData, p, right);
}
}
static
int
ItemSortCmd(
ClientData clientData,
Tcl_Interp *interp,
int
objc,
Tcl_Obj *CONST objv[]
)
{
TreeCtrl *tree = clientData;
TreeItem item, first, last, walk, lastChild;
TreeItemColumn column;
int
i, j, count, elemIndex, index, indexF = 0, indexL = 0;
int
sawColumn = FALSE, sawCmd = FALSE;
static
int
(*sortProc[5])(SortData *,
struct
SortItem *,
struct
SortItem *,
int
) =
{ CompareAscii, CompareDict, CompareDouble, CompareLong, CompareCmd };
SortData sortData;
TreeColumn treeColumn;
struct
SortElement *elemPtr;
int
notReally = FALSE;
int
result = TCL_OK;
if
(objc < 4) {
Tcl_WrongNumArgs(interp, 3, objv,
"item ?option ...?"
);
return
TCL_ERROR;
}
if
(TreeItem_FromObj(tree, objv[3], &item, IFO_NOT_NULL) != TCL_OK)
return
TCL_ERROR;
if
(item->numChildren < 1)
return
TCL_OK;
sortData.tree = tree;
sortData.columnCount = 1;
sortData.columns[0].column = 0;
sortData.columns[0].sortBy = SORT_ASCII;
sortData.columns[0].order = 1;
sortData.columns[0].elemCount = 0;
sortData.result = TCL_OK;
first = item->firstChild;
last = item->lastChild;
for
(i = 4; i < objc; ) {
static
CONST
char
*optionName[] = {
"-ascii"
,
"-column"
,
"-command"
,
"-decreasing"
,
"-dictionary"
,
"-element"
,
"-first"
,
"-increasing"
,
"-integer"
,
"-last"
,
"-notreally"
,
"-real"
, NULL };
int
numArgs[] = { 1, 2, 2, 1, 1, 2, 2, 1, 1, 2, 1, 1 };
enum
{ OPT_ASCII, OPT_COLUMN, OPT_COMMAND, OPT_DECREASING, OPT_DICT,
OPT_ELEMENT, OPT_FIRST, OPT_INCREASING, OPT_INTEGER, OPT_LAST,
OPT_NOT_REALLY, OPT_REAL };
if
(Tcl_GetIndexFromObj(interp, objv[i], optionName,
"option"
, 0,
&index) != TCL_OK)
return
TCL_ERROR;
if
(objc - i < numArgs[index]) {
FormatResult(interp,
"missing value for \"%s\" option"
,
optionName[index]);
return
TCL_ERROR;
}
switch
(index) {
case
OPT_ASCII:
sortData.columns[sortData.columnCount - 1].sortBy = SORT_ASCII;
break
;
case
OPT_COLUMN:
if
(TreeColumn_FromObj(tree, objv[i + 1], &treeColumn,
CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK)
return
TCL_ERROR;
if
(sawColumn) {
if
(sortData.columnCount + 1 > MAX_SORT_COLUMNS) {
FormatResult(interp,
"can't compare more than %d columns"
,
MAX_SORT_COLUMNS);
return
TCL_ERROR;
}
sortData.columnCount++;
sortData.columns[sortData.columnCount - 1].sortBy = SORT_ASCII;
sortData.columns[sortData.columnCount - 1].order = 1;
sortData.columns[sortData.columnCount - 1].elemCount = 0;
}
sortData.columns[sortData.columnCount - 1].column = TreeColumn_Index(treeColumn);
sawColumn = TRUE;
break
;
case
OPT_COMMAND:
sortData.columns[sortData.columnCount - 1].command = objv[i + 1];
sortData.columns[sortData.columnCount - 1].sortBy = SORT_COMMAND;
sawCmd = TRUE;
break
;
case
OPT_DECREASING:
sortData.columns[sortData.columnCount - 1].order = 0;
break
;
case
OPT_DICT:
sortData.columns[sortData.columnCount - 1].sortBy = SORT_DICT;
break
;
case
OPT_ELEMENT: {
int
listObjc;
Tcl_Obj **listObjv;
if
(Tcl_ListObjGetElements(interp, objv[i + 1], &listObjc,
&listObjv) != TCL_OK)
return
TCL_ERROR;
elemPtr = sortData.columns[sortData.columnCount - 1].elems;
sortData.columns[sortData.columnCount - 1].elemCount = 0;
if
(listObjc == 0) {
}
else
if
(listObjc == 1) {
if
(TreeElement_FromObj(tree, listObjv[0], &elemPtr->elem)
!= TCL_OK) {
Tcl_AddErrorInfo(interp,
"\n (processing -element option)"
);
return
TCL_ERROR;
}
if
(!TreeElement_IsType(tree, elemPtr->elem,
"text"
)) {
FormatResult(interp,
"element %s is not of type \"text\""
,
Tcl_GetString(listObjv[0]));
Tcl_AddErrorInfo(interp,
"\n (processing -element option)"
);
return
TCL_ERROR;
}
elemPtr->style = NULL;
elemPtr->elemIndex = -1;
sortData.columns[sortData.columnCount - 1].elemCount++;
}
else
{
if
(listObjc & 1) {
FormatResult(interp,
"list must have even number of elements"
);
Tcl_AddErrorInfo(interp,
"\n (processing -element option)"
);
return
TCL_ERROR;
}
for
(j = 0; j < listObjc; j += 2) {
if
((TreeStyle_FromObj(tree, listObjv[j],
&elemPtr->style) != TCL_OK) ||
(TreeElement_FromObj(tree, listObjv[j + 1],
&elemPtr->elem) != TCL_OK) ||
(TreeStyle_FindElement(tree, elemPtr->style,
elemPtr->elem, &elemPtr->elemIndex) != TCL_OK)) {
Tcl_AddErrorInfo(interp,
"\n (processing -element option)"
);
return
TCL_ERROR;
}
if
(!TreeElement_IsType(tree, elemPtr->elem,
"text"
)) {
FormatResult(interp,
"element %s is not of type \"text\""
,
Tcl_GetString(listObjv[j + 1]));
Tcl_AddErrorInfo(interp,
"\n (processing -element option)"
);
return
TCL_ERROR;
}
sortData.columns[sortData.columnCount - 1].elemCount++;
elemPtr++;
}
}
break
;
}
case
OPT_FIRST:
if
(TreeItem_FromObj(tree, objv[i + 1], &first, IFO_NOT_NULL) != TCL_OK)
return
TCL_ERROR;
if
(first->parent != item) {
FormatResult(interp,
"item %s%d is not a child of item %s%d"
,
tree->itemPrefix, first->id, tree->itemPrefix, item->id);
return
TCL_ERROR;
}
break
;
case
OPT_INCREASING:
sortData.columns[sortData.columnCount - 1].order = 1;
break
;
case
OPT_INTEGER:
sortData.columns[sortData.columnCount - 1].sortBy = SORT_LONG;
break
;
case
OPT_LAST:
if
(TreeItem_FromObj(tree, objv[i + 1], &last, IFO_NOT_NULL) != TCL_OK)
return
TCL_ERROR;
if
(last->parent != item) {
FormatResult(interp,
"item %s%d is not a child of item %s%d"
,
tree->itemPrefix, last->id, tree->itemPrefix, item->id);
return
TCL_ERROR;
}
break
;
case
OPT_NOT_REALLY:
notReally = TRUE;
break
;
case
OPT_REAL:
sortData.columns[sortData.columnCount - 1].sortBy = SORT_DOUBLE;
break
;
}
i += numArgs[index];
}
if
((tree->columnCount < 1) && (sortData.columns[0].sortBy != SORT_COMMAND)) {
FormatResult(interp,
"there are no columns"
);
return
TCL_ERROR;
}
if
(first == last) {
if
(notReally)
Tcl_SetObjResult(interp, TreeItem_ToObj(tree, first));
return
TCL_OK;
}
for
(i = 0; i < sortData.columnCount; i++) {
sortData.columns[i].proc = sortProc[sortData.columns[i].sortBy];
if
(sortData.columns[i].sortBy == SORT_COMMAND) {
Tcl_Obj *obj = Tcl_DuplicateObj(sortData.columns[i].command);
Tcl_Obj *obj2 = Tcl_NewObj();
Tcl_IncrRefCount(obj);
if
(Tcl_ListObjAppendElement(interp, obj, obj2) != TCL_OK) {
Tcl_DecrRefCount(obj);
Tcl_IncrRefCount(obj2);
Tcl_DecrRefCount(obj2);
for
(j = 0; j < i; j++) {
if
(sortData.columns[j].sortBy == SORT_COMMAND) {
Tcl_DecrRefCount(sortData.columns[j].command);
}
}
return
TCL_ERROR;
}
(
void
) Tcl_ListObjAppendElement(interp, obj, obj2);
sortData.columns[i].command = obj;
}
}
index = 0;
walk = item->firstChild;
while
(walk != NULL) {
if
(walk == first)
indexF = index;
if
(walk == last)
indexL = index;
index++;
walk = walk->nextSibling;
}
if
(indexF > indexL) {
walk = last;
last = first;
first = walk;
index = indexL;
indexL = indexF;
indexF = index;
}
count = indexL - indexF + 1;
sortData.item1s = (
struct
SortItem1 *) ckalloc(
sizeof
(
struct
SortItem1) * count * sortData.columnCount);
sortData.items = (
struct
SortItem *) ckalloc(
sizeof
(
struct
SortItem) * count);
for
(i = 0; i < count; i++) {
sortData.items[i].item1 = sortData.item1s + i * sortData.columnCount;
sortData.items[i].obj = NULL;
}
index = 0;
walk = first;
while
(walk != last->nextSibling) {
struct
SortItem *sortItem = &sortData.items[index];
sortItem->item = walk;
#ifdef STABLE_SORT
sortItem->index = index;
#endif
if
(sawCmd) {
Tcl_Obj *obj = TreeItem_ToObj(tree, walk);
Tcl_IncrRefCount(obj);
sortData.items[index].obj = obj;
}
for
(i = 0; i < sortData.columnCount; i++) {
struct
SortItem1 *sortItem1 = sortItem->item1 + i;
if
(sortData.columns[i].sortBy == SORT_COMMAND)
continue
;
column = TreeItem_FindColumn(tree, walk, sortData.columns[i].column);
if
((column == NULL) || (column->style == NULL)) {
NoStyleMsg(tree, walk, sortData.columns[i].column);
result = TCL_ERROR;
goto
done;
}
if
(sortData.columns[i].elemCount == 0)
elemIndex = -1;
else
if
((sortData.columns[i].elemCount == 1) &&
(sortData.columns[i].elems[0].style == NULL)) {
if
(TreeStyle_FindElement(tree, column->style,
sortData.columns[i].elems[0].elem, &elemIndex) != TCL_OK) {
result = TCL_ERROR;
goto
done;
}
}
else
{
TreeStyle masterStyle = TreeStyle_GetMaster(tree, column->style);
elemIndex = -1;
for
(j = sortData.columns[i].elemCount - 1; j >= 0; j--) {
if
(sortData.columns[i].elems[j].style == masterStyle) {
elemIndex = sortData.columns[i].elems[j].elemIndex;
break
;
}
}
}
if
(TreeStyle_GetSortData(tree, column->style, elemIndex,
sortData.columns[i].sortBy,
&sortItem1->longValue,
&sortItem1->doubleValue,
&sortItem1->string) != TCL_OK) {
char
msg[128];
sprintf
(msg,
"\n (preparing to sort item %s%d column %s%d)"
,
tree->itemPrefix, walk->id,
tree->columnPrefix, TreeColumn_GetID(
Tree_FindColumn(tree, sortData.columns[i].column)));
Tcl_AddErrorInfo(interp, msg);
result = TCL_ERROR;
goto
done;
}
}
index++;
walk = walk->nextSibling;
}
quicksort(&sortData, sortData.items, sortData.items + count - 1);
if
(sortData.result != TCL_OK) {
result = sortData.result;
goto
done;
}
if
(sawCmd)
Tcl_ResetResult(interp);
if
(notReally) {
Tcl_Obj *listObj = Tcl_NewListObj(0, NULL);
Tcl_Obj *itemObj;
if
(sortData.columns[0].order == 1) {
for
(i = 0; i < count; i++) {
itemObj = sortData.items[i].obj;
if
(itemObj == NULL)
itemObj = TreeItem_ToObj(tree,
sortData.items[i].item);
Tcl_ListObjAppendElement(interp, listObj, itemObj);
}
}
else
{
for
(i = count - 1; i >= 0; i--) {
itemObj = sortData.items[i].obj;
if
(itemObj == NULL)
itemObj = TreeItem_ToObj(tree,
sortData.items[i].item);
Tcl_ListObjAppendElement(interp, listObj, itemObj);
}
}
Tcl_SetObjResult(interp, listObj);
goto
done;
}
first = first->prevSibling;
last = last->nextSibling;
if
(sortData.columns[0].order == 1) {
for
(i = 0; i < count - 1; i++) {
sortData.items[i].item->nextSibling = sortData.items[i + 1].item;
sortData.items[i + 1].item->prevSibling = sortData.items[i].item;
}
indexF = 0;
indexL = count - 1;
}
else
{
for
(i = count - 1; i > 0; i--) {
sortData.items[i].item->nextSibling = sortData.items[i - 1].item;
sortData.items[i - 1].item->prevSibling = sortData.items[i].item;
}
indexF = count - 1;
indexL = 0;
}
lastChild = item->lastChild;
sortData.items[indexF].item->prevSibling = first;
if
(first)
first->nextSibling = sortData.items[indexF].item;
else
item->firstChild = sortData.items[indexF].item;
sortData.items[indexL].item->nextSibling = last;
if
(last)
last->prevSibling = sortData.items[indexL].item;
else
item->lastChild = sortData.items[indexL].item;
if
((item->lastChild != lastChild) && tree->showLines && (tree->columnTree != NULL)) {
if
(lastChild->dInfo != NULL)
Tree_InvalidateItemDInfo(tree, tree->columnTree,
lastChild,
NULL);
if
(item->lastChild->dInfo != NULL)
Tree_InvalidateItemDInfo(tree, tree->columnTree,
item->lastChild,
NULL);
}
tree->updateIndex = 1;
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
done:
for
(i = 0; i < count; i++) {
if
(sortData.items[i].obj != NULL) {
Tcl_DecrRefCount(sortData.items[i].obj);
}
}
for
(i = 0; i < sortData.columnCount; i++) {
if
(sortData.columns[i].sortBy == SORT_COMMAND) {
Tcl_DecrRefCount(sortData.columns[i].command);
}
}
ckfree((
char
*) sortData.item1s);
ckfree((
char
*) sortData.items);
if
(tree->debug.enable && tree->debug.data) {
Tree_Debug(tree);
}
return
result;
}
static
int
TILSCompare(
CONST
VOID
*first_,
CONST
VOID
*second_
)
{
TreeItem first = *(TreeItem *) first_;
TreeItem second = *(TreeItem *) second_;
return
first->index - second->index;
}
void
TreeItemList_Sort(
TreeItemList *items
)
{
Tree_UpdateItemIndex(items->tree);
qsort
((
VOID
*) TreeItemList_Items(items),
(
size_t
) TreeItemList_Count(items),
sizeof
(TreeItem),
TILSCompare);
}
int
TreeItemCmd_Span(
TreeCtrl *tree,
int
objc,
Tcl_Obj *CONST objv[],
int
doHeaders
)
{
Tcl_Interp *interp = tree->interp;
TreeColumn treeColumn = tree->columns;
TreeItemList itemList;
TreeItem item;
TreeItemColumn column;
Tcl_Obj *listObj;
struct
columnSpan {
TreeColumnList columns;
int
span;
} staticCS[STATIC_SIZE], *cs = staticCS;
int
i, count = 0, span, changed = FALSE;
ItemForEach iter;
ColumnForEach citer;
int
flags = 0, result = TCL_OK;
if
(objc < 4) {
Tcl_WrongNumArgs(interp, 3, objv,
doHeaders ?
"header ?column? ?span? ?column span ...?"
:
"item ?column? ?span? ?column span ...?"
);
return
TCL_ERROR;
}
if
(objc < 6)
flags |= IFO_NOT_NULL | IFO_NOT_MANY;
if
(doHeaders) {
if
(TreeHeaderList_FromObj(tree, objv[3], &itemList, flags) != TCL_OK)
return
TCL_ERROR;
}
else
{
if
(TreeItemList_FromObj(tree, objv[3], &itemList, flags) != TCL_OK)
return
TCL_ERROR;
}
item = TreeItemList_Nth(&itemList, 0);
if
(objc == 4) {
listObj = Tcl_NewListObj(0, NULL);
column = item->columns;
while
(treeColumn != NULL) {
Tcl_ListObjAppendElement(interp, listObj,
Tcl_NewIntObj(column ? column->span : 1));
treeColumn = TreeColumn_Next(treeColumn);
if
(column != NULL)
column = column->next;
}
Tcl_SetObjResult(interp, listObj);
goto
okExit;
}
if
(objc == 5) {
if
(TreeItem_ColumnFromObj(tree, item, objv[4], &column, NULL, NULL,
CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
goto
errorExit;
}
Tcl_SetObjResult(interp, Tcl_NewIntObj(column ? column->span : 1));
goto
okExit;
}
if
(objc & 1) {
FormatResult(interp,
"missing argument after column \"%s\""
,
Tcl_GetString(objv[objc - 1]));
goto
errorExit;
}
STATIC_ALLOC(cs,
struct
columnSpan, objc / 2);
for
(i = 4; i < objc; i += 2) {
if
(TreeColumnList_FromObj(tree, objv[i], &cs[count].columns,
CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
result = TCL_ERROR;
goto
doneSPAN;
}
if
(Tcl_GetIntFromObj(interp, objv[i + 1], &span) != TCL_OK) {
result = TCL_ERROR;
goto
doneSPAN;
}
if
(span <= 0) {
FormatResult(interp,
"bad span \"%d\": must be > 0"
, span);
result = TCL_ERROR;
goto
doneSPAN;
}
cs[count].span = span;
count++;
}
ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
int
changedI = FALSE;
for
(i = 0; i < count; i++) {
COLUMN_FOR_EACH(treeColumn, &cs[i].columns, NULL, &citer) {
column = Item_CreateColumn(tree, item,
TreeColumn_Index(treeColumn), NULL);
if
(column->span != cs[i].span) {
if
(cs[i].span > 1) {
item->flags &= ~ITEM_FLAG_SPANS_SIMPLE;
}
TreeItem_SpansInvalidate(tree, item);
column->span = cs[i].span;
TreeItemColumn_InvalidateSize(tree, column);
changedI = TRUE;
TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
}
}
}
if
(changedI) {
TreeItem_InvalidateHeight(tree, item);
Tree_FreeItemDInfo(tree, item, NULL);
changed = TRUE;
}
}
if
(changed && !doHeaders)
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
doneSPAN:
for
(i = 0; i < count; i++) {
TreeColumnList_Free(&cs[i].columns);
}
STATIC_FREE(cs,
struct
columnSpan, objc / 2);
TreeItemList_Free(&itemList);
return
result;
okExit:
TreeItemList_Free(&itemList);
return
TCL_OK;
errorExit:
TreeItemList_Free(&itemList);
return
TCL_ERROR;
}
static
int
ItemSpanCmd(
ClientData clientData,
Tcl_Interp *interp,
int
objc,
Tcl_Obj *CONST objv[]
)
{
TreeCtrl *tree = clientData;
return
TreeItemCmd_Span(tree, objc, objv, FALSE);
}
int
TreeItemCmd_State(
TreeCtrl *tree,
int
objc,
Tcl_Obj *CONST objv[],
int
doHeaders
)
{
Tcl_Interp *interp = tree->interp;
int
domain = doHeaders ? STATE_DOMAIN_HEADER : STATE_DOMAIN_ITEM;
TreeStateDomain *domainPtr = &tree->stateDomain[domain];
int
tailFlag = doHeaders ? 0 : CFO_NOT_TAIL;
static
CONST
char
*commandNames[] = {
"define"
,
"forcolumn"
,
"get"
,
"linkage"
,
"names"
,
"set"
,
"undefine"
,
(
char
*) NULL
};
enum
{
COMMAND_DEFINE, COMMAND_FORCOLUMN, COMMAND_GET, COMMAND_LINKAGE,
COMMAND_NAMES, COMMAND_SET, COMMAND_UNDEFINE
};
int
index;
TreeItem item;
if
(objc < 4) {
Tcl_WrongNumArgs(interp, 3, objv,
doHeaders ?
"command header ?arg ...?"
:
"command item ?arg ...?"
);
return
TCL_ERROR;
}
if
(Tcl_GetIndexFromObj(interp, objv[3], commandNames,
"command"
, 0,
&index) != TCL_OK)
return
TCL_ERROR;
switch
(index) {
case
COMMAND_DEFINE: {
char
*string;
int
i, length, slot = -1;
if
(objc != 5) {
Tcl_WrongNumArgs(interp, 4, objv,
"stateName"
);
return
TCL_ERROR;
}
string = Tcl_GetStringFromObj(objv[4], &length);
if
(!length || (*string ==
'~'
) || (*string ==
'!'
)) {
FormatResult(interp,
"invalid state name \"%s\""
, string);
return
TCL_ERROR;
}
for
(i = 0; i < 32; i++) {
if
(domainPtr->stateNames[i] == NULL) {
if
(slot == -1)
slot = i;
continue
;
}
if
(
strcmp
(domainPtr->stateNames[i], string) == 0) {
FormatResult(interp,
"state \"%s\" already defined in domain \"%s\""
, string, domainPtr->name);
return
TCL_ERROR;
}
}
if
(slot == -1) {
FormatResult(interp,
"cannot define any more states in domain \"%s\""
, domainPtr->name);
return
TCL_ERROR;
}
domainPtr->stateNames[slot] = ckalloc(length + 1);
strcpy
(domainPtr->stateNames[slot], string);
break
;
}
case
COMMAND_FORCOLUMN: {
TreeItemList itemList;
TreeColumnList columns;
TreeColumn treeColumn;
Tcl_Obj *listObj;
TreeItemColumn column;
int
columnIndex;
int
i, states[3], stateOn, stateOff;
ItemForEach iter;
ColumnForEach citer;
int
flags = IFO_NOT_NULL;
int
result = TCL_OK;
if
(objc < 6 || objc > 7) {
Tcl_WrongNumArgs(interp, 4, objv,
doHeaders ?
"header column ?stateList?"
:
"item column ?stateList?"
);
return
TCL_ERROR;
}
if
(objc == 6)
flags |= IFO_NOT_MANY;
if
(doHeaders) {
if
(TreeHeaderList_FromObj(tree, objv[4], &itemList, flags)
!= TCL_OK)
return
TCL_ERROR;
}
else
{
if
(TreeItemList_FromObj(tree, objv[4], &itemList, flags)
!= TCL_OK)
return
TCL_ERROR;
}
TreeColumnList_Init(tree, &columns, 0);
if
(objc == 6) {
item = TreeItemList_Nth(&itemList, 0);
if
(TreeItem_ColumnFromObj(tree, item, objv[5], &column,
NULL, &columnIndex, CFO_NOT_NULL | CFO_NOT_TAIL) != TCL_OK) {
result = TCL_ERROR;
goto
doneFORC;
}
if
((column == NULL) || !column->cstate)
goto
doneFORC;
listObj = Tcl_NewListObj(0, NULL);
for
(i = 0; i < 32; i++) {
if
(domainPtr->stateNames[i] == NULL)
continue
;
if
(column->cstate & (1L << i)) {
Tcl_ListObjAppendElement(interp, listObj,
Tcl_NewStringObj(domainPtr->stateNames[i], -1));
}
}
Tcl_SetObjResult(interp, listObj);
goto
doneFORC;
}
if
(TreeColumnList_FromObj(tree, objv[5], &columns,
CFO_NOT_NULL | tailFlag) != TCL_OK) {
result = TCL_ERROR;
goto
doneFORC;
}
if
(Tree_StateFromListObj(tree, domain, objv[6], states, SFO_NOT_STATIC) != TCL_OK) {
result = TCL_ERROR;
goto
doneFORC;
}
if
((states[0] | states[1] | states[2]) == 0)
goto
doneFORC;
ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
COLUMN_FOR_EACH(treeColumn, &columns, NULL, &citer) {
columnIndex = TreeColumn_Index(treeColumn);
column = Item_CreateColumn(tree, item, columnIndex, NULL);
stateOn = states[STATE_OP_ON];
stateOff = states[STATE_OP_OFF];
stateOn |= ~column->cstate & states[STATE_OP_TOGGLE];
stateOff |= column->cstate & states[STATE_OP_TOGGLE];
TreeItemColumn_ChangeState(tree, item, column, treeColumn,
stateOff, stateOn);
}
}
doneFORC:
TreeColumnList_Free(&columns);
TreeItemList_Free(&itemList);
return
result;
}
case
COMMAND_GET: {
Tcl_Obj *listObj;
int
i, states[3];
if
(objc < 5 || objc > 6) {
Tcl_WrongNumArgs(interp, 4, objv,
doHeaders ?
"header ?state?"
:
"item ?state?"
);
return
TCL_ERROR;
}
if
(doHeaders) {
TreeItemList itemList;
if
(TreeHeaderList_FromObj(tree, objv[4], &itemList, IFO_NOT_NULL | IFO_NOT_MANY) != TCL_OK)
return
TCL_ERROR;
item = TreeItemList_Nth(&itemList, 0);
TreeItemList_Free(&itemList);
}
else
{
if
(TreeItem_FromObj(tree, objv[4], &item, IFO_NOT_NULL) != TCL_OK)
return
TCL_ERROR;
}
if
(objc == 6) {
states[STATE_OP_ON] = 0;
if
(Tree_StateFromObj(tree, domain, objv[5], states, NULL,
SFO_NOT_OFF | SFO_NOT_TOGGLE) != TCL_OK)
return
TCL_ERROR;
Tcl_SetObjResult(interp,
Tcl_NewBooleanObj((item->state & states[STATE_OP_ON]) != 0));
break
;
}
listObj = Tcl_NewListObj(0, NULL);
for
(i = 0; i < 32; i++) {
if
(domainPtr->stateNames[i] == NULL)
continue
;
if
(item->state & (1L << i)) {
Tcl_ListObjAppendElement(interp, listObj,
Tcl_NewStringObj(domainPtr->stateNames[i], -1));
}
}
Tcl_SetObjResult(interp, listObj);
break
;
}
case
COMMAND_LINKAGE: {
int
index;
if
(objc != 5) {
Tcl_WrongNumArgs(interp, 3, objv,
"state"
);
return
TCL_ERROR;
}
if
(Tree_StateFromObj(tree, domain, objv[4], NULL, &index,
SFO_NOT_OFF | SFO_NOT_TOGGLE) != TCL_OK)
return
TCL_ERROR;
Tcl_SetObjResult(interp, Tcl_NewStringObj(
(index < domainPtr->staticCount) ?
"static"
:
"dynamic"
, -1));
break
;
}
case
COMMAND_NAMES: {
Tcl_Obj *listObj;
int
i;
if
(objc != 4) {
Tcl_WrongNumArgs(interp, 4, objv, (
char
*) NULL);
return
TCL_ERROR;
}
listObj = Tcl_NewListObj(0, NULL);
for
(i = domainPtr->staticCount; i < 32; i++) {
if
(domainPtr->stateNames[i] != NULL)
Tcl_ListObjAppendElement(interp, listObj,
Tcl_NewStringObj(domainPtr->stateNames[i], -1));
}
Tcl_SetObjResult(interp, listObj);
break
;
}
case
COMMAND_SET: {
TreeItemList itemList, item2List;
int
states[3], stateOn, stateOff;
ItemForEach iter;
int
result = TCL_OK;
if
(objc < 6 || objc > 7) {
Tcl_WrongNumArgs(interp, 4, objv,
doHeaders ?
"header ?last? stateList"
:
"item ?last? stateList"
);
return
TCL_ERROR;
}
if
(doHeaders) {
if
(TreeHeaderList_FromObj(tree, objv[4], &itemList, IFO_NOT_NULL) != TCL_OK)
return
TCL_ERROR;
}
else
{
if
(TreeItemList_FromObj(tree, objv[4], &itemList, IFO_NOT_NULL) != TCL_OK)
return
TCL_ERROR;
}
if
(objc == 6) {
TreeItemList_Init(tree, &item2List, 0);
}
if
(objc == 7) {
if
(TreeItemList_FromObj(tree, objv[5], &item2List, IFO_NOT_NULL) != TCL_OK) {
result = TCL_ERROR;
goto
doneSET;
}
}
if
(Tree_StateFromListObj(tree, domain, objv[objc - 1], states,
SFO_NOT_STATIC) != TCL_OK) {
result = TCL_ERROR;
goto
doneSET;
}
if
((states[0] | states[1] | states[2]) == 0)
goto
doneSET;
ITEM_FOR_EACH(item, &itemList, &item2List, &iter) {
stateOn = states[STATE_OP_ON];
stateOff = states[STATE_OP_OFF];
stateOn |= ~item->state & states[STATE_OP_TOGGLE];
stateOff |= item->state & states[STATE_OP_TOGGLE];
TreeItem_ChangeState(tree, item, stateOff, stateOn);
}
if
(iter.error)
result = TCL_ERROR;
doneSET:
TreeItemList_Free(&itemList);
TreeItemList_Free(&item2List);
return
result;
}
case
COMMAND_UNDEFINE: {
int
i, index;
for
(i = 4; i < objc; i++) {
if
(Tree_StateFromObj(tree, domain, objv[i], NULL, &index,
SFO_NOT_STATIC | SFO_NOT_OFF | SFO_NOT_TOGGLE) != TCL_OK)
return
TCL_ERROR;
Tree_UndefineState(tree, domain, 1L << index);
PerStateInfo_Undefine(tree, &pstBitmap, &tree->buttonBitmap,
domain, 1L << index);
PerStateInfo_Undefine(tree, &pstImage, &tree->buttonImage,
domain, 1L << index);
ckfree(domainPtr->stateNames[index]);
domainPtr->stateNames[index] = NULL;
}
break
;
}
}
return
TCL_OK;
}
static
int
ItemStateCmd(
ClientData clientData,
Tcl_Interp *interp,
int
objc,
Tcl_Obj *CONST objv[]
)
{
TreeCtrl *tree = clientData;
return
TreeItemCmd_State(tree, objc, objv, FALSE);
}
int
TreeItemCmd_Tag(
TreeCtrl *tree,
int
objc,
Tcl_Obj *CONST objv[],
int
doHeaders
)
{
Tcl_Interp *interp = tree->interp;
static
CONST
char
*commandNames[] = {
"add"
,
"expr"
,
"names"
,
"remove"
, (
char
*) NULL
};
enum
{
COMMAND_ADD, COMMAND_EXPR, COMMAND_NAMES, COMMAND_REMOVE
};
int
index;
ItemForEach iter;
TreeItemList items;
TreeItem item;
int
result = TCL_OK;
if
(objc < 4) {
Tcl_WrongNumArgs(interp, 3, objv,
"command ?arg arg ...?"
);
return
TCL_ERROR;
}
if
(Tcl_GetIndexFromObj(interp, objv[3], commandNames,
"command"
, 0,
&index) != TCL_OK) {
return
TCL_ERROR;
}
switch
(index) {
case
COMMAND_ADD: {
int
i, numTags;
Tcl_Obj **listObjv;
Tk_Uid staticTags[STATIC_SIZE], *tags = staticTags;
if
(objc != 6) {
Tcl_WrongNumArgs(interp, 4, objv,
doHeaders ?
"header tagList"
:
"item tagList"
);
return
TCL_ERROR;
}
if
(doHeaders) {
if
(TreeHeaderList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
return
TCL_ERROR;
}
}
else
{
if
(TreeItemList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
return
TCL_ERROR;
}
}
if
(Tcl_ListObjGetElements(interp, objv[5], &numTags, &listObjv) != TCL_OK) {
result = TCL_ERROR;
break
;
}
STATIC_ALLOC(tags, Tk_Uid, numTags);
for
(i = 0; i < numTags; i++) {
tags[i] = Tk_GetUid(Tcl_GetString(listObjv[i]));
}
ITEM_FOR_EACH(item, &items, NULL, &iter) {
item->tagInfo = TagInfo_Add(tree, item->tagInfo, tags, numTags);
}
STATIC_FREE(tags, Tk_Uid, numTags);
break
;
}
case
COMMAND_EXPR: {
TagExpr expr;
int
ok = TRUE;
if
(objc != 6) {
Tcl_WrongNumArgs(interp, 4, objv,
doHeaders ?
"header tagExpr"
:
"item tagExpr"
);
return
TCL_ERROR;
}
if
(doHeaders) {
if
(TreeHeaderList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
return
TCL_ERROR;
}
}
else
{
if
(TreeItemList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
return
TCL_ERROR;
}
}
if
(TagExpr_Init(tree, objv[5], &expr) != TCL_OK) {
result = TCL_ERROR;
break
;
}
ITEM_FOR_EACH(item, &items, NULL, &iter) {
if
(!TagExpr_Eval(&expr, item->tagInfo)) {
ok = FALSE;
break
;
}
}
TagExpr_Free(&expr);
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(ok));
break
;
}
case
COMMAND_NAMES: {
Tcl_Obj *listObj;
Tk_Uid *tags = NULL;
int
i, tagSpace = 0, numTags = 0;
if
(objc != 5) {
Tcl_WrongNumArgs(interp, 4, objv, doHeaders ?
"header"
:
"item"
);
return
TCL_ERROR;
}
if
(doHeaders) {
if
(TreeHeaderList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
return
TCL_ERROR;
}
}
else
{
if
(TreeItemList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
return
TCL_ERROR;
}
}
ITEM_FOR_EACH(item, &items, NULL, &iter) {
tags = TagInfo_Names(tree, item->tagInfo, tags, &numTags, &tagSpace);
}
if
(numTags) {
listObj = Tcl_NewListObj(0, NULL);
for
(i = 0; i < numTags; i++) {
Tcl_ListObjAppendElement(NULL, listObj,
Tcl_NewStringObj((
char
*) tags[i], -1));
}
Tcl_SetObjResult(interp, listObj);
ckfree((
char
*) tags);
}
break
;
}
case
COMMAND_REMOVE: {
int
i, numTags;
Tcl_Obj **listObjv;
Tk_Uid staticTags[STATIC_SIZE], *tags = staticTags;
if
(objc != 6) {
Tcl_WrongNumArgs(interp, 4, objv,
doHeaders ?
"header tagList"
:
"item tagList"
);
return
TCL_ERROR;
}
if
(doHeaders) {
if
(TreeHeaderList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
return
TCL_ERROR;
}
}
else
{
if
(TreeItemList_FromObj(tree, objv[4], &items, IFO_NOT_NULL) != TCL_OK) {
return
TCL_ERROR;
}
}
if
(Tcl_ListObjGetElements(interp, objv[5], &numTags, &listObjv) != TCL_OK) {
result = TCL_ERROR;
break
;
}
STATIC_ALLOC(tags, Tk_Uid, numTags);
for
(i = 0; i < numTags; i++) {
tags[i] = Tk_GetUid(Tcl_GetString(listObjv[i]));
}
ITEM_FOR_EACH(item, &items, NULL, &iter) {
item->tagInfo = TagInfo_Remove(tree, item->tagInfo, tags, numTags);
}
STATIC_FREE(tags, Tk_Uid, numTags);
break
;
}
}
TreeItemList_Free(&items);
return
result;
}
static
int
ItemTagCmd(
ClientData clientData,
Tcl_Interp *interp,
int
objc,
Tcl_Obj *CONST objv[]
)
{
TreeCtrl *tree = clientData;
return
TreeItemCmd_Tag(tree, objc, objv, FALSE);
}
TagInfo *
TreeItem_GetTagInfo(
TreeCtrl *tree,
TreeItem item
)
{
return
item->tagInfo;
}
#ifdef SELECTION_VISIBLE
void
Tree_DeselectHidden(
TreeCtrl *tree
)
{
TreeItemList items;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
TreeItem item;
int
i;
if
(tree->selectCount < 1)
return
;
Tree_UpdateItemIndex(tree);
TreeItemList_Init(tree, &items, tree->selectCount);
hPtr = Tcl_FirstHashEntry(&tree->selection, &search);
while
(hPtr != NULL) {
item = (TreeItem) Tcl_GetHashKey(&tree->selection, hPtr);
if
(!TreeItem_ReallyVisible(tree, item))
TreeItemList_Append(&items, item);
hPtr = Tcl_NextHashEntry(&search);
}
for
(i = 0; i < TreeItemList_Count(&items); i++)
Tree_RemoveFromSelection(tree, TreeItemList_Nth(&items, i));
if
(TreeItemList_Count(&items)) {
TreeNotify_Selection(tree, NULL, &items);
}
TreeItemList_Free(&items);
}
#endif /* SELECTION_VISIBLE */
int
TreeItemCmd(
ClientData clientData,
Tcl_Interp *interp,
int
objc,
Tcl_Obj *CONST objv[]
)
{
TreeCtrl *tree = clientData;
enum
{
COMMAND_ANCESTORS,
COMMAND_BBOX,
COMMAND_BUTTONSTATE,
COMMAND_CGET,
COMMAND_CHILDREN,
COMMAND_COLLAPSE,
COMMAND_COMPARE,
#ifdef DEPRECATED
COMMAND_COMPLEX,
#endif
COMMAND_CONFIGURE,
COMMAND_COUNT,
COMMAND_CREATE,
COMMAND_DELETE,
COMMAND_DESCENDANTS,
COMMAND_DUMP,
COMMAND_ELEMENT,
COMMAND_ENABLED,
COMMAND_EXPAND,
COMMAND_FIRSTCHILD,
COMMAND_ID,
COMMAND_IMAGE,
COMMAND_ISANCESTOR,
COMMAND_ISOPEN,
COMMAND_LASTCHILD,
COMMAND_NEXTSIBLING,
COMMAND_NUMCHILDREN,
COMMAND_ORDER,
COMMAND_PARENT,
COMMAND_PREVSIBLING,
COMMAND_RANGE,
COMMAND_REMOVE,
COMMAND_RNC,
COMMAND_SORT,
COMMAND_SPAN,
COMMAND_STATE,
COMMAND_STYLE,
COMMAND_TAG,
COMMAND_TEXT,
COMMAND_TOGGLE
};
#define AF_NOT_ANCESTOR 0x00010000 /* item can't be ancestor of other item */
#define AF_NOT_EQUAL 0x00020000 /* second item can't be same as first */
#define AF_SAMEROOT 0x00040000 /* both items must be descendants of a
* common ancestor */
#define AF_NOT_ITEM 0x00080000 /* arg is not an Item */
#define AF_NOT_DELETED 0x00100000 /* item can't be deleted */
static
struct
{
char
*cmdName;
int
minArgs;
int
maxArgs;
int
flags;
int
flags2;
int
flags3;
char
*argString;
Tcl_ObjCmdProc *proc;
} argInfo[] = {
{
"ancestors"
, 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0,
"item"
, NULL },
{
"bbox"
, 0, 0, 0, 0, 0, NULL, ItemBboxCmd },
{
"buttonstate"
, 1, 2, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM, 0,
"item ?state?"
, NULL },
{
"cget"
, 2, 2, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM, 0,
"item option"
, NULL },
{
"children"
, 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0,
"item"
, NULL },
{
"collapse"
, 1, 2, IFO_NOT_NULL, AF_NOT_ITEM, 0,
"item ?-recurse?"
, NULL},
{
"compare"
, 3, 3, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM,
IFO_NOT_MANY | IFO_NOT_NULL,
"item1 op item2"
,
NULL },
#ifdef DEPRECATED
{
"complex"
, 2, 100000, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM,
AF_NOT_ITEM,
"item list ..."
, NULL },
#endif
{
"configure"
, 1, 100000, IFO_NOT_NULL, AF_NOT_ITEM, AF_NOT_ITEM,
"item ?option? ?value? ?option value ...?"
, NULL },
{
"count"
, 0, 1, 0, 0, 0,
"?itemDesc?"
, NULL},
{
"create"
, 0, 0, 0, 0, 0, NULL, ItemCreateCmd },
{
"delete"
, 1, 2, IFO_NOT_NULL, IFO_NOT_NULL | AF_SAMEROOT, 0,
"first ?last?"
, NULL },
{
"descendants"
, 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0,
"item"
,
NULL },
{
"dump"
, 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0,
"item"
, NULL },
{
"element"
, 0, 0, 0, 0, 0, NULL, ItemElementCmd },
{
"enabled"
, 1, 2, IFO_NOT_NULL, AF_NOT_ITEM, 0,
"item ?boolean?"
,
NULL },
{
"expand"
, 1, 2, IFO_NOT_NULL, AF_NOT_ITEM, 0,
"item ?-recurse?"
,
NULL},
{
"firstchild"
, 1, 2, IFO_NOT_MANY | IFO_NOT_NULL | AF_NOT_DELETED,
IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT | AF_NOT_ANCESTOR |
AF_NOT_EQUAL | AF_NOT_DELETED, 0,
"item ?newFirstChild?"
,
NULL },
{
"id"
, 1, 1, 0, 0, 0,
"item"
, NULL },
{
"image"
, 0, 0, 0, 0, 0, NULL, ItemImageCmd },
{
"isancestor"
, 2, 2, IFO_NOT_MANY | IFO_NOT_NULL, IFO_NOT_MANY |
IFO_NOT_NULL, 0,
"item item2"
, NULL },
{
"isopen"
, 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0,
"item"
, NULL },
{
"lastchild"
, 1, 2, IFO_NOT_MANY | IFO_NOT_NULL | AF_NOT_DELETED,
IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT | AF_NOT_ANCESTOR |
AF_NOT_EQUAL | AF_NOT_DELETED, 0,
"item ?newLastChild?"
, NULL },
{
"nextsibling"
, 1, 2, IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT |
IFO_NOT_ORPHAN, IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT |
AF_NOT_ANCESTOR | AF_NOT_EQUAL, 0,
"item ?newNextSibling?"
,
NULL },
{
"numchildren"
, 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0,
"item"
,
NULL },
{
"order"
, 1, 2, IFO_NOT_MANY | IFO_NOT_NULL, AF_NOT_ITEM, 0,
"item ?-visible?"
, NULL },
{
"parent"
, 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0,
"item"
, NULL },
{
"prevsibling"
, 1, 2, IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT |
IFO_NOT_ORPHAN, IFO_NOT_MANY | IFO_NOT_NULL | IFO_NOT_ROOT |
AF_NOT_ANCESTOR | AF_NOT_EQUAL, 0,
"item ?newPrevSibling?"
,
NULL },
{
"range"
, 2, 2, IFO_NOT_MANY | IFO_NOT_NULL, IFO_NOT_MANY |
IFO_NOT_NULL | AF_SAMEROOT, 0,
"first last"
, NULL },
{
"remove"
, 1, 1, IFO_NOT_NULL | IFO_NOT_ROOT, 0, 0,
"item"
, NULL },
{
"rnc"
, 1, 1, IFO_NOT_MANY | IFO_NOT_NULL, 0, 0,
"item"
, NULL },
{
"sort"
, 0, 0, 0, 0, 0, NULL, ItemSortCmd },
{
"span"
, 0, 0, 0, 0, 0, NULL, ItemSpanCmd },
{
"state"
, 0, 0, 0, 0, 0, NULL, ItemStateCmd },
{
"style"
, 0, 0, 0, 0, 0, NULL, ItemStyleCmd },
{
"tag"
, 0, 0, 0, 0, 0, NULL, ItemTagCmd },
{
"text"
, 0, 0, 0, 0, 0, NULL, ItemTextCmd },
{
"toggle"
, 1, 2, IFO_NOT_NULL, AF_NOT_ITEM, 0,
"item ?-recurse?"
,
NULL},
{ NULL }
};
int
index;
int
numArgs = objc - 3;
TreeItemList itemList, item2List;
TreeItem item = NULL, item2 = NULL, child;
ItemForEach iter;
int
result = TCL_OK;
if
(objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv,
"command ?arg arg ...?"
);
return
TCL_ERROR;
}
if
(Tcl_GetIndexFromObjStruct(interp, objv[2], argInfo,
sizeof
(argInfo[0]),
"command"
, 0, &index) != TCL_OK) {
return
TCL_ERROR;
}
if
(argInfo[index].proc != NULL)
return
argInfo[index].proc(clientData, interp, objc, objv);
if
((numArgs < argInfo[index].minArgs) ||
(numArgs > argInfo[index].maxArgs)) {
Tcl_WrongNumArgs(interp, 3, objv, argInfo[index].argString);
return
TCL_ERROR;
}
TreeItemList_Init(tree, &itemList, 0);
TreeItemList_Init(tree, &item2List, 0);
if
((numArgs >= 1) && !(argInfo[index].flags & AF_NOT_ITEM)) {
if
(TreeItemList_FromObj(tree, objv[3], &itemList,
argInfo[index].flags & 0xFFFF) != TCL_OK) {
goto
errorExit;
}
item = TreeItemList_Nth(&itemList, 0);
if
((argInfo[index].flags & AF_NOT_DELETED) && IS_DELETED(item)) {
FormatResult(interp,
"item %s%d is being deleted"
,
tree->itemPrefix, item->id);
goto
errorExit;
}
}
if
(((numArgs >= 2) && !(argInfo[index].flags2 & AF_NOT_ITEM)) ||
((numArgs >= 3) && !(argInfo[index].flags3 & AF_NOT_ITEM))) {
int
flags, obji;
if
(argInfo[index].flags2 & AF_NOT_ITEM) {
obji = 5;
flags = argInfo[index].flags3;
}
else
{
obji = 4;
flags = argInfo[index].flags2;
}
if
(TreeItemList_FromObj(tree, objv[obji], &item2List,
flags & 0xFFFF) != TCL_OK) {
goto
errorExit;
}
ITEM_FOR_EACH(item2, &item2List, NULL, &iter) {
if
((flags & AF_NOT_DELETED) && IS_DELETED(item2)) {
FormatResult(interp,
"item %s%d is being deleted"
,
tree->itemPrefix, item2->id);
goto
errorExit;
}
if
((flags & AF_NOT_EQUAL) && (item == item2)) {
FormatResult(interp,
"item %s%d same as second item"
, tree->itemPrefix,
item->id);
goto
errorExit;
}
if
((argInfo[index].flags & AF_NOT_ANCESTOR) &&
TreeItem_IsAncestor(tree, item, item2)) {
FormatResult(interp,
"item %s%d is ancestor of item %s%d"
,
tree->itemPrefix, item->id, tree->itemPrefix, item2->id);
goto
errorExit;
}
if
((flags & AF_NOT_ANCESTOR) &&
TreeItem_IsAncestor(tree, item2, item)) {
FormatResult(interp,
"item %s%d is ancestor of item %s%d"
,
tree->itemPrefix, item2->id, tree->itemPrefix, item->id);
goto
errorExit;
}
if
((flags & AF_SAMEROOT) &&
TreeItem_RootAncestor(tree, item) !=
TreeItem_RootAncestor(tree, item2)) {
reqSameRoot:
FormatResult(interp,
"item %s%d and item %s%d don't share a common ancestor"
,
tree->itemPrefix, item->id, tree->itemPrefix, item2->id);
goto
errorExit;
}
}
item2 = TreeItemList_Nth(&item2List, 0);
}
switch
(index) {
case
COMMAND_ANCESTORS: {
Tcl_Obj *listObj;
TreeItem parent = item->parent;
if
(parent == NULL)
break
;
listObj = Tcl_NewListObj(0, NULL);
while
(parent != NULL) {
Tcl_ListObjAppendElement(interp, listObj,
TreeItem_ToObj(tree, parent));
parent = parent->parent;
}
Tcl_SetObjResult(interp, listObj);
break
;
}
case
COMMAND_BUTTONSTATE: {
static
const
char
*stateNames[] = {
"active"
,
"normal"
,
"pressed"
, NULL
};
int
state;
if
(numArgs == 2) {
int
prevFlags = item->flags;
if
(Tcl_GetIndexFromObj(interp, objv[4], stateNames,
"state"
,
0, &state) != TCL_OK) {
goto
errorExit;
}
item->flags &= ~ITEM_FLAGS_BUTTONSTATE;
if
(state == 0)
item->flags |= ITEM_FLAG_BUTTONSTATE_ACTIVE;
else
if
(state == 2)
item->flags |= ITEM_FLAG_BUTTONSTATE_PRESSED;
if
(item->flags != prevFlags && tree->columnTree != NULL)
Tree_InvalidateItemDInfo(tree, tree->columnTree, item, NULL);
}
else
{
if
(item->flags & ITEM_FLAG_BUTTONSTATE_ACTIVE)
state = 0;
else
if
(item->flags & ITEM_FLAG_BUTTONSTATE_PRESSED)
state = 2;
else
state = 1;
}
Tcl_SetObjResult(interp, Tcl_NewStringObj(stateNames[state], -1));
break
;
}
case
COMMAND_CGET: {
Tcl_Obj *resultObjPtr;
resultObjPtr = Tk_GetOptionValue(interp, (
char
*) item,
tree->itemOptionTable, objv[4], tree->tkwin);
if
(resultObjPtr == NULL)
goto
errorExit;
Tcl_SetObjResult(interp, resultObjPtr);
break
;
}
case
COMMAND_CHILDREN: {
if
(item->numChildren != 0) {
Tcl_Obj *listObj;
listObj = Tcl_NewListObj(0, NULL);
child = item->firstChild;
while
(child != NULL) {
Tcl_ListObjAppendElement(interp, listObj,
TreeItem_ToObj(tree, child));
child = child->nextSibling;
}
Tcl_SetObjResult(interp, listObj);
}
break
;
}
case
COMMAND_COLLAPSE:
case
COMMAND_EXPAND:
case
COMMAND_TOGGLE: {
int
animate = 0;
int
recurse = 0;
int
mode = 0;
int
i, count;
TreeItemList items;
if
(numArgs > 1) {
static
const
char
*optionName[] = {
"-animate"
,
"-recurse"
,
NULL };
int
option;
for
(i = 4; i < objc; i++) {
if
(Tcl_GetIndexFromObj(interp, objv[i], optionName,
"option"
, 0, &option) != TCL_OK) {
goto
errorExit;
}
switch
(option) {
case
0:
animate = 1;
break
;
case
1:
recurse = 1;
break
;
}
}
}
switch
(index) {
case
COMMAND_COLLAPSE:
mode = 0;
break
;
case
COMMAND_EXPAND:
mode = 1;
break
;
case
COMMAND_TOGGLE:
mode = -1;
break
;
}
if
(animate) {
Tk_Image image;
Pixmap bitmap;
int
open;
if
(IS_ALL(item) || TreeItemList_Count(&itemList) != 1) {
FormatResult(interp,
"only 1 item may be specified with -animate"
);
goto
errorExit;
}
image = PerStateImage_ForState(tree, &tree->buttonImage, item->state, NULL);
bitmap = PerStateBitmap_ForState(tree, &tree->buttonBitmap, item->state, NULL);
if
(image != NULL || bitmap != None || !tree->useTheme
|| !TreeItem_HasButton(tree, item)) {
TreeItem_OpenClose(tree, item, mode);
break
;
}
open = (item->state & STATE_ITEM_OPEN) != 0;
if
(mode == -1 || open != mode) {
(
void
) TreeTheme_AnimateButtonStart(tree, item);
}
break
;
}
TreeItemList_Init(tree, &items, 0);
ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
TreeItemList_Append(&items, item);
if
(!iter.all && recurse) {
TreeItem_ListDescendants(tree, item, &items);
}
}
count = TreeItemList_Count(&items);
for
(i = 0; i < count; i++) {
item = TreeItemList_Nth(&items, i);
TreeItem_OpenClose(tree, item, mode);
}
TreeItemList_Free(&items);
#ifdef SELECTION_VISIBLE
Tree_DeselectHidden(tree);
#endif
break
;
}
case
COMMAND_COMPARE: {
static
CONST
char
*opName[] = {
"<"
,
"<="
,
"=="
,
">="
,
">"
,
"!="
, NULL };
enum
{ COP_LT, COP_LE, COP_EQ, COP_GE, COP_GT, COP_NE };
int
op, compare = 0, index1 = 0, index2 = 0;
if
(Tcl_GetIndexFromObj(interp, objv[4], opName,
"comparison operator"
, 0,
&op) != TCL_OK) {
goto
errorExit;
}
if
(op != COP_EQ && op != COP_NE) {
if
(TreeItem_RootAncestor(tree, item) !=
TreeItem_RootAncestor(tree, item2)) {
goto
reqSameRoot;
}
TreeItem_ToIndex(tree, item, &index1, NULL);
TreeItem_ToIndex(tree, item2, &index2, NULL);
}
switch
(op) {
case
COP_LT:
compare = index1 < index2;
break
;
case
COP_LE:
compare = index1 <= index2;
break
;
case
COP_EQ:
compare = item == item2;
break
;
case
COP_GE:
compare = index1 >= index2;
break
;
case
COP_GT:
compare = index1 > index2;
break
;
case
COP_NE:
compare = item != item2;
break
;
}
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(compare));
break
;
}
#ifdef DEPRECATED
case
COMMAND_COMPLEX: {
int
i, j, columnIndex;
int
objc1, objc2;
Tcl_Obj **objv1, **objv2;
TreeColumn treeColumn = tree->columns;
TreeItemColumn column;
int
eMask, cMask, iMask = 0;
if
(objc <= 4)
break
;
columnIndex = 0;
for
(i = 4; i < objc; i++, columnIndex++,
treeColumn = TreeColumn_Next(treeColumn)) {
if
(treeColumn == NULL) {
FormatResult(interp,
"column #%d doesn't exist"
,
columnIndex);
result = TCL_ERROR;
goto
doneComplex;
}
column = TreeItem_FindColumn(tree, item, columnIndex);
if
(column == NULL) {
FormatResult(interp,
"item %s%d doesn't have column %s%d"
,
tree->itemPrefix, item->id,
tree->columnPrefix, TreeColumn_GetID(treeColumn));
result = TCL_ERROR;
goto
doneComplex;
}
if
(Tcl_ListObjGetElements(interp, objv[i],
&objc1, &objv1) != TCL_OK) {
result = TCL_ERROR;
goto
doneComplex;
}
if
(objc1 == 0)
continue
;
if
(column->style == NULL) {
FormatResult(interp,
"item %s%d column %s%d has no style"
,
tree->itemPrefix, item->id,
tree->columnPrefix, TreeColumn_GetID(treeColumn));
result = TCL_ERROR;
goto
doneComplex;
}
cMask = 0;
for
(j = 0; j < objc1; j++) {
if
(Tcl_ListObjGetElements(interp, objv1[j],
&objc2, &objv2) != TCL_OK) {
result = TCL_ERROR;
goto
doneComplex;
}
if
(objc2 < 3) {
FormatResult(interp,
"wrong # args: should be \"element option value ...\""
);
result = TCL_ERROR;
goto
doneComplex;
}
if
(TreeStyle_ElementConfigureFromObj(tree, item, column,
column->style, objv2[0], objc2 - 1, objv2 + 1,
&eMask) != TCL_OK) {
result = TCL_ERROR;
goto
doneComplex;
}
cMask |= eMask;
iMask |= eMask;
}
if
(cMask & CS_LAYOUT)
TreeItemColumn_InvalidateSize(tree, column);
}
doneComplex:
if
(iMask & CS_DISPLAY)
Tree_InvalidateItemDInfo(tree, NULL, item, NULL);
if
(iMask & CS_LAYOUT) {
TreeColumns_InvalidateWidthOfItems(tree, NULL);
TreeItem_InvalidateHeight(tree, item);
Tree_FreeItemDInfo(tree, item, NULL);
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
}
break
;
}
#endif /* DEPRECATED*/
case
COMMAND_CONFIGURE: {
if
(objc <= 5) {
Tcl_Obj *resultObjPtr;
if
(IS_ALL(item) || (TreeItemList_Count(&itemList) > 1)) {
NotManyMsg(tree, FALSE);
goto
errorExit;
}
resultObjPtr = Tk_GetOptionInfo(interp, (
char
*) item,
tree->itemOptionTable,
(objc == 4) ? (Tcl_Obj *) NULL : objv[4],
tree->tkwin);
if
(resultObjPtr == NULL)
goto
errorExit;
Tcl_SetObjResult(interp, resultObjPtr);
break
;
}
ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
result = Item_Configure(tree, item, objc - 4, objv + 4);
if
(result != TCL_OK)
break
;
}
break
;
}
case
COMMAND_COUNT: {
int
count = tree->itemCount;
if
(objc == 4) {
count = 0;
ITEM_FOR_EACH(item, &itemList, &item2List, &iter) {
count++;
}
}
Tcl_SetObjResult(interp, Tcl_NewIntObj(count));
break
;
}
case
COMMAND_DELETE: {
TreeItemList deleted, selected;
int
i, count;
if
(tree->itemCount == 1)
break
;
if
(objc == 5) {
if
(item == NULL || item2 == NULL) {
FormatResult(interp,
"invalid range \"%s\" to \"%s\""
,
Tcl_GetString(objv[3]), Tcl_GetString(objv[4]));
goto
errorExit;
}
}
TreeItemList_Init(tree, &deleted, tree->itemCount - 1);
TreeItemList_Init(tree, &selected, tree->selectCount);
ITEM_FOR_EACH(item, &itemList, &item2List, &iter) {
if
(IS_ROOT(item))
continue
;
if
(IS_DELETED(item))
continue
;
item->flags |= ITEM_FLAG_DELETED;
TreeItemList_Append(&deleted, item);
if
(TreeItem_GetSelected(tree, item))
TreeItemList_Append(&selected, item);
if
(iter.all)
continue
;
if
(item->firstChild == NULL)
continue
;
item2 = item;
while
(item2->lastChild != NULL)
item2 = item2->lastChild;
item = item->firstChild;
while
(1) {
if
(IS_DELETED(item)) {
while
(item->lastChild != NULL)
item = item->lastChild;
}
else
{
item->flags |= ITEM_FLAG_DELETED;
TreeItemList_Append(&deleted, item);
if
(TreeItem_GetSelected(tree, item))
TreeItemList_Append(&selected, item);
}
if
(item == item2)
break
;
item = TreeItem_Next(tree, item);
}
}
count = TreeItemList_Count(&selected);
if
(count) {
for
(i = 0; i < count; i++) {
item = TreeItemList_Nth(&selected, i);
Tree_RemoveFromSelection(tree, item);
}
TreeNotify_Selection(tree, NULL, &selected);
}
count = TreeItemList_Count(&deleted);
if
(count) {
int
redoColumns = 0;
TreeNotify_ItemDeleted(tree, &deleted);
for
(i = 0; i < count; i++) {
item = TreeItemList_Nth(&deleted, i);
if
(TreeItem_ReallyVisible(tree, item))
redoColumns = 1;
TreeItem_RemoveFromParent(tree, item);
}
for
(i = 0; i < count; i++) {
item = TreeItemList_Nth(&deleted, i);
TreeItem_Delete(tree, item);
}
if
(redoColumns) {
TreeColumns_InvalidateWidthOfItems(tree, NULL);
TreeColumns_InvalidateSpans(tree);
}
}
TreeItemList_Free(&selected);
TreeItemList_Free(&deleted);
break
;
}
case
COMMAND_DESCENDANTS: {
Tcl_Obj *listObj;
if
(item->firstChild == NULL)
break
;
item2 = item;
while
(item2->lastChild != NULL)
item2 = item2->lastChild;
item = item->firstChild;
listObj = Tcl_NewListObj(0, NULL);
while
(1) {
Tcl_ListObjAppendElement(interp, listObj,
TreeItem_ToObj(tree, item));
if
(item == item2)
break
;
item = TreeItem_Next(tree, item);
}
Tcl_SetObjResult(interp, listObj);
break
;
}
case
COMMAND_DUMP: {
Tree_UpdateItemIndex(tree);
FormatResult(interp,
"index %d indexVis %d"
,
item->index, item->indexVis);
break
;
}
case
COMMAND_ENABLED: {
int
enabled;
TreeItemList newD;
int
stateOff, stateOn;
if
(objc == 4) {
if
(IS_ALL(item) || (TreeItemList_Count(&itemList) > 1)) {
NotManyMsg(tree, FALSE);
goto
errorExit;
}
Tcl_SetObjResult(interp,
Tcl_NewBooleanObj(item->state & STATE_ITEM_ENABLED));
break
;
}
if
(Tcl_GetBooleanFromObj(interp, objv[4], &enabled) != TCL_OK)
goto
errorExit;
stateOff = enabled ? 0 : STATE_ITEM_ENABLED;
stateOn = enabled ? STATE_ITEM_ENABLED : 0;
TreeItemList_Init(tree, &newD, tree->selectCount);
ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
if
(enabled != TreeItem_GetEnabled(tree, item)) {
TreeItem_ChangeState(tree, item, stateOff, stateOn);
if
(!enabled && TreeItem_GetSelected(tree, item)) {
Tree_RemoveFromSelection(tree, item);
TreeItemList_Append(&newD, item);
}
}
}
if
(TreeItemList_Count(&newD))
TreeNotify_Selection(tree, NULL, &newD);
TreeItemList_Free(&newD);
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(enabled));
break
;
}
case
COMMAND_FIRSTCHILD: {
if
(item2 != NULL && item2 != item->firstChild) {
TreeItem_RemoveFromParent(tree, item2);
item2->nextSibling = item->firstChild;
if
(item->firstChild != NULL)
item->firstChild->prevSibling = item2;
else
item->lastChild = item2;
item->firstChild = item2;
item2->parent = item;
item->numChildren++;
TreeItem_AddToParent(tree, item2);
#ifdef SELECTION_VISIBLE
Tree_DeselectHidden(tree);
#endif
}
if
(item->firstChild != NULL)
Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->firstChild));
break
;
}
case
COMMAND_ID: {
Tcl_Obj *listObj;
listObj = Tcl_NewListObj(0, NULL);
ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
Tcl_ListObjAppendElement(interp, listObj,
TreeItem_ToObj(tree, item));
}
Tcl_SetObjResult(interp, listObj);
break
;
}
case
COMMAND_ISANCESTOR: {
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(
TreeItem_IsAncestor(tree, item, item2)));
break
;
}
case
COMMAND_ISOPEN: {
Tcl_SetObjResult(interp, Tcl_NewBooleanObj(item->state & STATE_ITEM_OPEN));
break
;
}
case
COMMAND_LASTCHILD: {
if
(item2 != NULL && item2 != item->lastChild) {
TreeItem_RemoveFromParent(tree, item2);
item2->prevSibling = item->lastChild;
if
(item->lastChild != NULL)
item->lastChild->nextSibling = item2;
else
item->firstChild = item2;
item->lastChild = item2;
item2->parent = item;
item->numChildren++;
TreeItem_AddToParent(tree, item2);
#ifdef SELECTION_VISIBLE
Tree_DeselectHidden(tree);
#endif
}
if
(item->lastChild != NULL)
Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->lastChild));
break
;
}
case
COMMAND_NEXTSIBLING: {
if
(item2 != NULL && item2 != item->nextSibling) {
TreeItem_RemoveFromParent(tree, item2);
item2->prevSibling = item;
if
(item->nextSibling != NULL) {
item->nextSibling->prevSibling = item2;
item2->nextSibling = item->nextSibling;
}
else
item->parent->lastChild = item2;
item->nextSibling = item2;
item2->parent = item->parent;
item->parent->numChildren++;
TreeItem_AddToParent(tree, item2);
#ifdef SELECTION_VISIBLE
Tree_DeselectHidden(tree);
#endif
}
if
(item->nextSibling != NULL)
Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->nextSibling));
break
;
}
case
COMMAND_NUMCHILDREN: {
Tcl_SetObjResult(interp, Tcl_NewIntObj(item->numChildren));
break
;
}
case
COMMAND_ORDER: {
int
visible = FALSE;
if
(objc == 5) {
int
len;
char
*s = Tcl_GetStringFromObj(objv[4], &len);
if
((s[0] ==
'-'
) && (
strncmp
(s,
"-visible"
, len) == 0))
visible = TRUE;
else
{
FormatResult(interp,
"bad switch \"%s\": must be -visible"
,
s);
goto
errorExit;
}
}
Tree_UpdateItemIndex(tree);
Tcl_SetObjResult(interp,
Tcl_NewIntObj(visible ? item->indexVis : item->index));
break
;
}
case
COMMAND_RANGE: {
TreeItem itemFirst = item;
TreeItem itemLast = item2;
Tcl_Obj *listObj;
if
(itemFirst == itemLast) {
Tcl_SetObjResult(interp, TreeItem_ToObj(tree, itemFirst));
break
;
}
(
void
) TreeItem_FirstAndLast(tree, &itemFirst, &itemLast);
listObj = Tcl_NewListObj(0, NULL);
while
(itemFirst != NULL) {
Tcl_ListObjAppendElement(interp, listObj,
TreeItem_ToObj(tree, itemFirst));
if
(itemFirst == itemLast)
break
;
itemFirst = TreeItem_Next(tree, itemFirst);
}
Tcl_SetObjResult(interp, listObj);
break
;
}
case
COMMAND_PARENT: {
if
(item->parent != NULL)
Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->parent));
break
;
}
case
COMMAND_PREVSIBLING: {
if
(item2 != NULL && item2 != item->prevSibling) {
TreeItem_RemoveFromParent(tree, item2);
item2->nextSibling = item;
if
(item->prevSibling != NULL) {
item->prevSibling->nextSibling = item2;
item2->prevSibling = item->prevSibling;
}
else
item->parent->firstChild = item2;
item->prevSibling = item2;
item2->parent = item->parent;
item->parent->numChildren++;
TreeItem_AddToParent(tree, item2);
#ifdef SELECTION_VISIBLE
Tree_DeselectHidden(tree);
#endif
}
if
(item->prevSibling != NULL)
Tcl_SetObjResult(interp, TreeItem_ToObj(tree, item->prevSibling));
break
;
}
case
COMMAND_REMOVE: {
int
removed = FALSE;
ITEM_FOR_EACH(item, &itemList, NULL, &iter) {
if
(item->parent != NULL) {
TreeItem_RemoveFromParent(tree, item);
Tree_FreeItemDInfo(tree, item, NULL);
removed = TRUE;
}
}
if
(!removed)
break
;
if
(tree->debug.enable && tree->debug.data)
Tree_Debug(tree);
TreeColumns_InvalidateWidthOfItems(tree, NULL);
TreeColumns_InvalidateSpans(tree);
#ifdef SELECTION_VISIBLE
Tree_DeselectHidden(tree);
#endif
break
;
}
case
COMMAND_RNC: {
int
row,col;
if
(Tree_ItemToRNC(tree, item, &row, &col) == TCL_OK)
FormatResult(interp,
"%d %d"
, row, col);
break
;
}
}
TreeItemList_Free(&itemList);
TreeItemList_Free(&item2List);
return
result;
errorExit:
TreeItemList_Free(&itemList);
TreeItemList_Free(&item2List);
return
TCL_ERROR;
}
int
TreeItem_Debug(
TreeCtrl *tree,
TreeItem item
)
{
TreeItem child;
Tcl_Interp *interp = tree->interp;
int
count;
if
(item->parent == item) {
FormatResult(interp,
"parent of %d is itself"
, item->id);
return
TCL_ERROR;
}
if
(item->parent == NULL) {
if
(item->prevSibling != NULL) {
FormatResult(interp,
"parent of %d is nil, prevSibling is not nil"
,
item->id);
return
TCL_ERROR;
}
if
(item->nextSibling != NULL) {
FormatResult(interp,
"parent of %d is nil, nextSibling is not nil"
,
item->id);
return
TCL_ERROR;
}
}
if
(item->prevSibling != NULL) {
if
(item->prevSibling == item) {
FormatResult(interp,
"prevSibling of %d is itself"
,
item->id);
return
TCL_ERROR;
}
if
(item->prevSibling->nextSibling != item) {
FormatResult(interp,
"item%d.prevSibling.nextSibling is not it"
,
item->id);
return
TCL_ERROR;
}
}
if
(item->nextSibling != NULL) {
if
(item->nextSibling == item) {
FormatResult(interp,
"nextSibling of %d is itself"
,
item->id);
return
TCL_ERROR;
}
if
(item->nextSibling->prevSibling != item) {
FormatResult(interp,
"item%d.nextSibling->prevSibling is not it"
,
item->id);
return
TCL_ERROR;
}
}
if
(item->numChildren < 0) {
FormatResult(interp,
"numChildren of %d is %d"
,
item->id, item->numChildren);
return
TCL_ERROR;
}
if
(item->numChildren == 0) {
if
(item->firstChild != NULL) {
FormatResult(interp,
"item%d.numChildren is zero, firstChild is not nil"
,
item->id);
return
TCL_ERROR;
}
if
(item->lastChild != NULL) {
FormatResult(interp,
"item%d.numChildren is zero, lastChild is not nil"
,
item->id);
return
TCL_ERROR;
}
}
if
(item->numChildren > 0) {
if
(item->firstChild == NULL) {
FormatResult(interp,
"item%d.firstChild is nil"
,
item->id);
return
TCL_ERROR;
}
if
(item->firstChild == item) {
FormatResult(interp,
"item%d.firstChild is itself"
,
item->id);
return
TCL_ERROR;
}
if
(item->firstChild->parent != item) {
FormatResult(interp,
"item%d.firstChild.parent is not it"
,
item->id);
return
TCL_ERROR;
}
if
(item->firstChild->prevSibling != NULL) {
FormatResult(interp,
"item%d.firstChild.prevSibling is not nil"
,
item->id);
return
TCL_ERROR;
}
if
(item->lastChild == NULL) {
FormatResult(interp,
"item%d.lastChild is nil"
,
item->id);
return
TCL_ERROR;
}
if
(item->lastChild == item) {
FormatResult(interp,
"item%d.lastChild is itself"
,
item->id);
return
TCL_ERROR;
}
if
(item->lastChild->parent != item) {
FormatResult(interp,
"item%d.lastChild.parent is not it"
,
item->id);
return
TCL_ERROR;
}
if
(item->lastChild->nextSibling != NULL) {
FormatResult(interp,
"item%d.lastChild.nextSibling is not nil"
,
item->id);
return
TCL_ERROR;
}
count = 0;
child = item->firstChild;
while
(child != NULL) {
count++;
child = child->nextSibling;
}
if
(count != item->numChildren) {
FormatResult(interp,
"item%d.numChildren is %d, but counted %d"
,
item->id, item->numChildren, count);
return
TCL_ERROR;
}
child = item->firstChild;
while
(child != NULL) {
if
(child->parent != item) {
FormatResult(interp,
"child->parent of %d is not it"
,
item->id);
return
TCL_ERROR;
}
if
(TreeItem_Debug(tree, child) != TCL_OK)
return
TCL_ERROR;
child = child->nextSibling;
}
}
return
TCL_OK;
}
static
int
SpanWalkProc_Identify(
TreeCtrl *tree,
TreeItem item,
SpanInfo *spanPtr,
StyleDrawArgs *drawArgs,
ClientData clientData
)
{
struct
{
int
x;
int
y;
TreeColumn *columnPtr;
TreeElement *elemPtr;
} *data = clientData;
if
(item->header != NULL) {
if
((data->x < drawArgs->x
) ||
(data->x >= drawArgs->x + drawArgs->width))
return
0;
}
else
{
if
((data->x < drawArgs->x + drawArgs->indent) ||
(data->x >= drawArgs->x + drawArgs->width))
return
0;
}
(*data->columnPtr) = spanPtr->treeColumn;
if
((drawArgs->style != NULL) && !TreeStyle_IsHeaderStyle(tree, drawArgs->style))
(*data->elemPtr) = TreeStyle_Identify(drawArgs, data->x, data->y);
return
1;
}
void
TreeItem_Identify(
TreeCtrl *tree,
TreeItem item,
int
lock,
int
x,
int
y,
TreeColumn *columnPtr,
TreeElement *elemPtr
)
{
TreeRectangle tr;
struct
{
int
x;
int
y;
TreeColumn *columnPtr;
TreeElement *elemPtr;
} clientData;
(*columnPtr) = NULL;
(*elemPtr) = NULL;
if
(Tree_ItemBbox(tree, item, lock, &tr) < 0)
return
;
clientData.x = x;
clientData.y = y;
clientData.columnPtr = columnPtr;
clientData.elemPtr = elemPtr;
TreeItem_WalkSpans(tree, item, lock,
0, 0, TreeRect_Width(tr), TreeRect_Height(tr),
WALKSPAN_IGNORE_DND,
SpanWalkProc_Identify, (ClientData) &clientData);
}
static
int
SpanWalkProc_Identify2(
TreeCtrl *tree,
TreeItem item,
SpanInfo *spanPtr,
StyleDrawArgs *drawArgs,
ClientData clientData
)
{
Tcl_Obj *subListObj;
struct
{
int
x1;
int
y1;
int
x2;
int
y2;
Tcl_Obj *listObj;
} *data = clientData;
if
((data->x2 < drawArgs->x + drawArgs->indent) ||
(data->x1 >= drawArgs->x + drawArgs->width))
return
0;
subListObj = Tcl_NewListObj(0, NULL);
Tcl_ListObjAppendElement(tree->interp, subListObj,
TreeColumn_ToObj(tree, spanPtr->treeColumn));
if
(drawArgs->style != NULL) {
StyleDrawArgs drawArgsCopy = *drawArgs;
TreeStyle_Identify2(&drawArgsCopy, data->x1, data->y1,
data->x2, data->y2,
subListObj);
}
Tcl_ListObjAppendElement(tree->interp, data->listObj, subListObj);
return
drawArgs->x + drawArgs->width >= data->x2;
}
void
TreeItem_Identify2(
TreeCtrl *tree,
TreeItem item,
int
x1,
int
y1,
int
x2,
int
y2,
Tcl_Obj *listObj
)
{
TreeRectangle tr;
struct
{
int
x1;
int
y1;
int
x2;
int
y2;
Tcl_Obj *listObj;
} clientData;
if
(Tree_ItemBbox(tree, item, COLUMN_LOCK_NONE, &tr) < 0)
return
;
clientData.x1 = x1;
clientData.y1 = y1;
clientData.x2 = x2;
clientData.y2 = y2;
clientData.listObj = listObj;
TreeItem_WalkSpans(tree, item, COLUMN_LOCK_NONE,
TreeRect_Left(tr), TreeRect_Top(tr),
TreeRect_Width(tr), TreeRect_Height(tr),
WALKSPAN_IGNORE_DND,
SpanWalkProc_Identify2, (ClientData) &clientData);
}
static
int
SpanWalkProc_GetRects(
TreeCtrl *tree,
TreeItem item,
SpanInfo *spanPtr,
StyleDrawArgs *drawArgs,
ClientData clientData
)
{
int
objc;
Tcl_Obj *CONST *objv;
struct
{
TreeColumn treeColumn;
int
count;
Tcl_Obj *CONST *objv;
TreeRectangle *rects;
int
result;
} *data = clientData;
if
(spanPtr->treeColumn != data->treeColumn)
return
0;
if
(data->count == 0) {
data->rects[0].x = drawArgs->x + drawArgs->indent;
data->rects[0].y = drawArgs->y;
data->rects[0].width = drawArgs->width - drawArgs->indent;
data->rects[0].height = drawArgs->height;
#if 1
if
(item->header != NULL) {
data->rects[0].x = drawArgs->x;
data->rects[0].width = drawArgs->width;
}
#endif
data->result = 1;
return
1;
}
if
(drawArgs->style == NULL) {
NoStyleMsg(tree, item, TreeColumn_Index(spanPtr->treeColumn));
data->result = -1;
return
1;
}
if
(data->count == -1) {
objc = 0;
objv = NULL;
}
else
{
objc = data->count;
objv = data->objv;
}
data->result = TreeStyle_GetElemRects(drawArgs, objc, objv, data->rects);
return
1;
}
int
TreeItem_GetRects(
TreeCtrl *tree,
TreeItem item,
TreeColumn treeColumn,
int
count,
Tcl_Obj *CONST objv[],
TreeRectangle rects[]
)
{
TreeRectangle tr;
int
lock = TreeColumn_Lock(treeColumn);
struct
{
TreeColumn treeColumn;
int
count;
Tcl_Obj *CONST *objv;
TreeRectangle *rects;
int
result;
} clientData;
if
(Tree_ItemBbox(tree, item, lock, &tr) < 0)
return
0;
clientData.treeColumn = treeColumn;
clientData.count = count;
clientData.objv = objv;
clientData.rects = rects;
clientData.result = 0;
TreeItem_WalkSpans(tree, item, lock,
TreeRect_Left(tr), TreeRect_Top(tr),
TreeRect_Width(tr), TreeRect_Height(tr),
WALKSPAN_IGNORE_DND,
SpanWalkProc_GetRects, (ClientData) &clientData);
return
clientData.result;
}
TreeItemColumn
TreeItem_MakeColumnExist(
TreeCtrl *tree,
TreeItem item,
int
columnIndex
)
{
return
Item_CreateColumn(tree, item, columnIndex, NULL);
}
TreeItem
TreeItem_CreateHeader(
TreeCtrl *tree
)
{
TreeItem item, walk;
TreeHeader header;
item = Item_Alloc(tree, TRUE);
header = TreeHeader_CreateWithItem(tree, item);
if
(header == NULL) {
}
item->header = header;
(
void
) Item_CreateColumn(tree, item, tree->columnCount, NULL);
if
(tree->headerItems == NULL)
tree->headerItems = item;
else
{
walk = tree->headerItems;
while
(walk->nextSibling != NULL) {
walk = walk->nextSibling;
}
walk->nextSibling = item;
item->prevSibling = walk;
}
return
item;
}
TreeHeader
TreeItem_GetHeader(
TreeCtrl *tree,
TreeItem item
)
{
return
item->header;
}
TreeHeaderColumn
TreeItemColumn_GetHeaderColumn(
TreeCtrl *tree,
TreeItemColumn column
)
{
return
column->headerColumn;
}
static
int
IsHeaderOption(
Tcl_Interp *interp,
Tcl_Obj *objPtr
)
{
static
CONST
char
*headerOptions[] = {
"-height"
,
"-tags"
,
"-visible"
, NULL
};
int
index;
if
(Tcl_GetIndexFromObj(interp, objPtr, headerOptions,
"option"
, 0, &index) != TCL_OK)
return
FALSE;
return
TRUE;
}
int
TreeItem_ConsumeHeaderCget(
TreeCtrl *tree,
TreeItem item,
Tcl_Obj *objPtr
)
{
Tcl_Interp *interp = tree->interp;
Tcl_Obj *resultObjPtr;
if
(!IsHeaderOption(interp, objPtr)) {
FormatResult(interp,
"unknown option \"%s\""
, Tcl_GetString(objPtr));
return
TCL_ERROR;
}
resultObjPtr = Tk_GetOptionValue(interp, (
char
*) item,
tree->itemOptionTable, objPtr, tree->tkwin);
if
(resultObjPtr == NULL)
return
TCL_ERROR;
Tcl_SetObjResult(interp, resultObjPtr);
return
TCL_OK;
}
int
TreeItem_ConsumeHeaderConfig(
TreeCtrl *tree,
TreeItem item,
int
objc,
Tcl_Obj *CONST objv[]
)
{
Tcl_Interp *interp = tree->interp;
int
i;
if
(objc <= 0)
return
TCL_OK;
for
(i = 0; i < objc; i += 2) {
if
(!IsHeaderOption(interp, objv[i])) {
FormatResult(interp,
"unknown option \"%s\""
, Tcl_GetString(objv[i]));
return
TCL_ERROR;
}
}
return
Item_Configure(tree, item, objc, objv);
}
int
TreeItem_GetHeaderOptionInfo(
TreeCtrl *tree,
TreeHeader header,
Tcl_Obj *objPtr,
Tcl_Obj *resultObjPtr
)
{
Tcl_Interp *interp = tree->interp;
TreeItem item = TreeHeader_GetItem(header);
Tcl_Obj *listObjPtr;
static
CONST
char
*headerOptions[] = {
"-height"
,
"-tags"
,
"-visible"
, NULL
};
int
i;
if
(objPtr != NULL) {
if
(!IsHeaderOption(interp, objPtr)) {
FormatResult(interp,
"unknown option \"%s\""
, Tcl_GetString(objPtr));
return
TCL_ERROR;
}
listObjPtr = Tk_GetOptionInfo(tree->interp, (
char
*) item,
tree->itemOptionTable, objPtr, tree->tkwin);
if
(listObjPtr == NULL)
return
TCL_ERROR;
Tcl_SetObjResult(interp, listObjPtr);
return
TCL_OK;
}
for
(i = 0; headerOptions[i] != NULL; i++) {
objPtr = Tcl_NewStringObj(headerOptions[i], -1);
Tcl_IncrRefCount(objPtr);
listObjPtr = Tk_GetOptionInfo(tree->interp, (
char
*) item,
tree->itemOptionTable, objPtr, tree->tkwin);
Tcl_DecrRefCount(objPtr);
if
(listObjPtr == NULL)
return
TCL_ERROR;
if
(Tcl_ListObjAppendElement(tree->interp, resultObjPtr, listObjPtr) != TCL_OK)
return
TCL_ERROR;
}
return
TCL_OK;
}
int
TreeItem_InitWidget(
TreeCtrl *tree
)
{
ItemButtonCO_Init(itemOptionSpecs,
"-button"
, ITEM_FLAG_BUTTON,
ITEM_FLAG_BUTTON_AUTO);
BooleanFlagCO_Init(itemOptionSpecs,
"-visible"
, ITEM_FLAG_VISIBLE);
BooleanFlagCO_Init(itemOptionSpecs,
"-wrap"
, ITEM_FLAG_WRAP);
tree->itemOptionTable = Tk_CreateOptionTable(tree->interp, itemOptionSpecs);
tree->root = Item_AllocRoot(tree);
tree->activeItem = tree->root;
tree->anchorItem = tree->root;
return
TCL_OK;
}
void
TreeItem_FreeWidget(
TreeCtrl *tree
)
{
SpanInfoStack *siStack = tree->itemSpanPriv;
while
(siStack != NULL) {
SpanInfoStack *next = siStack->next;
if
(siStack->spans != NULL)
ckfree((
char
*) siStack->spans);
if
(siStack->columns != NULL)
ckfree((
char
*) siStack->columns);
ckfree((
char
*) siStack);
siStack = next;
}
}