/* * Item.c -- Implementation of items. * * Authors : Patrick Lecoanet. * Creation date : * * $Id$ */ /* * Copyright (c) 1993 - 2000 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. * */ #include /* For INT_MAX */ #include #include #include #include #include #ifdef GLX #include #include #endif #include "Field.h" #include "Item.h" #include "Group.h" #include "Types.h" #include "WidgetInfo.h" #include "Geo.h" #include "Draw.h" #include "MapInfo.h" #include "Image.h" #include "Color.h" #include "tkZinc.h" #include "OverlapMan.h" static const char rcsid[] = "$Id$"; static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $"; static ZnList item_classes = NULL; static ZnList item_stack = NULL; /* * This array must be kept in sync with the * corresponding defines in Types.h. */ static char *attribute_type_strings[] = { "", "boolean", "bitmap", "bitmaplist", "string", "font", "edgelist", "relief", "dimension", "priority", "alignment", "autoalignment", "lineend", "labelformat", "linestyle", "lineshape", "item", "angle", "integer", "unsignedint", "point", "anchor", "taglist", "mapinfo", "image", "leaderanchors", "joinstyle", "capstyle", "gradient", "gradientlist", "window", "alpha", }; /* ********************************************************************************** * * Forward functions * ********************************************************************************** */ static void Invalidate(Item item, int reason); static void AttributeToObj(WidgetInfo *wi, void *record, ZnAttrConfig *desc, char *buffer, Tcl_Obj *result); /* ********************************************************************************** * * InitAttrDesc -- * ********************************************************************************** */ static void InitAttrDesc(ZnAttrConfig *attr_desc) { if (!attr_desc) { return; } while (attr_desc->type != ZN_CONFIG_END) { attr_desc->uid = Tk_GetUid(attr_desc->name); attr_desc++; } } /* ********************************************************************************** * * AttributesInfo -- * ********************************************************************************** */ int ZnAttributesInfo(WidgetInfo *wi, void *record, ZnAttrConfig *desc, int argc, Tcl_Obj *CONST args[]) { Tk_Uid attr_uid = NULL; Tcl_Obj *l, *entries[5]; char buffer[256]; if (argc == 1) { attr_uid = Tk_GetUid(Tcl_GetString(args[0])); while (True) { if (desc->type == ZN_CONFIG_END) { Tcl_AppendResult(wi->interp, "unknown attribute \"", Tcl_GetString(args[0]), "\"", NULL); return ZN_ERROR; } else if (attr_uid == desc->uid) { break; } else { desc++; } } entries[0] = NewStringObj(desc->name); entries[1] = NewStringObj(attribute_type_strings[desc->type]); entries[2] = NewBooleanObj(desc->read_only ? 1 : 0); entries[3] = NewStringObj(""); entries[4] = NewStringObj(""); AttributeToObj(wi, record, desc, buffer, entries[4]); Tcl_SetObjResult(wi->interp, Tcl_NewListObj(5, entries)); } else { l = Tcl_GetObjResult(wi->interp); while (desc->type != ZN_CONFIG_END) { entries[0] = NewStringObj(desc->name); entries[1] = NewStringObj(attribute_type_strings[desc->type]); entries[2] = NewBooleanObj(desc->read_only ? 1 : 0); entries[3] = NewStringObj(""); entries[4] = NewStringObj(""); AttributeToObj(wi, record, desc, buffer, entries[4]); Tcl_ListObjAppendElement(wi->interp, l, Tcl_NewListObj(5, entries)); desc++; } } return TCL_OK; } /* ********************************************************************************** * * ZnConfigureAttributes -- * ********************************************************************************** */ int ZnConfigureAttributes(WidgetInfo *wi, void *record, ZnAttrConfig *attr_desc, int argc, Tcl_Obj *CONST args[], int *flags) { int i; Tk_Uid attr_uid; ZnAttrConfig *desc; ZnPtr valp; char *str; for (i = 0; i < argc; i += 2) { attr_uid = Tk_GetUid(Tcl_GetString(args[i])); desc = attr_desc; while (True) { if (desc->type == ZN_CONFIG_END) { Tcl_AppendResult(wi->interp, "unknown attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } else if (attr_uid == desc->uid) { if (desc->read_only) { Tcl_AppendResult(wi->interp, "attribute \"", Tcl_GetString(args[i]), "\" can only be read", NULL); return ZN_ERROR; } valp = ((char *) record) + desc->offset; /*printf("record <0x%X>, valp <0x%X>, offset %d\n", record, valp, desc->offset);*/ switch (desc->type) { case ZN_CONFIG_GRADIENT: { ZnGradient *g; Tk_Uid new_name = Tk_GetUid(Tcl_GetString(args[i+1])); char *name = NULL; if (*((ZnGradient **) valp)) { name = ZnNameOfGradient(*((ZnGradient **) valp)); } if (name != new_name) { g = ZnGetGradient(wi->interp, wi->win, new_name); if (!g) { Tcl_AppendResult(wi->interp, " gradient expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (*((ZnGradient **) valp)) { ZnFreeGradient(*((ZnGradient **) valp)); } *((ZnGradient **) valp) = g; *flags |= desc->flags; } break; } case ZN_CONFIG_GRADIENT_LIST: { ZnList new_grad_list = NULL; ZnGradient **grads; int num_grads, j, k; Tcl_Obj **elems; if (Tcl_ListObjGetElements(wi->interp, args[i+1], &num_grads, &elems) == ZN_ERROR) { Tcl_AppendResult(wi->interp, " gradient list expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (num_grads) { new_grad_list = ZnListNew(num_grads, sizeof(ZnGradient *)); ZnListAssertSize(new_grad_list, num_grads); grads = ZnListArray(new_grad_list); for (j = 0; j < num_grads; j++) { str = Tcl_GetString(elems[j]); if (!*str) { if (j == 0) { goto grads_err; } grads[j] = grads[j-1]; } else { grads[j] = ZnGetGradient(wi->interp, wi->win, str); } if (!grads[j]) { grads_err: Tcl_AppendResult(wi->interp, " invalid gradient \"", str, "\" in gradient list", NULL); for (k = 0; k < j; k++) { ZnFreeGradient(grads[k]); } ZnListFree(new_grad_list); return ZN_ERROR; } } } if (*((ZnList *) valp)) { num_grads = ZnListSize(*((ZnList *) valp)); grads = ZnListArray(*((ZnList *) valp)); for (j = 0; j < num_grads; j++) { if (grads[j]) { ZnFreeGradient(grads[j]); } } ZnListFree(*((ZnList *) valp)); *((ZnList *) valp) = new_grad_list; *flags |= desc->flags; } else { if (new_grad_list) { *((ZnList *) valp) = new_grad_list; *flags |= desc->flags; } } break; } case ZN_CONFIG_BOOL: { int b; if (Tcl_GetBooleanFromObj(wi->interp, args[i+1], &b) != ZN_OK) { Tcl_AppendResult(wi->interp, " boolean expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (b ^ (ISSET(*((unsigned short *) valp), desc->bool_bit) != 0)) { ASSIGN(*((unsigned short *) valp), desc->bool_bit, b); *flags |= desc->flags; } break; } case ZN_CONFIG_IMAGE: case ZN_CONFIG_BITMAP: { ZnImage image = ZnUnspecifiedImage; char *msg; #if 0 char *name = ""; if (*((ZnImage *) valp) != ZnUnspecifiedImage) { name = ZnNameOfImage(*((ZnImage *) valp)); } #endif str = Tcl_GetString(args[i+1]); #if 0 if (strcmp(name, str) != 0) { #endif if (strlen(str) != 0) { if (desc->type == ZN_CONFIG_IMAGE) { image = ZnGetImage(wi, str); msg = " image expected for attribute \""; } else{ image = ZnGetBitmap(wi, str); msg = " bitmap expected for attribute \""; } if (image == ZnUnspecifiedImage) { Tcl_AppendResult(wi->interp, msg, Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } } if (*((ZnImage *) valp) != ZnUnspecifiedImage) { ZnFreeImage(*((ZnImage *) valp)); } *((ZnImage *) valp) = image; *flags |= desc->flags; #if 0 } #endif break; } case ZN_CONFIG_BITMAP_LIST: { ZnList new_pat_list = NULL; ZnImage *pats; int num_pats, j, k; Tcl_Obj **elems; if (Tcl_ListObjGetElements(wi->interp, args[i+1], &num_pats, &elems) == ZN_ERROR) { Tcl_AppendResult(wi->interp, " pattern list expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (num_pats) { new_pat_list = ZnListNew(num_pats, sizeof(Pixmap)); ZnListAssertSize(new_pat_list, num_pats); pats = ZnListArray(new_pat_list); for (j = 0; j < num_pats; j++) { str = Tcl_GetString(elems[j]); if (strlen(str) != 0) { pats[j] = ZnGetBitmap(wi, str); if (pats[j] == None) { Tcl_AppendResult(wi->interp, " unknown pattern \"", str, "\" in pattern list", NULL); for (k = 0; k < j; k++) { ZnFreeImage(pats[k]); } ZnListFree(new_pat_list); return ZN_ERROR; } } else { pats[j] = ZnUnspecifiedImage; } } } if (*((ZnList *) valp)) { num_pats = ZnListSize(*((ZnList *) valp)); pats = ZnListArray(*((ZnList *) valp)); for (j = 0; j < num_pats; j++) { if (pats[j] != ZnUnspecifiedImage) { ZnFreeImage(pats[j]); } } ZnListFree(*((ZnList *) valp)); *((ZnList *) valp) = new_pat_list; *flags |= desc->flags; } else { if (new_pat_list) { *((ZnList *) valp) = new_pat_list; *flags |= desc->flags; } } break; } case ZN_CONFIG_TAG_LIST: { int num_tags, j; Tcl_Obj **elems; if (Tcl_ListObjGetElements(wi->interp, args[i+1], &num_tags, &elems) == ZN_ERROR) { Tcl_AppendResult(wi->interp, " tag list expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (*((ZnList *) valp)) { ITEM.FreeTags((Item) record); *flags |= desc->flags; } if (num_tags) { for (j = 0; j < num_tags; j++) { ITEM.AddTag((Item) record, Tk_GetUid(Tcl_GetString(elems[j]))); } *flags |= desc->flags; } break; } case ZN_CONFIG_STRING: case ZN_CONFIG_MAP_INFO: { char *text = NULL; str = Tcl_GetString(args[i+1]); if (!*((char **) valp) || strcmp(str, *((char **) valp)) != 0) { if (strlen(str)) { text = (char *) ZnMalloc(strlen(str)+1); strcpy(text, str); } if (*((char **) valp)) { ZnFree(*((char **) valp)); } *((char **) valp) = text; *flags |= desc->flags; } break; } case ZN_CONFIG_FONT: { Tk_Font font; Tk_Uid name = ""; if (*((Tk_Font *) valp)) { name = Tk_NameOfFont(*((Tk_Font *) valp)); } str = Tcl_GetString(args[i+1]); if (strcmp(name, str) != 0) { font = Tk_GetFont(wi->interp, wi->win, str); if (!font) { Tcl_AppendResult(wi->interp, " font expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (*((Tk_Font *) valp)) { Tk_FreeFont(*((Tk_Font *) valp)); } *((Tk_Font *) valp) = font; *flags |= desc->flags; } break; } case ZN_CONFIG_EDGE_LIST: { Border border = NO_BORDER; int j, len, largc; Tcl_Obj **largv; if (Tcl_ListObjGetElements(wi->interp, args[i+1], &largc, &largv) == ZN_ERROR) { border_error: Tcl_AppendResult(wi->interp, " border expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } for (j = 0; j < largc; j++) { str = Tcl_GetString(largv[j]); len = strlen(str); if (strncasecmp(str, LEFT_SPEC, len) == 0) { border |= LEFT_BORDER; } else if (strncasecmp(str, RIGHT_SPEC, len) == 0) { border |= RIGHT_BORDER; } else if (strncasecmp(str, TOP_SPEC, len) == 0) { border |= TOP_BORDER; } else if (strncasecmp(str, BOTTOM_SPEC, len) == 0) { border |= BOTTOM_BORDER; } else if (strncasecmp(str, CONTOUR_SPEC, len) == 0) { border |= CONTOUR_BORDER; } else if (strncasecmp(str, OBLIQUE_SPEC, len) == 0) { border |= OBLIQUE; } else if (strncasecmp(str, COUNTER_OBLIQUE_SPEC, len) == 0) { border |= COUNTER_OBLIQUE; } else if (strncasecmp(str, NO_BORDER_SPEC, len) == 0) { border |= NO_BORDER; } else { goto border_error; } } if (border != *((Border *) valp)) { *((Border *) valp) = border; *flags |= desc->flags; } break; } case ZN_CONFIG_LINE_SHAPE: { LineShape line_shape; int len; str = Tcl_GetString(args[i+1]); len = strlen(str); if (strncasecmp(str, STRAIGHT_SPEC, len) == 0) { line_shape = LINE_STRAIGHT; } else if (strncasecmp(str, RIGHT_LIGHTNING_SPEC, len) == 0) { line_shape = LINE_RIGHT_LIGHTNING; } else if (strncasecmp(str, LEFT_LIGHTNING_SPEC, len) == 0) { line_shape = LINE_LEFT_LIGHTNING; } else if (strncasecmp(str, RIGHT_CORNER_SPEC, len) == 0) { line_shape = LINE_RIGHT_CORNER; } else if (strncasecmp(str, LEFT_CORNER_SPEC, len) == 0) { line_shape = LINE_LEFT_CORNER; } else if (strncasecmp(str, DOUBLE_RIGHT_CORNER_SPEC, len) == 0) { line_shape = LINE_DOUBLE_RIGHT_CORNER; } else if (strncasecmp(str, DOUBLE_LEFT_CORNER_SPEC, len) == 0) { line_shape = LINE_DOUBLE_LEFT_CORNER; } else { Tcl_AppendResult(wi->interp, " line shape expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (line_shape != *((LineShape *) valp)) { *((LineShape *) valp) = line_shape; *flags |= desc->flags; } break; } case ZN_CONFIG_LINE_STYLE: { LineStyle line_style; int len; str = Tcl_GetString(args[i+1]); len = strlen(str); if (strncasecmp(str, SIMPLE_SPEC, len) == 0) line_style = LINE_SIMPLE; else if (strncasecmp(str, DASHED_SPEC, len) == 0) line_style = LINE_DASHED; else if (strncasecmp(str, MIXED_SPEC, len) == 0) line_style = LINE_MIXED; else if (strncasecmp(str, DOTTED_SPEC, len) == 0) line_style = LINE_DOTTED; else { Tcl_AppendResult(wi->interp, " line style expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (line_style != *((LineStyle *) valp)) { *((LineStyle *) valp) = line_style; *flags |= desc->flags; } break; } case ZN_CONFIG_LINE_END: { ZnLineEnd line_end = NULL; str = Tcl_GetString(args[i+1]); if (strlen(str) != 0) { line_end = LineEndCreate(wi->interp, str); if (line_end == NULL) { return ZN_ERROR; } } if (*((ZnLineEnd *) valp) != NULL) { LineEndDelete(*((ZnLineEnd *) valp)); *((ZnLineEnd *) valp) = line_end; *flags |= desc->flags; } else { if (line_end != NULL) { *((ZnLineEnd *) valp) = line_end; *flags |= desc->flags; } } break; } case ZN_CONFIG_RELIEF: { ReliefStyle relief; if (ZnGetRelief(wi, Tcl_GetString(args[i+1]), &relief) == ZN_ERROR) { Tcl_AppendResult(wi->interp, " relief expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (relief != *((ReliefStyle *) valp)) { /*printf("valp <0x%X>, flags <0x%X>, relief %d\n", valp, flags, relief);*/ *((ReliefStyle *) valp) = relief; *flags |= desc->flags; } break; } case ZN_CONFIG_JOIN_STYLE: { int join; if (Tk_GetJoinStyle(wi->interp, Tcl_GetString(args[i+1]), &join) == ZN_ERROR) { Tcl_AppendResult(wi->interp, " join expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (join != *((int *) valp)) { *((int *) valp) = join; *flags |= desc->flags; } break; } case ZN_CONFIG_CAP_STYLE: { int cap; if (Tk_GetCapStyle(wi->interp, Tcl_GetString(args[i+1]), &cap) == ZN_ERROR) { Tcl_AppendResult(wi->interp, " cap expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (cap != *((int *) valp)) { *((int *) valp) = cap; *flags |= desc->flags; } break; } case ZN_CONFIG_POINT: { ZnPoint point; int largc; Tcl_Obj **largv; if ((Tcl_ListObjGetElements(wi->interp, args[i+1], &largc, &largv) == ZN_ERROR) || (largc != 2)) { point_error: Tcl_AppendResult(wi->interp, " position expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if ((Tcl_GetDoubleFromObj(wi->interp, largv[0], &point.x) == ZN_ERROR) || (Tcl_GetDoubleFromObj(wi->interp, largv[1], &point.y) == ZN_ERROR)) { goto point_error; } if ((point.x != ((ZnPoint *) valp)->x) || (point.y != ((ZnPoint *) valp)->y)) { *((ZnPoint *) valp) = point; *flags |= desc->flags; } break; } case ZN_CONFIG_DIM: { ZnDim dim; if (Tcl_GetDoubleFromObj(wi->interp, args[i+1], &dim) == ZN_ERROR) { Tcl_AppendResult(wi->interp, " dimension expected for attribute \"", Tcl_GetString(args[i+1]), "\"", NULL); return ZN_ERROR; } if (dim != *((ZnDim *) valp)) { *((ZnDim *) valp) = dim; *flags |= desc->flags; } break; } case ZN_CONFIG_PRI: { int pri; if (Tcl_GetIntFromObj(wi->interp, args[i+1], &pri) == ZN_ERROR) { return ZN_ERROR; } if (pri < 0) { Tcl_AppendResult(wi->interp, " priority must be a positive integer \"", Tcl_GetString(args[i+1]), "\"", NULL); return ZN_ERROR; } if (pri != *((int *) valp)) { *((int *) valp) = pri; ITEM.UpdateItemPriority((Item) record, ZN_NO_ITEM, True); *flags |= desc->flags; } break; } case ZN_CONFIG_ITEM: /* * Can be an item id or a tag. In this last case * consider only the first item (unspecified order) * associated with the tag. */ { Item item2; int result; TagSearch *search_var = NULL; if (strlen(Tcl_GetString(args[i+1])) == 0) { item2 = ZN_NO_ITEM; } else { result = ZnItemWithTagOrId(wi, args[i+1], &item2, &search_var); ZnTagSearchDestroy(search_var); if ((result == ZN_ERROR) || (item2 == ZN_NO_ITEM)) { Tcl_AppendResult(wi->interp, " unknown item \"", Tcl_GetString(args[i+1]), "\"", NULL); return ZN_ERROR; } } if (item2 != *((Item *) valp)) { *((Item *) valp) = item2; *flags |= desc->flags; } } break; case ZN_CONFIG_WINDOW: { ZnWindow win, ancestor, parent; str = Tcl_GetString(args[i+1]); if (strlen(str) == 0) { win = NULL; } else { win = Tk_NameToWindow(wi->interp, str, wi->win); if (win == NULL) { return ZN_ERROR; } else { /* * Make sure that the zinc widget is either the parent of the * window associated with the item or a descendant of that * parent. Also, don't allow a toplevel window or the widget * itself to be managed. */ parent = Tk_Parent(win); for (ancestor = wi->win; ; ancestor = Tk_Parent(ancestor)) { if (ancestor == parent) { break; } if (((Tk_FakeWin *) (ancestor))->flags & TK_TOP_LEVEL) { badWindow: Tcl_AppendResult(wi->interp, "can't use ", Tk_PathName(win), " in a window item of this zinc widget", (char *) NULL); win = NULL; return TCL_ERROR; } } if (((Tk_FakeWin *) (win))->flags & TK_TOP_LEVEL) { goto badWindow; } if (win == wi->win) { goto badWindow; } if (win != *((ZnWindow *) valp)) { *((ZnWindow *) valp) = win; *flags |= desc->flags; } } } } break; case ZN_CONFIG_INT: case ZN_CONFIG_UINT: case ZN_CONFIG_ANGLE: case ZN_CONFIG_ALPHA: { int integer; if (Tcl_GetIntFromObj(wi->interp, args[i+1], &integer) == ZN_ERROR) { return ZN_ERROR; } switch (desc->type) { case ZN_CONFIG_ANGLE: if ((integer > 360) || (integer < -360)) { integer = integer % 360; } break; case ZN_CONFIG_UINT: if (integer < 0) { integer = 0; } break; case ZN_CONFIG_ALPHA: if (integer < 0) { integer = 0; } if (integer > 100) { integer = 100; } break; } if (integer != *((int *) valp)) { *((int *) valp) = integer; *flags |= desc->flags; } break; } case ZN_CONFIG_ALIGNMENT: { Tk_Justify justify; if (Tk_GetJustify(wi->interp, Tcl_GetString(args[i+1]), &justify) == ZN_ERROR) { Tcl_AppendResult(wi->interp, " justify expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (justify != *((ZnJustify *) valp)) { *((ZnJustify *) valp) = justify; *flags |= desc->flags; } break; } case ZN_CONFIG_ANCHOR: { Tk_Anchor anchor; if (Tk_GetAnchor(wi->interp, Tcl_GetString(args[i+1]), &anchor) == ZN_ERROR) { Tcl_AppendResult(wi->interp, " anchor expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (anchor != *((ZnAnchor *) valp)) { *((ZnAnchor *) valp) = anchor; *flags |= desc->flags; } break; } case ZN_CONFIG_LABEL_FORMAT: { ZnLabelFormat frmt = NULL; str = Tcl_GetString(args[i+1]); while (*str && (*str == ' ')) { str++; } if (strlen(str) != 0) { Item item = (Item) record; frmt = LabelFormatCreate(wi->interp, str, FIELD.NumFields(item->class->GetFieldSet(item))); if (frmt == NULL) { return ZN_ERROR; } } if (*((ZnLabelFormat *) valp) != NULL) { LabelFormatDelete(*((ZnLabelFormat *) valp)); *((ZnLabelFormat *) valp) = frmt; *flags |= desc->flags; } else { if (frmt != NULL) { *((ZnLabelFormat *) valp) = frmt; *flags |= desc->flags; } } break; } case ZN_CONFIG_AUTO_ALIGNMENT: { AutoAlign aa; int j; str = Tcl_GetString(args[i+1]); if (strcmp(str, "-") == 0) { aa.automatic = False; } else if (strlen(str) == 3) { aa.automatic = True; for (j = 0; j < 3; j++) { switch(str[j]) { case 'l': case 'L': aa.align[j] = ZnJustifyLeft; break; case 'c': case 'C': aa.align[j] = ZnJustifyCenter; break; case 'r': case 'R': aa.align[j] = ZnJustifyRight; break; default: Tcl_AppendResult(wi->interp, "invalid auto justify specifcation \"", Tcl_GetString(args[i+1]), "\" should be - or a triple of lcr", NULL); return ZN_ERROR; } } } else { Tcl_AppendResult(wi->interp, "invalid auto alignment specification \"", Tcl_GetString(args[i+1]), "\" should be - or a triple of lcr", NULL); return ZN_ERROR; } if ((aa.automatic != ((AutoAlign *) valp)->automatic) || (aa.align[0] != ((AutoAlign *) valp)->align[0]) || (aa.align[1] != ((AutoAlign *) valp)->align[1]) || (aa.align[2] != ((AutoAlign *) valp)->align[2])) { *((AutoAlign *) valp) = aa; *flags |= desc->flags; } break; } case ZN_CONFIG_LEADER_ANCHORS: /* * Format is: lChar leftLeaderAnchor [ lChar rightLeaderAnchor] * * If lChar is a '|', leftLeaderAnchor and rightLeaderAnchor are the indices * of the fields that serve to anchor the label's leader. More specifically * the bottom left corner of the left field and the bottom right corner of * the right field are used as the anchors. * If lChar is '%', leftLeaderAnchor and rightLeaderAnchor should be * specified as 'valxval', 'val' being a percentage (max 100) of the * width/height of the label bounding box. * If rightLeaderAnchor is not specified it defaults to leftLeaderAnchor. * If neither of them are specified, the center of the label is used as an * anchor. */ { LeaderAnchors lanch = NULL; int anchors[4]; int index, num_tok, anchor_index=0; str = Tcl_GetString(args[i+1]); while (*str && (*str == ' ')) { str++; } while (*str && (anchor_index < 4)) { switch (*str) { case '|': num_tok = sscanf(str, "|%d%n", &anchors[anchor_index], &index); if (num_tok != 1) { la_error: Tcl_AppendResult(wi->interp, " incorrect leader anchors \"", Tcl_GetString(args[i+1]), "\"", NULL); return ZN_ERROR; } anchors[anchor_index+1] = -1; break; case '%': num_tok = sscanf(str, "%%%dx%d%n", &anchors[anchor_index], &anchors[anchor_index+1], &index); if (num_tok != 2) { goto la_error; } if (anchors[anchor_index] < 0) { anchors[anchor_index] = 0; } if (anchors[anchor_index] > 100) { anchors[anchor_index] = 100; } if (anchors[anchor_index+1] < 0) { anchors[anchor_index+1] = 0; } if (anchors[anchor_index+1] > 100) { anchors[anchor_index+1] = 100; } break; } anchor_index += 2; str += index; } /* * If empty, pick the default (center of the bounding box). */ if (anchor_index != 0) { lanch = (LeaderAnchors ) ZnMalloc(sizeof(LeaderAnchorsStruct)); lanch->left_x = anchors[0]; lanch->left_y = anchors[1]; if (anchor_index == 2) { lanch->right_x = lanch->left_x; lanch->right_y = lanch->left_y; } else { lanch->right_x = anchors[2]; lanch->right_y = anchors[3]; } } if (*((LeaderAnchors *) valp) != NULL) { ZnFree(*((LeaderAnchors *) valp)); *((LeaderAnchors *) valp) = lanch; *flags |= desc->flags; } else { if (lanch != NULL) { *((LeaderAnchors *) valp) = lanch; *flags |= desc->flags; } } break; } } break; } else { desc++; } } } return ZN_OK; } /* ********************************************************************************** * * AttributeToObj -- * * Returns the obj representation of the attribute pointed * by 'valp'. The attribute type is given by 'type'. The function * never fail. The buffer parameter should be able to * contain 256 characters at least. * ********************************************************************************** */ static void AttributeToObj(WidgetInfo *wi, void *record, ZnAttrConfig *desc, char *buffer, Tcl_Obj *result) { char *valp = ((char *) record) + desc->offset; Tk_Uid str = ""; Tcl_Obj *o; int i; switch (desc->type) { case ZN_CONFIG_GRADIENT: if (*((ZnGradient **) valp)) { str = ZnNameOfGradient(*((ZnGradient **) valp)); Tcl_SetStringObj(result, str, strlen(str)); } break; case ZN_CONFIG_GRADIENT_LIST: { int num_grads; ZnGradient **grads; if (*((ZnList *) valp)) { grads = ZnListArray(*((ZnList *) valp)); num_grads = ZnListSize(*((ZnList *) valp)); for (i = 0; i < num_grads; i++) { o = NewStringObj(ZnNameOfGradient(grads[i])); Tcl_ListObjAppendElement(wi->interp, result, o); } } } break; case ZN_CONFIG_BOOL: Tcl_SetBooleanObj(result, ISSET(*((unsigned short *) valp), desc->bool_bit)?1:0); break; case ZN_CONFIG_IMAGE: case ZN_CONFIG_BITMAP: str = ""; if (*((ZnImage *) valp)) { str = ZnNameOfImage(*((ZnImage *) valp)); } Tcl_SetStringObj(result, str, strlen(str)); break; case ZN_CONFIG_BITMAP_LIST: { int num_pats=0; ZnImage *pats; if (*((ZnList *) valp)) { pats = (ZnImage *) ZnListArray(*((ZnList *) valp)); num_pats = ZnListSize(*((ZnList *) valp)); for (i = 0; i < num_pats; i++) { if (pats[i] != ZnUnspecifiedImage) { o = NewStringObj(ZnNameOfImage(pats[i])); } else { o = NewStringObj(""); } Tcl_ListObjAppendElement(wi->interp, result, o); } } break; } case ZN_CONFIG_TAG_LIST: { int num_tags=0; Tk_Uid *tags; if (*((ZnList *) valp)) { tags = (Tk_Uid *) ZnListArray(*((ZnList *) valp)); num_tags = ZnListSize(*((ZnList *) valp)); for (i = 0; i < num_tags; i++) { Tcl_ListObjAppendElement(wi->interp, result, NewStringObj(tags[i])); } } break; } case ZN_CONFIG_STRING: case ZN_CONFIG_MAP_INFO: str = *((char **) valp); if (str) { Tcl_SetStringObj(result, str, strlen(str)); } else { Tcl_SetStringObj(result, "", 0); } break; case ZN_CONFIG_FONT: str = ""; if (*((Tk_Font *) valp)) { str = Tk_NameOfFont(*((Tk_Font *) valp)); } Tcl_SetStringObj(result, str, strlen(str)); break; case ZN_CONFIG_EDGE_LIST: { Border border = *((Border *) valp); if (border == NO_BORDER) { Tcl_SetStringObj(result, NO_BORDER_SPEC, strlen(NO_BORDER_SPEC)); break; } buffer[0] = 0; if ((border & CONTOUR_BORDER) == CONTOUR_BORDER) { strcat(buffer, CONTOUR_SPEC); } else { if (border & LEFT_BORDER) { strcat(buffer, LEFT_SPEC); } if (border & RIGHT_BORDER) { if (buffer[0] != 0) { strcat(buffer, " "); } strcat(buffer, RIGHT_SPEC); } if (border & TOP_BORDER) { if (buffer[0] != 0) { strcat(buffer, " "); } strcat(buffer, TOP_SPEC); } if (border & BOTTOM_BORDER) { if (buffer[0] != 0) { strcat(buffer, " "); } strcat(buffer, BOTTOM_SPEC); } } if (border & OBLIQUE) { if (buffer[0] != 0) { strcat(buffer, " "); } strcat(buffer, OBLIQUE_SPEC); } if (border & COUNTER_OBLIQUE) { if (buffer[0] != 0) { strcat(buffer, " "); } strcat(buffer, COUNTER_OBLIQUE_SPEC); } Tcl_SetStringObj(result, buffer, strlen(buffer)); } break; case ZN_CONFIG_LINE_SHAPE: { LineShape line_shape = *((LineShape *) valp); switch (line_shape) { case LINE_STRAIGHT: str = STRAIGHT_SPEC; break; case LINE_RIGHT_LIGHTNING: str = RIGHT_LIGHTNING_SPEC; break; case LINE_LEFT_LIGHTNING: str = LEFT_LIGHTNING_SPEC; break; case LINE_RIGHT_CORNER: str = RIGHT_CORNER_SPEC; break; case LINE_LEFT_CORNER: str = LEFT_CORNER_SPEC; break; case LINE_DOUBLE_RIGHT_CORNER: str = DOUBLE_RIGHT_CORNER_SPEC; break; case LINE_DOUBLE_LEFT_CORNER: str = DOUBLE_LEFT_CORNER_SPEC; break; } Tcl_SetStringObj(result, str, strlen(str)); break; } case ZN_CONFIG_LINE_STYLE: { LineStyle line_style = *((LineStyle *) valp); switch (line_style) { case LINE_SIMPLE: str = SIMPLE_SPEC; break; case LINE_DASHED: str = DASHED_SPEC; break; case LINE_MIXED: str = MIXED_SPEC; break; case LINE_DOTTED: str = DOTTED_SPEC; break; } Tcl_SetStringObj(result, str, strlen(str)); break; } case ZN_CONFIG_LINE_END: { ZnLineEnd line_end = *((ZnLineEnd *) valp); if (line_end) { str = LineEndGetString(line_end); Tcl_SetStringObj(result, str, strlen(str)); } break; } case ZN_CONFIG_RELIEF: str = ZnNameOfRelief(*((ReliefStyle *) valp)); Tcl_SetStringObj(result, str, strlen(str)); break; case ZN_CONFIG_JOIN_STYLE: str = Tk_NameOfJoinStyle(*((int *) valp)); Tcl_SetStringObj(result, str, strlen(str)); break; case ZN_CONFIG_CAP_STYLE: str = Tk_NameOfCapStyle(*((int *) valp)); Tcl_SetStringObj(result, str, strlen(str)); break; case ZN_CONFIG_POINT: { Tcl_ListObjAppendElement(wi->interp, result, NewDoubleObj(((ZnPoint *) valp)->x)); Tcl_ListObjAppendElement(wi->interp, result, NewDoubleObj(((ZnPoint *) valp)->y)); break; } case ZN_CONFIG_ITEM: if (*((Item *) valp) != ZN_NO_ITEM) { Tcl_SetLongObj(result, (*((Item *) valp))->id); } break; case ZN_CONFIG_WINDOW: if (*((ZnWindow *) valp) != NULL) { str = Tk_PathName(*((ZnWindow *) valp)); Tcl_SetStringObj(result, str, strlen(str)); } break; case ZN_CONFIG_INT: case ZN_CONFIG_UINT: case ZN_CONFIG_PRI: case ZN_CONFIG_ANGLE: case ZN_CONFIG_ALPHA: Tcl_SetIntObj(result, *((int *) valp)); break; case ZN_CONFIG_DIM: Tcl_SetDoubleObj(result, *((ZnDim *) valp)); break; case ZN_CONFIG_ALIGNMENT: str = Tk_NameOfJustify(*((ZnJustify *) valp)); Tcl_SetStringObj(result, str, strlen(str)); break; case ZN_CONFIG_ANCHOR: str = Tk_NameOfAnchor(*((Tk_Anchor *) valp)); Tcl_SetStringObj(result, str, strlen(str)); break; case ZN_CONFIG_LABEL_FORMAT: { ZnLabelFormat frmt = *((ZnLabelFormat *) valp); if (frmt) { str = LabelFormatGetString(frmt); Tcl_SetStringObj(result, str, strlen(str)); } break; } case ZN_CONFIG_AUTO_ALIGNMENT: { AutoAlign *aa = (AutoAlign *) valp; int i; if (aa->automatic == False) { str = "-"; Tcl_SetStringObj(result, str, strlen(str)); } else { buffer[0] = 0; str = buffer; for (i = 0; i < 3; i++) { switch (aa->align[i]) { case ZnJustifyLeft: strcat(buffer, "l"); break; case ZnJustifyCenter: strcat(buffer, "c"); break; case ZnJustifyRight: strcat(buffer, "r"); break; } } Tcl_SetStringObj(result, buffer, strlen(buffer)); } } break; case ZN_CONFIG_LEADER_ANCHORS: { LeaderAnchors lanch = *((LeaderAnchors *) valp); char *ptr = buffer; int count; if (!lanch) { ptr = "%50x50"; } else { if (lanch->left_y < 0) { count = sprintf(ptr, "|%d", lanch->left_x); } else { count = sprintf(ptr, "%%%dx%d", lanch->left_x, lanch->left_y); } ptr += count; if (lanch->right_y < 0) { count = sprintf(ptr, "|%d", lanch->right_x); } else { count = sprintf(ptr, "%%%dx%d", lanch->right_x, lanch->right_y); } ptr = buffer; } Tcl_SetStringObj(result, ptr, strlen(ptr)); } } } /* ********************************************************************************** * * ZnQueryAttribute -- * ********************************************************************************** */ int ZnQueryAttribute(WidgetInfo *wi, void *record, ZnAttrConfig *desc, Tcl_Obj *attr_name) { Tk_Uid attr_uid = Tk_GetUid(Tcl_GetString(attr_name)); Tcl_Obj *result = NULL; char buffer[256]; while (True) { if (desc->type == ZN_CONFIG_END) { Tcl_AppendResult(wi->interp, "unknown attribute \"", attr_uid, "\"", NULL); return ZN_ERROR; } else if (attr_uid == desc->uid) { result = Tcl_GetObjResult(wi->interp); AttributeToObj(wi, record, desc, buffer, result); break; } else { desc++; } } return ZN_OK; } /* ********************************************************************************** * * ZnItemClassList -- * ********************************************************************************** */ ZnList ZnItemClassList() { return item_classes; } /* ********************************************************************************** * * ZnLookupItemClass -- * ********************************************************************************** */ ItemClass ZnLookupItemClass(char *class_name) { ItemClass *class; int i, num_classes; class = (ItemClass *) ZnListArray(item_classes); num_classes = ZnListSize(item_classes); for (i = 0; i < num_classes; i++) { if (strcasecmp((class[i])->name, class_name) == 0) { return class[i]; } } return NULL; } /* ********************************************************************************** * * ZnAddItemClass -- * ********************************************************************************** */ void ZnAddItemClass(ItemClass class) { if (!ZnLookupItemClass(class->name)) { ZnListAdd(item_classes, &class, ZnListTail); InitAttrDesc(class->attr_desc); } } /* ********************************************************************************** * * ZnItemInit -- * Initialize classes static state. * ********************************************************************************** */ void ZnItemInit() { /* First check if static part already inited */ if (item_classes == NULL) { item_classes = ZnListNew(16, sizeof(ItemClass)); ZnAddItemClass(ZnTrack); ZnAddItemClass(ZnWayPoint); ZnAddItemClass(ZnMap); ZnAddItemClass(ZnReticle); ZnAddItemClass(ZnTabular); ZnAddItemClass(ZnRectangle); ZnAddItemClass(ZnArc); ZnAddItemClass(ZnCurve); ZnAddItemClass(ZnTriangles); ZnAddItemClass(ZnGroup); ZnAddItemClass(ZnIcon); ZnAddItemClass(ZnText); ZnAddItemClass(ZnWind); InitAttrDesc(FIELD.attr_desc); } } /* ********************************************************************************** * * UpdateItemDependency -- Method * Update the group dependency list following a change in the * connection of an item. * ********************************************************************************** */ static void UpdateItemDependency(Item item, Item old_connection) { if (old_connection == ZN_NO_ITEM) { /* Add a connection */ ZnInsertDependentItem(item); } else if (item->connected_item == ZN_NO_ITEM) { /* Remove a connection */ ZnExtractDependentItem(item); } else { /* Move at end to ensure that it will be updated after * the (new) item it depends upon. */ ZnExtractDependentItem(item); ZnInsertDependentItem(item); } } /* ********************************************************************************** * * ExtractItem -- * Extract an item from its context, includes updating graphic * state flags. * ********************************************************************************** */ static void ExtractItem(Item item) { WidgetInfo *wi = item->wi; Item group = item->parent; /* damage bounding boxes */ if (ISSET(item->flags, VISIBLE_BIT)) { ZnDamage(wi, &item->item_bounding_box); } /* * Tell that we need to repick */ if (item->class != ZnGroup) { SET(wi->events_flags, INTERNAL_NEED_REPICK); } if (group != ZN_NO_ITEM) { /* Remove me from dependency list. */ ZnExtractDependentItem(item); /* Disconnect all dependents on me. */ ZnDisconnectDependentItems(item); /* * Remove me as a clip item. */ ZnGroupRemoveClip(group, item); /* * Remove me from item list. */ ZnGroupExtractItem(item); } } /* ********************************************************************************** * * InsertItem -- Method * * Insert an item in the display list according to its priority. * It is inserted in front of items of lower or same priority. If * mark_item is not ZN_NO_ITEM the insertion is done relative * to this item, before it if 'before' is True, after it otherwise. * mark_item must be in the group 'group'. * ********************************************************************************** */ static void InsertItem(Item item, Item grp, Item mark_item, ZnBool before) { if (!grp) { grp = item->wi->top_group; } item->parent = grp; if (mark_item && (mark_item->parent != grp)) { mark_item = ZN_NO_ITEM; } ZnGroupInsertItem(grp, item, mark_item, before); } /* ********************************************************************************** * * UpdateItemPriority -- Method * Reorder a group's item list following a change in an * item priority or a call to lower/raise. * ********************************************************************************** */ static void UpdateItemPriority(Item item, Item mark_item, ZnBool before) { Item parent = item->parent; ZnGroupExtractItem(item); InsertItem(item, parent, mark_item, before); Invalidate(item, ZN_DRAW_FLAG); SET(item->wi->events_flags, INTERNAL_NEED_REPICK); } /* ********************************************************************************** * * SetId, * FreeId -- Method * Get a fresh object id from the widget and enter the new * object with this id in the object hash table. The id is * incremented. FreeId on the other hand suppress the item * from the hash table and set its object id to zero. * ********************************************************************************** */ static void SetId(Item item) { WidgetInfo *wi = item->wi; Tcl_HashEntry *entry; int dummy; item->id = wi->obj_id; wi->obj_id++; entry = Tcl_CreateHashEntry(wi->id_table, (char *) item->id, &dummy); Tcl_SetHashValue(entry, item); } static void FreeId(Item item) { Tcl_HashEntry *entry; if (item->id) { entry = Tcl_FindHashEntry(item->wi->id_table, (char *) item->id); if (entry) { Tcl_DeleteHashEntry(entry); item->id = 0; } } } /* ********************************************************************************** * * AddTag -- Method * Add a tag to the item. If the tag is already on the list it * is not added. As a side effect the tag/item pair is added to * the tag table of the widget. * 'tag' must be a Tk_Uid. * ********************************************************************************** */ static void AddTag(Item item, Tk_Uid tag) { int num, i; char **ptr; /* * No tags yet. */ if (!item->tags) { item->tags = ZnListNew(1, sizeof(char *)); } else { /* * If the tag is already there, that's done. */ ptr = (char **) ZnListArray(item->tags); num = ZnListSize(item->tags); for (i = 0; i < num; i++) { if (ptr[i] == tag) { return; } } } /* * Add it. */ ZnListAdd(item->tags, &tag, ZnListTail); } /* ********************************************************************************** * * RemoveTag -- Method * ********************************************************************************** */ static void RemoveTag(Item item, Tk_Uid tag) { int indx, num; char **ptr; if (!item->tags) { return; } /* * look up the tag in the list. */ ptr = (char **) ZnListArray(item->tags); num = ZnListSize(item->tags); for (indx = 0; indx < num; indx++) { if (ptr[indx] == tag) { /* The tag list is not freed when empty to avoid * overhead when using tags intensively. */ ZnListDelete(item->tags, indx); return; } } } /* ********************************************************************************** * * FreeTags -- Method * ********************************************************************************** */ static void FreeTags(Item item) { if (!item->tags) { return; } ZnListFree(item->tags); item->tags = NULL; } /* ********************************************************************************** * * HasTag -- Method * ********************************************************************************** */ static ZnBool HasTag(Item item, Tk_Uid tag) { int num; Tk_Uid *tags; if (!item->tags || !ZnListSize(item->tags)) { return False; } else { num = ZnListSize(item->tags); tags = ZnListArray(item->tags); for (tags = ZnListArray(item->tags); num > 0; tags++, num--) { if (*tags == tag) { return True; } } } return False; } /* ********************************************************************************** * * ZnCreateItem -- * * InsertItem and ConfigureItem must be called after CreateItem * to finalize the setup of a new item. This is so even if * there are no attributes to be changed after creation. * ConfigureItem must be called in this case with the 'init' * parameter set to True. * ********************************************************************************** */ Item ZnCreateItem(WidgetInfo *wi, ItemClass item_class, int *argc, Tcl_Obj *CONST *args[]) { Item item; item = (Item) ZnMalloc(item_class->size); /* Initialize common state */ item->class = item_class; item->wi = wi; item->parent = NULL; item->previous = ZN_NO_ITEM; item->next = ZN_NO_ITEM; CLEAR(item->flags, UPDATE_DEPENDENT_BIT); item->inv_flags = 0; item->transfo = NULL; item->parent = NULL; item->connected_item = ZN_NO_ITEM; #ifdef GLX item->gl_list = 0; #endif ResetBBox(&item->item_bounding_box); /* Init item specific attributes */ if (item_class->Init(item, argc, args) == ZN_ERROR) { ZnFree(item); return ZN_NO_ITEM; } SetId(item); item->tags = NULL; SET(wi->events_flags, INTERNAL_NEED_REPICK); wi->num_items++; return (item); } /* ********************************************************************************** * * CloneItem -- Method * Can't clone the top level group. * ********************************************************************************** */ static Item CloneItem(Item model) { WidgetInfo *wi = model->wi; Item item; Tk_Uid *tags; int i, num_tags; if (!model->parent) { return ZN_NO_ITEM; } item = (Item) ZnMalloc(model->class->size); memcpy(item, model, model->class->size); item->previous = ZN_NO_ITEM; item->next = ZN_NO_ITEM; item->connected_item = ZN_NO_ITEM; CLEAR(item->flags, UPDATE_DEPENDENT_BIT); item->inv_flags = 0; SetId(item); if (model->tags) { item->tags = NULL; tags = (Tk_Uid *) ZnListArray(model->tags); num_tags = ZnListSize(model->tags); for (i = num_tags-1; i >= 0; i--, tags++) { AddTag(item, *tags); } } if (item->transfo) { item->transfo = ZnTransfoDuplicate(item->transfo); } /* Call item's clone to duplicate non shared resources */ item->class->Clone(item); SET(wi->events_flags, INTERNAL_NEED_REPICK); wi->num_items++; return item; } /* ********************************************************************************** * * ConfigureItem -- Method * ********************************************************************************** */ static int ConfigureItem(Item item, int field, int argc, Tcl_Obj *CONST argv[], ZnBool init) { WidgetInfo *wi = item->wi; int flags; ZnBool previous_visible = init ? False : ISSET(item->flags, VISIBLE_BIT); flags = 0; ASSIGN(flags, ZN_COORDS_FLAG, init); if (argv) { if (field < 0){ if (item->class->Configure(item, argc, argv, &flags) == ZN_ERROR) { return ZN_ERROR; } if (item->class->GetFieldSet && ISSET(flags, ZN_CLFC_FLAG)) { FIELD.ClearFieldCache(item->class->GetFieldSet(item), -1); } } else if (item->class->GetFieldSet) { if (FIELD.ConfigureField(item->class->GetFieldSet(item), field, argc, argv, &flags) == ZN_ERROR) { return ZN_ERROR; } } else { return ZN_ERROR; } } if (previous_visible && ISCLEAR(item->flags, VISIBLE_BIT)) { /* * Special case when the item has its visibility * just turned out. */ ZnDamage(wi, &item->item_bounding_box); } Invalidate(item, flags); return ZN_OK; } /* ********************************************************************************** * * QueryItem -- Method * ********************************************************************************** */ static int QueryItem(Item item, int field, int argc, Tcl_Obj *CONST argv[]) { if (field < 0) { return item->class->Query(item, argc, argv); } else if (item->class->GetFieldSet) { return FIELD.QueryField(item->class->GetFieldSet(item), field, argc, argv); } return ZN_ERROR; } /* ********************************************************************************** * * ComposeTransform -- * Compose a (item) transform with current_t in new_t. * ********************************************************************************** */ static void ComposeTransform(ZnTransfo *transfo, ZnTransfo *current_t, ZnTransfo *new_t, ZnBool compose_scale, ZnBool compose_rot) { ZnBool full; full = compose_scale && compose_rot; if (!transfo && full) { *new_t = *current_t; return; } if (full) { /* * Full concatenation. */ /*ZnPrintTransfo(transfo);*/ ZnTransfoCompose(new_t, transfo, current_t); } else { ZnPoint scale, trans, local_scale, local_trans; ZnReal local_rot, rot; ZnTransfo t, t2; /* * Need to decompose the local transform in translation, * rotation and scale. */ ZnTransfoSetIdentity(&t); ZnTransfoSetIdentity(new_t); ZnTransfoDecompose(transfo, &local_scale, &local_trans, &local_rot, NULL); ZnTranslate(&t, local_trans.x, local_trans.y); ZnTransfoCompose(&t2, &t, current_t); ZnTransfoDecompose(&t2, &scale, &trans, &rot, NULL); if (compose_scale) { ZnScale(new_t, scale.x, scale.y); } if (compose_rot) { ZnRotateRad(new_t, rot); } ZnScale(new_t, local_scale.x, local_scale.y); ZnRotateRad(new_t, local_rot); ZnTranslate(new_t, trans.x, trans.y); } } /* ********************************************************************************** * * GetItemTransform -- Method * Compute the current transform for an item. * ********************************************************************************** */ static void GetItemTransform(Item item, ZnTransfo *t) { Item *items; int i; ZnTransfo t_tmp, *t1, *t2, *swap; if (item_stack == NULL) { item_stack = ZnListNew(16, sizeof(Item)); } else { ZnListEmpty(item_stack); } while (item != ZN_NO_ITEM) { ZnListAdd(item_stack, &item, ZnListTail); item = item->parent; } ZnTransfoSetIdentity(t); t1 = t; t2 = &t_tmp; items = (Item *) ZnListArray(item_stack); for (i = ZnListSize(item_stack)-1; i >= 0; i--) { ComposeTransform(items[i]->transfo, t1, t2, ISSET(items[i]->flags, COMPOSE_SCALE_BIT), ISSET(items[i]->flags, COMPOSE_ROTATION_BIT)); swap = t2; t2 = t1; t1 = swap; } if (t1 != t) { *t = *t1; } } /* ********************************************************************************** * * ZnResetTransformStack * ZnInitTransformStack * ZnFreeTransformStack * ZnCurrentTransform * ZnPushTransform * ZnPopTransform -- * ********************************************************************************** */ void ZnResetTransformStack(WidgetInfo *wi) { ZnListAssertSize(wi->transfo_stack, 1); wi->current_transfo = (ZnTransfo *) ZnListAt(wi->transfo_stack, 0); ZnTransfoSetIdentity(wi->current_transfo); } void ZnInitTransformStack(WidgetInfo *wi) { wi->transfo_stack = ZnListNew(8, sizeof(ZnTransfo)); ZnResetTransformStack(wi); } void ZnFreeTransformStack(WidgetInfo *wi) { ZnListFree(wi->transfo_stack); } void ZnPushTransform(WidgetInfo *wi, ZnTransfo *transfo, ZnBool compose_scale, ZnBool compose_rot) { ZnTransfo *next_t; int num_t; /* * Push the current transform and concatenate * the new transform taking into account the * combination flags. */ num_t = ZnListSize(wi->transfo_stack); ZnListAssertSize(wi->transfo_stack, num_t+1); next_t = (ZnTransfo *) ZnListAt(wi->transfo_stack, num_t); ComposeTransform(transfo, wi->current_transfo, next_t, compose_scale, compose_rot); wi->current_transfo = next_t; } void ZnPopTransform(WidgetInfo *wi) { /* * Restore the previous transform. */ ZnListDelete(wi->transfo_stack, ZnListTail); wi->current_transfo = (ZnTransfo *) ZnListAt(wi->transfo_stack, ZnListTail); } /* ********************************************************************************** * * ZnResetClipStack * ZnInitClipStack * ZnFreeClipStack * ZnCurrentClip * ZnPushClip * ZnPopClip -- * ********************************************************************************** */ /* * Describe the clipping at a given node * of the item hierarchy. */ typedef struct _ClipState { ZnBool simple; /* The clip is an aligned rectangle. */ Region region; /* The X region used to draw and to */ /* probe for picking. */ ZnBBox clip_box; /* The bounding box of the clip area. */ } ClipState; void ZnResetClipStack(WidgetInfo *wi) { int i; ClipState *clips = (ClipState *) ZnListArray(wi->clip_stack); /* * Should not happen, clip stack should be * empty when this function is called. */ for (i = ZnListSize(wi->clip_stack)-1; i >= 0; i--) { XDestroyRegion(clips[i].region); } ZnListEmpty(wi->clip_stack); wi->current_clip = NULL; } void ZnInitClipStack(WidgetInfo *wi) { wi->clip_stack = ZnListNew(8, sizeof(ClipState)); ZnResetClipStack(wi); } void ZnFreeClipStack(WidgetInfo *wi) { ZnListFree(wi->clip_stack); } ZnBool ZnCurrentClip(WidgetInfo *wi, Region *reg, ZnBBox **clip_box, ZnBool *simple) { if (wi->current_clip) { if (reg) { *reg = wi->current_clip->region; } if (clip_box) { *clip_box = &wi->current_clip->clip_box; } if (simple) { *simple = wi->current_clip->simple; } return True; } return False; } /* * If simple is True poly is a pointer to an * array of two points. In the other case it * is a regular pointer to a multi contour poly. */ void ZnPushClip(WidgetInfo *wi, ZnTriStrip *tristrip, ZnBool simple, ZnBool set_gc) { int i, j, num_clips; int num_pts, max_num_pts; ZnPoint *p; ClipState *previous_clip=NULL; Region reg, reg_op, reg_to; XRectangle rect; XPoint xpts[3]; if (tristrip->num_strips == 0) { return; } max_num_pts = tristrip->strips[0].num_points; for (j = 0; j < tristrip->num_strips; j++) { num_pts = tristrip->strips[j].num_points; if (num_pts > max_num_pts) { num_pts = max_num_pts; } } if ((simple && (max_num_pts < 2)) || (!simple && (max_num_pts < 3))) { return; } num_clips = ZnListSize(wi->clip_stack); /* printf("PushClip: num clips %d\n", num_clips);fflush(stdout);*/ if (num_clips != 0) { previous_clip = (ClipState *) ZnListAt(wi->clip_stack, ZnListTail); } ZnListAssertSize(wi->clip_stack, num_clips+1); wi->current_clip = (ClipState *) ZnListAt(wi->clip_stack, ZnListTail); wi->current_clip->simple = simple; /* * Compute the local region. */ if (simple) { rect.x = tristrip->strips[0].points[0].x; rect.y = tristrip->strips[0].points[0].y; rect.width = tristrip->strips[0].points[1].x - tristrip->strips[0].points[0].x; rect.height = tristrip->strips[0].points[1].y - tristrip->strips[0].points[0].y; reg = XCreateRegion(); XUnionRectWithRegion(&rect, reg, reg); /*printf("Adding a simple clip: %d, %d, %d, %d\n", rect.x, rect.y, rect.width, rect.height);*/ } else { reg = XCreateRegion(); for (j = 0; j < tristrip->num_strips; j++) { num_pts = tristrip->strips[j].num_points; p = tristrip->strips[j].points; if (tristrip->strips[j].fan) { xpts[0].x = REAL_TO_INT(p->x); xpts[0].y = REAL_TO_INT(p->y); p++; xpts[1].x = REAL_TO_INT(p->x); xpts[1].y = REAL_TO_INT(p->y); p++; for (i = 2; i < num_pts; i++, p++) { xpts[2].x = REAL_TO_INT(p->x); xpts[2].y = REAL_TO_INT(p->y); reg_op = XPolygonRegion(xpts, 3, EvenOddRule); reg_to = XCreateRegion(); XUnionRegion(reg, reg_op, reg_to); XDestroyRegion(reg); XDestroyRegion(reg_op); reg = reg_to; xpts[1] = xpts[2]; } } else { xpts[0].x = p->x; xpts[0].y = p->y; p++; xpts[1].x = p->x; xpts[1].y = p->y; p++; for (i = 2 ; i < num_pts; i++, p++) { xpts[2].x = p->x; xpts[2].y = p->y; reg_op = XPolygonRegion(xpts, 3, EvenOddRule); reg_to = XCreateRegion(); XUnionRegion(reg, reg_op, reg_to); XDestroyRegion(reg); XDestroyRegion(reg_op); reg = reg_to; xpts[0] = xpts[1]; xpts[1] = xpts[2]; } } } } /* * Combine with previous region if any. */ if (previous_clip) { wi->current_clip->region = XCreateRegion(); XIntersectRegion(reg, previous_clip->region, wi->current_clip->region); XDestroyRegion(reg); /*printf("Merging with previous clip\n");*/ } else { wi->current_clip->region = reg; } XClipBox(wi->current_clip->region, &rect); wi->current_clip->clip_box.orig.x = rect.x; wi->current_clip->clip_box.orig.y = rect.y; wi->current_clip->clip_box.corner.x = rect.x + rect.width; wi->current_clip->clip_box.corner.y = rect.y + rect.height; /* * Set the clipping in the GC. */ if (set_gc) { if (wi->render) { #ifdef GLX glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glEnable(GL_STENCIL_TEST); glStencilFunc(GL_EQUAL, num_clips, 0xFF); glStencilOp(GL_KEEP, GL_INCR, GL_INCR); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); if (simple) { /* printf("Clip box is : %d, %d, %d, %d, num_clips : %d\n", rect.x, rect.y, rect.width, rect.height, num_clips);*/ glBegin(GL_QUADS); glVertex2f(wi->current_clip->clip_box.orig.x, wi->current_clip->clip_box.orig.y); glVertex2f(wi->current_clip->clip_box.orig.x, wi->current_clip->clip_box.corner.y); glVertex2f(wi->current_clip->clip_box.corner.x, wi->current_clip->clip_box.corner.y); glVertex2f(wi->current_clip->clip_box.corner.x, wi->current_clip->clip_box.orig.y); glEnd(); } else { for (j = 0; j < tristrip->num_strips; j++) { num_pts = tristrip->strips[j].num_points; p = tristrip->strips[j].points; if (tristrip->strips[j].fan) { glBegin(GL_TRIANGLE_FAN); } else { glBegin(GL_TRIANGLE_STRIP); } for (i = 0; i < num_pts; i++, p++) { glVertex2f(p->x, p->y); } glEnd(); } } glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glStencilFunc(GL_EQUAL, num_clips+1, 0xFF); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); #endif } else { XSetRegion(wi->dpy, wi->gc, wi->current_clip->region); } } } void ZnPopClip(WidgetInfo *wi, ZnBool set_gc) { int num_clips; if (wi->current_clip == NULL) { return; } XDestroyRegion(wi->current_clip->region); ZnListDelete(wi->clip_stack, ZnListTail); num_clips = ZnListSize(wi->clip_stack); if (num_clips != 0) { wi->current_clip = (ClipState *) ZnListAt(wi->clip_stack, ZnListTail); } else { wi->current_clip = NULL; } /* * Set the clipping in the GC. */ if (set_gc) { if (num_clips != 0) { if (wi->render) { #ifdef GLX glStencilFunc(GL_EQUAL, num_clips+1, 0xFF); glStencilOp(GL_KEEP, GL_DECR, GL_DECR); glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); #if 0 if (wi->current_clip->simple) { #endif glBegin(GL_QUADS); glVertex2f(wi->current_clip->clip_box.orig.x, wi->current_clip->clip_box.orig.y); glVertex2f(wi->current_clip->clip_box.orig.x, wi->current_clip->clip_box.corner.y); glVertex2f(wi->current_clip->clip_box.corner.x, wi->current_clip->clip_box.corner.y); glVertex2f(wi->current_clip->clip_box.corner.x, wi->current_clip->clip_box.orig.y); glEnd(); #if 0 } else { } #endif glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glStencilFunc(GL_EQUAL, num_clips, 0xFF); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); #endif } else { XSetRegion(wi->dpy, wi->gc, wi->current_clip->region); } } else { /*printf("resetting clip mask\n");*/ if (wi->render) { #ifdef GLX glClear(GL_STENCIL_BUFFER_BIT); glDisable(GL_STENCIL_TEST); #endif } else { XSetClipMask(wi->dpy, wi->gc, None); } } } /*printf("PopClip: num clips %d\n", ZnListSize(wi->clip_stack));fflush(stdout);*/ } /* ********************************************************************************** * * Invalidate -- Method * ********************************************************************************** */ static void Invalidate(Item item, int reason) { if (ISSET(item->inv_flags, ZN_TRANSFO_FLAG)) { return; } if (ISSET(reason, ZN_COORDS_FLAG) || ISSET(reason, ZN_TRANSFO_FLAG)) { Item parent = item->parent; while ((parent != NULL) && ISCLEAR(parent->inv_flags, ZN_COORDS_FLAG) && ISCLEAR(parent->inv_flags, ZN_TRANSFO_FLAG)) { SET(parent->inv_flags, ZN_COORDS_FLAG); /*printf("invalidate coords for parent %d\n", parent->id);*/ parent = parent->parent; } /* * There is no need to set the DRAW flag to force the invalidation * of the current bounding box. This will be done by ComputeCoordinates * in Group. */ item->inv_flags |= reason; /*printf("invalidate %s for item %d, flags %s\n", ISSET(reason, ZN_TRANSFO_FLAG)?"TRANSFO":"COORDS", item->id, ISSET(item->inv_flags, ZN_TRANSFO_FLAG)?"TRANSFO":"COORDS");*/ ZnNeedRedisplay(item->wi); } else if (ISSET(reason, ZN_DRAW_FLAG)) { if (ISSET(item->flags, VISIBLE_BIT)) { /*printf("invalidate graphics for item %d\n", item->id);*/ ZnDamage(item->wi, &item->item_bounding_box); #ifdef GLX /* * Remove the item display list so that it will be recreated * to reflect the changes. */ if (item->gl_list) { glDeleteLists(item->gl_list, 1); item->gl_list = 0; } #endif } } } /* ********************************************************************************** * * InvalidateItems -- Method * Invalidate the geometric state of all items belonging * to a given class. The search for items starts at group * and proceed depth first. * ********************************************************************************** */ static void InvalidateItems(Item group, ItemClass item_class) { Item item; if (group->class != ZnGroup) { return; } item = ZnGroupHead(group); while (item != ZN_NO_ITEM) { if (item->class == item_class) { Invalidate(item, ZN_COORDS_FLAG); } else if (item->class == ZnGroup) { InvalidateItems(item, item_class); } item = item->next; } } /* ********************************************************************************** * * ResetTransfo * SetTransfo * TranslateItem * ScaleItem * RotateItem -- Methods * Set of functions that deal with item transform. They take care * of all details including managing NULL transforms and invalidating * the item hierarchy. * ********************************************************************************** */ static void ResetTransfo(Item item) { if (item->transfo) { ZnFree(item->transfo); item->transfo = NULL; } Invalidate(item, ZN_TRANSFO_FLAG); } static void SetTransfo(Item item, ZnTransfo *t) { if (item->transfo) { ZnFree(item->transfo); } if (!t || ZnTransfoIsIdentity(t)) { item->transfo = NULL; } else { item->transfo = ZnTransfoDuplicate(t); } Invalidate(item, ZN_TRANSFO_FLAG); } static void TranslateItem(Item item, ZnReal dx, ZnReal dy) { if (!item->transfo) { item->transfo = ZnTransfoNew(); } ZnTranslate(item->transfo, dx, dy); Invalidate(item, ZN_TRANSFO_FLAG); } static void ScaleItem(Item item, ZnReal sx, ZnReal sy) { if (!item->transfo) { item->transfo = ZnTransfoNew(); } ZnScale(item->transfo, sx, sy); Invalidate(item, ZN_TRANSFO_FLAG); } static void RotateItem(Item item, ZnReal angle, ZnPoint *p) { if (!item->transfo) { item->transfo = ZnTransfoNew(); } if (p) { ZnTranslate(item->transfo, -p->x, -p->y); } ZnRotateRad(item->transfo, angle); if (p) { ZnTranslate(item->transfo, p->x, p->y); } Invalidate(item, ZN_TRANSFO_FLAG); } /* ********************************************************************************** * * DestroyItem -- Method * ********************************************************************************** */ static void DestroyItem(Item item) { WidgetInfo *wi = item->wi; TextInfo *ti = &wi->text_info; /* * Extract it from its group. */ ExtractItem(item); /* * Update state variables to prevent dangling pointers. */ if (wi->current_item == item) { wi->current_item = ZN_NO_ITEM; wi->current_part = ZN_NO_PART; } if (wi->new_item == item) { wi->new_item = ZN_NO_ITEM; wi->new_part = ZN_NO_PART; } if ((wi->hot_item == item) || (wi->hot_prev) == item) { wi->hot_item = ZN_NO_ITEM; } if (ti->sel_item == item) { ti->sel_item = ZN_NO_ITEM; ti->sel_field = ZN_NO_PART; } if (ti->anchor_item == item) { ti->anchor_item = ZN_NO_ITEM; ti->anchor_field = ZN_NO_PART; } if (wi->focus_item == item) { wi->focus_item = ZN_NO_ITEM; wi->focus_field = ZN_NO_PART; } /* * Call per class removal code. */ (item->class->Destroy)(item); /* * Free the transform if any. */ if (item->transfo) { ZnFree(item->transfo); } /* * Remove the item from the item table and free * all its tags. */ FreeId(item); FreeTags(item); /* * Free the item own memory */ ZnFree(item); wi->num_items--; } /* ********************************************************************************** * * Generic methods on items -- * ********************************************************************************** */ struct _ITEM ITEM = { CloneItem, DestroyItem, ConfigureItem, QueryItem, InsertItem, UpdateItemPriority, UpdateItemDependency, ExtractItem, SetId, FreeId, AddTag, RemoveTag, FreeTags, HasTag, ResetTransfo, SetTransfo, TranslateItem, ScaleItem, RotateItem, Invalidate, InvalidateItems, GetItemTransform };