/*
* tkTreeStyle.c --
*
* This module implements styles for treectrl widgets.
*
* Copyright (c) 2002-2011 Tim Baker
*/
#include "tkTreeCtrl.h"
#include "tkTreeElem.h"
/* This is the roundUp argument to TreeAlloc_CAlloc. */
#define ELEMENT_LINK_ROUND 1
/* Define this for performance gain and increased memory usage. */
/* When undefined, there is quite a performance hit when elements are
* squeezable and a style has less than its needed width. The memory
* savings are not too great when undefined. */
#define CACHE_STYLE_SIZE
/* Define this for performance gain and increased memory usage. */
#define CACHE_ELEM_SIZE
typedef struct MStyle MStyle;
typedef struct IStyle IStyle;
typedef struct MElementLink MElementLink;
typedef struct IElementLink IElementLink;
/*
* A data structure of the following type is kept for each master style.
* Master styles are created by the [style create] widget command.
*/
struct MStyle
{
MStyle *master; /* Always NULL. Needed to distinguish between
* an MStyle and IStyle. */
Tk_Uid name; /* Unique identifier. */
int numElements; /* Size of elements[]. */
MElementLink *elements; /* Array of master elements. */
int vertical; /* -orient */
int buttonY; /* -buttony */
Tcl_Obj *buttonYObj; /* -buttony */
int stateDomain; /* STATE_DOMAIN_XXX index. */
int hasWindowElem; /* TRUE if style has any window elements. */
int hidden; /* Hackish flag for hidden header styles. */
int hasHeaderElem; /* Hackish flag to remember if the style has
* any elements of type 'header'. Headers
* are a fixed height on Aqua. */
};
/*
* A data structure of the following type is kept for each instance style.
* Instance styles are created when a style is assigned to an item-column.
*/
struct IStyle
{
MStyle *master; /* Always non-NULL. */
IElementLink *elements; /* Array of master or instance elements. */
int neededWidth; /* Requested size of this style based on */
int neededHeight; /* layout of the elements. */
#ifdef TREECTRL_DEBUG
int neededState;
#endif
#ifdef CACHE_STYLE_SIZE
int minWidth;
int minHeight;
int layoutWidth;
int layoutHeight;
#endif
};
#define ELF_eEXPAND_W 0x0001 /* expand Layout.ePadX[0] */
#define ELF_eEXPAND_N 0x0002
#define ELF_eEXPAND_E 0x0004
#define ELF_eEXPAND_S 0x0008
#define ELF_iEXPAND_W 0x0010 /* expand Layout.iPadX[0] */
#define ELF_iEXPAND_N 0x0020
#define ELF_iEXPAND_E 0x0040
#define ELF_iEXPAND_S 0x0080
#define ELF_SQUEEZE_X 0x0100 /* shrink Layout.useWidth if needed */
#define ELF_SQUEEZE_Y 0x0200
#define ELF_DETACH 0x0400
#define ELF_INDENT 0x0800 /* don't layout under button&line area */
#define ELF_STICKY_W 0x1000
#define ELF_STICKY_N 0x2000
#define ELF_STICKY_E 0x4000
#define ELF_STICKY_S 0x8000
#define ELF_iEXPAND_X 0x00010000 /* expand Layout.useWidth */
#define ELF_iEXPAND_Y 0x00020000
#define ELF_CENTER_X 0x00040000
#define ELF_CENTER_Y 0x00080000
#define ELF_eEXPAND_WE (ELF_eEXPAND_W | ELF_eEXPAND_E)
#define ELF_eEXPAND_NS (ELF_eEXPAND_N | ELF_eEXPAND_S)
#define ELF_eEXPAND (ELF_eEXPAND_WE | ELF_eEXPAND_NS)
#define ELF_iEXPAND_WE (ELF_iEXPAND_W | ELF_iEXPAND_E)
#define ELF_iEXPAND_NS (ELF_iEXPAND_N | ELF_iEXPAND_S)
#define ELF_iEXPAND (ELF_iEXPAND_WE | ELF_iEXPAND_NS)
#define ELF_EXPAND_WE (ELF_eEXPAND_WE | ELF_iEXPAND_WE)
#define ELF_EXPAND_NS (ELF_eEXPAND_NS | ELF_iEXPAND_NS)
#define ELF_EXPAND_W (ELF_eEXPAND_W | ELF_iEXPAND_W)
#define ELF_EXPAND_N (ELF_eEXPAND_N | ELF_iEXPAND_N)
#define ELF_EXPAND_E (ELF_eEXPAND_E | ELF_iEXPAND_E)
#define ELF_EXPAND_S (ELF_eEXPAND_S | ELF_iEXPAND_S)
#define ELF_STICKY (ELF_STICKY_W | ELF_STICKY_N | ELF_STICKY_E | ELF_STICKY_S)
#define IS_CENTER_X(e) (((e)->flags & ELF_CENTER_X) != 0)
#define IS_CENTER_Y(e) (((e)->flags & ELF_CENTER_Y) != 0)
#define IS_DETACH(e) (((e)->flags & ELF_DETACH) != 0)
#define IS_UNION(e) ((e)->onion != NULL)
#define DETACH_OR_UNION(e) (IS_DETACH(e) || IS_UNION(e))
/*
* An array of these is kept for each master style, one per element.
* Most of the fields are set by the [style layout] widget command.
*/
struct MElementLink
{
TreeElement elem; /* Master element. */
int ePadX[2]; /* external horizontal padding */
int ePadY[2]; /* external vertical padding */
int iPadX[2]; /* internal horizontal padding */
int iPadY[2]; /* internal vertical padding */
int flags; /* ELF_xxx */
int *onion, onionCount; /* -union option info */
int minWidth, fixedWidth, maxWidth;
int minHeight, fixedHeight, maxHeight;
PerStateInfo draw; /* -draw */
PerStateInfo visible; /* -visible */
};
/*
* An array of these is kept for each instance style, one per element.
*/
struct IElementLink
{
TreeElement elem; /* Master or instance element. */
#ifdef CACHE_ELEM_SIZE
int neededWidth;
int neededHeight;
int layoutWidth;
int layoutHeight;
#endif
};
#ifdef ALLOC_HAX
static CONST char *MStyleUid = "MStyle", *IStyleUid = "IStyle",
*MElementLinkUid = "MElementLink", *IElementLinkUid = "IElementLink";
#endif
static char *orientStringTable[] = { "horizontal", "vertical", (char *) NULL };
static Tk_OptionSpec styleOptionSpecs[] = {
{TK_OPTION_PIXELS, "-buttony", (char *) NULL, (char *) NULL,
(char *) NULL, Tk_Offset(MStyle, buttonYObj),
Tk_Offset(MStyle, buttonY),
TK_OPTION_NULL_OK, (ClientData) NULL, 0},
{TK_OPTION_STRING_TABLE, "-orient", (char *) NULL, (char *) NULL,
"horizontal", -1, Tk_Offset(MStyle, vertical),
0, (ClientData) orientStringTable, 0},
{TK_OPTION_END, (char *) NULL, (char *) NULL, (char *) NULL,
(char *) NULL, 0, -1, 0, (ClientData) NULL, 0}
};
/*
* The following structure is used to hold layout information about a
* single element. This information is not cached anywhere.
*/
struct Layout
{
MElementLink *master;
IElementLink *eLink;
int useWidth;
int useHeight;
#ifndef CACHE_ELEM_SIZE
int neededWidth;
int neededHeight;
#endif
int x; /* left of ePad */
int y; /* above ePad */
int eWidth; /* ePad + iPad + useWidth + iPad + ePad */
int eHeight; /* ePad + iPad + useHeight + iPad + ePad */
int iWidth; /* iPad + useWidth + iPad */
int iHeight; /* iPad + useHeight + iPad */
int ePadX[2]; /* external horizontal padding */
int ePadY[2]; /* external vertical padding */
int iPadX[2]; /* internal horizontal padding */
int iPadY[2]; /* internal vertical padding */
int uPadX[2]; /* padding due to -union */
int uPadY[2]; /* padding due to -union */
int temp;
int visible; /* TRUE if the element should be displayed. */
int unionFirst, unionLast; /* First and last visible elements in this
* element's -union */
int unionParent; /* TRUE if this element is in one or more element's
* -union */
/* These are all hacks for header elements. */
int eMargins[4]; /* Content margins, including arrow width but not -arrowpadx */
int uMargins[4]; /* Content margins, including arrow width and -arrowpadx */
int eUnionBbox[4]; /* Bounds of elements in a this element's -union,
* including external padding of those elements. */
int iUnionBbox[4]; /* Bounds of elements in a this element's -union,
* not including external padding of those elements. */
int arrowHeight; /* Height of the sort arrow + -arrowpady. */
};
#define IS_HIDDEN(L) ((L)->visible == 0)
/*
*----------------------------------------------------------------------
*
* Style_DoExpandH --
*
* Add extra horizontal space to a single element. The space is
* distributed from right to left until all available space
* is used or expansion is not possible.
*
* Results:
* Layout.ePadX, Layout.iPadX, and Layout.useWidth may be
* updated. The amount of available space that was used is
* returned.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
Style_DoExpandH(
struct Layout *layout, /* Layout to be adjusted. */
int extraSpace /* Amount of extra space to add. */
)
{
MElementLink *eLink1 = layout->master;
int flags = eLink1->flags;
int numExpand = 0, spaceRemaining, spaceUsed = 0;
int *ePadX, *iPadX;
if (!(flags & (ELF_EXPAND_WE | ELF_iEXPAND_X)))
return 0;
ePadX = layout->ePadX;
iPadX = layout->iPadX;
spaceRemaining = extraSpace;
if (spaceRemaining <= 0)
return 0;
if (layout->temp)
numExpand = layout->temp;
/* For -detach or vertical layout, just set layout->temp to zero */
else {
if (flags & ELF_eEXPAND_W) numExpand++;
if (flags & ELF_iEXPAND_W) numExpand++;
if (flags & ELF_iEXPAND_X) {
if ((eLink1->maxWidth < 0) ||
(eLink1->maxWidth > layout->useWidth))
numExpand++;
}
if (flags & ELF_iEXPAND_E) numExpand++;
if (flags & ELF_eEXPAND_E) numExpand++;
}
while ((spaceRemaining > 0) && (numExpand > 0)) {
int each = (spaceRemaining >= numExpand) ? (spaceRemaining / numExpand) : 1;
numExpand = 0;
/* Allocate extra space to the *right* padding first so that any
* extra single pixel is given to the right. */
if (flags & ELF_eEXPAND_E) {
int add = each;
ePadX[PAD_BOTTOM_RIGHT] += add;
layout->eWidth += add;
spaceRemaining -= add;
spaceUsed += add;
if (!spaceRemaining)
break;
numExpand++;
}
if (flags & ELF_iEXPAND_E) {
int add = each;
iPadX[PAD_BOTTOM_RIGHT] += add;
layout->iWidth += add;
layout->eWidth += add;
spaceRemaining -= add;
spaceUsed += add;
if (!spaceRemaining)
break;
numExpand++;
}
if (flags & ELF_iEXPAND_X) {
int max = eLink1->maxWidth;
if ((max < 0) || (layout->useWidth < max)) {
int add = (max < 0) ? each : MIN(each, max - layout->useWidth);
layout->useWidth += add;
layout->iWidth += add;
layout->eWidth += add;
spaceRemaining -= add;
spaceUsed += add;
if ((max >= 0) && (max == layout->useWidth))
layout->temp--;
if (!spaceRemaining)
break;
if ((max < 0) || (max > layout->useWidth))
numExpand++;
}
}
if (flags & ELF_iEXPAND_W) {
int add = each;
iPadX[PAD_TOP_LEFT] += add;
layout->iWidth += add;
layout->eWidth += add;
spaceRemaining -= add;
spaceUsed += add;
if (!spaceRemaining)
break;
numExpand++;
}
if (flags & ELF_eEXPAND_W) {
int add = each;
ePadX[PAD_TOP_LEFT] += add;
layout->eWidth += add;
spaceRemaining -= add;
spaceUsed += add;
if (!spaceRemaining)
break;
numExpand++;
}
}
return spaceUsed;
}
/*
*----------------------------------------------------------------------
*
* Style_DoExpandV --
*
* Add extra vertical space to a single element. The space is
* distributed from bottom to top until all available space
* is used or expansion is not possible.
*
* Results:
* Layout.ePadY, Layout.iPadY, and Layout.useHeight may be
* updated. The amount of available space that was used is
* returned.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
Style_DoExpandV(
struct Layout *layout, /* Layout to be adjusted. */
int extraSpace /* Amount of extra space to add. */
)
{
MElementLink *eLink1 = layout->master;
int flags = eLink1->flags;
int numExpand = 0, spaceRemaining, spaceUsed = 0;
int *ePadY, *iPadY;
if (!(flags & (ELF_EXPAND_NS | ELF_iEXPAND_Y)))
return 0;
ePadY = layout->ePadY;
iPadY = layout->iPadY;
spaceRemaining = extraSpace;
if (spaceRemaining <= 0)
return 0;
if (layout->temp)
numExpand = layout->temp;
/* For -detach or vertical layout, just set layout->temp to zero */
else {
if (flags & ELF_eEXPAND_N) numExpand++;
if (flags & ELF_iEXPAND_N) numExpand++;
if (flags & ELF_iEXPAND_Y) {
if ((eLink1->maxHeight < 0) ||
(eLink1->maxHeight > layout->useHeight))
numExpand++;
}
if (flags & ELF_iEXPAND_S) numExpand++;
if (flags & ELF_eEXPAND_S) numExpand++;
}
while ((spaceRemaining > 0) && (numExpand > 0)) {
int each = (spaceRemaining >= numExpand) ? (spaceRemaining / numExpand) : 1;
numExpand = 0;
/* Allocate extra space to the *bottom* padding first so that any
* extra single pixel is given to the bottom. */
if (flags & ELF_eEXPAND_S) {
int add = each;
ePadY[PAD_BOTTOM_RIGHT] += add;
layout->eHeight += add;
spaceRemaining -= add;
spaceUsed += add;
if (!spaceRemaining)
break;
numExpand++;
}
if (flags & ELF_iEXPAND_S) {
int add = each;
iPadY[PAD_BOTTOM_RIGHT] += add;
layout->iHeight += add;
layout->eHeight += add;
spaceRemaining -= add;
spaceUsed += add;
if (!spaceRemaining)
break;
numExpand++;
}
if (flags & ELF_iEXPAND_Y) {
int max = eLink1->maxHeight;
if ((max < 0) || (layout->useHeight < max)) {
int add = (max < 0) ? each : MIN(each, max - layout->useHeight);
layout->useHeight += add;
layout->iHeight += add;
layout->eHeight += add;
spaceRemaining -= add;
spaceUsed += add;
if ((max >= 0) && (max == layout->useHeight))
layout->temp--;
if (!spaceRemaining)
break;
if ((max < 0) || (max > layout->useHeight))
numExpand++;
}
}
if (flags & ELF_iEXPAND_N) {
int add = each;
iPadY[PAD_TOP_LEFT] += add;
layout->iHeight += add;
layout->eHeight += add;
spaceRemaining -= add;
spaceUsed += add;
if (!spaceRemaining)
break;
numExpand++;
}
if (flags & ELF_eEXPAND_N) {
int add = each;
ePadY[PAD_TOP_LEFT] += add;
layout->eHeight += add;
spaceRemaining -= add;
spaceUsed += add;
if (!spaceRemaining)
break;
numExpand++;
}
}
return spaceUsed;
}
/*
*----------------------------------------------------------------------
*
* ElementLink_NeededSize --
*
* Calculate the needed width and height of an element.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
Element_NeededSize(
TreeCtrl *tree, /* Widget info. */
MElementLink *eLink1, /* Master style layout info. */
TreeElement elem, /* Master/Instance element. */
int state, /* STATE_xxx flags. */
int *widthPtr, /* Out: width */
int *heightPtr /* Out: height */
)
{
TreeElementArgs args;
int width, height;
if ((eLink1->fixedWidth >= 0) && (eLink1->fixedHeight >= 0)) {
width = eLink1->fixedWidth;
height = eLink1->fixedHeight;
} else {
args.tree = tree;
args.state = state;
args.elem = elem;
args.needed.fixedWidth = eLink1->fixedWidth;
args.needed.fixedHeight = eLink1->fixedHeight;
if (eLink1->maxWidth > eLink1->minWidth)
args.needed.maxWidth = eLink1->maxWidth;
else
args.needed.maxWidth = -1;
if (eLink1->maxHeight > eLink1->minHeight)
args.needed.maxHeight = eLink1->maxHeight;
else
args.needed.maxHeight = -1;
(*args.elem->typePtr->neededProc)(&args);
width = args.needed.width;
height = args.needed.height;
if (eLink1->fixedWidth >= 0)
width = eLink1->fixedWidth;
else if ((eLink1->minWidth >= 0) &&
(width < eLink1->minWidth))
width = eLink1->minWidth;
else if ((eLink1->maxWidth >= 0) &&
(width > eLink1->maxWidth))
width = eLink1->maxWidth;
if (eLink1->fixedHeight >= 0)
height = eLink1->fixedHeight;
else if ((eLink1->minHeight >= 0) &&
(height < eLink1->minHeight))
height = eLink1->minHeight;
else if ((eLink1->maxHeight >= 0) &&
(height > eLink1->maxHeight))
height = eLink1->maxHeight;
}
*widthPtr = width;
*heightPtr = height;
}
/*
*----------------------------------------------------------------------
*
* Layout_CalcVisibility --
*
* Recursively calculate the visibility of each element.
*
* Results:
* Layout.visible is set for each element if it wasn't done
* already. For -union elements the first and last visible
* elements in the union are determined. If there are no
* visible elements in the union then the union element
* itself is marked 'not visible'.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
Layout_CalcVisibility(
TreeCtrl *tree,
int state,
MStyle *masterStyle,
struct Layout layouts[],
int iElem
)
{
struct Layout *layout = &layouts[iElem];
MElementLink *eLink = &masterStyle->elements[iElem];
int i, visible = 0;
if (layout->temp != 0)
return; /* Already did this one */
layout->temp = 1;
layout->visible = PerStateBoolean_ForState(tree, &eLink->visible,
state, NULL) != 0;
if (IS_HIDDEN(layout) || !IS_UNION(eLink))
return;
/* Remember the first and last visible elements surrounded by
* this -union element. */
layout->unionFirst = layout->unionLast = -1;
for (i = 0; i < eLink->onionCount; i++) {
struct Layout *layout2 = &layouts[eLink->onion[i]];
Layout_CalcVisibility(tree, state, masterStyle, layouts, eLink->onion[i]);
if (!IS_HIDDEN(layout2)) {
if (layout->unionFirst == -1)
layout->unionFirst = eLink->onion[i];
layout->unionLast = eLink->onion[i];
visible++;
}
}
/* If there are no visible elements surrounded by this -union
* element, then hide it. */
if (visible == 0)
layout->visible = 0;
}
/*
*----------------------------------------------------------------------
*
* Layout_AddUnionPadding --
*
* Recursively determine the amount of -union padding around
* each element that is part of a -union.
*
* Results:
* If the element isn't a -union element itself then the
* Layout.uPadX and Layout.uPadY fields are updated.
* If the element is a -union element itself then its padding
* is added to the total padding and passed on to its -union
* elements recursively.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
Layout_AddUnionPadding(
TreeCtrl *tree, /* Widget info. */
MStyle *masterStyle, /* Style being layed out. */
struct Layout layouts[], /* Layout info for every element. */
int iElemParent, /* Whose -union iElem is in. */
int iElem, /* The element to update. */
const int totalPadX[2], /* The cumulative padding around */
const int totalPadY[2] /* iElemParent plus the -ipad */
/* padding of iElemParent itself. */
)
{
struct Layout *layoutP = &layouts[iElemParent];
struct Layout *layout = &layouts[iElem];
MElementLink *eLink = &masterStyle->elements[iElem];
int *ePadX, *ePadY, *iPadX, *iPadY, *uPadX, *uPadY;
int padX[2], padY[2];
int i;
#ifdef TREECTRL_DEBUG
if (IS_HIDDEN(layoutP) || IS_HIDDEN(layout))
panic("Layout_AddUnionPadding: element is hidden");
#endif
uPadX = layout->uPadX;
uPadY = layout->uPadY;
if (masterStyle->vertical) {
uPadX[PAD_TOP_LEFT] = MAX(uPadX[PAD_TOP_LEFT], totalPadX[PAD_TOP_LEFT]);
uPadX[PAD_BOTTOM_RIGHT] = MAX(uPadX[PAD_BOTTOM_RIGHT], totalPadX[PAD_BOTTOM_RIGHT]);
if (iElem == layoutP->unionFirst) /* topmost */
uPadY[PAD_TOP_LEFT] = MAX(uPadY[PAD_TOP_LEFT], totalPadY[PAD_TOP_LEFT]);
if (iElem == layoutP->unionLast) /* bottommost */
uPadY[PAD_BOTTOM_RIGHT] = MAX(uPadY[PAD_BOTTOM_RIGHT], totalPadY[PAD_BOTTOM_RIGHT]);
} else {
if (iElem == layoutP->unionFirst) /* leftmost */
uPadX[PAD_TOP_LEFT] = MAX(uPadX[PAD_TOP_LEFT], totalPadX[PAD_TOP_LEFT]);
if (iElem == layoutP->unionLast) /* rightmost */
uPadX[PAD_BOTTOM_RIGHT] = MAX(uPadX[PAD_BOTTOM_RIGHT], totalPadX[PAD_BOTTOM_RIGHT]);
uPadY[PAD_TOP_LEFT] = MAX(uPadY[PAD_TOP_LEFT], totalPadY[PAD_TOP_LEFT]);
uPadY[PAD_BOTTOM_RIGHT] = MAX(uPadY[PAD_BOTTOM_RIGHT], totalPadY[PAD_BOTTOM_RIGHT]);
}
/* In the original header-layout code, the bitmap/image/text/arrow's
* -XXXpady padding did not overlap the content margins, so the top and
* bottom external padding of elements in a header element's -union is
* added to the union padding around each element. */
/* In the original header-layout code, the -arrowpadx padding would
* overlap the padding of the adjacent bitmap/image/text, so any elements
* adjacent to the sort arrow have their union padding increased by any
* positive difference between their external padding and the sort arrow's
* external padding. */
/* In the original header-layout code, the total width of the header was
* equal to the width of the bitmap/image/text plus overlapped padding
* between them plus padding on the exteme left and right, so, even if
* there is no arrow padding, the union padding of elements on the extreme
* left or right is increased by those element's external padding. */
if (ELEMENT_TYPE_MATCHES(layoutP->master->elem->typePtr, &treeElemTypeHeader)) {
int arrowPadLeft = layoutP->uMargins[2] - layoutP->eMargins[2];
int arrowPadRight = layoutP->uMargins[0] - layoutP->eMargins[0];
if (masterStyle->vertical) {
uPadX[PAD_TOP_LEFT] += MAX(layout->ePadX[PAD_TOP_LEFT] - arrowPadRight, 0);
uPadX[PAD_BOTTOM_RIGHT] += MAX(layout->ePadX[PAD_BOTTOM_RIGHT] - arrowPadLeft, 0);
if (iElem == layoutP->unionFirst) /* topmost */
uPadY[PAD_TOP_LEFT] += layout->ePadY[PAD_TOP_LEFT];
if (iElem == layoutP->unionLast) /* bottommost */
uPadY[PAD_BOTTOM_RIGHT] += layout->ePadY[PAD_BOTTOM_RIGHT];
} else {
if (iElem == layoutP->unionFirst) /* leftmost */
uPadX[PAD_TOP_LEFT] += MAX(layout->ePadX[PAD_TOP_LEFT] - arrowPadRight, 0);
if (iElem == layoutP->unionLast) /* rightmost */
uPadX[PAD_BOTTOM_RIGHT] += MAX(layout->ePadX[PAD_BOTTOM_RIGHT] - arrowPadLeft, 0);
uPadY[PAD_TOP_LEFT] += layout->ePadY[PAD_TOP_LEFT];
uPadY[PAD_BOTTOM_RIGHT] += layout->ePadY[PAD_BOTTOM_RIGHT];
}
}
if (!IS_UNION(eLink)){
return;
}
ePadX = layout->ePadX;
ePadY = layout->ePadY;
iPadX = layout->iPadX;
iPadY = layout->iPadY;
for (i = 0; i < 2; i++) {
padX[i] = MAX(totalPadX[i], ePadX[i]) + iPadX[i] + layout->uMargins[i*2];
padY[i] = MAX(totalPadY[i], ePadY[i]) + iPadY[i] + layout->uMargins[i*2+1];
}
for (i = 0; i < eLink->onionCount; i++) {
struct Layout *layout2 = &layouts[eLink->onion[i]];
if (IS_HIDDEN(layout2))
continue;
Layout_AddUnionPadding(tree, masterStyle, layouts, iElem,
eLink->onion[i], padX, padY
);
}
}
/*
*----------------------------------------------------------------------
*
* Layout_ExpandUnionH --
*
* Add extra horizontal space to a single IS_UNION element.
* Expansion of -union elements is different than non-union
* elements. Expanding a -union element never changes the size or
* position of any element other than the -union element itself.
*
* Results:
* Layout.ePadX and Layout.iPadX are given extra space as needed.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
Layout_ExpandUnionH(
StyleDrawArgs *drawArgs, /* Various args. */
MStyle *masterStyle, /* Style being layed out. */
struct Layout layouts[], /* Layout info for every element. */
int iElem /* The element to update. */
)
{
struct Layout *layout = &layouts[iElem];
MElementLink *eLink1 = &masterStyle->elements[iElem];
int *ePadX, *iPadX, *uPadX;
int extraWidth, x, indent = drawArgs->indent;
#ifdef TREECTRL_DEBUG
if (IS_HIDDEN(layout))
panic("Layout_ExpandUnionH: element is hidden");
if (!IS_UNION(eLink1))
panic("Layout_ExpandUnionH: element is !union");
#endif
if (!(eLink1->flags & ELF_EXPAND_WE))
return;
/* Hack -- header elements may not be indented by -canvaspadx. */
if ((masterStyle->stateDomain == STATE_DOMAIN_HEADER) &&
!(eLink1->flags & ELF_INDENT))
indent = 0;
if (drawArgs->width - (layout->eWidth + indent) <= 0)
return;
ePadX = layout->ePadX;
iPadX = layout->iPadX;
uPadX = layout->uPadX;
x = layout->x + ePadX[PAD_TOP_LEFT] - MAX(ePadX[PAD_TOP_LEFT], uPadX[PAD_TOP_LEFT]);
extraWidth = x - indent;
if ((extraWidth > 0) && (eLink1->flags & ELF_EXPAND_W)) {
/* External and internal expansion: W */
if ((eLink1->flags & ELF_EXPAND_W) == ELF_EXPAND_W) {
int eExtra = extraWidth / 2;
int iExtra = extraWidth - extraWidth / 2;
layout->x = indent + uPadX[PAD_TOP_LEFT];
/* External expansion */
ePadX[PAD_TOP_LEFT] += eExtra;
layout->eWidth += extraWidth;
/* Internal expansion */
iPadX[PAD_TOP_LEFT] += iExtra;
layout->iWidth += iExtra;
}
/* External expansion only: W */
else if (eLink1->flags & ELF_eEXPAND_W) {
ePadX[PAD_TOP_LEFT] += extraWidth;
layout->x = indent + uPadX[PAD_TOP_LEFT];
layout->eWidth += extraWidth;
}
/* Internal expansion only: W */
else {
iPadX[PAD_TOP_LEFT] += extraWidth;
layout->x = indent + uPadX[PAD_TOP_LEFT];
layout->iWidth += extraWidth;
layout->eWidth += extraWidth;
}
}
x = layout->x + layout->eWidth - ePadX[PAD_BOTTOM_RIGHT] + MAX(ePadX[PAD_BOTTOM_RIGHT], uPadX[PAD_BOTTOM_RIGHT]);
extraWidth = drawArgs->width - x;
if ((extraWidth > 0) && (eLink1->flags & ELF_EXPAND_E)) {
/* External and internal expansion: E */
if ((eLink1->flags & ELF_EXPAND_E) == ELF_EXPAND_E) {
int eExtra = extraWidth / 2;
int iExtra = extraWidth - extraWidth / 2;
/* External expansion */
ePadX[PAD_BOTTOM_RIGHT] += eExtra;
layout->eWidth += extraWidth; /* all the space */
/* Internal expansion */
iPadX[PAD_BOTTOM_RIGHT] += iExtra;
layout->iWidth += iExtra;
}
/* External expansion only: E */
else if (eLink1->flags & ELF_eEXPAND_E) {
ePadX[PAD_BOTTOM_RIGHT] += extraWidth;
layout->eWidth += extraWidth;
}
/* Internal expansion only: E */
else {
iPadX[PAD_BOTTOM_RIGHT] += extraWidth;
layout->iWidth += extraWidth;
layout->eWidth += extraWidth;
}
}
}
/*
*----------------------------------------------------------------------
*
* Layout_ExpandUnionV --
*
* Add extra vertical space to a single IS_UNION element.
* Expansion of -union elements is different than non-union
* elements. Expanding a -union element never changes the size or
* position of any element other than the -union element itself.
*
* Results:
* Layout.ePadY and Layout.iPadY are given extra space as needed.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
Layout_ExpandUnionV(
StyleDrawArgs *drawArgs, /* Various args. */
MStyle *masterStyle, /* Style being layed out. */
struct Layout layouts[], /* Layout info for every element. */
int iElem /* The element to update. */
)
{
struct Layout *layout = &layouts[iElem];
MElementLink *eLink1 = &masterStyle->elements[iElem];
int *ePadY, *iPadY, *uPadY;
int extraHeight, y;
#ifdef TREECTRL_DEBUG
if (IS_HIDDEN(layout))
panic("Layout_ExpandUnionV: element is hidden");
if (!IS_UNION(eLink1))
panic("Layout_ExpandUnionV: element is !union");
#endif
if (!(eLink1->flags & ELF_EXPAND_NS))
return;
if (drawArgs->height - layout->eHeight <= 0)
return;
ePadY = layout->ePadY;
iPadY = layout->iPadY;
uPadY = layout->uPadY;
y = layout->y + ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]);
extraHeight = y;
if ((extraHeight > 0) && (eLink1->flags & ELF_EXPAND_N)) {
/* External and internal expansion: N */
if ((eLink1->flags & ELF_EXPAND_N) == ELF_EXPAND_N) {
int eExtra = extraHeight / 2;
int iExtra = extraHeight - extraHeight / 2;
/* External expansion */
ePadY[PAD_TOP_LEFT] += eExtra;
layout->y = uPadY[PAD_TOP_LEFT];
layout->eHeight += extraHeight;
/* Internal expansion */
iPadY[PAD_TOP_LEFT] += iExtra;
layout->iHeight += iExtra;
}
/* External expansion only: N */
else if (eLink1->flags & ELF_eEXPAND_N) {
ePadY[PAD_TOP_LEFT] += extraHeight;
layout->y = uPadY[PAD_TOP_LEFT];
layout->eHeight += extraHeight;
}
/* Internal expansion only: N */
else {
iPadY[PAD_TOP_LEFT] += extraHeight;
layout->y = uPadY[PAD_TOP_LEFT];
layout->iHeight += extraHeight;
layout->eHeight += extraHeight;
}
}
y = layout->y + layout->eHeight - ePadY[PAD_BOTTOM_RIGHT] + MAX(ePadY[PAD_BOTTOM_RIGHT], uPadY[PAD_BOTTOM_RIGHT]);
extraHeight = drawArgs->height - y;
if ((extraHeight > 0) && (eLink1->flags & ELF_EXPAND_S)) {
/* External and internal expansion: S */
if ((eLink1->flags & ELF_EXPAND_S) == ELF_EXPAND_S) {
int eExtra = extraHeight / 2;
int iExtra = extraHeight - extraHeight / 2;
/* External expansion */
ePadY[PAD_BOTTOM_RIGHT] += eExtra;
layout->eHeight += extraHeight; /* all the space */
/* Internal expansion */
iPadY[PAD_BOTTOM_RIGHT] += iExtra;
layout->iHeight += iExtra;
}
/* External expansion only: S */
else if (eLink1->flags & ELF_eEXPAND_S) {
ePadY[PAD_BOTTOM_RIGHT] += extraHeight;
layout->eHeight += extraHeight;
}
/* Internal expansion only */
else {
iPadY[PAD_BOTTOM_RIGHT] += extraHeight;
layout->iHeight += extraHeight;
layout->eHeight += extraHeight;
}
}
}
/*
*----------------------------------------------------------------------
*
* Layout_CalcUnionLayoutH --
*
* Recursively calculate the horizontal size and position of
* a -union element.
*
* Results:
* The Layout record for the element is updated by getting the
* horizontal bounds of each element in its -union (after they
* have their size and position calculated). Then expansion
* is performed.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
Layout_CalcUnionLayoutH(
StyleDrawArgs *drawArgs, /* Various args. */
MStyle *masterStyle, /* Style being layed out. */
struct Layout layouts[], /* Layout info for every element. */
int iElem /* Index of the union element. */
)
{
struct Layout *layout = &layouts[iElem];
MElementLink *eLink = &masterStyle->elements[iElem];
int *ePadX, *iPadX;
int i, iW, iE, eW, eE;
#ifdef TREECTRL_DEBUG
if (IS_HIDDEN(layout))
panic("Layout_CalcUnionLayoutH: element is hidden");
#endif
if (!IS_UNION(eLink))
return;
eW = iW = 1000000, eE = iE = -1000000;
for (i = 0; i < eLink->onionCount; i++) {
struct Layout *layout2 = &layouts[eLink->onion[i]];
if (IS_HIDDEN(layout2))
continue;
Layout_CalcUnionLayoutH(drawArgs, masterStyle, layouts, eLink->onion[i]);
iW = MIN(iW, layout2->x + layout2->ePadX[PAD_TOP_LEFT]);
iE = MAX(iE, layout2->x + layout2->ePadX[PAD_TOP_LEFT] + layout2->iWidth);
eW = MIN(eW, layout2->x);
eE = MAX(eE, layout2->x + layout2->eWidth);
}
layout->iUnionBbox[0] = iW;
layout->iUnionBbox[2] = iE;
layout->eUnionBbox[0] = eW;
layout->eUnionBbox[2] = eE;
ePadX = layout->ePadX;
iPadX = layout->iPadX;
layout->x = iW - layout->uMargins[0] - iPadX[PAD_TOP_LEFT] - ePadX[PAD_TOP_LEFT];
layout->useWidth = layout->uMargins[0] + (iE - iW) + layout->uMargins[2];
layout->iWidth = iPadX[PAD_TOP_LEFT] + layout->useWidth + iPadX[PAD_BOTTOM_RIGHT];
layout->eWidth = ePadX[PAD_TOP_LEFT] + layout->iWidth + ePadX[PAD_BOTTOM_RIGHT];
Layout_ExpandUnionH(drawArgs, masterStyle, layouts, iElem);
}
/*
*----------------------------------------------------------------------
*
* Layout_CalcUnionLayoutV --
*
* Recursively calculate the vertical size and position of
* a -union element.
*
* Results:
* The Layout record for the element is updated by getting the
* vertical bounds of each element in its -union (after they
* have their size and position calculated). Then expansion
* is performed.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
Layout_CalcUnionLayoutV(
StyleDrawArgs *drawArgs, /* Various args. */
MStyle *masterStyle, /* Style being layed out. */
struct Layout layouts[], /* Layout info for every element. */
int iElem /* Index of the union element. */
)
{
struct Layout *layout = &layouts[iElem];
MElementLink *eLink = &masterStyle->elements[iElem];
int *ePadY, *iPadY;
int i, iN, iS, eN, eS;
#ifdef TREECTRL_DEBUG
if (IS_HIDDEN(layout))
panic("Layout_CalcUnionLayoutV: element is hidden");
#endif
if (!IS_UNION(eLink))
return;
eN = iN = 1000000, eS = iS = -1000000;
for (i = 0; i < eLink->onionCount; i++) {
struct Layout *layout2 = &layouts[eLink->onion[i]];
if (IS_HIDDEN(layout2))
continue;
Layout_CalcUnionLayoutV(drawArgs, masterStyle, layouts, eLink->onion[i]);
iN = MIN(iN, layout2->y + layout2->ePadY[PAD_TOP_LEFT]);
iS = MAX(iS, layout2->y + layout2->ePadY[PAD_TOP_LEFT] + layout2->iHeight);
eN = MIN(eN, layout2->y);
eS = MAX(eS, layout2->y + layout2->eHeight);
}
layout->iUnionBbox[1] = iN;
layout->iUnionBbox[3] = iS;
layout->eUnionBbox[1] = eN;
layout->eUnionBbox[3] = eS;
ePadY = layout->ePadY;
iPadY = layout->iPadY;
layout->y = iN - layout->uMargins[1] - iPadY[PAD_TOP_LEFT] - ePadY[PAD_TOP_LEFT];
layout->useHeight = layout->uMargins[1] + (iS - iN) + layout->uMargins[3];
layout->iHeight = iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT];
layout->eHeight = ePadY[PAD_TOP_LEFT] + layout->iHeight + ePadY[PAD_BOTTOM_RIGHT];
Layout_ExpandUnionV(drawArgs, masterStyle, layouts, iElem);
}
/*
*----------------------------------------------------------------------
*
* Layout_ExpandElementsH --
*
* Adds extra horizontal space to a range of elements.
*
* Results:
* layouts[] is updated.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
Layout_ExpandElementsH(
StyleDrawArgs *drawArgs, /* Various args. */
struct Layout layouts[], /* All the layouts for the style. */
int iElemMin, /* Index of the first element. */
int iElemMax, /* Index of the last element. */
int maxX /* The right edge limiting expansion. */
)
{
MElementLink *eLink1;
int i, j, numExpand = 0, rightEdge = 0, rightEdgeU = 0;
int spaceRemaining, totalUsed = 0;
if (iElemMin > iElemMax)
return 0;
/* Each element has 5 areas that can optionally expand. */
for (i = iElemMin; i <= iElemMax; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
eLink1 = layout->master;
layout->temp = 0;
if (DETACH_OR_UNION(eLink1))
continue;
rightEdge = layout->x + layout->ePadX[PAD_TOP_LEFT] +
layout->iWidth +
layout->ePadX[PAD_BOTTOM_RIGHT];
rightEdgeU = MAX(rightEdgeU, layout->x + layout->ePadX[PAD_TOP_LEFT] +
layout->iWidth +
MAX(layout->ePadX[PAD_BOTTOM_RIGHT], layout->uPadX[PAD_BOTTOM_RIGHT]));
if (eLink1->flags & ELF_eEXPAND_W) layout->temp++;
if (eLink1->flags & ELF_iEXPAND_W) layout->temp++;
if (eLink1->flags & ELF_iEXPAND_X) {
if ((eLink1->maxWidth < 0) ||
(eLink1->maxWidth > layout->useWidth))
layout->temp++;
}
if (eLink1->flags & ELF_iEXPAND_E) layout->temp++;
if (eLink1->flags & ELF_eEXPAND_E) layout->temp++;
numExpand += layout->temp;
}
if (numExpand == 0)
return 0;
spaceRemaining = maxX - rightEdge;
if (drawArgs->width - rightEdgeU < spaceRemaining)
spaceRemaining = drawArgs->width - rightEdgeU;
if (spaceRemaining <= 0)
return 0;
while ((spaceRemaining > 0) && (numExpand > 0)) {
int each = (spaceRemaining >= numExpand) ? spaceRemaining / numExpand : 1;
numExpand = 0;
for (i = iElemMin; i <= iElemMax; i++) {
struct Layout *layout = &layouts[i];
int spaceUsed;
if (IS_HIDDEN(layout))
continue;
if (!layout->temp)
continue;
eLink1 = layout->master;
spaceUsed = Style_DoExpandH(layout,
MIN(each * layout->temp, spaceRemaining));
if (spaceUsed) {
/* Shift following elements to the right */
for (j = i + 1; j <= iElemMax; j++)
if (!IS_HIDDEN(&layouts[j]) && !DETACH_OR_UNION(layouts[j].master))
layouts[j].x += spaceUsed;
totalUsed += spaceUsed;
spaceRemaining -= spaceUsed;
if (!spaceRemaining)
break;
numExpand += layout->temp;
} else
layout->temp = 0;
}
}
return totalUsed;
}
/*
*----------------------------------------------------------------------
*
* Layout_ExpandElementsV --
*
* Adds extra vertical space to a range of elements.
*
* Results:
* layouts[] is updated.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
Layout_ExpandElementsV(
StyleDrawArgs *drawArgs, /* Various args. */
struct Layout layouts[], /* All the layouts for the style. */
int iElemMin, /* Index of the first element. */
int iElemMax, /* Index of the last element. */
int maxY /* The bottom edge limiting expansion. */
)
{
MElementLink *eLink1;
int i, j, numExpand = 0, bottomEdge = 0, bottomEdgeU = 0;
int spaceRemaining, totalUsed = 0;
if (iElemMin > iElemMax)
return 0;
/* Each element has 5 areas that can optionally expand. */
for (i = iElemMin; i <= iElemMax; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
eLink1 = layout->master;
layout->temp = 0;
if (DETACH_OR_UNION(eLink1))
continue;
bottomEdge = layout->y + layout->ePadY[PAD_TOP_LEFT] +
layout->iHeight +
layout->ePadY[PAD_BOTTOM_RIGHT];
bottomEdgeU = MAX(bottomEdgeU, layout->y + layout->ePadY[PAD_TOP_LEFT] +
layout->iHeight +
MAX(layout->ePadY[PAD_BOTTOM_RIGHT], layout->uPadY[PAD_BOTTOM_RIGHT]));
if (eLink1->flags & ELF_eEXPAND_N) layout->temp++;
if (eLink1->flags & ELF_iEXPAND_N) layout->temp++;
if (eLink1->flags & ELF_iEXPAND_Y) {
if ((eLink1->maxHeight < 0) ||
(eLink1->maxHeight > layout->useHeight))
layout->temp++;
}
if (eLink1->flags & ELF_iEXPAND_S) layout->temp++;
if (eLink1->flags & ELF_eEXPAND_S) layout->temp++;
numExpand += layout->temp;
}
if (numExpand == 0)
return 0;
spaceRemaining = maxY - bottomEdge;
if (drawArgs->height - bottomEdgeU < spaceRemaining)
spaceRemaining = drawArgs->height - bottomEdgeU;
if (spaceRemaining <= 0)
return 0;
while ((spaceRemaining > 0) && (numExpand > 0)) {
int each = (spaceRemaining >= numExpand) ? spaceRemaining / numExpand : 1;
numExpand = 0;
for (i = iElemMin; i <= iElemMax; i++) {
struct Layout *layout = &layouts[i];
int spaceUsed;
if (IS_HIDDEN(layout))
continue;
if (!layout->temp)
continue;
eLink1 = layout->master;
spaceUsed = Style_DoExpandV(layout,
MIN(each * layout->temp, spaceRemaining));
if (spaceUsed) {
/* Shift following elements down */
for (j = i + 1; j <= iElemMax; j++)
if (!IS_HIDDEN(&layouts[j]) && !DETACH_OR_UNION(layouts[j].master))
layouts[j].y += spaceUsed;
totalUsed += spaceUsed;
spaceRemaining -= spaceUsed;
if (!spaceRemaining)
break;
numExpand += layout->temp;
} else
layout->temp = 0;
}
}
return totalUsed;
}
/*
*----------------------------------------------------------------------
*
* Style_DoLayoutH --
*
* Calculate the horizontal size and position of each element.
* This gets called if the style -orient option is horizontal or
* vertical.
*
* Results:
* layouts[] is updated.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
Style_DoLayoutH(
StyleDrawArgs *drawArgs, /* Various args. */
struct Layout layouts[] /* Array of layout records to be
* filled in, one per element. Should be
* uninitialized. */
)
{
TreeCtrl *tree = drawArgs->tree;
IStyle *style = (IStyle *) drawArgs->style;
MStyle *masterStyle = style->master;
MElementLink *eLinks1, *eLink1;
IElementLink *eLinks2, *eLink2;
int x = drawArgs->indent;
int *ePadX, *iPadX, *uPadX, *ePadY, *iPadY;
int numExpandWE = 0;
int numSqueezeX = 0;
int i, j, eLinkCount = 0;
int rightEdge = 0;
int iCenterMin = -1, iCenterMax = -1;
eLinks1 = masterStyle->elements;
eLinks2 = style->elements;
eLinkCount = masterStyle->numElements;
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
layout->unionParent = 0;
layout->temp = 0; /* see Layout_CalcVisibility */
}
for (i = 0; i < eLinkCount; i++) {
Layout_CalcVisibility(drawArgs->tree, drawArgs->state, masterStyle,
layouts, i);
}
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
eLink2 = &eLinks2[i];
if (IS_UNION(eLink1)) {
for (j = 0; j < eLink1->onionCount; j++) {
struct Layout *layout2 = &layouts[eLink1->onion[j]];
if (!IS_HIDDEN(layout2)) {
layout2->unionParent = 1;
}
}
}
layout->eLink = eLink2;
layout->master = eLink1;
/* Width before squeezing/expanding */
if (IS_UNION(eLink1)) {
layout->useWidth = 0;
} else {
#ifdef CACHE_ELEM_SIZE
layout->useWidth = eLink2->neededWidth;
#else
Element_NeededSize(drawArgs->tree, eLink1, eLink2->elem,
drawArgs->state, &layout->neededWidth, &layout->neededHeight);
layout->useWidth = layout->neededWidth;
#endif
}
for (j = 0; j < 2; j++) {
/* Pad values before expansion */
layout->ePadX[j] = eLink1->ePadX[j];
layout->ePadY[j] = eLink1->ePadY[j];
layout->iPadX[j] = eLink1->iPadX[j];
layout->iPadY[j] = eLink1->iPadY[j];
/* No -union padding yet */
layout->uPadX[j] = 0;
layout->uPadY[j] = 0;
layout->iUnionBbox[j] = layout->iUnionBbox[j+2] = -1;
layout->eUnionBbox[j] = layout->eUnionBbox[j+2] = -1;
}
TreeElement_GetContentMargins(tree, layout->eLink->elem, drawArgs->state, layout->eMargins, layout->uMargins, &layout->arrowHeight);
/* Count all non-union, non-detach squeezeable elements */
if (DETACH_OR_UNION(eLink1))
continue;
if (eLink1->flags & ELF_SQUEEZE_X)
numSqueezeX++;
if (IS_CENTER_X(eLink1)) {
if (iCenterMin == -1)
iCenterMin = i;
iCenterMax = i;
}
}
/*
e1 {
e2 <----------
e3 {
e7
e4 {
e2 <----------
e5
}
}
}
*/
/* Calculate the padding around elements surrounded by -union elements */
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
int padx[2], pady[2];
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
/* Start at the top level of each -union heirarchy. */
if (!IS_UNION(eLink1) || layout->unionParent)
continue;
ePadX = layout->ePadX;
ePadY = layout->ePadY;
iPadX = layout->iPadX;
iPadY = layout->iPadY;
for (j = 0; j < 2; j++) {
padx[j] = ePadX[j] + iPadX[j] + layout->uMargins[j*2];
pady[j] = ePadY[j] + iPadY[j] + layout->uMargins[j*2+1];
}
for (j = 0; j < eLink1->onionCount; j++) {
struct Layout *layout2 = &layouts[eLink1->onion[j]];
if (IS_HIDDEN(layout2))
continue;
Layout_AddUnionPadding(tree, masterStyle, layouts, i,
eLink1->onion[j], padx, pady
);
}
}
/* Left-to-right layout. Make the width of some elements less than they
* need */
if (!masterStyle->vertical &&
(drawArgs->width < style->neededWidth + drawArgs->indent) &&
(numSqueezeX > 0)) {
int numSqueeze = numSqueezeX;
int spaceRemaining = (style->neededWidth + drawArgs->indent) - drawArgs->width;
while ((spaceRemaining > 0) && (numSqueeze > 0)) {
int each = (spaceRemaining >= numSqueeze) ? (spaceRemaining / numSqueeze) : 1;
numSqueeze = 0;
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
int min = 0;
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (DETACH_OR_UNION(eLink1))
continue;
if (!(eLink1->flags & ELF_SQUEEZE_X))
continue;
if (eLink1->minWidth >= 0)
min = eLink1->minWidth;
if (layout->useWidth > min) {
int sub = MIN(each, layout->useWidth - min);
layout->useWidth -= sub;
spaceRemaining -= sub;
if (!spaceRemaining) break;
if (layout->useWidth > min)
numSqueeze++;
}
}
}
}
/* Reduce the width of all non-union elements, except for the
* cases handled above. */
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
int width, subtract;
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (IS_UNION(eLink1))
continue;
if (!(eLink1->flags & ELF_SQUEEZE_X))
continue;
if (!IS_DETACH(eLink1) && !masterStyle->vertical)
continue;
ePadX = layout->ePadX;
iPadX = layout->iPadX;
uPadX = layout->uPadX;
width =
MAX(ePadX[PAD_TOP_LEFT], uPadX[PAD_TOP_LEFT]) +
iPadX[PAD_TOP_LEFT] + layout->useWidth + iPadX[PAD_BOTTOM_RIGHT] +
MAX(ePadX[PAD_BOTTOM_RIGHT], uPadX[PAD_BOTTOM_RIGHT]);
subtract = width - drawArgs->width;
if (!IS_DETACH(eLink1) || (eLink1->flags & ELF_INDENT))
subtract += drawArgs->indent;
if (subtract > 0) {
if ((eLink1->minWidth >= 0) &&
(eLink1->minWidth <= layout->useWidth) &&
(layout->useWidth - subtract < eLink1->minWidth))
layout->useWidth = eLink1->minWidth;
else
layout->useWidth -= subtract;
}
}
/* Calculate the horizontal position and size of all non-union, non-detach
* elements. */
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
int right;
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
eLink2 = &eLinks2[i];
if (DETACH_OR_UNION(eLink1))
continue;
ePadX = layout->ePadX;
iPadX = layout->iPadX;
uPadX = layout->uPadX;
layout->x = x + abs(ePadX[PAD_TOP_LEFT] - MAX(ePadX[PAD_TOP_LEFT], uPadX[PAD_TOP_LEFT]));
layout->iWidth = iPadX[PAD_TOP_LEFT] + layout->useWidth + iPadX[PAD_BOTTOM_RIGHT];
layout->eWidth = ePadX[PAD_TOP_LEFT] + layout->iWidth + ePadX[PAD_BOTTOM_RIGHT];
right = layout->x + ePadX[PAD_TOP_LEFT] +
layout->iWidth +
MAX(ePadX[PAD_BOTTOM_RIGHT], uPadX[PAD_BOTTOM_RIGHT]);
rightEdge = MAX(rightEdge, right);
if (!masterStyle->vertical)
x = layout->x + layout->eWidth;
/* Count number that want to expand */
if (eLink1->flags & (ELF_EXPAND_WE | ELF_iEXPAND_X))
numExpandWE++;
}
/* Left-to-right layout. Center some elements horizontally. */
if (!masterStyle->vertical && (iCenterMin != -1) /*&& (drawArgs->width > style->neededWidth + drawArgs->indent)*/) {
int widthCenter, spaceRemaining;
int x1, x2, dx;
/* Calculate the new x-coordinate of the left-most centered element
* such that all the centered elements are centered in the total
* width available to the style (minus indent). The calculation
* ignores all padding on the left and right edges of the
* centered elements when considered as a group. */
x1 = layouts[iCenterMax].x + layouts[iCenterMax].eWidth - layouts[iCenterMax].ePadX[PAD_BOTTOM_RIGHT];
x2 = layouts[iCenterMin].x + layouts[iCenterMin].ePadX[PAD_TOP_LEFT];
widthCenter = x1 - x2;
spaceRemaining = (drawArgs->width - drawArgs->indent - widthCenter);
x = drawArgs->indent + spaceRemaining / 2;
/* Don't push elements so far to the right that they go past the
* right side of the style. */
dx = x - (layouts[iCenterMin].x + layouts[iCenterMin].ePadX[PAD_TOP_LEFT]);
if (dx > drawArgs->width - rightEdge)
x -= dx - (drawArgs->width - rightEdge);
if (x > layouts[iCenterMin].x + layouts[iCenterMin].ePadX[PAD_TOP_LEFT]) {
int dx = x - (layouts[iCenterMin].x + layouts[iCenterMin].ePadX[PAD_TOP_LEFT]);
for (i = iCenterMin; i < eLinkCount; i++) {
if (!IS_HIDDEN(&layouts[i]) && !DETACH_OR_UNION(&eLinks1[i]))
layouts[i].x += dx;
}
}
/* Expand all elements to the left of the centered elements. */
Layout_ExpandElementsH(drawArgs, layouts, 0, iCenterMin - 1,
/* Left-side union padding does separate the elements. */
x - MAX(layouts[iCenterMin].ePadX[PAD_TOP_LEFT], layouts[iCenterMin].uPadX[PAD_TOP_LEFT]));
/* Expand all elements to the right of the centered elements. */
Layout_ExpandElementsH(drawArgs, layouts, iCenterMax + 1, eLinkCount - 1,
drawArgs->width);
/* Disable justification. */
rightEdge = drawArgs->width;
}
/* Left-to-right layout. Expand some elements horizontally if we have
* more space available horizontally than is needed by the Style. */
if (!masterStyle->vertical &&
(iCenterMin == -1) &&
(drawArgs->width > rightEdge) &&
(numExpandWE > 0)) {
rightEdge += Layout_ExpandElementsH(drawArgs, layouts, 0, eLinkCount - 1,
drawArgs->width);
}
/* Top-to-bottom layout. Center individual elements horizontally. */
if (masterStyle->vertical && (iCenterMin != -1)) {
for (i = iCenterMin; i <= iCenterMax; i++) {
struct Layout *layout = &layouts[i];
int right, spaceRemaining, dx;
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (DETACH_OR_UNION(eLink1))
continue;
if (!IS_CENTER_X(eLink1))
continue;
spaceRemaining = (drawArgs->width - drawArgs->indent - layout->iWidth);
x = drawArgs->indent + spaceRemaining / 2;
/* Don't push elements so far to the right that they go past the
* right side of the style. */
dx = x - (layout->x + layout->ePadX[PAD_TOP_LEFT]);
right = layout->x + layout->ePadX[PAD_TOP_LEFT] +
layout->iWidth +
MAX(layout->ePadX[PAD_BOTTOM_RIGHT], layout->uPadX[PAD_BOTTOM_RIGHT]);
if (dx > drawArgs->width - right)
x -= dx - (drawArgs->width - right);
if (x > layout->x + layout->ePadX[PAD_TOP_LEFT]) {
dx = x - (layout->x + layout->ePadX[PAD_TOP_LEFT]);
layout->x += dx;
}
}
}
/* Top-to-bottom layout. Expand some elements horizontally */
if (masterStyle->vertical && (numExpandWE > 0)) {
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
int right;
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (DETACH_OR_UNION(eLink1))
continue;
if (IS_CENTER_X(eLink1))
continue;
right = layout->x + layout->ePadX[PAD_TOP_LEFT] +
layout->iWidth +
MAX(layout->ePadX[PAD_BOTTOM_RIGHT], layout->uPadX[PAD_BOTTOM_RIGHT]);
layout->temp = 0;
right += Style_DoExpandH(layout, drawArgs->width - right);
rightEdge = MAX(rightEdge, right);
}
}
/* Now handle column justification */
/* All the non-union, non-detach elements are moved as a group */
if (drawArgs->width > rightEdge) {
int dx = drawArgs->width - rightEdge;
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (DETACH_OR_UNION(eLink1))
continue;
if (masterStyle->vertical && IS_CENTER_X(eLink1))
continue;
switch (drawArgs->justify) {
case TK_JUSTIFY_LEFT:
break;
case TK_JUSTIFY_RIGHT:
layout->x += dx;
break;
case TK_JUSTIFY_CENTER:
layout->x += dx / 2;
break;
}
}
}
/* Position and expand -detach elements */
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
int right;
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
eLink2 = &eLinks2[i];
if (!IS_DETACH(eLink1) || IS_UNION(eLink1))
continue;
ePadX = layout->ePadX;
iPadX = layout->iPadX;
uPadX = layout->uPadX;
layout->x = abs(ePadX[PAD_TOP_LEFT] - MAX(ePadX[PAD_TOP_LEFT], uPadX[PAD_TOP_LEFT]));
if (eLink1->flags & ELF_INDENT)
layout->x += drawArgs->indent;
layout->iWidth = iPadX[PAD_TOP_LEFT] + layout->useWidth + iPadX[PAD_BOTTOM_RIGHT];
layout->eWidth = ePadX[PAD_TOP_LEFT] + layout->iWidth + ePadX[PAD_BOTTOM_RIGHT];
right = layout->x + layout->ePadX[PAD_TOP_LEFT] +
layout->iWidth +
MAX(layout->ePadX[PAD_BOTTOM_RIGHT], layout->uPadX[PAD_BOTTOM_RIGHT]);
layout->temp = 0;
Style_DoExpandH(layout, drawArgs->width - right);
}
/* Position and expand -union elements. */
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
/* Start at the top level of each -union heirarchy. */
if (!IS_UNION(eLink1) || layout->unionParent)
continue;
Layout_CalcUnionLayoutH(drawArgs, masterStyle, layouts, i);
}
/* Add internal padding to display area for -union elements */
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (!IS_UNION(eLink1))
continue;
iPadX = layout->iPadX;
layout->useWidth += iPadX[PAD_TOP_LEFT] + iPadX[PAD_BOTTOM_RIGHT];
iPadX[PAD_TOP_LEFT] = iPadX[PAD_BOTTOM_RIGHT] = 0;
}
}
/*
*----------------------------------------------------------------------
*
* Style_DoLayoutV --
*
* Calculate the vertical size and position of each element.
* This gets called if the style -orient option is horizontal or
* vertical.
*
* Results:
* layouts[] is updated.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
Style_DoLayoutV(
StyleDrawArgs *drawArgs, /* Various args. */
struct Layout layouts[] /* Array of layout records to be updated,
* one per element. Should be initialized
* by Style_DoLayoutH(). */
)
{
IStyle *style = (IStyle *) drawArgs->style;
MStyle *masterStyle = style->master;
MElementLink *eLinks1, *eLink1;
int y = 0;
int *ePadY, *iPadY, *uPadY;
int numExpandNS = 0;
int numSqueezeY = 0;
int i, eLinkCount = 0;
int bottomEdge = 0;
int iCenterMin = -1, iCenterMax = -1;
eLinks1 = masterStyle->elements;
eLinkCount = masterStyle->numElements;
for (i = 0; i < eLinkCount; i++) {
if (IS_HIDDEN(&layouts[i]))
continue;
eLink1 = &eLinks1[i];
/* Count all non-union, non-detach squeezeable elements */
if (DETACH_OR_UNION(eLink1))
continue;
if (eLink1->flags & ELF_SQUEEZE_Y)
numSqueezeY++;
if (IS_CENTER_Y(eLink1)) {
if (iCenterMin == -1)
iCenterMin = i;
iCenterMax = i;
}
}
/* Top-top-bottom layout. Make the height of some elements less than they
* need */
if (masterStyle->vertical &&
(drawArgs->height < style->neededHeight) &&
(numSqueezeY > 0)) {
int numSqueeze = numSqueezeY;
int spaceRemaining = style->neededHeight - drawArgs->height;
while ((spaceRemaining > 0) && (numSqueeze > 0)) {
int each = (spaceRemaining >= numSqueeze) ? (spaceRemaining / numSqueeze) : 1;
numSqueeze = 0;
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
int min = 0;
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (DETACH_OR_UNION(eLink1))
continue;
if (!(eLink1->flags & ELF_SQUEEZE_Y))
continue;
if (eLink1->minHeight >= 0)
min = eLink1->minHeight;
if (layout->useHeight > min) {
int sub = MIN(each, layout->useHeight - min);
layout->useHeight -= sub;
spaceRemaining -= sub;
if (!spaceRemaining) break;
if (layout->useHeight > min)
numSqueeze++;
}
}
}
}
/* Reduce the height of all non-union elements, except for the
* cases handled above. */
if (drawArgs->height < style->neededHeight) {
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
int height, subtract;
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (IS_UNION(eLink1))
continue;
if (!(eLink1->flags & ELF_SQUEEZE_Y))
continue;
if (!IS_DETACH(eLink1) && masterStyle->vertical)
continue;
ePadY = layout->ePadY;
iPadY = layout->iPadY;
uPadY = layout->uPadY;
height =
MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]) +
iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT] +
MAX(ePadY[PAD_BOTTOM_RIGHT], uPadY[PAD_BOTTOM_RIGHT]);
subtract = height - drawArgs->height;
if (subtract > 0) {
if ((eLink1->minHeight >= 0) &&
(eLink1->minHeight <= layout->useHeight) &&
(layout->useHeight - subtract < eLink1->minHeight))
layout->useHeight = eLink1->minHeight;
else
layout->useHeight -= subtract;
}
}
}
/* Layout elements top-to-bottom */
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
int bottom;
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (DETACH_OR_UNION(eLink1))
continue;
ePadY = layout->ePadY;
iPadY = layout->iPadY;
uPadY = layout->uPadY;
layout->y = y + abs(ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]));
layout->iHeight = iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT];
layout->eHeight = ePadY[PAD_TOP_LEFT] + layout->iHeight + ePadY[PAD_BOTTOM_RIGHT];
if (masterStyle->vertical)
y = layout->y + layout->eHeight;
if (masterStyle->vertical) {
bottom = layout->y + layout->ePadY[PAD_TOP_LEFT] +
layout->iHeight +
MAX(ePadY[PAD_BOTTOM_RIGHT], uPadY[PAD_BOTTOM_RIGHT]);
bottomEdge = MAX(bottomEdge, bottom);
}
/* Count number that want to expand */
if (eLink1->flags & (ELF_EXPAND_NS | ELF_iEXPAND_Y))
numExpandNS++;
}
/* Top-to-bottom layout. Center some elements vertically. */
if (masterStyle->vertical && (iCenterMin != -1) /*&& (drawArgs->height > style->neededHeight)*/) {
int heightCenter, spaceRemaining;
int y1, y2, dy;
/* Calculate the new y-coordinate of the top-most centered element
* such that all the centered elements are centered in the total
* height available to the style. The calculation
* ignores all padding on the top and bottom edges of the
* centered elements when considered as a group. */
y1 = layouts[iCenterMax].y + layouts[iCenterMax].eHeight - layouts[iCenterMax].ePadY[PAD_BOTTOM_RIGHT];
y2 = layouts[iCenterMin].y + layouts[iCenterMin].ePadY[PAD_TOP_LEFT];
heightCenter = y1 - y2;
spaceRemaining = (drawArgs->height - heightCenter);
y = spaceRemaining / 2;
/* Don't push elements so far to the bottom that they go past the
* bottom side of the style. */
dy = y - (layouts[iCenterMin].y + layouts[iCenterMin].ePadY[PAD_TOP_LEFT]);
if (dy > drawArgs->height - bottomEdge)
y -= dy - (drawArgs->height - bottomEdge);
if (y > layouts[iCenterMin].y + layouts[iCenterMin].ePadY[PAD_TOP_LEFT]) {
int dy = y - (layouts[iCenterMin].y + layouts[iCenterMin].ePadY[PAD_TOP_LEFT]);
for (i = iCenterMin; i < eLinkCount; i++) {
if (!IS_HIDDEN(&layouts[i]) && !DETACH_OR_UNION(&eLinks1[i]))
layouts[i].y += dy;
}
}
/* Expand all elements above the centered elements. */
Layout_ExpandElementsV(drawArgs, layouts, 0, iCenterMin - 1,
/* Top-side union padding does separate the elements. */
y - MAX(layouts[iCenterMin].ePadY[PAD_TOP_LEFT], layouts[iCenterMin].uPadY[PAD_TOP_LEFT]));
/* Expand all elements below the centered elements. */
Layout_ExpandElementsV(drawArgs, layouts, iCenterMax + 1, eLinkCount - 1,
drawArgs->height);
}
/* Top-to-bottom layout. Expand some elements vertically if we have
* more space available vertically than is needed by the Style. */
if (masterStyle->vertical &&
(iCenterMin == -1) &&
(drawArgs->height > bottomEdge) &&
(numExpandNS > 0)) {
bottomEdge += Layout_ExpandElementsV(drawArgs, layouts, 0, eLinkCount - 1,
drawArgs->height);
}
/* Left-to-right layout. Center individual elements vertically. */
if (!masterStyle->vertical && (iCenterMin != -1)) {
for (i = iCenterMin; i <= iCenterMax; i++) {
struct Layout *layout = &layouts[i];
int bottom, spaceRemaining, dy;
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (DETACH_OR_UNION(eLink1))
continue;
if (!IS_CENTER_Y(eLink1))
continue;
spaceRemaining = (drawArgs->height - layout->iHeight);
y = spaceRemaining / 2;
/* Don't push elements so far to the bottom that they go past the
* right side of the style. */
dy = y - (layout->y + layout->ePadY[PAD_TOP_LEFT]);
bottom = layout->y + layout->ePadY[PAD_TOP_LEFT] +
layout->iHeight +
MAX(layout->ePadY[PAD_BOTTOM_RIGHT], layout->uPadY[PAD_BOTTOM_RIGHT]);
if (dy > drawArgs->height - bottom)
y -= dy - (drawArgs->height - bottom);
if (y > layout->y + layout->ePadY[PAD_TOP_LEFT]) {
dy = y - (layout->y + layout->ePadY[PAD_TOP_LEFT]);
layout->y += dy;
}
}
}
/* Left-to-right layout. Expand some elements vertically */
if (!masterStyle->vertical && (numExpandNS > 0)) {
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
int bottom;
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (DETACH_OR_UNION(eLink1))
continue;
if (IS_CENTER_Y(eLink1))
continue;
bottom = layout->y + layout->ePadY[PAD_TOP_LEFT] +
layout->iHeight +
MAX(layout->ePadY[PAD_BOTTOM_RIGHT], layout->uPadY[PAD_BOTTOM_RIGHT]);
layout->temp = 0;
Style_DoExpandV(layout, drawArgs->height - bottom);
}
}
/* Position and expand -detach elements */
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
int bottom;
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (!IS_DETACH(eLink1) || IS_UNION(eLink1))
continue;
ePadY = layout->ePadY;
iPadY = layout->iPadY;
uPadY = layout->uPadY;
layout->y = abs(ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]));
layout->iHeight = iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT];
layout->eHeight = ePadY[PAD_TOP_LEFT] + layout->iHeight + ePadY[PAD_BOTTOM_RIGHT];
bottom = layout->y + layout->ePadY[PAD_TOP_LEFT] +
layout->iHeight +
MAX(layout->ePadY[PAD_BOTTOM_RIGHT], layout->uPadY[PAD_BOTTOM_RIGHT]);
layout->temp = 0;
Style_DoExpandV(layout, drawArgs->height - bottom);
}
/* Now calculate layout of -union elements. */
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
/* Start at the top level of each -union heirarchy. */
if (!IS_UNION(eLink1) || layout->unionParent)
continue;
Layout_CalcUnionLayoutV(drawArgs, masterStyle, layouts, i);
}
/* Add internal padding to display area for -union elements */
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (!IS_UNION(eLink1))
continue;
iPadY = layout->iPadY;
layout->useHeight += iPadY[PAD_TOP_LEFT] + iPadY[PAD_BOTTOM_RIGHT];
iPadY[PAD_TOP_LEFT] = iPadY[PAD_BOTTOM_RIGHT] = 0;
}
}
/*
*----------------------------------------------------------------------
*
* Layout_Size --
*
* Calculate the height and width of a style after all the
* elements have been arranged.
*
* Results:
* The height and width of the style.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
Layout_Size(
int vertical, /* TRUE if elements are arranged from top
* to bottom. */
int numLayouts, /* Number of layout records. */
struct Layout layouts[], /* Initialized layout records. */
int *widthPtr, /* Returned width. */
int *heightPtr /* Returned height. */
)
{
int i, W, N, E, S;
int width = 0, height = 0;
W = 1000000, N = 1000000, E = -1000000, S = -1000000;
for (i = 0; i < numLayouts; i++) {
struct Layout *layout = &layouts[i];
int w, n, e, s;
int *ePadX, *uPadX, *ePadY, *uPadY;
if (IS_HIDDEN(layout))
continue;
ePadX = layout->ePadX, uPadX = layout->uPadX;
ePadY = layout->ePadY, uPadY = layout->uPadY;
w = layout->x + ePadX[PAD_TOP_LEFT] - MAX(ePadX[PAD_TOP_LEFT], uPadX[PAD_TOP_LEFT]);
n = layout->y + ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]);
e = layout->x + layout->eWidth - ePadX[PAD_BOTTOM_RIGHT] + MAX(ePadX[PAD_BOTTOM_RIGHT], uPadX[PAD_BOTTOM_RIGHT]);
s = layout->y + layout->eHeight - ePadY[PAD_BOTTOM_RIGHT] + MAX(ePadY[PAD_BOTTOM_RIGHT], uPadY[PAD_BOTTOM_RIGHT]);
if (vertical) {
N = MIN(N, n);
S = MAX(S, s);
width = MAX(width, e - w);
} else {
W = MIN(W, w);
E = MAX(E, e);
height = MAX(height, s - n);
}
}
if (vertical)
height = MAX(height, S - N);
else
width = MAX(width, E - W);
(*widthPtr) = width;
(*heightPtr) = height;
}
/*
*----------------------------------------------------------------------
*
* Style_DoLayoutNeededV --
*
* Calculate the vertical size and position of each element.
* This is similar to Style_DoLayoutV but without expansion or
* squeezing. Also, the size and position of -union elements
* is not calculated.
*
* Results:
* layouts[] is updated.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
Style_DoLayoutNeededV(
StyleDrawArgs *drawArgs, /* Various args. */
struct Layout layouts[] /* Array of layout records to be updated,
* one per element. Should be initialized
* by Style_DoLayoutH(). */
)
{
IStyle *style = (IStyle *) drawArgs->style;
MStyle *masterStyle = style->master;
MElementLink *eLinks1, *eLink1;
int *ePadY, *iPadY, *uPadY;
int i;
int y = 0;
eLinks1 = masterStyle->elements;
/* Layout elements left-to-right, or top-to-bottom */
for (i = 0; i < masterStyle->numElements; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
/* The size of a -union element is determined by the elements
* it surrounds */
if (IS_UNION(eLink1)) {
/* I don't need good values because I'm only calculating the
* needed height */
layout->y = layout->iHeight = layout->eHeight = 0;
continue;
}
/* -detach elements are positioned by themselves */
if (IS_DETACH(eLink1))
continue;
ePadY = layout->ePadY;
iPadY = layout->iPadY;
uPadY = layout->uPadY;
layout->y = y + abs(ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]));
layout->iHeight = iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT];
layout->eHeight = ePadY[PAD_TOP_LEFT] + layout->iHeight + ePadY[PAD_BOTTOM_RIGHT];
if (masterStyle->vertical)
y = layout->y + layout->eHeight;
}
/* -detach elements */
for (i = 0; i < masterStyle->numElements; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (!IS_DETACH(eLink1) || IS_UNION(eLink1))
continue;
ePadY = layout->ePadY;
iPadY = layout->iPadY;
uPadY = layout->uPadY;
layout->y = abs(ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]));
layout->iHeight = iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT];
layout->eHeight = ePadY[PAD_TOP_LEFT] + layout->iHeight + ePadY[PAD_BOTTOM_RIGHT];
}
}
/*
*----------------------------------------------------------------------
*
* Style_DoLayout --
*
* Calculate the size and position of each element.
*
* Results:
* layouts[] is updated.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
/* Arrange all the Elements considering drawArgs.width and maybe drawArgs.height */
static void
Style_DoLayout(
StyleDrawArgs *drawArgs, /* Various args. */
struct Layout layouts[], /* Uninitialized records to be filled in. */
int neededV, /* TRUE if drawArgs.height should be ignored. */
char *file, /* debug */
int line /* debug */
)
{
TreeCtrl *tree = drawArgs->tree;
IStyle *style = (IStyle *) drawArgs->style;
MStyle *masterStyle = style->master;
int state = drawArgs->state;
int i;
if (style->neededWidth == -1)
panic("Style_DoLayout(file %s line %d): style.neededWidth == -1",
file, line);
#ifdef CACHE_STYLE_STYLE
if (style->minWidth + drawArgs->indent > drawArgs->width)
panic("Style_DoLayout(file %s line %d): style.minWidth + drawArgs->indent %d > drawArgs.width %d",
file, line, style->minWidth + drawArgs->indent, drawArgs->width);
#endif
Style_DoLayoutH(drawArgs, layouts);
for (i = 0; i < masterStyle->numElements; i++) {
struct Layout *layout = &layouts[i];
MElementLink *eLink1 = layout->master;
IElementLink *eLink2 = layout->eLink;
TreeElementArgs args;
if (IS_HIDDEN(layout))
continue;
/* The size of a -union element is determined by the elements
* it surrounds */
if (IS_UNION(eLink1)) {
layout->useHeight = 0;
continue;
}
#ifdef CACHE_ELEM_SIZE
layout->useHeight = eLink2->neededHeight;
#else
layout->useHeight = layout->neededHeight;
#endif
/* If a Text Element is given less width than it needs (due to
* -squeeze x layout), then it may wrap lines. This means
* the height can vary depending on the width. */
if (eLink2->elem->typePtr->heightProc == NULL)
continue;
if (eLink1->fixedHeight >= 0)
continue;
#ifdef CACHE_ELEM_SIZE
/* Not squeezed */
if (layout->useWidth >= eLink2->neededWidth)
continue;
/* Already calculated the height at this width */
if (layout->useWidth == eLink2->layoutWidth) {
layout->useHeight = eLink2->layoutHeight;
continue;
}
#else
/* Not squeezed */
if (layout->useWidth >= layout->neededWidth)
continue;
#endif
args.tree = tree;
args.state = state;
args.elem = eLink2->elem;
args.height.fixedWidth = layout->useWidth;
(*args.elem->typePtr->heightProc)(&args);
if (eLink1->fixedHeight >= 0)
layout->useHeight = eLink1->fixedHeight;
else if ((eLink1->minHeight >= 0) &&
(args.height.height < eLink1->minHeight))
layout->useHeight = eLink1->minHeight;
else if ((eLink1->maxHeight >= 0) &&
(args.height.height > eLink1->maxHeight))
layout->useHeight = eLink1->maxHeight;
else
layout->useHeight = args.height.height;
#ifdef CACHE_ELEM_SIZE
eLink2->layoutWidth = layout->useWidth;
eLink2->layoutHeight = layout->useHeight;
#endif
}
if (neededV) {
Style_DoLayoutNeededV(drawArgs, layouts);
} else {
Style_DoLayoutV(drawArgs, layouts);
}
}
/*
*----------------------------------------------------------------------
*
* Style_NeededSize --
*
* Calculate the width and height of a style based only on
* the requested size of each element.
*
* Results:
* The width and height. The minimum width and height is equal to
* the requested width and height minus any squeezing.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
Style_NeededSize(
TreeCtrl *tree, /* Widget info. */
IStyle *style, /* Style to calculate size of. */
int state, /* STATE_xxx flags. */
int *widthPtr, /* Returned width. */
int *heightPtr, /* Returned height. */
int *minWidthPtr, /* Returned minimum width. */
int *minHeightPtr /* Returned minimum height. */
)
{
MStyle *masterStyle = style->master;
MElementLink *eLinks1, *eLink1;
IElementLink *eLinks2, *eLink2;
struct Layout staticLayouts[STATIC_SIZE], *layouts = staticLayouts;
int *ePadX, *iPadX, *uPadX, *ePadY, *iPadY, *uPadY;
int i, j, eLinkCount = masterStyle->numElements;
int x = 0, y = 0;
int squeezeX = 0, squeezeY = 0;
int headerHeight = 0;
STATIC_ALLOC(layouts, struct Layout, eLinkCount);
eLinks1 = masterStyle->elements;
eLinks2 = style->elements;
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
layout->unionParent = 0;
layout->temp = 0; /* see Layout_CalcVisibility */
}
for (i = 0; i < eLinkCount; i++) {
Layout_CalcVisibility(tree, state, masterStyle, layouts, i);
}
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
eLink1 = &eLinks1[i];
eLink2 = &eLinks2[i];
if (IS_HIDDEN(layout))
continue;
if (IS_UNION(eLink1)) {
for (j = 0; j < eLink1->onionCount; j++) {
struct Layout *layout2 = &layouts[eLink1->onion[j]];
if (!IS_HIDDEN(layout2))
layout2->unionParent = 1;
}
}
layout->master = eLink1;
layout->eLink = eLink2;
if (!IS_UNION(eLink1)) {
#ifdef CACHE_ELEM_SIZE
if ((eLink2->neededWidth == -1) || (eLink2->neededHeight == -1)) {
Element_NeededSize(tree, eLink1, eLink2->elem, state,
&eLink2->neededWidth, &eLink2->neededHeight);
eLink2->layoutWidth = -1;
}
layout->useWidth = eLink2->neededWidth;
layout->useHeight = eLink2->neededHeight;
#else
Element_NeededSize(tree, eLink1, eLink2->elem, state,
&layout->neededWidth, &layout->neededHeight);
layout->useWidth = layout->neededWidth;
#endif
}
for (j = 0; j < 2; j++) {
layout->ePadX[j] = eLink1->ePadX[j];
layout->ePadY[j] = eLink1->ePadY[j];
layout->iPadX[j] = eLink1->iPadX[j];
layout->iPadY[j] = eLink1->iPadY[j];
/* No -union padding yet */
layout->uPadX[j] = 0;
layout->uPadY[j] = 0;
}
TreeElement_GetContentMargins(tree, layout->eLink->elem, state,
layout->eMargins, layout->uMargins, &layout->arrowHeight);
if (IS_UNION(eLink1)) {
headerHeight = MAX(headerHeight, layout->eMargins[1] +
layout->arrowHeight + layout->eMargins[3]);
}
}
/* Calculate the padding around elements surrounded by -union elements */
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
int padx[2], pady[2];
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
/* Start at the top level of each -union heirarchy. */
if (!IS_UNION(eLink1) || layout->unionParent)
continue;
ePadX = layout->ePadX;
ePadY = layout->ePadY;
iPadX = layout->iPadX;
iPadY = layout->iPadY;
for (j = 0; j < 2; j++) {
padx[j] = ePadX[j] + iPadX[j] + layout->uMargins[j*2];
pady[j] = ePadY[j] + iPadY[j] + layout->uMargins[j*2+1];
}
for (j = 0; j < eLink1->onionCount; j++) {
struct Layout *layout2 = &layouts[eLink1->onion[j]];
if (IS_HIDDEN(layout2))
continue;
Layout_AddUnionPadding(tree, masterStyle, layouts, i,
eLink1->onion[j], padx, pady
);
}
}
/* Layout elements left-to-right, or top-to-bottom */
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
eLink2 = &eLinks2[i];
/* The size of a -union element is determined by the elements
* it surrounds */
if (IS_UNION(eLink1)) {
layout->x = layout->y = layout->eWidth = layout->eHeight = 0;
for (j = 0; j < 2; j++) {
layout->ePadX[j] = 0;
layout->ePadY[j] = 0;
layout->iPadX[j] = 0;
layout->iPadY[j] = 0;
layout->uPadX[j] = 0;
layout->uPadY[j] = 0;
}
continue;
}
if (eLink1->flags & ELF_SQUEEZE_X) squeezeX++;
if (eLink1->flags & ELF_SQUEEZE_Y) squeezeY++;
/* -detach elements are positioned by themselves */
if (IS_DETACH(eLink1))
continue;
ePadX = layout->ePadX;
ePadY = layout->ePadY;
iPadX = layout->iPadX;
iPadY = layout->iPadY;
uPadX = layout->uPadX;
uPadY = layout->uPadY;
layout->x = x + abs(ePadX[PAD_TOP_LEFT] - MAX(ePadX[PAD_TOP_LEFT], uPadX[PAD_TOP_LEFT]));
layout->y = y + abs(ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]));
layout->iWidth = iPadX[PAD_TOP_LEFT] + layout->useWidth + iPadX[PAD_BOTTOM_RIGHT];
layout->iHeight = iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT];
layout->eWidth = ePadX[PAD_TOP_LEFT] + layout->iWidth + ePadX[PAD_BOTTOM_RIGHT];
layout->eHeight = ePadY[PAD_TOP_LEFT] + layout->iHeight + ePadY[PAD_BOTTOM_RIGHT];
if (masterStyle->vertical)
y = layout->y + layout->eHeight;
else
x = layout->x + layout->eWidth;
}
/* -detach elements */
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
eLink2 = &eLinks2[i];
if (!IS_DETACH(eLink1) || IS_UNION(eLink1))
continue;
ePadX = layout->ePadX;
ePadY = layout->ePadY;
iPadX = layout->iPadX;
iPadY = layout->iPadY;
uPadX = layout->uPadX;
uPadY = layout->uPadY;
layout->x = abs(ePadX[PAD_TOP_LEFT] - MAX(ePadX[PAD_TOP_LEFT], uPadX[PAD_TOP_LEFT]));
layout->y = abs(ePadY[PAD_TOP_LEFT] - MAX(ePadY[PAD_TOP_LEFT], uPadY[PAD_TOP_LEFT]));
layout->iWidth = iPadX[PAD_TOP_LEFT] + layout->useWidth + iPadX[PAD_BOTTOM_RIGHT];
layout->iHeight = iPadY[PAD_TOP_LEFT] + layout->useHeight + iPadY[PAD_BOTTOM_RIGHT];
layout->eWidth = ePadX[PAD_TOP_LEFT] + layout->iWidth + ePadX[PAD_BOTTOM_RIGHT];
layout->eHeight = ePadY[PAD_TOP_LEFT] + layout->iHeight + ePadY[PAD_BOTTOM_RIGHT];
}
Layout_Size(masterStyle->vertical, eLinkCount, layouts,
widthPtr, heightPtr);
/*
the height of a header was:
content margin top +
MAX(text pady top + height + pady bottom,
image pady top + height + pady bottom,
arrow pady top + height + pady bottom)
+ content margin bottom
*/
*heightPtr = MAX(*heightPtr, headerHeight);
if (squeezeX || squeezeY) {
for (i = 0; i < eLinkCount; i++) {
struct Layout *layout = &layouts[i];
int subtract;
if (IS_HIDDEN(layout))
continue;
eLink1 = &eLinks1[i];
if (IS_UNION(eLink1))
continue;
if (eLink1->flags & ELF_SQUEEZE_X) {
if ((eLink1->minWidth >= 0) &&
(eLink1->minWidth <= layout->useWidth)) {
subtract = layout->useWidth - eLink1->minWidth;
} else {
subtract = layout->useWidth;
}
layout->eWidth -= subtract;
if (!masterStyle->vertical && !IS_DETACH(eLink1)) {
for (j = i + 1; j < eLinkCount; j++)
if (!IS_HIDDEN(&layouts[j]) && !DETACH_OR_UNION(&eLinks1[j]))
layouts[j].x -= subtract;
}
}
if (eLink1->flags & ELF_SQUEEZE_Y) {
if ((eLink1->minHeight >= 0) &&
(eLink1->minHeight <= layout->useHeight)) {
subtract = layout->useHeight - eLink1->minHeight;
} else {
subtract = layout->useHeight;
}
layout->eHeight -= subtract;
if (masterStyle->vertical && !IS_DETACH(eLink1)) {
for (j = i + 1; j < eLinkCount; j++)
if (!IS_HIDDEN(&layouts[j]) && !DETACH_OR_UNION(&eLinks1[j]))
layouts[j].y -= subtract;
}
}
}
Layout_Size(masterStyle->vertical, eLinkCount, layouts,
minWidthPtr, minHeightPtr);
} else {
*minWidthPtr = *widthPtr;
*minHeightPtr = *heightPtr;
}
STATIC_FREE(layouts, struct Layout, eLinkCount);
}
/*
*----------------------------------------------------------------------
*
* Style_CheckNeededSize --
*
* If the style's requested size is out-of-date then recalculate
* Style.neededWidth, Style.neededHeight, Style.minWidth, and
* Style.minHeight.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
Style_CheckNeededSize(
TreeCtrl *tree, /* Widget info. */
IStyle *style, /* Style info. */
int state /* STATE_xxx flags. */
)
{
if (style->neededWidth == -1) {
int minWidth, minHeight;
Style_NeededSize(tree, style, state,
&style->neededWidth, &style->neededHeight, &minWidth, &minHeight);
#ifdef CACHE_STYLE_SIZE
style->minWidth = minWidth;
style->minHeight = minHeight;
style->layoutWidth = -1;
#endif /* CACHE_STYLE_SIZE */
#ifdef TREECTRL_DEBUG
style->neededState = state;
#endif
}
#ifdef TREECTRL_DEBUG
if (style->neededState != state)
panic("Style_CheckNeededSize: neededState %d != state %d\n",
style->neededState, state);
#endif
}
#ifndef CACHE_STYLE_SIZE
static void
Style_MinSize(
TreeCtrl *tree, /* Widget info. */
IStyle *style, /* Style info. */
int state, /* STATE_xxx flags. */
int *minWidthPtr,
int *minHeightPtr
)
{
int i, hasSqueeze = FALSE;
for (i = 0; i < style->master->numElements; i++) {
MElementLink *eLink1 = &style->master->elements[i];
if (!IS_UNION(eLink1) &&
(eLink1->flags & (ELF_SQUEEZE_X | ELF_SQUEEZE_Y))) {
hasSqueeze = TRUE;
break;
}
}
if (hasSqueeze) {
int width, height;
Style_NeededSize(tree, style, state, &width, &height,
minWidthPtr, minHeightPtr);
} else {
*minWidthPtr = style->neededWidth;
*minHeightPtr = style->neededHeight;
}
}
#endif /* !CACHE_STYLE_SIZE */
/*
*----------------------------------------------------------------------
*
* TreeStyle_NeededWidth --
*
* Return the requested width of a style.
*
* Results:
* The requested width. If the requested size is out-of-date
* then it is recalculated.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_NeededWidth(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_, /* Style token. */
int state /* STATE_xxx flags. */
)
{
IStyle *style = (IStyle *) style_;
Style_CheckNeededSize(tree, style, state);
return style->neededWidth;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_NeededHeight --
*
* Return the requested height of a style.
*
* Results:
* The requested height. If the requested size is out-of-date
* then it is recalculated.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_NeededHeight(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_, /* Style token. */
int state /* STATE_xxx flags. */
)
{
IStyle *style = (IStyle *) style_;
Style_CheckNeededSize(tree, style, state);
return style->neededHeight;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_UseHeight --
*
* Return the height of a style for a given state and width.
*
* Results:
* The height of the style.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_UseHeight(
StyleDrawArgs *drawArgs /* Various args. */
)
{
TreeCtrl *tree = drawArgs->tree;
IStyle *style = (IStyle *) drawArgs->style;
MStyle *masterStyle = style->master;
int state = drawArgs->state;
struct Layout staticLayouts[STATIC_SIZE], *layouts = staticLayouts;
int width, height, minWidth;
#ifndef CACHE_STYLE_SIZE
int minHeight;
#endif
Style_CheckNeededSize(tree, style, state);
#ifdef CACHE_STYLE_SIZE
minWidth = style->minWidth;
#else
if (drawArgs->width < style->neededWidth + drawArgs->indent)
Style_MinSize(tree, style, state, &minWidth, &minHeight);
else
minWidth = style->neededWidth;
#endif
/*
* If we have:
* a) infinite space available, or
* b) more width than the style needs, or
* c) less width than the style needs, but it has no -squeeze x elements
* then return the needed height of the style. This is safe since no
* text elements will be growing vertically when lines wrap.
*/
if ((drawArgs->width == -1) ||
(drawArgs->width >= style->neededWidth + drawArgs->indent) ||
(style->neededWidth == minWidth)) {
return style->neededHeight;
}
/* We never lay out the style at less than the minimum width */
if (drawArgs->width < minWidth + drawArgs->indent)
drawArgs->width = minWidth + drawArgs->indent;
#ifdef CACHE_STYLE_SIZE
/* We have less space than the style needs, and have already calculated
* the height of the style at this width. (The height may change because
* of text elements wrapping lines). */
if (drawArgs->width == style->layoutWidth)
return style->layoutHeight;
#endif
STATIC_ALLOC(layouts, struct Layout, masterStyle->numElements);
Style_DoLayout(drawArgs, layouts, TRUE, __FILE__, __LINE__);
Layout_Size(style->master->vertical, masterStyle->numElements, layouts,
&width, &height);
STATIC_FREE(layouts, struct Layout, masterStyle->numElements);
#ifdef CACHE_STYLE_SIZE
style->layoutWidth = drawArgs->width;
style->layoutHeight = height;
#endif
return height;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_Draw --
*
* Draw all the elements in a style.
*
* Results:
* None.
*
* Side effects:
* Stuff is drawn.
*
*----------------------------------------------------------------------
*/
void
TreeStyle_Draw(
StyleDrawArgs *drawArgs /* Various args. */
)
{
IStyle *style = (IStyle *) drawArgs->style;
MStyle *masterStyle = style->master;
TreeCtrl *tree = drawArgs->tree;
TreeRectangle bounds;
TreeElementArgs args;
int i, j, x, y, minWidth, minHeight;
struct Layout staticLayouts[STATIC_SIZE], *layouts = staticLayouts;
#undef DEBUG_DRAW
#ifdef DEBUG_DRAW
int debugDraw = TRUE;
#endif
Style_CheckNeededSize(tree, style, drawArgs->state);
#ifdef CACHE_STYLE_SIZE
minWidth = style->minWidth;
minHeight = style->minHeight;
#else
if ((drawArgs->width < style->neededWidth + drawArgs->indent) ||
(drawArgs->height < style->neededHeight)) {
Style_MinSize(tree, style, drawArgs->state, &minWidth, &minHeight);
} else {
minWidth = style->neededWidth;
minHeight = style->neededHeight;
}
#endif
/* Get the bounds allowed for drawing (in window coordinates), inside
* the item-column(s) and inside the header/borders. */
x = drawArgs->x + tree->drawableXOrigin - tree->xOrigin;
y = drawArgs->y + tree->drawableYOrigin - tree->yOrigin;
TreeRect_SetXYWH(bounds, x, y, drawArgs->width, drawArgs->height);
TreeRect_Intersect(&args.display.bounds, &bounds, &drawArgs->bounds);
TreeRect_SetXYWH(args.display.spanBbox, drawArgs->x, drawArgs->y,
drawArgs->width, drawArgs->height);
/* We never lay out the style at less than the minimum size */
if (drawArgs->width < minWidth + drawArgs->indent)
drawArgs->width = minWidth + drawArgs->indent;
if (drawArgs->height < minHeight)
drawArgs->height = minHeight;
STATIC_ALLOC(layouts, struct Layout, masterStyle->numElements);
Style_DoLayout(drawArgs, layouts, FALSE, __FILE__, __LINE__);
args.tree = tree;
args.state = drawArgs->state;
args.display.td = drawArgs->td;
args.display.drawable = drawArgs->td.drawable;
args.display.column = drawArgs->column; /* needed for gradients */
args.display.item = drawArgs->item; /* needed for gradients */
args.display.spanIndex = drawArgs->spanIndex;
args.display.indent = drawArgs->indent;
for (i = 0; i < masterStyle->numElements; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
/* Don't "draw" window elements. TreeStyle_UpdateWindowPositions()
* does that for us. */
if (ELEMENT_TYPE_MATCHES(layout->eLink->elem->typePtr, &treeElemTypeWindow))
continue;
if (PerStateBoolean_ForState(tree, &layout->master->draw,
drawArgs->state, NULL) == 0)
continue;
#ifdef DEBUG_DRAW
if (debugDraw && layout->master->onion != NULL)
continue;
#endif
if ((layout->useWidth > 0) && (layout->useHeight > 0)) {
args.elem = layout->eLink->elem;
args.display.x = drawArgs->x + layout->x + layout->ePadX[PAD_TOP_LEFT];
args.display.y = drawArgs->y + layout->y + layout->ePadY[PAD_TOP_LEFT];
args.display.x += layout->iPadX[PAD_TOP_LEFT];
args.display.y += layout->iPadY[PAD_TOP_LEFT];
args.display.width = layout->useWidth;
args.display.height = layout->useHeight;
args.display.sticky = layout->master->flags & ELF_STICKY;
/* This is used by the header element to adjust the arrow position
* according to -arrowgravity. */
for (j = 0; j < 4; j++) {
args.display.eUnionBbox[j] = layout->eUnionBbox[j];
args.display.iUnionBbox[j] = layout->iUnionBbox[j];
}
#ifdef DEBUG_DRAW
if (debugDraw) {
XColor *color[3];
GC gc[3];
if (layout->master->onion != NULL) {
color[0] = Tk_GetColor(tree->interp, tree->tkwin, "blue2");
gc[0] = Tk_GCForColor(color[0], Tk_WindowId(tree->tkwin));
color[1] = Tk_GetColor(tree->interp, tree->tkwin, "blue3");
gc[1] = Tk_GCForColor(color[1], Tk_WindowId(tree->tkwin));
} else {
color[0] = Tk_GetColor(tree->interp, tree->tkwin, "gray50");
gc[0] = Tk_GCForColor(color[0], Tk_WindowId(tree->tkwin));
color[1] = Tk_GetColor(tree->interp, tree->tkwin, "gray60");
gc[1] = Tk_GCForColor(color[1], Tk_WindowId(tree->tkwin));
color[2] = Tk_GetColor(tree->interp, tree->tkwin, "gray70");
gc[2] = Tk_GCForColor(color[2], Tk_WindowId(args.tree->tkwin));
}
/* external */
XFillRectangle(tree->display, args.display.drawable,
gc[2],
args.display.x - layout->ePadX[PAD_TOP_LEFT],
args.display.y - layout->ePadY[PAD_TOP_LEFT],
layout->eWidth, layout->eHeight);
/* internal */
XFillRectangle(tree->display, args.display.drawable,
gc[1],
args.display.x, args.display.y,
args.display.width, args.display.height);
/* needed */
if (!layout->master->onion && !(layout->master->flags & ELF_DETACH))
XFillRectangle(tree->display, args.display.drawable,
gc[0],
args.display.x + layout->iPadX[PAD_TOP_LEFT],
args.display.y + layout->iPadY[PAD_TOP_LEFT],
layout->eLink->neededWidth, layout->eLink->neededHeight);
} else
#endif /* DEBUG_DRAW */
(*args.elem->typePtr->displayProc)(&args);
}
}
#ifdef DEBUG_DRAW
if (debugDraw)
for (i = 0; i < masterStyle->numElements; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
if (layout->master->onion == NULL)
continue;
if (layout->useWidth > 0 && layout->useHeight > 0) {
args.elem = layout->eLink->elem;
args.display.x = drawArgs->x + layout->x + layout->ePadX[PAD_TOP_LEFT];
args.display.y = drawArgs->y + layout->y + layout->ePadY[PAD_TOP_LEFT];
args.display.width = layout->iWidth;
args.display.height = layout->iHeight;
{
XColor *color[3];
GC gc[3];
color[0] = Tk_GetColor(tree->interp, tree->tkwin, "blue2");
gc[0] = Tk_GCForColor(color[0], Tk_WindowId(tree->tkwin));
color[1] = Tk_GetColor(tree->interp, tree->tkwin, "blue3");
gc[1] = Tk_GCForColor(color[1], Tk_WindowId(tree->tkwin));
/* external */
XDrawRectangle(tree->display, args.display.drawable,
gc[0],
args.display.x - layout->ePadX[PAD_TOP_LEFT],
args.display.y - layout->ePadY[PAD_TOP_LEFT],
layout->eWidth - 1, layout->eHeight - 1);
/* internal */
XDrawRectangle(tree->display, args.display.drawable,
gc[1],
args.display.x, args.display.y,
args.display.width - 1, args.display.height - 1);
}
}
}
#endif /* DEBUG_DRAW */
STATIC_FREE(layouts, struct Layout, masterStyle->numElements);
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_UpdateWindowPositions --
*
* Call the displayProc on each window element so it can update
* its geometry. This is needed if an item was scrolled and its
* displayProc wasn't otherwise called.
*
* Results:
* None.
*
* Side effects:
* Possible window geometry changes.
*
*----------------------------------------------------------------------
*/
void
TreeStyle_UpdateWindowPositions(
StyleDrawArgs *drawArgs /* Various args. */
)
{
IStyle *style = (IStyle *) drawArgs->style;
MStyle *masterStyle = style->master;
TreeCtrl *tree = drawArgs->tree;
TreeRectangle bounds;
TreeElementArgs args;
int i, x, y, minWidth, minHeight;
struct Layout staticLayouts[STATIC_SIZE], *layouts = staticLayouts;
int numElements = masterStyle->numElements;
if (!masterStyle->hasWindowElem)
return;
Style_CheckNeededSize(tree, style, drawArgs->state);
#ifdef CACHE_STYLE_SIZE
minWidth = style->minWidth;
minHeight = style->minHeight;
#else
if ((drawArgs->width < style->neededWidth + drawArgs->indent) ||
(drawArgs->height < style->neededHeight)) {
Style_MinSize(tree, style, drawArgs->state, &minWidth, &minHeight);
} else {
minWidth = style->neededWidth;
minHeight = style->neededHeight;
}
#endif
/* Get the bounds allowed for drawing (in window coordinates), inside
* the item-column(s) and inside the header/borders. */
x = drawArgs->x + tree->drawableXOrigin - tree->xOrigin;
y = drawArgs->y + tree->drawableYOrigin - tree->yOrigin;
TreeRect_SetXYWH(bounds, x, y, drawArgs->width, drawArgs->height);
TreeRect_Intersect(&args.display.bounds, &bounds, &drawArgs->bounds);
/* We never lay out the style at less than the minimum size */
if (drawArgs->width < minWidth + drawArgs->indent)
drawArgs->width = minWidth + drawArgs->indent;
if (drawArgs->height < minHeight)
drawArgs->height = minHeight;
STATIC_ALLOC(layouts, struct Layout, numElements);
Style_DoLayout(drawArgs, layouts, FALSE, __FILE__, __LINE__);
args.tree = tree;
args.state = drawArgs->state;
args.display.td = drawArgs->td;
args.display.drawable = drawArgs->td.drawable;
for (i = 0; i < numElements; i++) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
if (!ELEMENT_TYPE_MATCHES(layout->eLink->elem->typePtr, &treeElemTypeWindow))
continue;
if (PerStateBoolean_ForState(tree, &layout->master->draw,
drawArgs->state, NULL) == 0)
continue;
if ((layout->useWidth > 0) && (layout->useHeight > 0)) {
int requests;
TreeDisplay_GetReadyForTrouble(tree, &requests);
args.elem = layout->eLink->elem;
args.display.x = drawArgs->x + layout->x + layout->ePadX[PAD_TOP_LEFT];
args.display.y = drawArgs->y + layout->y + layout->ePadY[PAD_TOP_LEFT];
args.display.x += layout->iPadX[PAD_TOP_LEFT];
args.display.y += layout->iPadY[PAD_TOP_LEFT];
args.display.width = layout->useWidth;
args.display.height = layout->useHeight;
args.display.sticky = layout->master->flags & ELF_STICKY;
(*args.elem->typePtr->displayProc)(&args);
/* Updating the position of a window may generate a <Configure>
* or <Map> event on that window. Binding scripts on those
* events could do anything, including deleting items and
* thus the style we are drawing. In other cases (such as when
* using Tile widgets I notice), the Tk_GeomMgr.requestProc
* may get called which calls Tree_ElementChangedItself which
* calls FreeDItemInfo which frees a DItem we are in the middle
* of displaying. So if anything was done that caused a display
* request, then abort abort abort. */
if (TreeDisplay_WasThereTrouble(tree, requests))
break;
}
}
STATIC_FREE(layouts, struct Layout, numElements);
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_OnScreen --
*
* Call the onScreenProc (if non-NULL) on each element so it can
* update its visibility when an item's visibility changes.
*
* Results:
* None.
*
* Side effects:
* Possible window visibility changes.
*
*----------------------------------------------------------------------
*/
void
TreeStyle_OnScreen(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_, /* Style token. */
int onScreen /* Boolean indicating whether the item
* using the style is on screen anymore. */
)
{
IStyle *style = (IStyle *) style_;
TreeElementArgs args;
int i;
args.tree = tree;
args.screen.visible = onScreen;
for (i = 0; i < style->master->numElements; i++) {
IElementLink *eLink = &style->elements[i];
if (eLink->elem->typePtr->onScreenProc == NULL)
continue;
args.elem = eLink->elem;
(*args.elem->typePtr->onScreenProc)(&args);
}
}
/*
*----------------------------------------------------------------------
*
* Element_FreeResources --
*
* Free memory etc associated with an Element.
*
* Results:
* None.
*
* Side effects:
* Memory is deallocated.
*
*----------------------------------------------------------------------
*/
static void
Element_FreeResources(
TreeCtrl *tree, /* Widget info. */
TreeElement elem /* Record to free. */
)
{
TreeElementType *typePtr = elem->typePtr;
TreeElementArgs args;
Tcl_HashEntry *hPtr;
if (elem->master == NULL) {
hPtr = Tcl_FindHashEntry(&tree->elementHash, elem->name);
Tcl_DeleteHashEntry(hPtr);
}
args.tree = tree;
args.elem = elem;
(*typePtr->deleteProc)(&args);
Tk_FreeConfigOptions((char *) elem,
typePtr->optionTable,
tree->tkwin);
DynamicOption_Free(tree, elem->options, typePtr->optionSpecs);
#ifdef ALLOC_HAX
TreeAlloc_Free(tree->allocData, typePtr->name, (char *) elem, typePtr->size);
#else
WFREE(elem, TreeElement_);
#endif
}
/*
*----------------------------------------------------------------------
*
* MElementLink_Init --
*
* Initialize (don't allocate) a MElementLink.
*
* Results:
* eLink is filled with default values.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static MElementLink *
MElementLink_Init(
MElementLink *eLink, /* Existing record to initialize. */
TreeElement elem /* Existing element to point to. */
)
{
memset(eLink, '\0', sizeof(MElementLink));
eLink->elem = elem;
eLink->flags |= ELF_INDENT;
eLink->minWidth = eLink->fixedWidth = eLink->maxWidth = -1;
eLink->minHeight = eLink->fixedHeight = eLink->maxHeight = -1;
eLink->flags |= ELF_STICKY;
return eLink;
}
/*
*----------------------------------------------------------------------
*
* MElementLink_FreeResources --
*
* Free memory etc associated with an MElementLink.
*
* Results:
* None.
*
* Side effects:
* Memory is deallocated.
*
*----------------------------------------------------------------------
*/
static void
MElementLink_FreeResources(
TreeCtrl *tree, /* Widget info. */
MElementLink *eLink /* Record to free. */
)
{
if (eLink->onion != NULL)
WCFREE(eLink->onion, int, eLink->onionCount);
PerStateInfo_Free(tree, &pstBoolean, &eLink->draw);
if (eLink->draw.obj != NULL) {
Tcl_DecrRefCount(eLink->draw.obj);
}
PerStateInfo_Free(tree, &pstBoolean, &eLink->visible);
if (eLink->visible.obj != NULL) {
Tcl_DecrRefCount(eLink->visible.obj);
}
}
/*
*----------------------------------------------------------------------
*
* IElementLink_FreeResources --
*
* Free memory etc associated with an ElementLink.
*
* Results:
* None.
*
* Side effects:
* Memory is deallocated.
*
*----------------------------------------------------------------------
*/
static void
IElementLink_FreeResources(
TreeCtrl *tree, /* Widget info. */
IElementLink *eLink /* Record to free. */
)
{
if (eLink->elem->master != NULL)
Element_FreeResources(tree, eLink->elem);
}
/*
*----------------------------------------------------------------------
*
* MStyle_FreeResources --
*
* Free memory etc associated with a Style.
*
* Results:
* None.
*
* Side effects:
* Memory is deallocated.
*
*----------------------------------------------------------------------
*/
static void
MStyle_FreeResources(
TreeCtrl *tree, /* Widget info. */
MStyle *style /* Style to free. */
)
{
Tcl_HashEntry *hPtr;
int i;
hPtr = Tcl_FindHashEntry(&tree->styleHash, style->name);
Tcl_DeleteHashEntry(hPtr);
if (style->numElements > 0) {
for (i = 0; i < style->numElements; i++)
MElementLink_FreeResources(tree, &style->elements[i]);
#ifdef ALLOC_HAX
TreeAlloc_CFree(tree->allocData, MElementLinkUid, (char *) style->elements,
sizeof(MElementLink), style->numElements, ELEMENT_LINK_ROUND);
#else
WCFREE(style->elements, MElementLink, style->numElements);
#endif
}
#ifdef ALLOC_HAX
TreeAlloc_Free(tree->allocData, MStyleUid, (char *) style, sizeof(MStyle));
#else
WFREE(style, MStyle);
#endif
}
/*
*----------------------------------------------------------------------
*
* IStyle_FreeResources --
*
* Free memory etc associated with a Style.
*
* Results:
* None.
*
* Side effects:
* Memory is deallocated.
*
*----------------------------------------------------------------------
*/
static void
IStyle_FreeResources(
TreeCtrl *tree, /* Widget info. */
IStyle *style /* Style to free. */
)
{
MStyle *masterStyle = style->master;
int i;
if (masterStyle->numElements > 0) {
for (i = 0; i < masterStyle->numElements; i++)
IElementLink_FreeResources(tree, &style->elements[i]);
#ifdef ALLOC_HAX
TreeAlloc_CFree(tree->allocData, IElementLinkUid,
(char *) style->elements, sizeof(IElementLink),
masterStyle->numElements, ELEMENT_LINK_ROUND);
#else
WCFREE(style->elements, IElementLink, masterStyle->numElements);
#endif
}
#ifdef ALLOC_HAX
TreeAlloc_Free(tree->allocData, IStyleUid, (char *) style, sizeof(IStyle));
#else
WFREE(style, IStyle);
#endif
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_FreeResources --
*
* Free memory etc associated with a Style.
*
* Results:
* None.
*
* Side effects:
* Memory is deallocated.
*
*----------------------------------------------------------------------
*/
void
TreeStyle_FreeResources(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_ /* Token of style to free. */
)
{
MStyle *masterStyle = (MStyle *) style_;
IStyle *style = (IStyle *) style_;
if (style->master == NULL)
MStyle_FreeResources(tree, masterStyle);
else
IStyle_FreeResources(tree, style);
}
/*
*----------------------------------------------------------------------
*
* MStyle_FindElem --
*
* Find an ElementLink in a style.
*
* Results:
* If found, a pointer to the ElementLink and index in the
* style's array of ElementLinks is returned; otherwise NULL
* is returned.
*
* Side effects:
* World peace.
*
*----------------------------------------------------------------------
*/
static MElementLink *
MStyle_FindElem(
TreeCtrl *tree, /* Widget info. */
MStyle *style, /* Style to search. */
TreeElement master, /* Master element to find. */
int *index /* Returned index, may be NULL. */
)
{
int i;
for (i = 0; i < style->numElements; i++) {
MElementLink *eLink = &style->elements[i];
if (eLink->elem->name == master->name) {
if (index != NULL) (*index) = i;
return eLink;
}
}
return NULL;
}
/*
*----------------------------------------------------------------------
*
* IStyle_FindElem --
*
* Find an ElementLink in a style.
*
* Results:
* If found, a pointer to the ElementLink and index in the
* style's array of ElementLinks is returned; otherwise NULL
* is returned.
*
* Side effects:
* World peace.
*
*----------------------------------------------------------------------
*/
static IElementLink *
IStyle_FindElem(
TreeCtrl *tree, /* Widget info. */
IStyle *style, /* Style to search. */
TreeElement master, /* Master element to find. */
int *index /* Returned index, may be NULL. */
)
{
MStyle *masterStyle = style->master;
int i;
for (i = 0; i < masterStyle->numElements; i++) {
IElementLink *eLink = &style->elements[i];
if (eLink->elem->name == master->name) {
if (index != NULL) (*index) = i;
return eLink;
}
}
return NULL;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_FindElement --
*
* Find an ElementLink in a style.
*
* Results:
* If found, the index in the style's array of ElementLinks is
* returned with TCL_OK. Otherwise TCL_ERROR is returned and an
* error message is placed in the interpreter result.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_FindElement(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_, /* Token of style to search. */
TreeElement elem, /* Master element to find. */
int *index /* Returned index, may be NULL. */
)
{
MStyle *masterStyle = (MStyle *) style_;
IStyle *style = (IStyle *) style_;
if (((style->master == NULL) &&
(MStyle_FindElem(tree, masterStyle, elem, index) == NULL)) ||
((style->master != NULL) &&
(IStyle_FindElem(tree, style, elem, index) == NULL))) {
FormatResult(tree->interp, "style %s does not use element %s",
style->master ? style->master->name : masterStyle->name,
elem->name);
return TCL_ERROR;
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* Element_CreateAndConfig --
*
* Allocate and initialize a new Element (master or instance).
*
* Results:
* An Element is allocated, its createProc is called, default
* configuration options are set, then the configProc and changeProc
* are called to handle any given configurations options. If an
* error occurs NULL is returned.
*
* Side effects:
* Memory is allocated.
*
*----------------------------------------------------------------------
*/
static TreeElement
Element_CreateAndConfig(
TreeCtrl *tree, /* Widget info. */
TreeItem item, /* Item containing the element. Should
* be NULL for a master element. */
TreeItemColumn column, /* Item-column containing the element.
* Should be NULL for a master element. */
TreeElement masterElem, /* Master element if creating an instance. */
TreeElementType *type, /* Element type. Should be NULL when
* creating an instance. */
CONST char *name, /* Name of master element, NULL for an
* instance. */
int objc, /* Array of intialial configuration. */
Tcl_Obj *CONST objv[] /* options. */
)
{
TreeElement elem;
TreeElementArgs args;
Tcl_Obj *staticObjV[STATIC_SIZE], **objV = staticObjV;
int i, objC = 0, domain = STATE_DOMAIN_ITEM;
STATIC_ALLOC(objV, Tcl_Obj *, objc);
/* Filter out -statedomain and its value. */
/* FIXME: there is no way to query this. */
for (i = 0; i < objc; i += 2) {
int length;
CONST char *s = Tcl_GetStringFromObj(objv[i], &length);
if (strncmp(s, "-statedomain", length) == 0) {
if (i + 1 == objc) {
FormatResult(tree->interp, "value for \"%s\" missing", s);
STATIC_FREE(objV, Tcl_Obj *, objc);
return NULL;
}
s = Tcl_GetStringFromObj(objv[i + 1], &length);
if (strncmp(s, "header", length) == 0)
domain = STATE_DOMAIN_HEADER;
else if (strncmp(s, "item", length) != 0) {
FormatResult(tree->interp, "unknown state domain \"%s\"", s);
STATIC_FREE(objV, Tcl_Obj *, objc);
return NULL;
}
} else {
objV[objC++] = objv[i];
if (i + 1 < objc)
objV[objC++] = objv[i + 1];
}
}
if (masterElem != NULL) {
type = masterElem->typePtr;
name = masterElem->name;
domain = masterElem->stateDomain;
}
#ifdef ALLOC_HAX
elem = (TreeElement) TreeAlloc_Alloc(tree->allocData, type->name,
type->size);
#else
elem = (TreeElement) ckalloc(type->size);
#endif
memset(elem, '\0', type->size);
elem->name = Tk_GetUid(name);
elem->typePtr = type;
elem->master = masterElem;
elem->stateDomain = domain;
args.tree = tree;
args.elem = elem;
args.create.item = item;
args.create.column = column;
if ((*type->createProc)(&args) != TCL_OK) {
STATIC_FREE(objV, Tcl_Obj *, objc);
#ifdef ALLOC_HAX
TreeAlloc_Free(tree->allocData, type->name, (char *) elem, type->size);
#else
WFREE(elem, TreeElement_);
#endif
return NULL;
}
if (Tk_InitOptions(tree->interp, (char *) elem,
type->optionTable, tree->tkwin) != TCL_OK) {
STATIC_FREE(objV, Tcl_Obj *, objc);
#ifdef ALLOC_HAX
TreeAlloc_Free(tree->allocData, type->name, (char *) elem, type->size);
#else
WFREE(elem, TreeElement_);
#endif
return NULL;
}
args.config.objc = objC;
args.config.objv = objV;
args.config.flagSelf = 0;
args.config.item = item;
args.config.column = column;
if ((*type->configProc)(&args) != TCL_OK) {
(*type->deleteProc)(&args);
STATIC_FREE(objV, Tcl_Obj *, objc);
Tk_FreeConfigOptions((char *) elem,
type->optionTable,
tree->tkwin);
DynamicOption_Free(tree, elem->options, type->optionSpecs);
#ifdef ALLOC_HAX
TreeAlloc_Free(tree->allocData, type->name, (char *) elem, type->size);
#else
WFREE(elem, TreeElement_);
#endif
return NULL;
}
args.change.flagSelf = args.config.flagSelf;
args.change.flagTree = 0;
args.change.flagMaster = 0;
(*type->changeProc)(&args);
STATIC_FREE(objV, Tcl_Obj *, objc);
return elem;
}
/*
*----------------------------------------------------------------------
*
* Style_CreateElem --
*
* Allocate and initialize a new instance Element in a IStyle
* (if it doesn't already exist) and return its associated
* IElementLink.
*
* Results:
* If the style already has a matching instance element, then a
* pointer to an existing IElementLink is returned.
* If the style does not already have a matching instance element,
* then a new one is created and a pointer to an existing
* IElementLink is returned.
* If an error occurs creating the new element the result is
* NULL.
*
* Side effects:
* Memory is allocated.
*
*----------------------------------------------------------------------
*/
static IElementLink *
Style_CreateElem(
TreeCtrl *tree, /* Widget info. */
TreeItem item, /* Item containing the element. */
TreeItemColumn column, /* Item-column containing the element. */
IStyle *style, /* Style to search/add the element to. */
TreeElement masterElem, /* Element to find or create and instance of. */
int *isNew) /* If non-NULL, set to TRUE if a new instance
* element was created. */
{
MStyle *masterStyle = style->master;
IElementLink *eLink = NULL;
TreeElement elem;
int i;
if (masterElem->master != NULL)
panic("Style_CreateElem called with instance Element");
if (isNew != NULL) (*isNew) = FALSE;
for (i = 0; i < masterStyle->numElements; i++) {
eLink = &style->elements[i];
if (eLink->elem == masterElem) {
/* Allocate instance Element here */
break;
}
/* Instance Style already has instance Element */
if (eLink->elem->name == masterElem->name)
return eLink;
}
/* Error: Element isn't in the master Style */
if (i == masterStyle->numElements)
return NULL;
elem = Element_CreateAndConfig(tree, item, column, masterElem, NULL, NULL, 0, NULL);
if (elem == NULL)
return NULL;
eLink->elem = elem;
if (isNew != NULL) (*isNew) = TRUE;
return eLink;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_NewInstance --
*
* Create and initialize a new instance of a master style.
*
* Results:
* A new instance Style. The new array of ElementLinks is
* initialized to contain pointers to master elements; instance
* elements are created the first time they are configured.
*
* Side effects:
* Memory is allocated.
*
*----------------------------------------------------------------------
*/
TreeStyle
TreeStyle_NewInstance(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_ /* Master style to create instance of. */
)
{
MStyle *style = (MStyle *) style_;
IStyle *copy;
IElementLink *eLink;
int i;
#ifdef ALLOC_HAX
copy = (IStyle *) TreeAlloc_Alloc(tree->allocData, IStyleUid, sizeof(IStyle));
#else
copy = (IStyle *) ckalloc(sizeof(IStyle));
#endif
memset(copy, '\0', sizeof(IStyle));
copy->master = style;
copy->neededWidth = -1;
copy->neededHeight = -1;
if (style->numElements > 0) {
#ifdef ALLOC_HAX
copy->elements = (IElementLink *) TreeAlloc_CAlloc(tree->allocData,
IElementLinkUid, sizeof(IElementLink), style->numElements,
ELEMENT_LINK_ROUND);
#else
copy->elements = (IElementLink *) ckalloc(sizeof(IElementLink) *
style->numElements);
#endif
memset(copy->elements, '\0', sizeof(IElementLink) * style->numElements);
for (i = 0; i < style->numElements; i++) {
eLink = &copy->elements[i];
eLink->elem = style->elements[i].elem;
#ifdef CACHE_ELEM_SIZE
eLink->neededWidth = -1;
eLink->neededHeight = -1;
#endif
}
}
return (TreeStyle) copy;
}
/*
*----------------------------------------------------------------------
*
* TreeElement_FromObj --
*
* Convert a Tcl_Obj to a master element.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeElement_FromObj(
TreeCtrl *tree, /* Widget info. */
Tcl_Obj *obj, /* Object to convert from. */
TreeElement *elemPtr /* Returned master element token. */
)
{
char *name;
Tcl_HashEntry *hPtr;
name = Tcl_GetString(obj);
hPtr = Tcl_FindHashEntry(&tree->elementHash, name);
if ((hPtr == NULL) || ((TreeElement) Tcl_GetHashValue(hPtr))->hidden) {
Tcl_AppendResult(tree->interp, "element \"", name, "\" doesn't exist",
NULL);
return TCL_ERROR;
}
(*elemPtr) = (TreeElement) Tcl_GetHashValue(hPtr);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* TreeElement_IsType --
*
* Determine if an element is of a certain type.
*
* Results:
* TRUE if the type matches, otherwise FALSE.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeElement_IsType(
TreeCtrl *tree, /* Widget info. */
TreeElement elem, /* Element to check. */
CONST char *type /* NULL-terminated element type name. */
)
{
return strcmp(elem->typePtr->name, type) == 0;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_FromObj --
*
* Convert a Tcl_Obj to a master style.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_FromObj(
TreeCtrl *tree, /* Widget info. */
Tcl_Obj *obj, /* Object to convert from. */
TreeStyle *stylePtr) /* Returned master style token. */
{
char *name;
Tcl_HashEntry *hPtr;
name = Tcl_GetString(obj);
hPtr = Tcl_FindHashEntry(&tree->styleHash, name);
if (hPtr == NULL || ((MStyle *) Tcl_GetHashValue(hPtr))->hidden) {
Tcl_AppendResult(tree->interp, "style \"", name, "\" doesn't exist",
NULL);
return TCL_ERROR;
}
(*stylePtr) = (TreeStyle) Tcl_GetHashValue(hPtr);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* TreeElement_ToObj --
*
* Create a new Tcl_Obj representing an element.
*
* Results:
* A Tcl_Obj.
*
* Side effects:
* Memory is allocated.
*
*----------------------------------------------------------------------
*/
Tcl_Obj *
TreeElement_ToObj(
TreeElement elem /* Element to create Tcl_Obj from. */
)
{
return Tcl_NewStringObj(elem->name, -1);
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_ToObj --
*
* Create a new Tcl_Obj representing a style.
*
* Results:
* A Tcl_Obj.
*
* Side effects:
* Memory is allocated.
*
*----------------------------------------------------------------------
*/
Tcl_Obj *
TreeStyle_ToObj(
TreeStyle style_ /* Style token to create Tcl_Obj from. */
)
{
MStyle *masterStyle = (MStyle *) style_;
IStyle *style = (IStyle *) style_;
if (style->master != NULL)
masterStyle = style->master;
return Tcl_NewStringObj(masterStyle->name, -1);
}
/*
*----------------------------------------------------------------------
*
* Style_Changed --
*
* Called when a master style is configured or the layout of one
* of its elements changes.
*
* Results:
* For each item-column using an instance of the given master
* style, size and display info is marked out-of-date.
*
* Side effects:
* Display changes.
*
*----------------------------------------------------------------------
*/
static void
Style_Changed(
TreeCtrl *tree, /* Widget info. */
MStyle *masterStyle /* Style that changed. */
)
{
TreeItem item;
TreeItemColumn column;
TreeColumn treeColumn;
Tcl_HashTable *tablePtr = &tree->itemHash;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
int columnIndex, layout;
int updateDInfo = FALSE;
IStyle *style;
int tailOK;
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
if (hPtr == NULL) {
tablePtr = &tree->headerHash;
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
}
while (hPtr != NULL) {
item = (TreeItem) Tcl_GetHashValue(hPtr);
tailOK = TreeItem_GetHeader(tree, item) != NULL;
treeColumn = Tree_FirstColumn(tree, -1, TreeItem_GetHeader(tree, item) != NULL);
column = TreeItem_GetFirstColumn(tree, item);
columnIndex = 0;
layout = FALSE;
while (column != NULL) {
style = (IStyle *) TreeItemColumn_GetStyle(tree, column);
if ((style != NULL) && (style->master == masterStyle)) {
#ifdef CACHE_ELEM_SIZE
int i;
for (i = 0; i < masterStyle->numElements; i++) {
IElementLink *eLink = &style->elements[i];
/* This is needed if the -width/-height layout options change */
eLink->neededWidth = eLink->neededHeight = -1;
}
#endif
style->neededWidth = style->neededHeight = -1;
TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
TreeItemColumn_InvalidateSize(tree, column);
layout = TRUE;
}
columnIndex++;
column = TreeItemColumn_GetNext(tree, column);
treeColumn = Tree_ColumnToTheRight(treeColumn, FALSE, tailOK);
}
if (layout) {
TreeItem_InvalidateHeight(tree, item);
Tree_FreeItemDInfo(tree, item, NULL);
updateDInfo = TRUE;
}
hPtr = Tcl_NextHashEntry(&search);
if (hPtr == NULL && tablePtr == &tree->itemHash) {
tablePtr = &tree->headerHash;
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
}
}
if (updateDInfo)
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
}
/*
*----------------------------------------------------------------------
*
* MStyle_ChangeElementsAux --
*
* Update the list of elements used by a master style. Elements
* may be inserted or deleted.
*
* Results:
* The list of elements in the style is updated.
*
* Side effects:
* Memory may be allocated/deallocated.
*
*----------------------------------------------------------------------
*/
static void
MStyle_ChangeElementsAux(
TreeCtrl *tree, /* Widget info. */
MStyle *style, /* Master style to be updated. */
int count, /* The number of elements in the style after
* this routine finishes. */
TreeElement *elemList, /* List of master elements the style uses. */
int *map /* Array of indexes into the list of elements
* currently used by the style. */
)
{
MElementLink *eLinks = NULL;
int i, staticKeep[STATIC_SIZE], *keep = staticKeep;
STATIC_ALLOC(keep, int, style->numElements);
if (count > 0) {
#ifdef ALLOC_HAX
eLinks = (MElementLink *) TreeAlloc_CAlloc(tree->allocData,
MElementLinkUid, sizeof(MElementLink), count,
ELEMENT_LINK_ROUND);
#else
eLinks = (MElementLink *) ckalloc(sizeof(MElementLink) * count);
#endif
}
/* Assume we are discarding all the old ElementLinks */
for (i = 0; i < style->numElements; i++)
keep[i] = 0;
for (i = 0; i < count; i++) {
if (map[i] != -1) {
eLinks[i] = style->elements[map[i]];
keep[map[i]] = 1;
} else {
MElementLink_Init(&eLinks[i], elemList[i]);
}
}
if (style->numElements > 0) {
/* Free unused ElementLinks */
for (i = 0; i < style->numElements; i++) {
if (!keep[i]) {
MElementLink_FreeResources(tree, &style->elements[i]);
}
}
#ifdef ALLOC_HAX
TreeAlloc_CFree(tree->allocData, MElementLinkUid,
(char *) style->elements, sizeof(MElementLink),
style->numElements, ELEMENT_LINK_ROUND);
#else
WCFREE(style->elements, MElementLink, style->numElements);
#endif
}
STATIC_FREE(keep, int, style->numElements);
style->elements = eLinks;
style->numElements = count;
/* Hack - Remember if any of the elements are of type 'header' or
* 'window'. */
style->hasHeaderElem = FALSE;
style->hasWindowElem = FALSE;
for (i = 0; i < count; i++) {
if (ELEMENT_TYPE_MATCHES(eLinks[i].elem->typePtr, &treeElemTypeHeader))
style->hasHeaderElem = TRUE;
if (ELEMENT_TYPE_MATCHES(eLinks[i].elem->typePtr, &treeElemTypeWindow))
style->hasWindowElem = TRUE;
}
}
/*
*----------------------------------------------------------------------
*
* IStyle_ChangeElementsAux --
*
* Update the list of elements used by an instance style. Elements
* may be inserted or deleted.
*
* Results:
* The list of elements in the style is updated.
*
* Side effects:
* Memory may be allocated/deallocated.
*
*----------------------------------------------------------------------
*/
static void
IStyle_ChangeElementsAux(
TreeCtrl *tree, /* Widget info. */
IStyle *style, /* Instance style to be updated. */
int oldCount, /* The previous number of elements. */
int count, /* The number of elements in the style after
* this routine finishes. */
TreeElement *elemList, /* List of master elements the style uses. */
int *map /* Array of indexes into the list of elements
* currently used by the style. */
)
{
IElementLink *eLink, *eLinks = NULL;
int i, staticKeep[STATIC_SIZE], *keep = staticKeep;
STATIC_ALLOC(keep, int, oldCount);
if (count > 0) {
#ifdef ALLOC_HAX
eLinks = (IElementLink *) TreeAlloc_CAlloc(tree->allocData,
IElementLinkUid, sizeof(IElementLink), count,
ELEMENT_LINK_ROUND);
#else
eLinks = (IElementLink *) ckalloc(sizeof(IElementLink) * count);
#endif
}
/* Assume we are discarding all the old ElementLinks */
for (i = 0; i < oldCount; i++)
keep[i] = 0;
for (i = 0; i < count; i++) {
if (map[i] != -1) {
eLinks[i] = style->elements[map[i]];
keep[map[i]] = 1;
} else {
eLink = &eLinks[i];
eLink->elem = elemList[i];
#ifdef CACHE_ELEM_SIZE
eLink->neededWidth = eLink->neededHeight = -1;
#endif
}
}
if (oldCount > 0) {
/* Free unused ElementLinks */
for (i = 0; i < oldCount; i++) {
if (!keep[i]) {
IElementLink_FreeResources(tree, &style->elements[i]);
}
}
#ifdef ALLOC_HAX
TreeAlloc_CFree(tree->allocData, IElementLinkUid,
(char *) style->elements, sizeof(IElementLink),
oldCount, ELEMENT_LINK_ROUND);
#else
WCFREE(style->elements, IElementLink, oldCount);
#endif
}
STATIC_FREE(keep, int, oldCount);
style->elements = eLinks;
}
/*
*----------------------------------------------------------------------
*
* Style_ChangeElements --
*
* Update the list of elements used by a style. Elements
* may be inserted or deleted.
*
* Results:
* The list of elements in the master style is updated. For
* each item-column using an instance of the master style,
* the list of elements is updated.
*
* Side effects:
* Display changes.
*
*----------------------------------------------------------------------
*/
static void
Style_ChangeElements(
TreeCtrl *tree, /* Widget info. */
MStyle *masterStyle, /* Master style to be updated. */
int count, /* The number of elements in the style after
* this routine finishes. */
TreeElement *elemList, /* List of master elements the style uses. */
int *map /* Array of indexes into the list of elements
* currently used by the style. */
)
{
TreeItem item;
TreeItemColumn column;
TreeColumn treeColumn;
Tcl_HashTable *tablePtr = &tree->itemHash;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
int columnIndex, layout;
int updateDInfo = FALSE;
IStyle *style;
int i, j, k, oldCount;
int tailOK;
/* Update -union lists */
for (i = 0; i < masterStyle->numElements; i++) {
MElementLink *eLink = &masterStyle->elements[i];
int staticKeep[STATIC_SIZE], *keep = staticKeep;
int onionCnt = 0, *onion = NULL;
if (eLink->onion == NULL)
continue;
STATIC_ALLOC(keep, int, eLink->onionCount);
/* Check every Element in this -union */
for (j = 0; j < eLink->onionCount; j++) {
MElementLink *eLink2 = &masterStyle->elements[eLink->onion[j]];
/* Check the new list of Elements */
keep[j] = -1;
for (k = 0; k < count; k++) {
/* This new Element is in the -union */
if (elemList[k] == eLink2->elem) {
keep[j] = k;
onionCnt++;
break;
}
}
}
if (onionCnt > 0) {
if (onionCnt != eLink->onionCount)
onion = (int *) ckalloc(sizeof(int) * onionCnt);
else
onion = eLink->onion;
k = 0;
for (j = 0; j < eLink->onionCount; j++) {
if (keep[j] != -1)
onion[k++] = keep[j];
}
}
STATIC_FREE(keep, int, eLink->onionCount);
if (onionCnt != eLink->onionCount) {
WCFREE(eLink->onion, int, eLink->onionCount);
eLink->onion = onion;
eLink->onionCount = onionCnt;
}
}
oldCount = masterStyle->numElements;
MStyle_ChangeElementsAux(tree, masterStyle, count, elemList, map);
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
if (hPtr == NULL) {
tablePtr = &tree->headerHash;
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
}
while (hPtr != NULL) {
item = (TreeItem) Tcl_GetHashValue(hPtr);
tailOK = TreeItem_GetHeader(tree, item) != NULL;
treeColumn = Tree_FirstColumn(tree, -1, TreeItem_GetHeader(tree, item) != NULL);
column = TreeItem_GetFirstColumn(tree, item);
columnIndex = 0;
layout = FALSE;
while (column != NULL) {
style = (IStyle *) TreeItemColumn_GetStyle(tree, column);
if ((style != NULL) && (style->master == masterStyle)) {
IStyle_ChangeElementsAux(tree, style, oldCount, count, elemList, map);
style->neededWidth = style->neededHeight = -1;
TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
TreeItemColumn_InvalidateSize(tree, column);
layout = TRUE;
}
columnIndex++;
column = TreeItemColumn_GetNext(tree, column);
treeColumn = Tree_ColumnToTheRight(treeColumn, FALSE, tailOK);
}
if (layout) {
TreeItem_InvalidateHeight(tree, item);
Tree_FreeItemDInfo(tree, item, NULL);
updateDInfo = TRUE;
}
hPtr = Tcl_NextHashEntry(&search);
if (hPtr == NULL && tablePtr == &tree->itemHash) {
tablePtr = &tree->headerHash;
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
}
}
if (updateDInfo)
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
}
/*
*----------------------------------------------------------------------
*
* Style_ElemChanged --
*
* Called when a master element or TreeCtrl is configured.
*
* Results:
* A check is made on each item-column to see if it is using
* the element. The size of any element/column/item affected
* is marked out-of-date.
*
* Side effects:
* Display changes.
*
*----------------------------------------------------------------------
*/
static void
Style_ElemChanged(
TreeCtrl *tree, /* Widget info. */
MStyle *masterStyle, /* Master style that uses the element. */
TreeElement masterElem, /* Master element affected by the change. */
int masterElemIndex, /* Index of masterElem in masterStyle. */
int flagM, /* Flags returned by TreeElementType.configProc()
* if the master element was configured,
* zero if the TreeCtrl was configured. */
int flagT, /* TREE_CONF_xxx flags if the TreeCtrl was
* configured, zero if the master element
* was configured. */
int csM /* CS_xxx flags returned by
* TreeElementType.changeProc(). */
)
{
TreeItem item;
TreeItemColumn column;
TreeColumn treeColumn;
Tcl_HashTable *tablePtr = &tree->itemHash;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
IElementLink *eLink;
int columnIndex;
TreeElementArgs args;
IStyle *style;
int eMask, cMask, iMask;
int updateDInfo = FALSE, tailOK;
args.tree = tree;
args.change.flagTree = flagT;
args.change.flagMaster = flagM;
args.change.flagSelf = 0;
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
if (hPtr == NULL) {
tablePtr = &tree->headerHash;
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
}
while (hPtr != NULL) {
item = (TreeItem) Tcl_GetHashValue(hPtr);
tailOK = TreeItem_GetHeader(tree, item) != NULL;
treeColumn = Tree_FirstColumn(tree, -1, tailOK);
column = TreeItem_GetFirstColumn(tree, item);
columnIndex = 0;
iMask = 0;
while (column != NULL) {
cMask = 0;
style = (IStyle *) TreeItemColumn_GetStyle(tree, column);
if ((style != NULL) && (style->master == masterStyle)) {
eLink = &style->elements[masterElemIndex];
if (eLink->elem == masterElem) {
#ifdef CACHE_ELEM_SIZE
if (csM & CS_LAYOUT)
eLink->neededWidth = eLink->neededHeight = -1;
#endif
cMask |= csM;
}
/* Instance element */
else {
args.elem = eLink->elem;
eMask = (*masterElem->typePtr->changeProc)(&args);
#ifdef CACHE_ELEM_SIZE
if (eMask & CS_LAYOUT)
eLink->neededWidth = eLink->neededHeight = -1;
#endif
cMask |= eMask;
}
iMask |= cMask;
if (cMask & CS_LAYOUT) {
style->neededWidth = style->neededHeight = -1;
TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
TreeItemColumn_InvalidateSize(tree, column);
}
else if (cMask & CS_DISPLAY) {
Tree_InvalidateItemDInfo(tree, treeColumn, item, NULL);
}
}
columnIndex++;
column = TreeItemColumn_GetNext(tree, column);
treeColumn = Tree_ColumnToTheRight(treeColumn, FALSE, tailOK);
}
if (iMask & CS_LAYOUT) {
TreeItem_InvalidateHeight(tree, item);
Tree_FreeItemDInfo(tree, item, NULL);
updateDInfo = TRUE;
}
else if (iMask & CS_DISPLAY) {
}
hPtr = Tcl_NextHashEntry(&search);
if (hPtr == NULL && tablePtr == &tree->itemHash) {
tablePtr = &tree->headerHash;
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
}
}
if (updateDInfo)
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_GetButtonY --
*
* Return the value of the -buttony style option.
*
* Results:
* Pixel value or -1 if unspecified.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_GetButtonY(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_ /* Master or instance style token. */
)
{
MStyle *style = (MStyle *) style_;
MStyle *master = (style->master != NULL) ? style->master : style;
return (master->buttonYObj == NULL) ? -1 : master->buttonY;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_GetMaster --
*
* Return the master style for an instance style.
*
* Results:
* Token for the master style.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
TreeStyle
TreeStyle_GetMaster(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_ /* Instance style token. */
)
{
return (TreeStyle) ((IStyle *) style_)->master;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_GetName --
*
* Return the name of a style.
*
* Results:
* String name.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
CONST char *
TreeStyle_GetName(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_ /* Master or instance style token. */
)
{
MStyle *style = (MStyle *) style_;
MStyle *master = (style->master != NULL) ? style->master : style;
return master->name;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_GetStateDomain --
*
* Return the state domain for a style.
*
* Results:
* STATE_DOMAIN_XXX index.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_GetStateDomain(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_ /* Master or instance style token. */
)
{
MStyle *style = (MStyle *) style_;
MStyle *master = (style->master != NULL) ? style->master : style;
return master->stateDomain;
}
/*
*----------------------------------------------------------------------
*
* Style_GetImageOrText --
*
* Return the value of a configuration option for an element.
*
* Results:
* The result of Tk_GetOptionValue for an option of the first
* element of the proper type (if any), otherwise NULL.
*
* Side effects:
* A Tcl_Obj may be allocated.
*
*----------------------------------------------------------------------
*/
static Tcl_Obj *
Style_GetImageOrText(
TreeCtrl *tree, /* Widget info. */
IStyle *style, /* Style. */
TreeElementType *typePtr, /* Type of element to look for. */
Tcl_Obj *optionNameObj, /* Pointer to a Tcl_Obj holding the
* option name. */
TreeElement *elemPtr /* Returned element or NULL. */
)
{
IElementLink *eLink;
int i;
for (i = 0; i < style->master->numElements; i++) {
eLink = &style->elements[i];
if (ELEMENT_TYPE_MATCHES(eLink->elem->typePtr, typePtr)) {
Tcl_Obj *resultObjPtr;
resultObjPtr = Tk_GetOptionValue(tree->interp,
(char *) eLink->elem, eLink->elem->typePtr->optionTable,
optionNameObj, tree->tkwin);
(*elemPtr) = style->master->elements[i].elem;
return resultObjPtr;
}
}
(*elemPtr) = NULL;
return NULL;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_GetImage --
*
* Return the value of the -image option for the first
* image element in a style (if any).
*
* Results:
* The result of Tk_GetOptionValue if the element was found,
* otherwise NULL.
*
* Side effects:
* A Tcl_Obj may be allocated.
*
*----------------------------------------------------------------------
*/
Tcl_Obj *
TreeStyle_GetImage(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_, /* Token for style to examine. */
TreeElement *elemPtr /* Returned element or NULL. */
)
{
return Style_GetImageOrText(tree, (IStyle *) style_, &treeElemTypeImage,
tree->imageOptionNameObj, elemPtr);
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_GetText --
*
* Return the value of the -text option for the first
* text element in a style (if any).
*
* Results:
* The result of Tk_GetOptionValue if the element was found,
* otherwise NULL.
*
* Side effects:
* A Tcl_Obj may be allocated.
*
*----------------------------------------------------------------------
*/
Tcl_Obj *
TreeStyle_GetText(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_, /* Token for style to examine. */
TreeElement *elemPtr /* Returned element or NULL. */
)
{
return Style_GetImageOrText(tree, (IStyle *) style_, &treeElemTypeText,
tree->textOptionNameObj, elemPtr);
}
/*
*----------------------------------------------------------------------
*
* Style_SetImageOrText --
*
* Set the value of a configuration option for the first
* element of the proper type in a style (if any).
*
* Results:
* A standard Tcl result.
*
* Side effects:
* Size of the element and style will be marked out-of-date.
* A Tcl_Obj may be allocated.
*
*----------------------------------------------------------------------
*/
static int
Style_SetImageOrText(
TreeCtrl *tree, /* Widget info. */
TreeItem item, /* Item containing the style. Needed if
* a new instance Element is created. */
TreeItemColumn column, /* Item-column containing the style */
IStyle *style, /* The style */
TreeElementType *typePtr, /* Element type to look for. */
Tcl_Obj *optionNameObj, /* Pointer to a Tcl_Obj holding the option
* name. */
Tcl_Obj *valueObj, /* New value for the config option. */
TreeElement *elemPtr /* Returned element or NULL. */
)
{
MStyle *masterStyle = style->master;
IElementLink *eLink;
int i;
(*elemPtr) = NULL;
for (i = 0; i < masterStyle->numElements; i++) {
TreeElement masterElem = masterStyle->elements[i].elem;
if (ELEMENT_TYPE_MATCHES(masterElem->typePtr, typePtr)) {
Tcl_Obj *objv[2];
TreeElementArgs args;
eLink = Style_CreateElem(tree, item, column, style, masterElem, NULL);
objv[0] = optionNameObj;
objv[1] = valueObj;
args.tree = tree;
args.elem = eLink->elem;
args.config.objc = 2;
args.config.objv = objv;
args.config.flagSelf = 0;
args.config.item = item;
args.config.column = column;
if ((*eLink->elem->typePtr->configProc)(&args) != TCL_OK)
return TCL_ERROR;
args.change.flagSelf = args.config.flagSelf;
args.change.flagTree = 0;
args.change.flagMaster = 0;
(void) (*eLink->elem->typePtr->changeProc)(&args);
#ifdef CACHE_ELEM_SIZE
eLink->neededWidth = eLink->neededHeight = -1;
#endif
style->neededWidth = style->neededHeight = -1;
(*elemPtr) = masterElem;
break;
}
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_SetImage --
*
* Set the value of the -image option for the first image
* element in a style (if any).
*
* Results:
* A standard Tcl result.
*
* Side effects:
* Size of the element and style will be marked out-of-date.
* A Tcl_Obj may be allocated.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_SetImage(
TreeCtrl *tree, /* Widget info. */
TreeItem item, /* Item containing the style. */
TreeItemColumn column, /* Item-column containing the style. */
TreeStyle style_, /* The instance style. */
Tcl_Obj *valueObj, /* New value for -image option. */
TreeElement *elemPtr /* Returned element or NULL. */
)
{
return Style_SetImageOrText(tree, item, column, (IStyle *) style_,
&treeElemTypeImage, tree->imageOptionNameObj, valueObj, elemPtr);
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_SetText --
*
* Set the value of the -text option for the first text
* element in a style (if any).
*
* Results:
* A standard Tcl result.
*
* Side effects:
* Size of the element and style will be marked out-of-date.
* A Tcl_Obj may be allocated.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_SetText(
TreeCtrl *tree, /* Widget info. */
TreeItem item, /* Item containing the style. */
TreeItemColumn column, /* Item-column containing the style. */
TreeStyle style_, /* The instance style. */
Tcl_Obj *valueObj, /* New value for -text option. */
TreeElement *elemPtr /* Returned element or NULL. */
)
{
return Style_SetImageOrText(tree, item, column, (IStyle *) style_,
&treeElemTypeText, tree->textOptionNameObj, valueObj, elemPtr);
}
/*
*----------------------------------------------------------------------
*
* Style_Deleted --
*
* Called when a master style is about to be deleted. Any
* item-columns using an instance of the style have their style
* freed.
*
* Results:
* The TreeCtrl -defaultstyle option is updated if the deleted
* style was specified in the value of the option.
*
* Side effects:
* Display changes. Memory is deallocated.
*
*----------------------------------------------------------------------
*/
static void
Style_Deleted(
TreeCtrl *tree, /* Widget info. */
MStyle *masterStyle /* The master style being deleted. */
)
{
TreeItem item;
TreeItemColumn column;
TreeColumn treeColumn;
Tcl_HashTable *tablePtr = &tree->itemHash;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
IStyle *style;
int columnIndex;
int tailOK;
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
if (hPtr == NULL) {
tablePtr = &tree->headerHash;
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
}
while (hPtr != NULL) {
item = (TreeItem) Tcl_GetHashValue(hPtr);
tailOK = TreeItem_GetHeader(tree, item) != NULL;
treeColumn = Tree_FirstColumn(tree, -1, tailOK);
column = TreeItem_GetFirstColumn(tree, item);
columnIndex = 0;
while (column != NULL) {
style = (IStyle *) TreeItemColumn_GetStyle(tree, column);
if ((style != NULL) && (style->master == masterStyle)) {
TreeColumns_InvalidateWidthOfItems(tree, treeColumn);
TreeItemColumn_ForgetStyle(tree, column);
TreeItem_InvalidateHeight(tree, item);
Tree_FreeItemDInfo(tree, item, NULL);
}
columnIndex++;
column = TreeItemColumn_GetNext(tree, column);
treeColumn = Tree_ColumnToTheRight(treeColumn, FALSE, tailOK);
}
hPtr = Tcl_NextHashEntry(&search);
if (hPtr == NULL && tablePtr == &tree->itemHash) {
tablePtr = &tree->headerHash;
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
}
}
/* Update each column's -itemstyle option */
treeColumn = tree->columns;
while (treeColumn != NULL) {
TreeColumn_StyleDeleted(treeColumn, (TreeStyle) masterStyle);
treeColumn = TreeColumn_Next(treeColumn);
}
#ifdef DEPRECATED
/* Update -defaultstyle option */
if (tree->defaultStyle.stylesObj != NULL) {
Tcl_Obj *stylesObj = tree->defaultStyle.stylesObj;
if (Tcl_IsShared(stylesObj)) {
stylesObj = Tcl_DuplicateObj(stylesObj);
Tcl_DecrRefCount(tree->defaultStyle.stylesObj);
Tcl_IncrRefCount(stylesObj);
tree->defaultStyle.stylesObj = stylesObj;
}
for (columnIndex = 0; columnIndex < tree->defaultStyle.numStyles; columnIndex++) {
Tcl_Obj *emptyObj;
if (tree->defaultStyle.styles[columnIndex] != (TreeStyle) masterStyle)
continue;
tree->defaultStyle.styles[columnIndex] = NULL;
emptyObj = Tcl_NewObj();
Tcl_ListObjReplace(tree->interp, stylesObj, columnIndex, 1, 1, &emptyObj);
}
}
#endif /* DEPRECATED */
#ifdef DRAGIMAGE_STYLE
TreeDragImage_StyleDeleted(tree->dragImage, (TreeStyle) masterStyle);
#endif
}
/*
*----------------------------------------------------------------------
*
* Element_Changed --
*
* Called when a master element or TreeCtrl has been configured.
*
* Results:
* Every master and instance style using the element is updated.
*
* Side effects:
* Display changes.
*
*----------------------------------------------------------------------
*/
static void
Element_Changed(
TreeCtrl *tree, /* Widget info. */
TreeElement masterElem, /* Master element that may have changed. */
int flagM, /* Flags returned by TreeElementType.configProc()
* if the master element was configured,
* zero if the TreeCtrl was configured. */
int flagT, /* TREE_CONF_xxx flags if the TreeCtrl was
* configured, zero if the master element
* was configured. */
int csM /* CS_xxx flags returned by
* TreeElementType.changeProc(). */
)
{
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
MStyle *masterStyle;
MElementLink *eLink;
int i;
hPtr = Tcl_FirstHashEntry(&tree->styleHash, &search);
while (hPtr != NULL) {
masterStyle = (MStyle *) Tcl_GetHashValue(hPtr);
for (i = 0; i < masterStyle->numElements; i++) {
eLink = &masterStyle->elements[i];
if (eLink->elem == masterElem) {
Style_ElemChanged(tree, masterStyle, masterElem, i, flagM, flagT, csM);
break;
}
}
hPtr = Tcl_NextHashEntry(&search);
}
}
/*
*----------------------------------------------------------------------
*
* Element_Deleted --
*
* Called when a master element is about to be deleted.
*
* Results:
* The list of elements in any master styles using the element is
* updated. Ditto for instance styles.
*
* Side effects:
* Display changes.
*
*----------------------------------------------------------------------
*/
static void
Element_Deleted(
TreeCtrl *tree, /* Widget info. */
TreeElement masterElem /* Master element being deleted. */
)
{
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
MStyle *masterStyle;
MElementLink *eLink;
int i, j;
hPtr = Tcl_FirstHashEntry(&tree->styleHash, &search);
while (hPtr != NULL) {
masterStyle = (MStyle *) Tcl_GetHashValue(hPtr);
for (i = 0; i < masterStyle->numElements; i++) {
eLink = &masterStyle->elements[i];
if (eLink->elem == masterElem) {
TreeElement staticElemList[STATIC_SIZE],
*elemList = staticElemList;
int staticElemMap[STATIC_SIZE], *elemMap = staticElemMap;
STATIC_ALLOC(elemList, TreeElement, masterStyle->numElements);
STATIC_ALLOC(elemMap, int, masterStyle->numElements);
for (j = 0; j < masterStyle->numElements; j++) {
if (j == i)
continue;
elemList[(j < i) ? j : (j - 1)] =
masterStyle->elements[j].elem;
elemMap[(j < i) ? j : (j - 1)] = j;
}
Style_ChangeElements(tree, masterStyle,
masterStyle->numElements - 1, elemList, elemMap);
STATIC_FREE(elemList, TreeElement, masterStyle->numElements + 1);
STATIC_FREE(elemMap, int, masterStyle->numElements + 1);
break;
}
}
hPtr = Tcl_NextHashEntry(&search);
}
}
/*
*----------------------------------------------------------------------
*
* Tree_RedrawElement --
*
* A STUB export. Schedules a redraw of the given item.
*
* Results:
* None.
*
* Side effects:
* Display changes.
*
*----------------------------------------------------------------------
*/
void
Tree_RedrawElement(
TreeCtrl *tree, /* Widget info. */
TreeItem item, /* Item containing the element. */
TreeElement elem /* The element that changed. */
)
{
/* Master element */
if (elem->master == NULL) {
}
/* Instance element */
else {
Tree_InvalidateItemDInfo(tree, NULL, item, NULL);
}
}
typedef struct Iterate
{
TreeCtrl *tree;
TreeItem item;
TreeItemColumn column;
int columnIndex;
IStyle *style;
TreeElementType *elemTypePtr;
IElementLink *eLink;
Tcl_HashSearch search;
Tcl_HashEntry *hPtr;
} Iterate;
static int IterateItem(Iterate *iter)
{
int i;
while (iter->column != NULL) {
iter->style = (IStyle *) TreeItemColumn_GetStyle(iter->tree, iter->column);
if (iter->style != NULL) {
for (i = 0; i < iter->style->master->numElements; i++) {
iter->eLink = &iter->style->elements[i];
if (ELEMENT_TYPE_MATCHES(iter->eLink->elem->typePtr, iter->elemTypePtr))
return 1;
}
}
iter->column = TreeItemColumn_GetNext(iter->tree, iter->column);
iter->columnIndex++;
}
return 0;
}
TreeIterate
Tree_ElementIterateBegin(
TreeCtrl *tree,
TreeElementType *elemTypePtr)
{
Iterate *iter;
iter = (Iterate *) ckalloc(sizeof(Iterate));
iter->tree = tree;
iter->elemTypePtr = elemTypePtr;
iter->hPtr = Tcl_FirstHashEntry(&tree->itemHash, &iter->search);
while (iter->hPtr != NULL) {
iter->item = (TreeItem) Tcl_GetHashValue(iter->hPtr);
iter->column = TreeItem_GetFirstColumn(tree, iter->item);
iter->columnIndex = 0;
if (IterateItem(iter))
return (TreeIterate) iter;
iter->hPtr = Tcl_NextHashEntry(&iter->search);
}
ckfree((char *) iter);
return NULL;
}
TreeIterate
Tree_ElementIterateNext(
TreeIterate iter_)
{
Iterate *iter = (Iterate *) iter_;
iter->column = TreeItemColumn_GetNext(iter->tree, iter->column);
iter->columnIndex++;
if (IterateItem(iter))
return iter_;
iter->hPtr = Tcl_NextHashEntry(&iter->search);
while (iter->hPtr != NULL) {
iter->item = (TreeItem) Tcl_GetHashValue(iter->hPtr);
iter->column = TreeItem_GetFirstColumn(iter->tree, iter->item);
iter->columnIndex = 0;
if (IterateItem(iter))
return iter_;
iter->hPtr = Tcl_NextHashEntry(&iter->search);
}
ckfree((char *) iter);
return NULL;
}
/*
*----------------------------------------------------------------------
*
* Tree_ElementChangedItself --
*
* Called when an element has reconfigured itself outside of
* any API calls. For example, when a window associated with a
* window element is resized, or a text element's -textvariable
* is set.
*
* Results:
* None.
*
* Side effects:
* Display changes.
*
*----------------------------------------------------------------------
*/
void
Tree_ElementChangedItself(
TreeCtrl *tree, /* Widget info. */
TreeItem item, /* Item containing the element. */
TreeItemColumn column, /* Item-column containing the element. */
TreeElement elem, /* The element that changed. */
int flags, /* Element-specific configuration flags. */
int csM /* CS_xxx flags detailing the effects of
* the change. */
)
{
/* Master element. */
if (item == NULL) {
Element_Changed(tree, elem, flags, 0, csM);
return;
}
if (csM & CS_LAYOUT) {
IStyle *style = (IStyle *) TreeItemColumn_GetStyle(tree, column);
int i;
IElementLink *eLink = NULL;
int columnIndex;
if (style == NULL)
panic("Tree_ElementChangedItself but style is NULL\n");
for (i = 0; i < style->master->numElements; i++) {
eLink = &style->elements[i];
if (eLink->elem == elem)
break;
}
if (eLink == NULL)
panic("Tree_ElementChangedItself but eLink is NULL\n");
columnIndex = TreeItemColumn_Index(tree, item, column);
#ifdef CACHE_ELEM_SIZE
eLink->neededWidth = eLink->neededHeight = -1;
#endif
style->neededWidth = style->neededHeight = -1;
if (TreeItem_GetHeader(tree, item) == NULL)
TreeColumns_InvalidateWidthOfItems(tree, Tree_FindColumn(tree, columnIndex));
TreeItemColumn_InvalidateSize(tree, column);
TreeItem_InvalidateHeight(tree, item);
Tree_FreeItemDInfo(tree, item, NULL);
if (TreeItem_GetHeader(tree, item) == NULL)
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
}
else if (csM & CS_DISPLAY) {
int columnIndex;
columnIndex = TreeItemColumn_Index(tree, item, column);
Tree_InvalidateItemDInfo(tree, Tree_FindColumn(tree, columnIndex),
item, NULL);
}
}
void Tree_ElementIterateChanged(TreeIterate iter_, int mask)
{
Iterate *iter = (Iterate *) iter_;
if (mask & CS_LAYOUT) {
#ifdef CACHE_ELEM_SIZE
iter->eLink->neededWidth = iter->eLink->neededHeight = -1;
#endif
iter->style->neededWidth = iter->style->neededHeight = -1;
TreeColumns_InvalidateWidthOfItems(iter->tree,
Tree_FindColumn(iter->tree, iter->columnIndex));
TreeItemColumn_InvalidateSize(iter->tree, iter->column);
TreeItem_InvalidateHeight(iter->tree, iter->item);
Tree_FreeItemDInfo(iter->tree, iter->item, NULL);
Tree_DInfoChanged(iter->tree, DINFO_REDO_RANGES);
}
if (mask & CS_DISPLAY)
Tree_InvalidateItemDInfo(iter->tree, NULL, iter->item, NULL);
}
TreeElement Tree_ElementIterateGet(TreeIterate iter_)
{
Iterate *iter = (Iterate *) iter_;
return iter->eLink->elem;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_TreeChanged --
*
* Called when a TreeCtrl is configured. This handles changes to
* the -font option affecting text elements for example.
*
* Results:
* Calls the changeProc on every master element. Any elements
* affected by the change are eventually redisplayed.
*
* Side effects:
* Display changes.
*
*----------------------------------------------------------------------
*/
void
TreeStyle_TreeChanged(
TreeCtrl *tree, /* Widget info. */
int flagT /* TREE_CONF_xxx flags. */
)
{
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
TreeElement masterElem;
TreeElementArgs args;
int eMask;
if (flagT == 0)
return;
args.tree = tree;
args.change.flagTree = flagT;
args.change.flagMaster = 0;
args.change.flagSelf = 0;
hPtr = Tcl_FirstHashEntry(&tree->elementHash, &search);
while (hPtr != NULL) {
masterElem = (TreeElement) Tcl_GetHashValue(hPtr);
args.elem = masterElem;
eMask = (*masterElem->typePtr->changeProc)(&args);
Element_Changed(tree, masterElem, 0, flagT, eMask);
hPtr = Tcl_NextHashEntry(&search);
}
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_ElementCget --
*
* This procedure is invoked to process the [item element cget]
* widget command. See the user documentation for details on what
* it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_ElementCget(
TreeCtrl *tree, /* Widget info. */
TreeItem item, /* Item containing the element. */
TreeItemColumn column, /* Item-column containing the element. */
TreeStyle style_, /* Style containing the element. */
Tcl_Obj *elemObj, /* Name of the element. */
Tcl_Obj *optionNameObj /* Name of the config option. */
)
{
IStyle *style = (IStyle *) style_;
Tcl_Obj *resultObjPtr = NULL;
TreeElement elem;
IElementLink *eLink;
int inHeader = TreeItem_GetHeader(tree, item) != NULL;
if (TreeElement_FromObj(tree, elemObj, &elem) != TCL_OK)
return TCL_ERROR;
eLink = IStyle_FindElem(tree, style, elem, NULL);
if ((eLink != NULL) && (eLink->elem == elem)) {
int index = TreeItemColumn_Index(tree, item, column);
TreeColumn treeColumn = Tree_FindColumn(tree, index);
FormatResult(tree->interp,
"element %s is not configured in %s %s%d column %s%d",
elem->name, inHeader ? "header" : "item",
inHeader ? "" : tree->itemPrefix, TreeItem_GetID(tree, item),
tree->columnPrefix, TreeColumn_GetID(treeColumn));
return TCL_ERROR;
}
if (eLink == NULL) {
FormatResult(tree->interp, "style %s does not use element %s",
style->master->name, elem->name);
return TCL_ERROR;
}
resultObjPtr = Tk_GetOptionValue(tree->interp, (char *) eLink->elem,
eLink->elem->typePtr->optionTable, optionNameObj, tree->tkwin);
if (resultObjPtr == NULL)
return TCL_ERROR;
Tcl_SetObjResult(tree->interp, resultObjPtr);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_ElementConfigure --
*
* This procedure is invoked to process the [item element configure]
* widget command. See the user documentation for details on what
* it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_ElementConfigure(
TreeCtrl *tree, /* Widget info. */
TreeItem item, /* Item containing the element. */
TreeItemColumn column, /* Item-column containing the element. */
TreeStyle style_, /* Style containing the element. */
TreeElement elem, /* Name of the element. */
int objc, /* Number of arguments. */
Tcl_Obj **objv, /* Argument values. */
int *eMask /* Returned CS_xxx flags. */
)
{
IStyle *style = (IStyle *) style_;
IElementLink *eLink;
TreeElementArgs args;
int inHeader = TreeItem_GetHeader(tree, item) != NULL;
(*eMask) = 0;
if (objc <= 1) {
Tcl_Obj *resultObjPtr;
eLink = IStyle_FindElem(tree, style, elem, NULL);
if ((eLink != NULL) && (eLink->elem == elem)) {
int index = TreeItemColumn_Index(tree, item, column);
TreeColumn treeColumn = Tree_FindColumn(tree, index);
FormatResult(tree->interp,
"element %s is not configured in %s %s%d column %s%d",
elem->name, inHeader ? "header" : "item",
inHeader ? "" : tree->itemPrefix, TreeItem_GetID(tree, item),
tree->columnPrefix, TreeColumn_GetID(treeColumn));
return TCL_ERROR;
}
if (eLink == NULL) {
FormatResult(tree->interp, "style %s does not use element %s",
style->master->name, elem->name);
return TCL_ERROR;
}
resultObjPtr = Tk_GetOptionInfo(tree->interp, (char *) eLink->elem,
eLink->elem->typePtr->optionTable,
(objc == 0) ? (Tcl_Obj *) NULL : objv[0],
tree->tkwin);
if (resultObjPtr == NULL)
return TCL_ERROR;
Tcl_SetObjResult(tree->interp, resultObjPtr);
} else {
int isNew;
eLink = Style_CreateElem(tree, item, column, style, elem, &isNew);
if (eLink == NULL) {
FormatResult(tree->interp, "style %s does not use element %s",
style->master->name, elem->name);
return TCL_ERROR;
}
/* Do this before configProc(). If eLink was just allocated and an
* error occurs in configProc() it won't be done */
(*eMask) = 0;
if (isNew) {
#ifdef CACHE_ELEM_SIZE
eLink->neededWidth = eLink->neededHeight = -1;
#endif
style->neededWidth = style->neededHeight = -1;
(*eMask) = CS_DISPLAY | CS_LAYOUT;
}
args.tree = tree;
args.elem = eLink->elem;
args.config.objc = objc;
args.config.objv = objv;
args.config.flagSelf = 0;
args.config.item = item;
args.config.column = column;
if ((*args.elem->typePtr->configProc)(&args) != TCL_OK)
return TCL_ERROR;
args.change.flagSelf = args.config.flagSelf;
args.change.flagTree = 0;
args.change.flagMaster = 0;
(*eMask) |= (*elem->typePtr->changeProc)(&args);
if (!isNew && ((*eMask) & CS_LAYOUT)) {
#ifdef CACHE_ELEM_SIZE
eLink->neededWidth = eLink->neededHeight = -1;
#endif
style->neededWidth = style->neededHeight = -1;
}
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_ElementConfigureFromObj --
*
* This procedure is invoked to process the [item element configure]
* widget command. See the user documentation for details on what
* it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_ElementConfigureFromObj(
TreeCtrl *tree, /* Widget info. */
TreeItem item, /* Item containing the element. */
TreeItemColumn column, /* Item-column containing the element. */
TreeStyle style, /* Style containing the element. */
Tcl_Obj *elemObj, /* Name of the element. */
int objc, /* Number of arguments. */
Tcl_Obj **objv, /* Argument values. */
int *eMask /* Returned CS_xxx flags. */
)
{
TreeElement elem;
(*eMask) = 0;
if (TreeElement_FromObj(tree, elemObj, &elem) != TCL_OK)
return TCL_ERROR;
return TreeStyle_ElementConfigure(tree, item, column, style,
elem, objc, objv, eMask);
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_ElementActual --
*
* This procedure is invoked to process the [item element perstate]
* widget command. See the user documentation for details on what
* it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_ElementActual(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_, /* The style. */
int state, /* STATE_xxx flags. */
Tcl_Obj *elemObj, /* Name of the element. */
Tcl_Obj *optionNameObj /* Name of the config option. */
)
{
IStyle *style = (IStyle *) style_;
TreeElement masterElem;
IElementLink *eLink;
TreeElementArgs args;
if (TreeElement_FromObj(tree, elemObj, &masterElem) != TCL_OK)
return TCL_ERROR;
eLink = IStyle_FindElem(tree, style, masterElem, NULL);
if (eLink == NULL) {
FormatResult(tree->interp, "style %s does not use element %s",
style->master->name, masterElem->name);
return TCL_ERROR;
}
args.tree = tree;
args.elem = eLink->elem;
args.state = state;
args.actual.obj = optionNameObj;
return (*masterElem->typePtr->actualProc)(&args);
}
/*
*----------------------------------------------------------------------
*
* TreeElementCmd --
*
* This procedure is invoked to process the [element]
* widget command. See the user documentation for details on what
* it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*----------------------------------------------------------------------
*/
int
TreeElementCmd(
ClientData clientData, /* Widget info. */
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *CONST objv[] /* Argument values. */
)
{
TreeCtrl *tree = clientData;
static CONST char *commandNames[] = {
"cget", "configure", "create", "delete", "names", "perstate", "type",
(char *) NULL
};
enum {
COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_CREATE, COMMAND_DELETE,
COMMAND_NAMES, COMMAND_PERSTATE, COMMAND_TYPE
};
int index;
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg ...?");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0,
&index) != TCL_OK) {
return TCL_ERROR;
}
switch (index) {
case COMMAND_CGET: {
Tcl_Obj *resultObjPtr = NULL;
TreeElement elem;
if (objc != 5) {
Tcl_WrongNumArgs(interp, 3, objv, "name option");
return TCL_ERROR;
}
if (TreeElement_FromObj(tree, objv[3], &elem) != TCL_OK)
return TCL_ERROR;
/* Hack -- allow [cget -statedomain] but not [configure] */
{
int length;
CONST char *s = Tcl_GetStringFromObj(objv[4], &length);
/* FIXME: Check for minimum # unique chars. */
if (strncmp(s, "-statedomain", length) == 0 && length >= 6) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
tree->stateDomain[elem->stateDomain].name, -1));
break;
}
}
resultObjPtr = Tk_GetOptionValue(interp, (char *) elem,
elem->typePtr->optionTable, objv[4], tree->tkwin);
if (resultObjPtr == NULL)
return TCL_ERROR;
Tcl_SetObjResult(interp, resultObjPtr);
break;
}
case COMMAND_CONFIGURE: {
Tcl_Obj *resultObjPtr = NULL;
TreeElement elem;
int eMask;
if (objc < 4) {
Tcl_WrongNumArgs(interp, 3, objv, "name ?option? ?value option value ...?");
return TCL_ERROR;
}
if (TreeElement_FromObj(tree, objv[3], &elem) != TCL_OK)
return TCL_ERROR;
if (objc <= 5) {
resultObjPtr = Tk_GetOptionInfo(interp, (char *) elem,
elem->typePtr->optionTable,
(objc == 4) ? (Tcl_Obj *) NULL : objv[4],
tree->tkwin);
if (resultObjPtr == NULL)
return TCL_ERROR;
Tcl_SetObjResult(interp, resultObjPtr);
} else {
TreeElementArgs args;
args.tree = tree;
args.elem = elem;
args.config.objc = objc - 4;
args.config.objv = objv + 4;
args.config.flagSelf = 0;
args.config.item = NULL;
args.config.column = NULL;
if ((*elem->typePtr->configProc)(&args) != TCL_OK)
return TCL_ERROR;
args.change.flagSelf = args.config.flagSelf;
args.change.flagTree = 0;
args.change.flagMaster = 0;
eMask = (*elem->typePtr->changeProc)(&args);
Element_Changed(tree, elem, args.change.flagSelf, 0, eMask);
}
break;
}
case COMMAND_CREATE: {
char *name;
int length;
int isNew;
TreeElement elem;
TreeElementType *typePtr;
Tcl_HashEntry *hPtr;
if (objc < 5) {
Tcl_WrongNumArgs(interp, 3, objv, "name type ?option value ...?");
return TCL_ERROR;
}
name = Tcl_GetStringFromObj(objv[3], &length);
if (!length)
return TCL_ERROR;
hPtr = Tcl_FindHashEntry(&tree->elementHash, name);
if (hPtr != NULL) {
FormatResult(interp, "element \"%s\" already exists", name);
return TCL_ERROR;
}
if (TreeElement_TypeFromObj(tree, objv[4], &typePtr) != TCL_OK)
return TCL_ERROR;
elem = Element_CreateAndConfig(tree, NULL, NULL, NULL, typePtr, name, objc - 5, objv + 5);
if (elem == NULL)
return TCL_ERROR;
hPtr = Tcl_CreateHashEntry(&tree->elementHash, name, &isNew);
Tcl_SetHashValue(hPtr, elem);
Tcl_SetObjResult(interp, TreeElement_ToObj(elem));
break;
}
case COMMAND_DELETE: {
TreeElement elem;
int i;
for (i = 3; i < objc; i++) {
if (TreeElement_FromObj(tree, objv[i], &elem) != TCL_OK)
return TCL_ERROR;
Element_Deleted(tree, elem);
Element_FreeResources(tree, elem);
}
break;
}
case COMMAND_NAMES: {
Tcl_Obj *listObj;
Tcl_HashSearch search;
Tcl_HashEntry *hPtr;
TreeElement elem;
if (objc != 3) {
Tcl_WrongNumArgs(interp, 3, objv, NULL);
return TCL_ERROR;
}
listObj = Tcl_NewListObj(0, NULL);
hPtr = Tcl_FirstHashEntry(&tree->elementHash, &search);
while (hPtr != NULL) {
elem = (TreeElement) Tcl_GetHashValue(hPtr);
if (!elem->hidden)
Tcl_ListObjAppendElement(interp, listObj, TreeElement_ToObj(elem));
hPtr = Tcl_NextHashEntry(&search);
}
Tcl_SetObjResult(interp, listObj);
break;
}
/* T element perstate E option stateList */
case COMMAND_PERSTATE: {
TreeElement elem;
int states[3];
TreeElementArgs args;
if (objc != 6) {
Tcl_WrongNumArgs(tree->interp, 3, objv,
"element option stateList");
return TCL_ERROR;
}
if (TreeElement_FromObj(tree, objv[3], &elem) != TCL_OK)
return TCL_ERROR;
if (Tree_StateFromListObj(tree, elem->stateDomain, objv[5], states,
SFO_NOT_OFF | SFO_NOT_TOGGLE) != TCL_OK)
return TCL_ERROR;
args.tree = tree;
args.elem = elem;
args.state = states[STATE_OP_ON];
args.actual.obj = objv[4];
return (*elem->typePtr->actualProc)(&args);
}
case COMMAND_TYPE: {
TreeElement elem;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 3, objv, "name");
return TCL_ERROR;
}
if (TreeElement_FromObj(tree, objv[3], &elem) != TCL_OK)
return TCL_ERROR;
Tcl_SetResult(interp, elem->typePtr->name, TCL_STATIC); /* Tk_Uid */
break;
}
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* Style_CreateAndConfig --
*
* Allocate and initialize a master style.
*
* Results:
* Pointer to the new Style, or NULL if an error occurs.
*
* Side effects:
* Memory is allocated.
*
*----------------------------------------------------------------------
*/
static MStyle *
Style_CreateAndConfig(
TreeCtrl *tree, /* Widget info. */
char *name, /* Name of new style. */
int objc, /* Number of config-option arg-value pairs. */
Tcl_Obj *CONST objv[] /* Config-option arg-value pairs. */
)
{
MStyle *style;
int i, objC = 0;
Tcl_Obj *staticObjV[STATIC_SIZE], **objV = staticObjV;
int domain = STATE_DOMAIN_ITEM;
STATIC_ALLOC(objV, Tcl_Obj *, objc);
/* Filter out -statedomain and its value. */
/* FIXME: there is no way to query this. */
for (i = 0; i < objc; i += 2) {
int length;
CONST char *s = Tcl_GetStringFromObj(objv[i], &length);
if (strncmp(s, "-statedomain", length) == 0) {
if (i + 1 == objc) {
FormatResult(tree->interp, "value for \"%s\" missing", s);
STATIC_FREE(objV, Tcl_Obj *, objc);
return NULL;
}
s = Tcl_GetStringFromObj(objv[i + 1], &length);
if (strncmp(s, "header", length) == 0)
domain = STATE_DOMAIN_HEADER;
else if (strncmp(s, "item", length) != 0) {
FormatResult(tree->interp, "unknown state domain \"%s\"", s);
STATIC_FREE(objV, Tcl_Obj *, objc);
return NULL;
}
} else {
objV[objC++] = objv[i];
if (i + 1 < objc)
objV[objC++] = objv[i + 1];
}
}
#ifdef ALLOC_HAX
style = (MStyle *) TreeAlloc_Alloc(tree->allocData, MStyleUid,
sizeof(MStyle));
#else
style = (MStyle *) ckalloc(sizeof(MStyle));
#endif
memset(style, '\0', sizeof(MStyle));
style->name = Tk_GetUid(name);
style->stateDomain = domain;
if (Tk_InitOptions(tree->interp, (char *) style,
tree->styleOptionTable, tree->tkwin) != TCL_OK) {
STATIC_FREE(objV, Tcl_Obj *, objc);
#ifdef ALLOC_HAX
TreeAlloc_Free(tree->allocData, MStyleUid, (char *) style, sizeof(MStyle));
#else
WFREE(style, MStyle);
#endif
return NULL;
}
if (Tk_SetOptions(tree->interp, (char *) style,
tree->styleOptionTable, objC, objV, tree->tkwin,
NULL, NULL) != TCL_OK) {
STATIC_FREE(objV, Tcl_Obj *, objc);
Tk_FreeConfigOptions((char *) style, tree->styleOptionTable, tree->tkwin);
#ifdef ALLOC_HAX
TreeAlloc_Free(tree->allocData, MStyleUid, (char *) style, sizeof(MStyle));
#else
WFREE(style, MStyle);
#endif
return NULL;
}
STATIC_FREE(objV, Tcl_Obj *, objc);
return style;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_ListElements --
*
* Creates a Tcl list with the names of elements in a style.
*
* Results:
* If the style is a master style, the interpreter result holds
* a list of each element in the style. If the style is an
* instance style, the interpreter result holds a list of those
* elements configured for the style (i.e., instance elements).
*
* Side effects:
* Memory is allocated, interpreter result changed.
*
*----------------------------------------------------------------------
*/
void
TreeStyle_ListElements(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_ /* The style. */
)
{
MStyle *masterStyle = (MStyle *) style_;
IStyle *style = (IStyle *) style_;
Tcl_Obj *listObj;
TreeElement elem;
int i, numElements = TreeStyle_NumElements(tree, style_);
if (numElements <= 0)
return;
listObj = Tcl_NewListObj(0, NULL);
for (i = 0; i < numElements; i++) {
if (style->master != NULL) {
elem = style->elements[i].elem;
if (elem->master == NULL)
continue;
} else {
elem = masterStyle->elements[i].elem;
}
Tcl_ListObjAppendElement(tree->interp, listObj, TreeElement_ToObj(elem));
}
Tcl_SetObjResult(tree->interp, listObj);
}
enum {
OPTION_CENTER, OPTION_DETACH, OPTION_DRAW, OPTION_EXPAND, OPTION_HEIGHT,
OPTION_iEXPAND, OPTION_INDENT, OPTION_iPADX, OPTION_iPADY,
OPTION_MAXHEIGHT, OPTION_MAXWIDTH, OPTION_MINHEIGHT, OPTION_MINWIDTH,
OPTION_PADX, OPTION_PADY, OPTION_SQUEEZE, OPTION_STICKY, OPTION_UNION,
OPTION_WIDTH, OPTION_VISIBLE
};
/*
*----------------------------------------------------------------------
*
* LayoutOptionToObj --
*
* Return a Tcl_Obj holding the value of a style layout option
* for an element.
*
* Results:
* Pointer to a new Tcl_Obj or NULL if the option has no value.
*
* Side effects:
* A Tcl_Obj may be allocated.
*
*----------------------------------------------------------------------
*/
static Tcl_Obj *
LayoutOptionToObj(
TreeCtrl *tree, /* Widget info. */
MStyle *style, /* Master style using the element. */
MElementLink *eLink, /* Layout info for the element. */
int option /* OPTION_xxx constant. */
)
{
Tcl_Interp *interp = tree->interp;
switch (option) {
case OPTION_PADX:
return TreeCtrl_NewPadAmountObj(eLink->ePadX);
case OPTION_PADY:
return TreeCtrl_NewPadAmountObj(eLink->ePadY);
case OPTION_iPADX:
return TreeCtrl_NewPadAmountObj(eLink->iPadX);
case OPTION_iPADY:
return TreeCtrl_NewPadAmountObj(eLink->iPadY);
case OPTION_CENTER: {
char flags[2];
int n = 0;
if (eLink->flags & ELF_CENTER_X) flags[n++] = 'x';
if (eLink->flags & ELF_CENTER_Y) flags[n++] = 'y';
if (n)
return Tcl_NewStringObj(flags, n);
break;
}
case OPTION_DETACH:
return Tcl_NewStringObj(IS_DETACH(eLink) ? "yes" : "no", -1);
case OPTION_EXPAND: {
char flags[4];
int n = 0;
if (eLink->flags & ELF_eEXPAND_W) flags[n++] = 'w';
if (eLink->flags & ELF_eEXPAND_N) flags[n++] = 'n';
if (eLink->flags & ELF_eEXPAND_E) flags[n++] = 'e';
if (eLink->flags & ELF_eEXPAND_S) flags[n++] = 's';
if (n)
return Tcl_NewStringObj(flags, n);
break;
}
case OPTION_iEXPAND: {
char flags[6];
int n = 0;
if (eLink->flags & ELF_iEXPAND_X) flags[n++] = 'x';
if (eLink->flags & ELF_iEXPAND_Y) flags[n++] = 'y';
if (eLink->flags & ELF_iEXPAND_W) flags[n++] = 'w';
if (eLink->flags & ELF_iEXPAND_N) flags[n++] = 'n';
if (eLink->flags & ELF_iEXPAND_E) flags[n++] = 'e';
if (eLink->flags & ELF_iEXPAND_S) flags[n++] = 's';
if (n)
return Tcl_NewStringObj(flags, n);
break;
}
case OPTION_INDENT:
return Tcl_NewStringObj((eLink->flags & ELF_INDENT) ? "yes" : "no", -1);
case OPTION_SQUEEZE: {
char flags[2];
int n = 0;
if (eLink->flags & ELF_SQUEEZE_X) flags[n++] = 'x';
if (eLink->flags & ELF_SQUEEZE_Y) flags[n++] = 'y';
if (n)
return Tcl_NewStringObj(flags, n);
break;
}
case OPTION_UNION: {
int i;
Tcl_Obj *objPtr;
if (eLink->onionCount == 0)
break;
objPtr = Tcl_NewListObj(0, NULL);
for (i = 0; i < eLink->onionCount; i++)
Tcl_ListObjAppendElement(interp, objPtr,
TreeElement_ToObj(style->elements[eLink->onion[i]].elem));
return objPtr;
}
case OPTION_MAXHEIGHT: {
if (eLink->maxHeight >= 0)
return Tcl_NewIntObj(eLink->maxHeight);
break;
}
case OPTION_MINHEIGHT: {
if (eLink->minHeight >= 0)
return Tcl_NewIntObj(eLink->minHeight);
break;
}
case OPTION_HEIGHT: {
if (eLink->fixedHeight >= 0)
return Tcl_NewIntObj(eLink->fixedHeight);
break;
}
case OPTION_MAXWIDTH: {
if (eLink->maxWidth >= 0)
return Tcl_NewIntObj(eLink->maxWidth);
break;
}
case OPTION_MINWIDTH: {
if (eLink->minWidth >= 0)
return Tcl_NewIntObj(eLink->minWidth);
break;
}
case OPTION_WIDTH: {
if (eLink->fixedWidth >= 0)
return Tcl_NewIntObj(eLink->fixedWidth);
break;
}
case OPTION_STICKY: {
char flags[4];
int n = 0;
if (eLink->flags & ELF_STICKY_W) flags[n++] = 'w';
if (eLink->flags & ELF_STICKY_N) flags[n++] = 'n';
if (eLink->flags & ELF_STICKY_E) flags[n++] = 'e';
if (eLink->flags & ELF_STICKY_S) flags[n++] = 's';
if (n)
return Tcl_NewStringObj(flags, n);
break;
}
case OPTION_DRAW: {
return eLink->draw.obj;
}
case OPTION_VISIBLE: {
return eLink->visible.obj;
}
}
return NULL;
}
static int
UnionRecursiveCheck(
MStyle *mstyle,
int iElemUnion,
int iElemFind
)
{
int i;
for (i = 0; i < mstyle->elements[iElemUnion].onionCount; i++) {
if (mstyle->elements[iElemUnion].onion[i] == iElemFind)
return 1;
if (UnionRecursiveCheck(mstyle, mstyle->elements[iElemUnion].onion[i], iElemFind))
return 1;
}
return 0;
}
/*
*----------------------------------------------------------------------
*
* StyleLayoutCmd --
*
* This procedure is invoked to process the [style layout]
* widget command. See the user documentation for details on what
* it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*----------------------------------------------------------------------
*/
static int
StyleLayoutCmd(
ClientData clientData, /* Widget info. */
Tcl_Interp *interp, /* The current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *CONST objv[] /* Argument values. */
)
{
TreeCtrl *tree = clientData;
TreeStyle _style;
MStyle *style;
TreeElement elem;
MElementLink saved, *eLink;
int i, index, eIndex;
static CONST char *optionNames[] = {
"-center", "-detach", "-draw", "-expand", "-height", "-iexpand",
"-indent", "-ipadx", "-ipady", "-maxheight", "-maxwidth", "-minheight",
"-minwidth", "-padx", "-pady", "-squeeze", "-sticky", "-union",
"-width", "-visible",
(char *) NULL
};
if (objc < 5) {
Tcl_WrongNumArgs(interp, 3, objv, "name element ?option? ?value? ?option value ...?");
return TCL_ERROR;
}
if (TreeStyle_FromObj(tree, objv[3], &_style) != TCL_OK)
return TCL_ERROR;
style = (MStyle *) _style;
if (TreeElement_FromObj(tree, objv[4], &elem) != TCL_OK)
return TCL_ERROR;
eLink = MStyle_FindElem(tree, style, elem, &eIndex);
if (eLink == NULL) {
FormatResult(interp, "style %s does not use element %s",
style->name, elem->name);
return TCL_ERROR;
}
/* T style layout S E */
if (objc == 5) {
Tcl_Obj *listObj = Tcl_NewListObj(0, NULL);
Tcl_Obj *objPtr;
for (i = 0; optionNames[i] != NULL; i++) {
Tcl_ListObjAppendElement(interp, listObj,
Tcl_NewStringObj(optionNames[i], -1));
objPtr = LayoutOptionToObj(tree, style, eLink, i);
Tcl_ListObjAppendElement(interp, listObj,
objPtr ? objPtr : Tcl_NewObj());
}
Tcl_SetObjResult(interp, listObj);
return TCL_OK;
}
/* T style layout S E option */
if (objc == 6) {
Tcl_Obj *objPtr;
if (Tcl_GetIndexFromObj(interp, objv[5], optionNames, "option",
0, &index) != TCL_OK)
return TCL_ERROR;
objPtr = LayoutOptionToObj(tree, style, eLink, index);
if (objPtr != NULL)
Tcl_SetObjResult(interp, objPtr);
return TCL_OK;
}
saved = *eLink;
for (i = 5; i < objc; i += 2) {
if (i + 2 > objc) {
FormatResult(interp, "value for \"%s\" missing",
Tcl_GetString(objv[i]));
goto badConfig;
}
if (Tcl_GetIndexFromObj(interp, objv[i], optionNames, "option",
0, &index) != TCL_OK) {
goto badConfig;
}
switch (index) {
case OPTION_PADX: {
if (TreeCtrl_GetPadAmountFromObj(interp,
tree->tkwin, objv[i + 1],
&eLink->ePadX[PAD_TOP_LEFT],
&eLink->ePadX[PAD_BOTTOM_RIGHT]) != TCL_OK)
goto badConfig;
break;
}
case OPTION_PADY: {
if (TreeCtrl_GetPadAmountFromObj(interp,
tree->tkwin, objv[i + 1],
&eLink->ePadY[PAD_TOP_LEFT],
&eLink->ePadY[PAD_BOTTOM_RIGHT]) != TCL_OK)
goto badConfig;
break;
}
case OPTION_iPADX: {
if (TreeCtrl_GetPadAmountFromObj(interp,
tree->tkwin, objv[i + 1],
&eLink->iPadX[PAD_TOP_LEFT],
&eLink->iPadX[PAD_BOTTOM_RIGHT]) != TCL_OK)
goto badConfig;
break;
}
case OPTION_iPADY: {
if (TreeCtrl_GetPadAmountFromObj(interp,
tree->tkwin, objv[i + 1],
&eLink->iPadY[PAD_TOP_LEFT],
&eLink->iPadY[PAD_BOTTOM_RIGHT]) != TCL_OK)
goto badConfig;
break;
}
case OPTION_CENTER: {
static const CharFlag charFlags[] = {
{ 'x', ELF_CENTER_X },
{ 'y', ELF_CENTER_Y },
{ 0, 0 }
};
if (Tree_GetFlagsFromObj(tree, objv[i + 1], "center value",
charFlags, &eLink->flags) != TCL_OK) {
goto badConfig;
}
break;
}
case OPTION_DETACH: {
int detach;
if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &detach) != TCL_OK)
goto badConfig;
if (detach)
eLink->flags |= ELF_DETACH;
else
eLink->flags &= ~ELF_DETACH;
break;
}
case OPTION_EXPAND: {
static const CharFlag charFlags[] = {
{ 'n', ELF_eEXPAND_N },
{ 'e', ELF_eEXPAND_E },
{ 's', ELF_eEXPAND_S },
{ 'w', ELF_eEXPAND_W },
{ 0, 0 }
};
if (Tree_GetFlagsFromObj(tree, objv[i + 1], "expand value",
charFlags, &eLink->flags) != TCL_OK) {
goto badConfig;
}
break;
}
case OPTION_iEXPAND: {
static const CharFlag charFlags[] = {
{ 'x', ELF_iEXPAND_X },
{ 'y', ELF_iEXPAND_Y },
{ 'n', ELF_iEXPAND_N },
{ 'e', ELF_iEXPAND_E },
{ 's', ELF_iEXPAND_S },
{ 'w', ELF_iEXPAND_W },
{ 0, 0 }
};
if (Tree_GetFlagsFromObj(tree, objv[i + 1], "iexpand value",
charFlags, &eLink->flags) != TCL_OK) {
goto badConfig;
}
break;
}
case OPTION_INDENT: {
int indent;
if (Tcl_GetBooleanFromObj(interp, objv[i + 1], &indent) != TCL_OK)
goto badConfig;
if (indent)
eLink->flags |= ELF_INDENT;
else
eLink->flags &= ~ELF_INDENT;
break;
}
case OPTION_SQUEEZE: {
static const CharFlag charFlags[] = {
{ 'x', ELF_SQUEEZE_X },
{ 'y', ELF_SQUEEZE_Y },
{ 0, 0 }
};
if (Tree_GetFlagsFromObj(tree, objv[i + 1], "squeeze value",
charFlags, &eLink->flags) != TCL_OK) {
goto badConfig;
}
break;
}
case OPTION_UNION: {
int objc1;
Tcl_Obj **objv1;
int j, k, eIndex2, *onion, count = 0;
if (Tcl_ListObjGetElements(interp, objv[i + 1],
&objc1, &objv1) != TCL_OK)
goto badConfig;
if (objc1 == 0) {
if (eLink->onion != NULL) {
if (eLink->onion != saved.onion)
WCFREE(eLink->onion, int, eLink->onionCount);
eLink->onionCount = 0;
eLink->onion = NULL;
}
break;
}
onion = (int *) ckalloc(sizeof(int) * objc1);
for (j = 0; j < objc1; j++) {
TreeElement elem2;
MElementLink *eLink2;
if (TreeElement_FromObj(tree, objv1[j], &elem2) != TCL_OK) {
ckfree((char *) onion);
goto badConfig;
}
eLink2 = MStyle_FindElem(tree, style, elem2, &eIndex2);
if (eLink2 == NULL) {
ckfree((char *) onion);
FormatResult(interp,
"style %s does not use element %s",
style->name, elem2->name);
goto badConfig;
}
if (eLink == eLink2) {
ckfree((char *) onion);
FormatResult(interp,
"element %s can't form union with itself",
elem2->name);
goto badConfig;
}
if (UnionRecursiveCheck(style, eIndex2, eIndex)) {
ckfree((char *) onion);
FormatResult(interp,
"can't form a recursive union with element %s",
elem2->name);
goto badConfig;
}
/* Silently ignore duplicates */
for (k = 0; k < count; k++) {
if (onion[k] == eIndex2)
break;
}
if (k < count)
continue;
onion[count++] = eIndex2;
}
if ((eLink->onion != NULL) && (eLink->onion != saved.onion))
WCFREE(eLink->onion, int, eLink->onionCount);
if (count == objc1)
eLink->onion = onion;
else {
eLink->onion = (int *) ckalloc(sizeof(int) * count);
for (k = 0; k < count; k++)
eLink->onion[k] = onion[k];
ckfree((char *) onion);
}
eLink->onionCount = count;
break;
}
case OPTION_MAXHEIGHT: {
int height;
if (ObjectIsEmpty(objv[i + 1])) {
eLink->maxHeight = -1;
break;
}
if ((Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1],
&height) != TCL_OK) || (height < 0)) {
FormatResult(interp, "bad screen distance \"%s\"",
Tcl_GetString(objv[i + 1]));
goto badConfig;
}
eLink->maxHeight = height;
break;
}
case OPTION_MINHEIGHT: {
int height;
if (ObjectIsEmpty(objv[i + 1])) {
eLink->minHeight = -1;
break;
}
if ((Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1],
&height) != TCL_OK) || (height < 0)) {
FormatResult(interp, "bad screen distance \"%s\"",
Tcl_GetString(objv[i + 1]));
goto badConfig;
}
eLink->minHeight = height;
break;
}
case OPTION_HEIGHT: {
int height;
if (ObjectIsEmpty(objv[i + 1])) {
eLink->fixedHeight = -1;
break;
}
if ((Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1],
&height) != TCL_OK) || (height < 0)) {
FormatResult(interp, "bad screen distance \"%s\"",
Tcl_GetString(objv[i + 1]));
goto badConfig;
}
eLink->fixedHeight = height;
break;
}
case OPTION_MAXWIDTH: {
int width;
if (ObjectIsEmpty(objv[i + 1])) {
eLink->maxWidth = -1;
break;
}
if ((Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1],
&width) != TCL_OK) || (width < 0)) {
FormatResult(interp, "bad screen distance \"%s\"",
Tcl_GetString(objv[i + 1]));
goto badConfig;
}
eLink->maxWidth = width;
break;
}
case OPTION_MINWIDTH: {
int width;
if (ObjectIsEmpty(objv[i + 1])) {
eLink->minWidth = -1;
break;
}
if ((Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1],
&width) != TCL_OK) || (width < 0)) {
FormatResult(interp, "bad screen distance \"%s\"",
Tcl_GetString(objv[i + 1]));
goto badConfig;
}
eLink->minWidth = width;
break;
}
case OPTION_WIDTH: {
int width;
if (ObjectIsEmpty(objv[i + 1])) {
eLink->fixedWidth = -1;
break;
}
if ((Tk_GetPixelsFromObj(interp, tree->tkwin, objv[i + 1],
&width) != TCL_OK) || (width < 0)) {
FormatResult(interp, "bad screen distance \"%s\"",
Tcl_GetString(objv[i + 1]));
goto badConfig;
}
eLink->fixedWidth = width;
break;
}
case OPTION_STICKY: {
static const CharFlag charFlags[] = {
{ 'n', ELF_STICKY_N },
{ 'e', ELF_STICKY_E },
{ 's', ELF_STICKY_S },
{ 'w', ELF_STICKY_W },
{ 0, 0 }
};
if (Tree_GetFlagsFromObj(tree, objv[i + 1], "sticky value",
charFlags, &eLink->flags) != TCL_OK) {
goto badConfig;
}
break;
}
case OPTION_DRAW:
case OPTION_VISIBLE: {
PerStateInfo *psi, *psiSaved;
if (index == OPTION_DRAW) {
psi = &eLink->draw;
psiSaved = &saved.draw;
} else {
psi = &eLink->visible;
psiSaved = &saved.visible;
}
/* Already configured this once. */
if (psi->obj != NULL && psi->obj != psiSaved->obj) {
PerStateInfo_Free(tree, &pstBoolean, psi);
Tcl_DecrRefCount(psi->obj);
/* First configure. Don't free the saved data. */
} else {
psi->data = NULL;
psi->count = 0;
}
psi->obj = objv[i + 1];
Tcl_IncrRefCount(psi->obj);
if (PerStateInfo_FromObj(tree, style->stateDomain,
TreeStateFromObj, &pstBoolean, psi) != TCL_OK) {
goto badConfig;
}
break;
}
}
}
if (saved.onion && (eLink->onion != saved.onion))
WCFREE(saved.onion, int, saved.onionCount);
if (saved.draw.obj != NULL &&
saved.draw.obj != eLink->draw.obj) {
PerStateInfo_Free(tree, &pstBoolean, &saved.draw);
Tcl_DecrRefCount(saved.draw.obj);
}
if (saved.visible.obj != NULL &&
saved.visible.obj != eLink->visible.obj) {
PerStateInfo_Free(tree, &pstBoolean, &saved.visible);
Tcl_DecrRefCount(saved.visible.obj);
}
Style_Changed(tree, style);
return TCL_OK;
badConfig:
if (eLink->onion && (eLink->onion != saved.onion))
WCFREE(eLink->onion, int, eLink->onionCount);
if (eLink->draw.obj != NULL &&
eLink->draw.obj != saved.draw.obj) {
PerStateInfo_Free(tree, &pstBoolean, &eLink->draw);
Tcl_DecrRefCount(eLink->draw.obj);
}
if (eLink->visible.obj != NULL &&
eLink->visible.obj != saved.visible.obj) {
PerStateInfo_Free(tree, &pstBoolean, &eLink->visible);
Tcl_DecrRefCount(eLink->visible.obj);
}
*eLink = saved;
return TCL_ERROR;
}
/*
*----------------------------------------------------------------------
*
* TreeStyleCmd --
*
* This procedure is invoked to process the [style] widget
* command. See the user documentation for details on what it
* does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*----------------------------------------------------------------------
*/
int
TreeStyleCmd(
ClientData clientData, /* Widget info. */
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *CONST objv[] /* Argument values. */
)
{
TreeCtrl *tree = clientData;
static CONST char *commandNames[] = {
"cget", "configure", "create", "delete", "elements", "layout",
"names", (char *) NULL };
enum {
COMMAND_CGET, COMMAND_CONFIGURE, COMMAND_CREATE, COMMAND_DELETE,
COMMAND_ELEMENTS, COMMAND_LAYOUT, COMMAND_NAMES };
int index;
TreeStyle _style;
MStyle *style;
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv, "command ?arg arg ...?");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[2], commandNames, "command", 0,
&index) != TCL_OK) {
return TCL_ERROR;
}
switch (index) {
case COMMAND_CGET: {
Tcl_Obj *resultObjPtr;
if (objc != 5) {
Tcl_WrongNumArgs(interp, 3, objv, "name option");
return TCL_ERROR;
}
if (TreeStyle_FromObj(tree, objv[3], &_style) != TCL_OK)
return TCL_ERROR;
style = (MStyle *) _style;
/* Hack -- allow [cget -statedomain] but not [configure] */
{
int length;
CONST char *s = Tcl_GetStringFromObj(objv[4], &length);
/* FIXME: Check for minimum # unique chars. */
if (strncmp(s, "-statedomain", length) == 0 && length >= 7) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
tree->stateDomain[style->stateDomain].name, -1));
break;
}
}
resultObjPtr = Tk_GetOptionValue(interp, (char *) style,
tree->styleOptionTable, objv[4], tree->tkwin);
if (resultObjPtr == NULL)
return TCL_ERROR;
Tcl_SetObjResult(interp, resultObjPtr);
break;
}
case COMMAND_CONFIGURE: {
Tcl_Obj *resultObjPtr = NULL;
if (objc < 4) {
Tcl_WrongNumArgs(interp, 3, objv, "name ?option? ?value option value ...?");
return TCL_ERROR;
}
if (TreeStyle_FromObj(tree, objv[3], &_style) != TCL_OK)
return TCL_ERROR;
style = (MStyle *) _style;
if (objc <= 5) {
resultObjPtr = Tk_GetOptionInfo(interp, (char *) style,
tree->styleOptionTable,
(objc == 4) ? (Tcl_Obj *) NULL : objv[4],
tree->tkwin);
if (resultObjPtr == NULL)
return TCL_ERROR;
Tcl_SetObjResult(interp, resultObjPtr);
} else {
if (Tk_SetOptions(tree->interp, (char *) style,
tree->styleOptionTable, objc - 4, objv + 4, tree->tkwin,
NULL, NULL) != TCL_OK)
return TCL_ERROR;
Style_Changed(tree, style);
}
break;
}
case COMMAND_CREATE: {
char *name;
int len;
Tcl_HashEntry *hPtr;
int isNew;
if (objc < 4) {
Tcl_WrongNumArgs(interp, 3, objv, "name ?option value ...?");
return TCL_ERROR;
}
name = Tcl_GetStringFromObj(objv[3], &len);
if (!len) {
FormatResult(interp, "invalid style name \"\"");
return TCL_ERROR;
}
hPtr = Tcl_FindHashEntry(&tree->styleHash, name);
if (hPtr != NULL) {
FormatResult(interp, "style \"%s\" already exists", name);
return TCL_ERROR;
}
style = Style_CreateAndConfig(tree, name, objc - 4, objv + 4);
if (style == NULL)
return TCL_ERROR;
hPtr = Tcl_CreateHashEntry(&tree->styleHash, name, &isNew);
Tcl_SetHashValue(hPtr, style);
Tcl_SetObjResult(interp, TreeStyle_ToObj((TreeStyle) style));
break;
}
case COMMAND_DELETE: {
int i;
if (objc < 3) {
Tcl_WrongNumArgs(interp, 3, objv, "?name ...?");
return TCL_ERROR;
}
for (i = 3; i < objc; i++) {
if (TreeStyle_FromObj(tree, objv[i], &_style) != TCL_OK)
return TCL_ERROR;
Style_Deleted(tree, (MStyle *) _style);
TreeStyle_FreeResources(tree, _style);
}
break;
}
/* T style elements S ?{E ...}? */
case COMMAND_ELEMENTS: {
TreeElement elem, *elemList = NULL;
int i, j, count = 0;
int staticMap[STATIC_SIZE], *map = staticMap;
int listObjc;
Tcl_Obj **listObjv;
if (objc < 4 || objc > 5) {
Tcl_WrongNumArgs(interp, 3, objv, "name ?elementList?");
return TCL_ERROR;
}
if (TreeStyle_FromObj(tree, objv[3], &_style) != TCL_OK)
return TCL_ERROR;
style = (MStyle *) _style;
if (objc == 5) {
if (Tcl_ListObjGetElements(interp, objv[4], &listObjc, &listObjv) != TCL_OK)
return TCL_ERROR;
if (listObjc > 0)
elemList = (TreeElement *) ckalloc(sizeof(TreeElement_) * listObjc);
for (i = 0; i < listObjc; i++) {
if (TreeElement_FromObj(tree, listObjv[i], &elem) != TCL_OK) {
ckfree((char *) elemList);
return TCL_ERROR;
}
if (elem->stateDomain != style->stateDomain) {
FormatResult(interp,
"state domain conflict between style \"%s\" and element \"%s\"",
style->name, elem->name);
ckfree((char *) elemList);
return TCL_ERROR;
}
/* Ignore duplicate elements */
for (j = 0; j < count; j++) {
if (elemList[j] == elem)
break;
}
if (j < count)
continue;
elemList[count++] = elem;
}
STATIC_ALLOC(map, int, count);
for (i = 0; i < count; i++)
map[i] = -1;
/* Reassigning Elements to a Style */
if (style->numElements > 0) {
/* Check each Element */
for (i = 0; i < count; i++) {
/* See if this Element is already used by the Style */
for (j = 0; j < style->numElements; j++) {
if (elemList[i] == style->elements[j].elem) {
/* Preserve it */
map[i] = j;
break;
}
}
}
}
Style_ChangeElements(tree, style, count, elemList, map);
if (elemList != NULL)
ckfree((char *) elemList);
STATIC_FREE(map, int, count);
break;
}
TreeStyle_ListElements(tree, (TreeStyle) style);
break;
}
/* T style layout S E ?option? ?value? ?option value ...? */
case COMMAND_LAYOUT: {
return StyleLayoutCmd(clientData, interp, objc, objv);
}
case COMMAND_NAMES: {
Tcl_Obj *listObj;
Tcl_HashSearch search;
Tcl_HashEntry *hPtr;
listObj = Tcl_NewListObj(0, NULL);
hPtr = Tcl_FirstHashEntry(&tree->styleHash, &search);
while (hPtr != NULL) {
_style = (TreeStyle) Tcl_GetHashValue(hPtr);
if (!((MStyle *)_style)->hidden)
Tcl_ListObjAppendElement(interp, listObj,
TreeStyle_ToObj(_style));
hPtr = Tcl_NextHashEntry(&search);
}
Tcl_SetObjResult(interp, listObj);
break;
}
}
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* Tree_ButtonMaxSize --
*
* Return the maximum possible size of a button in any state. This
* includes the size of the -buttonimage and -buttonbitmap options,
* as well as the theme button and default +/- button.
*
* Results:
* Pixel size >= 0.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
Tree_ButtonMaxSize(
TreeCtrl *tree, /* Widget info. */
int *maxWidth, /* Returned maximum width. */
int *maxHeight /* Returned maximum height. */
)
{
int w, h, width = 0, height = 0;
PerStateImage_MaxSize(tree, &tree->buttonImage, &w, &h);
width = MAX(width, w);
height = MAX(height, h);
PerStateBitmap_MaxSize(tree, &tree->buttonBitmap, &w, &h);
width = MAX(width, w);
height = MAX(height, h);
if (tree->useTheme) {
if (TreeTheme_GetButtonSize(tree, Tk_WindowId(tree->tkwin),
TRUE, &w, &h) == TCL_OK) {
width = MAX(width, w);
height = MAX(height, h);
}
if (TreeTheme_GetButtonSize(tree, Tk_WindowId(tree->tkwin),
FALSE, &w, &h) == TCL_OK) {
width = MAX(width, w);
height = MAX(height, h);
}
}
(*maxWidth) = MAX(width, tree->buttonSize);
(*maxHeight) = MAX(height, tree->buttonSize);
}
/*
*----------------------------------------------------------------------
*
* Tree_ButtonHeight --
*
* Return the size of a button for a certain state.
*
* Results:
* Pixel size >= 0.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Tree_ButtonHeight(
TreeCtrl *tree, /* Widget info. */
int state /* STATE_xxx flags. */
)
{
Tk_Image image;
Pixmap bitmap;
int w, h;
image = PerStateImage_ForState(tree, &tree->buttonImage, state, NULL);
if (image != NULL) {
Tk_SizeOfImage(image, &w, &h);
return h;
}
bitmap = PerStateBitmap_ForState(tree, &tree->buttonBitmap, state, NULL);
if (bitmap != None) {
Tk_SizeOfBitmap(tree->display, bitmap, &w, &h);
return h;
}
if (tree->useTheme &&
TreeTheme_GetButtonSize(tree, Tk_WindowId(tree->tkwin),
(state & STATE_ITEM_OPEN) != 0, &w, &h) == TCL_OK)
return h;
return tree->buttonSize;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_Identify --
*
* Perform hit-testing on a style.
*
* Results:
* The element containing the given point, or NULL.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
TreeElement
TreeStyle_Identify(
StyleDrawArgs *drawArgs, /* Various args. */
int x, /* Window x-coord to hit-test against. */
int y /* Window y-coord to hit-test against. */
)
{
TreeCtrl *tree = drawArgs->tree;
IStyle *style = (IStyle *) drawArgs->style;
MStyle *masterStyle = style->master;
int state = drawArgs->state;
IElementLink *eLink = NULL;
int i, minWidth, minHeight;
struct Layout staticLayouts[STATIC_SIZE], *layouts = staticLayouts;
Style_CheckNeededSize(tree, style, state);
#ifdef CACHE_STYLE_SIZE
minWidth = style->minWidth;
minHeight = style->minHeight;
#else
Style_MinSize(tree, style, state, &minWidth, &minHeight);
#endif
if (drawArgs->width < minWidth + drawArgs->indent)
drawArgs->width = minWidth + drawArgs->indent;
if (drawArgs->height < minHeight)
drawArgs->height = minHeight;
x -= drawArgs->x;
STATIC_ALLOC(layouts, struct Layout, masterStyle->numElements);
Style_DoLayout(drawArgs, layouts, FALSE, __FILE__, __LINE__);
for (i = style->master->numElements - 1; i >= 0; i--) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
eLink = layout->eLink;
if ((x >= layout->x + layout->ePadX[PAD_TOP_LEFT]) && (x < layout->x + layout->ePadX[PAD_TOP_LEFT] + layout->iWidth) &&
(y >= layout->y + layout->ePadY[PAD_TOP_LEFT]) && (y < layout->y + layout->ePadY[PAD_TOP_LEFT] + layout->iHeight)) {
goto done;
}
}
eLink = NULL;
done:
STATIC_FREE(layouts, struct Layout, masterStyle->numElements);
if (eLink != NULL)
return eLink->elem;
return NULL;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_Identify2 --
*
* Return a list of elements overlapping the given area.
*
* Results:
* The names of any elements overlapping the given area are
* appended to the supplied list.
*
* Side effects:
* Memory is allocated.
*
*----------------------------------------------------------------------
*/
void
TreeStyle_Identify2(
StyleDrawArgs *drawArgs, /* Various args. */
int x1, int y1, /* Top-left of area to hit-test. */
int x2, int y2, /* Bottom-right of area to hit-test. */
Tcl_Obj *listObj /* Initialized list object to hold
* the result. */
)
{
TreeCtrl *tree = drawArgs->tree;
IStyle *style = (IStyle *) drawArgs->style;
MStyle *masterStyle = style->master;
int state = drawArgs->state;
IElementLink *eLink;
int i, minWidth, minHeight;
struct Layout staticLayouts[STATIC_SIZE], *layouts = staticLayouts;
Style_CheckNeededSize(tree, style, state);
#ifdef CACHE_STYLE_SIZE
minWidth = style->minWidth;
minHeight = style->minHeight;
#else
Style_MinSize(tree, style, state, &minWidth, &minHeight);
#endif
if (drawArgs->width < minWidth + drawArgs->indent)
drawArgs->width = minWidth + drawArgs->indent;
if (drawArgs->height < minHeight)
drawArgs->height = minHeight;
STATIC_ALLOC(layouts, struct Layout, masterStyle->numElements);
Style_DoLayout(drawArgs, layouts, FALSE, __FILE__, __LINE__);
for (i = style->master->numElements - 1; i >= 0; i--) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
eLink = layout->eLink;
if ((drawArgs->x + layout->x + layout->ePadX[PAD_TOP_LEFT] < x2) &&
(drawArgs->x + layout->x + layout->ePadX[PAD_TOP_LEFT] + layout->iWidth > x1) &&
(drawArgs->y + layout->y + layout->ePadY[PAD_TOP_LEFT] < y2) &&
(drawArgs->y + layout->y + layout->ePadY[PAD_TOP_LEFT] + layout->iHeight > y1)) {
Tcl_ListObjAppendElement(drawArgs->tree->interp, listObj,
Tcl_NewStringObj(eLink->elem->name, -1));
}
}
STATIC_FREE(layouts, struct Layout, masterStyle->numElements);
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_Remap --
*
* The guts of the [item style map] command. See the user
* documentation for details on what it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_Remap(
TreeCtrl *tree, /* Widget info. */
TreeStyle styleFrom_, /* Current instance style. */
TreeStyle styleTo_, /* Master style to "convert" the current
* style to. */
int objc, /* Must be even number. */
Tcl_Obj *CONST objv[] /* Array of old-new element names. */
)
{
IStyle *styleFrom = (IStyle *) styleFrom_;
MStyle *styleTo = (MStyle *) styleTo_;
int i, indexFrom, indexTo;
int staticMap[STATIC_SIZE], *map = staticMap;
IElementLink *eLink;
TreeElement elemFrom, elemTo;
TreeElement staticElemMap[STATIC_SIZE], *elemMap = staticElemMap;
int styleFromNumElements = styleFrom->master->numElements;
int result = TCL_OK;
/* Must be instance */
if ((styleFrom == NULL) || (styleFrom->master == NULL))
return TCL_ERROR;
/* Must be master */
if ((styleTo == NULL) || (styleTo->master != NULL))
return TCL_ERROR;
/* Nothing to do */
if (styleFrom->master == styleTo)
return TCL_OK;
if (objc & 1)
return TCL_ERROR;
STATIC_ALLOC(map, int, styleFromNumElements);
STATIC_ALLOC(elemMap, TreeElement, styleFromNumElements);
for (i = 0; i < styleFromNumElements; i++)
map[i] = -1;
for (i = 0; i < objc; i += 2) {
/* Get the old-style element */
if (TreeElement_FromObj(tree, objv[i], &elemFrom) != TCL_OK) {
result = TCL_ERROR;
goto done;
}
/* Verify the old style uses the element */
if (MStyle_FindElem(tree, styleFrom->master, elemFrom, &indexFrom) == NULL) {
FormatResult(tree->interp, "style %s does not use element %s",
styleFrom->master->name, elemFrom->name);
result = TCL_ERROR;
goto done;
}
/* Get the new-style element */
if (TreeElement_FromObj(tree, objv[i + 1], &elemTo) != TCL_OK) {
result = TCL_ERROR;
goto done;
}
/* Verify the new style uses the element */
if (MStyle_FindElem(tree, styleTo, elemTo, &indexTo) == NULL) {
FormatResult(tree->interp, "style %s does not use element %s",
styleTo->name, elemTo->name);
result = TCL_ERROR;
goto done;
}
/* Must be the same type */
if (elemFrom->typePtr != elemTo->typePtr) {
FormatResult(tree->interp, "can't map element type %s to %s",
elemFrom->typePtr->name, elemTo->typePtr->name);
result = TCL_ERROR;
goto done;
}
/* See if the instance style has any info for this element */
eLink = &styleFrom->elements[indexFrom];
if (eLink->elem->master != NULL) {
map[indexFrom] = indexTo;
elemMap[indexFrom] = eLink->elem;
}
}
for (i = 0; i < styleFromNumElements; i++) {
eLink = &styleFrom->elements[i];
indexTo = map[i];
/* Free info for any Elements not being remapped */
if ((indexTo == -1) && (eLink->elem->master != NULL)) {
elemFrom = eLink->elem->master;
Element_FreeResources(tree, eLink->elem);
eLink->elem = elemFrom;
}
/* Remap this Element */
if (indexTo != -1) {
elemMap[i]->master = styleTo->elements[indexTo].elem;
elemMap[i]->name = styleTo->elements[indexTo].elem->name;
}
}
if (styleFromNumElements != styleTo->numElements) {
#ifdef ALLOC_HAX
if (styleFromNumElements > 0)
TreeAlloc_CFree(tree->allocData, IElementLinkUid,
(char *) styleFrom->elements, sizeof(IElementLink),
styleFromNumElements, ELEMENT_LINK_ROUND);
styleFrom->elements = (IElementLink *) TreeAlloc_CAlloc(tree->allocData,
IElementLinkUid, sizeof(IElementLink), styleTo->numElements,
ELEMENT_LINK_ROUND);
#else
if (styleFromNumElements > 0)
WCFREE(styleFrom->elements, IElementLink, styleFromNumElements);
styleFrom->elements = (IElementLink *) ckalloc(sizeof(IElementLink) *
styleTo->numElements);
#endif
memset(styleFrom->elements, '\0', sizeof(IElementLink) * styleTo->numElements);
}
for (i = 0; i < styleTo->numElements; i++) {
styleFrom->elements[i].elem = styleTo->elements[i].elem;
#ifdef CACHE_ELEM_SIZE
styleFrom->elements[i].neededWidth = -1;
styleFrom->elements[i].neededHeight = -1;
#endif
}
for (i = 0; i < styleFromNumElements; i++) {
indexTo = map[i];
if (indexTo != -1)
styleFrom->elements[indexTo].elem = elemMap[i];
}
styleFrom->master = styleTo;
styleFrom->neededWidth = styleFrom->neededHeight = -1;
done:
STATIC_FREE(map, int, styleFromNumElements);
STATIC_FREE(elemMap, TreeElement, styleFromNumElements);
return result;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_GetSortData --
*
* Called by the [item sort] code. Returns a long, double or
* string value from a text element.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_GetSortData(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_, /* The style. */
int elemIndex, /* Index of a text element, or -1 to use
* the first text element. */
int type, /* SORT_xxx constant. */
long *lv, /* Returned for SORT_LONG. */
double *dv, /* Returned for SORT_DOUBLE. */
char **sv /* Returned for SORT_ASCII or SORT_DICT. */
)
{
IStyle *style = (IStyle *) style_;
IElementLink *eLink = style->elements;
int i;
if (elemIndex == -1) {
for (i = 0; i < style->master->numElements; i++) {
if (ELEMENT_TYPE_MATCHES(eLink->elem->typePtr, &treeElemTypeText))
return TreeElement_GetSortData(tree, eLink->elem, type, lv, dv, sv);
eLink++;
}
} else {
if ((elemIndex < 0) || (elemIndex >= style->master->numElements))
panic("bad elemIndex %d to TreeStyle_GetSortData", elemIndex);
eLink = &style->elements[elemIndex];
if (ELEMENT_TYPE_MATCHES(eLink->elem->typePtr, &treeElemTypeText))
return TreeElement_GetSortData(tree, eLink->elem, type, lv, dv, sv);
}
FormatResult(tree->interp, "can't find text element in style %s",
style->master->name);
return TCL_ERROR;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_GetElemRects --
*
* Return a list of rectangles for specified elements in a style.
*
* Results:
* The number of rects[] written.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_GetElemRects(
StyleDrawArgs *drawArgs, /* Various args. */
int objc, /* Number of element names. */
Tcl_Obj *CONST objv[], /* Array of element names. */
TreeRectangle rects[] /* Returned rectangles. */
)
{
IStyle *style = (IStyle *) drawArgs->style;
MStyle *master = style->master;
int i, j, count = 0, minWidth, minHeight;
struct Layout staticLayouts[STATIC_SIZE], *layouts = staticLayouts;
TreeElement staticElems[STATIC_SIZE], *elems = staticElems;
MElementLink *eLink;
STATIC_ALLOC(elems, TreeElement, objc);
for (j = 0; j < objc; j++) {
if (TreeElement_FromObj(drawArgs->tree, objv[j], &elems[j]) != TCL_OK) {
count = -1;
goto done;
}
eLink = MStyle_FindElem(drawArgs->tree, master, elems[j], NULL);
if (eLink == NULL) {
FormatResult(drawArgs->tree->interp,
"style %s does not use element %s",
master->name, elems[j]->name);
count = -1;
goto done;
}
}
Style_CheckNeededSize(drawArgs->tree, style, drawArgs->state);
#ifdef CACHE_STYLE_SIZE
minWidth = style->minWidth;
minHeight = style->minHeight;
#else
Style_MinSize(drawArgs->tree, style, drawArgs->state, &minWidth, &minHeight);
#endif
if (drawArgs->width < minWidth + drawArgs->indent)
drawArgs->width = minWidth + drawArgs->indent;
if (drawArgs->height < minHeight)
drawArgs->height = minHeight;
STATIC_ALLOC(layouts, struct Layout, master->numElements);
Style_DoLayout(drawArgs, layouts, FALSE, __FILE__, __LINE__);
for (i = master->numElements - 1; i >= 0; i--) {
struct Layout *layout = &layouts[i];
if (IS_HIDDEN(layout))
continue;
if (objc > 0) {
for (j = 0; j < objc; j++)
if (elems[j] == layout->eLink->elem ||
elems[j] == layout->master->elem)
break;
if (j == objc)
continue;
}
rects[count].x = drawArgs->x + layout->x + layout->ePadX[PAD_TOP_LEFT];
rects[count].y = drawArgs->y + layout->y + layout->ePadY[PAD_TOP_LEFT];
if (layout->master->onion == NULL) {
rects[count].x += layout->iPadX[PAD_TOP_LEFT];
rects[count].y += layout->iPadY[PAD_TOP_LEFT];
rects[count].width = layout->useWidth;
rects[count].height = layout->useHeight;
} else {
rects[count].width = layout->iWidth;
rects[count].height = layout->iHeight;
}
count++;
}
STATIC_FREE(layouts, struct Layout, master->numElements);
done:
STATIC_FREE(elems, TreeElement, objc);
return count;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_ChangeState --
*
* Called when the state of an item or item-column changes.
*
* Results:
* A bitmask of CS_DISPLAY and CS_LAYOUT values.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_ChangeState(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_, /* The instance style. */
int state1, /* The previous state. */
int state2 /* The current state. */
)
{
IStyle *style = (IStyle *) style_;
MStyle *masterStyle = style->master;
MElementLink *eLink1;
IElementLink *eLink2;
TreeElementArgs args;
int i, eMask, mask = 0;
int undisplay;
if (state1 == state2)
return 0;
args.tree = tree;
for (i = 0; i < masterStyle->numElements; i++) {
eLink2 = &style->elements[i];
args.elem = eLink2->elem;
args.states.state1 = state1;
args.states.state2 = state2;
args.states.draw1 = args.states.draw2 = TRUE;
args.states.visible1 = args.states.visible2 = TRUE;
eLink1 = &masterStyle->elements[i];
undisplay = FALSE;
eMask = 0;
/* Check for the style layout option -draw changing. */
if (eLink1->draw.count > 0) {
args.states.draw1 = PerStateBoolean_ForState(tree,
&eLink1->draw, state1, NULL) != 0;
args.states.draw2 = PerStateBoolean_ForState(tree,
&eLink1->draw, state2, NULL) != 0;
if (args.states.draw1 != args.states.draw2) {
eMask |= CS_DISPLAY;
if (!args.states.draw2)
undisplay = TRUE;
}
}
/* Check for the style layout option -visible changing. */
if (eLink1->visible.count > 0) {
args.states.visible1 = PerStateBoolean_ForState(tree,
&eLink1->visible, state1, NULL) != 0;
args.states.visible2 = PerStateBoolean_ForState(tree,
&eLink1->visible, state2, NULL) != 0;
/* FIXME: Changing visibility might not change the layout. */
if (args.states.visible1 != args.states.visible2) {
eMask |= CS_DISPLAY | CS_LAYOUT;
if (!args.states.visible2)
undisplay = TRUE;
}
}
/* Tell the element about the state change. */
eMask |= (*args.elem->typePtr->stateProc)(&args);
/* Hack: If a window element becomes hidden, then tell it it is
* not onscreen, otherwise it will never be "drawn" in the
* hidden state. */
if (undisplay && ELEMENT_TYPE_MATCHES(args.elem->typePtr,
&treeElemTypeWindow)) {
args.screen.visible = FALSE;
(*args.elem->typePtr->onScreenProc)(&args);
}
if (eMask) {
#ifdef CACHE_ELEM_SIZE
if (eMask & CS_LAYOUT)
eLink2->neededWidth = eLink2->neededHeight = -1;
#endif
mask |= eMask;
}
}
if (mask & CS_LAYOUT)
style->neededWidth = style->neededHeight = -1;
#ifdef TREECTRL_DEBUG
if (style->neededWidth != -1)
style->neededState = state2;
#endif
return mask;
}
/*
*----------------------------------------------------------------------
*
* Tree_UndefineState --
*
* The guts of the [state undefine] widget command.
*
* Results:
* The undefProc of every element is called to respond to the
* undefined state flag. The size of every element/column/item is
* marked out-of-date regardless of whether the state change
* affected the element.
*
* Side effects:
* Display changes.
*
*----------------------------------------------------------------------
*/
void
Tree_UndefineState(
TreeCtrl *tree, /* Widget info. */
int domain, /* STATE_DOMAIN_XXX index. */
int state /* STATE_xxx flag. */
)
{
TreeItem item;
TreeItemColumn column;
Tcl_HashTable *tablePtr = &tree->itemHash;
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
IElementLink *eLink;
int i, columnIndex;
TreeElementArgs args;
/* Undefine the state for the -draw and -visible style layout
* options for each element of this style. */
hPtr = Tcl_FirstHashEntry(&tree->styleHash, &search);
while (hPtr != NULL) {
MStyle *masterStyle = (MStyle *) Tcl_GetHashValue(hPtr);
for (i = 0; i < masterStyle->numElements; i++) {
MElementLink *eLink1 = &masterStyle->elements[i];
PerStateInfo_Undefine(tree, &pstBoolean, &eLink1->draw, masterStyle->stateDomain, state);
PerStateInfo_Undefine(tree, &pstBoolean, &eLink1->visible, masterStyle->stateDomain, state);
}
hPtr = Tcl_NextHashEntry(&search);
}
args.tree = tree;
args.state = state;
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
if (hPtr == NULL) {
tablePtr = &tree->headerHash;
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
}
while (hPtr != NULL) {
item = (TreeItem) Tcl_GetHashValue(hPtr);
column = TreeItem_GetFirstColumn(tree, item);
columnIndex = 0;
while (column != NULL) {
IStyle *style = (IStyle *) TreeItemColumn_GetStyle(tree, column);
if (style != NULL) {
for (i = 0; i < style->master->numElements; i++) {
eLink = &style->elements[i];
/* Instance element */
if (eLink->elem->master != NULL) {
args.elem = eLink->elem;
(*args.elem->typePtr->undefProc)(&args);
}
#ifdef CACHE_ELEM_SIZE
eLink->neededWidth = eLink->neededHeight = -1;
#endif
}
style->neededWidth = style->neededHeight = -1;
TreeItemColumn_InvalidateSize(tree, column);
}
columnIndex++;
column = TreeItemColumn_GetNext(tree, column);
}
TreeItem_InvalidateHeight(tree, item);
Tree_FreeItemDInfo(tree, item, NULL);
TreeItem_UndefineState(tree, item, state);
hPtr = Tcl_NextHashEntry(&search);
if (hPtr == NULL && tablePtr == &tree->itemHash) {
tablePtr = &tree->headerHash;
hPtr = Tcl_FirstHashEntry(tablePtr, &search);
}
}
TreeColumns_InvalidateWidthOfItems(tree, NULL);
Tree_DInfoChanged(tree, DINFO_REDO_RANGES);
hPtr = Tcl_FirstHashEntry(&tree->elementHash, &search);
while (hPtr != NULL) {
args.elem = (TreeElement) Tcl_GetHashValue(hPtr);
(*args.elem->typePtr->undefProc)(&args);
hPtr = Tcl_NextHashEntry(&search);
}
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_NumElements --
*
* Return the number of elements in a style.
*
* Results:
* The number of... oh nevermind.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_NumElements(
TreeCtrl *tree, /* Widget info. */
TreeStyle style_ /* The style. */
)
{
MStyle *masterStyle = ((MStyle *) style_);
IStyle *style = ((IStyle *) style_);
return (style->master == NULL) ?
masterStyle->numElements :
style->master->numElements;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_IsHeaderStyle --
*
* Determine if a style was created by Tree_MakeHeaderStyle for use
* in column headers.
*
* Results:
* TRUE if the style was created by the widget itself, FALSE if
* it was created by [style create].
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_IsHeaderStyle(
TreeCtrl *tree, /* Widget info. */
TreeStyle style /* The style. */
)
{
#if 1
if (((IStyle *)style)->master != NULL)
style = TreeStyle_GetMaster(tree, style);
/* Faster check. */
return ((MStyle *) style)->hidden;
#else
HeaderStyle *hs;
if (((IStyle *)style)->master != NULL)
style = TreeStyle_GetMaster(tree, style);
for (hs = tree->headerStyle.first; hs != NULL; hs = hs->next) {
if (hs->style == style)
return TRUE;
}
#endif
return FALSE;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_HasHeaderElement --
*
* Determine if a style has a 'header' type element.
*
* Results:
* TRUE if the style has a 'header' type element, FALSE otherwise.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_HasHeaderElement(
TreeCtrl *tree, /* Widget info. */
TreeStyle style /* The style. */
)
{
MStyle *mstyle = (MStyle *) style;
if (mstyle->master != NULL)
mstyle = (MStyle *) mstyle->master;
return mstyle->hasHeaderElem;
}
/*
*----------------------------------------------------------------------
*
* Tree_MakeHeaderStyle --
*
* Returns a master style suitable for use in a column header.
* If a style meeting the requirements exists, it is returned.
* Otherwise, a new style is created with a 'header' element
* and possibly a bitmap, image, and/or text element.
*
* Note that the any styles and elements created by this function
* are hidden from the user.
*
* Results:
* A master style token.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
TreeStyle
Tree_MakeHeaderStyle(
TreeCtrl *tree, /* Widget info. */
HeaderStyleParams *params /* Acts as a unique signature for the
* style. */
)
{
HeaderStyle *hs;
int i, count = 0, map[4], isNew;
char name[64];
MStyle *style;
TreeElement elem, elements[4];
MElementLink *eLink;
Tcl_HashEntry *hPtr;
if (params->image)
params->bitmap = 0;
if (!params->image && !params->bitmap) {
params->imagePadX[0] = params->imagePadX[1] = 0;
params->imagePadY[0] = params->imagePadY[1] = 0;
}
if (!params->text) {
params->textPadX[0] = params->textPadX[1] = 0;
params->textPadY[0] = params->textPadY[1] = 0;
}
for (hs = tree->headerStyle.first; hs != NULL; hs = hs->next) {
if (hs->params.justify != params->justify) continue;
if (hs->params.bitmap != params->bitmap) continue;
if (hs->params.image != params->image) continue;
if (hs->params.text != params->text) continue;
for (i = 0; i < 2; i++) {
if (hs->params.imagePadX[i] != params->imagePadX[i]) break;
if (hs->params.imagePadY[i] != params->imagePadY[i]) break;
if (hs->params.textPadX[i] != params->textPadX[i]) break;
if (hs->params.textPadY[i] != params->textPadY[i]) break;
}
if (i < 2) continue;
break;
}
if (hs != NULL) {
return hs->style;
}
/* Create the master elements shared by all header styles if they don't
* exist. */
if (tree->headerStyle.headerElem == NULL) {
Tcl_Obj *obj = Tcl_NewStringObj("header", -1);
TreeElementType *typePtr;
Tcl_IncrRefCount(obj);
TreeElement_TypeFromObj(tree, obj, &typePtr);
Tcl_DecrRefCount(obj);
sprintf(name, "treectrl_header_elem.header");
elem = Element_CreateAndConfig(tree, NULL, NULL, NULL, typePtr, name,
0, NULL);
elem->hidden = TRUE;
elem->stateDomain = STATE_DOMAIN_HEADER;
hPtr = Tcl_CreateHashEntry(&tree->elementHash, name, &isNew);
Tcl_SetHashValue(hPtr, elem);
tree->headerStyle.headerElem = elem;
}
if (tree->headerStyle.bitmapElem == NULL) {
Tcl_Obj *obj = Tcl_NewStringObj("bitmap", -1);
TreeElementType *typePtr;
Tcl_IncrRefCount(obj);
TreeElement_TypeFromObj(tree, obj, &typePtr);
Tcl_DecrRefCount(obj);
sprintf(name, "treectrl_header_elem.bitmap");
elem = Element_CreateAndConfig(tree, NULL, NULL, NULL, typePtr, name,
0, NULL);
elem->hidden = TRUE;
elem->stateDomain = STATE_DOMAIN_HEADER;
hPtr = Tcl_CreateHashEntry(&tree->elementHash, name, &isNew);
Tcl_SetHashValue(hPtr, elem);
tree->headerStyle.bitmapElem = elem;
}
if (tree->headerStyle.imageElem == NULL) {
Tcl_Obj *obj = Tcl_NewStringObj("image", -1);
TreeElementType *typePtr;
Tcl_IncrRefCount(obj);
TreeElement_TypeFromObj(tree, obj, &typePtr);
Tcl_DecrRefCount(obj);
sprintf(name, "treectrl_header_elem.image");
elem = Element_CreateAndConfig(tree, NULL, NULL, NULL, typePtr, name,
0, NULL);
elem->hidden = TRUE;
elem->stateDomain = STATE_DOMAIN_HEADER;
hPtr = Tcl_CreateHashEntry(&tree->elementHash, name, &isNew);
Tcl_SetHashValue(hPtr, elem);
tree->headerStyle.imageElem = elem;
}
if (tree->headerStyle.textElem == NULL) {
Tcl_Obj *obj = Tcl_NewStringObj("text", -1);
TreeElementType *typePtr;
Tcl_IncrRefCount(obj);
TreeElement_TypeFromObj(tree, obj, &typePtr);
Tcl_DecrRefCount(obj);
sprintf(name, "treectrl_header_elem.text");
elem = Element_CreateAndConfig(tree, NULL, NULL, NULL, typePtr, name,
0, NULL);
elem->hidden = TRUE;
elem->stateDomain = STATE_DOMAIN_HEADER;
hPtr = Tcl_CreateHashEntry(&tree->elementHash, name, &isNew);
Tcl_SetHashValue(hPtr, elem);
tree->headerStyle.textElem = elem;
}
sprintf(name, "treectrl_header_style_%d", tree->headerStyle.nextId);
style = Style_CreateAndConfig(tree, name, 0, NULL);
style->hidden = TRUE;
style->stateDomain = STATE_DOMAIN_HEADER;
hPtr = Tcl_CreateHashEntry(&tree->styleHash, name, &isNew);
Tcl_SetHashValue(hPtr, style);
/* Alloc element list. */
elements[count++] = tree->headerStyle.headerElem;
if (params->bitmap) elements[count++] = tree->headerStyle.bitmapElem;
if (params->image) elements[count++] = tree->headerStyle.imageElem;
if (params->text) elements[count++] = tree->headerStyle.textElem;
map[0] = map[1] = map[2] = map[3] = -1;
MStyle_ChangeElementsAux(tree, style, count, elements, map);
/* Layout. */
eLink = &style->elements[0]; /* header */
if (count > 1) {
eLink->onionCount = count - 1;
eLink->onion = (int *) ckalloc(sizeof(int) * eLink->onionCount);
for (i = 1; i < count; i++)
eLink->onion[i - 1] = i;
eLink->flags |= ELF_iEXPAND; /* wens */
eLink->flags &= ~ELF_INDENT;
} else {
eLink->flags |= ELF_DETACH | ELF_iEXPAND_X | ELF_iEXPAND_Y;
eLink->flags &= ~ELF_INDENT;
}
if (params->bitmap || params->image) {
eLink = &style->elements[1];
for (i = 0; i < 2; i++) {
eLink->ePadX[i] = params->imagePadX[i];
eLink->ePadY[i] = params->imagePadY[i];
}
eLink->flags |= ELF_eEXPAND_NS;
if (params->justify == TK_JUSTIFY_CENTER)
eLink->flags |= ELF_CENTER_X;
else if (params->justify == TK_JUSTIFY_RIGHT)
eLink->flags |= ELF_eEXPAND_W;
}
if (params->text) {
eLink = &style->elements[1 + (params->bitmap || params->image)];
for (i = 0; i < 2; i++) {
eLink->ePadX[i] = params->textPadX[i];
eLink->ePadY[i] = params->textPadY[i];
}
eLink->ePadX[0] = MAX(params->textPadX[0] - params->imagePadX[1], 0);
eLink->flags |= ELF_SQUEEZE_X | ELF_eEXPAND_NS;
if (params->justify == TK_JUSTIFY_CENTER)
eLink->flags |= ELF_CENTER_X;
else if (!(params->bitmap || params->image) && (params->justify == TK_JUSTIFY_RIGHT))
eLink->flags |= ELF_eEXPAND_W;
}
hs = (HeaderStyle *) ckalloc(sizeof(HeaderStyle));
hs->style = (TreeStyle) style;
hs->params = *params;
hs->next = tree->headerStyle.first;
tree->headerStyle.first = hs;
tree->headerStyle.nextId++;
return hs->style;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_InitWidget --
*
* Style-related package initialization.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
TreeStyle_InitWidget(
TreeCtrl *tree /* Widget info. */
)
{
#ifdef TREECTRL_DEBUG
if (STICKY_W != ELF_STICKY_W || STICKY_E != ELF_STICKY_E ||
STICKY_N != ELF_STICKY_N || STICKY_S != ELF_STICKY_S)
panic("STICKY_XYZ != ELF_STICKY_XYZ");
#endif
tree->styleOptionTable = Tk_CreateOptionTable(tree->interp,
styleOptionSpecs);
tree->imageOptionNameObj = Tcl_NewStringObj("-image", -1);
Tcl_IncrRefCount(tree->imageOptionNameObj);
tree->textOptionNameObj = Tcl_NewStringObj("-text", -1);
Tcl_IncrRefCount(tree->textOptionNameObj);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* TreeStyle_FreeWidget --
*
* Free style-related resources for a deleted TreeCtrl.
*
* Results:
* None.
*
* Side effects:
* Memory is freed.
*
*----------------------------------------------------------------------
*/
void
TreeStyle_FreeWidget(
TreeCtrl *tree /* Widget info. */
)
{
Tcl_HashEntry *hPtr;
Tcl_HashSearch search;
TreeElement elem;
TreeStyle style;
while (1) {
hPtr = Tcl_FirstHashEntry(&tree->styleHash, &search);
if (hPtr == NULL)
break;
style = (TreeStyle) Tcl_GetHashValue(hPtr);
TreeStyle_FreeResources(tree, style);
}
while (1) {
hPtr = Tcl_FirstHashEntry(&tree->elementHash, &search);
if (hPtr == NULL)
break;
elem = (TreeElement) Tcl_GetHashValue(hPtr);
Element_FreeResources(tree, elem);
}
Tcl_DeleteHashTable(&tree->elementHash);
Tcl_DeleteHashTable(&tree->styleHash);
Tcl_DecrRefCount(tree->imageOptionNameObj);
Tcl_DecrRefCount(tree->textOptionNameObj);
while (tree->headerStyle.first != NULL) {
HeaderStyle *next = tree->headerStyle.first->next;
ckfree((char *) tree->headerStyle.first);
tree->headerStyle.first = next;
}
}