/* * bltTable.c -- * * This module implements a table-based geometry manager * for the BLT toolkit. * * Copyright 1993-1998 Lucent Technologies, Inc. * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose and without fee is hereby * granted, provided that the above copyright notice appear in all * copies and that both that the copyright notice and warranty * disclaimer appear in supporting documentation, and that the names * of Lucent Technologies any of their entities not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. * * Lucent Technologies disclaims all warranties with regard to this * software, including all implied warranties of merchantability and * fitness. In no event shall Lucent Technologies be liable for any * special, indirect or consequential damages or any damages * whatsoever resulting from loss of use, data or profits, whether in * an action of contract, negligence or other tortuous action, arising * out of or in connection with the use or performance of this * software. * * The "table" geometry manager was created by George Howlett. */ /* * To do: * * 3) No way to detect if widget is already a container of another * geometry manager. This one is especially bad with toplevel * widgets, causing the window manager to lock-up trying to handle the * myriads of resize requests. * * Note: This problem continues in Tk 8.x. It's possible for a widget * to be a container for two different geometry managers. Each manager * will set its own requested geometry for the container widget. The * winner sets the geometry last (sometimes ad infinitum). * * 7) Relative sizing of partitions? * */ #include "bltInt.h" #include "bltTable.h" #define TABLE_THREAD_KEY "BLT Table Data" #define TABLE_DEF_PAD 0 /* * Default values for widget attributes. */ #define DEF_TABLE_ANCHOR "center" #define DEF_TABLE_COLUMNS "0" #define DEF_TABLE_FILL "none" #define DEF_TABLE_PAD "0" #define DEF_TABLE_PROPAGATE "1" #define DEF_TABLE_RESIZE "both" #define DEF_TABLE_ROWS "0" #define DEF_TABLE_SPAN "1" #define DEF_TABLE_CONTROL "normal" #define DEF_TABLE_WEIGHT "1.0" #define ENTRY_DEF_PAD 0 #define ENTRY_DEF_ANCHOR TK_ANCHOR_CENTER #define ENTRY_DEF_FILL FILL_NONE #define ENTRY_DEF_SPAN 1 #define ENTRY_DEF_CONTROL CONTROL_NORMAL #define ENTRY_DEF_IPAD 0 #define ROWCOL_DEF_RESIZE (RESIZE_BOTH | RESIZE_VIRGIN) #define ROWCOL_DEF_PAD 0 #define ROWCOL_DEF_WEIGHT 1.0 static Blt_Uid rowUid, columnUid; static void WidgetGeometryProc _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin)); static void WidgetCustodyProc _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin)); static Tk_GeomMgr tableMgrInfo = { "table", /* Name of geometry manager used by winfo */ WidgetGeometryProc, /* Procedure to for new geometry requests */ WidgetCustodyProc, /* Procedure when widget is taken away */ }; static int StringToLimits _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int offset)); static char *LimitsToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)); static Tk_CustomOption limitsOption = { StringToLimits, LimitsToString, (ClientData)0 }; static int StringToResize _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int offset)); static char *ResizeToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)); static Tk_CustomOption resizeOption = { StringToResize, ResizeToString, (ClientData)0 }; static int StringToControl _ANSI_ARGS_((ClientData clientData, Tcl_Interp *interp, Tk_Window tkwin, char *string, char *widgRec, int offset)); static char *ControlToString _ANSI_ARGS_((ClientData clientData, Tk_Window tkwin, char *widgRec, int offset, Tcl_FreeProc **freeProcPtr)); static Tk_CustomOption controlOption = { StringToControl, ControlToString, (ClientData)0 }; extern Tk_CustomOption bltPadOption; extern Tk_CustomOption bltFillOption; extern Tk_CustomOption bltDistanceOption; static Tk_ConfigSpec rowConfigSpecs[] = { {TK_CONFIG_CUSTOM, "-height", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(RowColumn, reqSize), TK_CONFIG_NULL_OK, &limitsOption}, {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL, DEF_TABLE_PAD, Tk_Offset(RowColumn, pad), TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption}, {TK_CONFIG_CUSTOM, "-resize", (char *)NULL, (char *)NULL, DEF_TABLE_RESIZE, Tk_Offset(RowColumn, resize), TK_CONFIG_DONT_SET_DEFAULT, &resizeOption}, {TK_CONFIG_DOUBLE, "-weight", (char *)NULL, (char *)NULL, DEF_TABLE_WEIGHT, Tk_Offset(RowColumn, weight), TK_CONFIG_NULL_OK | TK_CONFIG_DONT_SET_DEFAULT, &limitsOption}, {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} }; static Tk_ConfigSpec columnConfigSpecs[] = { {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL, DEF_TABLE_PAD, Tk_Offset(RowColumn, pad), TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption}, {TK_CONFIG_CUSTOM, "-resize", (char *)NULL, (char *)NULL, DEF_TABLE_RESIZE, Tk_Offset(RowColumn, resize), TK_CONFIG_DONT_SET_DEFAULT, &resizeOption}, {TK_CONFIG_DOUBLE, "-weight", (char *)NULL, (char *)NULL, DEF_TABLE_WEIGHT, Tk_Offset(RowColumn, weight), TK_CONFIG_NULL_OK | TK_CONFIG_DONT_SET_DEFAULT, &limitsOption}, {TK_CONFIG_CUSTOM, "-width", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(RowColumn, reqSize), TK_CONFIG_NULL_OK, &limitsOption}, {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} }; static Tk_ConfigSpec entryConfigSpecs[] = { {TK_CONFIG_ANCHOR, "-anchor", (char *)NULL, (char *)NULL, DEF_TABLE_ANCHOR, Tk_Offset(Entry, anchor), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_INT, "-columnspan", "columnSpan", (char *)NULL, DEF_TABLE_SPAN, Tk_Offset(Entry, column.span), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-columncontrol", "columnControl", (char *)NULL, DEF_TABLE_CONTROL, Tk_Offset(Entry, column.control), TK_CONFIG_DONT_SET_DEFAULT, &controlOption}, {TK_CONFIG_SYNONYM, "-cspan", "columnSpan", (char *)NULL, (char *)NULL, Tk_Offset(Entry, column.span), 0}, {TK_CONFIG_SYNONYM, "-ccontrol", "columnControl", (char *)NULL, (char *)NULL, Tk_Offset(Entry, column.control), 0}, {TK_CONFIG_CUSTOM, "-fill", (char *)NULL, (char *)NULL, DEF_TABLE_FILL, Tk_Offset(Entry, fill), TK_CONFIG_DONT_SET_DEFAULT, &bltFillOption}, {TK_CONFIG_SYNONYM, "-height", "reqHeight", (char *)NULL, (char *)NULL, Tk_Offset(Entry, reqHeight), 0}, {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(Entry, padX), 0, &bltPadOption}, {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(Entry, padY), 0, &bltPadOption}, {TK_CONFIG_CUSTOM, "-ipadx", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(Entry, ipadX), 0, &bltDistanceOption}, {TK_CONFIG_CUSTOM, "-ipady", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(Entry, ipadY), 0, &bltDistanceOption}, {TK_CONFIG_CUSTOM, "-reqheight", "reqHeight", (char *)NULL, (char *)NULL, Tk_Offset(Entry, reqHeight), TK_CONFIG_NULL_OK, &limitsOption}, {TK_CONFIG_CUSTOM, "-reqwidth", "reqWidth", (char *)NULL, (char *)NULL, Tk_Offset(Entry, reqWidth), TK_CONFIG_NULL_OK, &limitsOption}, {TK_CONFIG_INT, "-rowspan", "rowSpan", (char *)NULL, DEF_TABLE_SPAN, Tk_Offset(Entry, row.span), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-rowcontrol", "rowControl", (char *)NULL, DEF_TABLE_CONTROL, Tk_Offset(Entry, row.control), TK_CONFIG_DONT_SET_DEFAULT, &controlOption}, {TK_CONFIG_SYNONYM, "-rspan", "rowSpan", (char *)NULL, (char *)NULL, Tk_Offset(Entry, row.span), 0}, {TK_CONFIG_SYNONYM, "-rcontrol", "rowControl", (char *)NULL, (char *)NULL, Tk_Offset(Entry, row.control), 0}, {TK_CONFIG_SYNONYM, "-width", "reqWidth", (char *)NULL, (char *)NULL, Tk_Offset(Entry, reqWidth), 0}, {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} }; static Tk_ConfigSpec tableConfigSpecs[] = { {TK_CONFIG_CUSTOM, "-padx", (char *)NULL, (char *)NULL, DEF_TABLE_PAD, Tk_Offset(Table, padX), TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption}, {TK_CONFIG_CUSTOM, "-pady", (char *)NULL, (char *)NULL, DEF_TABLE_PAD, Tk_Offset(Table, padY), TK_CONFIG_DONT_SET_DEFAULT, &bltPadOption}, {TK_CONFIG_BOOLEAN, "-propagate", (char *)NULL, (char *)NULL, DEF_TABLE_PROPAGATE, Tk_Offset(Table, propagate), TK_CONFIG_DONT_SET_DEFAULT}, {TK_CONFIG_CUSTOM, "-reqheight", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(Table, reqHeight), TK_CONFIG_NULL_OK, &limitsOption}, {TK_CONFIG_CUSTOM, "-reqwidth", (char *)NULL, (char *)NULL, (char *)NULL, Tk_Offset(Table, reqWidth), TK_CONFIG_NULL_OK, &limitsOption}, {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} }; /* * Forward declarations */ static void ArrangeTable _ANSI_ARGS_((ClientData clientData)); static void DestroyTable _ANSI_ARGS_((DestroyData dataPtr)); static void DestroyEntry _ANSI_ARGS_((Entry * entryPtr)); static void TableEventProc _ANSI_ARGS_((ClientData clientData, XEvent *eventPtr)); static void BinEntry _ANSI_ARGS_((Table *tablePtr, Entry * entryPtr)); static RowColumn *InitSpan _ANSI_ARGS_((PartitionInfo * infoPtr, int start, int span)); static EntrySearchProc FindEntry; static Tcl_CmdProc TableCmd; static Tcl_InterpDeleteProc TableInterpDeleteProc; static Tk_EventProc WidgetEventProc; /* * ---------------------------------------------------------------------------- * * StringToLimits -- * * Converts the list of elements into zero or more pixel values which * determine the range of pixel values possible. An element can be in * any form accepted by Tk_GetPixels. The list has a different meaning * based upon the number of elements. * * # of elements: * * 0 - the limits are reset to the defaults. * 1 - the minimum and maximum values are set to this * value, freezing the range at a single value. * 2 - first element is the minimum, the second is the * maximum. * 3 - first element is the minimum, the second is the * maximum, and the third is the nominal value. * * Any element may be the empty string which indicates the default. * * Results: * The return value is a standard Tcl result. The min and max fields * of the range are set. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToLimits(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Widget of table */ char *string; /* New width list */ char *widgRec; /* Widget record */ int offset; /* Offset of limits */ { Limits *limitsPtr = (Limits *)(widgRec + offset); char **elemArr; int nElem; int limArr[3]; Tk_Window winArr[3]; int flags; elemArr = NULL; nElem = 0; /* Initialize limits to default values */ limArr[2] = LIMITS_NOM; limArr[1] = LIMITS_MAX; limArr[0] = LIMITS_MIN; winArr[0] = winArr[1] = winArr[2] = NULL; flags = 0; if (string != NULL) { int size; int i; if (Tcl_SplitList(interp, string, &nElem, &elemArr) != TCL_OK) { return TCL_ERROR; } if (nElem > 3) { Tcl_AppendResult(interp, "wrong # limits \"", string, "\"", (char *)NULL); goto error; } for (i = 0; i < nElem; i++) { if (elemArr[i][0] == '\0') { continue; /* Empty string: use default value */ } flags |= (LIMITS_SET_BIT << i); if ((elemArr[i][0] == '.') && ((elemArr[i][1] == '\0') || isalpha(UCHAR(elemArr[i][1])))) { Tk_Window tkwin2; /* Widget specified: save pointer to widget */ tkwin2 = Tk_NameToWindow(interp, elemArr[i], tkwin); if (tkwin2 == NULL) { goto error; } winArr[i] = tkwin2; } else { if (Tk_GetPixels(interp, tkwin, elemArr[i], &size) != TCL_OK) { goto error; } if ((size < LIMITS_MIN) || (size > LIMITS_MAX)) { Tcl_AppendResult(interp, "bad limits \"", string, "\"", (char *)NULL); goto error; } limArr[i] = size; } } Blt_Free(elemArr); } /* * Check the limits specified. We can't check the requested * size of widgets. */ switch (nElem) { case 1: flags |= (LIMITS_SET_MIN | LIMITS_SET_MAX); if (winArr[0] == NULL) { limArr[1] = limArr[0]; /* Set minimum and maximum to value */ } else { winArr[1] = winArr[0]; } break; case 2: if ((winArr[0] == NULL) && (winArr[1] == NULL) && (limArr[1] < limArr[0])) { Tcl_AppendResult(interp, "bad range \"", string, "\": min > max", (char *)NULL); return TCL_ERROR; /* Minimum is greater than maximum */ } break; case 3: if ((winArr[0] == NULL) && (winArr[1] == NULL)) { if (limArr[1] < limArr[0]) { Tcl_AppendResult(interp, "bad range \"", string, "\": min > max", (char *)NULL); return TCL_ERROR; /* Minimum is greater than maximum */ } if ((winArr[2] == NULL) && ((limArr[2] < limArr[0]) || (limArr[2] > limArr[1]))) { Tcl_AppendResult(interp, "nominal value \"", string, "\" out of range", (char *)NULL); return TCL_ERROR; /* Nominal is outside of range defined * by minimum and maximum */ } } break; } limitsPtr->min = limArr[0]; limitsPtr->max = limArr[1]; limitsPtr->nom = limArr[2]; limitsPtr->wMin = winArr[0]; limitsPtr->wMax = winArr[1]; limitsPtr->wNom = winArr[2]; limitsPtr->flags = flags; return TCL_OK; error: Blt_Free(elemArr); return TCL_ERROR; } /* * ---------------------------------------------------------------------------- * * ResetLimits -- * * Resets the limits to their default values. * * Results: * None. * * ---------------------------------------------------------------------------- */ INLINE static void ResetLimits(limitsPtr) Limits *limitsPtr; /* Limits to be imposed on the value */ { limitsPtr->flags = 0; limitsPtr->min = LIMITS_MIN; limitsPtr->max = LIMITS_MAX; limitsPtr->nom = LIMITS_NOM; limitsPtr->wNom = limitsPtr->wMax = limitsPtr->wMin = NULL; } /* * ---------------------------------------------------------------------------- * * GetBoundedWidth -- * * Bounds a given width value to the limits described in the limit * structure. The initial starting value may be overridden by the * nominal value in the limits. * * Results: * Returns the constrained value. * * ---------------------------------------------------------------------------- */ static int GetBoundedWidth(width, limitsPtr) int width; /* Initial value to be constrained */ Limits *limitsPtr; /* Limits to be imposed on the value */ { /* * Check widgets for requested width values; */ if (limitsPtr->wMin != NULL) { limitsPtr->min = Tk_ReqWidth(limitsPtr->wMin); } if (limitsPtr->wMax != NULL) { limitsPtr->max = Tk_ReqWidth(limitsPtr->wMax); } if (limitsPtr->wNom != NULL) { limitsPtr->nom = Tk_ReqWidth(limitsPtr->wNom); } if (limitsPtr->flags & LIMITS_SET_NOM) { width = limitsPtr->nom; /* Override initial value */ } if (width < limitsPtr->min) { width = limitsPtr->min; /* Bounded by minimum value */ } else if (width > limitsPtr->max) { width = limitsPtr->max; /* Bounded by maximum value */ } return width; } /* * ---------------------------------------------------------------------------- * * GetBoundedHeight -- * * Bounds a given value to the limits described in the limit * structure. The initial starting value may be overridden by the * nominal value in the limits. * * Results: * Returns the constrained value. * * ---------------------------------------------------------------------------- */ static int GetBoundedHeight(height, limitsPtr) int height; /* Initial value to be constrained */ Limits *limitsPtr; /* Limits to be imposed on the value */ { /* * Check widgets for requested height values; */ if (limitsPtr->wMin != NULL) { limitsPtr->min = Tk_ReqHeight(limitsPtr->wMin); } if (limitsPtr->wMax != NULL) { limitsPtr->max = Tk_ReqHeight(limitsPtr->wMax); } if (limitsPtr->wNom != NULL) { limitsPtr->nom = Tk_ReqHeight(limitsPtr->wNom); } if (limitsPtr->flags & LIMITS_SET_NOM) { height = limitsPtr->nom;/* Override initial value */ } if (height < limitsPtr->min) { height = limitsPtr->min;/* Bounded by minimum value */ } else if (height > limitsPtr->max) { height = limitsPtr->max;/* Bounded by maximum value */ } return height; } /* * ---------------------------------------------------------------------------- * * NameOfLimits -- * * Convert the values into a list representing the limits. * * Results: * The static string representation of the limits is returned. * * ---------------------------------------------------------------------------- */ static char * NameOfLimits(limitsPtr) Limits *limitsPtr; { Tcl_DString buffer; #define STRING_SPACE 200 static char string[STRING_SPACE + 1]; Tcl_DStringInit(&buffer); if (limitsPtr->wMin != NULL) { Tcl_DStringAppendElement(&buffer, Tk_PathName(limitsPtr->wMin)); } else if (limitsPtr->flags & LIMITS_SET_MIN) { Tcl_DStringAppendElement(&buffer, Blt_Itoa(limitsPtr->min)); } else { Tcl_DStringAppendElement(&buffer, ""); } if (limitsPtr->wMax != NULL) { Tcl_DStringAppendElement(&buffer, Tk_PathName(limitsPtr->wMax)); } else if (limitsPtr->flags & LIMITS_SET_MAX) { Tcl_DStringAppendElement(&buffer, Blt_Itoa(limitsPtr->max)); } else { Tcl_DStringAppendElement(&buffer, ""); } if (limitsPtr->wNom != NULL) { Tcl_DStringAppendElement(&buffer, Tk_PathName(limitsPtr->wNom)); } else if (limitsPtr->flags & LIMITS_SET_NOM) { Tcl_DStringAppendElement(&buffer, Blt_Itoa(limitsPtr->nom)); } else { Tcl_DStringAppendElement(&buffer, ""); } strncpy(string, Tcl_DStringValue(&buffer), STRING_SPACE); string[STRING_SPACE] = '\0'; return string; } /* * ---------------------------------------------------------------------------- * * LimitsToString -- * * Convert the limits of the pixel values allowed into a list. * * Results: * The string representation of the limits is returned. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static char * LimitsToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Row/column structure record */ int offset; /* Offset of widget RowColumn record */ Tcl_FreeProc **freeProcPtr; /* Memory deallocation routine */ { Limits *limitsPtr = (Limits *)(widgRec + offset); return NameOfLimits(limitsPtr); } /* * ---------------------------------------------------------------------------- * * StringToResize -- * * Converts the resize mode into its numeric representation. Valid * mode strings are "none", "expand", "shrink", or "both". * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToResize(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Not used. */ char *string; /* Resize style string */ char *widgRec; /* Entry structure record */ int offset; /* Offset of style in record */ { int *resizePtr = (int *)(widgRec + offset); unsigned int length; char c; c = string[0]; length = strlen(string); if ((c == 'n') && (strncmp(string, "none", length) == 0)) { *resizePtr = RESIZE_NONE; } else if ((c == 'b') && (strncmp(string, "both", length) == 0)) { *resizePtr = RESIZE_BOTH; } else if ((c == 'e') && (strncmp(string, "expand", length) == 0)) { *resizePtr = RESIZE_EXPAND; } else if ((c == 's') && (strncmp(string, "shrink", length) == 0)) { *resizePtr = RESIZE_SHRINK; } else { Tcl_AppendResult(interp, "bad resize argument \"", string, "\": should be \"none\", \"expand\", \"shrink\", or \"both\"", (char *)NULL); return TCL_ERROR; } return TCL_OK; } /* * ---------------------------------------------------------------------------- * * NameOfResize -- * * Converts the resize value into its string representation. * * Results: * Returns a pointer to the static name string. * * ---------------------------------------------------------------------------- */ static char * NameOfResize(resize) int resize; { switch (resize & RESIZE_BOTH) { case RESIZE_NONE: return "none"; case RESIZE_EXPAND: return "expand"; case RESIZE_SHRINK: return "shrink"; case RESIZE_BOTH: return "both"; default: return "unknown resize value"; } } /* * ---------------------------------------------------------------------------- * * ResizeToString -- * * Returns resize mode string based upon the resize flags. * * Results: * The resize mode string is returned. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static char * ResizeToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Row/column structure record */ int offset; /* Offset of resize in RowColumn record */ Tcl_FreeProc **freeProcPtr; /* Not used. */ { int resize = *(int *)(widgRec + offset); return NameOfResize(resize); } /* * ---------------------------------------------------------------------------- * * StringToControl -- * * Converts the control string into its numeric representation. * Valid control strings are "none", "normal", and "full". * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int StringToControl(clientData, interp, tkwin, string, widgRec, offset) ClientData clientData; /* Not used. */ Tcl_Interp *interp; /* Interpreter to send results back to */ Tk_Window tkwin; /* Not used. */ char *string; /* Control style string */ char *widgRec; /* Entry structure record */ int offset; /* Offset of style in record */ { double *controlPtr = (double *)(widgRec + offset); unsigned int length; int bool; char c; c = string[0]; length = strlen(string); if (Tcl_GetBoolean(NULL, string, &bool) == TCL_OK) { *controlPtr = bool; return TCL_OK; } if ((c == 'n') && (length > 1) && (strncmp(string, "normal", length) == 0)) { *controlPtr = CONTROL_NORMAL; } else if ((c == 'n') && (length > 1) && (strncmp(string, "none", length) == 0)) { *controlPtr = CONTROL_NONE; } else if ((c == 'f') && (strncmp(string, "full", length) == 0)) { *controlPtr = CONTROL_FULL; } else { double control; if ((Tcl_GetDouble(interp, string, &control) != TCL_OK) || (control < 0.0)) { Tcl_AppendResult(interp, "bad control argument \"", string, "\": should be \"normal\", \"none\", or \"full\"", (char *)NULL); return TCL_ERROR; } *controlPtr = control; } return TCL_OK; } /* * ---------------------------------------------------------------------------- * * NameOfControl -- * * Converts the control value into its string representation. * * Results: * Returns a pointer to the static name string. * * ---------------------------------------------------------------------------- */ static char * NameOfControl(control) double control; { if (control == CONTROL_NORMAL) { return "normal"; } else if (control == CONTROL_NONE) { return "none"; } else if (control == CONTROL_FULL) { return "full"; } else { static char string[TCL_DOUBLE_SPACE + 1]; sprintf(string, "%g", control); return string; } } /* * ---------------------------------------------------------------------------- * * ControlToString -- * * Returns control mode string based upon the control flags. * * Results: * The control mode string is returned. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static char * ControlToString(clientData, tkwin, widgRec, offset, freeProcPtr) ClientData clientData; /* Not used. */ Tk_Window tkwin; /* Not used. */ char *widgRec; /* Row/column structure record */ int offset; /* Offset of control in RowColumn record */ Tcl_FreeProc **freeProcPtr; /* Not used. */ { double control = *(double *)(widgRec + offset); return NameOfControl(control); } static void EventuallyArrangeTable(tablePtr) Table *tablePtr; { if (!(tablePtr->flags & ARRANGE_PENDING)) { tablePtr->flags |= ARRANGE_PENDING; Tcl_DoWhenIdle(ArrangeTable, tablePtr); } } /* * ---------------------------------------------------------------------------- * * TableEventProc -- * * This procedure is invoked by the Tk event handler when the * container widget is reconfigured or destroyed. * * The table will be rearranged at the next idle point if the * container widget has been resized or moved. There's a * distinction made between parent and non-parent container * arrangements. If the container is moved and it's the parent * of the widgets, they're are moved automatically. If it's * not the parent, those widgets need to be moved manually. * This can be a performance hit in rare cases where we're * scrolling the container (by moving the window) and there * are lots of non-child widgets arranged insided. * * Results: * None. * * Side effects: * Arranges for the table associated with tkwin to have its * layout re-computed and drawn at the next idle point. * * ---------------------------------------------------------------------------- */ static void TableEventProc(clientData, eventPtr) ClientData clientData; /* Information about widget */ XEvent *eventPtr; /* Information about event */ { register Table *tablePtr = clientData; if (eventPtr->type == ConfigureNotify) { if ((tablePtr->container.width != Tk_Width(tablePtr->tkwin)) || (tablePtr->container.height != Tk_Height(tablePtr->tkwin)) || (tablePtr->flags & NON_PARENT)) { EventuallyArrangeTable(tablePtr); } } else if (eventPtr->type == DestroyNotify) { if (tablePtr->flags & ARRANGE_PENDING) { Tcl_CancelIdleCall(ArrangeTable, tablePtr); } tablePtr->tkwin = NULL; Tcl_EventuallyFree(tablePtr, DestroyTable); } } /* * ---------------------------------------------------------------------------- * * WidgetEventProc -- * * This procedure is invoked by the Tk event handler when * StructureNotify events occur in a widget managed by the table. * For example, when a managed widget is destroyed, it frees the * corresponding entry structure and arranges for the table * layout to be re-computed at the next idle point. * * Results: * None. * * Side effects: * If the managed widget was deleted, the Entry structure gets * cleaned up and the table is rearranged. * * ---------------------------------------------------------------------------- */ static void WidgetEventProc(clientData, eventPtr) ClientData clientData; /* Pointer to Entry structure for widget * referred to by eventPtr. */ XEvent *eventPtr; /* Describes what just happened. */ { Entry *entryPtr = (Entry *) clientData; Table *tablePtr = entryPtr->tablePtr; if (eventPtr->type == ConfigureNotify) { int borderWidth; tablePtr->flags |= REQUEST_LAYOUT; borderWidth = Tk_Changes(entryPtr->tkwin)->border_width; if (entryPtr->borderWidth != borderWidth) { entryPtr->borderWidth = borderWidth; EventuallyArrangeTable(tablePtr); } } else if (eventPtr->type == DestroyNotify) { entryPtr->tkwin = NULL; DestroyEntry(entryPtr); tablePtr->flags |= REQUEST_LAYOUT; EventuallyArrangeTable(tablePtr); } } /* * ---------------------------------------------------------------------------- * * WidgetCustodyProc -- * * This procedure is invoked when a widget has been stolen by * another geometry manager. The information and memory * associated with the widget is released. * * Results: * None. * * Side effects: * Arranges for the table to have its layout re-arranged at the * next idle point. * * ---------------------------------------------------------------------------- */ /* ARGSUSED */ static void WidgetCustodyProc(clientData, tkwin) ClientData clientData; /* Information about the widget */ Tk_Window tkwin; /* Not used. */ { Entry *entryPtr = (Entry *) clientData; Table *tablePtr = entryPtr->tablePtr; if (Tk_IsMapped(entryPtr->tkwin)) { Tk_UnmapWindow(entryPtr->tkwin); } Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin); entryPtr->tkwin = NULL; DestroyEntry(entryPtr); tablePtr->flags |= REQUEST_LAYOUT; EventuallyArrangeTable(tablePtr); } /* * ---------------------------------------------------------------------------- * * WidgetGeometryProc -- * * This procedure is invoked by Tk_GeometryRequest for widgets * managed by the table geometry manager. * * Results: * None. * * Side effects: * Arranges for the table to have its layout re-computed and * re-arranged at the next idle point. * * ---------------------------------------------------------------------------- */ /* ARGSUSED */ static void WidgetGeometryProc(clientData, tkwin) ClientData clientData; /* Information about widget that got new * preferred geometry. */ Tk_Window tkwin; /* Other Tk-related information about the * widget. */ { Entry *entryPtr = (Entry *) clientData; entryPtr->tablePtr->flags |= REQUEST_LAYOUT; EventuallyArrangeTable(entryPtr->tablePtr); } /* * ---------------------------------------------------------------------------- * * FindEntry -- * * Searches for the table entry corresponding to the given * widget. * * Results: * If a structure associated with the widget exists, a pointer to * that structure is returned. Otherwise NULL. * * ---------------------------------------------------------------------------- */ static Entry * FindEntry(tablePtr, tkwin) Table *tablePtr; Tk_Window tkwin; /* Widget associated with table entry */ { Blt_HashEntry *hPtr; hPtr = Blt_FindHashEntry(&(tablePtr->entryTable), (char *)tkwin); if (hPtr == NULL) { return NULL; } return (Entry *) Blt_GetHashValue(hPtr); } static int GetEntry(interp, tablePtr, string, entryPtrPtr) Tcl_Interp *interp; Table *tablePtr; char *string; Entry **entryPtrPtr; { Tk_Window tkwin; Entry *entryPtr; tkwin = Tk_NameToWindow(interp, string, tablePtr->tkwin); if (tkwin == NULL) { return TCL_ERROR; } entryPtr = FindEntry(tablePtr, tkwin); if (entryPtr == NULL) { Tcl_AppendResult(interp, "\"", Tk_PathName(tkwin), "\" is not managed by any table", (char *)NULL); return TCL_ERROR; } *entryPtrPtr = entryPtr; return TCL_OK; } /* * ---------------------------------------------------------------------------- * * CreateEntry -- * * This procedure creates and initializes a new Entry structure * to hold a widget. A valid widget has a parent widget that is * either a) the container widget itself or b) a mutual ancestor * of the container widget. * * Results: * Returns a pointer to the new structure describing the new * widget entry. If an error occurred, then the return * value is NULL and an error message is left in interp->result. * * Side effects: * Memory is allocated and initialized for the Entry structure. * * ---------------------------------------------------------------------------- */ static Entry * CreateEntry(tablePtr, tkwin) Table *tablePtr; Tk_Window tkwin; { register Entry *entryPtr; int dummy; Tk_Window parent, ancestor; /* * Check that this widget can be managed by this table. A valid * widget has a parent widget that either * * 1) is the container widget, or * 2) is a mutual ancestor of the container widget. */ ancestor = Tk_Parent(tkwin); for (parent = tablePtr->tkwin; (parent != ancestor) && (!Tk_IsTopLevel(parent)); parent = Tk_Parent(parent)) { /* empty */ } if (ancestor != parent) { Tcl_AppendResult(tablePtr->interp, "can't manage \"", Tk_PathName(tkwin), "\" in table \"", Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL); return NULL; } entryPtr = Blt_Calloc(1, sizeof(Entry)); assert(entryPtr); /* Initialize the entry structure */ entryPtr->tkwin = tkwin; entryPtr->tablePtr = tablePtr; entryPtr->borderWidth = Tk_Changes(tkwin)->border_width; entryPtr->fill = ENTRY_DEF_FILL; entryPtr->row.control = entryPtr->column.control = ENTRY_DEF_CONTROL; entryPtr->anchor = ENTRY_DEF_ANCHOR; entryPtr->row.span = entryPtr->column.span = ENTRY_DEF_SPAN; ResetLimits(&(entryPtr->reqWidth)); ResetLimits(&(entryPtr->reqHeight)); /* * Add the entry to the following data structures. * * 1) A chain of widgets managed by the table. * 2) A hash table of widgets managed by the table. */ entryPtr->linkPtr = Blt_ChainAppend(tablePtr->chainPtr, entryPtr); entryPtr->hashPtr = Blt_CreateHashEntry(&(tablePtr->entryTable), (char *)tkwin, &dummy); Blt_SetHashValue(entryPtr->hashPtr, entryPtr); Tk_CreateEventHandler(tkwin, StructureNotifyMask, WidgetEventProc, entryPtr); Tk_ManageGeometry(tkwin, &tableMgrInfo, (ClientData)entryPtr); return entryPtr; } /* * ---------------------------------------------------------------------------- * * DestroyEntry -- * * Removes the Entry structure from the hash table and frees * the memory allocated by it. If the table is still in use * (i.e. was not called from DestoryTable), remove its entries * from the lists of row and column sorted partitions. * * Results: * None. * * Side effects: * Everything associated with the entry is freed up. * * ---------------------------------------------------------------------------- */ static void DestroyEntry(entryPtr) Entry *entryPtr; { Table *tablePtr = entryPtr->tablePtr; if (entryPtr->row.linkPtr != NULL) { Blt_ChainDeleteLink(entryPtr->row.chainPtr, entryPtr->row.linkPtr); } if (entryPtr->column.linkPtr != NULL) { Blt_ChainDeleteLink(entryPtr->column.chainPtr, entryPtr->column.linkPtr); } if (entryPtr->linkPtr != NULL) { Blt_ChainDeleteLink(tablePtr->chainPtr, entryPtr->linkPtr); } if (entryPtr->tkwin != NULL) { Tk_DeleteEventHandler(entryPtr->tkwin, StructureNotifyMask, WidgetEventProc, (ClientData)entryPtr); Tk_ManageGeometry(entryPtr->tkwin, (Tk_GeomMgr *)NULL, (ClientData)entryPtr); if ((tablePtr->tkwin != NULL) && (Tk_Parent(entryPtr->tkwin) != tablePtr->tkwin)) { Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin); } if (Tk_IsMapped(entryPtr->tkwin)) { Tk_UnmapWindow(entryPtr->tkwin); } } if (entryPtr->hashPtr != NULL) { Blt_DeleteHashEntry(&(tablePtr->entryTable), entryPtr->hashPtr); } Blt_Free(entryPtr); } /* * ---------------------------------------------------------------------------- * * ConfigureEntry -- * * This procedure is called to process an argv/argc list, plus * the Tk option database, in order to configure (or reconfigure) * one or more entries. Entries hold information about widgets * managed by the table geometry manager. * * Note: You can query only one widget at a time. But several * can be reconfigured at once. * * Results: * The return value is a standard Tcl result. If TCL_ERROR is * returned, then interp->result contains an error message. * * Side effects: * The table layout is recomputed and rearranged at the next idle * point. * * ---------------------------------------------------------------------------- */ static int ConfigureEntry(tablePtr, interp, entryPtr, argc, argv) Table *tablePtr; Tcl_Interp *interp; Entry *entryPtr; int argc; /* Option-value arguments */ char **argv; { int oldRowSpan, oldColSpan; if (entryPtr->tablePtr != tablePtr) { Tcl_AppendResult(interp, "widget \"", Tk_PathName(entryPtr->tkwin), "\" does not belong to table \"", Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL); return TCL_ERROR; } if (argc == 0) { return Tk_ConfigureInfo(interp, entryPtr->tkwin, entryConfigSpecs, (char *)entryPtr, (char *)NULL, 0); } else if (argc == 1) { return Tk_ConfigureInfo(interp, entryPtr->tkwin, entryConfigSpecs, (char *)entryPtr, argv[0], 0); } oldRowSpan = entryPtr->row.span; oldColSpan = entryPtr->column.span; if (Tk_ConfigureWidget(interp, entryPtr->tkwin, entryConfigSpecs, argc, argv, (char *)entryPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) { return TCL_ERROR; } if ((entryPtr->column.span < 1) || (entryPtr->column.span > USHRT_MAX)) { Tcl_AppendResult(interp, "bad column span specified for \"", Tk_PathName(entryPtr->tkwin), "\"", (char *)NULL); return TCL_ERROR; } if ((entryPtr->row.span < 1) || (entryPtr->row.span > USHRT_MAX)) { Tcl_AppendResult(interp, "bad row span specified for \"", Tk_PathName(entryPtr->tkwin), "\"", (char *)NULL); return TCL_ERROR; } if ((oldColSpan != entryPtr->column.span) || (oldRowSpan != entryPtr->row.span)) { BinEntry(tablePtr, entryPtr); } return TCL_OK; } /* * ---------------------------------------------------------------------------- * * PrintEntry -- * * Returns the name, position and options of a widget in the table. * * Results: * Returns a standard Tcl result. A list of the widget * attributes is left in interp->result. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static void PrintEntry(entryPtr, resultPtr) Entry *entryPtr; Tcl_DString *resultPtr; { char string[200]; sprintf(string, " %d,%d ", entryPtr->row.rcPtr->index, entryPtr->column.rcPtr->index); Tcl_DStringAppend(resultPtr, string, -1); Tcl_DStringAppend(resultPtr, Tk_PathName(entryPtr->tkwin), -1); if (entryPtr->ipadX != ENTRY_DEF_PAD) { Tcl_DStringAppend(resultPtr, " -ipadx ", -1); Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->ipadX), -1); } if (entryPtr->ipadY != ENTRY_DEF_PAD) { Tcl_DStringAppend(resultPtr, " -ipady ", -1); Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->ipadY), -1); } if (entryPtr->row.span != ENTRY_DEF_SPAN) { Tcl_DStringAppend(resultPtr, " -rowspan ", -1); Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->row.span), -1); } if (entryPtr->column.span != ENTRY_DEF_SPAN) { Tcl_DStringAppend(resultPtr, " -columnspan ", -1); Tcl_DStringAppend(resultPtr, Blt_Itoa(entryPtr->column.span), -1); } if (entryPtr->anchor != ENTRY_DEF_ANCHOR) { Tcl_DStringAppend(resultPtr, " -anchor ", -1); Tcl_DStringAppend(resultPtr, Tk_NameOfAnchor(entryPtr->anchor), -1); } if ((entryPtr->padLeft != ENTRY_DEF_PAD) || (entryPtr->padRight != ENTRY_DEF_PAD)) { Tcl_DStringAppend(resultPtr, " -padx ", -1); sprintf(string, "{%d %d}", entryPtr->padLeft, entryPtr->padRight); Tcl_DStringAppend(resultPtr, string, -1); } if ((entryPtr->padTop != ENTRY_DEF_PAD) || (entryPtr->padBottom != ENTRY_DEF_PAD)) { Tcl_DStringAppend(resultPtr, " -pady ", -1); sprintf(string, "{%d %d}", entryPtr->padTop, entryPtr->padBottom); Tcl_DStringAppend(resultPtr, string, -1); } if (entryPtr->fill != ENTRY_DEF_FILL) { Tcl_DStringAppend(resultPtr, " -fill ", -1); Tcl_DStringAppend(resultPtr, Blt_NameOfFill(entryPtr->fill), -1); } if (entryPtr->column.control != ENTRY_DEF_CONTROL) { Tcl_DStringAppend(resultPtr, " -columncontrol ", -1); Tcl_DStringAppend(resultPtr, NameOfControl(entryPtr->column.control), -1); } if (entryPtr->row.control != ENTRY_DEF_CONTROL) { Tcl_DStringAppend(resultPtr, " -rowcontrol ", -1); Tcl_DStringAppend(resultPtr, NameOfControl(entryPtr->row.control), -1); } if ((entryPtr->reqWidth.nom != LIMITS_NOM) || (entryPtr->reqWidth.min != LIMITS_MIN) || (entryPtr->reqWidth.max != LIMITS_MAX)) { Tcl_DStringAppend(resultPtr, " -reqwidth {", -1); Tcl_DStringAppend(resultPtr, NameOfLimits(&(entryPtr->reqWidth)), -1); Tcl_DStringAppend(resultPtr, "}", -1); } if ((entryPtr->reqHeight.nom != LIMITS_NOM) || (entryPtr->reqHeight.min != LIMITS_MIN) || (entryPtr->reqHeight.max != LIMITS_MAX)) { Tcl_DStringAppend(resultPtr, " -reqheight {", -1); Tcl_DStringAppend(resultPtr, NameOfLimits(&(entryPtr->reqHeight)), -1); Tcl_DStringAppend(resultPtr, "}", -1); } } /* * ---------------------------------------------------------------------------- * * InfoEntry -- * * Returns the name, position and options of a widget in the table. * * Results: * Returns a standard Tcl result. A list of the widget * attributes is left in interp->result. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int InfoEntry(interp, tablePtr, entryPtr) Tcl_Interp *interp; Table *tablePtr; Entry *entryPtr; { Tcl_DString dString; if (entryPtr->tablePtr != tablePtr) { Tcl_AppendResult(interp, "widget \"", Tk_PathName(entryPtr->tkwin), "\" does not belong to table \"", Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL); return TCL_ERROR; } Tcl_DStringInit(&dString); PrintEntry(entryPtr, &dString); Tcl_DStringResult(interp, &dString); return TCL_OK; } /* * ---------------------------------------------------------------------------- * * CreateRowColumn -- * * Creates and initializes a structure that manages the size of a * row or column in the table. There will be one of these * structures allocated for each row and column in the table, * regardless if a widget is contained in it or not. * * Results: * Returns a pointer to the newly allocated row or column * structure. * * ---------------------------------------------------------------------------- */ static RowColumn * CreateRowColumn() { RowColumn *rcPtr; rcPtr = Blt_Malloc(sizeof(RowColumn)); rcPtr->resize = ROWCOL_DEF_RESIZE; ResetLimits(&(rcPtr->reqSize)); rcPtr->nomSize = LIMITS_NOM; rcPtr->pad.side1 = rcPtr->pad.side2 = ROWCOL_DEF_PAD; rcPtr->size = rcPtr->index = rcPtr->minSpan = 0; rcPtr->weight = ROWCOL_DEF_WEIGHT; return rcPtr; } static PartitionInfo * ParseRowColumn2(tablePtr, string, numberPtr) Table *tablePtr; char *string; int *numberPtr; { char c; int n; PartitionInfo *infoPtr; c = tolower(string[0]); if (c == 'c') { infoPtr = &(tablePtr->columnInfo); } else if (c == 'r') { infoPtr = &(tablePtr->rowInfo); } else { Tcl_AppendResult(tablePtr->interp, "bad index \"", string, "\": must start with \"r\" or \"c\"", (char *)NULL); return NULL; } /* Handle row or column configuration queries */ if (Tcl_GetInt(tablePtr->interp, string + 1, &n) != TCL_OK) { return NULL; } *numberPtr = (int)n; return infoPtr; } static PartitionInfo * ParseRowColumn(tablePtr, string, numberPtr) Table *tablePtr; char *string; int *numberPtr; { int n; PartitionInfo *infoPtr; infoPtr = ParseRowColumn2(tablePtr, string, &n); if (infoPtr == NULL) { return NULL; } if ((n < 0) || (n >= Blt_ChainGetLength(infoPtr->chainPtr))) { Tcl_AppendResult(tablePtr->interp, "bad ", infoPtr->type, " index \"", string, "\"", (char *)NULL); return NULL; } *numberPtr = (int)n; return infoPtr; } /* * ---------------------------------------------------------------------------- * * GetRowColumn -- * * Gets the designated row or column from the table. If the row * or column index is greater than the size of the table, new * rows/columns will be automatically allocated. * * Results: * Returns a pointer to the row or column structure. * * ---------------------------------------------------------------------------- */ static RowColumn * GetRowColumn(infoPtr, n) PartitionInfo *infoPtr; int n; { Blt_ChainLink *linkPtr; RowColumn *rcPtr; register int i; for (i = Blt_ChainGetLength(infoPtr->chainPtr); i <= n; i++) { rcPtr = CreateRowColumn(); rcPtr->index = i; rcPtr->linkPtr = Blt_ChainAppend(infoPtr->chainPtr, (ClientData)rcPtr); } linkPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, n); if (linkPtr == NULL) { return NULL; } return Blt_ChainGetValue(linkPtr); } /* * ---------------------------------------------------------------------------- * * DeleteRowColumn -- * * Deletes a span of rows/columns from the table. The number of * rows/columns to be deleted is given by span. * * Results: * None. * * Side effects: * The size of the column partition array may be extended and * initialized. * * ---------------------------------------------------------------------------- */ static void DeleteRowColumn(tablePtr, infoPtr, rcPtr) Table *tablePtr; PartitionInfo *infoPtr; RowColumn *rcPtr; { Blt_ChainLink *linkPtr, *nextPtr; Entry *entryPtr; /* * Remove any entries that start in the row/column to be deleted. * They point to memory that will be freed. */ if (infoPtr->type == rowUid) { for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL; linkPtr = nextPtr) { nextPtr = Blt_ChainNextLink(linkPtr); entryPtr = Blt_ChainGetValue(linkPtr); if (entryPtr->row.rcPtr->index == rcPtr->index) { DestroyEntry(entryPtr); } } } else { for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL; linkPtr = nextPtr) { nextPtr = Blt_ChainNextLink(linkPtr); entryPtr = Blt_ChainGetValue(linkPtr); if (entryPtr->column.rcPtr->index == rcPtr->index) { DestroyEntry(entryPtr); } } } } /* * ---------------------------------------------------------------------------- * * ConfigureRowColumn -- * * This procedure is called to process an argv/argc list in order * to configure a row or column in the table geometry manager. * * Results: * The return value is a standard Tcl result. If TCL_ERROR is * returned, then interp->result holds an error message. * * Side effects: * Partition configuration options (bounds, resize flags, etc) * get set. New partitions may be created as necessary. The * table is recalculated and arranged at the next idle point. * * ---------------------------------------------------------------------------- */ static int ConfigureRowColumn(tablePtr, infoPtr, pattern, argc, argv) Table *tablePtr; /* Table to be configured */ PartitionInfo *infoPtr; char *pattern; int argc; char **argv; { RowColumn *rcPtr; register Blt_ChainLink *linkPtr; char string[200]; int nMatches; nMatches = 0; for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); sprintf(string, "%c%d", pattern[0], rcPtr->index); if (Tcl_StringMatch(string, pattern)) { if (argc == 0) { return Tk_ConfigureInfo(tablePtr->interp, tablePtr->tkwin, infoPtr->configSpecs, (char *)rcPtr, NULL, 0); } else if (argc == 1) { return Tk_ConfigureInfo(tablePtr->interp, tablePtr->tkwin, infoPtr->configSpecs, (char *)rcPtr, argv[0], 0); } else { if (Tk_ConfigureWidget(tablePtr->interp, tablePtr->tkwin, infoPtr->configSpecs, argc, argv, (char *)rcPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) { return TCL_ERROR; } } nMatches++; } } if (nMatches == 0) { int n; /* * We found no existing partitions matching this pattern, so * see if this designates an new partition (one beyond the * current range). */ if ((Tcl_GetInt(NULL, pattern + 1, &n) != TCL_OK) || (n < 0)) { Tcl_AppendResult(tablePtr->interp, "pattern \"", pattern, "\" matches no ", infoPtr->type, " in table \"", Tk_PathName(tablePtr->tkwin), "\"", (char *)NULL); return TCL_ERROR; } rcPtr = GetRowColumn(infoPtr, n); assert(rcPtr); if (Tk_ConfigureWidget(tablePtr->interp, tablePtr->tkwin, infoPtr->configSpecs, argc, argv, (char *)rcPtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) { return TCL_ERROR; } } EventuallyArrangeTable(tablePtr); return TCL_OK; } static void PrintRowColumn(interp, infoPtr, rcPtr, resultPtr) Tcl_Interp *interp; PartitionInfo *infoPtr; RowColumn *rcPtr; Tcl_DString *resultPtr; { char string[200]; char *padFmt, *sizeFmt; if (infoPtr->type == rowUid) { padFmt = " -pady {%d %d}"; sizeFmt = " -height {%s}"; } else { padFmt = " -padx {%d %d}"; sizeFmt = " -width {%s}"; } if (rcPtr->resize != ROWCOL_DEF_RESIZE) { Tcl_DStringAppend(resultPtr, " -resize ", -1); Tcl_DStringAppend(resultPtr, NameOfResize(rcPtr->resize), -1); } if ((rcPtr->pad.side1 != ROWCOL_DEF_PAD) || (rcPtr->pad.side2 != ROWCOL_DEF_PAD)) { sprintf(string, padFmt, rcPtr->pad.side1, rcPtr->pad.side2); Tcl_DStringAppend(resultPtr, string, -1); } if (rcPtr->weight != ROWCOL_DEF_WEIGHT) { Tcl_DStringAppend(resultPtr, " -weight ", -1); Tcl_DStringAppend(resultPtr, Blt_Dtoa(interp, rcPtr->weight), -1); } if ((rcPtr->reqSize.min != LIMITS_MIN) || (rcPtr->reqSize.nom != LIMITS_NOM) || (rcPtr->reqSize.max != LIMITS_MAX)) { sprintf(string, sizeFmt, NameOfLimits(&(rcPtr->reqSize))); Tcl_DStringAppend(resultPtr, string, -1); } } /* * ---------------------------------------------------------------------------- * * InfoRowColumn -- * * Returns the options of a partition in the table. * * Results: * Returns a standard Tcl result. A list of the partition * attributes is left in interp->result. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int InfoRowColumn(tablePtr, interp, pattern) Table *tablePtr; Tcl_Interp *interp; char *pattern; { RowColumn *rcPtr; char string[200]; PartitionInfo *infoPtr; char c; Blt_ChainLink *linkPtr, *lastPtr; Tcl_DString dString; c = pattern[0]; if ((c == 'r') || (c == 'R')) { infoPtr = &(tablePtr->rowInfo); } else { infoPtr = &(tablePtr->columnInfo); } Tcl_DStringInit(&dString); lastPtr = Blt_ChainLastLink(infoPtr->chainPtr); for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); sprintf(string, "%c%d", infoPtr->type[0], rcPtr->index); if (Tcl_StringMatch(string, pattern)) { Tcl_DStringAppend(&dString, string, -1); PrintRowColumn(interp, infoPtr, rcPtr, &dString); if (linkPtr != lastPtr) { Tcl_DStringAppend(&dString, " \\\n", -1); } else { Tcl_DStringAppend(&dString, "\n", -1); } } } Tcl_DStringResult(interp, &dString); return TCL_OK; } /* * ---------------------------------------------------------------------------- * * InitSpan -- * * Checks the size of the column partitions and extends the size * if a larger array is needed. * * Results: * Returns 1 if the column exists. Otherwise 0 is returned and * interp->result contains an error message. * * Side effects: * The size of the column partition array may be extended and * initialized. * * ---------------------------------------------------------------------------- */ static RowColumn * InitSpan(infoPtr, start, span) PartitionInfo *infoPtr; int start, span; { int length; RowColumn *rcPtr; register int i; Blt_ChainLink *linkPtr; length = Blt_ChainGetLength(infoPtr->chainPtr); for (i = length; i < (start + span); i++) { rcPtr = CreateRowColumn(); rcPtr->index = i; rcPtr->linkPtr = Blt_ChainAppend(infoPtr->chainPtr, (ClientData)rcPtr); } linkPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, start); return Blt_ChainGetValue(linkPtr); } /* * ---------------------------------------------------------------------------- * * Blt_GetTable -- * * Searches for a table associated by the path name of the widget * container. * * Errors may occur because * 1) pathName isn't a valid for any Tk widget, or * 2) there's no table associated with that widget as a container. * * Results: * If a table entry exists, a pointer to the Table structure is * returned. Otherwise NULL is returned. * * ---------------------------------------------------------------------------- */ /*LINTLIBRARY*/ int Blt_GetTable(dataPtr, interp, pathName, tablePtrPtr) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; /* Interpreter to report errors back to. */ char *pathName; /* Path name of the container widget. */ Table **tablePtrPtr; { Blt_HashEntry *hPtr; Tk_Window tkwin; tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp)); if (tkwin == NULL) { return TCL_ERROR; } hPtr = Blt_FindHashEntry(&(dataPtr->tableTable), (char *)tkwin); if (hPtr == NULL) { Tcl_AppendResult(interp, "no table associated with widget \"", pathName, "\"", (char *)NULL); return TCL_ERROR; } *tablePtrPtr = (Table *)Blt_GetHashValue(hPtr); return TCL_OK; } /* * ---------------------------------------------------------------------------- * * CreateTable -- * * This procedure creates and initializes a new Table structure * with tkwin as its container widget. The internal structures * associated with the table are initialized. * * Results: * Returns the pointer to the new Table structure describing the * new table geometry manager. If an error occurred, the return * value will be NULL and an error message is left in * interp->result. * * Side effects: * Memory is allocated and initialized, an event handler is set * up to watch tkwin, etc. * * ---------------------------------------------------------------------------- */ static Table * CreateTable(dataPtr, interp, pathName) TableInterpData *dataPtr; Tcl_Interp *interp; /* Interpreter associated with table. */ char *pathName; /* Path name of the container widget to be * associated with the new table. */ { register Table *tablePtr; Tk_Window tkwin; int dummy; Blt_HashEntry *hPtr; tkwin = Tk_NameToWindow(interp, pathName, Tk_MainWindow(interp)); if (tkwin == NULL) { return NULL; } tablePtr = Blt_Calloc(1, sizeof(Table)); assert(tablePtr); tablePtr->tkwin = tkwin; tablePtr->interp = interp; tablePtr->rowInfo.type = rowUid; tablePtr->rowInfo.configSpecs = rowConfigSpecs; tablePtr->rowInfo.chainPtr = Blt_ChainCreate(); tablePtr->columnInfo.type = columnUid; tablePtr->columnInfo.configSpecs = columnConfigSpecs; tablePtr->columnInfo.chainPtr = Blt_ChainCreate(); tablePtr->propagate = TRUE; tablePtr->arrangeProc = ArrangeTable; Blt_InitHashTable(&(tablePtr->entryTable), BLT_ONE_WORD_KEYS); tablePtr->findEntryProc = FindEntry; ResetLimits(&(tablePtr->reqWidth)); ResetLimits(&(tablePtr->reqHeight)); tablePtr->chainPtr = Blt_ChainCreate(); tablePtr->rowInfo.list = Blt_ListCreate(BLT_ONE_WORD_KEYS); tablePtr->columnInfo.list = Blt_ListCreate(BLT_ONE_WORD_KEYS); Tk_CreateEventHandler(tablePtr->tkwin, StructureNotifyMask, TableEventProc, (ClientData)tablePtr); hPtr = Blt_CreateHashEntry(&(dataPtr->tableTable), (char *)tkwin, &dummy); tablePtr->hashPtr = hPtr; tablePtr->tablePtr = &(dataPtr->tableTable); Blt_SetHashValue(hPtr, (ClientData)tablePtr); return tablePtr; } /* * ---------------------------------------------------------------------------- * * ConfigureTable -- * * This procedure is called to process an argv/argc list in order * to configure the table geometry manager. * * Results: * The return value is a standard Tcl result. If TCL_ERROR is * returned, then interp->result contains an error message. * * Side effects: * Table configuration options (-padx, -pady, etc.) get set. The * table is recalculated and arranged at the next idle point. * * ---------------------------------------------------------------------------- */ static int ConfigureTable(tablePtr, interp, argc, argv) Table *tablePtr; /* Table to be configured */ Tcl_Interp *interp; /* Interpreter to report results back to */ int argc; char **argv; /* Option-value pairs */ { if (argc == 0) { return Tk_ConfigureInfo(interp, tablePtr->tkwin, tableConfigSpecs, (char *)tablePtr, (char *)NULL, 0); } else if (argc == 1) { return Tk_ConfigureInfo(interp, tablePtr->tkwin, tableConfigSpecs, (char *)tablePtr, argv[0], 0); } if (Tk_ConfigureWidget(interp, tablePtr->tkwin, tableConfigSpecs, argc, argv, (char *)tablePtr, TK_CONFIG_ARGV_ONLY) != TCL_OK) { return TCL_ERROR; } /* Arrange for the table layout to be computed at the next idle point. */ tablePtr->flags |= REQUEST_LAYOUT; EventuallyArrangeTable(tablePtr); return TCL_OK; } static void PrintTable(tablePtr, resultPtr) Table *tablePtr; Tcl_DString *resultPtr; { char string[200]; if ((tablePtr->padLeft != TABLE_DEF_PAD) || (tablePtr->padRight != TABLE_DEF_PAD)) { sprintf(string, " -padx {%d %d}", tablePtr->padLeft, tablePtr->padRight); Tcl_DStringAppend(resultPtr, string, -1); } if ((tablePtr->padTop != TABLE_DEF_PAD) || (tablePtr->padBottom != TABLE_DEF_PAD)) { sprintf(string, " -pady {%d %d}", tablePtr->padTop, tablePtr->padBottom); Tcl_DStringAppend(resultPtr, string, -1); } if (!tablePtr->propagate) { Tcl_DStringAppend(resultPtr, " -propagate no", -1); } if ((tablePtr->reqWidth.min != LIMITS_MIN) || (tablePtr->reqWidth.nom != LIMITS_NOM) || (tablePtr->reqWidth.max != LIMITS_MAX)) { Tcl_DStringAppend(resultPtr, " -reqwidth {%s}", -1); Tcl_DStringAppend(resultPtr, NameOfLimits(&(tablePtr->reqWidth)), -1); } if ((tablePtr->reqHeight.min != LIMITS_MIN) || (tablePtr->reqHeight.nom != LIMITS_NOM) || (tablePtr->reqHeight.max != LIMITS_MAX)) { Tcl_DStringAppend(resultPtr, " -reqheight {%s}", -1); Tcl_DStringAppend(resultPtr, NameOfLimits(&(tablePtr->reqHeight)), -1); } } /* * ---------------------------------------------------------------------------- * * DestroyPartitions -- * * Clear each of the lists managing the entries. The entries in * the lists of row and column spans are themselves lists which * need to be cleared. * * ---------------------------------------------------------------------------- */ static void DestroyPartitions(infoPtr) PartitionInfo *infoPtr; { if (infoPtr->list != NULL) { Blt_Chain *chainPtr; Blt_ListNode node; for (node = Blt_ListFirstNode(infoPtr->list); node != NULL; node = Blt_ListNextNode(node)) { chainPtr = (Blt_Chain *)Blt_ListGetValue(node); if (chainPtr != NULL) { Blt_ChainDestroy(chainPtr); } } Blt_ListDestroy(infoPtr->list); } if (infoPtr->chainPtr != NULL) { Blt_ChainLink *linkPtr; RowColumn *rcPtr; for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); Blt_Free(rcPtr); } Blt_ChainDestroy(infoPtr->chainPtr); } } /* * ---------------------------------------------------------------------------- * * DestroyTable -- * * This procedure is invoked by Tcl_EventuallyFree or Tcl_Release to * clean up the Table structure at a safe time (when no-one is using * it anymore). * * Results: * None. * * Side effects: * Everything associated with the table geometry manager is freed up. * * ---------------------------------------------------------------------------- */ static void DestroyTable(dataPtr) DestroyData dataPtr; /* Table structure */ { Blt_ChainLink *linkPtr; Entry *entryPtr; Table *tablePtr = (Table *)dataPtr; /* Release the chain of entries. */ for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { entryPtr = Blt_ChainGetValue(linkPtr); entryPtr->linkPtr = NULL; /* Don't disrupt this chain of entries */ DestroyEntry(entryPtr); } Blt_ChainDestroy(tablePtr->chainPtr); DestroyPartitions(&(tablePtr->rowInfo)); DestroyPartitions(&(tablePtr->columnInfo)); Blt_DeleteHashTable(&(tablePtr->entryTable)); if (tablePtr->hashPtr != NULL) { Blt_DeleteHashEntry(tablePtr->tablePtr, tablePtr->hashPtr); } Blt_Free(tablePtr); } /* * ---------------------------------------------------------------------------- * * BinEntry -- * * Adds the entry to the lists of both row and column spans. The * layout of the table is done in order of partition spans, from * shorted to longest. The widgets spanning a particular number of * partitions are stored in a linked list. Each list is in turn, * contained within a master list. * * Results: * None. * * Side effects: * The entry is added to both the lists of row and columns spans. * This will effect the layout of the widgets. * * ---------------------------------------------------------------------------- */ static void BinEntry(tablePtr, entryPtr) Table *tablePtr; Entry *entryPtr; { Blt_ListNode node; Blt_List list; Blt_Chain *chainPtr; int key; /* * Remove the entry from both row and column lists. It will be * re-inserted into the table at the new position. */ if (entryPtr->column.linkPtr != NULL) { Blt_ChainUnlinkLink(entryPtr->column.chainPtr, entryPtr->column.linkPtr); } if (entryPtr->row.linkPtr != NULL) { Blt_ChainUnlinkLink(entryPtr->row.chainPtr, entryPtr->row.linkPtr); } list = tablePtr->rowInfo.list; key = 0; /* Initialize key to bogus span */ for (node = Blt_ListFirstNode(list); node != NULL; node = Blt_ListNextNode(node)) { key = (int)Blt_ListGetKey(node); if (entryPtr->row.span <= key) { break; } } if (key != entryPtr->row.span) { Blt_ListNode newNode; /* * Create a new list (bucket) to hold entries of that size * span and and link it into the list of buckets. */ newNode = Blt_ListCreateNode(list, (char *)entryPtr->row.span); Blt_ListSetValue(newNode, (char *)Blt_ChainCreate()); Blt_ListLinkBefore(list, newNode, node); node = newNode; } chainPtr = (Blt_Chain *) Blt_ListGetValue(node); if (entryPtr->row.linkPtr == NULL) { entryPtr->row.linkPtr = Blt_ChainAppend(chainPtr, entryPtr); } else { Blt_ChainLinkBefore(chainPtr, entryPtr->row.linkPtr, NULL); } entryPtr->row.chainPtr = chainPtr; list = tablePtr->columnInfo.list; key = 0; for (node = Blt_ListFirstNode(list); node != NULL; node = Blt_ListNextNode(node)) { key = (int)Blt_ListGetKey(node); if (entryPtr->column.span <= key) { break; } } if (key != entryPtr->column.span) { Blt_ListNode newNode; /* * Create a new list (bucket) to hold entries of that size * span and and link it into the list of buckets. */ newNode = Blt_ListCreateNode(list, (char *)entryPtr->column.span); Blt_ListSetValue(newNode, (char *)Blt_ChainCreate()); Blt_ListLinkBefore(list, newNode, node); node = newNode; } chainPtr = (Blt_Chain *) Blt_ListGetValue(node); /* Add the new entry to the span bucket */ if (entryPtr->column.linkPtr == NULL) { entryPtr->column.linkPtr = Blt_ChainAppend(chainPtr, entryPtr); } else { Blt_ChainLinkBefore(chainPtr, entryPtr->column.linkPtr, NULL); } entryPtr->column.chainPtr = chainPtr; } /* * ---------------------------------------------------------------------------- * * ParseIndex -- * * Parse the entry index string and return the row and column * numbers in their respective parameters. The format of a table * entry index is row,column where row is the row number and * column is the column number. Rows and columns are numbered * starting from zero. * * Results: * Returns a standard Tcl result. If TCL_OK is returned, the row * and column numbers are returned via rowPtr and columnPtr * respectively. * * ---------------------------------------------------------------------------- */ static int ParseIndex(interp, string, rowPtr, columnPtr) Tcl_Interp *interp; char *string; int *rowPtr, *columnPtr; { char *comma; long row, column; int result; comma = strchr(string, ','); if (comma == NULL) { Tcl_AppendResult(interp, "bad index \"", string, "\": should be \"row,column\"", (char *)NULL); return TCL_ERROR; } *comma = '\0'; result = ((Tcl_ExprLong(interp, string, &row) != TCL_OK) || (Tcl_ExprLong(interp, comma + 1, &column) != TCL_OK)); *comma = ','; /* Repair the argument */ if (result) { return TCL_ERROR; } if ((row < 0) || (row > (long)USHRT_MAX)) { Tcl_AppendResult(interp, "bad index \"", string, "\": row is out of range", (char *)NULL); return TCL_ERROR; } if ((column < 0) || (column > (long)USHRT_MAX)) { Tcl_AppendResult(interp, "bad index \"", string, "\": column is out of range", (char *)NULL); return TCL_ERROR; } *rowPtr = (int)row; *columnPtr = (int)column; return TCL_OK; } /* * ---------------------------------------------------------------------------- * * ManageEntry -- * * Inserts the given widget into the table at a given row and * column position. The widget can already be managed by this or * another table. The widget will be simply moved to the new * location in this table. * * The new widget is inserted into both a hash table (this is * used to locate the information associated with the widget) and * a list (used to indicate relative ordering of widgets). * * Results: * Returns a standard Tcl result. If an error occurred, TCL_ERROR is * returned and an error message is left in interp->result. * * Side Effects: * The table is re-computed and arranged at the next idle point. * * ---------------------------------------------------------------------------- */ static int ManageEntry(interp, tablePtr, tkwin, row, column, argc, argv) Tcl_Interp *interp; Table *tablePtr; Tk_Window tkwin; int row, column; int argc; char **argv; { Entry *entryPtr; int result = TCL_OK; entryPtr = FindEntry(tablePtr, tkwin); if ((entryPtr != NULL) && (entryPtr->tablePtr != tablePtr)) { /* The entry for the widget already exists. If it's * managed by another table, delete it. */ DestroyEntry(entryPtr); entryPtr = NULL; } if (entryPtr == NULL) { entryPtr = CreateEntry(tablePtr, tkwin); if (entryPtr == NULL) { return TCL_ERROR; } } if (argc > 0) { result = Tk_ConfigureWidget(tablePtr->interp, entryPtr->tkwin, entryConfigSpecs, argc, argv, (char *)entryPtr, TK_CONFIG_ARGV_ONLY); } if ((entryPtr->column.span < 1) || (entryPtr->row.span < 1)) { Tcl_AppendResult(tablePtr->interp, "bad span specified for \"", Tk_PathName(tkwin), "\"", (char *)NULL); DestroyEntry(entryPtr); return TCL_ERROR; } entryPtr->column.rcPtr = InitSpan(&(tablePtr->columnInfo), column, entryPtr->column.span); entryPtr->row.rcPtr = InitSpan(&(tablePtr->rowInfo), row, entryPtr->row.span); /* * Insert the entry into both the row and column layout lists */ BinEntry(tablePtr, entryPtr); return result; } /* * ---------------------------------------------------------------------------- * * BuildTable -- * * Processes an argv/argc list of table entries to add and * configure new widgets into the table. A table entry consists * of the widget path name, table index, and optional * configuration options. The first argument in the argv list is * the name of the table. If no table exists for the given * widget, a new one is created. * * Results: * Returns a standard Tcl result. If an error occurred, * TCL_ERROR is returned and an error message is left in * interp->result. * * Side Effects: * Memory is allocated, a new table is possibly created, etc. * The table is re-computed and arranged at the next idle point. * * ---------------------------------------------------------------------------- */ static int BuildTable(tablePtr, interp, argc, argv) Table *tablePtr; /* Table to manage new widgets */ Tcl_Interp *interp; /* Interpreter to report errors back to */ int argc; /* */ char **argv; /* List of widgets, indices, and options */ { Tk_Window tkwin; int row, column; int nextRow, nextColumn; register int i; /* Process any options specific to the table */ for (i = 2; i < argc; i += 2) { if (argv[i][0] != '-') { break; } } if (i > argc) { i = argc; } if (i > 2) { if (ConfigureTable(tablePtr, interp, i - 2, argv + 2) != TCL_OK) { return TCL_ERROR; } } nextRow = tablePtr->nRows; nextColumn = 0; argc -= i, argv += i; while (argc > 0) { /* * Allow the name of the widget and row/column index to be * specified in any order. */ if (argv[0][0] == '.') { tkwin = Tk_NameToWindow(interp, argv[0], tablePtr->tkwin); if (tkwin == NULL) { return TCL_ERROR; } if ((argc == 1) || (argv[1][0] == '-')) { /* No row,column index, use defaults instead */ row = nextRow, column = nextColumn; argc--, argv++; } else { if (ParseIndex(interp, argv[1], &row, &column) != TCL_OK) { return TCL_ERROR; /* Invalid row,column index */ } /* Skip over the widget pathname and table index. */ argc -= 2, argv += 2; } } else { if (ParseIndex(interp, argv[0], &row, &column) != TCL_OK) { return TCL_ERROR; } if (argc == 1) { Tcl_AppendResult(interp, "missing widget pathname after \"", argv[0], "\"", (char *)NULL); return TCL_ERROR; } tkwin = Tk_NameToWindow(interp, argv[1], tablePtr->tkwin); if (tkwin == NULL) { return TCL_ERROR; } /* Skip over the widget pathname and table index. */ argc -= 2, argv += 2; } /* Find the end of the widget's option-value pairs */ for (i = 0; i < argc; i += 2) { if (argv[i][0] != '-') { break; } } if (i > argc) { i = argc; } if (ManageEntry(interp, tablePtr, tkwin, row, column, i, argv) != TCL_OK) { return TCL_ERROR; } nextColumn = column + 1; argc -= i, argv += i; } /* Arrange for the new table layout to be calculated. */ tablePtr->flags |= REQUEST_LAYOUT; EventuallyArrangeTable(tablePtr); Tcl_SetResult(interp, Tk_PathName(tablePtr->tkwin), TCL_VOLATILE); return TCL_OK; } /* * ---------------------------------------------------------------------------- * * ParseItem -- * * Parses a string representing an item in the table. An item * may be one of the following: * Rn - Row index, where n is the index of row * Cn - Column index, where n is the index of column * r,c - Cell index, where r is the row index and c * is the column index. * * Results: * Returns a standard Tcl result. If no error occurred, TCL_OK * is returned. *RowPtr* will return the row index. *ColumnPtr* * will return the column index. If the row or column index is * not applicable, -1 is returned via *rowPtr* or *columnPtr*. * * ---------------------------------------------------------------------------- */ static int ParseItem(tablePtr, string, rowPtr, columnPtr) Table *tablePtr; char *string; int *rowPtr, *columnPtr; { char c; long partNum; c = tolower(string[0]); *rowPtr = *columnPtr = -1; if (c == 'r') { if (Tcl_ExprLong(tablePtr->interp, string + 1, &partNum) != TCL_OK) { return TCL_ERROR; } if ((partNum < 0) || (partNum >= tablePtr->nRows)) { Tcl_AppendResult(tablePtr->interp, "row index \"", string, "\" is out of range", (char *)NULL); return TCL_ERROR; } *rowPtr = (int)partNum; } else if (c == 'c') { if (Tcl_ExprLong(tablePtr->interp, string + 1, &partNum) != TCL_OK) { return TCL_ERROR; } if ((partNum < 0) || (partNum >= tablePtr->nColumns)) { Tcl_AppendResult(tablePtr->interp, "column index \"", string, "\" is out of range", (char *)NULL); return TCL_ERROR; } *columnPtr = (int)partNum; } else { if (ParseIndex(tablePtr->interp, string, rowPtr, columnPtr) != TCL_OK) { return TCL_ERROR; /* Invalid row,column index */ } if ((*rowPtr < 0) || (*rowPtr >= tablePtr->nRows) || (*columnPtr < 0) || (*columnPtr >= tablePtr->nColumns)) { Tcl_AppendResult(tablePtr->interp, "index \"", string, "\" is out of range", (char *)NULL); return TCL_ERROR; } } return TCL_OK; } /* * ---------------------------------------------------------------------------- * * TranslateAnchor -- * * Translate the coordinates of a given bounding box based upon * the anchor specified. The anchor indicates where the given xy * position is in relation to the bounding box. * * nw --- n --- ne * | | x,y ---+ * w center e | | * | | +-----+ * sw --- s --- se * * Results: * The translated coordinates of the bounding box are returned. * * ---------------------------------------------------------------------------- */ static void TranslateAnchor(dx, dy, anchor, xPtr, yPtr) int dx, dy; /* Difference between outer and inner regions */ Tk_Anchor anchor; /* Direction of the anchor */ int *xPtr, *yPtr; { int x, y; x = y = 0; switch (anchor) { case TK_ANCHOR_NW: /* Upper left corner */ break; case TK_ANCHOR_W: /* Left center */ y = (dy / 2); break; case TK_ANCHOR_SW: /* Lower left corner */ y = dy; break; case TK_ANCHOR_N: /* Top center */ x = (dx / 2); break; case TK_ANCHOR_CENTER: /* Centered */ x = (dx / 2); y = (dy / 2); break; case TK_ANCHOR_S: /* Bottom center */ x = (dx / 2); y = dy; break; case TK_ANCHOR_NE: /* Upper right corner */ x = dx; break; case TK_ANCHOR_E: /* Right center */ x = dx; y = (dy / 2); break; case TK_ANCHOR_SE: /* Lower right corner */ x = dx; y = dy; break; } *xPtr = (*xPtr) + x; *yPtr = (*yPtr) + y; } /* * ---------------------------------------------------------------------------- * * GetReqWidth -- * * Returns the width requested by the widget starting in the * given entry. The requested space also includes any internal * padding which has been designated for this widget. * * The requested width of the widget is always bounded by the limits * set in entryPtr->reqWidth. * * Results: * Returns the requested width of the widget. * * ---------------------------------------------------------------------------- */ static int GetReqWidth(entryPtr) Entry *entryPtr; { int width; width = Tk_ReqWidth(entryPtr->tkwin) + (2 * entryPtr->ipadX); width = GetBoundedWidth(width, &(entryPtr->reqWidth)); return width; } /* * ---------------------------------------------------------------------------- * * GetReqHeight -- * * Returns the height requested by the widget starting in the * given entry. The requested space also includes any internal * padding which has been designated for this widget. * * The requested height of the widget is always bounded by the * limits set in entryPtr->reqHeight. * * Results: * Returns the requested height of the widget. * * ---------------------------------------------------------------------------- */ static int GetReqHeight(entryPtr) Entry *entryPtr; { int height; height = Tk_ReqHeight(entryPtr->tkwin) + (2 * entryPtr->ipadY); height = GetBoundedHeight(height, &(entryPtr->reqHeight)); return height; } /* * ---------------------------------------------------------------------------- * * GetTotalSpan -- * * Sums the row/column space requirements for the entire table. * * Results: * Returns the space currently used in the span of partitions. * * ---------------------------------------------------------------------------- */ static int GetTotalSpan(infoPtr) PartitionInfo *infoPtr; { register int spaceUsed; Blt_ChainLink *linkPtr; RowColumn *rcPtr; /* Start of partitions */ spaceUsed = 0; for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); spaceUsed += rcPtr->size; } return spaceUsed; } /* * ---------------------------------------------------------------------------- * * GetSpan -- * * Determines the space used by rows/columns for an entry. * * Results: * Returns the space currently used in the span of partitions. * * ---------------------------------------------------------------------------- */ static int GetSpan(infoPtr, entryPtr) PartitionInfo *infoPtr; Entry *entryPtr; { RowColumn *startPtr; register int spaceUsed; int count; Blt_ChainLink *linkPtr; RowColumn *rcPtr; /* Start of partitions */ int span; /* Number of partitions spanned */ if (infoPtr->type == rowUid) { rcPtr = entryPtr->row.rcPtr; span = entryPtr->row.span; } else { rcPtr = entryPtr->column.rcPtr; span = entryPtr->column.span; } count = spaceUsed = 0; linkPtr = rcPtr->linkPtr; startPtr = Blt_ChainGetValue(linkPtr); for ( /*empty*/ ; (linkPtr != NULL) && (count < span); linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); spaceUsed += rcPtr->size; count++; } /* * Subtract off the padding on either side of the span, since the * widget can't grow into it. */ spaceUsed -= (startPtr->pad.side1 + rcPtr->pad.side2 + infoPtr->ePad); return spaceUsed; } /* * ---------------------------------------------------------------------------- * * GrowSpan -- * * Expand the span by the amount of the extra space needed. This * procedure is used in LayoutPartitions to grow the partitions * to their minimum nominal size, starting from a zero width and * height space. * * This looks more complicated than it really is. The idea is to * make the size of the partitions correspond to the smallest * entry spans. For example, if widget A is in column 1 and * widget B spans both columns 0 and 1, any extra space needed to * fit widget B should come from column 0. * * On the first pass we try to add space to partitions which have * not been touched yet (i.e. have no nominal size). Since the * row and column lists are sorted in ascending order of the * number of rows or columns spanned, the space is distributed * amongst the smallest spans first. * * The second pass handles the case of widgets which have the * same span. For example, if A and B, which span the same * number of partitions are the only widgets to span column 1, * column 1 would grow to contain the bigger of the two slices of * space. * * If there is still extra space after the first two passes, this * means that there were no partitions of with no widget spans or * the same order span that could be expanded. The third pass * will try to remedy this by parcelling out the left over space * evenly among the rest of the partitions. * * On each pass, we have to keep iterating over the span, evenly * doling out slices of extra space, because we may hit partition * limits as space is donated. In addition, if there are left * over pixels because of round-off, this will distribute them as * evenly as possible. For the worst case, it will take *span* * passes to expand the span. * * Results: * None. * * Side Effects: * The partitions in the span may be expanded. * * ---------------------------------------------------------------------------- */ static void GrowSpan(infoPtr, entryPtr, growth) PartitionInfo *infoPtr; Entry *entryPtr; int growth; /* The amount of extra space needed to * grow the span. */ { register RowColumn *rcPtr; Blt_ChainLink *linkPtr; int spaceLeft, ration; int nOpen; /* # of partitions with space available */ register int n; RowColumn *startPtr; /* Starting (column/row) partition */ int span; /* Number of partitions in the span */ if (infoPtr->type == rowUid) { startPtr = entryPtr->row.rcPtr; span = entryPtr->row.span; } else { startPtr = entryPtr->column.rcPtr; span = entryPtr->column.span; } /* * ------------------------------------------------------------------------ * * Pass 1: First add space to rows/columns that haven't determined * their nominal sizes yet. * * ------------------------------------------------------------------------ */ nOpen = 0; /* Find out how many partitions have no size yet */ linkPtr = startPtr->linkPtr; for (n = 0; n < span; n++) { rcPtr = Blt_ChainGetValue(linkPtr); if ((rcPtr->nomSize == LIMITS_NOM) && (rcPtr->maxSize > rcPtr->size)) { nOpen++; } linkPtr = Blt_ChainNextLink(linkPtr); } while ((nOpen > 0) && (growth > 0)) { ration = growth / nOpen; if (ration == 0) { ration = 1; } linkPtr = startPtr->linkPtr; for (n = 0; (n < span) && (growth > 0); n++) { rcPtr = Blt_ChainGetValue(linkPtr); spaceLeft = rcPtr->maxSize - rcPtr->size; if ((rcPtr->nomSize == LIMITS_NOM) && (spaceLeft > 0)) { if (ration < spaceLeft) { growth -= ration; rcPtr->size += ration; } else { growth -= spaceLeft; rcPtr->size += spaceLeft; nOpen--; } rcPtr->minSpan = span; rcPtr->control = entryPtr; } linkPtr = Blt_ChainNextLink(linkPtr); } } /* * ------------------------------------------------------------------------ * * Pass 2: Add space to partitions which have the same minimum span * * ------------------------------------------------------------------------ */ nOpen = 0; linkPtr = startPtr->linkPtr; for (n = 0; n < span; n++) { rcPtr = Blt_ChainGetValue(linkPtr); if ((rcPtr->minSpan == span) && (rcPtr->maxSize > rcPtr->size)) { nOpen++; } linkPtr = Blt_ChainNextLink(linkPtr); } while ((nOpen > 0) && (growth > 0)) { ration = growth / nOpen; if (ration == 0) { ration = 1; } linkPtr = startPtr->linkPtr; for (n = 0; (n < span) && (growth > 0); n++) { rcPtr = Blt_ChainGetValue(linkPtr); spaceLeft = rcPtr->maxSize - rcPtr->size; if ((rcPtr->minSpan == span) && (spaceLeft > 0)) { if (ration < spaceLeft) { growth -= ration; rcPtr->size += ration; } else { growth -= spaceLeft; rcPtr->size += spaceLeft; nOpen--; } rcPtr->control = entryPtr; } linkPtr = Blt_ChainNextLink(linkPtr); } } /* * ------------------------------------------------------------------------ * * Pass 3: Try to expand all the partitions with space still available * * ------------------------------------------------------------------------ */ /* Find out how many partitions still have space available */ nOpen = 0; linkPtr = startPtr->linkPtr; for (n = 0; n < span; n++) { rcPtr = Blt_ChainGetValue(linkPtr); if ((rcPtr->resize & RESIZE_EXPAND) && (rcPtr->maxSize > rcPtr->size)) { nOpen++; } /* Set the nominal size of the row/column. */ rcPtr->nomSize = rcPtr->size; linkPtr = Blt_ChainNextLink(linkPtr); } while ((nOpen > 0) && (growth > 0)) { ration = growth / nOpen; if (ration == 0) { ration = 1; } linkPtr = startPtr->linkPtr; for (n = 0; (n < span) && (growth > 0); n++) { rcPtr = Blt_ChainGetValue(linkPtr); linkPtr = Blt_ChainNextLink(linkPtr); if (!(rcPtr->resize & RESIZE_EXPAND)) { continue; } spaceLeft = rcPtr->maxSize - rcPtr->size; if (spaceLeft > 0) { if (ration < spaceLeft) { growth -= ration; rcPtr->size += ration; } else { growth -= spaceLeft; rcPtr->size += spaceLeft; nOpen--; } rcPtr->nomSize = rcPtr->size; rcPtr->control = entryPtr; } } } } /* * ---------------------------------------------------------------------------- * * AdjustPartitions -- * * Adjust the span by the amount of the extra space needed. If * the amount (adjustSpace) is negative, shrink the span, * otherwise expand it. Size constraints on the partitions may * prevent any or all of the spacing adjustments. * * This is very much like the GrowSpan procedure, but in this * case we are shrinking or expanding all the (row or column) * partitions. It uses a two pass approach, first giving space to * partitions which not are smaller/larger than their nominal * sizes. This is because constraints on the partitions may cause * resizing to be non-linear. * * If there is still extra space, this means that all partitions * are at least to their nominal sizes. The second pass will try * to add/remove the left over space evenly among all the * partitions which still have space available. * * Results: * None. * * Side Effects: * The size of the partitions in the span may be increased or * decreased. * * ---------------------------------------------------------------------------- */ static void AdjustPartitions(infoPtr, adjustment) PartitionInfo *infoPtr; /* Array of (column/row) partitions */ int adjustment; /* The amount of extra space to grow or shrink * the span. If negative, it represents the * amount of space to remove */ { register RowColumn *rcPtr; int ration; /* Amount of space to ration to each * row/column. */ int delta; /* Amount of space needed */ int spaceLeft; /* Amount of space still available */ int size; /* Amount of space requested for a particular * row/column. */ int nOpen; /* Number of rows/columns that still can * be adjusted. */ Blt_Chain *chainPtr; Blt_ChainLink *linkPtr; double totalWeight; chainPtr = infoPtr->chainPtr; /* * ------------------------------------------------------------------------ * * Pass 1: First adjust the size of rows/columns that still haven't * reached their nominal size. * * ------------------------------------------------------------------------ */ delta = adjustment; nOpen = 0; totalWeight = 0.0; for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); if (rcPtr->weight > 0.0) { if (delta < 0) { spaceLeft = rcPtr->size - rcPtr->nomSize; } else { spaceLeft = rcPtr->nomSize - rcPtr->size; } if (spaceLeft > 0) { nOpen++; totalWeight += rcPtr->weight; } } } while ((nOpen > 0) && (totalWeight > 0.0) && (delta != 0)) { ration = (int)(delta / totalWeight); if (ration == 0) { ration = (delta > 0) ? 1 : -1; } for (linkPtr = Blt_ChainFirstLink(chainPtr); (linkPtr != NULL) && (delta != 0); linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); if (rcPtr->weight > 0.0) { spaceLeft = rcPtr->nomSize - rcPtr->size; if (((delta > 0) && (spaceLeft > 0)) || ((delta < 0) && (spaceLeft < 0))) { size = (int)(ration * rcPtr->weight); if (size > delta) { size = delta; } if (ABS(size) < ABS(spaceLeft)) { delta -= size; rcPtr->size += size; } else { delta -= spaceLeft; rcPtr->size += spaceLeft; nOpen--; totalWeight -= rcPtr->weight; } } } } } /* * ------------------------------------------------------------------------ * * Pass 2: Adjust the partitions with space still available * * ------------------------------------------------------------------------ */ nOpen = 0; totalWeight = 0.0; for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); if (rcPtr->weight > 0.0) { if (delta > 0) { spaceLeft = rcPtr->maxSize - rcPtr->size; } else { spaceLeft = rcPtr->size - rcPtr->minSize; } if (spaceLeft > 0) { nOpen++; totalWeight += rcPtr->weight; } } } while ((nOpen > 0) && (totalWeight > 0.0) && (delta != 0)) { ration = (int)(delta / totalWeight); if (ration == 0) { ration = (delta > 0) ? 1 : -1; } linkPtr = Blt_ChainFirstLink(chainPtr); for ( /*empty*/ ; (linkPtr != NULL) && (delta != 0); linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); if (rcPtr->weight > 0.0) { if (delta > 0) { spaceLeft = rcPtr->maxSize - rcPtr->size; } else { spaceLeft = rcPtr->minSize - rcPtr->size; } if (((delta > 0) && (spaceLeft > 0)) || ((delta < 0) && (spaceLeft < 0))) { size = (int)(ration * rcPtr->weight); if (size > delta) { size = delta; } if (ABS(size) < ABS(spaceLeft)) { delta -= size; rcPtr->size += size; } else { delta -= spaceLeft; rcPtr->size += spaceLeft; nOpen--; totalWeight -= rcPtr->weight; } } } } } } /* * ---------------------------------------------------------------------------- * * ResetPartitions -- * * Sets/resets the size of each row and column partition to the * minimum limit of the partition (this is usually zero). This * routine gets called when new widgets are added, deleted, or * resized. * * Results: * None. * * Side Effects: * The size of each partition is re-initialized to its minimum * size. * * ---------------------------------------------------------------------------- */ static void ResetPartitions(tablePtr, infoPtr, limitsProc) Table *tablePtr; PartitionInfo *infoPtr; LimitsProc *limitsProc; { register RowColumn *rcPtr; register Blt_ChainLink *linkPtr; int pad, size; for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); /* * The constraint procedure below also has the desired * side-effect of setting the minimum, maximum, and nominal * values to the requested size of its associated widget (if * one exists). */ size = (*limitsProc) (0, &(rcPtr->reqSize)); pad = PADDING(rcPtr->pad) + infoPtr->ePad; if (rcPtr->reqSize.flags & LIMITS_SET_NOM) { /* * This could be done more cleanly. We want to ensure * that the requested nominal size is not overridden when * determining the normal sizes. So temporarily fix min * and max to the nominal size and reset them back later. */ rcPtr->minSize = rcPtr->maxSize = rcPtr->size = rcPtr->nomSize = size + pad; } else { /* The range defaults to 0..MAXINT */ rcPtr->minSize = rcPtr->reqSize.min + pad; rcPtr->maxSize = rcPtr->reqSize.max + pad; rcPtr->nomSize = LIMITS_NOM; rcPtr->size = pad; } rcPtr->minSpan = 0; rcPtr->control = NULL; rcPtr->count = 0; } } /* * ---------------------------------------------------------------------------- * * SetNominalSizes * * Sets the normal sizes for each partition. The partition size * is the requested widget size plus an amount of padding. In * addition, adjust the min/max bounds of the partition depending * upon the resize flags (whether the partition can be expanded * or shrunk from its normal size). * * Results: * Returns the total space needed for the all the partitions. * * Side Effects: * The nominal size of each partition is set. This is later used * to determine how to shrink or grow the table if the container * can't be resized to accommodate the exact size requirements * of all the partitions. * * ---------------------------------------------------------------------------- */ static int SetNominalSizes(tablePtr, infoPtr) Table *tablePtr; PartitionInfo *infoPtr; { register RowColumn *rcPtr; Blt_ChainLink *linkPtr; int pad, size, total; total = 0; for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); pad = PADDING(rcPtr->pad) + infoPtr->ePad; /* * Restore the real bounds after temporarily setting nominal * size. These values may have been set in ResetPartitions to * restrict the size of the paritition to the requested range. */ rcPtr->minSize = rcPtr->reqSize.min + pad; rcPtr->maxSize = rcPtr->reqSize.max + pad; size = rcPtr->size; if (size > rcPtr->maxSize) { size = rcPtr->maxSize; } else if (size < rcPtr->minSize) { size = rcPtr->minSize; } if ((infoPtr->ePad > 0) && (size < tablePtr->editPtr->minSize)) { size = tablePtr->editPtr->minSize; } rcPtr->nomSize = rcPtr->size = size; /* * If a partition can't be resized (to either expand or * shrink), hold its respective limit at its normal size. */ if (!(rcPtr->resize & RESIZE_EXPAND)) { rcPtr->maxSize = rcPtr->nomSize; } if (!(rcPtr->resize & RESIZE_SHRINK)) { rcPtr->minSize = rcPtr->nomSize; } if (rcPtr->control == NULL) { /* If a row/column contains no entries, then its size * should be locked. */ if (rcPtr->resize & RESIZE_VIRGIN) { rcPtr->maxSize = rcPtr->minSize = size; } else { if (!(rcPtr->resize & RESIZE_EXPAND)) { rcPtr->maxSize = size; } if (!(rcPtr->resize & RESIZE_SHRINK)) { rcPtr->minSize = size; } } rcPtr->nomSize = size; } total += rcPtr->nomSize; } return total; } /* * ---------------------------------------------------------------------------- * * LockPartitions * * Sets the maximum size of a row or column, if the partition * has a widget that controls it. * * Results: * None. * * ---------------------------------------------------------------------------- */ static void LockPartitions(infoPtr) PartitionInfo *infoPtr; { register RowColumn *rcPtr; Blt_ChainLink *linkPtr; for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); if (rcPtr->control != NULL) { /* Partition is controlled by this widget */ rcPtr->maxSize = rcPtr->size; } } } /* * ---------------------------------------------------------------------------- * * LayoutPartitions -- * * Calculates the normal space requirements for both the row and * column partitions. Each widget is added in order of the * number of rows or columns spanned, which defines the space * needed among in the partitions spanned. * * Results: * None. * * Side Effects: * * The sum of normal sizes set here will be used as the normal size * for the container widget. * * ---------------------------------------------------------------------------- */ static void LayoutPartitions(tablePtr) Table *tablePtr; { register Blt_ListNode node; Blt_Chain *chainPtr; Blt_ChainLink *linkPtr; register Entry *entryPtr; int needed, used, total; PartitionInfo *infoPtr; infoPtr = &(tablePtr->columnInfo); ResetPartitions(tablePtr, infoPtr, GetBoundedWidth); for (node = Blt_ListFirstNode(infoPtr->list); node != NULL; node = Blt_ListNextNode(node)) { chainPtr = (Blt_Chain *) Blt_ListGetValue(node); for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { entryPtr = Blt_ChainGetValue(linkPtr); if (entryPtr->column.control != CONTROL_FULL) { continue; } needed = GetReqWidth(entryPtr) + PADDING(entryPtr->padX) + 2 * (entryPtr->borderWidth + tablePtr->eEntryPad); if (needed <= 0) { continue; } used = GetSpan(infoPtr, entryPtr); if (needed > used) { GrowSpan(infoPtr, entryPtr, needed - used); } } } LockPartitions(infoPtr); for (node = Blt_ListFirstNode(infoPtr->list); node != NULL; node = Blt_ListNextNode(node)) { chainPtr = (Blt_Chain *) Blt_ListGetValue(node); for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { entryPtr = Blt_ChainGetValue(linkPtr); needed = GetReqWidth(entryPtr) + PADDING(entryPtr->padX) + 2 * (entryPtr->borderWidth + tablePtr->eEntryPad); if (entryPtr->column.control >= 0.0) { needed = (int)(needed * entryPtr->column.control); } if (needed <= 0) { continue; } used = GetSpan(infoPtr, entryPtr); if (needed > used) { GrowSpan(infoPtr, entryPtr, needed - used); } } } total = SetNominalSizes(tablePtr, infoPtr); tablePtr->normal.width = GetBoundedWidth(total, &(tablePtr->reqWidth)) + PADDING(tablePtr->padX) + 2 * (tablePtr->eTablePad + Tk_InternalBorderWidth(tablePtr->tkwin)); infoPtr = &(tablePtr->rowInfo); ResetPartitions(tablePtr, infoPtr, GetBoundedHeight); for (node = Blt_ListFirstNode(infoPtr->list); node != NULL; node = Blt_ListNextNode(node)) { chainPtr = (Blt_Chain *) Blt_ListGetValue(node); for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { entryPtr = Blt_ChainGetValue(linkPtr); if (entryPtr->row.control != CONTROL_FULL) { continue; } needed = GetReqHeight(entryPtr) + PADDING(entryPtr->padY) + 2 * (entryPtr->borderWidth + tablePtr->eEntryPad); if (needed <= 0) { continue; } used = GetSpan(infoPtr, entryPtr); if (needed > used) { GrowSpan(infoPtr, entryPtr, needed - used); } } } LockPartitions(&(tablePtr->rowInfo)); for (node = Blt_ListFirstNode(infoPtr->list); node != NULL; node = Blt_ListNextNode(node)) { chainPtr = Blt_ChainGetValue(node); for (linkPtr = Blt_ChainFirstLink(chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { entryPtr = Blt_ChainGetValue(linkPtr); needed = GetReqHeight(entryPtr) + PADDING(entryPtr->padY) + 2 * (entryPtr->borderWidth + tablePtr->eEntryPad); if (entryPtr->row.control >= 0.0) { needed = (int)(needed * entryPtr->row.control); } if (needed <= 0) { continue; } used = GetSpan(infoPtr, entryPtr); if (needed > used) { GrowSpan(infoPtr, entryPtr, needed - used); } } } total = SetNominalSizes(tablePtr, infoPtr); tablePtr->normal.height = GetBoundedHeight(total, &(tablePtr->reqHeight)) + PADDING(tablePtr->padY) + 2 * (tablePtr->eTablePad + Tk_InternalBorderWidth(tablePtr->tkwin)); } /* * ---------------------------------------------------------------------------- * * ArrangeEntries * * Places each widget at its proper location. First determines * the size and position of the each widget. It then considers the * following: * * 1. translation of widget position its parent widget. * 2. fill style * 3. anchor * 4. external and internal padding * 5. widget size must be greater than zero * * Results: * None. * * Side Effects: * The size of each partition is re-initialized its minimum size. * * ---------------------------------------------------------------------------- */ static void ArrangeEntries(tablePtr) Table *tablePtr; /* Table widget structure */ { register Blt_ChainLink *linkPtr; register Entry *entryPtr; register int spanWidth, spanHeight; int x, y; int winWidth, winHeight; int dx, dy; int maxX, maxY; int extra; maxX = tablePtr->container.width - (Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padRight + tablePtr->eTablePad); maxY = tablePtr->container.height - (Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padBottom + tablePtr->eTablePad); for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { entryPtr = Blt_ChainGetValue(linkPtr); x = entryPtr->column.rcPtr->offset + entryPtr->column.rcPtr->pad.side1 + entryPtr->padLeft + Tk_Changes(entryPtr->tkwin)->border_width + tablePtr->eEntryPad; y = entryPtr->row.rcPtr->offset + entryPtr->row.rcPtr->pad.side1 + entryPtr->padTop + Tk_Changes(entryPtr->tkwin)->border_width + tablePtr->eEntryPad; /* * Unmap any widgets that start beyond of the right edge of * the container. */ if ((x >= maxX) || (y >= maxY)) { if (Tk_IsMapped(entryPtr->tkwin)) { if (Tk_Parent(entryPtr->tkwin) != tablePtr->tkwin) { Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin); } Tk_UnmapWindow(entryPtr->tkwin); } continue; } extra = 2 * (entryPtr->borderWidth + tablePtr->eEntryPad); spanWidth = GetSpan(&(tablePtr->columnInfo), entryPtr) - (extra + PADDING(entryPtr->padX)); spanHeight = GetSpan(&(tablePtr->rowInfo), entryPtr) - (extra + PADDING(entryPtr->padY)); winWidth = GetReqWidth(entryPtr); winHeight = GetReqHeight(entryPtr); /* * * Compare the widget's requested size to the size of the span. * * 1) If the widget is larger than the span or if the fill flag * is set, make the widget the size of the span. Check that the * new size is within the bounds set for the widget. * * 2) Otherwise, position the widget in the space according to its * anchor. * */ if ((spanWidth <= winWidth) || (entryPtr->fill & FILL_X)) { winWidth = spanWidth; if (winWidth > entryPtr->reqWidth.max) { winWidth = entryPtr->reqWidth.max; } } if ((spanHeight <= winHeight) || (entryPtr->fill & FILL_Y)) { winHeight = spanHeight; if (winHeight > entryPtr->reqHeight.max) { winHeight = entryPtr->reqHeight.max; } } dx = dy = 0; if (spanWidth > winWidth) { dx = (spanWidth - winWidth); } if (spanHeight > winHeight) { dy = (spanHeight - winHeight); } if ((dx > 0) || (dy > 0)) { TranslateAnchor(dx, dy, entryPtr->anchor, &x, &y); } /* * Clip the widget at the bottom and/or right edge of the * container. */ if (winWidth > (maxX - x)) { winWidth = (maxX - x); } if (winHeight > (maxY - y)) { winHeight = (maxY - y); } /* * If the widget is too small (i.e. it has only an external * border) then unmap it. */ if ((winWidth < 1) || (winHeight < 1)) { if (Tk_IsMapped(entryPtr->tkwin)) { if (tablePtr->tkwin != Tk_Parent(entryPtr->tkwin)) { Tk_UnmaintainGeometry(entryPtr->tkwin, tablePtr->tkwin); } Tk_UnmapWindow(entryPtr->tkwin); } continue; } /* * Resize and/or move the widget as necessary. */ entryPtr->x = x; entryPtr->y = y; if (tablePtr->tkwin != Tk_Parent(entryPtr->tkwin)) { Tk_MaintainGeometry(entryPtr->tkwin, tablePtr->tkwin, x, y, winWidth, winHeight); } else { if ((x != Tk_X(entryPtr->tkwin)) || (y != Tk_Y(entryPtr->tkwin)) || (winWidth != Tk_Width(entryPtr->tkwin)) || (winHeight != Tk_Height(entryPtr->tkwin))) { Tk_MoveResizeWindow(entryPtr->tkwin, x, y, winWidth, winHeight); } if (!Tk_IsMapped(entryPtr->tkwin)) { Tk_MapWindow(entryPtr->tkwin); } } } } /* * ---------------------------------------------------------------------------- * * ArrangeTable -- * * * Results: * None. * * Side Effects: * The widgets in the table are possibly resized and redrawn. * * ---------------------------------------------------------------------------- */ static void ArrangeTable(clientData) ClientData clientData; { Table *tablePtr = clientData; int width, height; int offset; int padX, padY; int outerPad; RowColumn *columnPtr, *rowPtr; Blt_ChainLink *linkPtr; #ifdef notdef fprintf(stderr, "ArrangeTable(%s)\n", Tk_PathName(tablePtr->tkwin)); #endif Tcl_Preserve(tablePtr); tablePtr->flags &= ~ARRANGE_PENDING; tablePtr->rowInfo.ePad = tablePtr->columnInfo.ePad = tablePtr->eTablePad = tablePtr->eEntryPad = 0; if (tablePtr->editPtr != NULL) { tablePtr->rowInfo.ePad = tablePtr->columnInfo.ePad = tablePtr->editPtr->gridLineWidth; tablePtr->eTablePad = tablePtr->editPtr->gridLineWidth; tablePtr->eEntryPad = tablePtr->editPtr->entryPad; } /* * If the table has no children anymore, then don't do anything at * all: just leave the container widget's size as-is. */ if ((Blt_ChainGetLength(tablePtr->chainPtr) == 0) || (tablePtr->tkwin == NULL)) { Tcl_Release(tablePtr); return; } if (tablePtr->flags & REQUEST_LAYOUT) { tablePtr->flags &= ~REQUEST_LAYOUT; LayoutPartitions(tablePtr); } /* * Initially, try to fit the partitions exactly into the container * by resizing the container. If the widget's requested size is * different, send a request to the container widget's geometry * manager to resize. */ if ((tablePtr->propagate) && ((tablePtr->normal.width != Tk_ReqWidth(tablePtr->tkwin)) || (tablePtr->normal.height != Tk_ReqHeight(tablePtr->tkwin)))) { Tk_GeometryRequest(tablePtr->tkwin, tablePtr->normal.width, tablePtr->normal.height); EventuallyArrangeTable(tablePtr); Tcl_Release(tablePtr); return; } /* * Save the width and height of the container so we know when its * size has changed during ConfigureNotify events. */ tablePtr->container.width = Tk_Width(tablePtr->tkwin); tablePtr->container.height = Tk_Height(tablePtr->tkwin); outerPad = 2 * (Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->eTablePad); padX = outerPad + tablePtr->columnInfo.ePad + PADDING(tablePtr->padX); padY = outerPad + tablePtr->rowInfo.ePad + PADDING(tablePtr->padY); width = GetTotalSpan(&(tablePtr->columnInfo)) + padX; height = GetTotalSpan(&(tablePtr->rowInfo)) + padY; /* * If the previous geometry request was not fulfilled (i.e. the * size of the container is different from partitions' space * requirements), try to adjust size of the partitions to fit the * widget. */ if (tablePtr->container.width != width) { AdjustPartitions(&(tablePtr->columnInfo), tablePtr->container.width - width); width = GetTotalSpan(&(tablePtr->columnInfo)) + padX; } if (tablePtr->container.height != height) { AdjustPartitions(&(tablePtr->rowInfo), tablePtr->container.height - height); height = GetTotalSpan(&(tablePtr->rowInfo)) + padY; } /* * If after adjusting the size of the partitions the space * required does not equal the size of the widget, do one of the * following: * * 1) If it's smaller, center the table in the widget. * 2) If it's bigger, clip the partitions that extend beyond * the edge of the container. * * Set the row and column offsets (including the container's * internal border width). To be used later when positioning the * widgets. */ offset = Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padLeft + tablePtr->eTablePad; if (width < tablePtr->container.width) { offset += (tablePtr->container.width - width) / 2; } for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { columnPtr = Blt_ChainGetValue(linkPtr); columnPtr->offset = offset + tablePtr->columnInfo.ePad; offset += columnPtr->size; } offset = Tk_InternalBorderWidth(tablePtr->tkwin) + tablePtr->padTop + tablePtr->eTablePad; if (height < tablePtr->container.height) { offset += (tablePtr->container.height - height) / 2; } for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rowPtr = Blt_ChainGetValue(linkPtr); rowPtr->offset = offset + tablePtr->rowInfo.ePad; offset += rowPtr->size; } ArrangeEntries(tablePtr); if (tablePtr->editPtr != NULL) { /* Redraw the editor */ (*tablePtr->editPtr->drawProc) (tablePtr->editPtr); } Tcl_Release(tablePtr); } /* * ---------------------------------------------------------------------------- * * ArrangeOp -- * * Forces layout of the table geometry manager. This is useful * mostly for debugging the geometry manager. You can get the * geometry manager to calculate the normal (requested) width and * height of each row and column. Otherwise, you need to first * withdraw the container widget, invoke "update", and then query * the geometry manager. * * Results: * Returns a standard Tcl result. If the table is successfully * rearranged, TCL_OK is returned. Otherwise, TCL_ERROR is returned * and an error message is left in interp->result. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int ArrangeOp(dataPtr, interp, argc, argv) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; /* Interpreter to report errors to */ int argc; char **argv; /* Path name of container associated with * the table */ { Table *tablePtr; if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) { return TCL_ERROR; } tablePtr->flags |= REQUEST_LAYOUT; ArrangeTable(tablePtr); return TCL_OK; } /* * ---------------------------------------------------------------------------- * * CgetOp -- * * Returns the name, position and options of a widget in the table. * * Results: * Returns a standard Tcl result. A list of the widget attributes * is left in interp->result. * * -------------------------------------------------------------------------- */ /*ARGSUSED*/ static int CgetOp(dataPtr, interp, argc, argv) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Table *tablePtr; int length; char c; int n; PartitionInfo *infoPtr; if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) { return TCL_ERROR; } if (argc == 4) { return Tk_ConfigureValue(interp, tablePtr->tkwin, tableConfigSpecs, (char *)tablePtr, argv[3], 0); } c = argv[3][0]; length = strlen(argv[3]); if (c == '.') { /* Configure widget */ Entry *entryPtr; if (GetEntry(interp, tablePtr, argv[3], &entryPtr) != TCL_OK) { return TCL_ERROR; } return Tk_ConfigureValue(interp, entryPtr->tkwin, entryConfigSpecs, (char *)entryPtr, argv[4], 0); } else if ((c == 'c') && (strncmp(argv[3], "container", length) == 0)) { return Tk_ConfigureValue(interp, tablePtr->tkwin, tableConfigSpecs, (char *)tablePtr, argv[4], 0); } infoPtr = ParseRowColumn(tablePtr, argv[3], &n); if (infoPtr == NULL) { return TCL_ERROR; } return Tk_ConfigureValue(interp, tablePtr->tkwin, infoPtr->configSpecs, (char *)GetRowColumn(infoPtr, n), argv[4], 0); } /* * ---------------------------------------------------------------------------- * * ConfigureOp -- * * Returns the name, position and options of a widget in the table. * * Results: * Returns a standard Tcl result. A list of the table configuration * option information is left in interp->result. * * -------------------------------------------------------------------------- */ /*ARGSUSED*/ static int ConfigureOp(dataPtr, interp, argc, argv) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Table *tablePtr; int length; char c1, c2; int count; int result; char **items; register int i; if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) { return TCL_ERROR; } /* * Find the end of the items. Search until we see an option (-). */ argc -= 3, argv += 3; for (count = 0; count < argc; count++) { if (argv[count][0] == '-') { break; } } items = argv; /* Save the start of the item list */ argc -= count; /* Move beyond the items to the options */ argv += count; result = TCL_ERROR; /* Suppress compiler warning */ if (count == 0) { result = ConfigureTable(tablePtr, interp, argc, argv); } for (i = 0; i < count; i++) { c1 = items[i][0]; c2 = items[i][1]; length = strlen(items[i]); if (c1 == '.') { /* Configure widget */ Entry *entryPtr; if (GetEntry(interp, tablePtr, items[i], &entryPtr) != TCL_OK) { return TCL_ERROR; } result = ConfigureEntry(tablePtr, interp, entryPtr, argc, argv); } else if ((c1 == 'r') || (c1 == 'R')) { result = ConfigureRowColumn(tablePtr, &(tablePtr->rowInfo), items[i], argc, argv); } else if ((c1 == 'c') && (c2 == 'o') && (strncmp(argv[3], "container", length) == 0)) { result = ConfigureTable(tablePtr, interp, argc, argv); } else if ((c1 == 'c') || (c1 == 'C')) { result = ConfigureRowColumn(tablePtr, &(tablePtr->columnInfo), items[i], argc, argv); } else { Tcl_AppendResult(interp, "unknown item \"", items[i], "\": should be widget, row or column index, or \"container\"", (char *)NULL); return TCL_ERROR; } if (result == TCL_ERROR) { break; } if ((i + 1) < count) { Tcl_AppendResult(interp, "\n", (char *)NULL); } } tablePtr->flags |= REQUEST_LAYOUT; EventuallyArrangeTable(tablePtr); return result; } /* * ---------------------------------------------------------------------------- * * DeleteOp -- * * Deletes the specified rows and/or columns from the table. * Note that the row/column indices can be fixed only after * all the deletions have occurred. * * table delete .f r0 r1 r4 c0 * * Results: * Returns a standard Tcl result. * * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int DeleteOp(dataPtr, interp, argc, argv) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Table *tablePtr; char c; Blt_ChainLink *linkPtr, *nextPtr; PartitionInfo *infoPtr; char string[200]; int matches; register int i; RowColumn *rcPtr; if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) { return TCL_ERROR; } for (i = 3; i < argc; i++) { c = tolower(argv[i][0]); if ((c != 'r') && (c != 'c')) { Tcl_AppendResult(interp, "bad index \"", argv[i], "\": must start with \"r\" or \"c\"", (char *)NULL); return TCL_ERROR; } } matches = 0; for (i = 3; i < argc; i++) { c = tolower(argv[i][0]); infoPtr = (c == 'r') ? &(tablePtr->rowInfo) : &(tablePtr->columnInfo); for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL; linkPtr = nextPtr) { nextPtr = Blt_ChainNextLink(linkPtr); rcPtr = Blt_ChainGetValue(linkPtr); sprintf(string, "%c%d", argv[i][0], rcPtr->index); if (Tcl_StringMatch(string, argv[i])) { matches++; DeleteRowColumn(tablePtr, infoPtr, rcPtr); Blt_ChainDeleteLink(infoPtr->chainPtr, linkPtr); } } } if (matches > 0) { /* Fix indices */ i = 0; for (linkPtr = Blt_ChainFirstLink(tablePtr->columnInfo.chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); rcPtr->index = i++; } i = 0; for (linkPtr = Blt_ChainFirstLink(tablePtr->rowInfo.chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); rcPtr->index = i++; } tablePtr->flags |= REQUEST_LAYOUT; EventuallyArrangeTable(tablePtr); } return TCL_OK; } /* * ---------------------------------------------------------------------------- * * JoinOp -- * * Joins the specified span of rows/columns together into a * partition. The row/column indices can be fixed only after * all the deletions have occurred. * * table join .f r0 r3 * table join .f c2 c4 * Results: * Returns a standard Tcl result. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int JoinOp(dataPtr, interp, argc, argv) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Table *tablePtr; Blt_ChainLink *linkPtr, *nextPtr, *fromPtr; PartitionInfo *infoPtr, *info2Ptr; Entry *entryPtr; int from, to; /* Indices marking the span of * partitions to be joined together. */ int start, end; /* Entry indices. */ register int i; RowColumn *rcPtr; if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) { return TCL_ERROR; } infoPtr = ParseRowColumn(tablePtr, argv[3], &from); if (infoPtr == NULL) { return TCL_ERROR; } info2Ptr = ParseRowColumn(tablePtr, argv[4], &to); if (info2Ptr == NULL) { return TCL_ERROR; } if (infoPtr != info2Ptr) { Tcl_AppendResult(interp, "\"from\" and \"to\" must both be rows or columns", (char *)NULL); return TCL_ERROR; } if (from >= to) { return TCL_OK; /* No-op. */ } fromPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, from); rcPtr = Blt_ChainGetValue(fromPtr); /* * --------------------------------------------------------------- * * Reduce the span of all entries that currently cross any of the * trailing rows/columns. Also, if the entry starts in one of * these rows/columns, moved it to the designated "joined" * row/column. * * --------------------------------------------------------------- */ if (infoPtr->type == rowUid) { for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { entryPtr = Blt_ChainGetValue(linkPtr); start = entryPtr->row.rcPtr->index + 1; end = entryPtr->row.rcPtr->index + entryPtr->row.span - 1; if ((end < from) || ((start > to))) { continue; } entryPtr->row.span -= to - start + 1; if (start >= from) {/* Entry starts in a trailing partition. */ entryPtr->row.rcPtr = rcPtr; } } } else { for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { entryPtr = Blt_ChainGetValue(linkPtr); start = entryPtr->column.rcPtr->index + 1; end = entryPtr->column.rcPtr->index + entryPtr->column.span - 1; if ((end < from) || ((start > to))) { continue; } entryPtr->column.span -= to - start + 1; if (start >= from) {/* Entry starts in a trailing partition. */ entryPtr->column.rcPtr = rcPtr; } } } linkPtr = Blt_ChainNextLink(fromPtr); for (i = from + 1; i <= to; i++) { nextPtr = Blt_ChainNextLink(linkPtr); rcPtr = Blt_ChainGetValue(linkPtr); DeleteRowColumn(tablePtr, infoPtr, rcPtr); Blt_ChainDeleteLink(infoPtr->chainPtr, linkPtr); linkPtr = nextPtr; } i = 0; for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); rcPtr->index = i++; } tablePtr->flags |= REQUEST_LAYOUT; EventuallyArrangeTable(tablePtr); return TCL_OK; } /* * ---------------------------------------------------------------------------- * * ExtentsOp -- * * Returns a list of all the pathnames of the widgets managed by * a table. The table is determined from the name of the * container widget associated with the table. * * table extents .frame r0 c0 container * * Results: * Returns a standard Tcl result. If no error occurred, TCL_OK is * returned and a list of widgets managed by the table is left in * interp->result. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int ExtentsOp(dataPtr, interp, argc, argv) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; /* Interpreter to return results to. */ int argc; /* # of arguments */ char **argv; /* Command line arguments. */ { Table *tablePtr; Blt_ChainLink *linkPtr; RowColumn *rcPtr; RowColumn *c1Ptr, *r1Ptr, *c2Ptr, *r2Ptr; PartitionInfo *infoPtr; int x, y, width, height; char string[200]; char c; if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) { return TCL_ERROR; } c = tolower(argv[3][0]); if (c == 'r') { infoPtr = &(tablePtr->rowInfo); } else if (c == 'c') { infoPtr = &(tablePtr->columnInfo); } else { Tcl_AppendResult(interp, "unknown item \"", argv[3], "\": should be widget, row, or column", (char *)NULL); return TCL_ERROR; } for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); sprintf(string, "%c%d", argv[3][0], rcPtr->index); if (Tcl_StringMatch(string, argv[3])) { if (c == 'r') { r1Ptr = r2Ptr = rcPtr; c1Ptr = GetRowColumn(&(tablePtr->columnInfo), 0); c2Ptr = GetRowColumn(&(tablePtr->columnInfo), tablePtr->nColumns - 1); } else { c1Ptr = c2Ptr = rcPtr; r1Ptr = GetRowColumn(&(tablePtr->rowInfo), 0); r2Ptr = GetRowColumn(&(tablePtr->rowInfo), tablePtr->nRows - 1); } x = c1Ptr->offset; y = r1Ptr->offset; width = c2Ptr->offset + c2Ptr->size - x; height = r2Ptr->offset + r2Ptr->size - y; sprintf(string, "%c%d %d %d %d %d\n", argv[3][0], rcPtr->index, x, y, width, height); Tcl_AppendResult(interp, string, (char *)NULL); } } return TCL_OK; } /* * ---------------------------------------------------------------------------- * * ForgetOp -- * * Processes an argv/argc list of widget names and purges their * entries from their respective tables. The widgets are unmapped and * the tables are rearranged at the next idle point. Note that all * the named widgets do not need to exist in the same table. * * Results: * Returns a standard Tcl result. If an error occurred, TCL_ERROR is * returned and an error message is left in interp->result. * * Side Effects: * Memory is deallocated (the entry is destroyed), etc. The * affected tables are is re-computed and arranged at the next idle * point. * * ---------------------------------------------------------------------------- */ static int ForgetOp(dataPtr, interp, argc, argv) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Entry *entryPtr; register int i; Blt_HashEntry *hPtr; Blt_HashSearch cursor; Table *tablePtr; Tk_Window tkwin, mainWindow; tablePtr = NULL; mainWindow = Tk_MainWindow(interp); for (i = 2; i < argc; i++) { entryPtr = NULL; tkwin = Tk_NameToWindow(interp, argv[i], mainWindow); if (tkwin == NULL) { return TCL_ERROR; } for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor); hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { tablePtr = (Table *)Blt_GetHashValue(hPtr); if (tablePtr->interp != interp) { continue; } entryPtr = FindEntry(tablePtr, tkwin); if (entryPtr != NULL) { break; } } if (entryPtr == NULL) { Tcl_AppendResult(interp, "\"", argv[i], "\" is not managed by any table", (char *)NULL); return TCL_ERROR; } if (Tk_IsMapped(entryPtr->tkwin)) { Tk_UnmapWindow(entryPtr->tkwin); } /* Arrange for the call back here in the loop, because the * widgets may not belong to the same table. */ tablePtr->flags |= REQUEST_LAYOUT; EventuallyArrangeTable(tablePtr); DestroyEntry(entryPtr); } return TCL_OK; } /* * ---------------------------------------------------------------------------- * * InfoOp -- * * Returns the options of a widget or partition in the table. * * Results: * Returns a standard Tcl result. A list of the widget attributes * is left in interp->result. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int InfoOp(dataPtr, interp, argc, argv) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Table *tablePtr; int result; char c; register int i; if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) { return TCL_ERROR; } for (i = 3; i < argc; i++) { c = argv[i][0]; if (c == '.') { /* Entry information */ Entry *entryPtr; if (GetEntry(interp, tablePtr, argv[i], &entryPtr) != TCL_OK) { return TCL_ERROR; } result = InfoEntry(interp, tablePtr, entryPtr); } else if ((c == 'r') || (c == 'R') || (c == 'c') || (c == 'C')) { result = InfoRowColumn(tablePtr, interp, argv[i]); } else { Tcl_AppendResult(interp, "unknown item \"", argv[i], "\": should be widget, row, or column", (char *)NULL); return TCL_ERROR; } if (result != TCL_OK) { return TCL_ERROR; } if ((i + 1) < argc) { Tcl_AppendResult(interp, "\n", (char *)NULL); } } return TCL_OK; } /* * ---------------------------------------------------------------------------- * * InsertOp -- * * Inserts a span of rows/columns into the table. * * table insert .f r0 2 * table insert .f c0 5 * * Results: * Returns a standard Tcl result. A list of the widget * attributes is left in interp->result. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int InsertOp(dataPtr, interp, argc, argv) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Table *tablePtr; long int span; int before; PartitionInfo *infoPtr; RowColumn *rcPtr; register int i; Blt_ChainLink *beforePtr, *linkPtr; int linkBefore; if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) { return TCL_ERROR; } linkBefore = TRUE; if (argv[3][0] == '-') { if (strcmp(argv[3], "-before") == 0) { linkBefore = TRUE; argv++; argc--; } else if (strcmp(argv[3], "-after") == 0) { linkBefore = FALSE; argv++; argc--; } } if (argc == 3) { Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], "insert ", argv[2], "row|column ?span?", (char *)NULL); return TCL_ERROR; } infoPtr = ParseRowColumn(tablePtr, argv[3], &before); if (infoPtr == NULL) { return TCL_ERROR; } span = 1; if ((argc > 4) && (Tcl_ExprLong(interp, argv[4], &span) != TCL_OK)) { return TCL_ERROR; } if (span < 1) { Tcl_AppendResult(interp, "span value \"", argv[4], "\" can't be negative", (char *)NULL); return TCL_ERROR; } beforePtr = Blt_ChainGetNthLink(infoPtr->chainPtr, before); /* * Insert the new rows/columns from the designated point in the * chain. */ for (i = 0; i < span; i++) { rcPtr = CreateRowColumn(); linkPtr = Blt_ChainNewLink(); Blt_ChainSetValue(linkPtr, rcPtr); if (linkBefore) { Blt_ChainLinkBefore(infoPtr->chainPtr, linkPtr, beforePtr); } else { Blt_ChainLinkAfter(infoPtr->chainPtr, linkPtr, beforePtr); } rcPtr->linkPtr = linkPtr; } i = 0; for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); /* Reset the indices of the trailing rows/columns. */ rcPtr->index = i++; } tablePtr->flags |= REQUEST_LAYOUT; EventuallyArrangeTable(tablePtr); return TCL_OK; } /* * ---------------------------------------------------------------------------- * * SplitOp -- * * Splits a single row/column into multiple partitions. Any * widgets that span this row/column will be automatically * corrected to include the new rows/columns. * * table split .f r0 3 * table split .f c2 2 * Results: * Returns a standard Tcl result. A list of the widget * attributes is left in interp->result. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int SplitOp(dataPtr, interp, argc, argv) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Table *tablePtr; int number, split; int start, end; PartitionInfo *infoPtr; RowColumn *rcPtr; register int i; Blt_ChainLink *afterPtr, *linkPtr; Entry *entryPtr; if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) { return TCL_ERROR; } infoPtr = ParseRowColumn(tablePtr, argv[3], &number); if (infoPtr == NULL) { return TCL_ERROR; } split = 2; if (argc > 4) { if (Tcl_GetInt(interp, argv[4], &split) != TCL_OK) { return TCL_ERROR; } } if (split < 2) { Tcl_AppendResult(interp, "bad split value \"", argv[4], "\": should be 2 or greater", (char *)NULL); return TCL_ERROR; } afterPtr = Blt_ChainGetNthLink(infoPtr->chainPtr, number); /* * Append (split - 1) additional rows/columns starting * from the current point in the chain. */ for (i = 1; i < split; i++) { rcPtr = CreateRowColumn(); linkPtr = Blt_ChainNewLink(); Blt_ChainSetValue(linkPtr, rcPtr); Blt_ChainLinkAfter(infoPtr->chainPtr, linkPtr, afterPtr); rcPtr->linkPtr = linkPtr; } /* * Also increase the span of all entries that span this * row/column by split - 1. */ if (infoPtr->type == rowUid) { for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { entryPtr = Blt_ChainGetValue(linkPtr); start = entryPtr->row.rcPtr->index; end = entryPtr->row.rcPtr->index + entryPtr->row.span; if ((start <= number) && (number < end)) { entryPtr->row.span += (split - 1); } } } else { for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { entryPtr = Blt_ChainGetValue(linkPtr); start = entryPtr->column.rcPtr->index; end = entryPtr->column.rcPtr->index + entryPtr->column.span; if ((start <= number) && (number < end)) { entryPtr->column.span += (split - 1); } } } /* * Be careful to renumber the rows or columns only after * processing each entry. Otherwise row/column numbering * will be out of sync with the index. */ i = number; for (linkPtr = afterPtr; linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); rcPtr->index = i++; /* Renumber the trailing indices. */ } tablePtr->flags |= REQUEST_LAYOUT; EventuallyArrangeTable(tablePtr); return TCL_OK; } /* * ---------------------------------------------------------------------- * * RowColumnSearch -- * * Searches for the row or column designated by an x or y * coordinate. * * Results: * Returns a pointer to the row/column containing the given point. * If no row/column contains the coordinate, NULL is returned. * * ---------------------------------------------------------------------- */ static RowColumn * RowColumnSearch(infoPtr, x) PartitionInfo *infoPtr; int x; /* Search coordinate */ { Blt_ChainLink *linkPtr; RowColumn *rcPtr; for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); if (x > (rcPtr->offset + rcPtr->size)) { break; /* Too far, can't find row/column. */ } if (x > rcPtr->offset) { return rcPtr; } } return NULL; } /* *---------------------------------------------------------------------- * * LocateOp -- * * * Returns the row,column index given a screen coordinate. * * Results: * Returns a standard Tcl result. * *---------------------------------------------------------------------- */ /* ARGSUSED */ static int LocateOp(dataPtr, interp, argc, argv) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; int argc; char **argv; { int x, y; RowColumn *rowPtr, *columnPtr; Table *tablePtr; if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) { return TCL_ERROR; } if (Blt_GetPixels(interp, tablePtr->tkwin, argv[3], PIXELS_ANY, &x) != TCL_OK) { return TCL_ERROR; } if (Blt_GetPixels(interp, tablePtr->tkwin, argv[4], PIXELS_ANY, &y) != TCL_OK) { return TCL_ERROR; } rowPtr = RowColumnSearch(&(tablePtr->rowInfo), y); if (rowPtr == NULL) { return TCL_OK; } columnPtr = RowColumnSearch(&(tablePtr->columnInfo), x); if (columnPtr == NULL) { return TCL_OK; } Tcl_AppendElement(interp, Blt_Itoa(rowPtr->index)); Tcl_AppendElement(interp, Blt_Itoa(columnPtr->index)); return TCL_OK; } /* * ---------------------------------------------------------------------------- * * ContainersOp -- * * Returns a list of tables currently in use. A table is * associated by the name of its container widget. All tables * matching a given pattern are included in this list. If no * pattern is present (argc == 0), all tables are included. * * Results: * Returns a standard Tcl result. If no error occurred, TCL_OK is * returned and a list of tables is left in interp->result. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int ContainersOp(dataPtr, interp, argc, argv) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; /* Interpreter to return list of names to */ int argc; char **argv; /* Contains 0-1 arguments: search pattern */ { Blt_HashEntry *hPtr; Blt_HashSearch cursor; register Table *tablePtr; char *pattern; pattern = NULL; if (argc > 2) { if (argv[2][0] == '-') { unsigned int length; length = strlen(argv[2]); if ((length > 1) && (argv[2][1] == 'p') && (strncmp(argv[2], "-pattern", length) == 0)) { pattern = argv[3]; goto search; } else if ((length > 1) && (argv[2][1] == 's') && (strncmp(argv[2], "-slave", length) == 0)) { Tk_Window tkwin; if (argc != 4) { Tcl_AppendResult(interp, "needs widget argument for \"", argv[2], "\"", (char *)NULL); return TCL_ERROR; } tkwin = Tk_NameToWindow(interp, argv[3], Tk_MainWindow(interp)); if (tkwin == NULL) { return TCL_ERROR; } for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor); hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { tablePtr = (Table *)Blt_GetHashValue(hPtr); if (FindEntry(tablePtr, tkwin) != NULL) { Tcl_AppendElement(interp, Tk_PathName(tablePtr->tkwin)); } } return TCL_OK; } else { Tcl_AppendResult(interp, "bad switch \"", argv[2], "\" : \ should be \"-pattern\", or \"-slave\"", (char *)NULL); return TCL_ERROR; } } else { pattern = argv[2]; } } search: for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor); hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { tablePtr = (Table *)Blt_GetHashValue(hPtr); if (tablePtr->interp == interp) { if ((pattern == NULL) || (Tcl_StringMatch(Tk_PathName(tablePtr->tkwin), pattern))) { Tcl_AppendElement(interp, Tk_PathName(tablePtr->tkwin)); } } } return TCL_OK; } /* * ---------------------------------------------------------------------------- * * SaveOp -- * * Returns a list of all the commands necessary to rebuild the * the table. This includes the layout of the widgets and any * row, column, or table options set. * * Results: * Returns a standard Tcl result. If no error occurred, TCL_OK is * returned and a list of widget path names is left in interp->result. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int SaveOp(dataPtr, interp, argc, argv) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; int argc; char **argv; { Table *tablePtr; Blt_ChainLink *linkPtr, *lastPtr; Entry *entryPtr; PartitionInfo *infoPtr; RowColumn *rcPtr; Tcl_DString dString; int start, last; if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) { return TCL_ERROR; } Tcl_DStringInit(&dString); Tcl_DStringAppend(&dString, "\n# Table widget layout\n\n", -1); Tcl_DStringAppend(&dString, argv[0], -1); Tcl_DStringAppend(&dString, " ", -1); Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1); Tcl_DStringAppend(&dString, " \\\n", -1); lastPtr = Blt_ChainLastLink(tablePtr->chainPtr); for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { entryPtr = Blt_ChainGetValue(linkPtr); PrintEntry(entryPtr, &dString); if (linkPtr != lastPtr) { Tcl_DStringAppend(&dString, " \\\n", -1); } } Tcl_DStringAppend(&dString, "\n\n# Row configuration options\n\n", -1); infoPtr = &(tablePtr->rowInfo); for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); start = Tcl_DStringLength(&dString); Tcl_DStringAppend(&dString, argv[0], -1); Tcl_DStringAppend(&dString, " configure ", -1); Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1); Tcl_DStringAppend(&dString, " r", -1); Tcl_DStringAppend(&dString, Blt_Itoa(rcPtr->index), -1); last = Tcl_DStringLength(&dString); PrintRowColumn(interp, infoPtr, rcPtr, &dString); if (Tcl_DStringLength(&dString) == last) { Tcl_DStringSetLength(&dString, start); } else { Tcl_DStringAppend(&dString, "\n", -1); } } Tcl_DStringAppend(&dString, "\n\n# Column configuration options\n\n", -1); infoPtr = &(tablePtr->columnInfo); for (linkPtr = Blt_ChainFirstLink(infoPtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { rcPtr = Blt_ChainGetValue(linkPtr); start = Tcl_DStringLength(&dString); Tcl_DStringAppend(&dString, argv[0], -1); Tcl_DStringAppend(&dString, " configure ", -1); Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1); Tcl_DStringAppend(&dString, " c", -1); Tcl_DStringAppend(&dString, Blt_Itoa(rcPtr->index), -1); last = Tcl_DStringLength(&dString); PrintRowColumn(interp, infoPtr, rcPtr, &dString); if (Tcl_DStringLength(&dString) == last) { Tcl_DStringSetLength(&dString, start); } else { Tcl_DStringAppend(&dString, "\n", -1); } } start = Tcl_DStringLength(&dString); Tcl_DStringAppend(&dString, "\n\n# Table configuration options\n\n", -1); Tcl_DStringAppend(&dString, argv[0], -1); Tcl_DStringAppend(&dString, " configure ", -1); Tcl_DStringAppend(&dString, Tk_PathName(tablePtr->tkwin), -1); last = Tcl_DStringLength(&dString); PrintTable(tablePtr, &dString); if (Tcl_DStringLength(&dString) == last) { Tcl_DStringSetLength(&dString, start); } else { Tcl_DStringAppend(&dString, "\n", -1); } Tcl_DStringResult(interp, &dString); return TCL_OK; } /* * ---------------------------------------------------------------------------- * * SearchOp -- * * Returns a list of all the pathnames of the widgets managed by * a table geometry manager. The table is given by the path name of a * container widget associated with the table. * * Results: * Returns a standard Tcl result. If no error occurred, TCL_OK is * returned and a list of widget path names is left in interp->result. * * ---------------------------------------------------------------------------- */ /*ARGSUSED*/ static int SearchOp(dataPtr, interp, argc, argv) TableInterpData *dataPtr; /* Interpreter-specific data. */ Tcl_Interp *interp; /* Interpreter to return list of names to */ int argc; /* Number of arguments */ char **argv; /* Contains 1-2 arguments: pathname of container * widget associated with the table and search * pattern */ { Table *tablePtr; Blt_ChainLink *linkPtr; Entry *entryPtr; int rspan, cspan, rstart, cstart; char *pattern; char c; int flags; register int i; #define MATCH_PATTERN (1<<0) /* Find widgets whose path names * match a given pattern */ #define MATCH_INDEX_SPAN (1<<1) /* Find widgets that span index */ #define MATCH_INDEX_START (1<<2) /* Find widgets that start at index */ if (Blt_GetTable(dataPtr, interp, argv[2], &tablePtr) != TCL_OK) { return TCL_ERROR; } flags = 0; pattern = NULL; rspan = cspan = rstart = cstart = 0; /* Parse switches and arguments first */ for (i = 3; i < argc; i += 2) { if (argv[i][0] == '-') { unsigned int length; if ((i + 1) == argc) { Tcl_AppendResult(interp, "switch \"", argv[i], "\" needs value", (char *)NULL); return TCL_ERROR; } length = strlen(argv[i]); c = argv[i][1]; if ((c == 'p') && (length > 1) && (strncmp(argv[3], "-pattern", length) == 0)) { flags |= MATCH_PATTERN; pattern = argv[4]; } else if ((c == 's') && (length > 2) && (strncmp(argv[i], "-start", length) == 0)) { flags |= MATCH_INDEX_START; if (ParseItem(tablePtr, argv[i + 1], &rstart, &cstart) != TCL_OK) { return TCL_ERROR; } } else if ((c == 's') && (length > 2) && (strncmp(argv[i], "-span", length) == 0)) { flags |= MATCH_INDEX_SPAN; if (ParseItem(tablePtr, argv[4], &rspan, &cspan) != TCL_OK) { return TCL_ERROR; } } else { Tcl_AppendResult(interp, "bad switch \"", argv[3], "\" : \ should be \"-pattern\", \"-span\", or \"-start\"", (char *)NULL); return TCL_ERROR; } } else { if ((i + 1) == argc) { pattern = argv[i]; flags |= MATCH_PATTERN; } } } /* Then try to match entries with the search criteria */ for (linkPtr = Blt_ChainFirstLink(tablePtr->chainPtr); linkPtr != NULL; linkPtr = Blt_ChainNextLink(linkPtr)) { entryPtr = Blt_ChainGetValue(linkPtr); if ((flags & MATCH_PATTERN) && (pattern != NULL)) { if (Tcl_StringMatch(Tk_PathName(entryPtr->tkwin), pattern)) { goto match; } } if (flags & MATCH_INDEX_SPAN) { if ((rspan >= 0) && ((entryPtr->row.rcPtr->index <= rspan) || ((entryPtr->row.rcPtr->index + entryPtr->row.span) > rspan))) { goto match; } if ((cspan >= 0) && ((entryPtr->column.rcPtr->index <= cspan) || ((entryPtr->column.rcPtr->index + entryPtr->column.span) > cspan))) { goto match; } } if (flags & MATCH_INDEX_START) { if ((rstart >= 0) && (entryPtr->row.rcPtr->index == rstart)) { goto match; } if ((cstart >= 0) && (entryPtr->column.rcPtr->index == cstart)) { goto match; } } continue; match: Tcl_AppendElement(interp, Tk_PathName(entryPtr->tkwin)); } return TCL_OK; } /* * ---------------------------------------------------------------------------- * * Table operations. * * The fields for Blt_OpSpec are as follows: * * - operation name * - minimum number of characters required to disambiguate the operation name. * - function associated with operation. * - minimum number of arguments required. * - maximum number of arguments allowed (0 indicates no limit). * - usage string * * ---------------------------------------------------------------------------- */ static Blt_OpSpec operSpecs[] = { {"arrange", 1, (Blt_Op)ArrangeOp, 3, 3, "container",}, {"cget", 2, (Blt_Op)CgetOp, 4, 5, "container ?row|column|widget? option",}, {"configure", 3, (Blt_Op)ConfigureOp, 3, 0, "container ?row|column|widget?... ?option value?...",}, {"containers", 3, (Blt_Op)ContainersOp, 2, 4, "?switch? ?arg?",}, {"delete", 1, (Blt_Op)DeleteOp, 3, 0, "container row|column ?row|column?",}, {"extents", 1, (Blt_Op)ExtentsOp, 4, 4, "container row|column|widget",}, {"forget", 1, (Blt_Op)ForgetOp, 3, 0, "widget ?widget?...",}, {"info", 3, (Blt_Op)InfoOp, 3, 0, "container ?row|column|widget?...",}, {"insert", 3, (Blt_Op)InsertOp, 4, 6, "container ?-before|-after? row|column ?count?",}, {"join", 1, (Blt_Op)JoinOp, 5, 5, "container first last",}, {"locate", 2, (Blt_Op)LocateOp, 5, 5, "container x y",}, {"save", 2, (Blt_Op)SaveOp, 3, 3, "container",}, {"search", 2, (Blt_Op)SearchOp, 3, 0, "container ?switch arg?...",}, {"split", 2, (Blt_Op)SplitOp, 4, 5, "container row|column div",}, }; static int nSpecs = sizeof(operSpecs) / sizeof(Blt_OpSpec); /* * ---------------------------------------------------------------------------- * * TableCmd -- * * This procedure is invoked to process the Tcl command that * corresponds to the table geometry manager. See the user * documentation for details on what it does. * * Results: * A standard Tcl result. * * Side effects: * See the user documentation. * * ---------------------------------------------------------------------------- */ static int TableCmd(clientData, interp, argc, argv) ClientData clientData; /* Interpreter-specific data. */ Tcl_Interp *interp; int argc; char **argv; { TableInterpData *dataPtr = clientData; Blt_Op proc; int result; if ((argc > 1) && (argv[1][0] == '.')) { Table *tablePtr; if (Blt_GetTable(clientData, interp, argv[1], &tablePtr) != TCL_OK) { Tcl_ResetResult(interp); tablePtr = CreateTable(dataPtr, interp, argv[1]); if (tablePtr == NULL) { return TCL_ERROR; } } return BuildTable(tablePtr, interp, argc, argv); } proc = Blt_GetOp(interp, nSpecs, operSpecs, BLT_OP_ARG1, argc, argv, 0); if (proc == NULL) { return TCL_ERROR; } result = (*proc) (dataPtr, interp, argc, argv); return result; } /* * ----------------------------------------------------------------------- * * TableInterpDeleteProc -- * * This is called when the interpreter hosting the table command * is destroyed. * * Results: * None. * * Side effects: * Destroys all the hash table maintaining the names of the table * geomtry managers. * * ------------------------------------------------------------------------ */ /* ARGSUSED */ static void TableInterpDeleteProc(clientData, interp) ClientData clientData; /* Thread-specific data. */ Tcl_Interp *interp; { TableInterpData *dataPtr = clientData; Blt_HashEntry *hPtr; Blt_HashSearch cursor; Table *tablePtr; for (hPtr = Blt_FirstHashEntry(&(dataPtr->tableTable), &cursor); hPtr != NULL; hPtr = Blt_NextHashEntry(&cursor)) { tablePtr = (Table *)Blt_GetHashValue(hPtr); tablePtr->hashPtr = NULL; DestroyTable((DestroyData)tablePtr); } Blt_DeleteHashTable(&(dataPtr->tableTable)); Tcl_DeleteAssocData(interp, TABLE_THREAD_KEY); Blt_Free(dataPtr); } static TableInterpData * GetTableInterpData(interp) Tcl_Interp *interp; { TableInterpData *dataPtr; Tcl_InterpDeleteProc *proc; dataPtr = (TableInterpData *) Tcl_GetAssocData(interp, TABLE_THREAD_KEY, &proc); if (dataPtr == NULL) { dataPtr = Blt_Malloc(sizeof(TableInterpData)); assert(dataPtr); Tcl_SetAssocData(interp, TABLE_THREAD_KEY, TableInterpDeleteProc, dataPtr); Blt_InitHashTable(&(dataPtr->tableTable), BLT_ONE_WORD_KEYS); } return dataPtr; } /* * ---------------------------------------------------------------------------- * * Blt_TableInit -- * * This procedure is invoked to initialize the Tcl command that * corresponds to the table geometry manager. * * Results: * None. * * Side effects: * Creates the new command and adds an entry into a global Tcl * associative array. * * --------------------------------------------------------------------------- */ int Blt_TableInit(interp) Tcl_Interp *interp; { static Blt_CmdSpec cmdSpec = {"table", TableCmd, }; TableInterpData *dataPtr; dataPtr = GetTableInterpData(interp); cmdSpec.clientData = dataPtr; if (Blt_InitCmd(interp, "blt", &cmdSpec) == NULL) { return TCL_ERROR; } rowUid = (Blt_Uid)Tk_GetUid("row"); columnUid = (Blt_Uid)Tk_GetUid("column"); return TCL_OK; }