/* * Item.c -- Implementation of items. * * Authors : Patrick Lecoanet. * Creation date : * * $Id$ */ /* * Copyright (c) 1993 - 2005 CENA, Patrick Lecoanet -- * * See the file "Copyright" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * */ #include "Types.h" #include "Field.h" #include "Item.h" #include "Group.h" #include "WidgetInfo.h" #include "Geo.h" #include "Draw.h" #include "MapInfo.h" #include "Image.h" #include "Color.h" #include "tkZinc.h" #ifdef ATC #include "OverlapMan.h" #endif #include #include /* For INT_MAX */ #include #include #include 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 Item.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", "fillrule", "short", "unsignedshort" "char" "unsignedchar" }; #ifndef PTK static int SetAttrFromAny _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *obj)); /* * The structure below defines an object type that is used to cache the * result of looking up an attribute name. If an object has this type, then * its internalPtr1 field points to the attr_desc table in which it was looked up, * and the internalPtr2 field points to the entry that matched. */ Tcl_ObjType ZnAttrObjType = { "attribute", /* name */ (Tcl_FreeInternalRepProc *) NULL, /* freeIntRepProc */ (Tcl_DupInternalRepProc *) NULL, /* dupIntRepProc */ (Tcl_UpdateStringProc *) NULL, /* updateStringProc */ SetAttrFromAny /* setFromAnyProc */ }; #endif /* ********************************************************************************** * * Forward functions * ********************************************************************************** */ static void Invalidate(ZnItem item, int reason); static void AttributeToObj(Tcl_Interp *interp, void *record, ZnAttrConfig *desc, Tcl_Obj *result); /* ********************************************************************************** * * ZnUpdateItemImage -- * ********************************************************************************** */ void ZnUpdateItemImage(void *client_data) { ZnItem item = (ZnItem) client_data; /*printf("Invalidation of item %ld\n", item->id);*/ Invalidate(item, ZN_CLFC_FLAG | ZN_COORDS_FLAG); } /* ********************************************************************************** * * 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++; } } /* *---------------------------------------------------------------------- * * SetAttrFromAny -- * * This procedure is called to convert a Tcl object to an attribute * descriptor. This is only possible if given a attr_desc table, so * this method always returns an error. * *---------------------------------------------------------------------- */ #ifndef PTK static int SetAttrFromAny(Tcl_Interp *interp, Tcl_Obj *obj) { Tcl_AppendToObj(Tcl_GetObjResult(interp), "can't convert value to attribute except via GetAttrDesc", -1); return TCL_ERROR; } #endif /* ********************************************************************************** * * GetAttrDesc -- * ********************************************************************************** */ static ZnAttrConfig * GetAttrDesc(Tcl_Interp *interp, Tcl_Obj *arg, ZnAttrConfig *desc_table) { Tk_Uid attr_uid; ZnAttrConfig *desc; #ifndef PTK if (arg->typePtr == &ZnAttrObjType) { if (arg->internalRep.twoPtrValue.ptr1 == (void *) desc_table) { return (ZnAttrConfig *) arg->internalRep.twoPtrValue.ptr2; } } #endif /* * Answer not cached, look it up. */ attr_uid = Tk_GetUid(Tcl_GetString(arg)); desc = desc_table; while (True) { if (desc->type == ZN_CONFIG_END) { Tcl_AppendResult(interp, "unknown attribute \"", attr_uid, "\"", NULL); return NULL; } else if (attr_uid == desc->uid) { #ifndef PTK if ((arg->typePtr != NULL) && (arg->typePtr->freeIntRepProc != NULL)) { arg->typePtr->freeIntRepProc(arg); } arg->internalRep.twoPtrValue.ptr1 = (void *) desc_table; arg->internalRep.twoPtrValue.ptr2 = (void *) desc; arg->typePtr = &ZnAttrObjType; #endif return desc; } else { desc++; } } } /* ********************************************************************************** * * AttributesInfo -- * ********************************************************************************** */ int ZnAttributesInfo(Tcl_Interp *interp, void *record, ZnAttrConfig *desc_table, int argc, Tcl_Obj *CONST args[]) { Tcl_Obj *l, *entries[5]; if (argc == 1) { ZnAttrConfig *desc = GetAttrDesc(interp, args[0], desc_table); if (!desc) { return TCL_ERROR; } entries[0] = Tcl_NewStringObj(desc->name, -1); entries[1] = Tcl_NewStringObj(attribute_type_strings[desc->type], -1); entries[2] = Tcl_NewBooleanObj(desc->read_only ? 1 : 0); entries[3] = Tcl_NewStringObj("", -1); entries[4] = Tcl_NewStringObj("", -1); AttributeToObj(interp, record, desc, entries[4]); Tcl_SetObjResult(interp, Tcl_NewListObj(5, entries)); } else { l = Tcl_GetObjResult(interp); while (desc_table->type != ZN_CONFIG_END) { entries[0] = Tcl_NewStringObj(desc_table->name, -1); entries[1] = Tcl_NewStringObj(attribute_type_strings[desc_table->type], -1); entries[2] = Tcl_NewBooleanObj(desc_table->read_only ? 1 : 0); entries[3] = Tcl_NewStringObj("", -1); entries[4] = Tcl_NewStringObj("", -1); AttributeToObj(interp, record, desc_table, entries[4]); Tcl_ListObjAppendElement(interp, l, Tcl_NewListObj(5, entries)); desc_table++; } } return TCL_OK; } /* ********************************************************************************** * * ZnConfigureAttributes -- * ********************************************************************************** */ int ZnConfigureAttributes(ZnWInfo *wi, ZnItem item, void *record, ZnAttrConfig *desc_table, int argc, Tcl_Obj *CONST args[], int *flags) { int i; ZnAttrConfig *desc; ZnPtr valp; char *str; for (i = 0; i < argc; i += 2) { desc = GetAttrDesc(wi->interp, args[i], desc_table); if (!desc) { return TCL_ERROR; } else if (desc->read_only) { Tcl_AppendResult(wi->interp, "attribute \"", Tcl_GetString(args[i]), "\" can only be read", NULL); return TCL_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 TCL_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; unsigned int num_grads, j, k; Tcl_Obj **elems; if (Tcl_ListObjGetElements(wi->interp, args[i+1], &num_grads, &elems) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " gradient list expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_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 TCL_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) != TCL_OK) { Tcl_AppendResult(wi->interp, " boolean expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_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; ZnBool is_bmap = True; char *name = ""; if (*((ZnImage *) valp) != ZnUnspecifiedImage) { name = ZnNameOfImage(*((ZnImage *) valp)); } str = Tcl_GetString(args[i+1]); if (strcmp(name, str) != 0) { if (strlen(str) != 0) { if (desc->type == ZN_CONFIG_IMAGE) { image = ZnGetImage(wi, str, ZnUpdateItemImage, record); if (image == ZnUnspecifiedImage) { Tcl_AppendResult(wi->interp, " image expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } } else { image = ZnGetImage(wi, str, NULL, NULL); if ((image == ZnUnspecifiedImage) || (!(is_bmap = ZnImageIsBitmap(image)))) { if (!is_bmap) { ZnFreeImage(image, NULL, NULL); } Tcl_AppendResult(wi->interp, " bitmap expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } } } if (*((ZnImage *) valp) != ZnUnspecifiedImage) { ZnFreeImage(*((ZnImage *) valp), ZnUpdateItemImage, record); } *((ZnImage *) valp) = image; *flags |= desc->flags; } break; } case ZN_CONFIG_BITMAP_LIST: { ZnList new_pat_list = NULL; ZnImage *pats; unsigned int num_pats, j, k; Tcl_Obj **elems; ZnBool is_bmap = True; if (Tcl_ListObjGetElements(wi->interp, args[i+1], &num_pats, &elems) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " pattern list expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_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] = ZnGetImage(wi, str, NULL, NULL); if ((pats[j] == ZnUnspecifiedImage) || !(is_bmap = ZnImageIsBitmap(pats[j]))) { if (!is_bmap) { ZnFreeImage(pats[j], NULL, NULL); } for (k = 0; k < j; k++) { ZnFreeImage(pats[k], NULL, NULL); } ZnListFree(new_pat_list); Tcl_AppendResult(wi->interp, " unknown pattern \"", str, "\" in pattern list", NULL); return TCL_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], NULL, NULL); } } 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) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " tag list expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } if (*((ZnList *) valp)) { ZnITEM.FreeTags(item); *flags |= desc->flags; } if (num_tags) { for (j = 0; j < num_tags; j++) { ZnITEM.AddTag(item, 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 TCL_ERROR; } if (*((Tk_Font *) valp)) { Tk_FreeFont(*((Tk_Font *) valp)); } *((Tk_Font *) valp) = font; *flags |= desc->flags; } break; } case ZN_CONFIG_EDGE_LIST: { ZnBorder border; if (ZnGetBorder(wi, args[i+1], &border) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " edge list expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } if (border != *((ZnBorder *) valp)) { *((ZnBorder *) valp) = border; *flags |= desc->flags; } break; } case ZN_CONFIG_LINE_SHAPE: { ZnLineShape line_shape; if (ZnGetLineShape(wi, Tcl_GetString(args[i+1]), &line_shape) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " line shape expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } if (line_shape != *((ZnLineShape *) valp)) { *((ZnLineShape *) valp) = line_shape; *flags |= desc->flags; } break; } case ZN_CONFIG_LINE_STYLE: { ZnLineStyle line_style; if (ZnGetLineStyle(wi, Tcl_GetString(args[i+1]), &line_style) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " line style expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } if (line_style != *((ZnLineStyle *) valp)) { *((ZnLineStyle *) 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 = ZnLineEndCreate(wi->interp, str); if (line_end == NULL) { return TCL_ERROR; } } if (*((ZnLineEnd *) valp) != NULL) { ZnLineEndDelete(*((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: { ZnReliefStyle relief; if (ZnGetRelief(wi, Tcl_GetString(args[i+1]), &relief) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " relief expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } if (relief != *((ZnReliefStyle *) valp)) { /*printf("valp <0x%X>, flags <0x%X>, relief %d\n", valp, flags, relief);*/ *((ZnReliefStyle *) valp) = relief; *flags |= desc->flags; } break; } case ZN_CONFIG_JOIN_STYLE: { int join; if (Tk_GetJoinStyle(wi->interp, Tcl_GetString(args[i+1]), &join) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " join expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_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) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " cap expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } if (cap != *((int *) valp)) { *((int *) valp) = cap; *flags |= desc->flags; } break; } case ZN_CONFIG_POINT: { ZnPoint point; int largc; Tcl_Obj **largv; double d; if ((Tcl_ListObjGetElements(wi->interp, args[i+1], &largc, &largv) == TCL_ERROR) || (largc != 2)) { point_error: Tcl_AppendResult(wi->interp, " position expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } if (Tcl_GetDoubleFromObj(wi->interp, largv[0], &d) == TCL_ERROR) { goto point_error; } point.x = d; if (Tcl_GetDoubleFromObj(wi->interp, largv[1], &d) == TCL_ERROR) { goto point_error; } point.y = d; 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) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " dimension expected for attribute \"", Tcl_GetString(args[i+1]), "\"", NULL); return TCL_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) == TCL_ERROR) { return TCL_ERROR; } if (pri < 0) { Tcl_AppendResult(wi->interp, " priority must be a positive integer \"", Tcl_GetString(args[i+1]), "\"", NULL); return TCL_ERROR; } if (pri != *((unsigned short *) valp)) { *((unsigned short *) valp) = pri; ZnITEM.UpdateItemPriority(item, 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. */ { ZnItem item2; int result; ZnTagSearch *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 == TCL_ERROR) || (item2 == ZN_NO_ITEM)) { Tcl_AppendResult(wi->interp, " unknown item \"", Tcl_GetString(args[i+1]), "\"", NULL); return TCL_ERROR; } } if (item2 != *((ZnItem *) valp)) { *((ZnItem *) valp) = item2; *flags |= desc->flags; } } break; case ZN_CONFIG_WINDOW: { Tk_Window 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 TCL_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 != *((Tk_Window *) valp)) { *((Tk_Window *) valp) = win; *flags |= desc->flags; } } } } break; case ZN_CONFIG_CHAR: case ZN_CONFIG_UCHAR: case ZN_CONFIG_ALPHA: { int integer; if (Tcl_GetIntFromObj(wi->interp, args[i+1], &integer) == TCL_ERROR) { return TCL_ERROR; } switch (desc->type) { case ZN_CONFIG_UCHAR: if (integer < 0) { integer = 0; } case ZN_CONFIG_ALPHA: if (integer < 0) { integer = 0; } if (integer > 100) { integer = 100; } break; } if (integer != *((char *) valp)) { *((char *) valp) = integer; *flags |= desc->flags; } break; } case ZN_CONFIG_SHORT: case ZN_CONFIG_USHORT: { int integer; if (Tcl_GetIntFromObj(wi->interp, args[i+1], &integer) == TCL_ERROR) { return TCL_ERROR; } if (desc->type == ZN_CONFIG_SHORT) { if (integer < SHRT_MIN) { integer = SHRT_MIN; } else if (integer > SHRT_MAX) { integer = SHRT_MAX; } if (integer != *((short *) valp)) { *((short *) valp) = integer; *flags |= desc->flags; } } else { if (integer < 0) { integer = 0; } else if (integer > USHRT_MAX) { integer = USHRT_MAX; } if (integer != *((unsigned short *) valp)) { *((unsigned short *) valp) = integer; *flags |= desc->flags; } } break; } case ZN_CONFIG_INT: case ZN_CONFIG_UINT: case ZN_CONFIG_ANGLE: { int integer; if (Tcl_GetIntFromObj(wi->interp, args[i+1], &integer) == TCL_ERROR) { return TCL_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; } if (integer != *((int *) valp)) { *((int *) valp) = integer; *flags |= desc->flags; } break; } case ZN_CONFIG_FILL_RULE: { ZnFillRule fill_rule; if (ZnGetFillRule(wi, Tcl_GetString(args[i+1]), &fill_rule) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " fill rule expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } if (fill_rule != *((ZnFillRule *) valp)) { *((ZnFillRule *) valp) = fill_rule; *flags |= desc->flags; } break; } case ZN_CONFIG_ALIGNMENT: { Tk_Justify justify; if (Tk_GetJustify(wi->interp, Tcl_GetString(args[i+1]), &justify) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " justify expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } if (justify != *((Tk_Justify *) valp)) { *((Tk_Justify *) valp) = justify; *flags |= desc->flags; } break; } case ZN_CONFIG_ANCHOR: { Tk_Anchor anchor; if (Tk_GetAnchor(wi->interp, Tcl_GetString(args[i+1]), &anchor) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " anchor expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } if (anchor != *((Tk_Anchor *) valp)) { *((Tk_Anchor *) 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) { frmt = ZnLFCreate(wi->interp, str, ZnFIELD.NumFields(item->class->GetFieldSet(item))); if (frmt == NULL) { return TCL_ERROR; } } if (*((ZnLabelFormat *) valp) != NULL) { ZnLFDelete(*((ZnLabelFormat *) valp)); *((ZnLabelFormat *) valp) = frmt; *flags |= desc->flags; } else { if (frmt != NULL) { *((ZnLabelFormat *) valp) = frmt; *flags |= desc->flags; } } break; } case ZN_CONFIG_AUTO_ALIGNMENT: { ZnAutoAlign aa; if (ZnGetAutoAlign(wi, Tcl_GetString(args[i+1]), &aa) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " auto alignment expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } if ((aa.automatic != ((ZnAutoAlign *) valp)->automatic) || (aa.align[0] != ((ZnAutoAlign *) valp)->align[0]) || (aa.align[1] != ((ZnAutoAlign *) valp)->align[1]) || (aa.align[2] != ((ZnAutoAlign *) valp)->align[2])) { *((ZnAutoAlign *) valp) = aa; *flags |= desc->flags; } break; } case ZN_CONFIG_LEADER_ANCHORS: { ZnLeaderAnchors lanch = NULL; if (ZnGetLeaderAnchors(wi, Tcl_GetString(args[i+1]), &lanch) == TCL_ERROR) { Tcl_AppendResult(wi->interp, " leader anchors expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return TCL_ERROR; } if (*((ZnLeaderAnchors *) valp) != NULL) { ZnFree(*((ZnLeaderAnchors *) valp)); *((ZnLeaderAnchors *) valp) = lanch; *flags |= desc->flags; } else { if (lanch != NULL) { *((ZnLeaderAnchors *) valp) = lanch; *flags |= desc->flags; } } break; } } } return TCL_OK; } /* ********************************************************************************** * * AttributeToObj -- * * Returns the obj representation of the attribute pointed * by 'valp'. The attribute type is given by 'type'. The function * never fail. * ********************************************************************************** */ static void AttributeToObj(Tcl_Interp *interp, void *record, ZnAttrConfig *desc, Tcl_Obj *result) { char *valp = ((char *) record) + desc->offset; char *str = ""; Tcl_Obj *o; unsigned int i; char buffer[256]; switch (desc->type) { case ZN_CONFIG_GRADIENT: if (*((ZnGradient **) valp)) { str = ZnNameOfGradient(*((ZnGradient **) valp)); } break; case ZN_CONFIG_GRADIENT_LIST: { unsigned 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 = Tcl_NewStringObj(ZnNameOfGradient(grads[i]), -1); Tcl_ListObjAppendElement(interp, result, o); } return; } } break; case ZN_CONFIG_BOOL: Tcl_SetBooleanObj(result, ISSET(*((unsigned short *) valp), desc->bool_bit)?1:0); return; case ZN_CONFIG_IMAGE: case ZN_CONFIG_BITMAP: if (*((ZnImage *) valp)) { str = ZnNameOfImage(*((ZnImage *) valp)); } break; case ZN_CONFIG_BITMAP_LIST: { unsigned 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 = Tcl_NewStringObj(ZnNameOfImage(pats[i]), -1); } else { o = Tcl_NewStringObj("", -1); } Tcl_ListObjAppendElement(interp, result, o); } return; } break; } case ZN_CONFIG_TAG_LIST: { unsigned 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(interp, result, Tcl_NewStringObj(tags[i], -1)); } return; } break; } case ZN_CONFIG_STRING: case ZN_CONFIG_MAP_INFO: if (*((char **) valp)) { str = *((char **) valp); } break; case ZN_CONFIG_FONT: if (*((Tk_Font *) valp)) { str = (char *) Tk_NameOfFont(*((Tk_Font *) valp)); } break; case ZN_CONFIG_EDGE_LIST: str = buffer; ZnNameOfBorder(*((ZnBorder *) valp), buffer); break; case ZN_CONFIG_LINE_SHAPE: str = ZnNameOfLineShape(*((ZnLineShape *) valp)); break; case ZN_CONFIG_FILL_RULE: str = ZnNameOfFillRule(*((ZnFillRule *) valp)); break; case ZN_CONFIG_LINE_STYLE: str = ZnNameOfLineStyle(*((ZnLineStyle *) valp)); break; case ZN_CONFIG_LINE_END: if (*((ZnLineEnd *) valp)) { str = ZnLineEndGetString(*((ZnLineEnd *) valp)); } break; case ZN_CONFIG_RELIEF: str = ZnNameOfRelief(*((ZnReliefStyle *) valp)); break; case ZN_CONFIG_JOIN_STYLE: str = (char *) Tk_NameOfJoinStyle(*((int *) valp)); break; case ZN_CONFIG_CAP_STYLE: str = (char *) Tk_NameOfCapStyle(*((int *) valp)); break; case ZN_CONFIG_POINT: Tcl_ListObjAppendElement(interp, result, Tcl_NewDoubleObj(((ZnPoint *) valp)->x)); Tcl_ListObjAppendElement(interp, result, Tcl_NewDoubleObj(((ZnPoint *) valp)->y)); return; case ZN_CONFIG_ITEM: if (*((ZnItem *) valp) != ZN_NO_ITEM) { Tcl_SetLongObj(result, (int) (*((ZnItem *) valp))->id); return; } break; case ZN_CONFIG_WINDOW: if (*((Tk_Window *) valp) != NULL) { str = Tk_PathName(*((Tk_Window *) valp)); } break; case ZN_CONFIG_CHAR: Tcl_SetIntObj(result, *((char *) valp)); return; case ZN_CONFIG_UCHAR: case ZN_CONFIG_ALPHA: Tcl_SetIntObj(result, *((unsigned char *) valp)); return; case ZN_CONFIG_USHORT: case ZN_CONFIG_PRI: Tcl_SetIntObj(result, *((unsigned short *) valp)); return; case ZN_CONFIG_SHORT: Tcl_SetIntObj(result, *((short *) valp)); return; case ZN_CONFIG_UINT: Tcl_SetIntObj(result, *((unsigned int *) valp)); return; case ZN_CONFIG_INT: case ZN_CONFIG_ANGLE: Tcl_SetIntObj(result, *((int *) valp)); return; case ZN_CONFIG_DIM: Tcl_SetDoubleObj(result, *((ZnDim *) valp)); return; case ZN_CONFIG_ALIGNMENT: str = (char *) Tk_NameOfJustify(*((Tk_Justify *) valp)); break; case ZN_CONFIG_ANCHOR: str = (char *) Tk_NameOfAnchor(*((Tk_Anchor *) valp)); break; case ZN_CONFIG_LABEL_FORMAT: if (*((ZnLabelFormat *) valp)) { str = ZnLFGetString(*((ZnLabelFormat *) valp)); } break; case ZN_CONFIG_AUTO_ALIGNMENT: str = buffer; ZnNameOfAutoAlign((ZnAutoAlign *) valp, buffer); break; case ZN_CONFIG_LEADER_ANCHORS: str = buffer; ZnNameOfLeaderAnchors(*((ZnLeaderAnchors *) valp), buffer); break; } Tcl_SetStringObj(result, str, -1); } /* ********************************************************************************** * * ZnQueryAttribute -- * ********************************************************************************** */ int ZnQueryAttribute(Tcl_Interp *interp, void *record, ZnAttrConfig *desc_table, Tcl_Obj *attr_name) { ZnAttrConfig *desc = GetAttrDesc(interp, attr_name, desc_table); if (!desc) { return TCL_ERROR; } AttributeToObj(interp, record, desc, Tcl_GetObjResult(interp)); return TCL_OK; } /* ********************************************************************************** * * ZnItemClassList -- * ********************************************************************************** */ ZnList ZnItemClassList() { return item_classes; } /* ********************************************************************************** * * ZnLookupItemClass -- * ********************************************************************************** */ ZnItemClass ZnLookupItemClass(char *class_name) { ZnItemClass *class; int i, num_classes; class = (ZnItemClass *) ZnListArray(item_classes); num_classes = ZnListSize(item_classes); for (i = 0; i < num_classes; i++) { if (strcmp((class[i])->name, class_name) == 0) { return class[i]; } } return NULL; } /* ********************************************************************************** * * ZnAddItemClass -- * ********************************************************************************** */ void ZnAddItemClass(ZnItemClass 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(ZnItemClass)); #ifdef ATC ZnAddItemClass(ZnTrack); ZnAddItemClass(ZnWayPoint); ZnAddItemClass(ZnMap); ZnAddItemClass(ZnReticle); #endif ZnAddItemClass(ZnTabular); ZnAddItemClass(ZnRectangle); ZnAddItemClass(ZnArc); ZnAddItemClass(ZnCurve); ZnAddItemClass(ZnTriangles); ZnAddItemClass(ZnGroup); ZnAddItemClass(ZnIcon); ZnAddItemClass(ZnText); ZnAddItemClass(ZnWindow); InitAttrDesc(ZnFIELD.attr_desc); } } /* ********************************************************************************** * * UpdateItemDependency -- Method * Update the group dependency list following a change in the * connection of an item. * ********************************************************************************** */ static void UpdateItemDependency(ZnItem item, ZnItem 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(ZnItem item) { ZnWInfo *wi = item->wi; ZnItem group = item->parent; /* damage bounding boxes */ if (ISSET(item->flags, ZN_VISIBLE_BIT)) { ZnDamage(wi, &item->item_bounding_box); } /* * Tell that we need to repick */ if (item->class != ZnGroup) { SET(wi->flags, ZN_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(ZnItem item, ZnItem grp, ZnItem 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(ZnItem item, ZnItem mark_item, ZnBool before) { ZnItem parent = item->parent; ZnGroupExtractItem(item); InsertItem(item, parent, mark_item, before); Invalidate(item, ZN_DRAW_FLAG); SET(item->wi->flags, ZN_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(ZnItem item) { ZnWInfo *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(ZnItem 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(ZnItem 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, (void *) &tag, ZnListTail); } /* ********************************************************************************** * * RemoveTag -- Method * ********************************************************************************** */ static void RemoveTag(ZnItem item, Tk_Uid tag) { unsigned 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(ZnItem item) { if (!item->tags) { return; } ZnListFree(item->tags); item->tags = NULL; } /* ********************************************************************************** * * HasTag -- Method * ********************************************************************************** */ static ZnBool HasTag(ZnItem 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. * ********************************************************************************** */ ZnItem ZnCreateItem(ZnWInfo *wi, ZnItemClass item_class, int *argc, Tcl_Obj *CONST *args[]) { ZnItem 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, ZN_UPDATE_DEPENDENT_BIT); item->inv_flags = 0; item->transfo = NULL; item->parent = NULL; item->connected_item = ZN_NO_ITEM; #ifdef GL #ifdef GL_LIST item->gl_list = 0; #endif #endif ZnResetBBox(&item->item_bounding_box); /* Init item specific attributes */ if (item_class->Init(item, argc, args) == TCL_ERROR) { ZnFree(item); return ZN_NO_ITEM; } SetId(item); item->tags = NULL; SET(wi->flags, ZN_INTERNAL_NEED_REPICK); wi->num_items++; return (item); } /* ********************************************************************************** * * CloneItem -- Method * Can't clone the top level group. * ********************************************************************************** */ static ZnItem CloneItem(ZnItem model) { ZnWInfo *wi = model->wi; ZnItem item; Tk_Uid *tags; unsigned int num_tags; int i; if (!model->parent) { return ZN_NO_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, ZN_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->flags, ZN_INTERNAL_NEED_REPICK); wi->num_items++; Invalidate(item, ZN_COORDS_FLAG); return item; } /* ********************************************************************************** * * ConfigureItem -- Method * ********************************************************************************** */ static int ConfigureItem(ZnItem item, int field, int argc, Tcl_Obj *CONST argv[], ZnBool init) { ZnWInfo *wi = item->wi; int flags; ZnBool previous_visible = init ? False : ISSET(item->flags, ZN_VISIBLE_BIT); flags = 0; ASSIGN(flags, ZN_COORDS_FLAG, init); if (argv) { if (field < 0){ if (item->class->Configure(item, argc, argv, &flags) == TCL_ERROR) { return TCL_ERROR; } if (item->class->GetFieldSet && ISSET(flags, ZN_CLFC_FLAG)) { ZnFIELD.ClearFieldCache(item->class->GetFieldSet(item), -1); } } else if (item->class->GetFieldSet) { if (ZnFIELD.ConfigureField(item->class->GetFieldSet(item), field, argc, argv, &flags) == TCL_ERROR) { return TCL_ERROR; } } else { return TCL_ERROR; } } if (previous_visible && ISCLEAR(item->flags, ZN_VISIBLE_BIT)) { /* * Special case when the item has its visibility * just turned out. */ ZnDamage(wi, &item->item_bounding_box); } Invalidate(item, flags); return TCL_OK; } /* ********************************************************************************** * * QueryItem -- Method * ********************************************************************************** */ static int QueryItem(ZnItem 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 ZnFIELD.QueryField(item->class->GetFieldSet(item), field, argc, argv); } return TCL_ERROR; } /* ********************************************************************************** * * ComposeTransform -- * Compose a transform transfo with current_t to new_t. * ********************************************************************************** */ static void ComposeTransform(ZnTransfo *transfo, ZnPoint *pos, ZnTransfo *current_t, ZnTransfo *new_t, ZnBool compose_scale, ZnBool compose_rot) { ZnBool full; ZnTransfo t, t2; full = compose_scale && compose_rot; if (!transfo && !pos && full) { *new_t = *current_t; return; } if (full) { /* * Full concatenation. */ /*ZnPrintTransfo(transfo);*/ if (pos) { if (!transfo) { ZnTransfoSetIdentity(&t); } else { t = *transfo; } ZnTranslate(&t, pos->x, pos->y, False); ZnTransfoCompose(new_t, &t, current_t); } else { ZnTransfoCompose(new_t, transfo, current_t); } } else { ZnPoint scale, trans, local_scale, local_trans, p; ZnReal local_rot, rot; ZnTransfoSetIdentity(new_t); ZnTransfoDecompose(transfo, &local_scale, &local_trans, &local_rot, NULL); ZnScale(new_t, local_scale.x, local_scale.y); ZnRotateRad(new_t, local_rot); ZnTransfoDecompose(current_t, &scale, &trans, &rot, NULL); if (pos) { ZnTransfoSetIdentity(&t); ZnTranslate(&t, pos->x, pos->y, False); ZnTransfoCompose(&t2, &t, current_t); ZnTransformPoint(&t2, &local_trans, &p); } else { ZnTransformPoint(current_t, &local_trans, &p); } if (compose_scale) { ZnScale(new_t, scale.x, scale.y); } if (compose_rot) { ZnRotateRad(new_t, rot); } ZnTranslate(new_t, p.x, p.y, False); } } /* ********************************************************************************** * * GetItemTransform -- Method * Compute the current transform for an item. * ********************************************************************************** */ static void GetItemTransform(ZnItem item, ZnTransfo *t) { ZnItem *items; int i; ZnTransfo t_tmp, *t1, *t2, *swap; ZnPoint *pos; if (item_stack == NULL) { item_stack = ZnListNew(16, sizeof(ZnItem)); } 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 = (ZnItem *) ZnListArray(item_stack); for (i = ZnListSize(item_stack)-1; i >= 0; i--) { pos = NULL; if (items[i]->class->pos_offset >= 0) { pos = (ZnPoint *) (((char *) items[i]) + items[i]->class->pos_offset); if (pos->x == 0 && pos->y == 0) { pos = NULL; } } ComposeTransform(items[i]->transfo, pos, t1, t2, ISSET(items[i]->flags, ZN_COMPOSE_SCALE_BIT), ISSET(items[i]->flags, ZN_COMPOSE_ROTATION_BIT)); swap = t2; t2 = t1; t1 = swap; } if (t1 != t) { *t = *t1; } } /* ********************************************************************************** * * ZnResetTransformStack * ZnInitTransformStack * ZnFreeTransformStack * ZnCurrentTransform * ZnPushTransform * ZnPopTransform -- * ********************************************************************************** */ void ZnResetTransformStack(ZnWInfo *wi) { ZnListAssertSize(wi->transfo_stack, 1); wi->current_transfo = (ZnTransfo *) ZnListAt(wi->transfo_stack, 0); ZnTransfoSetIdentity(wi->current_transfo); } void ZnInitTransformStack(ZnWInfo *wi) { wi->transfo_stack = ZnListNew(8, sizeof(ZnTransfo)); ZnResetTransformStack(wi); } void ZnFreeTransformStack(ZnWInfo *wi) { ZnListFree(wi->transfo_stack); } void ZnPushTransform(ZnWInfo *wi, ZnTransfo *transfo, ZnPoint *pos, ZnBool compose_scale, ZnBool compose_rot) { ZnTransfo *next_t; unsigned 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, pos, wi->current_transfo, next_t, compose_scale, compose_rot); wi->current_transfo = next_t; } void ZnPopTransform(ZnWInfo *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. */ TkRegion region; /* The region used to draw and to */ /* probe for picking. */ ZnBBox clip_box; /* The bounding box of the clip area. */ } ClipState; void ZnResetClipStack(ZnWInfo *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--) { TkDestroyRegion(clips[i].region); } ZnListEmpty(wi->clip_stack); wi->current_clip = NULL; } void ZnInitClipStack(ZnWInfo *wi) { wi->clip_stack = ZnListNew(8, sizeof(ClipState)); ZnResetClipStack(wi); } void ZnFreeClipStack(ZnWInfo *wi) { ZnListFree(wi->clip_stack); } ZnBool ZnCurrentClip(ZnWInfo *wi, TkRegion *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(ZnWInfo *wi, ZnTriStrip *tristrip, ZnBool simple, ZnBool set_gc) { unsigned int i, j, num_clips; unsigned int num_pts, max_num_pts; ZnPoint *p; ClipState *previous_clip=NULL; TkRegion 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 = (short) tristrip->strips[0].points[0].x; rect.y = (short) tristrip->strips[0].points[0].y; rect.width = ((unsigned short) (tristrip->strips[0].points[1].x - tristrip->strips[0].points[0].x)); rect.height = ((unsigned short) (tristrip->strips[0].points[1].y - tristrip->strips[0].points[0].y)); reg = TkCreateRegion(); TkUnionRectWithRegion(&rect, reg, reg); /*printf("Adding a simple clip: %d, %d, %d, %d\n", rect.x, rect.y, rect.width, rect.height);*/ } else { reg = TkCreateRegion(); 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 = ZnNearestInt(p->x); xpts[0].y = ZnNearestInt(p->y); p++; xpts[1].x = ZnNearestInt(p->x); xpts[1].y = ZnNearestInt(p->y); p++; for (i = 2; i < num_pts; i++, p++) { xpts[2].x = ZnNearestInt(p->x); xpts[2].y = ZnNearestInt(p->y); reg_op = (TkRegion) ZnPolygonRegion(xpts, 3, EvenOddRule); reg_to = TkCreateRegion(); ZnUnionRegion(reg, reg_op, reg_to); TkDestroyRegion(reg); TkDestroyRegion(reg_op); reg = reg_to; xpts[1] = xpts[2]; } } else { xpts[0].x = (short) p->x; xpts[0].y = (short) p->y; p++; xpts[1].x = (short) p->x; xpts[1].y = (short) p->y; p++; for (i = 2 ; i < num_pts; i++, p++) { xpts[2].x = (short) p->x; xpts[2].y = (short) p->y; reg_op = (TkRegion) ZnPolygonRegion(xpts, 3, EvenOddRule); reg_to = TkCreateRegion(); ZnUnionRegion(reg, reg_op, reg_to); TkDestroyRegion(reg); TkDestroyRegion(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 = TkCreateRegion(); TkIntersectRegion(reg, previous_clip->region, wi->current_clip->region); TkDestroyRegion(reg); /*printf("Merging with previous clip\n");*/ } else { wi->current_clip->region = reg; } TkClipBox(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 GL glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glEnable(GL_STENCIL_TEST); glStencilFunc(GL_EQUAL, (GLint) 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); glVertex2d(wi->current_clip->clip_box.orig.x, wi->current_clip->clip_box.orig.y); glVertex2d(wi->current_clip->clip_box.orig.x, wi->current_clip->clip_box.corner.y); glVertex2d(wi->current_clip->clip_box.corner.x, wi->current_clip->clip_box.corner.y); glVertex2d(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++) { glVertex2d(p->x, p->y); } glEnd(); } } glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glStencilFunc(GL_EQUAL, (GLint) (num_clips+1), 0xFF); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); #endif } else { TkSetRegion(wi->dpy, wi->gc, wi->current_clip->region); } } } void ZnPopClip(ZnWInfo *wi, ZnBool set_gc) { int num_clips; if (wi->current_clip == NULL) { return; } TkDestroyRegion(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 GL glStencilFunc(GL_EQUAL, (GLint) (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); glVertex2d(wi->current_clip->clip_box.orig.x, wi->current_clip->clip_box.orig.y); glVertex2d(wi->current_clip->clip_box.orig.x, wi->current_clip->clip_box.corner.y); glVertex2d(wi->current_clip->clip_box.corner.x, wi->current_clip->clip_box.corner.y); glVertex2d(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, (GLint) num_clips, 0xFF); glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); #endif } else { TkSetRegion(wi->dpy, wi->gc, wi->current_clip->region); } } else { /*printf("resetting clip mask\n");*/ if (wi->render) { #ifdef GL 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(ZnItem item, int reason) { /* * Why this test has to be so an abrupt shortcut ? * It precludes addition of meaningful reasons * by subsequent invalidations . * if (ISSET(item->inv_flags, ZN_TRANSFO_FLAG)) { return; }*/ if (ISSET(reason, ZN_COORDS_FLAG) || ISSET(reason, ZN_TRANSFO_FLAG)) { ZnItem 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, ZN_VISIBLE_BIT)) { /*printf("invalidate graphics for item %d\n", item->id);*/ ZnDamage(item->wi, &item->item_bounding_box); #ifdef GL #ifdef GL_LIST /* * 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 #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(ZnItem group, ZnItemClass item_class) { ZnItem 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 * SkewItem * 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(ZnItem item) { if (item->transfo) { ZnFree(item->transfo); item->transfo = NULL; } Invalidate(item, ZN_TRANSFO_FLAG); } static void SetTransfo(ZnItem 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(ZnItem item, ZnReal dx, ZnReal dy, ZnBool abs) { if (!item->transfo) { item->transfo = ZnTransfoNew(); } ZnTranslate(item->transfo, dx, dy, abs); Invalidate(item, ZN_TRANSFO_FLAG); } static void ScaleItem(ZnItem item, ZnReal sx, ZnReal sy, ZnPoint *p) { if (!item->transfo) { item->transfo = ZnTransfoNew(); } if (p) { ZnTranslate(item->transfo, -p->x, -p->y, False); } ZnScale(item->transfo, sx, sy); if (p) { ZnTranslate(item->transfo, p->x, p->y, False); } Invalidate(item, ZN_TRANSFO_FLAG); } static void SkewItem(ZnItem item, ZnReal x_skew, ZnReal y_skew) { if (!item->transfo) { item->transfo = ZnTransfoNew(); } ZnSkewRad(item->transfo, x_skew, y_skew); Invalidate(item, ZN_TRANSFO_FLAG); } static void RotateItem(ZnItem item, ZnReal angle, ZnBool deg, ZnPoint *p) { if (!item->transfo) { item->transfo = ZnTransfoNew(); } if (p) { ZnTranslate(item->transfo, -p->x, -p->y, False); } if (deg) { ZnRotateDeg(item->transfo, angle); } else { ZnRotateRad(item->transfo, angle); } if (p) { ZnTranslate(item->transfo, p->x, p->y, False); } Invalidate(item, ZN_TRANSFO_FLAG); } /* ********************************************************************************** * * DestroyItem -- Method * ********************************************************************************** */ static void DestroyItem(ZnItem item) { ZnWInfo *wi = item->wi; ZnTextInfo *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 _ZnITEM ZnITEM = { CloneItem, DestroyItem, ConfigureItem, QueryItem, InsertItem, UpdateItemPriority, UpdateItemDependency, ExtractItem, SetId, FreeId, AddTag, RemoveTag, FreeTags, HasTag, ResetTransfo, SetTransfo, TranslateItem, ScaleItem, SkewItem, RotateItem, Invalidate, InvalidateItems, GetItemTransform };