aboutsummaryrefslogtreecommitdiff
path: root/generic/tkZinc.c
diff options
context:
space:
mode:
authorlecoanet2000-01-12 15:20:49 +0000
committerlecoanet2000-01-12 15:20:49 +0000
commit78e19e2b6a425f478f0f52332934ff366bc11798 (patch)
treed04d04368be6dac08a2ba3e2934099f32daab58d /generic/tkZinc.c
parent94bb0261c52d727cb4ea538a83ba9c9cbd59b241 (diff)
downloadtkzinc-78e19e2b6a425f478f0f52332934ff366bc11798.zip
tkzinc-78e19e2b6a425f478f0f52332934ff366bc11798.tar.gz
tkzinc-78e19e2b6a425f478f0f52332934ff366bc11798.tar.bz2
tkzinc-78e19e2b6a425f478f0f52332934ff366bc11798.tar.xz
Adaptation des ent�tes.
Suppression du code sur les lignes doubles dans l'interface MapInfo. Modification des ckalloc en RadarMalloc. Creation d'une commande 'monitor' de monitoring des perfs X11. Corrections de bugs dans RadarSearchWithTagOrId et RadarNextWithTagOrId. Adaptation pour pouvoir d�marrer dans un groupe autre que le top group. Les commandes find et addtag adoptent un param�tre suppl�mentaire optionnel d�signant ce groupe. Extension des possibilit�s de la commande 'coords' et s�paration du code dans une fonction 'Coords'. On peut maintenant ajouter/ retirer des points � certains items (curve). Ajout de la commande 'clone'.
Diffstat (limited to 'generic/tkZinc.c')
-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);
+}