/* * 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 and code excerpts in this file are from tkCanvas.c * and thus copyrighted: * * Copyright (c) 1991-1994 The Regents of the University of California. * Copyright (c) 1994-1997 Sun Microsystems, Inc. * Copyright (c) 1998-1999 by Scriptics Corporation. * */ static const char rcs_id[]="$Id$"; static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $"; static const char * const zinc_version = "zinc-version-" VERSION; #include "Types.h" #include "Geo.h" #include "Item.h" #include "Group.h" #include "WidgetInfo.h" #include "tkZinc.h" #include "MapInfo.h" #include "OverlapMan.h" #include "Track.h" #include "Transfo.h" #include "Image.h" #include "Draw.h" #include "Color.h" #ifndef _WIN32 #include "perfos.h" #endif #include "libtess/glu.h" #include #include #include #include #include #include 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 SYMBOL_WIDTH 8 #define SYMBOL_HEIGHT 8 static unsigned 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[ZN_NUM_ALPHA_STEPS][32][4]; 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; static Tk_Uid dot_uid; static Tk_Uid star_uid; static int ZnReliefParse _ANSI_ARGS_((ClientData client_data, Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj *ovalue, char *widget_rec, int offset)); static Tcl_Obj *ZnReliefPrint _ANSI_ARGS_((ClientData client_data, Tk_Window tkwin, char *widget_rec, int offset, Tcl_FreeProc **free_proc)); static int ZnGradientParse _ANSI_ARGS_((ClientData client_data, Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj *ovalue, char *widget_rec, int offset)); static Tcl_Obj *ZnGradientPrint _ANSI_ARGS_((ClientData client_data, Tk_Window tkwin, char *widget_rec, int offset, Tcl_FreeProc **free_proc)); static int ZnImageParse _ANSI_ARGS_((ClientData client_data, Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj *ovalue, char *widget_rec, int offset)); static int ZnBitmapParse _ANSI_ARGS_((ClientData client_data, Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj *ovalue, char *widget_rec, int offset)); static Tcl_Obj *ZnImagePrint _ANSI_ARGS_((ClientData client_data, Tk_Window tkwin, char *widget_rec, int offset, Tcl_FreeProc **free_proc)); static Tk_CustomOption reliefOption = { (Tk_OptionParseProc *) ZnReliefParse, (Tk_OptionPrintProc *) ZnReliefPrint, NULL }; static Tk_CustomOption gradientOption = { (Tk_OptionParseProc *) ZnGradientParse, (Tk_OptionPrintProc *) ZnGradientPrint, NULL }; static Tk_CustomOption imageOption = { (Tk_OptionParseProc *) ZnImageParse, (Tk_OptionPrintProc *) ZnImagePrint, NULL }; static Tk_CustomOption bitmapOption = { (Tk_OptionParseProc *) ZnBitmapParse, (Tk_OptionPrintProc *) ZnImagePrint, NULL }; /* * Information used for argv parsing. */ static Tk_ConfigSpec config_specs[] = { {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", "2", Tk_Offset(ZnWInfo, border_width), 0, NULL}, {TK_CONFIG_CUSTOM, "-backcolor", "backColor", "BackColor", "#c3c3c3", Tk_Offset(ZnWInfo, back_color), 0, &gradientOption}, {TK_CONFIG_BOOLEAN, "-confine", "confine", "Confine", "1", Tk_Offset(ZnWInfo, confine), 0, NULL}, {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor", "", Tk_Offset(ZnWInfo, cursor), TK_CONFIG_NULL_OK, NULL}, {TK_CONFIG_FONT, "-font", "font", "Font", "-adobe-helvetica-bold-r-normal--*-120-*-*-*-*-*-*", Tk_Offset(ZnWInfo, font), 0, NULL}, {TK_CONFIG_CUSTOM, "-forecolor", "foreColor", "Foreground", "Black", Tk_Offset(ZnWInfo, fore_color), 0, &gradientOption}, #ifndef _WIN32 {TK_CONFIG_BOOLEAN, "-fullreshape", "fullReshape", "FullReshape", "1", Tk_Offset(ZnWInfo, full_reshape), 0, NULL}, #endif {TK_CONFIG_PIXELS, "-height", "height", "Height", "7c", Tk_Offset(ZnWInfo, opt_height), 0, NULL}, {TK_CONFIG_CUSTOM, "-highlightbackground", "highlightBackground", "HighlightBackground", "#c3c3c3", Tk_Offset(ZnWInfo, highlight_bg_color), 0, &gradientOption}, {TK_CONFIG_CUSTOM, "-highlightcolor", "highlightColor", "HighlightColor", "Black", Tk_Offset(ZnWInfo, highlight_color), 0, &gradientOption}, {TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness", "HighlightThickness", "2", Tk_Offset(ZnWInfo, highlight_width), 0, NULL}, {TK_CONFIG_CUSTOM, "-insertbackground", "insertBackground", "Foreground", "Black", Tk_Offset(ZnWInfo, text_info.insert_color), 0, &gradientOption}, {TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime", "300", Tk_Offset(ZnWInfo, insert_off_time), 0, NULL}, {TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime", "600", Tk_Offset(ZnWInfo, insert_on_time), 0, NULL}, {TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth", "2", Tk_Offset(ZnWInfo, text_info.insert_width), 0, NULL}, {TK_CONFIG_CUSTOM, "-mapdistancesymbol", "mapDistanceSymbol", "MapDistanceSymbol", "AtcSymbol19", Tk_Offset(ZnWInfo, map_distance_symbol), TK_CONFIG_NULL_OK, &bitmapOption}, {TK_CONFIG_FONT, "-maptextfont", "mapTextFont", "MapTextFont", "-adobe-helvetica-bold-r-normal--*-120-*-*-*-*-*-*", Tk_Offset(ZnWInfo, map_text_font), 0, NULL}, #ifdef OM {TK_CONFIG_INT, "-overlapmanager", "overlapManager", "OverlapManager", "1", Tk_Offset(ZnWInfo, om_group_id), 0, NULL}, #endif {TK_CONFIG_INT, "-pickaperture", "pickAperture", "PickAperture", "1", Tk_Offset(ZnWInfo, pick_aperture), 0, NULL}, {TK_CONFIG_CUSTOM, "-relief", "relief", "Relief", "flat", Tk_Offset(ZnWInfo, relief), 0, &reliefOption}, {TK_CONFIG_INT, "-render", "render", "Render", "0", Tk_Offset(ZnWInfo, render), 0, NULL}, {TK_CONFIG_BOOLEAN, "-reshape", "reshape", "Reshape", "1", Tk_Offset(ZnWInfo, reshape), 0, NULL}, {TK_CONFIG_STRING, "-scrollregion", "scrollRegion", "ScrollRegion", "", Tk_Offset(ZnWInfo, region), TK_CONFIG_NULL_OK, NULL}, {TK_CONFIG_CUSTOM, "-selectbackground", "selectBackground", "Foreground", "#a0a0a0", Tk_Offset(ZnWInfo, text_info.sel_color), 0, &gradientOption}, {TK_CONFIG_DOUBLE, "-speedvectorlength", "speedVectorLength", "SpeedVectorLength", "3", Tk_Offset(ZnWInfo, speed_vector_length), 0, NULL}, {TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus", NULL, Tk_Offset(ZnWInfo, take_focus), TK_CONFIG_NULL_OK, NULL}, {TK_CONFIG_CUSTOM, "-tile", "tile", "Tile", "", Tk_Offset(ZnWInfo, tile), 0, &imageOption}, {TK_CONFIG_BOOLEAN, "-trackmanagehistory", "trackManageHistory", "TrackManageHistory", "1", Tk_Offset(ZnWInfo, track_manage_history), 0, NULL}, {TK_CONFIG_INT, "-trackmanagedhistorysize", "trackManagedHistorySize", "TrackManagedHistorySize", "6", Tk_Offset(ZnWInfo, track_managed_history_size), 0, NULL}, {TK_CONFIG_PIXELS, "-width", "width", "Width", "10c", Tk_Offset(ZnWInfo, opt_width), 0, NULL}, {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand", "", Tk_Offset(ZnWInfo, x_scroll_cmd), TK_CONFIG_NULL_OK, NULL}, {TK_CONFIG_PIXELS, "-xscrollincrement", "xScrollIncrement", "ScrollIncrement", "0", Tk_Offset(ZnWInfo, x_scroll_incr), 0, NULL}, {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand", "", Tk_Offset(ZnWInfo, y_scroll_cmd), TK_CONFIG_NULL_OK, NULL}, {TK_CONFIG_PIXELS, "-yscrollincrement", "yScrollIncrement", "ScrollIncrement", "0", Tk_Offset(ZnWInfo, y_scroll_incr), 0, NULL}, /* * Debug options. */ {TK_CONFIG_BOOLEAN, "-drawbboxes", "drawBBoxes", "DrawBBoxes", "0", Tk_Offset(ZnWInfo, draw_bboxes), 0, NULL}, {TK_CONFIG_CUSTOM, "-bboxcolor", "bboxColor", "BBoxColor", "Pink", Tk_Offset(ZnWInfo, bbox_color), 0, &gradientOption}, {TK_CONFIG_INT, "-lightangle", "lightAngle", "LightAngle", "120", Tk_Offset(ZnWInfo, light_angle), 0, NULL}, {TK_CONFIG_END, NULL, NULL, NULL, NULL, 0, 0, NULL} }; /* * These defines must be kept in sync with the config_specs array. */ #define BORDER_WIDTH_SPEC 0 #define BACK_COLOR_SPEC 1 #define CONFINE_SPEC 2 #define CURSOR_SPEC 3 #define FONT_SPEC 4 #define FORE_COLOR_SPEC 5 #define FULL_RESHAPE_SPEC 6 #define HEIGHT_SPEC 7 #define HIGHLIGHT_BACK_COLOR_SPEC 8 #define HIGHLIGHT_COLOR_SPEC 9 #define HIGHLIGHT_THICKNESS_SPEC 10 #define INSERT_COLOR_SPEC 11 #define INSERT_OFF_TIME_SPEC 12 #define INSERT_ON_TIME_SPEC 13 #define INSERT_WIDTH_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 RENDER_SPEC 20 #define RESHAPE_SPEC 21 #define SCROLL_REGION_SPEC 22 #define SELECT_COLOR_SPEC 23 #define SPEED_VECTOR_LENGTH_SPEC 24 #define TAKE_FOCUS_SPEC 25 #define TILE_SPEC 26 #define MANAGE_HISTORY_SPEC 27 #define MANAGED_HISTORY_SIZE_SPEC 28 #define WIDTH_SPEC 29 #define X_SCROLL_CMD_SPEC 30 #define X_SCROLL_INCREMENT_SPEC 31 #define Y_SCROLL_CMD_SPEC 32 #define Y_SCROLL_INCREMENT_SPEC 33 #define BBOXES_SPEC 34 #define BBOXES_COLOR_SPEC 35 #define LIGHT_ANGLE_SPEC 36 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_((ZnItem item, int field, 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, ZnWInfo *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_((ZnWInfo *wi, ZnBool got_focus)); static void Update _ANSI_ARGS_((ZnWInfo *wi)); static void Repair _ANSI_ARGS_((ZnWInfo *wi)); /* *---------------------------------------------------------------------- * * ZnReliefParse * ZnReliefPrint -- * Converter for the -relief option. * *---------------------------------------------------------------------- */ static int ZnReliefParse(ClientData client_data __unused, Tcl_Interp *interp __unused, Tk_Window tkwin __unused, Tcl_Obj *ovalue, char *widget_rec, int offset) { ZnReliefStyle *relief_ptr = (ZnReliefStyle *) (widget_rec + offset); ZnReliefStyle relief; #ifdef PTK char *value = Tcl_GetString(ovalue); #else char *value = (char *) ovalue; #endif int result = TCL_OK; if (value != NULL) { result = ZnGetRelief((ZnWInfo *) widget_rec, value, &relief); if (result == TCL_OK) { *relief_ptr = relief; } } return result; } static Tcl_Obj * ZnReliefPrint(ClientData client_data __unused, Tk_Window tkwin __unused, char *widget_rec, int offset, Tcl_FreeProc **free_proc __unused) { ZnReliefStyle relief = *(ZnReliefStyle *) (widget_rec + offset); #ifdef PTK return Tcl_NewStringObj(ZnNameOfRelief(relief), -1); #else return (Tcl_Obj *) ZnNameOfRelief(relief); #endif } /* *---------------------------------------------------------------------- * * ZnGradientParse * ZnGradientPrint -- * Converter for the -*color* options. * *---------------------------------------------------------------------- */ static int ZnGradientParse(ClientData client_data __unused, Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj *ovalue, char *widget_rec, int offset) { ZnGradient **grad_ptr = (ZnGradient **) (widget_rec + offset); ZnGradient *grad, *prev_grad; #ifdef PTK char *value = Tcl_GetString(ovalue); #else char *value = (char *) ovalue; #endif prev_grad = *grad_ptr; if ((value != NULL) && (*value != '\0')) { grad = ZnGetGradient(interp, tkwin, value); if (grad == NULL) { return TCL_ERROR; } if (prev_grad != NULL) { ZnFreeGradient(prev_grad); } *grad_ptr = grad; } return TCL_OK; } static Tcl_Obj * ZnGradientPrint(ClientData client_data __unused, Tk_Window tkwin __unused, char *widget_rec, int offset, Tcl_FreeProc **free_proc __unused) { ZnGradient *gradient = *(ZnGradient **) (widget_rec + offset); #ifdef PTK return Tcl_NewStringObj(ZnNameOfGradient(gradient), -1); #else return (Tcl_Obj *) ZnNameOfGradient(gradient); #endif } /* *---------------------------------------------------------------------- * * ZnBitmapParse * ZnImageParse * ZnImagePrint -- * Converter for the -*image* options. * *---------------------------------------------------------------------- */ static int ZnBitmapParse(ClientData client_data __unused, Tcl_Interp *interp __unused, Tk_Window tkwin __unused, Tcl_Obj *ovalue, char *widget_rec, int offset) { ZnImage *image_ptr = (ZnImage *) (widget_rec + offset); ZnImage image, prev_image; #ifdef PTK char *value = Tcl_GetString(ovalue); #else char *value = (char *) ovalue; #endif ZnWInfo *wi = (ZnWInfo*) widget_rec; prev_image = *image_ptr; if ((value != NULL) && (*value != '\0')) { image = ZnGetBitmap(wi, value); if (image == NULL) { return TCL_ERROR; } if (prev_image != NULL) { ZnFreeImage(prev_image); } *image_ptr = image; } else if (prev_image != NULL) { ZnFreeImage(prev_image); *image_ptr = NULL; } return TCL_OK; } static int ZnImageParse(ClientData client_data __unused, Tcl_Interp *interp __unused, Tk_Window tkwin __unused, Tcl_Obj *ovalue, char *widget_rec, int offset) { ZnImage *image_ptr = (ZnImage *) (widget_rec + offset); ZnImage image, prev_image; #ifdef PTK char *value = Tcl_GetString(ovalue); #else char *value = (char *) ovalue; #endif ZnWInfo *wi = (ZnWInfo*) widget_rec; prev_image = *image_ptr; if ((value != NULL) && (*value != '\0')) { image = ZnGetImage(wi, value); if (image == NULL) { return TCL_ERROR; } if (prev_image != NULL) { ZnFreeImage(prev_image); } *image_ptr = image; } else if (prev_image != NULL) { ZnFreeImage(prev_image); *image_ptr = NULL; } return TCL_OK; } static Tcl_Obj * ZnImagePrint(ClientData client_data __unused, Tk_Window tkwin __unused, char *widget_rec, int offset, Tcl_FreeProc **free_proc __unused) { ZnImage image = *(ZnImage *) (widget_rec + offset); #ifdef PTK return Tcl_NewStringObj(image?ZnNameOfImage(image):"", -1); #else return (Tcl_Obj *) (image?ZnNameOfImage(image):""); #endif } /* *---------------------------------------------------------------------- * * ZnGetAlphaStipple -- * Need to be handled per screen/dpy toolkit wide, not on a * widget basis. * *---------------------------------------------------------------------- */ static Pixmap ZnGetAlphaStipple(ZnWInfo *wi, unsigned int val) { if (val >= 255) return None; else return wi->alpha_stipples[(int) (val / 16)]; } /* *---------------------------------------------------------------------- * * ZnGetInactiveStipple -- * *---------------------------------------------------------------------- */ Pixmap ZnGetInactiveStipple(ZnWInfo *wi) { return ZnGetAlphaStipple(wi, 128); } /* *---------------------------------------------------------------------- * * ZnNeedRedisplay -- * *---------------------------------------------------------------------- */ void ZnNeedRedisplay(ZnWInfo *wi) { if (ISCLEAR(wi->flags, ZN_UPDATE_PENDING) && ISSET(wi->flags, ZN_REALIZED)) { /*printf("scheduling an update\n");*/ Tcl_DoWhenIdle(Redisplay, (ClientData) wi); SET(wi->flags, ZN_UPDATE_PENDING); } } /* *---------------------------------------------------------------------- * * 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; ZnWInfo *wi; Tk_Window tkwin; unsigned int num; ZnBool has_gl = False; #ifndef _WIN32 # if defined(GL) || defined(SHAPE) int major_op, first_err, first_evt; # endif # ifdef GL int major_glx, minor_glx; Display *dpy = Tk_Display(top_w); Screen *screen = Tk_Screen(top_w); int attribs[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_BUFFER_SIZE, 24, /*GLX_BUFFER_SIZE, 32,*/ GLX_STENCIL_SIZE, 8, /*GLX_ALPHA_SIZE, 8,*/ None }; # endif #endif InitZinc(interp); #ifdef GL # ifdef _WIN32 has_gl = True; # else if (XQueryExtension(dpy, "GLX", &major_op, &first_evt, &first_err)) { if (glXQueryExtension(dpy, &first_err, &first_evt)) { if (glXQueryVersion(dpy, &major_glx, &minor_glx)) { if ((major_glx == 1) && (minor_glx >= 1)) { has_gl = True; } } } } # endif #endif if (argc == 1) { Tcl_AppendResult(interp, VERSION, NULL); Tcl_AppendResult(interp, " X11", NULL); #ifdef GL # ifdef _WIN32 Tcl_AppendResult(interp, " GL", NULL); # else if (has_gl && glXChooseVisual(dpy, XScreenNumberOfScreen(screen), attribs)) { Tcl_AppendResult(interp, " GL", NULL); } # endif #endif return TCL_OK; } tkwin = Tk_CreateWindowFromPath(interp, top_w, Tcl_GetString(args[1]), NULL); if (tkwin == NULL) { return TCL_ERROR; } Tk_SetClass(tkwin, "Zinc"); /* * Allocate and initialize the widget record. */ wi = (ZnWInfo *) ZnMalloc(sizeof(ZnWInfo)); wi->win = tkwin; wi->interp = interp; wi->dpy = Tk_Display(tkwin); wi->screen = Tk_Screen(tkwin); ASSIGN(wi->flags, ZN_HAS_GL, has_gl); #if defined(SHAPE) && !defined(_WIN32) ASSIGN(wi->flags, ZN_HAS_X_SHAPE, XQueryExtension(wi->dpy, "SHAPE", &major_op, &first_evt, &first_err)); wi->reshape = wi->full_reshape = True; #else CLEAR(wi->flags, ZN_HAS_X_SHAPE); wi->reshape = wi->full_reshape = False; #endif wi->render = 0; 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->flags = 0; wi->binding_table = 0; wi->fore_color = NULL; wi->back_color = NULL; wi->relief_grad = NULL; wi->bbox_color = NULL; wi->draw_bboxes = 0; wi->light_angle = 120; wi->border_width = 0; wi->relief = ZN_RELIEF_FLAT; wi->opt_width = None; wi->opt_height = None; wi->font = 0; wi->map_text_font = 0; #ifdef GL wi->font_tfi = NULL; wi->map_font_tfi = NULL; wi->gl_context = NULL; # ifndef _WIN32 wi->gl_visual = NULL; # endif #endif 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->confine = 0; wi->origin.x = wi->origin.y = 0; wi->scroll_xo = wi->scroll_yo = 0; wi->scroll_xc = wi->scroll_yc = 0; wi->x_scroll_incr = wi->y_scroll_incr = 0; wi->x_scroll_cmd = wi->y_scroll_cmd = NULL; wi->region = 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_STRING_KEYS); wi->obj_id = 1; wi->num_items = 0; wi->top_group = ZnCreateItem(wi, ZnGroup, 0, NULL); #ifdef OM wi->om_group_id = 0; wi->om_group = wi->top_group; OmRegister((void *) wi, ZnSendTrackToOm, ZnSetLabelAngleFromOm, ZnQueryLabelPosition); #endif wi->gc = 0; wi->draw_buffer = 0; wi->pick_aperture = 0; wi->new_item = wi->current_item = ZN_NO_ITEM; wi->new_part = wi->current_part = ZN_NO_PART; wi->focus_item = ZN_NO_ITEM; wi->focus_field = ZN_NO_PART; CLEAR(wi->flags, ZN_MONITORING); #ifndef _WIN32 wi->total_draw_chrono = ZnNewChrono("Total draw time"); wi->this_draw_chrono = ZnNewChrono("Last draw time"); #endif wi->damaged_area_w = wi->damaged_area_h = 0; wi->work_item_list = NULL; wi->work_pts = ZnListNew(8, sizeof(ZnPoint)); #ifdef GL wi->work_doubles = ZnListNew(8, sizeof(double)); #endif wi->work_xpts = ZnListNew(8, sizeof(XPoint)); wi->work_strs = ZnListNew(8, sizeof(char *)); /* * Text management init. */ wi->text_info.sel_color = NULL; wi->text_info.sel_item = ZN_NO_ITEM; wi->text_info.sel_field = ZN_NO_PART; wi->text_info.sel_first = -1; wi->text_info.sel_last = -1; wi->text_info.anchor_item = ZN_NO_ITEM; wi->text_info.anchor_field = ZN_NO_PART; wi->text_info.sel_anchor = 0; wi->text_info.insert_color = NULL; wi->text_info.insert_width = 0; 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; ZnInitClipStack(wi); ZnInitTransformStack(wi); for (num = 0; num < ZN_NUM_ALPHA_STEPS; num++) { char name[TCL_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 TCL_ERROR; } ZnResetBBox(&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 a GLU tesselator. */ wi->tess = gluNewTess(); wi->tess_combine_list = NULL; /* * Allocate double buffer pixmap/image. */ if (wi->render) { #ifdef GL wi->gl_context = 0; ASSIGN(wi->flags, ZN_PRINT_CONFIG, (getenv("ZINC_GLX_INFO") != NULL)); if (ISSET(wi->flags, ZN_HAS_GL)) { # ifdef _WIN32 PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd 1, // version number PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | // support OpenGL PFD_DOUBLEBUFFER, // double buffered PFD_TYPE_RGBA, // RGBA type 0, // Color bits ignored 8, 0, 8, 0, 8, 0, 8, 0, // R, G, B, A (color shifts ignored ) 0, // accum bits ignored 0, 0, 0, 0, // accum R, G, B, A ignored 0, // no depth buffer 8, // 8 bits stencil buffer 0, // no auxiliary buffer PFD_MAIN_PLANE, // main layer 0, // reserved 0, 0, 0 // layer masks ignored }; int ipixel; Tk_MakeWindowExist(wi->win); wi->hwnd = Tk_GetHWND(Tk_WindowId(wi->win)); wi->hdc = GetDC(wi->hwnd); if (!wi->hdc) { OutputDebugString("Unable to get the hdc\n"); } ipixel = ChoosePixelFormat(wi->hdc, &pfd); /* sprintf(msg, "ipixel=%d dwFlags=0x%x req=0x%x iPixelType=%d\n", ipixel, pfd.dwFlags, PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL|PFD_DOUBLEBUFFER, pfd.iPixelType==PFD_TYPE_RGBA); OutputDebugString(msg);*/ if (!ipixel) { OutputDebugString("ChoosePixelFormat failed\n"); } wi->render = (SetPixelFormat(wi->hdc, ipixel, &pfd) == TRUE); if (wi->render) { wi->gl_context = wglCreateContext(wi->hdc); if (!wi->gl_context) { OutputDebugString("wglCreateContext failed\n"); } } ZnGLRelease(wi); # else /* _WIN32 */ int val; if (ISSET(wi->flags, ZN_PRINT_CONFIG)) { fprintf(stderr, "GLX version %d.%d\n", major_glx, minor_glx); } wi->gl_visual = glXChooseVisual(wi->dpy, XScreenNumberOfScreen(wi->screen), attribs); if (!wi->gl_visual) { fprintf(stderr, "No glx visual\n"); wi->render = 0; } else { wi->gl_context = glXCreateContext(wi->dpy, wi->gl_visual, NULL, wi->render==1); if (!wi->gl_context) { fprintf(stderr, "No glx context\n"); wi->render = 0; } else { if (ISSET(wi->flags, ZN_PRINT_CONFIG)) { fprintf(stderr, " Visual : 0x%x, ", (int) wi->gl_visual->visualid); glXGetConfig(wi->dpy, wi->gl_visual, GLX_RGBA, &val); fprintf(stderr, "RGBA : %d, ", val); glXGetConfig(wi->dpy, wi->gl_visual, GLX_DOUBLEBUFFER, &val); fprintf(stderr, "Double Buffer : %d, ", val); glXGetConfig(wi->dpy, wi->gl_visual, GLX_STENCIL_SIZE, &val); fprintf(stderr, "Stencil : %d, ", val); glXGetConfig(wi->dpy, wi->gl_visual, GLX_BUFFER_SIZE, &val); fprintf(stderr, "depth : %d, ", val); glXGetConfig(wi->dpy, wi->gl_visual, GLX_RED_SIZE, &val); fprintf(stderr, "red : %d, ", val); glXGetConfig(wi->dpy, wi->gl_visual, GLX_GREEN_SIZE, &val); fprintf(stderr, "green : %d, ", val); glXGetConfig(wi->dpy, wi->gl_visual, GLX_BLUE_SIZE, &val); fprintf(stderr, "blue : %d, ", val); glXGetConfig(wi->dpy, wi->gl_visual, GLX_ALPHA_SIZE, &val); fprintf(stderr, "alpha : %d\n", val); fprintf(stderr, " Direct Rendering: %d\n", glXIsDirect(wi->dpy, wi->gl_context)); } Tk_SetWindowVisual(wi->win, wi->gl_visual->visual, 24, XCreateColormap(wi->dpy, RootWindowOfScreen(wi->screen), wi->gl_visual->visual, AllocNone)); } } # endif /* _WIN32 */ } else { fprintf(stderr, "GL not available\n"); wi->render = 0; } #else /* GL */ wi->render = 0; #endif /* GL */ } if (!wi->render) { wi->draw_buffer = Tk_GetPixmap(wi->dpy, RootWindowOfScreen(wi->screen), wi->width, wi->height, Tk_Depth(wi->win)); } #ifdef PTK Tcl_SetObjResult(interp, LangWidgetObj(interp, tkwin)); #else Tcl_SetObjResult(interp, Tcl_NewStringObj(Tk_PathName(tkwin), -1)); #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(ZnItem item, int part) { if (part >= 0) { ZnFieldSet fs; if (!item->class->GetFieldSet) { return item; } fs = item->class->GetFieldSet(item); return (ClientData) (ZnFIELD.GetFieldStruct(fs, part % (int) ZnFIELD.NumFields(fs))); } 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. */ ZnTagSearch *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 TCL_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 TCL_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 TCL_ERROR; } if (! (tag - search->rewrite_buf)) { Tcl_AppendResult(interp, "Null quoted tag string in tag search expression", (char *) NULL); return TCL_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 TCL_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 TCL_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 TCL_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 TCL_ERROR; } } } breakwhile: if (found_tag && ! looking_for_tag) { return TCL_OK; } Tcl_AppendResult(interp, "Missing tag in tag search expression", (char *) NULL); return TCL_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 */ ZnItem 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; 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++]; /* * set result 1 if tag is found in item's tags */ result = ZnITEM.HasTag(item, uid) ? 1 : 0; } else if (uid == neg_tag_val_uid) { negate_result = ! negate_result; /* * assert(expr->index < expr->length); */ uid = expr->uids[expr->index++]; /* * set result 1 if tag is found in item's tags */ result = ZnITEM.HasTag(item, uid) ? 1 : 0; } 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; } static ZnItem LookupGroupFromPath(ZnItem start, Tk_Uid *names, unsigned int num_names) { Tk_Uid name, *tags; unsigned int count; ZnBool recursive; ZnItem result, current = ZnGroupHead(start); if (num_names == 0) { return start; } name = names[1]; recursive = (names[0] == star_uid); /* printf("LookupGroupFromPath; group: %d, nom: %s, recursive: %s\n", start->id, name, names[0]);*/ while (current != ZN_NO_ITEM) { if ((current->class == ZnGroup) && (current->tags)) { tags = ZnListArray(current->tags); count = ZnListSize(current->tags); for (; count > 0; tags++, count--) { if (name == *tags) { if (num_names > 2) { result = LookupGroupFromPath(current, names+2, num_names-2); return result; } else { return current; } } } /* * This group doesn't match try to search depth first. */ if (recursive) { result = LookupGroupFromPath(current, names, num_names); if (result != ZN_NO_ITEM) { return result; } } } current = current->next; } return ZN_NO_ITEM; } /* *-------------------------------------------------------------- * * 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(ZnWInfo *wi, Tcl_Obj *tag_obj, /* Object giving tag value, NULL * is the same as 'all'. */ ZnTagSearch **search_var) /* Record describing tag search; * will be initialized here. */ { Tk_Uid tag; int i; ZnTagSearch *search; ZnItem group = wi->top_group; ZnBool recursive = True; 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 = (ZnTagSearch *) ZnMalloc(sizeof(ZnTagSearch)); 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(ZnItem)); } TagSearchExprInit(&(search->expr)); /* How long is the tagOrId ? */ search->tag_len = strlen(tag); /* * Short-circuit impossible searches for null tags and * mark the search as 'over' for ZnTagSearchFirst and * ZnTagSearchNext. This test must not be migrated before * allocating search structures or special care must be * taken in ZnTagSearchDestroy to avoid deallocating unallocated * memory. */ if (search->tag_len == 0) { search->over = True; return TCL_OK; } /* * If a path specification exists in the tag, strip it from the * tag and search for a matching group. */ if (strpbrk(tag, ".*")) { Tk_Uid path; char c, *next; long id; Tcl_HashEntry *entry; ZnListEmpty(wi->work_strs); recursive = False; if ((*tag == '.') || (*tag == '*')) { recursive = (*tag == '*'); tag++; } path = tag; while ((next = strpbrk(path, ".*"))) { if (isdigit(*path)) { if (path == tag) { /* Group id is ok only in first section. */ c = *next; *next = '\0'; id = strtoul(path, NULL, 10); *next = c; group = wi->hot_item; if ((group == ZN_NO_ITEM) || (group->id != id)) { entry = Tcl_FindHashEntry(wi->id_table, (char *) id); if (entry != NULL) { group = (ZnItem) Tcl_GetHashValue(entry); } else { Tcl_AppendResult(wi->interp, "unknown group in path \"", tag, "\"", NULL); return TCL_ERROR; } } if (group->class != ZnGroup) { Tcl_AppendResult(wi->interp, "item is not a group in path \"", tag, "\"", NULL); return TCL_ERROR; } } else { Tcl_AppendResult(wi->interp, "misplaced group id in path \"", tag, "\"", NULL); return TCL_ERROR; } } else { ZnListAdd(wi->work_strs, (void *) (recursive ? &star_uid : &dot_uid), ZnListTail); c = *next; *next = '\0'; path = Tk_GetUid(path); *next = c; ZnListAdd(wi->work_strs, (void *) &path, ZnListTail); } recursive = (*next == '*'); path = next+1; } group = LookupGroupFromPath(group, ZnListArray(wi->work_strs), ZnListSize(wi->work_strs)); if (group == ZN_NO_ITEM) { Tcl_AppendResult(wi->interp, "path does not lead to a valid group\"", tag, "\"", NULL); return TCL_ERROR; } /* * Adjust tag to strip the path. */ tag = path; search->tag_len = strlen(tag); /* * If the tag consist only in a path description * assume that the tag all is implied. */ if (search->tag_len == 0) { tag = all_uid; search->tag_len = strlen(tag); } } /* * Make sure there is enough buffer to hold rewritten tags (30%). */ if ((unsigned int)(search->tag_len*1.3) >= search->rewrite_buf_alloc) { search->rewrite_buf_alloc = (unsigned int) (search->tag_len*1.3); search->rewrite_buf = ZnRealloc(search->rewrite_buf, search->rewrite_buf_alloc); } /* Initialize search */ search->wi = wi; search->over = False; search->type = 0; search->group = group; search->recursive = recursive; 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 (isdigit(*tag)) { char *end; search->id = strtoul(tag, &end, 0); if (*end == 0) { search->type = 1; 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) != TCL_OK) { /* Syntax error in tag expression */ /* Result message set by TagSearchScanExpr */ return TCL_ERROR; } search->expr->length = search->expr->index; } else { /* * For all other tags convert to a UID. */ search->expr->uid = Tk_GetUid(tag); if (search->expr->uid == all_uid) { /* * All items match. */ search->type = 2; } else { /* * Optimized single-tag search */ search->type = 3; } } return TCL_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 ZnItem ZnTagSearchFirst(ZnTagSearch *search) /* Record describing tag search */ { ZnItem item, previous; /* short circuit impossible searches for null tags */ if (search->over == True) { return ZN_NO_ITEM; } /* * 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 = (ZnItem) 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 = ZnGroupHead(search->group); return search->current; } item = ZnGroupHead(search->group); previous = ZN_NO_ITEM; do { while (item != ZN_NO_ITEM) { if (search->type == 3) { /* * Optimized single-tag search */ if (ZnITEM.HasTag(item, search->expr->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)) { ZnItem prev_group = (ZnItem) search->group; /* * 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 = item; previous = item; if (item == prev_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 = ZnGroupHead(search->group); } 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 = *(ZnItem *) ZnListAt(search->item_stack, ZnListTail); ZnListDelete(search->item_stack, ZnListTail); previous = *(ZnItem *) ZnListAt(search->item_stack, ZnListTail); ZnListDelete(search->item_stack, ZnListTail); } if (item != ZN_NO_ITEM) { search->group = 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 ZnItem ZnTagSearchNext(ZnTagSearch *search) /* Record describing search in progress. */ { ZnItem item, previous; 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 = ZnGroupHead(search->group); } 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 = 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 = ZnGroupHead(search->group); } 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 = *(ZnItem *) ZnListAt(search->item_stack, ZnListTail); ZnListDelete(search->item_stack, ZnListTail); previous = *(ZnItem *) ZnListAt(search->item_stack, ZnListTail); ZnListDelete(search->item_stack, ZnListTail); } if (item != ZN_NO_ITEM) { search->group = 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; } do { while (item != ZN_NO_ITEM) { if (search->type == 3) { /* * Optimized single-tag search */ if (ZnITEM.HasTag(item, search->expr->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 = item; previous = item; item = item->next; ZnListAdd(search->item_stack, &previous, ZnListTail); ZnListAdd(search->item_stack, &item, ZnListTail); previous = ZN_NO_ITEM; item = ZnGroupHead(search->group); } 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 = *(ZnItem *) ZnListAt(search->item_stack, ZnListTail); ZnListDelete(search->item_stack, ZnListTail); previous = *(ZnItem *) ZnListAt(search->item_stack, ZnListTail); ZnListDelete(search->item_stack, ZnListTail); } if (item != ZN_NO_ITEM) { search->group = 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(ZnTagSearch *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(ZnWInfo *wi, Tcl_Obj *tag_or_id, ZnItem *item, ZnTagSearch **search_var) { if (ZnTagSearchScan(wi, tag_or_id, search_var) != TCL_OK) { return TCL_ERROR; } *item = ZnTagSearchFirst(*search_var); return TCL_OK; } /* *---------------------------------------------------------------------- * * LayoutItems -- * * Perform layouts on items. It can position items horizontally, * vertically, along a path or with respect to a reference item. * It can also align on a grid, evenly space items and resize * items to a common reference. * *---------------------------------------------------------------------- */ static int LayoutItems(ZnWInfo *wi, int argc __unused, Tcl_Obj *CONST args[]) { int index/*, result*/; /*ZnItem item;*/ #ifdef PTK static char *layout_cmd_strings[] = { #else static CONST char *layout_cmd_strings[] = { #endif "align", "grid", "position", "scale", "space", NULL }; enum layout_cmds { ZN_L_ALIGN, ZN_L_GRID, ZN_L_POSITION, ZN_L_SCALE, ZN_L_SPACE }; if (Tcl_GetIndexFromObj(wi->interp, args[0], layout_cmd_strings, "layout command", 0, &index) != TCL_OK) { return TCL_ERROR; } switch((enum layout_cmds) index) { /* * align */ case ZN_L_ALIGN: break; /* * grid */ case ZN_L_GRID: break; /* * position */ case ZN_L_POSITION: break; /* * scale */ case ZN_L_SCALE: break; /* * space */ case ZN_L_SPACE: break; } return TCL_OK; } /* *---------------------------------------------------------------------- * * SetOrigin -- * * This procedure is invoked to translate the viewed area so * that the given point is displayed in the top left corner. * * Results: * None. * * Side effects: * Zinc will be redisplayed to reflect the change in ciew. * The scrollbars will be updated if there are any. * The top group transform is modified to achieve the effect, * it is not a good idea to mix view control and application * control of the top group transform. * *---------------------------------------------------------------------- */ static void SetOrigin(ZnWInfo *wi, ZnReal x_origin, ZnReal y_origin) { int left, right, top, bottom, delta; /* * If scroll increments have been set, round the window origin * to the nearest multiple of the increments. */ if (wi->x_scroll_incr > 0) { if (x_origin >= 0) { x_origin += wi->x_scroll_incr/2; } else { x_origin = (-x_origin) + wi->x_scroll_incr/2; } } if (wi->y_scroll_incr > 0) { if (y_origin >= 0) { y_origin += wi->y_scroll_incr/2; } else { y_origin = (-y_origin) + wi->y_scroll_incr/2; } } /* * Adjust the origin if necessary to keep as much as possible of the * canvas in the view. The variables left, right, etc. keep track of * how much extra space there is on each side of the view before it * will stick out past the scroll region. If one side sticks out past * the edge of the scroll region, adjust the view to bring that side * back to the edge of the scrollregion (but don't move it so much that * the other side sticks out now). If scroll increments are in effect, * be sure to adjust only by full increments. */ if (wi->confine && (wi->region != NULL)) { left = x_origin - wi->scroll_xo; right = wi->scroll_xc - (x_origin + Tk_Width(wi->win)); top = y_origin - wi->scroll_yo; bottom = wi->scroll_yc - (y_origin + Tk_Height(wi->win)); if ((left < 0) && (right > 0)) { delta = (right > -left) ? -left : right; if (wi->x_scroll_incr > 0) { delta -= delta % wi->x_scroll_incr; } x_origin += delta; } else if ((right < 0) && (left > 0)) { delta = (left > -right) ? -right : left; if (wi->x_scroll_incr > 0) { delta -= delta % wi->x_scroll_incr; } x_origin -= delta; } if ((top < 0) && (bottom > 0)) { delta = (bottom > -top) ? -top : bottom; if (wi->y_scroll_incr > 0) { delta -= delta % wi->y_scroll_incr; } y_origin += delta; } else if ((bottom < 0) && (top > 0)) { delta = (top > -bottom) ? -bottom : top; if (wi->y_scroll_incr > 0) { delta -= delta % wi->y_scroll_incr; } y_origin -= delta; } } /* * If the requested origin is not already set, translate the * top group and update the scrollbars. */ if ((wi->origin.x != x_origin) || (wi->origin.y != y_origin)) { wi->origin.x = x_origin; wi->origin.y = y_origin; ZnITEM.ResetTransfo(wi->top_group); ZnITEM.TranslateItem(wi->top_group, -x_origin, -y_origin); SET(wi->flags, ZN_UPDATE_SCROLLBARS); } } /* *---------------------------------------------------------------------- * * ScrollFractions -- * * Given the range that's visible in the window and the "100% * range", return a list of two real representing the scroll * fractions. This procedure is used for both x and y scrolling. * * Results: * Return a string as a Tcl_Obj holding two real numbers * describing the scroll fraction (between 0 and 1) corresponding * to the arguments. * * Side effects: * None. * *---------------------------------------------------------------------- */ static Tcl_Obj * ScrollFractions(ZnReal view1, /* Lowest coordinate visible in the window. */ ZnReal view2, /* Highest coordinate visible in the window. */ ZnReal region1,/* Lowest coordinate in the object. */ ZnReal region2)/* Highest coordinate in the object. */ { ZnReal range, f1, f2; char buffer[2*TCL_DOUBLE_SPACE+2]; range = region2 - region1; if (range <= 0) { f1 = 0; f2 = 1.0; } else { f1 = (view1 - region1)/range; if (f1 < 0) { f1 = 0.0; } f2 = (view2 - region1)/range; if (f2 > 1.0) { f2 = 1.0; } if (f2 < f1) { f2 = f1; } } sprintf(buffer, "%g %g", f1, f2); return Tcl_NewStringObj(buffer, -1); } /* *-------------------------------------------------------------- * * UpdateScrollbars -- * * This procedure is invoked whenever zinc has changed in * a way that requires scrollbars to be redisplayed (e.g. * the view has changed). * * Results: * None. * * Side effects: * If there are scrollbars associated with zinc, then * their scrolling commands are invoked to cause them to * redisplay. If errors occur, additional Tcl commands may * be invoked to process the errors. * *-------------------------------------------------------------- */ static void UpdateScrollbars(ZnWInfo *wi) { int result; Tcl_Interp *interp; int x_origin, y_origin, width, height; int scroll_xo, scroll_xc, scroll_yo, scroll_yc; char *x_scroll_cmd, *y_scroll_cmd; Tcl_Obj *fractions; /* * Save all the relevant values from wi, because it might be * deleted as part of either of the two calls to Tcl_VarEval below. */ interp = wi->interp; Tcl_Preserve((ClientData) interp); x_scroll_cmd = wi->x_scroll_cmd; if (x_scroll_cmd != NULL) { Tcl_Preserve((ClientData) x_scroll_cmd); } y_scroll_cmd = wi->y_scroll_cmd; if (y_scroll_cmd != NULL) { Tcl_Preserve((ClientData) y_scroll_cmd); } x_origin = wi->origin.x; y_origin = wi->origin.y; width = Tk_Width(wi->win); height = Tk_Height(wi->win); scroll_xo = wi->scroll_xo; scroll_xc = wi->scroll_xc; scroll_yo = wi->scroll_yo; scroll_yc = wi->scroll_yc; CLEAR(wi->flags, ZN_UPDATE_SCROLLBARS); if (wi->x_scroll_cmd != NULL) { fractions = ScrollFractions(x_origin, x_origin + width, scroll_xo, scroll_xc); result = Tcl_VarEval(interp, x_scroll_cmd, " ", Tcl_GetString(fractions), NULL); Tcl_DecrRefCount(fractions); if (result != TCL_OK) { Tcl_BackgroundError(interp); } Tcl_ResetResult(interp); Tcl_Release((ClientData) x_scroll_cmd); } if (y_scroll_cmd != NULL) { fractions = ScrollFractions(y_origin, y_origin + height, scroll_yo, scroll_yc); result = Tcl_VarEval(interp, y_scroll_cmd, " ", Tcl_GetString(fractions), NULL); Tcl_DecrRefCount(fractions); if (result != TCL_OK) { Tcl_BackgroundError(interp); } Tcl_ResetResult(interp); Tcl_Release((ClientData) y_scroll_cmd); } Tcl_Release((ClientData) interp); } /* *---------------------------------------------------------------------- * * ZnDoItem -- * * 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 ZnDoItem(Tcl_Interp *interp, ZnItem item, int part, Tk_Uid tag_uid) { if (tag_uid == NULL) { Tcl_Obj *l; l = Tcl_GetObjResult(interp); Tcl_ListObjAppendElement(interp, l, Tcl_NewLongObj(item->id)); if (part != ZN_NO_PART) { Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(part)); } } else { /*printf("Adding tag %s to item %d\n", tag_uid, item->id);*/ ZnITEM.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 TCL_ERROR is returned. * *---------------------------------------------------------------------- */ static int FindArea(ZnWInfo *wi, Tcl_Obj *CONST args[], Tk_Uid tag_uid, ZnBool enclosed, ZnBool recursive, ZnItem group) { ZnPos pos; ZnBBox area; ZnToAreaStruct ta; if (Tcl_GetDoubleFromObj(wi->interp, args[0], &area.orig.x) == TCL_ERROR) { return TCL_ERROR; } if (Tcl_GetDoubleFromObj(wi->interp, args[1], &area.orig.y) == TCL_ERROR) { return TCL_ERROR; } if (Tcl_GetDoubleFromObj(wi->interp, args[2], &area.corner.x) == TCL_ERROR) { return TCL_ERROR; } if (Tcl_GetDoubleFromObj(wi->interp, args[3], &area.corner.y) == TCL_ERROR) { return TCL_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; ta.tag_uid = tag_uid; ta.enclosed = enclosed; ta.in_group = group; ta.recursive = recursive; ta.report = False; ta.area = &area; wi->top_group->class->ToArea(wi->top_group, &ta); return TCL_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(ZnWInfo *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 */ ZnTagSearch **search_var) { Tk_Uid tag_uid = NULL; int index, result; ZnItem item; ZnBool recursive = True; ZnPickStruct ps; #ifdef PTK static char *search_cmd_strings[] = { #else static CONST char *search_cmd_strings[] = { #endif "above", "ancestors", "atpriority", "below", "closest", "enclosed", "overlapping", "withtag", "withtype", NULL }; enum search_cmds { ZN_S_ABOVE, ZN_S_ANCESTORS, 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) != TCL_OK) { return TCL_ERROR; } if (tag) { tag_uid = Tk_GetUid(Tcl_GetString(tag)); } switch((enum search_cmds) index) { /* * above */ case ZN_S_ABOVE: { if (argc != first+2) { Tcl_WrongNumArgs(wi->interp, first+1, args, "tagOrId"); return TCL_ERROR; } result = ZnItemWithTagOrId(wi, args[first+1], &item, search_var); if (result == TCL_OK) { if ((item != ZN_NO_ITEM) && (item->previous != ZN_NO_ITEM)) { ZnDoItem(wi->interp, item->previous, ZN_NO_PART, tag_uid); } } else { return TCL_ERROR; } } break; /* * ancestors */ case ZN_S_ANCESTORS: { Tk_Uid uid = NULL; if ((argc != first+2) && (argc != first+3)) { Tcl_WrongNumArgs(wi->interp, first+1, args, "tagOrId ?withTag?"); return TCL_ERROR; } result = ZnItemWithTagOrId(wi, args[first+1], &item, search_var); if (result == TCL_ERROR) { return TCL_ERROR; } item = item->parent; if (argc == first+3) { uid = Tk_GetUid(Tcl_GetString(args[first+2])); } while (item != ZN_NO_ITEM) { if (!uid || ZnITEM.HasTag(item, uid)) { ZnDoItem(wi->interp, item, ZN_NO_PART, tag_uid); } item = item->parent; } } break; /* * atpriority */ case ZN_S_ATPRIORITY: { int pri; if ((argc != first+2) && (argc != first+3)) { Tcl_WrongNumArgs(wi->interp, first+1, args, "pri ?tagOrId?"); return TCL_ERROR; } if (Tcl_GetIntFromObj(wi->interp, args[first+1], &pri) == TCL_ERROR) { return TCL_ERROR; } /* * Go through the item table and collect all items with * the given priority. */ if (ZnTagSearchScan(wi, (argc == first+3) ? args[first+2] : NULL, search_var) == TCL_ERROR) { return TCL_ERROR; } for (item = ZnTagSearchFirst(*search_var); item != ZN_NO_ITEM; item = ZnTagSearchNext(*search_var)) { if (item->priority == pri) { ZnDoItem(wi->interp, item, ZN_NO_PART, tag_uid); } } } break; /* * below */ case ZN_S_BELOW: { ZnItem next; if (argc != first+2) { Tcl_WrongNumArgs(wi->interp, first+1, args, "tagOrId"); return TCL_ERROR; } item = ZN_NO_ITEM; if (ZnTagSearchScan(wi, args[first+1], search_var) == TCL_ERROR) { return TCL_ERROR; } for (next = ZnTagSearchFirst(*search_var); next != ZN_NO_ITEM; next = ZnTagSearchNext(*search_var)) { item = next; } if ((item != ZN_NO_ITEM) && (item->next != ZN_NO_ITEM)) { ZnDoItem(wi->interp, item->next, ZN_NO_PART, tag_uid); } } break; /* * closest */ case ZN_S_CLOSEST: { int halo = 1; ZnPoint p; if ((argc < first+3) || (argc > first+6)) { Tcl_WrongNumArgs(wi->interp, first+1, args, "x y ?halo? ?start?, ?recursive?"); return TCL_ERROR; } if (Tcl_GetDoubleFromObj(wi->interp, args[first+1], &p.x) == TCL_ERROR) { return TCL_ERROR; } if (Tcl_GetDoubleFromObj(wi->interp, args[first+2], &p.y) == TCL_ERROR) { return TCL_ERROR; } if (argc > first+3) { if (Tcl_GetIntFromObj(wi->interp, args[first+3], &halo) == TCL_ERROR) { return TCL_ERROR; } if (halo < 0) { halo = 0; } } ps.in_group = ZN_NO_ITEM; ps.start_item = ZN_NO_ITEM; item = ZN_NO_ITEM; if (argc > (first+4)) { result = ZnItemWithTagOrId(wi, args[first+4], &item, search_var); if ((result == TCL_OK) && (item != ZN_NO_ITEM)) { if ((item->class == ZnGroup) && !ZnGroupAtomic) { ps.in_group = item; } else { ps.in_group = item->parent; ps.start_item = item->next; } } } if (argc > first+5) { if (Tcl_GetBooleanFromObj(wi->interp, args[first+5], &recursive) != TCL_OK) { return TCL_ERROR; } } /* * 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. */ ps.aperture = halo; ps.point = &p; ps.recursive = recursive; wi->top_group->class->Pick(wi->top_group, &ps); if (ps.a_item != ZN_NO_ITEM) { ZnDoItem(wi->interp, ps.a_item, ps.a_part, tag_uid); /*printf("first %d %d\n", ps.a_item->id, ps.a_part);*/ } } break; /* * enclosed */ case ZN_S_ENCLOSED: { if ((argc < first+5) || (argc > first+7)) { Tcl_WrongNumArgs(wi->interp, first+1, args, "x1 y1 x2 y2 ?inGroup? ?recursive?"); return TCL_ERROR; } item = wi->top_group; if (argc > first+5) { result = ZnItemWithTagOrId(wi, args[first+5], &item, search_var); if ((result != TCL_OK) || (item == ZN_NO_ITEM) || (item->class != ZnGroup)) { return TCL_ERROR; } } if (argc > first+6) { if (Tcl_GetBooleanFromObj(wi->interp, args[first+6], &recursive) != TCL_OK) { return TCL_ERROR; } } return FindArea(wi, args+first+1, tag_uid, True, recursive, item); } break; /* * overlapping */ case ZN_S_OVERLAPPING: { if ((argc < first+5) || (argc > first+7)) { Tcl_WrongNumArgs(wi->interp, first+1, args, "x1 y1 x2 y2 ?inGroup? ?recursive?"); return TCL_ERROR; } item = wi->top_group; if (argc > first+5) { result = ZnItemWithTagOrId(wi, args[first+5], &item, search_var); if ((result != TCL_OK) || (item == ZN_NO_ITEM) || (item->class != ZnGroup)) { return TCL_ERROR; } } if (argc > first+6) { if (Tcl_GetBooleanFromObj(wi->interp, args[first+6], &recursive) != TCL_OK) { return TCL_ERROR; } } return FindArea(wi, args+first+1, tag_uid, False, recursive, item); } break; /* * withtag */ case ZN_S_WITHTAG: { if (argc != first+2) { Tcl_WrongNumArgs(wi->interp, first+1, args, "tagOrId"); return TCL_ERROR; } if (ZnTagSearchScan(wi, args[first+1], search_var) == TCL_ERROR) { return TCL_ERROR; } for (item = ZnTagSearchFirst(*search_var); item != ZN_NO_ITEM; item = ZnTagSearchNext(*search_var)) { ZnDoItem(wi->interp, item, ZN_NO_PART, tag_uid); } } break; /* * withtype */ case ZN_S_WITHTYPE: { ZnItemClass cls; if ((argc != first+2) && (argc != first+3)) { Tcl_WrongNumArgs(wi->interp, first+1, args, "itemType ?tagOrId?"); return TCL_ERROR; } cls = ZnLookupItemClass(Tcl_GetString(args[first+1])); if (!cls) { Tcl_AppendResult(wi->interp, "unknown item type \"", Tcl_GetString(args[first+1]), "\"", NULL); return TCL_ERROR; } /* * Go through the item table and collect all items with * the given item type. */ if (ZnTagSearchScan(wi, (argc == first+3) ? args[first+2] : NULL, search_var) == TCL_ERROR) { return TCL_ERROR; } for (item = ZnTagSearchFirst(*search_var); item != ZN_NO_ITEM; item = ZnTagSearchNext(*search_var)) { if (item->class == cls) { ZnDoItem(wi->interp, item, ZN_NO_PART, tag_uid); } } } break; } return TCL_OK; } /* *---------------------------------------------------------------------- * * ZnParseCoordList -- * *---------------------------------------------------------------------- */ int ZnParseCoordList(ZnWInfo *wi, Tcl_Obj *arg, ZnPoint **pts, char **controls, unsigned int *num_pts, ZnBool *old_format) { Tcl_Obj **elems, **selems; int i, result, num_elems, num_selems; ZnPoint *p; int old_style, len; char *str; ZnReal r; if (controls) { *controls = NULL; } if (old_format) { *old_format = True; } result = Tcl_ListObjGetElements(wi->interp, arg, &num_elems, &elems); if (result == TCL_ERROR) { coord_error: Tcl_AppendResult(wi->interp, " malformed coord list", NULL); return TCL_ERROR; } if (num_elems == 0) { *num_pts = 0; *pts = NULL; return TCL_OK; } /* * If first element is not a sublist, consider the whole list * as a flat array of coordinates in the old style. It can still * be a single point with or without a control flag. * If not, the list consists in sublists describing each point * with its control flag. */ result = Tcl_GetDoubleFromObj(wi->interp, elems[0], &r); old_style = (result == TCL_OK); if (old_style) { if ((num_elems%2) == 0) { *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) == TCL_ERROR) { goto coord_error; } if (Tcl_GetDoubleFromObj(wi->interp, elems[i+1], &p->y) == TCL_ERROR) { goto coord_error; } /*printf("Parsed a point: %g@%g, ", p->x, p->y);*/ } /*printf("\n");*/ } else if (num_elems == 3) { *num_pts = 1; ZnListAssertSize(wi->work_pts, *num_pts); *pts = p = (ZnPoint *) ZnListArray(wi->work_pts); if (Tcl_GetDoubleFromObj(wi->interp, elems[0], &p->x) == TCL_ERROR) { goto coord_error; } if (Tcl_GetDoubleFromObj(wi->interp, elems[1], &p->y) == TCL_ERROR) { goto coord_error; } if (controls) { if (! *controls) { *controls = ZnMalloc(*num_pts * sizeof(char)); memset(*controls, 0, *num_pts * sizeof(char)); } str = Tcl_GetStringFromObj(elems[2], &len); if (len) { (*controls)[0] = str[0]; } } } else { goto coord_error; } } else { Tcl_ResetResult(wi->interp); *num_pts = num_elems; ZnListAssertSize(wi->work_pts, *num_pts); *pts = p = (ZnPoint *) ZnListArray(wi->work_pts); for (i = 0; i < num_elems; i++, p++) { result = Tcl_ListObjGetElements(wi->interp, elems[i], &num_selems, &selems); if ((result == TCL_ERROR) || (num_selems < 2) || (num_selems > 3)) { goto coord_error; } if (Tcl_GetDoubleFromObj(wi->interp, selems[0], &p->x) == TCL_ERROR) { goto coord_error; } if (Tcl_GetDoubleFromObj(wi->interp, selems[1], &p->y) == TCL_ERROR) { goto coord_error; } if (controls) { if (num_selems == 3) { if (! *controls) { *controls = ZnMalloc(*num_pts * sizeof(char)); memset(*controls, 0, *num_pts * sizeof(char)); } str = Tcl_GetStringFromObj(selems[2], &len); if (len) { (*controls)[i] = str[0]; } } } } } if (old_format) { *old_format = old_style; } return TCL_OK; } /* *---------------------------------------------------------------------- * * Contour -- * *---------------------------------------------------------------------- */ static int Contour(ZnWInfo *wi, int argc, Tcl_Obj *CONST args[], ZnTagSearch **search_var) { ZnPoint *points; ZnItem item, shape; unsigned int i, j, k,num_points; int cmd, cw, result; int winding_flag, revert = False; long index; char *controls; ZnBool simple=False; ZnPoly poly; ZnTransfo t, inv; ZnContour *contours; /* Keep this array in sync with ZnContourCmd in Types.h */ #ifdef PTK static char *op_strings[] = { #else static CONST char *op_strings[] = { #endif "add", "remove", NULL }; result = ZnItemWithTagOrId(wi, args[2], &item, search_var); if ((result == TCL_ERROR) || (item == ZN_NO_ITEM)){ Tcl_AppendResult(wi->interp, "unknown item \"", Tcl_GetString(args[2]), "\"", NULL); return TCL_ERROR; } if (!item->class->Contour) { if (item->class->GetClipVertices || item->class->GetContours) { Tcl_SetObjResult(wi->interp, Tcl_NewIntObj(1)); } else { Tcl_SetObjResult(wi->interp, Tcl_NewIntObj(0)); } return TCL_OK; } if (argc == 3) { /* * Requesting the number of contours. */ Tcl_SetObjResult(wi->interp, Tcl_NewIntObj(item->class->Contour(item, -1, 0, NULL))); return TCL_OK; } /* * Get the sub-command */ if (Tcl_GetIndexFromObj(wi->interp, args[3], op_strings, "contour operation", 0, &cmd) != TCL_OK) { return TCL_ERROR; } /* * Get the winding flag. */ if ((Tcl_GetIntFromObj(wi->interp, args[4], &winding_flag) != TCL_OK) || (winding_flag < -1) || (winding_flag > 1)) { Tcl_AppendResult(wi->interp, " incorrect winding flag, should be -1, 0, 1, \"", Tcl_GetString(args[4]), "\"", NULL); return TCL_ERROR; } index = ZnListTail; if (((argc == 6) && (cmd == ZN_CONTOUR_REMOVE)) || (argc == 7)) { /* Look for an index value. */ if (Tcl_GetLongFromObj(wi->interp, args[5], &index) != TCL_OK) { Tcl_AppendResult(wi->interp, " incorrect contour index \"", Tcl_GetString(args[5]), "\"", NULL); return TCL_ERROR; } argc--; args++; } if (cmd == ZN_CONTOUR_REMOVE) { Tcl_SetObjResult(wi->interp, Tcl_NewIntObj(item->class->Contour(item, ZN_CONTOUR_REMOVE, index, NULL))); } else { result = ZnItemWithTagOrId(wi, args[5], &shape, search_var); if ((result == TCL_ERROR) || (shape == ZN_NO_ITEM)) { Tcl_ResetResult(wi->interp); if (ZnParseCoordList(wi, args[5], &points, &controls, &num_points, NULL) == TCL_ERROR) { return TCL_ERROR; } ZnPolyContour1(&poly, NULL, num_points, False); /* * Allocate a fresh point array, ZnParseCoordList returns a shared * array. The control array is not shared and can be passed along. */ poly.contours[0].points = ZnMalloc(num_points*sizeof(ZnPoint)); cw = poly.contours[0].cw = !ZnTestCCW(points, num_points); if (winding_flag != 0) { revert = cw ^ (winding_flag == -1); } if (revert) { /* Revert the contour */ for (i = 0; i < num_points; i++) { poly.contours[0].points[num_points-i-1] = points[i]; } if (controls) { char ch; for (i = 0, j = num_points-1; i < j; i++, j--) { ch = controls[i]; controls[i] = controls[j]; controls[j] = ch; } } } else { memcpy(poly.contours[0].points, points, num_points*sizeof(ZnPoint)); } poly.contours[0].controls = controls; } else { if (winding_flag == 0) { Tcl_AppendResult(wi->interp, "Must supply an explicit winding direction (-1, 1)\nwhen adding a contour from an item", NULL); return TCL_ERROR; } /* * If something has changed in the geometry we need to * update or the shape will be erroneous. */ Update(wi); if (!shape->class->GetContours && !shape->class->GetClipVertices) { noshape: Tcl_AppendResult(wi->interp, "class: \"", shape->class->name, "\" can't give a polygonal shape", NULL); return TCL_ERROR; } if (!shape->class->GetContours) { ZnTriStrip tristrip; /* * If there is no GetContours method try to use * the GetClipVertices. It works only for simple * shapes (i.e tose returning a bounding box). */ tristrip.num_strips = 0; simple = shape->class->GetClipVertices(shape, &tristrip); if (!simple) { goto noshape; } ZnPolyContour1(&poly, tristrip.strip1.points, tristrip.strip1.num_points, False); poly.contours[0].controls = NULL; } else { poly.num_contours = 0; simple = shape->class->GetContours(shape, &poly); } if (poly.num_contours == 0) { return TCL_OK; } /* * Compute the tranform to map the device points * into the coordinate space of item. */ ZnITEM.GetItemTransform(item, &t); ZnTransfoInvert(&t, &inv); /* * Make a new transformed poly and unshare * the contour(s) returned by the item. */ if (simple) { ZnPoint p[4]; p[0] = poly.contours[0].points[0]; p[2] = poly.contours[0].points[1]; if (winding_flag == -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; } else { p[1].x = p[0].x; p[1].y = p[2].y; p[3].x = p[2].x; p[3].y = p[0].y; } points = ZnMalloc(4*sizeof(ZnPoint)); ZnTransformPoints(&inv, p, points, 4); poly.contours[0].points = points; poly.contours[0].num_points = 4; poly.contours[0].cw = (winding_flag == -1); poly.contours[0].controls = NULL; } else { /* Unshare the contour array */ contours = poly.contours; if (poly.num_contours == 1) { poly.contours = &poly.contour1; } else { poly.contours = ZnMalloc(poly.num_contours*sizeof(ZnContour)); } for (i = 0; i < poly.num_contours; i++) { points = contours[i].points; num_points = contours[i].num_points; cw = contours[i].cw; poly.contours[i].num_points = num_points; poly.contours[i].cw = cw; if (contours[i].controls) { /* * The controls array returned by GetContour is shared. * Here we unshare it. */ poly.contours[i].controls = ZnMalloc(num_points*sizeof(char)); } /* * Unshare the point array. */ poly.contours[i].points = ZnMalloc(num_points*sizeof(ZnPoint)); if (((poly.num_contours == 1) && ((winding_flag == -1) ^ cw)) || ((poly.num_contours > 1) && (winding_flag == -1))) { ZnPoint p; /* Revert and transform the coords */ poly.contours[i].cw = ! cw; for (j = 0, k = num_points-1; j < k; j++, k--) { p = points[j]; points[j] = points[k]; points[k] = p; } ZnTransformPoints(&inv, points, poly.contours[i].points, num_points); /* Revert the controls */ if (contours[i].controls) { for (j = 0; j < num_points; j++) { poly.contours[i].controls[num_points-j-1] = contours[i].controls[j]; } } } else { ZnTransformPoints(&inv, points, poly.contours[i].points, num_points); if (contours[i].controls) { memcpy(poly.contours[i].controls, contours[i].controls, num_points); } } } } } result = item->class->Contour(item, ZN_CONTOUR_ADD, index, &poly); if (revert) { result = -result; } Tcl_SetObjResult(wi->interp, Tcl_NewIntObj(result)); if (poly.contours != &poly.contour1) { /* * Must not use ZnPolyFree: the point and controls arrays * are passed along to the item and no longer ours. */ ZnFree(poly.contours); } } return TCL_OK; } /* *---------------------------------------------------------------------- * * Coords -- * *---------------------------------------------------------------------- */ static int Coords(ZnWInfo *wi, int argc, Tcl_Obj *CONST args[], ZnTagSearch **search_var) { ZnPoint *points; ZnItem item; unsigned int num_points, i; int result, cmd = ZN_COORDS_READ; long index, contour = 0; char *str, *controls = NULL; char c[2] = " "; Tcl_Obj *l, *entries[3]; result = ZnItemWithTagOrId(wi, args[2], &item, search_var); if ((result == TCL_ERROR) || (item == ZN_NO_ITEM)) { Tcl_AppendResult(wi->interp, " unknown item \"", Tcl_GetString(args[2]), "\"", NULL); return TCL_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, ZN_COORDS_READ_ALL, &points, &controls, &num_points) == TCL_ERROR) { return TCL_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++) { entries[0] = Tcl_NewDoubleObj(points->x); entries[1] = Tcl_NewDoubleObj(points->y); if (controls && controls[i]) { c[0] = controls[i]; entries[2] = Tcl_NewStringObj(c, -1); Tcl_ListObjAppendElement(wi->interp, l, Tcl_NewListObj(3, entries)); } else { Tcl_ListObjAppendElement(wi->interp, l, Tcl_NewListObj(2, entries)); } } return TCL_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 TCL_ERROR; } cmd = ZN_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 TCL_ERROR; } cmd = ZN_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) != TCL_OK) { Tcl_ResetResult(wi->interp); if (((argc == 5) && (cmd != ZN_COORDS_ADD) && (cmd != ZN_COORDS_REMOVE)) || (argc == 6) || (argc == 7)) { Tcl_AppendResult(wi->interp, " incorrect contour index \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } else if ((argc == 5) && (cmd != ZN_COORDS_ADD)) { Tcl_AppendResult(wi->interp, " incorrect coord index \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } else if (ZnParseCoordList(wi, args[argc-1], &points, &controls, &num_points, NULL) == TCL_ERROR) { return TCL_ERROR; } if (cmd == ZN_COORDS_ADD) { /* Append coords at end of default contour (0). */ if (item->class->Coords(item, 0, 0, ZN_COORDS_ADD_LAST, &points, &controls, &num_points) == TCL_ERROR) { return TCL_ERROR; } } else { /* Set all coords of default contour (0). */ if (item->class->Coords(item, 0, 0, ZN_COORDS_REPLACE_ALL, &points, &controls, &num_points) == TCL_ERROR) { return TCL_ERROR; } } if (controls) { ZnFree(controls); } return TCL_OK; } contour = index; if (argc == 4) { /* Get all coords of contour. */ if (item->class->Coords(item, contour, 0, ZN_COORDS_READ_ALL, &points, &controls, &num_points) == TCL_ERROR) { return TCL_ERROR; } goto coords_read; } else if ((argc == 5) && (cmd == ZN_COORDS_REMOVE)) { /* Remove coord at index in default contour (0). */ if (item->class->Coords(item, 0, index, ZN_COORDS_REMOVE, &points, &controls, &num_points) == TCL_ERROR) { return TCL_ERROR; } return TCL_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) != TCL_OK) { Tcl_ResetResult(wi->interp); if ((argc == 7) || ((argc == 6) && (cmd != ZN_COORDS_ADD))) { Tcl_AppendResult(wi->interp, " incorrect coord index \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } else if (ZnParseCoordList(wi, args[argc-1], &points, &controls, &num_points, NULL) == TCL_ERROR) { return TCL_ERROR; } if (cmd == ZN_COORDS_ADD) { /* Append coords at end of contour. */ if (item->class->Coords(item, contour, 0, ZN_COORDS_ADD_LAST, &points, &controls, &num_points) == TCL_ERROR) { return TCL_ERROR; } } else { /* Set all coords of contour. */ if (item->class->Coords(item, contour, 0, ZN_COORDS_REPLACE_ALL, &points, &controls, &num_points) == TCL_ERROR) { return TCL_ERROR; } } if (controls) { ZnFree(controls); } return TCL_OK; } if (argc == 5) { /* Get coord of contour at index. */ if (item->class->Coords(item, contour, index, ZN_COORDS_READ, &points, &controls, &num_points) == TCL_ERROR) { return TCL_ERROR; } if (num_points) { /*printf(" coords: read contour:%d, index:%d, point is %g@%g\n", contour, index, points->x, points->y); */ l = Tcl_GetObjResult(wi->interp); Tcl_ListObjAppendElement(wi->interp, l, Tcl_NewDoubleObj(points->x)); Tcl_ListObjAppendElement(wi->interp, l, Tcl_NewDoubleObj(points->y)); if (controls && controls[0]) { c[0] = controls[0]; Tcl_ListObjAppendElement(wi->interp, l, Tcl_NewStringObj(c, -1)); } } return TCL_OK; } else if ((argc == 6) && (cmd == ZN_COORDS_REMOVE)) { /* Remove coord of contour at index. */ if (item->class->Coords(item, contour, index, ZN_COORDS_REMOVE, &points, &controls, &num_points) == TCL_ERROR) { return TCL_ERROR; } return TCL_OK; } /* Set a single coord or add coords at index in contour. */ if (ZnParseCoordList(wi, args[argc-1], &points, &controls, &num_points, NULL) == TCL_ERROR) { return TCL_ERROR; } if (argc == 6) { num_points = 1; cmd = ZN_COORDS_REPLACE; } if (item->class->Coords(item, contour, index, cmd, &points, &controls, &num_points) == TCL_ERROR) { return TCL_ERROR; } if (controls) { ZnFree(controls); } return TCL_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[]) /* Arguments. */ { ZnWInfo *wi = (ZnWInfo *) client_data; int result, cmd_index, index; ZnItem item, item2; int field = ZN_NO_PART; unsigned int num = 0, i, j; char *end, *str; ZnTransfo *t = NULL; Tcl_Obj *l; ZnTagSearch *search_var = NULL; Tcl_HashEntry *entry; ZnPoint *points; unsigned int num_points; ZnList to_points; Tcl_Obj *entries[3]; char c[] = "c"; #ifdef PTK static char *sub_cmd_strings[] = { #else static CONST char *sub_cmd_strings[] = { #endif "add", "addtag", "anchorxy", "bbox", "becomes", "bind", "cget", "chggroup", "clone", "configure", "contour", "coords", "currentpart", "cursor", "dchars", "dtag", "find", "fit", "focus", "gdelete", "gettags", "gname", "group", "hasanchors", "hasfields", "hastag", "index", "insert", "itemcget", "itemconfigure", "layout", "lower", "monitor", "numparts", "postscript", "raise", "remove", "rotate", "scale", "select", "smooth", "tapply", "tdelete", "transform", "translate", "treset", "trestore", "tsave", "type", "vertexat", "xview", "yview", 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_GDELETE, ZN_W_GETTAGS, ZN_W_GNAME, 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_LAYOUT, 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, ZN_W_XVIEW, ZN_W_YVIEW }; #ifdef PTK static char *sel_cmd_strings[] = { #else static CONST char *sel_cmd_strings[] = { #endif "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 TCL_ERROR; } Tcl_Preserve((ClientData) wi); if (Tcl_GetIndexFromObj(interp, args[1], sub_cmd_strings, "subcommand", 0, &cmd_index) != TCL_OK) { goto error; } result = TCL_OK; /*printf("executing command \"%s\", argc=%d\n", Tcl_GetString(args[1]), argc);*/ switch((enum sub_cmds) cmd_index) { /* * add */ case ZN_W_ADD: { ZnItem group; ZnItemClass cls; if (argc == 2) { /* create subcommand alone, return the list of known * object types. */ ZnItemClass *classes = ZnListArray(ZnItemClassList()); num = ZnListSize(ZnItemClassList()); l = Tcl_GetObjResult(interp); for (i = 0; i < num; i++) { Tcl_ListObjAppendElement(interp, l, Tcl_NewStringObj(classes[i]->name, -1)); } 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 = ZnLookupItemClass(str); if (!cls) { Tcl_AppendResult(interp, "unknown item type \"", str, "\"", NULL); goto error; } result = ZnItemWithTagOrId(wi, args[3], &group, &search_var); if ((result == TCL_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 = ZnCreateItem(wi, cls, &argc, &args); if (item == ZN_NO_ITEM) { goto error; } ZnITEM.InsertItem(item, group, ZN_NO_ITEM, True); if (ZnITEM.ConfigureItem(item, ZN_NO_PART, argc, args, True) == TCL_ERROR) { goto error; } wi->hot_item = item; wi->hot_prev = item->previous; l = Tcl_NewLongObj(item->id); Tcl_SetObjResult(interp, l); } 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], &item, &search_var); if ((result == TCL_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. */ Update(wi); item->class->GetAnchor(item, anchor, &p); l = Tcl_GetObjResult(wi->interp); Tcl_ListObjAppendElement(wi->interp, l, Tcl_NewDoubleObj(p.x)); Tcl_ListObjAppendElement(wi->interp, l, Tcl_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; Update(wi); ZnResetBBox(&bbox); for (i = 0; i < argc; i++) { if (ZnTagSearchScan(wi, args[i], &search_var) == TCL_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { ZnAddBBoxToBBox(&bbox, &item->item_bounding_box); } } if (!ZnIsEmptyBBox(&bbox)) { l = Tcl_GetObjResult(interp); Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(bbox.orig.x)); Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(bbox.orig.y)); Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(bbox.corner.x)); Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(bbox.corner.y)); } } 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 = Tcl_NewStringObj(end+1, -1); if (item->class->Part(item, &l, &part) == TCL_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; str = Tcl_GetString(args[4]); 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; command = Tk_GetBinding(interp, wi->binding_table, elem, Tcl_GetString(args[3])); if (command == NULL) { char *string = Tcl_GetString(Tcl_GetObjResult(interp)); /* * Ignore missing binding errors. This is a special hack * that relies on the error message returned by FindSequence * in tkBind.c. */ if (string[0] != '\0') { goto error; } else { Tcl_ResetResult(interp); } } else { Tcl_SetObjResult(interp, command); } #else CONST char *command; command = Tk_GetBinding(interp, wi->binding_table, elem, Tcl_GetString(args[3])); if (command == NULL) { goto error; } Tcl_SetObjResult(interp, Tcl_NewStringObj(command, -1)); #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: { ZnItem grp, scan; 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], &item, &search_var); if ((result == TCL_ERROR) || (item == ZN_NO_ITEM)) { goto error; } result = ZnItemWithTagOrId(wi, args[3], &grp, &search_var); if ((result == TCL_ERROR) || (grp == ZN_NO_ITEM)|| (grp->class != ZnGroup)) { goto error; } if (item->parent == grp) { /* * Nothing to be done, the item is already in the * target group. */ goto done; } /* * Check the ancestors to find if item is an * ancestor of grp, which would lead to a * forbidden move. */ for (scan = grp; scan && (scan != item); scan = scan->parent); if (scan == item) { Tcl_AppendResult(interp, "\"", Tcl_GetString(args[3]), "\" is a descendant of \"", Tcl_GetString(args[2]), "\" and can't be used as its parent", NULL); goto error; } if (argc == 5) { if (Tcl_GetBooleanFromObj(interp, args[4], &adjust) != TCL_OK) { goto error; } } if ((item->parent == grp) || (item->parent == ZN_NO_ITEM)) { goto done; } if (adjust) { ZnITEM.GetItemTransform(grp, &t); ZnTransfoInvert(&t, &inv); ZnITEM.GetItemTransform(item->parent, &t); ZnTransfoCompose(&t2, &t, &inv); this_one = &t2; if (item->transfo) { ZnTransfoCompose(&t, item->transfo, &t2); this_one = &t; } } ZnITEM.ExtractItem(item); ZnITEM.InsertItem(item, grp, ZN_NO_ITEM, True); /* * The item can be a group in which case we must * use the ZN_TRANSFO_FLAG to force an update of * the children. In all other case ZN_COORDS_FLAG * is enough. */ ZnITEM.Invalidate(item, item->class==ZnGroup?ZN_TRANSFO_FLAG:ZN_COORDS_FLAG); if (adjust) { ZnITEM.SetTransfo(item, this_one); } } break; /* * clone */ case ZN_W_CLONE: { if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "clone tagOrId ?option value ...?"); goto error; } result = ZnItemWithTagOrId(wi, args[2], &item, &search_var); if ((result == TCL_ERROR) || (item == ZN_NO_ITEM) || (item == wi->top_group)) { goto error; } argc -= 3; args += 3; item2 = ZnITEM.CloneItem(item); ZnITEM.InsertItem(item2, item->parent, ZN_NO_ITEM, True); if (ZnITEM.ConfigureItem(item2, ZN_NO_PART, argc, args, False) == TCL_ERROR) { goto error; } l = Tcl_NewLongObj(item2->id); Tcl_SetObjResult(interp, l); } 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: { if ((argc < 3) || (argc > 7)) { Tcl_WrongNumArgs(interp, 1, args, "contour tagOrId ?operator windingFlag? ?index? ?coordListOrTagOrId?"); goto error; } if (Contour(wi, argc, args, &search_var) == TCL_ERROR) { goto error; } break; } /* * coords */ case ZN_W_COORDS: { if ((argc < 3) || (argc > 7)) { Tcl_WrongNumArgs(interp, 1, args, "coords tagOrId ?add/remove? ?contour? ?index? ?coordList?"); goto error; } if (Coords(wi, argc, args, &search_var) == TCL_ERROR) { goto error; } } break; /* * currentpart */ case ZN_W_CURRENTPART: { ZnBool only_fields = False; if ((argc != 2) && (argc != 3)) { Tcl_WrongNumArgs(interp, 1, args, "currentpart ?onlyFields?"); goto error; } if (argc == 3) { if (Tcl_GetBooleanFromObj(interp, args[2], &only_fields) != TCL_OK) { goto error; } } if ((wi->current_item != ZN_NO_ITEM) && (wi->current_item->class->Part != NULL) && ((wi->current_part >= 0) || !only_fields)) { l = NULL; wi->current_item->class->Part(wi->current_item, &l, &wi->current_part); Tcl_SetObjResult(interp, l); } } break; /* * cursor */ case ZN_W_CURSOR: { if ((argc != 4) && (argc != 5)) { Tcl_WrongNumArgs(interp, 1, args, "cursor tagOrId ?field? index"); goto error; } if (ZnTagSearchScan(wi, args[2], &search_var) == TCL_ERROR) { goto error; } if (argc == 5) { if (Tcl_GetIntFromObj(interp, args[3], &field) != TCL_OK) { field = ZN_NO_PART; if (Tcl_GetString(args[3])[0] != 0) { Tcl_AppendResult(interp, "invalid field index \"", Tcl_GetString(args[3]), "\", should be a positive integer", NULL); goto error; } } argc--; args++; } for (item = ZnTagSearchFirst(search_var); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if ((item->class->Cursor == NULL) || (item->class->Index == NULL)) { continue; } result = (*item->class->Index)(item, field, args[3], &index); if (result != TCL_OK) { goto error; } (*item->class->Cursor)(item, field, index); if ((item == wi->focus_item) && (field == wi->focus_field) && wi->text_info.cursor_on) { ZnITEM.Invalidate(item, ZN_DRAW_FLAG); } } } break; /* * dchars */ case ZN_W_DCHARS: { int first, last; ZnTextInfo *ti = &wi->text_info; if ((argc < 4) || (argc > 6)) { Tcl_WrongNumArgs(interp, 1, args, "dchars tagOrId ?field? first ?last?"); goto error; } if (ZnTagSearchScan(wi, args[2], &search_var) == TCL_ERROR) { goto error; } if (argc == 6) { if (Tcl_GetIntFromObj(interp, args[3], &field) != TCL_OK) { field = ZN_NO_PART; if (Tcl_GetString(args[3])[0] != 0) { Tcl_AppendResult(interp, "invalid field index \"", Tcl_GetString(args[3]), "\", should be a positive integer", NULL); goto error; } } argc--; args++; } for (item = ZnTagSearchFirst(search_var); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if ((item->class->Index == NULL) || (item->class->DeleteChars == NULL)) { continue; } result = (*item->class->Index)(item, field, args[3], &first); if (result != TCL_OK) { goto error; } if (argc == 5) { result = (*item->class->Index)(item, field, args[4], &last); if (result != TCL_OK) { goto error; } } else { last = first; } (*item->class->DeleteChars)(item, field, &first, &last); /* * Update indexes for the selection to reflect the * change. */ if ((ti->sel_item == item) && (ti->sel_field == field)) { int count = last + 1 - first; if (ti->sel_first > first) { ti->sel_first -= count; if (ti->sel_first < first) { ti->sel_first = first; } } if (ti->sel_last >= first) { ti->sel_last -= count; if (ti->sel_last < (first-1)) { ti->sel_last = first-1; } } if (ti->sel_first >= ti->sel_last) { ti->sel_item = ZN_NO_ITEM; ti->sel_field = ZN_NO_PART; } if ((ti->anchor_item == item) && (ti->anchor_field == field) && (ti->sel_anchor > first)) { ti->sel_anchor -= count; if (ti->sel_anchor < first) { ti->sel_anchor = first; } } } } } 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) == TCL_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { ZnITEM.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: { ZnReal error; if (argc != 4) { Tcl_WrongNumArgs(interp, 1, args, "fit coordList error"); goto error; } if (ZnParseCoordList(wi, args[2], &points, NULL, &num_points, NULL) == TCL_ERROR) { return TCL_ERROR; } if (Tcl_GetDoubleFromObj(interp, args[3], &error) == TCL_ERROR) { goto error; } to_points = ZnListNew(32, sizeof(ZnPoint)); ZnFitBezier(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++) { entries[0] = Tcl_NewDoubleObj(points->x); entries[1] = Tcl_NewDoubleObj(points->y); if (i % 3) { entries[2] = Tcl_NewStringObj(c, -1); Tcl_ListObjAppendElement(interp, l, Tcl_NewListObj(3, entries)); } else { Tcl_ListObjAppendElement(interp, l, Tcl_NewListObj(2, entries)); } } ZnListFree(to_points); } break; /* * focus */ case ZN_W_FOCUS: { if (argc > 4) { Tcl_WrongNumArgs(interp, 1, args, "focus ?tagOrId? ?field?"); goto error; } item = wi->focus_item; if (argc == 2) { field = wi->focus_field; if (item != ZN_NO_ITEM) { l = Tcl_GetObjResult(interp); Tcl_ListObjAppendElement(interp, l, Tcl_NewLongObj(item->id)); if (field != ZN_NO_PART) { Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(field)); } else { Tcl_ListObjAppendElement(interp, l, Tcl_NewStringObj("", -1)); } } break; } if ((item != ZN_NO_ITEM) && (item->class->Cursor != NULL) && ISSET(wi->flags, ZN_GOT_FOCUS)) { ZnITEM.Invalidate(item, ZN_COORDS_FLAG); } if (Tcl_GetString(args[2])[0] == 0) { wi->focus_item = ZN_NO_ITEM; wi->focus_field = ZN_NO_PART; break; } if (ZnItemWithTagOrId(wi, args[2], &item, &search_var) == TCL_ERROR) { goto error; } if (argc == 4) { if (Tcl_GetIntFromObj(interp, args[3], &field) != TCL_OK) { field = ZN_NO_PART; if (Tcl_GetString(args[3])[0] != 0) { Tcl_AppendResult(interp, "invalid field index \"", Tcl_GetString(args[3]), "\", should be a positive integer", NULL); goto error; } } } wi->focus_item = item; wi->focus_field = field; if (ISSET(wi->flags, ZN_GOT_FOCUS) && (item->class->Cursor != NULL)) { ZnITEM.Invalidate(wi->focus_item, ZN_COORDS_FLAG); } } break; /* * gdelete */ case ZN_W_GDELETE: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "gdelete gName"); goto error; } ZnDeleteGradientName(Tcl_GetString(args[2])); } 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], &item, &search_var); if ((result == TCL_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, Tcl_NewStringObj(tags[i], -1)); } } } break; /* * gname */ case ZN_W_GNAME: { ZnBool ok; if ((argc != 3) && (argc != 4)) { Tcl_WrongNumArgs(interp, 1, args, "gname ?grad? gName"); goto error; } if (argc == 3) { l = Tcl_NewBooleanObj(ZnGradientNameExists(Tcl_GetString(args[2]))); Tcl_SetObjResult(interp, l); } else { ok = ZnNameGradient(interp, wi->win, Tcl_GetString(args[2]), Tcl_GetString(args[3])); if (!ok) { goto error; } } } break; /* * group */ case ZN_W_GROUP: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "group tagOrId"); goto error; } result = ZnItemWithTagOrId(wi, args[2], &item, &search_var); if ((result == TCL_ERROR) || (item == ZN_NO_ITEM)) { goto error; } if (item->parent != ZN_NO_ITEM) { l = Tcl_NewLongObj(item->parent->id); Tcl_SetObjResult(interp, l); } else { /* * Top group is its own parent. */ l = Tcl_NewLongObj(item->id); Tcl_SetObjResult(interp, l); } } break; /* * hasanchors */ case ZN_W_HASANCHORS: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "hasanchors tagOrId"); goto error; } result = ZnItemWithTagOrId(wi, args[2], &item, &search_var); if ((result == TCL_ERROR) || (item == ZN_NO_ITEM)) { goto error; } l = Tcl_NewBooleanObj(item->class->has_anchors?1:0); Tcl_SetObjResult(interp, l); } break; /* * hasfields */ case ZN_W_HASFIELDS: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "hasfields tagOrId"); goto error; } result = ZnItemWithTagOrId(wi, args[2], &item, &search_var); if ((result == TCL_ERROR) || (item == ZN_NO_ITEM)) { goto error; } l = Tcl_NewBooleanObj(item->class->GetFieldSet?1:0); Tcl_SetObjResult(interp, l); } break; /* * hastag */ case ZN_W_HASTAG: { if (argc != 4) { Tcl_WrongNumArgs(interp, 1, args, "hastag tagOrId tag"); goto error; } result = ZnItemWithTagOrId(wi, args[2], &item, &search_var); if ((result == TCL_ERROR) || (item == ZN_NO_ITEM)) { goto error; } l = Tcl_NewBooleanObj(ZnITEM.HasTag(item, Tk_GetUid(Tcl_GetString(args[3])))); Tcl_SetObjResult(interp, l); } break; /* * index */ case ZN_W_INDEX: { if ((argc != 4) && (argc != 5)) { Tcl_WrongNumArgs(interp, 1, args, "index tagOrId ?field? string"); goto error; } if (ZnTagSearchScan(wi, args[2], &search_var) == TCL_ERROR) { goto error; } if (argc == 5) { if (Tcl_GetIntFromObj(interp, args[3], &field) != TCL_OK) { field = ZN_NO_PART; if (Tcl_GetString(args[3])[0] != 0) { Tcl_AppendResult(interp, "invalid field index \"", Tcl_GetString(args[3]), "\", should be a positive integer", NULL); goto error; } } argc--; args++; } for (item = ZnTagSearchFirst(search_var); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if (item->class->Index != NULL) { result = (*item->class->Index)(item, field, args[3], &index); if (result != TCL_OK) { goto error; } l = Tcl_NewIntObj(index); Tcl_SetObjResult(interp, l); goto done; } } Tcl_AppendResult(interp, "can't find an indexable item \"", Tcl_GetString(args[2]), "\"", NULL); goto error; } break; /* * insert */ case ZN_W_INSERT: { ZnTextInfo *ti = &wi->text_info; char *chars; if ((argc != 5) && (argc != 6)) { Tcl_WrongNumArgs(interp, 1, args, "insert tagOrId ?field? before string"); goto error; } if (ZnTagSearchScan(wi, args[2], &search_var) == TCL_ERROR) { goto error; } if (argc == 6) { if (Tcl_GetIntFromObj(interp, args[3], &field) != TCL_OK) { field = ZN_NO_PART; if (Tcl_GetString(args[3])[0] != 0) { Tcl_AppendResult(interp, "invalid field index \"", Tcl_GetString(args[3]), "\", should be a positive integer", NULL); goto error; } } argc--; args++; } for (item = ZnTagSearchFirst(search_var); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if ((item->class->Index == NULL) || (item->class->InsertChars == NULL)) { continue; } result = (*item->class->Index)(item, field, args[3], &index); if (result != TCL_OK) { goto error; } chars = Tcl_GetString(args[4]); (*item->class->InsertChars)(item, field, &index, chars); /* * Inserting characters invalidates selection indices. */ if ((ti->sel_item == item) && (ti->sel_field == field)) { int length = strlen(chars); if (ti->sel_first >= index) { ti->sel_first += length; } if (ti->sel_last >= index) { ti->sel_last += length; } if ((ti->anchor_item == item) && (ti->anchor_field == field) && (ti->sel_anchor >= index)) { ti->sel_anchor += length; } } } } break; /* * itemcget */ case ZN_W_ITEMCGET: { if (argc < 4) { itemcget_syntax: Tcl_WrongNumArgs(interp, 1, args, "itemcget tagOrId ?field? option"); goto error; } result = ZnItemWithTagOrId(wi, args[2], &item, &search_var); if ((result == TCL_ERROR) || (item == ZN_NO_ITEM)) { goto error; } if (argc == 5) { if (Tcl_GetIntFromObj(interp, args[3], &field) != TCL_OK) { field = ZN_NO_PART; if (Tcl_GetString(args[3])[0] != 0) { 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 (ZnITEM.QueryItem(item, field, 1, &args[3]) != TCL_OK) { goto error; } } break; /* * itemconfigure */ case ZN_W_ITEMCONFIGURE: { if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "itemconfigure tagOrId ?field? option value ?option value? ..."); goto error; } if (ZnTagSearchScan(wi, args[2], &search_var) == TCL_ERROR) { goto error; } if ((argc > 3) && (Tcl_GetString(args[3])[0] != '-')) { if (Tcl_GetIntFromObj(interp, args[3], &field) != TCL_OK) { field = ZN_NO_PART; if (Tcl_GetString(args[3])[0] != 0) { 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); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if (argc < 2) { if (field == ZN_NO_PART) { result = ZnAttributesInfo(wi, item, item->class->attr_desc, argc, args); } else if (item->class->GetFieldSet) { ZnFieldSet fs = item->class->GetFieldSet(item); if (field < (int) ZnFIELD.NumFields(fs)) { result = ZnAttributesInfo(wi, ZnFIELD.GetFieldStruct(fs, field), ZnFIELD.attr_desc, argc, args); } else { Tcl_AppendResult(interp, "field index out of bound", NULL); goto error; } } else { Tcl_AppendResult(interp, "the item does not support fields", NULL); goto error; } goto done; } else { result = ZnITEM.ConfigureItem(item, field, argc, args, False); } if (result == TCL_ERROR) { goto error; } } } break; /* * layout */ case ZN_W_LAYOUT: { if (argc < 4) { Tcl_WrongNumArgs(interp, 1, args, "layout operator ?args...? tagOrId ?tagOrId...?"); goto error; } if (LayoutItems(wi, argc-2, args+2) == TCL_ERROR) { goto error; } } break; /* * lower */ case ZN_W_LOWER: { ZnItem first, 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) == TCL_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var); 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) == TCL_ERROR) { goto error; } item = ZnTagSearchFirst(search_var); if ((item == ZN_NO_ITEM) || (item == wi->top_group)) { goto done; } first = item; if (mark == ZN_NO_ITEM) { mark = ZnGroupTail(item->parent); } group = mark->parent; do { if ((item->parent == group) && (item != mark)) { ZnITEM.UpdateItemPriority(item, mark, False); mark = item; } item = ZnTagSearchNext(search_var); } while ((item != ZN_NO_ITEM) && (item != first)); } break; /* * monitor */ case ZN_W_MONITOR: { #ifndef _WIN32 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) != TCL_OK) { goto error; } ASSIGN(wi->flags, ZN_MONITORING, on_off); if (on_off == True) { ZnResetChronos(wi->total_draw_chrono); ZnResetChronos(wi->this_draw_chrono); } } if ((argc == 2) || (on_off == False)) { long ttime, ltime; int num_actions; ZnGetChrono(wi->total_draw_chrono, &ttime, &num_actions); ZnGetChrono(wi->this_draw_chrono, <ime, NULL); l = Tcl_GetObjResult(interp); Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(num_actions)); Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(ltime)); Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(ttime)); /*Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(wi->damaged_area_w)); Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(wi->damaged_area_h)); Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(ttime));*/ } #endif } break; /* * numparts */ case ZN_W_NUMPARTS: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "numparts tagOrId"); goto error; } result = ZnItemWithTagOrId(wi, args[2], &item, &search_var); if ((result == TCL_ERROR) || (item == ZN_NO_ITEM)) { goto error; } l = Tcl_NewIntObj((int) item->class->num_parts); Tcl_SetObjResult(interp, l); } break; /* * postscript */ case ZN_W_POSTSCRIPT: { Tcl_AppendResult(interp, "Command not yet implemented", NULL); goto error; } break; /* * raise */ case ZN_W_RAISE: { ZnItem 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) == TCL_ERROR) { goto error; } mark = ZnTagSearchFirst(search_var); 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) == TCL_ERROR) { goto error; } item = ZnTagSearchFirst(search_var); if ((item == ZN_NO_ITEM) || (item == wi->top_group)) { goto done; } if (mark == ZN_NO_ITEM) { mark = ZnGroupHead(item->parent); } group = mark->parent; for (; item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { if (item->parent != group) { continue; } if (item != mark) { ZnITEM.UpdateItemPriority(item, mark, True); } } } break; /* * remove */ case ZN_W_REMOVE: { unsigned int num_fields; if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "remove tagOrId ?tagOrId ...?"); goto error; } argc -= 2; args += 2; for (i = 0; i < (unsigned int) argc; i++) { if (ZnTagSearchScan(wi, args[i], &search_var) == TCL_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var); 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->GetFieldSet) { num_fields = ZnFIELD.NumFields(item->class->GetFieldSet(item)); for (j = 0; j < 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))); } } ZnITEM.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) == TCL_ERROR) { goto error; } if (Tcl_GetDoubleFromObj(interp, args[5], &p.y) == TCL_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) == TCL_ERROR) { goto error; } } if (Tcl_GetDoubleFromObj(interp, args[3], &angle) == TCL_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); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { ZnITEM.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) == TCL_ERROR) { goto error; } } if (Tcl_GetDoubleFromObj(interp, args[3], &scale.x) == TCL_ERROR) { goto error; } if (Tcl_GetDoubleFromObj(interp, args[4], &scale.y) == TCL_ERROR) { goto error; } if (t) { ZnScale(t, scale.x, scale.y); } else { for (item = ZnTagSearchFirst(search_var); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { ZnITEM.ScaleItem(item, scale.x, scale.y); } } } break; /* * select */ case ZN_W_SELECT: { ZnTextInfo *ti = &wi->text_info; if (argc < 3) { Tcl_WrongNumArgs(interp, 1, args, "select option ?tagOrId? ?arg?"); goto error; } if (argc >= 4) { if (ZnTagSearchScan(wi, args[3], &search_var) == TCL_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var); 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 (Tcl_GetIndexFromObj(interp, args[2], sel_cmd_strings, "selection option", 0, &cmd_index) != TCL_OK) { goto error; } if ((argc == 5) || (argc == 6)) { if (argc == 6) { if (Tcl_GetIntFromObj(interp, args[4], &field) != TCL_OK) { field = ZN_NO_PART; if (Tcl_GetString(args[4])[0] != 0) { Tcl_AppendResult(interp, "invalid field index \"", Tcl_GetString(args[4]), "\", should be a positive integer", NULL); goto error; } } argc--; args++; } result = item->class->Index(item, field, args[4], &index); if (result != TCL_OK) { goto error; } } switch ((enum sel_cmds) cmd_index) { case ZN_SEL_ADJUST: if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "select adjust tagOrId ?field? index"); goto error; } if ((ti->sel_item == item) && (ti->sel_field == field)) { if (index < (ti->sel_first + ti->sel_last)/2) { ti->sel_anchor = ti->sel_last+1; } else { ti->sel_anchor = ti->sel_first; } } SelectTo(item, field, index); break; case ZN_SEL_CLEAR: if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "select clear"); goto error; } if (ti->sel_item != ZN_NO_ITEM) { ZnITEM.Invalidate(ti->sel_item, ZN_DRAW_FLAG); ti->sel_item = ZN_NO_ITEM; ti->sel_field = ZN_NO_PART; } break; case ZN_SEL_FROM: if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "select from tagOrId ?field? index"); goto error; } ti->anchor_item = item; ti->anchor_field = field; ti->sel_anchor = index; break; case ZN_SEL_ITEM: if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "select item"); goto error; } if (ti->sel_item != ZN_NO_ITEM) { l = Tcl_GetObjResult(interp); Tcl_ListObjAppendElement(interp, l, Tcl_NewLongObj(ti->sel_item->id)); if (ti->sel_field != ZN_NO_PART) { Tcl_ListObjAppendElement(interp, l, Tcl_NewIntObj(ti->sel_field)); } else { Tcl_ListObjAppendElement(interp, l, Tcl_NewStringObj("", -1)); } } break; case ZN_SEL_TO: if (argc != 5) { Tcl_WrongNumArgs(interp, 1, args, "select to tagOrId ?field? index"); goto error; } SelectTo(item, field, index); break; } } break; /* * smooth */ case ZN_W_SMOOTH: { if (argc != 3) { Tcl_WrongNumArgs(interp, 1, args, "smooth coordList"); goto error; } if (ZnParseCoordList(wi, args[2], &points, NULL, &num_points, NULL) == TCL_ERROR) { return TCL_ERROR; } to_points = ZnListNew(32, sizeof(ZnPoint)); ZnSmoothPathWithBezier(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++) { entries[0] = Tcl_NewDoubleObj(points->x); entries[1] = Tcl_NewDoubleObj(points->y); if (i % 3) { entries[2] = Tcl_NewStringObj(c, -1); Tcl_ListObjAppendElement(interp, l, Tcl_NewListObj(3, entries)); } else { Tcl_ListObjAppendElement(interp, l, Tcl_NewListObj(2, entries)); } } 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: { char *controls; ZnPoint *p, xp; ZnTransfo t, t2, inv, *this_one; ZnItem from, to; ZnBool old_format; if ((argc != 4) && (argc != 5)) { Tcl_WrongNumArgs(interp, 1, args, "transform ?tagOrIdFrom? tagOrIdTo coordlist"); goto error; } if (argc == 5) { result = ZnItemWithTagOrId(wi, args[2], &from, &search_var); if ((result == TCL_ERROR) || (from == ZN_NO_ITEM)) { goto error; } } result = ZnItemWithTagOrId(wi, args[argc-2], &to, &search_var); if ((result == TCL_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 { ZnITEM.GetItemTransform(to, &t); ZnTransfoInvert(&t, &inv); } this_one = &inv; if (argc == 5) { ZnITEM.GetItemTransform(from, &t); ZnTransfoCompose(&t2, &t, &inv); this_one = &t2; } /*ZnPrintTransfo(&t); ZnPrintTransfo(&inv);*/ if (ZnParseCoordList(wi, args[argc-1], &p, &controls, &num_points, &old_format) == TCL_ERROR) { Tcl_AppendResult(interp, " invalid coord list \"", Tcl_GetString(args[argc-1]), "\"", NULL); goto error; } l = Tcl_GetObjResult(interp); if (old_format) { for (i = 0; i < num_points; i++, p++) { 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, Tcl_NewDoubleObj(xp.x)); Tcl_ListObjAppendElement(interp, l, Tcl_NewDoubleObj(xp.y)); /* The next case only applies for a one point * list with a control flag. */ if (controls && controls[i]) { c[0] = controls[i]; Tcl_ListObjAppendElement(interp, l, Tcl_NewStringObj(c, -1)); } } } else { for (i = 0; i < num_points; i++, p++) { 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);*/ entries[0] = Tcl_NewDoubleObj(xp.x); entries[1] = Tcl_NewDoubleObj(xp.y); if (controls && controls[i]) { c[0] = controls[i]; entries[2] = Tcl_NewStringObj(c, -1); Tcl_ListObjAppendElement(interp, l, Tcl_NewListObj(3, entries)); } else { Tcl_ListObjAppendElement(interp, l, Tcl_NewListObj(2, entries)); } } } } 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) == TCL_ERROR) { goto error; } } if (Tcl_GetDoubleFromObj(interp, args[3], &trans.x) == TCL_ERROR) { goto error; } if (Tcl_GetDoubleFromObj(interp, args[4], &trans.y) == TCL_ERROR) { goto error; } if (t) { ZnTranslate(t, trans.x, trans.y); } else { for (item = ZnTagSearchFirst(search_var); item != ZN_NO_ITEM; item =ZnTagSearchNext(search_var)) { ZnITEM.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) == TCL_ERROR) { goto error; } } if (t) { ZnTransfoSetIdentity(t); } else { for (item = ZnTagSearchFirst(search_var); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { ZnITEM.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) == TCL_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var); item != ZN_NO_ITEM; item = ZnTagSearchNext(search_var)) { ZnITEM.SetTransfo(item, t); } } break; /* * tsave */ case ZN_W_TSAVE: { int new, invert=0; ZnTransfo *inv; if ((argc != 3) && (argc != 4) && (argc != 5)) { Tcl_WrongNumArgs(interp, 1, args, "tsave ?tagOrId? tName ?invert?"); goto error; } if (argc == 3) { entry = Tcl_FindHashEntry(wi->t_table, Tcl_GetString(args[2])); l = Tcl_NewBooleanObj(entry != NULL); Tcl_SetObjResult(interp, l); goto done; } result = ZnItemWithTagOrId(wi, args[2], &item, &search_var); if ((result == TCL_ERROR) || (item == ZN_NO_ITEM)) { goto error; } if (argc == 5) { if (Tcl_GetBooleanFromObj(interp, args[4], &invert) != TCL_OK) { goto error; } argc--; } 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], &item, &search_var); if (result == TCL_ERROR) { goto error; } if (item != ZN_NO_ITEM) { l = Tcl_NewStringObj(item->class->name, -1); Tcl_SetObjResult(interp, l); } } 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) == TCL_ERROR) { goto error; } for (item = ZnTagSearchFirst(search_var); 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) == TCL_ERROR) { goto error; } if (Tcl_GetDoubleFromObj(interp, args[4], &p.y) == TCL_ERROR) { goto error; } 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)); } /* xview */ case ZN_W_XVIEW: { int count, type; ZnReal new_x=0.0, fraction; if (argc == 2) { Tcl_SetObjResult(interp, ScrollFractions(wi->origin.x, wi->origin.x + Tk_Width(wi->win), wi->scroll_xo, wi->scroll_xc)); } else { type = Tk_GetScrollInfoObj(interp, argc, args, &fraction, &count); switch (type) { case TK_SCROLL_ERROR: result = TCL_ERROR; goto done; case TK_SCROLL_MOVETO: new_x = (wi->scroll_xo + (int) (fraction * (wi->scroll_xc - wi->scroll_xo) + 0.5)); break; case TK_SCROLL_PAGES: new_x = (int) (wi->origin.x + count * 0.9 * Tk_Width(wi->win)); break; case TK_SCROLL_UNITS: if (wi->x_scroll_incr > 0) { new_x = wi->origin.x + count * wi->x_scroll_incr; } else { new_x = (int) (wi->origin.x + count * 0.1 * Tk_Width(wi->win)); } break; } SetOrigin(wi, new_x, wi->origin.y); } break; } /*yview */ case ZN_W_YVIEW: { int count, type; ZnReal new_y = 0.0, fraction; if (argc == 2) { Tcl_SetObjResult(interp, ScrollFractions(wi->origin.y, wi->origin.y + Tk_Height(wi->win), wi->scroll_yo, wi->scroll_yc)); } else { type = Tk_GetScrollInfoObj(interp, argc, args, &fraction, &count); switch (type) { case TK_SCROLL_ERROR: result = TCL_ERROR; goto done; case TK_SCROLL_MOVETO: new_y = (wi->scroll_yo + (int) (fraction * (wi->scroll_yc - wi->scroll_yo) + 0.5)); break; case TK_SCROLL_PAGES: new_y = (int) (wi->origin.y + count * 0.9 * Tk_Height(wi->win)); break; case TK_SCROLL_UNITS: if (wi->y_scroll_incr > 0) { new_y = wi->origin.y + count * wi->y_scroll_incr; } else { new_y = (int) (wi->origin.y + count * 0.1 * Tk_Height(wi->win)); } break; } SetOrigin(wi, wi->origin.x, new_y); } break; } } 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 TCL_ERROR; } /* *---------------------------------------------------------------------- * * Configure -- * * This procedure is called to process an args/argc list in * conjunction with the Tk option database to configure (or * reconfigure) a Zinc widget. * * Results: * The return value is a standard Tcl result. If TCL_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. */ ZnWInfo *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; ZnBool init; int render; init = wi->fore_color == NULL; render = wi->render; if (Tk_ConfigureWidget(interp, wi->win, config_specs, argc, #ifdef PTK (Tcl_Obj **) args, (char *) wi, flags) != TCL_OK) { #else (CONST char **) args, (char *) wi, flags|TK_CONFIG_OBJS) != TCL_OK) { #endif return TCL_ERROR; } if (!init) { if (wi->render != render) { ZnWarning("It is not possible to change the -render option after widget creation.\n"); } wi->render = render; } #ifdef GL if (CONFIG_PROBE(FONT_SPEC) || !wi->font_tfi) { if (wi->font_tfi) { ZnFreeTexFont(wi->font_tfi); wi->font_tfi = NULL; } if (wi->render) { wi->font_tfi = ZnGetTexFont(wi, wi->font); } } if (CONFIG_PROBE(MAP_TEXT_FONT_SPEC) || !wi->map_font_tfi) { if (wi->map_font_tfi) { ZnFreeTexFont(wi->map_font_tfi); wi->map_font_tfi = NULL; } if (wi->render) { wi->map_font_tfi = ZnGetTexFont(wi, wi->map_text_font); } } #endif /* * Maintain the pick aperture within meaningful bounds. */ if (wi->pick_aperture < 0) { wi->pick_aperture = 0; } if (CONFIG_PROBE(BACK_COLOR_SPEC) || !wi->relief_grad) { XColor *color; unsigned short alpha; color = ZnGetGradientColor(wi->back_color, 0.0, &alpha); Tk_SetWindowBackground(wi->win, ZnPixel(color)); if (wi->relief_grad) { ZnFreeGradient(wi->relief_grad); wi->relief_grad = NULL; } if (wi->relief != ZN_RELIEF_FLAT) { wi->relief_grad = ZnGetReliefGradient(interp, wi->win, Tk_NameOfColor(color), alpha); } } 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; ZnDamage(wi, &bbox); } if (CONFIG_PROBE(RELIEF_SPEC)) { ZnNeedRedisplay(wi); } 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; ZnDamage(wi, &bbox); } if (CONFIG_PROBE(SPEED_VECTOR_LENGTH_SPEC) || CONFIG_PROBE(MANAGE_HISTORY_SPEC) || CONFIG_PROBE(MANAGED_HISTORY_SIZE_SPEC)) { ZnITEM.InvalidateItems(wi->top_group, ZnTrack); } if (CONFIG_PROBE(MAP_DISTANCE_SYMBOL_SPEC)) { ZnITEM.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) || ISCLEAR(wi->flags, ZN_REALIZED)) { Tk_GeometryRequest(wi->win, wi->opt_width, wi->opt_height); } if (CONFIG_PROBE(TILE_SPEC)) { bbox.orig.x = bbox.orig.y = 0; bbox.corner.x = wi->width; bbox.corner.y = wi->height; ZnDamage(wi, &bbox); } /* * Update the registration with the overlap manager. */ #ifdef OM if (CONFIG_PROBE(OVERLAP_MANAGER_SPEC)) { Tcl_HashEntry *entry; ZnItem 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 = (ZnItem) Tcl_GetHashValue(entry); if (grp->class == ZnGroup) { OmRegister((void *) wi, ZnSendTrackToOm, ZnSetLabelAngleFromOm, ZnQueryLabelPosition); wi->om_group = grp; } } } } #endif if (CONFIG_PROBE(INSERT_WIDTH_SPEC) && wi->focus_item) { ZnITEM.Invalidate(wi->focus_item, ZN_COORDS_FLAG); } /* * Update the blinking cursor timing if on/off time has changed. */ if (ISSET(wi->flags, ZN_GOT_FOCUS) && (CONFIG_PROBE(INSERT_ON_TIME_SPEC) || CONFIG_PROBE(INSERT_OFF_TIME_SPEC))) { Focus(wi, True); } if (CONFIG_PROBE(SCROLL_REGION_SPEC)) { /* * Compute the scroll region */ wi->scroll_xo = wi->scroll_yo = 0; wi->scroll_xc = wi->scroll_yc = 0; if (wi->region != NULL) { int argc2; #ifdef PTK char **args2; #else CONST char **args2; #endif if (Tcl_SplitList(interp, wi->region, &argc2, &args2) != TCL_OK) { return TCL_ERROR; } if (argc2 != 4) { Tcl_AppendResult(interp, "bad scrollRegion \"", wi->region, "\"", (char *) NULL); badRegion: ZnFree(wi->region); ZnFree(args2); wi->region = NULL; return TCL_ERROR; } if ((Tk_GetPixels(interp, wi->win, args2[0], &wi->scroll_xo) != TCL_OK) || (Tk_GetPixels(interp, wi->win, args2[1], &wi->scroll_yo) != TCL_OK) || (Tk_GetPixels(interp, wi->win, args2[2], &wi->scroll_xc) != TCL_OK) || (Tk_GetPixels(interp, wi->win, args2[3], &wi->scroll_yc) != TCL_OK)) { goto badRegion; } ZnFree(args2); } } if (CONFIG_PROBE(SCROLL_REGION_SPEC) || CONFIG_PROBE(CONFINE_SPEC)) { SetOrigin(wi, wi->origin.x, wi->origin.y); SET(wi->flags, ZN_UPDATE_SCROLLBARS); } 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) { ZnWInfo *wi = (ZnWInfo *) client_data; if (ISCLEAR(wi->flags, ZN_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->focus_item != ZN_NO_ITEM) && (wi->focus_item->class->Cursor != NULL)) { ZnITEM.Invalidate(wi->focus_item, ZN_DRAW_FLAG); } } static void Focus(ZnWInfo *wi, ZnBool got_focus) { Tcl_DeleteTimerHandler(wi->blink_handler); if (got_focus) { SET(wi->flags, ZN_GOT_FOCUS); 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 { CLEAR(wi->flags, ZN_GOT_FOCUS); wi->text_info.cursor_on = 0; wi->blink_handler = (Tcl_TimerToken) NULL; } if ((wi->focus_item != ZN_NO_ITEM) && (wi->focus_item->class->Cursor != NULL)){ ZnITEM.Invalidate(wi->focus_item, ZN_COORDS_FLAG); } /*printf("focus %s\n", got_focus ? "in" : "out");*/ if (wi->highlight_width > 0) { ZnNeedRedisplay(wi); } #ifdef GL_DAMAGE if (wi->render) { ZnBBox bbox; bbox.orig.x = bbox.orig.y = 0; bbox.corner.x = Tk_Width(wi->win); bbox.corner.y = Tk_Height(wi->win); ZnDamage(wi, &bbox); } #endif } /* *---------------------------------------------------------------------- * * 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. */ { ZnWInfo *wi = (ZnWInfo *) client_data; XGCValues values; /*printf("=============== DEBUT %s %d EVENT ==================\n", event->type == MapNotify ? "MAP": event->type == Expose? "EXPOSE" : event->type == ConfigureNotify ? "CONFIGURE" : event->type == DestroyNotify ? "DESTROY" : "??", event->type);*/ if (event->type == MapNotify) { if (!wi->gc) { SET(wi->flags, ZN_REALIZED); if (wi->render) { #ifdef GL GLfloat r[2]; ZnGLMakeCurrent(wi); glGetFloatv(ZN_GL_LINE_WIDTH_RANGE, r); wi->max_line_width = r[1]; glGetFloatv(ZN_GL_POINT_SIZE_RANGE, r); wi->max_point_width = r[1]; if (ISSET(wi->flags, ZN_PRINT_CONFIG)) { fprintf(stderr, "OpenGL version %s\n", (char *) glGetString(GL_VERSION)); fprintf(stderr, " Rendering engine: %s, ", (char *) glGetString(GL_RENDERER)); fprintf(stderr, " Vendor: %s\n", (char *) glGetString(GL_VENDOR)); fprintf(stderr, " Available extensions: %s\n", (char *) glGetString(GL_EXTENSIONS)); fprintf(stderr, "Max antialiased line width: %g\n", wi->max_line_width); fprintf(stderr, "Max antialiased point size: %g\n", wi->max_point_width); } ZnGLRelease(wi); #endif } /* * Get the work GC and suppress GraphicExpose * and NoExpose events reception. */ wi->gc = XCreateGC(wi->dpy, Tk_WindowId(wi->win), 0, NULL); values.graphics_exposures = False; XChangeGC(wi->dpy, wi->gc, GCGraphicsExposures, &values); /* * 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, Tk_WindowId(top_level), &root, &parent, &children, &num_children); if (root == parent) { wi->real_top = Tk_WindowId(top_level); } else { wi->real_top = parent; } if (children) { XFree(children); } } ZnNeedRedisplay(wi); } } else if (event->type == Expose) { ZnBBox bbox; ZnDim width, height; bbox.orig.x = (((XExposeEvent*) event)->x); bbox.orig.y = (((XExposeEvent*) event)->y); 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. */ ZnAddBBoxToBBox(&wi->exposed_area, &bbox); if (/*(((XExposeEvent*) event)->count == 0) &&*/ !ZnIsEmptyBBox(&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) { int int_width, int_height; ZnBBox bbox; int_width = Tk_Width(wi->win); int_height = Tk_Height(wi->win); if ((wi->width != int_width) || (wi->height != int_height)) { bbox.orig.x = bbox.orig.y = 0; bbox.corner.x = MAX(wi->width, int_width); bbox.corner.y = MAX(wi->height, int_height); wi->opt_width = wi->width = int_width; wi->opt_height = wi->height = int_height; ZnResetTransformStack(wi); SET(wi->flags, ZN_UPDATE_SCROLLBARS); /* * The call below is needed in order to recenter the view if * it's confined and the scroll region is smaller than the * window. */ SetOrigin(wi, wi->origin.x, wi->origin.y); ZnDamage(wi, &bbox); ZnITEM.Invalidate(wi->top_group, ZN_TRANSFO_FLAG); /* * Reallocate the double buffer pixmap/image. */ if (!wi->render) { /*printf("reallocating double buffer\n");*/ Tk_FreePixmap(wi->dpy, wi->draw_buffer); wi->draw_buffer = Tk_GetPixmap(wi->dpy, RootWindowOfScreen(wi->screen), int_width, int_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); ZnAddBBoxToBBox(&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; CLEAR(wi->flags, ZN_REALIZED); Tcl_DeleteCommandFromToken(wi->interp, wi->cmd); } if (ISSET(wi->flags, ZN_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(ZnWInfo *wi, XEvent *event) { #define NUM_STATIC 4 ClientData items[NUM_STATIC], *its; static unsigned int worksize = 128, len, num, num_tags; static char *workspace = NULL; unsigned int i, ptr; ClientData *tag_list = NULL; ZnItem item; int part; ZnBool bind_part, bind_item; #define BIND_ITEM(test) \ if (bind_item && (test)) { \ its[ptr] = (ClientData) all_uid; \ ptr++; \ for (i = 0; i < num_tags; i++) { \ its[ptr] = tag_list[i]; \ ptr++; \ } \ its[ptr] = (ClientData) 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->focus_item; part = wi->focus_field; } if ((item == ZN_NO_ITEM) || !item->class->IsSensitive(item, ZN_NO_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) && item->class->IsSensitive(item, part) && (wi->current_item->class->num_parts || wi->current_item->class->GetFieldSet)); /* * 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, currentp %d, newp %d\n", event->type==EnterNotify?"": event->type==LeaveNotify?"": event->type==MotionNotify?"":"other", wi->current_item?wi->current_item->id:0, wi->new_item?wi->new_item->id:0, bind_item?"bind":"nobind", wi->current_part, wi->new_part);*/ 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])+ TCL_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] = (ClientData) 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, (int) 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(ZnWInfo *wi, XEvent *event) { int button_down; /* * 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); if (!button_down) { wi->flags &= ~ZN_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->flags & ZN_REPICK_IN_PROGRESS) { fprintf(stderr, "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) { ZnPickStruct ps; ZnReal dist; ZnPoint p; p.x = wi->pick_event.xcrossing.x; p.y = wi->pick_event.xcrossing.y; ps.point = &p; ps.in_group = ZN_NO_ITEM; ps.start_item = ZN_NO_ITEM; ps.aperture = wi->pick_aperture; ps.recursive = True; dist = wi->top_group->class->Pick(wi->top_group, &ps); if (dist == 0.0) { wi->new_item = ps.a_item; wi->new_part = ps.a_part; } else { wi->new_item = ZN_NO_ITEM; wi->new_part = ZN_NO_PART; } } else { wi->new_item = ZN_NO_ITEM; wi->new_part = ZN_NO_PART; } /*printf("------ PickCurrentItem current: %d %d, new %d %d\n", wi->current_item==ZN_NO_ITEM?0:wi->current_item->id, wi->current_part, wi->new_item==ZN_NO_ITEM?0:wi->new_item->id, wi->new_part);*/ if ((wi->new_item == wi->current_item) && (wi->new_part == wi->current_part) && !(wi->flags & ZN_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. */ if (((wi->new_item != wi->current_item) || (wi->new_part != wi->current_part)) && (wi->current_item != ZN_NO_ITEM) && !(wi->flags & ZN_LEFT_GRABBED_ITEM)) { XEvent event; ZnItem item = wi->current_item; event = wi->pick_event; event.type = LeaveNotify; /*printf("== LEAVE %d %d ==\n", wi->current_item->id, 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->flags |= ZN_REPICK_IN_PROGRESS; DoEvent(wi, &event); wi->flags &= ~ZN_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' from %d\n", wi->current_item->id);*/ ZnITEM.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->flags |= ZN_LEFT_GRABBED_ITEM; return; } /* * Special note: it's possible that wi->new_item == wi->current_item * here. This can happen, for example, if ZN_LEFT_GRABBED_ITEM was set or * if there is only a change in the part number. */ wi->flags &= ~ZN_LEFT_GRABBED_ITEM; /* * Tentative pour éviter d'enlever puis de remettre * le tag current sur le même item en ces de changement * de partie. Ca marche pas pour l'instant. if (wi->current_item != wi->new_item) { if (wi->current_item != ZN_NO_ITEM) { printf("vvv Removing 'current' from %d\n", wi->current_item->id); ZnITEM.RemoveTag(wi->current_item, current_uid); } }*/ { ZnItem it = wi->current_item; wi->current_item = wi->new_item; wi->current_part = wi->new_part; /* * Added to enable DoEvent to make a special case for enter/leaves * between fields in the same item. It may interact with * ZN_LEFT_GRABBED_ITEM. */ wi->new_item = it; } if (wi->current_item != ZN_NO_ITEM) { XEvent event; /* * Add the tag 'current' to the current item under the pointer. */ /*printf("Adding 'current' to %d\n", wi->current_item->id);*/ ZnDoItem((Tcl_Interp *) NULL, wi->current_item, ZN_NO_PART, current_uid); /* * Then emit a fake Enter event on it. */ /*printf("== ENTER %d %d ==\n",wi->current_item->id, wi->current_part);*/ 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. */ { ZnWInfo *wi = (ZnWInfo *) 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) { 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)) { #ifdef GL_DAMAGE /* * Kludge to prevent incorrect redrawing after * a move window when using GL. This code force * a full window redraw on window enter/leaves * hoping it will realign the back buffer soon * enough to make the bug unnoticed. */ if (wi->render) { ZnBBox bbox; bbox.orig.x = bbox.orig.y = 0; bbox.corner.x = Tk_Width(wi->win); bbox.corner.y = Tk_Height(wi->win); ZnDamage(wi, &bbox); } #endif 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) { ZnWInfo *wi = (ZnWInfo *) client_data; ZnTextInfo *ti = &wi->text_info; if (ti->sel_item != ZN_NO_ITEM) { ZnITEM.Invalidate(ti->sel_item, ZN_DRAW_FLAG); } ti->sel_item = ZN_NO_ITEM; ti->sel_field = ZN_NO_PART; } /* *---------------------------------------------------------------------- * * 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(ZnItem item, int field, int index) { ZnWInfo *wi = item->wi; ZnTextInfo *ti = &wi->text_info; int old_first, old_last, old_field; ZnItem old_sel_item; old_first = ti->sel_first; old_last = ti->sel_last; old_sel_item = ti->sel_item; old_field = ti->sel_field; /* * Grab the selection if we don't own it already. */ if (ti->sel_item == ZN_NO_ITEM) { Tk_OwnSelection(wi->win, XA_PRIMARY, LostSelection, (ClientData) wi); } else if ((ti->sel_item != item) || (ti->sel_field != field)) { ZnITEM.Invalidate(ti->sel_item, ZN_DRAW_FLAG); } ti->sel_item = item; ti->sel_field = field; if ((ti->anchor_item != item) || (ti->anchor_field) != field) { ti->anchor_item = item; ti->anchor_field = field; ti->sel_anchor = index; } if (ti->sel_anchor <= index) { ti->sel_first = ti->sel_anchor; ti->sel_last = index; } else { ti->sel_first = index; ti->sel_last = ti->sel_anchor; } if ((ti->sel_first != old_first) || (ti->sel_last != old_last) || (item != old_sel_item)) { ZnITEM.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. */ { ZnWInfo *wi = (ZnWInfo *) client_data; ZnTextInfo *ti = &wi->text_info; if (ti->sel_item == ZN_NO_ITEM) { return -1; } if (ti->sel_item->class->Selection == NULL) { return -1; } return (*ti->sel_item->class->Selection)(ti->sel_item, ti->sel_field, 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. */ { ZnWInfo *wi = (ZnWInfo *) 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. */ { ZnWInfo *wi = (ZnWInfo *) 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. */ /*fprintf(stderr, "Item count before cleanup: %d\n", wi->num_items);*/ ZnITEM.DestroyItem(wi->top_group); /*fprintf(stderr, "Remaining item count: %d\n", wi->num_items);*/ /* * Remove the redisplay scheduled by the cleanup. * It will fire when the widget will be gone and * will corrupt memory. */ if (ISSET(wi->flags, ZN_UPDATE_PENDING)) { Tcl_CancelIdleCall(Redisplay, (ClientData) wi); } #ifdef GL if (wi->font_tfi) { ZnFreeTexFont(wi->font_tfi); } if (wi->map_font_tfi) { ZnFreeTexFont(wi->map_font_tfi); } #endif /* if (wi->font) { Tk_FreeFont(wi->font); } if (wi->map_text_font) { Tk_FreeFont(wi->map_text_font); }*/ for (num = 0; num < ZN_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. */ 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) { ZnFreeImage(wi->tile); wi->tile = ZnUnspecifiedImage; } /* Free the double buffer pixmap/image */ if (wi->draw_buffer) { Tk_FreePixmap(wi->dpy, wi->draw_buffer); } if (wi->fore_color) { ZnFreeGradient(wi->fore_color); } if (wi->back_color) { ZnFreeGradient(wi->back_color); } if (wi->relief_grad) { ZnFreeGradient(wi->relief_grad); } Tcl_DeleteTimerHandler(wi->blink_handler); Tk_FreeOptions(config_specs, (char *) wi, wi->dpy, 0); /* * Should be empty by now. */ ZnFreeTransformStack(wi); ZnFreeClipStack(wi); ZnListFree(wi->work_pts); #ifdef GL ZnListFree(wi->work_doubles); #endif ZnListFree(wi->work_xpts); ZnListFree(wi->work_strs); #ifndef _WIN32 ZnFreeChrono(wi->total_draw_chrono); ZnFreeChrono(wi->this_draw_chrono); #endif #ifdef GL if (wi->gl_context) { ZnGLDestroyContext(wi); } # ifndef _WIN32 if (wi->gl_visual) { XFree(wi->gl_visual); } # endif #endif if (wi->tess) { gluDeleteTess(wi->tess); } ZnFree(wi); /*printf("Destroy ending\n");*/ } /* ********************************************************************************** * * ZnDamage -- * ********************************************************************************** */ void ZnDamage(ZnWInfo *wi, ZnBBox *damage) { if ((damage == NULL) || ZnIsEmptyBBox(damage)) { return; } /*printf("damaging area: %g %g %g %g\n", damage->orig.x, damage->orig.y, damage->corner.x, damage->corner.y);*/ if (ZnIsEmptyBBox(&wi->damaged_area)) { wi->damaged_area.orig.x = damage->orig.x; wi->damaged_area.orig.y = damage->orig.y; wi->damaged_area.corner.x = damage->corner.x; wi->damaged_area.corner.y = damage->corner.y; ZnNeedRedisplay(wi); } else { wi->damaged_area.orig.x = MIN(wi->damaged_area.orig.x, damage->orig.x); wi->damaged_area.orig.y = MIN(wi->damaged_area.orig.y, damage->orig.y); wi->damaged_area.corner.x = MAX(wi->damaged_area.corner.x, damage->corner.x); wi->damaged_area.corner.y = MAX(wi->damaged_area.corner.y, damage->corner.y); } /*printf("damaged area: %g %g %g %g\n", wi->damaged_area.orig.x, wi->damaged_area.orig.y, wi->damaged_area.corner.x, wi->damaged_area.corner.y);*/ } static void ClampDamageArea(ZnWInfo *wi) { int width, height; if (wi->damaged_area.orig.x < wi->inset) { wi->damaged_area.orig.x = wi->inset; } if (wi->damaged_area.orig.y < wi->inset) { wi->damaged_area.orig.y = wi->inset; } if (wi->damaged_area.corner.x < wi->inset) { wi->damaged_area.corner.x = wi->inset; } if (wi->damaged_area.corner.y < wi->inset) { wi->damaged_area.corner.y = wi->inset; } width = wi->width - wi->inset; height = wi->height - wi->inset; if (wi->damaged_area.orig.x > width) { wi->damaged_area.orig.x = width; } if (wi->damaged_area.orig.y > height) { wi->damaged_area.orig.y = height; } if (wi->damaged_area.corner.x > width) { wi->damaged_area.corner.x = width; } if (wi->damaged_area.corner.y > height) { wi->damaged_area.corner.y = height; } } /* ********************************************************************************** * * Update -- * ********************************************************************************** */ static void Update(ZnWInfo *wi) { /* * Give the overlap manager a chance to do its work. */ #ifdef OM if ((wi->om_group != ZN_NO_ITEM) && ZnGroupCallOm(wi->om_group)) { ZnPoint scale={1.0,1.0}; if (wi->om_group->transfo) { ZnTransfoDecompose(wi->om_group->transfo, &scale, NULL, NULL, NULL); } OmProcessOverlap((void *) wi, wi->width, wi->height, scale.x); ZnGroupSetCallOm(wi->om_group, False); } #endif if (ISSET(wi->top_group->inv_flags, ZN_COORDS_FLAG) || ISSET(wi->top_group->inv_flags, ZN_TRANSFO_FLAG)) { wi->top_group->class->ComputeCoordinates(wi->top_group, False); } } /* ********************************************************************************** * * Repair -- * ********************************************************************************** */ static void Repair(ZnWInfo *wi) { XGCValues values; ZnPoint p[5]; ZnTriStrip tristrip; #ifdef GL XColor *color; #endif int int_width = Tk_Width(wi->win); int int_height = Tk_Height(wi->win); if (wi->render) { #ifdef GL #ifdef GL_DAMAGE ClampDamageArea(wi); /* * Merge the exposed area. */ ZnAddBBoxToBBox(&wi->damaged_area, &wi->exposed_area); if (ZnIsEmptyBBox(&wi->damaged_area)) { return; } #endif ZnGLMakeCurrent(wi); glEnable(GL_POINT_SMOOTH); glEnable(GL_LINE_SMOOTH); #if 0 glEnable(GL_POLYGON_SMOOTH); /* expensive ? */ #endif glEnable(GL_BLEND); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glClearStencil(0); color = ZnGetGradientColor(wi->back_color, 0.0, NULL); glClearColor((GLfloat) color->red/65536, (GLfloat) color->green/65536, (GLfloat) color->blue/65536, 0.0); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); /* * Init the composite group alpha. */ wi->alpha = 100; glViewport(0, 0, (GLsizei) int_width, int_height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, (GLfloat) int_width, (GLfloat) int_height, 0.0, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); #ifdef GL_DAMAGE glEnable(GL_SCISSOR_TEST); /* * Set the damaged area as the scissor area. */ wi->damaged_area.orig.x = ZnNearestInt(wi->damaged_area.orig.x); wi->damaged_area.orig.y = ZnNearestInt(wi->damaged_area.orig.y); wi->damaged_area.corner.x = ZnNearestInt(wi->damaged_area.corner.x); wi->damaged_area.corner.y = ZnNearestInt(wi->damaged_area.corner.y); glScissor((int) wi->damaged_area.orig.x, int_height - (int) wi->damaged_area.corner.y, (int) (wi->damaged_area.corner.x - wi->damaged_area.orig.x), (int) (wi->damaged_area.corner.y - wi->damaged_area.orig.y)); #else /* * We do not use the damaged area for GL rendering, * set it to the whole area. */ wi->damaged_area.orig.x = wi->damaged_area.orig.y = wi->inset; wi->damaged_area.corner.x = int_width-wi->inset; wi->damaged_area.corner.y = int_height-wi->inset; #endif /* * Clear the GL buffers. */ glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); /* * Setup the background tile if needed. */ if (wi->tile != ZnUnspecifiedImage) { ZnBBox bbox; bbox.orig.x = bbox.orig.y = 0.0; bbox.corner.x = int_width; bbox.corner.y = int_height; ZnRenderTile(wi, wi->tile, NULL, NULL, NULL, (ZnPoint *) &bbox); } wi->top_group->class->Render(wi->top_group); if ((wi->border_width > 0) || (wi->highlight_width > 0)) { unsigned short alpha; #ifdef GL_DAMAGE glDisable(GL_SCISSOR_TEST); #endif if (wi->highlight_width > 0) { color = ZnGetGradientColor(ISSET(wi->flags, ZN_GOT_FOCUS)?wi->highlight_color: wi->highlight_bg_color, 0.0, &alpha); alpha = ZnComposeAlpha(alpha, 100); glColor4us(color->red, color->green, color->blue, alpha); glBegin(GL_QUAD_STRIP); glVertex2d(0.0, 0.0); glVertex2i(wi->highlight_width, wi->highlight_width); glVertex2i(int_width, 0); glVertex2i(int_width - wi->highlight_width, wi->highlight_width); glVertex2i(int_width, int_height); glVertex2i(int_width - wi->highlight_width, int_height - wi->highlight_width); glVertex2i(0, int_height); glVertex2i(wi->highlight_width, int_height - wi->highlight_width); glVertex2i(0, 0); glVertex2i(wi->highlight_width, wi->highlight_width); glEnd(); } if (wi->border_width > 0) { if (wi->relief != ZN_RELIEF_FLAT) { p[4].x = p[4].y = p[3].y = p[1].x = wi->highlight_width; p[0] = p[4]; p[3].x = p[2].x = int_width - wi->highlight_width; p[2].y = p[1].y = int_height - wi->highlight_width; ZnRenderPolygonRelief(wi, wi->relief, wi->relief_grad, False, p, 5, (ZnReal) wi->border_width); } else { color = ZnGetGradientColor(wi->back_color, 0.0, &alpha); alpha = ZnComposeAlpha(alpha, 100); glColor4us(color->red, color->green, color->blue, alpha); glBegin(GL_QUAD_STRIP); glVertex2d(0.0, 0.0); glVertex2i(wi->highlight_width, wi->highlight_width); glVertex2i(int_width, 0); glVertex2i(int_width - wi->highlight_width, wi->highlight_width); glVertex2i(int_width, int_height); glVertex2i(int_width - wi->highlight_width, int_height - wi->highlight_width); glVertex2i(0, int_height); glVertex2i(wi->highlight_width, int_height - wi->highlight_width); glVertex2i(0, 0); glVertex2i(wi->highlight_width, wi->highlight_width); glEnd(); } } #ifdef GL_DAMAGE glEnable(GL_SCISSOR_TEST); #endif } glFlush(); /* Switch the GL buffers. */ ZnGLSwapBuffers(wi); ZnGLRelease(wi); /* * Wait the end of GL update if we need to synchronize * to monitor perfs. */ if (ISSET(wi->flags, ZN_MONITORING)) { ZnGLWaitGL(); } #endif } else { XRectangle r, rs[4]; ZnBBox merge; ClampDamageArea(wi); /* * Merge the damaged area with the exposed area. */ ZnResetBBox(&merge); ZnCopyBBox(&wi->damaged_area, &merge); ZnAddBBoxToBBox(&merge, &wi->exposed_area); if (!ZnIsEmptyBBox(&merge)) { /* Set the whole damaged area as clip rect. */ wi->damaged_area.orig.x = r.x = ZnNearestInt(wi->damaged_area.orig.x); wi->damaged_area.orig.y = r.y = ZnNearestInt(wi->damaged_area.orig.y); wi->damaged_area.corner.x = ZnNearestInt(wi->damaged_area.corner.x); wi->damaged_area.corner.y = ZnNearestInt(wi->damaged_area.corner.y); r.width = (unsigned short) (wi->damaged_area.corner.x - wi->damaged_area.orig.x); r.height = (unsigned short) (wi->damaged_area.corner.y - wi->damaged_area.orig.y); p[0] = wi->damaged_area.orig; p[1] = wi->damaged_area.corner; ZnTriStrip1(&tristrip, p, 2, False); ZnPushClip(wi, &tristrip, True, True); /* Fill the background of the double buffer pixmap. */ if (wi->tile == ZnUnspecifiedImage) { values.foreground = ZnPixel(ZnGetGradientColor(wi->back_color, 0.0, NULL)); values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCForeground, &values); } else { values.fill_style = FillTiled; values.tile = ZnImagePixmap(wi->tile); values.ts_x_origin = values.ts_y_origin = 0; XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCTile|GCTileStipXOrigin|GCTileStipYOrigin, &values); } /*printf("Repair : filling rectangle: %d %d %d %d\n", r.x, r.y, r.width, r.height);*/ XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, r.x, r.y, r.width, r.height); /* Draw the items */ /*printf("Drawing\n");*/ wi->top_group->class->Draw(wi->top_group); ZnPopClip(wi, True); /* * Send the merged area back to screen. */ merge.orig.x = MAX(merge.orig.x, wi->inset); merge.orig.y = MAX(merge.orig.y, wi->inset); merge.corner.x = MIN(merge.corner.x, int_width-wi->inset); merge.corner.y = MIN(merge.corner.y, int_height-wi->inset); ZnBBox2XRect(&merge, &r); XCopyArea(wi->dpy, wi->draw_buffer, Tk_WindowId(wi->win), wi->gc, r.x, r.y, r.width, r.height, r.x, r.y); } /* * Redraw the borders. */ if (wi->border_width > 0) { Pixmap save; save = wi->draw_buffer; wi->draw_buffer = Tk_WindowId(wi->win); if (wi->relief_grad != ZN_RELIEF_FLAT) { r.x = r.y = wi->highlight_width; r.width = int_width - 2*wi->highlight_width; r.height = int_height - 2*wi->highlight_width; ZnDrawRectangleRelief(wi, wi->relief, wi->relief_grad, &r, (ZnDim) wi->border_width); } else { XSetForeground(wi->dpy, wi->gc, ZnPixel(ZnGetGradientColor(wi->back_color, 0.0, NULL))); XSetFillStyle(wi->dpy, wi->gc, FillSolid); rs[0].x = rs[0].y = wi->highlight_width; rs[0].width = int_width - 2*wi->highlight_width; rs[0].height = wi->border_width; rs[1].x = int_width - wi->highlight_width - wi->border_width; rs[1].y = 0; rs[1].width = wi->border_width; rs[1].height = int_height - 2*wi->highlight_width; rs[2].x = 0; rs[2].y = int_height - wi->highlight_width - wi->border_width; rs[2].width = rs[0].width; rs[2].height = wi->border_width; rs[3].x = rs[3].y = wi->highlight_width; rs[3].width = wi->border_width; rs[3].height = rs[1].height; XFillRectangles(wi->dpy, Tk_WindowId(wi->win), wi->gc, rs, 4); } wi->draw_buffer = save; } if (wi->highlight_width > 0) { XSetForeground(wi->dpy, wi->gc, ZnPixel(ZnGetGradientColor(ISSET(wi->flags, ZN_GOT_FOCUS)?wi->highlight_color: wi->highlight_bg_color, 0.0, NULL))); XSetFillStyle(wi->dpy, wi->gc, FillSolid); rs[0].x = rs[0].y = 0; rs[0].width = int_width; rs[0].height = wi->highlight_width; rs[1].x = int_width - wi->highlight_width; rs[1].y = 0; rs[1].width = wi->highlight_width; rs[1].height = int_height; rs[2].x = 0; rs[2].y = int_height - wi->highlight_width; rs[2].width = int_width; rs[2].height = wi->highlight_width; rs[3].x = rs[3].y = 0; rs[3].width = wi->highlight_width; rs[3].height = int_height; XFillRectangles(wi->dpy, Tk_WindowId(wi->win), wi->gc, rs, 4); } } } /* *---------------------------------------------------------------------- * * 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. */ { ZnWInfo *wi = (ZnWInfo *) client_data; CLEAR(wi->flags, ZN_UPDATE_PENDING); if (ISCLEAR(wi->flags, ZN_REALIZED) || !Tk_IsMapped(wi->win)) { return; } if (ISSET(wi->flags, ZN_MONITORING)) { #ifndef _WIN32 ZnXStartChrono(wi->total_draw_chrono, wi->dpy, Tk_WindowId(wi->win)); ZnResetChronos(wi->this_draw_chrono); ZnXStartChrono(wi->this_draw_chrono, wi->dpy, Tk_WindowId(wi->win)); #endif } do { /* * Update the items. * NOTE: See above. */ 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->flags, ZN_INTERNAL_NEED_REPICK)) { Tk_Window tkwin; Tcl_Preserve((ClientData) wi); CLEAR(wi->flags, ZN_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->flags, ZN_INTERNAL_NEED_REPICK)); /* * Repair the scene where it is no longer up to date, * then send the merged area back to the screen. */ Repair(wi); /* * Reset the exposed & damaged areas. */ ZnResetBBox(&wi->exposed_area); ZnResetBBox(&wi->damaged_area); if (ISSET(wi->flags, ZN_MONITORING)) { #ifndef _WIN32 ZnXStopChrono(wi->total_draw_chrono, wi->dpy, Tk_WindowId(wi->win)); ZnXStopChrono(wi->this_draw_chrono, wi->dpy, Tk_WindowId(wi->win)); #endif } if (ISSET(wi->flags, ZN_UPDATE_SCROLLBARS)) { UpdateScrollbars(wi); } } static void InitZinc(Tcl_Interp *interp) { static ZnBool inited = False; unsigned int i, x, y, bit; char name[TCL_INTEGER_SPACE + 20]; if (inited) { return; } /* * 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 < ZN_NUM_ALPHA_STEPS; i++) { for (y = 0; y < 4; y++) { bitmaps[i][y][0] = 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][0] |= (1 << x); bitmaps[i][y][0] |= (1 << (4 + x)); } } bitmaps[i][y][1] = bitmaps[i][y][2] = bitmaps[i][y][3] = bitmaps[i][y][0]; bitmaps[i][y+4][0] = bitmaps[i][y+4][1] = bitmaps[i][y][0]; bitmaps[i][y+4][2] = bitmaps[i][y+4][3] = bitmaps[i][y][0]; bitmaps[i][y+8][0] = bitmaps[i][y+8][1] = bitmaps[i][y][0]; bitmaps[i][y+8][2] = bitmaps[i][y+8][3] = bitmaps[i][y][0]; bitmaps[i][y+12][0] = bitmaps[i][y+12][1] = bitmaps[i][y][0]; bitmaps[i][y+12][2] = bitmaps[i][y+12][3] = bitmaps[i][y][0]; bitmaps[i][y+16][0] = bitmaps[i][y+16][1] = bitmaps[i][y][0]; bitmaps[i][y+16][2] = bitmaps[i][y+16][3] = bitmaps[i][y][0]; bitmaps[i][y+20][0] = bitmaps[i][y+20][1] = bitmaps[i][y][0]; bitmaps[i][y+20][2] = bitmaps[i][y+20][3] = bitmaps[i][y][0]; bitmaps[i][y+24][0] = bitmaps[i][y+24][1] = bitmaps[i][y][0]; bitmaps[i][y+24][2] = bitmaps[i][y+24][3] = bitmaps[i][y][0]; bitmaps[i][y+28][0] = bitmaps[i][y+28][1] = bitmaps[i][y][0]; bitmaps[i][y+28][2] = bitmaps[i][y+28][3] = bitmaps[i][y][0]; } sprintf(name, "AlphaStipple%d", i); Tk_DefineBitmap(interp, Tk_GetUid(name), (char *) bitmaps[i], 32, 32); } /* * Initialize the item module. */ ZnItemInit(); 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("!"); dot_uid = Tk_GetUid("."); star_uid = Tk_GetUid("*"); inited = True; } #ifdef BUILD_Tkzinc # undef TCL_STORAGE_CLASS # define TCL_STORAGE_CLASS DLLEXPORT #endif /* *---------------------------------------------------------------------- * * Tkzinc_Init -- * * This procedure is invoked by Tcl_AppInit in tkAppInit.c to * initialize the widget. * *---------------------------------------------------------------------- */ EXTERN int Tkzinc_Init(Tcl_Interp *interp) /* Used for error reporting. */ { #ifndef PTK if ( # ifdef USE_TCL_STUBS Tcl_InitStubs(interp, "8.4", 0) # else Tcl_PkgRequire(interp, "Tcl", "8.4", 0) # endif == NULL) { return TCL_ERROR; } if ( # ifdef USE_TK_STUBS Tk_InitStubs(interp, "8.4", 0) # else Tcl_PkgRequire(interp, "Tk", "8.4", 0) # endif == NULL) { return TCL_ERROR; } #endif /* * Create additional commands */ Tcl_CreateObjCommand(interp, "zinc", ZincObjCmd, (ClientData) Tk_MainWindow(interp), (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "mapinfo", ZnMapInfoObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); Tcl_CreateObjCommand(interp, "videomap", ZnVideomapObjCmd, (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL); /* * Initialise Overlap manager library. */ OmInit(); #ifndef PTK if (Tcl_PkgProvide(interp, "Tkzinc", VERSION) == TCL_ERROR) { return TCL_ERROR; } #endif return TCL_OK; } EXTERN int Tkzinc_debug_Init(Tcl_Interp *interp) /* Used for error reporting. */ { return Tkzinc_Init(interp); } #ifdef _WIN32 /* *---------------------------------------------------------------------- * * DllEntryPoint -- * * This wrapper function is used by Windows to invoke the * initialization code for the DLL. If we are compiling * with Visual C++, this routine will be renamed to DllMain. * routine. * * Results: * Returns TRUE; * * Side effects: * None. * *---------------------------------------------------------------------- */ BOOL APIENTRY DllEntryPoint(HINSTANCE hInst __unused, /* Library instance handle. */ DWORD reason __unused, /* Reason this function is being called. */ LPVOID reserved __unused) /* Not used. */ { return TRUE; } #endif