From 5ebfe37da09d852e0ec8fccf71081209c0ccc590 Mon Sep 17 00:00:00 2001 From: lecoanet Date: Wed, 21 Jun 2000 15:36:16 +0000 Subject: * (WidgetObjCmd): Correction de la commande dtags. L'acc�s � la table de tags n'�tait pa prot�g�. * (WidgetObjCmd): On ne reporte plus d'erreur lors d'op�rations potentiellement multiples si aucun item n'est impliqu�. * R��criture compl�te du syst�me de recherche tag/id. Cela implique la suppression de la table de tags, l'utilisation homog�ne d'un seul algorithme de recherche d�livrant les items dans l'ordre d'affichage et enfin la possibilit� d'utiliser des connecteurs bool�ens pour associer des tags/id pour effectuer une recherche complexe. * Un certain nombre de sous commandes de find/addtag admettent un param�tre suppl�mentaire optionnel pour controler la r�cursivit� de la recherche. * La sous commande atpoint de find/addtag est remplac�e par closest avec une s�mantique identique � celle du canvas. !! INCOMPATIBILITE !! * la commande hasparts a �t� remplac�e par la commande numparts a priori plus utile. !! INCOMPATIBILITE !! * Ajout de la commande verticeat qui retourne l'index de la coordonn�e la plus proche d'un point donn� pour un item donn�. Ne fonctionne que pour Curve et Bezier (pour l'instant). * Le binding des parties priv�es aux �v�nements n'est plus sp�cifi� par des nombres n�gatifs mais par des symboles : connection, speedvector, leader, position. De m�me la commande currenpart retourne ces symboles. !! INCOMPATIBILITE !! * Les bindings associ�s � un items sont tous d�truits lorsqu'un item est d�truit. Pr�c�dement les bindings associ�s aux champs et aux parties priv�es n'�taient pas pris en compte. * Destruction du timer de clignotement lors de la destruction d'un zinc. * Correction d'un bug de redessin des bordures de la fen�tre. * Correction de pleins de bugs dans la commande mapinfo qui ne fonctionnait plus depuis le passage en Tcl_Obj. --- generic/tkZinc.c | 2241 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 1475 insertions(+), 766 deletions(-) diff --git a/generic/tkZinc.c b/generic/tkZinc.c index 452e80a..74cb1bd 100644 --- a/generic/tkZinc.c +++ b/generic/tkZinc.c @@ -63,7 +63,7 @@ static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " #include #include - +#if 0 typedef struct TagSearch { WidgetInfo *wi; Tk_Uid tag; @@ -74,6 +74,16 @@ typedef struct TagSearch { ZnList item_stack; ZnBool over; } TagSearch; +#endif +typedef struct _TagSearchExpr { + struct _TagSearchExpr *next; /* for linked lists of expressions - used in bindings */ + Tk_Uid uid; /* the uid of the whole expression */ + Tk_Uid *uids; /* expresion compiled to an array of uids */ + int allocated; /* available space for array of uids */ + int length; /* length of expression */ + int index; /* current position in expression evaluation */ + int match; /* this expression matches event's item's tags*/ +} TagSearchExpr; #define INTEGER_SPACE 30 @@ -116,6 +126,14 @@ static unsigned char bitmaps[NUM_ALPHA_STEPS][8]; static ZnBool inited = False; static Tk_Uid all_uid; static Tk_Uid current_uid; +static Tk_Uid and_uid; +static Tk_Uid or_uid; +static Tk_Uid xor_uid; +static Tk_Uid paren_uid; +static Tk_Uid end_paren_uid; +static Tk_Uid neg_paren_uid; +static Tk_Uid tag_val_uid; +static Tk_Uid neg_tag_val_uid; /* @@ -434,20 +452,21 @@ ZincObjCmd(ClientData client_data, /* Main window associated with wi->map_text_font = 0; wi->map_distance_symbol = None; wi->cursor = None; + wi->hot_item = ZN_NO_ITEM; + wi->hot_prev = ZN_NO_ITEM; wi->track_manage_history = False; wi->track_managed_history_size = 0; wi->speed_vector_length = 0; wi->tile = ZnUnspecifiedImage; wi->tile_name = NULL; - wi->tag_table = (Tcl_HashTable *) ZnMalloc(sizeof(Tcl_HashTable)); - Tcl_InitHashTable(wi->tag_table, TCL_ONE_WORD_KEYS); wi->id_table = (Tcl_HashTable *) ZnMalloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(wi->id_table, TCL_ONE_WORD_KEYS); wi->t_table = (Tcl_HashTable *) ZnMalloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(wi->t_table, TCL_ONE_WORD_KEYS); wi->obj_id = 1; + wi->num_items = 0; wi->top_group = ITEM_P.CreateItem(wi, ZnGroup, 0, NULL); @@ -461,12 +480,11 @@ ZincObjCmd(ClientData client_data, /* Main window associated with wi->events_flags = 0; wi->pick_aperture = 0; wi->new_item = wi->current_item = ZN_NO_ITEM; - wi->new_part = wi->current_part = 0; + wi->new_part = wi->current_part = ZN_NO_PART; wi->monitoring = False; wi->num_updates = 0; wi->last_time = wi->total_time = 0; - wi->num_items = 0; wi->work_item_list = NULL; wi->work_pts = ZnListNew(8, sizeof(ZnPoint)); @@ -571,193 +589,832 @@ EncodeItemPart(Item item, /* - *---------------------------------------------------------------------- + *-------------------------------------------------------------- * - * ZnSearchWithTagOrId - * ZnNextWithTagOrId -- - * - * These two functions implement an alternative way for - * collecting items that match a tag or id. It has to - * be used instead of ZnItemsWithTagOrId when it is - * mandatory for the returned items to be sorted in display - * list order. - * - * This is the way items are collected in the canvas, so - * the routines are borrowed from the canvas code. - * - * NOTE: - * These routines are a temporary solution primarily used in - * raise and lower. It should be eventually replaced by a solution - * involving only ZnItemsWithTagOrId. Especially when we'll - * be about to implement expressions with tags (&& || ^ !, etc). - * May be the best way is to sort the resulting list after - * query and selection. Need to investigate how this will be - * done. - * Or may be it should be the other way round, discards - * ZnItemsWithTagOrId and the tag table altogether. It - * depends on the cost of the various operations. + * All tag search procs below are lifted from tkCanvas.c, then + * modified to match our needs. * - *---------------------------------------------------------------------- + *-------------------------------------------------------------- */ -static Item -ZnSearchWithTagOrId(WidgetInfo *wi, - Tcl_Obj *tag, /* NULL is the same as 'all' */ - Item group, - TagSearch *tag_search) + +/* + *-------------------------------------------------------------- + * + * TagSearchExprInit -- + * + * This procedure allocates and initializes one + * TagSearchExpr struct. + * + *-------------------------------------------------------------- + */ +static void +TagSearchExprInit(TagSearchExpr **expr_var) { - Tk_Uid uid, *tags; - long id; - char *str = NULL; - int i, num_tags; - Tcl_HashEntry *entry; - Item item = ZN_NO_ITEM; + TagSearchExpr* expr = *expr_var; + + if (! expr) { + expr = (TagSearchExpr *) ZnMalloc(sizeof(TagSearchExpr)); + expr->allocated = 0; + expr->uids = NULL; + expr->next = NULL; + } + expr->uid = NULL; + expr->index = 0; + expr->length = 0; + *expr_var = expr; +} + + +/* + *-------------------------------------------------------------- + * + * TagSearchExprDestroy -- + * + * This procedure destroys one TagSearchExpr structure. + * + *-------------------------------------------------------------- + */ +static void +TagSearchExprDestroy(TagSearchExpr *expr) +{ + if (expr) { + if (expr->uids) { + ZnFree(expr->uids); + } + ZnFree(expr); + } +} + + +/* + *-------------------------------------------------------------- + * + * 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 *search is initialized + * such that a call to ZnTagSearchFirst, followed by + * successive calls to ZnTagSearchNext will return items + * that match tag. + * + * Side effects: + * + *-------------------------------------------------------------- + */ +static int +TagSearchScanExpr(Tcl_Interp *interp, /* Current interpreter. */ + TagSearch *search, /* 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; + + negate_result = 0; + found_tag = 0; + looking_for_tag = 1; + while (search->tag_index < search->tag_len) { + c = search->tag[search->tag_index++]; + + if (expr->allocated == expr->index) { + expr->allocated += 15; + if (expr->uids) { + expr->uids = (Tk_Uid *) ZnRealloc((char *) expr->uids, + expr->allocated * sizeof(Tk_Uid)); + } + else { + expr->uids = (Tk_Uid *) ZnMalloc(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 ZN_ERROR; + } + looking_for_tag++; + negate_result = 1; + break; + case '(': /* scan (negated) subexpr recursively */ + if (negate_result) { + expr->uids[expr->index++] = neg_paren_uid; + negate_result = 0; + } + else { + expr->uids[expr->index++] = paren_uid; + } + if (TagSearchScanExpr(interp, search, expr) != TCL_OK) { + /* Result string should be already set + * by nested call to tag_expr_scan() */ + return ZN_ERROR; + } + looking_for_tag = 0; + found_tag = 1; + break; + case '"': /* quoted tag string */ + if (negate_result) { + expr->uids[expr->index++] = neg_tag_val_uid; + negate_result = 0; + } + else { + expr->uids[expr->index++] = tag_val_uid; + } + tag = search->rewrite_buf; + found_endquote = 0; + while (search->tag_index < search->tag_len) { + c = search->tag[search->tag_index++]; + if (c == '\\') { + c = search->tag[search->tag_index++]; + } + if (c == '"') { + found_endquote = 1; + break; + } + *tag++ = c; + } + if (! found_endquote) { + Tcl_AppendResult(interp, "Missing endquote in tag search expression", + (char *) NULL); + return ZN_ERROR; + } + if (! (tag - search->rewrite_buf)) { + Tcl_AppendResult(interp, + "Null quoted tag string in tag search expression", + (char *) NULL); + return ZN_ERROR; + } + *tag++ = '\0'; + expr->uids[expr->index++] = Tk_GetUid(search->rewrite_buf); + 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 ZN_ERROR; + default: /* unquoted tag string */ + if (negate_result) { + expr->uids[expr->index++] = neg_tag_val_uid; + negate_result = 0; + } + else { + expr->uids[expr->index++] = tag_val_uid; + } + tag = search->rewrite_buf; + *tag++ = c; + /* copy rest of tag, including any embedded whitespace */ + while (search->tag_index < search->tag_len) { + c = search->tag[search->tag_index]; + if ((c == '!') || (c == '&') || (c == '|') || (c == '^') || + (c == '(') || (c == ')') || (c == '"')) { + break; + } + *tag++ = c; + search->tag_index++; + } + /* 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 = '\0'; + expr->uids[expr->index++] = Tk_GetUid(search->rewrite_buf); + 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 = search->tag[search->tag_index++]; + if (c != '&') { + Tcl_AppendResult(interp, "Singleton '&' in tag search expression", + (char *) NULL); + return ZN_ERROR; + } + expr->uids[expr->index++] = and_uid; + looking_for_tag = 1; + break; + case '|' : /* OR operator */ + c = search->tag[search->tag_index++]; + if (c != '|') { + Tcl_AppendResult(interp, "Singleton '|' in tag search expression", + (char *) NULL); + return ZN_ERROR; + } + expr->uids[expr->index++] = or_uid; + looking_for_tag = 1; + break; + case '^' : /* XOR operator */ + expr->uids[expr->index++] = xor_uid; + looking_for_tag = 1; + break; + case ')' : /* end subexpression */ + expr->uids[expr->index++] = end_paren_uid; + goto breakwhile; + default : /* syntax error */ + Tcl_AppendResult(interp, + "Invalid boolean operator in tag search expression", + (char *) NULL); + return ZN_ERROR; + } + } + } + breakwhile: + if (found_tag && ! looking_for_tag) { + return ZN_OK; + } + Tcl_AppendResult(interp, "Missing tag in tag search expression", + (char *) NULL); + return ZN_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(TagSearchExpr *expr, /* Search expression */ + Item item) /* 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 *tags; + int count; + int result=0; /* Value of expr so far */ + int paren_depth; + negate_result = 0; + looking_for_tag = 1; + while (expr->index < expr->length) { + uid = expr->uids[expr->index++]; + if (looking_for_tag) { + if (uid == tag_val_uid) { + /* + * assert(expr->index < expr->length); + */ + uid = expr->uids[expr->index++]; + result = 0; + /* + * set result 1 if tag is found in item's tags + */ + if (item->tags) { + tags = (Tk_Uid *) ZnListArray(item->tags); + count = ZnListSize(item->tags); + for (; count > 0; tags++, count--) { + if (*tags == uid) { + result = 1; + break; + } + } + } + } + else if (uid == neg_tag_val_uid) { + 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 + */ + if (item->tags) { + tags = (Tk_Uid *) ZnListArray(item->tags); + count = ZnListSize(item->tags); + for (; count > 0; tags++, count--) { + if (*tags == uid) { + result = 1; + break; + } + } + } + } + else if (uid == paren_uid) { + /* + * evaluate subexpressions with recursion + */ + result = TagSearchEvalExpr(expr, item); + } + else if (uid == neg_paren_uid) { + negate_result = ! negate_result; + /* + * evaluate subexpressions with recursion + */ + result = TagSearchEvalExpr(expr, item); + /* + * } else { + * assert(0); + */ + } + if (negate_result) { + result = ! result; + negate_result = 0; + } + looking_for_tag = 0; + } + else { /* ! looking_for_tag */ + if (((uid == and_uid) && (!result)) || ((uid == or_uid) && 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. + */ + paren_depth = 0; + while (expr->index < expr->length) { + uid = expr->uids[expr->index++]; + if ((uid == tag_val_uid) || (uid == neg_tag_val_uid)) { + expr->index++; + continue; + } + if ((uid == paren_uid) || (uid == neg_paren_uid)) { + paren_depth++; + continue; + } + if (uid == end_paren_uid) { + paren_depth--; + if (paren_depth < 0) { + break; + } + } + } + return result; + + } + else if (uid == xor_uid) { + /* + * if the previous result was 1 then negate the next result. + */ + negate_result = result; + } + else if (uid == end_paren_uid) { + return result; + /* + * } else { + * assert(0); + */ + } + looking_for_tag = 1; + } + } /* - * First init the search iterator. + * assert(! looking_for_tag); */ - tag_search->wi = wi; - tag_search->over = False; - tag_search->item_stack = ZnListNew(16, sizeof(Item)); - tag_search->group = (GroupItem) group; + return result; +} + +/* + *-------------------------------------------------------------- + * + * ZnTagSearchScan -- + * + * This procedure is called to initiate an enumeration of + * all items in a given zinc 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 *search is initialized such that a + * call to ZnTagSearchFirst, followed by successive calls + * to ZnTagSearchNext will return items that match tag. + * + * Side effects: + * search is linked into a list of searches in progress + * in zinc, so that elements can safely be deleted while + * the search is in progress. + * + *-------------------------------------------------------------- + */ +static int +ZnTagSearchScan(WidgetInfo *wi, + Tcl_Obj *tag_obj, /* Object giving tag value, NULL + * is the same as 'all'. */ + TagSearch **search_var) /* Record describing tag search; + * will be initialized here. */ +{ + char *tag; + int i; + TagSearch *search; + + if (tag_obj) { + tag = Tcl_GetString(tag_obj); + } + else { + tag = all_uid; + } + /* - * Try to consider the tag as an item id. + * Initialize the search. */ - if (tag) { - str = Tcl_GetString(tag); - if (isdigit(str[0])) { - if (Tcl_GetLongFromObj(wi->interp, tag, &id) == ZN_OK) { - entry = Tcl_FindHashEntry(wi->id_table, (char *) id); - if (entry != NULL) { - item = (Item) Tcl_GetHashValue(entry); + if (*search_var) { + search = *search_var; + } + else { + /* Allocate primary search struct on first call */ + *search_var = search = (TagSearch *) ZnMalloc(sizeof(TagSearch)); + search->expr = NULL; + + /* Allocate buffer for rewritten tags (after de-escaping) */ + search->rewrite_buf_alloc = 100; + search->rewrite_buf = ZnMalloc(search->rewrite_buf_alloc); + search->item_stack = ZnListNew(16, sizeof(Item)); + } + TagSearchExprInit(&(search->expr)); + + /* How long is the tagOrId ? */ + search->tag_len = strlen(tag); + + /* Make sure there is enough buffer to hold rewritten tags */ + if ((unsigned int)search->tag_len >= search->rewrite_buf_alloc) { + search->rewrite_buf_alloc = search->tag_len + 100; + search->rewrite_buf = ZnRealloc(search->rewrite_buf, + search->rewrite_buf_alloc); + } + + /* Initialize search */ + search->wi = wi; + search->over = False; + search->type = 0; + search->group = (GroupItem) wi->top_group; + search->recursive = True; + ZnListEmpty(search->item_stack); + + /* + * 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. + */ + if (search->tag_len && isdigit(*tag)) { + char *end; + + search->id = strtoul(tag, &end, 0); + if (*end == 0) { + search->type = 1; + return ZN_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. + */ + search->expr->uid = Tk_GetUid(tag); + + /* short circuit impossible searches for null tags */ + if (search->tag_len == 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 < search->tag_len; i++) { + if (tag[i] == '"') { + i++; + for ( ; i < search->tag_len; i++) { + if (tag[i] == '\\') { + i++; + continue; + } + if (tag[i] == '"') { + break; } - Tcl_ResetResult(wi->interp); - tag_search->over = True; - return item; + } + } + else { + if (((tag[i] == '&') && (tag[i+1] == '&')) || + ((tag[i] == '|') && (tag[i+1] == '|')) || + (tag[i] == '^') || (tag[i] == '!')) { + search->type = 4; + break; } } } - + + search->tag = tag; + search->tag_index = 0; + if (search->type == 4) { /* - * Now look for a tag. - */ - if (!tag) { - tag_search->tag = uid = all_uid; + * 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(wi->interp, search, search->expr) != ZN_OK) { + /* Syntax error in tag expression */ + /* Result message set by TagSearchScanExpr */ + return ZN_ERROR; + } + search->expr->length = search->expr->index; } else { - tag_search->tag = uid = Tk_GetUid(str); + if (search->expr->uid == all_uid) { + /* + * All items match. + */ + search->type = 2; + } + else { + /* + * Optimized single-tag search + */ + search->type = 3; + } + } + return ZN_OK; +} + + +/* + *-------------------------------------------------------------- + * + * ZnTagSearchFirst -- + * + * 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 *search + * is updated such that successive calls to ZnTagSearchNext + * will return successive items. + * + * Side effects: + * *search is linked into a list of searches in progress + * in zinc, so that elements can safely be deleted while + * the search is in progress. + * + *-------------------------------------------------------------- + */ +static Item +ZnTagSearchFirst(TagSearch *search, /* Record describing tag search */ + Item group, /* Start group */ + ZnBool recursive) /* Does the search walk down the + * tree ? */ +{ + Item item, previous; + Tk_Uid uid, *tags; + int count; + + /* short circuit impossible searches for null tags */ + if (search->tag_len == 0) { + return ZN_NO_ITEM; } + + search->group = (GroupItem) group; + search->recursive = recursive; + + /* + * 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 (search->type == 1) { + Tcl_HashEntry *entry; - if (uid == all_uid) { - tag_search->current = tag_search->group->head; - tag_search->previous = ZN_NO_ITEM; - return group; + item = search->wi->hot_item; + previous = search->wi->hot_prev; + if ((item == ZN_NO_ITEM) || (item->id != search->id) || + (previous == ZN_NO_ITEM) || (previous->next != item)) { + entry = Tcl_FindHashEntry(search->wi->id_table, (char *) search->id); + if (entry != NULL) { + item = (Item) Tcl_GetHashValue(entry); + previous = item->previous; + } + else { + previous = item = ZN_NO_ITEM; + } + } + search->previous = previous; + search->over = True; + search->wi->hot_item = item; + search->wi->hot_prev = previous; + return item; } - item = group; + if (search->type == 2) { + /* + * All items match. + */ + search->previous = ZN_NO_ITEM; + search->current = (Item) search->group->head; + return search->current; + } + + uid = search->expr->uid; + item = (Item) search->group->head; + previous = ZN_NO_ITEM; do { while (item != ZN_NO_ITEM) { - if (item->tags) { - tags = (Tk_Uid *) ZnListArray(item->tags); - num_tags = ZnListSize(item->tags); - for (i = 0; i < num_tags; i++, tags++) { - if (*tags == uid) { - tag_search->current = item; - tag_search->previous = item->previous; - return item; + if (search->type == 3) { + /* + * Optimized single-tag search + */ + if (item->tags) { + tags = (Tk_Uid *) ZnListArray(item->tags); + count = ZnListSize(item->tags); + for (; count > 0; tags++, count--) { + if (*tags == uid) { + search->previous = previous; + search->current = item; + return item; + } } } } - if (item->class == ZnGroup) { + else { + /* + * Type = 4. Search for an item matching + * the tag expression. + */ + search->expr->index = 0; + if (TagSearchEvalExpr(search->expr, item)) { + search->previous = previous; + search->current = item; + return item; + } + } + if ((item->class == ZnGroup) && (search->recursive)) { /* * Explore the hierarchy depth first using the item stack * to save the current node. */ - /*printf("ZnSearchWithTagOrId diving for tag\n");*/ - tag_search->group = (GroupItem) item; - item = item->next; - ZnListAdd(tag_search->item_stack, &item, ZnListTail); - item = tag_search->group->head; + /*printf("ZnTagSearchFirst diving for tag '%s', detph %d\n", + search->tag, ZnListSize(search->item_stack)/2);*/ + search->group = (GroupItem) item; + previous = item; + if (item == group) { + item = ZN_NO_ITEM; + } + else { + item = item->next; + } + ZnListAdd(search->item_stack, &previous, ZnListTail); + ZnListAdd(search->item_stack, &item, ZnListTail); + previous = ZN_NO_ITEM; + item = search->group->head; } else { + previous = item; item = item->next; } } - /*printf("ZnSearchWithTagOrId backup for tag\n");*/ - while ((item == ZN_NO_ITEM) && ZnListSize(tag_search->item_stack)) { - item = *(Item *) ZnListAt(tag_search->item_stack, ZnListTail); - ZnListDelete(tag_search->item_stack, ZnListTail); + /* + * Continue search on higher group level. + */ + /*printf("ZnTagSearchFirst backup for tag, detph %d\n", + ZnListSize(search->item_stack)/2);*/ + while ((item == ZN_NO_ITEM) && ZnListSize(search->item_stack)) { + item = *(Item *) ZnListAt(search->item_stack, ZnListTail); + ZnListDelete(search->item_stack, ZnListTail); + previous = *(Item *) ZnListAt(search->item_stack, ZnListTail); + ZnListDelete(search->item_stack, ZnListTail); } if (item != ZN_NO_ITEM) { - tag_search->group = (GroupItem) item->parent; + search->group = (GroupItem) item->parent; } } while (item != ZN_NO_ITEM); - tag_search->over = True; - ZnListFree(tag_search->item_stack); + search->previous = previous; + search->over = True; return ZN_NO_ITEM; } + +/* + *-------------------------------------------------------------- + * + * ZnTagSearchNext -- + * + * This procedure returns successive items that match a given + * tag; it should be called only after ZnTagSearchFirst 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. *search is updated so that the next call + * to this procedure will return the next item. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ static Item -ZnNextWithTagOrId(TagSearch *tag_search) +ZnTagSearchNext(TagSearch *search) /* Record describing search in progress. */ { - Item item, previous; - Tk_Uid uid, *tags; - int i, num_tags; - - if (tag_search->over) { + Item item, previous; + Tk_Uid uid, *tags; + int count; + + if (search->over) { return ZN_NO_ITEM; } - previous = tag_search->previous; + /* + * Find next item in list (this may not actually be a suitable + * one to return), and return if there are no items left. + */ + previous = search->previous; if (previous == ZN_NO_ITEM) { - item = tag_search->group->head; + item = search->group->head; } else { item = previous->next; } - if (item != tag_search->current) { + if (item != search->current) { /* - * The list has been edited and the previously - * returned item is gone. Don't step forward, - * just process the new next item. (What happen - * if the previous item is destroyed too. Do we - * NULLify the next pointer in this case ?) + * The structure of the list has changed. Probably the + * previously-returned item was removed from the list. + * In this case, don't advance previous; just return + * its new successor (i.e. do nothing here). */ - } - else if (item->class == ZnGroup){ + else if ((item->class == ZnGroup) && (search->recursive)) { /* * Explore the hierarchy depth first using the item stack * to save the current node. */ - tag_search->group = (GroupItem) item; + search->group = (GroupItem) item; + previous = item; item = item->next; - /*printf("ZnNextWithTagOrId diving for all, pushing %d\n", + /*printf("ZnTagSearchNext diving for all, pushing %d\n", item?item->id:0);*/ - ZnListAdd(tag_search->item_stack, &item, ZnListTail); + ZnListAdd(search->item_stack, &previous, ZnListTail); + ZnListAdd(search->item_stack, &item, ZnListTail); previous = ZN_NO_ITEM; - item = tag_search->group->head; + item = search->group->head; } else { previous = item; item = previous->next; } - + if (item == ZN_NO_ITEM) { - /*printf("ZnNextWithTagOrId backup for all\n");*/ - while ((item == ZN_NO_ITEM) && ZnListSize(tag_search->item_stack)) { + while ((item == ZN_NO_ITEM) && ZnListSize(search->item_stack)) { /* * End of list at this level, back up one level. */ - item = *(Item *) ZnListAt(tag_search->item_stack, ZnListTail); - ZnListDelete(tag_search->item_stack, ZnListTail); + item = *(Item *) ZnListAt(search->item_stack, ZnListTail); + ZnListDelete(search->item_stack, ZnListTail); + previous = *(Item *) ZnListAt(search->item_stack, ZnListTail); + ZnListDelete(search->item_stack, ZnListTail); } if (item != ZN_NO_ITEM) { - tag_search->group = (GroupItem) item->parent; - previous = item->previous; - /*printf("ZnNextWithTagOrId popping %d, previous %d, next %d\n", + search->group = (GroupItem) item->parent; + /*printf("ZnTagSearchNext popping %d, previous %d, next %d\n", item->id, (item->previous)?item->previous->id:0, (item->next)?item->next->id:0);*/ } @@ -765,78 +1422,111 @@ ZnNextWithTagOrId(TagSearch *tag_search) /* * Or finish the search if at top. */ - tag_search->over = True; - ZnListFree(tag_search->item_stack); + search->over = True; return ZN_NO_ITEM; } } - /* - * Handle the tag 'all' by returning the next item. - */ - uid = tag_search->tag; - if (uid == all_uid) { - tag_search->previous = previous; - tag_search->current = item; - /*printf("ZnNextWithTagOrId returning %d\n", item->id);*/ + if (search->type == 2) { + /* + * All items match. + */ + search->previous = previous; + search->current = item; return item; } - - /* - * Now, look for the requested tag in an item. - */ + + uid = search->expr->uid; do { while (item != ZN_NO_ITEM) { - if (item->tags) { - tags = (Tk_Uid *) ZnListArray(item->tags); - num_tags = ZnListSize(item->tags); - for (i = 0; i < num_tags; i++, tags++) { - if (*tags == uid) { - tag_search->current = item; - tag_search->previous = item->previous; - return item; + if (search->type == 3) { + /* + * Optimized single-tag search + */ + if (item->tags) { + tags = (Tk_Uid *) ZnListArray(item->tags); + count = ZnListSize(item->tags); + for ( ; count > 0; tags++, count--) { + if (*tags == uid) { + search->previous = previous; + search->current = item; + return item; + } } } } - if (item->class == ZnGroup) { + else { + /* + * Else.... evaluate tag expression + */ + search->expr->index = 0; + if (TagSearchEvalExpr(search->expr, item)) { + search->previous = previous; + search->current = item; + return item; + } + } + if ((item->class == ZnGroup) && (search->recursive)) { /* * Explore the hierarchy depth first using the item stack * to save the current node. */ - /*printf("ZnNextWithTagOrId diving for tag\n");*/ - tag_search->group = (GroupItem) item; + /*printf("ZnTagSearchNext diving for tag, depth %d\n", + ZnListSize(search->item_stack)/2);*/ + search->group = (GroupItem) item; + previous = item; item = item->next; - ZnListAdd(tag_search->item_stack, &item, ZnListTail); - item = tag_search->group->head; + ZnListAdd(search->item_stack, &previous, ZnListTail); + ZnListAdd(search->item_stack, &item, ZnListTail); + previous = ZN_NO_ITEM; + item = search->group->head; } else { + previous = item; item = item->next; } } - /*printf("ZnNextWithTagOrId backup for tag\n");*/ - while ((item == ZN_NO_ITEM) && ZnListSize(tag_search->item_stack)) { - item = *(Item *) ZnListAt(tag_search->item_stack, ZnListTail); - ZnListDelete(tag_search->item_stack, ZnListTail); + /*printf("ZnTagSearchNext backup for tag, depth %d\n", + ZnListSize(search->item_stack)/2);*/ + while ((item == ZN_NO_ITEM) && ZnListSize(search->item_stack)) { + item = *(Item *) ZnListAt(search->item_stack, ZnListTail); + ZnListDelete(search->item_stack, ZnListTail); + previous = *(Item *) ZnListAt(search->item_stack, ZnListTail); + ZnListDelete(search->item_stack, ZnListTail); } if (item != ZN_NO_ITEM) { - tag_search->group = (GroupItem) item->parent; + search->group = (GroupItem) item->parent; } } while (item != ZN_NO_ITEM); /* * Out of fuel. */ - tag_search->over = True; - ZnListFree(tag_search->item_stack); + search->previous = previous; + search->over = True; + return ZN_NO_ITEM; } + +/* + *-------------------------------------------------------------- + * + * ZnTagSearchDestroy -- + * + * This procedure destroys any dynamic structures that + * may have been allocated by TagSearchScan. + * + *-------------------------------------------------------------- + */ void -ZnDoneWithSearch(TagSearch *tag_search) +ZnTagSearchDestroy(TagSearch *search) /* Record describing tag search */ { - if (!tag_search->over) { - tag_search->over = True; - ZnListFree(tag_search->item_stack); + if (search) { + TagSearchExprDestroy(search->expr); + ZnListFree(search->item_stack); + ZnFree(search->rewrite_buf); + ZnFree(search); } } @@ -844,108 +1534,27 @@ ZnDoneWithSearch(TagSearch *tag_search) /* *---------------------------------------------------------------------- * - * ZnItemsWithTagOrId -- - * - * Collect the item(s) with the given tag or id. The function - * return the the first item matched in item and the whole - * search result in item_list, if not NULL. - * The function return value is always the total number of - * items matched. - * If nothing can be found, the function returns 0 and leave a - * message in the interp result. No other values are returned. + * ZnItemWithTagOrId -- * - * The caller must at least give the item parameter a legal - * value. + * Return the first item matching the given tag or id. The + * function returns the item in 'item' and the operation + * status as the function's value. * *---------------------------------------------------------------------- */ int -ZnItemsWithTagOrId(WidgetInfo *wi, - Tcl_Obj *tag_or_id, - Item *item, - Item **item_list) +ZnItemWithTagOrId(WidgetInfo *wi, + Tcl_Obj *tag_or_id, + Item group, + ZnBool recursive, + Item *item, + TagSearch **search_var) { - unsigned long id; - Tcl_HashEntry *entry; - Tk_Uid tag_uid; - ZnList items; - int num_items; - char *str; - - str = Tcl_GetString(tag_or_id); - if (isdigit(str[0])) { - if (Tcl_GetLongFromObj(wi->interp, tag_or_id, &id) == ZN_OK) { - Tcl_ResetResult(wi->interp); - entry = Tcl_FindHashEntry(wi->id_table, (char *) id); - if (entry == NULL) { - Tcl_AppendResult(wi->interp, "item \"", str, "\" doesn't exist", NULL); - return 0; - } - *item = (Item) Tcl_GetHashValue(entry); - if (item_list) { - *item_list = item; - } - return 1; - } - } - - /* - * It is a tag, try to find it and return the whole list. - */ - tag_uid = Tk_GetUid(str); - - /* - * If it is 'all' then build a list of all the items - * in the display list. - * BE CAREFUL this won't work recursively !! A tag - * search for 'all' can't be started if the result - * of a previous one is still in use in the same widget. - */ - if (tag_uid == all_uid) { - Item current; - TagSearch ts; - - if (wi->work_item_list) { - ZnListEmpty(wi->work_item_list); - } - else { - wi->work_item_list = ZnListNew(128, sizeof(Item)); - } - for (current = ZnSearchWithTagOrId(wi, NULL, wi->top_group, &ts); - current != NULL; current = ZnNextWithTagOrId(&ts)) { - ZnListAdd(wi->work_item_list, ¤t, ZnListTail); - } - if (ZnListSize(wi->work_item_list) != 0) { - *item = *((Item *) ZnListAt(wi->work_item_list, 0)); - if (item_list) { - *item_list = (Item *) ZnListArray(wi->work_item_list); - } - } - return ZnListSize(wi->work_item_list); - } - - /* - * Else, probe the tag table. - */ - entry = Tcl_FindHashEntry(wi->tag_table, tag_uid); - if (entry) { - items = (ZnList) Tcl_GetHashValue(entry); - num_items = ZnListSize(items); - if (num_items) { - if (item_list) { - *item_list = (Item *) ZnListArray(items); - *item = (*item_list)[0]; - } - else { - *item = *((Item *) ZnListAt(items, 0)); - } - } - return num_items; - } - else { - Tcl_AppendResult(wi->interp, "unknown tag \"", tag_uid, "\"", NULL); - return 0; + if (ZnTagSearchScan(wi, tag_or_id, search_var) != ZN_OK) { + return ZN_ERROR; } + *item = ZnTagSearchFirst(*search_var, group, recursive); + return ZN_OK; } @@ -985,27 +1594,6 @@ DoItem(Tcl_Interp *interp, /* *---------------------------------------------------------------------- * - * IsHeirOf -- - * Tells if item is a descendant, either direct or indirect, - * of group. - * - *---------------------------------------------------------------------- - */ -static ZnBool -IsHeirOf(Item item, - Item group) -{ - while ((item != ZN_NO_ITEM) && (item != group)) { - item = item->parent; - } - - return (item != ZN_NO_ITEM); -} - - -/* - *---------------------------------------------------------------------- - * * FindArea -- * Search the items that are enclosed or overlapping a given * area of the widget. It is used by FindItems. @@ -1023,8 +1611,7 @@ static int FindArea(WidgetInfo *wi, Tcl_Obj *CONST args[], Tk_Uid tag_uid, - Item group, - int enclosed) + ZnBool enclosed) { ZnPos pos; ZnBBox area; @@ -1058,9 +1645,6 @@ FindArea(WidgetInfo *wi, area.corner.x -= wi->inset; area.corner.y -= wi->inset; - /* - * BUG: The starting group should be considered instead of top_group.!! - */ wi->top_group->class->ToArea(wi->top_group, &area, tag_uid, enclosed, False); return ZN_OK; @@ -1087,18 +1671,19 @@ FindItems(WidgetInfo *wi, int argc, Tcl_Obj *CONST args[], Tcl_Obj *tag, /* NULL to search or tag to add tag. */ - int first) /* First arg to process in args */ + int first, /* First arg to process in args */ + TagSearch **search_var) { Tk_Uid tag_uid = NULL; - TagSearch ts; - int index, num_items; + ZnBool recursive = True; + int index, result; Item item, group = wi->top_group; static char *search_cmd_strings[] = { - "above", "all", "atpoint", "atpriority", "below", "enclosed", + "above", "all", "atpriority", "below", "closest", "enclosed", "overlapping", "withtag", "withtype", NULL }; enum search_cmds { - ZN_S_ABOVE, ZN_S_ALL, ZN_S_ATPOINT, ZN_S_ATPRIORITY, ZN_S_BELOW, + ZN_S_ABOVE, ZN_S_ALL, ZN_S_ATPRIORITY, ZN_S_BELOW, ZN_S_CLOSEST, ZN_S_ENCLOSED, ZN_S_OVERLAPPING, ZN_S_WITHTAG, ZN_S_WITHTYPE }; @@ -1117,18 +1702,29 @@ FindItems(WidgetInfo *wi, */ case ZN_S_ABOVE: { - if ((argc != first+2) && (argc != first+3)) { - Tcl_WrongNumArgs(wi->interp, first+1, args, "tagOrId ?inGroup?"); + if ((argc < first+2) || (argc > first+4)) { + Tcl_WrongNumArgs(wi->interp, first+1, args, + "tagOrId ?inGroup? ?recursive?"); return ZN_ERROR; } - if (argc == first+3) { - num_items = ZnItemsWithTagOrId(wi, args[first+2], &group, NULL); - if ((num_items == 0) || (group->class != ZnGroup)) { + if (argc == first+4) { + if (Tcl_GetBooleanFromObj(wi->interp, args[first+3], + &recursive) != ZN_OK) { + return ZN_ERROR; + } + } + if (argc > first+2) { + result = ZnItemWithTagOrId(wi, args[first+2], wi->top_group, True, + &group, search_var); + if ((result == ZN_ERROR) || (group == ZN_NO_ITEM) || + (group->class != ZnGroup)) { return ZN_ERROR; } } - item = ZnSearchWithTagOrId(wi, args[first+1], group, &ts); - if ((item != ZN_NO_ITEM) && (item->previous != ZN_NO_ITEM)) { + result = ZnItemWithTagOrId(wi, args[first+1], group, recursive, + &item, search_var); + if ((result == ZN_OK) && + (item != ZN_NO_ITEM) && (item->previous != ZN_NO_ITEM)) { DoItem(wi->interp, item->previous, ZN_NO_PART, tag_uid); } } @@ -1138,13 +1734,21 @@ FindItems(WidgetInfo *wi, */ case ZN_S_ALL: { - if ((argc != first+1) && (argc != first+2)) { - Tcl_WrongNumArgs(wi->interp, first+1, args, "?inGroup?"); + if ((argc < first+1) || (argc > first+3)) { + Tcl_WrongNumArgs(wi->interp, first+1, args, "?inGroup? ?recursive?"); return ZN_ERROR; } - if (argc == first+2) { - num_items = ZnItemsWithTagOrId(wi, args[first+1], &group, NULL); - if ((num_items == 0) || (group->class != ZnGroup)) { + if (argc == first+3) { + if (Tcl_GetBooleanFromObj(wi->interp, args[first+2], + &recursive) != ZN_OK) { + return ZN_ERROR; + } + } + if (argc > first+1) { + result = ZnItemWithTagOrId(wi, args[first+1], wi->top_group, True, + &group, search_var); + if ((result == ZN_ERROR) || (group == ZN_NO_ITEM) || + (group->class != ZnGroup)) { return ZN_ERROR; } } @@ -1153,65 +1757,15 @@ FindItems(WidgetInfo *wi, * Go through the item list and collect all item ids. They * are sorted from most visible to least visible. */ - for (item = ZnSearchWithTagOrId(wi, NULL, group, &ts); - item != ZN_NO_ITEM; item = ZnNextWithTagOrId(&ts)) { + if (ZnTagSearchScan(wi, NULL, search_var) == ZN_ERROR) { + return ZN_ERROR; + } + for (item = ZnTagSearchFirst(*search_var, group, recursive); + item != ZN_NO_ITEM; item = ZnTagSearchNext(*search_var)) { DoItem(wi->interp, item, ZN_NO_PART, tag_uid); } } break; - /* - * atpoint - */ - case ZN_S_ATPOINT: - { - int halo = 1; - ZnPoint p; - int part = ZN_NO_PART; - Item start_item = ZN_NO_ITEM; - - if ((argc < first+3) || (argc > first+5)) { - Tcl_WrongNumArgs(wi->interp, first+1, args, "x y ?halo? ?start?"); - return ZN_ERROR; - } - if (Tcl_GetDoubleFromObj(wi->interp, args[first+1], &p.x) == ZN_ERROR) { - return ZN_ERROR; - } - if (Tcl_GetDoubleFromObj(wi->interp, args[first+2], &p.y) == ZN_ERROR) { - return ZN_ERROR; - } - p.x -= wi->inset; - p.y -= wi->inset; - if (argc > first+3) { - if (Tcl_GetIntFromObj(wi->interp, args[first+3], &halo) == ZN_ERROR) { - return ZN_ERROR; - } - if (halo < 0) { - halo = 0; - } - } - if (argc > first+4) { - start_item = ZnSearchWithTagOrId(wi, args[first+4], group, &ts); - if (start_item != ZN_NO_ITEM) { - start_item = start_item->next; - } - } - /* - * We always start the search at the top group to use the - * transform and clip machinery of the group item. The items - * are not required to cache the device coords, etc. So we need - * to setup the correct context before calling the Pick method - * for each item. - */ - wi->top_group->class->Pick(wi->top_group, &p, start_item, halo, - &item, &part); - - if (item != ZN_NO_ITEM) { - DoItem(wi->interp, item, part, tag_uid); - /*printf("first %d %d\n", item->id, part);*/ - return TCL_OK; - } - } - break; /* * atpriority */ @@ -1219,16 +1773,24 @@ FindItems(WidgetInfo *wi, { int pri; - if ((argc != first+2) && (argc != first+3)) { - Tcl_WrongNumArgs(wi->interp, first+1, args, "pri ?inGroup?"); + if ((argc < first+2) || (argc > first+4)) { + Tcl_WrongNumArgs(wi->interp, first+1, args, "pri ?inGroup? ?recursive?"); return ZN_ERROR; } + if (argc == first+4) { + if (Tcl_GetBooleanFromObj(wi->interp, args[first+3], + &recursive) != ZN_OK) { + return ZN_ERROR; + } + } if (Tcl_GetIntFromObj(wi->interp, args[first+1], &pri) == ZN_ERROR) { return ZN_ERROR; } - if (argc == first+3) { - num_items = ZnItemsWithTagOrId(wi, args[first+2], &group, NULL); - if ((num_items == 0) || (group->class != ZnGroup)) { + if (argc > first+2) { + result = ZnItemWithTagOrId(wi, args[first+2], wi->top_group, True, + &group, search_var); + if ((result == ZN_ERROR) || (group == ZN_NO_ITEM) || + (group->class != ZnGroup)) { return ZN_ERROR; } } @@ -1237,8 +1799,11 @@ FindItems(WidgetInfo *wi, * Go through the item table and collect all items with * the given priority. */ - for (item = ZnSearchWithTagOrId(wi, NULL, group, &ts); - item != ZN_NO_ITEM; item = ZnNextWithTagOrId(&ts)) { + if (ZnTagSearchScan(wi, NULL, search_var) == ZN_ERROR) { + return ZN_ERROR; + } + for (item = ZnTagSearchFirst(*search_var, group, recursive); + item != ZN_NO_ITEM; item = ZnTagSearchNext(*search_var)) { if (item->priority == pri) { DoItem(wi->interp, item, ZN_NO_PART, tag_uid); } @@ -1252,19 +1817,31 @@ FindItems(WidgetInfo *wi, { Item next; - if ((argc != first+2) && (argc != first+3)) { - Tcl_WrongNumArgs(wi->interp, first+1, args, "tagOrId $inGroup?"); + if ((argc < first+2) || (argc > first+4)) { + Tcl_WrongNumArgs(wi->interp, first+1, args, + "tagOrId $inGroup? ?recursive?"); return ZN_ERROR; } - if (argc == first+3) { - num_items = ZnItemsWithTagOrId(wi, args[first+2], &group, NULL); - if ((num_items == 0) || (group->class != ZnGroup)) { + if (argc == first+4) { + if (Tcl_GetBooleanFromObj(wi->interp, args[first+3], + &recursive) != ZN_OK) { + return ZN_ERROR; + } + } + if (argc > first+2) { + result = ZnItemWithTagOrId(wi, args[first+2], wi->top_group, True, + &group, search_var); + if ((result == ZN_ERROR) || (group == ZN_NO_ITEM) || + (group->class != ZnGroup)) { return ZN_ERROR; } } item = ZN_NO_ITEM; - for (next = ZnSearchWithTagOrId(wi, args[first+1], group, &ts); - next != ZN_NO_ITEM; item = ZnNextWithTagOrId(&ts)) { + if (ZnTagSearchScan(wi, args[first+1], search_var) == ZN_ERROR) { + return ZN_ERROR; + } + for (next = ZnTagSearchFirst(*search_var, group, recursive); + next != ZN_NO_ITEM; next = ZnTagSearchNext(*search_var)) { item = next; } if ((item != ZN_NO_ITEM) && (item->next != ZN_NO_ITEM)) { @@ -1273,21 +1850,70 @@ FindItems(WidgetInfo *wi, } break; /* - * enclosed + * closest */ - case ZN_S_ENCLOSED: + case ZN_S_CLOSEST: { - if ((argc != first+5) && (argc != first+6)) { - Tcl_WrongNumArgs(wi->interp, first+1, args, "x1 y1 x2 y2 ?inGroup?"); + int halo = 1; + ZnPoint p; + int part = ZN_NO_PART; + Item start_item = ZN_NO_ITEM; + + if ((argc < first+3) || (argc > first+5)) { + Tcl_WrongNumArgs(wi->interp, first+1, args, "x y ?halo? ?start?"); + return ZN_ERROR; + } + if (Tcl_GetDoubleFromObj(wi->interp, args[first+1], &p.x) == ZN_ERROR) { + return ZN_ERROR; + } + if (Tcl_GetDoubleFromObj(wi->interp, args[first+2], &p.y) == ZN_ERROR) { return ZN_ERROR; } - if (argc == first+6) { - num_items = ZnItemsWithTagOrId(wi, args[first+5], &group, NULL); - if ((num_items == 0) || (group->class != ZnGroup)) { + p.x -= wi->inset; + p.y -= wi->inset; + if (argc > first+3) { + if (Tcl_GetIntFromObj(wi->interp, args[first+3], &halo) == ZN_ERROR) { return ZN_ERROR; } + if (halo < 0) { + halo = 0; + } + } + if (argc > (first+4)) { + result = ZnItemWithTagOrId(wi, args[first+4], wi->top_group, True, + &start_item, search_var); + if ((result == ZN_OK) && (start_item != ZN_NO_ITEM)) { + start_item = start_item->next; + } + } + /* + * We always start the search at the top group to use the + * transform and clip machinery of the group item. The items + * are not required to cache the device coords, etc. So we need + * to setup the correct context before calling the Pick method + * for each item. + */ + wi->top_group->class->Pick(wi->top_group, &p, start_item, halo, + &item, &part); + + if (item != ZN_NO_ITEM) { + DoItem(wi->interp, item, part, tag_uid); + /*printf("first %d %d\n", item->id, part);*/ + return TCL_OK; + } + } + break; + /* + * enclosed + */ + case ZN_S_ENCLOSED: + { + if (argc != first+5) { + Tcl_WrongNumArgs(wi->interp, first+1, args, + "x1 y1 x2 y2"); + return ZN_ERROR; } - return FindArea(wi, args+first+1, tag_uid, group, True); + return FindArea(wi, args+first+1, tag_uid, True); } break; /* @@ -1295,17 +1921,12 @@ FindItems(WidgetInfo *wi, */ case ZN_S_OVERLAPPING: { - if ((argc != first+5) && (argc != first+6)) { - Tcl_WrongNumArgs(wi->interp, first+1, args, "x1 y1 x2 y2 ?inGroup?"); + if (argc != first+5) { + Tcl_WrongNumArgs(wi->interp, first+1, args, + "x1 y1 x2 y2"); return ZN_ERROR; } - if (argc == first+6) { - num_items = ZnItemsWithTagOrId(wi, args[first+5], &group, NULL); - if ((num_items == 0) || (group->class != ZnGroup)) { - return ZN_ERROR; - } - } - return FindArea(wi, args+first+1, tag_uid, group, False); + return FindArea(wi, args+first+1, tag_uid, False); } break; /* @@ -1313,28 +1934,32 @@ FindItems(WidgetInfo *wi, */ case ZN_S_WITHTAG: { - Item *items; - int i; - - if ((argc != first+2) && (argc != first+3)) { - Tcl_WrongNumArgs(wi->interp, first+1, args, "tagOrId ?inGroup?"); + if ((argc < first+2) || (argc > first+4)) { + Tcl_WrongNumArgs(wi->interp, first+1, args, + "tagOrId ?inGroup? ?recursive?"); return ZN_ERROR; } - if (argc == first+3) { - num_items = ZnItemsWithTagOrId(wi, args[first+2], &group, NULL); - if ((num_items == 0) || (group->class != ZnGroup)) { + if (argc == first+4) { + if (Tcl_GetBooleanFromObj(wi->interp, args[first+3], + &recursive) != ZN_OK) { return ZN_ERROR; } } - num_items = ZnItemsWithTagOrId(wi, args[first+1], &item, &items); - if (num_items == 0) { - Tcl_SetObjResult(wi->interp, NewStringObj("")); - } - for (i = 0; i < num_items; i++) { - if (IsHeirOf(items[i], group)) { - DoItem(wi->interp, items[i], ZN_NO_PART, tag_uid); + if (argc > first+2) { + result = ZnItemWithTagOrId(wi, args[first+2], wi->top_group, True, + &group, search_var); + if ((result == ZN_ERROR) || + (group == ZN_NO_ITEM) || (group->class != ZnGroup)) { + return ZN_ERROR; } } + if (ZnTagSearchScan(wi, args[first+1], search_var) == ZN_ERROR) { + return ZN_ERROR; + } + for (item = ZnTagSearchFirst(*search_var, group, recursive); + item != ZN_NO_ITEM; item = ZnTagSearchNext(*search_var)) { + DoItem(wi->interp, item, ZN_NO_PART, tag_uid); + } } break; /* @@ -1344,8 +1969,9 @@ FindItems(WidgetInfo *wi, { ItemClass cls; - if ((argc != first+2) && (argc != first+3)) { - Tcl_WrongNumArgs(wi->interp, first+1, args, "itemType ?inGroup?"); + if ((argc < first+2) || (argc > first+4)) { + Tcl_WrongNumArgs(wi->interp, first+1, args, + "itemType ?inGroup? ?recursive?"); return ZN_ERROR; } cls = (ItemClass) ITEM_P.LookupItemClass(Tcl_GetString(args[first+1])); @@ -1354,9 +1980,17 @@ FindItems(WidgetInfo *wi, Tcl_GetString(args[first+1]), "\"", NULL); return ZN_ERROR; } - if (argc == first+3) { - num_items = ZnItemsWithTagOrId(wi, args[first+2], &group, NULL); - if ((num_items == 0) || (group->class != ZnGroup)) { + if (argc == first+4) { + if (Tcl_GetBooleanFromObj(wi->interp, args[first+3], + &recursive) != ZN_OK) { + return ZN_ERROR; + } + } + if (argc > first+2) { + result = ZnItemWithTagOrId(wi, args[first+2], wi->top_group, True, + &group, search_var); + if ((result == ZN_ERROR) || + (group == ZN_NO_ITEM) || (group->class != ZnGroup)) { return ZN_ERROR; } } @@ -1365,8 +1999,11 @@ FindItems(WidgetInfo *wi, * Go through the item table and collect all items with * the given item type. */ - for (item = ZnSearchWithTagOrId(wi, NULL, group, &ts); - item != ZN_NO_ITEM; item = ZnNextWithTagOrId(&ts)) { + if (ZnTagSearchScan(wi, NULL, search_var) == ZN_ERROR) { + return ZN_ERROR; + } + for (item = ZnTagSearchFirst(*search_var, group, recursive); + item != ZN_NO_ITEM; item = ZnTagSearchNext(*search_var)) { if (item->class == cls) { DoItem(wi->interp, item, ZN_NO_PART, tag_uid); } @@ -1430,12 +2067,13 @@ ParseCoordList(WidgetInfo *wi, static int Contour(WidgetInfo *wi, int argc, - Tcl_Obj *CONST args[]) + Tcl_Obj *CONST args[], + TagSearch **search_var) { ZnPoint *points; ZnPoint p[4], xp[4]; Item item, shape; - int cmd, num_points, num, i; + int cmd, num_points, result, i; ZnBool simple=False; ZnPoly poly; int index; @@ -1448,8 +2086,9 @@ Contour(WidgetInfo *wi, GPC_DIFF, GPC_INT, GPC_UNION, GPC_XOR }; - num = ZnItemsWithTagOrId(wi, args[2], &item, NULL); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &item, search_var); + if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)){ return ZN_ERROR; } if (!item->class->Contour) { @@ -1464,8 +2103,9 @@ Contour(WidgetInfo *wi, } cmd = ops[index]; - num = ZnItemsWithTagOrId(wi, args[4], &shape, NULL); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[4], wi->top_group, True, + &shape, search); + if ((result == ZN_ERROR) || (shape == ZN_NO_ITEM)) { Tcl_ResetResult(wi->interp); if (ParseCoordList(wi, args[4], &points, &num_points) == ZN_ERROR) { return ZN_ERROR; @@ -1547,18 +2187,20 @@ Contour(WidgetInfo *wi, static int Coords(WidgetInfo *wi, int argc, - Tcl_Obj *CONST args[]) + Tcl_Obj *CONST args[], + TagSearch **search_var) { ZnPoint *points; Item item; - int num_points, num = 0, i; + int num_points, result, i; int cmd = COORDS_READ; long index, contour = 0; char *str; Tcl_Obj *l; - num = ZnItemsWithTagOrId(wi, args[2], &item, NULL); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &item, search_var); + if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { return ZN_ERROR; } num_points = 0; @@ -1748,35 +2390,36 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ { WidgetInfo *wi = (WidgetInfo *) client_data; int result, cmd_index, index; - Item item, *items; - ZnList item_list; + Item item, item2; int num = 0, i, j; - char *end, *end2, *str; + char *end, *str; ZnTransfo *t = NULL; Tcl_Obj *l, *lobjs[4]; + TagSearch *search_var = NULL; + Tcl_HashEntry *entry; static char *sub_cmd_strings[] = { "add", "addtag", "anchorxy", "bbox", "becomes", "bind", "cget", "chggroup", "clone", "configure", "contour", - "coords", "currentpart", "cursor", "dchars", "dtag", - "find", "fit", "focus", "gettags", "group", - "hasanchors", "hasfields", "hasparts", "hastag", + "coords", "currentpart", "cursor", "dchars", + "dtag", "find", "fit", "focus", "gettags", "group", + "hasanchors", "hasfields", "hastag", "index", "insert", "itemcget", "itemconfigure", - "lower", "monitor", "postscript", "raise", "remove", - "rotate", "scale", "select", "smooth", "tapply", - "tdelete", "transform", "translate", "treset", - "trestore", "tsave", "type", NULL + "lower", "monitor", "numparts", "postscript", "raise", + "remove", "rotate", "scale", "select", "smooth", + "tapply", "tdelete", "transform", "translate", "treset", + "trestore", "tsave", "type", "verticeat", NULL }; enum sub_cmds { ZN_W_ADD, ZN_W_ADDTAG, ZN_W_ANCHORXY, ZN_W_BBOX, ZN_W_BECOMES, ZN_W_BIND, - ZN_W_CGET, ZN_W_CHGGROUP, ZN_W_CLONE, ZN_W_CONFIGURE, ZN_W_CONTOUR, - ZN_W_COORDS, ZN_W_CURRENTPART, ZN_W_CURSOR, ZN_W_DCHARS, ZN_W_DTAG, - ZN_W_FIND, ZN_W_FIT, ZN_W_FOCUS, ZN_W_GETTAGS, ZN_W_GROUP, - ZN_W_HASANCHORS, ZN_W_HASFIELDS, ZN_W_HASPARTS, ZN_W_HASTAG, + ZN_W_CGET, ZN_W_CHGGROUP, ZN_W_CLONE, ZN_W_CONFIGURE, + ZN_W_CONTOUR, ZN_W_COORDS, ZN_W_CURRENTPART, ZN_W_CURSOR, ZN_W_DCHARS, + ZN_W_DTAG, ZN_W_FIND, ZN_W_FIT, ZN_W_FOCUS, ZN_W_GETTAGS, ZN_W_GROUP, + ZN_W_HASANCHORS, ZN_W_HASFIELDS, ZN_W_HASTAG, ZN_W_INDEX, ZN_W_INSERT, ZN_W_ITEMCGET, ZN_W_ITEMCONFIGURE, - ZN_W_LOWER, ZN_W_MONITOR, ZN_W_POSTSCRIPT, ZN_W_RAISE, ZN_W_REMOVE, - ZN_W_ROTATE, ZN_W_SCALE, ZN_W_SELECT, ZN_W_SMOOTH, ZN_W_TAPPLY, - ZN_W_TDELETE, ZN_W_TRANSFORM, ZN_W_TRANSLATE, ZN_W_TRESET, - ZN_W_TRESTORE, ZN_W_TSAVE, ZN_W_TYPE + ZN_W_LOWER, ZN_W_MONITOR, ZN_W_NUMPARTS, ZN_W_POSTSCRIPT, ZN_W_RAISE, + ZN_W_REMOVE, ZN_W_ROTATE, ZN_W_SCALE, ZN_W_SELECT, ZN_W_SMOOTH, + ZN_W_TAPPLY, ZN_W_TDELETE, ZN_W_TRANSFORM, ZN_W_TRANSLATE, ZN_W_TRESET, + ZN_W_TRESTORE, ZN_W_TSAVE, ZN_W_TYPE, ZN_W_VERTICE_AT, }; static char *sel_cmd_strings[] = { "adjust", "clear", "from", "item", "to", NULL @@ -1799,7 +2442,8 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ } result = ZN_OK; - printf("executing command \"%s\", argc=%d\n", Tcl_GetString(args[1]), argc); + /*printf("executing command \"%s\", argc=%d\n", + Tcl_GetString(args[1]), argc);*/ switch((enum sub_cmds) cmd_index) { /* * add @@ -1834,8 +2478,10 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_AppendResult(interp, "unknown item type \"", str, "\"", NULL); goto error; } - num = ZnItemsWithTagOrId(wi, args[3], &group, NULL); - if (!num || (group->class != ZnGroup)) { + result = ZnItemWithTagOrId(wi, args[3], wi->top_group, True, + &group, &search_var); + if ((result == ZN_ERROR) || (group == ZN_NO_ITEM) || + (group->class != ZnGroup)) { Tcl_AppendResult(interp, ", group item expected, got \"", Tcl_GetString(args[3]), "\"", NULL); goto error; @@ -1851,6 +2497,8 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ if (ITEM.ConfigureItem(item, -1, argc, args, True) == ZN_ERROR) { goto error; } + wi->hot_item = item; + wi->hot_prev = item->previous; Tcl_SetObjResult(interp, NewLongObj(item->id)); } break; @@ -1863,7 +2511,7 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "addtag tag searchCommand ?arg arg ...?"); goto error; } - result = FindItems(wi, argc, args, args[2], 3); + result = FindItems(wi, argc, args, args[2], 3, &search_var); } break; /* @@ -1878,8 +2526,10 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "anchorxy tagOrId anchor"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, NULL); - if ((num == 0) || !item->class->has_anchors) { + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &item, &search_var); + if ((result == ZN_ERROR) || (item == ZN_NO_ITEM) || + !item->class->has_anchors) { Tcl_AppendResult(interp, "unknown item or doesn't support anchors \"", Tcl_GetString(args[2]), NULL); goto error; @@ -1914,7 +2564,6 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ { ZnBBox bbox; int i; - ZnBool found = False; if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "bbox tagOrId ?tagOrId ...?"); @@ -1926,13 +2575,15 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ ITEM_P.Update(wi); ResetBBox(&bbox); for (i = 0; i < argc; i++) { - num = ZnItemsWithTagOrId(wi, args[i], &item, &items); - found |= (num != 0); - for (j = 0; j < num; j++) { - AddBBoxToBBox(&bbox, &items[j]->item_bounding_box); + if (ZnTagSearchScan(wi, args[i], &search_var) == ZN_ERROR) { + goto error; + } + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + AddBBoxToBBox(&bbox, &item->item_bounding_box); } } - if (found && !IsEmptyBBox(&bbox)) { + if (!IsEmptyBBox(&bbox)) { lobjs[0] = NewDoubleObj(bbox.orig.x+wi->inset); lobjs[1] = NewDoubleObj(bbox.orig.y+wi->inset); lobjs[2] = NewDoubleObj(bbox.corner.x+wi->inset); @@ -1946,8 +2597,8 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ */ case ZN_W_BIND: { - ClientData elem = 0; - int part = ZN_NO_PART; + ClientData elem = 0; + int part = ZN_NO_PART; if ((argc < 3) || (argc > 5)) { Tcl_WrongNumArgs(interp, 1, args, "bind tagOrId ?sequence? ?command?"); @@ -1958,17 +2609,10 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ */ str = Tcl_GetString(args[2]); if (isdigit(str[0])) { - Tcl_HashEntry *entry; - int id; + int id; id = strtoul(str, &end, 0); - if (*end == ':') { - part = strtoul(end+1, &end2, 0); - if (*end2 != 0) { - goto bind_a_tag; - } - } - else if (*end != 0) { + if ((*end != 0) && (*end != ':')) { goto bind_a_tag; } entry = Tcl_FindHashEntry(wi->id_table, (char *) id); @@ -1981,7 +2625,17 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ goto error; } if (*end == ':') { - elem = EncodeItemPart((Item) elem, part); + if (item->class->Part) { + l = NewStringObj(end+1); + if (item->class->Part(item, &l, &part) == ZN_ERROR) { + goto error; + } + elem = EncodeItemPart(item, part); + } + else { + Tcl_AppendResult(interp, "item \"", str, "\" doesn't have parts", NULL); + goto error; + } } /*printf("adding element 0x%X to the binding table of item 0x%X\n", elem, item);*/ } @@ -2001,20 +2655,24 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ int append = 0; unsigned long mask; +#ifdef PTK + str = LangString(args[4]); +#else str = Tcl_GetString(args[4]); +#endif if (str[0] == 0) { result = Tk_DeleteBinding(interp, wi->binding_table, elem, Tcl_GetString(args[3])); goto done; - } - if (str[0] == '+') { - str++; - append = 1; - } + } #ifdef PTK mask = Tk_CreateBinding(interp, wi->binding_table, elem, Tcl_GetString(args[3]), args[4], append); #else + if (str[0] == '+') { + str++; + append = 1; + } mask = Tk_CreateBinding(interp, wi->binding_table, elem, Tcl_GetString(args[3]), str, append); #endif @@ -2076,20 +2734,22 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ */ case ZN_W_CHGGROUP: { - Item grp; - int adjust=0; + Item grp; + int adjust=0; ZnTransfo inv, t, t2, *this_one=NULL; if ((argc != 4) && (argc != 5)) { Tcl_WrongNumArgs(interp, 1, args, "chggroup tagOrIg group ?adjustTransform?"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, NULL); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &item, &search_var); + if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } - num = ZnItemsWithTagOrId(wi, args[3], &grp, NULL); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[3], wi->top_group, True, + &grp, &search_var); + if ((result == ZN_ERROR) || (grp == ZN_NO_ITEM)) { goto error; } if (argc == 5) { @@ -2128,20 +2788,20 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "clone tagOrId ?option value ...?"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, &items); - if (num == 0) { + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } argc -= 3; args += 3; l = Tcl_GetObjResult(interp); - for (i = 0; i < num; i++) { - item = ITEM.CloneItem(items[i]); - ITEM.InsertItem(item, items[i]->parent, ZN_NO_ITEM, True); - if (ITEM.ConfigureItem(item, -1, argc, args, False) == ZN_ERROR) { + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + item2 = ITEM.CloneItem(item); + ITEM.InsertItem(item2, item->parent, ZN_NO_ITEM, True); + if (ITEM.ConfigureItem(item2, -1, argc, args, False) == ZN_ERROR) { goto error; } - Tcl_ListObjAppendElement(interp, l, NewLongObj(item->id)); + Tcl_ListObjAppendElement(interp, l, NewLongObj(item2->id)); } } break; @@ -2195,7 +2855,7 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ "coords tagOrId ?add/remove? ?contour? ?index? ?coordList?"); goto error; } - if (Coords(wi, argc, args) == ZN_ERROR) { + if (Coords(wi, argc, args, &search_var) == ZN_ERROR) { goto error; } } @@ -2209,7 +2869,11 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "currentpart"); goto error; } - Tcl_SetObjResult(interp, Tcl_NewIntObj(wi->current_part)); + if (wi->current_item->class->Part != NULL) { + l = NULL; + wi->current_item->class->Part(wi->current_item, &l, &wi->current_part); + Tcl_SetObjResult(interp, l); + } } break; /* @@ -2221,20 +2885,23 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "cursor tagOrId index"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, &items); - for (i = 0; i < num; i++) { - if ((items[i]->class->Cursor == NULL) || - (items[i]->class->Index == NULL)) { + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { + goto error; + } + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + if ((item->class->Cursor == NULL) || + (item->class->Index == NULL)) { continue; } - result = (*items[i]->class->Index)(items[i], args[3], &index); + result = (*item->class->Index)(item, args[3], &index); if (result != ZN_OK) { goto error; } - (*items[i]->class->Cursor)(items[i], index); - if ((items[i] == wi->text_info.focus_item) && wi->text_info.cursor_on) { - ITEM.Invalidate(items[i], ZN_COORDS_FLAG); + (*item->class->Cursor)(item, index); + if ((item == wi->text_info.focus_item) && wi->text_info.cursor_on) { + ITEM.Invalidate(item, ZN_COORDS_FLAG); } } } @@ -2250,18 +2917,21 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "dchars tagOrId first ?last?"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, &items); - for (i = 0; i < num; i++) { - if ((items[i]->class->Index == NULL) || - (items[i]->class->DeleteChars == NULL)) { + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { + goto error; + } + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + if ((item->class->Index == NULL) || + (item->class->DeleteChars == NULL)) { continue; } - result = (*items[i]->class->Index)(items[i], args[3], &first); + result = (*item->class->Index)(item, args[3], &first); if (result != ZN_OK) { goto error; } if (argc == 5) { - result = (*items[i]->class->Index)(items[i], args[4], &last); + result = (*item->class->Index)(item, args[4], &last); if (result != ZN_OK) { goto error; } @@ -2269,7 +2939,7 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ else { last = first; } - (*items[i]->class->DeleteChars)(items[i], first, last); + (*item->class->DeleteChars)(item, first, last); } } break; @@ -2279,7 +2949,6 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ case ZN_W_DTAG: { Tk_Uid tag; - Tcl_HashEntry *entry; if ((argc != 3) && (argc != 4)) { Tcl_WrongNumArgs(interp, 1, args, "dtag tagOrId ?tagToDelete?"); @@ -2291,18 +2960,13 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ else { tag = Tk_GetUid(Tcl_GetString(args[2])); } - entry = Tcl_FindHashEntry(wi->tag_table, tag); - item_list = (ZnList) Tcl_GetHashValue(entry); - items = (Item *) ZnListArray(item_list); - for (i = ZnListSize(item_list)-1; i >= 0; i++) { - ITEM.RemoveTag(items[i], (char *) tag); + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { + goto error; + } + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + ITEM.RemoveTag(item, (char *) tag); } - /* - * The RemoveTag method *must* remove the tag table - * entry when it gets empty. Otherwise a tag probe - * we bring back an empty item list and this would - * not be reported as an error by ZnItemsWithTagOrId. - */ } break; /* @@ -2314,7 +2978,7 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "find searchCommand ?arg arg ...?"); goto error; } - result = FindItems(wi, argc, args, NULL, 2); + result = FindItems(wi, argc, args, NULL, 2, &search_var); } break; /* @@ -2372,17 +3036,16 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ wi->text_info.focus_item = ZN_NO_ITEM; goto done; } - num = ZnItemsWithTagOrId(wi, args[2], &item, &items); - item = ZN_NO_ITEM; - for (i = 0; i < num; i++) { - if (items[i]->class->Cursor != NULL) { + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { + goto error; + } + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + if (item->class->Cursor != NULL) { break; } } - if (num == 0) { - goto done; - } - wi->text_info.focus_item = items[i]; + wi->text_info.focus_item = item; if (wi->text_info.got_focus) { ITEM.Invalidate(wi->text_info.focus_item, ZN_COORDS_FLAG); } @@ -2399,8 +3062,9 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "gettags tagOrId"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, NULL); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &item, &search_var); + if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } if (!item->tags || !ZnListSize(item->tags)) { @@ -2425,8 +3089,9 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "group tagOrId"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, NULL); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &item, &search_var); + if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } if (item->parent != ZN_NO_ITEM) { @@ -2449,8 +3114,9 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "hasanchors tagOrId"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, NULL); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &item, &search_var); + if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } Tcl_SetObjResult(interp, NewBooleanObj(item->class->has_anchors?1:0)); @@ -2465,30 +3131,15 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "hasfields tagOrId"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, NULL); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &item, &search_var); + if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } Tcl_SetObjResult(interp, NewBooleanObj(item->class->has_fields?1:0)); } break; /* - * hasparts - */ - case ZN_W_HASPARTS: - { - if (argc != 3) { - Tcl_WrongNumArgs(interp, 1, args, "hasparts tagOrId"); - goto error; - } - num = ZnItemsWithTagOrId(wi, args[2], &item, NULL); - if (num == 0) { - goto error; - } - Tcl_SetObjResult(interp, NewBooleanObj(item->class->has_parts?1:0)); - } - break; - /* * hastag */ case ZN_W_HASTAG: @@ -2500,8 +3151,9 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "hastag tagOrId tag"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, NULL); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &item, &search_var); + if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } if (!item->tags || !ZnListSize(item->tags)) { @@ -2530,10 +3182,13 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "index tagOrId string"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, &items); - for (i = 0; i < num; i++) { - if (items[i]->class->Index != NULL) { - result = (*items[i]->class->Index)(items[i], args[3], &index); + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { + goto error; + } + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + if (item->class->Index != NULL) { + result = (*item->class->Index)(item, args[3], &index); if (result != ZN_OK) { goto error; } @@ -2555,17 +3210,20 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "insert tagOrId before string"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, &items); - for (i = 0; i < num; i++) { - if ((items[i]->class->Index == NULL) || - (items[i]->class->InsertChars == NULL)) { + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { + goto error; + } + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + if ((item->class->Index == NULL) || + (item->class->InsertChars == NULL)) { continue; } - result = (*items[i]->class->Index)(items[i], args[3], &index); + result = (*item->class->Index)(item, args[3], &index); if (result != ZN_OK) { goto error; } - (*items[i]->class->InsertChars)(items[i], index, Tcl_GetString(args[4])); + (*item->class->InsertChars)(item, index, Tcl_GetString(args[4])); } } break; @@ -2581,8 +3239,9 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "itemcget tagOrId ?field? option"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, NULL); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &item, &search_var); + if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } if (argc == 5) { @@ -2613,8 +3272,7 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ "itemconfigure tagOrId ?field? option value ?option value? ..."); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, &items); - if (num == 0) { + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } if ((argc > 3) && (Tcl_GetString(args[3])[0] != '-')) { @@ -2629,21 +3287,20 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ } argc -= 3; args += 3; - if (argc < 2) { - if (ITEM.AttributesInfo(item, field, argc, args) == ZN_ERROR) { + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + if (argc < 2) { + result = ITEM.AttributesInfo(item, field, argc, args); + } + else { + result = ITEM.ConfigureItem(item, field, argc, args, False); + } + if (result == ZN_ERROR) { goto error; } - goto done; - } - for (i = 0; i < num; i++) { - result = ITEM.ConfigureItem(items[i], field, argc, args, False); - } - /* - * Don't report errors when configuring many items/fields. All - * options are not supported by all item types. - */ - if ((num == 1) && (result == ZN_ERROR)) { - goto error; + if (argc < 2) { + goto done; + } } } break; @@ -2653,16 +3310,17 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ case ZN_W_LOWER: { Item mark = ZN_NO_ITEM; - TagSearch search; if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "lower tagOrId ?belowThis?"); goto error; } if (argc == 4) { - for (item = ZnSearchWithTagOrId(wi, args[3], wi->top_group, &search); - item != ZN_NO_ITEM; - item = ZnNextWithTagOrId(&search)) { + if (ZnTagSearchScan(wi, args[3], &search_var) == ZN_ERROR) { + goto error; + } + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { mark = item; } if (mark == ZN_NO_ITEM) { @@ -2671,16 +3329,17 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ goto error; } } - item = ZnSearchWithTagOrId(wi, args[2], wi->top_group, &search); - if (item == ZN_NO_ITEM) { - Tcl_AppendResult(interp, "unknown tag or item \"", - Tcl_GetString(args[2]), "\"", NULL); + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } + item = ZnTagSearchFirst(search_var, wi->top_group, True); + if (item == ZN_NO_ITEM) { + goto done; + } if (mark == ZN_NO_ITEM) { mark = ((GroupItem) item->parent)->tail; } - for (; item != ZN_NO_ITEM; item = ZnNextWithTagOrId(&search)) { + for (; item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if (item != mark) { ITEM.UpdateItemPriority(item, mark, False); mark = item; @@ -2718,6 +3377,23 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ } break; /* + * numparts + */ + case ZN_W_NUMPARTS: + { + if (argc != 3) { + Tcl_WrongNumArgs(interp, 1, args, "numparts tagOrId"); + goto error; + } + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &item, &search_var); + if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { + goto error; + } + Tcl_SetObjResult(interp, NewBooleanObj(item->class->num_parts!=0)); + } + break; + /* * postscript */ case ZN_W_POSTSCRIPT: @@ -2732,7 +3408,6 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ case ZN_W_RAISE: { Item mark = ZN_NO_ITEM; - TagSearch search; if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "raise tagOrId ?aboveThis?"); @@ -2742,24 +3417,27 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ /* * Find the topmost item with the tag. */ - mark = ZnSearchWithTagOrId(wi, args[3], wi->top_group, &search); + if (ZnTagSearchScan(wi, args[3], &search_var) == ZN_ERROR) { + goto error; + } + mark = ZnTagSearchFirst(search_var, wi->top_group, True); if (mark == ZN_NO_ITEM) { Tcl_AppendResult(interp, "unknown tag or item \"", Tcl_GetString(args[3]), "\"", NULL); goto error; } - ZnDoneWithSearch(&search); } - item = ZnSearchWithTagOrId(wi, args[2], wi->top_group, &search); - if (item == ZN_NO_ITEM) { - Tcl_AppendResult(interp, "unknown tag or item \"", - Tcl_GetString(args[2]), "\"", NULL); + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } + item = ZnTagSearchFirst(search_var, wi->top_group, True); + if (item == ZN_NO_ITEM) { + goto done; + } if (mark == ZN_NO_ITEM) { mark = ((GroupItem) item->parent)->head; } - for (; item != ZN_NO_ITEM; item = ZnNextWithTagOrId(&search)) { + for (; item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if (item != mark) { ITEM.UpdateItemPriority(item, mark, True); } @@ -2771,29 +3449,38 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ */ case ZN_W_REMOVE: { + FieldSet fs; + if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "remove tagOrId ?tagOrId ...?"); goto error; } argc -= 2; args += 2; - for (j = 0; j < argc; j++) { - num = ZnItemsWithTagOrId(wi, args[j], &item, &items); - if (num == 0) { + for (i = 0; i < argc; i++) { + if (ZnTagSearchScan(wi, args[i], &search_var) == ZN_ERROR) { goto error; } - for (i = num-1; i >= 0; i--) { - if (items[i] == wi->top_group) { + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + if (item == wi->top_group) { continue; } if (wi->binding_table != NULL) { - /* - * BUG: we can't actually destroy all the item's bindings - * if there are field bindings registered. - */ - Tk_DeleteAllBindings(wi->binding_table, (ClientData) items[i]); + Tk_DeleteAllBindings(wi->binding_table, (ClientData) item); + if (item->class->has_fields) { + fs = item->class->GetFieldSet(item); + for (j = 0; j < fs->num_fields; j++) { + Tk_DeleteAllBindings(wi->binding_table, + (ClientData) EncodeItemPart(item, j)); + } + } + for (j = 0; j < item->class->num_parts; j++) { + Tk_DeleteAllBindings(wi->binding_table, + (ClientData) EncodeItemPart(item, -(j+2))); + } } - ITEM.DestroyItem(items[i]); + ITEM.DestroyItem(item); } } } @@ -2803,8 +3490,8 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ */ case ZN_W_ROTATE: { - ZnReal angle; - ZnPoint p; + ZnReal angle; + ZnPoint p; if ((argc != 4) && (argc != 6)) { Tcl_WrongNumArgs(interp, 1, args, "rotate tagOrId angle ?centerX centerY?"); @@ -2819,23 +3506,19 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ goto error; } } - num = ZnItemsWithTagOrId(wi, args[2], &item, &items); - if (num == 0) { - Tcl_HashEntry *e; - e = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[2])); - if (e == NULL) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, - "\"", Tcl_GetString(args[2]), "\" must be either a tag or ", - "an id or a transform name", (char *) NULL); + entry = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[2])); + if (entry != NULL) { + t = (ZnTransfo *) Tcl_GetHashValue(entry); + } + else { + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } - t = (ZnTransfo *) Tcl_GetHashValue(e); } - if (Tcl_GetDoubleFromObj(interp, args[3], &angle) == ZN_ERROR) { goto error; } + if (t) { if (argc == 6) { ZnTranslate(t, -p.x, -p.y); @@ -2845,9 +3528,10 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ ZnTranslate(t, p.x, p.y); } } - if (num != 0) { - for (i = 0; i < num; i++) { - ITEM.RotateItem(items[i], angle, (argc == 6) ? &p : NULL); + else { + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + ITEM.RotateItem(item, angle, (argc == 6) ? &p : NULL); } } } @@ -2863,33 +3547,29 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "scale tagOrId xFactor yFactor"); goto error; } - if (argc == 5) { - num = ZnItemsWithTagOrId(wi, args[2], &item, &items); - if (num == 0) { - Tcl_HashEntry *e; - e = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[2])); - if (e == NULL) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "\"", Tcl_GetString(args[2]), - "\" must be either a tag or ", - "an id or a transform name", (char *) NULL); - goto error; - } - t = (ZnTransfo *) Tcl_GetHashValue(e); + entry = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[2])); + if (entry != NULL) { + t = (ZnTransfo *) Tcl_GetHashValue(entry); + } + else { + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { + goto error; } } - if (Tcl_GetDoubleFromObj(interp, args[argc-2], &scale.x) == ZN_ERROR) { + if (Tcl_GetDoubleFromObj(interp, args[3], &scale.x) == ZN_ERROR) { goto error; } - if (Tcl_GetDoubleFromObj(interp, args[argc-1], &scale.y) == ZN_ERROR) { + if (Tcl_GetDoubleFromObj(interp, args[4], &scale.y) == ZN_ERROR) { goto error; } + if (t) { ZnScale(t, scale.x, scale.y); } - if (num != 0) { - for (i = 0; i < num; i++) { - ITEM.ScaleItem(items[i], scale.x, scale.y); + else { + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + ITEM.ScaleItem(item, scale.x, scale.y); } } } @@ -2904,15 +3584,13 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ goto error; } if (argc >= 4) { - num = ZnItemsWithTagOrId(wi, args[3], &item, &items); - if (num == 0) { - goto done; + if (ZnTagSearchScan(wi, args[3], &search_var) == ZN_ERROR) { + goto error; } - item = ZN_NO_ITEM; - for (i = 0; i < num; i++) { - if ((items[i]->class->Index != NULL) && - (items[i]->class->Selection != NULL)) { - item = items[i]; + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + if ((item->class->Index != NULL) && + (item->class->Selection != NULL)) { break; } } @@ -3048,7 +3726,7 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ */ case ZN_W_TRANSFORM: { - int num_points; + int num_points; ZnPoint *p, xp; ZnTransfo t, t2, inv, *this_one; Item from, to; @@ -3059,13 +3737,15 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ } if (argc == 5) { - num = ZnItemsWithTagOrId(wi, args[2], &from, &items); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &from, &search_var); + if ((result == ZN_ERROR) || (from == ZN_NO_ITEM)) { goto error; } } - num = ZnItemsWithTagOrId(wi, args[argc-2], &to, &items); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[argc-2], wi->top_group, True, + &to, &search_var); + if ((result == ZN_ERROR) || (to == ZN_NO_ITEM)) { Tcl_HashEntry *e; /* * Try to find a named transform. @@ -3125,34 +3805,29 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "translate tagOrId xAmount yAmount"); goto error; } - num = 0; - if (argc == 5) { - num = ZnItemsWithTagOrId(wi, args[2], &item, &items); - if (num == 0) { - Tcl_HashEntry *e; - e = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[2])); - if (e == NULL) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "\"", Tcl_GetString(args[2]), - "\" must be either a tag or ", - "an id or a transform name", (char *) NULL); - goto error; - } - t = (ZnTransfo *) Tcl_GetHashValue(e); + entry = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[2])); + if (entry != NULL) { + t = (ZnTransfo *) Tcl_GetHashValue(entry); + } + else { + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { + goto error; } } - if (Tcl_GetDoubleFromObj(interp, args[argc-2], &trans.x) == ZN_ERROR) { + if (Tcl_GetDoubleFromObj(interp, args[3], &trans.x) == ZN_ERROR) { goto error; } - if (Tcl_GetDoubleFromObj(interp, args[argc-1], &trans.y) == ZN_ERROR) { + if (Tcl_GetDoubleFromObj(interp, args[4], &trans.y) == ZN_ERROR) { goto error; } + if (t) { ZnTranslate(t, trans.x, trans.y); } - if (num != 0) { - for (i = 0; i < num; i++) { - ITEM.TranslateItem(items[i], trans.x, trans.y); + else { + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item =ZnTagSearchNext(search_var)) { + ITEM.TranslateItem(item, trans.x, trans.y); } } } @@ -3166,27 +3841,23 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "treset tagOrId"); goto error; } - if (argc == 3) { - num = ZnItemsWithTagOrId(wi, args[2], &item, &items); - if (num == 0) { - Tcl_HashEntry *e; - e = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[2])); - if (e == NULL) { - Tcl_ResetResult(interp); - Tcl_AppendResult(interp, "\"", Tcl_GetString(args[2]), - "\" must be either a tag or ", - "an id or a transform name", (char *) NULL); - goto error; - } - t = (ZnTransfo *) Tcl_GetHashValue(e); + entry = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[2])); + if (entry != NULL) { + t = (ZnTransfo *) Tcl_GetHashValue(entry); + } + else { + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { + goto error; } } + if (t) { ZnTransfoSetIdentity(t); } - if (num != 0) { - for (i = 0; i < num; i++) { - ITEM.ResetTransfo(items[i]); + else { + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + ITEM.ResetTransfo(item); } } } @@ -3196,25 +3867,23 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ */ case ZN_W_TRESTORE: { - Tcl_HashEntry *e; - if (argc != 4) { Tcl_WrongNumArgs(interp, 1, args, "trestore tagOrId tName"); goto error; } - e = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[argc-1])); - if (e == NULL) { + entry = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[argc-1])); + if (entry == NULL) { Tcl_AppendResult(interp, "\"", Tcl_GetString(args[argc-1]), "\" must be a transform name", (char *) NULL); goto error; } - t = (ZnTransfo *) Tcl_GetHashValue(e); - num = ZnItemsWithTagOrId(wi, args[2], &item, &items); - if (num == 0) { + t = (ZnTransfo *) Tcl_GetHashValue(entry); + if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } - for (i = 0; i < num; i++) { - ITEM.SetTransfo(items[i], t); + for (item = ZnTagSearchFirst(search_var, wi->top_group, True); + item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { + ITEM.SetTransfo(item, t); } } break; @@ -3223,16 +3892,16 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ */ case ZN_W_TSAVE: { - Tcl_HashEntry *e; int new, invert=0; - ZnTransfo *inv; + ZnTransfo *inv; if ((argc != 4) && (argc != 5)) { Tcl_WrongNumArgs(interp, 1, args, "tsave tagOrId tName ?invert?"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, &items); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &item, &search_var); + if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } if (argc == 5) { @@ -3241,9 +3910,9 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ } } t = item->transfo; - e = Tcl_CreateHashEntry(wi->t_table, Tcl_GetString(args[argc-1]), &new); + entry = Tcl_CreateHashEntry(wi->t_table, Tcl_GetString(args[argc-1]), &new); if (!new) { - ZnTransfoFree((ZnTransfo *) Tcl_GetHashValue(e)); + ZnTransfoFree((ZnTransfo *) Tcl_GetHashValue(entry)); } if (invert) { inv = ZnTransfoNew(); @@ -3252,7 +3921,7 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ else { inv = ZnTransfoDuplicate(t); } - Tcl_SetHashValue(e, inv); + Tcl_SetHashValue(entry, inv); } break; /* @@ -3264,16 +3933,51 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_WrongNumArgs(interp, 1, args, "type tagOrId"); goto error; } - num = ZnItemsWithTagOrId(wi, args[2], &item, NULL); - if (num == 0) { + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &item, &search_var); + if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } Tcl_SetObjResult(interp, NewStringObj(item->class->name)); } break; + /* + * verticeat + */ + case ZN_W_VERTICE_AT: + { + ZnPoint p; + int contour, vertex, o_vertex; + + if (argc != 5) { + Tcl_WrongNumArgs(interp, 1, args, " verticeat tagOrId x y"); + goto error; + } + result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, + &item, &search_var); + if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { + goto error; + } + if (Tcl_GetDoubleFromObj(interp, args[3], &p.x) == ZN_ERROR) { + goto error; + } + if (Tcl_GetDoubleFromObj(interp, args[4], &p.y) == ZN_ERROR) { + goto error; + } + p.x -= wi->inset; + p.y -= wi->inset; + if (item->class->PickVertex) { + item->class->PickVertex(item, &p, &contour, &vertex, &o_vertex); + l = Tcl_GetObjResult(interp); + Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(contour)); + Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(vertex)); + Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(o_vertex)); + } + } } done: + ZnTagSearchDestroy(search_var); if (wi->work_item_list) { ZnListFree(wi->work_item_list); wi->work_item_list = NULL; @@ -3282,6 +3986,7 @@ WidgetObjCmd(ClientData client_data, /* Information about the widget. */ return result; error: + ZnTagSearchDestroy(search_var); if (wi->work_item_list) { ZnListFree(wi->work_item_list); wi->work_item_list = NULL; @@ -3774,7 +4479,7 @@ DoEvent(WidgetInfo *wi, num_tags = 0; its = items; bind_part = ((wi->current_part != ZN_NO_PART) && - (wi->current_item->class->has_parts || + (wi->current_item->class->num_parts || wi->current_item->class->has_fields)); /* * This is needed to avoid generating enter and leave @@ -3821,7 +4526,7 @@ DoEvent(WidgetInfo *wi, if (bind_part) { /* - * Add here a binding for each tag suffixed by :all + * Add here a binding for each tag suffixed by :part */ for (i = 0; i < num_tags; i++) { len = strlen(tag_list[i])+ INTEGER_SPACE; @@ -3834,7 +4539,7 @@ DoEvent(WidgetInfo *wi, ptr++; } /* - * Add here a binding for id:all + * Add here a binding for id:part */ its[ptr] = EncodeItemPart(item, wi->current_part); ptr++; @@ -3952,12 +4657,17 @@ PickCurrentItem(WidgetInfo *wi, * object, so the check for closest item can be skipped. */ if (wi->pick_event.type != LeaveNotify) { - ZnPoint p; + ZnPoint p; + ZnReal dist; p.x = wi->pick_event.xcrossing.x-wi->inset; p.y = wi->pick_event.xcrossing.y-wi->inset; - wi->top_group->class->Pick(wi->top_group, &p, NULL, wi->pick_aperture, - &wi->new_item, &wi->new_part); + dist = wi->top_group->class->Pick(wi->top_group, &p, NULL, wi->pick_aperture, + &wi->new_item, &wi->new_part); + if (dist != 0.0) { + wi->new_item = ZN_NO_ITEM; + wi->new_part = ZN_NO_PART; + } } else { wi->new_item = ZN_NO_ITEM; @@ -4291,7 +5001,8 @@ static int FetchSelection( ClientData client_data, int offset, /* Offset within selection of first * character to be returned. */ - char *buffer, /* Location in which to place selection. */ + char *buffer, /* Location in which to place + * selection. */ int max_bytes) /* Maximum number of bytes to place * at buffer, not including terminating * NULL character. */ @@ -4382,10 +5093,11 @@ Destroy(char *mem_ptr) /* Info about the widget. */ /* * Print remaining items. */ - /*printf("Remaining item count: %d\n", wi->num_items);*/ /* Free all items. */ + printf("Remaining item count: %d\n", wi->num_items); ITEM.DestroyItem(wi->top_group); + printf("Remaining item count: %d\n", wi->num_items); for (num = 0; num < NUM_ALPHA_STEPS; num++) { if (wi->alpha_stipples[num] != None) { @@ -4397,17 +5109,7 @@ Destroy(char *mem_ptr) /* Info about the widget. */ ZnFree(wi->id_table); /* - * Free the table contents before the table. - */ - entry = Tcl_FirstHashEntry(wi->tag_table, &search); - while (entry != NULL) { - ZnListFree((ZnList) Tcl_GetHashValue(entry)); - entry = Tcl_NextHashEntry(&search); - } - Tcl_DeleteHashTable(wi->tag_table); - ZnFree(wi->tag_table); - /* - * Free the table contents before the table. + * Free the transform table contents before the table. */ entry = Tcl_FirstHashEntry(wi->t_table, &search); while (entry != NULL) { @@ -4432,6 +5134,8 @@ Destroy(char *mem_ptr) /* Info about the widget. */ XFreePixmap(wi->dpy, wi->draw_buffer); } + Tcl_DeleteTimerHandler(wi->blink_handler); + Tk_FreeOptions(config_specs, (char *) wi, wi->dpy, 0); /* @@ -4538,6 +5242,23 @@ Redisplay(ClientData client_data) /* Information about the widget. */ */ ITEM_P.Repair(wi); + ResetBBox(&merge); + CopyBBox(&wi->damaged_area, &merge); + AddBBoxToBBox(&merge, &wi->exposed_area); + if (!IsEmptyBBox(&merge)) { + BBox2XRect(&merge, &r); + /*printf("redisplay %d %d %d %d\n", r.x, r.y, r.width, r.height);*/ + XCopyArea(wi->dpy, + wi->draw_buffer, ZnWindowId(wi->win), wi->gc, + r.x, r.y, r.width, r.height, r.x+wi->inset, r.y+wi->inset); + } + + /* + * Reset the exposed & damaged areas. + */ + ResetBBox(&wi->exposed_area); + ResetBBox(&wi->damaged_area); + /* * Redraw the borders. */ @@ -4561,23 +5282,6 @@ Redisplay(ClientData client_data) /* Information about the widget. */ Tk_DrawFocusHighlight(wi->win, gc, wi->highlight_width, Tk_WindowId(wi->win)); } - ResetBBox(&merge); - CopyBBox(&wi->damaged_area, &merge); - AddBBoxToBBox(&merge, &wi->exposed_area); - if (!IsEmptyBBox(&merge)) { - BBox2XRect(&merge, &r); - /*printf("redisplay %d %d %d %d\n", r.x, r.y, r.width, r.height);*/ - XCopyArea(wi->dpy, - wi->draw_buffer, ZnWindowId(wi->win), wi->gc, - r.x, r.y, r.width, r.height, r.x+wi->inset, r.y+wi->inset); - } - - /* - * Reset the exposed & damaged areas. - */ - ResetBBox(&wi->exposed_area); - ResetBBox(&wi->damaged_area); - if (wi->monitoring) { XSync(wi->dpy, 0); gettimeofday(&end, &tz); @@ -4932,6 +5636,8 @@ MapInfoObjCmd(ClientData client_data, return ZN_ERROR; } result = TCL_OK; + /*printf("mapinfo command \"%s\", argc=%d\n", + Tcl_GetString(args[2]), argc);*/ switch((enum sub_cmds) index) { /* @@ -4989,7 +5695,7 @@ MapInfoObjCmd(ClientData client_data, { MapInfoLineStyle line_style; MapInfoTextStyle text_style; - int i, index; + int i, insert; int coords[6]; ZnBool add_cmd = (enum sub_cmds) index == ZN_MI_ADD; int num_param = add_cmd ? 4 : 5; @@ -5004,14 +5710,14 @@ MapInfoObjCmd(ClientData client_data, return ZN_ERROR; } if (!add_cmd) { - if (Tcl_GetIntFromObj(interp, args[4], &index) == ZN_ERROR) { + if (Tcl_GetIntFromObj(interp, args[4], &insert) == ZN_ERROR) { return ZN_ERROR; } - if (index < 0) { - index = 0; + if (insert < 0) { + insert = 0; } } - if (Tcl_GetIndexFromObj(interp, args[0], e_type_strings, + if (Tcl_GetIndexFromObj(interp, args[3], e_type_strings, "elementType", 0, &index2) != ZN_OK) { return ZN_ERROR; } @@ -5040,7 +5746,7 @@ MapInfoObjCmd(ClientData client_data, coords[0], coords[1], coords[2], coords[3], coords[4]); } else { - MapInfoReplaceLine(master->map_info, index, NULL, line_style, + MapInfoReplaceLine(master->map_info, insert, NULL, line_style, coords[0], coords[1], coords[2], coords[3], coords[4]); } } @@ -5065,7 +5771,7 @@ MapInfoObjCmd(ClientData client_data, coords[1], coords[2]); } else { - MapInfoReplaceSymbol(master->map_info, index, NULL, coords[0], + MapInfoReplaceSymbol(master->map_info, insert, NULL, coords[0], coords[1], coords[2]); } } @@ -5097,7 +5803,7 @@ MapInfoObjCmd(ClientData client_data, else { /*printf("replace text ts %d ls %d %d %d %s\n", text_style, line_style, coords[0], coords[1], Tcl_GetString(args[num_param+4]));*/ - MapInfoReplaceText(master->map_info, index, NULL, text_style, + MapInfoReplaceText(master->map_info, insert, NULL, text_style, line_style, coords[0], coords[1], Tcl_GetString(args[num_param+4])); } @@ -5128,7 +5834,7 @@ MapInfoObjCmd(ClientData client_data, coords[5]); } else { - MapInfoReplaceArc(master->map_info, index, NULL, line_style, coords[0], + MapInfoReplaceArc(master->map_info, insert, NULL, line_style, coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); } } @@ -5151,9 +5857,7 @@ MapInfoObjCmd(ClientData client_data, if (master == NULL) { return ZN_ERROR; } - args += 3; - argc -= 3; - if (Tcl_GetIndexFromObj(interp, args[0], e_type_strings, + if (Tcl_GetIndexFromObj(interp, args[3], e_type_strings, "elementType", 0, &index2) != ZN_OK) { return ZN_ERROR; } @@ -5179,7 +5883,7 @@ MapInfoObjCmd(ClientData client_data, */ case ZN_MI_GET: { - int index; + int insert; if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "mapInfo get type index"); return ZN_ERROR; @@ -5188,15 +5892,13 @@ MapInfoObjCmd(ClientData client_data, if (master == NULL) { return ZN_ERROR; } - if (Tcl_GetIntFromObj(interp, args[4], &index) == ZN_ERROR) { + if (Tcl_GetIntFromObj(interp, args[4], &insert) == ZN_ERROR) { return ZN_ERROR; } - if (index < 0) { - index = 0; + if (insert < 0) { + insert = 0; } - args += 3; - argc -= 3; - if (Tcl_GetIndexFromObj(interp, args[0], e_type_strings, + if (Tcl_GetIndexFromObj(interp, args[3], e_type_strings, "elementType", 0, &index2) != ZN_OK) { return ZN_ERROR; } @@ -5206,7 +5908,7 @@ MapInfoObjCmd(ClientData client_data, MapInfoLineStyle line_style; int line_width; int x_from, y_from, x_to, y_to; - MapInfoGetLine(master->map_info, index, NULL, &line_style, + MapInfoGetLine(master->map_info, insert, NULL, &line_style, &line_width, &x_from, &y_from, &x_to, &y_to); lobjs[0] = NewStringObj(MapInfoLineStyleToString(line_style)); lobjs[1] = Tcl_NewIntObj(line_width); @@ -5221,7 +5923,7 @@ MapInfoObjCmd(ClientData client_data, { int x, y; char symbol; - MapInfoGetSymbol(master->map_info, index, NULL, &x, &y, &symbol); + MapInfoGetSymbol(master->map_info, insert, NULL, &x, &y, &symbol); lobjs[0] = Tcl_NewIntObj(x); lobjs[1] = Tcl_NewIntObj(y); lobjs[2] = Tcl_NewIntObj(symbol); @@ -5234,7 +5936,7 @@ MapInfoObjCmd(ClientData client_data, char *text; MapInfoTextStyle text_style; MapInfoLineStyle line_style; - MapInfoGetText(master->map_info, index, NULL, &text_style, &line_style, + MapInfoGetText(master->map_info, insert, NULL, &text_style, &line_style, &x, &y, &text); lobjs[0] = Tcl_NewIntObj(x); lobjs[1] = Tcl_NewIntObj(y); @@ -5250,7 +5952,7 @@ MapInfoObjCmd(ClientData client_data, int line_width; int center_x, center_y, start, extent; unsigned int radius; - MapInfoGetArc(master->map_info, index, NULL, &line_style, &line_width, + MapInfoGetArc(master->map_info, insert, NULL, &line_style, &line_width, ¢er_x, ¢er_y, &radius, &start, &extent); lobjs[0] = NewStringObj(MapInfoLineStyleToString(line_style)); lobjs[1] = Tcl_NewIntObj(line_width); @@ -5270,7 +5972,7 @@ MapInfoObjCmd(ClientData client_data, */ case ZN_MI_REMOVE: { - int index; + int insert; if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "mapInfo remove type index"); return ZN_ERROR; @@ -5279,30 +5981,28 @@ MapInfoObjCmd(ClientData client_data, if (master == NULL) { return ZN_ERROR; } - if (Tcl_GetIntFromObj(interp, args[4], &index) == ZN_ERROR) { + if (Tcl_GetIntFromObj(interp, args[4], &insert) == ZN_ERROR) { return ZN_ERROR; } - if (index < 0) { - index = 0; + if (insert < 0) { + insert = 0; } - args += 3; - argc -= 3; - if (Tcl_GetIndexFromObj(interp, args[0], e_type_strings, + if (Tcl_GetIndexFromObj(interp, args[3], e_type_strings, "elementType", 0, &index2) != ZN_OK) { return ZN_ERROR; } switch ((enum e_types) index2) { case ZN_E_LINE: - MapInfoRemoveLine(master->map_info, index); + MapInfoRemoveLine(master->map_info, insert); break; case ZN_E_SYMBOL: - MapInfoRemoveSymbol(master->map_info, index); + MapInfoRemoveSymbol(master->map_info, insert); break; case ZN_E_TEXT: - MapInfoRemoveText(master->map_info, index); + MapInfoRemoveText(master->map_info, insert); break; case ZN_E_ARC: - MapInfoRemoveArc(master->map_info, index); + MapInfoRemoveArc(master->map_info, insert); break; } UpdateMapInfoClients(master); @@ -5431,22 +6131,22 @@ VideomapObjCmd(ClientData client_data, case ZN_V_LOAD: { MapInfoId map_info; - int index; + int insert; if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "load filename index mapInfo"); return ZN_ERROR; } - if (Tcl_GetIntFromObj(interp, args[3], &index) == ZN_ERROR) { + if (Tcl_GetIntFromObj(interp, args[3], &insert) == ZN_ERROR) { return ZN_ERROR; } - if (index < 0) { - index = 0; + if (insert < 0) { + insert = 0; } if (ZnCreateMapInfo(interp, Tcl_GetString(args[4]), &map_info) == ZN_ERROR) { return ZN_ERROR; } - if (MapInfoGetVideomap(map_info, Tcl_GetString(args[2]), index) == ZN_ERROR) { + if (MapInfoGetVideomap(map_info, Tcl_GetString(args[2]), insert) == ZN_ERROR) { Tcl_AppendResult(interp, "unable to load videomap file \"", Tcl_GetString(args[2]), ":", Tcl_GetString(args[3]), "\"", NULL); @@ -5507,6 +6207,14 @@ InitZinc(Tcl_Interp *interp) { all_uid = Tk_GetUid("all"); current_uid = Tk_GetUid("current"); + and_uid = Tk_GetUid("&&"); + or_uid = Tk_GetUid("||"); + xor_uid = Tk_GetUid("^"); + paren_uid = Tk_GetUid("("); + end_paren_uid = Tk_GetUid(")"); + neg_paren_uid = Tk_GetUid("!("); + tag_val_uid = Tk_GetUid("!!"); + neg_tag_val_uid = Tk_GetUid("!"); inited = True; } @@ -5538,7 +6246,8 @@ Tkzinc_Init(Tcl_Interp *interp) /* Used for error reporting. */ } Tcl_CreateObjCommand(interp, "zinc", ZincObjCmd, - (ClientData) Tk_MainWindow(interp), (Tcl_CmdDeleteProc *) NULL); + (ClientData) Tk_MainWindow(interp), + (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "mapinfo", MapInfoObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "videomap", VideomapObjCmd, -- cgit v1.1