/* * Group.c -- Implementation of Group item. * * Authors : Patrick Lecoanet. * Creation date : Wed Jun 23 10:09:20 1999 * * $Id$ */ /* * Copyright (c) 1999 - 1999 CENA, Patrick Lecoanet -- * * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this code; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ #include "Types.h" #include "WidgetInfo.h" #include "Item.h" #include "Geo.h" #include "tkZinc.h" #include #include static const char rcsid[] = "$Id$"; static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $"; /* ********************************************************************************** * * Specific Group item record * ********************************************************************************** */ static ZnAttrConfig group_attrs[] = { { ZN_CONFIG_BOOL, "-atomic", NULL, Tk_Offset(GroupItemStruct, header.flags), ATOMIC_BIT, ZN_REPICK_FLAG, False }, { ZN_CONFIG_ITEM, "-clip", NULL, Tk_Offset(GroupItemStruct, clip), 0, ZN_COORDS_FLAG|ZN_ITEM_FLAG, False }, { ZN_CONFIG_BOOL, "-composerotation", NULL, Tk_Offset(GroupItemStruct, header.flags), COMPOSE_ROTATION_BIT, ZN_TRANSFO_FLAG, False }, { ZN_CONFIG_BOOL, "-composescale", NULL, Tk_Offset(GroupItemStruct, header.flags), COMPOSE_SCALE_BIT, ZN_TRANSFO_FLAG, False }, { ZN_CONFIG_PRI, "-priority", NULL, Tk_Offset(GroupItemStruct, header.priority), 0, ZN_DRAW_FLAG|ZN_REPICK_FLAG, False }, { ZN_CONFIG_BOOL, "-sensitive", NULL, Tk_Offset(GroupItemStruct, header.flags), SENSITIVE_BIT, ZN_REPICK_FLAG, False }, { ZN_CONFIG_TAGS, "-tags", NULL, Tk_Offset(GroupItemStruct, header.tags), 0, 0, False }, { ZN_CONFIG_BOOL, "-visible", NULL, Tk_Offset(GroupItemStruct, header.flags), VISIBLE_BIT, ZN_DRAW_FLAG|ZN_REPICK_FLAG|ZN_VIS_FLAG, False }, { ZN_CONFIG_END, NULL, NULL, 0, 0, 0 } }; /* ********************************************************************************** * * Init -- * ********************************************************************************** */ static int Init(Item item, int *argc, Tcl_Obj *CONST *args[]) { GroupItem group = (GroupItem) item; group->head = ZN_NO_ITEM; group->tail = ZN_NO_ITEM; group->clip = ZN_NO_ITEM; group->dependents = NULL; SET(item->flags, VISIBLE_BIT); SET(item->flags, SENSITIVE_BIT); SET(item->flags, COMPOSE_ROTATION_BIT); SET(item->flags, COMPOSE_SCALE_BIT); CLEAR(item->flags, ATOMIC_BIT); item->priority = DEFAULT_GROUP_PRIORITY; #ifdef OM group->call_om = False; #endif return ZN_OK; } /* ********************************************************************************** * * Clone -- * ********************************************************************************** */ static void Clone(Item item) { GroupItem group = (GroupItem) item; ZnList dependents; Item connected, current_item, new_item; Item *items; Tcl_HashTable mapping; Tcl_HashEntry *entry; int new, num_items, i; current_item = group->tail; group->head = group->tail = ZN_NO_ITEM; #ifdef OM group->call_om = False; #endif dependents = group->dependents; if (dependents) { Tcl_InitHashTable(&mapping, TCL_ONE_WORD_KEYS); } /* * First clone all the children, and build a mapping * table if there is some attachments to relink. */ while (current_item != ZN_NO_ITEM) { connected = current_item->connected_item; new_item = ITEM.CloneItem(current_item); new_item->connected_item = connected; ITEM.InsertItem(new_item, item, ZN_NO_ITEM, True); if (dependents) { entry = Tcl_CreateHashEntry(&mapping, (char *) current_item, &new); Tcl_SetHashValue(entry, (ClientData) new_item); } if (current_item == group->clip) { group->clip = new_item; } current_item = current_item->previous; } /* * Then rebuild the dependency list with * the new items. */ if (dependents) { /*printf("rebuilding dependents\n");*/ group->dependents = NULL; items = (Item *) ZnListArray(dependents); num_items = ZnListSize(dependents); for (i = 0; i < num_items; i++, items++) { entry = Tcl_FindHashEntry(&mapping, (char *) *items); if (entry == NULL) { ZnWarning("Can't find item correspondance in Group Clone"); abort(); } else { current_item = (Item) Tcl_GetHashValue(entry); } entry = Tcl_FindHashEntry(&mapping, (char *) current_item->connected_item); if (entry == NULL) { ZnWarning("Can't found item correspondance in Group Clone"); abort(); } else { /*printf("item %d correspond to ", current_item->connected_item->id);*/ current_item->connected_item = (Item) Tcl_GetHashValue(entry); /*printf("%d\n", current_item->connected_item->id);*/ ITEM.InsertDependentItem(current_item); } } Tcl_DeleteHashTable(&mapping); } } /* ********************************************************************************** * * Destroy -- * ********************************************************************************** */ static void Destroy(Item item) { GroupItem group = (GroupItem) item; Item current_item, next_item; current_item = group->head; while (current_item != ZN_NO_ITEM) { next_item = current_item->next; ITEM.DestroyItem(current_item); current_item = next_item; } if (group->dependents) { ZnListFree(group->dependents); } } /* ********************************************************************************** * * SetXShape -- * ********************************************************************************** */ static void SetXShape(Item grp) { #ifdef SHAPE WidgetInfo *wi = grp->wi; Item clip = ((GroupItem) grp)->clip; int i, j, num_pts, max_num_pts; ZnPos min_x, min_y, max_x, max_y; ZnPoly poly; ZnPoint *p; ZnBool simple; ZnDim width, height; XPoint *xpts, *xp; Region reg; if (!wi->has_x_shape) { return; } if ((clip == ZN_NO_ITEM) || !wi->reshape) { /* * Reset both clip just to be sure (the application can have * changed wi->full_reshape while resetting wi->reshape). */ XShapeCombineMask(wi->dpy, ZnWindowId(wi->win), ShapeBounding, 0, 0, None, ShapeSet); XShapeCombineMask(wi->dpy, wi->real_top, ShapeBounding, 0, 0, None, ShapeSet); } else { /* * Get the clip shape. */ simple = clip->class->GetClipVertices(clip, &poly); if (simple || (poly.num_contours == 0)) { /* * Nothing to do: after normalisation the rectangular shape will * fit exactly the window. We may test here if a shape is currently * active and reset the mask only in this case (need a flag in wi). */ XShapeCombineMask(wi->dpy, ZnWindowId(wi->win), ShapeBounding, 0, 0, None, ShapeSet); XShapeCombineMask(wi->dpy, wi->real_top, ShapeBounding, 0, 0, None, ShapeSet); } else { /* * First make the vertices start at zero. */ max_x = min_x = poly.contours[0].points[0].x; max_y = min_y = poly.contours[0].points[0].y; max_num_pts = poly.contours[0].num_points; for (j = 0; j < poly.num_contours; j++) { p = poly.contours[j].points; num_pts = poly.contours[j].num_points; if (num_pts > max_num_pts) { max_num_pts = num_pts; } for (i = 0; i < num_pts; p++, i++) { if (p->x < min_x) { min_x = p->x; } if (p->y < min_y) { min_y = p->y; } if (p->x > max_x) { max_x = p->x; } if (p->y > max_y) { max_y = p->y; } } } for (j = 0; j < poly.num_contours; j++) { p = poly.contours[j].points; num_pts = poly.contours[j].num_points; for (i = 0; i < num_pts; i++, p++) { p->x -= min_x; p->y -= min_y; } } max_x -= min_x; max_y -= min_y; /* * Now normalize the shape and map it to the window size. */ width = wi->width + 2*wi->inset; height = wi->height + 2*wi->inset; xpts = (XPoint *) ZnMalloc(max_num_pts * sizeof(XPoint)); XShapeCombineMask(wi->dpy, wi->full_reshape?ZnWindowId(wi->win):wi->real_top, ShapeBounding, 0, 0, None, ShapeSet); XShapeCombineRegion(wi->dpy, wi->full_reshape?wi->real_top:ZnWindowId(wi->win), ShapeBounding, 0, 0, None, ShapeSet); for (j = 0; j < poly.num_contours; j++) { p = poly.contours[j].points; num_pts = poly.contours[j].num_points; for (xp = xpts, i = 0; i < num_pts; i++, p++, xp++) { xp->x = (short) (p->x * width / max_x); xp->y = (short) (p->y * height / max_y); } /* * Translate it in a region and apply this region to the window. */ reg = XPolygonRegion(xpts, num_pts, EvenOddRule); XShapeCombineRegion(wi->dpy, wi->full_reshape?wi->real_top:ZnWindowId(wi->win), ShapeBounding, 0, 0, reg, poly.holes[j]?ShapeSubtract:ShapeUnion); XDestroyRegion(reg); } ZnFree(xpts); } } #endif } /* ********************************************************************************** * * Configure -- * ********************************************************************************** */ static int Configure(Item item, int argc, Tcl_Obj *CONST argv[], int *flags) { GroupItem group = (GroupItem) item; WidgetInfo *wi = item->wi; if (ITEM_P.ConfigureAttributes((char *) item, -1, argc, argv, flags) == ZN_ERROR) { return ZN_ERROR; } /* * If the clip item changed, check if it is a legal * item type that is inside this group. */ if (ISSET(*flags, ZN_ITEM_FLAG)) { if (group->clip && (!group->clip->class->GetClipVertices || (group->clip->parent != item))) { group->clip = ZN_NO_ITEM; Tcl_AppendResult(wi->interp, " clip item must be a child of the group", NULL); return ZN_ERROR; } if (!group->clip && (item == wi->top_group)) { SetXShape(item); } } return ZN_OK; } /* ********************************************************************************** * * Query -- * ********************************************************************************** */ static int Query(Item item, int argc, Tcl_Obj *CONST argv[]) { if (ITEM_P.QueryAttribute((char *) item, -1, argv[0]) == ZN_ERROR) { return ZN_ERROR; } return ZN_OK; } /* ********************************************************************************** * * PushClip -- * Save the current clip shape and current clipbox if needed. * Intersect the previous shape and the local to obtain the * new current shape. Use this shape to compute the current * clipbox and if set_gc is True compute the current region. * ********************************************************************************** */ static void PushClip(GroupItem group, ZnBool set_gc) { WidgetInfo *wi = ((Item) group)->wi; ZnPoly poly; ZnBool simple; if ((group->clip != ZN_NO_ITEM) && ((((Item) group) != wi->top_group) || !wi->reshape)) { simple = group->clip->class->GetClipVertices(group->clip, &poly); /*printf("Group: PushClip group %d\n", ((Item) group)->id);*/ ITEM_P.PushClip(wi, &poly, simple, set_gc); } } /* ********************************************************************************** * * PopClip -- * Re-install the previous clip shape if any (stack can be empty). * ********************************************************************************** */ static void PopClip(GroupItem group, ZnBool set_gc) { WidgetInfo *wi = ((Item) group)->wi; if ((group->clip != ZN_NO_ITEM) && ((((Item) group) != wi->top_group) || !wi->reshape)) { /*printf("Group: PopClip group %d\n", ((Item) group)->id);*/ ITEM_P.PopClip(wi, set_gc); } } /* ********************************************************************************** * * PushTransform -- * Save the current transform then concatenate the item transform to * form the new current transform. * ********************************************************************************** */ static void PushTransform(Item item) { if (!item->transfo && ISSET(item->flags, COMPOSE_SCALE_BIT) && ISSET(item->flags, COMPOSE_ROTATION_BIT)) { return; } ITEM_P.PushTransform(item->wi, item->transfo, ISSET(item->flags, COMPOSE_SCALE_BIT), ISSET(item->flags, COMPOSE_ROTATION_BIT)); /*printf("Pushing transfo for item: %d\n;", item->id); ZnPrintTransfo(wi->current_transfo);*/ } /* ********************************************************************************** * * PopTransform -- * Restore the previously saved transform from the stack. * ********************************************************************************** */ static void PopTransform(Item item) { if (!item->transfo && ISSET(item->flags, COMPOSE_SCALE_BIT) && ISSET(item->flags, COMPOSE_ROTATION_BIT)) { return; } ITEM_P.PopTransform(item->wi); /*printf("Popping transfo for item: %d\n", item->id); ZnPrintTransfo(wi->current_transfo);*/ } /* ********************************************************************************** * * ComputeCoordinates -- * Compute the geometrical elements of a group. First of all save the current * transform and combine it with the item transform. Then call the item * ComputeCoordinates method. * For regular child items (not groups) some of the code of the item * itself is factored out in CallRegularCC. * ********************************************************************************** */ static void CallRegularCC(Item item) { WidgetInfo *wi = item->wi; ZnBBox *clip_box; /* * Do some generic pre-work in behalf of the (regular) children. */ if (ISSET(item->flags, VISIBLE_BIT)) { ITEM_P.Damage(wi, &item->item_bounding_box); } PushTransform(item); /*printf("calling cc on regular item %d\n", item->id);*/ /*ZnPrintTransfo(wi->current_transfo);*/ item->class->ComputeCoordinates(item, False); /* * If a current clipbox exists adjust the item * bounding box accordingly. When computing coordinates * the damaged area is not pushed onto the clipstack, * the following predicate is thus valid for testing * a clipbox. */ if (ITEM_P.CurrentClip(wi, NULL, &clip_box, NULL)) { ZnBBox inter; IntersectBBox(&item->item_bounding_box, clip_box, &inter); item->item_bounding_box = inter; } /* * Do some generic post-work in behalf of the (regular) children. */ if (ISSET(item->inv_flags, ZN_REPICK_FLAG)) { SET(wi->events_flags, INTERNAL_NEED_REPICK); } if (ISSET(item->inv_flags, ZN_COORDS_FLAG) && (ISSET(item->flags, SENSITIVE_BIT) || ISSET(item->flags, VISIBLE_BIT))) { SET(wi->events_flags, INTERNAL_NEED_REPICK); } /* * Damage if the item is visible or if it is * a group clipper. */ if (ISSET(item->flags, VISIBLE_BIT) || (item == ((GroupItem) item->parent)->clip)) { ITEM_P.Damage(wi, &item->item_bounding_box); } PopTransform(item); item->inv_flags = 0; /*printf("Done cc on regular item %d\n", item->id);*/ } static void ComputeCoordinates(Item item, ZnBool force) { GroupItem group = (GroupItem) item; Item current_item; Item *deps; int num_deps, i; PushTransform(item); /* printf("Group.c\n"); ZnPrintTransfo(item->wi->current_transfo); printf("\n");*/ force |= ISSET(item->inv_flags, ZN_TRANSFO_FLAG); /* * Clip shape is computed in the group's local * coordinates. */ if (group->clip != ZN_NO_ITEM) { /* * Update the geometry of the clip item if needed. * Its bounding box will be clipped by the current * clipbox (i.e the clipbox of the group's parent). */ if (force || ISSET(group->clip->inv_flags, ZN_COORDS_FLAG) || ISSET(group->clip->inv_flags, ZN_TRANSFO_FLAG)) { /*printf("calling cc on clip item %d for group %d\n", group->clip->id, item->id);*/ CallRegularCC(group->clip); if (item == item->wi->top_group) { SetXShape(item); } /* * If the clip item has changed we need to compute * new clipped bounding boxes for all the children. */ force = True; } } PushClip(group, False); for (current_item = group->head; current_item != ZN_NO_ITEM; current_item = current_item->next) { /* * Skip the clip item, it has been already updated. * Skip as well items with a dependency, they will * be updated later. */ if ((current_item == group->clip) || (current_item->connected_item != ZN_NO_ITEM)) { continue; } if (force || ISSET(current_item->inv_flags, ZN_COORDS_FLAG) || ISSET(current_item->inv_flags, ZN_TRANSFO_FLAG)) { if (current_item->class != ZnGroup) { /*printf("calling cc on item %d\n", current_item->id);*/ CallRegularCC(current_item); } else { /*printf("calling cc on group %d\n", current_item->id);*/ current_item->class->ComputeCoordinates(current_item, force); } } } /* * Update coordinates and bounding boxes following * a possible change in connected items. Only regular * items can be concerned. */ if (group->dependents) { num_deps = ZnListSize(group->dependents); deps = (Item *) ZnListArray(group->dependents); for (i = 0; i < num_deps; i++) { current_item = deps[i]; if (force || ISSET(current_item->inv_flags, ZN_COORDS_FLAG) || ISSET(current_item->inv_flags, ZN_TRANSFO_FLAG) || ISSET(current_item->connected_item->flags, UPDATE_DEPENDENT_BIT)) { /*printf("Updating dependent: %d\n", current_item->id);*/ CallRegularCC(current_item); } } /* * Now, we must reset the update_dependent flag */ for (i = 0; i < num_deps; i++) { CLEAR(deps[i]->connected_item->flags, UPDATE_DEPENDENT_BIT); } /*printf("... done\n");*/ } /* * Compute the bounding box. */ ResetBBox(&item->item_bounding_box); current_item = group->head; while (current_item != ZN_NO_ITEM) { AddBBoxToBBox(&item->item_bounding_box, ¤t_item->item_bounding_box); current_item = current_item->next; } item->inv_flags = 0; PopClip(group, False); PopTransform(item); } /* ********************************************************************************** * * ToArea -- * Tell if the object is entirely outside (-1), * entirely inside (1) or in between (0). * ********************************************************************************** */ static int ToArea(Item item, ZnBBox *area, Tk_Uid tag_uid, int enclosed, ZnBool report) { GroupItem group = (GroupItem) item; Item current_item; ZnBBox enclosing, inter; int result; ZnBool outside = True; ZnBool atomic = ISSET(item->flags, ATOMIC_BIT); PushTransform(item); enclosing.orig.x = area->orig.x - 1; enclosing.orig.y = area->orig.y - 1; enclosing.corner.x = area->corner.x + 1; enclosing.corner.y = area->corner.y + 1; current_item = group->head; while (current_item != ZN_NO_ITEM) { if (ISCLEAR(current_item->flags, VISIBLE_BIT) && ISCLEAR(current_item->flags, SENSITIVE_BIT)) { goto cont_ta; } IntersectBBox(&enclosing, ¤t_item->item_bounding_box, &inter); if (IsEmptyBBox(&inter)) { goto cont_ta; } if (current_item->class != ZnGroup) { PushTransform(current_item); } /* * If this group is ATOMIC, ask the child groups to report * instead of adding their children to the result. */ result = current_item->class->ToArea(current_item, area, tag_uid, enclosed, atomic); outside &= (result == -1); /* * If this group is ATOMIC, it must report itself as matching * if a/ the request is 'enclosed' and all the children are * enclosed or b/ the request is 'overlapping' and at least one * child overlaps (or is enclosed). If not ATOMIC, the group * never match the area. * So here we can do early tests to shortcut the search when * the most stringent conditions are met. */ if (atomic) { if (!enclosed && (result >= 0)) { result = 0; goto out; } else if (enclosed && (result == 0)) { goto out; } } if (!atomic && (result >= enclosed)) { DoItem(item->wi->interp, current_item, ZN_NO_PART, tag_uid); } if (current_item->class != ZnGroup) { PopTransform(current_item); } cont_ta: current_item = current_item->next; } if (!atomic) { result = -1; } else if (outside) { result = -1; } else { result = 1; } out: PopTransform(item); return result; } /* ********************************************************************************** * * Draw -- * ********************************************************************************** */ static void Draw(Item item) { GroupItem group = (GroupItem) item; WidgetInfo *wi = item->wi; Item current_item; ZnBBox bbox, old_damaged_area, *clip_box; PushTransform(item); PushClip(group, True); if (group->clip != ZN_NO_ITEM) { ITEM_P.CurrentClip(wi, NULL, &clip_box, NULL); old_damaged_area = wi->damaged_area; IntersectBBox(&wi->damaged_area, clip_box, &bbox); wi->damaged_area = bbox; } current_item = group->tail; while (current_item != ZN_NO_ITEM) { if (ISSET(current_item->flags, VISIBLE_BIT)) { IntersectBBox(&wi->damaged_area, ¤t_item->item_bounding_box, &bbox); if (!IsEmptyBBox(&bbox)) { if (current_item->class != ZnGroup) { PushTransform(current_item); } current_item->class->Draw(current_item); if (wi->draw_bboxes) { XGCValues values; values.foreground = ZnPixel(wi->bbox_color); values.fill_style = FillSolid; values.line_width = 1; values.line_style = (current_item->class==ZnGroup)?LineOnOffDash:LineSolid; XChangeGC(wi->dpy, wi->gc, GCForeground|GCLineStyle|GCLineWidth|GCFillStyle, &values); XDrawRectangle(wi->dpy, wi->draw_buffer, wi->gc, current_item->item_bounding_box.orig.x, current_item->item_bounding_box.orig.y, current_item->item_bounding_box.corner.x - current_item->item_bounding_box.orig.x, current_item->item_bounding_box.corner.y - current_item->item_bounding_box.orig.y); } if (current_item->class != ZnGroup) { PopTransform(current_item); } } } current_item = current_item->previous; } if (group->clip != ZN_NO_ITEM) { wi->damaged_area = old_damaged_area; } PopClip(group, True); PopTransform(item); } /* ********************************************************************************** * * Render -- * ********************************************************************************** */ static void Render(Item item) { GroupItem group = (GroupItem) item; WidgetInfo *wi = item->wi; Item current_item; ZnBBox bbox, old_damaged_area, *clip_box; PushTransform(item); PushClip(group, True); if (group->clip != ZN_NO_ITEM) { ITEM_P.CurrentClip(wi, NULL, &clip_box, NULL); old_damaged_area = wi->damaged_area; IntersectBBox(&wi->damaged_area, clip_box, &bbox); wi->damaged_area = bbox; } current_item = group->tail; while (current_item != ZN_NO_ITEM) { if (ISSET(current_item->flags, VISIBLE_BIT)) { IntersectBBox(&wi->damaged_area, ¤t_item->item_bounding_box, &bbox); if (!IsEmptyBBox(&bbox)) { if (current_item->class != ZnGroup) { PushTransform(current_item); } current_item->class->Render(current_item); if (current_item->class != ZnGroup) { PopTransform(current_item); } } } current_item = current_item->previous; } if (group->clip != ZN_NO_ITEM) { wi->damaged_area = old_damaged_area; } PopClip(group, True); PopTransform(item); } /* ********************************************************************************** * * IsSensitive -- * ********************************************************************************** */ static ZnBool IsSensitive(Item item, int item_part) { ZnBool sensitive = ISSET(item->flags, SENSITIVE_BIT); Item parent = item->parent; while (sensitive && (parent != ZN_NO_ITEM)) { sensitive &= ISSET(item->flags, SENSITIVE_BIT); parent = parent->parent; } return sensitive; } /* ********************************************************************************** * * Pick -- * Given a point an an aperture, find the topmost group item/part * that is (a) within the pick_aperture * (b) the top most * (c) has either its sensibility or its visibility set. * * Results: * The return value is the distance of the picked item/part if one * has been found or a really big distance if not. a_item and a_part * are set to point the picked item/part or to ZN_NO_ITEM/ZN_NO_PART. * If the group is ATOMIC, a_item points the group instead of the * actual item. * * Side effects: * None. * ********************************************************************************** */ static double Pick(Item item, ZnPoint *p, Item start_item, int aperture, Item *a_item, int *a_part) { GroupItem group = (GroupItem) item; Item p_item, current_item; WidgetInfo *wi = item->wi; int p_part; double dist, best = 1e10; ZnBBox bbox, inter, *clip_box; ZnBool in_depth; *a_item= ZN_NO_ITEM; *a_part = ZN_NO_PART; if (group->head == ZN_NO_ITEM) { return best; } PushTransform(item); PushClip(group, False); bbox.orig.x = p->x - aperture; bbox.orig.y = p->y - aperture; bbox.corner.x = p->x + (aperture?aperture:1); bbox.corner.y = p->y + (aperture?aperture:1); if (ITEM_P.CurrentClip(wi, NULL, &clip_box, NULL)) { IntersectBBox(&bbox, clip_box, &inter); if (IsEmptyBBox(&inter)) { goto out; } } if ((start_item == ZN_NO_ITEM) || (start_item->parent == item)) { in_depth = False; } else { in_depth = True; } if ((start_item == ZN_NO_ITEM) || (start_item->parent != item)) { current_item = group->head; } else { current_item = start_item; } do { if (ISCLEAR(current_item->flags, SENSITIVE_BIT) && ISCLEAR(current_item->flags, VISIBLE_BIT)) { goto cont; } if (current_item->class != ZnGroup) { if (in_depth) { goto cont; } PushTransform(current_item); p_item = current_item; p_part = ZN_NO_PART; dist = current_item->class->Pick(current_item, p, NULL, 0, &p_item, &p_part); dist -= aperture; PopTransform(current_item); } else { dist = current_item->class->Pick(current_item, p, in_depth ? start_item : NULL, aperture, &p_item, &p_part); } if (dist < 0.0) { dist = 0.0; } if (dist >= best) { goto cont; } if (ISSET(item->flags, ATOMIC_BIT)) { *a_item = item; } else { *a_item = p_item; *a_part = p_part; } best = dist; /*printf("found %d:%d, at %g\n", (*a_item)->id, *a_part, dist);*/ if (dist == 0.0) { break; } cont: current_item = current_item->next; } while (current_item != ZN_NO_ITEM); out: PopClip(group, False); PopTransform(item); return best; } /* ********************************************************************************** * * Coords -- * Return or edit the group translation (can be also interpreted as the * position of the group origin in the group's parent). * ********************************************************************************** */ static int Coords(Item item, int contour, int index, int cmd, ZnPoint **pts, int *num_pts) { if ((cmd == COORDS_ADD) || (cmd == COORDS_ADD_LAST) || (cmd == COORDS_REMOVE)) { Tcl_AppendResult(item->wi->interp, " groups can't add or remove vertices", NULL); return ZN_ERROR; } else if ((cmd == COORDS_REPLACE) || (cmd == COORDS_REPLACE_ALL)) { if (*num_pts == 0) { Tcl_AppendResult(item->wi->interp, " coords command need 1 point on groups", NULL); return ZN_ERROR; } if (!item->transfo) { item->transfo = ZnTransfoNew(); } ZnSetTranslation(item->transfo, (*pts)[0].x, (*pts)[0].y); ITEM.Invalidate(item, ZN_TRANSFO_FLAG); } else if ((cmd == COORDS_READ) || (cmd == COORDS_READ_ALL)) { ZnPoint *p; ZnListAssertSize(item->wi->work_pts, 1); p = (ZnPoint *) ZnListArray(item->wi->work_pts); ZnTransfoDecompose(item->transfo, NULL, p, NULL, NULL); *num_pts = 1; *pts = p; } return ZN_OK; } /* ********************************************************************************** * * PostScript -- * ********************************************************************************** */ static void PostScript(Item item, PostScriptInfo ps_info) { } /* ********************************************************************************** * * Exported functions struct -- * ********************************************************************************** */ static ItemClassStruct GROUP_ITEM_CLASS = { sizeof(GroupItemStruct), False, /* has_fields */ 0, /* num_parts */ False, /* has_anchors */ "group", group_attrs, Init, Clone, Destroy, Configure, Query, NULL, /* GetFieldSet */ NULL, /* GetAnchor */ NULL, /* GetClipVertices */ Coords, NULL, /* InsertChars */ NULL, /* DeleteChars */ NULL, /* Cursor */ NULL, /* Index */ NULL, /* Part */ NULL, /* Selection */ NULL, /* Contour */ ComputeCoordinates, ToArea, Draw, Render, IsSensitive, Pick, NULL, /* PickVertex */ PostScript }; ZnItemClassId ZnGroup = (ZnItemClassId) &GROUP_ITEM_CLASS;