/* * 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 #include #include #include #include #include #include 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; 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); #ifdef OM wi->om_group_id = 0; wi->om_group = wi->top_group; OmRegister((void *) wi, SendTrackToOm, SetLabelAngleFromOm, QueryLabelPosition); #endif 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 tagOrId ?args?", NULL); goto error; } num = RadarItemsWithTagOrId(wi, LangString(args[2]), &item, &items); if (num == 0) { goto error; } argc -= 3; args += 3; for (i = 0; i < num; i++) { item = ITEM.CloneItem(items[i]); ITEM.InsertItem(item, items[i]->parent, RADAR_NO_ITEM, True); if (ITEM.ConfigureItem(item, -1, argc, args, False) == RADAR_ERROR) { goto error; } 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 . */ 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 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); }