diff options
Diffstat (limited to 'generic')
-rw-r--r-- | generic/tkTreeColumn.c | 1446 |
1 files changed, 1079 insertions, 367 deletions
diff --git a/generic/tkTreeColumn.c b/generic/tkTreeColumn.c index 9209f47..dade491 100644 --- a/generic/tkTreeColumn.c +++ b/generic/tkTreeColumn.c @@ -7,7 +7,7 @@ * Copyright (c) 2002-2003 Christian Krone * Copyright (c) 2003 ActiveState Corporation * - * RCS: @(#) $Id: tkTreeColumn.c,v 1.19 2005/05/01 01:24:45 treectrl Exp $ + * RCS: @(#) $Id: tkTreeColumn.c,v 1.20 2005/05/10 21:58:43 treectrl Exp $ */ #include "tkTreeCtrl.h" @@ -27,18 +27,18 @@ struct Column int widthHack; /* -widthhack */ Tk_Font tkfont; /* -font */ Tk_Justify justify; /* -justify */ - Tk_3DBorder border; /* -border */ + PerStateInfo border; /* -background */ Tcl_Obj *borderWidthObj; /* -borderwidth */ int borderWidth; /* -borderwidth */ - int relief; /* -relief */ XColor *textColor; /* -textcolor */ int expand; /* -expand */ int squeeze; /* -squeeze */ int visible; /* -visible */ char *tag; /* -tag */ char *imageString; /* -image */ + PerStateInfo arrowBitmap; /* -arrowbitmap */ + PerStateInfo arrowImage; /* -arrowimage */ Pixmap bitmap; /* -bitmap */ - int sunken; /* -sunken */ Tcl_Obj *itemBgObj; /* -itembackground */ int button; /* -button */ Tcl_Obj *textPadXObj; /* -textpadx */ @@ -49,8 +49,10 @@ struct Column int *imagePadX; /* -imagepadx */ Tcl_Obj *imagePadYObj; /* -imagepady */ int *imagePadY; /* -imagepady */ - Tcl_Obj *arrowPadObj; /* -arrowpad */ - int *arrowPad; /* -arrowpad */ + Tcl_Obj *arrowPadXObj; /* -arrowpadx */ + int *arrowPadX; /* -arrowpadx */ + Tcl_Obj *arrowPadYObj; /* -arrowpady */ + int *arrowPadY; /* -arrowpady */ #define ARROW_NONE 0 #define ARROW_UP 1 @@ -83,6 +85,17 @@ struct Column XColor **itemBgColor; GC bitmapGC; Column *next; +#define COL_LAYOUT +#ifdef COL_LAYOUT + TextLayout textLayout; /* multi-line titles */ + int textLayoutWidth; /* width passed to TextLayout_Compute */ + int textLayoutInvalid; +#define TEXT_WRAP_NULL -1 +#define TEXT_WRAP_CHAR 0 +#define TEXT_WRAP_WORD 1 + int textWrap; /* -textwrap */ + int textLines; /* -textlines */ +#endif }; static char *arrowST[] = { "none", "up", "down", (char *) NULL }; @@ -100,28 +113,43 @@ static char *stateST[] = { "normal", "active", "pressed", (char *) NULL }; #define COLU_CONF_TEXT 0x0200 #define COLU_CONF_BITMAP 0x0400 #define COLU_CONF_RANGES 0x0800 +#define COLU_CONF_ARROWBMP 0x1000 +#define COLU_CONF_ARROWIMG 0x2000 +#define COLU_CONF_BG 0x4000 static Tk_OptionSpec columnSpecs[] = { {TK_OPTION_STRING_TABLE, "-arrow", (char *) NULL, (char *) NULL, "none", -1, Tk_Offset(Column, arrow), - 0, (ClientData) arrowST, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, - {TK_OPTION_STRING_TABLE, "-arrowside", (char *) NULL, (char *) NULL, - "right", -1, Tk_Offset(Column, arrowSide), - 0, (ClientData) arrowSideST, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, + 0, (ClientData) arrowST, COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, + {TK_OPTION_STRING, "-arrowbitmap", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(Column, arrowBitmap.obj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_ARROWBMP | + COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, {TK_OPTION_STRING_TABLE, "-arrowgravity", (char *) NULL, (char *) NULL, "left", -1, Tk_Offset(Column, arrowGravity), 0, (ClientData) arrowSideST, COLU_CONF_DISPLAY}, - {TK_OPTION_CUSTOM, "-arrowpad", (char *) NULL, (char *) NULL, - "6", Tk_Offset(Column, arrowPadObj), - Tk_Offset(Column, arrowPad), 0, (ClientData) &PadAmountOption, - COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, + {TK_OPTION_STRING, "-arrowimage", (char *) NULL, (char *) NULL, + (char *) NULL, Tk_Offset(Column, arrowImage.obj), -1, + TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_ARROWIMG | + COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, + {TK_OPTION_CUSTOM, "-arrowpadx", (char *) NULL, (char *) NULL, + "6", Tk_Offset(Column, arrowPadXObj), Tk_Offset(Column, arrowPadX), + 0, (ClientData) &PadAmountOption, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, + {TK_OPTION_CUSTOM, "-arrowpady", (char *) NULL, (char *) NULL, + "0", Tk_Offset(Column, arrowPadYObj), Tk_Offset(Column, arrowPadY), + 0, (ClientData) &PadAmountOption, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, + {TK_OPTION_STRING_TABLE, "-arrowside", (char *) NULL, (char *) NULL, + "right", -1, Tk_Offset(Column, arrowSide), + 0, (ClientData) arrowSideST, COLU_CONF_NWIDTH | COLU_CONF_DISPLAY}, + /* NOTE: -background is a per-state option, so DEF_BUTTON_BG_COLOR + * must be a list of one element */ + {TK_OPTION_STRING, "-background", (char *) NULL, (char *) NULL, + DEF_BUTTON_BG_COLOR, Tk_Offset(Column, border.obj), -1, + 0, (ClientData) DEF_BUTTON_BG_MONO, COLU_CONF_BG | COLU_CONF_DISPLAY}, {TK_OPTION_BITMAP, "-bitmap", (char *) NULL, (char *) NULL, (char *) NULL, -1, Tk_Offset(Column, bitmap), TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_BITMAP | COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, - {TK_OPTION_BORDER, "-background", (char *) NULL, (char *) NULL, - DEF_BUTTON_BG_COLOR, -1, Tk_Offset(Column, border), - 0, (ClientData) DEF_BUTTON_BG_MONO, COLU_CONF_DISPLAY}, {TK_OPTION_PIXELS, "-borderwidth", (char *) NULL, (char *) NULL, "2", Tk_Offset(Column, borderWidthObj), Tk_Offset(Column, borderWidth), 0, (ClientData) NULL, COLU_CONF_TWIDTH | COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, @@ -157,22 +185,16 @@ static Tk_OptionSpec columnSpecs[] = { (char *) NULL, Tk_Offset(Column, minWidthObj), Tk_Offset(Column, minWidth), TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_TWIDTH}, - {TK_OPTION_RELIEF, "-relief", (char *) NULL, (char *) NULL, - "raised", -1, Tk_Offset(Column, relief), - 0, (ClientData) NULL, COLU_CONF_DISPLAY}, {TK_OPTION_BOOLEAN, "-squeeze", (char *) NULL, (char *) NULL, "0", -1, Tk_Offset(Column, squeeze), 0, (ClientData) NULL, COLU_CONF_TWIDTH}, {TK_OPTION_STRING_TABLE, "-state", (char *) NULL, (char *) NULL, - "normal", -1, Tk_Offset(Column, state), - 0, (ClientData) stateST, COLU_CONF_DISPLAY}, + "normal", -1, Tk_Offset(Column, state), 0, (ClientData) stateST, + COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, {TK_OPTION_PIXELS, "-stepwidth", (char *) NULL, (char *) NULL, (char *) NULL, Tk_Offset(Column, stepWidthObj), Tk_Offset(Column, stepWidth), TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_RANGES}, - {TK_OPTION_BOOLEAN, "-sunken", (char *) NULL, (char *) NULL, - "0", -1, Tk_Offset(Column, sunken), - 0, (ClientData) NULL, COLU_CONF_DISPLAY}, {TK_OPTION_STRING, "-tag", (char *) NULL, (char *) NULL, (char *) NULL, -1, Tk_Offset(Column, tag), TK_OPTION_NULL_OK, (ClientData) NULL, COLU_CONF_TAG}, @@ -183,6 +205,12 @@ static Tk_OptionSpec columnSpecs[] = { {TK_OPTION_COLOR, "-textcolor", (char *) NULL, (char *) NULL, DEF_BUTTON_FG, -1, Tk_Offset(Column, textColor), 0, (ClientData) NULL, COLU_CONF_DISPLAY}, +#ifdef COL_LAYOUT + {TK_OPTION_INT, "-textlines", (char *) NULL, (char *) NULL, + "1", -1, Tk_Offset(Column, textLines), + 0, (ClientData) NULL, COLU_CONF_TEXT | COLU_CONF_NWIDTH | + COLU_CONF_NHEIGHT | COLU_CONF_DISPLAY}, +#endif {TK_OPTION_CUSTOM, "-textpadx", (char *) NULL, (char *) NULL, "6", Tk_Offset(Column, textPadXObj), Tk_Offset(Column, textPadX), 0, (ClientData) &PadAmountOption, @@ -283,6 +311,20 @@ TreeColumn TreeColumn_Next(TreeColumn column_) return (TreeColumn) ((Column *) column_)->next; } +static int Column_MakeState(Column *column) +{ + int state = 0; + if (column->state == COLUMN_STATE_NORMAL) + state |= 1L << 0; + else if (column->state == COLUMN_STATE_ACTIVE) + state |= 1L << 1; + else if (column->state == COLUMN_STATE_PRESSED) + state |= 1L << 2; + if (column->arrow == ARROW_UP) + state |= 1L << 3; + return state; +} + static void Column_FreeColors(XColor **colors, int count) { int i; @@ -298,7 +340,70 @@ static void Column_FreeColors(XColor **colors, int count) wipefree((char *) colors, sizeof(XColor *) * count); } -static int Column_Config(Column *column, int objc, Tcl_Obj *CONST objv[]) +static int ColumnStateFromObj(TreeCtrl *tree, Tcl_Obj *obj, int *stateOff, int *stateOn) +{ + Tcl_Interp *interp = tree->interp; + int i, op = STATE_OP_ON, op2, op3, length, state = 0; + char ch0, *string; + CONST char *stateNames[4] = { "normal", "active", "pressed", "up" }; + int states[3]; + + states[STATE_OP_ON] = 0; + states[STATE_OP_OFF] = 0; + states[STATE_OP_TOGGLE] = 0; + + string = Tcl_GetStringFromObj(obj, &length); + if (length == 0) + goto unknown; + ch0 = string[0]; + if (ch0 == '!') { + op = STATE_OP_OFF; + ++string; + ch0 = string[0]; + } else if (ch0 == '~') { + if (1) { + FormatResult(interp, "can't specify '~' for this command"); + return TCL_ERROR; + } + op = STATE_OP_TOGGLE; + ++string; + ch0 = string[0]; + } + for (i = 0; i < 4; i++) { + if ((ch0 == stateNames[i][0]) && !strcmp(string, stateNames[i])) { + state = 1L << i; + break; + } + } + if (state == 0) + goto unknown; + + if (op == STATE_OP_ON) { + op2 = STATE_OP_OFF; + op3 = STATE_OP_TOGGLE; + } + else if (op == STATE_OP_OFF) { + op2 = STATE_OP_ON; + op3 = STATE_OP_TOGGLE; + } else { + op2 = STATE_OP_ON; + op3 = STATE_OP_OFF; + } + states[op2] &= ~state; + states[op3] &= ~state; + states[op] |= state; + + *stateOn |= states[STATE_OP_ON]; + *stateOff |= states[STATE_OP_OFF]; + + return TCL_OK; + +unknown: + FormatResult(interp, "unknown state \"%s\"", string); + return TCL_ERROR; +} + +static int Column_Config(Column *column, int objc, Tcl_Obj *CONST objv[], int createFlag) { TreeCtrl *tree = column->tree; Column saved, *walk; @@ -308,6 +413,7 @@ static int Column_Config(Column *column, int objc, Tcl_Obj *CONST objv[]) int mask; XGCValues gcValues; unsigned long gcMask; +/* int stateOld = Column_MakeState(column), stateNew;*/ for (error = 0; error <= 1; error++) { if (error == 0) { @@ -318,6 +424,33 @@ static int Column_Config(Column *column, int objc, Tcl_Obj *CONST objv[]) continue; } + /* Wouldn't have to do this if Tk_InitOptions() would return + * a mask of configured options like Tk_SetOptions() does. */ + if (createFlag) { + if (column->arrowBitmap.obj != NULL) + mask |= COLU_CONF_ARROWBMP; + if (column->arrowImage.obj != NULL) + mask |= COLU_CONF_ARROWIMG; + if (column->border.obj != NULL) + mask |= COLU_CONF_BG; + if ((column != (Column *) tree->columnTail) && (column->tag != NULL)) + mask |= COLU_CONF_TAG; + if (column->imageString != NULL) + mask |= COLU_CONF_IMAGE; + if (column->itemBgObj != NULL) + mask |= COLU_CONF_ITEMBG; + } + + /* + * Step 1: Save old values + */ + + if (mask & COLU_CONF_ARROWBMP) + PSTSave(&column->arrowBitmap, &saved.arrowBitmap); + if (mask & COLU_CONF_ARROWIMG) + PSTSave(&column->arrowImage, &saved.arrowImage); + if (mask & COLU_CONF_BG) + PSTSave(&column->border, &saved.border); if (mask & COLU_CONF_IMAGE) saved.image = column->image; if (mask & COLU_CONF_ITEMBG) { @@ -325,28 +458,25 @@ static int Column_Config(Column *column, int objc, Tcl_Obj *CONST objv[]) saved.itemBgCount = column->itemBgCount; } - if ((mask & COLU_CONF_TAG) && (column->tag != NULL)) { - if (column == (Column *) tree->columnTail) { - FormatResult(tree->interp, - "can't change tag of tail column"); + /* + * Step 2: Process new values + */ + + if (mask & COLU_CONF_ARROWBMP) { + if (PerStateInfo_FromObj(tree, ColumnStateFromObj, &pstBitmap, + &column->arrowBitmap) != TCL_OK) continue; - } - /* Verify -tag is unique */ - walk = (Column *) tree->columns; - if (!strcmp(column->tag, "tail")) { - FormatResult(tree->interp, "column tag \"%s\" is not unique", - column->tag); + } + + if (mask & COLU_CONF_ARROWIMG) { + if (PerStateInfo_FromObj(tree, ColumnStateFromObj, &pstImage, + &column->arrowImage) != TCL_OK) continue; - } - while (walk != NULL) { - if ((walk != column) && (walk->tag != NULL) && !strcmp(walk->tag, column->tag)) { - FormatResult(tree->interp, "column tag \"%s\" is not unique", - column->tag); - break; - } - walk = walk->next; - } - if (walk != NULL) + } + + if (mask & COLU_CONF_BG) { + if (PerStateInfo_FromObj(tree, ColumnStateFromObj, &pstBorder, + &column->border) != TCL_OK) continue; } @@ -378,13 +508,12 @@ static int Column_Config(Column *column, int objc, Tcl_Obj *CONST objv[]) for (i = 0; i < listObjc; i++) { /* Can specify "" for tree background */ (void) Tcl_GetStringFromObj(listObjv[i], &length); - if (length != 0) - { - colors[i] = Tk_AllocColorFromObj(tree->interp, - tree->tkwin, listObjv[i]); - if (colors[i] == NULL) - break; - } + if (length != 0) { + colors[i] = Tk_AllocColorFromObj(tree->interp, + tree->tkwin, listObjv[i]); + if (colors[i] == NULL) + break; + } } if (i < listObjc) { Column_FreeColors(colors, listObjc); @@ -395,22 +524,47 @@ static int Column_Config(Column *column, int objc, Tcl_Obj *CONST objv[]) } } - if (mask & COLU_CONF_IMAGE) { - if (saved.image != NULL) - Tk_FreeImage(saved.image); - } - if (mask & COLU_CONF_ITEMBG) { - Column_FreeColors(saved.itemBgColor, saved.itemBgCount); - - /* Set max -itembackground */ - tree->columnBgCnt = 0; + if ((mask & COLU_CONF_TAG) && (column->tag != NULL)) { + if (column == (Column *) tree->columnTail) { + FormatResult(tree->interp, + "can't change tag of tail column"); + continue; + } + if (!strcmp(column->tag, "tail")) { + FormatResult(tree->interp, "column tag \"%s\" is not unique", + column->tag); + continue; + } + /* Verify -tag is unique */ walk = (Column *) tree->columns; while (walk != NULL) { - if (walk->itemBgCount > tree->columnBgCnt) - tree->columnBgCnt = walk->itemBgCount; + if ((walk != column) && (walk->tag != NULL) && !strcmp(walk->tag, column->tag)) { + FormatResult(tree->interp, "column tag \"%s\" is not unique", + column->tag); + break; + } walk = walk->next; } + if (walk != NULL) + continue; } + + /* + * Step 3: Free saved values + */ + + if (mask & COLU_CONF_ARROWBMP) + PerStateInfo_Free(tree, &pstBitmap, &saved.arrowBitmap); + if (mask & COLU_CONF_ARROWIMG) + PerStateInfo_Free(tree, &pstImage, &saved.arrowImage); + if (mask & COLU_CONF_BG) + PerStateInfo_Free(tree, &pstBorder, &saved.border); + if (mask & COLU_CONF_IMAGE) { + if (saved.image != NULL) + Tk_FreeImage(saved.image); + } + if (mask & COLU_CONF_ITEMBG) + Column_FreeColors(saved.itemBgColor, saved.itemBgCount); Tk_FreeSavedOptions(&savedOptions); break; } else { @@ -418,6 +572,15 @@ static int Column_Config(Column *column, int objc, Tcl_Obj *CONST objv[]) Tcl_IncrRefCount(errorResult); Tk_RestoreSavedOptions(&savedOptions); + if (mask & COLU_CONF_ARROWBMP) + PSTRestore(tree, &pstBitmap, &column->arrowBitmap, + &saved.arrowBitmap); + if (mask & COLU_CONF_ARROWIMG) + PSTRestore(tree, &pstImage, &column->arrowImage, + &saved.arrowImage); + if (mask & COLU_CONF_BG) + PSTRestore(tree, &pstBorder, &column->border, + &saved.border); if (mask & COLU_CONF_IMAGE) { if (column->image != NULL) Tk_FreeImage(column->image); @@ -435,6 +598,15 @@ static int Column_Config(Column *column, int objc, Tcl_Obj *CONST objv[]) } } + /* Wouldn't have to do this if Tk_InitOptions() would return + * a mask of configured options like Tk_SetOptions() does. */ + if (createFlag) { + if (column->textObj != NULL) + mask |= COLU_CONF_TEXT; + if (column->bitmap != None) + mask |= COLU_CONF_BITMAP; + } + if (mask & COLU_CONF_TEXT) { if (column->textObj != NULL) (void) Tcl_GetStringFromObj(column->textObj, &column->textLen); @@ -460,8 +632,101 @@ static int Column_Config(Column *column, int objc, Tcl_Obj *CONST objv[]) } } - if (mask & COLU_CONF_ITEMBG) + if (mask & COLU_CONF_ITEMBG) { + /* Set max -itembackground */ + tree->columnBgCnt = 0; + walk = (Column *) tree->columns; + while (walk != NULL) { + if (walk->itemBgCount > tree->columnBgCnt) + tree->columnBgCnt = walk->itemBgCount; + walk = walk->next; + } + if (createFlag) + if (column->itemBgCount > tree->columnBgCnt) + tree->columnBgCnt = column->itemBgCount; Tree_DInfoChanged(tree, DINFO_INVALIDATE | DINFO_OUT_OF_DATE); + } + +#if 0 + stateNew = Column_MakeState(column); + if (stateOld != stateNew) { + int csMask = 0; + if (column->arrowImage.obj != NULL) { + Tk_Image image1, image2; + Pixmap bitmap1, bitmap2; + int arrowUp, arrowDown; + int w1, h1, w2, h2; + void *ptr1 = NULL, *ptr2 = NULL; + + /* image > bitmap > theme > draw */ + image1 = PerStateImage_ForState(tree, &column->arrowImage, stateOld, NULL); + if (image1 != NULL) { + Tk_SizeOfImage(image1, &w1, &h1); + ptr1 = image1; + } + if (ptr1 == NULL) { + bitmap1 = PerStateBitmap_ForState(tree, &column->arrowBitmap, stateOld, NULL); + if (bitmap1 != None) { + Tk_SizeOfBitmap(tree->display, bitmap1, &w1, &h1); + ptr1 = (void *) bitmap1; + } + } + if (ptr1 == NULL) { + w1 = h1 = 1; /* any value will do */ + ptr1 = (stateOld & (1L << 3)) ? &arrowUp : &arrowDown; + } + + /* image > bitmap > theme > draw */ + image2 = PerStateImage_ForState(tree, &column->arrowImage, stateNew, NULL); + if (image2 != NULL) { + Tk_SizeOfImage(image2, &w2, &h2); + ptr2 = image2; + } + if (ptr2 == NULL) { + bitmap2 = PerStateBitmap_ForState(tree, &column->arrowBitmap, stateNew, NULL); + if (bitmap2 != None) { + Tk_SizeOfBitmap(tree->display, bitmap2, &w2, &h2); + ptr2 = (void *) bitmap2; + } + } + if (ptr2 == NULL) { + w2 = h2 = 1; /* any value will do */ + ptr2 = (stateNew & (1L << 3)) ? &arrowUp : &arrowDown; + } + + if ((w1 != w2) || (h1 != h2)) { + csMask |= CS_LAYOUT | CS_DISPLAY; + } else if (ptr1 != ptr2) { + csMask |= CS_DISPLAY; + } + } + if (1 /* column->border.obj != NULL */ /* NEVER NULL */) { + Tk_3DBorder border1, border2; + border1 = PerStateBorder_ForState(tree, &column->border, + stateOld, NULL); + border2 = PerStateBorder_ForState(tree, &column->border, + stateNew, NULL); + if (border1 != border2) { + csMask |= CS_DISPLAY; + } + } + if (csMask & CS_LAYOUT) + mask |= COLU_CONF_NWIDTH | COLU_CONF_NHEIGHT; + if (csMask & CS_DISPLAY) + mask |= COLU_CONF_DISPLAY; +#ifdef THEME + if (tree->useTheme) + mask |= COLU_CONF_DISPLAY; /* assume no size changes */ +#endif + } +#endif + +#ifdef COL_LAYOUT + if (mask & (COLU_CONF_NWIDTH | COLU_CONF_TWIDTH)) + mask |= COLU_CONF_NHEIGHT; + if (mask & (COLU_CONF_JUSTIFY | COLU_CONF_TEXT)) + column->textLayoutInvalid = TRUE; +#endif if (mask & COLU_CONF_NWIDTH) column->neededWidth = -1; @@ -529,6 +794,10 @@ static Column *Column_Free(Column *column) Tk_FreeGC(tree->display, column->bitmapGC); if (column->image != NULL) Tk_FreeImage(column->image); +#ifdef COL_LAYOUT + if (column->textLayout != NULL) + TextLayout_Free(column->textLayout); +#endif Tk_FreeConfigOptions((char *) column, column->optionTable, tree->tkwin); WFREE(column, Column); tree->columnCount--; @@ -550,11 +819,383 @@ int TreeColumn_StepWidth(TreeColumn column_) return ((Column *) column_)->stepWidthObj ? ((Column *) column_)->stepWidth : -1; } +#ifdef COL_LAYOUT +static void Column_UpdateTextLayout(Column *column, int width) +{ + Tk_Font tkfont; + char *text = column->text; + int textLen = column->textLen; + int justify = column->justify; + int maxLines = MAX(column->textLines, 0); /* -textlines */ + int wrap = TEXT_WRAP_WORD; /* -textwrap */ + int flags = 0; + int i, multiLine = FALSE; + + if (column->textLayout != NULL) { + TextLayout_Free(column->textLayout); + column->textLayout = NULL; + } + + if ((text == NULL) || (textLen == 0)) + return; + + for (i = 0; i < textLen; i++) { + if ((text[i] == '\n') || (text[i] == '\r')) { + multiLine = TRUE; + break; + } + } + + if (!multiLine && ((maxLines == 1) || (!width || (width >= column->textWidth)))) + return; + + tkfont = column->tkfont ? column->tkfont : column->tree->tkfont; + + if (wrap == TEXT_WRAP_WORD) + flags |= TK_WHOLE_WORDS; + + column->textLayout = TextLayout_Compute(tkfont, text, + Tcl_NumUtfChars(text, textLen), width, justify, maxLines, flags); +} +#endif + +static void Column_GetArrowSize(Column *column, int *widthPtr, int *heightPtr) +{ + TreeCtrl *tree = column->tree; + int state = Column_MakeState(column); + int arrowWidth = -1, arrowHeight; + Tk_Image image; + Pixmap bitmap; + + /* image > bitmap > theme > draw */ + image = PerStateImage_ForState(tree, &column->arrowImage, + state, NULL); + if (image != NULL) { + Tk_SizeOfImage(image, &arrowWidth, &arrowHeight); + } + if (arrowWidth == -1) { + bitmap = PerStateBitmap_ForState(tree, &column->arrowBitmap, + state, NULL); + if (bitmap != None) { + Tk_SizeOfBitmap(tree->display, bitmap, &arrowWidth, &arrowHeight); + } + } +#ifdef THEME + /* FIXME: theme size */ +#endif + if (arrowWidth == -1) { + Tk_Font tkfont = column->tkfont ? column->tkfont : tree->tkfont; + Tk_FontMetrics fm; + Tk_GetFontMetrics(tkfont, &fm); + arrowWidth = (fm.linespace + column->textPadY[PAD_TOP_LEFT] + + column->textPadY[PAD_BOTTOM_RIGHT] + column->borderWidth * 2) / 2; + if (!(arrowWidth & 1)) + arrowWidth--; + arrowHeight = arrowWidth; + } + + (*widthPtr) = arrowWidth; + (*heightPtr) = arrowHeight; +} + +struct Layout +{ + Tk_Font tkfont; + Tk_FontMetrics fm; + int width; /* Provided by caller */ + int height; /* Provided by caller */ + int textLeft; + int textWidth; + int bytesThatFit; + int imageLeft; + int imageWidth; + int arrowLeft; + int arrowWidth; + int arrowHeight; +}; + +struct LayoutPart +{ + int padX[2]; + int padY[2]; + int width; + int height; + int left; + int top; +}; + +static void Column_DoLayoutH(Column *column, struct Layout *layout) +{ + struct LayoutPart *parts[3]; + struct LayoutPart partArrow, partImage, partText; + int i, padList[4], widthList[3], n = 0; + int iArrow = -1, iImage = -1, iText = -1; + int left, right; + int widthForText = 0; + + if (column->arrow != ARROW_NONE) { + Column_GetArrowSize(column, &partArrow.width, &partArrow.height); + partArrow.padX[PAD_TOP_LEFT] = column->arrowPadX[PAD_TOP_LEFT]; + partArrow.padX[PAD_BOTTOM_RIGHT] = column->arrowPadX[PAD_BOTTOM_RIGHT]; + partArrow.padY[PAD_TOP_LEFT] = column->arrowPadY[PAD_TOP_LEFT]; + partArrow.padY[PAD_BOTTOM_RIGHT] = column->arrowPadY[PAD_BOTTOM_RIGHT]; + } + if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_LEFT)) { + parts[n] = &partArrow; + padList[n] = partArrow.padX[PAD_TOP_LEFT]; + padList[n + 1] = partArrow.padX[PAD_BOTTOM_RIGHT]; + iArrow = n++; + } + if ((column->image != NULL) || (column->bitmap != None)) { + if (column->image != NULL) + Tk_SizeOfImage(column->image, &partImage.width, &partImage.height); + else + Tk_SizeOfBitmap(column->tree->display, column->bitmap, &partImage.width, &partImage.height); + partImage.padX[PAD_TOP_LEFT] = column->imagePadX[PAD_TOP_LEFT]; + partImage.padX[PAD_BOTTOM_RIGHT] = column->imagePadX[PAD_BOTTOM_RIGHT]; + partImage.padY[PAD_TOP_LEFT] = column->imagePadY[PAD_TOP_LEFT]; + partImage.padY[PAD_BOTTOM_RIGHT] = column->imagePadY[PAD_BOTTOM_RIGHT]; + parts[n] = &partImage; + padList[n] = MAX(partImage.padX[PAD_TOP_LEFT], padList[n]); + padList[n + 1] = partImage.padX[PAD_BOTTOM_RIGHT]; + iImage = n++; + } + if (column->textLen > 0) { + struct LayoutPart *parts2[3]; + int n2 = 0; + + partText.padX[PAD_TOP_LEFT] = column->textPadX[PAD_TOP_LEFT]; + partText.padX[PAD_BOTTOM_RIGHT] = column->textPadX[PAD_BOTTOM_RIGHT]; + partText.padY[PAD_TOP_LEFT] = column->textPadY[PAD_TOP_LEFT]; + partText.padY[PAD_BOTTOM_RIGHT] = column->textPadY[PAD_BOTTOM_RIGHT]; + + /* Calculate space for the text */ + if (iArrow != -1) + parts2[n2++] = &partArrow; + if (iImage != -1) + parts2[n2++] = &partImage; + parts2[n2++] = &partText; + if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_RIGHT)) + parts2[n2++] = &partArrow; + widthForText = layout->width; + for (i = 0; i < n2; i++) { + if (i) + widthForText -= MAX(parts2[i]->padX[0], parts2[i-1]->padX[1]); + else + widthForText -= parts2[i]->padX[0]; + if (parts2[i] != &partText) + widthForText -= parts2[i]->width; + } + widthForText -= parts2[n2-1]->padX[1]; + } + layout->bytesThatFit = 0; + if (widthForText > 0) { +#ifdef COL_LAYOUT + if (column->textLayoutInvalid || (column->textLayoutWidth != widthForText)) { + Column_UpdateTextLayout(column, widthForText); + column->textLayoutInvalid = FALSE; + column->textLayoutWidth = widthForText; + } + if (column->textLayout != NULL) { + TextLayout_Size(column->textLayout, &partText.width, &partText.height); + parts[n] = &partText; + padList[n] = MAX(partText.padX[PAD_TOP_LEFT], padList[n]); + padList[n + 1] = partText.padX[PAD_BOTTOM_RIGHT]; + iText = n++; + } else { +#endif + layout->tkfont = column->tkfont ? column->tkfont : column->tree->tkfont; + Tk_GetFontMetrics(layout->tkfont, &layout->fm); + if (widthForText >= column->textWidth) { + partText.width = column->textWidth; + partText.height = layout->fm.linespace; + layout->bytesThatFit = column->textLen; + } else { + partText.width = widthForText; + partText.height = layout->fm.linespace; + layout->bytesThatFit = Ellipsis(layout->tkfont, column->text, + column->textLen, &partText.width, "...", FALSE); + } + parts[n] = &partText; + padList[n] = MAX(partText.padX[PAD_TOP_LEFT], padList[n]); + padList[n + 1] = partText.padX[PAD_BOTTOM_RIGHT]; + iText = n++; +#ifdef COL_LAYOUT + } +#endif + } + if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_RIGHT)) { + parts[n] = &partArrow; + padList[n] = MAX(partArrow.padX[PAD_TOP_LEFT], padList[n]); + padList[n + 1] = partArrow.padX[PAD_BOTTOM_RIGHT]; + iArrow = n++; + } + + if (n == 0) + return; + + for (i = 0; i < n; i++) { + padList[i] = parts[i]->padX[0]; + if (i) + padList[i] = MAX(padList[i], parts[i-1]->padX[1]); + padList[i + 1] = parts[i]->padX[1]; + widthList[i] = parts[i]->width; + } + if (iText != -1) { + switch (column->justify) { + case TK_JUSTIFY_LEFT: + partText.left = 0; + break; + case TK_JUSTIFY_RIGHT: + partText.left = layout->width; + break; + case TK_JUSTIFY_CENTER: + if (iImage == -1) + partText.left = (layout->width - partText.width) / 2; + else + partText.left = (layout->width - partImage.width - + padList[iText] - partText.width) / 2 + partImage.width + + padList[iText]; + break; + } + } + + if (iImage != -1) { + switch (column->justify) { + case TK_JUSTIFY_LEFT: + partImage.left = 0; + break; + case TK_JUSTIFY_RIGHT: + partImage.left = layout->width; + break; + case TK_JUSTIFY_CENTER: + if (iText == -1) + partImage.left = (layout->width - partImage.width) / 2; + else + partImage.left = (layout->width - partImage.width - + padList[iText] - partText.width) / 2; + break; + } + } + + if (iArrow == -1) + goto finish; + + switch (column->justify) { + case TK_JUSTIFY_LEFT: + switch (column->arrowSide) { + case SIDE_LEFT: + partArrow.left = 0; + break; + case SIDE_RIGHT: + switch (column->arrowGravity) { + case SIDE_LEFT: + partArrow.left = 0; + break; + case SIDE_RIGHT: + partArrow.left = layout->width; + break; + } + break; + } + break; + case TK_JUSTIFY_RIGHT: + switch (column->arrowSide) { + case SIDE_LEFT: + switch (column->arrowGravity) { + case SIDE_LEFT: + partArrow.left = 0; + break; + case SIDE_RIGHT: + partArrow.left = layout->width; + break; + } + break; + case SIDE_RIGHT: + partArrow.left = layout->width; + break; + } + break; + case TK_JUSTIFY_CENTER: + switch (column->arrowSide) { + case SIDE_LEFT: + switch (column->arrowGravity) { + case SIDE_LEFT: + partArrow.left = 0; + break; + case SIDE_RIGHT: + if (n == 3) + partArrow.left = + (layout->width - widthList[1] - padList[2] - + widthList[2]) / 2 - padList[1] - widthList[0]; + else if (n == 2) + partArrow.left = + (layout->width - widthList[1]) / 2 - + padList[1] - widthList[0]; + else + partArrow.left = layout->width; + break; + } + break; + case SIDE_RIGHT: + switch (column->arrowGravity) { + case SIDE_LEFT: + if (n == 3) + partArrow.left = + (layout->width - widthList[0] - padList[1] - + widthList[1]) / 2 + widthList[0] + padList[1] + + widthList[1] + padList[2]; + else if (n == 2) + partArrow.left = + (layout->width - widthList[0]) / 2 + + widthList[0] + padList[1]; + else + partArrow.left = 0; + break; + case SIDE_RIGHT: + partArrow.left = layout->width; + break; + } + break; + } + break; + } + +finish: + right = layout->width - padList[n]; + for (i = n - 1; i >= 0; i--) { + if (parts[i]->left + parts[i]->width > right) + parts[i]->left = right - parts[i]->width; + right -= parts[i]->width + padList[i]; + } + left = padList[0]; + for (i = 0; i < n; i++) { + if (parts[i]->left < left) + parts[i]->left = left; + left += parts[i]->width + padList[i + 1]; + } + + if (iArrow != -1) { + layout->arrowLeft = partArrow.left; + layout->arrowWidth = partArrow.width; + layout->arrowHeight = partArrow.height; + } + if (iImage != -1) { + layout->imageLeft = partImage.left; + layout->imageWidth = partImage.width; + } + if (iText != -1) { + layout->textLeft = partText.left; + layout->textWidth = partText.width; + } +} + int TreeColumn_NeededWidth(TreeColumn column_) { Column *column = (Column *) column_; + TreeCtrl *tree = column->tree; int i, widthList[3], padList[4], n = 0; - int arrowWidth = 0; + int arrowWidth, arrowHeight; if (column->neededWidth >= 0) return column->neededWidth; @@ -562,15 +1203,12 @@ int TreeColumn_NeededWidth(TreeColumn column_) for (i = 0; i < 3; i++) widthList[i] = 0; for (i = 0; i < 4; i++) padList[i] = 0; - if (column->arrow != ARROW_NONE) { - arrowWidth = Tree_HeaderHeight(column->tree) / 2; - if (!(arrowWidth & 1)) - arrowWidth--; - } + if (column->arrow != ARROW_NONE) + Column_GetArrowSize(column, &arrowWidth, &arrowHeight); if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_LEFT)) { widthList[n] = arrowWidth; - padList[n] = column->arrowPad[PAD_TOP_LEFT]; - padList[n + 1] = column->arrowPad[PAD_BOTTOM_RIGHT]; + padList[n] = column->arrowPadX[PAD_TOP_LEFT]; + padList[n + 1] = column->arrowPadX[PAD_BOTTOM_RIGHT]; n++; } if ((column->image != NULL) || (column->bitmap != None)) { @@ -578,7 +1216,7 @@ int TreeColumn_NeededWidth(TreeColumn column_) if (column->image != NULL) Tk_SizeOfImage(column->image, &imgWidth, &imgHeight); else - Tk_SizeOfBitmap(column->tree->display, column->bitmap, &imgWidth, &imgHeight); + Tk_SizeOfBitmap(tree->display, column->bitmap, &imgWidth, &imgHeight); padList[n] = MAX(column->imagePadX[PAD_TOP_LEFT], padList[n]); padList[n + 1] = column->imagePadX[PAD_BOTTOM_RIGHT]; widthList[n] = imgWidth; @@ -587,13 +1225,23 @@ int TreeColumn_NeededWidth(TreeColumn column_) if (column->textLen > 0) { padList[n] = MAX(column->textPadX[PAD_TOP_LEFT], padList[n]); padList[n + 1] = column->textPadX[PAD_BOTTOM_RIGHT]; +#ifdef COL_LAYOUT + if (column->textLayoutInvalid || (column->textLayoutWidth != 0)) { + Column_UpdateTextLayout(column, 0); + column->textLayoutInvalid = FALSE; + column->textLayoutWidth = 0; + } + if (column->textLayout != NULL) + TextLayout_Size(column->textLayout, &widthList[n], NULL); + else +#endif widthList[n] = column->textWidth; n++; } if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_RIGHT)) { widthList[n] = arrowWidth; - padList[n] = column->arrowPad[PAD_TOP_LEFT]; - padList[n + 1] = column->arrowPad[PAD_BOTTOM_RIGHT]; + padList[n] = MAX(column->arrowPadX[PAD_TOP_LEFT], padList[n]); + padList[n + 1] = column->arrowPadX[PAD_BOTTOM_RIGHT]; n++; } @@ -610,6 +1258,7 @@ int TreeColumn_NeededWidth(TreeColumn column_) int TreeColumn_NeededHeight(TreeColumn column_) { Column *column = (Column *) column_; + TreeCtrl *tree = column->tree; #ifdef THEME int margins[4]; #endif @@ -618,27 +1267,50 @@ int TreeColumn_NeededHeight(TreeColumn column_) return column->neededHeight; column->neededHeight = 0; + if (column->arrow != ARROW_NONE) { + int arrowWidth, arrowHeight; + Column_GetArrowSize(column, &arrowWidth, &arrowHeight); + arrowHeight += column->arrowPadY[PAD_TOP_LEFT] + + column->arrowPadY[PAD_BOTTOM_RIGHT]; + column->neededHeight = MAX(column->neededHeight, arrowHeight); + } if ((column->image != NULL) || (column->bitmap != None)) { int imgWidth, imgHeight; if (column->image != NULL) Tk_SizeOfImage(column->image, &imgWidth, &imgHeight); else - Tk_SizeOfBitmap(column->tree->display, column->bitmap, &imgWidth, &imgHeight); + Tk_SizeOfBitmap(tree->display, column->bitmap, &imgWidth, &imgHeight); imgHeight += column->imagePadY[PAD_TOP_LEFT] + column->imagePadY[PAD_BOTTOM_RIGHT]; column->neededHeight = MAX(column->neededHeight, imgHeight); } if (column->text != NULL) { +#ifdef COL_LAYOUT + struct Layout layout; + layout.width = TreeColumn_UseWidth(column_); + layout.height = -1; + Column_DoLayoutH(column, &layout); + if (column->textLayout != NULL) { + int height; + TextLayout_Size(column->textLayout, NULL, &height); + height += column->textPadY[PAD_TOP_LEFT] + + column->textPadY[PAD_BOTTOM_RIGHT]; + column->neededHeight = MAX(column->neededHeight, height); + } else { +#endif Tk_Font tkfont = column->tkfont ? column->tkfont : column->tree->tkfont; Tk_FontMetrics fm; Tk_GetFontMetrics(tkfont, &fm); fm.linespace += column->textPadY[PAD_TOP_LEFT] + column->textPadY[PAD_BOTTOM_RIGHT]; column->neededHeight = MAX(column->neededHeight, fm.linespace); +#ifdef COL_LAYOUT + } +#endif } #ifdef THEME if (column->tree->useTheme && - (TreeTheme_GetHeaderContentMargins(column->tree, column->state, margins) == TCL_OK)) { + (TreeTheme_GetHeaderContentMargins(tree, column->state, margins) == TCL_OK)) { #ifdef WIN32 /* I'm hacking these margins since the default XP theme does not give * reasonable ContentMargins for HP_HEADERITEM */ @@ -801,7 +1473,7 @@ int TreeColumnCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_SetObjResult(interp, resultObjPtr); break; } - return Column_Config(column, objc - 4, objv + 4); + return Column_Config(column, objc - 4, objv + 4, FALSE); } case COMMAND_CREATE: @@ -810,7 +1482,7 @@ int TreeColumnCmd(ClientData clientData, Tcl_Interp *interp, int objc, Column *column; column = Column_Alloc(tree); - if (Column_Config(column, objc - 3, objv + 3) != TCL_OK) + if (Column_Config(column, objc - 3, objv + 3, TRUE) != TCL_OK) { Column_Free(column); return TCL_ERROR; @@ -1087,254 +1759,149 @@ int TreeColumnCmd(ClientData clientData, Tcl_Interp *interp, int objc, return TCL_OK; } -struct Layout -{ - Tk_Font tkfont; - Tk_FontMetrics fm; - int width; /* Provided by caller */ - int height; /* Provided by caller */ - int textLeft; - int textWidth; - int bytesThatFit; - int imageLeft; - int imageWidth; - int arrowLeft; - int arrowWidth; - int arrowHeight; -}; - -static void Column_Layout(Column *column, struct Layout *layout) +static void Column_DrawArrow(Column *column, Drawable drawable, int x, int y, + struct Layout layout) { - int i, leftList[3], widthList[3], padList[4], n = 0; - int iArrow = -1, iImage = -1, iText = -1; - int left, right; - - for (i = 0; i < 3; i++) leftList[i] = 0; - for (i = 0; i < 3; i++) widthList[i] = 0; - for (i = 0; i < 4; i++) padList[i] = 0; + TreeCtrl *tree = column->tree; + int height = tree->headerHeight; + int sunken = column->state == COLUMN_STATE_PRESSED; + Tk_Image image = NULL; + Pixmap bitmap; + Tk_3DBorder border; + int state = Column_MakeState(column); + int arrowPadY = column->arrowPadY[PAD_TOP_LEFT] + + column->arrowPadY[PAD_BOTTOM_RIGHT]; + + if (column->arrow == ARROW_NONE) + return; - if (column->arrow != ARROW_NONE) { - layout->arrowWidth = Tree_HeaderHeight(column->tree) / 2; - if (!(layout->arrowWidth & 1)) - layout->arrowWidth--; - layout->arrowHeight = layout->arrowWidth; - } - if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_LEFT)) { - widthList[n] = layout->arrowWidth; - padList[n] = column->arrowPad[PAD_TOP_LEFT]; - padList[n + 1] = column->arrowPad[PAD_BOTTOM_RIGHT]; - iArrow = n++; - } - if ((column->image != NULL) || (column->bitmap != None)) { - int imgWidth, imgHeight; - if (column->image != NULL) - Tk_SizeOfImage(column->image, &imgWidth, &imgHeight); - else - Tk_SizeOfBitmap(column->tree->display, column->bitmap, &imgWidth, &imgHeight); - padList[n] = MAX(column->imagePadX[PAD_TOP_LEFT], padList[n]); - padList[n + 1] = column->imagePadX[PAD_BOTTOM_RIGHT]; - widthList[n] = imgWidth; - layout->imageWidth = imgWidth; - iImage = n++; - } - if (column->textLen > 0) { - layout->tkfont = column->tkfont ? column->tkfont : column->tree->tkfont; - Tk_GetFontMetrics(layout->tkfont, &layout->fm); - layout->bytesThatFit = 0; - if (layout->width >= TreeColumn_NeededWidth((TreeColumn) column)) { - padList[n] = MAX(column->textPadX[PAD_TOP_LEFT], - padList[n]); - padList[n + 1] = column->textPadX[PAD_BOTTOM_RIGHT]; - widthList[n] = column->textWidth; - iText = n++; - layout->bytesThatFit = column->textLen; - layout->textWidth = column->textWidth; - } else { - int width = column->neededWidth - layout->width; - if (width < column->textWidth) { - width = column->textWidth - width; - layout->bytesThatFit = Ellipsis(layout->tkfont, column->text, - column->textLen, &width, "..."); - padList[n] = MAX(column->textPadX[PAD_TOP_LEFT], - padList[n]); - padList[n + 1] = column->textPadX[PAD_BOTTOM_RIGHT]; - widthList[n] = width; - iText = n++; - layout->textWidth = width; - } - } - } - if ((column->arrow != ARROW_NONE) && (column->arrowSide == SIDE_RIGHT)) { - widthList[n] = layout->arrowWidth; - padList[n] = column->arrowPad[PAD_TOP_LEFT]; - padList[n + 1] = column->arrowPad[PAD_BOTTOM_RIGHT]; - iArrow = n++; + image = PerStateImage_ForState(tree, &column->arrowImage, state, NULL); + if (image != NULL) { + Tk_RedrawImage(image, 0, 0, layout.arrowWidth, layout.arrowHeight, + drawable, + x + layout.arrowLeft + sunken, + y + (height - (layout.arrowHeight + arrowPadY)) / 2 + sunken); + return; } - if (n == 0) + bitmap = PerStateBitmap_ForState(tree, &column->arrowBitmap, state, NULL); + if (bitmap != None) { + int bx, by; + XGCValues gcValues; + GC gc; + gcValues.clip_mask = bitmap; + gcValues.graphics_exposures = False; + gc = Tk_GetGC(tree->tkwin, GCClipMask | GCGraphicsExposures, &gcValues); + bx = x + layout.arrowLeft + sunken; + by = y + (height - (layout.arrowHeight + arrowPadY)) / 2 + sunken; + XSetClipOrigin(tree->display, gc, bx, by); + XCopyPlane(tree->display, bitmap, drawable, gc, + 0, 0, + (unsigned int) layout.arrowWidth, (unsigned int) layout.arrowHeight, + bx, by, 1); + Tk_FreeGC(tree->display, gc); return; + } - if (iText != -1) { - switch (column->justify) { - case TK_JUSTIFY_LEFT: - leftList[iText] = 0; - break; - case TK_JUSTIFY_RIGHT: - leftList[iText] = layout->width; - break; - case TK_JUSTIFY_CENTER: - if (iImage == -1) - leftList[iText] = (layout->width - widthList[iText]) / 2; - else - leftList[iText] = (layout->width - widthList[iImage] - - padList[iText] - widthList[iText]) / 2 + widthList[iImage] + - padList[iText]; - break; - } +#ifdef THEME + if (tree->useTheme) { + if (TreeTheme_DrawHeaderArrow(tree, drawable, + column->arrow == ARROW_UP, x + layout.arrowLeft, + y + (height - (layout.arrowHeight + arrowPadY)) / 2, layout.arrowWidth, + layout.arrowHeight) == TCL_OK) + return; } +#endif - if (iImage != -1) { - switch (column->justify) { - case TK_JUSTIFY_LEFT: - leftList[iImage] = 0; - break; - case TK_JUSTIFY_RIGHT: - leftList[iImage] = layout->width; + if (1) { + int arrowWidth = layout.arrowWidth; + int arrowHeight = layout.arrowHeight; + int arrowTop = y + (height - (layout.arrowHeight + arrowPadY)) / 2 + column->arrowPadY[PAD_TOP_LEFT]; + int arrowBottom = arrowTop + arrowHeight; + XPoint points[5]; + int color1 = 0, color2 = 0; + int i; + + switch (column->arrow) { + case ARROW_UP: + points[0].x = x + layout.arrowLeft; + points[0].y = arrowBottom - 1; + points[1].x = x + layout.arrowLeft + arrowWidth / 2; + points[1].y = arrowTop - 1; + color1 = TK_3D_DARK_GC; + points[4].x = x + layout.arrowLeft + arrowWidth / 2; + points[4].y = arrowTop - 1; + points[3].x = x + layout.arrowLeft + arrowWidth - 1; + points[3].y = arrowBottom - 1; + points[2].x = x + layout.arrowLeft; + points[2].y = arrowBottom - 1; + color2 = TK_3D_LIGHT_GC; break; - case TK_JUSTIFY_CENTER: - if (iText == -1) - leftList[iImage] = (layout->width - widthList[iImage]) / 2; - else - leftList[iImage] = (layout->width - widthList[iImage] - - padList[iText] - widthList[iText]) / 2; + case ARROW_DOWN: + points[0].x = x + layout.arrowLeft + arrowWidth - 1; + points[0].y = arrowTop; + points[1].x = x + layout.arrowLeft + arrowWidth / 2; + points[1].y = arrowBottom; + color1 = TK_3D_LIGHT_GC; + points[2].x = x + layout.arrowLeft + arrowWidth - 1; + points[2].y = arrowTop; + points[3].x = x + layout.arrowLeft; + points[3].y = arrowTop; + points[4].x = x + layout.arrowLeft + arrowWidth / 2; + points[4].y = arrowBottom; + color2 = TK_3D_DARK_GC; break; } - } - - if (iArrow == -1) - goto finish; - - switch (column->justify) { - case TK_JUSTIFY_LEFT: - switch (column->arrowSide) { - case SIDE_LEFT: - leftList[iArrow] = 0; - break; - case SIDE_RIGHT: - switch (column->arrowGravity) { - case SIDE_LEFT: - leftList[iArrow] = 0; - break; - case SIDE_RIGHT: - leftList[iArrow] = layout->width; - break; - } - break; - } - break; - case TK_JUSTIFY_RIGHT: - switch (column->arrowSide) { - case SIDE_LEFT: - switch (column->arrowGravity) { - case SIDE_LEFT: - leftList[iArrow] = 0; - break; - case SIDE_RIGHT: - leftList[iArrow] = layout->width; - break; - } - break; - case SIDE_RIGHT: - leftList[iArrow] = layout->width; - break; - } - break; - case TK_JUSTIFY_CENTER: - switch (column->arrowSide) { - case SIDE_LEFT: - switch (column->arrowGravity) { - case SIDE_LEFT: - leftList[iArrow] = 0; - break; - case SIDE_RIGHT: - if (n == 3) - leftList[iArrow] = - (layout->width - widthList[1] - padList[2] - - widthList[2]) / 2 - padList[1] - widthList[0]; - else if (n == 2) - leftList[iArrow] = - (layout->width - widthList[1]) / 2 - - padList[1] - widthList[0]; - else - leftList[iArrow] = - (layout->width - widthList[0]) / 2; - break; - } - break; - case SIDE_RIGHT: - switch (column->arrowGravity) { - case SIDE_LEFT: - if (n == 3) - leftList[iArrow] = - (layout->width - widthList[0] - padList[1] - - widthList[1]) / 2 + widthList[0] + padList[1] + - widthList[1] + padList[2]; - else if (n == 2) - leftList[iArrow] = - (layout->width - widthList[0]) / 2 + - widthList[0] + padList[1]; - else - leftList[iArrow] = - (layout->width - widthList[0]) / 2; - break; - case SIDE_RIGHT: - leftList[iArrow] = layout->width; - break; - } - break; - } - break; - } - - finish: - right = layout->width - padList[n]; - for (i = n - 1; i >= 0; i--) { - if (leftList[i] + widthList[i] > right) - leftList[i] = right - widthList[i]; - right -= widthList[i] + padList[i]; - } - left = padList[0]; - for (i = 0; i < n; i++) { - if (leftList[i] < left) - leftList[i] = left; - - if (i == iArrow) - layout->arrowLeft = leftList[i]; - else if (i == iText) - layout->textLeft = leftList[i]; - else if (i == iImage) - layout->imageLeft = leftList[i]; + for (i = 0; i < 5; i++) { + points[i].x += sunken; + points[i].y += sunken; + } - left += widthList[i] + padList[i + 1]; + border = PerStateBorder_ForState(tree, &column->border, + Column_MakeState(column), NULL); + if (border == NULL) + border = tree->border; + XDrawLines(tree->display, drawable, + Tk_3DBorderGC(tree->tkwin, border, color2), + points + 2, 3, CoordModeOrigin); + XDrawLines(tree->display, drawable, + Tk_3DBorderGC(tree->tkwin, border, color1), + points, 2, CoordModeOrigin); } } -void TreeColumn_Draw(TreeColumn column_, Drawable drawable, int x, int y) +#ifdef COLUMN_DRAG_IMAGE +static void Column_Draw(Column *column, Drawable drawable, int x, int y, int dragImage) +#else +static void Column_Draw(Column *column, Drawable drawable, int x, int y) +#endif { - Column *column = (Column *) column_; TreeCtrl *tree = column->tree; int height = tree->headerHeight; struct Layout layout; int width = column->useWidth; - int relief = column->sunken ? TK_RELIEF_SUNKEN : TK_RELIEF_RAISED; + int sunken = column->state == COLUMN_STATE_PRESSED; + int relief = sunken ? TK_RELIEF_SUNKEN : TK_RELIEF_RAISED; + Tk_3DBorder border; #ifdef THEME int theme = TCL_ERROR; #endif layout.width = width; layout.height = height; - Column_Layout(column, &layout); + Column_DoLayoutH(column, &layout); + border = PerStateBorder_ForState(tree, &column->border, + Column_MakeState(column), NULL); + if (border == NULL) + border = tree->border; + +#ifdef COLUMN_DRAG_IMAGE + if (dragImage) { + GC gc = Tk_GCForColor(tree->columnDrag.color, Tk_WindowId(tree->tkwin)); + XFillRectangle(tree->display, drawable, gc, x, y, width, height); + } else { +#endif #ifdef THEME if (tree->useTheme) { theme = TreeTheme_DrawHeaderItem(tree, drawable, column->state, x, y, @@ -1342,26 +1909,29 @@ void TreeColumn_Draw(TreeColumn column_, Drawable drawable, int x, int y) } if (theme != TCL_OK) #endif - Tk_Fill3DRectangle(tree->tkwin, drawable, column->border, - x, y, width, height, 0, TK_RELIEF_FLAT /* column->borderWidth, relief */); + Tk_Fill3DRectangle(tree->tkwin, drawable, border, + x, y, width, height, 0, TK_RELIEF_FLAT); +#ifdef COLUMN_DRAG_IMAGE + } +#endif if (column->image != NULL) { int imgW, imgH, ix, iy, h; Tk_SizeOfImage(column->image, &imgW, &imgH); - ix = x + layout.imageLeft + column->sunken; + ix = x + layout.imageLeft + sunken; h = column->imagePadY[PAD_TOP_LEFT] + imgH + column->imagePadY[PAD_BOTTOM_RIGHT]; - iy = y + (height - h) / 2 + column->sunken; + iy = y + (height - h) / 2 + sunken; iy += column->imagePadY[PAD_TOP_LEFT]; Tk_RedrawImage(column->image, 0, 0, imgW, imgH, drawable, ix, iy); } else if (column->bitmap != None) { int imgW, imgH, bx, by, h; Tk_SizeOfBitmap(tree->display, column->bitmap, &imgW, &imgH); - bx = x + layout.imageLeft + column->sunken; + bx = x + layout.imageLeft + sunken; h = column->imagePadY[PAD_TOP_LEFT] + imgH + column->imagePadY[PAD_BOTTOM_RIGHT]; - by = y + (height - h) / 2 + column->sunken; + by = y + (height - h) / 2 + sunken; by += column->imagePadY[PAD_TOP_LEFT]; XSetClipOrigin(tree->display, column->bitmapGC, bx, by); XCopyPlane(tree->display, column->bitmap, drawable, column->bitmapGC, @@ -1370,7 +1940,28 @@ void TreeColumn_Draw(TreeColumn column_, Drawable drawable, int x, int y) XSetClipOrigin(tree->display, column->bitmapGC, 0, 0); } - if ((column->text != NULL) && (layout.bytesThatFit > 0)) { +#ifdef COL_LAYOUT + if ((column->text != NULL) && (column->textLayout != NULL)) { + int h; + XGCValues gcValues; + GC gc; + unsigned long mask; + TextLayout_Size(column->textLayout, NULL, &h); + h += column->textPadY[PAD_TOP_LEFT] + column->textPadY[PAD_BOTTOM_RIGHT]; + gcValues.font = Tk_FontId(column->tkfont ? column->tkfont : tree->tkfont); + gcValues.foreground = column->textColor->pixel; + gcValues.graphics_exposures = False; + mask = GCFont | GCForeground | GCGraphicsExposures; + gc = Tk_GetGC(tree->tkwin, mask, &gcValues); + TextLayout_Draw(tree->display, drawable, gc, + column->textLayout, + x + layout.textLeft + sunken, + y + (height - h) / 2 + column->textPadY[PAD_TOP_LEFT] + sunken, + 0, -1); + Tk_FreeGC(tree->display, gc); + } else +#endif + if ((column->text != NULL) && (layout.bytesThatFit != 0)) { XGCValues gcValues; GC gc; unsigned long mask; @@ -1396,10 +1987,10 @@ void TreeColumn_Draw(TreeColumn column_, Drawable drawable, int x, int y) gcValues.graphics_exposures = False; mask = GCFont | GCForeground | GCGraphicsExposures; gc = Tk_GetGC(tree->tkwin, mask, &gcValues); - tx = x + layout.textLeft + column->sunken; + tx = x + layout.textLeft + sunken; h = column->textPadY[PAD_TOP_LEFT] + layout.fm.linespace + column->textPadY[PAD_BOTTOM_RIGHT]; - ty = y + (height - h) / 2 + layout.fm.ascent + column->sunken; + ty = y + (height - h) / 2 + layout.fm.ascent + sunken; ty += column->textPadY[PAD_TOP_LEFT]; Tk_DrawChars(tree->display, drawable, gc, layout.tkfont, text, textLen, tx, ty); @@ -1408,75 +1999,122 @@ void TreeColumn_Draw(TreeColumn column_, Drawable drawable, int x, int y) ckfree(text); } -#ifdef THEME - if (!tree->useTheme || - (column->arrow == ARROW_NONE) || - (TreeTheme_DrawHeaderArrow(tree, drawable, column->arrow == ARROW_UP, x + layout.arrowLeft, y + (height - layout.arrowHeight) / 2, layout.arrowWidth, layout.arrowHeight) != TCL_OK)) +#ifdef COLUMN_DRAG_IMAGE + if (dragImage) + return; #endif - if (column->arrow != ARROW_NONE) { - int arrowWidth = layout.arrowWidth; - int arrowHeight = layout.arrowHeight; - int arrowTop = y + (height - arrowHeight) / 2; - int arrowBottom = arrowTop + arrowHeight; - XPoint points[5]; - int color1 = 0, color2 = 0; - int i; - switch (column->arrow) { - case ARROW_UP: - points[0].x = x + layout.arrowLeft; - points[0].y = arrowBottom - 1; - points[1].x = x + layout.arrowLeft + arrowWidth / 2; - points[1].y = arrowTop - 1; - color1 = TK_3D_DARK_GC; - points[4].x = x + layout.arrowLeft + arrowWidth / 2; - points[4].y = arrowTop - 1; - points[3].x = x + layout.arrowLeft + arrowWidth - 1; - points[3].y = arrowBottom - 1; - points[2].x = x + layout.arrowLeft; - points[2].y = arrowBottom - 1; - color2 = TK_3D_LIGHT_GC; - break; - case ARROW_DOWN: - points[0].x = x + layout.arrowLeft + arrowWidth - 1; - points[0].y = arrowTop; - points[1].x = x + layout.arrowLeft + arrowWidth / 2; - points[1].y = arrowBottom; - color1 = TK_3D_LIGHT_GC; - points[2].x = x + layout.arrowLeft + arrowWidth - 1; - points[2].y = arrowTop; - points[3].x = x + layout.arrowLeft; - points[3].y = arrowTop; - points[4].x = x + layout.arrowLeft + arrowWidth / 2; - points[4].y = arrowBottom; - color2 = TK_3D_DARK_GC; - break; - } - for (i = 0; i < 5; i++) { - points[i].x += column->sunken; - points[i].y += column->sunken; - } - XDrawLines(tree->display, drawable, - Tk_3DBorderGC(tree->tkwin, column->border, color2), - points + 2, 3, CoordModeOrigin); - XDrawLines(tree->display, drawable, - Tk_3DBorderGC(tree->tkwin, column->border, color1), - points, 2, CoordModeOrigin); - } + Column_DrawArrow(column, drawable, x, y, layout); #ifdef THEME if (theme != TCL_OK) #endif - Tk_Draw3DRectangle(tree->tkwin, drawable, column->border, + Tk_Draw3DRectangle(tree->tkwin, drawable, border, x, y, width, height, column->borderWidth, relief); } +#ifdef COLUMN_DRAG_IMAGE + +Tk_Image SetImageForColumn(TreeCtrl *tree, Column *column) +{ + Tk_PhotoHandle photoH; + Pixmap pixmap; + int width = column->useWidth; /* the entire column, not just what is visible */ + int height = tree->headerHeight; + XImage *ximage; + + photoH = Tk_FindPhoto(tree->interp, "TreeCtrlColumnImage"); + if (photoH == NULL) { + Tcl_GlobalEval(tree->interp, "image create photo TreeCtrlColumnImage"); + photoH = Tk_FindPhoto(tree->interp, "TreeCtrlColumnImage"); + if (photoH == NULL) + return NULL; + } + + pixmap = Tk_GetPixmap(tree->display, Tk_WindowId(tree->tkwin), + width, height, Tk_Depth(tree->tkwin)); + + Column_Draw(column, pixmap, 0, 0, TRUE); + + /* Pixmap -> XImage */ + ximage = XGetImage(tree->display, pixmap, 0, 0, + (unsigned int)width, (unsigned int)height, AllPlanes, ZPixmap); + if (ximage == NULL) + panic("ximage is NULL"); + + /* XImage -> Tk_Image */ + XImage2Photo(tree->interp, photoH, ximage, tree->columnDrag.alpha); + + XDestroyImage(ximage); + Tk_FreePixmap(tree->display, pixmap); + + return Tk_GetImage(tree->interp, tree->tkwin, "TreeCtrlColumnImage", + NULL, (ClientData) NULL); +} + +#if 0 +Tk_PhotoHandle SetImageForHeader(TreeCtrl *tree) +{ + Tk_PhotoHandle photoH; + Pixmap pixmap; + int width = Tk_Width(tree->tkwin) - tree->inset * 2; + int height = tree->headerHeight; + int doubleBuffer = tree->doubleBuffer; + XImage *ximage; + + photoH = Tk_FindPhoto(tree->interp, "TreeCtrlHeaderImage"); + if (photoH == NULL) + return NULL; + + pixmap = Tk_GetPixmap(tree->display, Tk_WindowId(tree->tkwin), + width, height, Tk_Depth(tree->tkwin)); + + tree->doubleBuffer = DOUBLEBUFFER_NONE; + Tree_DrawHeader(tree, pixmap, 0 - tree->xOrigin - tree->inset, 0); + tree->doubleBuffer = doubleBuffer; + + ximage = XGetImage(tree->display, pixmap, 0, 0, + (unsigned int)width, (unsigned int)height, AllPlanes, ZPixmap); + if (ximage == NULL) + panic("ximage is NULL"); + + SetTkImageFromXImage(tree, photoH, ximage, 255); + + XDestroyImage(ximage); + Tk_FreePixmap(tree->display, pixmap); + + return photoH; +} + +void CompositeColumnImageWithHeaderImage(TreeCtrl *tree, Column *column) +{ + Tk_PhotoImageBlock photoBlock; + Tk_PhotoHandle photoH1, photoH2; + int x, y; + + photoH1 = SetImageForHeader(tree); + photoH2 = SetImageForColumn(tree); + + if (!photoH1 || !photoH2) + return; + + Tk_PhotoGetImage(photoH2, &photoBlock); + + TK_PHOTOPUTBLOCK(interp, photoH1, &photoBlock, column->dragOffset, 0, + photoBlock.width, photoBlock.height, TK_PHOTO_COMPOSITE_OVERLAY); +} +#endif +#endif + void Tree_DrawHeader(TreeCtrl *tree, Drawable drawable, int x, int y) { Column *column = (Column *) tree->columns; Tk_Window tkwin = tree->tkwin; int minX, maxX, width, height; Drawable pixmap; +#ifdef COLUMN_DRAG_IMAGE + int x2 = x; +#endif /* Update layout if needed */ (void) Tree_HeaderHeight(tree); @@ -1494,7 +2132,11 @@ void Tree_DrawHeader(TreeCtrl *tree, Drawable drawable, int x, int y) while (column != NULL) { if (column->visible) { if ((x < maxX) && (x + column->useWidth > minX)) - TreeColumn_Draw((TreeColumn) column, pixmap, x, y); +#ifdef COLUMN_DRAG_IMAGE + Column_Draw(column, pixmap, x, y, FALSE); +#else + Column_Draw(column, pixmap, x, y); +#endif x += column->useWidth; } column = column->next; @@ -1510,9 +2152,56 @@ void Tree_DrawHeader(TreeCtrl *tree, Drawable drawable, int x, int y) (TreeTheme_DrawHeaderItem(tree, pixmap, 0, x, y, width, height) == TCL_OK)) { } else #endif - Tk_Fill3DRectangle(tkwin, pixmap, column->border, - x, y, width, height, column->borderWidth, column->relief); + { + Tk_3DBorder border; + border = PerStateBorder_ForState(tree, &column->border, + Column_MakeState(column), NULL); + if (border == NULL) + border = tree->border; + Tk_Fill3DRectangle(tkwin, pixmap, border, + x, y, width, height, column->borderWidth, TK_RELIEF_RAISED); + } } + +#ifdef COLUMN_DRAG_IMAGE + { + Tk_Image image = NULL; + int imageX = 0, imageW = 0, indDraw = FALSE, indX = 0; + + column = (Column *) tree->columns; + while (column != NULL) { + if (column->visible) { + if (tree->columnDrag.column == column->index) { + image = SetImageForColumn(tree, column); + imageX = x2; + imageW = column->useWidth; + } + if (tree->columnDrag.indColumn == column->index) { + indX = x2 - 1; + indDraw = TRUE; + } + x2 += column->useWidth; + } + if ((column->next == NULL) && (tree->columnDrag.indColumn == tree->columnCount)) { + indX = x2 - 1; + indDraw = TRUE; + } + column = column->next; + } + if (indDraw) { + GC gc = Tk_GCForColor(tree->columnDrag.indColor, Tk_WindowId(tree->tkwin)); + XFillRectangle(tree->display, pixmap, gc, + indX, y, 2, tree->headerHeight); + } + if (image != NULL) { + Tk_RedrawImage(image, 0, 0, imageW, + tree->headerHeight, pixmap, + imageX + tree->columnDrag.offset, y); + Tk_FreeImage(image); + } + } +#endif + if (tree->doubleBuffer == DOUBLEBUFFER_ITEM) { XCopyArea(tree->display, pixmap, drawable, tree->copyGC, minX, y, @@ -1654,6 +2343,24 @@ void Tree_InvalidateColumnWidth(TreeCtrl *tree, int columnIndex) Tree_DInfoChanged(tree, DINFO_CHECK_COLUMN_WIDTH); } +void Tree_InvalidateColumnHeight(TreeCtrl *tree, int columnIndex) +{ + Column *column; + + if (columnIndex == -1) { + column = (Column *) tree->columns; + while (column != NULL) { + column->neededHeight = -1; + column = column->next; + } + } else { + column = (Column *) Tree_FindColumn(tree, columnIndex); + if (column != NULL) + column->neededHeight = -1; + } + tree->headerHeight = -1; +} + void TreeColumn_TreeChanged(TreeCtrl *tree, int flagT) { Column *column; @@ -1665,6 +2372,10 @@ void TreeColumn_TreeChanged(TreeCtrl *tree, int flagT) if ((column->tkfont == NULL) && (column->textLen > 0)) { column->textWidth = Tk_TextWidth(tree->tkfont, column->text, column->textLen); column->neededHeight = -1; +#ifdef COL_LAYOUT + column->textLayoutInvalid = TRUE; + column->neededWidth = -1; +#endif } column = column->next; } @@ -1735,6 +2446,7 @@ void Tree_InitColumns(TreeCtrl *tree) strcpy(column->tag, "tail"); tree->columnTail = (TreeColumn) column; tree->columnCount = 0; + Column_Config(column, 0, NULL, TRUE); } void Tree_FreeColumns(TreeCtrl *tree) |