aboutsummaryrefslogtreecommitdiff
path: root/generic/Group.c
diff options
context:
space:
mode:
authorlecoanet2000-01-13 09:53:16 +0000
committerlecoanet2000-01-13 09:53:16 +0000
commitf7df4b272180740dd9c5a4b44c16b47a5e630eb8 (patch)
treebfe396d88db90ca2af35af8732cef7a06146ffa2 /generic/Group.c
parent0e4478121b45e4e2f98334d6e435433b559f6e92 (diff)
downloadtkzinc-f7df4b272180740dd9c5a4b44c16b47a5e630eb8.zip
tkzinc-f7df4b272180740dd9c5a4b44c16b47a5e630eb8.tar.gz
tkzinc-f7df4b272180740dd9c5a4b44c16b47a5e630eb8.tar.bz2
tkzinc-f7df4b272180740dd9c5a4b44c16b47a5e630eb8.tar.xz
Correction des entetes.
Ajout du clonage. Corrections de bugs de prise en compte de modifs graphiques/geometriques dans ComputeCoordinates. Adaptation de la m�thode Coords.
Diffstat (limited to 'generic/Group.c')
-rw-r--r--generic/Group.c1175
1 files changed, 1175 insertions, 0 deletions
diff --git a/generic/Group.c b/generic/Group.c
new file mode 100644
index 0000000..9e68486
--- /dev/null
+++ b/generic/Group.c
@@ -0,0 +1,1175 @@
+/*
+ * 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 "tkRadar.h"
+
+#include <malloc.h>
+#include <X11/extensions/shape.h>
+
+static const char rcsid[] = "$Id$";
+static const char compile_id[]="$Compile: " __FILE__ " " __DATE__ " " __TIME__ " $";
+
+
+/*
+ **********************************************************************************
+ *
+ * Specific Group item record
+ *
+ **********************************************************************************
+ */
+static RadarAttrConfig group_attrs[] = {
+ { RADAR_CONFIG_BOOL, "-atomic", NULL,
+ Tk_Offset(GroupItemStruct, header.flags), ATOMIC_BIT,
+ RADAR_REPICK_FLAG, False },
+ { RADAR_CONFIG_ITEM, "-clip", NULL,
+ Tk_Offset(GroupItemStruct, clip), 0,
+ RADAR_COORDS_FLAG|RADAR_ITEM_FLAG, False },
+ { RADAR_CONFIG_BOOL, "-composerotation", NULL,
+ Tk_Offset(GroupItemStruct, header.flags), COMPOSE_ROTATION_BIT,
+ RADAR_TRANSFO_FLAG, False },
+ { RADAR_CONFIG_BOOL, "-composescale", NULL,
+ Tk_Offset(GroupItemStruct, header.flags), COMPOSE_SCALE_BIT,
+ RADAR_TRANSFO_FLAG, False },
+ { RADAR_CONFIG_PRI, "-priority", NULL,
+ Tk_Offset(GroupItemStruct, header.priority), 0,
+ RADAR_DRAW_FLAG|RADAR_REPICK_FLAG, False },
+ { RADAR_CONFIG_BOOL, "-sensitive", NULL,
+ Tk_Offset(GroupItemStruct, header.flags), SENSITIVE_BIT,
+ RADAR_REPICK_FLAG, False },
+ { RADAR_CONFIG_TAGS, "-tags", NULL,
+ Tk_Offset(GroupItemStruct, header.tags), 0, 0, False },
+ { RADAR_CONFIG_BOOL, "-visible", NULL,
+ Tk_Offset(GroupItemStruct, header.flags), VISIBLE_BIT,
+ RADAR_DRAW_FLAG|RADAR_REPICK_FLAG|RADAR_VIS_FLAG, False },
+
+ { RADAR_CONFIG_END, NULL, NULL, 0, 0, 0 }
+};
+
+
+/*
+ **********************************************************************************
+ *
+ * Init --
+ *
+ **********************************************************************************
+ */
+static int
+Init(Item item,
+ int *argc,
+ Arg **args)
+{
+ GroupItem group = (GroupItem) item;
+
+ group->head = RADAR_NO_ITEM;
+ group->tail = RADAR_NO_ITEM;
+ group->clip = RADAR_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 RADAR_OK;
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * Clone --
+ *
+ **********************************************************************************
+ */
+static void
+Clone(Item item)
+{
+ GroupItem group = (GroupItem) item;
+ RadarList dependents;
+ Item connected, current_item, new_item;
+ Item *items;
+ Tcl_HashTable mapping;
+ Tcl_HashEntry *entry;
+ int new, num_items, i;
+
+ current_item = group->head;
+ group->head = group->tail = RADAR_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 != RADAR_NO_ITEM) {
+ connected = current_item->connected_item;
+ new_item = ITEM.CloneItem(current_item);
+ new_item->connected_item = connected;
+ ITEM.InsertItem(new_item, item, RADAR_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->next;
+ }
+
+ /*
+ * Then rebuild the dependency list with
+ * the new items.
+ */
+ if (dependents) {
+ printf("rebuilding dependents\n");
+ group->dependents = NULL;
+ items = (Item *) RadarListArray(dependents);
+ num_items = RadarListSize(dependents);
+ for (i = 0; i < num_items; i++, items++) {
+ entry = Tcl_FindHashEntry(&mapping, (char *) *items);
+ if (entry == NULL) {
+ RadarWarning("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) {
+ RadarWarning("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 != RADAR_NO_ITEM) {
+ next_item = current_item->next;
+ ITEM.DestroyItem(current_item);
+ current_item = next_item;
+ }
+ if (group->dependents) {
+ RadarListFree(group->dependents);
+ }
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * SetXShape --
+ *
+ **********************************************************************************
+ */
+static void
+SetXShape(Item grp)
+{
+#ifdef SHAPE
+ WidgetInfo *wi = grp->wi;
+ Item clip = ((GroupItem) grp)->clip;
+ int i, num_pts;
+ RadarPos min_x, min_y, max_x, max_y;
+ RadarPoint *pts, *p;
+ RadarBool simple;
+ RadarDim width, height;
+ XPoint *xpts, *xp;
+ Region reg;
+
+ if (!wi->has_x_shape) {
+ return;
+ }
+
+ if ((clip == RADAR_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, RadarWindowId(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, &pts, &num_pts);
+ if (simple || (num_pts == 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, RadarWindowId(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.
+ */
+ p = &pts[0];
+ min_x = p->x;
+ min_y = p->y;
+ p++;
+ for (i = 1; i < num_pts; p++, i++) {
+ if (p->x < min_x) {
+ min_x = p->x;
+ }
+ if (p->y < min_y) {
+ min_y = p->y;
+ }
+ }
+ for (p = &pts[0], i = 0; i < num_pts; i++, p++) {
+ p->x -= min_x;
+ p->y -= min_y;
+ }
+ /*
+ * Now normalize the shape and map it to the window size.
+ */
+ p = &pts[0];
+ max_x = p->x;
+ max_y = p->y;
+ p++;
+ for (i = 1; i < num_pts; p++, i++) {
+ if (p->x > max_x) {
+ max_x = p->x;
+ }
+ if (p->y > max_y) {
+ max_y = p->y;
+ }
+ }
+ width = wi->width + 2*wi->border_width;
+ height = wi->height + 2*wi->border_width;
+ xpts = (XPoint *) RadarMalloc(num_pts * sizeof(XPoint));
+ for (p = &pts[0], 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);
+ XShapeCombineMask(wi->dpy, wi->full_reshape ? RadarWindowId(wi->win) : wi->real_top,
+ ShapeBounding, 0, 0, None, ShapeSet);
+ XShapeCombineRegion(wi->dpy, wi->full_reshape ? wi->real_top : RadarWindowId(wi->win),
+ ShapeBounding, 0, 0, reg, ShapeSet);
+ XDestroyRegion(reg);
+ RadarFree(xpts);
+ }
+ }
+#endif
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * Configure --
+ *
+ **********************************************************************************
+ */
+static int
+Configure(Item item,
+ int argc,
+ RadarAttrList argv,
+ int *flags)
+{
+ GroupItem group = (GroupItem) item;
+ WidgetInfo *wi = item->wi;
+
+ if (ITEM_P.ConfigureAttributes((char *) item, -1, argc, argv, flags) == RADAR_ERROR) {
+ return RADAR_ERROR;
+ }
+
+ /*
+ * If the clip item changed, check if it is a legal
+ * item type that is inside this group.
+ */
+ if (ISSET(*flags, RADAR_ITEM_FLAG)) {
+ if (group->clip &&
+ (!group->clip->class->GetClipVertices || (group->clip->parent != item))) {
+ group->clip = RADAR_NO_ITEM;
+ Tcl_AppendResult(wi->interp,
+ " clip item must be a child of the group", NULL);
+ return RADAR_ERROR;
+ }
+ if (!group->clip && (item == wi->top_group)) {
+ SetXShape(item);
+ }
+ }
+
+ return RADAR_OK;
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * Query --
+ *
+ **********************************************************************************
+ */
+static int
+Query(Item item,
+ int argc,
+ RadarAttrList argv)
+{
+ if (ITEM_P.QueryAttribute((char *) item, -1, argv[0]) == RADAR_ERROR) {
+ return RADAR_ERROR;
+ }
+
+ return RADAR_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,
+ RadarBool set_gc)
+{
+ WidgetInfo *wi = ((Item) group)->wi;
+ int i, num_c, num_pts;
+ RadarPoint *pts;
+ RadarBool simple, has_clip=False;
+ ClipState *previous_clip=NULL;
+ Region reg;
+ XRectangle rect;
+ XPoint *xpts;
+
+ if ((group->clip != RADAR_NO_ITEM) &&
+ ((((Item) group) != wi->top_group) || !wi->reshape)) {
+ simple = group->clip->class->GetClipVertices(group->clip, &pts, &num_pts);
+
+ if (num_pts != 0) {
+ has_clip = True;
+
+ num_c = RadarListSize(wi->clip_stack);
+ if (num_c != 0) {
+ previous_clip = (ClipState *) RadarListAt(wi->clip_stack, RadarListTail);
+ }
+ RadarListAssertSize(wi->clip_stack, num_c+1);
+ wi->current_clip = (ClipState *) RadarListAt(wi->clip_stack, RadarListTail);
+ wi->current_clip->simple = simple;
+
+ /*
+ * Compute the local region.
+ */
+ if (simple) {
+ rect.x = pts[0].x;
+ rect.y = pts[0].y;
+ rect.width = pts[1].x - pts[0].x;
+ rect.height = pts[1].y - pts[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 *) RadarMalloc(num_pts * sizeof(XPoint));
+ for (i = 0; i < num_pts; i++) {
+ xpts[i].x = pts[i].x;
+ xpts[i].y = pts[i].y;
+ }
+ reg = XPolygonRegion(xpts, num_pts, EvenOddRule);
+ RadarFree(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, and substitute the combined
+ * clip region/damaged region for the clip region.
+ */
+ if (set_gc) {
+ if ((((Item) group) == wi->top_group) || has_clip) {
+ if (wi->current_clip) {
+ reg = XCreateRegion();
+ XIntersectRegion(wi->current_clip->region, wi->damaged_region, reg);
+ XDestroyRegion(wi->current_clip->region);
+ wi->current_clip->region = reg;
+ XSetRegion(wi->dpy, wi->gc, wi->current_clip->region);
+ }
+ else {
+ XSetRegion(wi->dpy, wi->gc, wi->damaged_region);
+ }
+ }
+ }
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * PopClip --
+ * Re-install the previous clip shape if any (stack can be empty).
+ *
+ **********************************************************************************
+ */
+static void
+PopClip(GroupItem group,
+ RadarBool set_gc)
+{
+ WidgetInfo *wi = ((Item) group)->wi;
+
+ if ((group->clip != RADAR_NO_ITEM) &&
+ ((((Item) group) != wi->top_group) || !wi->reshape)) {
+ XDestroyRegion(wi->current_clip->region);
+ RadarListDelete(wi->clip_stack, RadarListTail);
+ if (RadarListSize(wi->clip_stack) != 0) {
+ wi->current_clip = (ClipState *) RadarListAt(wi->clip_stack, RadarListTail);
+ }
+ 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 if (((Item) group) == wi->top_group) {
+ /*printf("resetting clip mask\n");*/
+ XSetClipMask(wi->dpy, wi->gc, None);
+ }
+ else {
+ /*printf("resetting to damaged area\n");*/
+ XSetRegion(wi->dpy, wi->gc, wi->damaged_region);
+ }
+ }
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * PushTransform --
+ * Save the current transform then concatenate the item transform to
+ * form the new current transform.
+ *
+ **********************************************************************************
+ */
+static void
+PushTransform(Item item)
+{
+ WidgetInfo *wi;
+ RadarTransfo *next_t;
+ int num_t;
+
+ if (!item->transfo &&
+ ISSET(item->flags, COMPOSE_SCALE_BIT) &&
+ ISSET(item->flags, COMPOSE_ROTATION_BIT)) {
+ return;
+ }
+ /*
+ * Push the current transform and concatenate
+ * the item transform taking into account the
+ * combination flags.
+ */
+ wi = item->wi;
+ num_t = RadarListSize(wi->transfo_stack);
+ RadarListAssertSize(wi->transfo_stack, num_t+1);
+ next_t = (RadarTransfo *) RadarListAt(wi->transfo_stack, num_t);
+ ITEM.ComposeItemTransform(item, wi->current_transfo, next_t);
+ wi->current_transfo = next_t;
+ /*printf("Pushing transfo for item: %d\n;", item->id);
+ RadarPrintTransfo(wi->current_transfo);*/
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * PopTransform --
+ * Restore the previously saved transform from the stack.
+ *
+ **********************************************************************************
+ */
+static void
+PopTransform(Item item)
+{
+ WidgetInfo *wi;
+
+ if (!item->transfo &&
+ ISSET(item->flags, COMPOSE_SCALE_BIT) &&
+ ISSET(item->flags, COMPOSE_ROTATION_BIT)) {
+ return;
+ }
+
+ /*
+ * Restore the previous transform.
+ */
+ wi = item->wi;
+ RadarListDelete(wi->transfo_stack, RadarListTail);
+ wi->current_transfo = (RadarTransfo *) RadarListAt(wi->transfo_stack,
+ RadarListTail);
+ /*printf("Popping transfo for item: %d\n", item->id);
+ RadarPrintTransfo(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;
+
+ /*
+ * 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);*/
+ /*RadarPrintTransfo(wi->current_transfo);*/
+ item->class->ComputeCoordinates(item, False);
+ /*
+ * If a current clipbox exists adjust the item
+ * bounding box accordingly.
+ */
+ if (wi->current_clip) {
+ RadarBBox inter;
+
+ IntersectBBox(&item->item_bounding_box, &wi->current_clip->clip_box, &inter);
+ item->item_bounding_box = inter;
+ }
+ /*
+ * Do some generic post-work in behalf of the (regular) children.
+ */
+ if (ISSET(item->inv_flags, RADAR_REPICK_FLAG)) {
+ SET(wi->events_flags, INTERNAL_NEED_REPICK);
+ }
+ if (ISSET(item->inv_flags, RADAR_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,
+ RadarBool force)
+{
+ GroupItem group = (GroupItem) item;
+ Item current_item;
+ Item *deps;
+ int num_deps, i;
+
+ PushTransform(item);
+ /* printf("Group.c\n");
+ RadarPrintTransfo(item->wi->current_transfo);
+ printf("\n");*/
+
+ force |= ISSET(item->inv_flags, RADAR_TRANSFO_FLAG);
+
+ /*
+ * Clip shape is computed in the group's local
+ * coordinates.
+ */
+ if (group->clip != RADAR_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, RADAR_COORDS_FLAG) ||
+ ISSET(group->clip->inv_flags, RADAR_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 != RADAR_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 != RADAR_NO_ITEM)) {
+ continue;
+ }
+ if (force ||
+ ISSET(current_item->inv_flags, RADAR_COORDS_FLAG) ||
+ ISSET(current_item->inv_flags, RADAR_TRANSFO_FLAG)) {
+ if (current_item->class != RadarGroup) {
+ /*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 = RadarListSize(group->dependents);
+ deps = (Item *) RadarListArray(group->dependents);
+ for (i = 0; i < num_deps; i++) {
+ current_item = deps[i];
+ if (force ||
+ ISSET(current_item->inv_flags, RADAR_COORDS_FLAG) ||
+ ISSET(current_item->inv_flags, RADAR_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 != RADAR_NO_ITEM) {
+ AddBBoxToBBox(&item->item_bounding_box, &current_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,
+ RadarBBox *area,
+ Tk_Uid tag_uid,
+ int enclosed,
+ RadarBool report)
+{
+ GroupItem group = (GroupItem) item;
+ Item current_item;
+ RadarBBox enclosing, inter;
+ int result;
+ RadarBool outside = True;
+ RadarBool 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 != RADAR_NO_ITEM) {
+ if (ISCLEAR(current_item->flags, VISIBLE_BIT) &&
+ ISCLEAR(current_item->flags, SENSITIVE_BIT)) {
+ goto cont_ta;
+ }
+ IntersectBBox(&enclosing, &current_item->item_bounding_box, &inter);
+ if (IsEmptyBBox(&inter)) {
+ goto cont_ta;
+ }
+ if (current_item->class != RadarGroup) {
+ 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, RADAR_NO_PART, tag_uid);
+ }
+
+ if (current_item->class != RadarGroup) {
+ 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;
+ RadarBBox bbox;
+ RadarBBox old_damaged_area;
+
+ PushTransform(item);
+ PushClip(group, True);
+ if ((group->clip != RADAR_NO_ITEM) && wi->current_clip) {
+ old_damaged_area = wi->damaged_area;
+ IntersectBBox(&wi->damaged_area, &wi->current_clip->clip_box, &bbox);
+ wi->damaged_area = bbox;
+ }
+
+ current_item = group->tail;
+ while (current_item != RADAR_NO_ITEM) {
+ if (ISSET(current_item->flags, VISIBLE_BIT)) {
+ IntersectBBox(&wi->damaged_area, &current_item->item_bounding_box, &bbox);
+ if (!IsEmptyBBox(&bbox)) {
+ if (current_item->class != RadarGroup) {
+ PushTransform(current_item);
+ }
+ current_item->class->Draw(current_item);
+ if (wi->draw_bboxes) {
+ XGCValues values;
+ values.foreground = RadarPixel(wi->bbox_color);
+ values.fill_style = FillSolid;
+ values.line_width = 1;
+ values.line_style = (current_item->class==RadarGroup)?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 != RadarGroup) {
+ PopTransform(current_item);
+ }
+ }
+ }
+ current_item = current_item->previous;
+ }
+
+ if ((group->clip != RADAR_NO_ITEM) && wi->current_clip) {
+ wi->damaged_area = old_damaged_area;
+ }
+ PopClip(group, True);
+ PopTransform(item);
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * IsSensitive --
+ *
+ **********************************************************************************
+ */
+static RadarBool
+IsSensitive(Item item,
+ int item_part)
+{
+ RadarBool sensitive = ISSET(item->flags, SENSITIVE_BIT);
+ Item parent = item->parent;
+
+ while (sensitive && (parent != RADAR_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 RADAR_NO_ITEM/RADAR_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,
+ RadarPoint *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;
+ RadarBBox bbox, inter;
+ RadarBool in_depth;
+
+ *a_item= RADAR_NO_ITEM;
+ *a_part = RADAR_NO_PART;
+
+ if (group->head == RADAR_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 (wi->current_clip) {
+ IntersectBBox(&bbox, &wi->current_clip->clip_box, &inter);
+ if (IsEmptyBBox(&inter)) {
+ goto out;
+ }
+ }
+
+ if ((start_item == RADAR_NO_ITEM) || (start_item->parent == item)) {
+ in_depth = False;
+ }
+ else {
+ in_depth = True;
+ }
+ if ((start_item == RADAR_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;
+ }
+ IntersectBBox(&bbox, &current_item->item_bounding_box, &inter);
+ if (IsEmptyBBox(&inter)) {
+ goto cont;
+ }
+ if (current_item->class != RadarGroup) {
+ if (in_depth) {
+ goto cont;
+ }
+ PushTransform(current_item);
+ p_item = current_item;
+ p_part = RADAR_NO_PART;
+ dist = current_item->class->Pick(current_item, p, NULL, aperture,
+ &p_item, &p_part);
+ PopTransform(current_item);
+ }
+ else {
+ dist = current_item->class->Pick(current_item, p,
+ in_depth ? start_item : NULL,
+ aperture, &p_item, &p_part);
+ }
+ if ((dist > aperture) || (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 != RADAR_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 index,
+ int cmd,
+ RadarPoint **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 RADAR_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 RADAR_ERROR;
+ }
+ if (!item->transfo) {
+ item->transfo = RadarTransfoNew();
+ }
+ RadarSetTranslation(item->transfo, (*pts)[0].x, (*pts)[0].y);
+ ITEM.Invalidate(item, RADAR_TRANSFO_FLAG);
+ }
+ else if ((cmd == COORDS_READ) || (cmd == COORDS_READ_ALL)) {
+ RadarPoint *p;
+
+ RadarListAssertSize(item->wi->work_pts, 1);
+ p = (RadarPoint *) RadarListArray(item->wi->work_pts);
+ RadarTransfoDecompose(item->transfo, NULL, p, NULL, NULL);
+ *num_pts = 1;
+ *pts = p;
+ }
+ return RADAR_OK;
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * PostScript --
+ *
+ **********************************************************************************
+ */
+static void
+PostScript(Item item,
+ PostScriptInfo ps_info)
+{
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * Exported functions struct --
+ *
+ **********************************************************************************
+ */
+static ItemClassStruct GROUP_ITEM_CLASS = {
+ sizeof(GroupItemStruct),
+ False,
+ False,
+ False,
+ "group",
+ group_attrs,
+ Init,
+ Clone,
+ Destroy,
+ Configure,
+ Query,
+ NULL,
+ NULL,
+ NULL,
+ Coords,
+ ComputeCoordinates,
+ ToArea,
+ Draw,
+ IsSensitive,
+ Pick,
+ PostScript
+};
+
+RadarItemClassId RadarGroup = (RadarItemClassId) &GROUP_ITEM_CLASS;