/* * tkZinc.c -- Zinc 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 "tkZinc.h" #include "MapInfo.h" #include "patchlvl.h" #include "OverlapMan.h" #include "Track.h" #include "Transfo.h" #include "Image.h" #include "perfos.h" #ifdef GPC #include "gpc/gpc.h" #endif #include #include #include #include #include #include #ifdef SHM #include #include #endif #if 0 typedef struct TagSearch { WidgetInfo *wi; Tk_Uid tag; GroupItem group; Item current; Item previous; /* Needed to detect changes in the linked * list between calls. */ ZnList item_stack; ZnBool over; } TagSearch; #endif typedef struct _TagSearchExpr { struct _TagSearchExpr *next; /* for linked lists of expressions - used in bindings */ Tk_Uid uid; /* the uid of the whole expression */ Tk_Uid *uids; /* expresion compiled to an array of uids */ int allocated; /* available space for array of uids */ int length; /* length of expression */ int index; /* current position in expression evaluation */ int match; /* this expression matches event's item's tags*/ } TagSearchExpr; #define INTEGER_SPACE 30 #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 ZnBool inited = False; static Tk_Uid all_uid; static Tk_Uid current_uid; static Tk_Uid and_uid; static Tk_Uid or_uid; static Tk_Uid xor_uid; static Tk_Uid paren_uid; static Tk_Uid end_paren_uid; static Tk_Uid neg_paren_uid; static Tk_Uid tag_val_uid; static Tk_Uid neg_tag_val_uid; /* * 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", "#c3c3c3", 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", "Foreground", "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", "7c", Tk_Offset(WidgetInfo, opt_height), 0}, {TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground", "HighlightBackground", "#c3c3c3", Tk_Offset(WidgetInfo, highlight_bg_color), 0}, {TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor", "Black", Tk_Offset(WidgetInfo, highlight_color), 0}, {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness", "HighlightThickness", "2", Tk_Offset(WidgetInfo, highlight_width), 0}, {TK_CONFIG_COLOR, "-insertbackground", "insertBackground", "Foreground", "Black", Tk_Offset(WidgetInfo, text_info.insert_color), 0}, {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime", "300", Tk_Offset(WidgetInfo, insert_off_time), 0}, {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime", "600", Tk_Offset(WidgetInfo, insert_on_time), 0}, {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth", "2", Tk_Offset(WidgetInfo, text_info.insert_width), 0}, {TK_CONFIG_BOOLEAN, "-localrender", "localRender", "LocalRender", "0", Tk_Offset(WidgetInfo, local_render), 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_BOOLEAN, "-reshape", "reshape", "Reshape", "1", Tk_Offset(WidgetInfo, reshape), 0}, {TK_CONFIG_COLOR, "-selectbackground", "selectBackground", "Foreground", "#a0a0a0", Tk_Offset(WidgetInfo, text_info.sel_color), 0}, {TK_CONFIG_INT, "-speedvectorlength", "speedVectorLength", "SpeedVectorLength", "3", Tk_Offset(WidgetInfo, speed_vector_length), 0}, {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus", NULL, Tk_Offset(WidgetInfo, take_focus), TK_CONFIG_NULL_OK}, {TK_CONFIG_STRING, "-tile", "tile", "Tile", "", Tk_Offset(WidgetInfo, tile_name), 0}, {TK_CONFIG_BOOLEAN, "-trackmanagehistory", "trackManageHistory", "TrackManageHistory", "1", Tk_Offset(WidgetInfo, track_manage_history), 0}, {TK_CONFIG_INT, "-trackmanagedhistorysize", "trackManagedHistorySize", "TrackManagedHistorySize", "6", Tk_Offset(WidgetInfo, track_managed_history_size), 0}, {TK_CONFIG_PIXELS, "-width", "width", "Width", "10c", 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_INT, "-lightangle", "lightAngle", "LightAngle", "120", Tk_Offset(WidgetInfo, light_angle), 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_SPEC 5 #define HEIGHT_SPEC 6 #define HIGHLIGHT_BACK_COLOR_SPEC 7 #define HIGHLIGHT_COLOR_SPEC 8 #define HIGHLIGHT_THICKNESS_SPEC 9 #define INSERT_COLOR_SPEC 10 #define INSERT_OFF_TIME_SPEC 11 #define INSERT_ON_TIME_SPEC 12 #define INSERT_WIDTH_SPEC 13 #define LOCAL_RENDER_SPEC 14 #define MAP_DISTANCE_SYMBOL_SPEC 15 #define MAP_TEXT_FONT_SPEC 16 #define OVERLAP_MANAGER_SPEC 17 #define PICK_APERTURE_SPEC 18 #define RELIEF_SPEC 19 #define RESHAPE_SPEC 20 #define SELECT_COLOR_SPEC 21 #define SPEED_VECTOR_LENGTH_SPEC 22 #define TAKE_FOCUS_SPEC 23 #define TILE_SPEC 24 #define MANAGE_HISTORY_SPEC 25 #define MANAGED_HISTORY_SIZE_SPEC 26 #define WIDTH_SPEC 27 #define BBOXES_SPEC 28 #define BBOXES_COLOR_SPEC 29 #define LIGHT_ANGLE_SPEC 30 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 FetchSelection _ANSI_ARGS_((ClientData clientData, int offset, char *buffer, int maxBytes)); static void SelectTo _ANSI_ARGS_((Item item, int index)); static int WidgetObjCmd _ANSI_ARGS_((ClientData client_data, Tcl_Interp *, int argc, Tcl_Obj *CONST args[])); static int Configure _ANSI_ARGS_((Tcl_Interp *interp, WidgetInfo *wi, int argc, Tcl_Obj *CONST args[], int flags)); static void Redisplay _ANSI_ARGS_((ClientData client_data)); static void Destroy _ANSI_ARGS_((char *mem_ptr)); static void InitZinc _ANSI_ARGS_((Tcl_Interp *interp)); static void Focus _ANSI_ARGS_((WidgetInfo *wi, ZnBool got_focus)); #ifdef PTK Tcl_Obj * NewLongObj(long val) { Tcl_Obj *obj = Tcl_NewIntObj(0); Tcl_SetLongObj(obj, val); return obj; } #endif #ifdef PTK Tcl_Obj * NewBooleanObj(ZnBool val) { Tcl_Obj *obj = Tcl_NewIntObj(0); Tcl_SetBooleanObj(obj, val); return obj; } #endif #ifdef PTK Tcl_Obj * NewDoubleObj(ZnReal val) { Tcl_Obj *obj = Tcl_NewIntObj(0); Tcl_SetDoubleObj(obj, val); return obj; } #endif Tcl_Obj * NewStringObj(char *str) { return Tcl_NewStringObj(str, strlen(str)); } void SetStringObj(Tcl_Obj *o, char *str) { return Tcl_SetStringObj(o, str, strlen(str)); } #if 1 char * Tcl_GetString(Tcl_Obj *obj) { return Tcl_GetStringFromObj(obj, NULL); } #endif /* *---------------------------------------------------------------------- * * ZnGetAlphaStipple -- * Need to be handled per screen/dpy toolkit wide, not on a * widget basis. * *---------------------------------------------------------------------- */ Pixmap ZnGetAlphaStipple(WidgetInfo *wi, unsigned char val) { if (val >= 255) return None; else return wi->alpha_stipples[(int) (val / 16)]; } /* *---------------------------------------------------------------------- * * ZnGetInactiveStipple -- * *---------------------------------------------------------------------- */ Pixmap ZnGetInactiveStipple(WidgetInfo *wi) { return ZnGetAlphaStipple(wi, 128); } /* *---------------------------------------------------------------------- * * ZnNeedRedisplay -- * *---------------------------------------------------------------------- */ void ZnNeedRedisplay(WidgetInfo *wi) { if (!wi->update_pending) { /*printf("scheduling an update\n");*/ Tcl_DoWhenIdle(Redisplay, (ClientData) wi); wi->update_pending = 1; } } /* *---------------------------------------------------------------------- * * CreateSharedImageBuffer -- * *---------------------------------------------------------------------- */ #ifdef SHM void CreateSharedImageBuffer(WidgetInfo *wi) { /* * Allocate a shared image to be used as the back buffer. * The sequence is extracted from gdk image. */ wi->draw_buffer_im = XShmCreateImage(wi->dpy, Tk_Visual(wi->win), Tk_Depth(wi->win), ZPixmap, NULL, &wi->x_shm_info, wi->width, wi->height); if (wi->draw_buffer_im == NULL) { ZnWarning("XShmCreateImage failed, unable to use local rendering"); wi->local_render = False; } else { wi->x_shm_info.shmid = shmget(IPC_PRIVATE, wi->draw_buffer_im->bytes_per_line * wi->draw_buffer_im->height, IPC_CREAT | 0777); if (wi->x_shm_info.shmid == -1) { XDestroyImage(wi->draw_buffer_im); wi->draw_buffer_im = NULL; ZnWarning("shmget failed, unable to use local rendering"); wi->local_render = False; } else { wi->x_shm_info.readOnly = False; wi->x_shm_info.shmaddr = shmat(wi->x_shm_info.shmid, 0, 0); wi->draw_buffer_im->data = wi->x_shm_info.shmaddr; if (wi->x_shm_info.shmaddr == (char *) -1) { shmctl(wi->x_shm_info.shmid, IPC_RMID, 0); XDestroyImage(wi->draw_buffer_im); wi->draw_buffer_im = NULL; ZnWarning("shmat failed, unable to use local rendering"); wi->local_render = False; } else { wi->buf.buf = ZnMalloc(wi->draw_buffer_im->bytes_per_line * wi->draw_buffer_im->height * 3); wi->buf.rowstride = wi->draw_buffer_im->bytes_per_line * 3; wi->buf.ox = wi->buf.oy = 0; wi->buf.cx = wi->width; wi->buf.cy = wi->height; XShmAttach(wi->dpy, &wi->x_shm_info); XSync(wi->dpy, False); /* We mark the segment as destroyed so that when * the last process detaches, it will be deleted. * There is a small possibility of leaking if * we die in XShmAttach. In theory, a signal handler * could be set up. */ shmctl(wi->x_shm_info.shmid, IPC_RMID, 0); } } } } #endif /* *---------------------------------------------------------------------- * * ZincObjCmd -- * * This procedure is invoked to process the "zinc" Tcl * command. It creates a new "zinc" widget. * *---------------------------------------------------------------------- */ int ZincObjCmd(ClientData client_data, /* Main window associated with * interpreter. */ Tcl_Interp *interp, /* Current interpreter. */ int argc, /* Number of arguments. */ Tcl_Obj *CONST args[]) /* Argument strings. */ { Tk_Window top_w = (Tk_Window) client_data; WidgetInfo *wi; Tk_Window tkwin; unsigned int num; int major_op, minor_op, first_err, first_evt; if (!inited) { InitZinc(interp); } if (argc == 1) { Tcl_AppendResult(interp, ZINCVERSION, NULL); return TCL_OK; } if (argc < 2) { Tcl_WrongNumArgs(interp, 1, args, "pathName ?options?"); return ZN_ERROR; } tkwin = Tk_CreateWindowFromPath(interp, top_w, Tcl_GetString(args[1]), NULL); if (tkwin == NULL) { return ZN_ERROR; } Tk_SetClass(tkwin, "Zinc"); /* * Allocate and initialize the widget record. */ wi = (WidgetInfo *) ZnMalloc(sizeof(WidgetInfo)); wi->win = tkwin; wi->interp = interp; wi->dpy = Tk_Display(tkwin); wi->screen = Tk_Screen(tkwin); wi->has_x_shm = False; #ifdef SHM if (XQueryExtension(wi->dpy, "MIT-SHM", &major_op, &first_evt, &first_err)) { ZnBool pixmaps; if (XShmQueryVersion(wi->dpy, &major_op, &minor_op, &pixmaps) == True) { wi->has_x_shm = True; } } #endif #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->local_render = False; wi->real_top = None; #ifdef PTK wi->cmd = Lang_CreateWidget(interp, tkwin, (Tcl_CmdProc *) WidgetObjCmd, (ClientData) wi, CmdDeleted); #else wi->cmd = Tcl_CreateObjCommand(interp, Tk_PathName(tkwin), WidgetObjCmd, (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->light_angle = 120; wi->border_width = 0; wi->relief = TK_RELIEF_FLAT; wi->opt_width = None; wi->opt_height = None; wi->font = 0; wi->map_text_font = 0; wi->map_distance_symbol = None; wi->cursor = None; wi->hot_item = ZN_NO_ITEM; wi->hot_prev = ZN_NO_ITEM; wi->track_manage_history = False; wi->track_managed_history_size = 0; wi->speed_vector_length = 0; wi->tile = ZnUnspecifiedImage; wi->tile_name = NULL; wi->id_table = (Tcl_HashTable *) ZnMalloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(wi->id_table, TCL_ONE_WORD_KEYS); wi->t_table = (Tcl_HashTable *) ZnMalloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(wi->t_table, TCL_ONE_WORD_KEYS); wi->obj_id = 1; wi->num_items = 0; wi->top_group = ITEM_P.CreateItem(wi, ZnGroup, 0, NULL); #ifdef OM wi->om_group_id = 0; wi->om_group = wi->top_group; OmRegister((void *) wi, SendTrackToOm, SetLabelAngleFromOm, QueryLabelPosition); #endif wi->gc = 0; wi->draw_buffer = 0; wi->draw_buffer_im = 0; wi->events_flags = 0; wi->pick_aperture = 0; wi->new_item = wi->current_item = ZN_NO_ITEM; wi->new_part = wi->current_part = ZN_NO_PART; wi->monitoring = False; wi->total_draw_chrono = NewChrono("Total draw time"); wi->this_draw_chrono = NewChrono("Last draw time"); wi->damaged_area_w = wi->damaged_area_h = 0; wi->work_item_list = NULL; wi->work_pts = ZnListNew(8, sizeof(ZnPoint)); wi->work_xpts = ZnListNew(8, sizeof(XPoint)); /* * Text management init. */ wi->text_info.sel_color = NULL; wi->text_info.sel_item = ZN_NO_ITEM; wi->text_info.sel_first = -1; wi->text_info.sel_last = -1; wi->text_info.anchor_item = ZN_NO_ITEM; wi->text_info.sel_anchor = 0; wi->text_info.insert_color = NULL; wi->text_info.insert_width = 0; wi->text_info.focus_item = ZN_NO_ITEM; wi->text_info.got_focus = False; wi->text_info.cursor_on = False; wi->insert_on_time = 0; wi->insert_off_time = 0; wi->blink_handler = NULL; wi->take_focus = NULL; wi->highlight_width = 0; wi->highlight_color = NULL; wi->highlight_bg_color = NULL; ITEM_P.InitClipStack(wi); ITEM_P.InitTransformStack(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); Tk_CreateSelHandler(tkwin, XA_PRIMARY, XA_STRING, FetchSelection, (ClientData) wi, XA_STRING); if (Configure(interp, wi, argc-2, args+2, 0) != TCL_OK) { Tk_DestroyWindow(tkwin); return ZN_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/image. */ if (wi->local_render) { #ifdef SHM CreateSharedImageBuffer(wi); #endif } if (!wi->local_render) { wi->draw_buffer = XCreatePixmap(wi->dpy, RootWindowOfScreen(wi->screen), wi->width, wi->height, Tk_Depth(wi->win)); } #ifdef PTK Tcl_SetObjResult(interp, LangWidgetArg(interp, tkwin)); #else Tcl_SetObjResult(interp, NewStringObj(Tk_PathName(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 == ZN_NO_PART) { return item; } return (ClientData) (((char *) item)-part); } /* *-------------------------------------------------------------- * * All tag search procs below are lifted from tkCanvas.c, then * modified to match our needs. * *-------------------------------------------------------------- */ /* *-------------------------------------------------------------- * * TagSearchExprInit -- * * This procedure allocates and initializes one * TagSearchExpr struct. * *-------------------------------------------------------------- */ static void TagSearchExprInit(TagSearchExpr **expr_var) { TagSearchExpr* expr = *expr_var; if (! expr) { expr = (TagSearchExpr *) ZnMalloc(sizeof(TagSearchExpr)); expr->allocated = 0; expr->uids = NULL; expr->next = NULL; } expr->uid = NULL; expr->index = 0; expr->length = 0; *expr_var = expr; } /* *-------------------------------------------------------------- * * TagSearchExprDestroy -- * * This procedure destroys one TagSearchExpr structure. * *-------------------------------------------------------------- */ static void TagSearchExprDestroy(TagSearchExpr *expr) { if (expr) { if (expr->uids) { ZnFree(expr->uids); } ZnFree(expr); } } /* *-------------------------------------------------------------- * * TagSearchScanExpr -- * * This recursive procedure is called to scan a tag expression * and compile it into an array of Tk_Uids. * * Results: * The return value indicates if the tagOrId expression * was successfully scanned (syntax). * The information at *search is initialized * such that a call to ZnTagSearchFirst, followed by * successive calls to ZnTagSearchNext will return items * that match tag. * * Side effects: * *-------------------------------------------------------------- */ static int TagSearchScanExpr(Tcl_Interp *interp, /* Current interpreter. */ TagSearch *search, /* Search data */ TagSearchExpr *expr) /* Compiled expression result */ { int looking_for_tag; /* When true, scanner expects next char(s) * to be a tag, else operand expected */ int found_tag; /* One or more tags found */ int found_endquote; /* For quoted tag string parsing */ int negate_result; /* Pending negation of next tag value */ char *tag; /* tag from tag expression string */ char c; negate_result = 0; found_tag = 0; looking_for_tag = 1; while (search->tag_index < search->tag_len) { c = search->tag[search->tag_index++]; if (expr->allocated == expr->index) { expr->allocated += 15; if (expr->uids) { expr->uids = (Tk_Uid *) ZnRealloc((char *) expr->uids, expr->allocated * sizeof(Tk_Uid)); } else { expr->uids = (Tk_Uid *) ZnMalloc(expr->allocated * sizeof(Tk_Uid)); } } if (looking_for_tag) { switch (c) { case ' ': /* ignore unquoted whitespace */ case '\t': case '\n': case '\r': break; case '!': /* negate next tag or subexpr */ if (looking_for_tag > 1) { Tcl_AppendResult(interp, "Too many '!' in tag search expression", (char *) NULL); return ZN_ERROR; } looking_for_tag++; negate_result = 1; break; case '(': /* scan (negated) subexpr recursively */ if (negate_result) { expr->uids[expr->index++] = neg_paren_uid; negate_result = 0; } else { expr->uids[expr->index++] = paren_uid; } if (TagSearchScanExpr(interp, search, expr) != TCL_OK) { /* Result string should be already set * by nested call to tag_expr_scan() */ return ZN_ERROR; } looking_for_tag = 0; found_tag = 1; break; case '"': /* quoted tag string */ if (negate_result) { expr->uids[expr->index++] = neg_tag_val_uid; negate_result = 0; } else { expr->uids[expr->index++] = tag_val_uid; } tag = search->rewrite_buf; found_endquote = 0; while (search->tag_index < search->tag_len) { c = search->tag[search->tag_index++]; if (c == '\\') { c = search->tag[search->tag_index++]; } if (c == '"') { found_endquote = 1; break; } *tag++ = c; } if (! found_endquote) { Tcl_AppendResult(interp, "Missing endquote in tag search expression", (char *) NULL); return ZN_ERROR; } if (! (tag - search->rewrite_buf)) { Tcl_AppendResult(interp, "Null quoted tag string in tag search expression", (char *) NULL); return ZN_ERROR; } *tag++ = '\0'; expr->uids[expr->index++] = Tk_GetUid(search->rewrite_buf); looking_for_tag = 0; found_tag = 1; break; case '&': /* illegal chars when looking for tag */ case '|': case '^': case ')': Tcl_AppendResult(interp, "Unexpected operator in tag search expression", (char *) NULL); return ZN_ERROR; default: /* unquoted tag string */ if (negate_result) { expr->uids[expr->index++] = neg_tag_val_uid; negate_result = 0; } else { expr->uids[expr->index++] = tag_val_uid; } tag = search->rewrite_buf; *tag++ = c; /* copy rest of tag, including any embedded whitespace */ while (search->tag_index < search->tag_len) { c = search->tag[search->tag_index]; if ((c == '!') || (c == '&') || (c == '|') || (c == '^') || (c == '(') || (c == ')') || (c == '"')) { break; } *tag++ = c; search->tag_index++; } /* remove trailing whitespace */ while (1) { c = *--tag; /* there must have been one non-whitespace char, * so this will terminate */ if ((c != ' ') && (c != '\t') && (c != '\n') && (c != '\r')) { break; } } *++tag = '\0'; expr->uids[expr->index++] = Tk_GetUid(search->rewrite_buf); looking_for_tag = 0; found_tag = 1; } } else { /* ! looking_for_tag */ switch (c) { case ' ' : /* ignore whitespace */ case '\t' : case '\n' : case '\r' : break; case '&' : /* AND operator */ c = search->tag[search->tag_index++]; if (c != '&') { Tcl_AppendResult(interp, "Singleton '&' in tag search expression", (char *) NULL); return ZN_ERROR; } expr->uids[expr->index++] = and_uid; looking_for_tag = 1; break; case '|' : /* OR operator */ c = search->tag[search->tag_index++]; if (c != '|') { Tcl_AppendResult(interp, "Singleton '|' in tag search expression", (char *) NULL); return ZN_ERROR; } expr->uids[expr->index++] = or_uid; looking_for_tag = 1; break; case '^' : /* XOR operator */ expr->uids[expr->index++] = xor_uid; looking_for_tag = 1; break; case ')' : /* end subexpression */ expr->uids[expr->index++] = end_paren_uid; goto breakwhile; default : /* syntax error */ Tcl_AppendResult(interp, "Invalid boolean operator in tag search expression", (char *) NULL); return ZN_ERROR; } } } breakwhile: if (found_tag && ! looking_for_tag) { return ZN_OK; } Tcl_AppendResult(interp, "Missing tag in tag search expression", (char *) NULL); return ZN_ERROR; } /* *-------------------------------------------------------------- * * TagSearchEvalExpr -- * * This recursive procedure is called to eval a tag expression. * * Results: * The return value indicates if the tagOrId expression * successfully matched the tags of the current item. * * Side effects: * *-------------------------------------------------------------- */ static int TagSearchEvalExpr(TagSearchExpr *expr, /* Search expression */ Item item) /* Item being test for match */ { int looking_for_tag; /* When true, scanner expects next char(s) * to be a tag, else operand expected */ int negate_result; /* Pending negation of next tag value */ Tk_Uid uid; Tk_Uid *tags; int count; int result=0; /* Value of expr so far */ int paren_depth; negate_result = 0; looking_for_tag = 1; while (expr->index < expr->length) { uid = expr->uids[expr->index++]; if (looking_for_tag) { if (uid == tag_val_uid) { /* * assert(expr->index < expr->length); */ uid = expr->uids[expr->index++]; result = 0; /* * set result 1 if tag is found in item's tags */ if (item->tags) { tags = (Tk_Uid *) ZnListArray(item->tags); count = ZnListSize(item->tags); for (; count > 0; tags++, count--) { if (*tags == uid) { result = 1; break; } } } } else if (uid == neg_tag_val_uid) { negate_result = ! negate_result; /* * assert(expr->index < expr->length); */ uid = expr->uids[expr->index++]; result = 0; /* * set result 1 if tag is found in item's tags */ if (item->tags) { tags = (Tk_Uid *) ZnListArray(item->tags); count = ZnListSize(item->tags); for (; count > 0; tags++, count--) { if (*tags == uid) { result = 1; break; } } } } else if (uid == paren_uid) { /* * evaluate subexpressions with recursion */ result = TagSearchEvalExpr(expr, item); } else if (uid == neg_paren_uid) { negate_result = ! negate_result; /* * evaluate subexpressions with recursion */ result = TagSearchEvalExpr(expr, item); /* * } else { * assert(0); */ } if (negate_result) { result = ! result; negate_result = 0; } looking_for_tag = 0; } else { /* ! looking_for_tag */ if (((uid == and_uid) && (!result)) || ((uid == or_uid) && result)) { /* * short circuit expression evaluation * * if result before && is 0, or result before || is 1, then * the expression is decided and no further evaluation is needed. */ paren_depth = 0; while (expr->index < expr->length) { uid = expr->uids[expr->index++]; if ((uid == tag_val_uid) || (uid == neg_tag_val_uid)) { expr->index++; continue; } if ((uid == paren_uid) || (uid == neg_paren_uid)) { paren_depth++; continue; } if (uid == end_paren_uid) { paren_depth--; if (paren_depth < 0) { break; } } } return result; } else if (uid == xor_uid) { /* * if the previous result was 1 then negate the next result. */ negate_result = result; } else if (uid == end_paren_uid) { return result; /* * } else { * assert(0); */ } looking_for_tag = 1; } } /* * assert(! looking_for_tag); */ return result; } /* *-------------------------------------------------------------- * * ZnTagSearchScan -- * * This procedure is called to initiate an enumeration of * all items in a given zinc that contain a tag that matches * the tagOrId expression. * * Results: * The return value indicates if the tagOrId expression * was successfully scanned (syntax). * The information at *search is initialized such that a * call to ZnTagSearchFirst, followed by successive calls * to ZnTagSearchNext will return items that match tag. * * Side effects: * search is linked into a list of searches in progress * in zinc, so that elements can safely be deleted while * the search is in progress. * *-------------------------------------------------------------- */ static int ZnTagSearchScan(WidgetInfo *wi, Tcl_Obj *tag_obj, /* Object giving tag value, NULL * is the same as 'all'. */ TagSearch **search_var) /* Record describing tag search; * will be initialized here. */ { char *tag; int i; TagSearch *search; if (tag_obj) { tag = Tcl_GetString(tag_obj); } else { tag = all_uid; } /* * Initialize the search. */ if (*search_var) { search = *search_var; } else { /* Allocate primary search struct on first call */ *search_var = search = (TagSearch *) ZnMalloc(sizeof(TagSearch)); search->expr = NULL; /* Allocate buffer for rewritten tags (after de-escaping) */ search->rewrite_buf_alloc = 100; search->rewrite_buf = ZnMalloc(search->rewrite_buf_alloc); search->item_stack = ZnListNew(16, sizeof(Item)); } TagSearchExprInit(&(search->expr)); /* How long is the tagOrId ? */ search->tag_len = strlen(tag); /* Make sure there is enough buffer to hold rewritten tags */ if ((unsigned int)search->tag_len >= search->rewrite_buf_alloc) { search->rewrite_buf_alloc = search->tag_len + 100; search->rewrite_buf = ZnRealloc(search->rewrite_buf, search->rewrite_buf_alloc); } /* Initialize search */ search->wi = wi; search->over = False; search->type = 0; search->group = (GroupItem) wi->top_group; search->recursive = True; ZnListEmpty(search->item_stack); /* * Find the first matching item in one of several ways. If the tag * is a number then it selects the single item with the matching * identifier. */ if (search->tag_len && isdigit(*tag)) { char *end; search->id = strtoul(tag, &end, 0); if (*end == 0) { search->type = 1; return ZN_OK; } } /* * For all other tags and tag expressions convert to a UID. * This UID is kept forever, but this should be thought of * as a cache rather than as a memory leak. */ search->expr->uid = Tk_GetUid(tag); /* short circuit impossible searches for null tags */ if (search->tag_len == 0) { return TCL_OK; } /* * Pre-scan tag for at least one unquoted "&&" "||" "^" "!" * if not found then use string as simple tag */ for (i = 0; i < search->tag_len; i++) { if (tag[i] == '"') { i++; for ( ; i < search->tag_len; i++) { if (tag[i] == '\\') { i++; continue; } if (tag[i] == '"') { break; } } } else { if (((tag[i] == '&') && (tag[i+1] == '&')) || ((tag[i] == '|') && (tag[i+1] == '|')) || (tag[i] == '^') || (tag[i] == '!')) { search->type = 4; break; } } } search->tag = tag; search->tag_index = 0; if (search->type == 4) { /* * an operator was found in the prescan, so * now compile the tag expression into array of Tk_Uid * flagging any syntax errors found */ if (TagSearchScanExpr(wi->interp, search, search->expr) != ZN_OK) { /* Syntax error in tag expression */ /* Result message set by TagSearchScanExpr */ return ZN_ERROR; } search->expr->length = search->expr->index; } else { if (search->expr->uid == all_uid) { /* * All items match. */ search->type = 2; } else { /* * Optimized single-tag search */ search->type = 3; } } return ZN_OK; } /* *-------------------------------------------------------------- * * ZnTagSearchFirst -- * * This procedure is called to get the first item * item that matches a preestablished search predicate * that was set by TagSearchScan. * * Results: * The return value is a pointer to the first item, or NULL * if there is no such item. The information at *search * is updated such that successive calls to ZnTagSearchNext * will return successive items. * * Side effects: * *search is linked into a list of searches in progress * in zinc, so that elements can safely be deleted while * the search is in progress. * *-------------------------------------------------------------- */ static Item ZnTagSearchFirst(TagSearch *search, /* Record describing tag search */ Item group, /* Start group */ ZnBool recursive) /* Does the search walk down the * tree ? */ { Item item, previous; Tk_Uid uid, *tags; int count; /* short circuit impossible searches for null tags */ if (search->tag_len == 0) { return ZN_NO_ITEM; } search->group = (GroupItem) group; search->recursive = recursive; /* * Find the first matching item in one of several ways. If the tag * is a number then it selects the single item with the matching * identifier. In this case see if the item being requested is the * hot item, in which case the search can be skipped. */ if (search->type == 1) { Tcl_HashEntry *entry; item = search->wi->hot_item; previous = search->wi->hot_prev; if ((item == ZN_NO_ITEM) || (item->id != search->id) || (previous == ZN_NO_ITEM) || (previous->next != item)) { entry = Tcl_FindHashEntry(search->wi->id_table, (char *) search->id); if (entry != NULL) { item = (Item) Tcl_GetHashValue(entry); previous = item->previous; } else { previous = item = ZN_NO_ITEM; } } search->previous = previous; search->over = True; search->wi->hot_item = item; search->wi->hot_prev = previous; return item; } if (search->type == 2) { /* * All items match. */ search->previous = ZN_NO_ITEM; search->current = (Item) search->group->head; return search->current; } uid = search->expr->uid; item = (Item) search->group->head; previous = ZN_NO_ITEM; do { while (item != ZN_NO_ITEM) { if (search->type == 3) { /* * Optimized single-tag search */ if (item->tags) { tags = (Tk_Uid *) ZnListArray(item->tags); count = ZnListSize(item->tags); for (; count > 0; tags++, count--) { if (*tags == uid) { search->previous = previous; search->current = item; return item; } } } } else { /* * Type = 4. Search for an item matching * the tag expression. */ search->expr->index = 0; if (TagSearchEvalExpr(search->expr, item)) { search->previous = previous; search->current = item; return item; } } if ((item->class == ZnGroup) && (search->recursive)) { /* * Explore the hierarchy depth first using the item stack * to save the current node. */ /*printf("ZnTagSearchFirst diving for tag '%s', detph %d\n", search->tag, ZnListSize(search->item_stack)/2);*/ search->group = (GroupItem) item; previous = item; if (item == group) { item = ZN_NO_ITEM; } else { item = item->next; } ZnListAdd(search->item_stack, &previous, ZnListTail); ZnListAdd(search->item_stack, &item, ZnListTail); previous = ZN_NO_ITEM; item = search->group->head; } else { previous = item; item = item->next; } } /* * Continue search on higher group level. */ /*printf("ZnTagSearchFirst backup for tag, detph %d\n", ZnListSize(search->item_stack)/2);*/ while ((item == ZN_NO_ITEM) && ZnListSize(search->item_stack)) { item = *(Item *) ZnListAt(search->item_stack, ZnListTail); ZnListDelete(search->item_stack, ZnListTail); previous = *(Item *) ZnListAt(search->item_stack, ZnListTail); ZnListDelete(search->item_stack, ZnListTail); } if (item != ZN_NO_ITEM) { search->group = (GroupItem) item->parent; } } while (item != ZN_NO_ITEM); search->previous = previous; search->over = True; return ZN_NO_ITEM; } /* *-------------------------------------------------------------- * * ZnTagSearchNext -- * * This procedure returns successive items that match a given * tag; it should be called only after ZnTagSearchFirst has * been used to begin a search. * * Results: * The return value is a pointer to the next item that matches * the tag expr specified to TagSearchScan, or NULL if no such * item exists. *search is updated so that the next call * to this procedure will return the next item. * * Side effects: * None. * *-------------------------------------------------------------- */ static Item ZnTagSearchNext(TagSearch *search) /* Record describing search in progress. */ { Item item, previous; Tk_Uid uid, *tags; int count; if (search->over) { return ZN_NO_ITEM; } /* * Find next item in list (this may not actually be a suitable * one to return), and return if there are no items left. */ previous = search->previous; if (previous == ZN_NO_ITEM) { item = search->group->head; } else { item = previous->next; } if (item != search->current) { /* * The structure of the list has changed. Probably the * previously-returned item was removed from the list. * In this case, don't advance previous; just return * its new successor (i.e. do nothing here). */ } else if ((item->class == ZnGroup) && (search->recursive)) { /* * Explore the hierarchy depth first using the item stack * to save the current node. */ search->group = (GroupItem) item; previous = item; item = item->next; /*printf("ZnTagSearchNext diving for all, pushing %d\n", item?item->id:0);*/ ZnListAdd(search->item_stack, &previous, ZnListTail); ZnListAdd(search->item_stack, &item, ZnListTail); previous = ZN_NO_ITEM; item = search->group->head; } else { previous = item; item = previous->next; } if (item == ZN_NO_ITEM) { while ((item == ZN_NO_ITEM) && ZnListSize(search->item_stack)) { /* * End of list at this level, back up one level. */ item = *(Item *) ZnListAt(search->item_stack, ZnListTail); ZnListDelete(search->item_stack, ZnListTail); previous = *(Item *) ZnListAt(search->item_stack, ZnListTail); ZnListDelete(search->item_stack, ZnListTail); } if (item != ZN_NO_ITEM) { search->group = (GroupItem) item->parent; /*printf("ZnTagSearchNext popping %d, previous %d, next %d\n", item->id, (item->previous)?item->previous->id:0, (item->next)?item->next->id:0);*/ } else { /* * Or finish the search if at top. */ search->over = True; return ZN_NO_ITEM; } } if (search->type == 2) { /* * All items match. */ search->previous = previous; search->current = item; return item; } uid = search->expr->uid; do { while (item != ZN_NO_ITEM) { if (search->type == 3) { /* * Optimized single-tag search */ if (item->tags) { tags = (Tk_Uid *) ZnListArray(item->tags); count = ZnListSize(item->tags); for ( ; count > 0; tags++, count--) { if (*tags == uid) { search->previous = previous; search->current = item; return item; } } } } else { /* * Else.... evaluate tag expression */ search->expr->index = 0; if (TagSearchEvalExpr(search->expr, item)) { search->previous = previous; search->current = item; return item; } } if ((item->class == ZnGroup) && (search->recursive)) { /* * Explore the hierarchy depth first using the item stack * to save the current node. */ /*printf("ZnTagSearchNext diving for tag, depth %d\n", ZnListSize(search->item_stack)/2);*/ search->group = (GroupItem) item; previous = item; item = item->next; ZnListAdd(search->item_stack, &previous, ZnListTail); ZnListAdd(search->item_stack, &item, ZnListTail); previous = ZN_NO_ITEM; item = search->group->head; } else { previous = item; item = item->next; } } /*printf("ZnTagSearchNext backup for tag, depth %d\n", ZnListSize(search->item_stack)/2);*/ while ((item == ZN_NO_ITEM) && ZnListSize(search->item_stack)) { item = *(Item *) ZnListAt(search->item_stack, ZnListTail); ZnListDelete(search->item_stack, ZnListTail); previous = *(Item *) ZnListAt(search->item_stack, ZnListTail); ZnListDelete(search->item_stack, ZnListTail); } if (item != ZN_NO_ITEM) { search->group = (GroupItem) item->parent; } } while (item != ZN_NO_ITEM); /* * Out of fuel. */ search->previous = previous; search->over = True; return ZN_NO_ITEM; } /* *-------------------------------------------------------------- * * ZnTagSearchDestroy -- * * This procedure destroys any dynamic structures that * may have been allocated by TagSearchScan. * *-------------------------------------------------------------- */ void ZnTagSearchDestroy(TagSearch *search) /* Record describing tag search */ { if (search) { TagSearchExprDestroy(search->expr); ZnListFree(search->item_stack); ZnFree(search->rewrite_buf); ZnFree(search); } } /* *---------------------------------------------------------------------- * * ZnItemWithTagOrId -- * * Return the first item matching the given tag or id. The * function returns the item in 'item' and the operation * status as the function's value. * *---------------------------------------------------------------------- */ int ZnItemWithTagOrId(WidgetInfo *wi, Tcl_Obj *tag_or_id, Item group, ZnBool recursive, Item *item, TagSearch **search_var) { if (ZnTagSearchScan(wi, tag_or_id, search_var) != ZN_OK) { return ZN_ERROR; } *item = ZnTagSearchFirst(*search_var, group, recursive); return ZN_OK; } /* *---------------------------------------------------------------------- * * 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) { Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), NewLongObj(item->id)); if (part != ZN_NO_PART) { Tcl_ListObjAppendElement(interp, Tcl_GetObjResult(interp), Tcl_NewIntObj(part)); } } else { /*printf("Adding tag %s to item %d\n", tag_uid, item->id);*/ ITEM.AddTag(item, tag_uid); } } /* *---------------------------------------------------------------------- * * FindArea -- * Search the items that are enclosed or overlapping a given * area of the widget. 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 ZN_ERROR is returned. * *---------------------------------------------------------------------- */ static int FindArea(WidgetInfo *wi, Tcl_Obj *CONST args[], Tk_Uid tag_uid, ZnBool enclosed) { ZnPos pos; ZnBBox area; if (Tcl_GetDoubleFromObj(wi->interp, args[0], &area.orig.x) == ZN_ERROR) { return ZN_ERROR; } if (Tcl_GetDoubleFromObj(wi->interp, args[1], &area.orig.y) == ZN_ERROR) { return ZN_ERROR; } if (Tcl_GetDoubleFromObj(wi->interp, args[2], &area.corner.x) == ZN_ERROR) { return ZN_ERROR; } if (Tcl_GetDoubleFromObj(wi->interp, args[3], &area.corner.y) == ZN_ERROR) { return ZN_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->inset; area.orig.y -= wi->inset; area.corner.x -= wi->inset; area.corner.y -= wi->inset; wi->top_group->class->ToArea(wi->top_group, &area, tag_uid, enclosed, False); return ZN_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(WidgetInfo *wi, int argc, Tcl_Obj *CONST args[], Tcl_Obj *tag, /* NULL to search or tag to add tag. */ int first, /* First arg to process in args */ TagSearch **search_var) { Tk_Uid tag_uid = NULL; ZnBool recursive = True; int index, result; Item item, group = wi->top_group; static char *search_cmd_strings[] = { "above", "all", "atpriority", "below", "closest", "enclosed", "overlapping", "withtag", "withtype", NULL }; enum search_cmds { ZN_S_ABOVE, ZN_S_ALL, ZN_S_ATPRIORITY, ZN_S_BELOW, ZN_S_CLOSEST, ZN_S_ENCLOSED, ZN_S_OVERLAPPING, ZN_S_WITHTAG, ZN_S_WITHTYPE }; if (Tcl_GetIndexFromObj(wi->interp, args[first], search_cmd_strings, "search command", 0, &index) != ZN_OK) { return ZN_ERROR; } if (tag) { tag_uid = Tk_GetUid(Tcl_GetString(tag)); } switch((enum search_cmds) index) { /* * above */ case ZN_S_ABOVE: { if ((argc < first+2) || (argc > first+4)) { Tcl_WrongNumArgs(wi->interp, first+1, args, "tagOrId ?inGroup? ?recursive?"); return ZN_ERROR; } if (argc == first+4) { if (Tcl_GetBooleanFromObj(wi->interp, args[first+3], &recursive) != ZN_OK) { return ZN_ERROR; } } if (argc > first+2) { result = ZnItemWithTagOrId(wi, args[first+2], wi->top_group, True, &group, search_var); if ((result == ZN_ERROR) || (group == ZN_NO_ITEM) || (group->class != ZnGroup)) { return ZN_ERROR; } } result = ZnItemWithTagOrId(wi, args[first+1], group, recursive, &item, search_var); if ((result == ZN_OK) && (item != ZN_NO_ITEM) && (item->previous != ZN_NO_ITEM)) { DoItem(wi->interp, item->previous, ZN_NO_PART, tag_uid); } } break; /* * all */ case ZN_S_ALL: { if ((argc < first+1) || (argc > first+3)) { Tcl_WrongNumArgs(wi->interp, first+1, args, "?inGroup? ?recursive?"); return ZN_ERROR; } if (argc == first+3) { if (Tcl_GetBooleanFromObj(wi->interp, args[first+2], &recursive) != ZN_OK) { return ZN_ERROR; } } if (argc > first+1) { result = ZnItemWithTagOrId(wi, args[first+1], wi->top_group, True, &group, search_var); if ((result == ZN_ERROR) || (group == ZN_NO_ITEM) || (group->class != ZnGroup)) { return ZN_ERROR; } } /* * Go through the item list and collect all item ids. They * are sorted from most visible to least visible. */ if (ZnTagSearchScan(wi, NULL, search_var) == ZN_ERROR) { return ZN_ERROR; } for (item = ZnTagSearchFirst(*search_var, group, recursive); item != ZN_NO_ITEM; item = ZnTagSearchNext(*search_var)) { DoItem(wi->interp, item, ZN_NO_PART, tag_uid); } } break; /* * atpriority */ case ZN_S_ATPRIORITY: { int pri; if ((argc < first+2) || (argc > first+4)) { Tcl_WrongNumArgs(wi->interp, first+1, args, "pri ?inGroup? ?recursive?"); return ZN_ERROR; } if (argc == first+4) { if (Tcl_GetBooleanFromObj(wi->interp, args[first+3], &recursive) != ZN_OK) { return ZN_ERROR; } } if (Tcl_GetIntFromObj(wi->interp, args[first+1], &pri) == ZN_ERROR) { return ZN_ERROR; } if (argc > first+2) { result = ZnItemWithTagOrId(wi, args[first+2], wi->top_group, True, &group, search_var); if ((result == ZN_ERROR) || (group == ZN_NO_ITEM) || (group->class != ZnGroup)) { return ZN_ERROR; } } /* * Go through the item table and collect all items with * the given priority. */ if (ZnTagSearchScan(wi, NULL, search_var) == ZN_ERROR) { return ZN_ERROR; } for (item = ZnTagSearchFirst(*search_var, group, recursive); item != ZN_NO_ITEM; item = ZnTagSearchNext(*search_var)) { if (item->priority == pri) { DoItem(wi->interp, item, ZN_NO_PART, tag_uid); } } } break; /* * below */ case ZN_S_BELOW: { Item next; if ((argc < first+2) || (argc > first+4)) { Tcl_WrongNumArgs(wi->interp, first+1, args, "tagOrId $inGroup? ?recursive?"); return ZN_ERROR; } if (argc == first+4) { if (Tcl_GetBooleanFromObj(wi->interp, args[first+3], &recursive) != ZN_OK) { return ZN_ERROR; } } if (argc > first+2) { result = ZnItemWithTagOrId(wi, args[first+2], wi->top_group, True, &group, search_var); if ((result == ZN_ERROR) || (group == ZN_NO_ITEM) || (group->class != ZnGroup)) { return ZN_ERROR; } } item = ZN_NO_ITEM; if (ZnTagSearchScan(wi, args[first+1], search_var) == ZN_ERROR) { return ZN_ERROR; } for (next = ZnTagSearchFirst(*search_var, group, recursive); next != ZN_NO_ITEM; next = ZnTagSearchNext(*search_var)) { item = next; } if ((item != ZN_NO_ITEM) && (item->next != ZN_NO_ITEM)) { DoItem(wi->interp, item->next, ZN_NO_PART, tag_uid); } } break; /* * closest */ case ZN_S_CLOSEST: { int halo = 1; ZnPoint p; int part = ZN_NO_PART; Item start_item = ZN_NO_ITEM; if ((argc < first+3) || (argc > first+5)) { Tcl_WrongNumArgs(wi->interp, first+1, args, "x y ?halo? ?start?"); return ZN_ERROR; } if (Tcl_GetDoubleFromObj(wi->interp, args[first+1], &p.x) == ZN_ERROR) { return ZN_ERROR; } if (Tcl_GetDoubleFromObj(wi->interp, args[first+2], &p.y) == ZN_ERROR) { return ZN_ERROR; } p.x -= wi->inset; p.y -= wi->inset; if (argc > first+3) { if (Tcl_GetIntFromObj(wi->interp, args[first+3], &halo) == ZN_ERROR) { return ZN_ERROR; } if (halo < 0) { halo = 0; } } if (argc > (first+4)) { result = ZnItemWithTagOrId(wi, args[first+4], wi->top_group, True, &start_item, search_var); if ((result == ZN_OK) && (start_item != ZN_NO_ITEM)) { start_item = start_item->next; } } /* * We always start the search at the top group to use the * transform and clip machinery of the group item. The items * are not required to cache the device coords, etc. So we need * to setup the correct context before calling the Pick method * for each item. */ wi->top_group->class->Pick(wi->top_group, &p, start_item, halo, &item, &part); if (item != ZN_NO_ITEM) { DoItem(wi->interp, item, part, tag_uid); /*printf("first %d %d\n", item->id, part);*/ return TCL_OK; } } break; /* * enclosed */ case ZN_S_ENCLOSED: { if (argc != first+5) { Tcl_WrongNumArgs(wi->interp, first+1, args, "x1 y1 x2 y2"); return ZN_ERROR; } return FindArea(wi, args+first+1, tag_uid, True); } break; /* * overlapping */ case ZN_S_OVERLAPPING: { if (argc != first+5) { Tcl_WrongNumArgs(wi->interp, first+1, args, "x1 y1 x2 y2"); return ZN_ERROR; } return FindArea(wi, args+first+1, tag_uid, False); } break; /* * withtag */ case ZN_S_WITHTAG: { if ((argc < first+2) || (argc > first+4)) { Tcl_WrongNumArgs(wi->interp, first+1, args, "tagOrId ?inGroup? ?recursive?"); return ZN_ERROR; } if (argc == first+4) { if (Tcl_GetBooleanFromObj(wi->interp, args[first+3], &recursive) != ZN_OK) { return ZN_ERROR; } } if (argc > first+2) { result = ZnItemWithTagOrId(wi, args[first+2], wi->top_group, True, &group, search_var); if ((result == ZN_ERROR) || (group == ZN_NO_ITEM) || (group->class != ZnGroup)) { return ZN_ERROR; } } if (ZnTagSearchScan(wi, args[first+1], search_var) == ZN_ERROR) { return ZN_ERROR; } for (item = ZnTagSearchFirst(*search_var, group, recursive); item != ZN_NO_ITEM; item = ZnTagSearchNext(*search_var)) { DoItem(wi->interp, item, ZN_NO_PART, tag_uid); } } break; /* * withtype */ case ZN_S_WITHTYPE: { ItemClass cls; if ((argc < first+2) || (argc > first+4)) { Tcl_WrongNumArgs(wi->interp, first+1, args, "itemType ?inGroup? ?recursive?"); return ZN_ERROR; } cls = (ItemClass) ITEM_P.LookupItemClass(Tcl_GetString(args[first+1])); if (!cls) { Tcl_AppendResult(wi->interp, "unknown item type \"", Tcl_GetString(args[first+1]), "\"", NULL); return ZN_ERROR; } if (argc == first+4) { if (Tcl_GetBooleanFromObj(wi->interp, args[first+3], &recursive) != ZN_OK) { return ZN_ERROR; } } if (argc > first+2) { result = ZnItemWithTagOrId(wi, args[first+2], wi->top_group, True, &group, search_var); if ((result == ZN_ERROR) || (group == ZN_NO_ITEM) || (group->class != ZnGroup)) { return ZN_ERROR; } } /* * Go through the item table and collect all items with * the given item type. */ if (ZnTagSearchScan(wi, NULL, search_var) == ZN_ERROR) { return ZN_ERROR; } for (item = ZnTagSearchFirst(*search_var, group, recursive); item != ZN_NO_ITEM; item = ZnTagSearchNext(*search_var)) { if (item->class == cls) { DoItem(wi->interp, item, ZN_NO_PART, tag_uid); } } } break; } return TCL_OK; } /* *---------------------------------------------------------------------- * * ParseCoordList -- * *---------------------------------------------------------------------- */ int ParseCoordList(WidgetInfo *wi, Tcl_Obj *arg, ZnPoint **pts, int *num_pts) { Tcl_Obj **elems; int i, result, num_elems; ZnPoint *p; result = Tcl_ListObjGetElements(wi->interp, arg, &num_elems, &elems); if ((result == ZN_ERROR) || ((num_elems%2) != 0)) { coord_error: Tcl_AppendResult(wi->interp, " malformed coord list", NULL); return ZN_ERROR; } *num_pts = num_elems/2; ZnListAssertSize(wi->work_pts, *num_pts); *pts = p = (ZnPoint *) ZnListArray(wi->work_pts); for (i = 0; i < num_elems; i += 2, p++) { if (Tcl_GetDoubleFromObj(wi->interp, elems[i], &p->x) == ZN_ERROR) { goto coord_error; } if (Tcl_GetDoubleFromObj(wi->interp, elems[i+1], &p->y) == ZN_ERROR) { goto coord_error; } } return ZN_OK; } /* *---------------------------------------------------------------------- * * Contour -- * *---------------------------------------------------------------------- */ #ifdef GPC static int Contour(WidgetInfo *wi, int argc, Tcl_Obj *CONST args[], TagSearch **search_var) { ZnPoint *points; ZnPoint p[4], xp[4]; Item item, shape; int cmd, num_points, result, i; ZnBool simple=False; ZnPoly poly; int index; ZnTransfo t, inv; ZnContour *contours; static char *op_strings[] = { "diff", "inter", "union", "xor", NULL }; int ops[] = { GPC_DIFF, GPC_INT, GPC_UNION, GPC_XOR }; result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, &item, search_var); if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)){ return ZN_ERROR; } if (!item->class->Contour) { Tcl_AppendResult(wi->interp, "class: \"", item->class->name, "\" doesn't handle the contour method", NULL); return ZN_ERROR; } if (Tcl_GetIndexFromObj(wi->interp, args[3], op_strings, "polygon operator", 0, &index) != ZN_OK) { return ZN_ERROR; } cmd = ops[index]; result = ZnItemWithTagOrId(wi, args[4], wi->top_group, True, &shape, search_var); if ((result == ZN_ERROR) || (shape == ZN_NO_ITEM)) { Tcl_ResetResult(wi->interp); if (ParseCoordList(wi, args[4], &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } POLY_CONTOUR1(&poly, points, num_points); } else { /* * If something has changed in the geometry we need to * update or the shape will be erroneous. */ ITEM_P.Update(wi); if (!shape->class->GetClipVertices) { Tcl_AppendResult(wi->interp, "class: \"", shape->class->name, "\" can't give a polygonal shape", NULL); return ZN_ERROR; } simple = shape->class->GetClipVertices(shape, &poly); if (poly.num_contours == 0) { return ZN_OK; } /* * Compute the tranform to map the device points * into the coordinate space of item. */ ITEM.GetItemTransform(item, &t); ZnTransfoInvert(&t, &inv); /* * Make a new transformed poly. */ if (simple) { p[0] = poly.contours[0].points[0]; p[2] = poly.contours[0].points[1]; p[1].x = p[2].x; p[1].y = p[0].y; p[3].x = p[0].x; p[3].y = p[2].y; ZnTransformPoints(&inv, p, xp, 4); poly.contours[0].points = xp; poly.contours[0].num_points = 4; } else { if (poly.num_contours != 1) { contours = poly.contours; poly.contours = (ZnContour *) ZnMalloc(poly.num_contours*sizeof(ZnContour)); } else { contours = poly.contours; poly.contours = &poly.contour1; } for (i = 0; i < poly.num_contours; i++) { points = contours[i].points; num_points = contours[i].num_points; poly.contours[i].num_points = num_points; poly.contours[i].points = (ZnPoint *) ZnMalloc(num_points*sizeof(ZnPoint)); ZnTransformPoints(&inv, points, poly.contours[i].points, num_points); } } } item->class->Contour(item, cmd, &poly); if (!simple) { POLY_FREE(&poly); } return ZN_OK; } #endif /* *---------------------------------------------------------------------- * * Coords -- * *---------------------------------------------------------------------- */ static int Coords(WidgetInfo *wi, int argc, Tcl_Obj *CONST args[], TagSearch **search_var) { ZnPoint *points; Item item; int num_points, result, i; int cmd = COORDS_READ; long index, contour = 0; char *str; Tcl_Obj *l; result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, &item, search_var); if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { return ZN_ERROR; } num_points = 0; /* printf(" coords: argc=%d, item %d class: %s\n", argc, item->id, item->class->name);*/ if (argc == 3) { /* Get all coords of default contour (0). */ if (item->class->Coords(item, 0, 0, COORDS_READ_ALL, &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } coords_read: /*printf(" coords: read %d points, first is %g@%g\n", num_points, points->x, points->y);*/ l = Tcl_GetObjResult(wi->interp); for (i = 0; i < num_points; i++, points++) { Tcl_ListObjAppendElement(wi->interp, l, NewDoubleObj(points->x)); Tcl_ListObjAppendElement(wi->interp, l, NewDoubleObj(points->y)); } return ZN_OK; } /* * See if it is an ADD or REMOVE op. */ i = 3; str = Tcl_GetString(args[3]); if ((str[0] == 'a') && (strcmp(str, "add") == 0)) { if ((argc < 5) || (argc > 7)) { Tcl_WrongNumArgs(wi->interp, 1, args, "coords tagOrId add ?contour? ?index? coordList"); return ZN_ERROR; } cmd = COORDS_ADD; i++; } else if ((str[0] == 'r') && (strcmp(str, "remove") == 0)) { if ((argc != 5) && (argc != 6)) { Tcl_WrongNumArgs(wi->interp, 1, args, "coords tagOrId remove ?contour? index"); return ZN_ERROR; } cmd = COORDS_REMOVE; i++; } /* * Try to see if the next param is a vertex index, * a contour index or a coord list. */ /*printf(" coords: arg %d is %s\n", i, Tcl_GetString(args[i]));*/ if (Tcl_GetLongFromObj(wi->interp, args[i], &index) != ZN_OK) { if (((argc == 5) && (cmd != COORDS_ADD) && (cmd != COORDS_REMOVE)) || (argc == 6) || (argc == 7)) { Tcl_AppendResult(wi->interp, " incorrect contour index \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } else if ((argc == 5) && (cmd != COORDS_ADD)) { Tcl_AppendResult(wi->interp, " incorrect coord index \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } else if (ParseCoordList(wi, args[i], &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } if (cmd == COORDS_ADD) { /* Append coords at end of default contour (0). */ if (item->class->Coords(item, 0, 0, COORDS_ADD_LAST, &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } } else { /* Set all coords of default contour (0). */ if (item->class->Coords(item, 0, 0, COORDS_REPLACE_ALL, &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } } return ZN_OK; } contour = index; if (argc == 4) { /* Get all coords of contour. */ if (item->class->Coords(item, contour, 0, COORDS_READ_ALL, &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } goto coords_read; } else if ((argc == 5) && (cmd == COORDS_REMOVE)) { /* Remove coord at index in default contour (0). */ if (item->class->Coords(item, 0, index, COORDS_REMOVE, &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } return ZN_OK; } /* * Try to see if the next param is a vertex index or a coord list. */ i++; /*printf(" coords: arg %d is %s\n", i, Tcl_GetString(args[i]));*/ if (Tcl_GetLongFromObj(wi->interp, args[i], &index) != ZN_OK) { if ((argc == 7) || ((argc == 6) && (cmd != COORDS_ADD))) { Tcl_AppendResult(wi->interp, " incorrect coord index \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } else if (ParseCoordList(wi, args[i], &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } if (cmd == COORDS_ADD) { /* Append coords at end of contour. */ if (item->class->Coords(item, contour, 0, COORDS_ADD_LAST, &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } } else { /* Set all coords of contour. */ if (item->class->Coords(item, contour, 0, COORDS_REPLACE_ALL, &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } } return ZN_OK; } if (argc == 5) { /* Get coord of contour at index. */ if (item->class->Coords(item, contour, index, COORDS_READ, &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } goto coords_read; } else if ((argc == 6) && (cmd == COORDS_REMOVE)) { /* Remove coord of contour at index. */ if (item->class->Coords(item, contour, index, COORDS_REMOVE, &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } return ZN_OK; } /* Set a single coord or add coords at index in contour. */ if (ParseCoordList(wi, args[i+1], &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } if (argc == 6) { num_points = 1; cmd = COORDS_REPLACE; } if (item->class->Coords(item, contour, index, cmd, &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } return ZN_OK; } /* *---------------------------------------------------------------------- * * WidgetObjCmd -- * * 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 WidgetObjCmd(ClientData client_data, /* Information about the widget. */ Tcl_Interp *interp, /* Current interpreter. */ int argc, /* Number of arguments. */ Tcl_Obj *CONST args[]) /* Argument strings. */ { WidgetInfo *wi = (WidgetInfo *) client_data; int result, cmd_index, index; Item item, item2; int num = 0, i, j; char *end, *str; ZnTransfo *t = NULL; Tcl_Obj *l, *lobjs[10]; TagSearch *search_var = NULL; Tcl_HashEntry *entry; static char *sub_cmd_strings[] = { "add", "addtag", "anchorxy", "bbox", "becomes", "bind", "cget", "chggroup", "clone", "configure", "contour", "coords", "currentpart", "cursor", "dchars", "dtag", "find", "fit", "focus", "gettags", "group", "hasanchors", "hasfields", "hastag", "index", "insert", "itemcget", "itemconfigure", "lower", "monitor", "numparts", "postscript", "raise", "remove", "rotate", "scale", "select", "smooth", "tapply", "tdelete", "transform", "translate", "treset", "trestore", "tsave", "type", "vertexat", NULL }; enum sub_cmds { ZN_W_ADD, ZN_W_ADDTAG, ZN_W_ANCHORXY, ZN_W_BBOX, ZN_W_BECOMES, ZN_W_BIND, ZN_W_CGET, ZN_W_CHGGROUP, ZN_W_CLONE, ZN_W_CONFIGURE, ZN_W_CONTOUR, ZN_W_COORDS, ZN_W_CURRENTPART, ZN_W_CURSOR, ZN_W_DCHARS, ZN_W_DTAG, ZN_W_FIND, ZN_W_FIT, ZN_W_FOCUS, ZN_W_GETTAGS, ZN_W_GROUP, ZN_W_HASANCHORS, ZN_W_HASFIELDS, ZN_W_HASTAG, ZN_W_INDEX, ZN_W_INSERT, ZN_W_ITEMCGET, ZN_W_ITEMCONFIGURE, ZN_W_LOWER, ZN_W_MONITOR, ZN_W_NUMPARTS, ZN_W_POSTSCRIPT, ZN_W_RAISE, ZN_W_REMOVE, ZN_W_ROTATE, ZN_W_SCALE, ZN_W_SELECT, ZN_W_SMOOTH, ZN_W_TAPPLY, ZN_W_TDELETE, ZN_W_TRANSFORM, ZN_W_TRANSLATE, ZN_W_TRESET, ZN_W_TRESTORE, ZN_W_TSAVE, ZN_W_TYPE, ZN_W_VERTEX_AT, }; static char *sel_cmd_strings[] = { "adjust", "clear", "from", "item", "to", NULL }; enum sel_cmds { ZN_SEL_ADJUST, ZN_SEL_CLEAR, ZN_SEL_FROM, ZN_SEL_ITEM, ZN_SEL_TO }; if (argc < 2) { Tcl_WrongNumArgs(interp, 1, args, "subcommand ?args?"); return ZN_ERROR; } Tcl_Preserve((ClientData) wi); if (Tcl_GetIndexFromObj(interp, args[1], sub_cmd_strings, "subcommand", 0, &cmd_index) != ZN_OK) { goto error; } result = ZN_OK; /*printf("executing command \"%s\", argc=%d\n", Tcl_GetString(args[1]), argc);*/ switch((enum sub_cmds) cmd_index) { /* * add */ case ZN_W_ADD: { Item group; ItemClass cls; if (argc == 2) { /* create subcommand alone, return the list of known * object types. */ ItemClass *classes = ZnListArray(ITEM_P.ItemClassList()); num = ZnListSize(ITEM_P.ItemClassList()); l = Tcl_GetObjResult(interp); for (i = 0; i < num; i++) { Tcl_ListObjAppendElement(interp, l, NewStringObj(classes[i]->name)); } goto done; } if ((argc < 4)) { add_err: Tcl_WrongNumArgs(interp, 1, args, "add type group ?args?"); goto error; } str = Tcl_GetString(args[2]); if (str[0] == '-') { goto add_err; } cls = ITEM_P.LookupItemClass(str); if (!cls) { Tcl_AppendResult(interp, "unknown item type \"", str, "\"", NULL); goto error; } result = ZnItemWithTagOrId(wi, args[3], wi->top_group, True, &group, &search_var); if ((result == ZN_ERROR) || (group == ZN_NO_ITEM) || (group->class != ZnGroup)) { Tcl_AppendResult(interp, ", group item expected, got \"", Tcl_GetString(args[3]), "\"", NULL); goto error; } argc -= 4; args += 4; item = ITEM_P.CreateItem(wi, cls, &argc, &args); if (item == ZN_NO_ITEM) { goto error; } ITEM.InsertItem(item, group, ZN_NO_ITEM, True); if (ITEM.ConfigureItem(item, -1, argc, args, True) == ZN_ERROR) { goto error; } wi->hot_item = item; wi->hot_prev = item->previous; l = NewLongObj(item->id); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } break; /* * addtag */ case ZN_W_ADDTAG: { if (argc < 4) { Tcl_WrongNumArgs(interp, 1, args, "addtag tag searchCommand ?arg arg ...?"); goto error; } result = FindItems(wi, argc, args, args[2], 3, &search_var); } break; /* * anchorxy */ case ZN_W_ANCHORXY: { Tk_Anchor anchor; ZnPoint p; if (argc != 4) { Tcl_WrongNumArgs(interp, 1, args, "anchorxy tagOrId anchor"); goto error; } result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, &item, &search_var); if ((result == ZN_ERROR) || (item == ZN_NO_ITEM) || !item->class->has_anchors) { Tcl_AppendResult(interp, "unknown item or doesn't support anchors \"", Tcl_GetString(args[2]), NULL); goto error; } if (Tk_GetAnchor(interp, Tcl_GetString(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); l = Tcl_GetObjResult(wi->interp); Tcl_ListObjAppendElement(wi->interp, l, NewDoubleObj(p.x)); Tcl_ListObjAppendElement(wi->interp, l, NewDoubleObj(p.y)); } break; /* * becomes */ case ZN_W_BECOMES: { Tcl_AppendResult(interp, "Command not yet implemented", NULL); goto error; } break; /* * bbox */ case ZN_W_BBOX: { ZnBBox bbox; int i; if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "bbox tagOrId ?tagOrId ...?"); goto error; } argc -= 2; args += 2; ITEM_P.Update(wi); ResetBBox(&bbox); for (i = 0; i < argc; i++) { if (ZnTagSearchScan(wi, args[i], &search_var) == ZN_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { AddBBoxToBBox(&bbox, &item->item_bounding_box); } } if (!IsEmptyBBox(&bbox)) { lobjs[0] = NewDoubleObj(bbox.orig.x+wi->inset); lobjs[1] = NewDoubleObj(bbox.orig.y+wi->inset); lobjs[2] = NewDoubleObj(bbox.corner.x+wi->inset); lobjs[3] = NewDoubleObj(bbox.corner.y+wi->inset); l = Tcl_NewListObj(4, lobjs); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } } break; /* * bind */ case ZN_W_BIND: { ClientData elem = 0; int part = ZN_NO_PART; if ((argc < 3) || (argc > 5)) { Tcl_WrongNumArgs(interp, 1, args, "bind tagOrId ?sequence? ?command?"); goto error; } /* * Test if (a) an itemid or (b) an itemid:part or (c) a tag is provided. */ str = Tcl_GetString(args[2]); if (isdigit(str[0])) { int id; id = strtoul(str, &end, 0); if ((*end != 0) && (*end != ':')) { goto bind_a_tag; } entry = Tcl_FindHashEntry(wi->id_table, (char *) id); if (entry == NULL) { Tcl_AppendResult(interp, "item \"", str, "\" doesn't exist", NULL); goto error; } item = elem = Tcl_GetHashValue(entry); if (!elem) { goto error; } if (*end == ':') { if (item->class->Part) { l = NewStringObj(end+1); if (item->class->Part(item, &l, &part) == ZN_ERROR) { goto error; } elem = EncodeItemPart(item, part); } else { Tcl_AppendResult(interp, "item \"", str, "\" doesn't have parts", NULL); goto error; } } /*printf("adding element 0x%X to the binding table of item 0x%X\n", elem, item);*/ } else { bind_a_tag: elem = (ClientData) Tk_GetUid(str); } /* * 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; #ifdef PTK str = LangString(args[4]); #else str = Tcl_GetString(args[4]); #endif if (str[0] == 0) { result = Tk_DeleteBinding(interp, wi->binding_table, elem, Tcl_GetString(args[3])); goto done; } #ifdef PTK mask = Tk_CreateBinding(interp, wi->binding_table, elem, Tcl_GetString(args[3]), args[4], append); #else if (str[0] == '+') { str++; append = 1; } mask = Tk_CreateBinding(interp, wi->binding_table, elem, Tcl_GetString(args[3]), str, append); #endif 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, Tcl_GetString(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) { #ifdef PTK Tcl_Obj *command; #else char *command; #endif command = Tk_GetBinding(interp, wi->binding_table, elem, Tcl_GetString(args[3])); if (command == NULL) { goto error; } #ifdef PTK Tcl_SetObjResult(interp, command); Tcl_DecrRefCount(command); #else Tcl_SetObjResult(interp, NewStringObj(command)); #endif } else { Tk_GetAllBindings(interp, wi->binding_table, elem); } } break; /* * cget */ case ZN_W_CGET: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "cget option"); goto error; } result = Tk_ConfigureValue(interp, wi->win, config_specs, (char *) wi, Tcl_GetString(args[2]), 0); } break; /* * chggroup */ case ZN_W_CHGGROUP: { Item grp; int adjust=0; ZnTransfo inv, t, t2, *this_one=NULL; if ((argc != 4) && (argc != 5)) { Tcl_WrongNumArgs(interp, 1, args, "chggroup tagOrIg group ?adjustTransform?"); goto error; } result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, &item, &search_var); if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } result = ZnItemWithTagOrId(wi, args[3], wi->top_group, True, &grp, &search_var); if ((result == ZN_ERROR) || (grp == ZN_NO_ITEM)|| (grp->class != ZnGroup)) { goto error; } if (argc == 5) { if (Tcl_GetBooleanFromObj(interp, args[4], &adjust) != ZN_OK) { goto error; } } if ((item->parent == grp) || (item->parent == ZN_NO_ITEM)) { goto done; } if (adjust) { ITEM.GetItemTransform(grp, &t); ZnTransfoInvert(&t, &inv); ITEM.GetItemTransform(item->parent, &t); ZnTransfoCompose(&t2, &t, &inv); this_one = &t2; if (item->transfo) { ZnTransfoCompose(&t, item->transfo, &t2); this_one = &t; } } ITEM.RemoveItem(item); ITEM.InsertItem(item, grp, ZN_NO_ITEM, True); ITEM.Invalidate(item, ZN_COORDS_FLAG); if (adjust) { ITEM.SetTransfo(item, this_one); } } break; /* * clone */ case ZN_W_CLONE: { if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "clone tagOrId ?option value ...?"); goto error; } if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } argc -= 3; args += 3; l = Tcl_GetObjResult(interp); for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { item2 = ITEM.CloneItem(item); ITEM.InsertItem(item2, item->parent, ZN_NO_ITEM, True); if (ITEM.ConfigureItem(item2, -1, argc, args, False) == ZN_ERROR) { goto error; } Tcl_ListObjAppendElement(interp, l, NewLongObj(item2->id)); } } break; /* * configure */ case ZN_W_CONFIGURE: { 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, Tcl_GetString(args[2]), 0); } else { result = Configure(interp, wi, argc-2, args+2, TK_CONFIG_ARGV_ONLY); } } break; /* * contour */ case ZN_W_CONTOUR: { #ifdef GPC if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "contour tagOrId operator coordListOrTagOrId"); goto error; } if (Contour(wi, argc, args, &search_var) == ZN_ERROR) { goto error; } break; #else Tcl_AppendResult(interp, "Command \"", Tcl_GetString(args[1]), "\" not available (compile Zinc with GPC).", NULL); goto error; #endif } /* * coords */ case ZN_W_COORDS: { if ((argc < 3) || (argc > 6)) { Tcl_WrongNumArgs(interp, 1, args, "coords tagOrId ?add/remove? ?contour? ?index? ?coordList?"); goto error; } if (Coords(wi, argc, args, &search_var) == ZN_ERROR) { goto error; } } break; /* * currentpart */ case ZN_W_CURRENTPART: { if (argc != 2) { Tcl_WrongNumArgs(interp, 1, args, "currentpart"); goto error; } if (wi->current_item->class->Part != NULL) { l = NULL; wi->current_item->class->Part(wi->current_item, &l, &wi->current_part); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } } break; /* * cursor */ case ZN_W_CURSOR: { if (argc != 4) { Tcl_WrongNumArgs(interp, 1, args, "cursor tagOrId index"); goto error; } if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if ((item->class->Cursor == NULL) || (item->class->Index == NULL)) { continue; } result = (*item->class->Index)(item, args[3], &index); if (result != ZN_OK) { goto error; } (*item->class->Cursor)(item, index); if ((item == wi->text_info.focus_item) && wi->text_info.cursor_on) { ITEM.Invalidate(item, ZN_COORDS_FLAG); } } } break; /* * dchars */ case ZN_W_DCHARS: { int first, last; if ((argc != 4) && (argc != 5)) { Tcl_WrongNumArgs(interp, 1, args, "dchars tagOrId first ?last?"); goto error; } if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if ((item->class->Index == NULL) || (item->class->DeleteChars == NULL)) { continue; } result = (*item->class->Index)(item, args[3], &first); if (result != ZN_OK) { goto error; } if (argc == 5) { result = (*item->class->Index)(item, args[4], &last); if (result != ZN_OK) { goto error; } } else { last = first; } (*item->class->DeleteChars)(item, first, last); } } break; /* * dtag */ case ZN_W_DTAG: { Tk_Uid tag; if ((argc != 3) && (argc != 4)) { Tcl_WrongNumArgs(interp, 1, args, "dtag tagOrId ?tagToDelete?"); goto error; } if (argc == 4) { tag = Tk_GetUid(Tcl_GetString(args[3])); } else { tag = Tk_GetUid(Tcl_GetString(args[2])); } if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { ITEM.RemoveTag(item, (char *) tag); } } break; /* * find */ case ZN_W_FIND: { if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "find searchCommand ?arg arg ...?"); goto error; } result = FindItems(wi, argc, args, NULL, 2, &search_var); } break; /* * fit */ case ZN_W_FIT: { ZnPoint *points; int num_points; ZnReal error; ZnList to_points; if (argc != 4) { Tcl_WrongNumArgs(interp, 1, args, "fit coordList error"); goto error; } if (ParseCoordList(wi, args[2], &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } if (Tcl_GetDoubleFromObj(interp, args[3], &error) == ZN_ERROR) { goto error; } to_points = ZnListNew(32, sizeof(ZnPoint)); FitBezier(points, num_points, error, to_points); points = (ZnPoint *) ZnListArray(to_points); num_points = ZnListSize(to_points); l = Tcl_GetObjResult(interp); for (i = 0; i < num_points; i++, points++) { Tcl_ListObjAppendElement(interp, l, NewDoubleObj(points->x)); Tcl_ListObjAppendElement(interp, l, NewDoubleObj(points->y)); } ZnListFree(to_points); } break; /* * focus */ case ZN_W_FOCUS: { if ((argc != 2) && (argc != 3)) { Tcl_WrongNumArgs(interp, 1, args, "focus ?tagOrId?"); goto error; } item = wi->text_info.focus_item; if (argc == 2) { if (item != ZN_NO_ITEM) { l = NewLongObj(item->id); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } goto done; } if ((item != ZN_NO_ITEM) && (wi->text_info.got_focus)) { ITEM.Invalidate(item, ZN_COORDS_FLAG); } if (Tcl_GetString(args[2])[0] == 0) { wi->text_info.focus_item = ZN_NO_ITEM; goto done; } if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if (item->class->Cursor != NULL) { break; } } wi->text_info.focus_item = item; if (wi->text_info.got_focus) { ITEM.Invalidate(wi->text_info.focus_item, ZN_COORDS_FLAG); } } break; /* * gettags */ case ZN_W_GETTAGS: { Tk_Uid *tags; if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "gettags tagOrId"); goto error; } result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, &item, &search_var); if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } if (!item->tags || !ZnListSize(item->tags)) { goto done; } else { num = ZnListSize(item->tags); tags = (Tk_Uid *) ZnListArray(item->tags); l = Tcl_GetObjResult(interp); for (i = 0; i < num; i++) { Tcl_ListObjAppendElement(interp, l, NewStringObj(tags[i])); } } } break; /* * group */ case ZN_W_GROUP: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "group tagOrId"); goto error; } result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, &item, &search_var); if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } if (item->parent != ZN_NO_ITEM) { l = NewLongObj(item->parent->id); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } else { /* * Top group is its own parent. */ l = NewLongObj(item->id); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } } break; /* * hasanchors */ case ZN_W_HASANCHORS: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "hasanchors tagOrId"); goto error; } result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, &item, &search_var); if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } l = NewBooleanObj(item->class->has_anchors?1:0); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } break; /* * hasfields */ case ZN_W_HASFIELDS: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "hasfields tagOrId"); goto error; } result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, &item, &search_var); if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } l = NewBooleanObj(item->class->has_fields?1:0); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } break; /* * hastag */ case ZN_W_HASTAG: { Tk_Uid tag_uid; Tk_Uid *tags; if (argc != 4) { Tcl_WrongNumArgs(interp, 1, args, "hastag tagOrId tag"); goto error; } result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, &item, &search_var); if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } if (!item->tags || !ZnListSize(item->tags)) { l = NewBooleanObj(0); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } else { num = ZnListSize(item->tags); tag_uid = Tk_GetUid(Tcl_GetString(args[3])); tags = (Tk_Uid *) ZnListArray(item->tags); for (i = 0; i < num; i++) { if (tags[i] == tag_uid) { l = NewBooleanObj(1); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif goto done; } } l = NewBooleanObj(0); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } } break; /* * index */ case ZN_W_INDEX: { if (argc != 4) { Tcl_WrongNumArgs(interp, 1, args, "index tagOrId string"); goto error; } if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if (item->class->Index != NULL) { result = (*item->class->Index)(item, args[3], &index); if (result != ZN_OK) { goto error; } l = Tcl_NewIntObj(index); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif goto done; } } Tcl_AppendResult(interp, "can't find an indexable item \"", Tcl_GetString(args[2]), "\"", NULL); goto error; } break; /* * insert */ case ZN_W_INSERT: { if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "insert tagOrId before string"); goto error; } if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if ((item->class->Index == NULL) || (item->class->InsertChars == NULL)) { continue; } result = (*item->class->Index)(item, args[3], &index); if (result != ZN_OK) { goto error; } (*item->class->InsertChars)(item, index, Tcl_GetString(args[4])); } } break; /* * itemcget */ case ZN_W_ITEMCGET: { int field = -1; if (argc < 4) { itemcget_syntax: Tcl_WrongNumArgs(interp, 1, args, "itemcget tagOrId ?field? option"); goto error; } result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, &item, &search_var); if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } if (argc == 5) { if (Tcl_GetIntFromObj(interp, args[3], &field) != ZN_OK) { Tcl_AppendResult(interp, "invalid field index \"", Tcl_GetString(args[3]), "\", should be a positive integer", NULL); goto error; } argc--; args++; } if (argc != 4) { goto itemcget_syntax; } if (ITEM.QueryItem(item, field, 1, &args[3]) != ZN_OK) { goto error; } } break; /* * itemconfigure */ case ZN_W_ITEMCONFIGURE: { int field = -1; if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "itemconfigure tagOrId ?field? option value ?option value? ..."); goto error; } if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } if ((argc > 3) && (Tcl_GetString(args[3])[0] != '-')) { if (Tcl_GetIntFromObj(interp, args[3], &field) != ZN_OK) { Tcl_AppendResult(interp, "invalid field index \"", Tcl_GetString(args[3]), "\", should be a positive integer", NULL); goto error; } argc--; args++; } argc -= 3; args += 3; for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if (argc < 2) { result = ITEM.AttributesInfo(item, field, argc, args); } else { result = ITEM.ConfigureItem(item, field, argc, args, False); } if (result == ZN_ERROR) { goto error; } if (argc < 2) { goto done; } } } break; /* * lower */ case ZN_W_LOWER: { Item group, mark = ZN_NO_ITEM; if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "lower tagOrId ?belowThis?"); goto error; } if (argc == 4) { if (ZnTagSearchScan(wi, args[3], &search_var) == ZN_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { mark = item; } if (mark == ZN_NO_ITEM) { Tcl_AppendResult(interp, "unknown tag or item \"", Tcl_GetString(args[3]), "\"", NULL); goto error; } } if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } item = ZnTagSearchFirst(search_var, wi->top_group, True); if (item == ZN_NO_ITEM) { goto done; } if (mark == ZN_NO_ITEM) { mark = ((GroupItem) item->parent)->tail; } group = mark->parent; for (; item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if (item->parent != group) { continue; } if (item != mark) { ITEM.UpdateItemPriority(item, mark, False); mark = item; } } } break; /* * monitor */ case ZN_W_MONITOR: { ZnBool on_off; if ((argc != 2) && (argc != 3)) { Tcl_WrongNumArgs(interp, 1, args, "monitor ?onOff?"); goto error; } if (argc == 3) { if (Tcl_GetBooleanFromObj(interp, args[2], &on_off) != ZN_OK) { goto error; } wi->monitoring = on_off; if (on_off == True) { ResetChronos(wi->total_draw_chrono); ResetChronos(wi->this_draw_chrono); } } if ((argc == 2) || (on_off == False)) { long ttime, ltime; int num_actions; GetChrono(wi->total_draw_chrono, &ttime, &num_actions); GetChrono(wi->this_draw_chrono, <ime, NULL); lobjs[0] = Tcl_NewIntObj(num_actions); lobjs[1] = Tcl_NewIntObj(ltime); lobjs[2] = Tcl_NewIntObj(ttime); l = Tcl_NewListObj(3, lobjs); /*lobjs[2] = Tcl_NewIntObj(wi->damaged_area_w); lobjs[3] = Tcl_NewIntObj(wi->damaged_area_h); lobjs[4] = Tcl_NewIntObj(ttime); l = Tcl_NewListObj(5, lobjs);*/ Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } } break; /* * numparts */ case ZN_W_NUMPARTS: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "numparts tagOrId"); goto error; } result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, &item, &search_var); if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } l = NewBooleanObj(item->class->num_parts!=0); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } break; /* * postscript */ case ZN_W_POSTSCRIPT: { Tcl_AppendResult(interp, "Command not yet implemented", NULL); goto error; } break; /* * raise */ case ZN_W_RAISE: { Item group, mark = ZN_NO_ITEM; if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "raise tagOrId ?aboveThis?"); goto error; } if (argc == 4) { /* * Find the topmost item with the tag. */ if (ZnTagSearchScan(wi, args[3], &search_var) == ZN_ERROR) { goto error; } mark = ZnTagSearchFirst(search_var, wi->top_group, True); if (mark == ZN_NO_ITEM) { Tcl_AppendResult(interp, "unknown tag or item \"", Tcl_GetString(args[3]), "\"", NULL); goto error; } } if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } item = ZnTagSearchFirst(search_var, wi->top_group, True); if (item == ZN_NO_ITEM) { goto done; } if (mark == ZN_NO_ITEM) { mark = ((GroupItem) item->parent)->head; } group = mark->parent; for (; item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if (item->parent != group) { continue; } if (item != mark) { ITEM.UpdateItemPriority(item, mark, True); } } } break; /* * remove */ case ZN_W_REMOVE: { FieldSet fs; if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "remove tagOrId ?tagOrId ...?"); goto error; } argc -= 2; args += 2; for (i = 0; i < argc; i++) { if (ZnTagSearchScan(wi, args[i], &search_var) == ZN_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if (item == wi->top_group) { continue; } if (wi->binding_table != NULL) { Tk_DeleteAllBindings(wi->binding_table, (ClientData) item); if (item->class->has_fields) { fs = item->class->GetFieldSet(item); for (j = 0; j < fs->num_fields; j++) { Tk_DeleteAllBindings(wi->binding_table, (ClientData) EncodeItemPart(item, j)); } } for (j = 0; j < item->class->num_parts; j++) { Tk_DeleteAllBindings(wi->binding_table, (ClientData) EncodeItemPart(item, -(j+2))); } } ITEM.DestroyItem(item); } } } break; /* * rotate */ case ZN_W_ROTATE: { ZnReal angle; ZnPoint p; if ((argc != 4) && (argc != 6)) { Tcl_WrongNumArgs(interp, 1, args, "rotate tagOrId angle ?centerX centerY?"); goto error; } if (argc == 6) { if (Tcl_GetDoubleFromObj(interp, args[4], &p.x) == ZN_ERROR) { goto error; } if (Tcl_GetDoubleFromObj(interp, args[5], &p.y) == ZN_ERROR) { goto error; } } entry = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[2])); if (entry != NULL) { t = (ZnTransfo *) Tcl_GetHashValue(entry); } else { if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } } if (Tcl_GetDoubleFromObj(interp, args[3], &angle) == ZN_ERROR) { goto error; } if (t) { if (argc == 6) { ZnTranslate(t, -p.x, -p.y); } ZnRotateRad(t, angle); if (argc == 6) { ZnTranslate(t, p.x, p.y); } } else { for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { ITEM.RotateItem(item, angle, (argc == 6) ? &p : NULL); } } } break; /* * scale */ case ZN_W_SCALE: { ZnPoint scale; if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "scale tagOrId xFactor yFactor"); goto error; } entry = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[2])); if (entry != NULL) { t = (ZnTransfo *) Tcl_GetHashValue(entry); } else { if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } } if (Tcl_GetDoubleFromObj(interp, args[3], &scale.x) == ZN_ERROR) { goto error; } if (Tcl_GetDoubleFromObj(interp, args[4], &scale.y) == ZN_ERROR) { goto error; } if (t) { ZnScale(t, scale.x, scale.y); } else { for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { ITEM.ScaleItem(item, scale.x, scale.y); } } } break; /* * select */ case ZN_W_SELECT: { if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "select option ?tagOrId? ?arg?"); goto error; } if (argc >= 4) { if (ZnTagSearchScan(wi, args[3], &search_var) == ZN_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if ((item->class->Index != NULL) && (item->class->Selection != NULL)) { break; } } if (item == ZN_NO_ITEM) { Tcl_AppendResult(interp, "can't find an indexable item \"", Tcl_GetString(args[3]), "\"", NULL); goto error; } } if (argc == 5) { result = item->class->Index(item, args[4], &index); if (result != ZN_OK) { goto error; } } if (Tcl_GetIndexFromObj(interp, args[2], sel_cmd_strings, "selection option", 0, &cmd_index) != ZN_OK) { goto error; } switch ((enum sel_cmds) cmd_index) { case ZN_SEL_ADJUST: if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "select adjust tagOrId index"); goto error; } if (wi->text_info.sel_item == item) { if (index < (wi->text_info.sel_first + wi->text_info.sel_last)/2) { wi->text_info.sel_anchor = wi->text_info.sel_last+1; } else { wi->text_info.sel_anchor = wi->text_info.sel_first; } } SelectTo(item, index); break; case ZN_SEL_CLEAR: if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "select clear"); goto error; } if (wi->text_info.sel_item != ZN_NO_ITEM) { ITEM.Invalidate(wi->text_info.sel_item, ZN_DRAW_FLAG); wi->text_info.sel_item = ZN_NO_ITEM; } break; case ZN_SEL_FROM: if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "select from tagOrId index"); goto error; } wi->text_info.anchor_item = item; wi->text_info.sel_anchor = index; break; case ZN_SEL_ITEM: if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "select item"); goto error; } if (wi->text_info.sel_item != ZN_NO_ITEM) { l = NewLongObj(wi->text_info.sel_item->id); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } break; case ZN_SEL_TO: if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "select to tagOrId index"); goto error; } SelectTo(item, index); break; } } break; /* * smooth */ case ZN_W_SMOOTH: { ZnPoint *points; int num_points; ZnList to_points; if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "smooth coordList"); goto error; } if (ParseCoordList(wi, args[2], &points, &num_points) == ZN_ERROR) { return ZN_ERROR; } to_points = ZnListNew(32, sizeof(ZnPoint)); SmoothPathWithBezier(points, num_points, to_points); points = (ZnPoint *) ZnListArray(to_points); num_points = ZnListSize(to_points); l = Tcl_GetObjResult(interp); for (i = 0; i < num_points; i++, points++) { Tcl_ListObjAppendElement(interp, l, NewDoubleObj(points->x)); Tcl_ListObjAppendElement(interp, l, NewDoubleObj(points->y)); } ZnListFree(to_points); } break; /* * tapply */ case ZN_W_TAPPLY: { Tcl_AppendResult(interp, "Command not yet implemented", NULL); goto error; } break; /* * tdelete */ case ZN_W_TDELETE: { Tcl_HashEntry *e; if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "tdelete tName"); goto error; } e = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[2])); if (e == NULL) { Tcl_AppendResult(interp, "\"", Tcl_GetString(args[2]), "\" must be a transform name", (char *) NULL); goto error; } t = (ZnTransfo *) Tcl_GetHashValue(e); ZnTransfoFree(t); Tcl_DeleteHashEntry(e); } break; /* * transform */ case ZN_W_TRANSFORM: { int num_points; ZnPoint *p, xp; ZnTransfo t, t2, inv, *this_one; Item from, to; if ((argc != 4) && (argc != 5)) { Tcl_WrongNumArgs(interp, 1, args, "transform ?tagOrIdFrom? tagOrIdTo coordlist"); goto error; } if (argc == 5) { result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, &from, &search_var); if ((result == ZN_ERROR) || (from == ZN_NO_ITEM)) { goto error; } } result = ZnItemWithTagOrId(wi, args[argc-2], wi->top_group, True, &to, &search_var); if ((result == ZN_ERROR) || (to == ZN_NO_ITEM)) { Tcl_HashEntry *e; /* * Try to find a named transform. */ e = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[argc-2])); if (e == NULL) { Tcl_ResetResult(interp); Tcl_AppendResult(interp, "\"", Tcl_GetString(args[argc-2]), "\" must be either a tag or ", "an id or a transform name", (char *) NULL); goto error; } inv = *((ZnTransfo *) Tcl_GetHashValue(e)); } else { ITEM.GetItemTransform(to, &t); ZnTransfoInvert(&t, &inv); } this_one = &inv; if (argc == 5) { ITEM.GetItemTransform(from, &t); ZnTransfoCompose(&t2, &t, &inv); this_one = &t2; } /*ZnPrintTransfo(&t); ZnPrintTransfo(&inv);*/ if (ParseCoordList(wi, args[argc-1], &p, &num_points) == ZN_ERROR) { Tcl_AppendResult(interp, " invalid coord list \"", Tcl_GetString(args[argc-1]), "\"", NULL); goto error; } l = Tcl_GetObjResult(interp); for (i = 0; i < num_points; i++, p++) { /* * Need to adjust for the border. */ if (argc != 5) { p->x -= wi->inset; p->y -= wi->inset; } ZnTransformPoint(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);*/ Tcl_ListObjAppendElement(interp, l, NewDoubleObj(xp.x)); Tcl_ListObjAppendElement(interp, l, NewDoubleObj(xp.y)); } } break; /* * translate */ case ZN_W_TRANSLATE: { ZnPoint trans; if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "translate tagOrId xAmount yAmount"); goto error; } entry = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[2])); if (entry != NULL) { t = (ZnTransfo *) Tcl_GetHashValue(entry); } else { if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } } if (Tcl_GetDoubleFromObj(interp, args[3], &trans.x) == ZN_ERROR) { goto error; } if (Tcl_GetDoubleFromObj(interp, args[4], &trans.y) == ZN_ERROR) { goto error; } if (t) { ZnTranslate(t, trans.x, trans.y); } else { for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item =ZnTagSearchNext(search_var)) { ITEM.TranslateItem(item, trans.x, trans.y); } } } break; /* * treset */ case ZN_W_TRESET: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "treset tagOrId"); goto error; } entry = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[2])); if (entry != NULL) { t = (ZnTransfo *) Tcl_GetHashValue(entry); } else { if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } } if (t) { ZnTransfoSetIdentity(t); } else { for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { ITEM.ResetTransfo(item); } } } break; /* * trestore */ case ZN_W_TRESTORE: { if (argc != 4) { Tcl_WrongNumArgs(interp, 1, args, "trestore tagOrId tName"); goto error; } entry = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[argc-1])); if (entry == NULL) { Tcl_AppendResult(interp, "\"", Tcl_GetString(args[argc-1]), "\" must be a transform name", (char *) NULL); goto error; } t = (ZnTransfo *) Tcl_GetHashValue(entry); if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { ITEM.SetTransfo(item, t); } } break; /* * tsave */ case ZN_W_TSAVE: { int new, invert=0; ZnTransfo *inv; if ((argc != 4) && (argc != 5)) { Tcl_WrongNumArgs(interp, 1, args, "tsave tagOrId tName ?invert?"); goto error; } result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, &item, &search_var); if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } if (argc == 5) { if (Tcl_GetBooleanFromObj(interp, args[4], &invert) != ZN_OK) { goto error; } } t = item->transfo; entry = Tcl_CreateHashEntry(wi->t_table, Tcl_GetString(args[argc-1]), &new); if (!new) { ZnTransfoFree((ZnTransfo *) Tcl_GetHashValue(entry)); } if (invert) { inv = ZnTransfoNew(); ZnTransfoInvert(t, inv); } else { inv = ZnTransfoDuplicate(t); } Tcl_SetHashValue(entry, inv); } break; /* * type */ case ZN_W_TYPE: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "type tagOrId"); goto error; } result = ZnItemWithTagOrId(wi, args[2], wi->top_group, True, &item, &search_var); if ((result == ZN_ERROR) || (item == ZN_NO_ITEM)) { goto error; } l = NewStringObj(item->class->name); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } break; /* * vertexat */ case ZN_W_VERTEX_AT: { ZnPoint p; int contour, vertex, o_vertex; if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, " vertexat tagOrId x y"); goto error; } if (ZnTagSearchScan(wi, args[2], &search_var) == ZN_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var, wi->top_group, True); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if (item->class->PickVertex != NULL) { break; } } if (item == ZN_NO_ITEM) { Tcl_AppendResult(interp, "can't find a suitable item \"", Tcl_GetString(args[2]), "\"", NULL); goto error; } if (Tcl_GetDoubleFromObj(interp, args[3], &p.x) == ZN_ERROR) { goto error; } if (Tcl_GetDoubleFromObj(interp, args[4], &p.y) == ZN_ERROR) { goto error; } p.x -= wi->inset; p.y -= wi->inset; item->class->PickVertex(item, &p, &contour, &vertex, &o_vertex); l = Tcl_GetObjResult(interp); Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(contour)); Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(vertex)); Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(o_vertex)); } } done: ZnTagSearchDestroy(search_var); if (wi->work_item_list) { ZnListFree(wi->work_item_list); wi->work_item_list = NULL; } Tcl_Release((ClientData) wi); return result; error: ZnTagSearchDestroy(search_var); if (wi->work_item_list) { ZnListFree(wi->work_item_list); wi->work_item_list = NULL; } Tcl_Release((ClientData) wi); return ZN_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; ZnBBox bbox; InvalidateImage(wi->tile_name); bbox.orig.x = bbox.orig.y = 0; bbox.corner.x = wi->width; bbox.corner.y = wi->height; ITEM_P.Damage(wi, &bbox); ZnNeedRedisplay(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 ZN_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. */ Tcl_Obj *CONST args[], /* Arguments. */ int flags) /* Flags to pass to Tk_ConfigureWidget. */ { #define CONFIG_PROBE(offset) (ISSET(config_specs[offset].specFlags, \ TK_CONFIG_OPTION_SPECIFIED)) ZnBBox bbox; Visual *visual; int depth; Bool local_render; local_render = wi->local_render; if (Tk_ConfigureWidget(interp, wi->win, config_specs, argc, #ifdef PTK (Tcl_Obj **) args, (char *) wi, flags) != TCL_OK) { #else (char **) args, (char *) wi, flags|TK_CONFIG_OBJS) != TCL_OK) { #endif return ZN_ERROR; } if (flags & TK_CONFIG_ARGV_ONLY) { /* * Do not allow modification of the rendering scheme after * the widget creation. */ wi->local_render = local_render; } else { /* * Turn off local rendering if shared memory is not supported. * or if we cannot use the chosen visual, we cannot work with * any visuals, only true color, 16, 24, 32 bits, and perhaps 15 bits * and direct color (to be checked). */ wi->local_render = wi->local_render && wi->has_x_shm; if (wi->local_render) { visual = Tk_Visual(wi->win); depth = Tk_Depth(wi->win); if (((visual->class != TrueColor) && (visual->class != DirectColor)) || ((depth != 16) && (depth != 24) && (depth != 32))) { wi->local_render = False; } } } /* * 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) || CONFIG_PROBE(LIGHT_ANGLE_SPEC)) { bbox.orig.x = bbox.orig.y = 0; bbox.corner.x = wi->width; bbox.corner.y = wi->height; ITEM_P.Damage(wi, &bbox); ZnNeedRedisplay(wi); } if (CONFIG_PROBE(RELIEF_SPEC)) { ZnNeedRedisplay(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). */ wi->inset = wi->border_width + wi->highlight_width; if (CONFIG_PROBE(BORDER_WIDTH_SPEC) || CONFIG_PROBE(HIGHLIGHT_THICKNESS_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, ZN_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, ZnTrack); } if (CONFIG_PROBE(MAP_DISTANCE_SYMBOL_SPEC)) { ITEM.InvalidateItems(wi->top_group, ZnMap); } /* * Request the new geometry. */ if (CONFIG_PROBE(WIDTH_SPEC) || CONFIG_PROBE(HEIGHT_SPEC) || CONFIG_PROBE(BORDER_WIDTH_SPEC) || CONFIG_PROBE(HIGHLIGHT_THICKNESS_SPEC) || !wi->realized) { Tk_GeometryRequest(wi->win, wi->opt_width + 2*wi->inset, wi->opt_height + 2*wi->inset); } if (CONFIG_PROBE(TILE_SPEC)) { if (wi->tile != ZnUnspecifiedImage) { 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); ZnNeedRedisplay(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 != ZN_NO_ITEM) { OmUnregister((void *) wi); wi->om_group = ZN_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 == ZnGroup) { OmRegister((void *) wi, SendTrackToOm, SetLabelAngleFromOm, QueryLabelPosition); wi->om_group = grp; } } } } #endif /* * Update the blinking cursor timing if on/off time has changed. */ if (wi->text_info.got_focus && (CONFIG_PROBE(INSERT_ON_TIME_SPEC) || CONFIG_PROBE(INSERT_OFF_TIME_SPEC))) { Focus(wi, True); } return TCL_OK; } /* *---------------------------------------------------------------------- * * Focus -- * * This procedure is called whenever a zinc gets or loses the * input focus. It's also called whenever the window is * reconfigured while it has the focus. * * Results: * None. * * Side effects: * The cursor gets turned on or off. * *---------------------------------------------------------------------- */ static void Blink(ClientData client_data) { WidgetInfo *wi = (WidgetInfo *) client_data; if (!wi->text_info.got_focus || (wi->insert_off_time == 0)) { return; } if (wi->text_info.cursor_on) { wi->text_info.cursor_on = 0; wi->blink_handler = Tcl_CreateTimerHandler(wi->insert_off_time, Blink, client_data); } else { wi->text_info.cursor_on = 1; wi->blink_handler = Tcl_CreateTimerHandler(wi->insert_on_time, Blink, client_data); } if (wi->text_info.focus_item != ZN_NO_ITEM) { ITEM.Invalidate(wi->text_info.focus_item, ZN_DRAW_FLAG); } } static void Focus(WidgetInfo *wi, ZnBool got_focus) { Tcl_DeleteTimerHandler(wi->blink_handler); if (got_focus) { wi->text_info.got_focus = 1; wi->text_info.cursor_on = 1; if (wi->insert_off_time != 0) { wi->blink_handler = Tcl_CreateTimerHandler(wi->insert_off_time, Blink, (ClientData) wi); } } else { wi->text_info.got_focus = 0; wi->text_info.cursor_on = 0; wi->blink_handler = (Tcl_TimerToken) NULL; } if (wi->text_info.focus_item != ZN_NO_ITEM) { ITEM.Invalidate(wi->text_info.focus_item, ZN_COORDS_FLAG); } } /* *---------------------------------------------------------------------- * * Event -- * * This procedure is invoked by the Tk dispatcher for various * events on Zincs. * * 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) /* Information about event. */ { WidgetInfo *wi = (WidgetInfo *) client_data; /*printf("=============== DEBUT %s EVENT ==================\n", event->type == MapNotify ? "MAP": event->type == Expose? "EXPOSE" : event->type == ConfigureNotify ? "CONFIGURE" : event->type == DestroyNotify ? "DESTROY" : "??");*/ if (event->type == MapNotify) { if (!wi->gc) { wi->realized = True; /* * Get the work GC and suppress GraphicExpose * and NoExpose events reception. */ wi->gc = XCreateGC(wi->dpy, ZnWindowId(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, ZnWindowId(top_level), &root, &parent, &children, &num_children); if (root == parent) { wi->real_top = ZnWindowId(top_level); } else { wi->real_top = parent; } XFree((char *) children); } } } else if (event->type == Expose) { ZnBBox bbox; ZnDim width, height; bbox.orig.x = (((XExposeEvent*) event)->x - wi->inset); bbox.orig.y = (((XExposeEvent*) event)->y - wi->inset); width = ((XExposeEvent*) event)->width; height = ((XExposeEvent*) event)->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 = MIN(wi->width, bbox.orig.x + width); bbox.corner.y = MIN(wi->height, bbox.orig.y + height); /*printf("expose %d %d %d %d\n", ((XExposeEvent*) event)->x, ((XExposeEvent*) event)->y, ((XExposeEvent*) event)->width, ((XExposeEvent*) event)->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)->count == 0) && !IsEmptyBBox(&wi->exposed_area)) { ZnNeedRedisplay(wi); } } /* * Resize the double buffer pixmap and prepare to redisplay * the whole scene. The transform parameters are not * modified as a result of the resize. If the application * need such change, it can bind a handler on . */ else if (event->type == ConfigureNotify) { ZnDim w, h; ZnBBox bbox; /* w = ((XConfigureEvent*) event)->width-2*wi->inset; h = ((XConfigureEvent*) event)->height-2*wi->inset;*/ w = Tk_Width(wi->win)-2*wi->inset; h = Tk_Height(wi->win)-2*wi->inset; if ((wi->width != w) || (wi->height != h)) { /*printf("reallocating double buffer\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, ZN_TRANSFO_FLAG); /* * Reallocate the double buffer pixmap/image. */ if (wi->local_render) { #ifdef SHM /* Free the image and allocate a bigger one. */ XShmDetach(wi->dpy, &wi->x_shm_info); XDestroyImage(wi->draw_buffer_im); wi->draw_buffer_im = NULL; shmdt(wi->x_shm_info.shmaddr); shmctl(wi->x_shm_info.shmid, IPC_RMID, 0); ZnFree(wi->buf.buf); CreateSharedImageBuffer(wi); #endif } if (!wi->local_render) { 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); ZnNeedRedisplay(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->type == DestroyNotify) { if (wi->win != NULL) { wi->win = NULL; wi->realized = False; Tcl_DeleteCommandFromToken(wi->interp, wi->cmd); } if (wi->update_pending) { Tcl_CancelIdleCall(Redisplay, (ClientData) wi); } Tcl_EventuallyFree((ClientData) wi, Destroy); } else if (event->type == FocusIn) { if (event->xfocus.detail != NotifyInferior) { Focus(wi, True); } } else if (event->type == FocusOut) { if (event->xfocus.detail != NotifyInferior) { Focus(wi, False); } } /*printf("=============== FIN %s EVENT ==================\n", event->type == MapNotify ? "MAP": event->type == Expose? "EXPOSE" : event->type == ConfigureNotify ? "CONFIGURE" : event->type == DestroyNotify ? "DESTROY" : "??");*/ } /* *---------------------------------------------------------------------- * * DoEvent -- * * Trigger the bindings associated with an event. * *---------------------------------------------------------------------- */ static void DoEvent(WidgetInfo *wi, XEvent *event) { #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; int part; ZnBool 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; part = wi->current_part; if ((event->type == KeyPress) || (event->type == KeyRelease)) { item = wi->text_info.focus_item; part = ZN_NO_PART; } if ((item == ZN_NO_ITEM) || !item->class->IsSensitive(item, part)) { 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 != ZN_NO_PART) && (wi->current_item->class->num_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->type != EnterNotify) && (event->type != LeaveNotify)) || (wi->current_item != wi->new_item)); /*printf("type=%s, current=%d, new=%d --> %s\n", event->type==EnterNotify?"Enter": event->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 = ZnMalloc(worksize); } } if (item->tags) { num_tags = ZnListSize(item->tags); if (bind_item) { num += num_tags; } if (bind_part) { num += num_tags; } tag_list = (ClientData *) ZnListArray(item->tags); if (num > NUM_STATIC) { its = (ClientData *) ZnMalloc(num*sizeof(ClientData)); } } ptr = 0; BIND_ITEM(event->type != LeaveNotify); if (bind_part) { /* * Add here a binding for each tag suffixed by :part */ for (i = 0; i < num_tags; i++) { len = strlen(tag_list[i])+ INTEGER_SPACE; if (worksize < len) { worksize = len + 10; workspace = ZnRealloc(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:part */ its[ptr] = EncodeItemPart(item, wi->current_part); ptr++; } BIND_ITEM(event->type == LeaveNotify); /* * Invoke the binding system. */ if (wi->win != NULL) { Tk_BindEvent(wi->binding_table, event, wi->win, num, its); } if (its != items) { ZnFree(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 widget, 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) { 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 widget. The saved event * 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 != &wi->pick_event) { if ((event->type == MotionNotify) || (event->type == ButtonRelease)) { wi->pick_event.xcrossing.type = EnterNotify; wi->pick_event.xcrossing.serial = event->xmotion.serial; wi->pick_event.xcrossing.send_event = event->xmotion.send_event; wi->pick_event.xcrossing.display = event->xmotion.display; wi->pick_event.xcrossing.window = event->xmotion.window; wi->pick_event.xcrossing.root = event->xmotion.root; wi->pick_event.xcrossing.subwindow = None; wi->pick_event.xcrossing.time = event->xmotion.time; wi->pick_event.xcrossing.x = event->xmotion.x; wi->pick_event.xcrossing.y = event->xmotion.y; wi->pick_event.xcrossing.x_root = event->xmotion.x_root; wi->pick_event.xcrossing.y_root = event->xmotion.y_root; wi->pick_event.xcrossing.mode = NotifyNormal; wi->pick_event.xcrossing.detail = NotifyNonlinear; wi->pick_event.xcrossing.same_screen = event->xmotion.same_screen; wi->pick_event.xcrossing.focus = False; wi->pick_event.xcrossing.state = event->xmotion.state; } else { wi->pick_event = *event; } } /* * 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) { ZnPoint p; ZnReal dist; p.x = wi->pick_event.xcrossing.x-wi->inset; p.y = wi->pick_event.xcrossing.y-wi->inset; dist = wi->top_group->class->Pick(wi->top_group, &p, NULL, wi->pick_aperture, &wi->new_item, &wi->new_part); if (dist != 0.0) { wi->new_item = ZN_NO_ITEM; wi->new_part = ZN_NO_PART; } } else { wi->new_item = ZN_NO_ITEM; } 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 != ZN_NO_ITEM) && wi->current_item->class->IsSensitive(wi->current_item, wi->current_part) && !(wi->events_flags & LEFT_GRABBED_ITEM) && !prev_left_grabbed_item) { XEvent event; Item item = wi->current_item; event = wi->pick_event; event.type = LeaveNotify; /*printf("==LEAVE== %d, sensitive %d\n", wi->current_item->id, wi->current_item->class->IsSensitive(wi->current_item, wi->current_part));*/ /* * If the event's detail happens to be NotifyInferior the * binding mechanism will discard the event. To be consistent, * always use NotifyAncestor. */ event.xcrossing.detail = NotifyAncestor; wi->events_flags |= REPICK_IN_PROGRESS; DoEvent(wi, &event); wi->events_flags &= ~REPICK_IN_PROGRESS; /* * The check on item below is needed because there could be an * event handler for that deletes the current item. */ if ((item == wi->current_item) && !button_down) { /*printf("Removing 'current' to %d\n", wi->current_item->id);*/ 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) { if (wi->current_item != ZN_NO_ITEM) { /*printf("***Removing 'current' to %d\n", wi->current_item->id);*/ ITEM.RemoveTag(wi->current_item, current_uid); } wi->current_item = wi->new_item; wi->new_item = ZN_NO_ITEM; } wi->current_part = wi->new_part; if ((wi->current_item != ZN_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. */ /*printf("Adding 'current' to %d\n", wi->current_item->id);*/ DoItem((Tcl_Interp *) NULL, wi->current_item, ZN_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) /* 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->type == ButtonPress) || (event->type == ButtonRelease)) { int mask; switch (event->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->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->xbutton.state; PickCurrentItem(wi, event); wi->state ^= mask; if ((wi->current_item != ZN_NO_ITEM) && wi->current_item->class->IsSensitive(wi->current_item, wi->current_part)) { DoEvent(wi, event); } } 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->xbutton.state; DoEvent(wi, event); event->xbutton.state ^= mask; wi->state = event->xbutton.state; PickCurrentItem(wi, event); event->xbutton.state ^= mask; } goto done; } else if ((event->type == EnterNotify) || (event->type == LeaveNotify)) { wi->state = event->xcrossing.state; PickCurrentItem(wi, event); goto done; } else if (event->type == MotionNotify) { wi->state = event->xmotion.state; PickCurrentItem(wi, event); } DoEvent(wi, event); done: Tcl_Release((ClientData) wi); } /* *---------------------------------------------------------------------- * * LostSelection -- * * This procedure is called back by Tk when the selection is * grabbed away from a zinc widget. * * Results: * None. * * Side effects: * The existing selection is unhighlighted, and the window is * marked as not containing a selection. * *---------------------------------------------------------------------- */ static void LostSelection(ClientData client_data) { WidgetInfo *wi = (WidgetInfo *) client_data; if (wi->text_info.sel_item != ZN_NO_ITEM) { ITEM.Invalidate(wi->text_info.sel_item, ZN_DRAW_FLAG); } wi->text_info.sel_item = ZN_NO_ITEM; } /* *---------------------------------------------------------------------- * * SelectTo -- * * Modify the selection by moving its un-anchored end. This could * make the selection either larger or smaller. * * Results: * None. * * Side effects: * The selection changes. * *---------------------------------------------------------------------- */ static void SelectTo(Item item, int index) { WidgetInfo *wi = item->wi; int old_first, old_last; Item old_sel_item; old_first = wi->text_info.sel_first; old_last = wi->text_info.sel_last; old_sel_item = wi->text_info.sel_item; /* * Grab the selection if we don't own it already. */ if (wi->text_info.sel_item == ZN_NO_ITEM) { Tk_OwnSelection(wi->win, XA_PRIMARY, LostSelection, (ClientData) wi); } else if (wi->text_info.sel_item != item) { ITEM.Invalidate(wi->text_info.sel_item, ZN_DRAW_FLAG); } wi->text_info.sel_item = item; if (wi->text_info.anchor_item != item) { wi->text_info.anchor_item = item; wi->text_info.sel_anchor = index; } if (wi->text_info.sel_anchor <= index) { wi->text_info.sel_first = wi->text_info.sel_anchor; wi->text_info.sel_last = index; } else { wi->text_info.sel_first = index; wi->text_info.sel_last = wi->text_info.sel_anchor; } if ((wi->text_info.sel_first != old_first) || (wi->text_info.sel_last != old_last) || (item != old_sel_item)) { ITEM.Invalidate(item, ZN_DRAW_FLAG); } } /* *-------------------------------------------------------------- * * FetchSelection -- * * This procedure is invoked by Tk to return part or all of * the selection, when the selection is in a zinc widget. * This procedure always returns the selection as a STRING. * * Results: * The return value is the number of non-NULL bytes stored * at buffer. Buffer is filled (or partially filled) with a * NULL-terminated string containing part or all of the selection, * as given by offset and maxBytes. * * Side effects: * None. * *-------------------------------------------------------------- */ static int FetchSelection( ClientData client_data, int offset, /* Offset within selection of first * character to be returned. */ char *buffer, /* Location in which to place * selection. */ int max_bytes) /* Maximum number of bytes to place * at buffer, not including terminating * NULL character. */ { WidgetInfo *wi = (WidgetInfo *) client_data; if (wi->text_info.sel_item == NULL) { return -1; } if (wi->text_info.sel_item->class->Selection == NULL) { return -1; } return (*wi->text_info.sel_item->class->Selection)(wi->text_info.sel_item, offset, buffer, max_bytes); } /* *---------------------------------------------------------------------- * * 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 the widget at a safe time * (when no-one is using it anymore). * * Results: * None. * * Side effects: * Everything associated with the widget is freed up. * *---------------------------------------------------------------------- */ static void Destroy(char *mem_ptr) /* Info about the 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 != ZN_NO_ITEM) { OmUnregister((void *) wi); } #endif /* * Print remaining items. */ /* Free all items. */ printf("Remaining item count: %d\n", wi->num_items); ITEM.DestroyItem(wi->top_group); printf("Remaining item count: %d\n", wi->num_items); for (num = 0; num < NUM_ALPHA_STEPS; num++) { if (wi->alpha_stipples[num] != None) { Tk_FreeBitmap(wi->dpy, wi->alpha_stipples[num]); } } Tcl_DeleteHashTable(wi->id_table); ZnFree(wi->id_table); /* * Free the transform table contents before the table. */ entry = Tcl_FirstHashEntry(wi->t_table, &search); while (entry != NULL) { ZnTransfoFree((ZnTransfo *) Tcl_GetHashValue(entry)); entry = Tcl_NextHashEntry(&search); } Tcl_DeleteHashTable(wi->t_table); ZnFree(wi->t_table); if (wi->binding_table != NULL) { Tk_DeleteBindingTable(wi->binding_table); } /* Free the tile */ if (wi->tile != ZnUnspecifiedImage) { Tk_FreeImage(wi->tile); wi->tile = ZnUnspecifiedImage; } /* Free the double buffer pixmap/image */ if (wi->draw_buffer) { XFreePixmap(wi->dpy, wi->draw_buffer); } if (wi->draw_buffer_im) { #ifdef SHM XShmDetach(wi->dpy, &wi->x_shm_info); XDestroyImage(wi->draw_buffer_im); shmdt(wi->x_shm_info.shmaddr); shmctl(wi->x_shm_info.shmid, IPC_RMID, 0); ZnFree(wi->buf.buf); #endif } Tcl_DeleteTimerHandler(wi->blink_handler); Tk_FreeOptions(config_specs, (char *) wi, wi->dpy, 0); /* * Should be empty by now. */ ITEM_P.FreeTransformStack(wi); ITEM_P.FreeClipStack(wi); ZnListFree(wi->work_pts); ZnListFree(wi->work_xpts); FreeChrono(wi->total_draw_chrono); FreeChrono(wi->this_draw_chrono); ZnFree(wi); /*printf("Destroy ending\n");*/ } /* *---------------------------------------------------------------------- * * Redisplay -- * * This procedure redraws the contents of a Zinc 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 the widget. */ { WidgetInfo *wi = (WidgetInfo *) client_data; ZnBBox merge; Tk_Window tkwin; XRectangle r; wi->update_pending = 0; if (!wi->realized || !Tk_IsMapped(wi->win)) { return; } if (wi->monitoring) { XStartChrono(wi->total_draw_chrono, wi->dpy, ZnWindowId(wi->win)); ResetChronos(wi->this_draw_chrono); XStartChrono(wi->this_draw_chrono, wi->dpy, ZnWindowId(wi->win)); } 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, ZN_COORDS_FLAG) || ISSET(wi->top_group->inv_flags, ZN_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); wi->damaged_area_w = wi->damaged_area.corner.x-wi->damaged_area.orig.x; wi->damaged_area_h = wi->damaged_area.corner.y-wi->damaged_area.orig.y; AddBBoxToBBox(&merge, &wi->exposed_area); if (!IsEmptyBBox(&merge)) { BBox2XRect(&merge, &r); if (wi->local_render) { #ifdef SHM XPutImage(wi->dpy, ZnWindowId(wi->win), wi->gc, wi->draw_buffer_im, r.x, r.y, r.x+wi->inset, r.y+wi->inset, r.width, r.height); #endif } else { XCopyArea(wi->dpy, wi->draw_buffer, ZnWindowId(wi->win), wi->gc, r.x, r.y, r.width, r.height, r.x+wi->inset, r.y+wi->inset); } } /* * Reset the exposed & damaged areas. */ ResetBBox(&wi->exposed_area); ResetBBox(&wi->damaged_area); /* * Redraw the borders. */ if (wi->border_width > 0) { /*printf("win size %d %d\n", Tk_Width(wi->win), Tk_Height(wi->win));*/ Tk_Draw3DRectangle(wi->win, ZnWindowId(wi->win), wi->bg_border, wi->highlight_width, wi->highlight_width, Tk_Width(wi->win) - 2*wi->highlight_width, Tk_Height(wi->win) - 2*wi->highlight_width, wi->border_width, wi->relief); } if (wi->highlight_width > 0) { GC gc; if (wi->text_info.got_focus) { gc = Tk_GCForColor(wi->highlight_color, Tk_WindowId(wi->win)); } else { gc = Tk_GCForColor(wi->highlight_bg_color, Tk_WindowId(wi->win)); } Tk_DrawFocusHighlight(wi->win, gc, wi->highlight_width, Tk_WindowId(wi->win)); } if (wi->monitoring) { XStopChrono(wi->total_draw_chrono, wi->dpy, ZnWindowId(wi->win)); XStopChrono(wi->this_draw_chrono, wi->dpy, ZnWindowId(wi->win)); } } /* *-------------------------------------------------------------------------- * * MapInfo stuff that should go eventually in its own file. * *-------------------------------------------------------------------------- */ static Tcl_HashTable mapInfoTable; static ZnBool map_info_inited = False; typedef struct { ClientData client_data; MapInfoChangeProc proc; } MapInfoClient; typedef struct { MapInfoId map_info; ZnBool deleted; ZnList 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 = ZnListSize(master->clients); client = (MapInfoClient *) ZnListArray(master->clients); for (i = 0; i < num; i++, client++) { (*client->proc)(client->client_data, master->map_info); } } int ZnCreateMapInfo(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 *) ZnMalloc(sizeof(MapInfoMaster)); master->map_info = MapInfoCreate(name); master->deleted = False; master->clients = ZnListNew(1, sizeof(MapInfoClient)); Tcl_SetHashValue(entry, master); } if (map_info) { *map_info = master->map_info; } return TCL_OK; } int ZnDuplicateMapInfo(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 ZN_ERROR; } master = (MapInfoMaster *) ZnMalloc(sizeof(MapInfoMaster)); master->map_info = MapInfoDuplicate(map_info); master->deleted = False; master->clients = ZnListNew(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 ZnDeleteMapInfo(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 ZN_ERROR; } master = (MapInfoMaster *) Tcl_GetHashValue(entry); if (ZnListSize(master->clients) != 0) { master->deleted = True; MapInfoEmpty(master->map_info); UpdateMapInfoClients(master); } else { MapInfoDelete(master->map_info); ZnListFree(master->clients); Tcl_DeleteHashEntry(entry); ZnFree(master); } return TCL_OK; } MapInfoId ZnGetMapInfo(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; ZnListAdd(master->clients, &client, ZnListTail); return master->map_info; } void ZnFreeMapInfo(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 *) ZnListArray(master->clients); num = ZnListSize(master->clients); for (i = 0; i < num; i++, client++) { if ((client->client_data == client_data) && (client->proc == proc)) { ZnListDelete(master->clients, i); return; } } } void ZnUpdateMapInfoClients(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 ZN_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 ZN_ERROR; } int MapInfoObjCmd(ClientData client_data, Tcl_Interp *interp, /* Current interpreter. */ int argc, /* Number of arguments. */ Tcl_Obj *CONST args[]) { int index, index2, result; MapInfoMaster *master; Tcl_Obj *l, *lobjs[7]; static char *sub_cmd_strings[] = { "add", "count", "create", "delete", "duplicate", "get", "remove", "replace", "scale", "translate", NULL }; static char *e_type_strings[] = { "arc", "line", "symbol", "text", NULL }; enum sub_cmds { ZN_MI_ADD, ZN_MI_COUNT, ZN_MI_CREATE, ZN_MI_DELETE, ZN_MI_DUPLICATE, ZN_MI_GET, ZN_MI_REMOVE, ZN_MI_REPLACE, ZN_MI_SCALE, ZN_MI_TRANSLATE }; enum e_types { ZN_E_ARC, ZN_E_LINE, ZN_E_SYMBOL, ZN_E_TEXT }; if (!inited) { InitZinc(interp); } if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "mapInfo/name subCmd ?args?"); return ZN_ERROR; } if (Tcl_GetIndexFromObj(interp, args[2], sub_cmd_strings, "subCmd", 0, &index) != ZN_OK) { return ZN_ERROR; } result = TCL_OK; /*printf("mapinfo command \"%s\", argc=%d\n", Tcl_GetString(args[2]), argc);*/ switch((enum sub_cmds) index) { /* * create */ case ZN_MI_CREATE: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "name create"); return ZN_ERROR; } if (ZnCreateMapInfo(interp, Tcl_GetString(args[1]), NULL) == ZN_ERROR) { return ZN_ERROR; } } break; /* * delete */ case ZN_MI_DELETE: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "mapInfo delete"); return ZN_ERROR; } if (ZnDeleteMapInfo(interp, Tcl_GetString(args[1])) == ZN_ERROR) { return ZN_ERROR; } } break; /* * duplicate */ case ZN_MI_DUPLICATE: { if (argc != 4) { Tcl_WrongNumArgs(interp, 1, args, "mapInfo duplicate name"); return ZN_ERROR; } master = LookupMapInfoMaster(interp, Tcl_GetString(args[1])); if (master == NULL) { return ZN_ERROR; } if (ZnDuplicateMapInfo(interp, Tcl_GetString(args[3]), master->map_info) == ZN_ERROR) { return ZN_ERROR; } } break; /* * add/replace */ case ZN_MI_ADD: case ZN_MI_REPLACE: { MapInfoLineStyle line_style; MapInfoTextStyle text_style; int i, insert; int coords[6]; ZnBool add_cmd = (enum sub_cmds) index == ZN_MI_ADD; int num_param = add_cmd ? 4 : 5; if (argc < num_param) { Tcl_WrongNumArgs(interp, 3, args, add_cmd ? "elementType ?args?" : "elementType index ?args?"); return ZN_ERROR; } master = LookupMapInfoMaster(interp, Tcl_GetString(args[1])); if (master == NULL) { return ZN_ERROR; } if (!add_cmd) { if (Tcl_GetIntFromObj(interp, args[4], &insert) == ZN_ERROR) { return ZN_ERROR; } if (insert < 0) { insert = 0; } } if (Tcl_GetIndexFromObj(interp, args[3], e_type_strings, "elementType", 0, &index2) != ZN_OK) { return ZN_ERROR; } switch ((enum e_types) index2) { case ZN_E_LINE: { if (argc != (num_param+6)) { Tcl_WrongNumArgs(interp, 4, args, add_cmd ? "style width x1 y1 x2 y2" : "index style width x1 y1 x2 y2"); return ZN_ERROR; } if (MapInfoLineStyleFromString(interp, Tcl_GetString(args[num_param]), &line_style) == ZN_ERROR) { return ZN_ERROR; } for (i = 0; i < 5; i++) { if (Tcl_GetIntFromObj(interp, args[num_param+i+1], &coords[i]) == ZN_ERROR) { return ZN_ERROR; } } if (coords[0] < 0) { coords[0] = 0; } if (add_cmd) { MapInfoAddLine(master->map_info, ZnListTail, NULL, line_style, coords[0], coords[1], coords[2], coords[3], coords[4]); } else { MapInfoReplaceLine(master->map_info, insert, NULL, line_style, coords[0], coords[1], coords[2], coords[3], coords[4]); } } break; case ZN_E_SYMBOL: { if (argc != (num_param+3)) { Tcl_WrongNumArgs(interp, 4, args, add_cmd ? "x y intVal" : "index x y intVal"); return ZN_ERROR; } for (i = 0; i < 3; i++) { if (Tcl_GetIntFromObj(interp, args[num_param+i], &coords[i]) == ZN_ERROR) { return ZN_ERROR; } } if (coords[2] < 0) { coords[2] = 0; } if (add_cmd) { MapInfoAddSymbol(master->map_info, ZnListTail, NULL, coords[0], coords[1], coords[2]); } else { MapInfoReplaceSymbol(master->map_info, insert, NULL, coords[0], coords[1], coords[2]); } } case ZN_E_TEXT: { if (argc != (num_param+5)) { Tcl_WrongNumArgs(interp, 4, args, add_cmd ? "textStyle lineStyle x y string" : "index textStyle lineStyle x y string"); return ZN_ERROR; } if (MapInfoTextStyleFromString(interp, Tcl_GetString(args[num_param]), &text_style) == ZN_ERROR) { return ZN_ERROR; } if (MapInfoLineStyleFromString(interp, Tcl_GetString(args[num_param+1]), &line_style) == ZN_ERROR) { return ZN_ERROR; } for (i = 0; i < 2; i++) { if (Tcl_GetIntFromObj(interp, args[num_param+i+2], &coords[i]) == ZN_ERROR) { return ZN_ERROR; } } if (add_cmd) { MapInfoAddText(master->map_info, ZnListTail, NULL, text_style, line_style, coords[0], coords[1], Tcl_GetString(args[num_param+4])); } else { /*printf("replace text ts %d ls %d %d %d %s\n", text_style, line_style, coords[0], coords[1], Tcl_GetString(args[num_param+4]));*/ MapInfoReplaceText(master->map_info, insert, NULL, text_style, line_style, coords[0], coords[1], Tcl_GetString(args[num_param+4])); } } break; case ZN_E_ARC: { if (argc != (num_param+7)) { Tcl_WrongNumArgs(interp, 4, args, add_cmd ? "style width cx cy radius start extent" : "index style width cx cy radius start extent"); return ZN_ERROR; } if (MapInfoLineStyleFromString(interp, Tcl_GetString(args[num_param]), &line_style) == ZN_ERROR) { return ZN_ERROR; } for (i = 0; i < 6; i++) { if (Tcl_GetIntFromObj(interp, args[num_param+i+1], &coords[i]) == ZN_ERROR) { return ZN_ERROR; } } if (coords[0] < 0) { coords[0] = 0; } if (add_cmd) { MapInfoAddArc(master->map_info, ZnListTail, NULL, line_style, coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); } else { MapInfoReplaceArc(master->map_info, insert, NULL, line_style, coords[0], coords[1], coords[2], coords[3], coords[4], coords[5]); } } break; } UpdateMapInfoClients(master); } break; /* * count */ case ZN_MI_COUNT: { int count = 0; if (argc != 4) { Tcl_WrongNumArgs(interp, 1, args, "mapInfo count type"); return ZN_ERROR; } master = LookupMapInfoMaster(interp, Tcl_GetString(args[1])); if (master == NULL) { return ZN_ERROR; } if (Tcl_GetIndexFromObj(interp, args[3], e_type_strings, "elementType", 0, &index2) != ZN_OK) { return ZN_ERROR; } switch ((enum e_types) index2) { case ZN_E_LINE: count = MapInfoNumLines(master->map_info); break; case ZN_E_SYMBOL: count = MapInfoNumSymbols(master->map_info); break; case ZN_E_TEXT: count = MapInfoNumTexts(master->map_info); break; case ZN_E_ARC: count = MapInfoNumArcs(master->map_info); break; } l = Tcl_NewIntObj(count); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } break; /* * get */ case ZN_MI_GET: { int insert; if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "mapInfo get type index"); return ZN_ERROR; } master = LookupMapInfoMaster(interp, Tcl_GetString(args[1])); if (master == NULL) { return ZN_ERROR; } if (Tcl_GetIntFromObj(interp, args[4], &insert) == ZN_ERROR) { return ZN_ERROR; } if (insert < 0) { insert = 0; } if (Tcl_GetIndexFromObj(interp, args[3], e_type_strings, "elementType", 0, &index2) != ZN_OK) { return ZN_ERROR; } switch ((enum e_types) index2) { case ZN_E_LINE: { MapInfoLineStyle line_style; int line_width; int x_from, y_from, x_to, y_to; MapInfoGetLine(master->map_info, insert, NULL, &line_style, &line_width, &x_from, &y_from, &x_to, &y_to); lobjs[0] = NewStringObj(MapInfoLineStyleToString(line_style)); lobjs[1] = Tcl_NewIntObj(line_width); lobjs[2] = Tcl_NewIntObj(x_from); lobjs[3] = Tcl_NewIntObj(y_from); lobjs[4] = Tcl_NewIntObj(x_to); lobjs[5] = Tcl_NewIntObj(y_to); l = Tcl_NewListObj(6, lobjs); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } break; case ZN_E_SYMBOL: { int x, y; char symbol; MapInfoGetSymbol(master->map_info, insert, NULL, &x, &y, &symbol); lobjs[0] = Tcl_NewIntObj(x); lobjs[1] = Tcl_NewIntObj(y); lobjs[2] = Tcl_NewIntObj(symbol); l = Tcl_NewListObj(3, lobjs); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } break; case ZN_E_TEXT: { int x, y; char *text; MapInfoTextStyle text_style; MapInfoLineStyle line_style; MapInfoGetText(master->map_info, insert, NULL, &text_style, &line_style, &x, &y, &text); lobjs[0] = Tcl_NewIntObj(x); lobjs[1] = Tcl_NewIntObj(y); lobjs[2] = NewStringObj(MapInfoTextStyleToString(text_style)); lobjs[3] = NewStringObj(MapInfoLineStyleToString(line_style)); lobjs[4] = NewStringObj(text); l = Tcl_NewListObj(5, lobjs); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } break; case ZN_E_ARC: { MapInfoLineStyle line_style; int line_width; int center_x, center_y, start, extent; unsigned int radius; MapInfoGetArc(master->map_info, insert, NULL, &line_style, &line_width, ¢er_x, ¢er_y, &radius, &start, &extent); lobjs[0] = NewStringObj(MapInfoLineStyleToString(line_style)); lobjs[1] = Tcl_NewIntObj(line_width); lobjs[2] = Tcl_NewIntObj(center_x); lobjs[3] = Tcl_NewIntObj(center_y); lobjs[4] = Tcl_NewIntObj(radius); lobjs[5] = Tcl_NewIntObj(start); lobjs[6] = Tcl_NewIntObj(extent); l = Tcl_NewListObj(7, lobjs); Tcl_SetObjResult(interp, l); #ifdef PTK Tcl_DecrRefCount(l); #endif } break; } } break; /* * remove */ case ZN_MI_REMOVE: { int insert; if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "mapInfo remove type index"); return ZN_ERROR; } master = LookupMapInfoMaster(interp, Tcl_GetString(args[1])); if (master == NULL) { return ZN_ERROR; } if (Tcl_GetIntFromObj(interp, args[4], &insert) == ZN_ERROR) { return ZN_ERROR; } if (insert < 0) { insert = 0; } if (Tcl_GetIndexFromObj(interp, args[3], e_type_strings, "elementType", 0, &index2) != ZN_OK) { return ZN_ERROR; } switch ((enum e_types) index2) { case ZN_E_LINE: MapInfoRemoveLine(master->map_info, insert); break; case ZN_E_SYMBOL: MapInfoRemoveSymbol(master->map_info, insert); break; case ZN_E_TEXT: MapInfoRemoveText(master->map_info, insert); break; case ZN_E_ARC: MapInfoRemoveArc(master->map_info, insert); break; } UpdateMapInfoClients(master); } break; /* * scale */ case ZN_MI_SCALE: { double factor; if (argc != 4) { Tcl_WrongNumArgs(interp, 1, args, "mapInfo scale factor"); return ZN_ERROR; } master = LookupMapInfoMaster(interp, Tcl_GetString(args[1])); if (master == NULL) { return ZN_ERROR; } if (Tcl_GetDoubleFromObj(interp, args[3], &factor) == ZN_ERROR) { return ZN_ERROR; } MapInfoScale(master->map_info, factor); UpdateMapInfoClients(master); } break; /* * translate */ case ZN_MI_TRANSLATE: { int x, y; if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "mapInfo translate xAmount yAmount"); return ZN_ERROR; } master = LookupMapInfoMaster(interp, Tcl_GetString(args[1])); if (master == NULL) { return ZN_ERROR; } if (Tcl_GetIntFromObj(interp, args[3], &x) == ZN_ERROR) { return ZN_ERROR; } if (Tcl_GetIntFromObj(interp, args[4], &y) == ZN_ERROR) { return ZN_ERROR; } MapInfoTranslate(master->map_info, x, y); UpdateMapInfoClients(master); } break; } return TCL_OK; } /* *---------------------------------------------------------------------- * * VideomapObjCmd -- * * *---------------------------------------------------------------------- */ int VideomapObjCmd(ClientData client_data, Tcl_Interp *interp, /* Current interpreter. */ int argc, /* Number of arguments. */ Tcl_Obj *CONST args[]) { ZnList ids; int index; int *id_array, id_num, i; Tcl_Obj *l; static char *sub_cmd_strings[] = { "ids", "load", NULL }; enum sub_cmds { ZN_V_IDS, ZN_V_LOAD }; if (!inited) { InitZinc(interp); } if (argc < 2) { Tcl_WrongNumArgs(interp, 1, args, "?subCmd? filename $args?"); return ZN_ERROR; } if (Tcl_GetIndexFromObj(interp, args[1], sub_cmd_strings, "subCmd", 0, &index) != ZN_OK) { return ZN_ERROR; } switch((enum sub_cmds) index) { /* * ids */ case ZN_V_IDS: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args,"ids filename"); return ZN_ERROR; } ids = MapInfoVideomapIds(Tcl_GetString(args[2])); if (ids == NULL) { Tcl_AppendResult(interp, "unable to look at videomap file \"", Tcl_GetString(args[2]), "\"", NULL); return ZN_ERROR; } id_array = (int *) ZnListArray(ids); id_num = ZnListSize(ids); l = Tcl_GetObjResult(interp); for (i = 0; i < id_num; i++) { Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(id_array[i])); } ZnListFree(ids); } break; /* * load */ case ZN_V_LOAD: { MapInfoId map_info; int insert; if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "load filename index mapInfo"); return ZN_ERROR; } if (Tcl_GetIntFromObj(interp, args[3], &insert) == ZN_ERROR) { return ZN_ERROR; } if (insert < 0) { insert = 0; } if (ZnCreateMapInfo(interp, Tcl_GetString(args[4]), &map_info) == ZN_ERROR) { return ZN_ERROR; } if (MapInfoGetVideomap(map_info, Tcl_GetString(args[2]), insert) == ZN_ERROR) { Tcl_AppendResult(interp, "unable to load videomap file \"", Tcl_GetString(args[2]), ":", Tcl_GetString(args[3]), "\"", NULL); return ZN_ERROR; } ZnUpdateMapInfoClients(map_info); } break; } return TCL_OK; } static void InitZinc(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"); and_uid = Tk_GetUid("&&"); or_uid = Tk_GetUid("||"); xor_uid = Tk_GetUid("^"); paren_uid = Tk_GetUid("("); end_paren_uid = Tk_GetUid(")"); neg_paren_uid = Tk_GetUid("!("); tag_val_uid = Tk_GetUid("!!"); neg_tag_val_uid = Tk_GetUid("!"); inited = True; } /* *---------------------------------------------------------------------- * * Tkzinc_Init -- * * This procedure is invoked by Tcl_AppInit in tkAppInit.c to * initialize the widget. * *---------------------------------------------------------------------- */ int Tkzinc_Init(Tcl_Interp *interp) /* Used for error reporting. */ { if (!Tk_MainWindow(interp)) { printf("Tk main window not created"); return ZN_ERROR; } /* * Create additional commands */ if (Tcl_PkgProvide(interp, "zinc", "3.0") == ZN_ERROR) { return ZN_ERROR; } Tcl_CreateObjCommand(interp, "zinc", ZincObjCmd, (ClientData) Tk_MainWindow(interp), (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "mapinfo", MapInfoObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "videomap", VideomapObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); return TCL_OK; } int Tkzinc_debug_Init(Tcl_Interp *interp) /* Used for error reporting. */ { return Tkzinc_Init(interp); }