tkCanvas.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:173k
源码类别:

通讯编程

开发平台:

Visual C++

  1.     }
  2.     searchPtr->lastPtr = lastPtr;
  3.     searchPtr->searchOver = 1;
  4.     canvasPtr->hotPtr = itemPtr;
  5.     canvasPtr->hotPrevPtr = lastPtr;
  6.     return itemPtr;
  7. }
  8.     }
  9.     searchPtr->tag = uid = Tk_GetUid(tag);
  10.     if (uid == Tk_GetUid("all")) {
  11. /*
  12.  * All items match.
  13.  */
  14. searchPtr->tag = NULL;
  15. searchPtr->lastPtr = NULL;
  16. searchPtr->currentPtr = canvasPtr->firstItemPtr;
  17. return canvasPtr->firstItemPtr;
  18.     }
  19.     /*
  20.      * None of the above.  Search for an item with a matching tag.
  21.      */
  22.     for (lastPtr = NULL, itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  23.     lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
  24. for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
  25. count > 0; tagPtr++, count--) {
  26.     if (*tagPtr == uid) {
  27. searchPtr->lastPtr = lastPtr;
  28. searchPtr->currentPtr = itemPtr;
  29. return itemPtr;
  30.     }
  31. }
  32.     }
  33.     searchPtr->lastPtr = lastPtr;
  34.     searchPtr->searchOver = 1;
  35.     return NULL;
  36. }
  37. /*
  38.  *--------------------------------------------------------------
  39.  *
  40.  * NextItem --
  41.  *
  42.  * This procedure returns successive items that match a given
  43.  * tag;  it should be called only after StartTagSearch has been
  44.  * used to begin a search.
  45.  *
  46.  * Results:
  47.  * The return value is a pointer to the next item that matches
  48.  * the tag specified to StartTagSearch, or NULL if no such
  49.  * item exists.  *SearchPtr is updated so that the next call
  50.  * to this procedure will return the next item.
  51.  *
  52.  * Side effects:
  53.  * None.
  54.  *
  55.  *--------------------------------------------------------------
  56.  */
  57. static Tk_Item *
  58. NextItem(searchPtr)
  59.     TagSearch *searchPtr; /* Record describing search in
  60.  * progress. */
  61. {
  62.     Tk_Item *itemPtr, *lastPtr;
  63.     int count;
  64.     Tk_Uid uid;
  65.     Tk_Uid *tagPtr;
  66.     /*
  67.      * Find next item in list (this may not actually be a suitable
  68.      * one to return), and return if there are no items left.
  69.      */
  70.     lastPtr = searchPtr->lastPtr;
  71.     if (lastPtr == NULL) {
  72. itemPtr = searchPtr->canvasPtr->firstItemPtr;
  73.     } else {
  74. itemPtr = lastPtr->nextPtr;
  75.     }
  76.     if ((itemPtr == NULL) || (searchPtr->searchOver)) {
  77. searchPtr->searchOver = 1;
  78. return NULL;
  79.     }
  80.     if (itemPtr != searchPtr->currentPtr) {
  81. /*
  82.  * The structure of the list has changed.  Probably the
  83.  * previously-returned item was removed from the list.
  84.  * In this case, don't advance lastPtr;  just return
  85.  * its new successor (i.e. do nothing here).
  86.  */
  87.     } else {
  88. lastPtr = itemPtr;
  89. itemPtr = lastPtr->nextPtr;
  90.     }
  91.     /*
  92.      * Handle special case of "all" search by returning next item.
  93.      */
  94.     uid = searchPtr->tag;
  95.     if (uid == NULL) {
  96. searchPtr->lastPtr = lastPtr;
  97. searchPtr->currentPtr = itemPtr;
  98. return itemPtr;
  99.     }
  100.     /*
  101.      * Look for an item with a particular tag.
  102.      */
  103.     for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
  104. for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
  105. count > 0; tagPtr++, count--) {
  106.     if (*tagPtr == uid) {
  107. searchPtr->lastPtr = lastPtr;
  108. searchPtr->currentPtr = itemPtr;
  109. return itemPtr;
  110.     }
  111. }
  112.     }
  113.     searchPtr->lastPtr = lastPtr;
  114.     searchPtr->searchOver = 1;
  115.     return NULL;
  116. }
  117. #else /* USE_OLD_TAG_SEARCH */
  118. /*
  119.  *----------------------------------------------------------------------
  120.  *
  121.  * GetStaticUids --
  122.  *
  123.  *This procedure is invoked to return a structure filled with
  124.  *the Uids used when doing tag searching. If it was never before
  125.  *called in the current thread, it initializes the structure for
  126.  *that thread (uids are only ever local to one thread [Bug
  127.  *1114977]).
  128.  *
  129.  * Results:
  130.  *None.
  131.  *
  132.  * Side effects:
  133.  *None.
  134.  *
  135.  *----------------------------------------------------------------------
  136.  */
  137. static SearchUids *
  138. GetStaticUids()
  139. {
  140.     SearchUids *searchUids = (SearchUids *)
  141.     Tcl_GetThreadData(&dataKey, sizeof(SearchUids));
  142.     if (searchUids->allUid == NULL) {
  143. searchUids->allUid       = Tk_GetUid("all");
  144. searchUids->currentUid   = Tk_GetUid("current");
  145. searchUids->andUid       = Tk_GetUid("&&");
  146. searchUids->orUid        = Tk_GetUid("||");
  147. searchUids->xorUid       = Tk_GetUid("^");
  148. searchUids->parenUid     = Tk_GetUid("(");
  149. searchUids->endparenUid  = Tk_GetUid(")");
  150. searchUids->negparenUid  = Tk_GetUid("!(");
  151. searchUids->tagvalUid    = Tk_GetUid("!!");
  152. searchUids->negtagvalUid = Tk_GetUid("!");
  153.     }
  154.     return searchUids;
  155. }
  156. /*
  157.  *--------------------------------------------------------------
  158.  *
  159.  * TagSearchExprInit --
  160.  *
  161.  *      This procedure allocates and initializes one TagSearchExpr struct.
  162.  *
  163.  * Results:
  164.  *
  165.  * Side effects:
  166.  *
  167.  *--------------------------------------------------------------
  168.  */
  169. static void
  170. TagSearchExprInit(exprPtrPtr)
  171.     TagSearchExpr **exprPtrPtr;
  172. {
  173.     TagSearchExpr* expr = *exprPtrPtr;
  174.     if (! expr) {
  175. expr = (TagSearchExpr *) ckalloc(sizeof(TagSearchExpr));
  176. expr->allocated = 0;
  177. expr->uids = NULL;
  178. expr->next = NULL;
  179.     }
  180.     expr->uid = NULL;
  181.     expr->index = 0;
  182.     expr->length = 0;
  183.     *exprPtrPtr = expr;
  184. }
  185.  
  186. /*
  187.  *--------------------------------------------------------------
  188.  *
  189.  * TagSearchExprDestroy --
  190.  *
  191.  *      This procedure destroys one TagSearchExpr structure.
  192.  *
  193.  * Results:
  194.  *
  195.  * Side effects:
  196.  *
  197.  *--------------------------------------------------------------
  198.      */
  199. static void
  200. TagSearchExprDestroy(expr)
  201.     TagSearchExpr *expr;
  202. {
  203.     if (expr) {
  204.      if (expr->uids) {
  205.          ckfree((char *)expr->uids);
  206. }
  207.         ckfree((char *)expr);
  208.     }
  209. }
  210. /*
  211.  *--------------------------------------------------------------
  212.  *
  213.  * TagSearchScan --
  214.  *
  215.  *      This procedure is called to initiate an enumeration of
  216.  *      all items in a given canvas that contain a tag that matches
  217.  *      the tagOrId expression.
  218.  *
  219.  * Results:
  220.  *      The return value indicates if the tagOrId expression
  221.  *      was successfully scanned (syntax).
  222.  *      The information at *searchPtr is initialized
  223.  *      such that a call to TagSearchFirst, followed by
  224.  *      successive calls to TagSearchNext will return items
  225.  *      that match tag.
  226.  *
  227.  * Side effects:
  228.  *      SearchPtr is linked into a list of searches in progress
  229.  *      on canvasPtr, so that elements can safely be deleted
  230.  *      while the search is in progress.
  231.  *
  232.  *--------------------------------------------------------------
  233.  */
  234. static int
  235. TagSearchScan(canvasPtr, tagObj, searchPtrPtr)
  236.     TkCanvas *canvasPtr;                /* Canvas whose items are to be
  237.                                          * searched. */
  238.     Tcl_Obj *tagObj;                    /* Object giving tag value. */
  239.     TagSearch **searchPtrPtr;           /* Record describing tag search;
  240.                                          * will be initialized here. */
  241. {
  242.     char *tag = Tcl_GetStringFromObj(tagObj,NULL);
  243.     int i;
  244.     TagSearch *searchPtr;
  245.     /*
  246.      * Initialize the search.
  247.      */
  248.     if (*searchPtrPtr) {
  249.         searchPtr = *searchPtrPtr;
  250.     } else {
  251.         /* Allocate primary search struct on first call */
  252.         *searchPtrPtr = searchPtr = (TagSearch *) ckalloc(sizeof(TagSearch));
  253. searchPtr->expr = NULL;
  254.         /* Allocate buffer for rewritten tags (after de-escaping) */
  255.         searchPtr->rewritebufferAllocated = 100;
  256.         searchPtr->rewritebuffer =
  257.             ckalloc(searchPtr->rewritebufferAllocated);
  258.     }
  259.     TagSearchExprInit(&(searchPtr->expr));
  260.     /* How long is the tagOrId ? */
  261.     searchPtr->stringLength = strlen(tag);
  262.     /* Make sure there is enough buffer to hold rewritten tags */
  263.     if ((unsigned int)searchPtr->stringLength >=
  264.     searchPtr->rewritebufferAllocated) {
  265.         searchPtr->rewritebufferAllocated = searchPtr->stringLength + 100;
  266.         searchPtr->rewritebuffer =
  267.             ckrealloc(searchPtr->rewritebuffer,
  268.     searchPtr->rewritebufferAllocated);
  269.     }
  270.     /* Initialize search */
  271.     searchPtr->canvasPtr = canvasPtr;
  272.     searchPtr->searchOver = 0;
  273.     searchPtr->type = 0;
  274.     /*
  275.      * Find the first matching item in one of several ways. If the tag
  276.      * is a number then it selects the single item with the matching
  277.      * identifier.  In this case see if the item being requested is the
  278.      * hot item, in which case the search can be skipped.
  279.      */
  280.     if (searchPtr->stringLength && isdigit(UCHAR(*tag))) {
  281.         char *end;
  282.         searchPtr->id = strtoul(tag, &end, 0);
  283.         if (*end == 0) {
  284.             searchPtr->type = 1;
  285.             return TCL_OK;
  286. }
  287.     }
  288.     /*
  289.      * For all other tags and tag expressions convert to a UID.
  290.      * This UID is kept forever, but this should be thought of
  291.      * as a cache rather than as a memory leak.
  292.      */
  293.     searchPtr->expr->uid = Tk_GetUid(tag);
  294.     /* short circuit impossible searches for null tags */
  295.     if (searchPtr->stringLength == 0) {
  296. return TCL_OK;
  297.     }
  298.     /*
  299.      * Pre-scan tag for at least one unquoted "&&" "||" "^" "!"
  300.      *   if not found then use string as simple tag
  301.      */
  302.     for (i = 0; i < searchPtr->stringLength ; i++) {
  303.         if (tag[i] == '"') {
  304.             i++;
  305.             for ( ; i < searchPtr->stringLength; i++) {
  306.                 if (tag[i] == '\') {
  307.                     i++;
  308.                     continue;
  309.                 }
  310.                 if (tag[i] == '"') {
  311.                     break;
  312.                 }
  313.             }
  314.         } else {
  315.             if ((tag[i] == '&' && tag[i+1] == '&')
  316.              || (tag[i] == '|' && tag[i+1] == '|')
  317.              || (tag[i] == '^')
  318.              || (tag[i] == '!')) {
  319.                 searchPtr->type = 4;
  320.                 break;
  321.             }
  322.         }
  323.     }
  324.     searchPtr->string = tag;
  325.     searchPtr->stringIndex = 0;
  326.     if (searchPtr->type == 4) {
  327.         /*
  328.          * an operator was found in the prescan, so
  329.          * now compile the tag expression into array of Tk_Uid
  330.          * flagging any syntax errors found
  331.          */
  332. if (TagSearchScanExpr(canvasPtr->interp, searchPtr, searchPtr->expr) != TCL_OK) {
  333.             /* Syntax error in tag expression */
  334.     /* Result message set by TagSearchScanExpr */
  335.     return TCL_ERROR;
  336. }
  337. searchPtr->expr->length = searchPtr->expr->index;
  338.     } else {
  339.         if (searchPtr->expr->uid == GetStaticUids()->allUid) {
  340.             /*
  341.              * All items match.
  342.              */
  343.             searchPtr->type = 2;
  344.         } else {
  345.             /*
  346.              * Optimized single-tag search
  347.              */
  348.             searchPtr->type = 3;
  349.         }
  350.     }
  351.     return TCL_OK;
  352. }
  353. /*
  354.  *--------------------------------------------------------------
  355.  *
  356.  * TagSearchDestroy --
  357.  *
  358.  *      This procedure destroys any dynamic structures that
  359.  *      may have been allocated by TagSearchScan.
  360.  *
  361.  * Results:
  362.  *
  363.  * Side effects:
  364.  *
  365.  *--------------------------------------------------------------
  366.  */
  367. static void
  368. TagSearchDestroy(searchPtr)
  369.     TagSearch *searchPtr;               /* Record describing tag search */
  370. {
  371.     if (searchPtr) {
  372.         TagSearchExprDestroy(searchPtr->expr);
  373.         ckfree((char *)searchPtr->rewritebuffer);
  374.         ckfree((char *)searchPtr);
  375.     }
  376. }
  377. /*
  378.  *--------------------------------------------------------------
  379.  *
  380.  * TagSearchScanExpr --
  381.  *
  382.  *      This recursive procedure is called to scan a tag expression
  383.  *      and compile it into an array of Tk_Uids.
  384.  *
  385.  * Results:
  386.  *      The return value indicates if the tagOrId expression
  387.  *      was successfully scanned (syntax).
  388.  *      The information at *searchPtr is initialized
  389.  *      such that a call to TagSearchFirst, followed by
  390.  *      successive calls to TagSearchNext will return items
  391.  *      that match tag.
  392.  *
  393.  * Side effects:
  394.  *
  395.  *--------------------------------------------------------------
  396.  */
  397. static int
  398. TagSearchScanExpr(interp, searchPtr, expr)
  399.     Tcl_Interp *interp;         /* Current interpreter. */
  400.     TagSearch *searchPtr;       /* Search data */
  401.     TagSearchExpr *expr; /* compiled expression result */
  402. {
  403.     int looking_for_tag;        /* When true, scanner expects
  404.                                  * next char(s) to be a tag,
  405.                                  * else operand expected */
  406.     int found_tag;              /* One or more tags found */
  407.     int found_endquote;         /* For quoted tag string parsing */
  408.     int negate_result;          /* Pending negation of next tag value */
  409.     char *tag;                  /* tag from tag expression string */
  410.     char c;
  411.     SearchUids *searchUids; /* Collection of uids for basic search
  412.  * expression terms. */
  413.  
  414.     searchUids = GetStaticUids();
  415.     negate_result = 0;
  416.     found_tag = 0;
  417.     looking_for_tag = 1;
  418.     while (searchPtr->stringIndex < searchPtr->stringLength) {
  419.         c = searchPtr->string[searchPtr->stringIndex++];
  420.         if (expr->allocated == expr->index) {
  421.             expr->allocated += 15;
  422.     if (expr->uids) {
  423. expr->uids =
  424.                     (Tk_Uid *) ckrealloc((char *)(expr->uids),
  425.                     (expr->allocated)*sizeof(Tk_Uid));
  426.     } else {
  427. expr->uids =
  428. (Tk_Uid *) ckalloc((expr->allocated)*sizeof(Tk_Uid));
  429.     }
  430.         }
  431.         if (looking_for_tag) {
  432.             switch (c) {
  433.                 case ' '  : /* ignore unquoted whitespace */
  434.                 case 't' :
  435.                 case 'n' :
  436.                 case 'r' :
  437.                     break;
  438.                 case '!'  : /* negate next tag or subexpr */
  439.                     if (looking_for_tag > 1) {
  440.                         Tcl_AppendResult(interp,
  441. "Too many '!' in tag search expression",
  442. (char *) NULL);
  443.                         return TCL_ERROR;
  444.                     }
  445.                     looking_for_tag++;
  446.                     negate_result = 1;
  447.                     break;
  448.                 case '('  : /* scan (negated) subexpr recursively */
  449.                     if (negate_result) {
  450.                         expr->uids[expr->index++] = searchUids->negparenUid;
  451.                         negate_result = 0;
  452.     } else {
  453.                         expr->uids[expr->index++] = searchUids->parenUid;
  454.     }
  455.                     if (TagSearchScanExpr(interp, searchPtr, expr) != TCL_OK) {
  456.                         /* Result string should be already set
  457.                          * by nested call to tag_expr_scan() */
  458. return TCL_ERROR;
  459.     }
  460.                     looking_for_tag = 0;
  461.                     found_tag = 1;
  462.                     break;
  463.                 case '"'  : /* quoted tag string */
  464.                     if (negate_result) {
  465.                         expr->uids[expr->index++] = searchUids->negtagvalUid;
  466.                         negate_result = 0;
  467.                     } else {
  468.                         expr->uids[expr->index++] = searchUids->tagvalUid;
  469.     }
  470.                     tag = searchPtr->rewritebuffer;
  471.                     found_endquote = 0;
  472.                     while (searchPtr->stringIndex < searchPtr->stringLength) {
  473.                         c = searchPtr->string[searchPtr->stringIndex++];
  474.                         if (c == '\') {
  475.                             c = searchPtr->string[searchPtr->stringIndex++];
  476. }
  477.                         if (c == '"') {
  478.                             found_endquote = 1;
  479.     break;
  480. }
  481.                         *tag++ = c;
  482.                     }
  483.                     if (! found_endquote) {
  484.                         Tcl_AppendResult(interp,
  485. "Missing endquote in tag search expression",
  486. (char *) NULL);
  487.                         return TCL_ERROR;
  488.                     }
  489.                     if (! (tag - searchPtr->rewritebuffer)) {
  490.                         Tcl_AppendResult(interp,
  491.                             "Null quoted tag string in tag search expression",
  492.                             (char *) NULL);
  493.                         return TCL_ERROR;
  494.                     }
  495.                     *tag++ = '';
  496.                     expr->uids[expr->index++] =
  497.     Tk_GetUid(searchPtr->rewritebuffer);
  498.                     looking_for_tag = 0;
  499.                     found_tag = 1;
  500.                     break;
  501.                 case '&'  : /* illegal chars when looking for tag */
  502.                 case '|'  :
  503.                 case '^'  :
  504.                 case ')'  :
  505.                     Tcl_AppendResult(interp,
  506.     "Unexpected operator in tag search expression",
  507.     (char *) NULL);
  508.                     return TCL_ERROR;
  509.                 default : /* unquoted tag string */
  510.                     if (negate_result) {
  511.                         expr->uids[expr->index++] = searchUids->negtagvalUid;
  512.                         negate_result = 0;
  513.                     } else {
  514.                         expr->uids[expr->index++] = searchUids->tagvalUid;
  515.                     }
  516.                     tag = searchPtr->rewritebuffer;
  517.                     *tag++ = c;
  518.                     /* copy rest of tag, including any embedded whitespace */
  519.                     while (searchPtr->stringIndex < searchPtr->stringLength) {
  520.                         c = searchPtr->string[searchPtr->stringIndex];
  521.                         if (c == '!' || c == '&' || c == '|' || c == '^'
  522. || c == '(' || c == ')' || c == '"') {
  523.     break;
  524.                         }
  525.                         *tag++ = c;
  526.                         searchPtr->stringIndex++;
  527.                     }
  528.                     /* remove trailing whitespace */
  529.                     while (1) {
  530.                         c = *--tag;
  531.                         /* there must have been one non-whitespace char,
  532.                          *  so this will terminate */
  533.                         if (c != ' ' && c != 't' && c != 'n' && c != 'r') {
  534.                             break;
  535. }
  536.                     }
  537.                     *++tag = '';
  538.                     expr->uids[expr->index++] =
  539.     Tk_GetUid(searchPtr->rewritebuffer);
  540.                     looking_for_tag = 0;
  541.                     found_tag = 1;
  542.             }
  543.         } else {    /* ! looking_for_tag */
  544.             switch (c) {
  545.                 case ' '  : /* ignore whitespace */
  546.                 case 't' :
  547.                 case 'n' :
  548.                 case 'r' :
  549.                     break;
  550.                 case '&'  : /* AND operator */
  551.                     c = searchPtr->string[searchPtr->stringIndex++];
  552.                     if (c != '&') {
  553.                         Tcl_AppendResult(interp,
  554.                                 "Singleton '&' in tag search expression",
  555.                                 (char *) NULL);
  556.                         return TCL_ERROR;
  557.                     }
  558.                     expr->uids[expr->index++] = searchUids->andUid;
  559.                     looking_for_tag = 1;
  560.                     break;
  561.                 case '|'  : /* OR operator */
  562.                     c = searchPtr->string[searchPtr->stringIndex++];
  563.                     if (c != '|') {
  564.                         Tcl_AppendResult(interp,
  565.                                 "Singleton '|' in tag search expression",
  566.                                 (char *) NULL);
  567.                         return TCL_ERROR;
  568.                     }
  569.                     expr->uids[expr->index++] = searchUids->orUid;
  570.                     looking_for_tag = 1;
  571.                     break;
  572.                 case '^'  : /* XOR operator */
  573.                     expr->uids[expr->index++] = searchUids->xorUid;
  574.                     looking_for_tag = 1;
  575.                     break;
  576.                 case ')'  : /* end subexpression */
  577.                     expr->uids[expr->index++] = searchUids->endparenUid;
  578.                     goto breakwhile;
  579.                 default   : /* syntax error */
  580.                     Tcl_AppendResult(interp,
  581.     "Invalid boolean operator in tag search expression",
  582.     (char *) NULL);
  583.                     return TCL_ERROR;
  584.             }
  585.         }
  586.     }
  587.     breakwhile:
  588.     if (found_tag && ! looking_for_tag) {
  589.         return TCL_OK;
  590.     }
  591.     Tcl_AppendResult(interp, "Missing tag in tag search expression",
  592.     (char *) NULL);
  593.     return TCL_ERROR;
  594. }
  595. /*
  596.  *--------------------------------------------------------------
  597.  *
  598.  * TagSearchEvalExpr --
  599.  *
  600.  *      This recursive procedure is called to eval a tag expression.
  601.  *
  602.  * Results:
  603.  *      The return value indicates if the tagOrId expression
  604.  *      successfully matched the tags of the current item.
  605.  *
  606.  * Side effects:
  607.  *
  608.  *--------------------------------------------------------------
  609.  */
  610. static int
  611. TagSearchEvalExpr(expr, itemPtr)
  612.     TagSearchExpr *expr;        /* Search expression */
  613.     Tk_Item *itemPtr;           /* Item being test for match */
  614. {
  615.     int looking_for_tag;        /* When true, scanner expects
  616.                                  * next char(s) to be a tag,
  617.                                  * else operand expected */
  618.     int negate_result;          /* Pending negation of next tag value */
  619.     Tk_Uid uid;
  620.     Tk_Uid *tagPtr;
  621.     int count;
  622.     int result;                 /* Value of expr so far */
  623.     int parendepth;
  624.     SearchUids *searchUids; /* Collection of uids for basic search
  625.  * expression terms. */
  626.     searchUids = GetStaticUids();
  627.     result = 0;  /* just to keep the compiler quiet */
  628.     negate_result = 0;
  629.     looking_for_tag = 1;
  630.     while (expr->index < expr->length) {
  631.         uid = expr->uids[expr->index++];
  632.         if (looking_for_tag) {
  633.             if (uid == searchUids->tagvalUid) {
  634. /*
  635.  *              assert(expr->index < expr->length);
  636.  */
  637.                 uid = expr->uids[expr->index++];
  638.                 result = 0;
  639.                 /*
  640.                  * set result 1 if tag is found in item's tags
  641.                  */
  642.                 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
  643.                     count > 0; tagPtr++, count--) {
  644.                     if (*tagPtr == uid) {
  645.                         result = 1;
  646.                         break;
  647.                     }
  648.                 }
  649.             } else if (uid == searchUids->negtagvalUid) {
  650.                 negate_result = ! negate_result;
  651. /*
  652.  *              assert(expr->index < expr->length);
  653.  */
  654.                 uid = expr->uids[expr->index++];
  655.                 result = 0;
  656.                 /*
  657.                  * set result 1 if tag is found in item's tags
  658.                  */
  659.                 for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
  660.                     count > 0; tagPtr++, count--) {
  661.                     if (*tagPtr == uid) {
  662.                         result = 1;
  663.                         break;
  664.                     }
  665.                 }
  666.             } else if (uid == searchUids->parenUid) {
  667.                 /*
  668.                  * evaluate subexpressions with recursion
  669.                  */
  670.                 result = TagSearchEvalExpr(expr, itemPtr);
  671.             } else if (uid == searchUids->negparenUid) {
  672.                 negate_result = ! negate_result;
  673.                 /*
  674.                  * evaluate subexpressions with recursion
  675.                  */
  676.                 result = TagSearchEvalExpr(expr, itemPtr);
  677. /*
  678.  *          } else {
  679.  *              assert(0);
  680.  */
  681.             }
  682.             if (negate_result) {
  683.                 result = ! result;
  684.                 negate_result = 0;
  685.             }
  686.             looking_for_tag = 0;
  687.         } else {    /* ! looking_for_tag */
  688.             if (((uid == searchUids->andUid) && (!result)) ||
  689.     ((uid == searchUids->orUid) && result)) {
  690.                 /*
  691.                  * short circuit expression evaluation
  692.                  *
  693.                  * if result before && is 0, or result before || is 1,
  694.                  *   then the expression is decided and no further
  695.                  *   evaluation is needed.
  696.                  */
  697.                     parendepth = 0;
  698. while (expr->index < expr->length) {
  699.     uid = expr->uids[expr->index++];
  700.     if (uid == searchUids->tagvalUid ||
  701.     uid == searchUids->negtagvalUid) {
  702. expr->index++;
  703. continue;
  704.     }
  705.     if (uid == searchUids->parenUid ||
  706.     uid == searchUids->negparenUid) {
  707. parendepth++;
  708. continue;
  709.     } 
  710.     if (uid == searchUids->endparenUid) {
  711. parendepth--;
  712. if (parendepth < 0) {
  713.     break;
  714. }
  715.     }
  716. }
  717.                 return result;
  718.             } else if (uid == searchUids->xorUid) {
  719.                 /*
  720.                  * if the previous result was 1
  721.                  *   then negate the next result
  722.                  */
  723.                 negate_result = result;
  724.             } else if (uid == searchUids->endparenUid) {
  725.                 return result;
  726. /*
  727.  *          } else {
  728.  *               assert(0);
  729.  */
  730.             }
  731.             looking_for_tag = 1;
  732.         }
  733.     }
  734. /*
  735.  *  assert(! looking_for_tag);
  736.  */
  737.     return result;
  738. }
  739. /*
  740.  *--------------------------------------------------------------
  741.  *
  742.  * TagSearchFirst --
  743.  *
  744.  *      This procedure is called to get the first item
  745.  *      item that matches a preestablished search predicate
  746.  *      that was set by TagSearchScan.
  747.  *
  748.  * Results:
  749.  *      The return value is a pointer to the first item, or NULL
  750.  *      if there is no such item.  The information at *searchPtr
  751.  *      is updated such that successive calls to TagSearchNext
  752.  *      will return successive items.
  753.  *
  754.  * Side effects:
  755.  *      SearchPtr is linked into a list of searches in progress
  756.  *      on canvasPtr, so that elements can safely be deleted
  757.  *      while the search is in progress.
  758.  *
  759.  *--------------------------------------------------------------
  760.  */
  761. static Tk_Item *
  762. TagSearchFirst(searchPtr)
  763.     TagSearch *searchPtr;               /* Record describing tag search */
  764. {
  765.     Tk_Item *itemPtr, *lastPtr;
  766.     Tk_Uid uid, *tagPtr;
  767.     int count;
  768.     /* short circuit impossible searches for null tags */
  769.     if (searchPtr->stringLength == 0) {
  770.         return NULL;
  771.     }
  772.     /*
  773.      * Find the first matching item in one of several ways. If the tag
  774.      * is a number then it selects the single item with the matching
  775.      * identifier.  In this case see if the item being requested is the
  776.      * hot item, in which case the search can be skipped.
  777.      */
  778.     if (searchPtr->type == 1) {
  779.         Tcl_HashEntry *entryPtr;
  780.         itemPtr = searchPtr->canvasPtr->hotPtr;
  781.         lastPtr = searchPtr->canvasPtr->hotPrevPtr;
  782.         if ((itemPtr == NULL) || (itemPtr->id != searchPtr->id) ||
  783. (lastPtr == NULL) || (lastPtr->nextPtr != itemPtr)) {
  784.             entryPtr = Tcl_FindHashEntry(&searchPtr->canvasPtr->idTable,
  785.     (char *) searchPtr->id);
  786.             if (entryPtr != NULL) {
  787.                 itemPtr = (Tk_Item *)Tcl_GetHashValue(entryPtr);
  788.                 lastPtr = itemPtr->prevPtr;
  789.             } else {
  790.                 lastPtr = itemPtr = NULL;
  791.             }
  792.         }
  793.         searchPtr->lastPtr = lastPtr;
  794.         searchPtr->searchOver = 1;
  795.         searchPtr->canvasPtr->hotPtr = itemPtr;
  796.         searchPtr->canvasPtr->hotPrevPtr = lastPtr;
  797.         return itemPtr;
  798.     }
  799.     if (searchPtr->type == 2) {
  800.         /*
  801.          * All items match.
  802.          */
  803.         searchPtr->lastPtr = NULL;
  804.         searchPtr->currentPtr = searchPtr->canvasPtr->firstItemPtr;
  805.         return searchPtr->canvasPtr->firstItemPtr;
  806.     }
  807.     if (searchPtr->type == 3) {
  808.         /*
  809.          * Optimized single-tag search
  810.          */
  811.         uid = searchPtr->expr->uid;
  812.         for (lastPtr = NULL, itemPtr = searchPtr->canvasPtr->firstItemPtr;
  813.                 itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
  814.             for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
  815.                     count > 0; tagPtr++, count--) {
  816.                 if (*tagPtr == uid) {
  817.                     searchPtr->lastPtr = lastPtr;
  818.                     searchPtr->currentPtr = itemPtr;
  819.                     return itemPtr;
  820.                 }
  821.             }
  822.         }
  823.     } else {
  824. /*
  825.  * None of the above.  Search for an item matching the tag expression.
  826.  */
  827. for (lastPtr = NULL, itemPtr = searchPtr->canvasPtr->firstItemPtr;
  828. itemPtr != NULL; lastPtr=itemPtr, itemPtr=itemPtr->nextPtr) {
  829.     searchPtr->expr->index = 0;
  830.     if (TagSearchEvalExpr(searchPtr->expr, itemPtr)) {
  831. searchPtr->lastPtr = lastPtr;
  832. searchPtr->currentPtr = itemPtr;
  833. return itemPtr;
  834.     }
  835. }
  836.     }
  837.     searchPtr->lastPtr = lastPtr;
  838.     searchPtr->searchOver = 1;
  839.     return NULL;
  840. }
  841. /*
  842.  *--------------------------------------------------------------
  843.  *
  844.  * TagSearchNext --
  845.  *
  846.  *      This procedure returns successive items that match a given
  847.  *      tag;  it should be called only after TagSearchFirst has been
  848.  *      used to begin a search.
  849.  *
  850.  * Results:
  851.  *      The return value is a pointer to the next item that matches
  852.  *      the tag expr specified to TagSearchScan, or NULL if no such
  853.  *      item exists.  *SearchPtr is updated so that the next call
  854.  *      to this procedure will return the next item.
  855.  *
  856.  * Side effects:
  857.  *      None.
  858.  *
  859.  *--------------------------------------------------------------
  860.  */
  861. static Tk_Item *
  862. TagSearchNext(searchPtr)
  863.     TagSearch *searchPtr;               /* Record describing search in
  864.                                          * progress. */
  865. {
  866.     Tk_Item *itemPtr, *lastPtr;
  867.     Tk_Uid uid, *tagPtr;
  868.     int count;
  869.     /*
  870.      * Find next item in list (this may not actually be a suitable
  871.      * one to return), and return if there are no items left.
  872.      */
  873.     lastPtr = searchPtr->lastPtr;
  874.     if (lastPtr == NULL) {
  875.         itemPtr = searchPtr->canvasPtr->firstItemPtr;
  876.     } else {
  877.         itemPtr = lastPtr->nextPtr;
  878.     }
  879.     if ((itemPtr == NULL) || (searchPtr->searchOver)) {
  880.         searchPtr->searchOver = 1;
  881.         return NULL;
  882.     }
  883.     if (itemPtr != searchPtr->currentPtr) {
  884.         /*
  885.          * The structure of the list has changed.  Probably the
  886.          * previously-returned item was removed from the list.
  887.          * In this case, don't advance lastPtr;  just return
  888.          * its new successor (i.e. do nothing here).
  889.          */
  890.     } else {
  891.         lastPtr = itemPtr;
  892.         itemPtr = lastPtr->nextPtr;
  893.     }
  894.     if (searchPtr->type == 2) {
  895.         /*
  896.          * All items match.
  897.          */
  898.         searchPtr->lastPtr = lastPtr;
  899.         searchPtr->currentPtr = itemPtr;
  900.         return itemPtr;
  901.     }
  902.     if (searchPtr->type == 3) {
  903.         /*
  904.          * Optimized single-tag search
  905.          */
  906.         uid = searchPtr->expr->uid;
  907.         for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
  908.             for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
  909.                     count > 0; tagPtr++, count--) {
  910.                 if (*tagPtr == uid) {
  911.                     searchPtr->lastPtr = lastPtr;
  912.                     searchPtr->currentPtr = itemPtr;
  913.                     return itemPtr;
  914.                 }
  915.             }
  916.         }
  917.         searchPtr->lastPtr = lastPtr;
  918.         searchPtr->searchOver = 1;
  919.         return NULL;
  920.     }
  921.     /*
  922.      * Else.... evaluate tag expression
  923.      */
  924.     for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
  925.         searchPtr->expr->index = 0;
  926.         if (TagSearchEvalExpr(searchPtr->expr, itemPtr)) {
  927.             searchPtr->lastPtr = lastPtr;
  928.             searchPtr->currentPtr = itemPtr;
  929.             return itemPtr;
  930.         }
  931.     }
  932.     searchPtr->lastPtr = lastPtr;
  933.     searchPtr->searchOver = 1;
  934.     return NULL;
  935. }
  936. #endif /* USE_OLD_TAG_SEARCH */
  937. /*
  938.  *--------------------------------------------------------------
  939.  *
  940.  * DoItem --
  941.  *
  942.  * This is a utility procedure called by FindItems.  It
  943.  * either adds itemPtr's id to the result forming in interp,
  944.  * or it adds a new tag to itemPtr, depending on the value
  945.  * of tag.
  946.  *
  947.  * Results:
  948.  * None.
  949.  *
  950.  * Side effects:
  951.  * If tag is NULL then itemPtr's id is added as a list element
  952.  * to the interp's result;  otherwise tag is added to itemPtr's
  953.  * list of tags.
  954.  *
  955.  *--------------------------------------------------------------
  956.  */
  957. static void
  958. DoItem(interp, itemPtr, tag)
  959.     Tcl_Interp *interp; /* Interpreter in which to (possibly)
  960.  * record item id. */
  961.     Tk_Item *itemPtr; /* Item to (possibly) modify. */
  962.     Tk_Uid tag; /* Tag to add to those already
  963.  * present for item, or NULL. */
  964. {
  965.     Tk_Uid *tagPtr;
  966.     int count;
  967.     /*
  968.      * Handle the "add-to-result" case and return, if appropriate.
  969.      */
  970.     if (tag == NULL) {
  971. char msg[TCL_INTEGER_SPACE];
  972. sprintf(msg, "%d", itemPtr->id);
  973. Tcl_AppendElement(interp, msg);
  974. return;
  975.     }
  976.     for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
  977.     count > 0; tagPtr++, count--) {
  978. if (tag == *tagPtr) {
  979.     return;
  980. }
  981.     }
  982.     /*
  983.      * Grow the tag space if there's no more room left in the current
  984.      * block.
  985.      */
  986.     if (itemPtr->tagSpace == itemPtr->numTags) {
  987. Tk_Uid *newTagPtr;
  988. itemPtr->tagSpace += 5;
  989. newTagPtr = (Tk_Uid *) ckalloc((unsigned)
  990. (itemPtr->tagSpace * sizeof(Tk_Uid)));
  991. memcpy((VOID *) newTagPtr, (VOID *) itemPtr->tagPtr,
  992. (itemPtr->numTags * sizeof(Tk_Uid)));
  993. if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
  994.     ckfree((char *) itemPtr->tagPtr);
  995. }
  996. itemPtr->tagPtr = newTagPtr;
  997. tagPtr = &itemPtr->tagPtr[itemPtr->numTags];
  998.     }
  999.     /*
  1000.      * Add in the new tag.
  1001.      */
  1002.     *tagPtr = tag;
  1003.     itemPtr->numTags++;
  1004. }
  1005. /*
  1006.  *--------------------------------------------------------------
  1007.  *
  1008.  * FindItems --
  1009.  *
  1010.  * This procedure does all the work of implementing the
  1011.  * "find" and "addtag" options of the canvas widget command,
  1012.  * which locate items that have certain features (location,
  1013.  * tags, position in display list, etc.).
  1014.  *
  1015.  * Results:
  1016.  * A standard Tcl return value.  If newTag is NULL, then a
  1017.  * list of ids from all the items that match argc/argv is
  1018.  * returned in the interp's result.  If newTag is NULL, then
  1019.  * the normal the interp's result is an empty string.  If an error
  1020.  * occurs, then the interp's result will hold an error message.
  1021.  *
  1022.  * Side effects:
  1023.  * If newTag is non-NULL, then all the items that match the
  1024.  * information in argc/argv have that tag added to their
  1025.  * lists of tags.
  1026.  *
  1027.  *--------------------------------------------------------------
  1028.  */
  1029. static int
  1030. #ifdef USE_OLD_TAG_SEARCH
  1031. FindItems(interp, canvasPtr, argc, argv, newTag, first)
  1032. #else /* USE_OLD_TAG_SEARCH */
  1033. FindItems(interp, canvasPtr, argc, argv, newTag, first, searchPtrPtr)
  1034. #endif /* USE_OLD_TAG_SEARCH */
  1035.     Tcl_Interp *interp; /* Interpreter for error reporting. */
  1036.     TkCanvas *canvasPtr; /* Canvas whose items are to be
  1037.  * searched. */
  1038.     int argc; /* Number of entries in argv.  Must be
  1039.  * greater than zero. */
  1040.     Tcl_Obj *CONST *argv; /* Arguments that describe what items
  1041.  * to search for (see user doc on
  1042.  * "find" and "addtag" options). */
  1043.     Tcl_Obj *newTag; /* If non-NULL, gives new tag to set
  1044.  * on all found items;  if NULL, then
  1045.  * ids of found items are returned
  1046.  * in the interp's result. */
  1047.     int first; /* For error messages:  gives number
  1048.  * of elements of argv which are already
  1049.  * handled. */
  1050. #ifndef USE_OLD_TAG_SEARCH
  1051.     TagSearch **searchPtrPtr;           /* From CanvasWidgetCmd local vars*/
  1052. #endif /* not USE_OLD_TAG_SEARCH */
  1053. {
  1054. #ifdef USE_OLD_TAG_SEARCH
  1055.     TagSearch search;
  1056. #endif /* USE_OLD_TAG_SEARCH */
  1057.     Tk_Item *itemPtr;
  1058.     Tk_Uid uid;
  1059.     int index;
  1060.     static CONST char *optionStrings[] = {
  1061. "above", "all", "below", "closest",
  1062. "enclosed", "overlapping", "withtag", NULL
  1063.     };
  1064.     enum options {
  1065. CANV_ABOVE, CANV_ALL, CANV_BELOW, CANV_CLOSEST,
  1066. CANV_ENCLOSED, CANV_OVERLAPPING, CANV_WITHTAG
  1067.     };
  1068.     if (newTag != NULL) {
  1069. uid = Tk_GetUid(Tcl_GetStringFromObj(newTag, NULL));
  1070.     } else {
  1071. uid = NULL;
  1072.     }
  1073.     if (Tcl_GetIndexFromObj(interp, argv[first], optionStrings, "search command", 0,
  1074.     &index) != TCL_OK) {
  1075. return TCL_ERROR;
  1076.     }
  1077.     switch ((enum options) index) {
  1078.       case CANV_ABOVE: {
  1079. Tk_Item *lastPtr = NULL;
  1080. if (argc != first+2) {
  1081.     Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId");
  1082.     return TCL_ERROR;
  1083. }
  1084. #ifdef USE_OLD_TAG_SEARCH
  1085. for (itemPtr = StartTagSearch(canvasPtr, argv[first+1], &search);
  1086. itemPtr != NULL; itemPtr = NextItem(&search)) {
  1087. #else /* USE_OLD_TAG_SEARCH */
  1088.         if (TagSearchScan(canvasPtr, argv[first+1], searchPtrPtr) != TCL_OK) {
  1089.             return TCL_ERROR;
  1090.         }
  1091.         for (itemPtr = TagSearchFirst(*searchPtrPtr);
  1092.                 itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) {
  1093. #endif /* USE_OLD_TAG_SEARCH */
  1094.     lastPtr = itemPtr;
  1095. }
  1096. if ((lastPtr != NULL) && (lastPtr->nextPtr != NULL)) {
  1097.     DoItem(interp, lastPtr->nextPtr, uid);
  1098. }
  1099. break;
  1100.       }
  1101.       case CANV_ALL: {
  1102. if (argc != first+1) {
  1103.     Tcl_WrongNumArgs(interp, first+1, argv, (char *) NULL);
  1104.     return TCL_ERROR;
  1105. }
  1106. for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  1107. itemPtr = itemPtr->nextPtr) {
  1108.     DoItem(interp, itemPtr, uid);
  1109. }
  1110. break;
  1111.       }
  1112.       case CANV_BELOW: {
  1113. Tk_Item *itemPtr;
  1114. if (argc != first+2) {
  1115.     Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId");
  1116.     return TCL_ERROR;
  1117. }
  1118. #ifdef USE_OLD_TAG_SEARCH
  1119. itemPtr = StartTagSearch(canvasPtr, argv[first+1], &search);
  1120. #else /* USE_OLD_TAG_SEARCH */
  1121.         if (TagSearchScan(canvasPtr, argv[first+1], searchPtrPtr) != TCL_OK) {
  1122.             return TCL_ERROR;
  1123.         }
  1124.         itemPtr = TagSearchFirst(*searchPtrPtr);
  1125. #endif /* USE_OLD_TAG_SEARCH */
  1126. if (itemPtr != NULL) {
  1127.     if (itemPtr->prevPtr != NULL) {
  1128. DoItem(interp, itemPtr->prevPtr, uid);
  1129.     }
  1130. }
  1131. break;
  1132.       }
  1133.       case CANV_CLOSEST: {
  1134. double closestDist;
  1135. Tk_Item *startPtr, *closestPtr;
  1136. double coords[2], halo;
  1137. int x1, y1, x2, y2;
  1138. if ((argc < first+3) || (argc > first+5)) {
  1139.     Tcl_WrongNumArgs(interp, first+1, argv, "x y ?halo? ?start?");
  1140.     return TCL_ERROR;
  1141. }
  1142. if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[first+1],
  1143. &coords[0]) != TCL_OK) || (Tk_CanvasGetCoordFromObj(interp,
  1144. (Tk_Canvas) canvasPtr, argv[first+2], &coords[1]) != TCL_OK)) {
  1145.     return TCL_ERROR;
  1146. }
  1147. if (argc > first+3) {
  1148.     if (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[first+3],
  1149.     &halo) != TCL_OK) {
  1150. return TCL_ERROR;
  1151.     }
  1152.     if (halo < 0.0) {
  1153. Tcl_AppendResult(interp, "can't have negative halo value "",
  1154. Tcl_GetString(argv[3]), """, (char *) NULL);
  1155. return TCL_ERROR;
  1156.     }
  1157. } else {
  1158.     halo = 0.0;
  1159. }
  1160. /*
  1161.  * Find the item at which to start the search.
  1162.  */
  1163. startPtr = canvasPtr->firstItemPtr;
  1164. if (argc == first+5) {
  1165. #ifdef USE_OLD_TAG_SEARCH
  1166.     itemPtr = StartTagSearch(canvasPtr, argv[first+4], &search);
  1167. #else /* USE_OLD_TAG_SEARCH */
  1168.             if (TagSearchScan(canvasPtr, argv[first+4], searchPtrPtr) != TCL_OK) {
  1169.                 return TCL_ERROR;
  1170.             }
  1171.             itemPtr = TagSearchFirst(*searchPtrPtr);
  1172. #endif /* USE_OLD_TAG_SEARCH */
  1173.     if (itemPtr != NULL) {
  1174. startPtr = itemPtr;
  1175.     }
  1176. }
  1177. /*
  1178.  * The code below is optimized so that it can eliminate most
  1179.  * items without having to call their item-specific procedures.
  1180.  * This is done by keeping a bounding box (x1, y1, x2, y2) that
  1181.  * an item's bbox must overlap if the item is to have any
  1182.  * chance of being closer than the closest so far.
  1183.  */
  1184. itemPtr = startPtr;
  1185. while(itemPtr && (itemPtr->state == TK_STATE_HIDDEN ||
  1186.     (itemPtr->state == TK_STATE_NULL && canvasPtr->canvas_state == TK_STATE_HIDDEN))) {
  1187.     itemPtr = itemPtr->nextPtr;
  1188. }
  1189. if (itemPtr == NULL) {
  1190.     return TCL_OK;
  1191. }
  1192. closestDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
  1193. itemPtr, coords) - halo;
  1194. if (closestDist < 0.0) {
  1195.     closestDist = 0.0;
  1196. }
  1197. while (1) {
  1198.     double newDist;
  1199.     /*
  1200.      * Update the bounding box using itemPtr, which is the
  1201.      * new closest item.
  1202.      */
  1203.     x1 = (int) (coords[0] - closestDist - halo - 1);
  1204.     y1 = (int) (coords[1] - closestDist - halo - 1);
  1205.     x2 = (int) (coords[0] + closestDist + halo + 1);
  1206.     y2 = (int) (coords[1] + closestDist + halo + 1);
  1207.     closestPtr = itemPtr;
  1208.     /*
  1209.      * Search for an item that beats the current closest one.
  1210.      * Work circularly through the canvas's item list until
  1211.      * getting back to the starting item.
  1212.      */
  1213.     while (1) {
  1214. itemPtr = itemPtr->nextPtr;
  1215. if (itemPtr == NULL) {
  1216.     itemPtr = canvasPtr->firstItemPtr;
  1217. }
  1218. if (itemPtr == startPtr) {
  1219.     DoItem(interp, closestPtr, uid);
  1220.     return TCL_OK;
  1221. }
  1222. if (itemPtr->state == TK_STATE_HIDDEN || (itemPtr->state == TK_STATE_NULL &&
  1223. canvasPtr->canvas_state == TK_STATE_HIDDEN)) {
  1224.     continue;
  1225. }
  1226. if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
  1227. || (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
  1228.     continue;
  1229. }
  1230. newDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
  1231. itemPtr, coords) - halo;
  1232. if (newDist < 0.0) {
  1233.     newDist = 0.0;
  1234. }
  1235. if (newDist <= closestDist) {
  1236.     closestDist = newDist;
  1237.     break;
  1238. }
  1239.     }
  1240. }
  1241. break;
  1242.       }
  1243.       case CANV_ENCLOSED: {
  1244. if (argc != first+5) {
  1245.     Tcl_WrongNumArgs(interp, first+1, argv, "x1 y1 x2 y2");
  1246.     return TCL_ERROR;
  1247. }
  1248. return FindArea(interp, canvasPtr, argv+first+1, uid, 1);
  1249.       }
  1250.       case CANV_OVERLAPPING: {
  1251. if (argc != first+5) {
  1252.     Tcl_WrongNumArgs(interp, first+1, argv, "x1 y1 x2 y2");
  1253.     return TCL_ERROR;
  1254. }
  1255. return FindArea(interp, canvasPtr, argv+first+1, uid, 0);
  1256.       }
  1257.       case CANV_WITHTAG: {
  1258. if (argc != first+2) {
  1259.     Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId");
  1260.     return TCL_ERROR;
  1261. }
  1262. #ifdef USE_OLD_TAG_SEARCH
  1263. for (itemPtr = StartTagSearch(canvasPtr, argv[first+1], &search);
  1264. itemPtr != NULL; itemPtr = NextItem(&search)) {
  1265. #else /* USE_OLD_TAG_SEARCH */
  1266.         if (TagSearchScan(canvasPtr, argv[first+1], searchPtrPtr) != TCL_OK) {
  1267.             return TCL_ERROR;
  1268.         }
  1269.         for (itemPtr = TagSearchFirst(*searchPtrPtr);
  1270.                 itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) {
  1271. #endif /* USE_OLD_TAG_SEARCH */
  1272.     DoItem(interp, itemPtr, uid);
  1273. }
  1274.       }
  1275.     }
  1276.     return TCL_OK;
  1277. }
  1278. /*
  1279.  *--------------------------------------------------------------
  1280.  *
  1281.  * FindArea --
  1282.  *
  1283.  * This procedure implements area searches for the "find"
  1284.  * and "addtag" options.
  1285.  *
  1286.  * Results:
  1287.  * A standard Tcl return value.  If newTag is NULL, then a
  1288.  * list of ids from all the items overlapping or enclosed
  1289.  * by the rectangle given by argc is returned in the interp's result.
  1290.  * If newTag is NULL, then the normal the interp's result is an
  1291.  * empty string.  If an error occurs, then the interp's result will
  1292.  * hold an error message.
  1293.  *
  1294.  * Side effects:
  1295.  * If uid is non-NULL, then all the items overlapping
  1296.  * or enclosed by the area in argv have that tag added to
  1297.  * their lists of tags.
  1298.  *
  1299.  *--------------------------------------------------------------
  1300.  */
  1301. static int
  1302. FindArea(interp, canvasPtr, argv, uid, enclosed)
  1303.     Tcl_Interp *interp; /* Interpreter for error reporting
  1304.  * and result storing. */
  1305.     TkCanvas *canvasPtr; /* Canvas whose items are to be
  1306.  * searched. */
  1307.     Tcl_Obj *CONST *argv; /* Array of four arguments that
  1308.  * give the coordinates of the
  1309.  * rectangular area to search. */
  1310.     Tk_Uid uid; /* If non-NULL, gives new tag to set
  1311.  * on all found items;  if NULL, then
  1312.  * ids of found items are returned
  1313.  * in the interp's result. */
  1314.     int enclosed; /* 0 means overlapping or enclosed
  1315.  * items are OK, 1 means only enclosed
  1316.  * items are OK. */
  1317. {
  1318.     double rect[4], tmp;
  1319.     int x1, y1, x2, y2;
  1320.     Tk_Item *itemPtr;
  1321.     if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[0],
  1322. &rect[0]) != TCL_OK)
  1323.     || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[1],
  1324. &rect[1]) != TCL_OK)
  1325.     || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[2],
  1326. &rect[2]) != TCL_OK)
  1327.     || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[3],
  1328. &rect[3]) != TCL_OK)) {
  1329. return TCL_ERROR;
  1330.     }
  1331.     if (rect[0] > rect[2]) {
  1332. tmp = rect[0]; rect[0] = rect[2]; rect[2] = tmp;
  1333.     }
  1334.     if (rect[1] > rect[3]) {
  1335. tmp = rect[1]; rect[1] = rect[3]; rect[3] = tmp;
  1336.     }
  1337.     /*
  1338.      * Use an integer bounding box for a quick test, to avoid
  1339.      * calling item-specific code except for items that are close.
  1340.      */
  1341.     x1 = (int) (rect[0]-1.0);
  1342.     y1 = (int) (rect[1]-1.0);
  1343.     x2 = (int) (rect[2]+1.0);
  1344.     y2 = (int) (rect[3]+1.0);
  1345.     for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  1346.     itemPtr = itemPtr->nextPtr) {
  1347. if (itemPtr->state == TK_STATE_HIDDEN || (itemPtr->state == TK_STATE_NULL &&
  1348. canvasPtr->canvas_state == TK_STATE_HIDDEN)) {
  1349.     continue;
  1350. }
  1351. if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
  1352. || (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
  1353.     continue;
  1354. }
  1355. if ((*itemPtr->typePtr->areaProc)((Tk_Canvas) canvasPtr, itemPtr, rect)
  1356. >= enclosed) {
  1357.     DoItem(interp, itemPtr, uid);
  1358. }
  1359.     }
  1360.     return TCL_OK;
  1361. }
  1362. /*
  1363.  *--------------------------------------------------------------
  1364.  *
  1365.  * RelinkItems --
  1366.  *
  1367.  * Move one or more items to a different place in the
  1368.  * display order for a canvas.
  1369.  *
  1370.  * Results:
  1371.  * None.
  1372.  *
  1373.  * Side effects:
  1374.  * The items identified by "tag" are moved so that they
  1375.  * are all together in the display list and immediately
  1376.  * after prevPtr.  The order of the moved items relative
  1377.  * to each other is not changed.
  1378.  *
  1379.  *--------------------------------------------------------------
  1380.  */
  1381. #ifdef USE_OLD_TAG_SEARCH
  1382. static void
  1383. RelinkItems(canvasPtr, tag, prevPtr)
  1384. #else /* USE_OLD_TAG_SEARCH */
  1385. static int
  1386. RelinkItems(canvasPtr, tag, prevPtr, searchPtrPtr)
  1387. #endif /* USE_OLD_TAG_SEARCH */
  1388.     TkCanvas *canvasPtr; /* Canvas to be modified. */
  1389.     Tcl_Obj *tag; /* Tag identifying items to be moved
  1390.  * in the redisplay list. */
  1391.     Tk_Item *prevPtr; /* Reposition the items so that they
  1392.  * go just after this item (NULL means
  1393.  * put at beginning of list). */
  1394. #ifndef USE_OLD_TAG_SEARCH
  1395.     TagSearch **searchPtrPtr;   /* From CanvasWidgetCmd local vars */
  1396. #endif /* not USE_OLD_TAG_SEARCH */
  1397. {
  1398.     Tk_Item *itemPtr;
  1399. #ifdef USE_OLD_TAG_SEARCH
  1400.     TagSearch search;
  1401. #endif /* USE_OLD_TAG_SEARCH */
  1402.     Tk_Item *firstMovePtr, *lastMovePtr;
  1403.     /*
  1404.      * Find all of the items to be moved and remove them from
  1405.      * the list, making an auxiliary list running from firstMovePtr
  1406.      * to lastMovePtr.  Record their areas for redisplay.
  1407.      */
  1408.     firstMovePtr = lastMovePtr = NULL;
  1409. #ifdef USE_OLD_TAG_SEARCH
  1410.     for (itemPtr = StartTagSearch(canvasPtr, tag, &search);
  1411.     itemPtr != NULL; itemPtr = NextItem(&search)) {
  1412. #else /* USE_OLD_TAG_SEARCH */
  1413.     if (TagSearchScan(canvasPtr, tag, searchPtrPtr) != TCL_OK) {
  1414.         return TCL_ERROR;
  1415.     }
  1416.     for (itemPtr = TagSearchFirst(*searchPtrPtr);
  1417.             itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) {
  1418. #endif /* USE_OLD_TAG_SEARCH */
  1419. if (itemPtr == prevPtr) {
  1420.     /*
  1421.      * Item after which insertion is to occur is being
  1422.      * moved!  Switch to insert after its predecessor.
  1423.      */
  1424.     prevPtr = prevPtr->prevPtr;
  1425. }
  1426. if (itemPtr->prevPtr == NULL) {
  1427.     if (itemPtr->nextPtr != NULL) {
  1428. itemPtr->nextPtr->prevPtr = NULL;
  1429.     }
  1430.     canvasPtr->firstItemPtr = itemPtr->nextPtr;
  1431. } else {
  1432.     if (itemPtr->nextPtr != NULL) {
  1433. itemPtr->nextPtr->prevPtr = itemPtr->prevPtr;
  1434.     }
  1435.     itemPtr->prevPtr->nextPtr = itemPtr->nextPtr;
  1436. }
  1437. if (canvasPtr->lastItemPtr == itemPtr) {
  1438.     canvasPtr->lastItemPtr = itemPtr->prevPtr;
  1439. }
  1440. if (firstMovePtr == NULL) {
  1441.     itemPtr->prevPtr = NULL;
  1442.     firstMovePtr = itemPtr;
  1443. } else {
  1444.     itemPtr->prevPtr = lastMovePtr;
  1445.     lastMovePtr->nextPtr = itemPtr;
  1446. }
  1447. lastMovePtr = itemPtr;
  1448. EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
  1449. canvasPtr->flags |= REPICK_NEEDED;
  1450.     }
  1451.     /*
  1452.      * Insert the list of to-be-moved items back into the canvas's
  1453.      * at the desired position.
  1454.      */
  1455.     if (firstMovePtr == NULL) {
  1456. #ifdef USE_OLD_TAG_SEARCH
  1457. return;
  1458. #else /* USE_OLD_TAG_SEARCH */
  1459.         return TCL_OK;
  1460. #endif /* USE_OLD_TAG_SEARCH */
  1461.     }
  1462.     if (prevPtr == NULL) {
  1463. if (canvasPtr->firstItemPtr != NULL) {
  1464.     canvasPtr->firstItemPtr->prevPtr = lastMovePtr;
  1465. }
  1466. lastMovePtr->nextPtr = canvasPtr->firstItemPtr;
  1467. canvasPtr->firstItemPtr = firstMovePtr;
  1468.     } else {
  1469. if (prevPtr->nextPtr != NULL) {
  1470.     prevPtr->nextPtr->prevPtr = lastMovePtr;
  1471. }
  1472. lastMovePtr->nextPtr = prevPtr->nextPtr;
  1473. if (firstMovePtr != NULL) {
  1474.     firstMovePtr->prevPtr = prevPtr;
  1475. }
  1476. prevPtr->nextPtr = firstMovePtr;
  1477.     }
  1478.     if (canvasPtr->lastItemPtr == prevPtr) {
  1479. canvasPtr->lastItemPtr = lastMovePtr;
  1480.     }
  1481. #ifndef USE_OLD_TAG_SEARCH
  1482.     return TCL_OK;
  1483. #endif /* not USE_OLD_TAG_SEARCH */
  1484. }
  1485. /*
  1486.  *--------------------------------------------------------------
  1487.  *
  1488.  * CanvasBindProc --
  1489.  *
  1490.  * This procedure is invoked by the Tk dispatcher to handle
  1491.  * events associated with bindings on items.
  1492.  *
  1493.  * Results:
  1494.  * None.
  1495.  *
  1496.  * Side effects:
  1497.  * Depends on the command invoked as part of the binding
  1498.  * (if there was any).
  1499.  *
  1500.  *--------------------------------------------------------------
  1501.  */
  1502. static void
  1503. CanvasBindProc(clientData, eventPtr)
  1504.     ClientData clientData; /* Pointer to canvas structure. */
  1505.     XEvent *eventPtr; /* Pointer to X event that just
  1506.  * happened. */
  1507. {
  1508.     TkCanvas *canvasPtr = (TkCanvas *) clientData;
  1509.     Tcl_Preserve((ClientData) canvasPtr);
  1510.     /*
  1511.      * This code below keeps track of the current modifier state in
  1512.      * canvasPtr>state.  This information is used to defer repicks of
  1513.      * the current item while buttons are down.
  1514.      */
  1515.     if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) {
  1516. int mask;
  1517. switch (eventPtr->xbutton.button) {
  1518.     case Button1:
  1519. mask = Button1Mask;
  1520. break;
  1521.     case Button2:
  1522. mask = Button2Mask;
  1523. break;
  1524.     case Button3:
  1525. mask = Button3Mask;
  1526. break;
  1527.     case Button4:
  1528. mask = Button4Mask;
  1529. break;
  1530.     case Button5:
  1531. mask = Button5Mask;
  1532. break;
  1533.     default:
  1534. mask = 0;
  1535. break;
  1536. }
  1537. /*
  1538.  * For button press events, repick the current item using the
  1539.  * button state before the event, then process the event.  For
  1540.  * button release events, first process the event, then repick
  1541.  * the current item using the button state *after* the event
  1542.  * (the button has logically gone up before we change the
  1543.  * current item).
  1544.  */
  1545. if (eventPtr->type == ButtonPress) {
  1546.     /*
  1547.      * On a button press, first repick the current item using
  1548.      * the button state before the event, the process the event.
  1549.      */
  1550.     canvasPtr->state = eventPtr->xbutton.state;
  1551.     PickCurrentItem(canvasPtr, eventPtr);
  1552.     canvasPtr->state ^= mask;
  1553.     CanvasDoEvent(canvasPtr, eventPtr);
  1554. } else {
  1555.     /*
  1556.      * Button release: first process the event, with the button
  1557.      * still considered to be down.  Then repick the current
  1558.      * item under the assumption that the button is no longer down.
  1559.      */
  1560.     canvasPtr->state = eventPtr->xbutton.state;
  1561.     CanvasDoEvent(canvasPtr, eventPtr);
  1562.     eventPtr->xbutton.state ^= mask;
  1563.     canvasPtr->state = eventPtr->xbutton.state;
  1564.     PickCurrentItem(canvasPtr, eventPtr);
  1565.     eventPtr->xbutton.state ^= mask;
  1566. }
  1567. goto done;
  1568.     } else if ((eventPtr->type == EnterNotify)
  1569.     || (eventPtr->type == LeaveNotify)) {
  1570. canvasPtr->state = eventPtr->xcrossing.state;
  1571. PickCurrentItem(canvasPtr, eventPtr);
  1572. goto done;
  1573.     } else if (eventPtr->type == MotionNotify) {
  1574. canvasPtr->state = eventPtr->xmotion.state;
  1575. PickCurrentItem(canvasPtr, eventPtr);
  1576.     }
  1577.     CanvasDoEvent(canvasPtr, eventPtr);
  1578.     done:
  1579.     Tcl_Release((ClientData) canvasPtr);
  1580. }
  1581. /*
  1582.  *--------------------------------------------------------------
  1583.  *
  1584.  * PickCurrentItem --
  1585.  *
  1586.  * Find the topmost item in a canvas that contains a given
  1587.  * location and mark the the current item.  If the current
  1588.  * item has changed, generate a fake exit event on the old
  1589.  * current item, a fake enter event on the new current item
  1590.  * item and force a redraw of the two items. Canvas items
  1591.  *      that are hidden or disabled are ignored.
  1592.  *
  1593.  * Results:
  1594.  * None.
  1595.  *
  1596.  * Side effects:
  1597.  * The current item for canvasPtr may change.  If it does,
  1598.  * then the commands associated with item entry and exit
  1599.  * could do just about anything.  A binding script could
  1600.  * delete the canvas, so callers should protect themselves
  1601.  * with Tcl_Preserve and Tcl_Release.
  1602.  *
  1603.  *--------------------------------------------------------------
  1604.  */
  1605. static void
  1606. PickCurrentItem(canvasPtr, eventPtr)
  1607.     TkCanvas *canvasPtr; /* Canvas widget in which to select
  1608.  * current item. */
  1609.     XEvent *eventPtr; /* Event describing location of
  1610.  * mouse cursor.  Must be EnterWindow,
  1611.  * LeaveWindow, ButtonRelease, or
  1612.  * MotionNotify. */
  1613. {
  1614.     double coords[2];
  1615.     int buttonDown;
  1616.     Tk_Item *prevItemPtr;
  1617. #ifndef USE_OLD_TAG_SEARCH
  1618.     SearchUids *searchUids = GetStaticUids();
  1619. #endif
  1620.     /*
  1621.      * Check whether or not a button is down.  If so, we'll log entry
  1622.      * and exit into and out of the current item, but not entry into
  1623.      * any other item.  This implements a form of grabbing equivalent
  1624.      * to what the X server does for windows.
  1625.      */
  1626.     buttonDown = canvasPtr->state
  1627.     & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask);
  1628.     if (!buttonDown) {
  1629. canvasPtr->flags &= ~LEFT_GRABBED_ITEM;
  1630.     }
  1631.     /*
  1632.      * Save information about this event in the canvas.  The event in
  1633.      * the canvas is used for two purposes:
  1634.      *
  1635.      * 1. Event bindings: if the current item changes, fake events are
  1636.      *    generated to allow item-enter and item-leave bindings to trigger.
  1637.      * 2. Reselection: if the current item gets deleted, can use the
  1638.      *    saved event to find a new current item.
  1639.      * Translate MotionNotify events into EnterNotify events, since that's
  1640.      * what gets reported to item handlers.
  1641.      */
  1642.     if (eventPtr != &canvasPtr->pickEvent) {
  1643. if ((eventPtr->type == MotionNotify)
  1644. || (eventPtr->type == ButtonRelease)) {
  1645.     canvasPtr->pickEvent.xcrossing.type = EnterNotify;
  1646.     canvasPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial;
  1647.     canvasPtr->pickEvent.xcrossing.send_event
  1648.     = eventPtr->xmotion.send_event;
  1649.     canvasPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display;
  1650.     canvasPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window;
  1651.     canvasPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root;
  1652.     canvasPtr->pickEvent.xcrossing.subwindow = None;
  1653.     canvasPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time;
  1654.     canvasPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x;
  1655.     canvasPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y;
  1656.     canvasPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root;
  1657.     canvasPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root;
  1658.     canvasPtr->pickEvent.xcrossing.mode = NotifyNormal;
  1659.     canvasPtr->pickEvent.xcrossing.detail = NotifyNonlinear;
  1660.     canvasPtr->pickEvent.xcrossing.same_screen
  1661.     = eventPtr->xmotion.same_screen;
  1662.     canvasPtr->pickEvent.xcrossing.focus = False;
  1663.     canvasPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state;
  1664. } else  {
  1665.     canvasPtr->pickEvent = *eventPtr;
  1666. }
  1667.     }
  1668.     /*
  1669.      * If this is a recursive call (there's already a partially completed
  1670.      * call pending on the stack;  it's in the middle of processing a
  1671.      * Leave event handler for the old current item) then just return;
  1672.      * the pending call will do everything that's needed.
  1673.      */
  1674.     if (canvasPtr->flags & REPICK_IN_PROGRESS) {
  1675. return;
  1676.     }
  1677.     /*
  1678.      * A LeaveNotify event automatically means that there's no current
  1679.      * object, so the check for closest item can be skipped.
  1680.      */
  1681.     coords[0] = canvasPtr->pickEvent.xcrossing.x + canvasPtr->xOrigin;
  1682.     coords[1] = canvasPtr->pickEvent.xcrossing.y + canvasPtr->yOrigin;
  1683.     if (canvasPtr->pickEvent.type != LeaveNotify) {
  1684. canvasPtr->newCurrentPtr = CanvasFindClosest(canvasPtr, coords);
  1685.     } else {
  1686. canvasPtr->newCurrentPtr = NULL;
  1687.     }
  1688.     if ((canvasPtr->newCurrentPtr == canvasPtr->currentItemPtr)
  1689.     && !(canvasPtr->flags & LEFT_GRABBED_ITEM)) {
  1690. /*
  1691.  * Nothing to do:  the current item hasn't changed.
  1692.  */
  1693. return;
  1694.     }
  1695.     /*
  1696.      * Simulate a LeaveNotify event on the previous current item and
  1697.      * an EnterNotify event on the new current item.  Remove the "current"
  1698.      * tag from the previous current item and place it on the new current
  1699.      * item.
  1700.      */
  1701.     if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr)
  1702.     && (canvasPtr->currentItemPtr != NULL)
  1703.     && !(canvasPtr->flags & LEFT_GRABBED_ITEM)) {
  1704. XEvent event;
  1705. Tk_Item *itemPtr = canvasPtr->currentItemPtr;
  1706. int i;
  1707. event = canvasPtr->pickEvent;
  1708. event.type = LeaveNotify;
  1709. /*
  1710.  * If the event's detail happens to be NotifyInferior the
  1711.  * binding mechanism will discard the event.  To be consistent,
  1712.  * always use NotifyAncestor.
  1713.  */
  1714. event.xcrossing.detail = NotifyAncestor;
  1715. canvasPtr->flags |= REPICK_IN_PROGRESS;
  1716. CanvasDoEvent(canvasPtr, &event);
  1717. canvasPtr->flags &= ~REPICK_IN_PROGRESS;
  1718. /*
  1719.  * The check below is needed because there could be an event
  1720.  * handler for <LeaveNotify> that deletes the current item.
  1721.  */
  1722. if ((itemPtr == canvasPtr->currentItemPtr) && !buttonDown) {
  1723.     for (i = itemPtr->numTags-1; i >= 0; i--) {
  1724. #ifdef USE_OLD_TAG_SEARCH
  1725. if (itemPtr->tagPtr[i] == Tk_GetUid("current")) {
  1726. #else /* USE_OLD_TAG_SEARCH */
  1727. if (itemPtr->tagPtr[i] == searchUids->currentUid) {
  1728. #endif /* USE_OLD_TAG_SEARCH */
  1729.     itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
  1730.     itemPtr->numTags--;
  1731.     break;
  1732. }
  1733.     }
  1734. }
  1735.     
  1736. /*
  1737.  * Note:  during CanvasDoEvent above, it's possible that
  1738.  * canvasPtr->newCurrentPtr got reset to NULL because the
  1739.  * item was deleted.
  1740.  */
  1741.     }
  1742.     if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr) && buttonDown) {
  1743. canvasPtr->flags |= LEFT_GRABBED_ITEM;
  1744. return;
  1745.     }
  1746.     /*
  1747.      * Special note:  it's possible that canvasPtr->newCurrentPtr ==
  1748.      * canvasPtr->currentItemPtr here.  This can happen, for example,
  1749.      * if LEFT_GRABBED_ITEM was set.
  1750.      */
  1751.     prevItemPtr = canvasPtr->currentItemPtr;
  1752.     canvasPtr->flags &= ~LEFT_GRABBED_ITEM;
  1753.     canvasPtr->currentItemPtr = canvasPtr->newCurrentPtr;
  1754.     if (prevItemPtr != NULL && prevItemPtr != canvasPtr->currentItemPtr &&
  1755.     (prevItemPtr->redraw_flags & TK_ITEM_STATE_DEPENDANT)) {
  1756. EventuallyRedrawItem((Tk_Canvas) canvasPtr, prevItemPtr);
  1757. (*prevItemPtr->typePtr->configProc)(canvasPtr->interp,
  1758. (Tk_Canvas) canvasPtr, prevItemPtr, 0, (Tcl_Obj **) NULL,
  1759. TK_CONFIG_ARGV_ONLY);
  1760.     }
  1761.     if (canvasPtr->currentItemPtr != NULL) {
  1762. XEvent event;
  1763. #ifdef USE_OLD_TAG_SEARCH
  1764. DoItem((Tcl_Interp *) NULL, canvasPtr->currentItemPtr, 
  1765.                 Tk_GetUid("current"));
  1766. #else /* USE_OLD_TAG_SEARCH */
  1767. DoItem((Tcl_Interp *) NULL, canvasPtr->currentItemPtr,
  1768. searchUids->currentUid);
  1769. #endif /* USE_OLD_TAG_SEA */
  1770. if ((canvasPtr->currentItemPtr->redraw_flags & TK_ITEM_STATE_DEPENDANT &&
  1771. prevItemPtr != canvasPtr->currentItemPtr)) {
  1772.     (*canvasPtr->currentItemPtr->typePtr->configProc)(canvasPtr->interp,
  1773.     (Tk_Canvas) canvasPtr, canvasPtr->currentItemPtr, 0, (Tcl_Obj **) NULL,
  1774.     TK_CONFIG_ARGV_ONLY);
  1775.     EventuallyRedrawItem((Tk_Canvas) canvasPtr,
  1776.     canvasPtr->currentItemPtr);
  1777. }
  1778. event = canvasPtr->pickEvent;
  1779. event.type = EnterNotify;
  1780. event.xcrossing.detail = NotifyAncestor;
  1781. CanvasDoEvent(canvasPtr, &event);
  1782.     }
  1783. }
  1784. /*
  1785.  *----------------------------------------------------------------------
  1786.  *
  1787.  * CanvasFindClosest --
  1788.  *
  1789.  * Given x and y coordinates, find the topmost canvas item that
  1790.  * is "close" to the coordinates. Canvas items that are hidden
  1791.  * or disabled are ignored.
  1792.  *
  1793.  * Results:
  1794.  * The return value is a pointer to the topmost item that is
  1795.  * close to (x,y), or NULL if no item is close.
  1796.  *
  1797.  * Side effects:
  1798.  * None.
  1799.  *
  1800.  *----------------------------------------------------------------------
  1801.  */
  1802. static Tk_Item *
  1803. CanvasFindClosest(canvasPtr, coords)
  1804.     TkCanvas *canvasPtr; /* Canvas widget to search. */
  1805.     double coords[2]; /* Desired x,y position in canvas,
  1806.  * not screen, coordinates.) */
  1807. {
  1808.     Tk_Item *itemPtr;
  1809.     Tk_Item *bestPtr;
  1810.     int x1, y1, x2, y2;
  1811.     x1 = (int) (coords[0] - canvasPtr->closeEnough);
  1812.     y1 = (int) (coords[1] - canvasPtr->closeEnough);
  1813.     x2 = (int) (coords[0] + canvasPtr->closeEnough);
  1814.     y2 = (int) (coords[1] + canvasPtr->closeEnough);
  1815.     bestPtr = NULL;
  1816.     for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
  1817.     itemPtr = itemPtr->nextPtr) {
  1818. if (itemPtr->state == TK_STATE_HIDDEN || itemPtr->state==TK_STATE_DISABLED ||
  1819. (itemPtr->state == TK_STATE_NULL && (canvasPtr->canvas_state == TK_STATE_HIDDEN ||
  1820. canvasPtr->canvas_state == TK_STATE_DISABLED))) {
  1821.     continue;
  1822. }
  1823. if ((itemPtr->x1 > x2) || (itemPtr->x2 < x1)
  1824. || (itemPtr->y1 > y2) || (itemPtr->y2 < y1)) {
  1825.     continue;
  1826. }
  1827. if ((*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
  1828. itemPtr, coords) <= canvasPtr->closeEnough) {
  1829.     bestPtr = itemPtr;
  1830. }
  1831.     }
  1832.     return bestPtr;
  1833. }
  1834. /*
  1835.  *--------------------------------------------------------------
  1836.  *
  1837.  * CanvasDoEvent --
  1838.  *
  1839.  * This procedure is called to invoke binding processing
  1840.  * for a new event that is associated with the current item
  1841.  * for a canvas.
  1842.  *
  1843.  * Results:
  1844.  * None.
  1845.  *
  1846.  * Side effects:
  1847.  * Depends on the bindings for the canvas.  A binding script
  1848.  * could delete the canvas, so callers should protect themselves
  1849.  * with Tcl_Preserve and Tcl_Release.
  1850.  *
  1851.  *--------------------------------------------------------------
  1852.  */
  1853. static void
  1854. CanvasDoEvent(canvasPtr, eventPtr)
  1855.     TkCanvas *canvasPtr; /* Canvas widget in which event
  1856.  * occurred. */
  1857.     XEvent *eventPtr; /* Real or simulated X event that
  1858.  * is to be processed. */
  1859. {
  1860. #define NUM_STATIC 3
  1861.     ClientData staticObjects[NUM_STATIC];
  1862.     ClientData *objectPtr;
  1863.     int numObjects, i;
  1864.     Tk_Item *itemPtr;
  1865. #ifndef USE_OLD_TAG_SEARCH
  1866.     TagSearchExpr *expr;
  1867.     int numExprs;
  1868.     SearchUids *searchUids = GetStaticUids();
  1869. #endif /* not USE_OLD_TAG_SEARCH */
  1870.     if (canvasPtr->bindingTable == NULL) {
  1871. return;
  1872.     }
  1873.     itemPtr = canvasPtr->currentItemPtr;
  1874.     if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) {
  1875. itemPtr = canvasPtr->textInfo.focusItemPtr;
  1876.     }
  1877.     if (itemPtr == NULL) {
  1878. return;
  1879.     }
  1880. #ifdef USE_OLD_TAG_SEARCH
  1881.     /*
  1882.      * Set up an array with all the relevant objects for processing
  1883.      * this event.  The relevant objects are (a) the event's item,
  1884.      * (b) the tags associated with the event's item, and (c) the
  1885.      * tag "all".  If there are a lot of tags then malloc an array
  1886.      * to hold all of the objects.
  1887.      */
  1888.     numObjects = itemPtr->numTags + 2;
  1889. #else /* USE_OLD_TAG_SEARCH */
  1890.     /*
  1891.      * Set up an array with all the relevant objects for processing
  1892.      * this event.  The relevant objects are:
  1893.      * (a) the event's item,
  1894.      * (b) the tags associated with the event's item, 
  1895.      * (c) the expressions that are true for the event's item's tags, and
  1896.      * (d) the tag "all". 
  1897.      *
  1898.      * If there are a lot of tags then malloc an array to hold all of
  1899.      * the objects.
  1900.      */
  1901.     /*
  1902.      * flag and count all expressions that match item's tags
  1903.      */
  1904.     numExprs = 0;
  1905.     expr = canvasPtr->bindTagExprs;
  1906.     while (expr) {
  1907. expr->index = 0;
  1908.      expr->match = TagSearchEvalExpr(expr, itemPtr);
  1909. if (expr->match) {
  1910.     numExprs++;
  1911. }
  1912. expr = expr->next;
  1913.     }
  1914.     numObjects = itemPtr->numTags + numExprs + 2;
  1915. #endif /* not USE_OLD_TAG_SEARCH */
  1916.     if (numObjects <= NUM_STATIC) {
  1917. objectPtr = staticObjects;
  1918.     } else {
  1919. objectPtr = (ClientData *)
  1920. ckalloc((unsigned) (numObjects * sizeof(ClientData)));
  1921.     }
  1922. #ifdef USE_OLD_TAG_SEARCH
  1923.     objectPtr[0] = (ClientData) Tk_GetUid("all");
  1924. #else /* USE_OLD_TAG_SEARCH */
  1925.     objectPtr[0] = (ClientData) searchUids->allUid;
  1926. #endif /* USE_OLD_TAG_SEARCH */
  1927.     for (i = itemPtr->numTags-1; i >= 0; i--) {
  1928. objectPtr[i+1] = (ClientData) itemPtr->tagPtr[i];
  1929.     }
  1930.     objectPtr[itemPtr->numTags+1] = (ClientData) itemPtr;
  1931. #ifndef USE_OLD_TAG_SEARCH
  1932.     /*
  1933.      * copy uids of matching expressions into object array
  1934.      */
  1935.     i = itemPtr->numTags+2;
  1936.     expr = canvasPtr->bindTagExprs;
  1937.     while (expr) {
  1938.      if (expr->match) {
  1939.     objectPtr[i++] = (int *) expr->uid;
  1940. }
  1941. expr = expr->next;
  1942.     }
  1943. #endif /* not USE_OLD_TAG_SEARCH */
  1944.     /*
  1945.      * Invoke the binding system, then free up the object array if
  1946.      * it was malloc-ed.
  1947.      */
  1948.     if (canvasPtr->tkwin != NULL) {
  1949. Tk_BindEvent(canvasPtr->bindingTable, eventPtr, canvasPtr->tkwin,
  1950. numObjects, objectPtr);
  1951.     }
  1952.     if (objectPtr != staticObjects) {
  1953. ckfree((char *) objectPtr);
  1954.     }
  1955. }
  1956. /*
  1957.  *----------------------------------------------------------------------
  1958.  *
  1959.  * CanvasBlinkProc --
  1960.  *
  1961.  * This procedure is called as a timer handler to blink the
  1962.  * insertion cursor off and on.
  1963.  *
  1964.  * Results:
  1965.  * None.
  1966.  *
  1967.  * Side effects:
  1968.  * The cursor gets turned on or off, redisplay gets invoked,
  1969.  * and this procedure reschedules itself.
  1970.  *
  1971.  *----------------------------------------------------------------------
  1972.  */
  1973. static void
  1974. CanvasBlinkProc(clientData)
  1975.     ClientData clientData; /* Pointer to record describing entry. */
  1976. {
  1977.     TkCanvas *canvasPtr = (TkCanvas *) clientData;
  1978.     if (!canvasPtr->textInfo.gotFocus || (canvasPtr->insertOffTime == 0)) {
  1979. return;
  1980.     }
  1981.     if (canvasPtr->textInfo.cursorOn) {
  1982. canvasPtr->textInfo.cursorOn = 0;
  1983. canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
  1984. canvasPtr->insertOffTime, CanvasBlinkProc,
  1985. (ClientData) canvasPtr);
  1986.     } else {
  1987. canvasPtr->textInfo.cursorOn = 1;
  1988. canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
  1989. canvasPtr->insertOnTime, CanvasBlinkProc,
  1990. (ClientData) canvasPtr);
  1991.     }
  1992.     if (canvasPtr->textInfo.focusItemPtr != NULL) {
  1993. EventuallyRedrawItem((Tk_Canvas) canvasPtr,
  1994. canvasPtr->textInfo.focusItemPtr);
  1995.     }
  1996. }
  1997. /*
  1998.  *----------------------------------------------------------------------
  1999.  *
  2000.  * CanvasFocusProc --
  2001.  *
  2002.  * This procedure is called whenever a canvas gets or loses the
  2003.  * input focus.  It's also called whenever the window is
  2004.  * reconfigured while it has the focus.
  2005.  *
  2006.  * Results:
  2007.  * None.
  2008.  *
  2009.  * Side effects:
  2010.  * The cursor gets turned on or off.
  2011.  *
  2012.  *----------------------------------------------------------------------
  2013.  */
  2014. static void
  2015. CanvasFocusProc(canvasPtr, gotFocus)
  2016.     TkCanvas *canvasPtr; /* Canvas that just got or lost focus. */
  2017.     int gotFocus; /* 1 means window is getting focus, 0 means
  2018.  * it's losing it. */
  2019. {
  2020.     Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler);
  2021.     if (gotFocus) {
  2022. canvasPtr->textInfo.gotFocus = 1;
  2023. canvasPtr->textInfo.cursorOn = 1;
  2024. if (canvasPtr->insertOffTime != 0) {
  2025.     canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
  2026.     canvasPtr->insertOffTime, CanvasBlinkProc,
  2027.     (ClientData) canvasPtr);
  2028. }
  2029.     } else {
  2030. canvasPtr->textInfo.gotFocus = 0;
  2031. canvasPtr->textInfo.cursorOn = 0;
  2032. canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
  2033.     }
  2034.     if (canvasPtr->textInfo.focusItemPtr != NULL) {
  2035. EventuallyRedrawItem((Tk_Canvas) canvasPtr,
  2036. canvasPtr->textInfo.focusItemPtr);
  2037.     }
  2038.     if (canvasPtr->highlightWidth > 0) {
  2039. canvasPtr->flags |= REDRAW_BORDERS;
  2040. if (!(canvasPtr->flags & REDRAW_PENDING)) {
  2041.     Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
  2042.     canvasPtr->flags |= REDRAW_PENDING;
  2043. }
  2044.     }
  2045. }
  2046. /*
  2047.  *----------------------------------------------------------------------
  2048.  *
  2049.  * CanvasSelectTo --
  2050.  *
  2051.  * Modify the selection by moving its un-anchored end.  This could
  2052.  * make the selection either larger or smaller.
  2053.  *
  2054.  * Results:
  2055.  * None.
  2056.  *
  2057.  * Side effects:
  2058.  * The selection changes.
  2059.  *
  2060.  *----------------------------------------------------------------------
  2061.  */
  2062. static void
  2063. CanvasSelectTo(canvasPtr, itemPtr, index)
  2064.     TkCanvas *canvasPtr; /* Information about widget. */
  2065.     Tk_Item *itemPtr; /* Item that is to hold selection. */
  2066.     int index; /* Index of element that is to become the
  2067.  * "other" end of the selection. */
  2068. {
  2069.     int oldFirst, oldLast;
  2070.     Tk_Item *oldSelPtr;
  2071.     oldFirst = canvasPtr->textInfo.selectFirst;
  2072.     oldLast = canvasPtr->textInfo.selectLast;
  2073.     oldSelPtr = canvasPtr->textInfo.selItemPtr;
  2074.     /*
  2075.      * Grab the selection if we don't own it already.
  2076.      */
  2077.     if (canvasPtr->textInfo.selItemPtr == NULL) {
  2078. Tk_OwnSelection(canvasPtr->tkwin, XA_PRIMARY, CanvasLostSelection,
  2079. (ClientData) canvasPtr);
  2080.     } else if (canvasPtr->textInfo.selItemPtr != itemPtr) {
  2081. EventuallyRedrawItem((Tk_Canvas) canvasPtr,
  2082. canvasPtr->textInfo.selItemPtr);
  2083.     }
  2084.     canvasPtr->textInfo.selItemPtr = itemPtr;
  2085.     if (canvasPtr->textInfo.anchorItemPtr != itemPtr) {
  2086. canvasPtr->textInfo.anchorItemPtr = itemPtr;
  2087. canvasPtr->textInfo.selectAnchor = index;
  2088.     }
  2089.     if (canvasPtr->textInfo.selectAnchor <= index) {
  2090. canvasPtr->textInfo.selectFirst = canvasPtr->textInfo.selectAnchor;
  2091. canvasPtr->textInfo.selectLast = index;
  2092.     } else {
  2093. canvasPtr->textInfo.selectFirst = index;
  2094. canvasPtr->textInfo.selectLast = canvasPtr->textInfo.selectAnchor - 1;
  2095.     }
  2096.     if ((canvasPtr->textInfo.selectFirst != oldFirst)
  2097.     || (canvasPtr->textInfo.selectLast != oldLast)
  2098.     || (itemPtr != oldSelPtr)) {
  2099. EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
  2100.     }
  2101. }
  2102. /*
  2103.  *--------------------------------------------------------------
  2104.  *
  2105.  * CanvasFetchSelection --
  2106.  *
  2107.  * This procedure is invoked by Tk to return part or all of
  2108.  * the selection, when the selection is in a canvas widget.
  2109.  * This procedure always returns the selection as a STRING.
  2110.  *
  2111.  * Results:
  2112.  * The return value is the number of non-NULL bytes stored
  2113.  * at buffer.  Buffer is filled (or partially filled) with a
  2114.  * NULL-terminated string containing part or all of the selection,
  2115.  * as given by offset and maxBytes.
  2116.  *
  2117.  * Side effects:
  2118.  * None.
  2119.  *
  2120.  *--------------------------------------------------------------
  2121.  */
  2122. static int
  2123. CanvasFetchSelection(clientData, offset, buffer, maxBytes)
  2124.     ClientData clientData; /* Information about canvas widget. */
  2125.     int offset; /* Offset within selection of first
  2126.  * character to be returned. */
  2127.     char *buffer; /* Location in which to place
  2128.  * selection. */
  2129.     int maxBytes; /* Maximum number of bytes to place
  2130.  * at buffer, not including terminating
  2131.  * NULL character. */
  2132. {
  2133.     TkCanvas *canvasPtr = (TkCanvas *) clientData;
  2134.     if (canvasPtr->textInfo.selItemPtr == NULL) {
  2135. return -1;
  2136.     }
  2137.     if (canvasPtr->textInfo.selItemPtr->typePtr->selectionProc == NULL) {
  2138. return -1;
  2139.     }
  2140.     return (*canvasPtr->textInfo.selItemPtr->typePtr->selectionProc)(
  2141.     (Tk_Canvas) canvasPtr, canvasPtr->textInfo.selItemPtr, offset,
  2142.     buffer, maxBytes);
  2143. }
  2144. /*
  2145.  *----------------------------------------------------------------------
  2146.  *
  2147.  * CanvasLostSelection --
  2148.  *
  2149.  * This procedure is called back by Tk when the selection is
  2150.  * grabbed away from a canvas widget.
  2151.  *
  2152.  * Results:
  2153.  * None.
  2154.  *
  2155.  * Side effects:
  2156.  * The existing selection is unhighlighted, and the window is
  2157.  * marked as not containing a selection.
  2158.  *
  2159.  *----------------------------------------------------------------------
  2160.  */
  2161. static void
  2162. CanvasLostSelection(clientData)
  2163.     ClientData clientData; /* Information about entry widget. */
  2164. {
  2165.     TkCanvas *canvasPtr = (TkCanvas *) clientData;
  2166.     if (canvasPtr->textInfo.selItemPtr != NULL) {
  2167. EventuallyRedrawItem((Tk_Canvas) canvasPtr,
  2168. canvasPtr->textInfo.selItemPtr);
  2169.     }
  2170.     canvasPtr->textInfo.selItemPtr = NULL;
  2171. }
  2172. /*
  2173.  *--------------------------------------------------------------
  2174.  *
  2175.  * GridAlign --
  2176.  *
  2177.  * Given a coordinate and a grid spacing, this procedure
  2178.  * computes the location of the nearest grid line to the
  2179.  * coordinate.
  2180.  *
  2181.  * Results:
  2182.  * The return value is the location of the grid line nearest
  2183.  * to coord.
  2184.  *
  2185.  * Side effects:
  2186.  * None.
  2187.  *
  2188.  *--------------------------------------------------------------
  2189.  */
  2190. static double
  2191. GridAlign(coord, spacing)
  2192.     double coord; /* Coordinate to grid-align. */
  2193.     double spacing; /* Spacing between grid lines.   If <= 0
  2194.  * then no alignment is done. */
  2195. {
  2196.     if (spacing <= 0.0) {
  2197. return coord;
  2198.     }
  2199.     if (coord < 0) {
  2200. return -((int) ((-coord)/spacing + 0.5)) * spacing;
  2201.     }
  2202.     return ((int) (coord/spacing + 0.5)) * spacing;
  2203. }
  2204. /*
  2205.  *----------------------------------------------------------------------
  2206.  *
  2207.  * ScrollFractions --
  2208.  *
  2209.  * Given the range that's visible in the window and the "100%
  2210.  * range" for what's in the canvas, return a list of two
  2211.  * doubles representing the scroll fractions.  This procedure
  2212.  * is used for both x and y scrolling.
  2213.  *
  2214.  * Results:
  2215.  * The memory pointed to by string is modified to hold
  2216.  * two real numbers containing the scroll fractions (between
  2217.  * 0 and 1) corresponding to the other arguments.
  2218.  *
  2219.  * Side effects:
  2220.  * None.
  2221.  *
  2222.  *----------------------------------------------------------------------
  2223.  */
  2224. static Tcl_Obj *
  2225. ScrollFractions(screen1, screen2, object1, object2)
  2226.     int screen1; /* Lowest coordinate visible in the window. */
  2227.     int screen2; /* Highest coordinate visible in the window. */
  2228.     int object1; /* Lowest coordinate in the object. */
  2229.     int object2; /* Highest coordinate in the object. */
  2230. {
  2231.     double range, f1, f2;
  2232.     char buffer[2*TCL_DOUBLE_SPACE+2];
  2233.     range = object2 - object1;
  2234.     if (range <= 0) {
  2235. f1 = 0;
  2236. f2 = 1.0;
  2237.     } else {
  2238. f1 = (screen1 - object1)/range;
  2239. if (f1 < 0) {
  2240.     f1 = 0.0;
  2241. }
  2242. f2 = (screen2 - object1)/range;
  2243. if (f2 > 1.0) {
  2244.     f2 = 1.0;
  2245. }
  2246. if (f2 < f1) {
  2247.     f2 = f1;
  2248. }
  2249.     }
  2250.     sprintf(buffer, "%g %g", f1, f2);
  2251.     return Tcl_NewStringObj(buffer, -1);
  2252. }
  2253. /*
  2254.  *--------------------------------------------------------------
  2255.  *
  2256.  * CanvasUpdateScrollbars --
  2257.  *
  2258.  * This procedure is invoked whenever a canvas has changed in
  2259.  * a way that requires scrollbars to be redisplayed (e.g. the
  2260.  * view in the canvas has changed).
  2261.  *
  2262.  * Results:
  2263.  * None.
  2264.  *
  2265.  * Side effects:
  2266.  * If there are scrollbars associated with the canvas, then
  2267.  * their scrolling commands are invoked to cause them to
  2268.  * redisplay.  If errors occur, additional Tcl commands may
  2269.  * be invoked to process the errors.
  2270.  *
  2271.  *--------------------------------------------------------------
  2272.  */
  2273. static void
  2274. CanvasUpdateScrollbars(canvasPtr)
  2275.     TkCanvas *canvasPtr; /* Information about canvas. */
  2276. {
  2277.     int result;
  2278.     Tcl_Interp *interp;
  2279.     int xOrigin, yOrigin, inset, width, height, scrollX1, scrollX2,
  2280.         scrollY1, scrollY2;
  2281.     char *xScrollCmd, *yScrollCmd;
  2282.     /*
  2283.      * Save all the relevant values from the canvasPtr, because it might be
  2284.      * deleted as part of either of the two calls to Tcl_VarEval below.
  2285.      */
  2286.     
  2287.     interp = canvasPtr->interp;
  2288.     Tcl_Preserve((ClientData) interp);
  2289.     xScrollCmd = canvasPtr->xScrollCmd;
  2290.     if (xScrollCmd != (char *) NULL) {
  2291.         Tcl_Preserve((ClientData) xScrollCmd);
  2292.     }
  2293.     yScrollCmd = canvasPtr->yScrollCmd;
  2294.     if (yScrollCmd != (char *) NULL) {
  2295.         Tcl_Preserve((ClientData) yScrollCmd);
  2296.     }
  2297.     xOrigin = canvasPtr->xOrigin;
  2298.     yOrigin = canvasPtr->yOrigin;
  2299.     inset = canvasPtr->inset;
  2300.     width = Tk_Width(canvasPtr->tkwin);
  2301.     height = Tk_Height(canvasPtr->tkwin);
  2302.     scrollX1 = canvasPtr->scrollX1;
  2303.     scrollX2 = canvasPtr->scrollX2;
  2304.     scrollY1 = canvasPtr->scrollY1;
  2305.     scrollY2 = canvasPtr->scrollY2;
  2306.     canvasPtr->flags &= ~UPDATE_SCROLLBARS;
  2307.     if (canvasPtr->xScrollCmd != NULL) {
  2308. Tcl_Obj *fractions = ScrollFractions(xOrigin + inset,
  2309. xOrigin + width - inset, scrollX1, scrollX2);
  2310. result = Tcl_VarEval(interp, xScrollCmd, " ", 
  2311. Tcl_GetString(fractions), (char *) NULL);
  2312. Tcl_DecrRefCount(fractions);
  2313. if (result != TCL_OK) {
  2314.     Tcl_BackgroundError(interp);
  2315. }
  2316. Tcl_ResetResult(interp);
  2317.         Tcl_Release((ClientData) xScrollCmd);
  2318.     }
  2319.     if (yScrollCmd != NULL) {
  2320. Tcl_Obj *fractions = ScrollFractions(yOrigin + inset,
  2321. yOrigin + height - inset, scrollY1, scrollY2);
  2322. result = Tcl_VarEval(interp, yScrollCmd, " ", 
  2323. Tcl_GetString(fractions), (char *) NULL);
  2324. Tcl_DecrRefCount(fractions);
  2325. if (result != TCL_OK) {
  2326.     Tcl_BackgroundError(interp);
  2327. }
  2328. Tcl_ResetResult(interp);
  2329.         Tcl_Release((ClientData) yScrollCmd);
  2330.     }
  2331.     Tcl_Release((ClientData) interp);
  2332. }
  2333. /*
  2334.  *--------------------------------------------------------------
  2335.  *
  2336.  * CanvasSetOrigin --
  2337.  *
  2338.  * This procedure is invoked to change the mapping between
  2339.  * canvas coordinates and screen coordinates in the canvas
  2340.  * window.
  2341.  *
  2342.  * Results:
  2343.  * None.
  2344.  *
  2345.  * Side effects:
  2346.  * The canvas will be redisplayed to reflect the change in
  2347.  * view.  In addition, scrollbars will be updated if there
  2348.  * are any.
  2349.  *
  2350.  *--------------------------------------------------------------
  2351.  */
  2352. static void
  2353. CanvasSetOrigin(canvasPtr, xOrigin, yOrigin)
  2354.     TkCanvas *canvasPtr; /* Information about canvas. */
  2355.     int xOrigin; /* New X origin for canvas (canvas x-coord
  2356.  * corresponding to left edge of canvas
  2357.  * window). */
  2358.     int yOrigin; /* New Y origin for canvas (canvas y-coord
  2359.  * corresponding to top edge of canvas
  2360.  * window). */
  2361. {
  2362.     int left, right, top, bottom, delta;
  2363.     /*
  2364.      * If scroll increments have been set, round the window origin
  2365.      * to the nearest multiple of the increments.  Remember, the
  2366.      * origin is the place just inside the borders,  not the upper
  2367.      * left corner.
  2368.      */
  2369.     if (canvasPtr->xScrollIncrement > 0) {
  2370. if (xOrigin >= 0) {
  2371.     xOrigin += canvasPtr->xScrollIncrement/2;
  2372.     xOrigin -= (xOrigin + canvasPtr->inset)
  2373.     % canvasPtr->xScrollIncrement;
  2374. } else {
  2375.     xOrigin = (-xOrigin) + canvasPtr->xScrollIncrement/2;
  2376.     xOrigin = -(xOrigin - (xOrigin - canvasPtr->inset)
  2377.     % canvasPtr->xScrollIncrement);
  2378. }
  2379.     }
  2380.     if (canvasPtr->yScrollIncrement > 0) {
  2381. if (yOrigin >= 0) {
  2382.     yOrigin += canvasPtr->yScrollIncrement/2;
  2383.     yOrigin -= (yOrigin + canvasPtr->inset)
  2384.     % canvasPtr->yScrollIncrement;
  2385. } else {
  2386.     yOrigin = (-yOrigin) + canvasPtr->yScrollIncrement/2;
  2387.     yOrigin = -(yOrigin - (yOrigin - canvasPtr->inset)
  2388.     % canvasPtr->yScrollIncrement);
  2389. }
  2390.     }
  2391.     /*
  2392.      * Adjust the origin if necessary to keep as much as possible of the
  2393.      * canvas in the view.  The variables left, right, etc. keep track of
  2394.      * how much extra space there is on each side of the view before it
  2395.      * will stick out past the scroll region.  If one side sticks out past
  2396.      * the edge of the scroll region, adjust the view to bring that side
  2397.      * back to the edge of the scrollregion (but don't move it so much that
  2398.      * the other side sticks out now).  If scroll increments are in effect,
  2399.      * be sure to adjust only by full increments.
  2400.      */
  2401.     if ((canvasPtr->confine) && (canvasPtr->regionString != NULL)) {
  2402. left = xOrigin + canvasPtr->inset - canvasPtr->scrollX1;
  2403. right = canvasPtr->scrollX2
  2404. - (xOrigin + Tk_Width(canvasPtr->tkwin) - canvasPtr->inset);
  2405. top = yOrigin + canvasPtr->inset - canvasPtr->scrollY1;
  2406. bottom = canvasPtr->scrollY2
  2407. - (yOrigin + Tk_Height(canvasPtr->tkwin) - canvasPtr->inset);
  2408. if ((left < 0) && (right > 0)) {
  2409.     delta = (right > -left) ? -left : right;
  2410.     if (canvasPtr->xScrollIncrement > 0) {
  2411. delta -= delta % canvasPtr->xScrollIncrement;
  2412.     }
  2413.     xOrigin += delta;
  2414. } else if ((right < 0) && (left > 0)) {
  2415.     delta = (left > -right) ? -right : left;
  2416.     if (canvasPtr->xScrollIncrement > 0) {
  2417. delta -= delta % canvasPtr->xScrollIncrement;
  2418.     }
  2419.     xOrigin -= delta;
  2420. }
  2421. if ((top < 0) && (bottom > 0)) {
  2422.     delta = (bottom > -top) ? -top : bottom;
  2423.     if (canvasPtr->yScrollIncrement > 0) {
  2424. delta -= delta % canvasPtr->yScrollIncrement;
  2425.     }
  2426.     yOrigin += delta;
  2427. } else if ((bottom < 0) && (top > 0)) {
  2428.     delta = (top > -bottom) ? -bottom : top;
  2429.     if (canvasPtr->yScrollIncrement > 0) {
  2430. delta -= delta % canvasPtr->yScrollIncrement;
  2431.     }
  2432.     yOrigin -= delta;
  2433. }
  2434.     }
  2435.     if ((xOrigin == canvasPtr->xOrigin) && (yOrigin == canvasPtr->yOrigin)) {
  2436. return;
  2437.     }
  2438.     /*
  2439.      * Tricky point: must redisplay not only everything that's visible
  2440.      * in the window's final configuration, but also everything that was
  2441.      * visible in the initial configuration.  This is needed because some
  2442.      * item types, like windows, need to know when they move off-screen
  2443.      * so they can explicitly undisplay themselves.
  2444.      */
  2445.     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  2446.     canvasPtr->xOrigin, canvasPtr->yOrigin,
  2447.     canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
  2448.     canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
  2449.     canvasPtr->xOrigin = xOrigin;
  2450.     canvasPtr->yOrigin = yOrigin;
  2451.     canvasPtr->flags |= UPDATE_SCROLLBARS;
  2452.     Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
  2453.     canvasPtr->xOrigin, canvasPtr->yOrigin,
  2454.     canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
  2455.     canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
  2456. }
  2457. /*
  2458.  *----------------------------------------------------------------------
  2459.  *
  2460.  * GetStringsFromObjs
  2461.  *
  2462.  * Results:
  2463.  * Converts object list into string list.
  2464.  *
  2465.  * Side effects:
  2466.  * Memory is allocated for the argv array, which must
  2467.  * be freed using ckfree() when no longer needed.
  2468.  *
  2469.  *----------------------------------------------------------------------
  2470.  */
  2471. /* ARGSUSED */
  2472. static CONST char **
  2473. GetStringsFromObjs(argc, objv)
  2474.     int argc;
  2475.     Tcl_Obj *CONST objv[];
  2476. {
  2477.     register int i;
  2478.     CONST char **argv;
  2479.     if (argc <= 0) {
  2480. return NULL;
  2481.     }
  2482.     argv = (CONST char **) ckalloc((argc+1) * sizeof(char *));
  2483.     for (i = 0; i < argc; i++) {
  2484. argv[i] = Tcl_GetStringFromObj(objv[i], NULL);
  2485.     }
  2486.     argv[argc] = 0;
  2487.     return argv;
  2488. }
  2489. /*
  2490.  *--------------------------------------------------------------
  2491.  *
  2492.  * Tk_CanvasPsColor --
  2493.  *
  2494.  * This procedure is called by individual canvas items when
  2495.  * they want to set a color value for output.  Given information
  2496.  * about an X color, this procedure will generate Postscript
  2497.  * commands to set up an appropriate color in Postscript.
  2498.  *
  2499.  * Results:
  2500.  * Returns a standard Tcl return value.  If an error occurs
  2501.  * then an error message will be left in interp->result.
  2502.  * If no error occurs, then additional Postscript will be
  2503.  * appended to interp->result.
  2504.  *
  2505.  * Side effects:
  2506.  * None.
  2507.  *
  2508.  *--------------------------------------------------------------
  2509.  */
  2510. int
  2511. Tk_CanvasPsColor(interp, canvas, colorPtr)
  2512.     Tcl_Interp *interp; /* Interpreter for returning Postscript
  2513.  * or error message. */
  2514.     Tk_Canvas canvas; /* Information about canvas. */
  2515.     XColor *colorPtr; /* Information about color. */
  2516. {
  2517.     return Tk_PostscriptColor(interp, ((TkCanvas *) canvas)->psInfo,
  2518.     colorPtr);
  2519. }
  2520. /*
  2521.  *--------------------------------------------------------------
  2522.  *
  2523.  * Tk_CanvasPsFont --
  2524.  *
  2525.  * This procedure is called by individual canvas items when
  2526.  * they want to output text.  Given information about an X
  2527.  * font, this procedure will generate Postscript commands
  2528.  * to set up an appropriate font in Postscript.
  2529.  *
  2530.  * Results:
  2531.  * Returns a standard Tcl return value.  If an error occurs
  2532.  * then an error message will be left in interp->result.
  2533.  * If no error occurs, then additional Postscript will be
  2534.  * appended to the interp->result.
  2535.  *
  2536.  * Side effects:
  2537.  * The Postscript font name is entered into psInfoPtr->fontTable
  2538.  * if it wasn't already there.
  2539.  *
  2540.  *--------------------------------------------------------------
  2541.  */
  2542. int
  2543. Tk_CanvasPsFont(interp, canvas, tkfont)
  2544.     Tcl_Interp *interp; /* Interpreter for returning Postscript
  2545.  * or error message. */
  2546.     Tk_Canvas canvas; /* Information about canvas. */
  2547.     Tk_Font tkfont; /* Information about font in which text
  2548.  * is to be printed. */
  2549. {
  2550.     return Tk_PostscriptFont(interp, ((TkCanvas *) canvas)->psInfo, tkfont);
  2551. }
  2552. /*
  2553.  *--------------------------------------------------------------
  2554.  *
  2555.  * Tk_CanvasPsBitmap --
  2556.  *
  2557.  * This procedure is called to output the contents of a
  2558.  * sub-region of a bitmap in proper image data format for
  2559.  * Postscript (i.e. data between angle brackets, one bit
  2560.  * per pixel).
  2561.  *
  2562.  * Results:
  2563.  * Returns a standard Tcl return value.  If an error occurs
  2564.  * then an error message will be left in interp->result.
  2565.  * If no error occurs, then additional Postscript will be
  2566.  * appended to interp->result.
  2567.  *
  2568.  * Side effects:
  2569.  * None.
  2570.  *
  2571.  *--------------------------------------------------------------
  2572.  */
  2573. int
  2574. Tk_CanvasPsBitmap(interp, canvas, bitmap, startX, startY, width, height)
  2575.     Tcl_Interp *interp; /* Interpreter for returning Postscript
  2576.  * or error message. */
  2577.     Tk_Canvas canvas; /* Information about canvas. */
  2578.     Pixmap bitmap; /* Bitmap for which to generate
  2579.  * Postscript. */
  2580.     int startX, startY; /* Coordinates of upper-left corner
  2581.  * of rectangular region to output. */
  2582.     int width, height; /* Height of rectangular region. */
  2583. {
  2584.     return Tk_PostscriptBitmap(interp, ((TkCanvas *) canvas)->tkwin,
  2585.     ((TkCanvas *) canvas)->psInfo, bitmap, startX, startY,
  2586.     width, height);
  2587. }
  2588. /*
  2589.  *--------------------------------------------------------------
  2590.  *
  2591.  * Tk_CanvasPsStipple --
  2592.  *
  2593.  * This procedure is called by individual canvas items when
  2594.  * they have created a path that they'd like to be filled with
  2595.  * a stipple pattern.  Given information about an X bitmap,
  2596.  * this procedure will generate Postscript commands to fill
  2597.  * the current clip region using a stipple pattern defined by the
  2598.  * bitmap.
  2599.  *
  2600.  * Results:
  2601.  * Returns a standard Tcl return value.  If an error occurs
  2602.  * then an error message will be left in interp->result.
  2603.  * If no error occurs, then additional Postscript will be
  2604.  * appended to interp->result.
  2605.  *
  2606.  * Side effects:
  2607.  * None.
  2608.  *
  2609.  *--------------------------------------------------------------
  2610.  */
  2611. int
  2612. Tk_CanvasPsStipple(interp, canvas, bitmap)
  2613.     Tcl_Interp *interp; /* Interpreter for returning Postscript
  2614.  * or error message. */
  2615.     Tk_Canvas canvas; /* Information about canvas. */
  2616.     Pixmap bitmap; /* Bitmap to use for stippling. */
  2617. {
  2618.     return Tk_PostscriptStipple(interp, ((TkCanvas *) canvas)->tkwin,
  2619.     ((TkCanvas *) canvas)->psInfo, bitmap);
  2620. }
  2621. /*
  2622.  *--------------------------------------------------------------
  2623.  *
  2624.  * Tk_CanvasPsY --
  2625.  *
  2626.  * Given a y-coordinate in canvas coordinates, this procedure
  2627.  * returns a y-coordinate to use for Postscript output.
  2628.  *
  2629.  * Results:
  2630.  * Returns the Postscript coordinate that corresponds to
  2631.  * "y".
  2632.  *
  2633.  * Side effects:
  2634.  * None.
  2635.  *
  2636.  *--------------------------------------------------------------
  2637.  */
  2638. double
  2639. Tk_CanvasPsY(canvas, y)
  2640.     Tk_Canvas canvas; /* Token for canvas on whose behalf
  2641.  * Postscript is being generated. */
  2642.     double y; /* Y-coordinate in canvas coords. */
  2643. {
  2644.     return Tk_PostscriptY(y, ((TkCanvas *) canvas)->psInfo);
  2645. }
  2646. /*
  2647.  *--------------------------------------------------------------
  2648.  *
  2649.  * Tk_CanvasPsPath --
  2650.  *
  2651.  * Given an array of points for a path, generate Postscript
  2652.  * commands to create the path.
  2653.  *
  2654.  * Results:
  2655.  * Postscript commands get appended to what's in interp->result.
  2656.  *
  2657.  * Side effects:
  2658.  * None.
  2659.  *
  2660.  *--------------------------------------------------------------
  2661.  */
  2662. void
  2663. Tk_CanvasPsPath(interp, canvas, coordPtr, numPoints)
  2664.     Tcl_Interp *interp; /* Put generated Postscript in this
  2665.  * interpreter's result field. */
  2666.     Tk_Canvas canvas; /* Canvas on whose behalf Postscript
  2667.  * is being generated. */
  2668.     double *coordPtr; /* Pointer to first in array of
  2669.  * 2*numPoints coordinates giving
  2670.  * points for path. */
  2671.     int numPoints; /* Number of points at *coordPtr. */
  2672. {
  2673.     Tk_PostscriptPath(interp, ((TkCanvas *) canvas)->psInfo,
  2674.     coordPtr, numPoints);
  2675. }