/* * 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 #include "Item.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__ " $"; #define FIELD_SENSITIVE_BIT 1 #define FIELD_VISIBLE_BIT 2 #define FILLED_BIT 4 #define TEXT_ON_TOP_BIT 8 #define CACHE_OK 16 static ZnList item_classes = NULL; static ZnList item_stack = NULL; /* * Field record. */ typedef struct _FieldStruct { /* Public data */ ZnColor color; ZnColor back_color; ZnColor border_color; Pixmap fill_pattern; char *text; char *image_name; char *tile_name; ZnFont font; unsigned char flags; Border border_edges; ZnJustify alignment; ReliefStyle relief; int relief_thickness; AutoAlign auto_alignment; /* Private data */ ZnImage image; ZnImage tile; ZnColorGradient gradient; short orig_x; short orig_y; short corner_x; short corner_y; FieldSet field_set; } FieldStruct, *Field; /* * The -text, -image, -border, -relief, -visible and * -filled attributes set the ZN_COORDS_FLAG to update * the leader that might protude if not clipped by the text. */ ZnAttrConfig field_attrs[] = { { ZN_CONFIG_JUSTIFY, "-alignment", NULL, Tk_Offset(FieldStruct, alignment), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_AUTO_JUSTIFY, "-autoalignment", NULL, Tk_Offset(FieldStruct, auto_alignment), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_COLOR, "-backcolor", NULL, Tk_Offset(FieldStruct, back_color), 0, ZN_DRAW_FLAG|ZN_BORDER_FLAG, False }, { ZN_CONFIG_BORDER, "-border", NULL, Tk_Offset(FieldStruct, border_edges), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_COLOR, "-bordercolor", NULL, Tk_Offset(FieldStruct, border_color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_COLOR, "-color", NULL, Tk_Offset(FieldStruct, color), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-filled", NULL, Tk_Offset(FieldStruct, flags), FILLED_BIT, ZN_COORDS_FLAG, False }, { ZN_CONFIG_PATTERN, "-fillpattern", NULL, Tk_Offset(FieldStruct, fill_pattern), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_FONT, "-font", NULL, Tk_Offset(FieldStruct, font), 0, ZN_DRAW_FLAG|ZN_CLFC_FLAG, False }, { ZN_CONFIG_IMAGE, "-image", NULL, Tk_Offset(FieldStruct, image_name), 0, ZN_COORDS_FLAG|ZN_IMAGE_FLAG|ZN_CLFC_FLAG, False }, { ZN_CONFIG_RELIEF, "-relief", NULL, Tk_Offset(FieldStruct, relief), 0, ZN_COORDS_FLAG, False }, { ZN_CONFIG_DIM, "-reliefthickness", NULL, Tk_Offset(FieldStruct, relief_thickness), 0, ZN_DRAW_FLAG, False }, { ZN_CONFIG_BOOL, "-sensitive", NULL, Tk_Offset(FieldStruct, flags), FIELD_SENSITIVE_BIT, ZN_REPICK_FLAG, False }, { ZN_CONFIG_TEXT, "-text", NULL, Tk_Offset(FieldStruct, text), 0, ZN_COORDS_FLAG|ZN_CLFC_FLAG, False }, { ZN_CONFIG_IMAGE, "-tile", NULL, Tk_Offset(FieldStruct, tile_name), 0, ZN_COORDS_FLAG|ZN_TILE_FLAG|ZN_CLFC_FLAG, False }, { ZN_CONFIG_BOOL, "-visible", NULL, Tk_Offset(FieldStruct, flags), FIELD_VISIBLE_BIT, ZN_COORDS_FLAG|ZN_CLFC_FLAG, False }, /* Keep ZN_COORDS_FLAG here */ { ZN_CONFIG_END, NULL, NULL, 0, 0, 0 } }; /* * This array must be kept in sync with the * corresponding defines in Types.h. */ static char *attribute_type_strings[] = { "", "color", "boolean", "pattern", "text", "font", "border", "relief", "dimension", "priority", "justify", "autojustify", "lineend", "labelformat", "linestyle", "lineshape", "item", "angle", "integer", "unsignedint", "point", "rectangle", "patterns", "anchor", "tags", "points", "mapinfo", "image", "leaderanchors", "joinstyle", "capstyle", "gradientgeometry", "gradientcolor", "window" }; /* ********************************************************************************** * * Forward functions * ********************************************************************************** */ static void Damage(WidgetInfo *wi, ZnBBox *damage); static void Invalidate(Item item, int reason); static int ConfigureField(FieldSet field_set, unsigned int field, int argc, Tcl_Obj *CONST argv[], int *flags); static int QueryField(FieldSet field_set, unsigned int field, int argc, Tcl_Obj *CONST argv[]); static Tcl_Obj *AttributeToObj(WidgetInfo *wi, char *record, ZnAttrConfig *desc, char *buffer); static void FieldImageChange(ClientData client_data, int x, int y, int width, int height, int image_width, int image_height); static void FieldTileChange(ClientData client_data, int x, int y, int width, int height, int image_width, int image_height); static void GetLabelBBox(FieldSet field_set, ZnDim *w, ZnDim *h); /* ********************************************************************************** * * 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 -- * ********************************************************************************** */ static int AttributesInfo(Item item, int field, /* 0< means the item itself. */ int argc, Tcl_Obj *CONST args[]) { WidgetInfo *wi = item->wi; char *record; ZnAttrConfig *desc; Tk_Uid attr_uid = NULL; Tcl_Obj *l, *entries[5]; char buffer[256]; if (field < 0) { record = (char *) item; desc = item->class->attr_desc; } else { FieldSet field_set; if (!item->class->has_fields) { Tcl_AppendResult(wi->interp, "item class \"", item->class->name, "\" doesn't support fields", NULL); return ZN_ERROR; } field_set = item->class->GetFieldSet(item); if (field >= field_set->num_fields) { Tcl_AppendResult(wi->interp, "invalid field index \"", NULL); return ZN_ERROR; } record = (char *) &field_set->fields[field]; desc = field_attrs; } 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] = AttributeToObj(wi, record, desc, buffer); #ifdef PTK l = Tcl_Merge(5, entries); Tcl_SetObjResult(wi->interp, l); #else Tcl_SetObjResult(wi->interp, Tcl_NewListObj(5, entries)); #endif } else { #ifdef PTK Tcl_Obj *o; #endif 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] = AttributeToObj(wi, record, desc, buffer); #ifdef PTK o = NULL; LangSetArg(&o, Tcl_Merge(5, entries)); Tcl_ListObjAppendElement(wi->interp, l, o); #else Tcl_ListObjAppendElement(wi->interp, l, Tcl_NewListObj(5, entries)); #endif desc++; } } return TCL_OK; } /* ********************************************************************************** * * ConfigureAttributes -- * ********************************************************************************** */ static int ConfigureAttributes(char *record, int field, /* 0< means item itself. */ int argc, Tcl_Obj *CONST args[], int *flags) { WidgetInfo *wi; Item item = NULL; int i; Tk_Uid attr_uid; ZnAttrConfig *desc; ZnPtr valp; ZnAttrConfig *attr_desc; FieldSet field_set = NULL; char *str; if (field < 0) { item = (Item) record; wi = item->wi; attr_desc = item->class->attr_desc; } else { field_set = (FieldSet) record; wi = field_set->wi; if (field >= field_set->num_fields) { Tcl_AppendResult(wi->interp, "invalid field index \"", NULL); return ZN_ERROR; } record = (char *) &field_set->fields[field]; /*printf("record <0x%X>, field %d\n", record, field);*/ attr_desc = field_attrs; } 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) { /*printf("ERROR: record <0x%X>\n", record);*/ 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 = record + desc->offset; /*printf("record <0x%X>, valp <0x%X>, offset %d\n", record, valp, desc->offset);*/ switch (desc->type) { case ZN_CONFIG_COLOR: { XColor *color; Tk_Uid new_name = Tk_GetUid(Tcl_GetString(args[i+1])); char *name = NULL; if (*((XColor **) valp)) { name = ZnNameOfColor(*((XColor **) valp)); } if (name != new_name) { color = ZnGetColor(wi->interp, wi->win, new_name); if (!color) { Tcl_AppendResult(wi->interp, " color expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (*((XColor **) valp)) { ZnFreeColor(*((XColor **) valp)); } *((XColor **) valp) = color; *flags |= desc->flags; } break; } case ZN_CONFIG_GRADIENT_COLOR: { ZnColorGradient cg; Tk_Uid new_name = Tk_GetUid(Tcl_GetString(args[i+1])); char *name = NULL; if (*((ZnColorGradient *) valp)) { name = ZnNameOfColorGradient(*((ZnColorGradient *) valp)); } if (name != new_name) { cg = ZnGetColorGradient(wi->interp, wi->win, new_name); if (!cg) { Tcl_AppendResult(wi->interp, " color gradient expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (*((ZnColorGradient *) valp)) { ZnFreeColorGradient(*((ZnColorGradient *) valp)); } *((ZnColorGradient *) valp) = cg; *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(*((char *) valp), desc->bool_bit) != 0)) { ASSIGN(*((char *) valp), desc->bool_bit, b); *flags |= desc->flags; } break; } case ZN_CONFIG_PATTERN: { Pixmap pattern = ZnUnspecifiedPattern; char *name = ""; if (*((Pixmap *) valp) != ZnUnspecifiedPattern) { name = Tk_NameOfBitmap(wi->dpy, *((Pixmap *) valp)); } str = Tcl_GetString(args[i+1]); if (strcmp(name, str) != 0) { if (strlen(str) != 0) { pattern = Tk_GetBitmap(wi->interp, wi->win, Tk_GetUid(str)); if (pattern == None) { Tcl_AppendResult(wi->interp, " pattern expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } } if (*((Pixmap *) valp) != ZnUnspecifiedPattern) { Tk_FreeBitmap(wi->dpy, *((Pixmap *) valp)); } *((Pixmap *) valp) = pattern; *flags |= desc->flags; } break; } case ZN_CONFIG_PATTERNS: { ZnList new_pat_list = NULL; Pixmap *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 = (Pixmap *) ZnListArray(new_pat_list); for (j = 0; j < num_pats; j++) { str = Tcl_GetString(elems[j]); if (strlen(str) != 0) { pats[j] = Tk_GetBitmap(wi->interp, wi->win, Tk_GetUid(str)); if (pats[j] == None) { Tcl_AppendResult(wi->interp, " unknown pattern \"", str, "\" in pattern list", NULL); for (k = 0; k < j; k++) { Tk_FreeBitmap(wi->dpy, pats[k]); } ZnListFree(new_pat_list); return ZN_ERROR; } } else { pats[j] = ZnUnspecifiedPattern; } } } if (*((ZnList *) valp)) { num_pats = ZnListSize(*((ZnList *) valp)); pats = (Pixmap *) ZnListArray(*((ZnList *) valp)); for (j = 0; j < num_pats; j++) { if (pats[j] != ZnUnspecifiedPattern) { Tk_FreeBitmap(wi->dpy, 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_TAGS: { 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); *flags |= desc->flags; } if (num_tags) { for (j = 0; j < num_tags; j++) { ITEM.AddTag(item, Tk_GetUid(Tcl_GetString(elems[j]))); } *flags |= desc->flags; } break; } case ZN_CONFIG_TEXT: case ZN_CONFIG_MAP_INFO: case ZN_CONFIG_IMAGE: { char *text = ""; str = Tcl_GetString(args[i+1]); if (strcmp(str, *((char **) valp)) != 0) { if (strlen(str) != 0) { text = (char *) ZnMalloc(strlen(str)+1); strcpy(text, str); } if (strlen(*((char **) valp)) != 0) { ZnFree(*((char **) valp)); } *((char **) valp) = text; *flags |= desc->flags; } break; } case ZN_CONFIG_FONT: { Tk_Font font; char *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_BORDER: { 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_LEFT_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_LEFT_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_GRADIENT_GEOM: { ZnGradientGeom gg = NULL; str = Tcl_GetString(args[i+1]); if (strlen(str) != 0) { gg = GradientGeomCreate(wi->interp, str); if (gg == NULL) { return ZN_ERROR; } } if (*((ZnGradientGeom *) valp) != NULL) { GradientGeomDelete(*((ZnGradientGeom *) valp)); *((ZnGradientGeom *) valp) = gg; *flags |= desc->flags; } else { if (gg != NULL) { *((ZnGradientGeom *) valp) = gg; *flags |= desc->flags; } } break; } case ZN_CONFIG_RELIEF: { int relief; if (Tk_GetRelief(wi->interp, 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) = (ReliefStyle) 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_RECT: { ZnRect rect; int largc; Tcl_Obj **largv; if ((Tcl_ListObjGetElements(wi->interp, args[i+1], &largc, &largv) == ZN_ERROR) || largc != 4) { rect_error: Tcl_AppendResult(wi->interp, " rectangle expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if ((Tcl_GetDoubleFromObj(wi->interp, largv[0], &rect.x) == ZN_ERROR) || (Tcl_GetDoubleFromObj(wi->interp, largv[1], &rect.y) == ZN_ERROR) || (Tcl_GetDoubleFromObj(wi->interp, largv[2], &rect.w) == ZN_ERROR) || (Tcl_GetDoubleFromObj(wi->interp, largv[3], &rect.h) == ZN_ERROR)) { goto rect_error; } if ((rect.x != ((ZnRect *) valp)->x) && (rect.y != ((ZnRect *) valp)->y) && (rect.w != ((ZnRect *) valp)->w) && (rect.h != ((ZnRect *) valp)->h)) { *((ZnRect *) valp) = rect; *flags |= desc->flags; } break; } case ZN_CONFIG_DIM: { int size; if (Tk_GetPixels(wi->interp, wi->win, Tcl_GetString(args[i+1]), &size) == ZN_ERROR) { Tcl_AppendResult(wi->interp, " dimension expected for attribute \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } if (size != *((int *) valp)) { *((int *) valp) = size; *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], wi->top_group, True, &item2, &search_var); ZnTagSearchDestroy(search_var); if ((result == ZN_ERROR) || (item2 == ZN_NO_ITEM)) { 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: { int integer; if (Tcl_GetIntFromObj(wi->interp, args[i+1], &integer) == ZN_ERROR) { return ZN_ERROR; } if (desc->type == ZN_CONFIG_ANGLE) { if ((integer < 0) || (integer > 360)) { Tcl_AppendResult(wi->interp, " angle must be between 0 and 360 \"", Tcl_GetString(args[i+1]), "\"", NULL); return ZN_ERROR; } } else if (desc->type == ZN_CONFIG_UINT) { if (integer < 0) { Tcl_AppendResult(wi->interp, " positive integer expected for \"", Tcl_GetString(args[i]), "\"", NULL); return ZN_ERROR; } } if (integer != *((int *) valp)) { *((int *) valp) = integer; *flags |= desc->flags; } break; } case ZN_CONFIG_JUSTIFY: { 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) { frmt = LabelFormatCreate(wi->interp, str, item->class->GetFieldSet(item)->num_fields); 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_JUSTIFY: { 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 Tcl_Obj * AttributeToObj(WidgetInfo *wi, char *record, ZnAttrConfig *desc, char *buffer) { Tcl_Obj *result = NULL; char *valp = record + desc->offset; char *str = ""; Tcl_Obj *o, *objs[4]; int i; #ifdef PTK Tcl_Obj **tmp=NULL; #endif switch (desc->type) { case ZN_CONFIG_COLOR: if (*((XColor **) valp)) { result = NewStringObj(ZnNameOfColor(*((XColor **) valp))); } break; case ZN_CONFIG_BOOL: result = NewBooleanObj(ISSET(*((char *) valp), desc->bool_bit)?1:0); break; case ZN_CONFIG_PATTERN: if (*((Pixmap *) valp)) { result = NewStringObj(Tk_NameOfBitmap(wi->dpy, *((Pixmap *) valp))); } break; case ZN_CONFIG_PATTERNS: { int num_pats=0; Pixmap *pats; #ifndef PTK result = Tcl_NewListObj(0, NULL); #endif if (*((ZnList *) valp)) { pats = (Pixmap *) ZnListArray(*((ZnList *) valp)); num_pats = ZnListSize(*((ZnList *) valp)); #ifdef PTK tmp = (Tcl_Obj **) ZnMalloc(num_pats * sizeof(Tcl_Obj *)); #endif for (i = 0; i < num_pats; i++) { if (pats[i] != ZnUnspecifiedPattern) { o = NewStringObj(Tk_NameOfBitmap(wi->dpy, pats[i])); } else { o = NewStringObj(""); } #ifdef PTK tmp[i] = o; #else Tcl_ListObjAppendElement(wi->interp, result, o); #endif } } #ifdef PTK result = Tcl_Merge(num_pats, tmp); ZnFree(tmp); #endif break; } case ZN_CONFIG_TAGS: { int num_tags=0; Tk_Uid *tags; #ifndef PTK result = Tcl_NewListObj(0, NULL); #endif if (*((ZnList *) valp)) { tags = (Tk_Uid *) ZnListArray(*((ZnList *) valp)); num_tags = ZnListSize(*((ZnList *) valp)); #ifdef PTK tmp = (Tcl_Obj **) ZnMalloc(num_tags * sizeof(Tcl_Obj *)); #endif for (i = 0; i < num_tags; i++) { #ifdef PTK tmp[i] = NewStringObj(tags[i]); #else Tcl_ListObjAppendElement(wi->interp, result, NewStringObj(tags[i])); #endif } } #ifdef PTK result = Tcl_Merge(num_tags, tmp); ZnFree(tmp); #endif break; } case ZN_CONFIG_TEXT: case ZN_CONFIG_MAP_INFO: case ZN_CONFIG_IMAGE: result = NewStringObj(*((char **) valp)); break; case ZN_CONFIG_FONT: if (*((Tk_Font *) valp)) { result = NewStringObj(Tk_NameOfFont(*((Tk_Font *) valp))); } break; case ZN_CONFIG_BORDER: { Border border = *((Border *) valp); if (border == NO_BORDER) { result = NewStringObj(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); } result = NewStringObj(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; } result = NewStringObj(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; } result = NewStringObj(str); break; } case ZN_CONFIG_LINE_END: { ZnLineEnd line_end = *((ZnLineEnd *) valp); if (line_end) { result = NewStringObj(LineEndGetString(line_end)); } break; } case ZN_CONFIG_GRADIENT_GEOM: { ZnGradientGeom gg = *((ZnGradientGeom *) valp); if (gg) { result = NewStringObj(GradientGeomGetString(gg)); } break; } case ZN_CONFIG_RELIEF: result = NewStringObj(Tk_NameOfRelief(*((ReliefStyle *) valp))); break; case ZN_CONFIG_JOIN_STYLE: result = NewStringObj(Tk_NameOfJoinStyle(*((int *) valp))); break; case ZN_CONFIG_CAP_STYLE: result = NewStringObj(Tk_NameOfCapStyle(*((int *) valp))); break; case ZN_CONFIG_POINT: { objs[0] = NewDoubleObj(((ZnPoint *) valp)->x); objs[1] = NewDoubleObj(((ZnPoint *) valp)->y); #ifdef PTK result = Tcl_Merge(2, objs); #else result = Tcl_NewListObj(2, objs); #endif break; } case ZN_CONFIG_RECT: { objs[0] = NewDoubleObj(((ZnRect *) valp)->x); objs[1] = NewDoubleObj(((ZnRect *) valp)->y); objs[2] = NewDoubleObj(((ZnRect *) valp)->w); objs[3] = NewDoubleObj(((ZnRect *) valp)->h); #ifdef PTK result = Tcl_Merge(4, objs); #else result = Tcl_NewListObj(4, objs); #endif break; } case ZN_CONFIG_ITEM: if (*((Item *) valp) != ZN_NO_ITEM) { result = NewLongObj((*((Item *) valp))->id); } break; case ZN_CONFIG_WINDOW: if (*((ZnWindow *) valp) != NULL) { result = NewStringObj(Tk_PathName(*((ZnWindow *) valp))); } break; case ZN_CONFIG_INT: case ZN_CONFIG_UINT: case ZN_CONFIG_DIM: case ZN_CONFIG_PRI: case ZN_CONFIG_ANGLE: result = Tcl_NewIntObj(*((int *) valp)); break; case ZN_CONFIG_JUSTIFY: result = NewStringObj(Tk_NameOfJustify(*((ZnJustify *) valp))); break; case ZN_CONFIG_ANCHOR: result = NewStringObj(Tk_NameOfAnchor(*((Tk_Anchor *) valp))); break; case ZN_CONFIG_LABEL_FORMAT: { ZnLabelFormat frmt = *((ZnLabelFormat *) valp); if (frmt) { result = NewStringObj(LabelFormatGetString(frmt)); } break; } case ZN_CONFIG_AUTO_JUSTIFY: { AutoAlign *aa = (AutoAlign *) valp; int i; if (aa->automatic == False) { result = NewStringObj("-"); } 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; } } result = NewStringObj(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; } result = NewStringObj(ptr); } } if (result == NULL) { result = NewStringObj(""); } return result; } /* ********************************************************************************** * * QueryAttribute -- * ********************************************************************************** */ static int QueryAttribute(char *record, int field, /* 0< means item itself. */ Tcl_Obj *attr_name) { WidgetInfo *wi; Item item; Tk_Uid attr_uid = Tk_GetUid(Tcl_GetString(attr_name)); Tcl_Obj *result = NULL; char buffer[256]; ZnAttrConfig *desc; if (field < 0) { item = (Item) record; wi = item->wi; desc = item->class->attr_desc; } else { FieldSet field_set = (FieldSet) record; wi = field_set->wi; if (field >= field_set->num_fields) { Tcl_AppendResult(wi->interp, "invalid field index \"", NULL); return ZN_ERROR; } record = (char *) &field_set->fields[field]; desc = field_attrs; } 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 = AttributeToObj(wi, record, desc, buffer); Tcl_SetObjResult(wi->interp, result); #ifdef PTK Tcl_DecrRefCount(result); #endif break; } else { desc++; } } return ZN_OK; } /* ********************************************************************************** * * ItemClassList -- * ********************************************************************************** */ static ZnList ItemClassList() { return item_classes; } /* ********************************************************************************** * * LookupItemClass -- * ********************************************************************************** */ static ItemClass LookupItemClass(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; } /* ********************************************************************************** * * AddItemClass -- * ********************************************************************************** */ static void AddItemClass(ItemClass class) { if (!LookupItemClass(class->name)) { ZnListAdd(item_classes, &class, ZnListTail); InitAttrDesc(class->attr_desc); } } /* ********************************************************************************** * * GlobalModuleInit -- * Initialize classes static state. * ********************************************************************************** */ static void GlobalModuleInit() { /* First check if static part already inited */ if (item_classes == NULL) { item_classes = ZnListNew(16, sizeof(ItemClass)); AddItemClass(ZnTrack); AddItemClass(ZnWayPoint); AddItemClass(ZnMap); AddItemClass(ZnReticle); AddItemClass(ZnTabular); AddItemClass(ZnRectangle); AddItemClass(ZnArc); AddItemClass(ZnCurve); AddItemClass(ZnBezier); AddItemClass(ZnGroup); AddItemClass(ZnIcon); AddItemClass(ZnText); AddItemClass(ZnWind); /*AddItemClass(ZnMosaic);*/ InitAttrDesc(field_attrs); } } /* ********************************************************************************** * * ComputeFieldAttachment -- * Compute the location/size of the field, computing attachments if any. * ********************************************************************************** */ static void ComputeFieldAttachment(FieldSet field_set, int field, ZnBBox *field_bbox) { ZnBBox ref_bbox; ZnDim real_width, real_height; int ref_field, num_fields; char x_attach, y_attach, x_dim, y_dim; short width_spec, height_spec; int x_spec, y_spec, icon_width=0, icon_height=0; Field field_ptr; Tk_FontMetrics fm; field_ptr = &field_set->fields[field]; if (ISSET(field_ptr->flags, CACHE_OK)) { field_bbox->orig.x = (ZnPos) field_ptr->orig_x; field_bbox->orig.y = (ZnPos) field_ptr->orig_y; field_bbox->corner.x = field_ptr->corner_x; field_bbox->corner.y = field_ptr->corner_y; return; } /* * Preset this field to a default position/size and pretend * its cache is ok to break any deadlocks. */ field_ptr->orig_x = field_ptr->orig_y = 0; field_ptr->corner_x = field_ptr->corner_y = 0; field_bbox->orig.x = field_bbox->orig.y = 0; field_bbox->corner.x = field_bbox->corner.y = 0; SET(field_ptr->flags, CACHE_OK); num_fields = LabelFormatNumFields(field_set->label_format); LabelFormatGetField(field_set->label_format, field, &x_attach, &y_attach, &x_dim, &y_dim, &x_spec, &y_spec, &width_spec, &height_spec); /* * First try to compute the field size which may be a factor * of the field content (but not a factor of other fields). */ if ((field_ptr->image != ZnUnspecifiedImage) && ((x_dim == LF_DIM_ICON) || (y_dim == LF_DIM_ICON) || (x_dim == LF_DIM_AUTO) || (y_dim == LF_DIM_AUTO))) { Tk_SizeOfImage(field_ptr->image, &icon_width, &icon_height); } switch (x_dim) { case LF_DIM_FONT: real_width = (ZnDim) (width_spec*ZnTextWidth(field_ptr->font, "N", 1)/100); break; case LF_DIM_ICON: real_width = (ZnDim) (width_spec*icon_width/100); break; case LF_DIM_AUTO: { int len = strlen(field_ptr->text); ZnDim text_width; real_width = 0.0; if (field_ptr->image != ZnUnspecifiedImage) { real_width = (ZnDim) icon_width; } if (len) { text_width = (ZnDim) ZnTextWidth(field_ptr->font, field_ptr->text, len); real_width = text_width < real_width ? real_width : text_width; } real_width += (ZnDim) width_spec; break; } case LF_DIM_LABEL: { ZnDim lh; GetLabelBBox(field_set, &real_width, &lh); break; } case LF_DIM_PIXEL: default: real_width = (ZnDim) width_spec; break; } /*printf("field %d, width = %g\n", field, real_width);*/ switch (y_dim) { case LF_DIM_FONT: { Tk_GetFontMetrics(field_ptr->font, &fm); real_height = (ZnDim) (height_spec*(fm.ascent + fm.descent)/100); break; } case LF_DIM_ICON: real_height = (ZnDim) (height_spec*icon_height/100); break; case LF_DIM_AUTO: { ZnDim text_height; real_height = 0.0; if (field_ptr->image != ZnUnspecifiedImage) { real_height = (ZnDim) icon_height; } if (strlen(field_ptr->text)) { Tk_GetFontMetrics(field_ptr->font, &fm); text_height = (ZnDim) (fm.ascent + fm.descent); real_height = text_height < real_height ? real_height : text_height; } real_height += (ZnDim) height_spec; break; } case LF_DIM_LABEL: { ZnDim lw; GetLabelBBox(field_set, &lw, &real_height); break; } case LF_DIM_PIXEL: default: real_height = (ZnDim) height_spec; break; } /*printf("field %d, height = %g\n", field, real_height);*/ /* * Update the cache with the newly computed infos * (breaking of deadlocks). */ field_bbox->corner.x = field_ptr->corner_x = real_width; field_bbox->corner.y = field_ptr->corner_y = real_height; /* * Then try to deduce the position, resolving any attachments * if needed. */ /* * Do the x axis. */ if (x_dim != LF_DIM_LABEL) { if (x_attach == LF_ATTACH_PIXEL) { field_bbox->orig.x = (ZnPos) x_spec; field_bbox->corner.x = field_bbox->orig.x + real_width; } else { ref_field = x_spec; field_bbox->orig.x = field_bbox->corner.x = 0; if ((ref_field < 0) || (ref_field >= num_fields)) { ZnWarning ("Attached (x) to an inexistant field geometry"); } else { ComputeFieldAttachment(field_set, ref_field, &ref_bbox); switch (x_attach) { case LF_ATTACH_FWD: if (ISSET(field_set->fields[ref_field].flags, FIELD_VISIBLE_BIT)) { field_bbox->orig.x = ref_bbox.corner.x; } else { field_bbox->orig.x = ref_bbox.orig.x; } field_bbox->corner.x = field_bbox->orig.x + real_width; break; case LF_ATTACH_BWD: if (ISSET(field_set->fields[ref_field].flags, FIELD_VISIBLE_BIT)) { field_bbox->corner.x = ref_bbox.orig.x; } else { field_bbox->corner.x = ref_bbox.corner.x; } field_bbox->orig.x = field_bbox->corner.x - real_width; break; case LF_ATTACH_LEFT: field_bbox->orig.x = ref_bbox.orig.x; field_bbox->corner.x = field_bbox->orig.x + real_width; break; case LF_ATTACH_RIGHT: if (ISSET(field_set->fields[ref_field].flags, FIELD_VISIBLE_BIT)) { field_bbox->corner.x = ref_bbox.corner.x; } else { field_bbox->corner.x = ref_bbox.orig.x; } field_bbox->orig.x = field_bbox->corner.x - real_width; break; } } } /*printf("field %d, x = %g\n", field, field_bbox->orig.x);*/ } /* * Then the y axis. */ if (y_dim != LF_DIM_LABEL) { if (y_attach == LF_ATTACH_PIXEL) { field_bbox->orig.y = (ZnPos) y_spec; field_bbox->corner.y = field_bbox->orig.y + real_height; } else { ref_field = y_spec; field_bbox->orig.y = field_bbox->corner.y = 0; if ((ref_field < 0) || (ref_field >= num_fields)) { ZnWarning ("Attached (y) to an inexistant field geometry"); } else { ComputeFieldAttachment(field_set, ref_field, &ref_bbox); switch (y_attach) { case LF_ATTACH_FWD: if (ISSET(field_set->fields[ref_field].flags, FIELD_VISIBLE_BIT)) { field_bbox->orig.y = ref_bbox.corner.y; } else { field_bbox->orig.y = ref_bbox.orig.y; } field_bbox->corner.y = field_bbox->orig.y + real_height; break; case LF_ATTACH_BWD: if (ISSET(field_set->fields[ref_field].flags, FIELD_VISIBLE_BIT)) { field_bbox->corner.y = ref_bbox.orig.y; } else { field_bbox->corner.y = ref_bbox.corner.y; } field_bbox->orig.y = field_bbox->corner.y - real_height; break; case LF_ATTACH_LEFT: field_bbox->orig.y = ref_bbox.orig.y; field_bbox->corner.y = field_bbox->orig.y + real_height; break; case LF_ATTACH_RIGHT: if (ISSET(field_set->fields[ref_field].flags, FIELD_VISIBLE_BIT)) { field_bbox->corner.y = ref_bbox.corner.y; } else { field_bbox->corner.y = ref_bbox.orig.y; } field_bbox->orig.y = field_bbox->corner.y - real_height; break; } } } /*printf("field %d, y = %g\n", field, field_bbox->orig.y);*/ } field_ptr->orig_x = (short) field_bbox->orig.x; field_ptr->orig_y = (short) field_bbox->orig.y; field_ptr->corner_x = (short) field_bbox->corner.x; field_ptr->corner_y = (short) field_bbox->corner.y; SET(field_ptr->flags, CACHE_OK); } /* ********************************************************************************** * * ClearFieldCache -- * Reset the geometric cache of all fields depending on a given field (or * of all fields if the field is < 0). Clear also the label bounding box * cache if some action has been taken on a field. * ********************************************************************************** */ void ClearFieldCache(FieldSet field_set, int field) { int i, num_fields; ZnBool clear_bbox; int x_spec, y_spec; char x_attach, y_attach, x_dim, y_dim; short width_spec, height_spec; if (field < 0) { for (i = 0; i < field_set->num_fields; i++) { CLEAR(field_set->fields[i].flags, CACHE_OK); } field_set->label_width = field_set->label_height = -1.0; return; } clear_bbox = False; if (!field_set->label_format) { return; } num_fields = LabelFormatNumFields(field_set->label_format); if (field >= num_fields) { return; } LabelFormatGetField(field_set->label_format, field, &x_attach, &y_attach, &x_dim, &y_dim, &x_spec, &y_spec, &width_spec, &height_spec); if ((x_dim != LF_DIM_PIXEL) || (y_dim != LF_DIM_PIXEL)) { CLEAR(field_set->fields[field].flags, CACHE_OK); } for (i = 0; i < num_fields; i++) { LabelFormatGetField(field_set->label_format, i, &x_attach, &y_attach, &x_dim, &y_dim, &x_spec, &y_spec, &width_spec, &height_spec); if ((x_attach == LF_ATTACH_PIXEL) && (y_attach == LF_ATTACH_PIXEL)) { continue; } if (x_attach != LF_ATTACH_PIXEL) { if ((x_spec == field) && ISSET(field_set->fields[i].flags, CACHE_OK)) { CLEAR(field_set->fields[i].flags, CACHE_OK); ClearFieldCache(field_set, i); clear_bbox = True; } } if (y_attach != LF_ATTACH_PIXEL) { if ((y_spec == field) && ISSET(field_set->fields[i].flags, CACHE_OK)) { CLEAR(field_set->fields[i].flags, CACHE_OK); ClearFieldCache(field_set, i); clear_bbox = True; } } } if (clear_bbox) { field_set->label_width = field_set->label_height = -1.0; } } /* ********************************************************************************** * * GetLabelBBox -- * ********************************************************************************** */ void GetLabelBBox(FieldSet field_set, ZnDim *w, ZnDim *h) { ZnBBox bbox, tmp_bbox; ZnLabelFormat lf; int i, num_fields; ZnDim clip_w, clip_h; if ((field_set->label_width >= 0.0) && (field_set->label_height >= 0.0)) { *w = field_set->label_width; *h = field_set->label_height; return; } lf = field_set->label_format; if (lf == NULL) { *w = *h = field_set->label_width = field_set->label_height = 0.0; return; } ResetBBox(&bbox); num_fields = LabelFormatNumFields(lf); for (i = 0; i < num_fields; i++) { ComputeFieldAttachment(field_set, i, &tmp_bbox); /*printf("field %d bbox %g %g %g %g\n", i, tmp_bbox.orig.x, tmp_bbox.orig.y, tmp_bbox.corner.x, tmp_bbox.corner.y);*/ AddBBoxToBBox(&bbox, &tmp_bbox); } field_set->label_width = bbox.corner.x; field_set->label_height = bbox.corner.y; /*printf("GetLabelBBox size before clipping; w = %g, h = %g\n", field_set->label_width, field_set->label_height);*/ if (LabelFormatGetClipBox(lf, &clip_w, &clip_h)) { if (clip_w < field_set->label_width) { field_set->label_width = clip_w; } if (clip_h < field_set->label_height) { field_set->label_height = clip_h; } } *w = field_set->label_width; *h = field_set->label_height; /*printf("GetLabelBBox returns computed size; w = %g, h = %g\n", *w, *h);*/ } /* ********************************************************************************** * * GetFieldBBox -- * Compute the location of the field described * by the field entry index in the item current LabelFormat. * ********************************************************************************** */ static void GetFieldBBox(FieldSet field_set, unsigned int index, ZnBBox *field_bbox) { if (field_set->label_format) { ComputeFieldAttachment(field_set, index, field_bbox); field_bbox->orig.x += field_set->label_pos.x; field_bbox->orig.y += field_set->label_pos.y; field_bbox->corner.x += field_set->label_pos.x; field_bbox->corner.y += field_set->label_pos.y; } else { ResetBBox(field_bbox); } } /* ********************************************************************************** * * ComputeFieldTextLocation -- * Compute the position of the text in a field. This is a position * that we can give to XDrawText. The position is deduced from the * field bounding box passed in bbox. * Return also the text bounding box. * ********************************************************************************** */ static void ComputeFieldTextLocation(Field field_ptr, ZnBBox *bbox, ZnPoint *pos, ZnBBox *text_bbox) { ZnDim w, h; Tk_FontMetrics fm; Tk_GetFontMetrics(field_ptr->font, &fm); w = ZnTextWidth(field_ptr->font, field_ptr->text, strlen(field_ptr->text)); h = fm.ascent + fm.descent; text_bbox->orig.y = (bbox->orig.y + bbox->corner.y - h) / 2; text_bbox->corner.y = text_bbox->orig.y + h; pos->y = text_bbox->orig.y + fm.ascent; switch (field_ptr->alignment) { case ZnJustifyLeft: text_bbox->orig.x = bbox->orig.x; break; case ZnJustifyRight: text_bbox->orig.x = bbox->corner.x - w - 1; break; default: text_bbox->orig.x = (bbox->orig.x + bbox->corner.x - w) / 2; break; } text_bbox->corner.x = text_bbox->orig.x + w; pos->x = text_bbox->orig.x; } /* ********************************************************************************** * * LeaderToLabel -- * Compute the segment part of segment that lies * outside the fields of item. * ********************************************************************************** */ static void LeaderToLabel(FieldSet field_set, ZnPoint *start, ZnPoint *end) { int b_num; ZnPoint delta, inf, sup; ZnPos xt=0, yu=0, yw=0, xv=0; Field field_ptr; int i; ZnBBox field_bbox; /* Intersection points : */ /* T |xt / delta_y U |x1 V |y1 W |yw / delta_x */ /* |y2 |yu / delta_x |xv / delta_y |x2 */ /* */ /* y = ax + b; */ /* a = delta_y / delta_x */ /* b = (y * delta_x - x * delta_y) / delta_x */ delta.x = start->x - end->x; delta.y = start->y - end->y; b_num = start->y*delta.x - start->x*delta.y; for (i = 0; i < LabelFormatNumFields(field_set->label_format); i++) { field_ptr = &field_set->fields[i]; /* * If the field is made invisible or has no graphics of * its own, don't clip. */ if (ISCLEAR(field_ptr->flags, FIELD_VISIBLE_BIT) || ((*field_ptr->text == 0) && ISCLEAR(field_ptr->flags, FILLED_BIT) && (field_ptr->border_edges == NO_BORDER) && (field_ptr->relief == RELIEF_FLAT) && (field_ptr->image == ZnUnspecifiedImage))) { continue; } /* * field_bbox is in absolute device coordinates. */ GetFieldBBox(field_set, i, &field_bbox); /* * Adjust leader on real text, not on field boundaries. This is * important when there are leading and trailing spaces. * The correct test here is really *field_ptr->text, an empty * text is represented by an empty string NOT by a NULL pointer. */ if (*field_ptr->text && ISCLEAR(field_ptr->flags, FILLED_BIT) && (field_ptr->border_edges == NO_BORDER) && (field_ptr->relief == RELIEF_FLAT) && (field_ptr->image == ZnUnspecifiedImage)) { ZnBBox text_bbox; ZnPoint text_pos; /* dummy */ int space_width; int scan_forw, scan_back; space_width = ZnTextWidth(field_ptr->font, " ", 1); ComputeFieldTextLocation(field_ptr, &field_bbox, &text_pos, &text_bbox); /* * Correct adjusments made by ComputeFieldTextLocation (Vincent Pomey). * * PLC: IMHO, this is to compensate for exotic fonts like 'symbolesATC'. * I am not planning to port this to Tk for two reasons: * 1/ Current positions are no longer implemented as characters * and 2/ Tk does not give access (easily) to lbearings and rbearings. * This patch has been contributed by Phidias team. I don't know the * problem it was meant to solve. * text_bbox.x -= field_ptr->font->per_char[field_ptr->text[0]].lbearing + 3; * text_bbox.width += field_ptr->font->per_char[field_ptr->text[0]].lbearing + 3; */ /* * Change bbox according to leading and trailing spaces. */ scan_forw = 0; while (field_ptr->text[scan_forw] == ' ') { /* leading spaces */ text_bbox.orig.x += space_width; scan_forw++; } /* * Empty text. */ if (field_ptr->text[scan_forw] == 0) { continue; } scan_back = strlen(field_ptr->text)-1; while ((field_ptr->text[scan_back] == ' ') && (scan_back > scan_forw)) { /* trailing spaces */ text_bbox.corner.x -= space_width; scan_back--; } field_bbox = text_bbox; } if (field_bbox.corner.x <= field_bbox.orig.x) { continue; } if ((start->x >= field_bbox.orig.x) && (start->x < field_bbox.corner.x) && (start->y >= field_bbox.orig.y) && (start->y < field_bbox.corner.y)) { end->x = start->x; end->y = start->y; } if (delta.x) { yu = (field_bbox.orig.x*delta.y + b_num) / delta.x; yw = (field_bbox.corner.x*delta.y + b_num) / delta.x; } if (delta.y) { xt = (field_bbox.corner.y*delta.x - b_num) / delta.y; xv = (field_bbox.orig.y*delta.x - b_num) / delta.y; } inf.x = MIN(start->x, end->x); sup.x = MAX(start->x, end->x); inf.y = MIN(start->y, end->y); sup.y = MAX(start->y, end->y); if (delta.x) { if ((yu >= field_bbox.orig.y) && (yu <= field_bbox.corner.y) && (field_bbox.orig.x >= inf.x) && (field_bbox.orig.x <= sup.x) && (yu >= inf.y) && (yu <= sup.y)) { end->x = field_bbox.orig.x; end->y = yu; inf.x = MIN(start->x, end->x); sup.x = MAX(start->x, end->x); inf.y = MIN(start->y, end->y); sup.y = MAX(start->y, end->y); } if ((yw >= field_bbox.orig.y) && (yw <= field_bbox.corner.y) && (field_bbox.corner.x >= inf.x) && (field_bbox.corner.x <= sup.x) && (yw >= inf.y) && (yw <= sup.y)) { end->x = field_bbox.corner.x; end->y = yw; inf.x = MIN(start->x, end->x); sup.x = MAX(start->x, end->x); inf.y = MIN(start->y, end->y); sup.y = MAX(start->y, end->y); } } if (delta.y) { if ((xt >= field_bbox.orig.x) && (xt <= field_bbox.corner.x) && (xt >= inf.x) && (xt <= sup.x) && (field_bbox.corner.y >= inf.y) && (field_bbox.corner.y <= sup.y)) { end->x = xt; end->y = field_bbox.corner.y; inf.x = MIN(start->x, end->x); sup.x = MAX(start->x, end->x); inf.y = MIN(start->y, end->y); sup.y = MAX(start->y, end->y); } if ((xv >= field_bbox.orig.x) && (xv <= field_bbox.corner.x) && (xv >= inf.x) && (xv <= sup.x) && (field_bbox.orig.y >= inf.y) && (field_bbox.orig.y <= sup.y)) { end->x = xv; end->y = field_bbox.orig.y; inf.x = MIN(start->x, end->x); sup.x = MAX(start->x, end->x); inf.y = MIN(start->y, end->y); sup.y = MAX(start->y, end->y); } } } } /* ********************************************************************************** * * InsertDependentItem -- * ********************************************************************************** */ static void InsertDependentItem(Item item) { GroupItem group = (GroupItem) item->parent; if (!group) { return; } if (!group->dependents) { group->dependents = ZnListNew(2, sizeof(Item)); } ZnListAdd(group->dependents, &item, ZnListTail); } /* ********************************************************************************** * * ExtractDependentItem -- * ********************************************************************************** */ static int CmpItem(void *item1, void *item2) { return (item1 == item2); } static void ExtractDependentItem(Item item) { GroupItem group = (GroupItem) item->parent; int index; if (!group || !group->dependents) { return; } index = ZnListDo(group->dependents, item, CmpItem); if (index >= 0) { ZnListDelete(group->dependents, index); if (ZnListSize(group->dependents) == 0) { ZnListFree(group->dependents); group->dependents = NULL; } } } /* ********************************************************************************** * * UpdateItemDependency -- * 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 */ InsertDependentItem(item); } else if (item->connected_item == ZN_NO_ITEM) { /* Remove a connection */ ExtractDependentItem(item); } else { /* Move at end to ensure that it will be updated after * the (new) item it depends upon. */ ExtractDependentItem(item); InsertDependentItem(item); } } /* ********************************************************************************** * * DisconnectDependentItems -- * * ********************************************************************************** */ static void DisconnectDependentItems(Item item) { Item current_item; GroupItem group = (GroupItem) item->parent; Item *deps; int num_deps, i; if (!group || !group->dependents) { return; } deps = (Item *) ZnListArray(group->dependents); num_deps = ZnListSize(group->dependents); for (i = num_deps-1; i >= 0; i--) { current_item = deps[i]; if (current_item->connected_item == item) { current_item->connected_item = ZN_NO_ITEM; ZnListDelete(group->dependents, i); Invalidate(current_item, ZN_COORDS_FLAG); } } if (ZnListSize(group->dependents) == 0) { ZnListFree(group->dependents); group->dependents = NULL; } } /* ********************************************************************************** * * InsertItem -- * * 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) { WidgetInfo *wi = item->wi; GroupItem group = (GroupItem) grp; if (!group) { group = (GroupItem) wi->top_group; } item->parent = (Item) group; if (mark_item && (mark_item->parent != (Item) group)) { mark_item = ZN_NO_ITEM; } /* * Empty list, add the first item. */ if (group->head == ZN_NO_ITEM) { group->head = item; group->tail = item; item->previous = item->next = ZN_NO_ITEM; return; } if (mark_item != ZN_NO_ITEM) { /* * Better leave here, mark_item will not * have the links set right. */ if (mark_item == item) { return; } /* * Force the priority to be the same as the reference * item; */ item->priority = mark_item->priority; } else { mark_item = group->head; while ((mark_item != ZN_NO_ITEM) && (mark_item->priority > item->priority)) { mark_item = mark_item->next; } before = True; } if (before && (mark_item != ZN_NO_ITEM)) { /* * Insert before mark. */ item->next = mark_item; item->previous = mark_item->previous; if (mark_item->previous == ZN_NO_ITEM) { group->head = item; } else { mark_item->previous->next = item; } mark_item->previous = item; } else { /* * Insert after mark either because 'before' is False * and mark_item valid or because the right place is at * the end of the list and mark_item is ZN_NO_ITEM. */ if (mark_item == ZN_NO_ITEM) { group->tail->next = item; item->previous = group->tail; group->tail = item; } else { item->previous = mark_item; item->next = mark_item->next; if (item->next == ZN_NO_ITEM) { group->tail = item; } else { item->next->previous = item; } mark_item->next = item; } } } /* ********************************************************************************** * * ExtractItem -- * ********************************************************************************** */ static void ExtractItem(Item item) { GroupItem group; if (!item->parent) { return; } group = (GroupItem) item->parent; if (item->previous != ZN_NO_ITEM) { item->previous->next = item->next; } else { group->head = item->next; } if (item->next != ZN_NO_ITEM) { item->next->previous = item->previous; } else { group->tail = item->previous; } item->previous = ZN_NO_ITEM; item->next = ZN_NO_ITEM; item->parent = NULL; } /* ********************************************************************************** * * UpdateItemPriority -- * 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; ExtractItem(item); InsertItem(item, parent, mark_item, before); Invalidate(item, ZN_DRAW_FLAG); SET(item->wi->events_flags, INTERNAL_NEED_REPICK); } /* ********************************************************************************** * * SetId, * FreeId -- * 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 -- * 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 -- * ********************************************************************************** */ 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 -- * ********************************************************************************** */ static void FreeTags(Item item) { if (!item->tags) { return; } ZnListFree(item->tags); item->tags = NULL; } /* ********************************************************************************** * * CreateItem -- * * 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. * ********************************************************************************** */ static Item CreateItem(WidgetInfo *wi, ItemClass item_class, int *argc, Tcl_Obj *CONST *args[]) { Item item; item = (Item) ZnMalloc(item_class->item_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; 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 -- * 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->item_size); memcpy(item, model, model->class->item_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 duplicate not shared resources */ item->class->Clone(item); SET(wi->events_flags, INTERNAL_NEED_REPICK); wi->num_items++; return item; } /* ********************************************************************************** * * ConfigureItem -- * ********************************************************************************** */ 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->has_fields && ISSET(flags, ZN_CLFC_FLAG)) { ClearFieldCache(item->class->GetFieldSet(item), -1); } } else if (item->class->has_fields) { FieldSet field_set = item->class->GetFieldSet(item); if (ConfigureField(field_set, 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. */ Damage(wi, &item->item_bounding_box); ZnNeedRedisplay(wi); } Invalidate(item, flags); return ZN_OK; } /* ********************************************************************************** * * QueryItem -- * ********************************************************************************** */ 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->has_fields) { FieldSet field_set = item->class->GetFieldSet(item); return QueryField(field_set, 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 -- * 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; } } /* ********************************************************************************** * * ResetTransformStack * InitTransformStack * FreeTransformStack * CurrentTransform * PushTransform * PopTransform -- * ********************************************************************************** */ static void ResetTransformStack(WidgetInfo *wi) { ZnListAssertSize(wi->transfo_stack, 1); wi->current_transfo = (ZnTransfo *) ZnListAt(wi->transfo_stack, 0); ZnTransfoSetIdentity(wi->current_transfo); } static void InitTransformStack(WidgetInfo *wi) { wi->transfo_stack = ZnListNew(8, sizeof(ZnTransfo)); ResetTransformStack(wi); } static void FreeTransformStack(WidgetInfo *wi) { ZnListFree(wi->transfo_stack); } static void PushTransform(WidgetInfo *wi, ZnTransfo *transfo, ZnBool compose_scale, ZnBool compose_rot) { ZnTransfo *next_t; int num_t; if (transfo == NULL) { return; } /* * 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; } static void PopTransform(WidgetInfo *wi) { /* * Restore the previous transform. */ ZnListDelete(wi->transfo_stack, ZnListTail); wi->current_transfo = (ZnTransfo *) ZnListAt(wi->transfo_stack, ZnListTail); } /* ********************************************************************************** * * ResetClipStack * InitClipStack * FreeClipStack * CurrentClip * PushClip * PopClip -- * ********************************************************************************** */ /* * 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; static void ResetClipStack(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; } static void InitClipStack(WidgetInfo *wi) { wi->clip_stack = ZnListNew(8, sizeof(ClipState)); ResetClipStack(wi); } static void FreeClipStack(WidgetInfo *wi) { ZnListFree(wi->clip_stack); } static ZnBool CurrentClip(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. */ static void PushClip(WidgetInfo *wi, ZnPoly *poly, ZnBool simple, ZnBool set_gc) { int i, j, num_c; int num_pts, max_num_pts; ZnPoint *p; ClipState *previous_clip=NULL; Region reg, reg_op, reg_to; XRectangle rect; XPoint *xpts; if (poly->num_contours == 0) { return; } max_num_pts = poly->contours[0].num_points; for (j = 0; j < poly->num_contours; j++) { num_pts = poly->contours[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_c = ZnListSize(wi->clip_stack); /* printf("PushClip: num clips %d\n", num_c);fflush(stdout);*/ if (num_c != 0) { previous_clip = (ClipState *) ZnListAt(wi->clip_stack, ZnListTail); } ZnListAssertSize(wi->clip_stack, num_c+1); wi->current_clip = (ClipState *) ZnListAt(wi->clip_stack, ZnListTail); wi->current_clip->simple = simple; /* * Compute the local region. */ if (simple) { rect.x = poly->contours[0].points[0].x; rect.y = poly->contours[0].points[0].y; rect.width = poly->contours[0].points[1].x - poly->contours[0].points[0].x; rect.height = poly->contours[0].points[1].y - poly->contours[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 { xpts = (XPoint *) ZnMalloc(max_num_pts * sizeof(XPoint)); reg = XCreateRegion(); for (j = 0; j < poly->num_contours; j++) { num_pts = poly->contours[j].num_points; p = poly->contours[j].points; for (i = 0; i < num_pts; i++, p++) { xpts[i].x = p->x; xpts[i].y = p->y; } reg_op = XPolygonRegion(xpts, num_pts, EvenOddRule); reg_to = XCreateRegion(); if (poly->holes[j]) { XSubtractRegion(reg, reg_op, reg_to); } else { XUnionRegion(reg, reg_op, reg_to); } XDestroyRegion(reg); XDestroyRegion(reg_op); reg = reg_to; } ZnFree(xpts); } /* * 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; /*printf("Clip box is : %d, %d, %d, %d\n", rect.x, rect.y, rect.width, rect.height);*/ /* * Set the clipping in the GC. */ if (set_gc) { XSetRegion(wi->dpy, wi->gc, wi->current_clip->region); } } static void PopClip(WidgetInfo *wi, ZnBool set_gc) { if (wi->current_clip == NULL) { return; } XDestroyRegion(wi->current_clip->region); ZnListDelete(wi->clip_stack, ZnListTail); if (ZnListSize(wi->clip_stack) != 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 (wi->current_clip) { XSetRegion(wi->dpy, wi->gc, wi->current_clip->region); } else { /*printf("resetting clip mask\n");*/ XSetClipMask(wi->dpy, wi->gc, None); } } /*printf("PopClip: num clips %d\n", ZnListSize(wi->clip_stack));fflush(stdout);*/ } /* ********************************************************************************** * * Invalidate -- * ********************************************************************************** */ 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);*/ Damage(item->wi, &item->item_bounding_box); ZnNeedRedisplay(item->wi); } } } /* ********************************************************************************** * * InvalidateItems -- * Invalidate the geometric state of all items of a group * belonging to a given class. * ********************************************************************************** */ static void InvalidateItems(Item group, ItemClass item_class) { Item item; if (group->class != ZnGroup) { return; } item = ((GroupItem) group)->head; while (item != ZN_NO_ITEM) { if (item->class == item_class) { Invalidate(item, ZN_COORDS_FLAG); } item = item->next; } } /* ********************************************************************************** * * ResetTransfo * SetTransfo * TranslateItem * ScaleItem * RotateItem -- * 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); } /* ********************************************************************************** * * RemoveItem -- * Extract an item from its context, includes updating graphic * state flags. * ********************************************************************************** */ static void RemoveItem(Item item) { WidgetInfo *wi = item->wi; GroupItem group; group = (GroupItem) item->parent; /* damage bounding boxes */ if (ISSET(item->flags, VISIBLE_BIT)) { Damage(wi, &item->item_bounding_box); ZnNeedRedisplay(wi); } /* * Tell that we need to repick */ if (item->class != ZnGroup) { SET(wi->events_flags, INTERNAL_NEED_REPICK); } if (group) { if (group->dependents) { /* Remove me from dependency list. */ ExtractDependentItem(item); /* Disconnect all dependents on me. */ DisconnectDependentItems(item); } /* * Remove me from item list. */ ExtractItem(item); /* * Remove me as a clip item. */ if (group->clip == item) { group->clip = ZN_NO_ITEM; Invalidate((Item) group, ZN_COORDS_FLAG); } } } /* ********************************************************************************** * * DestroyItem -- * ********************************************************************************** */ static void DestroyItem(Item item) { WidgetInfo *wi = item->wi; /* * Extract it from its group. */ RemoveItem(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 (wi->text_info.sel_item == item) { wi->text_info.sel_item = ZN_NO_ITEM; } if (wi->text_info.sel_item == item) { wi->text_info.focus_item = ZN_NO_ITEM; } /* * 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--; } /* ********************************************************************************** * * FieldImageChange -- * ********************************************************************************** */ static void FieldImageChange(ClientData client_data, int x, int y, int width, int height, int image_width, int image_height) { Field field = (Field) client_data; int i; ZnBBox bbox; if (field->image != ZnUnspecifiedImage) { i = (((char *) field) - ((char *) field->field_set->fields)) / sizeof(FieldStruct); GetFieldBBox(field->field_set, i, &bbox); Damage(field->field_set->wi, &bbox); ZnNeedRedisplay(field->field_set->wi); ClearFieldCache(field->field_set, i); } } /* ********************************************************************************** * * FieldTileChange -- * ********************************************************************************** */ static void FieldTileChange(ClientData client_data, int x, int y, int width, int height, int image_width, int image_height) { Field field = (Field) client_data; int i; ZnBBox bbox; if (field->tile != ZnUnspecifiedImage) { i = (((char *) field) - ((char *) field->field_set->fields)) / sizeof(FieldStruct); InvalidateImage(field->tile_name); GetFieldBBox(field->field_set, i, &bbox); Damage(field->field_set->wi, &bbox); ZnNeedRedisplay(field->field_set->wi); } } /* ********************************************************************************** * * InitFields -- * * Perform the init of each field in a FieldSet. The number of such * fields must have been inited before calling this fun. * ********************************************************************************** */ static void InitFields(FieldSet field_set) { WidgetInfo *wi = field_set->wi; Field field; unsigned int i, num_fields; /*printf("size of a field = %d\n", sizeof(FieldStruct));*/ if (field_set->num_fields < 0) { field_set->num_fields = 0; } if (!field_set->num_fields) { return; } num_fields = field_set->num_fields; field_set->fields = (Field) ZnMalloc(num_fields*sizeof(FieldStruct)); for (i = 0; i < num_fields; i++){ field = &field_set->fields[i]; field->field_set = field_set; field->color = ZnGetColorByValue(wi->win, wi->fore_color); field->back_color = ZnGetColorByValue(wi->win, wi->back_color); field->border_color = ZnGetColorByValue(wi->win, wi->fore_color); SET(field->flags, FIELD_VISIBLE_BIT); SET(field->flags, FIELD_SENSITIVE_BIT); CLEAR(field->flags, FILLED_BIT); CLEAR(field->flags, CACHE_OK); field->fill_pattern = ZnUnspecifiedPattern; field->text = ""; field->image = ZnUnspecifiedImage; field->image_name = ""; field->tile = ZnUnspecifiedImage; field->tile_name = ""; field->font = Tk_GetFont(wi->interp, wi->win, Tk_NameOfFont(wi->font)); field->border_edges = NO_BORDER; field->alignment = ZnJustifyLeft; field->auto_alignment.automatic = False; field->relief = RELIEF_FLAT; field->relief_thickness = 2; SET(field->flags, TEXT_ON_TOP_BIT); field->gradient = NULL; } field_set->label_pos.x = field_set->label_pos.y = 0.0; field_set->label_width = field_set->label_height = -1.0; } /* ********************************************************************************** * * CloneFields -- * ********************************************************************************** */ static void CloneFields(FieldSet field_set) { WidgetInfo *wi = field_set->wi; Field field, fields_ret; unsigned int i, num_fields; char *text; if (field_set->label_format) { field_set->label_format = LabelFormatDuplicate(field_set->label_format); } num_fields = field_set->num_fields; if (!num_fields) { return; } fields_ret = (Field) ZnMalloc(num_fields*sizeof(FieldStruct)); memcpy(fields_ret, field_set->fields, num_fields*sizeof(FieldStruct)); field_set->fields = fields_ret; for (i = 0; i < num_fields; i++) { field = &fields_ret[i]; if (field->gradient) { field->gradient = ZnGetColorGradientByValue(field->gradient); } if (strlen(field->image_name) != 0) { text = ZnMalloc((strlen(field->image_name) + 1) * sizeof(char)); strcpy(text, field->image_name); field->image_name = text; field->image = Tk_GetImage(wi->interp, wi->win, field->image_name, FieldImageChange, (ClientData) field); } if (strlen(field->tile_name) != 0) { text = ZnMalloc((strlen(field->tile_name) + 1) * sizeof(char)); strcpy(text, field->tile_name); field->tile_name = text; field->tile = Tk_GetImage(wi->interp, wi->win, field->tile_name, FieldTileChange, (ClientData) field); } if (field->fill_pattern != ZnUnspecifiedPattern) { field->fill_pattern = Tk_GetBitmap(wi->interp, wi->win, Tk_NameOfBitmap(wi->dpy, field->fill_pattern)); } field->font = Tk_GetFont(wi->interp, wi->win, Tk_NameOfFont(field->font)); field->color = ZnGetColorByValue(wi->win, field->color); field->back_color = ZnGetColorByValue(wi->win, field->back_color); field->border_color = ZnGetColorByValue(wi->win, field->border_color); if (strlen(field->text) != 0) { text = (char *) ZnMalloc((strlen(field->text) + 1) * sizeof(char)); strcpy(text, field->text); field->text = text; } } } /* ********************************************************************************** * * ConfigureField -- * ********************************************************************************** */ static int ConfigureField(FieldSet field_set, unsigned int field, int argc, Tcl_Obj *CONST argv[], int *flags) { int i; Field field_ptr; ZnBBox bbox; WidgetInfo *wi = field_set->wi; if (field >= field_set->num_fields) { return ZN_ERROR; } field_ptr = &field_set->fields[field]; if (ConfigureAttributes((char *) field_set, field, argc, argv, flags) == ZN_ERROR) { return ZN_ERROR; } if (ISSET(*flags, ZN_REPICK_FLAG)) { SET(wi->events_flags, INTERNAL_NEED_REPICK); } if (ISSET(*flags, ZN_CLFC_FLAG)) { ClearFieldCache(field_set, field); } if (field_ptr->gradient && (ISSET(*flags, ZN_BORDER_FLAG) || (field_ptr->relief == RELIEF_FLAT))) { ZnFreeColorGradient(field_ptr->gradient); field_ptr->gradient = NULL; } if ((field_ptr->relief != RELIEF_FLAT) && !field_ptr->gradient) { field_ptr->gradient = ZnGetReliefGradient(wi->interp, wi->win, ZnNameOfColor(field_ptr->back_color)); } if (ISSET(*flags, ZN_IMAGE_FLAG)) { Tk_Image image; if (strcmp(field_ptr->image_name, "") != 0) { image = Tk_GetImage(wi->interp, wi->win, field_ptr->image_name, FieldImageChange, (ClientData) field_ptr); if (image == NULL) { /* * The name will not be in sync with the image in * this case. */ return ZN_ERROR; } } else { image = ZnUnspecifiedImage; } if (field_ptr->image != ZnUnspecifiedImage) { Tk_FreeImage(field_ptr->image); } field_ptr->image = image; } if (ISSET(*flags, ZN_TILE_FLAG)) { Tk_Image tile; if (strcmp(field_ptr->tile_name, "") != 0) { tile = Tk_GetImage(wi->interp, wi->win, field_ptr->tile_name, FieldTileChange, (ClientData) field_ptr); if (tile == NULL) { return ZN_ERROR; } } else { tile = ZnUnspecifiedImage; } if (field_ptr->tile != ZnUnspecifiedImage) { Tk_FreeImage(field_ptr->tile); } field_ptr->tile = tile; } /* * This is done here to limit the redraw to the area of the * modified fields. */ if (ISCLEAR(*flags, ZN_COORDS_FLAG) && field_set->label_format && ISSET(*flags, ZN_DRAW_FLAG)) { for (i = 0; i < LabelFormatNumFields(field_set->label_format); i++) { if (i == field) { GetFieldBBox(field_set, i, &bbox); Damage(wi, &bbox); break; } } } return ZN_OK; } /* ********************************************************************************** * * QueryField -- * ********************************************************************************** */ static int QueryField(FieldSet field_set, unsigned int field, int argc, Tcl_Obj *CONST argv[]) { Field field_ptr; if (field >= field_set->num_fields) { return ZN_ERROR; } field_ptr = &field_set->fields[field]; if (QueryAttribute((char *) field_set, field, argv[0]) == ZN_ERROR) { return ZN_ERROR; } return ZN_OK; } /* ********************************************************************************** * * FreeFields -- * ********************************************************************************** */ static void FreeFields(FieldSet field_set) { WidgetInfo *wi = field_set->wi; unsigned int i, num_fields; Field field; if (field_set->label_format) { LabelFormatDelete(field_set->label_format); } num_fields = field_set->num_fields; for (i = 0; i < num_fields; i++) { field = &field_set->fields[i]; if (strlen(field->text) != 0) { ZnFree(field->text); } if (field->gradient) { ZnFreeColorGradient(field->gradient); } if (field->image != ZnUnspecifiedImage) { Tk_FreeImage(field->image); field->image = ZnUnspecifiedImage; } if (strlen(field->image_name) != 0) { ZnFree(field->image_name); } if (field->tile != ZnUnspecifiedImage) { Tk_FreeImage(field->tile); field->tile = ZnUnspecifiedImage; } if (strlen(field->tile_name) != 0) { ZnFree(field->tile_name); } if (field->fill_pattern != ZnUnspecifiedPattern) { Tk_FreeBitmap(wi->dpy, field->fill_pattern); field->fill_pattern = ZnUnspecifiedPattern; } /*printf("freeing a font\n");*/ Tk_FreeFont(field->font); ZnFreeColor(field->color); ZnFreeColor(field->back_color); ZnFreeColor(field->border_color); } if (num_fields) { ZnFree(field_set->fields); } } /* ********************************************************************************** * * ComputeFieldImageLocation -- * Compute the bounding box of the pixmap in a field. The position is * deduced from the field bounding box passed in bbox. * ********************************************************************************** */ static void ComputeFieldImageLocation(Field field_ptr, ZnBBox *bbox, ZnBBox *pm_bbox) { int width, height; Tk_SizeOfImage(field_ptr->image, &width, &height); pm_bbox->orig.y = (bbox->orig.y + bbox->corner.y - height) / 2; pm_bbox->corner.y = pm_bbox->orig.y + height; switch (field_ptr->alignment) { case ZnJustifyLeft: pm_bbox->orig.x = bbox->orig.x; break; case ZnJustifyRight: pm_bbox->orig.x = bbox->corner.x - width - 1; break; default: pm_bbox->orig.x = (bbox->orig.x + bbox->corner.x - width) / 2; break; } pm_bbox->corner.x = pm_bbox->orig.x + width; } /* ********************************************************************************** * * DrawFields -- * ********************************************************************************** */ static void DrawFields(FieldSet field_set) { WidgetInfo *wi = field_set->wi; int i; /* This one *NEED* to be an int */ int j, num; Field field_ptr; XGCValues values; ZnBBox label_clip_box, clip_bbox, bbox, *global_clip_box; ZnBBox text_bbox, clip_text_bbox; ZnPoint pts[2]; ZnPoly poly; XRectangle r; ZnPoint text_pos; ZnBBox pm_bbox, clip_pm_bbox; ZnBool restore = False; ZnDim lwidth, lheight; if (field_set->label_format && LabelFormatNumFields(field_set->label_format)) { bbox.orig = field_set->label_pos; GetLabelBBox(field_set, &lwidth, &lheight); bbox.corner.x = field_set->label_pos.x + lwidth; bbox.corner.y = field_set->label_pos.y + lheight; CurrentClip(wi, NULL, &global_clip_box, NULL); IntersectBBox(global_clip_box, &bbox, &label_clip_box); if (IsEmptyBBox(&label_clip_box)) { return; } num = LabelFormatNumFields(field_set->label_format); for (i = 0; i < num; i++) { field_ptr = &field_set->fields[i]; if (ISCLEAR(field_ptr->flags, FIELD_VISIBLE_BIT)) { continue; } GetFieldBBox(field_set, i, &bbox); IntersectBBox(&label_clip_box, &bbox, &clip_bbox); if (IsEmptyBBox(&clip_bbox)) { continue; } if (field_ptr->text) { ComputeFieldTextLocation(field_ptr, &bbox, &text_pos, &text_bbox); } else { ResetBBox(&text_bbox); } IntersectBBox(&clip_bbox, &text_bbox, &clip_text_bbox); if (field_ptr->image != ZnUnspecifiedImage) { ComputeFieldImageLocation(field_ptr, &bbox, &pm_bbox); } else { ResetBBox(&pm_bbox); } IntersectBBox(&clip_bbox, &pm_bbox, &clip_pm_bbox); /* we must call XSetClipRectangles only if it's required */ if (clip_bbox.orig.x != bbox.orig.x || clip_bbox.orig.y != bbox.orig.y || clip_bbox.corner.x != bbox.corner.x || clip_bbox.corner.y != bbox.corner.y || clip_text_bbox.orig.x != text_bbox.orig.x || clip_text_bbox.orig.y != text_bbox.orig.y || clip_text_bbox.corner.x != text_bbox.corner.x || clip_text_bbox.corner.y != text_bbox.corner.y || clip_pm_bbox.orig.x != pm_bbox.orig.x || clip_pm_bbox.orig.y != pm_bbox.orig.y || clip_pm_bbox.corner.x != pm_bbox.corner.x || clip_pm_bbox.corner.y != pm_bbox.corner.y) { /* we must clip. */ pts[0] = clip_bbox.orig; pts[1] = clip_bbox.corner; POLY_CONTOUR1(&poly, pts, 2); PushClip(wi, &poly, True, True); restore = True; } /* * Draw the background. */ if (ISSET(field_ptr->flags, FILLED_BIT)) { values.foreground = ZnPixel(field_ptr->back_color); if (field_ptr->tile != ZnUnspecifiedImage) { /* Fill tiled */ Pixmap pmap = GetImagePixmap(wi->win, field_ptr->tile_name, field_ptr->tile, NULL); values.fill_style = FillTiled; values.tile = pmap; values.ts_x_origin = (int) bbox.orig.x; values.ts_y_origin = (int) bbox.orig.y; XChangeGC(wi->dpy, wi->gc, GCTileStipXOrigin|GCTileStipYOrigin|GCFillStyle|GCTile, &values); } else if (field_ptr->fill_pattern != ZnUnspecifiedPattern) { /* Fill stippled */ values.fill_style = FillStippled; values.stipple = field_ptr->fill_pattern; values.ts_x_origin = (int) bbox.orig.x; values.ts_y_origin = (int) bbox.orig.y; XChangeGC(wi->dpy, wi->gc, GCTileStipXOrigin|GCTileStipYOrigin|GCFillStyle|GCStipple|GCForeground, &values); } else { /* Fill solid */ values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCForeground|GCFillStyle, &values); } BBox2XRect(&clip_bbox, &r); XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, r.x, r.y, r.width, r.height); } /* * Draw the image and the text, whichever is in back depends on * the value of text_on_top. */ for (j = 0; j < 2; j++) { if ((j == 0 && ISSET(field_ptr->flags, TEXT_ON_TOP_BIT)) || (j == 1 && ISCLEAR(field_ptr->flags, TEXT_ON_TOP_BIT))) { /* * Draw the image. */ if (field_ptr->image != ZnUnspecifiedImage) { Tk_RedrawImage(field_ptr->image, clip_pm_bbox.orig.x-pm_bbox.orig.x, clip_pm_bbox.orig.y-pm_bbox.orig.y, REAL_TO_INT(clip_pm_bbox.corner.x-clip_pm_bbox.orig.x), REAL_TO_INT(clip_pm_bbox.corner.y-clip_pm_bbox.orig.y), wi->draw_buffer, clip_pm_bbox.orig.x, clip_pm_bbox.orig.y); } } else { /* * Draw the text. */ if (field_ptr->text && strlen(field_ptr->text)) { values.foreground = ZnPixel(field_ptr->color); values.fill_style = FillSolid; values.font = ZnFontId(field_ptr->font); XChangeGC(wi->dpy, wi->gc, GCForeground | GCFillStyle | GCFont, &values); Tk_DrawChars(wi->dpy, wi->draw_buffer, wi->gc, field_ptr->font, field_ptr->text, strlen(field_ptr->text), text_pos.x, text_pos.y); } } } BBox2XRect(&bbox, &r); /* * Draw the border relief. */ if ((field_ptr->relief != RELIEF_FLAT) && (field_ptr->relief_thickness > 1)) { DrawRectangleRelief(wi, field_ptr->relief, field_ptr->gradient, &r, (unsigned int) field_ptr->relief_thickness); } /* * Draw the border line. */ if (field_ptr->border_edges != NO_BORDER) { values.foreground = ZnPixel(field_ptr->border_color); values.line_width = 0; values.line_style = LineSolid; values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCForeground | GCLineWidth | GCLineStyle | GCFillStyle, &values); if (field_ptr->border_edges & LEFT_BORDER) { XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, r.x, r.y, r.x, r.y + r.height - 1); } if (field_ptr->border_edges & RIGHT_BORDER) { XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, r.x + r.width - 1, r.y, r.x + r.width - 1, r.y + r.height - 1); } if (field_ptr->border_edges & TOP_BORDER) { XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, r.x, r.y, r.x + r.width - 1, r.y); } if (field_ptr->border_edges & BOTTOM_BORDER) { XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, r.x, r.y + r.height - 1, r.x + r.width - 1, r.y + r.height - 1); } if (field_ptr->border_edges & OBLIQUE) { XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, r.x, r.y, r.x + r.width - 1, r.y + r.height - 1); } if (field_ptr->border_edges & COUNTER_OBLIQUE) { XDrawLine(wi->dpy, wi->draw_buffer, wi->gc, r.x, r.y + r.height - 1, r.x + r.width - 1, r.y); } } if (restore) { /* Restore the previous clip. */ PopClip(wi, True); restore = False; } } } } /* ********************************************************************************** * * RenderFields -- * ********************************************************************************** */ static void RenderFields(FieldSet field_set) { /* WidgetInfo *wi = field_set->wi;*/ } /* ********************************************************************************** * * IsFieldsSensitive -- * ********************************************************************************** */ static ZnBool IsFieldSensitive(FieldSet field_set, int part) { if ((part >= 0) && (part < field_set->num_fields)) { return ISSET(field_set->fields[part].flags, FIELD_SENSITIVE_BIT); } else { return False; } } /* ********************************************************************************** * * FieldsPick -- * Return the first field that contains . * A field is selected if is over a non transparent area. * Such areas are : a solid filled background, a text, an icon. * This does *NOT* do with *GLOBAL* visible and sensitive. * But we need to take into account local field visible and * sensitive as they modifiy local transparency. Local means * within a single item. * ********************************************************************************** */ static double FieldsPick(FieldSet field_set, ZnPoint *p, int *part) { Field field_ptr; ZnBBox bbox; int i, best_field = 0; double new_dist, dist = 1e40; if (field_set->label_format) { for (i = LabelFormatNumFields(field_set->label_format)-1; i >= 0; i--) { field_ptr = &field_set->fields[i]; if (ISCLEAR(field_ptr->flags, FIELD_VISIBLE_BIT) && ISCLEAR(field_ptr->flags, FIELD_SENSITIVE_BIT)) { continue; } GetFieldBBox(field_set, i, &bbox); new_dist = RectangleToPointDist(&bbox, p); if (new_dist < dist) { dist = new_dist; best_field = i; } if (dist <= 0.0) { dist = 0.0; break; } } } *part = best_field; return dist; } /* ********************************************************************************** * * FieldsToArea -- * Return -1 if no field is in the given area, 1 if they are * all in it or 0 if there is some overlap. The function consider * only fields that are either sensible or visible. * ********************************************************************************** */ static int FieldsToArea(FieldSet field_set, ZnBBox *area) { Field field_ptr; ZnBBox bbox; int inside = -1; int i; ZnBool first_done = False; for (i = LabelFormatNumFields(field_set->label_format)-1; i >= 0; i--) { field_ptr = &field_set->fields[i]; if (ISCLEAR(field_ptr->flags, FIELD_VISIBLE_BIT) && ISCLEAR(field_ptr->flags, FIELD_SENSITIVE_BIT)) { continue; } GetFieldBBox(field_set, i, &bbox); if (!first_done) { first_done = True; inside = BBoxInBBox(&bbox, area); if (inside == 0) { return 0; } } else { if (BBoxInBBox(&bbox, area) != inside) { return 0; } } } return inside; } /* ********************************************************************************** * * SetFieldsAutoAlign -- * ********************************************************************************** */ static void SetFieldsAutoAlign(Item item, int alignment) { int i; FieldSet field_set; Field field; if (!item->class->has_fields) { return; } if ((alignment >= AA_LEFT) && (alignment <= AA_RIGHT)) { field_set = item->class->GetFieldSet(item); for (i = 0; i < field_set->num_fields; i++) { field = &field_set->fields[i]; if (field->auto_alignment.automatic) { field->alignment = field->auto_alignment.align[alignment]; } } } } static void Damage(WidgetInfo *wi, ZnBBox *damage) { if ((damage == NULL) || IsEmptyBBox(damage)) { return; } /*printf("damaging area: %g %g %g %g\n", damage->orig.x, damage->orig.y, damage->corner.x, damage->corner.y);*/ if (IsEmptyBBox(&wi->damaged_area)) { wi->damaged_area.orig.x = damage->orig.x; wi->damaged_area.orig.y = damage->orig.y; wi->damaged_area.corner.x = damage->corner.x; wi->damaged_area.corner.y = damage->corner.y; } else { wi->damaged_area.orig.x = MIN(wi->damaged_area.orig.x, damage->orig.x); wi->damaged_area.orig.y = MIN(wi->damaged_area.orig.y, damage->orig.y); wi->damaged_area.corner.x = MAX(wi->damaged_area.corner.x, damage->corner.x); wi->damaged_area.corner.y = MAX(wi->damaged_area.corner.y, damage->corner.y); } if (wi->damaged_area.orig.x < 0) { wi->damaged_area.orig.x = 0; } if (wi->damaged_area.orig.y < 0) { wi->damaged_area.orig.y = 0; } if (wi->damaged_area.corner.x > wi->width) { wi->damaged_area.corner.x = wi->width; } if (wi->damaged_area.corner.y > wi->height) { wi->damaged_area.corner.y = wi->height; } /*printf("damaged area: %g %g %g %g\n", wi->damaged_area.orig.x, wi->damaged_area.orig.y, wi->damaged_area.corner.x, wi->damaged_area.corner.y);*/ } static void Update(WidgetInfo *wi) { /* * Give the overlap manager a chance to do its work. */ #ifdef OM if ((wi->om_group != ZN_NO_ITEM) && ((GroupItem) wi->om_group)->call_om) { ZnPoint scale={1.0,1.0}; if (wi->om_group->transfo) { ZnTransfoDecompose(wi->om_group->transfo, &scale, NULL, NULL, NULL); } OmProcessOverlap((void *) wi, wi->width, wi->height, scale.x); ((GroupItem) wi->om_group)->call_om = False; } #endif if (ISSET(wi->top_group->inv_flags, ZN_COORDS_FLAG) || ISSET(wi->top_group->inv_flags, ZN_TRANSFO_FLAG)) { wi->top_group->class->ComputeCoordinates(wi->top_group, False); } } #ifdef SHM static void EnsureRGBBuf(WidgetInfo *wi, int r, int g, int b) { unsigned char *ptr; int y; ptr = wi->buf.buf; for (y = wi->buf.oy; y < wi->buf.cy; y++) { art_rgb_fill_run(ptr, r, g, b, wi->buf.cx - wi->buf.ox); ptr += wi->buf.rowstride; } } static void RenderSVP(WidgetInfo *wi, ArtSVP *svp, int r, int g, int b, int alpha, int tile_x, int tile_y, ArtPixBuf *tile) { if (tile) { tile_svp_alpha(svp, wi->buf.ox, wi->buf.oy, wi->buf.cx, wi->buf.cy, tile_x, tile_y, tile, alpha, wi->buf.buf, wi->buf.rowstride); } else { art_rgb_svp_alpha(svp, wi->buf.ox, wi->buf.oy, wi->buf.cx, wi->buf.cy, ((r & 0xff00) << 16) | ((g & 0xff00) << 8) | (b & 0xff00) | (alpha & 0xff), wi->buf.buf, wi->buf.rowstride, NULL); } } /* * Working only for 16 bits displays with 5r6g5b mask. */ static void RGBToXImage(WidgetInfo *wi, int x0, int y0, int width, int height) { int x, y; int rowstride; art_u8 *obuf, *obptr; int bpl; art_u8 *bptr, *bp2; art_u8 r, g, b; /*printf("RGBToXImage: x0 %d, y0 %d, width %d, height %d, wwidth %d, wheight %d\n", x0, y0, width, height, wi->width, wi->height);*/ rowstride = wi->buf.rowstride; bptr = wi->buf.buf/* + y0 * rowstride + x0 * 3*/; bpl = wi->draw_buffer_im->bytes_per_line; obuf = ((art_u8 *) wi->draw_buffer_im->data) + y0 * bpl + x0 * 2; for (y = 0; y < height; y++) { bp2 = bptr; obptr = obuf; if (((unsigned long) obuf | (unsigned long) bp2) & 3) { for (x = 0; x < width; x++) { r = *bp2++; g = *bp2++; b = *bp2++; ((art_u16 *)obptr)[0] = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3); obptr += 2; } } else { for (x = 0; x < width - 3; x += 4) { art_u32 r1b0g0r0; art_u32 g2r2b1g1; art_u32 b3g3r3b2; r1b0g0r0 = ((art_u32 *)bp2)[0]; g2r2b1g1 = ((art_u32 *)bp2)[1]; b3g3r3b2 = ((art_u32 *)bp2)[2]; ((art_u32 *)obptr)[0] = ((r1b0g0r0 & 0xf8) << 8) | ((r1b0g0r0 & 0xfc00) >> 5) | ((r1b0g0r0 & 0xf80000) >> 19) | (r1b0g0r0 & 0xf8000000) | ((g2r2b1g1 & 0xfc) << 19) | ((g2r2b1g1 & 0xf800) << 5); ((art_u32 *)obptr)[1] = ((g2r2b1g1 & 0xf80000) >> 8) | ((g2r2b1g1 & 0xfc000000) >> 21) | ((b3g3r3b2 & 0xf8) >> 3) | ((b3g3r3b2 & 0xf800) << 16) | ((b3g3r3b2 & 0xfc0000) << 3) | ((b3g3r3b2 & 0xf8000000) >> 11); bp2 += 12; obptr += 8; } for (; x < width; x++) { r = *bp2++; g = *bp2++; b = *bp2++; ((art_u16 *)obptr)[0] = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3); obptr += 2; } } bptr += rowstride; obuf += bpl; } } #endif static void Repair(WidgetInfo *wi) { XGCValues values; XRectangle r; ZnPoint pts[2]; ZnPoly poly; /* To be done only if we are realized and there is something to update. */ if (wi->realized && !IsEmptyBBox(&wi->damaged_area)) { /* Set the whole damaged area as clip rect. */ wi->damaged_area.orig.x = r.x = REAL_TO_INT(wi->damaged_area.orig.x); wi->damaged_area.orig.y = r.y = REAL_TO_INT(wi->damaged_area.orig.y); wi->damaged_area.corner.x = REAL_TO_INT(wi->damaged_area.corner.x); wi->damaged_area.corner.y = REAL_TO_INT(wi->damaged_area.corner.y); r.width = wi->damaged_area.corner.x - wi->damaged_area.orig.x; r.height = wi->damaged_area.corner.y - wi->damaged_area.orig.y; pts[0] = wi->damaged_area.orig; pts[1] = wi->damaged_area.corner; POLY_CONTOUR1(&poly, pts, 2); PushClip(wi, &poly, True, True); #ifdef PERFOS XStartChrono(draw_time, wi->dpy, wi->draw_buffer); /*StartChrono(int_draw_time);*/ #endif if (wi->local_render) { /* if (wi->tile == ZnUnspecifiedImage) { * } * else { * }*/ wi->buf.ox = (int) wi->damaged_area.orig.x; wi->buf.oy = (int) wi->damaged_area.orig.y; wi->buf.cx = (int) wi->damaged_area.corner.x; wi->buf.cy = (int) wi->damaged_area.corner.y; EnsureRGBBuf(wi, wi->back_color->red >> 8, wi->back_color->green >> 8, wi->back_color->blue >> 8); wi->top_group->class->Render(wi->top_group); RGBToXImage(wi, r.x, r.y, r.width, r.height); } else { /* Fill the background of the double buffer pixmap. */ if (wi->tile == ZnUnspecifiedImage) { values.foreground = ZnPixel(wi->back_color); values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCForeground, &values); } else { values.fill_style = FillTiled; values.tile = GetImagePixmap(wi->win, wi->tile_name, wi->tile, NULL); values.ts_x_origin = values.ts_y_origin = 0; XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCTile|GCTileStipXOrigin|GCTileStipYOrigin, &values); } /*printf("Repair : filling rectangle: %d %d %d %d\n", r.x, r.y, r.width, r.height);*/ XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, r.x, r.y, r.width, r.height); /* Draw the items */ /*printf("Drawing\n");*/ wi->top_group->class->Draw(wi->top_group); } #ifdef PERFOS /*StopChrono(int_draw_time);*/ XStopChrono(draw_time, wi->dpy, wi->draw_buffer); #endif PopClip(wi, True); } } /* ********************************************************************************** * * Exported functions structs -- * ********************************************************************************** */ struct _ITEM_P ITEM_P = { GlobalModuleInit, CreateItem, AddItemClass, LookupItemClass, ItemClassList, Damage, Repair, Update, ConfigureAttributes, QueryAttribute, InitFields, CloneFields, FreeFields, DrawFields, RenderFields, FieldsToArea, IsFieldSensitive, FieldsPick, LeaderToLabel, GetLabelBBox, GetFieldBBox, InitTransformStack, FreeTransformStack, ResetTransformStack, PushTransform, PopTransform, InitClipStack, FreeClipStack, ResetClipStack, PushClip, PopClip, CurrentClip, RenderSVP }; struct _ITEM ITEM = { CloneItem, DestroyItem, ConfigureItem, QueryItem, AttributesInfo, SetFieldsAutoAlign, InsertItem, UpdateItemPriority, InsertDependentItem, UpdateItemDependency, RemoveItem, SetId, FreeId, AddTag, RemoveTag, FreeTags, ResetTransfo, SetTransfo, TranslateItem, ScaleItem, RotateItem, Invalidate, InvalidateItems, GetItemTransform };