From f7df4b272180740dd9c5a4b44c16b47a5e630eb8 Mon Sep 17 00:00:00 2001 From: lecoanet Date: Thu, 13 Jan 2000 09:53:16 +0000 Subject: 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. --- generic/Group.c | 1175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1175 insertions(+) create mode 100644 generic/Group.c (limited to 'generic/Group.c') 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 +#include + +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, ¤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, + 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, ¤t_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, ¤t_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, ¤t_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; -- cgit v1.1