/*
* ptkCanvGroup.c --
*
* This file implements grid items for canvas
* widgets.
*
* Copyright (c) 1991-1994 The Regents of the University of California.
* Copyright (c) 1994-1996 Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* RCS: @(#) $Id: tkGroup.c,v 1.2 1998/09/14 18:23:16 stanton Exp $
*/
#include "tkPort.h"
#include "tk.h"
#include "tkInt.h"
#include "tkCanvases.h"
/*
* The structure below defines the record for each rectangle/oval item.
*/
typedef struct GroupItem {
Tk_Item header; /* Generic stuff that's the same for all
* types. MUST BE FIRST IN STRUCTURE. */
double posn[2]; /* Nonminal position of the group */
Tcl_Interp *interp; /* For error reporting */
Tk_Canvas canvas; /* Needed to find items by id when configured */
int numMembers; /* Number of items in the group */
int numSlots; /* Space in the array */
Tk_Item **members; /* array of items */
} GroupItem;
/*
* Information used for parsing configuration specs:
*/
static int MembersParseProc _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, Tk_Window tkwin,
Tcl_Obj * value, char *recordPtr, int offset));
static Tcl_Obj * MembersPrintProc _ANSI_ARGS_((ClientData clientData,
Tk_Window tkwin, char *recordPtr, int offset,
Tcl_FreeProc **freeProcPtr));
static Tk_CustomOption stateOption = {
TkStateParseProc,
TkStatePrintProc, (ClientData) 3
};
static Tk_CustomOption tagsOption = {
Tk_CanvasTagsParseProc,
Tk_CanvasTagsPrintProc, (ClientData) NULL
};
static Tk_CustomOption membersOption = {
MembersParseProc,
MembersPrintProc, (ClientData) NULL
};
static Tk_ConfigSpec configSpecs[] = {
{TK_CONFIG_CUSTOM, "-members", NULL, NULL,
"1.0", Tk_Offset(GroupItem, members),
TK_CONFIG_DONT_SET_DEFAULT, &membersOption},
{TK_CONFIG_CUSTOM, "-state", NULL, NULL,
NULL, Tk_Offset(Tk_Item, state),TK_CONFIG_NULL_OK,
&stateOption},
{TK_CONFIG_CUSTOM, "-tags", NULL, NULL,
NULL, 0, TK_CONFIG_NULL_OK, &tagsOption},
{TK_CONFIG_CALLBACK, "-updatecommand", NULL, NULL,
NULL, Tk_Offset(Tk_Item, updateCmd), TK_CONFIG_NULL_OK},
{TK_CONFIG_END, NULL, NULL, NULL,
NULL, 0, 0}
};
/*
* Prototypes for procedures defined in this file:
*/
static void ComputeGroupBbox _ANSI_ARGS_((Tk_Canvas canvas,
GroupItem *groupPtr));
static int ConfigureGroup _ANSI_ARGS_((Tcl_Interp *interp,
Tk_Canvas canvas, Tk_Item *itemPtr, int argc,
Tcl_Obj *CONST *args, int flags));
static int CreateGroup _ANSI_ARGS_((Tcl_Interp *interp,
Tk_Canvas canvas, struct Tk_Item *itemPtr,
int argc, Tcl_Obj *CONST *args));
static void DeleteGroup _ANSI_ARGS_((Tk_Canvas canvas,
Tk_Item *itemPtr, Display *display));
static void DisplayGroup _ANSI_ARGS_((Tk_Canvas canvas,
Tk_Item *itemPtr, Display *display, Drawable dst,
int x, int y, int width, int height));
static int GroupCoords _ANSI_ARGS_((Tcl_Interp *interp,
Tk_Canvas canvas, Tk_Item *itemPtr, int argc,
Tcl_Obj *CONST *args));
static int GroupToPostscript _ANSI_ARGS_((Tcl_Interp *interp,
Tk_Canvas canvas, Tk_Item *itemPtr, int prepass));
static int GroupToArea _ANSI_ARGS_((Tk_Canvas canvas,
Tk_Item *itemPtr, double *areaPtr));
static double GroupToPoint _ANSI_ARGS_((Tk_Canvas canvas,
Tk_Item *itemPtr, double *pointPtr));
static void ScaleGroup _ANSI_ARGS_((Tk_Canvas canvas,
Tk_Item *itemPtr, double originX, double originY,
double scaleX, double scaleY));
static void TranslateGroup _ANSI_ARGS_((Tk_Canvas canvas,
Tk_Item *itemPtr, double deltaX, double deltaY));
static int GroupIndex _ANSI_ARGS_((Tcl_Interp *interp,
Tk_Canvas canvas, Tk_Item *itemPtr, Tcl_Obj *indexString,
int *indexPtr));
static int GroupInsert _ANSI_ARGS_((Tk_Canvas canvas,
Tk_Item *itemPtr, int beforeThis, Tcl_Obj *string));
static void GroupInsertProc _ANSI_ARGS_((Tk_Canvas canvas,
Tk_Item *itemPtr, int beforeThis, Tcl_Obj *string));
static void GroupDChars _ANSI_ARGS_((Tk_Canvas canvas,
Tk_Item *itemPtr, int first, int last));
/*
* The structures below defines the rectangle and oval item types
* by means of procedures that can be invoked by generic item code.
*/
Tk_ItemType ptkCanvGroupType = {
"group", /* name */
sizeof(GroupItem), /* itemSize */
CreateGroup, /* createProc */
configSpecs, /* configSpecs */
ConfigureGroup, /* configureProc */
GroupCoords, /* coordProc */
DeleteGroup, /* deleteProc */
DisplayGroup, /* displayProc */
TK_ITEM_ALWAYS_REDRAW|TK_CONFIG_OBJS,/* flags */
GroupToPoint, /* pointProc */
GroupToArea, /* areaProc */
GroupToPostscript, /* postscriptProc */
ScaleGroup, /* scaleProc */
TranslateGroup, /* translateProc */
GroupIndex, /* indexProc */
(Tk_ItemCursorProc *) NULL, /* icursorProc */ /* Abuse to set active? */
(Tk_ItemSelectionProc *) NULL, /* selectionProc */
GroupInsertProc, /* insertProc */
GroupDChars, /* dTextProc */
(Tk_ItemType *) NULL, /* nextPtr */
(Tk_ItemBboxProc *) ComputeGroupBbox,/* bboxProc */
Tk_Offset(Tk_VisitorType, visitGroup), /* acceptProc */
NULL, /* getCoordPtr */
NULL /* setCoordPtr */
};
static void
ShowMembers(char *f,GroupItem *groupPtr)
{
int i;
LangDebug("%s gid=%d %d [",f,groupPtr->header.id, groupPtr->numMembers);
if (groupPtr->numMembers > groupPtr->numSlots)
abort();
for (i=0; i < groupPtr->numMembers; i++)
{
if (groupPtr->members[i])
{
LangDebug(" %d",groupPtr->members[i]->id);
}
else
{
LangDebug(" NULL",groupPtr->members[i]->id);
}
}
LangDebug("]\n");
}
/*
*--------------------------------------------------------------
*
* CreateGroup --
*
* This procedure is invoked to create a new rectangle
* or oval item in a canvas.
*
* Results:
* A standard Tcl return value. If an error occurred in
* creating the item, then an error message is left in
* Tcl_GetResult(interp); in this case itemPtr is left uninitialized,
* so it can be safely freed by the caller.
*
* Side effects:
* A new rectangle or oval item is created.
*
*--------------------------------------------------------------
*/
static int
CreateGroup(interp, canvas, itemPtr, argc, args)
Tcl_Interp *interp; /* For error reporting. */
Tk_Canvas canvas; /* Canvas to hold new item. */
Tk_Item *itemPtr; /* Record to hold new item; header
* has been initialized by caller. */
int argc; /* Number of arguments in args. */
Tcl_Obj *CONST *args; /* Arguments describing group. */
{
GroupItem *groupPtr = (GroupItem *) itemPtr;
int i;
if (argc==1) {
i = 1;
} else {
char *arg = Tcl_GetStringFromObj(args[1], NULL);
if ((argc>1) && (arg[0] == '-')
&& (arg[1] >= 'a') && (arg[1] <= 'z')) {
i = 1;
} else {
i = 2;
}
}
if (argc < i) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
Tk_PathName(Tk_CanvasTkwin(canvas)), " create ",
itemPtr->typePtr->name, " x1 y1 ?options?\"",
NULL);
return TCL_ERROR;
}
/*
* Carry out initialization that is needed in order to clean
* up after errors during the the remainder of this procedure.
*/
groupPtr->canvas = canvas;
groupPtr->interp = interp;
groupPtr->members = NULL;
groupPtr->numSlots = 0;
groupPtr->numMembers = 0;
/*
* Process the arguments to fill in the item record.
*/
if ((GroupCoords(interp, canvas, itemPtr, i, args) != TCL_OK)) {
goto error;
}
if (ConfigureGroup(interp, canvas, itemPtr, argc-i, args+i, 0)
== TCL_OK) {
return TCL_OK;
}
error:
DeleteGroup(canvas, itemPtr, Tk_Display(Tk_CanvasTkwin(canvas)));
return TCL_ERROR;
}
/*
*--------------------------------------------------------------
*
* GroupCoords --
*
* This procedure is invoked to process the "coords" widget
* command on rectangles and ovals. See the user documentation
* for details on what it does.
*
* Results:
* Returns TCL_OK or TCL_ERROR, and sets Tcl_GetResult(interp).
*
* Side effects:
* The coordinates for the given item may be changed.
*
*--------------------------------------------------------------
*/
static int
GroupCoords(interp, canvas, itemPtr, argc, args)
Tcl_Interp *interp; /* Used for error reporting. */
Tk_Canvas canvas; /* Canvas containing item. */
Tk_Item *itemPtr; /* Item whose coordinates are to be
* read or modified. */
int argc; /* Number of coordinates supplied in
* args. */
Tcl_Obj *CONST *args; /* Array of coordinates: x1, y1,
* x2, y2, ... */
{
GroupItem *groupPtr = (GroupItem *) itemPtr;
char c0[TCL_DOUBLE_SPACE];
if (argc == 0) {
Tcl_Obj *obj = Tcl_NewObj();
Tcl_Obj *subobj = Tcl_NewDoubleObj(groupPtr->posn[0]);
Tcl_ListObjAppendElement(interp, obj, subobj);
subobj = Tcl_NewDoubleObj(groupPtr->posn[1]);
Tcl_ListObjAppendElement(interp, obj, subobj);
Tcl_SetObjResult(interp, obj);
} else if ((argc == 1)||(argc == 2)) {
double newX;
double newY;
if (argc==1) {
if (Tcl_ListObjGetElements(interp, args[0], &argc, (Tcl_Obj ***)&args) != TCL_OK) {
return TCL_ERROR;
} else if (argc != 2) {
sprintf(c0,"%d",argc);
Tcl_AppendResult(interp, "wrong # coordinates: expected 2, got ",
c0, NULL);
return TCL_ERROR;
}
}
if ((Tk_CanvasGetCoordFromObj(interp, canvas, args[0],
&newX) != TCL_OK)
|| (Tk_CanvasGetCoordFromObj(interp, canvas, args[1],
&newY) != TCL_OK)) {
return TCL_ERROR;
}
TranslateGroup(canvas, itemPtr, newX - groupPtr->posn[0], newY - groupPtr->posn[1]);
} else {
sprintf(c0,"%d",argc);
Tcl_AppendResult(interp, "wrong # coordinates: expected 0 or 4, got ",
c0, NULL);
return TCL_ERROR;
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* ConfigureGroup --
*
* This procedure is invoked to configure various aspects
* of a rectangle or oval item, such as its border and
* background colors.
*
* Results:
* A standard Tcl result code. If an error occurs, then
* an error message is left in Tcl_GetResult(interp).
*
* Side effects:
* Configuration information, such as colors and stipple
* patterns, may be set for itemPtr.
*
*--------------------------------------------------------------
*/
static int
ConfigureGroup(interp, canvas, itemPtr, argc, args, flags)
Tcl_Interp *interp; /* Used for error reporting. */
Tk_Canvas canvas; /* Canvas containing itemPtr. */
Tk_Item *itemPtr; /* Rectangle item to reconfigure. */
int argc; /* Number of elements in args. */
Tcl_Obj *CONST *args; /* Arguments describing things to configure. */
int flags; /* Flags to pass to Tk_ConfigureWidget. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
GroupItem *groupPtr = (GroupItem *) itemPtr;
Tk_Item *saveGroup = canvasPtr->activeGroup;
Tk_State state = Tk_GetItemState(canvas, itemPtr);
int i;
Tk_Window tkwin = Tk_CanvasTkwin(canvas);
if (Tk_ConfigureWidget(interp, tkwin, configSpecs, argc, args,
(char *) groupPtr, flags|TK_CONFIG_OBJS) != TCL_OK) {
return TCL_ERROR;
}
/*
* A few of the options require additional processing, such as
* graphics contexts.
*/
itemPtr->redraw_flags &= ~TK_ITEM_STATE_DEPENDANT;
ComputeGroupBbox(canvas, groupPtr);
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* DeleteGroup --
*
* This procedure is called to clean up the data structure
* associated with a rectangle or oval item.
*
* Results:
* None.
*
* Side effects:
* Resources associated with itemPtr are released.
*
*--------------------------------------------------------------
*/
static void
DeleteGroup(canvas, itemPtr, display)
Tk_Canvas canvas; /* Info about overall widget. */
Tk_Item *itemPtr; /* Item that is being deleted. */
Display *display; /* Display containing window for
* canvas. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
GroupItem *groupPtr = (GroupItem *) itemPtr;
Tk_Item *saveGroup = canvasPtr->activeGroup;
Tk_State state = Tk_GetItemState(canvas, itemPtr);
int i;
canvasPtr->activeGroup = itemPtr;
for (i=groupPtr->numMembers-1; i >= 0; i--) {
Tk_Item *subitemPtr = groupPtr->members[i];
TkGroupRemoveItem(subitemPtr);
#ifdef DELETE_GROUP_DELETES_MEMBERS
if (subitemPtr != NULL) {
(*subitemPtr->typePtr->deleteProc)(canvas, subitemPtr, display);
}
#endif
}
canvasPtr->activeGroup = saveGroup;
if (groupPtr->members) {
ckfree((char *) groupPtr->members);
}
}
void
TkGroupRemoveItem(itemPtr)
Tk_Item *itemPtr;
{
GroupItem *groupPtr = (GroupItem *) (itemPtr->group);
if (groupPtr != NULL) {
int i;
for (i=groupPtr->numMembers-1; i >= 0; i--) {
if (groupPtr->members[i] == itemPtr) {
int j;
for (j=i+1; j < groupPtr->numMembers; j++) {
groupPtr->members[j-1] = groupPtr->members[j];
}
itemPtr->redraw_flags |= FORCE_REDRAW;
groupPtr->numMembers--;
itemPtr->group = NULL;
return;
}
}
}
itemPtr->group = NULL;
LangDebug("Cannot find %d in %d\n",itemPtr->id, groupPtr->header.id);
}
/*
*--------------------------------------------------------------
*
* ComputeGroupBbox --
*
* This procedure is invoked to compute the bounding box of
* all the pixels that may be drawn as part of a rectangle
* or oval.
*
* Results:
* None.
*
* Side effects:
* The fields x1, y1, x2, and y2 are updated in the header
* for itemPtr.
*
*--------------------------------------------------------------
*/
/* ARGSUSED */
static void
ComputeGroupBbox(canvas, groupPtr)
Tk_Canvas canvas; /* Canvas that contains item. */
GroupItem *groupPtr; /* Item whose bbox is to be
* recomputed. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
Tk_Item *saveGroup = canvasPtr->activeGroup;
Tk_State state = Tk_GetItemState(canvas, &groupPtr->header);
int seen = 0;
int i;
canvasPtr->activeGroup = &groupPtr->header;
for (i=0; i < groupPtr->numMembers; i++) {
Tk_Item *subitemPtr = groupPtr->members[i];
if (subitemPtr != NULL) {
if (Tk_GetItemState(canvas, subitemPtr) == TK_STATE_HIDDEN) {
continue;
}
if (seen++ == 0) {
groupPtr->header.x1 = subitemPtr->x1;
groupPtr->header.y1 = subitemPtr->y1;
groupPtr->header.x2 = subitemPtr->x2;
groupPtr->header.y2 = subitemPtr->y2;
} else {
if (subitemPtr->x1 < groupPtr->header.x1) {
groupPtr->header.x1 = subitemPtr->x1;
}
if (subitemPtr->y1 < groupPtr->header.y1) {
groupPtr->header.y1 = subitemPtr->y1;
}
if (subitemPtr->x2 > groupPtr->header.x2) {
groupPtr->header.x2 = subitemPtr->x2;
}
if (subitemPtr->y2 > groupPtr->header.y2) {
groupPtr->header.y2 = subitemPtr->y2;
}
}
}
}
canvasPtr->activeGroup = saveGroup;
/* If all items were hidden then have a "null" bbox */
if (seen == 0) {
groupPtr->header.x1 = groupPtr->posn[0];
groupPtr->header.y1 = groupPtr->posn[1];
groupPtr->header.x2 = groupPtr->header.x1;
groupPtr->header.y2 = groupPtr->header.y1;
}
}
/*
*--------------------------------------------------------------
*
* DisplayGroup --
*
* This procedure is invoked to draw a rectangle or oval
* item in a given drawable.
*
* Results:
* None.
*
* Side effects:
* ItemPtr is drawn in drawable using the transformation
* information in canvas.
*
*--------------------------------------------------------------
*/
static void
DisplayGroup(canvas, itemPtr, display, drawable, x, y, width, height)
Tk_Canvas canvas; /* Canvas that contains item. */
Tk_Item *itemPtr; /* Item to be displayed. */
Display *display; /* Display on which to draw item. */
Drawable drawable; /* Pixmap or window in which to draw
* item. */
int x, y, width, height; /* Describes region of canvas that
* must be redisplayed (not used). */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
GroupItem *groupPtr = (GroupItem *) itemPtr;
Tk_Item *saveGroup = canvasPtr->activeGroup;
Tk_State state = Tk_GetItemState(canvas, itemPtr);
int i;
if (state == TK_STATE_HIDDEN) {
return;
}
canvasPtr->activeGroup = itemPtr;
for (i=0; i < groupPtr->numMembers; i++) {
Tk_Item *subitemPtr = groupPtr->members[i];
if (subitemPtr != NULL) {
if (Tk_GetItemState(canvas, subitemPtr) == TK_STATE_HIDDEN) {
continue;
}
if (drawable != None ||
(subitemPtr->typePtr->alwaysRedraw & 1)) {
if (subitemPtr->updateCmd) {
if (canvasPtr->updateCmds == NULL) {
canvasPtr->updateCmds = Tcl_NewListObj(0,NULL);
}
Tcl_IncrRefCount(subitemPtr->updateCmd);
Tcl_ListObjAppendElement(canvasPtr->interp,canvasPtr->updateCmds,
subitemPtr->updateCmd);
}
(*subitemPtr->typePtr->displayProc)(canvas, subitemPtr, display,
drawable, x, y, width, height);
}
}
}
canvasPtr->activeGroup = saveGroup;
}
/*
*--------------------------------------------------------------
*
* GroupToPoint --
*
* Computes the distance from a given point to a given
* group, in canvas units.
*
* Results:
* The return value is 0 if the point whose x and y coordinates
* are coordPtr[0] and coordPtr[1] is inside the group. If the
* point isn't inside the rectangle then the return value is the
* distance from the point to the group.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
/* ARGSUSED */
static double
GroupToPoint(canvas, itemPtr, pointPtr)
Tk_Canvas canvas; /* Canvas containing item. */
Tk_Item *itemPtr; /* Item to check against point. */
double *pointPtr; /* Pointer to x and y coordinates. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
GroupItem *groupPtr = (GroupItem *) itemPtr;
Tk_Item *saveGroup = canvasPtr->activeGroup;
Tk_State state = Tk_GetItemState(canvas, itemPtr);
int i;
double best = 1.0e36;
if (state == TK_STATE_HIDDEN) {
return best;
}
/* If the group is active it is invisible to picking */
if (state == TK_STATE_ACTIVE) {
return best;
}
canvasPtr->activeGroup = itemPtr;
for (i=0; i < groupPtr->numMembers; i++) {
Tk_Item *subitemPtr = groupPtr->members[i];
if (subitemPtr != NULL) {
double try = (*subitemPtr->typePtr->pointProc)(canvas, subitemPtr, pointPtr);
if (try < best) {
best = try;
if (best == 0.0) {
break;
}
}
}
}
canvasPtr->activeGroup = saveGroup;
return best;
}
/*
*--------------------------------------------------------------
*
* GroupToArea --
*
* This procedure is called to determine whether an item
* lies entirely inside, entirely outside, or overlapping
* a given rectangle.
*
* Results:
* -1 is returned if the item is entirely outside the area
* given by rectPtr, 0 if it overlaps, and 1 if it is entirely
* inside the given area.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
/* ARGSUSED */
static int
GroupToArea(canvas, itemPtr, areaPtr)
Tk_Canvas canvas; /* Canvas containing item. */
Tk_Item *itemPtr; /* Item to check against rectangle. */
double *areaPtr; /* Pointer to array of four coordinates
* (x1, y1, x2, y2) describing rectangular
* area. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
GroupItem *groupPtr = (GroupItem *) itemPtr;
Tk_Item *saveGroup = canvasPtr->activeGroup;
Tk_State state = Tk_GetItemState(canvas, itemPtr);
int i;
#define ALL_OUTSIDE 1
#define ALL_INSIDE 2
int seen = ALL_INSIDE|ALL_OUTSIDE;
if (state == TK_STATE_HIDDEN) {
return -1;
}
/* If the group is active it is invisible to picking */
if (state == TK_STATE_ACTIVE) {
return -1;
}
canvasPtr->activeGroup = itemPtr;
for (i=0; i < groupPtr->numMembers; i++) {
Tk_Item *subitemPtr = groupPtr->members[i];
if (subitemPtr != NULL) {
int inner = (*subitemPtr->typePtr->areaProc)(canvas, subitemPtr, areaPtr);
if (inner < 0) /* outside */
seen &= ~ALL_INSIDE; /* clear the inside option */
if (inner == 0) /* overlap */
seen = 0;
if (inner > 0) /* inside */
seen &= ~ALL_OUTSIDE; /* clear the outside option */
if (seen == 0)
break;
}
}
canvasPtr->activeGroup = saveGroup;
switch (seen) {
case 0 :
return 0;
case ALL_INSIDE :
return 1;
default:
case ALL_OUTSIDE :
return -1;
}
}
/*
*--------------------------------------------------------------
*
* ScaleGroup --
*
* This procedure is invoked to rescale a rectangle or oval
* item.
*
* Results:
* None.
*
* Side effects:
* The rectangle or oval referred to by itemPtr is rescaled
* so that the following transformation is applied to all
* point coordinates:
* x' = originX + scaleX*(x-originX)
* y' = originY + scaleY*(y-originY)
*
*--------------------------------------------------------------
*/
static void
ScaleGroup(canvas, itemPtr, originX, originY, scaleX, scaleY)
Tk_Canvas canvas; /* Canvas containing rectangle. */
Tk_Item *itemPtr; /* Rectangle to be scaled. */
double originX, originY; /* Origin about which to scale rect. */
double scaleX; /* Amount to scale in X direction. */
double scaleY; /* Amount to scale in Y direction. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
GroupItem *groupPtr = (GroupItem *) itemPtr;
Tk_Item *saveGroup = canvasPtr->activeGroup;
Tk_State state = Tk_GetItemState(canvas, itemPtr);
int i;
groupPtr->posn[0] = originX + scaleX*(groupPtr->posn[0] - originX);
groupPtr->posn[1] = originY + scaleY*(groupPtr->posn[1] - originY);
canvasPtr->activeGroup = itemPtr;
for (i=0; i < groupPtr->numMembers; i++) {
Tk_Item *subitemPtr = groupPtr->members[i];
if (subitemPtr != NULL) {
(*subitemPtr->typePtr->scaleProc)(canvas, subitemPtr, originX, originY, scaleX, scaleY);
}
}
canvasPtr->activeGroup = saveGroup;
ComputeGroupBbox(canvas, groupPtr);
}
/*
*--------------------------------------------------------------
*
* TranslateGroup --
*
* This procedure is called to move a rectangle or oval by a
* given amount.
*
* Results:
* None.
*
* Side effects:
* The position of the rectangle or oval is offset by
* (xDelta, yDelta), and the bounding box is updated in the
* generic part of the item structure.
*
*--------------------------------------------------------------
*/
static void
TranslateGroup(canvas, itemPtr, deltaX, deltaY)
Tk_Canvas canvas; /* Canvas containing item. */
Tk_Item *itemPtr; /* Item that is being moved. */
double deltaX, deltaY; /* Amount by which item is to be
* moved. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
GroupItem *groupPtr = (GroupItem *) itemPtr;
Tk_Item *saveGroup = canvasPtr->activeGroup;
int i;
groupPtr->posn[0] += deltaX;
groupPtr->posn[1] += deltaY;
canvasPtr->activeGroup = itemPtr;
for (i=0; i < groupPtr->numMembers; i++) {
Tk_Item *subitemPtr = groupPtr->members[i];
if (subitemPtr != NULL) {
(*subitemPtr->typePtr->translateProc)(canvas, subitemPtr, deltaX, deltaY);
}
}
canvasPtr->activeGroup = saveGroup;
ComputeGroupBbox(canvas, groupPtr);
}
/*
*--------------------------------------------------------------
*
* GroupToPostscript --
*
* This procedure is called to generate Postscript for
* rectangle and oval items.
*
* Results:
* The return value is a standard Tcl result. If an error
* occurs in generating Postscript then an error message is
* left in Tcl_GetResult(interp), replacing whatever used to be there.
* If no error occurs, then Postscript for the rectangle is
* appended to the result.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
static int
GroupToPostscript(interp, canvas, itemPtr, prepass)
Tcl_Interp *interp; /* Interpreter for error reporting. */
Tk_Canvas canvas; /* Information about overall canvas. */
Tk_Item *itemPtr; /* Item for which Postscript is
* wanted. */
int prepass; /* 1 means this is a prepass to
* collect font information; 0 means
* final Postscript is being created. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
GroupItem *groupPtr = (GroupItem *) itemPtr;
Tk_Item *saveGroup = canvasPtr->activeGroup;
Tk_State state = Tk_GetItemState(canvas, itemPtr);
int code = TCL_OK;
int i;
if (state == TK_STATE_HIDDEN) {
return code;
}
canvasPtr->activeGroup = itemPtr;
for (i=0; i < groupPtr->numMembers; i++) {
Tk_Item *subitemPtr = groupPtr->members[i];
if (subitemPtr != NULL) {
if (Tk_GetItemState(canvas, subitemPtr) == TK_STATE_HIDDEN) {
continue;
}
code = (*subitemPtr->typePtr->postscriptProc)(interp, canvas,
subitemPtr, prepass);
if (code != TCL_OK) {
break;
}
}
}
canvasPtr->activeGroup = saveGroup;
return code;
}
static int
GroupIndex(interp, canvas, itemPtr, obj, indexPtr)
Tcl_Interp *interp;
Tk_Canvas canvas;
Tk_Item *itemPtr;
Tcl_Obj *obj;
int *indexPtr;
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
GroupItem *groupPtr = (GroupItem *) itemPtr;
Tk_Item *saveGroup = canvasPtr->activeGroup;
Tk_State state = Tk_GetItemState(canvas, itemPtr);
int i;
int length;
int id;
char *string;
double point[2];
double bestDist;
char *end, *p;
Tcl_Obj **objv;
*indexPtr = -1;
if (Tcl_ListObjGetElements(interp, obj, &i, &objv) == TCL_OK && i == 2
&& Tk_CanvasGetCoordFromObj(interp, canvas, objv[0], &point[0]) == TCL_OK
&& Tk_CanvasGetCoordFromObj(interp, canvas, objv[1], &point[1]) == TCL_OK) {
goto doxy;
}
string = Tcl_GetStringFromObj(obj, &length);
if (string[0] == 'e') {
if (strncmp(string, "end", length) == 0) {
*indexPtr = groupPtr->numMembers;
} else {
badIndex:
/*
* Some of the paths here leave messages in interp->result,
* so we have to clear it out before storing our own message.
*/
Tcl_SetResult(interp, (char *) NULL, TCL_STATIC);
Tcl_AppendResult(interp, "bad index \"", string, "\"",
(char *) NULL);
return TCL_ERROR;
}
} else if (string[0] == '@') {
p = string+1;
point[0] = strtod(p, &end);
if ((end == p) || (*end != ',')) {
goto badIndex;
}
p = end+1;
point[1] = strtod(p, &end);
if ((end == p) || (*end != 0)) {
goto badIndex;
}
doxy:
bestDist = 1.0e36;
*indexPtr = 0;
canvasPtr->activeGroup = itemPtr;
for(i=0; i < groupPtr->numMembers; i++) {
Tk_Item *subitemPtr = groupPtr->members[i];
double dist = (*subitemPtr->typePtr->pointProc)(canvas, subitemPtr, point);
if (dist < bestDist) {
bestDist = dist;
*indexPtr = i;
}
}
canvasPtr->activeGroup = saveGroup;
} else {
if (Tcl_GetIntFromObj(interp, obj, &id) == TCL_OK) {
for(i=0; i < groupPtr->numMembers; i++) {
Tk_Item *subitemPtr = groupPtr->members[i];
if (subitemPtr != NULL && subitemPtr->id == id) {
*indexPtr = i;
return TCL_OK;
}
}
goto badIndex;
} else {
return TCL_ERROR;
}
}
return TCL_OK;
}
static int
GroupInsert(canvas, itemPtr, beforeThis, string)
Tk_Canvas canvas;
Tk_Item *itemPtr;
int beforeThis;
Tcl_Obj *string;
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
GroupItem *groupPtr = (GroupItem *) itemPtr;
Tk_Item *saveGroup = canvasPtr->activeGroup;
Tk_State state = Tk_GetItemState(canvas, itemPtr);
Tcl_Obj **objv;
int argc;
int i;
int id;
if (Tcl_ListObjGetElements(groupPtr->interp,string,&argc,&objv) == TCL_OK) {
int count = 0;
for (i=0; i < argc; i++) {
if (Tcl_GetIntFromObj(groupPtr->interp,objv[i],&id) == TCL_OK) {
Tcl_HashEntry *entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) id);
if (entryPtr != NULL) {
Tk_Item *subitemPtr = (Tk_Item *) Tcl_GetHashValue(entryPtr);
if (subitemPtr == NULL
|| subitemPtr == itemPtr
|| subitemPtr->group == itemPtr) {
continue;
}
if (subitemPtr->group != NULL) {
TkGroupRemoveItem(subitemPtr);
}
count++;
}
} else {
return TCL_ERROR;
}
}
i = count + groupPtr->numMembers;
if (i > groupPtr->numSlots) {
if (groupPtr->members == NULL) {
groupPtr->members = (Tk_Item **)ckalloc(i*sizeof(Tk_Item *));
} else {
groupPtr->members = (Tk_Item **)ckrealloc((char *)groupPtr->members,
i*sizeof(Tk_Item *));
}
if (groupPtr->members != NULL) {
groupPtr->numSlots = i;
} else {
groupPtr->numMembers = 0;
groupPtr->numSlots = 0;
Tcl_SetResult(groupPtr->interp,"Out of memory",TCL_STATIC);
return TCL_ERROR;
}
}
/* Move tail up */
for (i=groupPtr->numMembers-1; i >= beforeThis; i--) {
groupPtr->members[i+count] = groupPtr->members[i];
}
/* Fill in slots */
groupPtr->numMembers += count;
for (i=0; i < argc; i++) {
groupPtr->members[beforeThis] = NULL;
if (Tcl_GetIntFromObj(groupPtr->interp,objv[i],&id) == TCL_OK) {
Tcl_HashEntry *entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) id);
if (entryPtr != NULL) {
Tk_Item *subitemPtr = (Tk_Item *) Tcl_GetHashValue(entryPtr);
if (subitemPtr == NULL
|| subitemPtr == itemPtr
|| subitemPtr->group == itemPtr) {
continue;
}
subitemPtr->group = itemPtr;
subitemPtr->redraw_flags |= FORCE_REDRAW;
groupPtr->members[beforeThis] = subitemPtr;
beforeThis++;
count--;
}
}
}
if (count != 0) {
abort();
}
ComputeGroupBbox(groupPtr->canvas, groupPtr);
return TCL_OK;
} else {
return TCL_ERROR;
}
}
/* Just like above but with void return to go in the function table */
static void
GroupInsertProc(canvas, itemPtr, beforeThis, string)
Tk_Canvas canvas;
Tk_Item *itemPtr;
int beforeThis;
Tcl_Obj *string;
{
GroupInsert(canvas, itemPtr, beforeThis, string);
}
static void
GroupDChars(canvas, itemPtr, first, last)
Tk_Canvas canvas;
Tk_Item *itemPtr;
int first;
int last;
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
GroupItem *groupPtr = (GroupItem *) itemPtr;
Tk_Item *saveGroup = canvasPtr->activeGroup;
Tk_State state = Tk_GetItemState(canvas, itemPtr);
int i;
if (first < 0) {
first = 0;
}
if (last >= groupPtr->numMembers) {
last = groupPtr->numMembers-1;
}
if (first > last) {
return;
}
for (i=last; i >= first; i--) {
TkGroupRemoveItem(groupPtr->members[i]);
}
ComputeGroupBbox(groupPtr->canvas, groupPtr);
}
static int
MembersParseProc(clientData,interp,tkwin,value,recordPtr,offset)
ClientData clientData;
Tcl_Interp *interp;
Tk_Window tkwin;
Tcl_Obj * value;
char *recordPtr;
int offset;
{
Tk_Item *itemPtr = (Tk_Item *) recordPtr;
GroupItem *groupPtr = (GroupItem *) itemPtr;
int code = TCL_OK;
Tk_CanvasEventuallyRedraw(groupPtr->canvas, itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
GroupDChars(groupPtr->canvas, itemPtr, 0, groupPtr->numMembers-1);
code = GroupInsert(groupPtr->canvas, itemPtr, 0, value);
Tk_CanvasEventuallyRedraw(groupPtr->canvas, itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
return code;
}
static Tcl_Obj *
MembersPrintProc(clientData,tkwin,recordPtr,offset,freeProcPtr)
ClientData clientData;
Tk_Window tkwin;
char *recordPtr;
int offset;
Tcl_FreeProc **freeProcPtr;
{
GroupItem *groupPtr = (GroupItem *) recordPtr;
Tcl_Obj *result = Tcl_NewListObj(0,NULL);
int i;
for (i=0; i < groupPtr->numMembers; i++) {
Tk_Item *subitemPtr = groupPtr->members[i];
if (subitemPtr != NULL) {
Tcl_ListObjAppendElement(groupPtr->interp,result,
Tcl_NewIntObj(subitemPtr->id));
}
}
return result;
}