home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / tkisrc04.zip / tk / os2 / tkCanvas.c < prev    next >
C/C++ Source or Header  |  1998-08-07  |  114KB  |  3,733 lines

  1. /* 
  2.  * tkCanvas.c --
  3.  *
  4.  *    This module implements canvas widgets for the Tk toolkit.
  5.  *    A canvas displays a background and a collection of graphical
  6.  *    objects such as rectangles, lines, and texts.
  7.  *
  8.  * Copyright (c) 1991-1994 The Regents of the University of California.
  9.  * Copyright (c) 1994-1995 Sun Microsystems, Inc.
  10.  *
  11.  * See the file "license.terms" for information on usage and redistribution
  12.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  13.  *
  14.  * SCCS: @(#) tkCanvas.c 1.119 96/03/21 11:26:39
  15.  */
  16.  
  17. #include "default.h"
  18. #include "tkInt.h"
  19. #include "tkPort.h"
  20. #include "tkCanvas.h"
  21.  
  22. /*
  23.  * See tkCanvas.h for key data structures used to implement canvases.
  24.  */
  25.  
  26. /*
  27.  * The structure defined below is used to keep track of a tag search
  28.  * in progress.  Only the "prevPtr" field should be accessed by anyone
  29.  * other than StartTagSearch and NextItem.
  30.  */
  31.  
  32. typedef struct TagSearch {
  33.     TkCanvas *canvasPtr;    /* Canvas widget being searched. */
  34.     Tk_Uid tag;            /* Tag to search for.   0 means return
  35.                  * all items. */
  36.     Tk_Item *prevPtr;        /* Item just before last one found (or NULL
  37.                  * if last one found was first in the item
  38.                  * list of canvasPtr). */
  39.     Tk_Item *currentPtr;    /* Pointer to last item returned. */
  40.     int searchOver;        /* Non-zero means NextItem should always
  41.                  * return NULL. */
  42. } TagSearch;
  43.  
  44. /*
  45.  * Information used for argv parsing.
  46.  */
  47.  
  48. static Tk_ConfigSpec configSpecs[] = {
  49.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  50.     DEF_CANVAS_BG_COLOR, Tk_Offset(TkCanvas, bgBorder),
  51.     TK_CONFIG_COLOR_ONLY},
  52.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  53.     DEF_CANVAS_BG_MONO, Tk_Offset(TkCanvas, bgBorder),
  54.     TK_CONFIG_MONO_ONLY},
  55.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  56.     (char *) NULL, 0, 0},
  57.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  58.     (char *) NULL, 0, 0},
  59.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  60.     DEF_CANVAS_BORDER_WIDTH, Tk_Offset(TkCanvas, borderWidth), 0},
  61.     {TK_CONFIG_DOUBLE, "-closeenough", "closeEnough", "CloseEnough",
  62.     DEF_CANVAS_CLOSE_ENOUGH, Tk_Offset(TkCanvas, closeEnough), 0},
  63.     {TK_CONFIG_BOOLEAN, "-confine", "confine", "Confine",
  64.     DEF_CANVAS_CONFINE, Tk_Offset(TkCanvas, confine), 0},
  65.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  66.     DEF_CANVAS_CURSOR, Tk_Offset(TkCanvas, cursor), TK_CONFIG_NULL_OK},
  67.     {TK_CONFIG_PIXELS, "-height", "height", "Height",
  68.     DEF_CANVAS_HEIGHT, Tk_Offset(TkCanvas, height), 0},
  69.     {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
  70.     "HighlightBackground", DEF_CANVAS_HIGHLIGHT_BG,
  71.     Tk_Offset(TkCanvas, highlightBgColorPtr), 0},
  72.     {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
  73.     DEF_CANVAS_HIGHLIGHT, Tk_Offset(TkCanvas, highlightColorPtr), 0},
  74.     {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
  75.     "HighlightThickness",
  76.     DEF_CANVAS_HIGHLIGHT_WIDTH, Tk_Offset(TkCanvas, highlightWidth), 0},
  77.     {TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
  78.     DEF_CANVAS_INSERT_BG, Tk_Offset(TkCanvas, textInfo.insertBorder), 0},
  79.     {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
  80.     DEF_CANVAS_INSERT_BD_COLOR,
  81.     Tk_Offset(TkCanvas, textInfo.insertBorderWidth), TK_CONFIG_COLOR_ONLY},
  82.     {TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
  83.     DEF_CANVAS_INSERT_BD_MONO,
  84.     Tk_Offset(TkCanvas, textInfo.insertBorderWidth), TK_CONFIG_MONO_ONLY},
  85.     {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime",
  86.     DEF_CANVAS_INSERT_OFF_TIME, Tk_Offset(TkCanvas, insertOffTime), 0},
  87.     {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime",
  88.     DEF_CANVAS_INSERT_ON_TIME, Tk_Offset(TkCanvas, insertOnTime), 0},
  89.     {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
  90.     DEF_CANVAS_INSERT_WIDTH, Tk_Offset(TkCanvas, textInfo.insertWidth), 0},
  91.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  92.     DEF_CANVAS_RELIEF, Tk_Offset(TkCanvas, relief), 0},
  93.     {TK_CONFIG_STRING, "-scrollregion", "scrollRegion", "ScrollRegion",
  94.     DEF_CANVAS_SCROLL_REGION, Tk_Offset(TkCanvas, regionString),
  95.     TK_CONFIG_NULL_OK},
  96.     {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
  97.     DEF_CANVAS_SELECT_COLOR, Tk_Offset(TkCanvas, textInfo.selBorder),
  98.     TK_CONFIG_COLOR_ONLY},
  99.     {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
  100.     DEF_CANVAS_SELECT_MONO, Tk_Offset(TkCanvas, textInfo.selBorder),
  101.     TK_CONFIG_MONO_ONLY},
  102.     {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
  103.     DEF_CANVAS_SELECT_BD_COLOR,
  104.     Tk_Offset(TkCanvas, textInfo.selBorderWidth), TK_CONFIG_COLOR_ONLY},
  105.     {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
  106.     DEF_CANVAS_SELECT_BD_MONO, Tk_Offset(TkCanvas, textInfo.selBorderWidth),
  107.     TK_CONFIG_MONO_ONLY},
  108.     {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
  109.     DEF_CANVAS_SELECT_FG_COLOR, Tk_Offset(TkCanvas, textInfo.selFgColorPtr),
  110.     TK_CONFIG_COLOR_ONLY},
  111.     {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
  112.     DEF_CANVAS_SELECT_FG_MONO, Tk_Offset(TkCanvas, textInfo.selFgColorPtr),
  113.     TK_CONFIG_MONO_ONLY},
  114.     {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
  115.     DEF_CANVAS_TAKE_FOCUS, Tk_Offset(TkCanvas, takeFocus),
  116.     TK_CONFIG_NULL_OK},
  117.     {TK_CONFIG_PIXELS, "-width", "width", "Width",
  118.     DEF_CANVAS_WIDTH, Tk_Offset(TkCanvas, width), 0},
  119.     {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
  120.     DEF_CANVAS_X_SCROLL_CMD, Tk_Offset(TkCanvas, xScrollCmd),
  121.     TK_CONFIG_NULL_OK},
  122.     {TK_CONFIG_PIXELS, "-xscrollincrement", "xScrollIncrement",
  123.     "ScrollIncrement",
  124.     DEF_CANVAS_X_SCROLL_INCREMENT, Tk_Offset(TkCanvas, xScrollIncrement),
  125.     0},
  126.     {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
  127.     DEF_CANVAS_Y_SCROLL_CMD, Tk_Offset(TkCanvas, yScrollCmd),
  128.     TK_CONFIG_NULL_OK},
  129.     {TK_CONFIG_PIXELS, "-yscrollincrement", "yScrollIncrement",
  130.     "ScrollIncrement",
  131.     DEF_CANVAS_Y_SCROLL_INCREMENT, Tk_Offset(TkCanvas, yScrollIncrement),
  132.     0},
  133.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  134.     (char *) NULL, 0, 0}
  135. };
  136.  
  137. /*
  138.  * List of all the item types known at present:
  139.  */
  140.  
  141. static Tk_ItemType *typeList = NULL;    /* NULL means initialization hasn't
  142.                      * been done yet. */
  143.  
  144. /*
  145.  * Standard item types provided by Tk:
  146.  */
  147.  
  148. extern Tk_ItemType tkArcType, tkBitmapType, tkImageType, tkLineType;
  149. extern Tk_ItemType tkOvalType, tkPolygonType;
  150. extern Tk_ItemType tkRectangleType, tkTextType, tkWindowType;
  151.  
  152. /*
  153.  * Various Tk_Uid's used by this module (set up during initialization):
  154.  */
  155.  
  156. static Tk_Uid allUid = NULL;
  157. static Tk_Uid currentUid = NULL;
  158.  
  159. /*
  160.  * Statistics counters:
  161.  */
  162.  
  163. static int numIdSearches;
  164. static int numSlowSearches;
  165.  
  166. /*
  167.  * Prototypes for procedures defined later in this file:
  168.  */
  169.  
  170. static void        CanvasBindProc _ANSI_ARGS_((ClientData clientData,
  171.                 XEvent *eventPtr));
  172. static void        CanvasBlinkProc _ANSI_ARGS_((ClientData clientData));
  173. static void        CanvasCmdDeletedProc _ANSI_ARGS_((
  174.                 ClientData clientData));
  175. static void        CanvasDoEvent _ANSI_ARGS_((TkCanvas *canvasPtr,
  176.                 XEvent *eventPtr));
  177. static void        CanvasEventProc _ANSI_ARGS_((ClientData clientData,
  178.                 XEvent *eventPtr));
  179. static int        CanvasFetchSelection _ANSI_ARGS_((
  180.                 ClientData clientData, int offset,
  181.                 char *buffer, int maxBytes));
  182. static Tk_Item *    CanvasFindClosest _ANSI_ARGS_((TkCanvas *canvasPtr,
  183.                 double coords[2]));
  184. static void        CanvasFocusProc _ANSI_ARGS_((TkCanvas *canvasPtr,
  185.                 int gotFocus));
  186. static void        CanvasLostSelection _ANSI_ARGS_((
  187.                 ClientData clientData));
  188. static void        CanvasSelectTo _ANSI_ARGS_((TkCanvas *canvasPtr,
  189.                 Tk_Item *itemPtr, int index));
  190. static void        CanvasSetOrigin _ANSI_ARGS_((TkCanvas *canvasPtr,
  191.                 int xOrigin, int yOrigin));
  192. static void        CanvasUpdateScrollbars _ANSI_ARGS_((
  193.                 TkCanvas *canvasPtr));
  194. static int        CanvasWidgetCmd _ANSI_ARGS_((ClientData clientData,
  195.                 Tcl_Interp *interp, int argc, char **argv));
  196. static int        ConfigureCanvas _ANSI_ARGS_((Tcl_Interp *interp,
  197.                 TkCanvas *canvasPtr, int argc, char **argv,
  198.                 int flags));
  199. static void        DestroyCanvas _ANSI_ARGS_((char *memPtr));
  200. static void        DisplayCanvas _ANSI_ARGS_((ClientData clientData));
  201. static void        DoItem _ANSI_ARGS_((Tcl_Interp *interp,
  202.                 Tk_Item *itemPtr, Tk_Uid tag));
  203. static int        FindItems _ANSI_ARGS_((Tcl_Interp *interp,
  204.                 TkCanvas *canvasPtr, int argc, char **argv,
  205.                 char *newTag, char *cmdName, char *option));
  206. static int        FindArea _ANSI_ARGS_((Tcl_Interp *interp,
  207.                 TkCanvas *canvasPtr, char **argv, Tk_Uid uid,
  208.                 int enclosed));
  209. static double        GridAlign _ANSI_ARGS_((double coord, double spacing));
  210. static void        InitCanvas _ANSI_ARGS_((void));
  211. static Tk_Item *    NextItem _ANSI_ARGS_((TagSearch *searchPtr));
  212. static void        PickCurrentItem _ANSI_ARGS_((TkCanvas *canvasPtr,
  213.                 XEvent *eventPtr));
  214. static void        PrintScrollFractions _ANSI_ARGS_((int screen1,
  215.                 int screen2, int object1, int object2,
  216.                 char *string));
  217. static void        RelinkItems _ANSI_ARGS_((TkCanvas *canvasPtr,
  218.                 char *tag, Tk_Item *prevPtr));
  219. static Tk_Item *    StartTagSearch _ANSI_ARGS_((TkCanvas *canvasPtr,
  220.                 char *tag, TagSearch *searchPtr));
  221.  
  222. /*
  223.  *--------------------------------------------------------------
  224.  *
  225.  * Tk_CanvasCmd --
  226.  *
  227.  *    This procedure is invoked to process the "canvas" Tcl
  228.  *    command.  See the user documentation for details on what
  229.  *    it does.
  230.  *
  231.  * Results:
  232.  *    A standard Tcl result.
  233.  *
  234.  * Side effects:
  235.  *    See the user documentation.
  236.  *
  237.  *--------------------------------------------------------------
  238.  */
  239.  
  240. int
  241. Tk_CanvasCmd(clientData, interp, argc, argv)
  242.     ClientData clientData;        /* Main window associated with
  243.                  * interpreter. */
  244.     Tcl_Interp *interp;        /* Current interpreter. */
  245.     int argc;            /* Number of arguments. */
  246.     char **argv;        /* Argument strings. */
  247. {
  248.     Tk_Window tkwin = (Tk_Window) clientData;
  249.     TkCanvas *canvasPtr;
  250.     Tk_Window new;
  251.  
  252.     if (typeList == NULL) {
  253.     InitCanvas();
  254.     }
  255.  
  256.     if (argc < 2) {
  257.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  258.         argv[0], " pathName ?options?\"", (char *) NULL);
  259.     return TCL_ERROR;
  260.     }
  261.  
  262.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  263.     if (new == NULL) {
  264.     return TCL_ERROR;
  265.     }
  266.  
  267.     /*
  268.      * Initialize fields that won't be initialized by ConfigureCanvas,
  269.      * or which ConfigureCanvas expects to have reasonable values
  270.      * (e.g. resource pointers).
  271.      */
  272.  
  273.     canvasPtr = (TkCanvas *) ckalloc(sizeof(TkCanvas));
  274.     canvasPtr->tkwin = new;
  275.     canvasPtr->display = Tk_Display(new);
  276.     canvasPtr->interp = interp;
  277.     canvasPtr->widgetCmd = Tcl_CreateCommand(interp,
  278.         Tk_PathName(canvasPtr->tkwin), CanvasWidgetCmd,
  279.         (ClientData) canvasPtr, CanvasCmdDeletedProc);
  280.     canvasPtr->firstItemPtr = NULL;
  281.     canvasPtr->lastItemPtr = NULL;
  282.     canvasPtr->borderWidth = 0;
  283.     canvasPtr->bgBorder = NULL;
  284.     canvasPtr->relief = TK_RELIEF_FLAT;
  285.     canvasPtr->highlightWidth = 0;
  286.     canvasPtr->highlightBgColorPtr = NULL;
  287.     canvasPtr->highlightColorPtr = NULL;
  288.     canvasPtr->inset = 0;
  289.     canvasPtr->pixmapGC = None;
  290.     canvasPtr->width = None;
  291.     canvasPtr->height = None;
  292.     canvasPtr->confine = 0;
  293.     canvasPtr->textInfo.selBorder = NULL;
  294.     canvasPtr->textInfo.selBorderWidth = 0;
  295.     canvasPtr->textInfo.selFgColorPtr = NULL;
  296.     canvasPtr->textInfo.selItemPtr = NULL;
  297.     canvasPtr->textInfo.selectFirst = -1;
  298.     canvasPtr->textInfo.selectLast = -1;
  299.     canvasPtr->textInfo.anchorItemPtr = NULL;
  300.     canvasPtr->textInfo.selectAnchor = 0;
  301.     canvasPtr->textInfo.insertBorder = NULL;
  302.     canvasPtr->textInfo.insertWidth = 0;
  303.     canvasPtr->textInfo.insertBorderWidth = 0;
  304.     canvasPtr->textInfo.focusItemPtr = NULL;
  305.     canvasPtr->textInfo.gotFocus = 0;
  306.     canvasPtr->textInfo.cursorOn = 0;
  307.     canvasPtr->insertOnTime = 0;
  308.     canvasPtr->insertOffTime = 0;
  309.     canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
  310.     canvasPtr->xOrigin = canvasPtr->yOrigin = 0;
  311.     canvasPtr->drawableXOrigin = canvasPtr->drawableYOrigin = 0;
  312.     canvasPtr->bindingTable = NULL;
  313.     canvasPtr->currentItemPtr = NULL;
  314.     canvasPtr->newCurrentPtr = NULL;
  315.     canvasPtr->closeEnough = 0.0;
  316.     canvasPtr->pickEvent.type = LeaveNotify;
  317.     canvasPtr->pickEvent.xcrossing.x = 0;
  318.     canvasPtr->pickEvent.xcrossing.y = 0;
  319.     canvasPtr->state = 0;
  320.     canvasPtr->xScrollCmd = NULL;
  321.     canvasPtr->yScrollCmd = NULL;
  322.     canvasPtr->scrollX1 = 0;
  323.     canvasPtr->scrollY1 = 0;
  324.     canvasPtr->scrollX2 = 0;
  325.     canvasPtr->scrollY2 = 0;
  326.     canvasPtr->regionString = NULL;
  327.     canvasPtr->xScrollIncrement = 0;
  328.     canvasPtr->yScrollIncrement = 0;
  329.     canvasPtr->scanX = 0;
  330.     canvasPtr->scanXOrigin = 0;
  331.     canvasPtr->scanY = 0;
  332.     canvasPtr->scanYOrigin = 0;
  333.     canvasPtr->hotPtr = NULL;
  334.     canvasPtr->hotPrevPtr = NULL;
  335.     canvasPtr->cursor = None;
  336.     canvasPtr->takeFocus = NULL;
  337.     canvasPtr->pixelsPerMM = WidthOfScreen(Tk_Screen(new));
  338.     canvasPtr->pixelsPerMM /= WidthMMOfScreen(Tk_Screen(new));
  339.     canvasPtr->flags = 0;
  340.     canvasPtr->nextId = 1;
  341.     canvasPtr->psInfoPtr = NULL;
  342.  
  343.     Tk_SetClass(canvasPtr->tkwin, "Canvas");
  344.     Tk_CreateEventHandler(canvasPtr->tkwin,
  345.         ExposureMask|StructureNotifyMask|FocusChangeMask,
  346.         CanvasEventProc, (ClientData) canvasPtr);
  347.     Tk_CreateEventHandler(canvasPtr->tkwin, KeyPressMask|KeyReleaseMask
  348.         |ButtonPressMask|ButtonReleaseMask|EnterWindowMask
  349.         |LeaveWindowMask|PointerMotionMask, CanvasBindProc,
  350.         (ClientData) canvasPtr);
  351.     Tk_CreateSelHandler(canvasPtr->tkwin, XA_PRIMARY, XA_STRING,
  352.         CanvasFetchSelection, (ClientData) canvasPtr, XA_STRING);
  353.     if (ConfigureCanvas(interp, canvasPtr, argc-2, argv+2, 0) != TCL_OK) {
  354.     goto error;
  355.     }
  356.  
  357.     interp->result = Tk_PathName(canvasPtr->tkwin);
  358.     return TCL_OK;
  359.  
  360.     error:
  361.     Tk_DestroyWindow(canvasPtr->tkwin);
  362.     return TCL_ERROR;
  363. }
  364.  
  365. /*
  366.  *--------------------------------------------------------------
  367.  *
  368.  * CanvasWidgetCmd --
  369.  *
  370.  *    This procedure is invoked to process the Tcl command
  371.  *    that corresponds to a widget managed by this module.
  372.  *    See the user documentation for details on what it does.
  373.  *
  374.  * Results:
  375.  *    A standard Tcl result.
  376.  *
  377.  * Side effects:
  378.  *    See the user documentation.
  379.  *
  380.  *--------------------------------------------------------------
  381.  */
  382.  
  383. static int
  384. CanvasWidgetCmd(clientData, interp, argc, argv)
  385.     ClientData clientData;        /* Information about canvas
  386.                      * widget. */
  387.     Tcl_Interp *interp;            /* Current interpreter. */
  388.     int argc;                /* Number of arguments. */
  389.     char **argv;            /* Argument strings. */
  390. {
  391.     TkCanvas *canvasPtr = (TkCanvas *) clientData;
  392.     size_t length;
  393.     int c, result;
  394.     Tk_Item *itemPtr = NULL;        /* Initialization needed only to
  395.                      * prevent compiler warning. */
  396.     TagSearch search;
  397.  
  398.     if (argc < 2) {
  399.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  400.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  401.     return TCL_ERROR;
  402.     }
  403.     Tcl_Preserve((ClientData) canvasPtr);
  404.     result = TCL_OK;
  405.     c = argv[1][0];
  406.     length = strlen(argv[1]);
  407.     if ((c == 'a') && (strncmp(argv[1], "addtag", length) == 0)) {
  408.     if (argc < 4) {
  409.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  410.             argv[0], " addtags tag searchCommand ?arg arg ...?\"",
  411.             (char *) NULL);
  412.         goto error;
  413.     }
  414.     result = FindItems(interp, canvasPtr, argc-3, argv+3, argv[2], argv[0],
  415.         " addtag tag");
  416.     } else if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)
  417.         && (length >= 2)) {
  418.     int i, gotAny;
  419.     int x1 = 0, y1 = 0, x2 = 0, y2 = 0;    /* Initializations needed
  420.                          * only to prevent compiler
  421.                          * warnings. */
  422.  
  423.     if (argc < 3) {
  424.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  425.             argv[0], " bbox tagOrId ?tagOrId ...?\"",
  426.             (char *) NULL);
  427.         goto error;
  428.     }
  429.     gotAny = 0;
  430.     for (i = 2; i < argc; i++) {
  431.         for (itemPtr = StartTagSearch(canvasPtr, argv[i], &search);
  432.             itemPtr != NULL; itemPtr = NextItem(&search)) {
  433.         if ((itemPtr->x1 >= itemPtr->x2)
  434.             || (itemPtr->y1 >= itemPtr->y2)) {
  435.             continue;
  436.         }
  437.         if (!gotAny) {
  438.             x1 = itemPtr->x1;
  439.             y1 = itemPtr->y1;
  440.             x2 = itemPtr->x2;
  441.             y2 = itemPtr->y2;
  442.             gotAny = 1;
  443.         } else {
  444.             if (itemPtr->x1 < x1) {
  445.             x1 = itemPtr->x1;
  446.             }
  447.             if (itemPtr->y1 < y1) {
  448.             y1 = itemPtr->y1;
  449.             }
  450.             if (itemPtr->x2 > x2) {
  451.             x2 = itemPtr->x2;
  452.             }
  453.             if (itemPtr->y2 > y2) {
  454.             y2 = itemPtr->y2;
  455.             }
  456.         }
  457.         }
  458.     }
  459.     if (gotAny) {
  460.         sprintf(interp->result, "%d %d %d %d", x1, y1, x2, y2);
  461.     }
  462.     } else if ((c == 'b') && (strncmp(argv[1], "bind", length) == 0)
  463.         && (length >= 2)) {
  464.     ClientData object;
  465.  
  466.     if ((argc < 3) || (argc > 5)) {
  467.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  468.             argv[0], " bind tagOrId ?sequence? ?command?\"",
  469.             (char *) NULL);
  470.         goto error;
  471.     }
  472.  
  473.     /*
  474.      * Figure out what object to use for the binding (individual
  475.      * item vs. tag).
  476.      */
  477.  
  478.     object = 0;
  479.     if (isdigit(UCHAR(argv[2][0]))) {
  480.         int id;
  481.         char *end;
  482.  
  483.         id = strtoul(argv[2], &end, 0);
  484.         if (*end != 0) {
  485.         goto bindByTag;
  486.         }
  487.         for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  488.             itemPtr = itemPtr->nextPtr) {
  489.         if (itemPtr->id == id) {
  490.             object = (ClientData) itemPtr;
  491.             break;
  492.         }
  493.         }
  494.         if (object == 0) {
  495.         Tcl_AppendResult(interp, "item \"", argv[2],
  496.             "\" doesn't exist", (char *) NULL);
  497.         goto error;
  498.         }
  499.     } else {
  500.         bindByTag:
  501.         object = (ClientData) Tk_GetUid(argv[2]);
  502.     }
  503.  
  504.     /*
  505.      * Make a binding table if the canvas doesn't already have
  506.      * one.
  507.      */
  508.  
  509.     if (canvasPtr->bindingTable == NULL) {
  510.         canvasPtr->bindingTable = Tk_CreateBindingTable(interp);
  511.     }
  512.  
  513.     if (argc == 5) {
  514.         int append = 0;
  515.         unsigned long mask;
  516.  
  517.         if (argv[4][0] == 0) {
  518.         result = Tk_DeleteBinding(interp, canvasPtr->bindingTable,
  519.             object, argv[3]);
  520.         goto done;
  521.         }
  522.         if (argv[4][0] == '+') {
  523.         argv[4]++;
  524.         append = 1;
  525.         }
  526.         mask = Tk_CreateBinding(interp, canvasPtr->bindingTable,
  527.             object, argv[3], argv[4], append);
  528.         if (mask == 0) {
  529.         goto error;
  530.         }
  531.         if (mask & (unsigned) ~(ButtonMotionMask|Button1MotionMask
  532.             |Button2MotionMask|Button3MotionMask|Button4MotionMask
  533.             |Button5MotionMask|ButtonPressMask|ButtonReleaseMask
  534.             |EnterWindowMask|LeaveWindowMask|KeyPressMask
  535.             |KeyReleaseMask|PointerMotionMask)) {
  536.         Tk_DeleteBinding(interp, canvasPtr->bindingTable,
  537.             object, argv[3]);
  538.         Tcl_ResetResult(interp);
  539.         Tcl_AppendResult(interp, "requested illegal events; ",
  540.             "only key, button, motion, and enter/leave ",
  541.             "events may be used", (char *) NULL);
  542.         goto error;
  543.         }
  544.     } else if (argc == 4) {
  545.         char *command;
  546.     
  547.         command = Tk_GetBinding(interp, canvasPtr->bindingTable,
  548.             object, argv[3]);
  549.         if (command == NULL) {
  550.         goto error;
  551.         }
  552.         interp->result = command;
  553.     } else {
  554.         Tk_GetAllBindings(interp, canvasPtr->bindingTable, object);
  555.     }
  556.     } else if ((c == 'c') && (strcmp(argv[1], "canvasx") == 0)) {
  557.     int x;
  558.     double grid;
  559.  
  560.     if ((argc < 3) || (argc > 4)) {
  561.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  562.             argv[0], " canvasx screenx ?gridspacing?\"",
  563.             (char *) NULL);
  564.         goto error;
  565.     }
  566.     if (Tk_GetPixels(interp, canvasPtr->tkwin, argv[2], &x) != TCL_OK) {
  567.         goto error;
  568.     }
  569.     if (argc == 4) {
  570.         if (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[3],
  571.             &grid) != TCL_OK) {
  572.         goto error;
  573.         }
  574.     } else {
  575.         grid = 0.0;
  576.     }
  577.     x += canvasPtr->xOrigin;
  578.     Tcl_PrintDouble(interp, GridAlign((double) x, grid), interp->result);
  579.     } else if ((c == 'c') && (strcmp(argv[1], "canvasy") == 0)) {
  580.     int y;
  581.     double grid;
  582.  
  583.     if ((argc < 3) || (argc > 4)) {
  584.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  585.             argv[0], " canvasy screeny ?gridspacing?\"",
  586.             (char *) NULL);
  587.         goto error;
  588.     }
  589.     if (Tk_GetPixels(interp, canvasPtr->tkwin, argv[2], &y) != TCL_OK) {
  590.         goto error;
  591.     }
  592.     if (argc == 4) {
  593.         if (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr,
  594.             argv[3], &grid) != TCL_OK) {
  595.         goto error;
  596.         }
  597.     } else {
  598.         grid = 0.0;
  599.     }
  600.     y += canvasPtr->yOrigin;
  601.     Tcl_PrintDouble(interp, GridAlign((double) y, grid), interp->result);
  602.     } else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
  603.         && (length >= 2)) {
  604.     if (argc != 3) {
  605.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  606.             argv[0], " cget option\"",
  607.             (char *) NULL);
  608.         goto error;
  609.     }
  610.     result = Tk_ConfigureValue(interp, canvasPtr->tkwin, configSpecs,
  611.         (char *) canvasPtr, argv[2], 0);
  612.     } else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  613.         && (length >= 3)) {
  614.     if (argc == 2) {
  615.         result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
  616.             (char *) canvasPtr, (char *) NULL, 0);
  617.     } else if (argc == 3) {
  618.         result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
  619.             (char *) canvasPtr, argv[2], 0);
  620.     } else {
  621.         result = ConfigureCanvas(interp, canvasPtr, argc-2, argv+2,
  622.             TK_CONFIG_ARGV_ONLY);
  623.     }
  624.     } else if ((c == 'c') && (strncmp(argv[1], "coords", length) == 0)
  625.         && (length >= 3)) {
  626.     if (argc < 3) {
  627.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  628.             argv[0], " coords tagOrId ?x y x y ...?\"",
  629.             (char *) NULL);
  630.         goto error;
  631.     }
  632.     itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  633.     if (itemPtr != NULL) {
  634.         if (argc != 3) {
  635.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  636.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  637.         }
  638.         if (itemPtr->typePtr->coordProc != NULL) {
  639.         result = (*itemPtr->typePtr->coordProc)(interp,
  640.             (Tk_Canvas) canvasPtr, itemPtr, argc-3, argv+3);
  641.         }
  642.         if (argc != 3) {
  643.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  644.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  645.         }
  646.     }
  647.     } else if ((c == 'c') && (strncmp(argv[1], "create", length) == 0)
  648.         && (length >= 2)) {
  649.     Tk_ItemType *typePtr;
  650.     Tk_ItemType *matchPtr = NULL;
  651.     Tk_Item *itemPtr;
  652.  
  653.     if (argc < 3) {
  654.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  655.             argv[0], " create type ?arg arg ...?\"", (char *) NULL);
  656.         goto error;
  657.     }
  658.     c = argv[2][0];
  659.     length = strlen(argv[2]);
  660.     for (typePtr = typeList; typePtr != NULL; typePtr = typePtr->nextPtr) {
  661.         if ((c == typePtr->name[0])
  662.             && (strncmp(argv[2], typePtr->name, length) == 0)) {
  663.         if (matchPtr != NULL) {
  664.             badType:
  665.             Tcl_AppendResult(interp,
  666.                 "unknown or ambiguous item type \"",
  667.                 argv[2], "\"", (char *) NULL);
  668.             goto error;
  669.         }
  670.         matchPtr = typePtr;
  671.         }
  672.     }
  673.     if (matchPtr == NULL) {
  674.         goto badType;
  675.     }
  676.     typePtr = matchPtr;
  677.     itemPtr = (Tk_Item *) ckalloc((unsigned) typePtr->itemSize);
  678.     itemPtr->id = canvasPtr->nextId;
  679.     canvasPtr->nextId++;
  680.     itemPtr->tagPtr = itemPtr->staticTagSpace;
  681.     itemPtr->tagSpace = TK_TAG_SPACE;
  682.     itemPtr->numTags = 0;
  683.     itemPtr->typePtr = typePtr;
  684.     if ((*typePtr->createProc)(interp, (Tk_Canvas) canvasPtr,
  685.         itemPtr, argc-3, argv+3) != TCL_OK) {
  686.         ckfree((char *) itemPtr);
  687.         goto error;
  688.     }
  689.     itemPtr->nextPtr = NULL;
  690.     canvasPtr->hotPtr = itemPtr;
  691.     canvasPtr->hotPrevPtr = canvasPtr->lastItemPtr;
  692.     if (canvasPtr->lastItemPtr == NULL) {
  693.         canvasPtr->firstItemPtr = itemPtr;
  694.     } else {
  695.         canvasPtr->lastItemPtr->nextPtr = itemPtr;
  696.     }
  697.     canvasPtr->lastItemPtr = itemPtr;
  698.     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  699.         itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  700.     canvasPtr->flags |= REPICK_NEEDED;
  701.     sprintf(interp->result, "%d", itemPtr->id);
  702.     } else if ((c == 'd') && (strncmp(argv[1], "dchars", length) == 0)
  703.         && (length >= 2)) {
  704.     int first, last;
  705.  
  706.     if ((argc != 4) && (argc != 5)) {
  707.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  708.             argv[0], " dchars tagOrId first ?last?\"",
  709.             (char *) NULL);
  710.         goto error;
  711.     }
  712.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  713.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  714.         if ((itemPtr->typePtr->indexProc == NULL)
  715.             || (itemPtr->typePtr->dCharsProc == NULL)) {
  716.         continue;
  717.         }
  718.         if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
  719.             itemPtr, argv[3], &first) != TCL_OK) {
  720.         goto error;
  721.         }
  722.         if (argc == 5) {
  723.         if ((*itemPtr->typePtr->indexProc)(interp,
  724.             (Tk_Canvas) canvasPtr, itemPtr, argv[4], &last)
  725.             != TCL_OK) {
  726.             goto error;
  727.         }
  728.         } else {
  729.         last = first;
  730.         }
  731.  
  732.         /*
  733.          * Redraw both item's old and new areas:  it's possible
  734.          * that a delete could result in a new area larger than
  735.          * the old area.
  736.          */
  737.  
  738.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  739.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  740.         (*itemPtr->typePtr->dCharsProc)((Tk_Canvas) canvasPtr,
  741.             itemPtr, first, last);
  742.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  743.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  744.     }
  745.     } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)
  746.         && (length >= 2)) {
  747.     int i;
  748.  
  749.     for (i = 2; i < argc; i++) {
  750.         for (itemPtr = StartTagSearch(canvasPtr, argv[i], &search);
  751.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  752.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  753.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  754.         if (canvasPtr->bindingTable != NULL) {
  755.             Tk_DeleteAllBindings(canvasPtr->bindingTable,
  756.                 (ClientData) itemPtr);
  757.         }
  758.         (*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr,
  759.             canvasPtr->display);
  760.         if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
  761.             ckfree((char *) itemPtr->tagPtr);
  762.         }
  763.         if (search.prevPtr == NULL) {
  764.             canvasPtr->firstItemPtr = itemPtr->nextPtr;
  765.             if (canvasPtr->firstItemPtr == NULL) {
  766.             canvasPtr->lastItemPtr = NULL;
  767.             }
  768.         } else {
  769.             search.prevPtr->nextPtr = itemPtr->nextPtr;
  770.         }
  771.         if (canvasPtr->lastItemPtr == itemPtr) {
  772.             canvasPtr->lastItemPtr = search.prevPtr;
  773.         }
  774.         ckfree((char *) itemPtr);
  775.         if (itemPtr == canvasPtr->currentItemPtr) {
  776.             canvasPtr->currentItemPtr = NULL;
  777.             canvasPtr->flags |= REPICK_NEEDED;
  778.         }
  779.         if (itemPtr == canvasPtr->newCurrentPtr) {
  780.             canvasPtr->newCurrentPtr = NULL;
  781.             canvasPtr->flags |= REPICK_NEEDED;
  782.         }
  783.         if (itemPtr == canvasPtr->textInfo.focusItemPtr) {
  784.             canvasPtr->textInfo.focusItemPtr = NULL;
  785.         }
  786.         if (itemPtr == canvasPtr->textInfo.selItemPtr) {
  787.             canvasPtr->textInfo.selItemPtr = NULL;
  788.         }
  789.         if ((itemPtr == canvasPtr->hotPtr)
  790.             || (itemPtr == canvasPtr->hotPrevPtr)) {
  791.             canvasPtr->hotPtr = NULL;
  792.         }
  793.         }
  794.     }
  795.     } else if ((c == 'd') && (strncmp(argv[1], "dtag", length) == 0)
  796.         && (length >= 2)) {
  797.     Tk_Uid tag;
  798.     int i;
  799.  
  800.     if ((argc != 3) && (argc != 4)) {
  801.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  802.             argv[0], " dtag tagOrId ?tagToDelete?\"",
  803.             (char *) NULL);
  804.         goto error;
  805.     }
  806.     if (argc == 4) {
  807.         tag = Tk_GetUid(argv[3]);
  808.     } else {
  809.         tag = Tk_GetUid(argv[2]);
  810.     }
  811.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  812.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  813.         for (i = itemPtr->numTags-1; i >= 0; i--) {
  814.         if (itemPtr->tagPtr[i] == tag) {
  815.             itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
  816.             itemPtr->numTags--;
  817.         }
  818.         }
  819.     }
  820.     } else if ((c == 'f') && (strncmp(argv[1], "find", length) == 0)
  821.         && (length >= 2)) {
  822.     if (argc < 3) {
  823.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  824.             argv[0], " find searchCommand ?arg arg ...?\"",
  825.             (char *) NULL);
  826.         goto error;
  827.     }
  828.     result = FindItems(interp, canvasPtr, argc-2, argv+2, (char *) NULL,
  829.         argv[0]," find");
  830.     } else if ((c == 'f') && (strncmp(argv[1], "focus", length) == 0)
  831.         && (length >= 2)) {
  832.     if (argc > 3) {
  833.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  834.             argv[0], " focus ?tagOrId?\"",
  835.             (char *) NULL);
  836.         goto error;
  837.     }
  838.     itemPtr = canvasPtr->textInfo.focusItemPtr;
  839.     if (argc == 2) {
  840.         if (itemPtr != NULL) {
  841.         sprintf(interp->result, "%d", itemPtr->id);
  842.         }
  843.         goto done;
  844.     }
  845.     if ((itemPtr != NULL) && (canvasPtr->textInfo.gotFocus)) {
  846.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  847.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  848.     }
  849.     if (argv[2][0] == 0) {
  850.         canvasPtr->textInfo.focusItemPtr = NULL;
  851.         goto done;
  852.     }
  853.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  854.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  855.         if (itemPtr->typePtr->icursorProc != NULL) {
  856.         break;
  857.         }
  858.     }
  859.     if (itemPtr == NULL) {
  860.         goto done;
  861.     }
  862.     canvasPtr->textInfo.focusItemPtr = itemPtr;
  863.     if (canvasPtr->textInfo.gotFocus) {
  864.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  865.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  866.     }
  867.     } else if ((c == 'g') && (strncmp(argv[1], "gettags", length) == 0)) {
  868.     if (argc != 3) {
  869.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  870.             argv[0], " gettags tagOrId\"", (char *) NULL);
  871.         goto error;
  872.     }
  873.     itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  874.     if (itemPtr != NULL) {
  875.         int i;
  876.         for (i = 0; i < itemPtr->numTags; i++) {
  877.         Tcl_AppendElement(interp, (char *) itemPtr->tagPtr[i]);
  878.         }
  879.     }
  880.     } else if ((c == 'i') && (strncmp(argv[1], "icursor", length) == 0)
  881.         && (length >= 2)) {
  882.     int index;
  883.  
  884.     if (argc != 4) {
  885.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  886.             argv[0], " icursor tagOrId index\"",
  887.             (char *) NULL);
  888.         goto error;
  889.     }
  890.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  891.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  892.         if ((itemPtr->typePtr->indexProc == NULL)
  893.             || (itemPtr->typePtr->icursorProc == NULL)) {
  894.         goto done;
  895.         }
  896.         if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
  897.             itemPtr, argv[3], &index) != TCL_OK) {
  898.         goto error;
  899.         }
  900.         (*itemPtr->typePtr->icursorProc)((Tk_Canvas) canvasPtr, itemPtr,
  901.             index);
  902.         if ((itemPtr == canvasPtr->textInfo.focusItemPtr)
  903.             && (canvasPtr->textInfo.cursorOn)) {
  904.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  905.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  906.         }
  907.     }
  908.     } else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
  909.         && (length >= 3)) {
  910.     int index;
  911.  
  912.     if (argc != 4) {
  913.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  914.             argv[0], " index tagOrId string\"",
  915.             (char *) NULL);
  916.         goto error;
  917.     }
  918.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  919.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  920.         if (itemPtr->typePtr->indexProc != NULL) {
  921.         break;
  922.         }
  923.     }
  924.     if (itemPtr == NULL) {
  925.         Tcl_AppendResult(interp, "can't find an indexable item \"",
  926.             argv[2], "\"", (char *) NULL);
  927.         goto error;
  928.     }
  929.     if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
  930.         itemPtr, argv[3], &index) != TCL_OK) {
  931.         goto error;
  932.     }
  933.     sprintf(interp->result, "%d", index);
  934.     } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
  935.         && (length >= 3)) {
  936.     int beforeThis;
  937.  
  938.     if (argc != 5) {
  939.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  940.             argv[0], " insert tagOrId beforeThis string\"",
  941.             (char *) NULL);
  942.         goto error;
  943.     }
  944.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  945.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  946.         if ((itemPtr->typePtr->indexProc == NULL)
  947.             || (itemPtr->typePtr->insertProc == NULL)) {
  948.         continue;
  949.         }
  950.         if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
  951.             itemPtr, argv[3], &beforeThis) != TCL_OK) {
  952.         goto error;
  953.         }
  954.  
  955.         /*
  956.          * Redraw both item's old and new areas:  it's possible
  957.          * that an insertion could result in a new area either
  958.          * larger or smaller than the old area.
  959.          */
  960.  
  961.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  962.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  963.         (*itemPtr->typePtr->insertProc)((Tk_Canvas) canvasPtr,
  964.             itemPtr, beforeThis, argv[4]);
  965.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, itemPtr->x1,
  966.             itemPtr->y1, itemPtr->x2, itemPtr->y2);
  967.     }
  968.     } else if ((c == 'i') && (strncmp(argv[1], "itemcget", length) == 0)
  969.         && (length >= 6)) {
  970.     if (argc != 4) {
  971.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  972.             argv[0], " itemcget tagOrId option\"",
  973.             (char *) NULL);
  974.         return TCL_ERROR;
  975.     }
  976.     itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  977.     if (itemPtr != NULL) {
  978.         result = Tk_ConfigureValue(canvasPtr->interp, canvasPtr->tkwin,
  979.             itemPtr->typePtr->configSpecs, (char *) itemPtr,
  980.             argv[3], 0);
  981.     }
  982.     } else if ((c == 'i') && (strncmp(argv[1], "itemconfigure", length) == 0)
  983.         && (length >= 6)) {
  984.     if (argc < 3) {
  985.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  986.             argv[0], " itemconfigure tagOrId ?option value ...?\"",
  987.             (char *) NULL);
  988.         goto error;
  989.     }
  990.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  991.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  992.         if (argc == 3) {
  993.         result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
  994.             itemPtr->typePtr->configSpecs, (char *) itemPtr,
  995.             (char *) NULL, 0);
  996.         } else if (argc == 4) {
  997.         result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
  998.             itemPtr->typePtr->configSpecs, (char *) itemPtr,
  999.             argv[3], 0);
  1000.         } else {
  1001.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  1002.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  1003.         result = (*itemPtr->typePtr->configProc)(interp,
  1004.             (Tk_Canvas) canvasPtr, itemPtr, argc-3, argv+3,
  1005.             TK_CONFIG_ARGV_ONLY);
  1006.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  1007.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  1008.         canvasPtr->flags |= REPICK_NEEDED;
  1009.         }
  1010.         if ((result != TCL_OK) || (argc < 5)) {
  1011.         break;
  1012.         }
  1013.     }
  1014.     } else if ((c == 'l') && (strncmp(argv[1], "lower", length) == 0)) {
  1015.     Tk_Item *prevPtr;
  1016.  
  1017.     if ((argc != 3) && (argc != 4)) {
  1018.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1019.             argv[0], " lower tagOrId ?belowThis?\"",
  1020.             (char *) NULL);
  1021.         goto error;
  1022.     }
  1023.  
  1024.     /*
  1025.      * First find the item just after which we'll insert the
  1026.      * named items.
  1027.      */
  1028.  
  1029.     if (argc == 3) {
  1030.         prevPtr = NULL;
  1031.     } else {
  1032.         prevPtr = StartTagSearch(canvasPtr, argv[3], &search);
  1033.         if (prevPtr != NULL) {
  1034.         prevPtr = search.prevPtr;
  1035.         } else {
  1036.         Tcl_AppendResult(interp, "tag \"", argv[3],
  1037.             "\" doesn't match any items", (char *) NULL);
  1038.         goto error;
  1039.         }
  1040.     }
  1041.     RelinkItems(canvasPtr, argv[2], prevPtr);
  1042.     } else if ((c == 'm') && (strncmp(argv[1], "move", length) == 0)) {
  1043.     double xAmount, yAmount;
  1044.  
  1045.     if (argc != 5) {
  1046.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1047.             argv[0], " move tagOrId xAmount yAmount\"",
  1048.             (char *) NULL);
  1049.         goto error;
  1050.     }
  1051.     if ((Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[3],
  1052.         &xAmount) != TCL_OK) || (Tk_CanvasGetCoord(interp,
  1053.         (Tk_Canvas) canvasPtr, argv[4], &yAmount) != TCL_OK)) {
  1054.         goto error;
  1055.     }
  1056.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  1057.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  1058.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  1059.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  1060.         (void) (*itemPtr->typePtr->translateProc)((Tk_Canvas) canvasPtr,
  1061.             itemPtr,  xAmount, yAmount);
  1062.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  1063.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  1064.         canvasPtr->flags |= REPICK_NEEDED;
  1065.     }
  1066.     } else if ((c == 'p') && (strncmp(argv[1], "postscript", length) == 0)) {
  1067.     result = TkCanvPostscriptCmd(canvasPtr, interp, argc, argv);
  1068.     } else if ((c == 'r') && (strncmp(argv[1], "raise", length) == 0)) {
  1069.     Tk_Item *prevPtr;
  1070.  
  1071.     if ((argc != 3) && (argc != 4)) {
  1072.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1073.             argv[0], " raise tagOrId ?aboveThis?\"",
  1074.             (char *) NULL);
  1075.         goto error;
  1076.     }
  1077.  
  1078.     /*
  1079.      * First find the item just after which we'll insert the
  1080.      * named items.
  1081.      */
  1082.  
  1083.     if (argc == 3) {
  1084.         prevPtr = canvasPtr->lastItemPtr;
  1085.     } else {
  1086.         prevPtr = NULL;
  1087.         for (itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
  1088.             itemPtr != NULL; itemPtr = NextItem(&search)) {
  1089.         prevPtr = itemPtr;
  1090.         }
  1091.         if (prevPtr == NULL) {
  1092.         Tcl_AppendResult(interp, "tagOrId \"", argv[3],
  1093.             "\" doesn't match any items", (char *) NULL);
  1094.         goto error;
  1095.         }
  1096.     }
  1097.     RelinkItems(canvasPtr, argv[2], prevPtr);
  1098.     } else if ((c == 's') && (strncmp(argv[1], "scale", length) == 0)
  1099.         && (length >= 3)) {
  1100.     double xOrigin, yOrigin, xScale, yScale;
  1101.  
  1102.     if (argc != 7) {
  1103.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1104.             argv[0], " scale tagOrId xOrigin yOrigin xScale yScale\"",
  1105.             (char *) NULL);
  1106.         goto error;
  1107.     }
  1108.     if ((Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr,
  1109.             argv[3], &xOrigin) != TCL_OK)
  1110.         || (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr,
  1111.             argv[4], &yOrigin) != TCL_OK)
  1112.         || (Tcl_GetDouble(interp, argv[5], &xScale) != TCL_OK)
  1113.         || (Tcl_GetDouble(interp, argv[6], &yScale) != TCL_OK)) {
  1114.         goto error;
  1115.     }
  1116.     if ((xScale == 0.0) || (yScale == 0.0)) {
  1117.         interp->result = "scale factor cannot be zero";
  1118.         goto error;
  1119.     }
  1120.     for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  1121.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  1122.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  1123.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  1124.         (void) (*itemPtr->typePtr->scaleProc)((Tk_Canvas) canvasPtr,
  1125.             itemPtr, xOrigin, yOrigin, xScale, yScale);
  1126.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  1127.             itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  1128.         canvasPtr->flags |= REPICK_NEEDED;
  1129.     }
  1130.     } else if ((c == 's') && (strncmp(argv[1], "scan", length) == 0)
  1131.         && (length >= 3)) {
  1132.     int x, y;
  1133.  
  1134.     if (argc != 5) {
  1135.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1136.             argv[0], " scan mark|dragto x y\"", (char *) NULL);
  1137.         goto error;
  1138.     }
  1139.     if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
  1140.         || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){
  1141.         goto error;
  1142.     }
  1143.     if ((argv[2][0] == 'm')
  1144.         && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
  1145.         canvasPtr->scanX = x;
  1146.         canvasPtr->scanXOrigin = canvasPtr->xOrigin;
  1147.         canvasPtr->scanY = y;
  1148.         canvasPtr->scanYOrigin = canvasPtr->yOrigin;
  1149.     } else if ((argv[2][0] == 'd')
  1150.         && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
  1151.         int newXOrigin, newYOrigin, tmp;
  1152.  
  1153.         /*
  1154.          * Compute a new view origin for the canvas, amplifying the
  1155.          * mouse motion.
  1156.          */
  1157.  
  1158.         tmp = canvasPtr->scanXOrigin - 10*(x - canvasPtr->scanX)
  1159.             - canvasPtr->scrollX1;
  1160.         newXOrigin = canvasPtr->scrollX1 + tmp;
  1161.         tmp = canvasPtr->scanYOrigin - 10*(y - canvasPtr->scanY)
  1162.             - canvasPtr->scrollY1;
  1163.         newYOrigin = canvasPtr->scrollY1 + tmp;
  1164.         CanvasSetOrigin(canvasPtr, newXOrigin, newYOrigin);
  1165.     } else {
  1166.         Tcl_AppendResult(interp, "bad scan option \"", argv[2],
  1167.             "\": must be mark or dragto", (char *) NULL);
  1168.         goto error;
  1169.     }
  1170.     } else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
  1171.         && (length >= 2)) {
  1172.     int index;
  1173.  
  1174.     if (argc < 3) {
  1175.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1176.             argv[0], " select option ?tagOrId? ?arg?\"", (char *) NULL);
  1177.         goto error;
  1178.     }
  1179.     if (argc >= 4) {
  1180.         for (itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
  1181.             itemPtr != NULL; itemPtr = NextItem(&search)) {
  1182.         if ((itemPtr->typePtr->indexProc != NULL)
  1183.             && (itemPtr->typePtr->selectionProc != NULL)){
  1184.             break;
  1185.         }
  1186.         }
  1187.         if (itemPtr == NULL) {
  1188.         Tcl_AppendResult(interp,
  1189.             "can't find an indexable and selectable item \"",
  1190.             argv[3], "\"", (char *) NULL);
  1191.         goto error;
  1192.         }
  1193.     }
  1194.     if (argc == 5) {
  1195.         if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
  1196.             itemPtr, argv[4], &index) != TCL_OK) {
  1197.         goto error;
  1198.         }
  1199.     }
  1200.     length = strlen(argv[2]);
  1201.     c = argv[2][0];
  1202.     if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
  1203.         if (argc != 5) {
  1204.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1205.             argv[0], " select adjust tagOrId index\"",
  1206.             (char *) NULL);
  1207.         goto error;
  1208.         }
  1209.         if (canvasPtr->textInfo.selItemPtr == itemPtr) {
  1210.         if (index < (canvasPtr->textInfo.selectFirst
  1211.             + canvasPtr->textInfo.selectLast)/2) {
  1212.             canvasPtr->textInfo.selectAnchor =
  1213.                 canvasPtr->textInfo.selectLast + 1;
  1214.         } else {
  1215.             canvasPtr->textInfo.selectAnchor =
  1216.                 canvasPtr->textInfo.selectFirst;
  1217.         }
  1218.         }
  1219.         CanvasSelectTo(canvasPtr, itemPtr, index);
  1220.     } else if ((c == 'c') && (argv[2] != NULL)
  1221.         && (strncmp(argv[2], "clear", length) == 0)) {
  1222.         if (argc != 3) {
  1223.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1224.             argv[0], " select clear\"", (char *) NULL);
  1225.         goto error;
  1226.         }
  1227.         if (canvasPtr->textInfo.selItemPtr != NULL) {
  1228.         Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  1229.             canvasPtr->textInfo.selItemPtr->x1,
  1230.             canvasPtr->textInfo.selItemPtr->y1,
  1231.             canvasPtr->textInfo.selItemPtr->x2,
  1232.             canvasPtr->textInfo.selItemPtr->y2);
  1233.         canvasPtr->textInfo.selItemPtr = NULL;
  1234.         }
  1235.         goto done;
  1236.     } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
  1237.         if (argc != 5) {
  1238.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1239.             argv[0], " select from tagOrId index\"",
  1240.             (char *) NULL);
  1241.         goto error;
  1242.         }
  1243.         canvasPtr->textInfo.anchorItemPtr = itemPtr;
  1244.         canvasPtr->textInfo.selectAnchor = index;
  1245.     } else if ((c == 'i') && (strncmp(argv[2], "item", length) == 0)) {
  1246.         if (argc != 3) {
  1247.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1248.             argv[0], " select item\"", (char *) NULL);
  1249.         goto error;
  1250.         }
  1251.         if (canvasPtr->textInfo.selItemPtr != NULL) {
  1252.         sprintf(interp->result, "%d",
  1253.             canvasPtr->textInfo.selItemPtr->id);
  1254.         }
  1255.     } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
  1256.         if (argc != 5) {
  1257.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1258.             argv[0], " select to tagOrId index\"",
  1259.             (char *) NULL);
  1260.         goto error;
  1261.         }
  1262.         CanvasSelectTo(canvasPtr, itemPtr, index);
  1263.     } else {
  1264.         Tcl_AppendResult(interp, "bad select option \"", argv[2],
  1265.             "\": must be adjust, clear, from, item, or to",
  1266.             (char *) NULL);
  1267.         goto error;
  1268.     }
  1269.     } else if ((c == 't') && (strncmp(argv[1], "type", length) == 0)) {
  1270.     if (argc != 3) {
  1271.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  1272.             argv[0], " type tag\"", (char *) NULL);
  1273.         goto error;
  1274.     }
  1275.     itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
  1276.     if (itemPtr != NULL) {
  1277.         interp->result = itemPtr->typePtr->name;
  1278.     }
  1279.     } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
  1280.     int count, type;
  1281.     int newX = 0;        /* Initialization needed only to prevent
  1282.                  * gcc warnings. */
  1283.     double fraction;
  1284.  
  1285.     if (argc == 2) {
  1286.         PrintScrollFractions(canvasPtr->xOrigin + canvasPtr->inset,
  1287.             canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin)
  1288.             - canvasPtr->inset, canvasPtr->scrollX1,
  1289.             canvasPtr->scrollX2, interp->result);
  1290.     } else {
  1291.         type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
  1292.         switch (type) {
  1293.         case TK_SCROLL_ERROR:
  1294.             goto error;
  1295.         case TK_SCROLL_MOVETO:
  1296.             newX = canvasPtr->scrollX1 - canvasPtr->inset
  1297.                 + (int) (fraction * (canvasPtr->scrollX2
  1298.                 - canvasPtr->scrollX1) + 0.5);
  1299.             break;
  1300.         case TK_SCROLL_PAGES:
  1301.             newX = canvasPtr->xOrigin + count * .9
  1302.                 * (Tk_Width(canvasPtr->tkwin) - 2*canvasPtr->inset);
  1303.             break;
  1304.         case TK_SCROLL_UNITS:
  1305.             if (canvasPtr->xScrollIncrement > 0) {
  1306.             newX = canvasPtr->xOrigin
  1307.                 + count*canvasPtr->xScrollIncrement;
  1308.             } else {
  1309.             newX = canvasPtr->xOrigin + count * .1
  1310.                 * (Tk_Width(canvasPtr->tkwin)
  1311.                 - 2*canvasPtr->inset);
  1312.             }
  1313.             break;
  1314.         }
  1315.         CanvasSetOrigin(canvasPtr, newX, canvasPtr->yOrigin);
  1316.     }
  1317.     } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
  1318.     int count, type;
  1319.     int newY = 0;        /* Initialization needed only to prevent
  1320.                  * gcc warnings. */
  1321.     double fraction;
  1322.  
  1323.     if (argc == 2) {
  1324.         PrintScrollFractions(canvasPtr->yOrigin + canvasPtr->inset,
  1325.             canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin)
  1326.             - canvasPtr->inset, canvasPtr->scrollY1,
  1327.             canvasPtr->scrollY2, interp->result);
  1328.     } else {
  1329.         type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
  1330.         switch (type) {
  1331.         case TK_SCROLL_ERROR:
  1332.             goto error;
  1333.         case TK_SCROLL_MOVETO:
  1334.             newY = canvasPtr->scrollY1 - canvasPtr->inset
  1335.                 + (int) (fraction*(canvasPtr->scrollY2
  1336.                 - canvasPtr->scrollY1) + 0.5);
  1337.             break;
  1338.         case TK_SCROLL_PAGES:
  1339.             newY = canvasPtr->yOrigin + count * .9
  1340.                 * (Tk_Height(canvasPtr->tkwin)
  1341.                 - 2*canvasPtr->inset);
  1342.             break;
  1343.         case TK_SCROLL_UNITS:
  1344.             if (canvasPtr->yScrollIncrement > 0) {
  1345.             newY = canvasPtr->yOrigin
  1346.                 + count*canvasPtr->yScrollIncrement;
  1347.             } else {
  1348.             newY = canvasPtr->yOrigin + count * .1
  1349.                 * (Tk_Height(canvasPtr->tkwin)
  1350.                 - 2*canvasPtr->inset);
  1351.             }
  1352.             break;
  1353.         }
  1354.         CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, newY);
  1355.     }
  1356.     } else {
  1357.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  1358.         "\": must be addtag, bbox, bind, ",
  1359.         "canvasx, canvasy, cget, configure, coords, create, ",
  1360.         "dchars, delete, dtag, find, focus, ",
  1361.         "gettags, icursor, index, insert, itemcget, itemconfigure, ",
  1362.         "lower, move, postscript, raise, scale, scan, ",
  1363.         "select, type, xview, or yview",
  1364.         (char *) NULL);  
  1365.     goto error;
  1366.     }
  1367.     done:
  1368.     Tcl_Release((ClientData) canvasPtr);
  1369.     return result;
  1370.  
  1371.     error:
  1372.     Tcl_Release((ClientData) canvasPtr);
  1373.     return TCL_ERROR;
  1374. }
  1375.  
  1376. /*
  1377.  *----------------------------------------------------------------------
  1378.  *
  1379.  * DestroyCanvas --
  1380.  *
  1381.  *    This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
  1382.  *    to clean up the internal structure of a canvas at a safe time
  1383.  *    (when no-one is using it anymore).
  1384.  *
  1385.  * Results:
  1386.  *    None.
  1387.  *
  1388.  * Side effects:
  1389.  *    Everything associated with the canvas is freed up.
  1390.  *
  1391.  *----------------------------------------------------------------------
  1392.  */
  1393.  
  1394. static void
  1395. DestroyCanvas(memPtr)
  1396.     char *memPtr;        /* Info about canvas widget. */
  1397. {
  1398.     TkCanvas *canvasPtr = (TkCanvas *) memPtr;
  1399.     Tk_Item *itemPtr;
  1400.  
  1401.     /*
  1402.      * Free up all of the items in the canvas.
  1403.      */
  1404.  
  1405.     for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  1406.         itemPtr = canvasPtr->firstItemPtr) {
  1407.     canvasPtr->firstItemPtr = itemPtr->nextPtr;
  1408.     (*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr,
  1409.         canvasPtr->display);
  1410.     if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
  1411.         ckfree((char *) itemPtr->tagPtr);
  1412.     }
  1413.     ckfree((char *) itemPtr);
  1414.     }
  1415.  
  1416.     /*
  1417.      * Free up all the stuff that requires special handling,
  1418.      * then let Tk_FreeOptions handle all the standard option-related
  1419.      * stuff.
  1420.      */
  1421.  
  1422.     if (canvasPtr->pixmapGC != None) {
  1423.     Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC);
  1424.     }
  1425.     Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler);
  1426.     if (canvasPtr->bindingTable != NULL) {
  1427.     Tk_DeleteBindingTable(canvasPtr->bindingTable);
  1428.     }
  1429.     Tk_FreeOptions(configSpecs, (char *) canvasPtr, canvasPtr->display, 0);
  1430.     ckfree((char *) canvasPtr);
  1431. }
  1432.  
  1433. /*
  1434.  *----------------------------------------------------------------------
  1435.  *
  1436.  * ConfigureCanvas --
  1437.  *
  1438.  *    This procedure is called to process an argv/argc list, plus
  1439.  *    the Tk option database, in order to configure (or
  1440.  *    reconfigure) a canvas widget.
  1441.  *
  1442.  * Results:
  1443.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  1444.  *    returned, then interp->result contains an error message.
  1445.  *
  1446.  * Side effects:
  1447.  *    Configuration information, such as colors, border width,
  1448.  *    etc. get set for canvasPtr;  old resources get freed,
  1449.  *    if there were any.
  1450.  *
  1451.  *----------------------------------------------------------------------
  1452.  */
  1453.  
  1454. static int
  1455. ConfigureCanvas(interp, canvasPtr, argc, argv, flags)
  1456.     Tcl_Interp *interp;        /* Used for error reporting. */
  1457.     TkCanvas *canvasPtr;    /* Information about widget;  may or may
  1458.                  * not already have values for some fields. */
  1459.     int argc;            /* Number of valid entries in argv. */
  1460.     char **argv;        /* Arguments. */
  1461.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  1462. {
  1463.     XGCValues gcValues;
  1464.     GC new;
  1465.  
  1466.     if (Tk_ConfigureWidget(interp, canvasPtr->tkwin, configSpecs,
  1467.         argc, argv, (char *) canvasPtr, flags) != TCL_OK) {
  1468.     return TCL_ERROR;
  1469.     }
  1470.  
  1471.     /*
  1472.      * A few options need special processing, such as setting the
  1473.      * background from a 3-D border and creating a GC for copying
  1474.      * bits to the screen.
  1475.      */
  1476.  
  1477.     Tk_SetBackgroundFromBorder(canvasPtr->tkwin, canvasPtr->bgBorder);
  1478.  
  1479.     if (canvasPtr->highlightWidth < 0) {
  1480.     canvasPtr->highlightWidth = 0;
  1481.     }
  1482.     canvasPtr->inset = canvasPtr->borderWidth + canvasPtr->highlightWidth;
  1483.  
  1484.     gcValues.function = GXcopy;
  1485.     gcValues.foreground = Tk_3DBorderColor(canvasPtr->bgBorder)->pixel;
  1486.     gcValues.graphics_exposures = False;
  1487.     new = Tk_GetGC(canvasPtr->tkwin,
  1488.         GCFunction|GCForeground|GCGraphicsExposures, &gcValues);
  1489.     if (canvasPtr->pixmapGC != None) {
  1490.     Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC);
  1491.     }
  1492.     canvasPtr->pixmapGC = new;
  1493.  
  1494.     /*
  1495.      * Reset the desired dimensions for the window.
  1496.      */
  1497.  
  1498.     Tk_GeometryRequest(canvasPtr->tkwin, canvasPtr->width + 2*canvasPtr->inset,
  1499.         canvasPtr->height + 2*canvasPtr->inset);
  1500.  
  1501.     /*
  1502.      * Restart the cursor timing sequence in case the on-time or off-time
  1503.      * just changed.
  1504.      */
  1505.  
  1506.     if (canvasPtr->textInfo.gotFocus) {
  1507.     CanvasFocusProc(canvasPtr, 1);
  1508.     }
  1509.  
  1510.     /*
  1511.      * Recompute the scroll region.
  1512.      */
  1513.  
  1514.     canvasPtr->scrollX1 = 0;
  1515.     canvasPtr->scrollY1 = 0;
  1516.     canvasPtr->scrollX2 = 0;
  1517.     canvasPtr->scrollY2 = 0;
  1518.     if (canvasPtr->regionString != NULL) {
  1519.     int argc2;
  1520.     char **argv2;
  1521.  
  1522.     if (Tcl_SplitList(canvasPtr->interp, canvasPtr->regionString,
  1523.         &argc2, &argv2) != TCL_OK) {
  1524.         return TCL_ERROR;
  1525.     }
  1526.     if (argc2 != 4) {
  1527.         Tcl_AppendResult(interp, "bad scrollRegion \"",
  1528.             canvasPtr->regionString, "\"", (char *) NULL);
  1529.         badRegion:
  1530.         ckfree(canvasPtr->regionString);
  1531.         ckfree((char *) argv2);
  1532.         canvasPtr->regionString = NULL;
  1533.         return TCL_ERROR;
  1534.     }
  1535.     if ((Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
  1536.             argv2[0], &canvasPtr->scrollX1) != TCL_OK)
  1537.         || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
  1538.             argv2[1], &canvasPtr->scrollY1) != TCL_OK)
  1539.         || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
  1540.             argv2[2], &canvasPtr->scrollX2) != TCL_OK)
  1541.         || (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
  1542.             argv2[3], &canvasPtr->scrollY2) != TCL_OK)) {
  1543.         goto badRegion;
  1544.     }
  1545.     ckfree((char *) argv2);
  1546.     }
  1547.  
  1548.     /*
  1549.      * Reset the canvas's origin (this is a no-op unless confine
  1550.      * mode has just been turned on or the scroll region has changed).
  1551.      */
  1552.  
  1553.     CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin);
  1554.     canvasPtr->flags |= UPDATE_SCROLLBARS|REDRAW_BORDERS;
  1555.     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  1556.         canvasPtr->xOrigin, canvasPtr->yOrigin,
  1557.         canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
  1558.         canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
  1559.     return TCL_OK;
  1560. }
  1561.  
  1562. /*
  1563.  *--------------------------------------------------------------
  1564.  *
  1565.  * DisplayCanvas --
  1566.  *
  1567.  *    This procedure redraws the contents of a canvas window.
  1568.  *    It is invoked as a do-when-idle handler, so it only runs
  1569.  *    when there's nothing else for the application to do.
  1570.  *
  1571.  * Results:
  1572.  *    None.
  1573.  *
  1574.  * Side effects:
  1575.  *    Information appears on the screen.
  1576.  *
  1577.  *--------------------------------------------------------------
  1578.  */
  1579.  
  1580. static void
  1581. DisplayCanvas(clientData)
  1582.     ClientData clientData;    /* Information about widget. */
  1583. {
  1584.     TkCanvas *canvasPtr = (TkCanvas *) clientData;
  1585.     Tk_Window tkwin = canvasPtr->tkwin;
  1586.     Tk_Item *itemPtr;
  1587.     Pixmap pixmap;
  1588.     int screenX1, screenX2, screenY1, screenY2, width, height;
  1589.  
  1590.     if (canvasPtr->tkwin == NULL) {
  1591.     return;
  1592.     }
  1593.     if (!Tk_IsMapped(tkwin)) {
  1594.     goto done;
  1595.     }
  1596.  
  1597.     /*
  1598.      * Choose a new current item if that is needed (this could cause
  1599.      * event handlers to be invoked).
  1600.      */
  1601.  
  1602.     while (canvasPtr->flags & REPICK_NEEDED) {
  1603.     Tcl_Preserve((ClientData) canvasPtr);
  1604.     canvasPtr->flags &= ~REPICK_NEEDED;
  1605.     PickCurrentItem(canvasPtr, &canvasPtr->pickEvent);
  1606.     tkwin = canvasPtr->tkwin;
  1607.     Tcl_Release((ClientData) canvasPtr);
  1608.     if (tkwin == NULL) {
  1609.         return;
  1610.     }
  1611.     }
  1612.  
  1613.     /*
  1614.      * Compute the intersection between the area that needs redrawing
  1615.      * and the area that's visible on the screen.
  1616.      */
  1617.  
  1618.     if ((canvasPtr->redrawX1 < canvasPtr->redrawX2)
  1619.         && (canvasPtr->redrawY1 < canvasPtr->redrawY2)) {
  1620.     screenX1 = canvasPtr->xOrigin + canvasPtr->inset;
  1621.     screenY1 = canvasPtr->yOrigin + canvasPtr->inset;
  1622.     screenX2 = canvasPtr->xOrigin + Tk_Width(tkwin) - canvasPtr->inset;
  1623.     screenY2 = canvasPtr->yOrigin + Tk_Height(tkwin) - canvasPtr->inset;
  1624.     if (canvasPtr->redrawX1 > screenX1) {
  1625.         screenX1 = canvasPtr->redrawX1;
  1626.     }
  1627.     if (canvasPtr->redrawY1 > screenY1) {
  1628.         screenY1 = canvasPtr->redrawY1;
  1629.     }
  1630.     if (canvasPtr->redrawX2 < screenX2) {
  1631.         screenX2 = canvasPtr->redrawX2;
  1632.     }
  1633.     if (canvasPtr->redrawY2 < screenY2) {
  1634.         screenY2 = canvasPtr->redrawY2;
  1635.     }
  1636.     if ((screenX1 >= screenX2) || (screenY1 >= screenY2)) {
  1637.         goto borders;
  1638.     }
  1639.     
  1640.     /*
  1641.      * Redrawing is done in a temporary pixmap that is allocated
  1642.      * here and freed at the end of the procedure.  All drawing
  1643.      * is done to the pixmap, and the pixmap is copied to the
  1644.      * screen at the end of the procedure. The temporary pixmap
  1645.      * serves two purposes:
  1646.      *
  1647.      * 1. It provides a smoother visual effect (no clearing and
  1648.      *    gradual redraw will be visible to users).
  1649.      * 2. It allows us to redraw only the objects that overlap
  1650.      *    the redraw area.  Otherwise incorrect results could
  1651.      *      occur from redrawing things that stick outside of
  1652.      *      the redraw area (we'd have to redraw everything in
  1653.      *    order to make the overlaps look right).
  1654.      *
  1655.      * Some tricky points about the pixmap:
  1656.      *
  1657.      * 1. We only allocate a large enough pixmap to hold the
  1658.      *    area that has to be redisplayed.  This saves time in
  1659.      *    in the X server for large objects that cover much
  1660.      *    more than the area being redisplayed:  only the area
  1661.      *    of the pixmap will actually have to be redrawn.
  1662.      * 2. Some X servers (e.g. the one for DECstations) have troubles
  1663.      *    with characters that overlap an edge of the pixmap (on the
  1664.      *    DEC servers, as of 8/18/92, such characters are drawn one
  1665.      *    pixel too far to the right).  To handle this problem,
  1666.      *    make the pixmap a bit larger than is absolutely needed
  1667.      *    so that for normal-sized fonts the characters that overlap
  1668.      *    the edge of the pixmap will be outside the area we care
  1669.      *    about.
  1670.      */
  1671.     
  1672.     canvasPtr->drawableXOrigin = screenX1 - 30;
  1673.     canvasPtr->drawableYOrigin = screenY1 - 30;
  1674.     pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
  1675.         (screenX2 + 30 - canvasPtr->drawableXOrigin),
  1676.         (screenY2 + 30 - canvasPtr->drawableYOrigin),
  1677.         Tk_Depth(tkwin));
  1678.     
  1679.     /*
  1680.      * Clear the area to be redrawn.
  1681.      */
  1682.     
  1683.     width = screenX2 - screenX1;
  1684.     height = screenY2 - screenY1;
  1685.     
  1686.     XFillRectangle(Tk_Display(tkwin), pixmap, canvasPtr->pixmapGC,
  1687.         screenX1 - canvasPtr->drawableXOrigin,
  1688.         screenY1 - canvasPtr->drawableYOrigin, (unsigned int) width,
  1689.         (unsigned int) height);
  1690.     
  1691.     /*
  1692.      * Scan through the item list, redrawing those items that need it.
  1693.      * An item must be redraw if either (a) it intersects the smaller
  1694.      * on-screen area or (b) it intersects the full canvas area and its
  1695.      * type requests that it be redrawn always (e.g. so subwindows can
  1696.      * be unmapped when they move off-screen).
  1697.      */
  1698.     
  1699.     for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  1700.         itemPtr = itemPtr->nextPtr) {
  1701.         if ((itemPtr->x1 >= screenX2)
  1702.             || (itemPtr->y1 >= screenY2)
  1703.             || (itemPtr->x2 < screenX1)
  1704.             || (itemPtr->y2 < screenY1)) {
  1705.         if (!itemPtr->typePtr->alwaysRedraw
  1706.             || (itemPtr->x1 >= canvasPtr->redrawX2)
  1707.             || (itemPtr->y1 >= canvasPtr->redrawY2)
  1708.             || (itemPtr->x2 < canvasPtr->redrawX1)
  1709.             || (itemPtr->y2 < canvasPtr->redrawY1)) {
  1710.             continue;
  1711.         }
  1712.         }
  1713.         (*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr, itemPtr,
  1714.             canvasPtr->display, pixmap, screenX1, screenY1, width,
  1715.             height);
  1716.     }
  1717.     
  1718.     /*
  1719.      * Copy from the temporary pixmap to the screen, then free up
  1720.      * the temporary pixmap.
  1721.      */
  1722.     
  1723.     XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
  1724.         canvasPtr->pixmapGC,
  1725.         screenX1 - canvasPtr->drawableXOrigin,
  1726.         screenY1 - canvasPtr->drawableYOrigin,
  1727.         (unsigned) (screenX2 - screenX1),
  1728.         (unsigned) (screenY2 - screenY1),
  1729.         screenX1 - canvasPtr->xOrigin, screenY1 - canvasPtr->yOrigin);
  1730.     Tk_FreePixmap(Tk_Display(tkwin), pixmap);
  1731.     }
  1732.  
  1733.     /*
  1734.      * Draw the window borders, if needed.
  1735.      */
  1736.  
  1737.     borders:
  1738.     if (canvasPtr->flags & REDRAW_BORDERS) {
  1739.     canvasPtr->flags &= ~REDRAW_BORDERS;
  1740.     if (canvasPtr->borderWidth > 0) {
  1741.         Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin),
  1742.             canvasPtr->bgBorder, canvasPtr->highlightWidth,
  1743.             canvasPtr->highlightWidth,
  1744.             Tk_Width(tkwin) - 2*canvasPtr->highlightWidth,
  1745.             Tk_Height(tkwin) - 2*canvasPtr->highlightWidth,
  1746.             canvasPtr->borderWidth, canvasPtr->relief);
  1747.     }
  1748.     if (canvasPtr->highlightWidth != 0) {
  1749.         GC gc;
  1750.     
  1751.         if (canvasPtr->textInfo.gotFocus) {
  1752.         gc = Tk_GCForColor(canvasPtr->highlightColorPtr,
  1753.             Tk_WindowId(tkwin));
  1754.         } else {
  1755.         gc = Tk_GCForColor(canvasPtr->highlightBgColorPtr,
  1756.             Tk_WindowId(tkwin));
  1757.         }
  1758.         Tk_DrawFocusHighlight(tkwin, gc, canvasPtr->highlightWidth,
  1759.             Tk_WindowId(tkwin));
  1760.     }
  1761.     }
  1762.  
  1763.     done:
  1764.     canvasPtr->flags &= ~REDRAW_PENDING;
  1765.     canvasPtr->redrawX1 = canvasPtr->redrawX2 = 0;
  1766.     canvasPtr->redrawY1 = canvasPtr->redrawY2 = 0;
  1767.     if (canvasPtr->flags & UPDATE_SCROLLBARS) {
  1768.     CanvasUpdateScrollbars(canvasPtr);
  1769.     }
  1770. }
  1771.  
  1772. /*
  1773.  *--------------------------------------------------------------
  1774.  *
  1775.  * CanvasEventProc --
  1776.  *
  1777.  *    This procedure is invoked by the Tk dispatcher for various
  1778.  *    events on canvases.
  1779.  *
  1780.  * Results:
  1781.  *    None.
  1782.  *
  1783.  * Side effects:
  1784.  *    When the window gets deleted, internal structures get
  1785.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1786.  *
  1787.  *--------------------------------------------------------------
  1788.  */
  1789.  
  1790. static void
  1791. CanvasEventProc(clientData, eventPtr)
  1792.     ClientData clientData;    /* Information about window. */
  1793.     XEvent *eventPtr;        /* Information about event. */
  1794. {
  1795.     TkCanvas *canvasPtr = (TkCanvas *) clientData;
  1796.  
  1797.     if (eventPtr->type == Expose) {
  1798.     int x, y;
  1799.  
  1800.     x = eventPtr->xexpose.x + canvasPtr->xOrigin;
  1801.     y = eventPtr->xexpose.y + canvasPtr->yOrigin;
  1802.     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, x, y,
  1803.         x + eventPtr->xexpose.width,
  1804.         y + eventPtr->xexpose.height);
  1805.     if ((eventPtr->xexpose.x < canvasPtr->inset)
  1806.         || (eventPtr->xexpose.y < canvasPtr->inset)
  1807.         || ((eventPtr->xexpose.x + eventPtr->xexpose.width)
  1808.             > (Tk_Width(canvasPtr->tkwin) - canvasPtr->inset))
  1809.         || ((eventPtr->xexpose.y + eventPtr->xexpose.height)
  1810.             > (Tk_Height(canvasPtr->tkwin) - canvasPtr->inset))) {
  1811.         canvasPtr->flags |= REDRAW_BORDERS;
  1812.     }
  1813.     } else if (eventPtr->type == DestroyNotify) {
  1814.     if (canvasPtr->tkwin != NULL) {
  1815.         canvasPtr->tkwin = NULL;
  1816.         Tcl_DeleteCommand(canvasPtr->interp,
  1817.             Tcl_GetCommandName(canvasPtr->interp,
  1818.             canvasPtr->widgetCmd));
  1819.     }
  1820.     if (canvasPtr->flags & REDRAW_PENDING) {
  1821.         Tcl_CancelIdleCall(DisplayCanvas, (ClientData) canvasPtr);
  1822.     }
  1823.     Tcl_EventuallyFree((ClientData) canvasPtr, DestroyCanvas);
  1824.     } else if (eventPtr->type == ConfigureNotify) {
  1825.     canvasPtr->flags |= UPDATE_SCROLLBARS;
  1826.  
  1827.     /*
  1828.      * The call below is needed in order to recenter the canvas if
  1829.      * it's confined and its scroll region is smaller than the window.
  1830.      */
  1831.  
  1832.     CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin);
  1833.     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, canvasPtr->xOrigin,
  1834.         canvasPtr->yOrigin,
  1835.         canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
  1836.         canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
  1837.     canvasPtr->flags |= REDRAW_BORDERS;
  1838.     } else if (eventPtr->type == FocusIn) {
  1839.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1840.         CanvasFocusProc(canvasPtr, 1);
  1841.     }
  1842.     } else if (eventPtr->type == FocusOut) {
  1843.     if (eventPtr->xfocus.detail != NotifyInferior) {
  1844.         CanvasFocusProc(canvasPtr, 0);
  1845.     }
  1846.     } else if (eventPtr->type == UnmapNotify) {
  1847.     Tk_Item *itemPtr;
  1848.  
  1849.     /*
  1850.      * Special hack:  if the canvas is unmapped, then must notify
  1851.      * all items with "alwaysRedraw" set, so that they know that
  1852.      * they are no longer displayed.
  1853.      */
  1854.  
  1855.     for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  1856.         itemPtr = itemPtr->nextPtr) {
  1857.         if (itemPtr->typePtr->alwaysRedraw) {
  1858.         (*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr,
  1859.             itemPtr, canvasPtr->display, None, 0, 0, 0, 0);
  1860.         }
  1861.     }
  1862.     }
  1863. }
  1864.  
  1865. /*
  1866.  *----------------------------------------------------------------------
  1867.  *
  1868.  * CanvasCmdDeletedProc --
  1869.  *
  1870.  *    This procedure is invoked when a widget command is deleted.  If
  1871.  *    the widget isn't already in the process of being destroyed,
  1872.  *    this command destroys it.
  1873.  *
  1874.  * Results:
  1875.  *    None.
  1876.  *
  1877.  * Side effects:
  1878.  *    The widget is destroyed.
  1879.  *
  1880.  *----------------------------------------------------------------------
  1881.  */
  1882.  
  1883. static void
  1884. CanvasCmdDeletedProc(clientData)
  1885.     ClientData clientData;    /* Pointer to widget record for widget. */
  1886. {
  1887.     TkCanvas *canvasPtr = (TkCanvas *) clientData;
  1888.     Tk_Window tkwin = canvasPtr->tkwin;
  1889.  
  1890.     /*
  1891.      * This procedure could be invoked either because the window was
  1892.      * destroyed and the command was then deleted (in which case tkwin
  1893.      * is NULL) or because the command was deleted, and then this procedure
  1894.      * destroys the widget.
  1895.      */
  1896.  
  1897.     if (tkwin != NULL) {
  1898.     canvasPtr->tkwin = NULL;
  1899.     Tk_DestroyWindow(tkwin);
  1900.     }
  1901. }
  1902.  
  1903. /*
  1904.  *--------------------------------------------------------------
  1905.  *
  1906.  * Tk_CanvasEventuallyRedraw --
  1907.  *
  1908.  *    Arrange for part or all of a canvas widget to redrawn at
  1909.  *    some convenient time in the future.
  1910.  *
  1911.  * Results:
  1912.  *    None.
  1913.  *
  1914.  * Side effects:
  1915.  *    The screen will eventually be refreshed.
  1916.  *
  1917.  *--------------------------------------------------------------
  1918.  */
  1919.  
  1920. void
  1921. Tk_CanvasEventuallyRedraw(canvas, x1, y1, x2, y2)
  1922.     Tk_Canvas canvas;        /* Information about widget. */
  1923.     int x1, y1;            /* Upper left corner of area to redraw.
  1924.                  * Pixels on edge are redrawn. */
  1925.     int x2, y2;            /* Lower right corner of area to redraw.
  1926.                  * Pixels on edge are not redrawn. */
  1927. {
  1928.     TkCanvas *canvasPtr = (TkCanvas *) canvas;
  1929.     if ((x1 == x2) || (y1 == y2)) {
  1930.     return;
  1931.     }
  1932.     if (canvasPtr->flags & REDRAW_PENDING) {
  1933.     if (x1 <= canvasPtr->redrawX1) {
  1934.         canvasPtr->redrawX1 = x1;
  1935.     }
  1936.     if (y1 <= canvasPtr->redrawY1) {
  1937.         canvasPtr->redrawY1 = y1;
  1938.     }
  1939.     if (x2 >= canvasPtr->redrawX2) {
  1940.         canvasPtr->redrawX2 = x2;
  1941.     }
  1942.     if (y2 >= canvasPtr->redrawY2) {
  1943.         canvasPtr->redrawY2 = y2;
  1944.     }
  1945.     } else {
  1946.     canvasPtr->redrawX1 = x1;
  1947.     canvasPtr->redrawY1 = y1;
  1948.     canvasPtr->redrawX2 = x2;
  1949.     canvasPtr->redrawY2 = y2;
  1950.     Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
  1951.     canvasPtr->flags |= REDRAW_PENDING;
  1952.     }
  1953. }
  1954.  
  1955. /*
  1956.  *--------------------------------------------------------------
  1957.  *
  1958.  * Tk_CreateItemType --
  1959.  *
  1960.  *    This procedure may be invoked to add a new kind of canvas
  1961.  *    element to the core item types supported by Tk.
  1962.  *
  1963.  * Results:
  1964.  *    None.
  1965.  *
  1966.  * Side effects:
  1967.  *    From now on, the new item type will be useable in canvas
  1968.  *    widgets (e.g. typePtr->name can be used as the item type
  1969.  *    in "create" widget commands).  If there was already a
  1970.  *    type with the same name as in typePtr, it is replaced with
  1971.  *    the new type.
  1972.  *
  1973.  *--------------------------------------------------------------
  1974.  */
  1975.  
  1976. void
  1977. Tk_CreateItemType(typePtr)
  1978.     Tk_ItemType *typePtr;        /* Information about item type;
  1979.                      * storage must be statically
  1980.                      * allocated (must live forever). */
  1981. {
  1982.     Tk_ItemType *typePtr2, *prevPtr;
  1983.  
  1984.     if (typeList == NULL) {
  1985.     InitCanvas();
  1986.     }
  1987.  
  1988.     /*
  1989.      * If there's already an item type with the given name, remove it.
  1990.      */
  1991.  
  1992.     for (typePtr2 = typeList, prevPtr = NULL; typePtr2 != NULL;
  1993.         prevPtr = typePtr2, typePtr2 = typePtr2->nextPtr) {
  1994.     if (strcmp(typePtr2->name, typePtr->name) == 0) {
  1995.         if (prevPtr == NULL) {
  1996.         typeList = typePtr2->nextPtr;
  1997.         } else {
  1998.         prevPtr->nextPtr = typePtr2->nextPtr;
  1999.         }
  2000.         break;
  2001.     }
  2002.     }
  2003.     typePtr->nextPtr = typeList;
  2004.     typeList = typePtr;
  2005. }
  2006.  
  2007. /*
  2008.  *----------------------------------------------------------------------
  2009.  *
  2010.  * Tk_GetItemTypes --
  2011.  *
  2012.  *    This procedure returns a pointer to the list of all item
  2013.  *    types.
  2014.  *
  2015.  * Results:
  2016.  *    The return value is a pointer to the first in the list
  2017.  *    of item types currently supported by canvases.
  2018.  *
  2019.  * Side effects:
  2020.  *    None.
  2021.  *
  2022.  *----------------------------------------------------------------------
  2023.  */
  2024.  
  2025. Tk_ItemType *
  2026. Tk_GetItemTypes()
  2027. {
  2028.     if (typeList == NULL) {
  2029.     InitCanvas();
  2030.     }
  2031.     return typeList;
  2032. }
  2033.  
  2034. /*
  2035.  *--------------------------------------------------------------
  2036.  *
  2037.  * InitCanvas --
  2038.  *
  2039.  *    This procedure is invoked to perform once-only-ever
  2040.  *    initialization for the module, such as setting up
  2041.  *    the type table.
  2042.  *
  2043.  * Results:
  2044.  *    None.
  2045.  *
  2046.  * Side effects:
  2047.  *    None.
  2048.  *
  2049.  *--------------------------------------------------------------
  2050.  */
  2051.  
  2052. static void
  2053. InitCanvas()
  2054. {
  2055.     if (typeList != NULL) {
  2056.     return;
  2057.     }
  2058.     typeList = &tkRectangleType;
  2059.     tkRectangleType.nextPtr = &tkTextType;
  2060.     tkTextType.nextPtr = &tkLineType;
  2061.     tkLineType.nextPtr = &tkPolygonType;
  2062.     tkPolygonType.nextPtr = &tkImageType;
  2063.     tkImageType.nextPtr = &tkOvalType;
  2064.     tkOvalType.nextPtr = &tkBitmapType;
  2065.     tkBitmapType.nextPtr = &tkArcType;
  2066.     tkArcType.nextPtr = &tkWindowType;
  2067.     tkWindowType.nextPtr = NULL;
  2068.     allUid = Tk_GetUid("all");
  2069.     currentUid = Tk_GetUid("current");
  2070. }
  2071.  
  2072. /*
  2073.  *--------------------------------------------------------------
  2074.  *
  2075.  * StartTagSearch --
  2076.  *
  2077.  *    This procedure is called to initiate an enumeration of
  2078.  *    all items in a given canvas that contain a given tag.
  2079.  *
  2080.  * Results:
  2081.  *    The return value is a pointer to the first item in
  2082.  *    canvasPtr that matches tag, or NULL if there is no
  2083.  *    such item.  The information at *searchPtr is initialized
  2084.  *    such that successive calls to NextItem will return
  2085.  *    successive items that match tag.
  2086.  *
  2087.  * Side effects:
  2088.  *    SearchPtr is linked into a list of searches in progress
  2089.  *    on canvasPtr, so that elements can safely be deleted
  2090.  *    while the search is in progress.  EndTagSearch must be
  2091.  *    called at the end of the search to unlink searchPtr from
  2092.  *    this list.
  2093.  *
  2094.  *--------------------------------------------------------------
  2095.  */
  2096.  
  2097. static Tk_Item *
  2098. StartTagSearch(canvasPtr, tag, searchPtr)
  2099.     TkCanvas *canvasPtr;        /* Canvas whose items are to be
  2100.                      * searched. */
  2101.     char *tag;                /* String giving tag value. */
  2102.     TagSearch *searchPtr;        /* Record describing tag search;
  2103.                      * will be initialized here. */
  2104. {
  2105.     int id;
  2106.     Tk_Item *itemPtr, *prevPtr;
  2107.     Tk_Uid *tagPtr;
  2108.     Tk_Uid uid;
  2109.     int count;
  2110.  
  2111.     /*
  2112.      * Initialize the search.
  2113.      */
  2114.  
  2115.     searchPtr->canvasPtr = canvasPtr;
  2116.     searchPtr->searchOver = 0;
  2117.  
  2118.     /*
  2119.      * Find the first matching item in one of several ways. If the tag
  2120.      * is a number then it selects the single item with the matching
  2121.      * identifier.  In this case see if the item being requested is the
  2122.      * hot item, in which case the search can be skipped.
  2123.      */
  2124.  
  2125.     if (isdigit(UCHAR(*tag))) {
  2126.     char *end;
  2127.  
  2128.     numIdSearches++;
  2129.     id = strtoul(tag, &end, 0);
  2130.     if (*end == 0) {
  2131.         itemPtr = canvasPtr->hotPtr;
  2132.         prevPtr = canvasPtr->hotPrevPtr;
  2133.         if ((itemPtr == NULL) || (itemPtr->id != id) || (prevPtr == NULL)
  2134.             || (prevPtr->nextPtr != itemPtr)) {
  2135.         numSlowSearches++;
  2136.         for (prevPtr = NULL, itemPtr = canvasPtr->firstItemPtr;
  2137.             itemPtr != NULL;
  2138.             prevPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
  2139.             if (itemPtr->id == id) {
  2140.             break;
  2141.             }
  2142.         }
  2143.         }
  2144.         searchPtr->prevPtr = prevPtr;
  2145.         searchPtr->searchOver = 1;
  2146.         canvasPtr->hotPtr = itemPtr;
  2147.         canvasPtr->hotPrevPtr = prevPtr;
  2148.         return itemPtr;
  2149.     }
  2150.     }
  2151.  
  2152.     searchPtr->tag = uid = Tk_GetUid(tag);
  2153.     if (uid == allUid) {
  2154.  
  2155.     /*
  2156.      * All items match.
  2157.      */
  2158.  
  2159.     searchPtr->tag = NULL;
  2160.     searchPtr->prevPtr = NULL;
  2161.     searchPtr->currentPtr = canvasPtr->firstItemPtr;
  2162.     return canvasPtr->firstItemPtr;
  2163.     }
  2164.  
  2165.     /*
  2166.      * None of the above.  Search for an item with a matching tag.
  2167.      */
  2168.  
  2169.     for (prevPtr = NULL, itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  2170.         prevPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
  2171.     for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
  2172.         count > 0; tagPtr++, count--) {
  2173.         if (*tagPtr == uid) {
  2174.         searchPtr->prevPtr = prevPtr;
  2175.         searchPtr->currentPtr = itemPtr;
  2176.         return itemPtr;
  2177.         }
  2178.     }
  2179.     }
  2180.     searchPtr->prevPtr = prevPtr;
  2181.     searchPtr->searchOver = 1;
  2182.     return NULL;
  2183. }
  2184.  
  2185. /*
  2186.  *--------------------------------------------------------------
  2187.  *
  2188.  * NextItem --
  2189.  *
  2190.  *    This procedure returns successive items that match a given
  2191.  *    tag;  it should be called only after StartTagSearch has been
  2192.  *    used to begin a search.
  2193.  *
  2194.  * Results:
  2195.  *    The return value is a pointer to the next item that matches
  2196.  *    the tag specified to StartTagSearch, or NULL if no such
  2197.  *    item exists.  *SearchPtr is updated so that the next call
  2198.  *    to this procedure will return the next item.
  2199.  *
  2200.  * Side effects:
  2201.  *    None.
  2202.  *
  2203.  *--------------------------------------------------------------
  2204.  */
  2205.  
  2206. static Tk_Item *
  2207. NextItem(searchPtr)
  2208.     TagSearch *searchPtr;        /* Record describing search in
  2209.                      * progress. */
  2210. {
  2211.     Tk_Item *itemPtr, *prevPtr;
  2212.     int count;
  2213.     Tk_Uid uid;
  2214.     Tk_Uid *tagPtr;
  2215.  
  2216.     /*
  2217.      * Find next item in list (this may not actually be a suitable
  2218.      * one to return), and return if there are no items left.
  2219.      */
  2220.  
  2221.     prevPtr = searchPtr->prevPtr;
  2222.     if (prevPtr == NULL) {
  2223.     itemPtr = searchPtr->canvasPtr->firstItemPtr;
  2224.     } else {
  2225.     itemPtr = prevPtr->nextPtr;
  2226.     }
  2227.     if ((itemPtr == NULL) || (searchPtr->searchOver)) {
  2228.     searchPtr->searchOver = 1;
  2229.     return NULL;
  2230.     }
  2231.     if (itemPtr != searchPtr->currentPtr) {
  2232.     /*
  2233.      * The structure of the list has changed.  Probably the
  2234.      * previously-returned item was removed from the list.
  2235.      * In this case, don't advance prevPtr;  just return
  2236.      * its new successor (i.e. do nothing here).
  2237.      */
  2238.     } else {
  2239.     prevPtr = itemPtr;
  2240.     itemPtr = prevPtr->nextPtr;
  2241.     }
  2242.  
  2243.     /*
  2244.      * Handle special case of "all" search by returning next item.
  2245.      */
  2246.  
  2247.     uid = searchPtr->tag;
  2248.     if (uid == NULL) {
  2249.     searchPtr->prevPtr = prevPtr;
  2250.     searchPtr->currentPtr = itemPtr;
  2251.     return itemPtr;
  2252.     }
  2253.  
  2254.     /*
  2255.      * Look for an item with a particular tag.
  2256.      */
  2257.  
  2258.     for ( ; itemPtr != NULL; prevPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
  2259.     for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
  2260.         count > 0; tagPtr++, count--) {
  2261.         if (*tagPtr == uid) {
  2262.         searchPtr->prevPtr = prevPtr;
  2263.         searchPtr->currentPtr = itemPtr;
  2264.         return itemPtr;
  2265.         }
  2266.     }
  2267.     }
  2268.     searchPtr->prevPtr = prevPtr;
  2269.     searchPtr->searchOver = 1;
  2270.     return NULL;
  2271. }
  2272.  
  2273. /*
  2274.  *--------------------------------------------------------------
  2275.  *
  2276.  * DoItem --
  2277.  *
  2278.  *    This is a utility procedure called by FindItems.  It
  2279.  *    either adds itemPtr's id to the result forming in interp,
  2280.  *    or it adds a new tag to itemPtr, depending on the value
  2281.  *    of tag.
  2282.  *
  2283.  * Results:
  2284.  *    None.
  2285.  *
  2286.  * Side effects:
  2287.  *    If tag is NULL then itemPtr's id is added as a list element
  2288.  *    to interp->result;  otherwise tag is added to itemPtr's
  2289.  *    list of tags.
  2290.  *
  2291.  *--------------------------------------------------------------
  2292.  */
  2293.  
  2294. static void
  2295. DoItem(interp, itemPtr, tag)
  2296.     Tcl_Interp *interp;            /* Interpreter in which to (possibly)
  2297.                      * record item id. */
  2298.     Tk_Item *itemPtr;            /* Item to (possibly) modify. */
  2299.     Tk_Uid tag;                /* Tag to add to those already
  2300.                      * present for item, or NULL. */
  2301. {
  2302.     Tk_Uid *tagPtr;
  2303.     int count;
  2304.  
  2305.     /*
  2306.      * Handle the "add-to-result" case and return, if appropriate.
  2307.      */
  2308.  
  2309.     if (tag == NULL) {
  2310.     char msg[30];
  2311.     sprintf(msg, "%d", itemPtr->id);
  2312.     Tcl_AppendElement(interp, msg);
  2313.     return;
  2314.     }
  2315.  
  2316.     for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
  2317.         count > 0; tagPtr++, count--) {
  2318.     if (tag == *tagPtr) {
  2319.         return;
  2320.     }
  2321.     }
  2322.  
  2323.     /*
  2324.      * Grow the tag space if there's no more room left in the current
  2325.      * block.
  2326.      */
  2327.  
  2328.     if (itemPtr->tagSpace == itemPtr->numTags) {
  2329.     Tk_Uid *newTagPtr;
  2330.  
  2331.     itemPtr->tagSpace += 5;
  2332.     newTagPtr = (Tk_Uid *) ckalloc((unsigned)
  2333.         (itemPtr->tagSpace * sizeof(Tk_Uid)));
  2334.     memcpy((VOID *) newTagPtr, (VOID *) itemPtr->tagPtr,
  2335.         (itemPtr->numTags * sizeof(Tk_Uid)));
  2336.     if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
  2337.         ckfree((char *) itemPtr->tagPtr);
  2338.     }
  2339.     itemPtr->tagPtr = newTagPtr;
  2340.     tagPtr = &itemPtr->tagPtr[itemPtr->numTags];
  2341.     }
  2342.  
  2343.     /*
  2344.      * Add in the new tag.
  2345.      */
  2346.  
  2347.     *tagPtr = tag;
  2348.     itemPtr->numTags++;
  2349. }
  2350.  
  2351. /*
  2352.  *--------------------------------------------------------------
  2353.  *
  2354.  * FindItems --
  2355.  *
  2356.  *    This procedure does all the work of implementing the
  2357.  *    "find" and "addtag" options of the canvas widget command,
  2358.  *    which locate items that have certain features (location,
  2359.  *    tags, position in display list, etc.).
  2360.  *
  2361.  * Results:
  2362.  *    A standard Tcl return value.  If newTag is NULL, then a
  2363.  *    list of ids from all the items that match argc/argv is
  2364.  *    returned in interp->result.  If newTag is NULL, then
  2365.  *    the normal interp->result is an empty string.  If an error
  2366.  *    occurs, then interp->result will hold an error message.
  2367.  *
  2368.  * Side effects:
  2369.  *    If newTag is non-NULL, then all the items that match the
  2370.  *    information in argc/argv have that tag added to their
  2371.  *    lists of tags.
  2372.  *
  2373.  *--------------------------------------------------------------
  2374.  */
  2375.  
  2376. static int
  2377. FindItems(interp, canvasPtr, argc, argv, newTag, cmdName, option)
  2378.     Tcl_Interp *interp;            /* Interpreter for error reporting. */
  2379.     TkCanvas *canvasPtr;        /* Canvas whose items are to be
  2380.                      * searched. */
  2381.     int argc;                /* Number of entries in argv.  Must be
  2382.                      * greater than zero. */
  2383.     char **argv;            /* Arguments that describe what items
  2384.                      * to search for (see user doc on
  2385.                      * "find" and "addtag" options). */
  2386.     char *newTag;            /* If non-NULL, gives new tag to set
  2387.                      * on all found items;  if NULL, then
  2388.                      * ids of found items are returned
  2389.                      * in interp->result. */
  2390.     char *cmdName;            /* Name of original Tcl command, for
  2391.                      * use in error messages. */
  2392.     char *option;            /* For error messages:  gives option
  2393.                      * from Tcl command and other stuff
  2394.                      * up to what's in argc/argv. */
  2395. {
  2396.     int c;
  2397.     size_t length;
  2398.     TagSearch search;
  2399.     Tk_Item *itemPtr;
  2400.     Tk_Uid uid;
  2401.  
  2402.     if (newTag != NULL) {
  2403.     uid = Tk_GetUid(newTag);
  2404.     } else {
  2405.     uid = NULL;
  2406.     }
  2407.     c = argv[0][0];
  2408.     length = strlen(argv[0]);
  2409.     if ((c == 'a') && (strncmp(argv[0], "above", length) == 0)
  2410.         && (length >= 2)) {
  2411.     Tk_Item *lastPtr = NULL;
  2412.     if (argc != 2) {
  2413.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  2414.             cmdName, option, " above tagOrId", (char *) NULL);
  2415.         return TCL_ERROR;
  2416.     }
  2417.     for (itemPtr = StartTagSearch(canvasPtr, argv[1], &search);
  2418.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  2419.         lastPtr = itemPtr;
  2420.     }
  2421.     if ((lastPtr != NULL) && (lastPtr->nextPtr != NULL)) {
  2422.         DoItem(interp, lastPtr->nextPtr, uid);
  2423.     }
  2424.     } else if ((c == 'a') && (strncmp(argv[0], "all", length) == 0)
  2425.         && (length >= 2)) {
  2426.     if (argc != 1) {
  2427.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  2428.             cmdName, option, " all", (char *) NULL);
  2429.         return TCL_ERROR;
  2430.     }
  2431.  
  2432.     for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  2433.         itemPtr = itemPtr->nextPtr) {
  2434.         DoItem(interp, itemPtr, uid);
  2435.     }
  2436.     } else if ((c == 'b') && (strncmp(argv[0], "below", length) == 0)) {
  2437.     if (argc != 2) {
  2438.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  2439.             cmdName, option, " below tagOrId", (char *) NULL);
  2440.         return TCL_ERROR;
  2441.     }
  2442.     itemPtr = StartTagSearch(canvasPtr, argv[1], &search);
  2443.     if (search.prevPtr != NULL) {
  2444.         DoItem(interp, search.prevPtr, uid);
  2445.     }
  2446.     } else if ((c == 'c') && (strncmp(argv[0], "closest", length) == 0)) {
  2447.     double closestDist;
  2448.     Tk_Item *startPtr, *closestPtr;
  2449.     double coords[2], halo;
  2450.     int x1, y1, x2, y2;
  2451.  
  2452.     if ((argc < 3) || (argc > 5)) {
  2453.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  2454.             cmdName, option, " closest x y ?halo? ?start?",
  2455.             (char *) NULL);
  2456.         return TCL_ERROR;
  2457.     }
  2458.     if ((Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[1],
  2459.         &coords[0]) != TCL_OK) || (Tk_CanvasGetCoord(interp,
  2460.         (Tk_Canvas) canvasPtr, argv[2], &coords[1]) != TCL_OK)) {
  2461.         return TCL_ERROR;
  2462.     }
  2463.     if (argc > 3) {
  2464.         if (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[3],
  2465.             &halo) != TCL_OK) {
  2466.         return TCL_ERROR;
  2467.         }
  2468.         if (halo < 0.0) {
  2469.         Tcl_AppendResult(interp, "can't have negative halo value \"",
  2470.             argv[3], "\"", (char *) NULL);
  2471.         return TCL_ERROR;
  2472.         }
  2473.     } else {
  2474.         halo = 0.0;
  2475.     }
  2476.  
  2477.     /*
  2478.      * Find the item at which to start the search.
  2479.      */
  2480.  
  2481.     startPtr = canvasPtr->firstItemPtr;
  2482.     if (argc == 5) {
  2483.         itemPtr = StartTagSearch(canvasPtr, argv[4], &search);
  2484.         if (itemPtr != NULL) {
  2485.         startPtr = itemPtr;
  2486.         }
  2487.     }
  2488.  
  2489.     /*
  2490.      * The code below is optimized so that it can eliminate most
  2491.      * items without having to call their item-specific procedures.
  2492.      * This is done by keeping a bounding box (x1, y1, x2, y2) that
  2493.      * an item's bbox must overlap if the item is to have any
  2494.      * chance of being closer than the closest so far.
  2495.      */
  2496.  
  2497.     itemPtr = startPtr;
  2498.     if (itemPtr == NULL) {
  2499.         return TCL_OK;
  2500.     }
  2501.     closestDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
  2502.         itemPtr, coords) - halo;
  2503.     if (closestDist < 0.0) {
  2504.         closestDist = 0.0;
  2505.     }
  2506.     while (1) {
  2507.         double newDist;
  2508.  
  2509.         /*
  2510.          * Update the bounding box using itemPtr, which is the
  2511.          * new closest item.
  2512.          */
  2513.  
  2514.         x1 = (coords[0] - closestDist - halo - 1);
  2515.         y1 = (coords[1] - closestDist - halo - 1);
  2516.         x2 = (coords[0] + closestDist + halo + 1);
  2517.         y2 = (coords[1] + closestDist + halo + 1);
  2518.         closestPtr = itemPtr;
  2519.  
  2520.         /*
  2521.          * Search for an item that beats the current closest one.
  2522.          * Work circularly through the canvas's item list until
  2523.          * getting back to the starting item.
  2524.          */
  2525.  
  2526.         while (1) {
  2527.         itemPtr = itemPtr->nextPtr;
  2528.         if (itemPtr == NULL) {
  2529.             itemPtr = canvasPtr->firstItemPtr;
  2530.         }
  2531.         if (itemPtr == startPtr) {
  2532.             DoItem(interp, closestPtr, uid);
  2533.             return TCL_OK;
  2534.         }
  2535.         if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
  2536.             || (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
  2537.             continue;
  2538.         }
  2539.         newDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
  2540.             itemPtr, coords) - halo;
  2541.         if (newDist < 0.0) {
  2542.             newDist = 0.0;
  2543.         }
  2544.         if (newDist <= closestDist) {
  2545.             closestDist = newDist;
  2546.             break;
  2547.         }
  2548.         }
  2549.     }
  2550.     } else if ((c == 'e') && (strncmp(argv[0], "enclosed", length) == 0)) {
  2551.     if (argc != 5) {
  2552.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  2553.             cmdName, option, " enclosed x1 y1 x2 y2", (char *) NULL);
  2554.         return TCL_ERROR;
  2555.     }
  2556.     return FindArea(interp, canvasPtr, argv+1, uid, 1);
  2557.     } else if ((c == 'o') && (strncmp(argv[0], "overlapping", length) == 0)) {
  2558.     if (argc != 5) {
  2559.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  2560.             cmdName, option, " overlapping x1 y1 x2 y2",
  2561.             (char *) NULL);
  2562.         return TCL_ERROR;
  2563.     }
  2564.     return FindArea(interp, canvasPtr, argv+1, uid, 0);
  2565.     } else if ((c == 'w') && (strncmp(argv[0], "withtag", length) == 0)) {
  2566.         if (argc != 2) {
  2567.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  2568.             cmdName, option, " withtag tagOrId", (char *) NULL);
  2569.         return TCL_ERROR;
  2570.     }
  2571.     for (itemPtr = StartTagSearch(canvasPtr, argv[1], &search);
  2572.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  2573.         DoItem(interp, itemPtr, uid);
  2574.     }
  2575.     } else  {
  2576.     Tcl_AppendResult(interp, "bad search command \"", argv[0],
  2577.         "\": must be above, all, below, closest, enclosed, ",
  2578.         "overlapping, or withtag", (char *) NULL);
  2579.     return TCL_ERROR;
  2580.     }
  2581.     return TCL_OK;
  2582. }
  2583.  
  2584. /*
  2585.  *--------------------------------------------------------------
  2586.  *
  2587.  * FindArea --
  2588.  *
  2589.  *    This procedure implements area searches for the "find"
  2590.  *    and "addtag" options.
  2591.  *
  2592.  * Results:
  2593.  *    A standard Tcl return value.  If newTag is NULL, then a
  2594.  *    list of ids from all the items overlapping or enclosed
  2595.  *    by the rectangle given by argc is returned in interp->result.
  2596.  *    If newTag is NULL, then the normal interp->result is an
  2597.  *    empty string.  If an error occurs, then interp->result will
  2598.  *    hold an error message.
  2599.  *
  2600.  * Side effects:
  2601.  *    If uid is non-NULL, then all the items overlapping
  2602.  *    or enclosed by the area in argv have that tag added to
  2603.  *    their lists of tags.
  2604.  *
  2605.  *--------------------------------------------------------------
  2606.  */
  2607.  
  2608. static int
  2609. FindArea(interp, canvasPtr, argv, uid, enclosed)
  2610.     Tcl_Interp *interp;            /* Interpreter for error reporting
  2611.                      * and result storing. */
  2612.     TkCanvas *canvasPtr;        /* Canvas whose items are to be
  2613.                      * searched. */
  2614.     char **argv;            /* Array of four arguments that
  2615.                      * give the coordinates of the
  2616.                      * rectangular area to search. */
  2617.     Tk_Uid uid;                /* If non-NULL, gives new tag to set
  2618.                      * on all found items;  if NULL, then
  2619.                      * ids of found items are returned
  2620.                      * in interp->result. */
  2621.     int enclosed;            /* 0 means overlapping or enclosed
  2622.                      * items are OK, 1 means only enclosed
  2623.                      * items are OK. */
  2624. {
  2625.     double rect[4], tmp;
  2626.     int x1, y1, x2, y2;
  2627.     Tk_Item *itemPtr;
  2628.  
  2629.     if ((Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[0],
  2630.         &rect[0]) != TCL_OK)
  2631.         || (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[1],
  2632.         &rect[1]) != TCL_OK)
  2633.         || (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[2],
  2634.         &rect[2]) != TCL_OK)
  2635.         || (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[3],
  2636.         &rect[3]) != TCL_OK)) {
  2637.     return TCL_ERROR;
  2638.     }
  2639.     if (rect[0] > rect[2]) {
  2640.     tmp = rect[0]; rect[0] = rect[2]; rect[2] = tmp;
  2641.     }
  2642.     if (rect[1] > rect[3]) {
  2643.     tmp = rect[1]; rect[1] = rect[3]; rect[3] = tmp;
  2644.     }
  2645.  
  2646.     /*
  2647.      * Use an integer bounding box for a quick test, to avoid
  2648.      * calling item-specific code except for items that are close.
  2649.      */
  2650.  
  2651.     x1 = (rect[0]-1.0);
  2652.     y1 = (rect[1]-1.0);
  2653.     x2 = (rect[2]+1.0);
  2654.     y2 = (rect[3]+1.0);
  2655.     for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  2656.         itemPtr = itemPtr->nextPtr) {
  2657.     if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
  2658.         || (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
  2659.         continue;
  2660.     }
  2661.     if ((*itemPtr->typePtr->areaProc)((Tk_Canvas) canvasPtr, itemPtr, rect)
  2662.         >= enclosed) {
  2663.         DoItem(interp, itemPtr, uid);
  2664.     }
  2665.     }
  2666.     return TCL_OK;
  2667. }
  2668.  
  2669. /*
  2670.  *--------------------------------------------------------------
  2671.  *
  2672.  * RelinkItems --
  2673.  *
  2674.  *    Move one or more items to a different place in the
  2675.  *    display order for a canvas.
  2676.  *
  2677.  * Results:
  2678.  *    None.
  2679.  *
  2680.  * Side effects:
  2681.  *    The items identified by "tag" are moved so that they
  2682.  *    are all together in the display list and immediately
  2683.  *    after prevPtr.  The order of the moved items relative
  2684.  *    to each other is not changed.
  2685.  *
  2686.  *--------------------------------------------------------------
  2687.  */
  2688.  
  2689. static void
  2690. RelinkItems(canvasPtr, tag, prevPtr)
  2691.     TkCanvas *canvasPtr;    /* Canvas to be modified. */
  2692.     char *tag;            /* Tag identifying items to be moved
  2693.                  * in the redisplay list. */
  2694.     Tk_Item *prevPtr;        /* Reposition the items so that they
  2695.                  * go just after this item (NULL means
  2696.                  * put at beginning of list). */
  2697. {
  2698.     Tk_Item *itemPtr;
  2699.     TagSearch search;
  2700.     Tk_Item *firstMovePtr, *lastMovePtr;
  2701.  
  2702.     /*
  2703.      * Find all of the items to be moved and remove them from
  2704.      * the list, making an auxiliary list running from firstMovePtr
  2705.      * to lastMovePtr.  Record their areas for redisplay.
  2706.      */
  2707.  
  2708.     firstMovePtr = lastMovePtr = NULL;
  2709.     for (itemPtr = StartTagSearch(canvasPtr, tag, &search);
  2710.         itemPtr != NULL; itemPtr = NextItem(&search)) {
  2711.     if (itemPtr == prevPtr) {
  2712.         /*
  2713.          * Item after which insertion is to occur is being
  2714.          * moved!  Switch to insert after its predecessor.
  2715.          */
  2716.  
  2717.         prevPtr = search.prevPtr;
  2718.     }
  2719.     if (search.prevPtr == NULL) {
  2720.         canvasPtr->firstItemPtr = itemPtr->nextPtr;
  2721.     } else {
  2722.         search.prevPtr->nextPtr = itemPtr->nextPtr;
  2723.     }
  2724.     if (canvasPtr->lastItemPtr == itemPtr) {
  2725.         canvasPtr->lastItemPtr = search.prevPtr;
  2726.     }
  2727.     if (firstMovePtr == NULL) {
  2728.         firstMovePtr = itemPtr;
  2729.     } else {
  2730.         lastMovePtr->nextPtr = itemPtr;
  2731.     }
  2732.     lastMovePtr = itemPtr;
  2733.     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, itemPtr->x1, itemPtr->y1,
  2734.         itemPtr->x2, itemPtr->y2);
  2735.     canvasPtr->flags |= REPICK_NEEDED;
  2736.     }
  2737.  
  2738.     /*
  2739.      * Insert the list of to-be-moved items back into the canvas's
  2740.      * at the desired position.
  2741.      */
  2742.  
  2743.     if (firstMovePtr == NULL) {
  2744.     return;
  2745.     }
  2746.     if (prevPtr == NULL) {
  2747.     lastMovePtr->nextPtr = canvasPtr->firstItemPtr;
  2748.     canvasPtr->firstItemPtr = firstMovePtr;
  2749.     } else {
  2750.     lastMovePtr->nextPtr = prevPtr->nextPtr;
  2751.     prevPtr->nextPtr = firstMovePtr;
  2752.     }
  2753.     if (canvasPtr->lastItemPtr == prevPtr) {
  2754.     canvasPtr->lastItemPtr = lastMovePtr;
  2755.     }
  2756. }
  2757.  
  2758. /*
  2759.  *--------------------------------------------------------------
  2760.  *
  2761.  * CanvasBindProc --
  2762.  *
  2763.  *    This procedure is invoked by the Tk dispatcher to handle
  2764.  *    events associated with bindings on items.
  2765.  *
  2766.  * Results:
  2767.  *    None.
  2768.  *
  2769.  * Side effects:
  2770.  *    Depends on the command invoked as part of the binding
  2771.  *    (if there was any).
  2772.  *
  2773.  *--------------------------------------------------------------
  2774.  */
  2775.  
  2776. static void
  2777. CanvasBindProc(clientData, eventPtr)
  2778.     ClientData clientData;        /* Pointer to canvas structure. */
  2779.     XEvent *eventPtr;            /* Pointer to X event that just
  2780.                      * happened. */
  2781. {
  2782.     TkCanvas *canvasPtr = (TkCanvas *) clientData;
  2783.  
  2784.     Tcl_Preserve((ClientData) canvasPtr);
  2785.  
  2786.     /*
  2787.      * This code below keeps track of the current modifier state in
  2788.      * canvasPtr>state.  This information is used to defer repicks of
  2789.      * the current item while buttons are down.
  2790.      */
  2791.  
  2792.     if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) {
  2793.     int mask;
  2794.  
  2795.     switch (eventPtr->xbutton.button) {
  2796.         case Button1:
  2797.         mask = Button1Mask;
  2798.         break;
  2799.         case Button2:
  2800.         mask = Button2Mask;
  2801.         break;
  2802.         case Button3:
  2803.         mask = Button3Mask;
  2804.         break;
  2805.         case Button4:
  2806.         mask = Button4Mask;
  2807.         break;
  2808.         case Button5:
  2809.         mask = Button5Mask;
  2810.         break;
  2811.         default:
  2812.         mask = 0;
  2813.         break;
  2814.     }
  2815.  
  2816.     /*
  2817.      * For button press events, repick the current item using the
  2818.      * button state before the event, then process the event.  For
  2819.      * button release events, first process the event, then repick
  2820.      * the current item using the button state *after* the event
  2821.      * (the button has logically gone up before we change the
  2822.      * current item).
  2823.      */
  2824.  
  2825.     if (eventPtr->type == ButtonPress) {
  2826.         /*
  2827.          * On a button press, first repick the current item using
  2828.          * the button state before the event, the process the event.
  2829.          */
  2830.  
  2831.         canvasPtr->state = eventPtr->xbutton.state;
  2832.         PickCurrentItem(canvasPtr, eventPtr);
  2833.         canvasPtr->state ^= mask;
  2834.         CanvasDoEvent(canvasPtr, eventPtr);
  2835.     } else {
  2836.         /*
  2837.          * Button release: first process the event, with the button
  2838.          * still considered to be down.  Then repick the current
  2839.          * item under the assumption that the button is no longer down.
  2840.          */
  2841.  
  2842.         canvasPtr->state = eventPtr->xbutton.state;
  2843.         CanvasDoEvent(canvasPtr, eventPtr);
  2844.         eventPtr->xbutton.state ^= mask;
  2845.         canvasPtr->state = eventPtr->xbutton.state;
  2846.         PickCurrentItem(canvasPtr, eventPtr);
  2847.         eventPtr->xbutton.state ^= mask;
  2848.     }
  2849.     goto done;
  2850.     } else if ((eventPtr->type == EnterNotify)
  2851.         || (eventPtr->type == LeaveNotify)) {
  2852.     canvasPtr->state = eventPtr->xcrossing.state;
  2853.     PickCurrentItem(canvasPtr, eventPtr);
  2854.     goto done;
  2855.     } else if (eventPtr->type == MotionNotify) {
  2856.     canvasPtr->state = eventPtr->xmotion.state;
  2857.     PickCurrentItem(canvasPtr, eventPtr);
  2858.     }
  2859.     CanvasDoEvent(canvasPtr, eventPtr);
  2860.  
  2861.     done:
  2862.     Tcl_Release((ClientData) canvasPtr);
  2863. }
  2864.  
  2865. /*
  2866.  *--------------------------------------------------------------
  2867.  *
  2868.  * PickCurrentItem --
  2869.  *
  2870.  *    Find the topmost item in a canvas that contains a given
  2871.  *    location and mark the the current item.  If the current
  2872.  *    item has changed, generate a fake exit event on the old
  2873.  *    current item and a fake enter event on the new current
  2874.  *    item.
  2875.  *
  2876.  * Results:
  2877.  *    None.
  2878.  *
  2879.  * Side effects:
  2880.  *    The current item for canvasPtr may change.  If it does,
  2881.  *    then the commands associated with item entry and exit
  2882.  *    could do just about anything.  A binding script could
  2883.  *    delete the canvas, so callers should protect themselves
  2884.  *    with Tcl_Preserve and Tcl_Release.
  2885.  *
  2886.  *--------------------------------------------------------------
  2887.  */
  2888.  
  2889. static void
  2890. PickCurrentItem(canvasPtr, eventPtr)
  2891.     TkCanvas *canvasPtr;        /* Canvas widget in which to select
  2892.                      * current item. */
  2893.     XEvent *eventPtr;            /* Event describing location of
  2894.                      * mouse cursor.  Must be EnterWindow,
  2895.                      * LeaveWindow, ButtonRelease, or
  2896.                      * MotionNotify. */
  2897. {
  2898.     double coords[2];
  2899.     int buttonDown;
  2900.  
  2901.     /*
  2902.      * Check whether or not a button is down.  If so, we'll log entry
  2903.      * and exit into and out of the current item, but not entry into
  2904.      * any other item.  This implements a form of grabbing equivalent
  2905.      * to what the X server does for windows.
  2906.      */
  2907.  
  2908.     buttonDown = canvasPtr->state
  2909.         & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask);
  2910.     if (!buttonDown) {
  2911.     canvasPtr->flags &= ~LEFT_GRABBED_ITEM;
  2912.     }
  2913.  
  2914.     /*
  2915.      * Save information about this event in the canvas.  The event in
  2916.      * the canvas is used for two purposes:
  2917.      *
  2918.      * 1. Event bindings: if the current item changes, fake events are
  2919.      *    generated to allow item-enter and item-leave bindings to trigger.
  2920.      * 2. Reselection: if the current item gets deleted, can use the
  2921.      *    saved event to find a new current item.
  2922.      * Translate MotionNotify events into EnterNotify events, since that's
  2923.      * what gets reported to item handlers.
  2924.      */
  2925.  
  2926.     if (eventPtr != &canvasPtr->pickEvent) {
  2927.     if ((eventPtr->type == MotionNotify)
  2928.         || (eventPtr->type == ButtonRelease)) {
  2929.         canvasPtr->pickEvent.xcrossing.type = EnterNotify;
  2930.         canvasPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial;
  2931.         canvasPtr->pickEvent.xcrossing.send_event
  2932.             = eventPtr->xmotion.send_event;
  2933.         canvasPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display;
  2934.         canvasPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window;
  2935.         canvasPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root;
  2936.         canvasPtr->pickEvent.xcrossing.subwindow = None;
  2937.         canvasPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time;
  2938.         canvasPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x;
  2939.         canvasPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y;
  2940.         canvasPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root;
  2941.         canvasPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root;
  2942.         canvasPtr->pickEvent.xcrossing.mode = NotifyNormal;
  2943.         canvasPtr->pickEvent.xcrossing.detail = NotifyNonlinear;
  2944.         canvasPtr->pickEvent.xcrossing.same_screen
  2945.             = eventPtr->xmotion.same_screen;
  2946.         canvasPtr->pickEvent.xcrossing.focus = False;
  2947.         canvasPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state;
  2948.     } else  {
  2949.         canvasPtr->pickEvent = *eventPtr;
  2950.     }
  2951.     }
  2952.  
  2953.     /*
  2954.      * If this is a recursive call (there's already a partially completed
  2955.      * call pending on the stack;  it's in the middle of processing a
  2956.      * Leave event handler for the old current item) then just return;
  2957.      * the pending call will do everything that's needed.
  2958.      */
  2959.  
  2960.     if (canvasPtr->flags & REPICK_IN_PROGRESS) {
  2961.     return;
  2962.     }
  2963.  
  2964.     /*
  2965.      * A LeaveNotify event automatically means that there's no current
  2966.      * object, so the check for closest item can be skipped.
  2967.      */
  2968.  
  2969.     coords[0] = canvasPtr->pickEvent.xcrossing.x + canvasPtr->xOrigin;
  2970.     coords[1] = canvasPtr->pickEvent.xcrossing.y + canvasPtr->yOrigin;
  2971.     if (canvasPtr->pickEvent.type != LeaveNotify) {
  2972.     canvasPtr->newCurrentPtr = CanvasFindClosest(canvasPtr, coords);
  2973.     } else {
  2974.     canvasPtr->newCurrentPtr = NULL;
  2975.     }
  2976.  
  2977.     if ((canvasPtr->newCurrentPtr == canvasPtr->currentItemPtr)
  2978.         && !(canvasPtr->flags & LEFT_GRABBED_ITEM)) {
  2979.     /*
  2980.      * Nothing to do:  the current item hasn't changed.
  2981.      */
  2982.  
  2983.     return;
  2984.     }
  2985.  
  2986.     /*
  2987.      * Simulate a LeaveNotify event on the previous current item and
  2988.      * an EnterNotify event on the new current item.  Remove the "current"
  2989.      * tag from the previous current item and place it on the new current
  2990.      * item.
  2991.      */
  2992.  
  2993.     if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr)
  2994.         && (canvasPtr->currentItemPtr != NULL)
  2995.         && !(canvasPtr->flags & LEFT_GRABBED_ITEM)) {
  2996.     XEvent event;
  2997.     Tk_Item *itemPtr = canvasPtr->currentItemPtr;
  2998.     int i;
  2999.  
  3000.     event = canvasPtr->pickEvent;
  3001.     event.type = LeaveNotify;
  3002.  
  3003.     /*
  3004.      * If the event's detail happens to be NotifyInferior the
  3005.      * binding mechanism will discard the event.  To be consistent,
  3006.      * always use NotifyAncestor.
  3007.      */
  3008.  
  3009.     event.xcrossing.detail = NotifyAncestor;
  3010.     canvasPtr->flags |= REPICK_IN_PROGRESS;
  3011.     CanvasDoEvent(canvasPtr, &event);
  3012.     canvasPtr->flags &= ~REPICK_IN_PROGRESS;
  3013.  
  3014.     /*
  3015.      * The check below is needed because there could be an event
  3016.      * handler for <LeaveNotify> that deletes the current item.
  3017.      */
  3018.  
  3019.     if ((itemPtr == canvasPtr->currentItemPtr) && !buttonDown) {
  3020.         for (i = itemPtr->numTags-1; i >= 0; i--) {
  3021.         if (itemPtr->tagPtr[i] == currentUid) {
  3022.             itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
  3023.             itemPtr->numTags--;
  3024.             break;
  3025.         }
  3026.         }
  3027.     }
  3028.     
  3029.     /*
  3030.      * Note:  during CanvasDoEvent above, it's possible that
  3031.      * canvasPtr->newCurrentPtr got reset to NULL because the
  3032.      * item was deleted.
  3033.      */
  3034.     }
  3035.     if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr) && buttonDown) {
  3036.     canvasPtr->flags |= LEFT_GRABBED_ITEM;
  3037.     return;
  3038.     }
  3039.  
  3040.     /*
  3041.      * Special note:  it's possible that canvasPtr->newCurrentPtr ==
  3042.      * canvasPtr->currentItemPtr here.  This can happen, for example,
  3043.      * if LEFT_GRABBED_ITEM was set.
  3044.      */
  3045.  
  3046.     canvasPtr->flags &= ~LEFT_GRABBED_ITEM;
  3047.     canvasPtr->currentItemPtr = canvasPtr->newCurrentPtr;
  3048.     if (canvasPtr->currentItemPtr != NULL) {
  3049.     XEvent event;
  3050.  
  3051.     DoItem((Tcl_Interp *) NULL, canvasPtr->currentItemPtr, currentUid);
  3052.     event = canvasPtr->pickEvent;
  3053.     event.type = EnterNotify;
  3054.     event.xcrossing.detail = NotifyAncestor;
  3055.     CanvasDoEvent(canvasPtr, &event);
  3056.     }
  3057. }
  3058.  
  3059. /*
  3060.  *----------------------------------------------------------------------
  3061.  *
  3062.  * CanvasFindClosest --
  3063.  *
  3064.  *    Given x and y coordinates, find the topmost canvas item that
  3065.  *    is "close" to the coordinates.
  3066.  *
  3067.  * Results:
  3068.  *    The return value is a pointer to the topmost item that is
  3069.  *    close to (x,y), or NULL if no item is close.
  3070.  *
  3071.  * Side effects:
  3072.  *    None.
  3073.  *
  3074.  *----------------------------------------------------------------------
  3075.  */
  3076.  
  3077. static Tk_Item *
  3078. CanvasFindClosest(canvasPtr, coords)
  3079.     TkCanvas *canvasPtr;        /* Canvas widget to search. */
  3080.     double coords[2];            /* Desired x,y position in canvas,
  3081.                      * not screen, coordinates.) */
  3082. {
  3083.     Tk_Item *itemPtr;
  3084.     Tk_Item *bestPtr;
  3085.     int x1, y1, x2, y2;
  3086.  
  3087.     x1 = coords[0] - canvasPtr->closeEnough;
  3088.     y1 = coords[1] - canvasPtr->closeEnough;
  3089.     x2 = coords[0] + canvasPtr->closeEnough;
  3090.     y2 = coords[1] + canvasPtr->closeEnough;
  3091.  
  3092.     bestPtr = NULL;
  3093.     for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  3094.         itemPtr = itemPtr->nextPtr) {
  3095.     if ((itemPtr->x1 > x2) || (itemPtr->x2 < x1)
  3096.         || (itemPtr->y1 > y2) || (itemPtr->y2 < y1)) {
  3097.         continue;
  3098.     }
  3099.     if ((*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
  3100.         itemPtr, coords) <= canvasPtr->closeEnough) {
  3101.         bestPtr = itemPtr;
  3102.     }
  3103.     }
  3104.     return bestPtr;
  3105. }
  3106.  
  3107. /*
  3108.  *--------------------------------------------------------------
  3109.  *
  3110.  * CanvasDoEvent --
  3111.  *
  3112.  *    This procedure is called to invoke binding processing
  3113.  *    for a new event that is associated with the current item
  3114.  *    for a canvas.
  3115.  *
  3116.  * Results:
  3117.  *    None.
  3118.  *
  3119.  * Side effects:
  3120.  *    Depends on the bindings for the canvas.  A binding script
  3121.  *    could delete the canvas, so callers should protect themselves
  3122.  *    with Tcl_Preserve and Tcl_Release.
  3123.  *
  3124.  *--------------------------------------------------------------
  3125.  */
  3126.  
  3127. static void
  3128. CanvasDoEvent(canvasPtr, eventPtr)
  3129.     TkCanvas *canvasPtr;        /* Canvas widget in which event
  3130.                      * occurred. */
  3131.     XEvent *eventPtr;            /* Real or simulated X event that
  3132.                      * is to be processed. */
  3133. {
  3134. #define NUM_STATIC 3
  3135.     ClientData staticObjects[NUM_STATIC];
  3136.     ClientData *objectPtr;
  3137.     int numObjects, i;
  3138.     Tk_Item *itemPtr;
  3139.  
  3140.     if (canvasPtr->bindingTable == NULL) {
  3141.     return;
  3142.     }
  3143.  
  3144.     itemPtr = canvasPtr->currentItemPtr;
  3145.     if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) {
  3146.     itemPtr = canvasPtr->textInfo.focusItemPtr;
  3147.     }
  3148.     if (itemPtr == NULL) {
  3149.     return;
  3150.     }
  3151.  
  3152.     /*
  3153.      * Set up an array with all the relevant objects for processing
  3154.      * this event.  The relevant objects are (a) the event's item,
  3155.      * (b) the tags associated with the event's item, and (c) the
  3156.      * tag "all".  If there are a lot of tags then malloc an array
  3157.      * to hold all of the objects.
  3158.      */
  3159.  
  3160.     numObjects = itemPtr->numTags + 2;
  3161.     if (numObjects <= NUM_STATIC) {
  3162.     objectPtr = staticObjects;
  3163.     } else {
  3164.     objectPtr = (ClientData *) ckalloc((unsigned)
  3165.         (numObjects * sizeof(ClientData)));
  3166.     }
  3167.     objectPtr[0] = (ClientData) allUid;
  3168.     for (i = itemPtr->numTags-1; i >= 0; i--) {
  3169.     objectPtr[i+1] = (ClientData) itemPtr->tagPtr[i];
  3170.     }
  3171.     objectPtr[itemPtr->numTags+1] = (ClientData) itemPtr;
  3172.  
  3173.     /*
  3174.      * Invoke the binding system, then free up the object array if
  3175.      * it was malloc-ed.
  3176.      */
  3177.  
  3178.     if (canvasPtr->tkwin != NULL) {
  3179.     Tk_BindEvent(canvasPtr->bindingTable, eventPtr, canvasPtr->tkwin,
  3180.         numObjects, objectPtr);
  3181.     }
  3182.     if (objectPtr != staticObjects) {
  3183.     ckfree((char *) objectPtr);
  3184.     }
  3185. }
  3186.  
  3187. /*
  3188.  *----------------------------------------------------------------------
  3189.  *
  3190.  * CanvasBlinkProc --
  3191.  *
  3192.  *    This procedure is called as a timer handler to blink the
  3193.  *    insertion cursor off and on.
  3194.  *
  3195.  * Results:
  3196.  *    None.
  3197.  *
  3198.  * Side effects:
  3199.  *    The cursor gets turned on or off, redisplay gets invoked,
  3200.  *    and this procedure reschedules itself.
  3201.  *
  3202.  *----------------------------------------------------------------------
  3203.  */
  3204.  
  3205. static void
  3206. CanvasBlinkProc(clientData)
  3207.     ClientData clientData;    /* Pointer to record describing entry. */
  3208. {
  3209.     TkCanvas *canvasPtr = (TkCanvas *) clientData;
  3210.  
  3211.     if (!canvasPtr->textInfo.gotFocus || (canvasPtr->insertOffTime == 0)) {
  3212.     return;
  3213.     }
  3214.     if (canvasPtr->textInfo.cursorOn) {
  3215.     canvasPtr->textInfo.cursorOn = 0;
  3216.     canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
  3217.         canvasPtr->insertOffTime, CanvasBlinkProc,
  3218.         (ClientData) canvasPtr);
  3219.     } else {
  3220.     canvasPtr->textInfo.cursorOn = 1;
  3221.     canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
  3222.         canvasPtr->insertOnTime, CanvasBlinkProc,
  3223.         (ClientData) canvasPtr);
  3224.     }
  3225.     if (canvasPtr->textInfo.focusItemPtr != NULL) {
  3226.     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  3227.         canvasPtr->textInfo.focusItemPtr->x1,
  3228.         canvasPtr->textInfo.focusItemPtr->y1,
  3229.         canvasPtr->textInfo.focusItemPtr->x2,
  3230.         canvasPtr->textInfo.focusItemPtr->y2);
  3231.     }
  3232. }
  3233.  
  3234. /*
  3235.  *----------------------------------------------------------------------
  3236.  *
  3237.  * CanvasFocusProc --
  3238.  *
  3239.  *    This procedure is called whenever a canvas gets or loses the
  3240.  *    input focus.  It's also called whenever the window is
  3241.  *    reconfigured while it has the focus.
  3242.  *
  3243.  * Results:
  3244.  *    None.
  3245.  *
  3246.  * Side effects:
  3247.  *    The cursor gets turned on or off.
  3248.  *
  3249.  *----------------------------------------------------------------------
  3250.  */
  3251.  
  3252. static void
  3253. CanvasFocusProc(canvasPtr, gotFocus)
  3254.     TkCanvas *canvasPtr;    /* Canvas that just got or lost focus. */
  3255.     int gotFocus;        /* 1 means window is getting focus, 0 means
  3256.                  * it's losing it. */
  3257. {
  3258.     Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler);
  3259.     if (gotFocus) {
  3260.     canvasPtr->textInfo.gotFocus = 1;
  3261.     canvasPtr->textInfo.cursorOn = 1;
  3262.     if (canvasPtr->insertOffTime != 0) {
  3263.         canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
  3264.             canvasPtr->insertOffTime, CanvasBlinkProc,
  3265.             (ClientData) canvasPtr);
  3266.     }
  3267.     } else {
  3268.     canvasPtr->textInfo.gotFocus = 0;
  3269.     canvasPtr->textInfo.cursorOn = 0;
  3270.     canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
  3271.     }
  3272.     if (canvasPtr->textInfo.focusItemPtr != NULL) {
  3273.     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  3274.         canvasPtr->textInfo.focusItemPtr->x1,
  3275.         canvasPtr->textInfo.focusItemPtr->y1,
  3276.         canvasPtr->textInfo.focusItemPtr->x2,
  3277.         canvasPtr->textInfo.focusItemPtr->y2);
  3278.     }
  3279.     if (canvasPtr->highlightWidth > 0) {
  3280.     canvasPtr->flags |= REDRAW_BORDERS;
  3281.     if (!(canvasPtr->flags & REDRAW_PENDING)) {
  3282.         Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
  3283.         canvasPtr->flags |= REDRAW_PENDING;
  3284.     }
  3285.     }
  3286. }
  3287.  
  3288. /*
  3289.  *----------------------------------------------------------------------
  3290.  *
  3291.  * CanvasSelectTo --
  3292.  *
  3293.  *    Modify the selection by moving its un-anchored end.  This could
  3294.  *    make the selection either larger or smaller.
  3295.  *
  3296.  * Results:
  3297.  *    None.
  3298.  *
  3299.  * Side effects:
  3300.  *    The selection changes.
  3301.  *
  3302.  *----------------------------------------------------------------------
  3303.  */
  3304.  
  3305. static void
  3306. CanvasSelectTo(canvasPtr, itemPtr, index)
  3307.     TkCanvas *canvasPtr;    /* Information about widget. */
  3308.     Tk_Item *itemPtr;        /* Item that is to hold selection. */
  3309.     int index;            /* Index of element that is to become the
  3310.                  * "other" end of the selection. */
  3311. {
  3312.     int oldFirst, oldLast;
  3313.     Tk_Item *oldSelPtr;
  3314.  
  3315.     oldFirst = canvasPtr->textInfo.selectFirst;
  3316.     oldLast = canvasPtr->textInfo.selectLast;
  3317.     oldSelPtr = canvasPtr->textInfo.selItemPtr;
  3318.  
  3319.     /*
  3320.      * Grab the selection if we don't own it already.
  3321.      */
  3322.  
  3323.     if (canvasPtr->textInfo.selItemPtr == NULL) {
  3324.     Tk_OwnSelection(canvasPtr->tkwin, XA_PRIMARY, CanvasLostSelection,
  3325.         (ClientData) canvasPtr);
  3326.     } else if (canvasPtr->textInfo.selItemPtr != itemPtr) {
  3327.     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  3328.         canvasPtr->textInfo.selItemPtr->x1,
  3329.         canvasPtr->textInfo.selItemPtr->y1,
  3330.         canvasPtr->textInfo.selItemPtr->x2,
  3331.         canvasPtr->textInfo.selItemPtr->y2);
  3332.     }
  3333.     canvasPtr->textInfo.selItemPtr = itemPtr;
  3334.  
  3335.     if (canvasPtr->textInfo.anchorItemPtr != itemPtr) {
  3336.     canvasPtr->textInfo.anchorItemPtr = itemPtr;
  3337.     canvasPtr->textInfo.selectAnchor = index;
  3338.     }
  3339.     if (canvasPtr->textInfo.selectAnchor <= index) {
  3340.     canvasPtr->textInfo.selectFirst = canvasPtr->textInfo.selectAnchor;
  3341.     canvasPtr->textInfo.selectLast = index;
  3342.     } else {
  3343.     canvasPtr->textInfo.selectFirst = index;
  3344.     canvasPtr->textInfo.selectLast = canvasPtr->textInfo.selectAnchor - 1;
  3345.     }
  3346.     if ((canvasPtr->textInfo.selectFirst != oldFirst)
  3347.         || (canvasPtr->textInfo.selectLast != oldLast)
  3348.         || (itemPtr != oldSelPtr)) {
  3349.     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  3350.         itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
  3351.     }
  3352. }
  3353.  
  3354. /*
  3355.  *--------------------------------------------------------------
  3356.  *
  3357.  * CanvasFetchSelection --
  3358.  *
  3359.  *    This procedure is invoked by Tk to return part or all of
  3360.  *    the selection, when the selection is in a canvas widget.
  3361.  *    This procedure always returns the selection as a STRING.
  3362.  *
  3363.  * Results:
  3364.  *    The return value is the number of non-NULL bytes stored
  3365.  *    at buffer.  Buffer is filled (or partially filled) with a
  3366.  *    NULL-terminated string containing part or all of the selection,
  3367.  *    as given by offset and maxBytes.
  3368.  *
  3369.  * Side effects:
  3370.  *    None.
  3371.  *
  3372.  *--------------------------------------------------------------
  3373.  */
  3374.  
  3375. static int
  3376. CanvasFetchSelection(clientData, offset, buffer, maxBytes)
  3377.     ClientData clientData;        /* Information about canvas widget. */
  3378.     int offset;                /* Offset within selection of first
  3379.                      * character to be returned. */
  3380.     char *buffer;            /* Location in which to place
  3381.                      * selection. */
  3382.     int maxBytes;            /* Maximum number of bytes to place
  3383.                      * at buffer, not including terminating
  3384.                      * NULL character. */
  3385. {
  3386.     TkCanvas *canvasPtr = (TkCanvas *) clientData;
  3387.  
  3388.     if (canvasPtr->textInfo.selItemPtr == NULL) {
  3389.     return -1;
  3390.     }
  3391.     if (canvasPtr->textInfo.selItemPtr->typePtr->selectionProc == NULL) {
  3392.     return -1;
  3393.     }
  3394.     return (*canvasPtr->textInfo.selItemPtr->typePtr->selectionProc)(
  3395.         (Tk_Canvas) canvasPtr, canvasPtr->textInfo.selItemPtr, offset,
  3396.         buffer, maxBytes);
  3397. }
  3398.  
  3399. /*
  3400.  *----------------------------------------------------------------------
  3401.  *
  3402.  * CanvasLostSelection --
  3403.  *
  3404.  *    This procedure is called back by Tk when the selection is
  3405.  *    grabbed away from a canvas widget.
  3406.  *
  3407.  * Results:
  3408.  *    None.
  3409.  *
  3410.  * Side effects:
  3411.  *    The existing selection is unhighlighted, and the window is
  3412.  *    marked as not containing a selection.
  3413.  *
  3414.  *----------------------------------------------------------------------
  3415.  */
  3416.  
  3417. static void
  3418. CanvasLostSelection(clientData)
  3419.     ClientData clientData;        /* Information about entry widget. */
  3420. {
  3421.     TkCanvas *canvasPtr = (TkCanvas *) clientData;
  3422.  
  3423.     if (canvasPtr->textInfo.selItemPtr != NULL) {
  3424.     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  3425.         canvasPtr->textInfo.selItemPtr->x1,
  3426.         canvasPtr->textInfo.selItemPtr->y1,
  3427.         canvasPtr->textInfo.selItemPtr->x2,
  3428.         canvasPtr->textInfo.selItemPtr->y2);
  3429.     }
  3430.     canvasPtr->textInfo.selItemPtr = NULL;
  3431. }
  3432.  
  3433. /*
  3434.  *--------------------------------------------------------------
  3435.  *
  3436.  * GridAlign --
  3437.  *
  3438.  *    Given a coordinate and a grid spacing, this procedure
  3439.  *    computes the location of the nearest grid line to the
  3440.  *    coordinate.
  3441.  *
  3442.  * Results:
  3443.  *    The return value is the location of the grid line nearest
  3444.  *    to coord.
  3445.  *
  3446.  * Side effects:
  3447.  *    None.
  3448.  *
  3449.  *--------------------------------------------------------------
  3450.  */
  3451.  
  3452. static double
  3453. GridAlign(coord, spacing)
  3454.     double coord;        /* Coordinate to grid-align. */
  3455.     double spacing;        /* Spacing between grid lines.   If <= 0
  3456.                  * then no alignment is done. */
  3457. {
  3458.     if (spacing <= 0.0) {
  3459.     return coord;
  3460.     }
  3461.     if (coord < 0) {
  3462.     return -((int) ((-coord)/spacing + 0.5)) * spacing;
  3463.     }
  3464.     return ((int) (coord/spacing + 0.5)) * spacing;
  3465. }
  3466.  
  3467. /*
  3468.  *----------------------------------------------------------------------
  3469.  *
  3470.  * PrintScrollFractions --
  3471.  *
  3472.  *    Given the range that's visible in the window and the "100%
  3473.  *    range" for what's in the canvas, print a string containing
  3474.  *    the scroll fractions.  This procedure is used for both x
  3475.  *    and y scrolling.
  3476.  *
  3477.  * Results:
  3478.  *    The memory pointed to by string is modified to hold
  3479.  *    two real numbers containing the scroll fractions (between
  3480.  *    0 and 1) corresponding to the other arguments.
  3481.  *
  3482.  * Side effects:
  3483.  *    None.
  3484.  *
  3485.  *----------------------------------------------------------------------
  3486.  */
  3487.  
  3488. static void
  3489. PrintScrollFractions(screen1, screen2, object1, object2, string)
  3490.     int screen1;        /* Lowest coordinate visible in the window. */
  3491.     int screen2;        /* Highest coordinate visible in the window. */
  3492.     int object1;        /* Lowest coordinate in the object. */
  3493.     int object2;        /* Highest coordinate in the object. */
  3494.     char *string;        /* Two real numbers get printed here.  Must
  3495.                  * have enough storage for two %g
  3496.                  * conversions. */
  3497. {
  3498.     double range, f1, f2;
  3499.  
  3500.     range = object2 - object1;
  3501.     if (range <= 0) {
  3502.     f1 = 0;
  3503.     f2 = 1.0;
  3504.     } else {
  3505.     f1 = (screen1 - object1)/range;
  3506.     if (f1 < 0) {
  3507.         f1 = 0.0;
  3508.     }
  3509.     f2 = (screen2 - object1)/range;
  3510.     if (f2 > 1.0) {
  3511.         f2 = 1.0;
  3512.     }
  3513.     if (f2 < f1) {
  3514.         f2 = f1;
  3515.     }
  3516.     }
  3517.     sprintf(string, "%g %g", f1, f2);
  3518. }
  3519.  
  3520. /*
  3521.  *--------------------------------------------------------------
  3522.  *
  3523.  * CanvasUpdateScrollbars --
  3524.  *
  3525.  *    This procedure is invoked whenever a canvas has changed in
  3526.  *    a way that requires scrollbars to be redisplayed (e.g. the
  3527.  *    view in the canvas has changed).
  3528.  *
  3529.  * Results:
  3530.  *    None.
  3531.  *
  3532.  * Side effects:
  3533.  *    If there are scrollbars associated with the canvas, then
  3534.  *    their scrolling commands are invoked to cause them to
  3535.  *    redisplay.  If errors occur, additional Tcl commands may
  3536.  *    be invoked to process the errors.
  3537.  *
  3538.  *--------------------------------------------------------------
  3539.  */
  3540.  
  3541. static void
  3542. CanvasUpdateScrollbars(canvasPtr)
  3543.     TkCanvas *canvasPtr;        /* Information about canvas. */
  3544. {
  3545.     int result;
  3546.     char buffer[200];
  3547.     Tcl_Interp *interp;
  3548.     int xOrigin, yOrigin, inset, width, height, scrollX1, scrollX2,
  3549.         scrollY1, scrollY2;
  3550.     char *xScrollCmd, *yScrollCmd;
  3551.  
  3552.     /*
  3553.      * Save all the relevant values from the canvasPtr, because it might be
  3554.      * deleted as part of either of the two calls to Tcl_VarEval below.
  3555.      */
  3556.     
  3557.     interp = canvasPtr->interp;
  3558.     Tcl_Preserve((ClientData) interp);
  3559.     xScrollCmd = canvasPtr->xScrollCmd;
  3560.     if (xScrollCmd != (char *) NULL) {
  3561.         Tcl_Preserve((ClientData) xScrollCmd);
  3562.     }
  3563.     yScrollCmd = canvasPtr->yScrollCmd;
  3564.     if (yScrollCmd != (char *) NULL) {
  3565.         Tcl_Preserve((ClientData) yScrollCmd);
  3566.     }
  3567.     xOrigin = canvasPtr->xOrigin;
  3568.     yOrigin = canvasPtr->yOrigin;
  3569.     inset = canvasPtr->inset;
  3570.     width = Tk_Width(canvasPtr->tkwin);
  3571.     height = Tk_Height(canvasPtr->tkwin);
  3572.     scrollX1 = canvasPtr->scrollX1;
  3573.     scrollX2 = canvasPtr->scrollX2;
  3574.     scrollY1 = canvasPtr->scrollY1;
  3575.     scrollY2 = canvasPtr->scrollY2;
  3576.     canvasPtr->flags &= ~UPDATE_SCROLLBARS;
  3577.     if (canvasPtr->xScrollCmd != NULL) {
  3578.     PrintScrollFractions(xOrigin + inset, xOrigin + width - inset,
  3579.                 scrollX1, scrollX2, buffer);
  3580.     result = Tcl_VarEval(interp, xScrollCmd, " ", buffer, (char *) NULL);
  3581.     if (result != TCL_OK) {
  3582.         Tcl_BackgroundError(interp);
  3583.     }
  3584.     Tcl_ResetResult(interp);
  3585.         Tcl_Release((ClientData) xScrollCmd);
  3586.     }
  3587.  
  3588.     if (yScrollCmd != NULL) {
  3589.     PrintScrollFractions(yOrigin + inset, yOrigin + height - inset,
  3590.                 scrollY1, scrollY2, buffer);
  3591.     result = Tcl_VarEval(interp, yScrollCmd, " ", buffer, (char *) NULL);
  3592.     if (result != TCL_OK) {
  3593.         Tcl_BackgroundError(interp);
  3594.     }
  3595.     Tcl_ResetResult(interp);
  3596.         Tcl_Release((ClientData) yScrollCmd);
  3597.     }
  3598.     Tcl_Release((ClientData) interp);
  3599. }
  3600.  
  3601. /*
  3602.  *--------------------------------------------------------------
  3603.  *
  3604.  * CanvasSetOrigin --
  3605.  *
  3606.  *    This procedure is invoked to change the mapping between
  3607.  *    canvas coordinates and screen coordinates in the canvas
  3608.  *    window.
  3609.  *
  3610.  * Results:
  3611.  *    None.
  3612.  *
  3613.  * Side effects:
  3614.  *    The canvas will be redisplayed to reflect the change in
  3615.  *    view.  In addition, scrollbars will be updated if there
  3616.  *    are any.
  3617.  *
  3618.  *--------------------------------------------------------------
  3619.  */
  3620.  
  3621. static void
  3622. CanvasSetOrigin(canvasPtr, xOrigin, yOrigin)
  3623.     TkCanvas *canvasPtr;    /* Information about canvas. */
  3624.     int xOrigin;        /* New X origin for canvas (canvas x-coord
  3625.                  * corresponding to left edge of canvas
  3626.                  * window). */
  3627.     int yOrigin;        /* New Y origin for canvas (canvas y-coord
  3628.                  * corresponding to top edge of canvas
  3629.                  * window). */
  3630. {
  3631.     int left, right, top, bottom, delta;
  3632.  
  3633.     /*
  3634.      * If scroll increments have been set, round the window origin
  3635.      * to the nearest multiple of the increments.  Remember, the
  3636.      * origin is the place just inside the borders,  not the upper
  3637.      * left corner.
  3638.      */
  3639.  
  3640.     if (canvasPtr->xScrollIncrement > 0) {
  3641.     if (xOrigin >= 0) {
  3642.         xOrigin += canvasPtr->xScrollIncrement/2;
  3643.         xOrigin -= (xOrigin + canvasPtr->inset)
  3644.             % canvasPtr->xScrollIncrement;
  3645.     } else {
  3646.         xOrigin = (-xOrigin) + canvasPtr->xScrollIncrement/2;
  3647.         xOrigin = -(xOrigin - (xOrigin - canvasPtr->inset)
  3648.             % canvasPtr->xScrollIncrement);
  3649.     }
  3650.     }
  3651.     if (canvasPtr->yScrollIncrement > 0) {
  3652.     if (yOrigin >= 0) {
  3653.         yOrigin += canvasPtr->yScrollIncrement/2;
  3654.         yOrigin -= (yOrigin + canvasPtr->inset)
  3655.             % canvasPtr->yScrollIncrement;
  3656.     } else {
  3657.         yOrigin = (-yOrigin) + canvasPtr->yScrollIncrement/2;
  3658.         yOrigin = -(yOrigin - (yOrigin - canvasPtr->inset)
  3659.             % canvasPtr->yScrollIncrement);
  3660.     }
  3661.     }
  3662.  
  3663.     /*
  3664.      * Adjust the origin if necessary to keep as much as possible of the
  3665.      * canvas in the view.  The variables left, right, etc. keep track of
  3666.      * how much extra space there is on each side of the view before it
  3667.      * will stick out past the scroll region.  If one side sticks out past
  3668.      * the edge of the scroll region, adjust the view to bring that side
  3669.      * back to the edge of the scrollregion (but don't move it so much that
  3670.      * the other side sticks out now).  If scroll increments are in effect,
  3671.      * be sure to adjust only by full increments.
  3672.      */
  3673.  
  3674.     if ((canvasPtr->confine) && (canvasPtr->regionString != NULL)) {
  3675.     left = xOrigin + canvasPtr->inset - canvasPtr->scrollX1;
  3676.     right = canvasPtr->scrollX2
  3677.         - (xOrigin + Tk_Width(canvasPtr->tkwin) - canvasPtr->inset);
  3678.     top = yOrigin + canvasPtr->inset - canvasPtr->scrollY1;
  3679.     bottom = canvasPtr->scrollY2
  3680.         - (yOrigin + Tk_Height(canvasPtr->tkwin) - canvasPtr->inset);
  3681.     if ((left < 0) && (right > 0)) {
  3682.         delta = (right > -left) ? -left : right;
  3683.         if (canvasPtr->xScrollIncrement > 0) {
  3684.         delta -= delta % canvasPtr->xScrollIncrement;
  3685.         }
  3686.         xOrigin += delta;
  3687.     } else if ((right < 0) && (left > 0)) {
  3688.         delta = (left > -right) ? -right : left;
  3689.         if (canvasPtr->xScrollIncrement > 0) {
  3690.         delta -= delta % canvasPtr->xScrollIncrement;
  3691.         }
  3692.         xOrigin -= delta;
  3693.     }
  3694.     if ((top < 0) && (bottom > 0)) {
  3695.         delta = (bottom > -top) ? -top : bottom;
  3696.         if (canvasPtr->yScrollIncrement > 0) {
  3697.         delta -= delta % canvasPtr->yScrollIncrement;
  3698.         }
  3699.         yOrigin += delta;
  3700.     } else if ((bottom < 0) && (top > 0)) {
  3701.         delta = (top > -bottom) ? -bottom : top;
  3702.         if (canvasPtr->yScrollIncrement > 0) {
  3703.         delta -= delta % canvasPtr->yScrollIncrement;
  3704.         }
  3705.         yOrigin -= delta;
  3706.     }
  3707.     }
  3708.  
  3709.     if ((xOrigin == canvasPtr->xOrigin) && (yOrigin == canvasPtr->yOrigin)) {
  3710.     return;
  3711.     }
  3712.  
  3713.     /*
  3714.      * Tricky point: must redisplay not only everything that's visible
  3715.      * in the window's final configuration, but also everything that was
  3716.      * visible in the initial configuration.  This is needed because some
  3717.      * item types, like windows, need to know when they move off-screen
  3718.      * so they can explicitly undisplay themselves.
  3719.      */
  3720.  
  3721.     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  3722.         canvasPtr->xOrigin, canvasPtr->yOrigin,
  3723.         canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
  3724.         canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
  3725.     canvasPtr->xOrigin = xOrigin;
  3726.     canvasPtr->yOrigin = yOrigin;
  3727.     canvasPtr->flags |= UPDATE_SCROLLBARS;
  3728.     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  3729.         canvasPtr->xOrigin, canvasPtr->yOrigin,
  3730.         canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
  3731.         canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
  3732. }
  3733.