diff options
-rw-r--r-- | generic/tkZinc.c | 4632 |
1 files changed, 4632 insertions, 0 deletions
diff --git a/generic/tkZinc.c b/generic/tkZinc.c new file mode 100644 index 0000000..5a1a26c --- /dev/null +++ b/generic/tkZinc.c @@ -0,0 +1,4632 @@ +/* + * tkRadar.c -- Radar widget for the Tk Toolkit. Main module. + * + * Authors : Patrick Lecoanet. + * Creation date : Mon Feb 1 12:13:24 1999 + * + * $Id$ + */ + +/* + * Copyright (c) 1993 - 1999 CENA, Patrick Lecoanet -- + * + * This code is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This code is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this code; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * Some functions in this file are derived from tkCanvas.c and thus + * copyrighted: + * + * Copyright (c) 1991-1994 The Regents of the University of California. + * Copyright (c) 1994-1995 Sun Microsystems, Inc. + * + */ + +static const char rcs_id[]="$Id$"; +static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $"; + + +#include "Types.h" +#include "Geo.h" +#include "Item.h" +#include "WidgetInfo.h" +#include "tkRadar.h" +#include "MapInfo.h" +#include "patchlvl.h" +#include "OverlapMan.h" +#include "Track.h" +#include "Transfo.h" +#include "Image.h" + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <math.h> +#include <sys/time.h> +#include <unistd.h> + + +typedef struct TagSearch { + WidgetInfo *wi; + Tk_Uid tag; + GroupItem group; + Item current; + Item previous; /* Needed to detect changes in the linked + * list between calls. */ + RadarList item_stack; + RadarBool over; +} TagSearch; + + +#define INTEGER_SPACE 30 + +#define SYMBOL_WIDTH 8 +#define SYMBOL_HEIGHT 8 +static char SYMBOLS_bits[][SYMBOL_WIDTH*SYMBOL_HEIGHT/8] = { + { 0x18, 0x18, 0x24, 0x24, 0x5a, 0x5a, 0x81, 0xff }, + { 0xff, 0x81, 0x99, 0xbd, 0xbd, 0x99, 0x81, 0xff }, + { 0x18, 0x24, 0x42, 0x99, 0x99, 0x42, 0x24, 0x18 }, + { 0x18, 0x3c, 0x5a, 0xff, 0xff, 0x5a, 0x3c, 0x18 }, + { 0x18, 0x24, 0x42, 0x81, 0x81, 0x42, 0x24, 0x18 }, + { 0x3c, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3c }, + { 0x18, 0x18, 0x24, 0x24, 0x42, 0x42, 0x81, 0xff }, + { 0xff, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xff }, + { 0x18, 0x18, 0x3c, 0x3c, 0x66, 0x66, 0xff, 0xff }, + { 0xff, 0xff, 0xe7, 0xc3, 0xc3, 0xe7, 0xff, 0xff }, + { 0x18, 0x3c, 0x7e, 0xe7, 0xe7, 0x7e, 0x3c, 0x18 }, + { 0x18, 0x3c, 0x66, 0xc3, 0xc3, 0x66, 0x3c, 0x18 }, + { 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18 }, + { 0x3c, 0x7e, 0xff, 0xff, 0xff, 0xff, 0x7e, 0x3c }, + { 0x18, 0x18, 0x3c, 0x3c, 0x7e, 0x7e, 0xff, 0xff }, + { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, + { 0x18, 0x7e, 0x7e, 0xff, 0xff, 0x7e, 0x7e, 0x18 }, + { 0x18, 0x66, 0x42, 0x81, 0x81, 0x42, 0x66, 0x18 }, + { 0x00, 0x00, 0x18, 0x3c, 0x3c, 0x18, 0x00, 0x00 }, + { 0x00, 0x18, 0x3c, 0x7e, 0x7e, 0x3c, 0x18, 0x00 }, + { 0x18, 0x3c, 0x7e, 0xff, 0xff, 0x7e, 0x3c, 0x18 }, + { 0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81 }, +}; + +static unsigned char dither4x4[4][4] = { + { 0, 8, 2, 10 }, + { 12, 4, 14, 6 }, + { 3, 11, 1, 9 }, + { 15, 7, 13, 5 } +}; +static unsigned char bitmaps[NUM_ALPHA_STEPS][8]; + +static RadarBool inited = False; +static Tk_Uid all_uid; +static Tk_Uid current_uid; + + +/* + * Information used for argv parsing. + */ +static Tk_ConfigSpec config_specs[] = { + {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", + "2", Tk_Offset(WidgetInfo, border_width), 0}, + {TK_CONFIG_BORDER, "-backcolor", "backColor", "BackColor", + "White", Tk_Offset(WidgetInfo, bg_border), 0}, + {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", + "", Tk_Offset(WidgetInfo, cursor), TK_CONFIG_NULL_OK}, + {TK_CONFIG_FONT, "-font", "font", "Font", + "-adobe-helvetica-bold-r-normal--*-120-*-*-*-*-*-*", + Tk_Offset(WidgetInfo, font), 0}, + {TK_CONFIG_COLOR, "-forecolor", "foreColor", "ForeColor", + "Black", Tk_Offset(WidgetInfo, fore_color), 0}, + {TK_CONFIG_BOOLEAN, "-fullreshape", "fullReshape", "FullReshape", + "1", Tk_Offset(WidgetInfo, full_reshape), 0}, + {TK_CONFIG_PIXELS, "-height", "height", "Height", + "100", Tk_Offset(WidgetInfo, opt_height), 0}, + {TK_CONFIG_BITMAP, "-mapdistancesymbol", "mapDistanceSymbol", "MapDistanceSymbol", + "AtcSymbol19", Tk_Offset(WidgetInfo, map_distance_symbol), TK_CONFIG_NULL_OK}, + {TK_CONFIG_FONT, "-maptextfont", "mapTextFont", "MapTextFont", + "-adobe-helvetica-bold-r-normal--*-120-*-*-*-*-*-*", + Tk_Offset(WidgetInfo, map_text_font), 0}, +#ifdef OM + {TK_CONFIG_INT, "-overlapmanager", "overlapManager", "OverlapManager", "1", + Tk_Offset(WidgetInfo, om_group_id), 0}, +#endif + {TK_CONFIG_INT, "-pickaperture", "pickAperture", "PickAperture", + "1", Tk_Offset(WidgetInfo, pick_aperture), 0}, + {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", + "flat", Tk_Offset(WidgetInfo, relief), 0}, + {TK_CONFIG_INT, "-speedvectorlength", "speedVectorLength", + "SpeedVectorLength", "3", Tk_Offset(WidgetInfo, speed_vector_length), 0}, + {TK_CONFIG_INT, "-trackmanagedhistorysize", "trackManagedHistorySize", + "TrackManagedHistorySize", "6", Tk_Offset(WidgetInfo, track_managed_history_size), 0}, + {TK_CONFIG_BOOLEAN, "-trackmanagehistory", "trackManageHistory", "TrackManageHistory", + "1", Tk_Offset(WidgetInfo, track_manage_history), 0}, + {TK_CONFIG_BOOLEAN, "-reshape", "reshape", "Reshape", + "1", Tk_Offset(WidgetInfo, reshape), 0}, + {TK_CONFIG_STRING, "-tile", "tile", "Tile", + "", Tk_Offset(WidgetInfo, tile_name), 0}, + {TK_CONFIG_PIXELS, "-width", "width", "Width", + "100", Tk_Offset(WidgetInfo, opt_width), 0}, + /* + * Debug options. + */ + {TK_CONFIG_BOOLEAN, "-drawbboxes", "drawBBoxes", + "DrawBBoxes", "0", Tk_Offset(WidgetInfo, draw_bboxes), 0}, + {TK_CONFIG_COLOR, "-bboxcolor", "bboxColor", "BBoxColor", + "Pink", Tk_Offset(WidgetInfo, bbox_color), 0}, + + {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0} +}; + +/* + * These defines must be kept in sync with the config_specs array. + */ +#define BORDER_WIDTH_SPEC 0 +#define BACK_COLOR_SPEC 1 +#define CURSOR_SPEC 2 +#define FONT_SPEC 3 +#define FORE_COLOR_SPEC 4 +#define FULL_RESHAPE 5 +#define HEIGHT_SPEC 6 +#define MAP_DISTANCE_SYMBOL_SPEC 7 +#define MAP_TEXT_FONT_SPEC 8 +#define OVERLAP_MANAGER_SPEC 9 +#define PICK_APERTURE_SPEC 10 +#define RELIEF_SPEC 11 +#define SPEED_VECTOR_LENGTH_SPEC 12 +#define MANAGED_HISTORY_SIZE_SPEC 13 +#define MANAGE_HISTORY_SPEC 14 +#define RESHAPE_SPEC 15 +#define TILE_SPEC 16 +#define WIDTH_SPEC 17 +#define BBOXES_SPEC 18 +#define BBOXES_COLOR_SPEC 19 + + +static void CmdDeleted _ANSI_ARGS_((ClientData client_data)); +static void Event _ANSI_ARGS_((ClientData client_data, XEvent *eventPtr)); +static void Bind _ANSI_ARGS_((ClientData client_data, XEvent *eventPtr)); +static int WidgetCmd _ANSI_ARGS_((ClientData client_data, + Tcl_Interp *, int argc, Arg *args)); +static int Configure _ANSI_ARGS_((Tcl_Interp *interp, WidgetInfo *wi, + int argc, Arg *args, int flags)); +static void Redisplay _ANSI_ARGS_((ClientData client_data)); +static void Destroy _ANSI_ARGS_((char *mem_ptr)); +static void InitRadar _ANSI_ARGS_((Tcl_Interp *interp)); + +/* + *---------------------------------------------------------------------- + * + * RadarGetAlphaStipple -- + * Need to be handled per screen/dpy toolkit wide, not on a + * widget basis. + * + *---------------------------------------------------------------------- + */ +Pixmap +RadarGetAlphaStipple(WidgetInfo *wi, + unsigned char val) +{ + if (val >= 255) + return None; + else + return wi->alpha_stipples[(int) (val / 16)]; +} + +/* + *---------------------------------------------------------------------- + * + * RadarGetInactiveStipple -- + * + *---------------------------------------------------------------------- + */ +Pixmap +RadarGetInactiveStipple(WidgetInfo *wi) +{ + return RadarGetAlphaStipple(wi, 128); +} + + +/* + *---------------------------------------------------------------------- + * + * RadarNeedRedisplay -- + * + *---------------------------------------------------------------------- + */ +void +RadarNeedRedisplay(WidgetInfo *wi) +{ + if (!wi->update_pending) { + /*printf("scheduling an update\n");*/ + Tcl_DoWhenIdle(Redisplay, (ClientData) wi); + wi->update_pending = 1; + } +} + + +/* + *---------------------------------------------------------------------- + * + * RadarCmd -- + * + * This procedure is invoked to process the "radar" Tcl + * command. It creates a new "radar" widget. + * + *---------------------------------------------------------------------- + */ + +int +RadarCmd(ClientData client_data, /* Main window associated with + * interpreter. */ + Tcl_Interp *interp, /* Current interpreter. */ + int argc, /* Number of arguments. */ + Arg *args) /* Argument strings. */ +{ + Tk_Window main = (Tk_Window) client_data; + WidgetInfo *wi; + Tk_Window tkwin; + unsigned int num; + int major_op, first_err, first_evt; + + if (!inited) { + InitRadar(interp); + } + + if (argc == 1) { + Tcl_AppendResult(interp, XRADARVERSION, NULL); + return TCL_OK; + } + if (argc < 2) { + Tcl_AppendResult(interp, "wrong # args: should be \"", + LangString(args[0]), " pathName ?options?\"", NULL); + return RADAR_ERROR; + } + + tkwin = Tk_CreateWindowFromPath(interp, main, LangString(args[1]), NULL); + if (tkwin == NULL) { + return RADAR_ERROR; + } + Tk_SetClass(tkwin, "Radar"); + + /* + * Allocate and initialize the widget record. + */ + wi = (WidgetInfo *) RadarMalloc(sizeof(WidgetInfo)); + wi->win = tkwin; + wi->interp = interp; + wi->dpy = Tk_Display(tkwin); + wi->screen = Tk_Screen(tkwin); + wi->has_x_shm = XQueryExtension(wi->dpy, "MIT-SHM", + &major_op, &first_evt, &first_err); +#ifdef SHAPE + wi->has_x_shape = XQueryExtension(wi->dpy, "SHAPE", + &major_op, &first_evt, &first_err); +#else + wi->has_x_shape = False; +#endif + wi->has_x_input = XQueryExtension(wi->dpy, "XInputExtension", + &major_op, &first_evt, &first_err); + wi->reshape = wi->full_reshape = True; + wi->real_top = None; + +#ifndef PTK + wi->cmd = Tcl_CreateCommand(interp, Tk_PathName(tkwin), WidgetCmd, + (ClientData) wi, CmdDeleted); +#else + wi->cmd = Lang_CreateWidget(interp,tkwin, WidgetCmd, (ClientData) wi, CmdDeleted); + +#endif + wi->binding_table = 0; + wi->realized = False; + wi->update_pending = 0; + wi->bg_border = NULL; + wi->back_color = NULL; /* back_color will be initialized by Configure */ + wi->fore_color = NULL; + wi->bbox_color = NULL; + wi->draw_bboxes = 0; + wi->border_width = 0; + wi->relief = TK_RELIEF_FLAT; + wi->opt_width = None; + wi->opt_height = None; + wi->font = 0; + wi->map_text_font = 0; + wi->map_distance_symbol = None; + wi->cursor = None; +#ifdef OM + wi->om_group_id = 1; + wi->om_group = RADAR_NO_ITEM; +#endif + wi->track_manage_history = False; + wi->track_managed_history_size = 0; + wi->speed_vector_length = 0; + wi->tile = RadarUnspecifiedImage; + wi->tile_name = ""; + wi->tag_table = (Tcl_HashTable *) RadarMalloc(sizeof(Tcl_HashTable)); + Tcl_InitHashTable(wi->tag_table, TCL_ONE_WORD_KEYS); + wi->id_table = (Tcl_HashTable *) RadarMalloc(sizeof(Tcl_HashTable)); + Tcl_InitHashTable(wi->id_table, TCL_ONE_WORD_KEYS); + wi->t_table = (Tcl_HashTable *) RadarMalloc(sizeof(Tcl_HashTable)); + Tcl_InitHashTable(wi->t_table, TCL_ONE_WORD_KEYS); + wi->obj_id = 1; + wi->top_group = ITEM_P.CreateItem(wi, RadarGroup, 0, NULL); + wi->gc = 0; + wi->draw_buffer = 0; + wi->events_flags = 0; + wi->pick_aperture = 0; + wi->new_item = wi->current_item = RADAR_NO_ITEM; + wi->new_part = wi->current_part = 0; + + wi->monitoring = False; + wi->num_updates = 0; + wi->last_time = wi->total_time = 0; + + wi->work_item_list = NULL; + wi->work_pts = RadarListNew(8, sizeof(RadarPoint)); + + wi->clip_stack = RadarListNew(8, sizeof(ClipState)); + ITEM_P.ResetClipStack(wi); + wi->transfo_stack = RadarListNew(8, sizeof(RadarTransfo)); + ITEM_P.ResetTransformStack(wi); + + for (num = 0; num < NUM_ALPHA_STEPS; num++) { + char name[INTEGER_SPACE+12]; + + sprintf(name, "AlphaStipple%d", num); + wi->alpha_stipples[num] = Tk_GetBitmap(interp, tkwin, Tk_GetUid(name)); + } + + Tk_CreateEventHandler(tkwin, + ExposureMask|StructureNotifyMask|FocusChangeMask, + Event, (ClientData) wi); + Tk_CreateEventHandler(tkwin, KeyPressMask|KeyReleaseMask| + ButtonPressMask|ButtonReleaseMask|EnterWindowMask| + LeaveWindowMask|PointerMotionMask, Bind, + (ClientData) wi); + + if (Configure(interp, wi, argc-2, args+2, 0) != TCL_OK) { + Tk_DestroyWindow(tkwin); + return RADAR_ERROR; + } + + ResetBBox(&wi->exposed_area); + wi->damaged_area.orig.x = wi->damaged_area.orig.y = 0; + wi->damaged_area.corner.x = wi->width = wi->opt_width; + wi->damaged_area.corner.y = wi->height = wi->opt_height; + + /* + * Allocate double buffer pixmap. + */ + wi->draw_buffer = XCreatePixmap(wi->dpy, RootWindowOfScreen(wi->screen), + wi->width, wi->height, + DefaultDepthOfScreen(wi->screen)); + +#ifndef PTK + Tcl_SetResult(interp, Tk_PathName(tkwin), TCL_STATIC); +#else + Tcl_ArgResult(interp, LangWidgetArg(interp, tkwin)); +#endif + + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * EncodeItemPart -- + * + * Form a ClientData value from an item/part that is suitable + * as a key in a binding table. + * + *---------------------------------------------------------------------- + */ +ClientData +EncodeItemPart(Item item, + int part) +{ + if (part >= 0) { + FieldSet field_set; + if (!item->class->has_fields) { + return item; + } + field_set = item->class->GetFieldSet(item); + return (ClientData) (((char *) field_set->fields)+(part%field_set->num_fields)); + } + else if (part == RADAR_NO_PART) { + return item; + } + return (ClientData) (((char *) item)-part); +} + + +/* + *---------------------------------------------------------------------- + * + * RadarSearchWithTagOrId + * RadarNextWithTagOrId -- + * + * These two functions implement an alternative way for + * collecting items that match a tag or id. It has to + * be used instead of RadarItemsWithTagOrId 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 RadarItemsWithTagOrId. 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 + * RadarItemsWithTagOrId and the tag table altogether. It + * depends on the cost of the various operations. + * + *---------------------------------------------------------------------- + */ +static Item +RadarSearchWithTagOrId(WidgetInfo *wi, + char *tag, + Item group, + TagSearch *tag_search) +{ + Tk_Uid uid, *tags; + int id, i, num_tags; + char *end; + Tcl_HashEntry *entry; + Item item = RADAR_NO_ITEM; + + /* + * First init the search iterator. + */ + tag_search->wi = wi; + tag_search->over = False; + tag_search->item_stack = RadarListNew(16, sizeof(Item)); + tag_search->group = (GroupItem) group; + + /* + * Try to consider the tag as an item id. + */ + if (isdigit(*tag)) { + id = strtoul(tag, &end, 0); + if (*end == 0) { + entry = Tcl_FindHashEntry(wi->id_table, (char *) id); + if (entry != NULL) { + item = (Item) Tcl_GetHashValue(entry); + } + tag_search->over = True; + return item; + } + } + + /* + * Now look for a tag. + */ + tag_search->tag = uid = Tk_GetUid(tag); + if (uid == all_uid) { + tag_search->current = tag_search->group->head; + tag_search->previous = RADAR_NO_ITEM; + return tag_search->current; + } + + item = tag_search->group->head; + do { + while (item != RADAR_NO_ITEM) { + if (item->tags) { + tags = (Tk_Uid *) RadarListArray(item->tags); + num_tags = RadarListSize(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 (item->class == RadarGroup) { + /* + * Explore the hierarchy depth first using the item stack + * to save the current node. + */ + /*printf("RadarSearchWithTagOrId diving for tag\n");*/ + tag_search->group = (GroupItem) item; + item = item->next; + RadarListAdd(tag_search->item_stack, &item, RadarListTail); + item = tag_search->group->head; + } + else { + item = item->next; + } + } + /*printf("RadarSearchWithTagOrId backup for tag\n");*/ + while ((item == RADAR_NO_ITEM) && RadarListSize(tag_search->item_stack)) { + item = *(Item *) RadarListAt(tag_search->item_stack, RadarListTail); + RadarListDelete(tag_search->item_stack, RadarListTail); + } + if (item != RADAR_NO_ITEM) { + tag_search->group = (GroupItem) item->parent; + } + } while (item != RADAR_NO_ITEM); + + tag_search->over = True; + RadarListFree(tag_search->item_stack); + return NULL; +} + +static Item +RadarNextWithTagOrId(TagSearch *tag_search) +{ + Item item, previous; + Tk_Uid uid, *tags; + int i, num_tags; + + if (tag_search->over) { + return RADAR_NO_ITEM; + } + previous = tag_search->previous; + if (previous == RADAR_NO_ITEM) { + item = tag_search->group->head; + } + else { + item = previous->next; + } + + if (item != tag_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 ?) + */ + + } + else if (item->class == RadarGroup){ + /* + * Explore the hierarchy depth first using the item stack + * to save the current node. + */ + tag_search->group = (GroupItem) item; + item = item->next; + /*printf("RadarNextWithTagOrId diving for all, pushing %d\n", + item?item->id:0);*/ + RadarListAdd(tag_search->item_stack, &item, RadarListTail); + previous = RADAR_NO_ITEM; + item = tag_search->group->head; + } + else { + previous = item; + item = previous->next; + } + + if (item == RADAR_NO_ITEM) { + /*printf("RadarNextWithTagOrId backup for all\n");*/ + while ((item == RADAR_NO_ITEM) && RadarListSize(tag_search->item_stack)) { + /* + * End of list at this level, back up one level. + */ + item = *(Item *) RadarListAt(tag_search->item_stack, RadarListTail); + RadarListDelete(tag_search->item_stack, RadarListTail); + } + if (item != RADAR_NO_ITEM) { + tag_search->group = (GroupItem) item->parent; + previous = item->previous; + /*printf("RadarNextWithTagOrId popping %d, previous %d, next %d\n", + item->id, (item->previous)?item->previous->id:0, + (item->next)?item->next->id:0);*/ + } + else { + /* + * Or finish the search if at top. + */ + tag_search->over = True; + RadarListFree(tag_search->item_stack); + return RADAR_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("RadarNextWithTagOrId returning %d\n", item->id);*/ + return item; + } + + /* + * Now, look for the requested tag in an item. + */ + do { + while (item != RADAR_NO_ITEM) { + if (item->tags) { + tags = (Tk_Uid *) RadarListArray(item->tags); + num_tags = RadarListSize(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 (item->class == RadarGroup) { + /* + * Explore the hierarchy depth first using the item stack + * to save the current node. + */ + /*printf("RadarNextWithTagOrId diving for tag\n");*/ + tag_search->group = (GroupItem) item; + item = item->next; + RadarListAdd(tag_search->item_stack, &item, RadarListTail); + item = tag_search->group->head; + } + else { + item = item->next; + } + } + /*printf("RadarNextWithTagOrId backup for tag\n");*/ + while ((item == RADAR_NO_ITEM) && RadarListSize(tag_search->item_stack)) { + item = *(Item *) RadarListAt(tag_search->item_stack, RadarListTail); + RadarListDelete(tag_search->item_stack, RadarListTail); + } + if (item != RADAR_NO_ITEM) { + tag_search->group = (GroupItem) item->parent; + } + } while (item != RADAR_NO_ITEM); + + /* + * Out of fuel. + */ + tag_search->over = True; + RadarListFree(tag_search->item_stack); + return RADAR_NO_ITEM; +} + +void +RadarDoneWithSearch(TagSearch *tag_search) +{ + if (!tag_search->over) { + tag_search->over = True; + RadarListFree(tag_search->item_stack); + } +} + + +/* + *---------------------------------------------------------------------- + * + * RadarItemsWithTagOrId -- + * + * 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. + * + * The caller must at least give the item parameter a legal + * value. + * + *---------------------------------------------------------------------- + */ +int +RadarItemsWithTagOrId(WidgetInfo *wi, + char *tag_or_id, + Item *item, + Item **item_list) +{ + unsigned long id; + char *end; + Tcl_HashEntry *entry; + Tk_Uid tag_uid; + RadarList items; + int num_items; + + if (isdigit(*tag_or_id)) { + id = strtoul(tag_or_id, &end, 0); + if (*end == 0) { + entry = Tcl_FindHashEntry(wi->id_table, (char *) id); + if (entry == NULL) { + Tcl_AppendResult(wi->interp, + "item \"", tag_or_id, "\" 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(tag_or_id); + + /* + * If it is 'all' then build a list of all the items + * in the radar 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) { + RadarListEmpty(wi->work_item_list); + } + else { + wi->work_item_list = RadarListNew(128, sizeof(Item)); + } + for (current = RadarSearchWithTagOrId(wi, all_uid, wi->top_group, &ts); + current != NULL; current = RadarNextWithTagOrId(&ts)) { + RadarListAdd(wi->work_item_list, ¤t, RadarListTail); + } + if (RadarListSize(wi->work_item_list) != 0) { + *item = *((Item *) RadarListAt(wi->work_item_list, 0)); + if (item_list) { + *item_list = (Item *) RadarListArray(wi->work_item_list); + } + } + return RadarListSize(wi->work_item_list); + } + + /* + * Else, probe the tag table. + */ + entry = Tcl_FindHashEntry(wi->tag_table, tag_uid); + if (entry) { + items = (RadarList) Tcl_GetHashValue(entry); + num_items = RadarListSize(items); + if (num_items) { + if (item_list) { + *item_list = (Item *) RadarListArray(items); + *item = (*item_list)[0]; + } + else { + *item = *((Item *) RadarListAt(items, 0)); + } + } + return num_items; + } + else { + Tcl_AppendResult(wi->interp, + "unknown tag \"", tag_or_id, "\"", NULL); + return 0; + } +} + + +/* + *---------------------------------------------------------------------- + * + * DoItem -- + * + * Either add a tag to an item or add the item id/part to the + * interpreter result, depending on the value of tag. If tag + * is NULL, the item id/part is added to the result, otherwise + * the tag is added to the item. + * + *---------------------------------------------------------------------- + */ +void +DoItem(Tcl_Interp *interp, + Item item, + int part, + Tk_Uid tag_uid) +{ + if (tag_uid == NULL) { + char msg[INTEGER_SPACE]; + sprintf(msg, "%d", item->id); + Tcl_AppendElement(interp, msg); + if (part != RADAR_NO_PART) { + sprintf(msg, "%d", part); + Tcl_AppendElement(interp, msg); + } + } + else { + /*printf("Adding tag %s to item %d\n", tag_uid, item->id);*/ + ITEM.AddTag(item, tag_uid); + } +} + + +/* + *---------------------------------------------------------------------- + * + * IsHeirOf -- + * Tells if item is a descendant, either direct or indirect, + * of group. + * + *---------------------------------------------------------------------- + */ +static RadarBool +IsHeirOf(Item item, + Item group) +{ + while ((item != RADAR_NO_ITEM) && (item->parent != group)) { + item = item->parent; + } + + return (item != RADAR_NO_ITEM); +} + + +/* + *---------------------------------------------------------------------- + * + * FindArea -- + * Search the items that are enclosed or overlapping a given + * area of the radar. It is used by FindItems. + * If tag_uid is not NULL, all the items found are tagged with + * tag_uid. If tag_uid is NULL, the items found are added to the + * interp result. If enclosed is 1, the search look for + * items enclosed in the area. If enclosed is 0, it looks + * for overlapping and enclosed items. + * If an error occurs, a message is left in the interp result + * and RADAR_ERROR is returned. + * + *---------------------------------------------------------------------- + */ +static int +FindArea(Tcl_Interp *interp, + WidgetInfo *wi, + Arg *args, + Tk_Uid tag_uid, + Item group, + int enclosed) +{ + RadarPos pos; + RadarBBox area; + + if (Tcl_GetDouble(interp, args[0], &area.orig.x) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (Tcl_GetDouble(interp, args[1], &area.orig.y) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (Tcl_GetDouble(interp, args[2], &area.corner.x) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (Tcl_GetDouble(interp, args[3], &area.corner.y) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (area.corner.x < area.orig.x) { + pos = area.orig.x; + area.orig.x = area.corner.x; + area.corner.x = pos; + } + if (area.corner.y < area.orig.y) { + pos = area.orig.y; + area.orig.y = area.corner.y; + area.corner.y = pos; + } + area.corner.x += 1; + area.corner.y += 1; + area.orig.x -= wi->border_width; + area.orig.y -= wi->border_width; + area.corner.x -= wi->border_width; + area.corner.y -= wi->border_width; + + /* + * 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 RADAR_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * FindItems -- + * + * This procedure interprets the small object query langage for + * commands like addtag and find. + * If new_tag is NULL, the procedure collects all the objects + * matching the request and return them in the interpreter result. + * If new_tag is non NULL, it is interpreted as the tag to add to + * all matching objects. In this case the interpreter result is + * left empty. + * + *---------------------------------------------------------------------- + */ +static int +FindItems(Tcl_Interp *interp, + WidgetInfo *wi, + int argc, + Arg *args, + char *new_tag, /* NULL to search or tag string to add tag. */ + char *cmd_name, /* To report errors */ + char *option) /* Also to report errors meaningfully */ +{ + Tk_Uid tag_uid = NULL; + TagSearch ts; + char c; + int length, num_items; + Item item, group = wi->top_group; + + if (new_tag != NULL) { + tag_uid = Tk_GetUid(new_tag); + } + + c = LangString(args[0])[0]; + length = strlen(LangString(args[0])); + + /* + * above + */ + if ((c == 'a') && (strncmp(LangString(args[0]), "above", length) == 0)) { + if ((argc != 2) && (argc != 3)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", + cmd_name, option, " above tagOrId ?inGroup?", NULL); + return RADAR_ERROR; + } + if (argc == 3) { + num_items = RadarItemsWithTagOrId(wi, LangString(args[2]), &group, NULL); + if ((num_items == 0) || (group->class != RadarGroup)) { + return RADAR_ERROR; + } + } + item = RadarSearchWithTagOrId(wi, LangString(args[1]), group, &ts); + if ((item != RADAR_NO_ITEM) && (item->previous != RADAR_NO_ITEM)) { + DoItem(interp, item->previous, RADAR_NO_PART, tag_uid); + } + } + + /* + * all + */ + else if ((c == 'a') && (strncmp(LangString(args[0]), "all", length) == 0)) { + if ((argc != 1) && (argc != 2)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", + cmd_name, option, " all ?inGroup?", NULL); + return RADAR_ERROR; + } + if (argc == 2) { + num_items = RadarItemsWithTagOrId(wi, LangString(args[1]), &group, NULL); + if ((num_items == 0) || (group->class != RadarGroup)) { + return RADAR_ERROR; + } + } + + /* + * Go through the item list and collect all item ids. They + * are sorted from most visible to least visible. + */ + for (item = RadarSearchWithTagOrId(wi, all_uid, group, &ts); + item != RADAR_NO_ITEM; item = RadarNextWithTagOrId(&ts)) { + DoItem(interp, item, RADAR_NO_PART, tag_uid); + } + } + + /* + * atpoint + */ + else if ((c == 'a') && (strncmp(LangString(args[0]), "atpoint", length) == 0)) { + int halo = 1; + RadarPoint p; + int part = RADAR_NO_PART; + Item start_item = RADAR_NO_ITEM; + + if ((argc < 3) || (argc > 5)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", + cmd_name, option, " atpoint x y ?halo? ?start?", NULL); + return RADAR_ERROR; + } + if (Tcl_GetDouble(interp, args[1], &p.x) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (Tcl_GetDouble(interp, args[2], &p.y) == RADAR_ERROR) { + return RADAR_ERROR; + } + p.x -= wi->border_width; + p.y -= wi->border_width; + if (argc > 3) { + if (Tcl_GetInt(interp, args[3], &halo) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (halo < 0) { + halo = 0; + } + } + if (argc > 4) { + start_item = RadarSearchWithTagOrId(wi, LangString(args[4]), group, &ts); + if (start_item != RADAR_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 != RADAR_NO_ITEM) { + DoItem(interp, item, part, tag_uid); + /*printf("first %d %d\n", item->id, part);*/ + return TCL_OK; + } + } + + /* + * atpriority + */ + else if ((c == 'a') && (strncmp(LangString(args[0]), "atpriority", length) == 0)) { + int pri; + + if ((argc != 2) && (argc != 3)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", + cmd_name, option, " atpriority pri ?inGroup?", NULL); + return RADAR_ERROR; + } + if (Tcl_GetInt(interp, args[1], &pri) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (argc == 3) { + num_items = RadarItemsWithTagOrId(wi, LangString(args[2]), &group, NULL); + if ((num_items == 0) || (group->class != RadarGroup)) { + return RADAR_ERROR; + } + } + + /* + * Go through the item table and collect all items with + * the given priority. + */ + for (item = RadarSearchWithTagOrId(wi, all_uid, group, &ts); + item != RADAR_NO_ITEM; item = RadarNextWithTagOrId(&ts)) { + if (item->priority == pri) { + DoItem(interp, item, RADAR_NO_PART, tag_uid); + } + } + } + + /* + * below + */ + else if ((c == 'b') && (strncmp(LangString(args[0]), "below", length) == 0)) { + Item next; + + if ((argc != 2) && (argc != 3)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", + cmd_name, option, " below tagOrId $inGroup?", NULL); + return RADAR_ERROR; + } + if (argc == 3) { + num_items = RadarItemsWithTagOrId(wi, LangString(args[2]), &group, NULL); + if ((num_items == 0) || (group->class != RadarGroup)) { + return RADAR_ERROR; + } + } + item = RADAR_NO_ITEM; + for (next = RadarSearchWithTagOrId(wi, LangString(args[1]), group, &ts); + next != RADAR_NO_ITEM; item = RadarNextWithTagOrId(&ts)) { + item = next; + } + if ((item != RADAR_NO_ITEM) && (item->next != RADAR_NO_ITEM)) { + DoItem(interp, item->next, RADAR_NO_PART, tag_uid); + } + } + + /* + * enclosed + */ + else if ((c == 'e') && (strncmp(LangString(args[0]), "enclosed", length) == 0)) { + if ((argc != 5) && (argc != 6)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", + cmd_name, option, " enclosed x1 y1 x2 y2 ?inGroup?", NULL); + return RADAR_ERROR; + } + if (argc == 6) { + num_items = RadarItemsWithTagOrId(wi, LangString(args[5]), &group, NULL); + if ((num_items == 0) || (group->class != RadarGroup)) { + return RADAR_ERROR; + } + } + return FindArea(interp, wi, args+1, tag_uid, group, True); + } + + /* + * overlapping + */ + else if ((c == 'o') && (strncmp(LangString(args[0]), "overlapping", length) == 0)) { + if ((argc != 5) && (argc != 6)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", + cmd_name, option, " overlapping x1 y1 x2 y2 ?inGroup?", NULL); + return RADAR_ERROR; + } + if (argc == 6) { + num_items = RadarItemsWithTagOrId(wi, LangString(args[5]), &group, NULL); + if ((num_items == 0) || (group->class != RadarGroup)) { + return RADAR_ERROR; + } + } + return FindArea(interp, wi, args+1, tag_uid, group, False); + } + + /* + * withtag + */ + else if ((c == 'w') && (strncmp(LangString(args[0]), "withtag", length) == 0)) { + Item *items; + int i; + + if ((argc != 2) && (argc != 3)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", + cmd_name, option, " withtag tagOrId ?inGroup?", NULL); + return RADAR_ERROR; + } + if (argc == 3) { + num_items = RadarItemsWithTagOrId(wi, LangString(args[2]), &group, NULL); + if ((num_items == 0) || (group->class != RadarGroup)) { + return RADAR_ERROR; + } + } + num_items = RadarItemsWithTagOrId(wi, LangString(args[1]), &item, &items); + if (num_items == 0) { + return RADAR_ERROR; + } + for (i = 0; i < num_items; i++) { + if (IsHeirOf(items[i], group)) { + DoItem(interp, items[i], RADAR_NO_PART, tag_uid); + } + } + } + + /* + * withtype + */ + else if ((c == 'w') && (strncmp(LangString(args[0]), "withtype", length) == 0)) { + ItemClass cls; + + if ((argc != 2) && (argc != 3)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", + cmd_name, option, " withtype itemType ?inGroup?", NULL); + return RADAR_ERROR; + } + cls = (ItemClass) ITEM_P.LookupItemClass(LangString(args[1])); + if (!cls) { + Tcl_AppendResult(interp, "unknown item type \"", + LangString(args[1]), "\"", NULL); + return RADAR_ERROR; + } + if (argc == 3) { + num_items = RadarItemsWithTagOrId(wi, LangString(args[2]), &group, NULL); + if ((num_items == 0) || (group->class != RadarGroup)) { + return RADAR_ERROR; + } + } + + /* + * Go through the item table and collect all items with + * the given item type. + */ + for (item = RadarSearchWithTagOrId(wi, all_uid, group, &ts); + item != RADAR_NO_ITEM; item = RadarNextWithTagOrId(&ts)) { + if (item->class == cls) { + DoItem(interp, item, RADAR_NO_PART, tag_uid); + } + } + } + else { + Tcl_AppendResult(interp, "bad search command \"", LangString(args[0]), + "\": must be above, all, atpoint, atpriority, below, " + "enclosed, overlapping, withtag or withtype", NULL); + return RADAR_ERROR; + } + + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * ParseCoordList -- + * + *---------------------------------------------------------------------- + */ +int +ParseCoordList(WidgetInfo *wi, + Arg arg, + RadarPoint **pts, + int *num_pts) +{ + Arg *elems; + int i, result, num_elems; + RadarPoint *p; +#ifdef PTK + LangFreeProc *freeProc = NULL; +#endif + + result = Lang_SplitList(wi->interp, arg, &num_elems, &elems, &freeProc); + if ((result == RADAR_ERROR) || ((num_elems%2) != 0)) { + coord_error: +#ifdef PTK + if (elems != NULL && freeProc) { + (*freeProc)(num_elems, elems); + } +#endif + Tcl_AppendResult(wi->interp, " malformed coord list", NULL); + return RADAR_ERROR; + } + + *num_pts = num_elems/2; + RadarListAssertSize(wi->work_pts, *num_pts); + *pts = p = (RadarPoint *) RadarListArray(wi->work_pts); + for (i = 0; i < num_elems; i += 2, p++) { + if (Tcl_GetDouble(wi->interp, elems[i], &p->x) == RADAR_ERROR) { + coord_error2: +#ifndef PTK + Tcl_Free((char *) elems); +#endif + goto coord_error; + } + if (Tcl_GetDouble(wi->interp, elems[i+1], &p->y) == RADAR_ERROR) { + goto coord_error2; + } + } +#ifndef PTK + Tcl_Free((char *) elems); +#else + if (freeProc) { + (*freeProc)(num_elems, elems); + } +#endif + + return RADAR_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * Coords -- + * + *---------------------------------------------------------------------- + */ +static int +Coords(WidgetInfo *wi, + int argc, + Arg *args) +{ + RadarPoint *points; + Item item; + int num_points, num = 0, i; + int cmd = COORDS_READ; + long index; + char *end, c; + char msg[INTEGER_SPACE]; + + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, NULL); + if (num == 0) { + return RADAR_ERROR; + } + num_points = 0; + if (argc == 3) { + /* Get all coords. */ + cmd = COORDS_READ_ALL; + if (item->class->Coords(item, 0, cmd, &points, &num_points) == RADAR_ERROR) { + return RADAR_ERROR; + } + for (i = 0; i < num_points; i++, points++) { + sprintf(msg, "%g", points->x); + Tcl_AppendElement(wi->interp, msg); + sprintf(msg, "%g", points->y); + Tcl_AppendElement(wi->interp, msg); + } + return RADAR_OK; + } + + i = 3; + c = LangString(args[i])[0]; + if ((c == 'a') && (strcmp(LangString(args[i]), "add") == 0)) { + if ((argc != 5) && (argc != 6)) { + Tcl_AppendResult(wi->interp, "wrong # args: should be \"", LangString(args[0]), + "\" coords tagOrId add ?index? coordList", NULL); + return RADAR_ERROR; + } + cmd = COORDS_ADD; + i++; + } + else if ((c == 'r') && (strcmp(LangString(args[i]), "remove") == 0)) { + if (argc != 5) { + Tcl_AppendResult(wi->interp, "wrong # args: should be \"", LangString(args[0]), + "\" coords tagOrId remove index", NULL); + return RADAR_ERROR; + } + cmd = COORDS_REMOVE; + i++; + } + + index = strtol(LangString(args[i]), &end, 10); + if (*end != '\0') { + if (((argc == 5) && (cmd != COORDS_ADD)) || (argc == 6)) { + Tcl_AppendResult(wi->interp, " incorrect coord index \"", + args[i], "\"", NULL); + return RADAR_ERROR; + } + else if (ParseCoordList(wi, args[i], &points, &num_points) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (cmd == COORDS_ADD) { + /* Append coords. */ + cmd = COORDS_ADD_LAST; + if (item->class->Coords(item, 0, cmd, &points, &num_points) == RADAR_ERROR) { + return RADAR_ERROR; + } + } + else { + /* Set all coords. */ + cmd = COORDS_REPLACE_ALL; + if (item->class->Coords(item, 0, cmd, &points, &num_points) == RADAR_ERROR) { + return RADAR_ERROR; + } + } + return RADAR_OK; + } + + if (argc == (i+2)) { + /* Set a single coord or add coords at index. */ + if (ParseCoordList(wi, args[i+1], &points, &num_points) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (argc == 5) { + num_points = 1; + cmd = COORDS_REPLACE; + } + if (item->class->Coords(item, index, cmd, &points, &num_points) == RADAR_ERROR) { + return RADAR_ERROR; + } + } + else { + /* Read a single coord or remove a coord. */ + if (item->class->Coords(item, index, cmd, &points, &num_points) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (num_points) { + sprintf(msg, "%g", points->x); + Tcl_AppendElement(wi->interp, msg); + sprintf(msg, "%g", points->y); + Tcl_AppendElement(wi->interp, msg); + } + } + return RADAR_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * WidgetCmd -- + * + * This procedure is invoked to process the Tcl command + * that corresponds to a widget managed by this module. + * See the user documentation for details on what it does. + * + * Results: + * A standard Tcl result. + * + * Side effects: + * See the user documentation. + * + *---------------------------------------------------------------------- + */ +static int +WidgetCmd(ClientData client_data, /* Information about Radar widget. */ + Tcl_Interp *interp, /* Current interpreter. */ + int argc, /* Number of arguments. */ + Arg *args) /* Argument strings. */ +{ + WidgetInfo *wi = (WidgetInfo *) client_data; + char c; + int result; + int length; + Item item, *items; + RadarList item_list; + int num = 0, i, j; + char msg[INTEGER_SPACE]; + char *end, *end2; + RadarTransfo *t = NULL; + + if (argc < 2) { + Tcl_AppendResult(interp, "wrong # of args: \"", + LangString(args[0]), " subcommand ?args?.\"", NULL); + return RADAR_ERROR; + } + + Tcl_Preserve((ClientData) wi); + + c = LangString(args[1])[0]; + length = strlen(LangString(args[1])); + result = TCL_OK; + + /* + * add + */ + if ((c == 'a') && (strcmp(LangString(args[1]), "add") == 0)) { + Item group; + + if (argc == 2) { /* create subcommand alone, return the list of known + * object types. */ + ItemClass *classes = RadarListArray(ITEM_P.ItemClassList()); + + num = RadarListSize(ITEM_P.ItemClassList()); + for (i = 0; i < num; i++) { + Tcl_AppendElement(interp, classes[i]->name); + } + goto done; + } + if ((argc < 4) || (LangString(args[2])[0] == '-')) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" add type group ?args?", NULL); + goto error; + } + { + ItemClass cls; + + cls = ITEM_P.LookupItemClass(LangString(args[2])); + if (!cls) { + Tcl_AppendResult(interp, "unknown item type \"", LangString(args[2]), + "\"", NULL); + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[3]), &group, NULL); + if (!num || (group->class != RadarGroup)) { + Tcl_AppendResult(interp, ", group item expected, got \"", + LangString(args[3]), "\"", NULL); + goto error; + } + + argc -= 4; + args += 4; + item = ITEM_P.CreateItem(wi, cls, &argc, &args); + if (item == RADAR_NO_ITEM) { + goto error; + } + ITEM.InsertItem(item, group, RADAR_NO_ITEM, True); + if (ITEM.ConfigureItem(item, -1, argc, args, True) == RADAR_ERROR) { + goto error; + } + sprintf(msg, "%d", item->id); + Tcl_SetResult(interp, msg, TCL_VOLATILE); + } + } + + /* + * addtag + */ + else if ((c == 'a') && (strcmp(LangString(args[1]), "addtag") == 0)) { + if (argc < 4) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" addtag tag searchCommand ?arg arg ...?", NULL); + goto error; + } + result = FindItems(interp, wi, argc-3, args+3, LangString(args[2]), + LangString(args[0]), " addtag tag"); + } + + /* + * anchorxy + */ + else if ((c == 'a') && (strcmp(LangString(args[1]), "anchorxy") == 0)) { + Tk_Anchor anchor; + RadarPoint p; + + if (argc != 4) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" anchorxy tagOrId anchor", NULL); + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, NULL); + if ((num == 0) || !item->class->has_anchors) { + Tcl_AppendResult(interp, "unkown item or doesn't support anchors \"", + LangString(args[2]), NULL); + goto error; + } + if (Tk_GetAnchor(interp, LangString(args[3]), &anchor)) { + goto error; + } + /* + * If something has changed in the geometry we need to + * update or the anchor location will be erroneous. + */ + ITEM_P.Update(wi); + item->class->GetAnchor(item, anchor, &p); + sprintf(msg, "%g", p.x); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%g", p.y); + Tcl_AppendElement(interp, msg); + } + + /* + * becomes + */ + else if ((c == 'b') && (strncmp(LangString(args[1]), "becomes", length) == 0)) { + Tcl_AppendResult(interp, "Command not yet implemented", NULL); + goto error; + } + + /* + * bbox + */ + else if ((c == 'b') && (strncmp(LangString(args[1]), "bbox", length) == 0)) { + RadarBBox bbox; + int i; + RadarBool found = False; + + if (argc < 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" bbox tagOrId ?tagOrId ...?", NULL); + goto error; + } + argc -= 2; + args += 2; + + ITEM_P.Update(wi); + ResetBBox(&bbox); + for (i = 0; i < argc; i++) { + num = RadarItemsWithTagOrId(wi, LangString(args[i]), &item, &items); + found |= (num != 0); + for (j = 0; j < num; j++) { + AddBBoxToBBox(&bbox, &items[j]->item_bounding_box); + } + } + if (found && !IsEmptyBBox(&bbox)) { + sprintf(msg, "%g", bbox.orig.x+wi->border_width); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%g", bbox.orig.y+wi->border_width); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%g", bbox.corner.x+wi->border_width); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%g", bbox.corner.y+wi->border_width); + Tcl_AppendElement(interp, msg); + } + } + + /* + * bind + */ + else if ((c == 'b') && (strncmp(LangString(args[1]), "bind", length) == 0)) { + ClientData elem = 0; + int part = RADAR_NO_PART; + + if ((argc < 3) || (argc > 5)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" bind tagOrId ?sequence? ?command?", NULL); + goto error; + } + /* + * Test if (a) an itemid or (b) an itemid:part or (c) a tag is provided. + */ + if (isdigit(LangString(args[2])[0])) { + Tcl_HashEntry *entry; + int id; + + id = strtoul(LangString(args[2]), &end, 0); + if (*end == ':') { + part = strtoul(end+1, &end2, 0); + if (*end2 != 0) { + goto bind_a_tag; + } + } + else if (*end != 0) { + goto bind_a_tag; + } + entry = Tcl_FindHashEntry(wi->id_table, (char *) id); + if (entry == NULL) { + Tcl_AppendResult(interp, + "item \"", id, "\" doesn't exist", NULL); + goto error; + } + item = elem = Tcl_GetHashValue(entry); + if (!elem) { + goto error; + } + if (*end == ':') { + elem = EncodeItemPart((Item) elem, part); + } + /*printf("adding element 0x%X to the binding table of item 0x%X\n", elem, item);*/ + } + else { + bind_a_tag: + elem = (ClientData) Tk_GetUid(LangString(args[2])); + } + + /* + * Make a binding table if the widget doesn't already have one. + */ + if (wi->binding_table == NULL) { + wi->binding_table = Tk_CreateBindingTable(interp); + } + + if (argc == 5) { + int append = 0; + unsigned long mask; + + if (LangString(args[4])[0] == 0) { + result = Tk_DeleteBinding(interp, wi->binding_table, elem, LangString(args[3])); + goto done; + } + if (LangString(args[4])[0] == '+') { + args[4] = LangStringArg(LangString(args[4])+1); + append = 1; + } + mask = Tk_CreateBinding(interp, wi->binding_table, + elem, LangString(args[3]), args[4], append); + if (mask == 0) { + goto error; + } + if (mask & (unsigned) ~(ButtonMotionMask | Button1MotionMask | + Button2MotionMask | Button3MotionMask | + Button4MotionMask | Button5MotionMask | + ButtonPressMask | ButtonReleaseMask | + EnterWindowMask | LeaveWindowMask | + KeyPressMask | KeyReleaseMask | + PointerMotionMask)) { + Tk_DeleteBinding(interp, wi->binding_table, elem, LangString(args[3])); + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "requested illegal events; ", + "only key, button, motion, and enter/leave ", + "events may be used", NULL); + goto error; + } + } + else if (argc == 4) { + Arg command; + + command = Tk_GetBinding(interp, wi->binding_table, elem, LangString(args[3])); + if (command == NULL) { + goto error; + } +#ifndef PTK + Tcl_SetResult(interp, command, TCL_STATIC); +#else + Tcl_ArgResult(interp, command); +#endif + } + else { + Tk_GetAllBindings(interp, wi->binding_table, elem); + } + } + + /* + * cget + */ + else if ((c == 'c') && (strncmp(LangString(args[1]), "cget", length) == 0)) { + if (argc != 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" cget option", NULL); + goto error; + } + result = Tk_ConfigureValue(interp, wi->win, config_specs, + (char *) wi, LangString(args[2]), 0); + } + + /* + * chggroup + */ + else if ((c == 'c') && (strncmp(LangString(args[1]), "chggroup", length) == 0)) { + Item grp; + int adjust=0; + RadarTransfo inv, t, t2, *this_one; + + if ((argc != 4) && (argc != 5)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" chggroup tagOrIg group ?adjustTransform?", NULL); + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, NULL); + if (num == 0) { + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[3]), &grp, NULL); + if (num == 0) { + goto error; + } + if (argc == 5) { + if (Tcl_GetBoolean(interp, args[4], &adjust) != RADAR_OK) { + goto error; + } + } + if ((item->parent == grp) || (item->parent == RADAR_NO_ITEM)) { + goto done; + } + if (adjust) { + ITEM.GetTransform(grp, &t); + RadarTransfoInvert(&t, &inv); + ITEM.GetTransform(item->parent, &t); + RadarTransfoCompose(&t2, &t, &inv); + this_one = &t2; + if (item->transfo) { + RadarTransfoCompose(&t, item->transfo, &t2); + this_one = &t; + } + } + ITEM.RemoveItem(item); + ITEM.InsertItem(item, grp, RADAR_NO_ITEM, True); + ITEM.Invalidate(item, RADAR_COORDS_FLAG); + if (adjust) { + ITEM.SetTransfo(item, this_one); + } + } + + /* + * clone + */ + else if ((c == 'c') && (strncmp(LangString(args[1]), "clone", length) == 0)) { + if (argc != 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" clone tagOrIg", NULL); + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, &items); + if (num == 0) { + goto error; + } + for (i = 0; i < num; i++) { + item = ITEM.CloneItem(items[i]); + ITEM.InsertItem(item, items[i]->parent, RADAR_NO_ITEM, True); + sprintf(msg, "%d", item->id); + Tcl_AppendElement(interp, msg); + } + } + + /* + * configure + */ + else if ((c == 'c') && (strncmp(LangString(args[1]), "configure", length) == 0)) { + if (argc == 2) { + result = Tk_ConfigureInfo(interp, wi->win, config_specs, + (char *) wi, (char *) NULL, 0); + } + else if (argc == 3) { + result = Tk_ConfigureInfo(interp, wi->win, config_specs, + (char *) wi, LangString(args[2]), 0); + } + else { + result = Configure(interp, wi, argc-2, args+2, TK_CONFIG_ARGV_ONLY); + } + } + + /* + * coords + */ + else if ((c == 'c') && (strncmp(LangString(args[1]), "coords", length) == 0)) { + if ((argc < 3) && (argc > 6)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" coords tagOrId ?add/remove? ?index? ?coordList?", NULL); + goto error; + } + if (Coords(wi, argc, args) == RADAR_ERROR) { + goto error; + } + } + + /* + * currentpart + */ + else if ((c == 'c') && (strncmp(LangString(args[1]), "currentpart", length) == 0)) { + if (argc != 2) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" currentpart", NULL); + goto error; + } + sprintf(msg, "%d", wi->current_part); + Tcl_AppendResult(interp, msg, NULL); + } + + /* + * dtag + */ + else if ((c == 'd') && (strncmp(LangString(args[1]), "dtag", length) == 0)) { + Tk_Uid tag; + Tcl_HashEntry *entry; + + if ((argc != 3) && (argc != 4)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" dtag tagOrId ?tagToDelete?", NULL); + goto error; + } + if (argc == 4) { + tag = Tk_GetUid(LangString(args[3])); + } else { + tag = Tk_GetUid(LangString(args[2])); + } + entry = Tcl_FindHashEntry(wi->tag_table, tag); + item_list = (RadarList) Tcl_GetHashValue(entry); + items = (Item *) RadarListArray(item_list); + for (i = RadarListSize(item_list)-1; i >= 0; i++) { + ITEM.RemoveTag(items[i], (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 RadarItemsWithTagOrId. + */ + } + + /* + * find + */ + else if ((c == 'f') && (strncmp(LangString(args[1]), "find", length) == 0)) { + if (argc < 3) { + Tcl_AppendResult(interp, + "wrong # args: should be \"", LangString(args[0]), + "\" find searchCommand ?arg arg ...?", NULL); + goto error; + } + result = FindItems(interp, wi, argc-2, args+2, NULL, LangString(args[0]), " find"); + } + + /* + * gettags + */ + else if ((c == 'g') && (strncmp(LangString(args[1]), "gettags", length) == 0)) { + Tk_Uid *tags; + + if (argc != 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" gettags tagOrId", NULL); + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, NULL); + if (num == 0) { + goto error; + } + if (!item->tags || !RadarListSize(item->tags)) { + goto done; + } + else { + num = RadarListSize(item->tags); + tags = (Tk_Uid *) RadarListArray(item->tags); + for (i = 0; i < num; i++) { + Tcl_AppendElement(interp, tags[i]); + } + } + } + + /* + * group + */ + else if ((c == 'g') && (strncmp(LangString(args[1]), "group", length) == 0)) { + if (argc != 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" grouph tagOrId", NULL); + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, NULL); + if (num == 0) { + goto error; + } + if (item->parent != RADAR_NO_ITEM) { + sprintf(msg, "%d", item->parent->id); + Tcl_SetResult(interp, msg, TCL_VOLATILE); + } + else { + Tcl_SetResult(interp, "", TCL_STATIC); + } + } + + /* + * hasanchors + */ + else if ((c == 'h') && (strncmp(LangString(args[1]), "hasanchors", length) == 0)) { + if (argc != 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" hasanchors tagOrId", NULL); + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, NULL); + if (num == 0) { + goto error; + } + Tcl_SetResult(interp, item->class->has_anchors ? "1" : "0", TCL_STATIC); + } + + /* + * hasfields + */ + else if ((c == 'h') && (strncmp(LangString(args[1]), "hasfields", length) == 0)) { + if (argc != 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" hasfields tagOrId", NULL); + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, NULL); + if (num == 0) { + goto error; + } + Tcl_SetResult(interp, item->class->has_fields ? "1" : "0", TCL_STATIC); + } + + /* + * hasparts + */ + else if ((c == 'h') && (strncmp(LangString(args[1]), "hasparts", length) == 0)) { + if (argc != 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" hasparts tagOrId", NULL); + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, NULL); + if (num == 0) { + goto error; + } + Tcl_SetResult(interp, item->class->has_parts ? "1" : "0", TCL_STATIC); + } + + /* + * hastag + */ + else if ((c == 'h') && (strncmp(LangString(args[1]), "hastag", length) == 0)) { + Tk_Uid tag_uid; + Tk_Uid *tags; + + if (argc != 4) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" hastag tagOrId tag", NULL); + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, NULL); + if (num == 0) { + goto error; + } + if (!item->tags || !RadarListSize(item->tags)) { + Tcl_AppendResult(interp, "0", NULL); + } + else { + num = RadarListSize(item->tags); + tag_uid = Tk_GetUid(LangString(args[3])); + tags = (Tk_Uid *) RadarListArray(item->tags); + for (i = 0; i < num; i++) { + if (tags[i] == tag_uid) { + Tcl_SetResult(interp, "1", TCL_STATIC); + goto done; + } + } + Tcl_SetResult(interp, "0", TCL_STATIC); + } + } + + /* + * itemcget + */ + else if ((c == 'i') && (strncmp(LangString(args[1]), "itemcget", length) == 0)) { + int field = -1; +#ifndef PTK + char *largv[1]; +#else + Arg *largv = LangAllocVec(1); +#endif + if (argc < 4) { + itemcget_syntax: + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" itemcget tagOrId ?field? option", NULL); + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, NULL); + if (num == 0) { + goto error; + } + if (LangString(args[3])[0] != '-') { + char *end; + if (argc != 5) { + goto itemcget_syntax; + } + field = strtoul(LangString(args[3]), &end, 0); + if (*end == 0) { + argc--; + args++; + } + else { + Tcl_AppendResult(interp, "invalid field index \"", LangString(args[3]), + "\", should be a positive integer", NULL); + goto error; + } + } + if (argc != 4) { + goto itemcget_syntax; + } + largv[0] = LangCopyArg(args[3]); + if (ITEM.QueryItem(item, field, 1, largv) != RADAR_OK) { +#ifdef PTK + LangFreeVec(1, largv); +#endif + goto error; + } +#ifdef PTK + LangFreeVec(1, largv); +#endif + } + + /* + * itemconfigure + */ + else if ((c == 'i') && (strncmp(LangString(args[1]), "itemconfigure", length) == 0)) { + int field = -1; + if (argc < 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" itemconfigure tagOrId ?field? option value ?option value? ...", NULL); + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, &items); + if (num == 0) { + goto error; + } + if ((argc > 3) && (LangString(args[3])[0] != '-')) { + char *end; + field = strtoul(LangString(args[3]), &end, 0); + if (*end == 0) { + argc--; + args++; + } + else { + Tcl_AppendResult(interp, "invalid field index \"", LangString(args[3]), + "\", should be a positive integer", NULL); + goto error; + } + } + argc -= 3; + args += 3; + if (argc < 2) { + if (ITEM.AttributesInfo(item, field, argc, args) == RADAR_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 == RADAR_ERROR)) { + goto error; + } + } + + /* + * lower + */ + else if ((c == 'l') && (strncmp(LangString(args[1]), "lower", length) == 0)) { + Item mark = RADAR_NO_ITEM; + TagSearch search; + + if (argc < 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" lower tagOrId ?belowThis?", NULL); + goto error; + } + if (argc == 4) { + for (item = RadarSearchWithTagOrId(wi, LangString(args[3]), wi->top_group, &search); + item != RADAR_NO_ITEM; + item = RadarNextWithTagOrId(&search)) { + mark = item; + } + if (mark == RADAR_NO_ITEM) { + Tcl_AppendResult(interp, "unkown tag or item \"", + LangString(args[3]), "\"", NULL); + goto error; + } + } + item = RadarSearchWithTagOrId(wi, LangString(args[2]), wi->top_group, &search); + if (item == RADAR_NO_ITEM) { + Tcl_AppendResult(interp, "unkown tag or item \"", + LangString(args[2]), "\"", NULL); + goto error; + } + if (mark == RADAR_NO_ITEM) { + mark = ((GroupItem) item->parent)->tail; + } + for (; item != RADAR_NO_ITEM; item = RadarNextWithTagOrId(&search)) { + if (item != mark) { + ITEM.UpdateItemPriority(item, mark, False); + mark = item; + } + } + } + + /* + * monitor + */ + else if ((c == 'm') && (strncmp(LangString(args[1]), "monitor", length) == 0)) { + RadarBool on_off; + + if ((argc != 2) && (argc != 3)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" monitor ?onOff?", NULL); + goto error; + } + if (argc == 3) { + if (Tcl_GetBoolean(interp, args[2], &on_off) != RADAR_OK) { + goto error; + } + wi->monitoring = on_off; + if (on_off == True) { + wi->num_updates = 0; + wi->last_time = wi->total_time = 0; + } + } + if ((argc == 2) || (on_off == False)) { + sprintf(msg, "%d", wi->num_updates); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%d", wi->last_time); + Tcl_AppendElement(interp, msg); + /* sprintf(msg, "%d", wi->num_updates!=0?wi->total_time/wi->num_updates:0);*/ + sprintf(msg, "%d", wi->total_time); + Tcl_AppendElement(interp, msg); + } + } + + /* + * raise + */ + else if ((c == 'r') && (strncmp(LangString(args[1]), "raise", length) == 0)) { + Item mark = RADAR_NO_ITEM; + TagSearch search; + + if (argc < 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" raise tagOrId ?belowThis?", NULL); + goto error; + } + if (argc == 4) { + /* + * Find the topmost item with the tag. + */ + mark = RadarSearchWithTagOrId(wi, LangString(args[3]), wi->top_group, &search); + if (mark == RADAR_NO_ITEM) { + Tcl_AppendResult(interp, "unkown tag or item \"", + LangString(args[3]), "\"", NULL); + goto error; + } + RadarDoneWithSearch(&search); + } + item = RadarSearchWithTagOrId(wi, LangString(args[2]), wi->top_group, &search); + if (item == RADAR_NO_ITEM) { + Tcl_AppendResult(interp, "unkown tag or item \"", + LangString(args[2]), "\"", NULL); + goto error; + } + if (mark == RADAR_NO_ITEM) { + mark = ((GroupItem) item->parent)->head; + } + for (; item != RADAR_NO_ITEM; item = RadarNextWithTagOrId(&search)) { + if (item != mark) { + ITEM.UpdateItemPriority(item, mark, True); + } + } + } + + /* + * remove + */ + else if ((c == 'r') && (strncmp(LangString(args[1]), "remove", length) == 0)) { + if (argc < 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" remove tagOrId ?tagOrId ...?", NULL); + goto error; + } + argc -= 2; + args += 2; + for (j = 0; j < argc; j++) { + num = RadarItemsWithTagOrId(wi, LangString(args[j]), &item, &items); + if (num == 0) { + goto error; + } + for (i = num-1; i >= 0; i--) { + 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]); + } + ITEM.DestroyItem(items[i]); + } + } + } + + /* + * rotate + */ + else if ((c == 'r') && (strncmp(LangString(args[1]), "rotate", length) == 0)) { + RadarReal angle; + RadarPoint p; + + if ((argc != 4) && (argc != 6)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" rotate tagOrId angle ?centerX centerY?", (char *) NULL); + goto error; + } + + if (argc == 6) { + if (Tcl_GetDouble(interp, args[4], &p.x) == RADAR_ERROR) { + goto error; + } + if (Tcl_GetDouble(interp, args[5], &p.y) == RADAR_ERROR) { + goto error; + } + } + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, &items); + if (num == 0) { + Tcl_HashEntry *e; + e = Tcl_FindHashEntry(wi->t_table, LangString(args[2])); + if (e == NULL) { + Tcl_FreeResult(interp); + Tcl_AppendResult(interp, + "\"", LangString(args[2]), "\" must be either a tag or ", + "an id or a transform name", (char *) NULL); + goto error; + } + t = (RadarTransfo *) Tcl_GetHashValue(e); + } + + if (Tcl_GetDouble(interp, args[3], &angle) == RADAR_ERROR) { + goto error; + } + if (t) { + if (argc == 6) { + RadarTranslate(t, -p.x, -p.y); + } + RadarRotateRad(t, angle); + if (argc == 6) { + RadarTranslate(t, p.x, p.y); + } + } + if (num != 0) { + for (i = 0; i < num; i++) { + ITEM.RotateItem(items[i], angle, (argc == 6) ? &p : NULL); + } + } + } + + /* + * scale + */ + else if ((c == 's') && (strncmp(LangString(args[1]), "scale", length) == 0)) { + RadarPoint scale; + + if (argc != 5) { + Tcl_AppendResult(interp, + "wrong # args: should be \"", LangString(args[0]), + "\" scale tagOrId xFactor yFactor", (char *) NULL); + goto error; + } + if (argc == 5) { + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, &items); + if (num == 0) { + Tcl_HashEntry *e; + e = Tcl_FindHashEntry(wi->t_table, LangString(args[2])); + if (e == NULL) { + Tcl_FreeResult(interp); + Tcl_AppendResult(interp, "\"", LangString(args[2]), + "\" must be either a tag or ", + "an id or a transform name", (char *) NULL); + goto error; + } + t = (RadarTransfo *) Tcl_GetHashValue(e); + } + } + if (Tcl_GetDouble(interp, args[argc-2], &scale.x) == RADAR_ERROR) { + goto error; + } + if (Tcl_GetDouble(interp, args[argc-1], &scale.y) == RADAR_ERROR) { + goto error; + } + if (t) { + RadarScale(t, scale.x, scale.y); + } + if (num != 0) { + for (i = 0; i < num; i++) { + ITEM.ScaleItem(items[i], scale.x, scale.y); + } + } + } + + /* + * tapply + */ + else if ((c == 't') && (strncmp(LangString(args[1]), "tapply", length) == 0)) { + Tcl_AppendResult(interp, "Command not yet implemented", NULL); + goto error; + } + + /* + * tdelete + */ + else if ((c == 't') && (strncmp(LangString(args[1]), "tdelete", length) == 0)) { + Tcl_HashEntry *e; + if (argc != 3) { + Tcl_AppendResult(interp, + "wrong # args: should be \"", LangString(args[0]), + "\" tdelete tName", NULL); + goto error; + } + e = Tcl_FindHashEntry(wi->t_table, LangString(args[2])); + if (e == NULL) { + Tcl_AppendResult(interp, "\"", LangString(args[2]), + "\" must be a transform name", (char *) NULL); + goto error; + } + t = (RadarTransfo *) Tcl_GetHashValue(e); + RadarTransfoFree(t); + Tcl_DeleteHashEntry(e); + } + + /* + * transform + */ + else if ((c == 't') && (strncmp(LangString(args[1]), "transform", length) == 0)) { + int num_points; + RadarPoint *p, xp; + RadarTransfo t, t2, inv, *this_one; + Item from, to; + + if ((argc != 4) && (argc != 5)) { + Tcl_AppendResult(interp, + "wrong # args: should be \"", LangString(args[0]), + "\" transform ?tagOrIdFrom? tagOrIdTo coordlist", NULL); + goto error; + } + + if (argc == 5) { + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &from, &items); + if (num == 0) { + goto error; + } + } + num = RadarItemsWithTagOrId(wi, LangString(args[argc-2]), &to, &items); + if (num == 0) { + Tcl_HashEntry *e; + /* + * Try to find a named transform. + */ + e = Tcl_FindHashEntry(wi->t_table, LangString(args[argc-2])); + if (e == NULL) { + Tcl_FreeResult(interp); + Tcl_AppendResult(interp, "\"", LangString(args[argc-2]), + "\" must be either a tag or ", + "an id or a transform name", (char *) NULL); + goto error; + } + inv = *((RadarTransfo *) Tcl_GetHashValue(e)); + } + else { + ITEM.GetTransform(to, &t); + RadarTransfoInvert(&t, &inv); + } + this_one = &inv; + if (argc == 5) { + ITEM.GetTransform(from, &t); + RadarTransfoCompose(&t2, &t, &inv); + this_one = &t2; + } + /*RadarPrintTransfo(&t); + RadarPrintTransfo(&inv);*/ + + if (ParseCoordList(wi, args[argc-1], &p, &num_points) == RADAR_ERROR) { + Tcl_AppendResult(interp, + " invalid coord list \"", args[argc-1], "\"", NULL); + goto error; + } + for (i = 0; i < num_points; i++, p++) { + /* + * Need to adjust for the border. + */ + if (argc != 5) { + p->x -= wi->border_width; + p->y -= wi->border_width; + } + RadarTransformPoint(this_one, p, &xp); + /*printf("p->x=%g, p->y=%g, xp.x=%g, xp.y=%g\n", p->x, p->y, xp.x, xp.y);*/ + sprintf(msg, "%g", xp.x); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%g", xp.y); + Tcl_AppendElement(interp, msg); + } + } + + /* + * translate + */ + else if ((c == 't') && (strncmp(LangString(args[1]), "translate", length) == 0)) { + RadarPoint trans; + + if (argc != 5) { + Tcl_AppendResult(interp, + "wrong # args: should be \"", LangString(args[0]), + "\" translate tagOrId xAmount yAmount", NULL); + goto error; + } + num = 0; + if (argc == 5) { + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, &items); + if (num == 0) { + Tcl_HashEntry *e; + e = Tcl_FindHashEntry(wi->t_table, LangString(args[2])); + if (e == NULL) { + Tcl_FreeResult(interp); + Tcl_AppendResult(interp, "\"", LangString(args[2]), + "\" must be either a tag or ", + "an id or a transform name", (char *) NULL); + goto error; + } + t = (RadarTransfo *) Tcl_GetHashValue(e); + } + } + if (Tcl_GetDouble(interp, args[argc-2], &trans.x) == RADAR_ERROR) { + goto error; + } + if (Tcl_GetDouble(interp, args[argc-1], &trans.y) == RADAR_ERROR) { + goto error; + } + if (t) { + RadarTranslate(t, trans.x, trans.y); + } + if (num != 0) { + for (i = 0; i < num; i++) { + ITEM.TranslateItem(items[i], trans.x, trans.y); + } + } + } + + /* + * treset + */ + else if ((c == 't') && (strncmp(LangString(args[1]), "treset", length) == 0)) { + if (argc != 3) { + Tcl_AppendResult(interp, + "wrong # args: should be \"", LangString(args[0]), + "\" treset tagOrId", NULL); + goto error; + } + if (argc == 3) { + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, &items); + if (num == 0) { + Tcl_HashEntry *e; + e = Tcl_FindHashEntry(wi->t_table, LangString(args[2])); + if (e == NULL) { + Tcl_FreeResult(interp); + Tcl_AppendResult(interp, + "\"", LangString(args[2]), "\" must be either a tag or ", + "an id or a transform name", (char *) NULL); + goto error; + } + t = (RadarTransfo *) Tcl_GetHashValue(e); + } + } + if (t) { + RadarTransfoSetIdentity(t); + } + if (num != 0) { + for (i = 0; i < num; i++) { + ITEM.ResetTransfo(items[i]); + } + } + } + + /* + * trestore + */ + else if ((c == 't') && (strncmp(LangString(args[1]), "trestore", length) == 0)) { + Tcl_HashEntry *e; + + if (argc != 4) { + Tcl_AppendResult(interp, + "wrong # args: should be \"", LangString(args[0]), + "\" trestore tagOrId tName", NULL); + goto error; + } + e = Tcl_FindHashEntry(wi->t_table, LangString(args[argc-1])); + if (e == NULL) { + Tcl_AppendResult(interp, "\"", LangString(args[argc-1]), + "\" must be a transform name", (char *) NULL); + goto error; + } + t = (RadarTransfo *) Tcl_GetHashValue(e); + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, &items); + if (num == 0) { + goto error; + } + for (i = 0; i < num; i++) { + ITEM.SetTransfo(items[i], t); + } + } + + /* + * tsave + */ + else if ((c == 't') && (strncmp(LangString(args[1]), "tsave", length) == 0)) { + Tcl_HashEntry *e; + int new, invert=0; + RadarTransfo *inv; + + if ((argc != 4) && (argc != 5)) { + Tcl_AppendResult(interp, + "wrong # args: should be \"", LangString(args[0]), + "\" tsave tagOrId tName ?invert?", NULL); + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, &items); + if (num == 0) { + goto error; + } + if (argc == 5) { + if (Tcl_GetBoolean(interp, args[4], &invert) != RADAR_OK) { + goto error; + } + } + t = item->transfo; + e = Tcl_CreateHashEntry(wi->t_table, LangString(args[argc-1]), &new); + if (!new) { + RadarTransfoFree((RadarTransfo *) Tcl_GetHashValue(e)); + } + if (invert) { + inv = RadarTransfoNew(); + RadarTransfoInvert(t, inv); + } + else { + inv = RadarTransfoDuplicate(t); + } + Tcl_SetHashValue(e, inv); + } + + /* + * type + */ + else if ((c == 't') && (strncmp(LangString(args[1]), "type", length) == 0)) { + if (argc != 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" type tagOrId", NULL); + goto error; + } + num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, NULL); + if (num == 0) { + goto error; + } + Tcl_SetResult(interp, item->class->name, TCL_STATIC); + } + + else { + Tcl_AppendResult(interp, "invalid command \"", LangString(args[1]), + "\": must be " + "add, addtag, anchorxy, bbox, becomes, bind, cget, " + "chggroup, clone, configure, coords, currentpart, " + "dtag, find, gettags, group, hasanchors, " + "hasfields, hasparts, hastag, itemcget, " + "itemconfigure, lower, monitor, raise, remove, rotate, " + "scale, tapply, tdelete, transform, translate, " + "treset, trestore, tsave, type, ", NULL); + goto error; + } + done: + if (wi->work_item_list) { + RadarListFree(wi->work_item_list); + wi->work_item_list = NULL; + } + Tcl_Release((ClientData) wi); + return result; + + error: + if (wi->work_item_list) { + RadarListFree(wi->work_item_list); + wi->work_item_list = NULL; + } + Tcl_Release((ClientData) wi); + return RADAR_ERROR; +} + + +/* + ********************************************************************************** + * + * TileChange -- + * + ********************************************************************************** + */ +static void +TileChange(ClientData client_data, + int x, + int y, + int width, + int height, + int image_width, + int image_height) +{ + WidgetInfo *wi = (WidgetInfo *) client_data; + RadarBBox bbox; + + InvalidateImage(wi->tile); + bbox.orig.x = bbox.orig.y = 0; + bbox.corner.x = wi->width; + bbox.corner.y = wi->height; + ITEM_P.Damage(wi, &bbox); + RadarNeedRedisplay(wi); +} + + +/* + *---------------------------------------------------------------------- + * + * Configure -- + * + * This procedure is called to process an args/argc list in + * conjunction with the Tk option database to configure (or + * reconfigure) a Act widget. + * + * Results: + * The return value is a standard Tcl result. If RADAR_ERROR is + * returned, then interp->result contains an error message. + * + * Side effects: + * Configuration information, such as colors, border width, + * etc. get set for the widget; old resources get freed, + * if there were any. + * + *---------------------------------------------------------------------- + */ +static int +Configure(Tcl_Interp *interp, /* Used for error reporting. */ + WidgetInfo *wi, /* Information about widget. */ + int argc, /* Number of valid entries in args. */ + Arg *args, /* Arguments. */ + int flags) /* Flags to pass to Tk_ConfigureWidget. */ +{ +#define CONFIG_PROBE(offset) (ISSET(config_specs[offset].specFlags, \ + TK_CONFIG_OPTION_SPECIFIED)) + RadarBBox bbox; + + if (Tk_ConfigureWidget(interp, wi->win, config_specs, + argc, args, (char *) wi, flags) != TCL_OK) { + return RADAR_ERROR; + } + + /* + * Maintain the pick aperture within meaningful bounds. + */ + if (wi->pick_aperture < 0) { + wi->pick_aperture = 0; + } + + wi->back_color = Tk_3DBorderColor(wi->bg_border); + Tk_SetBackgroundFromBorder(wi->win, wi->bg_border); + + if (CONFIG_PROBE(BACK_COLOR_SPEC)) { + bbox.orig.x = bbox.orig.y = 0; + bbox.corner.x = wi->width; + bbox.corner.y = wi->height; + ITEM_P.Damage(wi, &bbox); + RadarNeedRedisplay(wi); + } + if (CONFIG_PROBE(RELIEF_SPEC)) { + RadarNeedRedisplay(wi); + } + + /* + * Update the items in the case borderwidth changed. This change + * results in a change of the window size which might not be + * acknowledged by the geometry manager. In this case the actual + * area available to display items and the global transform are + * modified. + * If the changes in geometry are actually performed the code below + * might be a duplicate effort (done in Event). + */ + if (CONFIG_PROBE(BORDER_WIDTH_SPEC)) { + bbox.orig.x = bbox.orig.y = 0; + bbox.corner.x = wi->width; + bbox.corner.y = wi->height; + ITEM_P.Damage(wi, &bbox); + ITEM_P.ResetTransformStack(wi); + ITEM.Invalidate(wi->top_group, RADAR_TRANSFO_FLAG); + } + if (CONFIG_PROBE(SPEED_VECTOR_LENGTH_SPEC) || + CONFIG_PROBE(MANAGE_HISTORY_SPEC) || + CONFIG_PROBE(MANAGED_HISTORY_SIZE_SPEC)) { + ITEM.InvalidateItems(wi->top_group, RadarTrack); + } + if (CONFIG_PROBE(MAP_DISTANCE_SYMBOL_SPEC)) { + ITEM.InvalidateItems(wi->top_group, RadarMap); + } + + /* + * Request the new geometry. + */ + if (CONFIG_PROBE(WIDTH_SPEC) || CONFIG_PROBE(HEIGHT_SPEC) || + CONFIG_PROBE(BORDER_WIDTH_SPEC) || !wi->realized) { + Tk_GeometryRequest(wi->win, wi->opt_width + 2*wi->border_width, + wi->opt_height + 2*wi->border_width); + } + + if (CONFIG_PROBE(TILE_SPEC)) { + if (wi->tile != RadarUnspecifiedImage) { + Tk_FreeImage(wi->tile); + } + wi->tile = Tk_GetImage(wi->interp, wi->win, wi->tile_name, + TileChange, (ClientData) wi); + bbox.orig.x = bbox.orig.y = 0; + bbox.corner.x = wi->width; + bbox.corner.y = wi->height; + ITEM_P.Damage(wi, &bbox); + RadarNeedRedisplay(wi); + } + + /* + * Update the registration with the overlap manager. + */ +#ifdef OM + if (CONFIG_PROBE(OVERLAP_MANAGER_SPEC)) { + Tcl_HashEntry *entry; + Item grp; + + if (wi->om_group != RADAR_NO_ITEM) { + OmUnregister((void *) wi); + wi->om_group = RADAR_NO_ITEM; + } + if (wi->om_group_id != 0) { + entry = Tcl_FindHashEntry(wi->id_table, (char *) wi->om_group_id); + if (entry != NULL) { + grp = (Item) Tcl_GetHashValue(entry); + if (grp->class == RadarGroup) { + OmRegister((void *) wi, SendTrackToOm, + SetLabelAngleFromOm, QueryLabelPosition); + wi->om_group = grp; + } + } + } + } +#endif + + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * Event -- + * + * This procedure is invoked by the Tk dispatcher for various + * events on Radars. + * + * Results: + * None. + * + * Side effects: + * When the window gets deleted, internal structures get + * cleaned up. When it gets exposed, it is redisplayed. + * + *---------------------------------------------------------------------- + */ +static void +Event(ClientData client_data, /* Information about widget. */ + XEvent *event_ptr) /* Information about event. */ +{ + WidgetInfo *wi = (WidgetInfo *) client_data; + + /*printf("=============== DEBUT %s EVENT ==================\n", + event_ptr->type == MapNotify ? "MAP": + event_ptr->type == Expose? "EXPOSE" : + event_ptr->type == ConfigureNotify ? "CONFIGURE" : + event_ptr->type == DestroyNotify ? "DESTROY" : + "??");*/ + if (event_ptr->type == MapNotify) { + if (!wi->gc) { + wi->realized = True; + /* + * Get the work GC and suppress GraphicExpose + * and NoExpose events reception. + */ + wi->gc = XCreateGC(wi->dpy, RadarWindowId(wi->win), 0, NULL); + XSetGraphicsExposures(wi->dpy, wi->gc, False); + /* + * Set the real top window above us. + */ + { + Window parent, root, *children; + Tk_Window top_level; + int num_children; + + top_level = wi->win; + while (!Tk_IsTopLevel(top_level)) { + top_level = Tk_Parent(top_level); + } + XQueryTree(wi->dpy, RadarWindowId(top_level), &root, &parent, + &children, &num_children); + if (root == parent) { + wi->real_top = RadarWindowId(top_level); + } + else { + wi->real_top = parent; + } + XFree((char *) children); + } + } + } + else if (event_ptr->type == Expose) { + RadarBBox bbox; + RadarDim width, height; + + bbox.orig.x = ((XExposeEvent*) event_ptr)->x - wi->border_width; + bbox.orig.y = ((XExposeEvent*) event_ptr)->y - wi->border_width; + width = ((XExposeEvent*) event_ptr)->width; + height = ((XExposeEvent*) event_ptr)->height; + if (bbox.orig.x < 0) { + width += bbox.orig.x; + bbox.orig.x = 0; + } + if (bbox.orig.y < 0) { + height += bbox.orig.y; + bbox.orig.y = 0; + } + bbox.corner.x = bbox.orig.x + width; + bbox.corner.y = bbox.orig.y + height; + + /*printf("expose %d %d %d %d\n", + ((XExposeEvent*) event_ptr)->x, ((XExposeEvent*) event_ptr)->y, + ((XExposeEvent*) event_ptr)->width, ((XExposeEvent*) event_ptr)->height);*/ + /* + * Add the exposed area to the expose region and + * schedule an asynchronous redisplay of the window + * if we are done adding exposed parts. + */ + AddBBoxToBBox(&wi->exposed_area, &bbox); + if ((((XExposeEvent*) event_ptr)->count == 0) && + !IsEmptyBBox(&wi->exposed_area)) { + RadarNeedRedisplay(wi); + } + } + /* + * Resize the double buffer pixmap and prepare to redisplay + * the whole scene. The transform parameters are not + * modified as a result of the resize. If the application + * need such change, it can bind a handler on <Configure>. + */ + else if (event_ptr->type == ConfigureNotify) { + RadarDim w, h; + RadarBBox bbox; + + /* w = ((XConfigureEvent*) event_ptr)->width-2*wi->border_width; + h = ((XConfigureEvent*) event_ptr)->height-2*wi->border_width;*/ + w = Tk_Width(wi->win)-2*wi->border_width; + h = Tk_Height(wi->win)-2*wi->border_width; + + if ((wi->width != w) || (wi->height != h)) { + /*printf("reallocating pixmap\n");*/ + bbox.orig.x = bbox.orig.y = 0; + bbox.corner.x = MAX(wi->width, w); + bbox.corner.y = MAX(wi->height, h); + wi->opt_width = wi->width = w; + wi->opt_height = wi->height = h; + + ITEM_P.ResetTransformStack(wi); + ITEM_P.Damage(wi, &bbox); + ITEM.Invalidate(wi->top_group, RADAR_TRANSFO_FLAG); + + /* + * Reallocate the double buffer pixmap. + */ + XFreePixmap(wi->dpy, wi->draw_buffer); + wi->draw_buffer = XCreatePixmap(wi->dpy, RootWindowOfScreen(wi->screen), + wi->width, wi->height, + DefaultDepthOfScreen(wi->screen)); + } + else { + /* + * In case of a window reconfiguration following a change + * of border size, set the exposed area to force a copy + * of the back buffer to the screen. + */ + bbox.orig.x = bbox.orig.y = 0; + bbox.corner.x = Tk_Width(wi->win); + bbox.corner.y = Tk_Height(wi->win); + AddBBoxToBBox(&wi->exposed_area, &bbox); + RadarNeedRedisplay(wi); + } + } + /* + * Take into account that the window has been actually cancelled. + * Remove the corresponding widget command, unregister any + * pending Redisplay and eventually free the widget's memory. + */ + else if (event_ptr->type == DestroyNotify) { + if (wi->win != NULL) { + wi->win = NULL; + wi->realized = False; +#ifndef PTK + Tcl_DeleteCommand(wi->interp, Tcl_GetCommandName(wi->interp, wi->cmd)); +#else + Lang_DeleteWidget(wi->interp, wi->cmd); +#endif + } + if (wi->update_pending) { + Tcl_CancelIdleCall(Redisplay, (ClientData) wi); + } + Tcl_EventuallyFree((ClientData) wi, Destroy); + } + /*printf("=============== FIN %s EVENT ==================\n", + event_ptr->type == MapNotify ? "MAP": + event_ptr->type == Expose? "EXPOSE" : + event_ptr->type == ConfigureNotify ? "CONFIGURE" : + event_ptr->type == DestroyNotify ? "DESTROY" : + "??");*/ +} + + +/* + *---------------------------------------------------------------------- + * + * DoEvent -- + * + * Trigger the bindings associated with an event. + * + *---------------------------------------------------------------------- + */ +static void +DoEvent(WidgetInfo *wi, + XEvent *event_ptr) +{ +#define NUM_STATIC 4 + ClientData items[NUM_STATIC], *its; + static int worksize = 128; + static char *workspace = NULL; + int num, num_tags, i, len, ptr; + ClientData *tag_list = NULL; + Item item; + RadarBool bind_part, bind_item; + +#define BIND_ITEM(test) \ + if (bind_item && (test)) { \ + its[ptr] = all_uid; \ + ptr++; \ + for (i = 0; i < num_tags; i++) { \ + its[ptr] = tag_list[i]; \ + ptr++; \ + } \ + its[ptr] = item; \ + ptr++; \ + } + + if (wi->binding_table == NULL) { + /*printf("no bindings\n");*/ + return; + } + + item = wi->current_item; + if (item == NULL) { + /*printf("no current item, leaving\n");*/ + return; + } + + /* + * Set up an array with all the relevant elements for processing + * this event. The relevant elements are (a) the event's item/part + * tag (i.e item:part), (b) the event's item, (c) the tags + * associated with the event's item, and (d) the tag 'all'. + */ + num = 0; + num_tags = 0; + its = items; + bind_part = ((wi->current_part != RADAR_NO_PART) && + (wi->current_item->class->has_parts || + wi->current_item->class->has_fields)); + /* + * This is needed to avoid generating enter and leave + * event on items when crossing a field boundary inside + * the item. If not bind_item we do not want to trigger + * any binding related to the item only those pertaining + * to the fields. + */ + bind_item = (((event_ptr->type != EnterNotify) && + (event_ptr->type != LeaveNotify)) || + (wi->current_item != wi->new_item)); + /*printf("type=%s, current=%d, new=%d --> %s\n", + event_ptr->type==EnterNotify?"Enter": + event_ptr->type==LeaveNotify?"Leave":"other", + wi->current_item?wi->current_item->id:0, + wi->new_item?wi->new_item->id:0, + bind_item?"bind":"nobind");*/ + if (bind_item) { + num += 2; + } + if (bind_part) { + num++; + if (!workspace) { + workspace = RadarMalloc(worksize); + } + } + if (item->tags) { + num_tags = RadarListSize(item->tags); + if (bind_item) { + num += num_tags; + } + if (bind_part) { + num += num_tags; + } + tag_list = (ClientData *) RadarListArray(item->tags); + if (num > NUM_STATIC) { + its = (ClientData *) RadarMalloc(num*sizeof(ClientData)); + } + } + + ptr = 0; + + BIND_ITEM(event_ptr->type != LeaveNotify); + + if (bind_part) { + /* + * Add here a binding for each tag suffixed by :all + */ + for (i = 0; i < num_tags; i++) { + len = strlen(tag_list[i])+ INTEGER_SPACE; + if (worksize < len) { + worksize = len + 10; + workspace = RadarRealloc(workspace, len); + } + sprintf(workspace, "%s:%d", (char *) tag_list[i], wi->current_part); + its[ptr] = Tk_GetUid(workspace); + ptr++; + } + /* + * Add here a binding for id:all + */ + its[ptr] = EncodeItemPart(item, wi->current_part); + ptr++; + } + + BIND_ITEM(event_ptr->type == LeaveNotify); + + /* + * Invoke the binding system. + */ + if (wi->win != NULL) { + Tk_BindEvent(wi->binding_table, event_ptr, wi->win, num, its); + } + if (its != items) { + RadarFree(its); + } + +#undef BIND_ITEM +} + + +/* + *---------------------------------------------------------------------- + * + * PickCurrentItem -- + * + * Finds the topmost item/field that contains the pointer and mark + * it has the current item. Generates Enter/leave events on the + * old and new current items/fields has necessary. + * + * Results: + * None. + * + * Side effects: + * The current item/field may change. If it does, + * then the commands associated with item entry and exit + * could do just about anything. A binding script could + * delete the radar, so callers should protect themselves + * with Tcl_Preserve and Tcl_Release. + * + * Note: + * See the Bind function's note. + * + *---------------------------------------------------------------------- + */ +static void +PickCurrentItem(WidgetInfo *wi, + XEvent *event_ptr) +{ + int button_down; + int prev_left_grabbed_item; + + /* + * Check whether or not a button is down. If so, we'll log entry + * and exit into and out of the current item, but not entry into + * any other item. This implements a form of grabbing equivalent + * to what the X server does for windows. + */ + button_down = wi->state + & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask); + prev_left_grabbed_item = wi->events_flags & LEFT_GRABBED_ITEM; + if (!button_down) { + wi->events_flags &= ~LEFT_GRABBED_ITEM; + } + + /* + * Save information about this event in the radar. The event in + * the radar is used for two purposes: + * + * 1. Event bindings: if the current item changes, fake events are + * generated to allow item-enter and item-leave bindings to trigger. + * 2. Reselection: if the current item gets deleted, can use the + * saved event to find a new current item. + * Translate MotionNotify events into EnterNotify events, since that's + * what gets reported to item handlers. + */ + if (event_ptr != &wi->pick_event) { + if ((event_ptr->type == MotionNotify) || (event_ptr->type == ButtonRelease)) { + wi->pick_event.xcrossing.type = EnterNotify; + wi->pick_event.xcrossing.serial = event_ptr->xmotion.serial; + wi->pick_event.xcrossing.send_event = event_ptr->xmotion.send_event; + wi->pick_event.xcrossing.display = event_ptr->xmotion.display; + wi->pick_event.xcrossing.window = event_ptr->xmotion.window; + wi->pick_event.xcrossing.root = event_ptr->xmotion.root; + wi->pick_event.xcrossing.subwindow = None; + wi->pick_event.xcrossing.time = event_ptr->xmotion.time; + wi->pick_event.xcrossing.x = event_ptr->xmotion.x; + wi->pick_event.xcrossing.y = event_ptr->xmotion.y; + wi->pick_event.xcrossing.x_root = event_ptr->xmotion.x_root; + wi->pick_event.xcrossing.y_root = event_ptr->xmotion.y_root; + wi->pick_event.xcrossing.mode = NotifyNormal; + wi->pick_event.xcrossing.detail = NotifyNonlinear; + wi->pick_event.xcrossing.same_screen = event_ptr->xmotion.same_screen; + wi->pick_event.xcrossing.focus = False; + wi->pick_event.xcrossing.state = event_ptr->xmotion.state; + } + else { + wi->pick_event = *event_ptr; + } + } + + /* + * If this is a recursive call (there's already a partially completed + * call pending on the stack; it's in the middle of processing a + * Leave event handler for the old current item) then just return; + * the pending call will do everything that's needed. + */ + if (wi->events_flags & REPICK_IN_PROGRESS) { + printf("PickCurrentItem recursive\n"); + return; + } + + /* + * A LeaveNotify event automatically means that there's no current + * object, so the check for closest item can be skipped. + */ + if (wi->pick_event.type != LeaveNotify) { + RadarPoint p; + + p.x = wi->pick_event.xcrossing.x-wi->border_width; + p.y = wi->pick_event.xcrossing.y-wi->border_width; + wi->top_group->class->Pick(wi->top_group, &p, NULL, wi->pick_aperture, + &wi->new_item, &wi->new_part); + } + else { + wi->new_item = RADAR_NO_ITEM; + } + + if ((wi->new_item == wi->current_item) && (wi->new_part == wi->current_part) && + !(wi->events_flags & LEFT_GRABBED_ITEM)) { + /* + * Nothing to do: the current item/part hasn't changed. + */ + return; + } + + /* + * Simulate a LeaveNotify event on the previous current item and + * an EnterNotify event on the new current item. Remove the "current" + * tag from the previous current item and place it on the new current + * item. + * Note that the test for prev_left_grabbed_item is needed to prevent + * calling leave when the grab is released. + */ + if (((wi->new_item != wi->current_item) || (wi->new_part != wi->current_part)) && + (wi->current_item != RADAR_NO_ITEM) && + wi->current_item->class->IsSensitive(wi->current_item, wi->current_part) && + !(wi->events_flags & LEFT_GRABBED_ITEM) && + !prev_left_grabbed_item) { + XEvent event; + Item item = wi->current_item; + + event = wi->pick_event; + event.type = LeaveNotify; + + /*printf("==LEAVE== %d, sensitive %d\n", wi->current_item->id, + wi->current_item->class->IsSensitive(wi->current_item, wi->current_part));*/ + /* + * If the event's detail happens to be NotifyInferior the + * binding mechanism will discard the event. To be consistent, + * always use NotifyAncestor. + */ + event.xcrossing.detail = NotifyAncestor; + wi->events_flags |= REPICK_IN_PROGRESS; + DoEvent(wi, &event); + wi->events_flags &= ~REPICK_IN_PROGRESS; + + /* + * The check on item below is needed because there could be an + * event handler for <LeaveNotify> that deletes the current item. + */ + if ((item == wi->current_item) && !button_down) { + ITEM.RemoveTag(item, current_uid); + } + /* + * Note: during DoEvent above, it's possible that + * wi->new_item got reset to NULL because the + * item was deleted. + */ + } + if (((wi->new_item != wi->current_item) || + (wi->new_part != wi->current_part)) && button_down) { + wi->events_flags |= LEFT_GRABBED_ITEM; + return; + } + + /* + * Special note: it's possible that wi->new_item == wi->current_item + * here. This can happen, for example, if LEFT_GRABBED_ITEM was set or + * if there is only a change in the part number. + */ + wi->events_flags &= ~LEFT_GRABBED_ITEM; + /* + * Added to enable DoEvent to make a special case for enter/leaves + * between fields in the same item. It may interact with + * LEFT_GRABBED_ITEM. + */ + if (wi->current_item != wi->new_item) { + wi->current_item = wi->new_item; + wi->new_item = RADAR_NO_ITEM; + } + wi->current_part = wi->new_part; + + if ((wi->current_item != RADAR_NO_ITEM) && + wi->current_item->class->IsSensitive(wi->current_item, wi->current_part)) { + XEvent event; + + /* + * Add the tag 'current' to the current item under the pointer. + */ + DoItem((Tcl_Interp *) NULL, wi->current_item, RADAR_NO_PART, current_uid); + /* + * Then emit a fake Enter event on it. + */ + /*printf("==ENTER==\n");*/ + event = wi->pick_event; + event.type = EnterNotify; + event.xcrossing.detail = NotifyAncestor; + DoEvent(wi, &event); + } +} + + +/* + *---------------------------------------------------------------------- + * + * Bind -- + * + * This procedure is invoked by the Tk dispatcher to handle + * events associated with bindings on items. + * + * Results: + * None. + * + * Side effects: + * Depends on the command invoked as part of the binding + * (if there was any). + * + * Note: + * This has been taken as is from the Tk canvas. It might not + * not be fully adequate for the purpose. But at least this + * provides two benefits: a/ It is believe to be correct and + * b/ users are accustomed to its behavior. + * + *---------------------------------------------------------------------- + */ +static void +Bind(ClientData client_data, /* Information about widget. */ + XEvent *event_ptr) /* Information about event. */ +{ + WidgetInfo *wi = (WidgetInfo *) client_data; + + Tcl_Preserve((ClientData) wi); + + /* + * This code below keeps track of the current modifier state in + * wi->state. This information is used to defer repicks of + * the current item while buttons are down. + */ + if ((event_ptr->type == ButtonPress) || (event_ptr->type == ButtonRelease)) { + int mask; + + switch (event_ptr->xbutton.button) { + case Button1: + mask = Button1Mask; + break; + case Button2: + mask = Button2Mask; + break; + case Button3: + mask = Button3Mask; + break; + case Button4: + mask = Button4Mask; + break; + case Button5: + mask = Button5Mask; + break; + default: + mask = 0; + break; + } + /* + * For button press events, repick the current item using the + * button state before the event, then process the event. For + * button release events, first process the event, then repick + * the current item using the button state *after* the event + * (the button has logically gone up before we change the + * current item). + */ + + if (event_ptr->type == ButtonPress) { + /* + * On a button press, first repick the current item using + * the button state before the event, the process the event. + */ + wi->state = event_ptr->xbutton.state; + PickCurrentItem(wi, event_ptr); + wi->state ^= mask; + DoEvent(wi, event_ptr); + } + else { + /* + * Button release: first process the event, with the button + * still considered to be down. Then repick the current + * item under the assumption that the button is no longer down. + */ + wi->state = event_ptr->xbutton.state; + DoEvent(wi, event_ptr); + event_ptr->xbutton.state ^= mask; + wi->state = event_ptr->xbutton.state; + PickCurrentItem(wi, event_ptr); + event_ptr->xbutton.state ^= mask; + } + goto done; + } + + else if ((event_ptr->type == EnterNotify) || (event_ptr->type == LeaveNotify)) { + wi->state = event_ptr->xcrossing.state; + PickCurrentItem(wi, event_ptr); + goto done; + } + + else if (event_ptr->type == MotionNotify) { + wi->state = event_ptr->xmotion.state; + PickCurrentItem(wi, event_ptr); + } + + /*printf("=event=\n");*/ + if ((wi->current_item != RADAR_NO_ITEM) && + wi->current_item->class->IsSensitive(wi->current_item, wi->current_part)) { + DoEvent(wi, event_ptr); + } + +done: + Tcl_Release((ClientData) wi); +} + + +/* + *---------------------------------------------------------------------- + * + * CmdDeleted -- + * + * This procedure is invoked when a widget command is deleted. If + * the widget isn't already in the process of being destroyed, + * this command destroys it. + * + * Results: + * None. + * + * Side effects: + * The widget is destroyed. + * + *---------------------------------------------------------------------- + */ +static void +CmdDeleted(ClientData client_data) /* Pointer to widget record for widget. */ +{ + WidgetInfo *wi = (WidgetInfo *) client_data; + + if (wi->win != NULL) { + Tk_DestroyWindow(wi->win); + wi->win = NULL; + } +} + + +/* + *---------------------------------------------------------------------- + * + * Destroy -- + * + * This procedure is invoked by Tk_EventuallyFree or Tk_Release + * to clean up the internal structure of a Radar at a safe time + * (when no-one is using it anymore). + * + * Results: + * None. + * + * Side effects: + * Everything associated with the Radar is freed up. + * + *---------------------------------------------------------------------- + */ +static void +Destroy(char *mem_ptr) /* Info about Radar widget. */ +{ + WidgetInfo *wi = (WidgetInfo *) mem_ptr; + unsigned int num; + Tcl_HashSearch search; + Tcl_HashEntry *entry; + + /*printf("Destroy begining\n");*/ + /* + * This procedure could be invoked either because the window was + * destroyed and the command was then deleted (in which case win + * is NULL) or because the command was deleted, and then this procedure + * destroys the widget. + */ + /* + * Unregister form the overlap manager. + */ +#ifdef OM + if (wi->om_group != RADAR_NO_ITEM) { + OmUnregister((void *) wi); + } +#endif + + /* Free all items. */ + ITEM.DestroyItem(wi->top_group); + + for (num = 0; num < NUM_ALPHA_STEPS; num++) { + if (wi->alpha_stipples[num] != None) { + Tk_FreeBitmap(wi->dpy, wi->alpha_stipples[num]); + } + } + + Tcl_DeleteHashTable(wi->id_table); + + RadarFree(wi->id_table); + /* + * Free the table contents before the table. + */ + entry = Tcl_FirstHashEntry(wi->tag_table, &search); + while (entry != NULL) { + RadarListFree((RadarList) Tcl_GetHashValue(entry)); + entry = Tcl_NextHashEntry(&search); + } + Tcl_DeleteHashTable(wi->tag_table); + RadarFree(wi->tag_table); + /* + * Free the table contents before the table. + */ + entry = Tcl_FirstHashEntry(wi->t_table, &search); + while (entry != NULL) { + RadarTransfoFree((RadarTransfo *) Tcl_GetHashValue(entry)); + entry = Tcl_NextHashEntry(&search); + } + Tcl_DeleteHashTable(wi->t_table); + RadarFree(wi->t_table); + + if (wi->binding_table != NULL) { + Tk_DeleteBindingTable(wi->binding_table); + } + + /* Free the tile */ + if (strlen(wi->tile_name) != 0) { + RadarFree(wi->tile_name); + } + if (wi->tile != RadarUnspecifiedImage) { + Tk_FreeImage(wi->tile); + } + + /* Free the double buffer pixmap */ + if (wi->draw_buffer) { + XFreePixmap(wi->dpy, wi->draw_buffer); + } + + Tk_FreeOptions(config_specs, (char *) wi, wi->dpy, 0); + + /* + * Should be empty by now. + */ + RadarListFree(wi->transfo_stack); + RadarListFree(wi->clip_stack); + + RadarListFree(wi->work_pts); + + RadarFree(wi); + /*printf("Destroy ending\n");*/ +} + + +/* + *---------------------------------------------------------------------- + * + * Redisplay -- + * + * This procedure redraws the contents of a Radar window. + * It is invoked as a do-when-idle handler, so it only runs + * when there's nothing else for the application to do. + * + * Results: + * None. + * + * Side effects: + * Information appears on the screen. + * + *---------------------------------------------------------------------- + */ + +static void +Redisplay(ClientData client_data) /* Information about radar. */ +{ + WidgetInfo *wi = (WidgetInfo *) client_data; + RadarBBox merge; + Tk_Window tkwin; + XRectangle r; + struct timeval start, end; + struct timezone tz; + + wi->update_pending = 0; + if (!wi->realized || !Tk_IsMapped(wi->win)) { + return; + } + + /* + * Give the overlap manager a chance to do its work. + */ +#ifdef OM + if ((wi->om_group != RADAR_NO_ITEM) && + ((GroupItem) wi->om_group)->call_om) { + RadarPoint scale={1.0,1.0}; + if (wi->om_group->transfo) { + RadarTransfoDecompose(wi->om_group->transfo, &scale, + NULL, NULL, NULL); + } + OmProcessOverlap((void *) wi, wi->width, wi->height, scale.x); + ((GroupItem) wi->om_group)->call_om = False; + } +#endif + + if (wi->monitoring) { + gettimeofday(&start, &tz); + } + + do { + /* + * Update the items. + * NOTE: See above. + */ + ITEM_P.Update(wi); + + /* + * Do enter/leave processing after the overlap manager + * has finished with the items. Do it has many times + * as needed, each round may trigger callbacks that + * result in moved items and so forth. It can even + * lead to the widget destruction, this is the reason + * for Tcl_Preserve/Tcl_Release. + */ + if (ISSET(wi->events_flags, INTERNAL_NEED_REPICK)) { + Tcl_Preserve((ClientData) wi); + CLEAR(wi->events_flags, INTERNAL_NEED_REPICK); + PickCurrentItem(wi, &wi->pick_event); + tkwin = wi->win; + Tcl_Release((ClientData) wi); + if (tkwin == NULL) { + return; + } + } + } + while (ISSET(wi->top_group->inv_flags, RADAR_COORDS_FLAG) || + ISSET(wi->top_group->inv_flags, RADAR_TRANSFO_FLAG) || + ISSET(wi->events_flags, INTERNAL_NEED_REPICK)); + + /* + * Repair the scene where it is no longer up to date, + * then merge the repaired area with the exposed area + * and send it back to the screen. + */ + 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, RadarWindowId(wi->win), wi->gc, + r.x, r.y, r.width, r.height, r.x+wi->border_width, r.y+wi->border_width); + } + /* + * Redraw the borders. + */ + if (wi->border_width > 0) { + /*printf("win size %d %d\n", Tk_Width(wi->win), Tk_Height(wi->win));*/ + Tk_Draw3DRectangle(wi->win, RadarWindowId(wi->win), + wi->bg_border, 0, 0, + Tk_Width(wi->win), Tk_Height(wi->win), + wi->border_width, wi->relief); + + } + /* + * Reset the exposed & damaged areas. + */ + ResetBBox(&wi->exposed_area); + ResetBBox(&wi->damaged_area); + + if (wi->monitoring) { + XSync(wi->dpy, 0); + gettimeofday(&end, &tz); + wi->num_updates++; + wi->last_time = (end.tv_sec-start.tv_sec)*1000 + (end.tv_usec-start.tv_usec)/1000; + wi->total_time += wi->last_time; + } +} + + +/* + *-------------------------------------------------------------------------- + * + * MapInfo stuff that should go eventually in its own file. + * + *-------------------------------------------------------------------------- + */ + +static Tcl_HashTable mapInfoTable; +static RadarBool map_info_inited = False; + +typedef struct { + ClientData client_data; + MapInfoChangeProc proc; +} MapInfoClient; + +typedef struct { + MapInfoId map_info; + RadarBool deleted; + RadarList clients; +} MapInfoMaster; + +static void +MapInfoInit() +{ + Tcl_InitHashTable(&mapInfoTable, TCL_ONE_WORD_KEYS); + + map_info_inited = True; +} + +static void +UpdateMapInfoClients(MapInfoMaster *master) +{ + int i, num; + MapInfoClient *client; + + num = RadarListSize(master->clients); + client = (MapInfoClient *) RadarListArray(master->clients); + for (i = 0; i < num; i++, client++) { + (*client->proc)(client->client_data, master->map_info); + } +} + +int +Radar_CreateMapInfo(Tcl_Interp *interp, + char *name, + MapInfoId *map_info) +{ + Tk_Uid uid = Tk_GetUid(name); + Tcl_HashEntry *entry; + int new; + MapInfoMaster *master; + + if (!map_info_inited) { + MapInfoInit(); + } + + entry = Tcl_CreateHashEntry(&mapInfoTable, uid, &new); + if (!new) { + /* + * Empty the map info if it is not. + */ + master = (MapInfoMaster *) Tcl_GetHashValue(entry); + if (master->deleted) { + master->deleted = False; + } + else { + MapInfoEmpty(master->map_info); + UpdateMapInfoClients(master); + } + } + else { + master = (MapInfoMaster *) RadarMalloc(sizeof(MapInfoMaster)); + master->map_info = MapInfoCreate(name); + master->deleted = False; + master->clients = RadarListNew(1, sizeof(MapInfoClient)); + Tcl_SetHashValue(entry, master); + } + if (map_info) { + *map_info = master->map_info; + } + return TCL_OK; +} + +int +Radar_DuplicateMapInfo(Tcl_Interp *interp, + char *name, + MapInfoId map_info) +{ + Tk_Uid uid = Tk_GetUid(name); + Tcl_HashEntry *entry; + int new; + MapInfoMaster *master; + + if (!map_info_inited) { + MapInfoInit(); + } + + entry = Tcl_CreateHashEntry(&mapInfoTable, uid, &new); + if (!new) { + Tcl_AppendResult(interp, "duplicate mapinfo \"", name, "\" already exists", NULL); + return RADAR_ERROR; + } + master = (MapInfoMaster *) RadarMalloc(sizeof(MapInfoMaster)); + master->map_info = MapInfoDuplicate(map_info); + master->deleted = False; + master->clients = RadarListNew(1, sizeof(MapInfoClient)); + Tcl_SetHashValue(entry, master); + + return TCL_OK; +} + +MapInfoMaster * +LookupMapInfoMaster(Tcl_Interp *interp, + char *name) +{ + Tk_Uid uid = Tk_GetUid(name); + Tcl_HashEntry *entry; + MapInfoMaster *master; + + if (!map_info_inited) { + MapInfoInit(); + } + + entry = Tcl_FindHashEntry(&mapInfoTable, uid); + if (entry == NULL) { + mp_error: + Tcl_AppendResult(interp, "mapinfo \"", name, "\" doesn't exist", NULL); + return NULL; + } + master = (MapInfoMaster *) Tcl_GetHashValue(entry); + if (master->deleted) { + goto mp_error; + } + return master; +} + +int +Radar_DeleteMapInfo(Tcl_Interp *interp, + char *name) +{ + MapInfoMaster *master; + Tk_Uid uid = Tk_GetUid(name); + Tcl_HashEntry *entry; + + if (!map_info_inited) { + MapInfoInit(); + } + + entry = Tcl_FindHashEntry(&mapInfoTable, uid); + if (entry == NULL) { + return RADAR_ERROR; + } + + master = (MapInfoMaster *) Tcl_GetHashValue(entry); + if (RadarListSize(master->clients) != 0) { + master->deleted = True; + MapInfoEmpty(master->map_info); + UpdateMapInfoClients(master); + } + else { + MapInfoDelete(master->map_info); + RadarListFree(master->clients); + Tcl_DeleteHashEntry(entry); + RadarFree(master); + } + + return TCL_OK; +} + +MapInfoId +Radar_GetMapInfo(Tcl_Interp *interp, + char *name, + MapInfoChangeProc proc, + ClientData client_data) +{ + MapInfoMaster *master; + MapInfoClient client; + + master = LookupMapInfoMaster(interp, name); + if (master == NULL) { + return NULL; + } + client.proc = proc; + client.client_data = client_data; + RadarListAdd(master->clients, &client, RadarListTail); + + return master->map_info; +} + +void +Radar_FreeMapInfo(MapInfoId map_info, + MapInfoChangeProc proc, + ClientData client_data) +{ + Tk_Uid uid = Tk_GetUid(MapInfoName(map_info)); + Tcl_HashEntry *entry; + MapInfoMaster *master; + MapInfoClient *client; + int num, i; + + if (!map_info_inited) { + MapInfoInit(); + } + + entry = Tcl_FindHashEntry(&mapInfoTable, uid); + if (entry == NULL) { + return; + } + master = (MapInfoMaster *) Tcl_GetHashValue(entry); + client = (MapInfoClient *) RadarListArray(master->clients); + num = RadarListSize(master->clients); + for (i = 0; i < num; i++, client++) { + if ((client->client_data == client_data) && + (client->proc == proc)) { + RadarListDelete(master->clients, i); + return; + } + } +} + +void +Radar_UpdateMapInfoClients(MapInfoId map_info) +{ + Tk_Uid uid = Tk_GetUid(MapInfoName(map_info)); + Tcl_HashEntry *entry; + MapInfoMaster *master; + + if (!map_info_inited) { + MapInfoInit(); + } + + entry = Tcl_FindHashEntry(&mapInfoTable, uid); + if (entry == NULL) { + return; + } + master = (MapInfoMaster *) Tcl_GetHashValue(entry); + UpdateMapInfoClients(master); +} + + +/* + * These arrays must be kept in sync with the MapInfoLineStyle + * and MapInfoTextStyle enums. + */ +static char *line_style_strings[] = { + "simple", + "dashed", + "dotted", + "mixed", + "marked", +}; + +static char *text_style_strings[] = { + "normal", + "underlined" +}; + +static char * +MapInfoLineStyleToString(MapInfoLineStyle line_style) +{ + return line_style_strings[line_style]; +} + +static int +MapInfoLineStyleFromString(Tcl_Interp *interp, + char *str, + MapInfoLineStyle *line_style) +{ + int i, num = sizeof(line_style_strings)/sizeof(char *); + + for (i = 0; i < num; i++) { + if (strcmp(str, line_style_strings[i]) == 0) { + *line_style = i; + return TCL_OK; + } + } + Tcl_AppendResult(interp, " incorrect mapinfo line style \"", + str,"\"", NULL); + return RADAR_ERROR; +} + +static char * +MapInfoTextStyleToString(MapInfoTextStyle text_style) +{ + return text_style_strings[text_style]; +} + +static int +MapInfoTextStyleFromString(Tcl_Interp *interp, + char *str, + MapInfoTextStyle *text_style) +{ + int i, num = sizeof(text_style_strings)/sizeof(char *); + + for (i = 0; i < num; i++) { + if (strcmp(str, text_style_strings[i]) == 0) { + *text_style = i; + return TCL_OK; + } + } + Tcl_AppendResult(interp, " incorrect mapinfo text style \"", + str,"\"", NULL); + return RADAR_ERROR; +} + +int +MapInfoCmd(ClientData client_data, + Tcl_Interp *interp, /* Current interpreter. */ + int argc, /* Number of arguments. */ + Arg *args) +{ + char c, c2; + int length, result; + MapInfoMaster *master; + char msg[INTEGER_SPACE*7]; + + if (!inited) { + InitRadar(interp); + } + + if (argc < 2) { + Tcl_AppendResult(interp, "wrong # of args: \"", + LangString(args[0]), " subcommand ?args?.\"", NULL); + return RADAR_ERROR; + } + + c = LangString(args[2])[0]; + length = strlen(LangString(args[2])); + result = TCL_OK; + + /* + * create + */ + if ((c == 'c') && (strncmp(LangString(args[2]), "create", length) == 0)) { + if (argc != 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + " name create\"", NULL); + return RADAR_ERROR; + } + if (Radar_CreateMapInfo(interp, LangString(args[1]), NULL) == RADAR_ERROR) { + return RADAR_ERROR; + } + } + + /* + * delete + */ + else if ((c == 'd') && (strncmp(LangString(args[2]), "delete", length) == 0)) { + if (argc != 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" mapInfo delete", NULL); + return RADAR_ERROR; + } + if (Radar_DeleteMapInfo(interp, LangString(args[1])) == RADAR_ERROR) { + return RADAR_ERROR; + } + } + + /* + * duplicate + */ + else if ((c == 'd') && (strncmp(LangString(args[2]), "duplicate", length) == 0)) { + if (argc != 4) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" mapInfo duplicate name", NULL); + return RADAR_ERROR; + } + master = LookupMapInfoMaster(interp, LangString(args[1])); + if (master == NULL) { + return RADAR_ERROR; + } + if (Radar_DuplicateMapInfo(interp, LangString(args[3]), master->map_info) == RADAR_ERROR) { + return RADAR_ERROR; + } + } + + /* + * add/replace + */ + else if (((c == 'a') && (strncmp(LangString(args[2]), "add", length) == 0)) || + ((c == 'r') && (strncmp(LangString(args[2]), "replace", length) == 0))) { + MapInfoLineStyle line_style; + MapInfoTextStyle text_style; + char *imsg = (c == 'a') ? "" : " index"; + int i, index, num_param=4; + int coords[6]; + + if (c != 'a') { + num_param++; + } + if (argc < num_param) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" mapInfo ", LangString(args[2]), " type", imsg, + " ?args?", NULL); + return RADAR_ERROR; + } + master = LookupMapInfoMaster(interp, LangString(args[1])); + if (master == NULL) { + return RADAR_ERROR; + } + if (c != 'a') { + if (Tcl_GetInt(interp, args[4], &index) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (index < 0) { + index = 0; + } + } + c2 = LangString(args[3])[0]; + length = strlen(LangString(args[3])); + if ((c2 == 'l') && (strncmp(LangString(args[3]), "line", length) == 0)) { + if (argc != (num_param+6)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" mapInfo ", LangString(args[2]), " line", imsg, + " style width x1 y1 x2 y2", NULL); + return RADAR_ERROR; + } + if (MapInfoLineStyleFromString(interp, LangString(args[num_param]), + &line_style) == RADAR_ERROR) { + return RADAR_ERROR; + } + for (i = 0; i < 5; i++) { + if (Tcl_GetInt(interp, args[num_param+i+1], &coords[i]) == RADAR_ERROR) { + return RADAR_ERROR; + } + } + if (coords[0] < 0) { + coords[0] = 0; + } + if (c == 'a') { + MapInfoAddLine(master->map_info, RadarListTail, NULL, line_style, + coords[0], coords[1], coords[2], coords[3], coords[4]); + } + else { + MapInfoReplaceLine(master->map_info, index, NULL, line_style, + coords[0], coords[1], coords[2], coords[3], coords[4]); + } + } + else if ((c2 == 's') && (strncmp(LangString(args[3]), "symbol", length) == 0)) { + if (argc != (num_param+3)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" mapInfo ", LangString(args[2]), " symbol", imsg, + " x y intVal", NULL); + return RADAR_ERROR; + } + for (i = 0; i < 3; i++) { + if (Tcl_GetInt(interp, args[num_param+i], &coords[i]) == RADAR_ERROR) { + return RADAR_ERROR; + } + } + if (coords[2] < 0) { + coords[2] = 0; + } + if (c == 'a') { + MapInfoAddSymbol(master->map_info, RadarListTail, NULL, coords[0], + coords[1], coords[2]); + } + else { + MapInfoReplaceSymbol(master->map_info, index, NULL, coords[0], + coords[1], coords[2]); + } + } + else if ((c2 == 't') && (strncmp(LangString(args[3]), "text", length) == 0)) { + if (argc != (num_param+5)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" mapInfo ", LangString(args[2]), " text", imsg, + " textStyle lineStyle x y string", NULL); + return RADAR_ERROR; + } + if (MapInfoTextStyleFromString(interp, LangString(args[num_param]), + &text_style) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (MapInfoLineStyleFromString(interp, LangString(args[num_param+1]), + &line_style) == RADAR_ERROR) { + return RADAR_ERROR; + } + for (i = 0; i < 2; i++) { + if (Tcl_GetInt(interp, args[num_param+i+2], &coords[i]) == RADAR_ERROR) { + return RADAR_ERROR; + } + } + if (c == 'a') { + MapInfoAddText(master->map_info, RadarListTail, NULL, text_style, + line_style, coords[0], coords[1], LangString(args[num_param+4])); + } + else { + printf("replace text ts %d ls %d %d %d %s\n", text_style, + line_style, coords[0], coords[1], LangString(args[num_param+4])); + MapInfoReplaceText(master->map_info, index, NULL, text_style, + line_style, coords[0], coords[1], LangString(args[num_param+4])); + } + } + else if ((c2 == 'a') && (strncmp(LangString(args[3]), "arc", length) == 0)) { + if (argc != (num_param+7)) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" mapInfo ", LangString(args[2]), " arc", imsg, + " style width cx cy radius start extent", NULL); + return RADAR_ERROR; + } + if (MapInfoLineStyleFromString(interp, LangString(args[num_param]), + &line_style) == RADAR_ERROR) { + return RADAR_ERROR; + } + for (i = 0; i < 6; i++) { + if (Tcl_GetInt(interp, args[num_param+i+1], &coords[i]) == RADAR_ERROR) { + return RADAR_ERROR; + } + } + if (coords[0] < 0) { + coords[0] = 0; + } + if (c == 'a') { + MapInfoAddArc(master->map_info, RadarListTail, NULL, line_style, + coords[0], coords[1], coords[2], coords[3], coords[4], + coords[5]); + } + else { + MapInfoReplaceArc(master->map_info, index, NULL, line_style, coords[0], + coords[1], coords[2], coords[3], coords[4], coords[5]); + } + } + UpdateMapInfoClients(master); + } + + /* + * count + */ + else if ((c == 'c') && (strncmp(LangString(args[2]), "count", length) == 0)) { + int count = 0; + if (argc != 4) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" mapInfo count type", NULL); + return RADAR_ERROR; + } + master = LookupMapInfoMaster(interp, LangString(args[1])); + if (master == NULL) { + return RADAR_ERROR; + } + args += 3; + argc -= 3; + c2 = LangString(args[0])[0]; + length = strlen(LangString(args[0])); + if ((c2 == 'l') && (strncmp(LangString(args[0]), "line", length) == 0)) { + count = MapInfoNumLines(master->map_info); + } + else if ((c2 == 's') && (strncmp(LangString(args[0]), "symbol", length) == 0)) { + count = MapInfoNumSymbols(master->map_info); + } + else if ((c2 == 't') && (strncmp(LangString(args[0]), "text", length) == 0)) { + count = MapInfoNumTexts(master->map_info); + } + else if ((c2 == 'a') && (strncmp(LangString(args[0]), "arc", length) == 0)) { + count = MapInfoNumArcs(master->map_info); + } + sprintf(msg, "%d", count); + Tcl_SetResult(interp, msg, TCL_VOLATILE); + } + + /* + * get + */ + else if ((c == 'g') && (strncmp(LangString(args[2]), "get", length) == 0)) { + int index; + if (argc != 5) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" mapInfo get type index", NULL); + return RADAR_ERROR; + } + master = LookupMapInfoMaster(interp, LangString(args[1])); + if (master == NULL) { + return RADAR_ERROR; + } + if (Tcl_GetInt(interp, args[4], &index) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (index < 0) { + index = 0; + } + args += 3; + argc -= 3; + c2 = LangString(args[0])[0]; + length = strlen(LangString(args[0])); + if ((c2 == 'l') && (strncmp(LangString(args[0]), "line", length) == 0)) { + MapInfoLineStyle line_style; + int line_width; + int x_from, y_from, x_to, y_to; + MapInfoGetLine(master->map_info, index, NULL, &line_style, + &line_width, &x_from, &y_from, &x_to, &y_to); + Tcl_AppendElement(interp, MapInfoLineStyleToString(line_style)); + sprintf(msg, "%d", line_width); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%d", x_from); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%d", y_from); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%d", x_to); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%d", y_to); + Tcl_AppendElement(interp, msg); + } + else if ((c2 == 's') && (strncmp(LangString(args[0]), "symbol", length) == 0)) { + int x, y; + char symbol; + MapInfoGetSymbol(master->map_info, index, NULL, &x, &y, &symbol); + sprintf(msg, "%d", x); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%d", y); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%d", symbol); + Tcl_AppendElement(interp, msg); + } + else if ((c2 == 't') && (strncmp(LangString(args[0]), "text", length) == 0)) { + int x, y; + char *text; + MapInfoTextStyle text_style; + MapInfoLineStyle line_style; + MapInfoGetText(master->map_info, index, NULL, &text_style, &line_style, + &x, &y, &text); + sprintf(msg, "%d", x); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%d", y); + Tcl_AppendElement(interp, msg); + Tcl_AppendElement(interp, MapInfoTextStyleToString(text_style)); + Tcl_AppendElement(interp, MapInfoLineStyleToString(line_style)); + Tcl_AppendElement(interp, text); + } + else if ((c2 == 'a') && (strncmp(LangString(args[0]), "arc", length) == 0)) { + MapInfoLineStyle line_style; + int line_width; + int center_x, center_y, start, extent; + unsigned int radius; + MapInfoGetArc(master->map_info, index, NULL, &line_style, &line_width, + ¢er_x, ¢er_y, &radius, &start, &extent); + Tcl_AppendElement(interp, MapInfoLineStyleToString(line_style)); + sprintf(msg, "%d", line_width); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%d", center_x); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%d", center_y); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%d", radius); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%d", start); + Tcl_AppendElement(interp, msg); + sprintf(msg, "%d", extent); + Tcl_AppendElement(interp, msg); + } + } + + /* + * remove + */ + else if ((c == 'r') && (strncmp(LangString(args[2]), "remove", length) == 0)) { + int index; + if (argc != 5) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" mapInfo remove type index", NULL); + return RADAR_ERROR; + } + master = LookupMapInfoMaster(interp, LangString(args[1])); + if (master == NULL) { + return RADAR_ERROR; + } + if (Tcl_GetInt(interp, args[4], &index) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (index < 0) { + index = 0; + } + args += 3; + argc -= 3; + c2 = LangString(args[0])[0]; + length = strlen(LangString(args[0])); + if ((c2 == 'l') && (strncmp(LangString(args[0]), "line", length) == 0)) { + MapInfoRemoveLine(master->map_info, index); + } + else if ((c2 == 's') && (strncmp(LangString(args[0]), "symbol", length) == 0)) { + MapInfoRemoveSymbol(master->map_info, index); + } + else if ((c2 == 't') && (strncmp(LangString(args[0]), "text", length) == 0)) { + MapInfoRemoveText(master->map_info, index); + } + else if ((c2 == 'a') && (strncmp(LangString(args[0]), "arc", length) == 0)) { + MapInfoRemoveArc(master->map_info, index); + } + UpdateMapInfoClients(master); + } + + /* + * scale + */ + else if ((c == 's') && (strncmp(LangString(args[2]), "scale", length) == 0)) { + double factor; + + if (argc != 4) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" mapInfo scale factor", NULL); + return RADAR_ERROR; + } + master = LookupMapInfoMaster(interp, LangString(args[1])); + if (master == NULL) { + return RADAR_ERROR; + } + if (Tcl_GetDouble(interp, args[3], &factor) == RADAR_ERROR) { + return RADAR_ERROR; + } + MapInfoScale(master->map_info, factor); + UpdateMapInfoClients(master); + } + + /* + * translate + */ + else if ((c == 't') && (strncmp(LangString(args[2]), "translate", length) == 0)) { + int x, y; + + if (argc != 5) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" mapInfo translate xAmount yAmount", NULL); + return RADAR_ERROR; + } + master = LookupMapInfoMaster(interp, LangString(args[1])); + if (master == NULL) { + return RADAR_ERROR; + } + if (Tcl_GetInt(interp, args[3], &x) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (Tcl_GetInt(interp, args[4], &y) == RADAR_ERROR) { + return RADAR_ERROR; + } + MapInfoTranslate(master->map_info, x, y); + UpdateMapInfoClients(master); + } + + else { + Tcl_AppendResult(interp, "invalid command \"", LangString(args[2]), + "\": must be create, delete, duplicate, add, count, " + "get, replace, remove, scale, translate", NULL); + return RADAR_ERROR; + } + + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * VideomapCmd -- + * + * + *---------------------------------------------------------------------- + */ +int +VideomapCmd(ClientData client_data, + Tcl_Interp *interp, /* Current interpreter. */ + int argc, /* Number of arguments. */ + Arg *args) +{ + RadarList ids; + char c; + int length; + int *id_array, id_num, i; + char num_str[INTEGER_SPACE]; + + if (!inited) { + InitRadar(interp); + } + + if (argc < 2) { + Tcl_AppendResult(interp, "wrong # args: should be \"", + LangString(args[0]), " filename\"", NULL); + return RADAR_ERROR; + } + + c = LangString(args[1])[0]; + length = strlen(LangString(args[1])); + + /* + * ids + */ + if ((c == 'i') && (strncmp(LangString(args[1]), "ids", length) == 0)) { + if (argc != 3) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + " ids filename\"", NULL); + return RADAR_ERROR; + } + ids = MapInfoVideomapIds(LangString(args[2])); + if (ids == NULL) { + Tcl_AppendResult(interp, "unable to look at videomap file \"", + LangString(args[2]), "\"", NULL); + return RADAR_ERROR; + } + id_array = (int *) RadarListArray(ids); + id_num = RadarListSize(ids); + for (i = 0; i < id_num; i++) { + sprintf(num_str, "%d", id_array[i]); + Tcl_AppendElement(interp, num_str); + } + RadarListFree(ids); + } + + /* + * load + */ + else if ((c == 'l') && (strncmp(LangString(args[1]), "load", length) == 0)) { + MapInfoId map_info; + int index; + + if (argc != 5) { + Tcl_AppendResult(interp, "wrong # args: should be \"", LangString(args[0]), + "\" load filename index mapInfo", NULL); + return RADAR_ERROR; + } + if (Tcl_GetInt(interp, args[3], &index) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (index < 0) { + index = 0; + } + if (Radar_CreateMapInfo(interp, LangString(args[4]), &map_info) == RADAR_ERROR) { + return RADAR_ERROR; + } + if (MapInfoGetVideomap(map_info, LangString(args[2]), index) == RADAR_ERROR) { + Tcl_AppendResult(interp, "unable to load videomap file \"", LangString(args[2]), ":", + LangString(args[3]), "\"", NULL); + return RADAR_ERROR; + } + Radar_UpdateMapInfoClients(map_info); + } + + return TCL_OK; +} + + +static void +InitRadar(Tcl_Interp *interp) { + unsigned int i, x, y, bit; + char name[INTEGER_SPACE + 20]; + + /* + * Add the specific bitmaps. + */ + for (i = 0; i < sizeof(SYMBOLS_bits)/(SYMBOL_WIDTH*SYMBOL_HEIGHT/8); i++) { + sprintf(name, "AtcSymbol%d", i+1); + Tk_DefineBitmap(interp, Tk_GetUid(name), + SYMBOLS_bits[i], SYMBOL_WIDTH, SYMBOL_HEIGHT); + } + + for (i = 0; i < NUM_ALPHA_STEPS; i++) { + for (y = 0; y < 4; y++) { + bitmaps[i][y] = 0; + bitmaps[i][y+4] = 0; + for (x = 0; x < 4; x++) { + /* + * Use the dither4x4 matrix to determine if this bit is on + */ + bit = (i >= dither4x4[y][x]) ? 1 : 0; + /* + * set the bit in the array used to make the X Bitmap + * mirror the pattern in x & y to make an 8x8 bitmap. + */ + if (bit) { + bitmaps[i][y] |= (1 << x); + bitmaps[i][y] |= (1 << (4 + x)); + bitmaps[i][4+y] |= (1 << x); + bitmaps[i][4+y] |= (1 << (4 + x)); + } + } + } + sprintf(name, "AlphaStipple%d", i); + Tk_DefineBitmap(interp, Tk_GetUid(name), (char *) bitmaps[i], 8, 8); + } + + /* + * Initialize the item module. + */ + ITEM_P.GlobalModuleInit(); + + all_uid = Tk_GetUid("all"); + current_uid = Tk_GetUid("current"); + + inited = True; +} + + +/* + *---------------------------------------------------------------------- + * + * Tkradar_Init -- + * + * This procedure is invoked by Tcl_AppInit in tkAppInit.c to + * initialize the widget. + * + *---------------------------------------------------------------------- + */ +int +Tkradar_Init(Tcl_Interp *interp) /* Used for error reporting. */ +{ + if (!Tk_MainWindow(interp)) { + printf("Tk main window not created"); + return RADAR_ERROR; + } + + /* + * Create additional commands + */ + if (Tcl_PkgProvide(interp, "radar", "3.0") == RADAR_ERROR) { + return RADAR_ERROR; + } + + Tcl_CreateCommand(interp, "radar", RadarCmd, + (ClientData) Tk_MainWindow(interp), (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "mapinfo", MapInfoCmd, + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + Tcl_CreateCommand(interp, "videomap", VideomapCmd, + (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); + + return TCL_OK; +} + +int +Tkradar_debug_Init(Tcl_Interp *interp) /* Used for error reporting. */ +{ + return Tkradar_Init(interp); +} |