tkCanvas.c
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:173k
- }
- searchPtr->lastPtr = lastPtr;
- searchPtr->searchOver = 1;
- canvasPtr->hotPtr = itemPtr;
- canvasPtr->hotPrevPtr = lastPtr;
- return itemPtr;
- }
- }
- searchPtr->tag = uid = Tk_GetUid(tag);
- if (uid == Tk_GetUid("all")) {
- /*
- * All items match.
- */
- searchPtr->tag = NULL;
- searchPtr->lastPtr = NULL;
- searchPtr->currentPtr = canvasPtr->firstItemPtr;
- return canvasPtr->firstItemPtr;
- }
- /*
- * None of the above. Search for an item with a matching tag.
- */
- for (lastPtr = NULL, itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
- lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
- for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
- count > 0; tagPtr++, count--) {
- if (*tagPtr == uid) {
- searchPtr->lastPtr = lastPtr;
- searchPtr->currentPtr = itemPtr;
- return itemPtr;
- }
- }
- }
- searchPtr->lastPtr = lastPtr;
- searchPtr->searchOver = 1;
- return NULL;
- }
- /*
- *--------------------------------------------------------------
- *
- * NextItem --
- *
- * This procedure returns successive items that match a given
- * tag; it should be called only after StartTagSearch has been
- * used to begin a search.
- *
- * Results:
- * The return value is a pointer to the next item that matches
- * the tag specified to StartTagSearch, or NULL if no such
- * item exists. *SearchPtr is updated so that the next call
- * to this procedure will return the next item.
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
- static Tk_Item *
- NextItem(searchPtr)
- TagSearch *searchPtr; /* Record describing search in
- * progress. */
- {
- Tk_Item *itemPtr, *lastPtr;
- int count;
- Tk_Uid uid;
- Tk_Uid *tagPtr;
- /*
- * Find next item in list (this may not actually be a suitable
- * one to return), and return if there are no items left.
- */
- lastPtr = searchPtr->lastPtr;
- if (lastPtr == NULL) {
- itemPtr = searchPtr->canvasPtr->firstItemPtr;
- } else {
- itemPtr = lastPtr->nextPtr;
- }
- if ((itemPtr == NULL) || (searchPtr->searchOver)) {
- searchPtr->searchOver = 1;
- return NULL;
- }
- if (itemPtr != searchPtr->currentPtr) {
- /*
- * The structure of the list has changed. Probably the
- * previously-returned item was removed from the list.
- * In this case, don't advance lastPtr; just return
- * its new successor (i.e. do nothing here).
- */
- } else {
- lastPtr = itemPtr;
- itemPtr = lastPtr->nextPtr;
- }
- /*
- * Handle special case of "all" search by returning next item.
- */
- uid = searchPtr->tag;
- if (uid == NULL) {
- searchPtr->lastPtr = lastPtr;
- searchPtr->currentPtr = itemPtr;
- return itemPtr;
- }
- /*
- * Look for an item with a particular tag.
- */
- for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
- for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
- count > 0; tagPtr++, count--) {
- if (*tagPtr == uid) {
- searchPtr->lastPtr = lastPtr;
- searchPtr->currentPtr = itemPtr;
- return itemPtr;
- }
- }
- }
- searchPtr->lastPtr = lastPtr;
- searchPtr->searchOver = 1;
- return NULL;
- }
- #else /* USE_OLD_TAG_SEARCH */
- /*
- *----------------------------------------------------------------------
- *
- * GetStaticUids --
- *
- *This procedure is invoked to return a structure filled with
- *the Uids used when doing tag searching. If it was never before
- *called in the current thread, it initializes the structure for
- *that thread (uids are only ever local to one thread [Bug
- *1114977]).
- *
- * Results:
- *None.
- *
- * Side effects:
- *None.
- *
- *----------------------------------------------------------------------
- */
- static SearchUids *
- GetStaticUids()
- {
- SearchUids *searchUids = (SearchUids *)
- Tcl_GetThreadData(&dataKey, sizeof(SearchUids));
- if (searchUids->allUid == NULL) {
- searchUids->allUid = Tk_GetUid("all");
- searchUids->currentUid = Tk_GetUid("current");
- searchUids->andUid = Tk_GetUid("&&");
- searchUids->orUid = Tk_GetUid("||");
- searchUids->xorUid = Tk_GetUid("^");
- searchUids->parenUid = Tk_GetUid("(");
- searchUids->endparenUid = Tk_GetUid(")");
- searchUids->negparenUid = Tk_GetUid("!(");
- searchUids->tagvalUid = Tk_GetUid("!!");
- searchUids->negtagvalUid = Tk_GetUid("!");
- }
- return searchUids;
- }
- /*
- *--------------------------------------------------------------
- *
- * TagSearchExprInit --
- *
- * This procedure allocates and initializes one TagSearchExpr struct.
- *
- * Results:
- *
- * Side effects:
- *
- *--------------------------------------------------------------
- */
- static void
- TagSearchExprInit(exprPtrPtr)
- TagSearchExpr **exprPtrPtr;
- {
- TagSearchExpr* expr = *exprPtrPtr;
- if (! expr) {
- expr = (TagSearchExpr *) ckalloc(sizeof(TagSearchExpr));
- expr->allocated = 0;
- expr->uids = NULL;
- expr->next = NULL;
- }
- expr->uid = NULL;
- expr->index = 0;
- expr->length = 0;
- *exprPtrPtr = expr;
- }
-
- /*
- *--------------------------------------------------------------
- *
- * TagSearchExprDestroy --
- *
- * This procedure destroys one TagSearchExpr structure.
- *
- * Results:
- *
- * Side effects:
- *
- *--------------------------------------------------------------
- */
- static void
- TagSearchExprDestroy(expr)
- TagSearchExpr *expr;
- {
- if (expr) {
- if (expr->uids) {
- ckfree((char *)expr->uids);
- }
- ckfree((char *)expr);
- }
- }
- /*
- *--------------------------------------------------------------
- *
- * TagSearchScan --
- *
- * This procedure is called to initiate an enumeration of
- * all items in a given canvas that contain a tag that matches
- * the tagOrId expression.
- *
- * Results:
- * The return value indicates if the tagOrId expression
- * was successfully scanned (syntax).
- * The information at *searchPtr is initialized
- * such that a call to TagSearchFirst, followed by
- * successive calls to TagSearchNext will return items
- * that match tag.
- *
- * Side effects:
- * SearchPtr is linked into a list of searches in progress
- * on canvasPtr, so that elements can safely be deleted
- * while the search is in progress.
- *
- *--------------------------------------------------------------
- */
- static int
- TagSearchScan(canvasPtr, tagObj, searchPtrPtr)
- TkCanvas *canvasPtr; /* Canvas whose items are to be
- * searched. */
- Tcl_Obj *tagObj; /* Object giving tag value. */
- TagSearch **searchPtrPtr; /* Record describing tag search;
- * will be initialized here. */
- {
- char *tag = Tcl_GetStringFromObj(tagObj,NULL);
- int i;
- TagSearch *searchPtr;
- /*
- * Initialize the search.
- */
- if (*searchPtrPtr) {
- searchPtr = *searchPtrPtr;
- } else {
- /* Allocate primary search struct on first call */
- *searchPtrPtr = searchPtr = (TagSearch *) ckalloc(sizeof(TagSearch));
- searchPtr->expr = NULL;
- /* Allocate buffer for rewritten tags (after de-escaping) */
- searchPtr->rewritebufferAllocated = 100;
- searchPtr->rewritebuffer =
- ckalloc(searchPtr->rewritebufferAllocated);
- }
- TagSearchExprInit(&(searchPtr->expr));
- /* How long is the tagOrId ? */
- searchPtr->stringLength = strlen(tag);
- /* Make sure there is enough buffer to hold rewritten tags */
- if ((unsigned int)searchPtr->stringLength >=
- searchPtr->rewritebufferAllocated) {
- searchPtr->rewritebufferAllocated = searchPtr->stringLength + 100;
- searchPtr->rewritebuffer =
- ckrealloc(searchPtr->rewritebuffer,
- searchPtr->rewritebufferAllocated);
- }
- /* Initialize search */
- searchPtr->canvasPtr = canvasPtr;
- searchPtr->searchOver = 0;
- searchPtr->type = 0;
- /*
- * Find the first matching item in one of several ways. If the tag
- * is a number then it selects the single item with the matching
- * identifier. In this case see if the item being requested is the
- * hot item, in which case the search can be skipped.
- */
- if (searchPtr->stringLength && isdigit(UCHAR(*tag))) {
- char *end;
- searchPtr->id = strtoul(tag, &end, 0);
- if (*end == 0) {
- searchPtr->type = 1;
- return TCL_OK;
- }
- }
- /*
- * For all other tags and tag expressions convert to a UID.
- * This UID is kept forever, but this should be thought of
- * as a cache rather than as a memory leak.
- */
- searchPtr->expr->uid = Tk_GetUid(tag);
- /* short circuit impossible searches for null tags */
- if (searchPtr->stringLength == 0) {
- return TCL_OK;
- }
- /*
- * Pre-scan tag for at least one unquoted "&&" "||" "^" "!"
- * if not found then use string as simple tag
- */
- for (i = 0; i < searchPtr->stringLength ; i++) {
- if (tag[i] == '"') {
- i++;
- for ( ; i < searchPtr->stringLength; i++) {
- if (tag[i] == '\') {
- i++;
- continue;
- }
- if (tag[i] == '"') {
- break;
- }
- }
- } else {
- if ((tag[i] == '&' && tag[i+1] == '&')
- || (tag[i] == '|' && tag[i+1] == '|')
- || (tag[i] == '^')
- || (tag[i] == '!')) {
- searchPtr->type = 4;
- break;
- }
- }
- }
- searchPtr->string = tag;
- searchPtr->stringIndex = 0;
- if (searchPtr->type == 4) {
- /*
- * an operator was found in the prescan, so
- * now compile the tag expression into array of Tk_Uid
- * flagging any syntax errors found
- */
- if (TagSearchScanExpr(canvasPtr->interp, searchPtr, searchPtr->expr) != TCL_OK) {
- /* Syntax error in tag expression */
- /* Result message set by TagSearchScanExpr */
- return TCL_ERROR;
- }
- searchPtr->expr->length = searchPtr->expr->index;
- } else {
- if (searchPtr->expr->uid == GetStaticUids()->allUid) {
- /*
- * All items match.
- */
- searchPtr->type = 2;
- } else {
- /*
- * Optimized single-tag search
- */
- searchPtr->type = 3;
- }
- }
- return TCL_OK;
- }
- /*
- *--------------------------------------------------------------
- *
- * TagSearchDestroy --
- *
- * This procedure destroys any dynamic structures that
- * may have been allocated by TagSearchScan.
- *
- * Results:
- *
- * Side effects:
- *
- *--------------------------------------------------------------
- */
- static void
- TagSearchDestroy(searchPtr)
- TagSearch *searchPtr; /* Record describing tag search */
- {
- if (searchPtr) {
- TagSearchExprDestroy(searchPtr->expr);
- ckfree((char *)searchPtr->rewritebuffer);
- ckfree((char *)searchPtr);
- }
- }
- /*
- *--------------------------------------------------------------
- *
- * TagSearchScanExpr --
- *
- * This recursive procedure is called to scan a tag expression
- * and compile it into an array of Tk_Uids.
- *
- * Results:
- * The return value indicates if the tagOrId expression
- * was successfully scanned (syntax).
- * The information at *searchPtr is initialized
- * such that a call to TagSearchFirst, followed by
- * successive calls to TagSearchNext will return items
- * that match tag.
- *
- * Side effects:
- *
- *--------------------------------------------------------------
- */
- static int
- TagSearchScanExpr(interp, searchPtr, expr)
- Tcl_Interp *interp; /* Current interpreter. */
- TagSearch *searchPtr; /* Search data */
- TagSearchExpr *expr; /* compiled expression result */
- {
- int looking_for_tag; /* When true, scanner expects
- * next char(s) to be a tag,
- * else operand expected */
- int found_tag; /* One or more tags found */
- int found_endquote; /* For quoted tag string parsing */
- int negate_result; /* Pending negation of next tag value */
- char *tag; /* tag from tag expression string */
- char c;
- SearchUids *searchUids; /* Collection of uids for basic search
- * expression terms. */
-
- searchUids = GetStaticUids();
- negate_result = 0;
- found_tag = 0;
- looking_for_tag = 1;
- while (searchPtr->stringIndex < searchPtr->stringLength) {
- c = searchPtr->string[searchPtr->stringIndex++];
- if (expr->allocated == expr->index) {
- expr->allocated += 15;
- if (expr->uids) {
- expr->uids =
- (Tk_Uid *) ckrealloc((char *)(expr->uids),
- (expr->allocated)*sizeof(Tk_Uid));
- } else {
- expr->uids =
- (Tk_Uid *) ckalloc((expr->allocated)*sizeof(Tk_Uid));
- }
- }
- if (looking_for_tag) {
- switch (c) {
- case ' ' : /* ignore unquoted whitespace */
- case 't' :
- case 'n' :
- case 'r' :
- break;
- case '!' : /* negate next tag or subexpr */
- if (looking_for_tag > 1) {
- Tcl_AppendResult(interp,
- "Too many '!' in tag search expression",
- (char *) NULL);
- return TCL_ERROR;
- }
- looking_for_tag++;
- negate_result = 1;
- break;
- case '(' : /* scan (negated) subexpr recursively */
- if (negate_result) {
- expr->uids[expr->index++] = searchUids->negparenUid;
- negate_result = 0;
- } else {
- expr->uids[expr->index++] = searchUids->parenUid;
- }
- if (TagSearchScanExpr(interp, searchPtr, expr) != TCL_OK) {
- /* Result string should be already set
- * by nested call to tag_expr_scan() */
- return TCL_ERROR;
- }
- looking_for_tag = 0;
- found_tag = 1;
- break;
- case '"' : /* quoted tag string */
- if (negate_result) {
- expr->uids[expr->index++] = searchUids->negtagvalUid;
- negate_result = 0;
- } else {
- expr->uids[expr->index++] = searchUids->tagvalUid;
- }
- tag = searchPtr->rewritebuffer;
- found_endquote = 0;
- while (searchPtr->stringIndex < searchPtr->stringLength) {
- c = searchPtr->string[searchPtr->stringIndex++];
- if (c == '\') {
- c = searchPtr->string[searchPtr->stringIndex++];
- }
- if (c == '"') {
- found_endquote = 1;
- break;
- }
- *tag++ = c;
- }
- if (! found_endquote) {
- Tcl_AppendResult(interp,
- "Missing endquote in tag search expression",
- (char *) NULL);
- return TCL_ERROR;
- }
- if (! (tag - searchPtr->rewritebuffer)) {
- Tcl_AppendResult(interp,
- "Null quoted tag string in tag search expression",
- (char *) NULL);
- return TCL_ERROR;
- }
- *tag++ = ' ';
- expr->uids[expr->index++] =
- Tk_GetUid(searchPtr->rewritebuffer);
- looking_for_tag = 0;
- found_tag = 1;
- break;
- case '&' : /* illegal chars when looking for tag */
- case '|' :
- case '^' :
- case ')' :
- Tcl_AppendResult(interp,
- "Unexpected operator in tag search expression",
- (char *) NULL);
- return TCL_ERROR;
- default : /* unquoted tag string */
- if (negate_result) {
- expr->uids[expr->index++] = searchUids->negtagvalUid;
- negate_result = 0;
- } else {
- expr->uids[expr->index++] = searchUids->tagvalUid;
- }
- tag = searchPtr->rewritebuffer;
- *tag++ = c;
- /* copy rest of tag, including any embedded whitespace */
- while (searchPtr->stringIndex < searchPtr->stringLength) {
- c = searchPtr->string[searchPtr->stringIndex];
- if (c == '!' || c == '&' || c == '|' || c == '^'
- || c == '(' || c == ')' || c == '"') {
- break;
- }
- *tag++ = c;
- searchPtr->stringIndex++;
- }
- /* remove trailing whitespace */
- while (1) {
- c = *--tag;
- /* there must have been one non-whitespace char,
- * so this will terminate */
- if (c != ' ' && c != 't' && c != 'n' && c != 'r') {
- break;
- }
- }
- *++tag = ' ';
- expr->uids[expr->index++] =
- Tk_GetUid(searchPtr->rewritebuffer);
- looking_for_tag = 0;
- found_tag = 1;
- }
- } else { /* ! looking_for_tag */
- switch (c) {
- case ' ' : /* ignore whitespace */
- case 't' :
- case 'n' :
- case 'r' :
- break;
- case '&' : /* AND operator */
- c = searchPtr->string[searchPtr->stringIndex++];
- if (c != '&') {
- Tcl_AppendResult(interp,
- "Singleton '&' in tag search expression",
- (char *) NULL);
- return TCL_ERROR;
- }
- expr->uids[expr->index++] = searchUids->andUid;
- looking_for_tag = 1;
- break;
- case '|' : /* OR operator */
- c = searchPtr->string[searchPtr->stringIndex++];
- if (c != '|') {
- Tcl_AppendResult(interp,
- "Singleton '|' in tag search expression",
- (char *) NULL);
- return TCL_ERROR;
- }
- expr->uids[expr->index++] = searchUids->orUid;
- looking_for_tag = 1;
- break;
- case '^' : /* XOR operator */
- expr->uids[expr->index++] = searchUids->xorUid;
- looking_for_tag = 1;
- break;
- case ')' : /* end subexpression */
- expr->uids[expr->index++] = searchUids->endparenUid;
- goto breakwhile;
- default : /* syntax error */
- Tcl_AppendResult(interp,
- "Invalid boolean operator in tag search expression",
- (char *) NULL);
- return TCL_ERROR;
- }
- }
- }
- breakwhile:
- if (found_tag && ! looking_for_tag) {
- return TCL_OK;
- }
- Tcl_AppendResult(interp, "Missing tag in tag search expression",
- (char *) NULL);
- return TCL_ERROR;
- }
- /*
- *--------------------------------------------------------------
- *
- * TagSearchEvalExpr --
- *
- * This recursive procedure is called to eval a tag expression.
- *
- * Results:
- * The return value indicates if the tagOrId expression
- * successfully matched the tags of the current item.
- *
- * Side effects:
- *
- *--------------------------------------------------------------
- */
- static int
- TagSearchEvalExpr(expr, itemPtr)
- TagSearchExpr *expr; /* Search expression */
- Tk_Item *itemPtr; /* Item being test for match */
- {
- int looking_for_tag; /* When true, scanner expects
- * next char(s) to be a tag,
- * else operand expected */
- int negate_result; /* Pending negation of next tag value */
- Tk_Uid uid;
- Tk_Uid *tagPtr;
- int count;
- int result; /* Value of expr so far */
- int parendepth;
- SearchUids *searchUids; /* Collection of uids for basic search
- * expression terms. */
- searchUids = GetStaticUids();
- result = 0; /* just to keep the compiler quiet */
- negate_result = 0;
- looking_for_tag = 1;
- while (expr->index < expr->length) {
- uid = expr->uids[expr->index++];
- if (looking_for_tag) {
- if (uid == searchUids->tagvalUid) {
- /*
- * assert(expr->index < expr->length);
- */
- uid = expr->uids[expr->index++];
- result = 0;
- /*
- * set result 1 if tag is found in item's tags
- */
- for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
- count > 0; tagPtr++, count--) {
- if (*tagPtr == uid) {
- result = 1;
- break;
- }
- }
- } else if (uid == searchUids->negtagvalUid) {
- negate_result = ! negate_result;
- /*
- * assert(expr->index < expr->length);
- */
- uid = expr->uids[expr->index++];
- result = 0;
- /*
- * set result 1 if tag is found in item's tags
- */
- for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
- count > 0; tagPtr++, count--) {
- if (*tagPtr == uid) {
- result = 1;
- break;
- }
- }
- } else if (uid == searchUids->parenUid) {
- /*
- * evaluate subexpressions with recursion
- */
- result = TagSearchEvalExpr(expr, itemPtr);
- } else if (uid == searchUids->negparenUid) {
- negate_result = ! negate_result;
- /*
- * evaluate subexpressions with recursion
- */
- result = TagSearchEvalExpr(expr, itemPtr);
- /*
- * } else {
- * assert(0);
- */
- }
- if (negate_result) {
- result = ! result;
- negate_result = 0;
- }
- looking_for_tag = 0;
- } else { /* ! looking_for_tag */
- if (((uid == searchUids->andUid) && (!result)) ||
- ((uid == searchUids->orUid) && result)) {
- /*
- * short circuit expression evaluation
- *
- * if result before && is 0, or result before || is 1,
- * then the expression is decided and no further
- * evaluation is needed.
- */
- parendepth = 0;
- while (expr->index < expr->length) {
- uid = expr->uids[expr->index++];
- if (uid == searchUids->tagvalUid ||
- uid == searchUids->negtagvalUid) {
- expr->index++;
- continue;
- }
- if (uid == searchUids->parenUid ||
- uid == searchUids->negparenUid) {
- parendepth++;
- continue;
- }
- if (uid == searchUids->endparenUid) {
- parendepth--;
- if (parendepth < 0) {
- break;
- }
- }
- }
- return result;
- } else if (uid == searchUids->xorUid) {
- /*
- * if the previous result was 1
- * then negate the next result
- */
- negate_result = result;
- } else if (uid == searchUids->endparenUid) {
- return result;
- /*
- * } else {
- * assert(0);
- */
- }
- looking_for_tag = 1;
- }
- }
- /*
- * assert(! looking_for_tag);
- */
- return result;
- }
- /*
- *--------------------------------------------------------------
- *
- * TagSearchFirst --
- *
- * This procedure is called to get the first item
- * item that matches a preestablished search predicate
- * that was set by TagSearchScan.
- *
- * Results:
- * The return value is a pointer to the first item, or NULL
- * if there is no such item. The information at *searchPtr
- * is updated such that successive calls to TagSearchNext
- * will return successive items.
- *
- * Side effects:
- * SearchPtr is linked into a list of searches in progress
- * on canvasPtr, so that elements can safely be deleted
- * while the search is in progress.
- *
- *--------------------------------------------------------------
- */
- static Tk_Item *
- TagSearchFirst(searchPtr)
- TagSearch *searchPtr; /* Record describing tag search */
- {
- Tk_Item *itemPtr, *lastPtr;
- Tk_Uid uid, *tagPtr;
- int count;
- /* short circuit impossible searches for null tags */
- if (searchPtr->stringLength == 0) {
- return NULL;
- }
- /*
- * Find the first matching item in one of several ways. If the tag
- * is a number then it selects the single item with the matching
- * identifier. In this case see if the item being requested is the
- * hot item, in which case the search can be skipped.
- */
- if (searchPtr->type == 1) {
- Tcl_HashEntry *entryPtr;
- itemPtr = searchPtr->canvasPtr->hotPtr;
- lastPtr = searchPtr->canvasPtr->hotPrevPtr;
- if ((itemPtr == NULL) || (itemPtr->id != searchPtr->id) ||
- (lastPtr == NULL) || (lastPtr->nextPtr != itemPtr)) {
- entryPtr = Tcl_FindHashEntry(&searchPtr->canvasPtr->idTable,
- (char *) searchPtr->id);
- if (entryPtr != NULL) {
- itemPtr = (Tk_Item *)Tcl_GetHashValue(entryPtr);
- lastPtr = itemPtr->prevPtr;
- } else {
- lastPtr = itemPtr = NULL;
- }
- }
- searchPtr->lastPtr = lastPtr;
- searchPtr->searchOver = 1;
- searchPtr->canvasPtr->hotPtr = itemPtr;
- searchPtr->canvasPtr->hotPrevPtr = lastPtr;
- return itemPtr;
- }
- if (searchPtr->type == 2) {
- /*
- * All items match.
- */
- searchPtr->lastPtr = NULL;
- searchPtr->currentPtr = searchPtr->canvasPtr->firstItemPtr;
- return searchPtr->canvasPtr->firstItemPtr;
- }
- if (searchPtr->type == 3) {
- /*
- * Optimized single-tag search
- */
- uid = searchPtr->expr->uid;
- for (lastPtr = NULL, itemPtr = searchPtr->canvasPtr->firstItemPtr;
- itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
- for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
- count > 0; tagPtr++, count--) {
- if (*tagPtr == uid) {
- searchPtr->lastPtr = lastPtr;
- searchPtr->currentPtr = itemPtr;
- return itemPtr;
- }
- }
- }
- } else {
- /*
- * None of the above. Search for an item matching the tag expression.
- */
- for (lastPtr = NULL, itemPtr = searchPtr->canvasPtr->firstItemPtr;
- itemPtr != NULL; lastPtr=itemPtr, itemPtr=itemPtr->nextPtr) {
- searchPtr->expr->index = 0;
- if (TagSearchEvalExpr(searchPtr->expr, itemPtr)) {
- searchPtr->lastPtr = lastPtr;
- searchPtr->currentPtr = itemPtr;
- return itemPtr;
- }
- }
- }
- searchPtr->lastPtr = lastPtr;
- searchPtr->searchOver = 1;
- return NULL;
- }
- /*
- *--------------------------------------------------------------
- *
- * TagSearchNext --
- *
- * This procedure returns successive items that match a given
- * tag; it should be called only after TagSearchFirst has been
- * used to begin a search.
- *
- * Results:
- * The return value is a pointer to the next item that matches
- * the tag expr specified to TagSearchScan, or NULL if no such
- * item exists. *SearchPtr is updated so that the next call
- * to this procedure will return the next item.
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
- static Tk_Item *
- TagSearchNext(searchPtr)
- TagSearch *searchPtr; /* Record describing search in
- * progress. */
- {
- Tk_Item *itemPtr, *lastPtr;
- Tk_Uid uid, *tagPtr;
- int count;
- /*
- * Find next item in list (this may not actually be a suitable
- * one to return), and return if there are no items left.
- */
- lastPtr = searchPtr->lastPtr;
- if (lastPtr == NULL) {
- itemPtr = searchPtr->canvasPtr->firstItemPtr;
- } else {
- itemPtr = lastPtr->nextPtr;
- }
- if ((itemPtr == NULL) || (searchPtr->searchOver)) {
- searchPtr->searchOver = 1;
- return NULL;
- }
- if (itemPtr != searchPtr->currentPtr) {
- /*
- * The structure of the list has changed. Probably the
- * previously-returned item was removed from the list.
- * In this case, don't advance lastPtr; just return
- * its new successor (i.e. do nothing here).
- */
- } else {
- lastPtr = itemPtr;
- itemPtr = lastPtr->nextPtr;
- }
- if (searchPtr->type == 2) {
- /*
- * All items match.
- */
- searchPtr->lastPtr = lastPtr;
- searchPtr->currentPtr = itemPtr;
- return itemPtr;
- }
- if (searchPtr->type == 3) {
- /*
- * Optimized single-tag search
- */
- uid = searchPtr->expr->uid;
- for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
- for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
- count > 0; tagPtr++, count--) {
- if (*tagPtr == uid) {
- searchPtr->lastPtr = lastPtr;
- searchPtr->currentPtr = itemPtr;
- return itemPtr;
- }
- }
- }
- searchPtr->lastPtr = lastPtr;
- searchPtr->searchOver = 1;
- return NULL;
- }
- /*
- * Else.... evaluate tag expression
- */
- for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
- searchPtr->expr->index = 0;
- if (TagSearchEvalExpr(searchPtr->expr, itemPtr)) {
- searchPtr->lastPtr = lastPtr;
- searchPtr->currentPtr = itemPtr;
- return itemPtr;
- }
- }
- searchPtr->lastPtr = lastPtr;
- searchPtr->searchOver = 1;
- return NULL;
- }
- #endif /* USE_OLD_TAG_SEARCH */
- /*
- *--------------------------------------------------------------
- *
- * DoItem --
- *
- * This is a utility procedure called by FindItems. It
- * either adds itemPtr's id to the result forming in interp,
- * or it adds a new tag to itemPtr, depending on the value
- * of tag.
- *
- * Results:
- * None.
- *
- * Side effects:
- * If tag is NULL then itemPtr's id is added as a list element
- * to the interp's result; otherwise tag is added to itemPtr's
- * list of tags.
- *
- *--------------------------------------------------------------
- */
- static void
- DoItem(interp, itemPtr, tag)
- Tcl_Interp *interp; /* Interpreter in which to (possibly)
- * record item id. */
- Tk_Item *itemPtr; /* Item to (possibly) modify. */
- Tk_Uid tag; /* Tag to add to those already
- * present for item, or NULL. */
- {
- Tk_Uid *tagPtr;
- int count;
- /*
- * Handle the "add-to-result" case and return, if appropriate.
- */
- if (tag == NULL) {
- char msg[TCL_INTEGER_SPACE];
- sprintf(msg, "%d", itemPtr->id);
- Tcl_AppendElement(interp, msg);
- return;
- }
- for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
- count > 0; tagPtr++, count--) {
- if (tag == *tagPtr) {
- return;
- }
- }
- /*
- * Grow the tag space if there's no more room left in the current
- * block.
- */
- if (itemPtr->tagSpace == itemPtr->numTags) {
- Tk_Uid *newTagPtr;
- itemPtr->tagSpace += 5;
- newTagPtr = (Tk_Uid *) ckalloc((unsigned)
- (itemPtr->tagSpace * sizeof(Tk_Uid)));
- memcpy((VOID *) newTagPtr, (VOID *) itemPtr->tagPtr,
- (itemPtr->numTags * sizeof(Tk_Uid)));
- if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
- ckfree((char *) itemPtr->tagPtr);
- }
- itemPtr->tagPtr = newTagPtr;
- tagPtr = &itemPtr->tagPtr[itemPtr->numTags];
- }
- /*
- * Add in the new tag.
- */
- *tagPtr = tag;
- itemPtr->numTags++;
- }
- /*
- *--------------------------------------------------------------
- *
- * FindItems --
- *
- * This procedure does all the work of implementing the
- * "find" and "addtag" options of the canvas widget command,
- * which locate items that have certain features (location,
- * tags, position in display list, etc.).
- *
- * Results:
- * A standard Tcl return value. If newTag is NULL, then a
- * list of ids from all the items that match argc/argv is
- * returned in the interp's result. If newTag is NULL, then
- * the normal the interp's result is an empty string. If an error
- * occurs, then the interp's result will hold an error message.
- *
- * Side effects:
- * If newTag is non-NULL, then all the items that match the
- * information in argc/argv have that tag added to their
- * lists of tags.
- *
- *--------------------------------------------------------------
- */
- static int
- #ifdef USE_OLD_TAG_SEARCH
- FindItems(interp, canvasPtr, argc, argv, newTag, first)
- #else /* USE_OLD_TAG_SEARCH */
- FindItems(interp, canvasPtr, argc, argv, newTag, first, searchPtrPtr)
- #endif /* USE_OLD_TAG_SEARCH */
- Tcl_Interp *interp; /* Interpreter for error reporting. */
- TkCanvas *canvasPtr; /* Canvas whose items are to be
- * searched. */
- int argc; /* Number of entries in argv. Must be
- * greater than zero. */
- Tcl_Obj *CONST *argv; /* Arguments that describe what items
- * to search for (see user doc on
- * "find" and "addtag" options). */
- Tcl_Obj *newTag; /* If non-NULL, gives new tag to set
- * on all found items; if NULL, then
- * ids of found items are returned
- * in the interp's result. */
- int first; /* For error messages: gives number
- * of elements of argv which are already
- * handled. */
- #ifndef USE_OLD_TAG_SEARCH
- TagSearch **searchPtrPtr; /* From CanvasWidgetCmd local vars*/
- #endif /* not USE_OLD_TAG_SEARCH */
- {
- #ifdef USE_OLD_TAG_SEARCH
- TagSearch search;
- #endif /* USE_OLD_TAG_SEARCH */
- Tk_Item *itemPtr;
- Tk_Uid uid;
- int index;
- static CONST char *optionStrings[] = {
- "above", "all", "below", "closest",
- "enclosed", "overlapping", "withtag", NULL
- };
- enum options {
- CANV_ABOVE, CANV_ALL, CANV_BELOW, CANV_CLOSEST,
- CANV_ENCLOSED, CANV_OVERLAPPING, CANV_WITHTAG
- };
- if (newTag != NULL) {
- uid = Tk_GetUid(Tcl_GetStringFromObj(newTag, NULL));
- } else {
- uid = NULL;
- }
- if (Tcl_GetIndexFromObj(interp, argv[first], optionStrings, "search command", 0,
- &index) != TCL_OK) {
- return TCL_ERROR;
- }
- switch ((enum options) index) {
- case CANV_ABOVE: {
- Tk_Item *lastPtr = NULL;
- if (argc != first+2) {
- Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId");
- return TCL_ERROR;
- }
- #ifdef USE_OLD_TAG_SEARCH
- for (itemPtr = StartTagSearch(canvasPtr, argv[first+1], &search);
- itemPtr != NULL; itemPtr = NextItem(&search)) {
- #else /* USE_OLD_TAG_SEARCH */
- if (TagSearchScan(canvasPtr, argv[first+1], searchPtrPtr) != TCL_OK) {
- return TCL_ERROR;
- }
- for (itemPtr = TagSearchFirst(*searchPtrPtr);
- itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) {
- #endif /* USE_OLD_TAG_SEARCH */
- lastPtr = itemPtr;
- }
- if ((lastPtr != NULL) && (lastPtr->nextPtr != NULL)) {
- DoItem(interp, lastPtr->nextPtr, uid);
- }
- break;
- }
- case CANV_ALL: {
- if (argc != first+1) {
- Tcl_WrongNumArgs(interp, first+1, argv, (char *) NULL);
- return TCL_ERROR;
- }
- for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
- itemPtr = itemPtr->nextPtr) {
- DoItem(interp, itemPtr, uid);
- }
- break;
- }
- case CANV_BELOW: {
- Tk_Item *itemPtr;
- if (argc != first+2) {
- Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId");
- return TCL_ERROR;
- }
- #ifdef USE_OLD_TAG_SEARCH
- itemPtr = StartTagSearch(canvasPtr, argv[first+1], &search);
- #else /* USE_OLD_TAG_SEARCH */
- if (TagSearchScan(canvasPtr, argv[first+1], searchPtrPtr) != TCL_OK) {
- return TCL_ERROR;
- }
- itemPtr = TagSearchFirst(*searchPtrPtr);
- #endif /* USE_OLD_TAG_SEARCH */
- if (itemPtr != NULL) {
- if (itemPtr->prevPtr != NULL) {
- DoItem(interp, itemPtr->prevPtr, uid);
- }
- }
- break;
- }
- case CANV_CLOSEST: {
- double closestDist;
- Tk_Item *startPtr, *closestPtr;
- double coords[2], halo;
- int x1, y1, x2, y2;
- if ((argc < first+3) || (argc > first+5)) {
- Tcl_WrongNumArgs(interp, first+1, argv, "x y ?halo? ?start?");
- return TCL_ERROR;
- }
- if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[first+1],
- &coords[0]) != TCL_OK) || (Tk_CanvasGetCoordFromObj(interp,
- (Tk_Canvas) canvasPtr, argv[first+2], &coords[1]) != TCL_OK)) {
- return TCL_ERROR;
- }
- if (argc > first+3) {
- if (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[first+3],
- &halo) != TCL_OK) {
- return TCL_ERROR;
- }
- if (halo < 0.0) {
- Tcl_AppendResult(interp, "can't have negative halo value "",
- Tcl_GetString(argv[3]), """, (char *) NULL);
- return TCL_ERROR;
- }
- } else {
- halo = 0.0;
- }
- /*
- * Find the item at which to start the search.
- */
- startPtr = canvasPtr->firstItemPtr;
- if (argc == first+5) {
- #ifdef USE_OLD_TAG_SEARCH
- itemPtr = StartTagSearch(canvasPtr, argv[first+4], &search);
- #else /* USE_OLD_TAG_SEARCH */
- if (TagSearchScan(canvasPtr, argv[first+4], searchPtrPtr) != TCL_OK) {
- return TCL_ERROR;
- }
- itemPtr = TagSearchFirst(*searchPtrPtr);
- #endif /* USE_OLD_TAG_SEARCH */
- if (itemPtr != NULL) {
- startPtr = itemPtr;
- }
- }
- /*
- * The code below is optimized so that it can eliminate most
- * items without having to call their item-specific procedures.
- * This is done by keeping a bounding box (x1, y1, x2, y2) that
- * an item's bbox must overlap if the item is to have any
- * chance of being closer than the closest so far.
- */
- itemPtr = startPtr;
- while(itemPtr && (itemPtr->state == TK_STATE_HIDDEN ||
- (itemPtr->state == TK_STATE_NULL && canvasPtr->canvas_state == TK_STATE_HIDDEN))) {
- itemPtr = itemPtr->nextPtr;
- }
- if (itemPtr == NULL) {
- return TCL_OK;
- }
- closestDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
- itemPtr, coords) - halo;
- if (closestDist < 0.0) {
- closestDist = 0.0;
- }
- while (1) {
- double newDist;
- /*
- * Update the bounding box using itemPtr, which is the
- * new closest item.
- */
- x1 = (int) (coords[0] - closestDist - halo - 1);
- y1 = (int) (coords[1] - closestDist - halo - 1);
- x2 = (int) (coords[0] + closestDist + halo + 1);
- y2 = (int) (coords[1] + closestDist + halo + 1);
- closestPtr = itemPtr;
- /*
- * Search for an item that beats the current closest one.
- * Work circularly through the canvas's item list until
- * getting back to the starting item.
- */
- while (1) {
- itemPtr = itemPtr->nextPtr;
- if (itemPtr == NULL) {
- itemPtr = canvasPtr->firstItemPtr;
- }
- if (itemPtr == startPtr) {
- DoItem(interp, closestPtr, uid);
- return TCL_OK;
- }
- if (itemPtr->state == TK_STATE_HIDDEN || (itemPtr->state == TK_STATE_NULL &&
- canvasPtr->canvas_state == TK_STATE_HIDDEN)) {
- continue;
- }
- if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
- || (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
- continue;
- }
- newDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
- itemPtr, coords) - halo;
- if (newDist < 0.0) {
- newDist = 0.0;
- }
- if (newDist <= closestDist) {
- closestDist = newDist;
- break;
- }
- }
- }
- break;
- }
- case CANV_ENCLOSED: {
- if (argc != first+5) {
- Tcl_WrongNumArgs(interp, first+1, argv, "x1 y1 x2 y2");
- return TCL_ERROR;
- }
- return FindArea(interp, canvasPtr, argv+first+1, uid, 1);
- }
- case CANV_OVERLAPPING: {
- if (argc != first+5) {
- Tcl_WrongNumArgs(interp, first+1, argv, "x1 y1 x2 y2");
- return TCL_ERROR;
- }
- return FindArea(interp, canvasPtr, argv+first+1, uid, 0);
- }
- case CANV_WITHTAG: {
- if (argc != first+2) {
- Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId");
- return TCL_ERROR;
- }
- #ifdef USE_OLD_TAG_SEARCH
- for (itemPtr = StartTagSearch(canvasPtr, argv[first+1], &search);
- itemPtr != NULL; itemPtr = NextItem(&search)) {
- #else /* USE_OLD_TAG_SEARCH */
- if (TagSearchScan(canvasPtr, argv[first+1], searchPtrPtr) != TCL_OK) {
- return TCL_ERROR;
- }
- for (itemPtr = TagSearchFirst(*searchPtrPtr);
- itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) {
- #endif /* USE_OLD_TAG_SEARCH */
- DoItem(interp, itemPtr, uid);
- }
- }
- }
- return TCL_OK;
- }
- /*
- *--------------------------------------------------------------
- *
- * FindArea --
- *
- * This procedure implements area searches for the "find"
- * and "addtag" options.
- *
- * Results:
- * A standard Tcl return value. If newTag is NULL, then a
- * list of ids from all the items overlapping or enclosed
- * by the rectangle given by argc is returned in the interp's result.
- * If newTag is NULL, then the normal the interp's result is an
- * empty string. If an error occurs, then the interp's result will
- * hold an error message.
- *
- * Side effects:
- * If uid is non-NULL, then all the items overlapping
- * or enclosed by the area in argv have that tag added to
- * their lists of tags.
- *
- *--------------------------------------------------------------
- */
- static int
- FindArea(interp, canvasPtr, argv, uid, enclosed)
- Tcl_Interp *interp; /* Interpreter for error reporting
- * and result storing. */
- TkCanvas *canvasPtr; /* Canvas whose items are to be
- * searched. */
- Tcl_Obj *CONST *argv; /* Array of four arguments that
- * give the coordinates of the
- * rectangular area to search. */
- Tk_Uid uid; /* If non-NULL, gives new tag to set
- * on all found items; if NULL, then
- * ids of found items are returned
- * in the interp's result. */
- int enclosed; /* 0 means overlapping or enclosed
- * items are OK, 1 means only enclosed
- * items are OK. */
- {
- double rect[4], tmp;
- int x1, y1, x2, y2;
- Tk_Item *itemPtr;
- if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[0],
- &rect[0]) != TCL_OK)
- || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[1],
- &rect[1]) != TCL_OK)
- || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[2],
- &rect[2]) != TCL_OK)
- || (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, argv[3],
- &rect[3]) != TCL_OK)) {
- return TCL_ERROR;
- }
- if (rect[0] > rect[2]) {
- tmp = rect[0]; rect[0] = rect[2]; rect[2] = tmp;
- }
- if (rect[1] > rect[3]) {
- tmp = rect[1]; rect[1] = rect[3]; rect[3] = tmp;
- }
- /*
- * Use an integer bounding box for a quick test, to avoid
- * calling item-specific code except for items that are close.
- */
- x1 = (int) (rect[0]-1.0);
- y1 = (int) (rect[1]-1.0);
- x2 = (int) (rect[2]+1.0);
- y2 = (int) (rect[3]+1.0);
- for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
- itemPtr = itemPtr->nextPtr) {
- if (itemPtr->state == TK_STATE_HIDDEN || (itemPtr->state == TK_STATE_NULL &&
- canvasPtr->canvas_state == TK_STATE_HIDDEN)) {
- continue;
- }
- if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
- || (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
- continue;
- }
- if ((*itemPtr->typePtr->areaProc)((Tk_Canvas) canvasPtr, itemPtr, rect)
- >= enclosed) {
- DoItem(interp, itemPtr, uid);
- }
- }
- return TCL_OK;
- }
- /*
- *--------------------------------------------------------------
- *
- * RelinkItems --
- *
- * Move one or more items to a different place in the
- * display order for a canvas.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The items identified by "tag" are moved so that they
- * are all together in the display list and immediately
- * after prevPtr. The order of the moved items relative
- * to each other is not changed.
- *
- *--------------------------------------------------------------
- */
- #ifdef USE_OLD_TAG_SEARCH
- static void
- RelinkItems(canvasPtr, tag, prevPtr)
- #else /* USE_OLD_TAG_SEARCH */
- static int
- RelinkItems(canvasPtr, tag, prevPtr, searchPtrPtr)
- #endif /* USE_OLD_TAG_SEARCH */
- TkCanvas *canvasPtr; /* Canvas to be modified. */
- Tcl_Obj *tag; /* Tag identifying items to be moved
- * in the redisplay list. */
- Tk_Item *prevPtr; /* Reposition the items so that they
- * go just after this item (NULL means
- * put at beginning of list). */
- #ifndef USE_OLD_TAG_SEARCH
- TagSearch **searchPtrPtr; /* From CanvasWidgetCmd local vars */
- #endif /* not USE_OLD_TAG_SEARCH */
- {
- Tk_Item *itemPtr;
- #ifdef USE_OLD_TAG_SEARCH
- TagSearch search;
- #endif /* USE_OLD_TAG_SEARCH */
- Tk_Item *firstMovePtr, *lastMovePtr;
- /*
- * Find all of the items to be moved and remove them from
- * the list, making an auxiliary list running from firstMovePtr
- * to lastMovePtr. Record their areas for redisplay.
- */
- firstMovePtr = lastMovePtr = NULL;
- #ifdef USE_OLD_TAG_SEARCH
- for (itemPtr = StartTagSearch(canvasPtr, tag, &search);
- itemPtr != NULL; itemPtr = NextItem(&search)) {
- #else /* USE_OLD_TAG_SEARCH */
- if (TagSearchScan(canvasPtr, tag, searchPtrPtr) != TCL_OK) {
- return TCL_ERROR;
- }
- for (itemPtr = TagSearchFirst(*searchPtrPtr);
- itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) {
- #endif /* USE_OLD_TAG_SEARCH */
- if (itemPtr == prevPtr) {
- /*
- * Item after which insertion is to occur is being
- * moved! Switch to insert after its predecessor.
- */
- prevPtr = prevPtr->prevPtr;
- }
- if (itemPtr->prevPtr == NULL) {
- if (itemPtr->nextPtr != NULL) {
- itemPtr->nextPtr->prevPtr = NULL;
- }
- canvasPtr->firstItemPtr = itemPtr->nextPtr;
- } else {
- if (itemPtr->nextPtr != NULL) {
- itemPtr->nextPtr->prevPtr = itemPtr->prevPtr;
- }
- itemPtr->prevPtr->nextPtr = itemPtr->nextPtr;
- }
- if (canvasPtr->lastItemPtr == itemPtr) {
- canvasPtr->lastItemPtr = itemPtr->prevPtr;
- }
- if (firstMovePtr == NULL) {
- itemPtr->prevPtr = NULL;
- firstMovePtr = itemPtr;
- } else {
- itemPtr->prevPtr = lastMovePtr;
- lastMovePtr->nextPtr = itemPtr;
- }
- lastMovePtr = itemPtr;
- EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
- canvasPtr->flags |= REPICK_NEEDED;
- }
- /*
- * Insert the list of to-be-moved items back into the canvas's
- * at the desired position.
- */
- if (firstMovePtr == NULL) {
- #ifdef USE_OLD_TAG_SEARCH
- return;
- #else /* USE_OLD_TAG_SEARCH */
- return TCL_OK;
- #endif /* USE_OLD_TAG_SEARCH */
- }
- if (prevPtr == NULL) {
- if (canvasPtr->firstItemPtr != NULL) {
- canvasPtr->firstItemPtr->prevPtr = lastMovePtr;
- }
- lastMovePtr->nextPtr = canvasPtr->firstItemPtr;
- canvasPtr->firstItemPtr = firstMovePtr;
- } else {
- if (prevPtr->nextPtr != NULL) {
- prevPtr->nextPtr->prevPtr = lastMovePtr;
- }
- lastMovePtr->nextPtr = prevPtr->nextPtr;
- if (firstMovePtr != NULL) {
- firstMovePtr->prevPtr = prevPtr;
- }
- prevPtr->nextPtr = firstMovePtr;
- }
- if (canvasPtr->lastItemPtr == prevPtr) {
- canvasPtr->lastItemPtr = lastMovePtr;
- }
- #ifndef USE_OLD_TAG_SEARCH
- return TCL_OK;
- #endif /* not USE_OLD_TAG_SEARCH */
- }
- /*
- *--------------------------------------------------------------
- *
- * CanvasBindProc --
- *
- * This procedure is invoked by the Tk dispatcher to handle
- * events associated with bindings on items.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Depends on the command invoked as part of the binding
- * (if there was any).
- *
- *--------------------------------------------------------------
- */
- static void
- CanvasBindProc(clientData, eventPtr)
- ClientData clientData; /* Pointer to canvas structure. */
- XEvent *eventPtr; /* Pointer to X event that just
- * happened. */
- {
- TkCanvas *canvasPtr = (TkCanvas *) clientData;
- Tcl_Preserve((ClientData) canvasPtr);
- /*
- * This code below keeps track of the current modifier state in
- * canvasPtr>state. This information is used to defer repicks of
- * the current item while buttons are down.
- */
- if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) {
- int mask;
- switch (eventPtr->xbutton.button) {
- case Button1:
- mask = Button1Mask;
- break;
- case Button2:
- mask = Button2Mask;
- break;
- case Button3:
- mask = Button3Mask;
- break;
- case Button4:
- mask = Button4Mask;
- break;
- case Button5:
- mask = Button5Mask;
- break;
- default:
- mask = 0;
- break;
- }
- /*
- * For button press events, repick the current item using the
- * button state before the event, then process the event. For
- * button release events, first process the event, then repick
- * the current item using the button state *after* the event
- * (the button has logically gone up before we change the
- * current item).
- */
- if (eventPtr->type == ButtonPress) {
- /*
- * On a button press, first repick the current item using
- * the button state before the event, the process the event.
- */
- canvasPtr->state = eventPtr->xbutton.state;
- PickCurrentItem(canvasPtr, eventPtr);
- canvasPtr->state ^= mask;
- CanvasDoEvent(canvasPtr, eventPtr);
- } else {
- /*
- * Button release: first process the event, with the button
- * still considered to be down. Then repick the current
- * item under the assumption that the button is no longer down.
- */
- canvasPtr->state = eventPtr->xbutton.state;
- CanvasDoEvent(canvasPtr, eventPtr);
- eventPtr->xbutton.state ^= mask;
- canvasPtr->state = eventPtr->xbutton.state;
- PickCurrentItem(canvasPtr, eventPtr);
- eventPtr->xbutton.state ^= mask;
- }
- goto done;
- } else if ((eventPtr->type == EnterNotify)
- || (eventPtr->type == LeaveNotify)) {
- canvasPtr->state = eventPtr->xcrossing.state;
- PickCurrentItem(canvasPtr, eventPtr);
- goto done;
- } else if (eventPtr->type == MotionNotify) {
- canvasPtr->state = eventPtr->xmotion.state;
- PickCurrentItem(canvasPtr, eventPtr);
- }
- CanvasDoEvent(canvasPtr, eventPtr);
- done:
- Tcl_Release((ClientData) canvasPtr);
- }
- /*
- *--------------------------------------------------------------
- *
- * PickCurrentItem --
- *
- * Find the topmost item in a canvas that contains a given
- * location and mark the the current item. If the current
- * item has changed, generate a fake exit event on the old
- * current item, a fake enter event on the new current item
- * item and force a redraw of the two items. Canvas items
- * that are hidden or disabled are ignored.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The current item for canvasPtr may change. If it does,
- * then the commands associated with item entry and exit
- * could do just about anything. A binding script could
- * delete the canvas, so callers should protect themselves
- * with Tcl_Preserve and Tcl_Release.
- *
- *--------------------------------------------------------------
- */
- static void
- PickCurrentItem(canvasPtr, eventPtr)
- TkCanvas *canvasPtr; /* Canvas widget in which to select
- * current item. */
- XEvent *eventPtr; /* Event describing location of
- * mouse cursor. Must be EnterWindow,
- * LeaveWindow, ButtonRelease, or
- * MotionNotify. */
- {
- double coords[2];
- int buttonDown;
- Tk_Item *prevItemPtr;
- #ifndef USE_OLD_TAG_SEARCH
- SearchUids *searchUids = GetStaticUids();
- #endif
- /*
- * Check whether or not a button is down. If so, we'll log entry
- * and exit into and out of the current item, but not entry into
- * any other item. This implements a form of grabbing equivalent
- * to what the X server does for windows.
- */
- buttonDown = canvasPtr->state
- & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask);
- if (!buttonDown) {
- canvasPtr->flags &= ~LEFT_GRABBED_ITEM;
- }
- /*
- * Save information about this event in the canvas. The event in
- * the canvas is used for two purposes:
- *
- * 1. Event bindings: if the current item changes, fake events are
- * generated to allow item-enter and item-leave bindings to trigger.
- * 2. Reselection: if the current item gets deleted, can use the
- * saved event to find a new current item.
- * Translate MotionNotify events into EnterNotify events, since that's
- * what gets reported to item handlers.
- */
- if (eventPtr != &canvasPtr->pickEvent) {
- if ((eventPtr->type == MotionNotify)
- || (eventPtr->type == ButtonRelease)) {
- canvasPtr->pickEvent.xcrossing.type = EnterNotify;
- canvasPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial;
- canvasPtr->pickEvent.xcrossing.send_event
- = eventPtr->xmotion.send_event;
- canvasPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display;
- canvasPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window;
- canvasPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root;
- canvasPtr->pickEvent.xcrossing.subwindow = None;
- canvasPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time;
- canvasPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x;
- canvasPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y;
- canvasPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root;
- canvasPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root;
- canvasPtr->pickEvent.xcrossing.mode = NotifyNormal;
- canvasPtr->pickEvent.xcrossing.detail = NotifyNonlinear;
- canvasPtr->pickEvent.xcrossing.same_screen
- = eventPtr->xmotion.same_screen;
- canvasPtr->pickEvent.xcrossing.focus = False;
- canvasPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state;
- } else {
- canvasPtr->pickEvent = *eventPtr;
- }
- }
- /*
- * If this is a recursive call (there's already a partially completed
- * call pending on the stack; it's in the middle of processing a
- * Leave event handler for the old current item) then just return;
- * the pending call will do everything that's needed.
- */
- if (canvasPtr->flags & REPICK_IN_PROGRESS) {
- return;
- }
- /*
- * A LeaveNotify event automatically means that there's no current
- * object, so the check for closest item can be skipped.
- */
- coords[0] = canvasPtr->pickEvent.xcrossing.x + canvasPtr->xOrigin;
- coords[1] = canvasPtr->pickEvent.xcrossing.y + canvasPtr->yOrigin;
- if (canvasPtr->pickEvent.type != LeaveNotify) {
- canvasPtr->newCurrentPtr = CanvasFindClosest(canvasPtr, coords);
- } else {
- canvasPtr->newCurrentPtr = NULL;
- }
- if ((canvasPtr->newCurrentPtr == canvasPtr->currentItemPtr)
- && !(canvasPtr->flags & LEFT_GRABBED_ITEM)) {
- /*
- * Nothing to do: the current item hasn't changed.
- */
- return;
- }
- /*
- * Simulate a LeaveNotify event on the previous current item and
- * an EnterNotify event on the new current item. Remove the "current"
- * tag from the previous current item and place it on the new current
- * item.
- */
- if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr)
- && (canvasPtr->currentItemPtr != NULL)
- && !(canvasPtr->flags & LEFT_GRABBED_ITEM)) {
- XEvent event;
- Tk_Item *itemPtr = canvasPtr->currentItemPtr;
- int i;
- event = canvasPtr->pickEvent;
- event.type = LeaveNotify;
- /*
- * If the event's detail happens to be NotifyInferior the
- * binding mechanism will discard the event. To be consistent,
- * always use NotifyAncestor.
- */
- event.xcrossing.detail = NotifyAncestor;
- canvasPtr->flags |= REPICK_IN_PROGRESS;
- CanvasDoEvent(canvasPtr, &event);
- canvasPtr->flags &= ~REPICK_IN_PROGRESS;
- /*
- * The check below is needed because there could be an event
- * handler for <LeaveNotify> that deletes the current item.
- */
- if ((itemPtr == canvasPtr->currentItemPtr) && !buttonDown) {
- for (i = itemPtr->numTags-1; i >= 0; i--) {
- #ifdef USE_OLD_TAG_SEARCH
- if (itemPtr->tagPtr[i] == Tk_GetUid("current")) {
- #else /* USE_OLD_TAG_SEARCH */
- if (itemPtr->tagPtr[i] == searchUids->currentUid) {
- #endif /* USE_OLD_TAG_SEARCH */
- itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
- itemPtr->numTags--;
- break;
- }
- }
- }
-
- /*
- * Note: during CanvasDoEvent above, it's possible that
- * canvasPtr->newCurrentPtr got reset to NULL because the
- * item was deleted.
- */
- }
- if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr) && buttonDown) {
- canvasPtr->flags |= LEFT_GRABBED_ITEM;
- return;
- }
- /*
- * Special note: it's possible that canvasPtr->newCurrentPtr ==
- * canvasPtr->currentItemPtr here. This can happen, for example,
- * if LEFT_GRABBED_ITEM was set.
- */
- prevItemPtr = canvasPtr->currentItemPtr;
- canvasPtr->flags &= ~LEFT_GRABBED_ITEM;
- canvasPtr->currentItemPtr = canvasPtr->newCurrentPtr;
- if (prevItemPtr != NULL && prevItemPtr != canvasPtr->currentItemPtr &&
- (prevItemPtr->redraw_flags & TK_ITEM_STATE_DEPENDANT)) {
- EventuallyRedrawItem((Tk_Canvas) canvasPtr, prevItemPtr);
- (*prevItemPtr->typePtr->configProc)(canvasPtr->interp,
- (Tk_Canvas) canvasPtr, prevItemPtr, 0, (Tcl_Obj **) NULL,
- TK_CONFIG_ARGV_ONLY);
- }
- if (canvasPtr->currentItemPtr != NULL) {
- XEvent event;
- #ifdef USE_OLD_TAG_SEARCH
- DoItem((Tcl_Interp *) NULL, canvasPtr->currentItemPtr,
- Tk_GetUid("current"));
- #else /* USE_OLD_TAG_SEARCH */
- DoItem((Tcl_Interp *) NULL, canvasPtr->currentItemPtr,
- searchUids->currentUid);
- #endif /* USE_OLD_TAG_SEA */
- if ((canvasPtr->currentItemPtr->redraw_flags & TK_ITEM_STATE_DEPENDANT &&
- prevItemPtr != canvasPtr->currentItemPtr)) {
- (*canvasPtr->currentItemPtr->typePtr->configProc)(canvasPtr->interp,
- (Tk_Canvas) canvasPtr, canvasPtr->currentItemPtr, 0, (Tcl_Obj **) NULL,
- TK_CONFIG_ARGV_ONLY);
- EventuallyRedrawItem((Tk_Canvas) canvasPtr,
- canvasPtr->currentItemPtr);
- }
- event = canvasPtr->pickEvent;
- event.type = EnterNotify;
- event.xcrossing.detail = NotifyAncestor;
- CanvasDoEvent(canvasPtr, &event);
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * CanvasFindClosest --
- *
- * Given x and y coordinates, find the topmost canvas item that
- * is "close" to the coordinates. Canvas items that are hidden
- * or disabled are ignored.
- *
- * Results:
- * The return value is a pointer to the topmost item that is
- * close to (x,y), or NULL if no item is close.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static Tk_Item *
- CanvasFindClosest(canvasPtr, coords)
- TkCanvas *canvasPtr; /* Canvas widget to search. */
- double coords[2]; /* Desired x,y position in canvas,
- * not screen, coordinates.) */
- {
- Tk_Item *itemPtr;
- Tk_Item *bestPtr;
- int x1, y1, x2, y2;
- x1 = (int) (coords[0] - canvasPtr->closeEnough);
- y1 = (int) (coords[1] - canvasPtr->closeEnough);
- x2 = (int) (coords[0] + canvasPtr->closeEnough);
- y2 = (int) (coords[1] + canvasPtr->closeEnough);
- bestPtr = NULL;
- for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
- itemPtr = itemPtr->nextPtr) {
- if (itemPtr->state == TK_STATE_HIDDEN || itemPtr->state==TK_STATE_DISABLED ||
- (itemPtr->state == TK_STATE_NULL && (canvasPtr->canvas_state == TK_STATE_HIDDEN ||
- canvasPtr->canvas_state == TK_STATE_DISABLED))) {
- continue;
- }
- if ((itemPtr->x1 > x2) || (itemPtr->x2 < x1)
- || (itemPtr->y1 > y2) || (itemPtr->y2 < y1)) {
- continue;
- }
- if ((*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
- itemPtr, coords) <= canvasPtr->closeEnough) {
- bestPtr = itemPtr;
- }
- }
- return bestPtr;
- }
- /*
- *--------------------------------------------------------------
- *
- * CanvasDoEvent --
- *
- * This procedure is called to invoke binding processing
- * for a new event that is associated with the current item
- * for a canvas.
- *
- * Results:
- * None.
- *
- * Side effects:
- * Depends on the bindings for the canvas. A binding script
- * could delete the canvas, so callers should protect themselves
- * with Tcl_Preserve and Tcl_Release.
- *
- *--------------------------------------------------------------
- */
- static void
- CanvasDoEvent(canvasPtr, eventPtr)
- TkCanvas *canvasPtr; /* Canvas widget in which event
- * occurred. */
- XEvent *eventPtr; /* Real or simulated X event that
- * is to be processed. */
- {
- #define NUM_STATIC 3
- ClientData staticObjects[NUM_STATIC];
- ClientData *objectPtr;
- int numObjects, i;
- Tk_Item *itemPtr;
- #ifndef USE_OLD_TAG_SEARCH
- TagSearchExpr *expr;
- int numExprs;
- SearchUids *searchUids = GetStaticUids();
- #endif /* not USE_OLD_TAG_SEARCH */
- if (canvasPtr->bindingTable == NULL) {
- return;
- }
- itemPtr = canvasPtr->currentItemPtr;
- if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) {
- itemPtr = canvasPtr->textInfo.focusItemPtr;
- }
- if (itemPtr == NULL) {
- return;
- }
- #ifdef USE_OLD_TAG_SEARCH
- /*
- * Set up an array with all the relevant objects for processing
- * this event. The relevant objects are (a) the event's item,
- * (b) the tags associated with the event's item, and (c) the
- * tag "all". If there are a lot of tags then malloc an array
- * to hold all of the objects.
- */
- numObjects = itemPtr->numTags + 2;
- #else /* USE_OLD_TAG_SEARCH */
- /*
- * Set up an array with all the relevant objects for processing
- * this event. The relevant objects are:
- * (a) the event's item,
- * (b) the tags associated with the event's item,
- * (c) the expressions that are true for the event's item's tags, and
- * (d) the tag "all".
- *
- * If there are a lot of tags then malloc an array to hold all of
- * the objects.
- */
- /*
- * flag and count all expressions that match item's tags
- */
- numExprs = 0;
- expr = canvasPtr->bindTagExprs;
- while (expr) {
- expr->index = 0;
- expr->match = TagSearchEvalExpr(expr, itemPtr);
- if (expr->match) {
- numExprs++;
- }
- expr = expr->next;
- }
- numObjects = itemPtr->numTags + numExprs + 2;
- #endif /* not USE_OLD_TAG_SEARCH */
- if (numObjects <= NUM_STATIC) {
- objectPtr = staticObjects;
- } else {
- objectPtr = (ClientData *)
- ckalloc((unsigned) (numObjects * sizeof(ClientData)));
- }
- #ifdef USE_OLD_TAG_SEARCH
- objectPtr[0] = (ClientData) Tk_GetUid("all");
- #else /* USE_OLD_TAG_SEARCH */
- objectPtr[0] = (ClientData) searchUids->allUid;
- #endif /* USE_OLD_TAG_SEARCH */
- for (i = itemPtr->numTags-1; i >= 0; i--) {
- objectPtr[i+1] = (ClientData) itemPtr->tagPtr[i];
- }
- objectPtr[itemPtr->numTags+1] = (ClientData) itemPtr;
- #ifndef USE_OLD_TAG_SEARCH
- /*
- * copy uids of matching expressions into object array
- */
- i = itemPtr->numTags+2;
- expr = canvasPtr->bindTagExprs;
- while (expr) {
- if (expr->match) {
- objectPtr[i++] = (int *) expr->uid;
- }
- expr = expr->next;
- }
- #endif /* not USE_OLD_TAG_SEARCH */
- /*
- * Invoke the binding system, then free up the object array if
- * it was malloc-ed.
- */
- if (canvasPtr->tkwin != NULL) {
- Tk_BindEvent(canvasPtr->bindingTable, eventPtr, canvasPtr->tkwin,
- numObjects, objectPtr);
- }
- if (objectPtr != staticObjects) {
- ckfree((char *) objectPtr);
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * CanvasBlinkProc --
- *
- * This procedure is called as a timer handler to blink the
- * insertion cursor off and on.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The cursor gets turned on or off, redisplay gets invoked,
- * and this procedure reschedules itself.
- *
- *----------------------------------------------------------------------
- */
- static void
- CanvasBlinkProc(clientData)
- ClientData clientData; /* Pointer to record describing entry. */
- {
- TkCanvas *canvasPtr = (TkCanvas *) clientData;
- if (!canvasPtr->textInfo.gotFocus || (canvasPtr->insertOffTime == 0)) {
- return;
- }
- if (canvasPtr->textInfo.cursorOn) {
- canvasPtr->textInfo.cursorOn = 0;
- canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
- canvasPtr->insertOffTime, CanvasBlinkProc,
- (ClientData) canvasPtr);
- } else {
- canvasPtr->textInfo.cursorOn = 1;
- canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
- canvasPtr->insertOnTime, CanvasBlinkProc,
- (ClientData) canvasPtr);
- }
- if (canvasPtr->textInfo.focusItemPtr != NULL) {
- EventuallyRedrawItem((Tk_Canvas) canvasPtr,
- canvasPtr->textInfo.focusItemPtr);
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * CanvasFocusProc --
- *
- * This procedure is called whenever a canvas gets or loses the
- * input focus. It's also called whenever the window is
- * reconfigured while it has the focus.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The cursor gets turned on or off.
- *
- *----------------------------------------------------------------------
- */
- static void
- CanvasFocusProc(canvasPtr, gotFocus)
- TkCanvas *canvasPtr; /* Canvas that just got or lost focus. */
- int gotFocus; /* 1 means window is getting focus, 0 means
- * it's losing it. */
- {
- Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler);
- if (gotFocus) {
- canvasPtr->textInfo.gotFocus = 1;
- canvasPtr->textInfo.cursorOn = 1;
- if (canvasPtr->insertOffTime != 0) {
- canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
- canvasPtr->insertOffTime, CanvasBlinkProc,
- (ClientData) canvasPtr);
- }
- } else {
- canvasPtr->textInfo.gotFocus = 0;
- canvasPtr->textInfo.cursorOn = 0;
- canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
- }
- if (canvasPtr->textInfo.focusItemPtr != NULL) {
- EventuallyRedrawItem((Tk_Canvas) canvasPtr,
- canvasPtr->textInfo.focusItemPtr);
- }
- if (canvasPtr->highlightWidth > 0) {
- canvasPtr->flags |= REDRAW_BORDERS;
- if (!(canvasPtr->flags & REDRAW_PENDING)) {
- Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
- canvasPtr->flags |= REDRAW_PENDING;
- }
- }
- }
- /*
- *----------------------------------------------------------------------
- *
- * CanvasSelectTo --
- *
- * Modify the selection by moving its un-anchored end. This could
- * make the selection either larger or smaller.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The selection changes.
- *
- *----------------------------------------------------------------------
- */
- static void
- CanvasSelectTo(canvasPtr, itemPtr, index)
- TkCanvas *canvasPtr; /* Information about widget. */
- Tk_Item *itemPtr; /* Item that is to hold selection. */
- int index; /* Index of element that is to become the
- * "other" end of the selection. */
- {
- int oldFirst, oldLast;
- Tk_Item *oldSelPtr;
- oldFirst = canvasPtr->textInfo.selectFirst;
- oldLast = canvasPtr->textInfo.selectLast;
- oldSelPtr = canvasPtr->textInfo.selItemPtr;
- /*
- * Grab the selection if we don't own it already.
- */
- if (canvasPtr->textInfo.selItemPtr == NULL) {
- Tk_OwnSelection(canvasPtr->tkwin, XA_PRIMARY, CanvasLostSelection,
- (ClientData) canvasPtr);
- } else if (canvasPtr->textInfo.selItemPtr != itemPtr) {
- EventuallyRedrawItem((Tk_Canvas) canvasPtr,
- canvasPtr->textInfo.selItemPtr);
- }
- canvasPtr->textInfo.selItemPtr = itemPtr;
- if (canvasPtr->textInfo.anchorItemPtr != itemPtr) {
- canvasPtr->textInfo.anchorItemPtr = itemPtr;
- canvasPtr->textInfo.selectAnchor = index;
- }
- if (canvasPtr->textInfo.selectAnchor <= index) {
- canvasPtr->textInfo.selectFirst = canvasPtr->textInfo.selectAnchor;
- canvasPtr->textInfo.selectLast = index;
- } else {
- canvasPtr->textInfo.selectFirst = index;
- canvasPtr->textInfo.selectLast = canvasPtr->textInfo.selectAnchor - 1;
- }
- if ((canvasPtr->textInfo.selectFirst != oldFirst)
- || (canvasPtr->textInfo.selectLast != oldLast)
- || (itemPtr != oldSelPtr)) {
- EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
- }
- }
- /*
- *--------------------------------------------------------------
- *
- * CanvasFetchSelection --
- *
- * This procedure is invoked by Tk to return part or all of
- * the selection, when the selection is in a canvas widget.
- * This procedure always returns the selection as a STRING.
- *
- * Results:
- * The return value is the number of non-NULL bytes stored
- * at buffer. Buffer is filled (or partially filled) with a
- * NULL-terminated string containing part or all of the selection,
- * as given by offset and maxBytes.
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
- static int
- CanvasFetchSelection(clientData, offset, buffer, maxBytes)
- ClientData clientData; /* Information about canvas widget. */
- int offset; /* Offset within selection of first
- * character to be returned. */
- char *buffer; /* Location in which to place
- * selection. */
- int maxBytes; /* Maximum number of bytes to place
- * at buffer, not including terminating
- * NULL character. */
- {
- TkCanvas *canvasPtr = (TkCanvas *) clientData;
- if (canvasPtr->textInfo.selItemPtr == NULL) {
- return -1;
- }
- if (canvasPtr->textInfo.selItemPtr->typePtr->selectionProc == NULL) {
- return -1;
- }
- return (*canvasPtr->textInfo.selItemPtr->typePtr->selectionProc)(
- (Tk_Canvas) canvasPtr, canvasPtr->textInfo.selItemPtr, offset,
- buffer, maxBytes);
- }
- /*
- *----------------------------------------------------------------------
- *
- * CanvasLostSelection --
- *
- * This procedure is called back by Tk when the selection is
- * grabbed away from a canvas widget.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The existing selection is unhighlighted, and the window is
- * marked as not containing a selection.
- *
- *----------------------------------------------------------------------
- */
- static void
- CanvasLostSelection(clientData)
- ClientData clientData; /* Information about entry widget. */
- {
- TkCanvas *canvasPtr = (TkCanvas *) clientData;
- if (canvasPtr->textInfo.selItemPtr != NULL) {
- EventuallyRedrawItem((Tk_Canvas) canvasPtr,
- canvasPtr->textInfo.selItemPtr);
- }
- canvasPtr->textInfo.selItemPtr = NULL;
- }
- /*
- *--------------------------------------------------------------
- *
- * GridAlign --
- *
- * Given a coordinate and a grid spacing, this procedure
- * computes the location of the nearest grid line to the
- * coordinate.
- *
- * Results:
- * The return value is the location of the grid line nearest
- * to coord.
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
- static double
- GridAlign(coord, spacing)
- double coord; /* Coordinate to grid-align. */
- double spacing; /* Spacing between grid lines. If <= 0
- * then no alignment is done. */
- {
- if (spacing <= 0.0) {
- return coord;
- }
- if (coord < 0) {
- return -((int) ((-coord)/spacing + 0.5)) * spacing;
- }
- return ((int) (coord/spacing + 0.5)) * spacing;
- }
- /*
- *----------------------------------------------------------------------
- *
- * ScrollFractions --
- *
- * Given the range that's visible in the window and the "100%
- * range" for what's in the canvas, return a list of two
- * doubles representing the scroll fractions. This procedure
- * is used for both x and y scrolling.
- *
- * Results:
- * The memory pointed to by string is modified to hold
- * two real numbers containing the scroll fractions (between
- * 0 and 1) corresponding to the other arguments.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
- static Tcl_Obj *
- ScrollFractions(screen1, screen2, object1, object2)
- int screen1; /* Lowest coordinate visible in the window. */
- int screen2; /* Highest coordinate visible in the window. */
- int object1; /* Lowest coordinate in the object. */
- int object2; /* Highest coordinate in the object. */
- {
- double range, f1, f2;
- char buffer[2*TCL_DOUBLE_SPACE+2];
- range = object2 - object1;
- if (range <= 0) {
- f1 = 0;
- f2 = 1.0;
- } else {
- f1 = (screen1 - object1)/range;
- if (f1 < 0) {
- f1 = 0.0;
- }
- f2 = (screen2 - object1)/range;
- if (f2 > 1.0) {
- f2 = 1.0;
- }
- if (f2 < f1) {
- f2 = f1;
- }
- }
- sprintf(buffer, "%g %g", f1, f2);
- return Tcl_NewStringObj(buffer, -1);
- }
- /*
- *--------------------------------------------------------------
- *
- * CanvasUpdateScrollbars --
- *
- * This procedure is invoked whenever a canvas has changed in
- * a way that requires scrollbars to be redisplayed (e.g. the
- * view in the canvas has changed).
- *
- * Results:
- * None.
- *
- * Side effects:
- * If there are scrollbars associated with the canvas, then
- * their scrolling commands are invoked to cause them to
- * redisplay. If errors occur, additional Tcl commands may
- * be invoked to process the errors.
- *
- *--------------------------------------------------------------
- */
- static void
- CanvasUpdateScrollbars(canvasPtr)
- TkCanvas *canvasPtr; /* Information about canvas. */
- {
- int result;
- Tcl_Interp *interp;
- int xOrigin, yOrigin, inset, width, height, scrollX1, scrollX2,
- scrollY1, scrollY2;
- char *xScrollCmd, *yScrollCmd;
- /*
- * Save all the relevant values from the canvasPtr, because it might be
- * deleted as part of either of the two calls to Tcl_VarEval below.
- */
-
- interp = canvasPtr->interp;
- Tcl_Preserve((ClientData) interp);
- xScrollCmd = canvasPtr->xScrollCmd;
- if (xScrollCmd != (char *) NULL) {
- Tcl_Preserve((ClientData) xScrollCmd);
- }
- yScrollCmd = canvasPtr->yScrollCmd;
- if (yScrollCmd != (char *) NULL) {
- Tcl_Preserve((ClientData) yScrollCmd);
- }
- xOrigin = canvasPtr->xOrigin;
- yOrigin = canvasPtr->yOrigin;
- inset = canvasPtr->inset;
- width = Tk_Width(canvasPtr->tkwin);
- height = Tk_Height(canvasPtr->tkwin);
- scrollX1 = canvasPtr->scrollX1;
- scrollX2 = canvasPtr->scrollX2;
- scrollY1 = canvasPtr->scrollY1;
- scrollY2 = canvasPtr->scrollY2;
- canvasPtr->flags &= ~UPDATE_SCROLLBARS;
- if (canvasPtr->xScrollCmd != NULL) {
- Tcl_Obj *fractions = ScrollFractions(xOrigin + inset,
- xOrigin + width - inset, scrollX1, scrollX2);
- result = Tcl_VarEval(interp, xScrollCmd, " ",
- Tcl_GetString(fractions), (char *) NULL);
- Tcl_DecrRefCount(fractions);
- if (result != TCL_OK) {
- Tcl_BackgroundError(interp);
- }
- Tcl_ResetResult(interp);
- Tcl_Release((ClientData) xScrollCmd);
- }
- if (yScrollCmd != NULL) {
- Tcl_Obj *fractions = ScrollFractions(yOrigin + inset,
- yOrigin + height - inset, scrollY1, scrollY2);
- result = Tcl_VarEval(interp, yScrollCmd, " ",
- Tcl_GetString(fractions), (char *) NULL);
- Tcl_DecrRefCount(fractions);
- if (result != TCL_OK) {
- Tcl_BackgroundError(interp);
- }
- Tcl_ResetResult(interp);
- Tcl_Release((ClientData) yScrollCmd);
- }
- Tcl_Release((ClientData) interp);
- }
- /*
- *--------------------------------------------------------------
- *
- * CanvasSetOrigin --
- *
- * This procedure is invoked to change the mapping between
- * canvas coordinates and screen coordinates in the canvas
- * window.
- *
- * Results:
- * None.
- *
- * Side effects:
- * The canvas will be redisplayed to reflect the change in
- * view. In addition, scrollbars will be updated if there
- * are any.
- *
- *--------------------------------------------------------------
- */
- static void
- CanvasSetOrigin(canvasPtr, xOrigin, yOrigin)
- TkCanvas *canvasPtr; /* Information about canvas. */
- int xOrigin; /* New X origin for canvas (canvas x-coord
- * corresponding to left edge of canvas
- * window). */
- int yOrigin; /* New Y origin for canvas (canvas y-coord
- * corresponding to top edge of canvas
- * window). */
- {
- int left, right, top, bottom, delta;
- /*
- * If scroll increments have been set, round the window origin
- * to the nearest multiple of the increments. Remember, the
- * origin is the place just inside the borders, not the upper
- * left corner.
- */
- if (canvasPtr->xScrollIncrement > 0) {
- if (xOrigin >= 0) {
- xOrigin += canvasPtr->xScrollIncrement/2;
- xOrigin -= (xOrigin + canvasPtr->inset)
- % canvasPtr->xScrollIncrement;
- } else {
- xOrigin = (-xOrigin) + canvasPtr->xScrollIncrement/2;
- xOrigin = -(xOrigin - (xOrigin - canvasPtr->inset)
- % canvasPtr->xScrollIncrement);
- }
- }
- if (canvasPtr->yScrollIncrement > 0) {
- if (yOrigin >= 0) {
- yOrigin += canvasPtr->yScrollIncrement/2;
- yOrigin -= (yOrigin + canvasPtr->inset)
- % canvasPtr->yScrollIncrement;
- } else {
- yOrigin = (-yOrigin) + canvasPtr->yScrollIncrement/2;
- yOrigin = -(yOrigin - (yOrigin - canvasPtr->inset)
- % canvasPtr->yScrollIncrement);
- }
- }
- /*
- * Adjust the origin if necessary to keep as much as possible of the
- * canvas in the view. The variables left, right, etc. keep track of
- * how much extra space there is on each side of the view before it
- * will stick out past the scroll region. If one side sticks out past
- * the edge of the scroll region, adjust the view to bring that side
- * back to the edge of the scrollregion (but don't move it so much that
- * the other side sticks out now). If scroll increments are in effect,
- * be sure to adjust only by full increments.
- */
- if ((canvasPtr->confine) && (canvasPtr->regionString != NULL)) {
- left = xOrigin + canvasPtr->inset - canvasPtr->scrollX1;
- right = canvasPtr->scrollX2
- - (xOrigin + Tk_Width(canvasPtr->tkwin) - canvasPtr->inset);
- top = yOrigin + canvasPtr->inset - canvasPtr->scrollY1;
- bottom = canvasPtr->scrollY2
- - (yOrigin + Tk_Height(canvasPtr->tkwin) - canvasPtr->inset);
- if ((left < 0) && (right > 0)) {
- delta = (right > -left) ? -left : right;
- if (canvasPtr->xScrollIncrement > 0) {
- delta -= delta % canvasPtr->xScrollIncrement;
- }
- xOrigin += delta;
- } else if ((right < 0) && (left > 0)) {
- delta = (left > -right) ? -right : left;
- if (canvasPtr->xScrollIncrement > 0) {
- delta -= delta % canvasPtr->xScrollIncrement;
- }
- xOrigin -= delta;
- }
- if ((top < 0) && (bottom > 0)) {
- delta = (bottom > -top) ? -top : bottom;
- if (canvasPtr->yScrollIncrement > 0) {
- delta -= delta % canvasPtr->yScrollIncrement;
- }
- yOrigin += delta;
- } else if ((bottom < 0) && (top > 0)) {
- delta = (top > -bottom) ? -bottom : top;
- if (canvasPtr->yScrollIncrement > 0) {
- delta -= delta % canvasPtr->yScrollIncrement;
- }
- yOrigin -= delta;
- }
- }
- if ((xOrigin == canvasPtr->xOrigin) && (yOrigin == canvasPtr->yOrigin)) {
- return;
- }
- /*
- * Tricky point: must redisplay not only everything that's visible
- * in the window's final configuration, but also everything that was
- * visible in the initial configuration. This is needed because some
- * item types, like windows, need to know when they move off-screen
- * so they can explicitly undisplay themselves.
- */
- Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
- canvasPtr->xOrigin, canvasPtr->yOrigin,
- canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
- canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
- canvasPtr->xOrigin = xOrigin;
- canvasPtr->yOrigin = yOrigin;
- canvasPtr->flags |= UPDATE_SCROLLBARS;
- Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
- canvasPtr->xOrigin, canvasPtr->yOrigin,
- canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
- canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
- }
- /*
- *----------------------------------------------------------------------
- *
- * GetStringsFromObjs
- *
- * Results:
- * Converts object list into string list.
- *
- * Side effects:
- * Memory is allocated for the argv array, which must
- * be freed using ckfree() when no longer needed.
- *
- *----------------------------------------------------------------------
- */
- /* ARGSUSED */
- static CONST char **
- GetStringsFromObjs(argc, objv)
- int argc;
- Tcl_Obj *CONST objv[];
- {
- register int i;
- CONST char **argv;
- if (argc <= 0) {
- return NULL;
- }
- argv = (CONST char **) ckalloc((argc+1) * sizeof(char *));
- for (i = 0; i < argc; i++) {
- argv[i] = Tcl_GetStringFromObj(objv[i], NULL);
- }
- argv[argc] = 0;
- return argv;
- }
- /*
- *--------------------------------------------------------------
- *
- * Tk_CanvasPsColor --
- *
- * This procedure is called by individual canvas items when
- * they want to set a color value for output. Given information
- * about an X color, this procedure will generate Postscript
- * commands to set up an appropriate color in Postscript.
- *
- * Results:
- * Returns a standard Tcl return value. If an error occurs
- * then an error message will be left in interp->result.
- * If no error occurs, then additional Postscript will be
- * appended to interp->result.
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
- int
- Tk_CanvasPsColor(interp, canvas, colorPtr)
- Tcl_Interp *interp; /* Interpreter for returning Postscript
- * or error message. */
- Tk_Canvas canvas; /* Information about canvas. */
- XColor *colorPtr; /* Information about color. */
- {
- return Tk_PostscriptColor(interp, ((TkCanvas *) canvas)->psInfo,
- colorPtr);
- }
- /*
- *--------------------------------------------------------------
- *
- * Tk_CanvasPsFont --
- *
- * This procedure is called by individual canvas items when
- * they want to output text. Given information about an X
- * font, this procedure will generate Postscript commands
- * to set up an appropriate font in Postscript.
- *
- * Results:
- * Returns a standard Tcl return value. If an error occurs
- * then an error message will be left in interp->result.
- * If no error occurs, then additional Postscript will be
- * appended to the interp->result.
- *
- * Side effects:
- * The Postscript font name is entered into psInfoPtr->fontTable
- * if it wasn't already there.
- *
- *--------------------------------------------------------------
- */
- int
- Tk_CanvasPsFont(interp, canvas, tkfont)
- Tcl_Interp *interp; /* Interpreter for returning Postscript
- * or error message. */
- Tk_Canvas canvas; /* Information about canvas. */
- Tk_Font tkfont; /* Information about font in which text
- * is to be printed. */
- {
- return Tk_PostscriptFont(interp, ((TkCanvas *) canvas)->psInfo, tkfont);
- }
- /*
- *--------------------------------------------------------------
- *
- * Tk_CanvasPsBitmap --
- *
- * This procedure is called to output the contents of a
- * sub-region of a bitmap in proper image data format for
- * Postscript (i.e. data between angle brackets, one bit
- * per pixel).
- *
- * Results:
- * Returns a standard Tcl return value. If an error occurs
- * then an error message will be left in interp->result.
- * If no error occurs, then additional Postscript will be
- * appended to interp->result.
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
- int
- Tk_CanvasPsBitmap(interp, canvas, bitmap, startX, startY, width, height)
- Tcl_Interp *interp; /* Interpreter for returning Postscript
- * or error message. */
- Tk_Canvas canvas; /* Information about canvas. */
- Pixmap bitmap; /* Bitmap for which to generate
- * Postscript. */
- int startX, startY; /* Coordinates of upper-left corner
- * of rectangular region to output. */
- int width, height; /* Height of rectangular region. */
- {
- return Tk_PostscriptBitmap(interp, ((TkCanvas *) canvas)->tkwin,
- ((TkCanvas *) canvas)->psInfo, bitmap, startX, startY,
- width, height);
- }
- /*
- *--------------------------------------------------------------
- *
- * Tk_CanvasPsStipple --
- *
- * This procedure is called by individual canvas items when
- * they have created a path that they'd like to be filled with
- * a stipple pattern. Given information about an X bitmap,
- * this procedure will generate Postscript commands to fill
- * the current clip region using a stipple pattern defined by the
- * bitmap.
- *
- * Results:
- * Returns a standard Tcl return value. If an error occurs
- * then an error message will be left in interp->result.
- * If no error occurs, then additional Postscript will be
- * appended to interp->result.
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
- int
- Tk_CanvasPsStipple(interp, canvas, bitmap)
- Tcl_Interp *interp; /* Interpreter for returning Postscript
- * or error message. */
- Tk_Canvas canvas; /* Information about canvas. */
- Pixmap bitmap; /* Bitmap to use for stippling. */
- {
- return Tk_PostscriptStipple(interp, ((TkCanvas *) canvas)->tkwin,
- ((TkCanvas *) canvas)->psInfo, bitmap);
- }
- /*
- *--------------------------------------------------------------
- *
- * Tk_CanvasPsY --
- *
- * Given a y-coordinate in canvas coordinates, this procedure
- * returns a y-coordinate to use for Postscript output.
- *
- * Results:
- * Returns the Postscript coordinate that corresponds to
- * "y".
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
- double
- Tk_CanvasPsY(canvas, y)
- Tk_Canvas canvas; /* Token for canvas on whose behalf
- * Postscript is being generated. */
- double y; /* Y-coordinate in canvas coords. */
- {
- return Tk_PostscriptY(y, ((TkCanvas *) canvas)->psInfo);
- }
- /*
- *--------------------------------------------------------------
- *
- * Tk_CanvasPsPath --
- *
- * Given an array of points for a path, generate Postscript
- * commands to create the path.
- *
- * Results:
- * Postscript commands get appended to what's in interp->result.
- *
- * Side effects:
- * None.
- *
- *--------------------------------------------------------------
- */
- void
- Tk_CanvasPsPath(interp, canvas, coordPtr, numPoints)
- Tcl_Interp *interp; /* Put generated Postscript in this
- * interpreter's result field. */
- Tk_Canvas canvas; /* Canvas on whose behalf Postscript
- * is being generated. */
- double *coordPtr; /* Pointer to first in array of
- * 2*numPoints coordinates giving
- * points for path. */
- int numPoints; /* Number of points at *coordPtr. */
- {
- Tk_PostscriptPath(interp, ((TkCanvas *) canvas)->psInfo,
- coordPtr, numPoints);
- }