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