/*
* tkCanvas.c --
*
* This module implements canvas widgets for the Tk toolkit.
* A canvas displays a background and a collection of graphical
* objects such as rectangles, lines, and texts.
*
* Copyright (c) 1991-1994 The Regents of the University of California.
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
* Copyright (c) 1998-1999 by Scriptics Corporation.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* RCS: @(#) $Id: tkCanvas.c,v 1.21 2003/02/09 07:48:22 hobbs Exp $
*/
/* #define USE_OLD_TAG_SEARCH 1 */
#include "default.h"
#include "tkInt.h"
#include "tkPort.h"
#include "tkCanvases.h"
/*
* Structure needed for the canvas visitor
*/
typedef struct VisitAssocData {
struct VisitAssocData *nextPtr; /* pointer to next OptionAssocData */
struct Tk_VisitorType visitorType; /* (remaining chars) */
} VisitAssocData;
/*
* See tkCanvas.h for key data structures used to implement canvases.
*/
#ifdef USE_OLD_TAG_SEARCH
/*
* The structure defined below is used to keep track of a tag search
* in progress. No field should be accessed by anyone other than
* StartTagSearch and NextItem.
*/
typedef struct TagSearch {
TkCanvas *canvasPtr; /* Canvas widget being searched. */
Tk_Uid tag; /* Tag to search for. 0 means return
* all items. */
Tk_Item *currentPtr; /* Pointer to last item returned. */
Tk_Item *lastPtr; /* The item right before the currentPtr
* is tracked so if the currentPtr is
* deleted we don't have to start from the
* beginning. */
int searchOver; /* Non-zero means NextItem should always
* return NULL. */
} TagSearch;
#else /* USE_OLD_TAG_SEARCH */
/*
* The structure defined below is used to keep track of a tag search
* in progress. No field should be accessed by anyone other than
* TagSearchScan, TagSearchFirst, TagSearchNext,
* TagSearchScanExpr, TagSearchEvalExpr,
* TagSearchExprInit, TagSearchExprDestroy,
* TagSearchDestroy.
* (
* Not quite accurate: the TagSearch structure is also accessed from:
* CanvasWidgetCmd, FindItems, RelinkItems
* The only instances of the structure are owned by:
* CanvasWidgetCmd
* CanvasWidgetCmd is the only function that calls:
* FindItems, RelinkItems
* CanvasWidgetCmd, FindItems, RelinkItems, are the only functions that call
* TagSearch*
* )
*/
typedef struct TagSearch {
TkCanvas *canvasPtr; /* Canvas widget being searched. */
Tk_Item *currentPtr; /* Pointer to last item returned. */
Tk_Item *lastPtr; /* The item right before the currentPtr
* is tracked so if the currentPtr is
* deleted we don't have to start from the
* beginning. */
int searchOver; /* Non-zero means NextItem should always
* return NULL. */
int type; /* search type */
int id; /* item id for searches by id */
char *string; /* tag expression string */
int stringIndex; /* current position in string scan */
int stringLength; /* length of tag expression string */
char *rewritebuffer; /* tag string (after removing escapes) */
unsigned int rewritebufferAllocated; /* available space for rewrites */
TagSearchExpr *expr; /* compiled tag expression */
} TagSearch;
#endif /* USE_OLD_TAG_SEARCH */
/*
* Custom option for handling "-state" and "-offset"
*/
static Tk_CustomOption stateOption = {
TkStateParseProc,
TkStatePrintProc,
(ClientData) NULL /* only "normal" and "disabled" */
};
static Tk_CustomOption tileOption = {
Tk_TileParseProc,
Tk_TilePrintProc,
(ClientData) NULL
};
static Tk_CustomOption offsetOption = {
Tk_OffsetParseProc,
Tk_OffsetPrintProc,
(ClientData) TK_OFFSET_RELATIVE
};
static int CanvGroupParseProc _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, Tk_Window tkwin, Tcl_Obj * value, char *widgRec,
int offset));
static Tcl_Obj * CanvGroupPrintProc _ANSI_ARGS_((ClientData clientData,
Tk_Window tkwin, char *widgRec, int offset,
Tcl_FreeProc **freeProcPtr));
static Tk_CustomOption groupOption = {
CanvGroupParseProc,
CanvGroupPrintProc,
(ClientData) NULL
};
/*
* Information used for argv parsing.
*/
static Tk_ConfigSpec configSpecs[] = {
{TK_CONFIG_CUSTOM, "-activegroup", "activeGroup", "ActiveGroup", "0",
Tk_Offset(TkCanvas, activeGroup), TK_CONFIG_DONT_SET_DEFAULT, &groupOption},
{TK_CONFIG_BORDER, "-background", "background", "Background",
DEF_CANVAS_BG_COLOR, Tk_Offset(TkCanvas, bgBorder),
TK_CONFIG_COLOR_ONLY},
{TK_CONFIG_BORDER, "-background", "background", "Background",
DEF_CANVAS_BG_MONO, Tk_Offset(TkCanvas, bgBorder),
TK_CONFIG_MONO_ONLY},
{TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
(char *) NULL, 0, 0},
{TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
(char *) NULL, 0, 0},
{TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
DEF_CANVAS_BORDER_WIDTH, Tk_Offset(TkCanvas, borderWidth), 0},
{TK_CONFIG_DOUBLE, "-closeenough", "closeEnough", "CloseEnough",
DEF_CANVAS_CLOSE_ENOUGH, Tk_Offset(TkCanvas, closeEnough), 0},
{TK_CONFIG_BOOLEAN, "-confine", "confine", "Confine",
DEF_CANVAS_CONFINE, Tk_Offset(TkCanvas, confine), 0},
{TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
DEF_CANVAS_CURSOR, Tk_Offset(TkCanvas, cursor), TK_CONFIG_NULL_OK},
{TK_CONFIG_CUSTOM, "-disabledtile", "disabledtile", "Tile", (char *) NULL,
Tk_Offset(TkCanvas, disabledTile), TK_CONFIG_DONT_SET_DEFAULT, &tileOption},
{TK_CONFIG_PIXELS, "-height", "height", "Height",
DEF_CANVAS_HEIGHT, Tk_Offset(TkCanvas, height), 0},
{TK_CONFIG_COLOR, "-highlightbackground", "highlightBackground",
"HighlightBackground", DEF_CANVAS_HIGHLIGHT_BG,
Tk_Offset(TkCanvas, highlightBgColorPtr), 0},
{TK_CONFIG_COLOR, "-highlightcolor", "highlightColor", "HighlightColor",
DEF_CANVAS_HIGHLIGHT, Tk_Offset(TkCanvas, highlightColorPtr), 0},
{TK_CONFIG_PIXELS, "-highlightthickness", "highlightThickness",
"HighlightThickness",
DEF_CANVAS_HIGHLIGHT_WIDTH, Tk_Offset(TkCanvas, highlightWidth), 0},
{TK_CONFIG_BORDER, "-insertbackground", "insertBackground", "Foreground",
DEF_CANVAS_INSERT_BG, Tk_Offset(TkCanvas, textInfo.insertBorder), 0},
{TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
DEF_CANVAS_INSERT_BD_COLOR,
Tk_Offset(TkCanvas, textInfo.insertBorderWidth), TK_CONFIG_COLOR_ONLY},
{TK_CONFIG_PIXELS, "-insertborderwidth", "insertBorderWidth", "BorderWidth",
DEF_CANVAS_INSERT_BD_MONO,
Tk_Offset(TkCanvas, textInfo.insertBorderWidth), TK_CONFIG_MONO_ONLY},
{TK_CONFIG_INT, "-insertofftime", "insertOffTime", "OffTime",
DEF_CANVAS_INSERT_OFF_TIME, Tk_Offset(TkCanvas, insertOffTime), 0},
{TK_CONFIG_INT, "-insertontime", "insertOnTime", "OnTime",
DEF_CANVAS_INSERT_ON_TIME, Tk_Offset(TkCanvas, insertOnTime), 0},
{TK_CONFIG_PIXELS, "-insertwidth", "insertWidth", "InsertWidth",
DEF_CANVAS_INSERT_WIDTH, Tk_Offset(TkCanvas, textInfo.insertWidth), 0},
{TK_CONFIG_CUSTOM, "-offset", "offset", "Offset", "0 0",
Tk_Offset(TkCanvas, tsoffset),TK_CONFIG_DONT_SET_DEFAULT,
&offsetOption},
{TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
DEF_CANVAS_RELIEF, Tk_Offset(TkCanvas, relief), 0},
{TK_CONFIG_LANGARG, "-scrollregion", "scrollRegion", "ScrollRegion",
DEF_CANVAS_SCROLL_REGION, Tk_Offset(TkCanvas, regionArg),
TK_CONFIG_NULL_OK},
{TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
DEF_CANVAS_SELECT_COLOR, Tk_Offset(TkCanvas, textInfo.selBorder),
TK_CONFIG_COLOR_ONLY},
{TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
DEF_CANVAS_SELECT_MONO, Tk_Offset(TkCanvas, textInfo.selBorder),
TK_CONFIG_MONO_ONLY},
{TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
DEF_CANVAS_SELECT_BD_COLOR,
Tk_Offset(TkCanvas, textInfo.selBorderWidth), TK_CONFIG_COLOR_ONLY},
{TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
DEF_CANVAS_SELECT_BD_MONO, Tk_Offset(TkCanvas, textInfo.selBorderWidth),
TK_CONFIG_MONO_ONLY},
{TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
DEF_CANVAS_SELECT_FG_COLOR, Tk_Offset(TkCanvas, textInfo.selFgColorPtr),
TK_CONFIG_COLOR_ONLY},
{TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
DEF_CANVAS_SELECT_FG_MONO, Tk_Offset(TkCanvas, textInfo.selFgColorPtr),
TK_CONFIG_MONO_ONLY},
{TK_CONFIG_CUSTOM, "-state", "state", "State",
"normal", Tk_Offset(TkCanvas, canvas_state), TK_CONFIG_DONT_SET_DEFAULT,
&stateOption},
{TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
DEF_CANVAS_TAKE_FOCUS, Tk_Offset(TkCanvas, takeFocus),
TK_CONFIG_NULL_OK},
{TK_CONFIG_CUSTOM, "-tile", "tile", "Tile", (char *) NULL,
Tk_Offset(TkCanvas, tile),TK_CONFIG_DONT_SET_DEFAULT, &tileOption},
{TK_CONFIG_PIXELS, "-width", "width", "Width",
DEF_CANVAS_WIDTH, Tk_Offset(TkCanvas, width), 0},
{TK_CONFIG_CALLBACK, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
DEF_CANVAS_X_SCROLL_CMD, Tk_Offset(TkCanvas, xScrollCmd),
TK_CONFIG_NULL_OK},
{TK_CONFIG_PIXELS, "-xscrollincrement", "xScrollIncrement",
"ScrollIncrement",
DEF_CANVAS_X_SCROLL_INCREMENT, Tk_Offset(TkCanvas, xScrollIncrement),
0},
{TK_CONFIG_CALLBACK, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
DEF_CANVAS_Y_SCROLL_CMD, Tk_Offset(TkCanvas, yScrollCmd),
TK_CONFIG_NULL_OK},
{TK_CONFIG_PIXELS, "-yscrollincrement", "yScrollIncrement",
"ScrollIncrement",
DEF_CANVAS_Y_SCROLL_INCREMENT, Tk_Offset(TkCanvas, yScrollIncrement),
0},
{TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
(char *) NULL, 0, 0}
};
/*
* List of all the item types known at present:
*/
static Tk_ItemType *typeList = NULL; /* NULL means initialization hasn't
* been done yet. */
#ifndef USE_OLD_TAG_SEARCH
/*
* Uids for operands in compiled advanced tag search expressions
* Initialization is done by InitCanvas()
*/
static Tk_Uid allUid = NULL;
static Tk_Uid currentUid = NULL;
static Tk_Uid andUid = NULL;
static Tk_Uid orUid = NULL;
static Tk_Uid xorUid = NULL;
static Tk_Uid parenUid = NULL;
static Tk_Uid negparenUid = NULL;
static Tk_Uid endparenUid = NULL;
static Tk_Uid tagvalUid = NULL;
static Tk_Uid negtagvalUid = NULL;
#endif /* USE_OLD_TAG_SEARCH */
/*
* Standard item types provided by Tk:
*/
extern Tk_ItemType tkArcType, tkBitmapType, tkImageType, tkLineType;
extern Tk_ItemType tkOvalType, tkPolygonType;
extern Tk_ItemType tkRectangleType, tkTextType, tkWindowType;
/*
* Prototypes for procedures defined later in this file:
*/
static void CanvasBindProc _ANSI_ARGS_((ClientData clientData,
XEvent *eventPtr));
static void CanvasBlinkProc _ANSI_ARGS_((ClientData clientData));
static void CanvasCmdDeletedProc _ANSI_ARGS_((
ClientData clientData));
static void CanvasDoEvent _ANSI_ARGS_((TkCanvas *canvasPtr,
XEvent *eventPtr));
static void CanvasEventProc _ANSI_ARGS_((ClientData clientData,
XEvent *eventPtr));
static int CanvasFetchSelection _ANSI_ARGS_((
ClientData clientData, int offset,
char *buffer, int maxBytes));
static Tk_Item * CanvasFindClosest _ANSI_ARGS_((TkCanvas *canvasPtr,
double coords[2]));
static void CanvasFocusProc _ANSI_ARGS_((TkCanvas *canvasPtr,
int gotFocus));
static void CanvasLostSelection _ANSI_ARGS_((
ClientData clientData));
static void CanvasSelectTo _ANSI_ARGS_((TkCanvas *canvasPtr,
Tk_Item *itemPtr, int index));
static void CanvasSetOrigin _ANSI_ARGS_((TkCanvas *canvasPtr,
int xOrigin, int yOrigin));
static void CanvasUpdateScrollbars _ANSI_ARGS_((
TkCanvas *canvasPtr));
static int CanvasWidgetCmd _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp, int argc, Tcl_Obj *CONST *args));
static void CanvasWorldChanged _ANSI_ARGS_((
ClientData instanceData));
static int ConfigureCanvas _ANSI_ARGS_((Tcl_Interp *interp,
TkCanvas *canvasPtr, int argc, Tcl_Obj *CONST *args,
int flags));
static void DestroyCanvas _ANSI_ARGS_((char *memPtr));
static void DisplayCanvas _ANSI_ARGS_((ClientData clientData));
static void DoItem _ANSI_ARGS_((Tcl_Interp *interp,
Tk_Item *itemPtr, Tk_Uid tag));
static void EventuallyRedrawItem _ANSI_ARGS_((Tk_Canvas canvas,
Tk_Item *itemPtr));
#ifdef USE_OLD_TAG_SEARCH
static int FindItems _ANSI_ARGS_((Tcl_Interp *interp,
TkCanvas *canvasPtr, int argc, Tcl_Obj *CONST *args,
Tcl_Obj *newTagObj, int first));
#else /* USE_OLD_TAG_SEARCH */
static int FindItems _ANSI_ARGS_((Tcl_Interp *interp,
TkCanvas *canvasPtr, int argc, Tcl_Obj *CONST *args,
Tcl_Obj *newTagObj, int first,
TagSearch **searchPtrPtr));
#endif /* USE_OLD_TAG_SEARCH */
static int FindArea _ANSI_ARGS_((Tcl_Interp *interp,
TkCanvas *canvasPtr, Tcl_Obj *CONST *args, Tk_Uid uid,
int enclosed));
static double GridAlign _ANSI_ARGS_((double coord, double spacing));
static CONST char** GetStringsFromObjs _ANSI_ARGS_((int argc,
Tcl_Obj *CONST *objv));
static void TileChangedProc _ANSI_ARGS_((ClientData clientData,
Tk_Tile tile));
static void InitCanvas _ANSI_ARGS_((void));
#ifdef USE_OLD_TAG_SEARCH
static Tk_Item * NextItem _ANSI_ARGS_((TagSearch *searchPtr));
#endif /* USE_OLD_TAG_SEARCH */
static void PickCurrentItem _ANSI_ARGS_((TkCanvas *canvasPtr,
XEvent *eventPtr));
static Tcl_Obj * ScrollFractions _ANSI_ARGS_((int screen1,
int screen2, int object1, int object2));
#ifdef USE_OLD_TAG_SEARCH
static void RelinkItems _ANSI_ARGS_((TkCanvas *canvasPtr,
Tcl_Obj *tag, Tk_Item *prevPtr));
static Tk_Item * StartTagSearch _ANSI_ARGS_((TkCanvas *canvasPtr,
Tcl_Obj *tag, TagSearch *searchPtr));
#else /* USE_OLD_TAG_SEARCH */
static int RelinkItems _ANSI_ARGS_((TkCanvas *canvasPtr,
Tcl_Obj *tag, Tk_Item *prevPtr,
TagSearch **searchPtrPtr));
static void TagSearchExprInit _ANSI_ARGS_ ((
TagSearchExpr **exprPtrPtr));
static void TagSearchExprDestroy _ANSI_ARGS_((TagSearchExpr *expr));
static void TagSearchDestroy _ANSI_ARGS_((TagSearch *searchPtr));
static int TagSearchScan _ANSI_ARGS_((TkCanvas *canvasPtr,
Tcl_Obj *tag, TagSearch **searchPtrPtr));
static int TagSearchScanExpr _ANSI_ARGS_((Tcl_Interp *interp,
TagSearch *searchPtr, TagSearchExpr *expr));
static int TagSearchEvalExpr _ANSI_ARGS_((TagSearchExpr *expr,
Tk_Item *itemPtr));
static Tk_Item * TagSearchFirst _ANSI_ARGS_((TagSearch *searchPtr));
static Tk_Item * TagSearchNext _ANSI_ARGS_((TagSearch *searchPtr));
#endif /* USE_OLD_TAG_SEARCH */
/*
* The structure below defines canvas class behavior by means of procedures
* that can be invoked from generic window code.
*/
static Tk_ClassProcs canvasClass = {
sizeof(Tk_ClassProcs), /* size */
CanvasWorldChanged, /* worldChangedProc */
};
/*
*--------------------------------------------------------------
*
* Tk_CanvasObjCmd --
*
* This procedure is invoked to process the "canvas" Tcl
* command. See the user documentation for details on what
* it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*--------------------------------------------------------------
*/
int
Tk_CanvasObjCmd(clientData, interp, argc, objv)
ClientData clientData; /* Main window associated with
* interpreter. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
Tk_Window tkwin = (Tk_Window) clientData;
TkCanvas *canvasPtr;
Tk_Window new;
if (typeList == NULL) {
InitCanvas();
}
if (argc < 2) {
Tcl_WrongNumArgs(interp, 1, argv, "pathName ?options?");
return TCL_ERROR;
}
new = Tk_CreateWindowFromPath(interp, tkwin,
Tcl_GetString(objv[1]), (char *) NULL);
if (new == NULL) {
return TCL_ERROR;
}
/*
* Initialize fields that won't be initialized by ConfigureCanvas,
* or which ConfigureCanvas expects to have reasonable values
* (e.g. resource pointers).
*/
canvasPtr = (TkCanvas *) ckalloc(sizeof(TkCanvas));
canvasPtr->tkwin = new;
canvasPtr->display = Tk_Display(new);
canvasPtr->interp = interp;
canvasPtr->widgetCmd = Lang_CreateWidget(interp,
canvasPtr->tkwin, CanvasWidgetCmd,
(ClientData) canvasPtr, CanvasCmdDeletedProc);
canvasPtr->firstItemPtr = NULL;
canvasPtr->lastItemPtr = NULL;
canvasPtr->borderWidth = 0;
canvasPtr->bgBorder = NULL;
canvasPtr->relief = TK_RELIEF_FLAT;
canvasPtr->highlightWidth = 0;
canvasPtr->highlightBgColorPtr = NULL;
canvasPtr->highlightColorPtr = NULL;
canvasPtr->inset = 0;
canvasPtr->pixmapGC = None;
canvasPtr->width = None;
canvasPtr->height = None;
canvasPtr->confine = 0;
canvasPtr->textInfo.selBorder = NULL;
canvasPtr->textInfo.selBorderWidth = 0;
canvasPtr->textInfo.selFgColorPtr = NULL;
canvasPtr->textInfo.selItemPtr = NULL;
canvasPtr->textInfo.selectFirst = -1;
canvasPtr->textInfo.selectLast = -1;
canvasPtr->textInfo.anchorItemPtr = NULL;
canvasPtr->textInfo.selectAnchor = 0;
canvasPtr->textInfo.insertBorder = NULL;
canvasPtr->textInfo.insertWidth = 0;
canvasPtr->textInfo.insertBorderWidth = 0;
canvasPtr->textInfo.focusItemPtr = NULL;
canvasPtr->textInfo.gotFocus = 0;
canvasPtr->textInfo.cursorOn = 0;
canvasPtr->insertOnTime = 0;
canvasPtr->insertOffTime = 0;
canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
canvasPtr->xOrigin = canvasPtr->yOrigin = 0;
canvasPtr->drawableXOrigin = canvasPtr->drawableYOrigin = 0;
canvasPtr->bindingTable = NULL;
canvasPtr->currentItemPtr = NULL;
canvasPtr->newCurrentPtr = NULL;
canvasPtr->closeEnough = 0.0;
canvasPtr->pickEvent.type = LeaveNotify;
canvasPtr->pickEvent.xcrossing.x = 0;
canvasPtr->pickEvent.xcrossing.y = 0;
canvasPtr->state = 0;
canvasPtr->xScrollCmd = NULL;
canvasPtr->yScrollCmd = NULL;
canvasPtr->scrollX1 = 0;
canvasPtr->scrollY1 = 0;
canvasPtr->scrollX2 = 0;
canvasPtr->scrollY2 = 0;
canvasPtr->regionArg = NULL;
canvasPtr->xScrollIncrement = 0;
canvasPtr->yScrollIncrement = 0;
canvasPtr->scanX = 0;
canvasPtr->scanXOrigin = 0;
canvasPtr->scanY = 0;
canvasPtr->scanYOrigin = 0;
canvasPtr->hotPtr = NULL;
canvasPtr->hotPrevPtr = NULL;
canvasPtr->cursor = None;
canvasPtr->takeFocus = NULL;
canvasPtr->pixelsPerMM = WidthOfScreen(Tk_Screen(new));
canvasPtr->pixelsPerMM /= WidthMMOfScreen(Tk_Screen(new));
canvasPtr->flags = 0;
canvasPtr->nextId = 1;
canvasPtr->psInfo = NULL;
canvasPtr->canvas_state = TK_STATE_NORMAL;
canvasPtr->tile = NULL;
canvasPtr->disabledTile = NULL;
canvasPtr->tsoffset.flags = 0;
canvasPtr->tsoffset.xoffset = 0;
canvasPtr->tsoffset.yoffset = 0;
#ifndef USE_OLD_TAG_SEARCH
canvasPtr->bindTagExprs = NULL;
#endif
canvasPtr->activeGroup = 0;
canvasPtr->updateCmds = NULL;
Tcl_InitHashTable(&canvasPtr->idTable, TCL_ONE_WORD_KEYS);
Tk_SetClass(canvasPtr->tkwin, "Canvas");
Tk_SetClassProcs(canvasPtr->tkwin, &canvasClass, (ClientData) canvasPtr);
Tk_CreateEventHandler(canvasPtr->tkwin,
ExposureMask|StructureNotifyMask|FocusChangeMask,
CanvasEventProc, (ClientData) canvasPtr);
Tk_CreateEventHandler(canvasPtr->tkwin, KeyPressMask|KeyReleaseMask
|ButtonPressMask|ButtonReleaseMask|EnterWindowMask
|LeaveWindowMask|PointerMotionMask|VirtualEventMask,
CanvasBindProc, (ClientData) canvasPtr);
Tk_CreateSelHandler(canvasPtr->tkwin, XA_PRIMARY, XA_STRING,
CanvasFetchSelection, (ClientData) canvasPtr, XA_STRING);
if (ConfigureCanvas(interp, canvasPtr, argc-2, (Tcl_Obj **)objv+2, 0) != TCL_OK) {
goto error;
}
Tcl_SetResult(interp, Tk_PathName(canvasPtr->tkwin), TCL_STATIC);
return TCL_OK;
error:
Tk_DestroyWindow(canvasPtr->tkwin);
return TCL_ERROR;
}
/*
*--------------------------------------------------------------
*
* CanvasWidgetCmd --
*
* This procedure is invoked to process the Tcl command
* that corresponds to a widget managed by this module.
* See the user documentation for details on what it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*--------------------------------------------------------------
*/
static int
CanvasWidgetCmd(clientData, interp, objc, objv)
ClientData clientData; /* Information about canvas
* widget. */
Tcl_Interp *interp; /* Current interpreter. */
int objc; /* Number of arguments. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
{
TkCanvas *canvasPtr = (TkCanvas *) clientData;
unsigned int length;
int c, result;
Tk_Item *itemPtr = NULL; /* Initialization needed only to
* prevent compiler warning. */
#ifdef USE_OLD_TAG_SEARCH
TagSearch search;
#else /* USE_OLD_TAG_SEARCH */
TagSearch *searchPtr = NULL; /* Allocated by first TagSearchScan
* Freed by TagSearchDestroy */
#endif /* USE_OLD_TAG_SEARCH */
int index;
static CONST char *optionStrings[] = {
"addtag", "bbox", "bind", "canvasx",
"canvasy", "cget", "configure", "coords",
"create", "dchars", "delete", "dtag",
"find", "focus", "gettags", "icursor",
"index", "insert", "itemcget", "itemconfigure",
"lower", "move", "postscript", "raise",
"scale", "scan", "select", "type",
"xview", "yview",
NULL
};
enum options {
CANV_ADDTAG, CANV_BBOX, CANV_BIND, CANV_CANVASX,
CANV_CANVASY, CANV_CGET, CANV_CONFIGURE, CANV_COORDS,
CANV_CREATE, CANV_DCHARS, CANV_DELETE, CANV_DTAG,
CANV_FIND, CANV_FOCUS, CANV_GETTAGS, CANV_ICURSOR,
CANV_INDEX, CANV_INSERT, CANV_ITEMCGET, CANV_ITEMCONFIGURE,
CANV_LOWER, CANV_MOVE, CANV_POSTSCRIPT,CANV_RAISE,
CANV_SCALE, CANV_SCAN, CANV_SELECT, CANV_TYPE,
CANV_XVIEW, CANV_YVIEW,
CANV_VISITOR
};
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "option ?arg arg ...?");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[1], optionStrings, "option", 0,
&index) != TCL_OK) {
#ifdef NOT_YET
if (Tk_GetCanvasVisitor(interp, Tcl_GetStringFromObj(args[1],NULL))) {
index = CANV_VISITOR;
args--; argc++;
} else
#endif
return TCL_ERROR;
}
Tcl_Preserve((ClientData) canvasPtr);
result = TCL_OK;
switch ((enum options) index) {
case CANV_ADDTAG: {
if (objc < 4) {
Tcl_WrongNumArgs(interp, 2, objv, "tag searchCommand ?arg arg ...?");
result = TCL_ERROR;
goto done;
}
#ifdef USE_OLD_TAG_SEARCH
result = FindItems(interp, canvasPtr, objc, objv, objv[2], 3);
#else /* USE_OLD_TAG_SEARCH */
result = FindItems(interp, canvasPtr, objc, objv, objv[2], 3, &searchPtr);
#endif /* USE_OLD_TAG_SEARCH */
break;
}
case CANV_BBOX: {
int i, gotAny;
int x1 = 0, y1 = 0, x2 = 0, y2 = 0; /* Initializations needed
* only to prevent compiler
* warnings. */
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId ?tagOrId ...?");
result = TCL_ERROR;
goto done;
}
gotAny = 0;
for (i = 2; i < objc; i++) {
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[i], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[i], &searchPtr)) != TCL_OK) {
goto done;
}
for (itemPtr = TagSearchFirst(searchPtr);
itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
if ((itemPtr->x1 >= itemPtr->x2)
|| (itemPtr->y1 >= itemPtr->y2)) {
continue;
}
if (!gotAny) {
x1 = itemPtr->x1;
y1 = itemPtr->y1;
x2 = itemPtr->x2;
y2 = itemPtr->y2;
gotAny = 1;
} else {
if (itemPtr->x1 < x1) {
x1 = itemPtr->x1;
}
if (itemPtr->y1 < y1) {
y1 = itemPtr->y1;
}
if (itemPtr->x2 > x2) {
x2 = itemPtr->x2;
}
if (itemPtr->y2 > y2) {
y2 = itemPtr->y2;
}
}
}
}
if (gotAny) {
Tcl_IntResults(interp, 4, 0, x1, y1, x2, y2);
}
break;
}
case CANV_BIND: {
ClientData object;
if ((objc < 3) || (objc > 5)) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId ?sequence? ?command?");
result = TCL_ERROR;
goto done;
}
/*
* Figure out what object to use for the binding (individual
* item vs. tag).
*/
object = 0;
#ifdef USE_OLD_TAG_SEARCH
if (isdigit(UCHAR(Tcl_GetString(objv[2])[0]))) {
int id;
char *end;
Tcl_HashEntry *entryPtr;
id = strtoul(Tcl_GetString(objv[2]), &end, 0);
if (*end != 0) {
goto bindByTag;
}
entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) id);
if (entryPtr != NULL) {
itemPtr = (Tk_Item *) Tcl_GetHashValue(entryPtr);
object = (ClientData) itemPtr;
}
if (object == 0) {
Tcl_AppendResult(interp, "item \"", Tcl_GetString(objv[2]),
"\" doesn't exist", (char *) NULL);
result = TCL_ERROR;
goto done;
}
} else {
bindByTag:
object = (ClientData) Tk_GetUid(Tcl_GetString(objv[2]));
}
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) {
goto done;
}
if (searchPtr->type == 1) {
Tcl_HashEntry *entryPtr;
entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) searchPtr->id);
if (entryPtr != NULL) {
itemPtr = (Tk_Item *) Tcl_GetHashValue(entryPtr);
object = (ClientData) itemPtr;
}
if (object == 0) {
Tcl_AppendResult(interp, "item \"", Tcl_GetString(objv[2]),
"\" doesn't exist", (char *) NULL);
result = TCL_ERROR;
goto done;
}
} else {
object = (ClientData) searchPtr->expr->uid;
}
#endif /* USE_OLD_TAG_SEARCH */
/*
* Make a binding table if the canvas doesn't already have
* one.
*/
if (canvasPtr->bindingTable == NULL) {
canvasPtr->bindingTable = Tk_CreateBindingTable(interp);
}
if (objc == 5) {
int append = 0;
unsigned long mask;
char* argv4 = Tcl_GetStringFromObj(objv[4],NULL);
if (argv4[0] == 0) {
result = Tk_DeleteBinding(interp, canvasPtr->bindingTable,
object, Tcl_GetStringFromObj(objv[3], NULL));
goto done;
}
#ifndef USE_OLD_TAG_SEARCH
if (searchPtr->type == 4) {
/*
* if new tag expression, then insert in linked list
*/
TagSearchExpr *expr, **lastPtr;
lastPtr = &(canvasPtr->bindTagExprs);
while ((expr = *lastPtr) != NULL) {
if (expr->uid == searchPtr->expr->uid) {
break;
}
lastPtr = &(expr->next);
}
if (!expr) {
/*
* transfer ownership of expr to bindTagExprs list
*/
*lastPtr = searchPtr->expr;
searchPtr->expr->next = NULL;
/*
* flag in TagSearch that expr has changed ownership
* so that TagSearchDestroy doesn't try to free it
*/
searchPtr->expr = NULL;
}
}
#endif /* not USE_OLD_TAG_SEARCH */
if (argv4[0] == '+') {
argv4++;
append = 1;
}
mask = Tk_CreateBinding(interp, canvasPtr->bindingTable,
object, Tcl_GetStringFromObj(objv[3],NULL), objv[4], append);
if (mask == 0) {
result = TCL_ERROR;
goto done;
}
if (mask & (unsigned) ~(ButtonMotionMask|Button1MotionMask
|Button2MotionMask|Button3MotionMask|Button4MotionMask
|Button5MotionMask|ButtonPressMask|ButtonReleaseMask
|EnterWindowMask|LeaveWindowMask|KeyPressMask
|KeyReleaseMask|PointerMotionMask|VirtualEventMask)) {
Tk_DeleteBinding(interp, canvasPtr->bindingTable,
object, Tcl_GetStringFromObj(objv[3], NULL));
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "requested illegal events; ",
"only key, button, motion, enter, leave, and virtual ",
"events may be used", (char *) NULL);
result = TCL_ERROR;
goto done;
}
} else if (objc == 4) {
Tcl_Obj *command;
command = Tk_GetBinding(interp, canvasPtr->bindingTable,
object, Tcl_GetStringFromObj(objv[3], NULL));
if (command == NULL) {
CONST char *string;
string = Tcl_GetStringResult(interp);
/*
* Ignore missing binding errors. This is a special hack
* that relies on the error message returned by FindSequence
* in tkBind.c.
*/
if (string[0] != '\0') {
result = TCL_ERROR;
goto done;
} else {
Tcl_ResetResult(interp);
}
} else {
Tcl_SetObjResult(interp, command);
}
} else {
Tk_GetAllBindings(interp, canvasPtr->bindingTable, object);
}
break;
}
case CANV_CANVASX: {
int x;
double grid;
char buf[TCL_DOUBLE_SPACE];
if ((objc < 3) || (objc > 4)) {
Tcl_WrongNumArgs(interp, 2, objv, "screenx ?gridspacing?");
result = TCL_ERROR;
goto done;
}
if (Tk_GetPixelsFromObj(interp, canvasPtr->tkwin, objv[2], &x) != TCL_OK) {
result = TCL_ERROR;
goto done;
}
if (objc == 4) {
if (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, objv[3],
&grid) != TCL_OK) {
result = TCL_ERROR;
goto done;
}
} else {
grid = 0.0;
}
x += canvasPtr->xOrigin;
Tcl_SetDoubleObj(Tcl_GetObjResult(interp), GridAlign((double) x, grid));
break;
}
case CANV_CANVASY: {
int y;
double grid;
char buf[TCL_DOUBLE_SPACE];
if ((objc < 3) || (objc > 4)) {
Tcl_WrongNumArgs(interp, 2, objv, "screeny ?gridspacing?");
result = TCL_ERROR;
goto done;
}
if (Tk_GetPixelsFromObj(interp, canvasPtr->tkwin, objv[2], &y) != TCL_OK) {
result = TCL_ERROR;
goto done;
}
if (objc == 4) {
if (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr,
objv[3], &grid) != TCL_OK) {
result = TCL_ERROR;
goto done;
}
} else {
grid = 0.0;
}
y += canvasPtr->yOrigin;
Tcl_SetDoubleObj(Tcl_GetObjResult(interp), GridAlign((double) y, grid));
break;
}
case CANV_CGET: {
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "option");
result = TCL_ERROR;
goto done;
}
result = Tk_ConfigureValue(interp, canvasPtr->tkwin, configSpecs,
(char *) canvasPtr, Tcl_GetString(objv[2]), 0);
break;
}
case CANV_CONFIGURE: {
if (objc == 2) {
result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
(char *) canvasPtr, (char *) NULL, 0);
} else if (objc == 3) {
result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
(char *) canvasPtr, Tcl_GetString(objv[2]), 0);
} else {
result = ConfigureCanvas(interp, canvasPtr, objc-2, objv+2,
TK_CONFIG_ARGV_ONLY);
}
break;
}
case CANV_COORDS: {
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId ?x y x y ...?");
result = TCL_ERROR;
goto done;
}
#ifdef USE_OLD_TAG_SEARCH
itemPtr = StartTagSearch(canvasPtr, objv[2], &search);
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) {
goto done;
}
itemPtr = TagSearchFirst(searchPtr);
#endif /* USE_OLD_TAG_SEARCH */
if (itemPtr != NULL) {
if (objc != 3) {
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
}
if (itemPtr->typePtr->coordProc != NULL) {
if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
result = (*itemPtr->typePtr->coordProc)(interp,
(Tk_Canvas) canvasPtr, itemPtr, objc-3, objv+3);
} else {
CONST char **args = GetStringsFromObjs(objc-3, objv+3);
result = (*itemPtr->typePtr->coordProc)(interp,
(Tk_Canvas) canvasPtr, itemPtr, objc-3, (Tcl_Obj **) args);
if (args) ckfree((char *) args);
}
}
if (objc != 3) {
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
}
}
break;
}
case CANV_CREATE: {
Tk_ItemType *typePtr;
Tk_ItemType *matchPtr = NULL;
Tk_Item *itemPtr;
char buf[TCL_INTEGER_SPACE];
int isNew = 0;
Tcl_HashEntry *entryPtr;
char *arg;
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv, "type coords ?arg arg ...?");
result = TCL_ERROR;
goto done;
}
arg = Tcl_GetStringFromObj(objv[2], (int *) &length);
c = arg[0];
for (typePtr = typeList; typePtr != NULL; typePtr = typePtr->nextPtr) {
if ((c == typePtr->name[0])
&& (strncmp(arg, typePtr->name, length) == 0)) {
if (matchPtr != NULL) {
badType:
Tcl_AppendResult(interp,
"unknown or ambiguous item type \"",
arg, "\"", (char *) NULL);
result = TCL_ERROR;
goto done;
}
matchPtr = typePtr;
}
}
if (matchPtr == NULL) {
goto badType;
}
if (objc < 4) {
/*
* Allow more specific error return.
*/
Tcl_WrongNumArgs(interp, 3, objv, "coords ?arg arg ...?");
result = TCL_ERROR;
goto done;
}
typePtr = matchPtr;
itemPtr = (Tk_Item *) ckalloc((unsigned) typePtr->itemSize);
itemPtr->id = canvasPtr->nextId;
canvasPtr->nextId++;
itemPtr->group = NULL;
itemPtr->tagPtr = itemPtr->staticTagSpace;
itemPtr->tagSpace = TK_TAG_SPACE;
itemPtr->numTags = 0;
itemPtr->typePtr = typePtr;
itemPtr->state = TK_STATE_NULL;
itemPtr->redraw_flags = 0;
if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
result = (*typePtr->createProc)(interp, (Tk_Canvas) canvasPtr,
itemPtr, objc-3, objv+3);
} else {
CONST char **args = GetStringsFromObjs(objc-3, objv+3);
result = (*typePtr->createProc)(interp, (Tk_Canvas) canvasPtr,
itemPtr, objc-3, (Tcl_Obj **) args);
if (args) ckfree((char *) args);
}
if (result != TCL_OK) {
ckfree((char *) itemPtr);
result = TCL_ERROR;
goto done;
}
itemPtr->nextPtr = NULL;
entryPtr = Tcl_CreateHashEntry(&canvasPtr->idTable,
(char *) itemPtr->id, &isNew);
Tcl_SetHashValue(entryPtr, itemPtr);
itemPtr->prevPtr = canvasPtr->lastItemPtr;
canvasPtr->hotPtr = itemPtr;
canvasPtr->hotPrevPtr = canvasPtr->lastItemPtr;
if (canvasPtr->lastItemPtr == NULL) {
canvasPtr->firstItemPtr = itemPtr;
} else {
canvasPtr->lastItemPtr->nextPtr = itemPtr;
}
canvasPtr->lastItemPtr = itemPtr;
itemPtr->redraw_flags |= FORCE_REDRAW;
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
canvasPtr->flags |= REPICK_NEEDED;
Tcl_SetObjResult(interp,Tcl_NewIntObj(itemPtr->id));
break;
}
case CANV_DCHARS: {
int first, last;
int x1,x2,y1,y2;
if ((objc != 4) && (objc != 5)) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId first ?last?");
result = TCL_ERROR;
goto done;
}
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) {
goto done;
}
for (itemPtr = TagSearchFirst(searchPtr);
itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
if ((itemPtr->typePtr->indexProc == NULL)
|| (itemPtr->typePtr->dCharsProc == NULL)) {
continue;
}
if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
itemPtr, objv[3], &first);
} else {
result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
itemPtr, objv[3], &first);
}
if (result != TCL_OK) {
goto done;
}
if (objc == 5) {
if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
itemPtr, objv[4], &last);
} else {
result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
itemPtr, objv[4], &last);
}
if (result != TCL_OK) {
goto done;
}
} else {
last = first;
}
/*
* Redraw both item's old and new areas: it's possible
* that a delete could result in a new area larger than
* the old area. Except if the insertProc sets the
* TK_ITEM_DONT_REDRAW flag, nothing more needs to be done.
*/
x1 = itemPtr->x1; y1 = itemPtr->y1;
x2 = itemPtr->x2; y2 = itemPtr->y2;
itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW;
(*itemPtr->typePtr->dCharsProc)((Tk_Canvas) canvasPtr,
itemPtr, first, last);
if (!(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW)) {
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
x1, y1, x2, y2);
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
}
itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW;
}
break;
}
case CANV_DELETE: {
int i;
Tcl_HashEntry *entryPtr;
for (i = 2; i < objc; i++) {
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[i], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[i], &searchPtr)) != TCL_OK) {
goto done;
}
for (itemPtr = TagSearchFirst(searchPtr);
itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
if (canvasPtr->bindingTable != NULL) {
Tk_DeleteAllBindings(canvasPtr->bindingTable,
(ClientData) itemPtr);
}
if (itemPtr->group != NULL) {
TkGroupRemoveItem(itemPtr);
}
(*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr,
canvasPtr->display);
if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
ckfree((char *) itemPtr->tagPtr);
}
entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable,
(char *) itemPtr->id);
Tcl_DeleteHashEntry(entryPtr);
if (itemPtr->nextPtr != NULL) {
itemPtr->nextPtr->prevPtr = itemPtr->prevPtr;
}
if (itemPtr->updateCmd) {
LangFreeCallback(itemPtr->updateCmd);
itemPtr->updateCmd = NULL;
}
if (itemPtr->prevPtr != NULL) {
itemPtr->prevPtr->nextPtr = itemPtr->nextPtr;
}
if (canvasPtr->firstItemPtr == itemPtr) {
canvasPtr->firstItemPtr = itemPtr->nextPtr;
if (canvasPtr->firstItemPtr == NULL) {
canvasPtr->lastItemPtr = NULL;
}
}
if (canvasPtr->lastItemPtr == itemPtr) {
canvasPtr->lastItemPtr = itemPtr->prevPtr;
}
ckfree((char *) itemPtr);
if (itemPtr == canvasPtr->currentItemPtr) {
canvasPtr->currentItemPtr = NULL;
canvasPtr->flags |= REPICK_NEEDED;
}
if (itemPtr == canvasPtr->newCurrentPtr) {
canvasPtr->newCurrentPtr = NULL;
canvasPtr->flags |= REPICK_NEEDED;
}
if (itemPtr == canvasPtr->textInfo.focusItemPtr) {
canvasPtr->textInfo.focusItemPtr = NULL;
}
if (itemPtr == canvasPtr->textInfo.selItemPtr) {
canvasPtr->textInfo.selItemPtr = NULL;
}
if ((itemPtr == canvasPtr->hotPtr)
|| (itemPtr == canvasPtr->hotPrevPtr)) {
canvasPtr->hotPtr = NULL;
}
}
}
break;
}
case CANV_DTAG: {
Tk_Uid tag;
int i;
if ((objc != 3) && (objc != 4)) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId ?tagToDelete?");
result = TCL_ERROR;
goto done;
}
if (objc == 4) {
tag = Tk_GetUid(Tcl_GetStringFromObj(objv[3], NULL));
} else {
tag = Tk_GetUid(Tcl_GetStringFromObj(objv[2], NULL));
}
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) {
goto done;
}
for (itemPtr = TagSearchFirst(searchPtr);
itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
for (i = itemPtr->numTags-1; i >= 0; i--) {
if (itemPtr->tagPtr[i] == tag) {
itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
itemPtr->numTags--;
}
}
}
break;
}
case CANV_FIND: {
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv, "searchCommand ?arg arg ...?");
result = TCL_ERROR;
goto done;
}
#ifdef USE_OLD_TAG_SEARCH
result = FindItems(interp, canvasPtr, objc, objv, (Tcl_Obj *) NULL, 2);
#else /* USE_OLD_TAG_SEARCH */
result = FindItems(interp, canvasPtr, objc, objv,
(Tcl_Obj *) NULL, 2, &searchPtr);
#endif /* USE_OLD_TAG_SEARCH */
break;
}
case CANV_FOCUS: {
if (objc > 3) {
Tcl_WrongNumArgs(interp, 2, objv, "?tagOrId?");
result = TCL_ERROR;
goto done;
}
itemPtr = canvasPtr->textInfo.focusItemPtr;
if (objc == 2) {
if (itemPtr != NULL) {
Tcl_SetObjResult(interp, Tcl_NewIntObj(itemPtr->id));
}
goto done;
}
if ((itemPtr != NULL) && (canvasPtr->textInfo.gotFocus)) {
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
}
if (Tcl_GetStringFromObj(objv[2], NULL)[0] == 0) {
canvasPtr->textInfo.focusItemPtr = NULL;
goto done;
}
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) {
goto done;
}
for (itemPtr = TagSearchFirst(searchPtr);
itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
if (itemPtr->typePtr->icursorProc != NULL) {
break;
}
}
if (itemPtr == NULL) {
goto done;
}
canvasPtr->textInfo.focusItemPtr = itemPtr;
if (canvasPtr->textInfo.gotFocus) {
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
}
break;
}
case CANV_GETTAGS: {
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId");
result = TCL_ERROR;
goto done;
}
#ifdef USE_OLD_TAG_SEARCH
itemPtr = StartTagSearch(canvasPtr, objv[2], &search);
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) {
goto done;
}
itemPtr = TagSearchFirst(searchPtr);
#endif /* USE_OLD_TAG_SEARCH */
if (itemPtr != NULL) {
int i;
for (i = 0; i < itemPtr->numTags; i++) {
Tcl_AppendElement(interp, (char *) itemPtr->tagPtr[i]);
}
}
break;
}
case CANV_ICURSOR: {
int index;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId index");
result = TCL_ERROR;
goto done;
}
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) {
goto done;
}
for (itemPtr = TagSearchFirst(searchPtr);
itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
if ((itemPtr->typePtr->indexProc == NULL)
|| (itemPtr->typePtr->icursorProc == NULL)) {
goto done;
}
if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
itemPtr, objv[3], &index);
} else {
result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
itemPtr, objv[3], &index);
}
if (result != TCL_OK) {
goto done;
}
(*itemPtr->typePtr->icursorProc)((Tk_Canvas) canvasPtr, itemPtr,
index);
if ((itemPtr == canvasPtr->textInfo.focusItemPtr)
&& (canvasPtr->textInfo.cursorOn)) {
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
}
}
break;
}
case CANV_INDEX: {
int index;
char buf[TCL_INTEGER_SPACE];
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId string");
result = TCL_ERROR;
goto done;
}
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) {
goto done;
}
for (itemPtr = TagSearchFirst(searchPtr);
itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
if (itemPtr->typePtr->indexProc != NULL) {
break;
}
}
if (itemPtr == NULL) {
Tcl_AppendResult(interp, "can't find an indexable item \"",
Tcl_GetStringFromObj(objv[2], NULL), "\"", (char *) NULL);
result = TCL_ERROR;
goto done;
}
if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
itemPtr, objv[3], &index);
} else {
result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
itemPtr, objv[3], &index);
}
if (result != TCL_OK) {
goto done;
}
Tcl_SetObjResult(interp, Tcl_NewIntObj(index));
break;
}
case CANV_INSERT: {
int beforeThis;
int x1,x2,y1,y2;
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId beforeThis string");
result = TCL_ERROR;
goto done;
}
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) {
goto done;
}
for (itemPtr = TagSearchFirst(searchPtr);
itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
if ((itemPtr->typePtr->indexProc == NULL)
|| (itemPtr->typePtr->insertProc == NULL)) {
continue;
}
if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
itemPtr, objv[3], &beforeThis);
} else {
result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
itemPtr, objv[3], &beforeThis);
}
if (result != TCL_OK) {
goto done;
}
/*
* Redraw both item's old and new areas: it's possible
* that an insertion could result in a new area either
* larger or smaller than the old area. Except if the
* insertProc sets the TK_ITEM_DONT_REDRAW flag, nothing
* more needs to be done.
*/
x1 = itemPtr->x1; y1 = itemPtr->y1;
x2 = itemPtr->x2; y2 = itemPtr->y2;
itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW;
if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
(*itemPtr->typePtr->insertProc)((Tk_Canvas) canvasPtr,
itemPtr, beforeThis, objv[4]);
} else {
(*itemPtr->typePtr->insertProc)((Tk_Canvas) canvasPtr,
itemPtr, beforeThis, objv[4]);
}
if (!(itemPtr->redraw_flags & TK_ITEM_DONT_REDRAW)) {
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
x1, y1, x2, y2);
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
}
itemPtr->redraw_flags &= ~TK_ITEM_DONT_REDRAW;
}
break;
}
case CANV_ITEMCGET: {
if (objc != 4) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId option");
result = TCL_ERROR;
goto done;
}
#ifdef USE_OLD_TAG_SEARCH
itemPtr = StartTagSearch(canvasPtr, objv[2], &search);
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) {
goto done;
}
itemPtr = TagSearchFirst(searchPtr);
#endif /* USE_OLD_TAG_SEARCH */
if (itemPtr != NULL) {
result = Tk_ConfigureValue(canvasPtr->interp, canvasPtr->tkwin,
itemPtr->typePtr->configSpecs, (char *) itemPtr,
Tcl_GetStringFromObj(objv[3], NULL), 0);
}
break;
}
case CANV_ITEMCONFIGURE: {
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId ?option value ...?");
result = TCL_ERROR;
goto done;
}
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) {
goto done;
}
for (itemPtr = TagSearchFirst(searchPtr);
itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
if (objc == 3) {
result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
itemPtr->typePtr->configSpecs, (char *) itemPtr,
(char *) NULL, 0);
} else if (objc == 4) {
result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
itemPtr->typePtr->configSpecs, (char *) itemPtr,
Tcl_GetString(objv[3]), 0);
} else {
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
result = (*itemPtr->typePtr->configProc)(interp,
(Tk_Canvas) canvasPtr, itemPtr, objc-3, objv+3,
TK_CONFIG_ARGV_ONLY);
} else {
CONST char **args = GetStringsFromObjs(objc-3, objv+3);
result = (*itemPtr->typePtr->configProc)(interp,
(Tk_Canvas) canvasPtr, itemPtr, objc-3, (Tcl_Obj **) args,
TK_CONFIG_ARGV_ONLY);
}
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
canvasPtr->flags |= REPICK_NEEDED;
}
if ((result != TCL_OK) || (objc < 5)) {
break;
}
}
break;
}
case CANV_LOWER: {
Tk_Item *itemPtr;
if ((objc != 3) && (objc != 4)) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId ?belowThis?");
result = TCL_ERROR;
goto done;
}
/*
* First find the item just after which we'll insert the
* named items.
*/
if (objc == 3) {
itemPtr = NULL;
} else {
#ifdef USE_OLD_TAG_SEARCH
itemPtr = StartTagSearch(canvasPtr, objv[3], &search);
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[3], &searchPtr)) != TCL_OK) {
goto done;
}
itemPtr = TagSearchFirst(searchPtr);
#endif /* USE_OLD_TAG_SEARCH */
if (itemPtr == NULL) {
Tcl_AppendResult(interp, "tag \"", Tcl_GetString(objv[3]),
"\" doesn't match any items", (char *) NULL);
goto done;
}
itemPtr = itemPtr->prevPtr;
}
#ifdef USE_OLD_TAG_SEARCH
RelinkItems(canvasPtr, objv[2], itemPtr);
#else /* USE_OLD_TAG_SEARCH */
if ((result = RelinkItems(canvasPtr, objv[2], itemPtr, &searchPtr)) != TCL_OK) {
goto done;
}
#endif /* USE_OLD_TAG_SEARCH */
break;
}
case CANV_MOVE: {
double xAmount, yAmount;
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId xAmount yAmount");
result = TCL_ERROR;
goto done;
}
if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, objv[3],
&xAmount) != TCL_OK) || (Tk_CanvasGetCoordFromObj(interp,
(Tk_Canvas) canvasPtr, objv[4], &yAmount) != TCL_OK)) {
result = TCL_ERROR;
goto done;
}
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) {
goto done;
}
for (itemPtr = TagSearchFirst(searchPtr);
itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
(void) (*itemPtr->typePtr->translateProc)((Tk_Canvas) canvasPtr,
itemPtr, xAmount, yAmount);
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
canvasPtr->flags |= REPICK_NEEDED;
}
break;
}
case CANV_POSTSCRIPT: {
result = TkCanvPostscriptCmd(canvasPtr, interp, objc, objv);
break;
}
case CANV_RAISE: {
Tk_Item *prevPtr;
if ((objc != 3) && (objc != 4)) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId ?aboveThis?");
result = TCL_ERROR;
goto done;
}
/*
* First find the item just after which we'll insert the
* named items.
*/
if (objc == 3) {
prevPtr = canvasPtr->lastItemPtr;
} else {
prevPtr = NULL;
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[3], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[3], &searchPtr)) != TCL_OK) {
goto done;
}
for (itemPtr = TagSearchFirst(searchPtr);
itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
prevPtr = itemPtr;
}
if (prevPtr == NULL) {
Tcl_AppendResult(interp, "tagOrId \"", Tcl_GetStringFromObj(objv[3], NULL),
"\" doesn't match any items", (char *) NULL);
result = TCL_ERROR;
goto done;
}
}
#ifdef USE_OLD_TAG_SEARCH
RelinkItems(canvasPtr, objv[2], prevPtr);
#else /* USE_OLD_TAG_SEARCH */
result = RelinkItems(canvasPtr, objv[2], prevPtr, &searchPtr);
if (result != TCL_OK) {
goto done;
}
#endif /* USE_OLD_TAG_SEARCH */
break;
}
case CANV_SCALE: {
double xOrigin, yOrigin, xScale, yScale;
if (objc != 7) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId xOrigin yOrigin xScale yScale");
result = TCL_ERROR;
goto done;
}
if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr,
objv[3], &xOrigin) != TCL_OK)
|| (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr,
objv[4], &yOrigin) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[5], &xScale) != TCL_OK)
|| (Tcl_GetDoubleFromObj(interp, objv[6], &yScale) != TCL_OK)) {
result = TCL_ERROR;
goto done;
}
if ((xScale == 0.0) || (yScale == 0.0)) {
Tcl_SetResult(interp, "scale factor cannot be zero", TCL_STATIC);
result = TCL_ERROR;
goto done;
}
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) {
goto done;
}
for (itemPtr = TagSearchFirst(searchPtr);
itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
if (itemPtr->group != canvasPtr->activeGroup) {
continue;
}
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
(void) (*itemPtr->typePtr->scaleProc)((Tk_Canvas) canvasPtr,
itemPtr, xOrigin, yOrigin, xScale, yScale);
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
canvasPtr->flags |= REPICK_NEEDED;
}
break;
}
case CANV_SCAN: {
int x, y, gain=10;
static CONST char *optionStrings[] = {
"mark", "dragto", NULL
};
if (objc < 5) {
Tcl_WrongNumArgs(interp, 2, objv, "mark|dragto x y ?dragGain?");
result = TCL_ERROR;
} else if (Tcl_GetIndexFromObj(interp, objv[2], optionStrings,
"scan option", 0, &index) != TCL_OK) {
result = TCL_ERROR;
} else if ((objc != 5) && (objc != 5+index)) {
Tcl_WrongNumArgs(interp, 3, objv, index?"x y ?gain?":"x y");
result = TCL_ERROR;
} else if ((Tcl_GetIntFromObj(interp, objv[3], &x) != TCL_OK)
|| (Tcl_GetIntFromObj(interp, objv[4], &y) != TCL_OK)){
result = TCL_ERROR;
} else if ((objc == 6) &&
(Tcl_GetIntFromObj(interp, objv[5], &gain) != TCL_OK)) {
result = TCL_ERROR;
} else if (!index) {
canvasPtr->scanX = x;
canvasPtr->scanXOrigin = canvasPtr->xOrigin;
canvasPtr->scanY = y;
canvasPtr->scanYOrigin = canvasPtr->yOrigin;
} else {
int newXOrigin, newYOrigin, tmp;
/*
* Compute a new view origin for the canvas, amplifying the
* mouse motion.
*/
tmp = canvasPtr->scanXOrigin - gain*(x - canvasPtr->scanX)
- canvasPtr->scrollX1;
newXOrigin = canvasPtr->scrollX1 + tmp;
tmp = canvasPtr->scanYOrigin - gain*(y - canvasPtr->scanY)
- canvasPtr->scrollY1;
newYOrigin = canvasPtr->scrollY1 + tmp;
CanvasSetOrigin(canvasPtr, newXOrigin, newYOrigin);
}
break;
}
case CANV_SELECT: {
int index, optionindex;
static CONST char *optionStrings[] = {
"adjust", "clear", "from", "item", "to", NULL
};
enum options {
CANV_ADJUST, CANV_CLEAR, CANV_FROM, CANV_ITEM, CANV_TO
};
if (objc < 3) {
Tcl_WrongNumArgs(interp, 2, objv, "option ?tagOrId? ?arg?");
result = TCL_ERROR;
goto done;
}
if (objc >= 4) {
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[3], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[3], &searchPtr)) != TCL_OK) {
goto done;
}
for (itemPtr = TagSearchFirst(searchPtr);
itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
if ((itemPtr->typePtr->indexProc != NULL)
&& (itemPtr->typePtr->selectionProc != NULL)){
break;
}
}
if (itemPtr == NULL) {
Tcl_AppendResult(interp,
"can't find an indexable and selectable item \"",
Tcl_GetStringFromObj(objv[3], NULL), "\"", (char *) NULL);
result = TCL_ERROR;
goto done;
}
}
if (objc == 5) {
if (itemPtr->typePtr->alwaysRedraw & TK_CONFIG_OBJS) {
result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
itemPtr, objv[4], &index);
} else {
result = itemPtr->typePtr->indexProc(interp, (Tk_Canvas) canvasPtr,
itemPtr, objv[4], &index);
}
if (result != TCL_OK) {
goto done;
}
}
if (Tcl_GetIndexFromObj(interp, objv[2], optionStrings, "select option", 0,
&optionindex) != TCL_OK) {
result = TCL_ERROR;
goto done;
}
switch ((enum options) optionindex) {
case CANV_ADJUST: {
if (objc != 5) {
Tcl_WrongNumArgs(interp, 3, objv, "tagOrId index");
result = TCL_ERROR;
goto done;
}
if (canvasPtr->textInfo.selItemPtr == itemPtr) {
if (index < (canvasPtr->textInfo.selectFirst
+ canvasPtr->textInfo.selectLast)/2) {
canvasPtr->textInfo.selectAnchor =
canvasPtr->textInfo.selectLast + 1;
} else {
canvasPtr->textInfo.selectAnchor =
canvasPtr->textInfo.selectFirst;
}
}
CanvasSelectTo(canvasPtr, itemPtr, index);
break;
}
case CANV_CLEAR: {
if (objc != 3) {
Tcl_WrongNumArgs(interp, 3, objv, "tagOrId index");
result = TCL_ERROR;
goto done;
}
if (canvasPtr->textInfo.selItemPtr != NULL) {
EventuallyRedrawItem((Tk_Canvas) canvasPtr,
canvasPtr->textInfo.selItemPtr);
canvasPtr->textInfo.selItemPtr = NULL;
}
goto done;
break;
}
case CANV_FROM: {
if (objc != 5) {
Tcl_WrongNumArgs(interp, 3, objv, "tagOrId index");
result = TCL_ERROR;
goto done;
}
canvasPtr->textInfo.anchorItemPtr = itemPtr;
canvasPtr->textInfo.selectAnchor = index;
break;
}
case CANV_ITEM: {
if (objc != 3) {
Tcl_WrongNumArgs(interp, 3, objv, (char *) NULL);
result = TCL_ERROR;
goto done;
}
if (canvasPtr->textInfo.selItemPtr != NULL) {
char buf[TCL_INTEGER_SPACE];
sprintf(buf, "%d", canvasPtr->textInfo.selItemPtr->id);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
}
break;
}
case CANV_TO: {
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "tagOrId index");
result = TCL_ERROR;
goto done;
}
CanvasSelectTo(canvasPtr, itemPtr, index);
break;
}
}
break;
}
case CANV_TYPE: {
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "tag");
result = TCL_ERROR;
goto done;
}
#ifdef USE_OLD_TAG_SEARCH
itemPtr = StartTagSearch(canvasPtr, objv[2], &search);
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[2], &searchPtr)) != TCL_OK) {
goto done;
}
itemPtr = TagSearchFirst(searchPtr);
#endif /* USE_OLD_TAG_SEARCH */
if (itemPtr != NULL) {
Tcl_SetResult(interp, itemPtr->typePtr->name, TCL_STATIC);
}
break;
}
case CANV_XVIEW: {
int count, type;
int newX = 0; /* Initialization needed only to prevent
* gcc warnings. */
double fraction;
if (objc == 2) {
Tcl_SetObjResult(interp, ScrollFractions(
canvasPtr->xOrigin + canvasPtr->inset,
canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin)
- canvasPtr->inset, canvasPtr->scrollX1,
canvasPtr->scrollX2));
} else {
type = Tk_GetScrollInfo(interp, objc, objv, &fraction, &count);
switch (type) {
case TK_SCROLL_ERROR:
result = TCL_ERROR;
goto done;
case TK_SCROLL_MOVETO:
newX = canvasPtr->scrollX1 - canvasPtr->inset
+ (int) (fraction * (canvasPtr->scrollX2
- canvasPtr->scrollX1) + 0.5);
break;
case TK_SCROLL_PAGES:
newX = (int) (canvasPtr->xOrigin + count * .9
* (Tk_Width(canvasPtr->tkwin) - 2*canvasPtr->inset));
break;
case TK_SCROLL_UNITS:
if (canvasPtr->xScrollIncrement > 0) {
newX = canvasPtr->xOrigin
+ count*canvasPtr->xScrollIncrement;
} else {
newX = (int) (canvasPtr->xOrigin + count * .1
* (Tk_Width(canvasPtr->tkwin)
- 2*canvasPtr->inset));
}
break;
}
CanvasSetOrigin(canvasPtr, newX, canvasPtr->yOrigin);
}
break;
}
case CANV_YVIEW: {
int count, type;
int newY = 0; /* Initialization needed only to prevent
* gcc warnings. */
double fraction;
if (objc == 2) {
Tcl_SetObjResult(interp,ScrollFractions(\
canvasPtr->yOrigin + canvasPtr->inset,
canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin)
- canvasPtr->inset, canvasPtr->scrollY1,
canvasPtr->scrollY2));
} else {
type = Tk_GetScrollInfo(interp, objc, objv, &fraction, &count);
switch (type) {
case TK_SCROLL_ERROR:
result = TCL_ERROR;
goto done;
case TK_SCROLL_MOVETO:
newY = canvasPtr->scrollY1 - canvasPtr->inset
+ (int) (fraction*(canvasPtr->scrollY2
- canvasPtr->scrollY1) + 0.5);
break;
case TK_SCROLL_PAGES:
newY = (int) (canvasPtr->yOrigin + count * .9
* (Tk_Height(canvasPtr->tkwin)
- 2*canvasPtr->inset));
break;
case TK_SCROLL_UNITS:
if (canvasPtr->yScrollIncrement > 0) {
newY = canvasPtr->yOrigin
+ count*canvasPtr->yScrollIncrement;
} else {
newY = (int) (canvasPtr->yOrigin + count * .1
* (Tk_Height(canvasPtr->tkwin)
- 2*canvasPtr->inset));
}
break;
}
CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, newY);
}
break;
}
case CANV_VISITOR: {
Tk_VisitorType *matchPtr = NULL;
ClientData visitorPtr;
Tk_VisitorItemProc *visitProc = NULL;
Tk_Item *itemPtr;
if (objc < 4) {
Tcl_WrongNumArgs(interp, 1, objv, "?visitor? type tagOrId ?arg arg ...?");
result = TCL_ERROR;
goto done;
}
#ifdef NOT_YET
matchPtr = Tk_GetCanvasVisitor(interp, Tcl_GetStringFromObj(objv[2], NULL));
#endif
if (matchPtr == NULL) {
result = TCL_ERROR;
goto done;
}
if (matchPtr->flags & TK_CONFIG_OBJS) {
visitorPtr = (*matchPtr->startProc)(interp, (Tk_Canvas) canvasPtr,
objc-4, objv+4);
} else {
visitorPtr = (*matchPtr->startProc)(interp, (Tk_Canvas) canvasPtr,
objc-4, objv+4);
}
if (visitorPtr == (ClientData) NULL) {
result = TCL_ERROR;
goto done;
}
/*
* Now go through each of the items.
*/
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[3], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if ((result = TagSearchScan(canvasPtr, objv[3], &searchPtr)) != TCL_OK) {
goto done;
}
for (itemPtr = TagSearchFirst(searchPtr);
itemPtr != NULL; itemPtr = TagSearchNext(searchPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
#ifdef NOT_YET
if (!(itemPtr->typePtr->flags & TK_ITEM_VISITOR_SUPPORT) ||
itemPtr->typePtr->acceptProc < Tk_Offset(Tk_VisitorType,visitArc) ||
itemPtr->typePtr->acceptProc >= matchPtr->typeSize ||
(visitProc = *(Tk_VisitorItemProc **)((char *) matchPtr + itemPtr->typePtr->acceptProc)) == NULL) {
continue;
}
#endif
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
result = visitProc(canvasPtr->interp,
(Tk_Canvas) canvasPtr, visitorPtr, itemPtr);
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
canvasPtr->flags |= REPICK_NEEDED;
if (result != TCL_OK) {
break;
}
}
/*
* Now call the end function (if any).
*/
if (matchPtr->endProc != NULL) {
if ((*matchPtr->endProc)(interp, (Tk_Canvas) canvasPtr,
visitorPtr) != TCL_OK) {
result = TCL_ERROR;
}
}
}
}
done:
#ifndef USE_OLD_TAG_SEARCH
TagSearchDestroy(searchPtr);
#endif /* not USE_OLD_TAG_SEARCH */
Tcl_Release((ClientData) canvasPtr);
return result;
}
/*
*----------------------------------------------------------------------
*
* DestroyCanvas --
*
* This procedure is invoked by Tcl_EventuallyFree or Tcl_Release
* to clean up the internal structure of a canvas at a safe time
* (when no-one is using it anymore).
*
* Results:
* None.
*
* Side effects:
* Everything associated with the canvas is freed up.
*
*----------------------------------------------------------------------
*/
static void
DestroyCanvas(memPtr)
char *memPtr; /* Info about canvas widget. */
{
TkCanvas *canvasPtr = (TkCanvas *) memPtr;
Tk_Item *itemPtr;
#ifndef USE_OLD_TAG_SEARCH
TagSearchExpr *expr, *next;
#endif
/*
* Free up all of the items in the canvas.
*/
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
itemPtr = canvasPtr->firstItemPtr) {
canvasPtr->firstItemPtr = itemPtr->nextPtr;
if (itemPtr->group != NULL) {
TkGroupRemoveItem(itemPtr);
}
(*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr,
canvasPtr->display);
if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
ckfree((char *) itemPtr->tagPtr);
}
ckfree((char *) itemPtr);
}
/*
* Free up all the stuff that requires special handling,
* then let Tk_FreeOptions handle all the standard option-related
* stuff.
*/
Tcl_DeleteHashTable(&canvasPtr->idTable);
if (canvasPtr->pixmapGC != None) {
Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC);
}
if (canvasPtr->tile != NULL) {
Tk_FreeTile(canvasPtr->tile);
}
if (canvasPtr->disabledTile != NULL) {
Tk_FreeTile(canvasPtr->disabledTile);
}
#ifndef USE_OLD_TAG_SEARCH
expr = canvasPtr->bindTagExprs;
while (expr) {
next = expr->next;
TagSearchExprDestroy(expr);
expr = next;
}
#endif
Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler);
if (canvasPtr->bindingTable != NULL) {
Tk_DeleteBindingTable(canvasPtr->bindingTable);
}
Tk_FreeOptions(configSpecs, (char *) canvasPtr, canvasPtr->display, 0);
if (canvasPtr->updateCmds != NULL) {
Tcl_DecrRefCount(canvasPtr->updateCmds);
}
canvasPtr->tkwin = NULL;
ckfree((char *) canvasPtr);
}
/*
*----------------------------------------------------------------------
*
* ConfigureCanvas --
*
* This procedure is called to process an objv/objc list, plus
* the Tk option database, in order to configure (or
* reconfigure) a canvas widget.
*
* Results:
* The return value is a standard Tcl result. If TCL_ERROR is
* returned, then the interp's result contains an error message.
*
* Side effects:
* Configuration information, such as colors, border width,
* etc. get set for canvasPtr; old resources get freed,
* if there were any.
*
*----------------------------------------------------------------------
*/
static int
ConfigureCanvas(interp, canvasPtr, objc, objv, flags)
Tcl_Interp *interp; /* Used for error reporting. */
TkCanvas *canvasPtr; /* Information about widget; may or may
* not already have values for some fields. */
int objc; /* Number of valid entries in objv. */
Tcl_Obj *CONST objv[]; /* Argument objects. */
int flags; /* Flags to pass to Tk_ConfigureWidget. */
{
XGCValues gcValues;
GC new;
Tk_Tile tile;
Pixmap pixmap;
if (canvasPtr->disabledTile) {
Tk_SetTileChangedProc(canvasPtr->disabledTile, NULL, (ClientData) canvasPtr);
}
if (canvasPtr->tile) {
Tk_SetTileChangedProc(canvasPtr->tile, NULL, (ClientData) canvasPtr);
}
if (Tk_ConfigureWidget(interp, canvasPtr->tkwin, configSpecs,
objc, objv, (char *) canvasPtr,
flags|TK_CONFIG_OBJS) != TCL_OK) {
return TCL_ERROR;
}
/*
* A few options need special processing, such as setting the
* background from a 3-D border and creating a GC for copying
* bits to the screen.
*/
Tk_SetBackgroundFromBorder(canvasPtr->tkwin, canvasPtr->bgBorder);
if (canvasPtr->highlightWidth < 0) {
canvasPtr->highlightWidth = 0;
}
canvasPtr->inset = canvasPtr->borderWidth + canvasPtr->highlightWidth;
tile = canvasPtr->tile;
if (canvasPtr->canvas_state == TK_STATE_DISABLED &&
canvasPtr->disabledTile != NULL) {
tile = canvasPtr->disabledTile;
}
Tk_SetTileChangedProc(tile, TileChangedProc, (ClientData) canvasPtr);
gcValues.function = GXcopy;
gcValues.graphics_exposures = False;
if((pixmap = Tk_PixmapOfTile(tile)) != None) {
gcValues.fill_style = FillTiled;
gcValues.tile = pixmap;
new = Tk_GetGC(canvasPtr->tkwin,
GCFunction|GCGraphicsExposures|GCTile|GCFillStyle, &gcValues);
} else {
gcValues.foreground = Tk_3DBorderColor(canvasPtr->bgBorder)->pixel;
new = Tk_GetGC(canvasPtr->tkwin,
GCFunction|GCGraphicsExposures|GCForeground, &gcValues);
}
if (canvasPtr->pixmapGC != None) {
Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC);
}
canvasPtr->pixmapGC = new;
/*
* Reset the desired dimensions for the window.
*/
Tk_GeometryRequest(canvasPtr->tkwin, canvasPtr->width + 2*canvasPtr->inset,
canvasPtr->height + 2*canvasPtr->inset);
/*
* Restart the cursor timing sequence in case the on-time or off-time
* just changed.
*/
if (canvasPtr->textInfo.gotFocus) {
CanvasFocusProc(canvasPtr, 1);
}
/*
* Recompute the scroll region.
*/
canvasPtr->scrollX1 = 0;
canvasPtr->scrollY1 = 0;
canvasPtr->scrollX2 = 0;
canvasPtr->scrollY2 = 0;
if (canvasPtr->regionArg != NULL) {
int argc2;
Tcl_Obj * *args2;
if (Tcl_ListObjGetElements(canvasPtr->interp, canvasPtr->regionArg,
&argc2, &args2) != TCL_OK) {
return TCL_ERROR;
}
if (argc2 != 4) {
Tcl_AppendResult(interp, "bad scrollRegion \"",
Tcl_GetString(canvasPtr->regionArg), "\"", (char *) NULL);
badRegion:
/* ckfree(canvasPtr->regionArg); */
canvasPtr->regionArg = NULL;
return TCL_ERROR;
}
if ((Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
Tcl_GetString(args2[0]), &canvasPtr->scrollX1) != TCL_OK)
|| (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
Tcl_GetString(args2[1]), &canvasPtr->scrollY1) != TCL_OK)
|| (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
Tcl_GetString(args2[2]), &canvasPtr->scrollX2) != TCL_OK)
|| (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
Tcl_GetString(args2[3]), &canvasPtr->scrollY2) != TCL_OK)) {
goto badRegion;
}
}
flags = canvasPtr->tsoffset.flags;
if (flags & TK_OFFSET_LEFT) {
canvasPtr->tsoffset.xoffset = 0;
} else if (flags & TK_OFFSET_CENTER) {
canvasPtr->tsoffset.xoffset = canvasPtr->width/2;
} else if (flags & TK_OFFSET_RIGHT) {
canvasPtr->tsoffset.xoffset = canvasPtr->width;
}
if (flags & TK_OFFSET_TOP) {
canvasPtr->tsoffset.yoffset = 0;
} else if (flags & TK_OFFSET_MIDDLE) {
canvasPtr->tsoffset.yoffset = canvasPtr->height/2;
} else if (flags & TK_OFFSET_BOTTOM) {
canvasPtr->tsoffset.yoffset = canvasPtr->height;
}
/*
* Reset the canvas's origin (this is a no-op unless confine
* mode has just been turned on or the scroll region has changed).
*/
CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin);
canvasPtr->flags |= UPDATE_SCROLLBARS|REDRAW_BORDERS;
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
canvasPtr->xOrigin, canvasPtr->yOrigin,
canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
return TCL_OK;
}
/*
*---------------------------------------------------------------------------
*
* CanvasWorldChanged --
*
* This procedure is called when the world has changed in some
* way and the widget needs to recompute all its graphics contexts
* and determine its new geometry.
*
* Results:
* None.
*
* Side effects:
* Configures all items in the canvas with a empty argc/argv, for
* the side effect of causing all the items to recompute their
* geometry and to be redisplayed.
*
*---------------------------------------------------------------------------
*/
static void
CanvasWorldChanged(instanceData)
ClientData instanceData; /* Information about widget. */
{
TkCanvas *canvasPtr;
Tk_Item *itemPtr;
int result;
canvasPtr = (TkCanvas *) instanceData;
itemPtr = canvasPtr->firstItemPtr;
for ( ; itemPtr != NULL; itemPtr = itemPtr->nextPtr) {
result = (*itemPtr->typePtr->configProc)(canvasPtr->interp,
(Tk_Canvas) canvasPtr, itemPtr, 0, NULL,
TK_CONFIG_ARGV_ONLY);
if (result != TCL_OK) {
Tcl_ResetResult(canvasPtr->interp);
}
}
canvasPtr->flags |= REPICK_NEEDED;
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
canvasPtr->xOrigin, canvasPtr->yOrigin,
canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
}
/*
* ItemHidden
*
*/
static int
ItemHidden(canvasPtr, itemPtr, picking)
TkCanvas *canvasPtr;
Tk_Item *itemPtr;
int picking;
{
if (itemPtr->state == TK_STATE_HIDDEN ||
(picking && itemPtr->state == TK_STATE_DISABLED) ||
(itemPtr->state == TK_STATE_NULL &&
( canvasPtr->canvas_state == TK_STATE_HIDDEN ||
(picking && canvasPtr->canvas_state == TK_STATE_DISABLED)) )) {
return 1;
}
if (itemPtr->group != canvasPtr->activeGroup) {
if (!itemPtr->group) {
return 1;
}
if (!picking) {
return 1;
}
if (itemPtr->group->state != TK_STATE_ACTIVE) {
return 1;
}
/* We are member of an "active" group
we are hidden if group is hidden
*/
return ItemHidden(canvasPtr, itemPtr->group, picking);
}
return 0;
}
/*
*--------------------------------------------------------------
*
* DisplayCanvas --
*
* This procedure redraws the contents of a canvas window.
* It is invoked as a do-when-idle handler, so it only runs
* when there's nothing else for the application to do.
*
* Results:
* None.
*
* Side effects:
* Information appears on the screen.
*
*--------------------------------------------------------------
*/
static void
DisplayCanvas(clientData)
ClientData clientData; /* Information about widget. */
{
TkCanvas *canvasPtr = (TkCanvas *) clientData;
Tk_Window tkwin = canvasPtr->tkwin;
Tk_Item *itemPtr;
Pixmap pixmap;
int screenX1, screenX2, screenY1, screenY2, width, height;
#ifndef _LANG
Tcl_DString updateCmd;
#endif
Tk_Tile tile;
if (canvasPtr->tkwin == NULL) {
return;
}
#ifndef _LANG
Tcl_DStringInit(&updateCmd);
#endif
if (!Tk_IsMapped(tkwin)) {
goto done;
}
/*
* Choose a new current item if that is needed (this could cause
* event handlers to be invoked).
*/
while (canvasPtr->flags & REPICK_NEEDED) {
Tcl_Preserve((ClientData) canvasPtr);
canvasPtr->flags &= ~REPICK_NEEDED;
PickCurrentItem(canvasPtr, &canvasPtr->pickEvent);
tkwin = canvasPtr->tkwin;
Tcl_Release((ClientData) canvasPtr);
if (tkwin == NULL) {
return;
}
}
/*
* Scan through the item list, registering the bounding box
* for all items that didn't do that for the final coordinates
* yet. This can be determined by the FORCE_REDRAW flag.
*/
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
itemPtr = itemPtr->nextPtr) {
if (itemPtr->redraw_flags & FORCE_REDRAW) {
itemPtr->redraw_flags &= ~FORCE_REDRAW;
EventuallyRedrawItem((Tk_Canvas)canvasPtr, itemPtr);
itemPtr->redraw_flags &= ~FORCE_REDRAW;
}
}
/*
* Compute the intersection between the area that needs redrawing
* and the area that's visible on the screen.
*/
if ((canvasPtr->redrawX1 < canvasPtr->redrawX2)
&& (canvasPtr->redrawY1 < canvasPtr->redrawY2)) {
screenX1 = canvasPtr->xOrigin + canvasPtr->inset;
screenY1 = canvasPtr->yOrigin + canvasPtr->inset;
screenX2 = canvasPtr->xOrigin + Tk_Width(tkwin) - canvasPtr->inset;
screenY2 = canvasPtr->yOrigin + Tk_Height(tkwin) - canvasPtr->inset;
if (canvasPtr->redrawX1 > screenX1) {
screenX1 = canvasPtr->redrawX1;
}
if (canvasPtr->redrawY1 > screenY1) {
screenY1 = canvasPtr->redrawY1;
}
if (canvasPtr->redrawX2 < screenX2) {
screenX2 = canvasPtr->redrawX2;
}
if (canvasPtr->redrawY2 < screenY2) {
screenY2 = canvasPtr->redrawY2;
}
if ((screenX1 >= screenX2) || (screenY1 >= screenY2)) {
goto borders;
}
/*
* Redrawing is done in a temporary pixmap that is allocated
* here and freed at the end of the procedure. All drawing
* is done to the pixmap, and the pixmap is copied to the
* screen at the end of the procedure. The temporary pixmap
* serves two purposes:
*
* 1. It provides a smoother visual effect (no clearing and
* gradual redraw will be visible to users).
* 2. It allows us to redraw only the objects that overlap
* the redraw area. Otherwise incorrect results could
* occur from redrawing things that stick outside of
* the redraw area (we'd have to redraw everything in
* order to make the overlaps look right).
*
* Some tricky points about the pixmap:
*
* 1. We only allocate a large enough pixmap to hold the
* area that has to be redisplayed. This saves time in
* in the X server for large objects that cover much
* more than the area being redisplayed: only the area
* of the pixmap will actually have to be redrawn.
* 2. Some X servers (e.g. the one for DECstations) have troubles
* with characters that overlap an edge of the pixmap (on the
* DEC servers, as of 8/18/92, such characters are drawn one
* pixel too far to the right). To handle this problem,
* make the pixmap a bit larger than is absolutely needed
* so that for normal-sized fonts the characters that overlap
* the edge of the pixmap will be outside the area we care
* about.
*/
#ifdef __OPEN32__
# define Overlap 0
#else
# define Overlap 30
#endif
canvasPtr->drawableXOrigin = screenX1 - Overlap;
canvasPtr->drawableYOrigin = screenY1 - Overlap;
pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
(screenX2 + Overlap - canvasPtr->drawableXOrigin),
(screenY2 + Overlap - canvasPtr->drawableYOrigin),
Tk_Depth(tkwin));
/*
* Clear the area to be redrawn.
*/
width = screenX2 - screenX1;
height = screenY2 - screenY1;
tile = canvasPtr->tile;
if (canvasPtr->canvas_state == TK_STATE_DISABLED &&
canvasPtr->disabledTile != NULL) {
tile = canvasPtr->disabledTile;
}
if (tile != NULL) {
int w=0; int h=0;
int flags = canvasPtr->tsoffset.flags;
if (flags & (TK_OFFSET_CENTER|TK_OFFSET_MIDDLE)) {
Tk_SizeOfTile(tile, &w, &h);
if (flags & TK_OFFSET_CENTER) {
w /= 2;
} else {
w = 0;
}
if (flags & TK_OFFSET_MIDDLE) {
h /= 2;
} else {
h = 0;
}
}
canvasPtr->tsoffset.xoffset -= w;
canvasPtr->tsoffset.yoffset -= h;
Tk_CanvasSetOffset((Tk_Canvas) canvasPtr, canvasPtr->pixmapGC, &canvasPtr->tsoffset);
canvasPtr->tsoffset.xoffset += w;
canvasPtr->tsoffset.yoffset += h;
}
XFillRectangle(Tk_Display(tkwin), pixmap, canvasPtr->pixmapGC,
screenX1 - canvasPtr->drawableXOrigin,
screenY1 - canvasPtr->drawableYOrigin, (unsigned int) width,
(unsigned int) height);
if (tile != NULL) {
XSetTSOrigin(Tk_Display(tkwin), canvasPtr->pixmapGC, 0, 0);
}
/*
* Scan through the item list, redrawing those items that need it.
* An item must be redraw if either (a) it intersects the smaller
* on-screen area or (b) it intersects the full canvas area and its
* type requests that it be redrawn always (e.g. so subwindows can
* be unmapped when they move off-screen).
*/
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
itemPtr = itemPtr->nextPtr) {
if ((itemPtr->x1 >= screenX2)
|| (itemPtr->y1 >= screenY2)
|| (itemPtr->x2 < screenX1)
|| (itemPtr->y2 < screenY1)) {
if (!(itemPtr->typePtr->alwaysRedraw & 1)
|| (itemPtr->x1 >= canvasPtr->redrawX2)
|| (itemPtr->y1 >= canvasPtr->redrawY2)
|| (itemPtr->x2 < canvasPtr->redrawX1)
|| (itemPtr->y2 < canvasPtr->redrawY1)) {
if (!(itemPtr->redraw_flags & NEEDS_DISPLAY)) {
continue;
}
}
}
if (itemPtr->updateCmd) {
#ifndef _LANG
Tcl_DStringAppend(&updateCmd,itemPtr->updateCmd,-1);
Tcl_DStringAppend(&updateCmd,"\n",1);
#else
if (canvasPtr->updateCmds == NULL) {
canvasPtr->updateCmds = Tcl_NewListObj(0,NULL);
}
Tcl_IncrRefCount(itemPtr->updateCmd);
Tcl_ListObjAppendElement(canvasPtr->interp,canvasPtr->updateCmds,
itemPtr->updateCmd);
#endif
}
if (ItemHidden(canvasPtr,itemPtr, 0)) {
continue;
}
itemPtr->redraw_flags &= ~NEEDS_DISPLAY;
(*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr, itemPtr,
canvasPtr->display, pixmap, screenX1, screenY1, width,
height);
}
/*
* Copy from the temporary pixmap to the screen, then free up
* the temporary pixmap.
*/
XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
canvasPtr->pixmapGC,
screenX1 - canvasPtr->drawableXOrigin,
screenY1 - canvasPtr->drawableYOrigin,
(unsigned) (screenX2 - screenX1),
(unsigned) (screenY2 - screenY1),
screenX1 - canvasPtr->xOrigin, screenY1 - canvasPtr->yOrigin);
Tk_FreePixmap(Tk_Display(tkwin), pixmap);
}
/*
* Draw the window borders, if needed.
*/
borders:
if (canvasPtr->flags & REDRAW_BORDERS) {
canvasPtr->flags &= ~REDRAW_BORDERS;
if (canvasPtr->borderWidth > 0) {
Tk_Draw3DRectangle(tkwin, Tk_WindowId(tkwin),
canvasPtr->bgBorder, canvasPtr->highlightWidth,
canvasPtr->highlightWidth,
Tk_Width(tkwin) - 2*canvasPtr->highlightWidth,
Tk_Height(tkwin) - 2*canvasPtr->highlightWidth,
canvasPtr->borderWidth, canvasPtr->relief);
}
if (canvasPtr->highlightWidth != 0) {
GC fgGC, bgGC;
bgGC = Tk_GCForColor(canvasPtr->highlightBgColorPtr,
Tk_WindowId(tkwin));
if (canvasPtr->textInfo.gotFocus) {
fgGC = Tk_GCForColor(canvasPtr->highlightColorPtr,
Tk_WindowId(tkwin));
TkpDrawHighlightBorder(tkwin, fgGC, bgGC,
canvasPtr->highlightWidth, Tk_WindowId(tkwin));
} else {
TkpDrawHighlightBorder(tkwin, bgGC, bgGC,
canvasPtr->highlightWidth, Tk_WindowId(tkwin));
}
}
}
done:
canvasPtr->flags &= ~(REDRAW_PENDING|BBOX_NOT_EMPTY);
canvasPtr->redrawX1 = canvasPtr->redrawX2 = 0;
canvasPtr->redrawY1 = canvasPtr->redrawY2 = 0;
if (canvasPtr->flags & UPDATE_SCROLLBARS) {
CanvasUpdateScrollbars(canvasPtr);
}
#ifndef _LANG
if (Tcl_DStringValue(&updateCmd) && *Tcl_DStringValue(&updateCmd)) {
XFlush(Tk_Display(tkwin));
if (Tcl_GlobalEval(canvasPtr->interp,Tcl_DStringValue(&updateCmd))!=TCL_OK) {
Tcl_AddErrorInfo(canvasPtr->interp,"\n (command bound to canvas update)");
Tcl_BackgroundError(canvasPtr->interp);
}
}
Tcl_DStringFree(&updateCmd);
#else
if (canvasPtr->updateCmds != NULL) {
Tcl_Obj *updateCmds = canvasPtr->updateCmds;
int cmdc;
Tcl_Obj **cmds;
canvasPtr->updateCmds = NULL;
XFlush(Tk_Display(tkwin));
if (Tcl_ListObjGetElements(canvasPtr->interp,updateCmds,&cmdc,&cmds) == TCL_OK) {
int i;
for (i=0; i < cmdc; i++) {
if (LangDoCallback(canvasPtr->interp,cmds[i],0,0) != TCL_OK) {
Tcl_AddErrorInfo(canvasPtr->interp,"\n (command bound to canvas update)");
Tcl_BackgroundError(canvasPtr->interp);
}
}
}
Tcl_DecrRefCount(updateCmds);
}
#endif
}
/*
*--------------------------------------------------------------
*
* CanvasEventProc --
*
* This procedure is invoked by the Tk dispatcher for various
* events on canvases.
*
* Results:
* None.
*
* Side effects:
* When the window gets deleted, internal structures get
* cleaned up. When it gets exposed, it is redisplayed.
*
*--------------------------------------------------------------
*/
static void
CanvasEventProc(clientData, eventPtr)
ClientData clientData; /* Information about window. */
XEvent *eventPtr; /* Information about event. */
{
TkCanvas *canvasPtr = (TkCanvas *) clientData;
if (eventPtr->type == Expose) {
int x, y;
x = eventPtr->xexpose.x + canvasPtr->xOrigin;
y = eventPtr->xexpose.y + canvasPtr->yOrigin;
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, x, y,
x + eventPtr->xexpose.width,
y + eventPtr->xexpose.height);
if ((eventPtr->xexpose.x < canvasPtr->inset)
|| (eventPtr->xexpose.y < canvasPtr->inset)
|| ((eventPtr->xexpose.x + eventPtr->xexpose.width)
> (Tk_Width(canvasPtr->tkwin) - canvasPtr->inset))
|| ((eventPtr->xexpose.y + eventPtr->xexpose.height)
> (Tk_Height(canvasPtr->tkwin) - canvasPtr->inset))) {
canvasPtr->flags |= REDRAW_BORDERS;
}
} else if (eventPtr->type == DestroyNotify) {
if (canvasPtr->tkwin != NULL) {
Tcl_DeleteCommandFromToken(canvasPtr->interp,
canvasPtr->widgetCmd);
/* set to NULL not until calling Lang_DeleteWidget */
canvasPtr->tkwin = NULL;
}
if (canvasPtr->flags & REDRAW_PENDING) {
Tcl_CancelIdleCall(DisplayCanvas, (ClientData) canvasPtr);
}
Tcl_EventuallyFree((ClientData) canvasPtr,
(Tcl_FreeProc *) DestroyCanvas);
} else if (eventPtr->type == ConfigureNotify) {
canvasPtr->flags |= UPDATE_SCROLLBARS;
/*
* The call below is needed in order to recenter the canvas if
* it's confined and its scroll region is smaller than the window.
*/
CanvasSetOrigin(canvasPtr, canvasPtr->xOrigin, canvasPtr->yOrigin);
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, canvasPtr->xOrigin,
canvasPtr->yOrigin,
canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
canvasPtr->flags |= REDRAW_BORDERS;
} else if (eventPtr->type == FocusIn) {
if (eventPtr->xfocus.detail != NotifyInferior) {
CanvasFocusProc(canvasPtr, 1);
}
} else if (eventPtr->type == FocusOut) {
if (eventPtr->xfocus.detail != NotifyInferior) {
CanvasFocusProc(canvasPtr, 0);
}
} else if (eventPtr->type == UnmapNotify) {
Tk_Item *itemPtr;
/*
* Special hack: if the canvas is unmapped, then must notify
* all items with "alwaysRedraw" set, so that they know that
* they are no longer displayed.
*/
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
itemPtr = itemPtr->nextPtr) {
if (itemPtr->typePtr->alwaysRedraw & 1) {
(*itemPtr->typePtr->displayProc)((Tk_Canvas) canvasPtr,
itemPtr, canvasPtr->display, None, 0, 0, 0, 0);
}
}
}
}
/*
*----------------------------------------------------------------------
*
* CanvasCmdDeletedProc --
*
* This procedure is invoked when a widget command is deleted. If
* the widget isn't already in the process of being destroyed,
* this command destroys it.
*
* Results:
* None.
*
* Side effects:
* The widget is destroyed.
*
*----------------------------------------------------------------------
*/
static void
CanvasCmdDeletedProc(clientData)
ClientData clientData; /* Pointer to widget record for widget. */
{
TkCanvas *canvasPtr = (TkCanvas *) clientData;
Tk_Window tkwin = canvasPtr->tkwin;
/*
* This procedure could be invoked either because the window was
* destroyed and the command was then deleted (in which case tkwin
* is NULL) or because the command was deleted, and then this procedure
* destroys the widget.
*/
if (tkwin != NULL) {
canvasPtr->tkwin = NULL;
Tk_DestroyWindow(tkwin);
}
}
/*
*--------------------------------------------------------------
*
* Tk_CanvasEventuallyRedraw --
*
* Arrange for part or all of a canvas widget to redrawn at
* some convenient time in the future.
*
* Results:
* None.
*
* Side effects:
* The screen will eventually be refreshed.
*
*--------------------------------------------------------------
*/
void
Tk_CanvasEventuallyRedraw(canvas, x1, y1, x2, y2)
Tk_Canvas canvas; /* Information about widget. */
int x1, y1; /* Upper left corner of area to redraw.
* Pixels on edge are redrawn. */
int x2, y2; /* Lower right corner of area to redraw.
* Pixels on edge are not redrawn. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
/*
* If tkwin is NULL, the canvas has been destroyed, so we can't really
* redraw it.
*/
if (canvasPtr->tkwin == NULL) {
return;
}
if ((x1 >= x2) || (y1 >= y2) ||
(x2 < canvasPtr->xOrigin) || (y2 < canvasPtr->yOrigin) ||
(x1 >= canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin)) ||
(y1 >= canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin))) {
return;
}
if (canvasPtr->flags & BBOX_NOT_EMPTY) {
if (x1 <= canvasPtr->redrawX1) {
canvasPtr->redrawX1 = x1;
}
if (y1 <= canvasPtr->redrawY1) {
canvasPtr->redrawY1 = y1;
}
if (x2 >= canvasPtr->redrawX2) {
canvasPtr->redrawX2 = x2;
}
if (y2 >= canvasPtr->redrawY2) {
canvasPtr->redrawY2 = y2;
}
} else {
canvasPtr->redrawX1 = x1;
canvasPtr->redrawY1 = y1;
canvasPtr->redrawX2 = x2;
canvasPtr->redrawY2 = y2;
canvasPtr->flags |= BBOX_NOT_EMPTY;
}
if (!(canvasPtr->flags & REDRAW_PENDING)) {
Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
canvasPtr->flags |= REDRAW_PENDING;
}
}
/*
*--------------------------------------------------------------
*
* EventuallyRedrawItem --
*
* Arrange for part or all of a canvas widget to redrawn at
* some convenient time in the future.
*
* Results:
* None.
*
* Side effects:
* The screen will eventually be refreshed.
*
*--------------------------------------------------------------
*/
static void
EventuallyRedrawItem(canvas, itemPtr)
Tk_Canvas canvas; /* Information about widget. */
Tk_Item *itemPtr; /* item to be redrawn. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
if (itemPtr->group) {
(itemPtr->group->typePtr->bboxProc)(canvas,itemPtr->group);
EventuallyRedrawItem(canvas, itemPtr->group);
}
if ((itemPtr->x1 >= itemPtr->x2) || (itemPtr->y1 >= itemPtr->y2) ||
(itemPtr->x2 < canvasPtr->xOrigin) ||
(itemPtr->y2 < canvasPtr->yOrigin) ||
(itemPtr->x1 >= canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin)) ||
(itemPtr->y1 >= canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin))) {
if (!(itemPtr->typePtr->alwaysRedraw & 1)) {
return;
}
}
if (!(itemPtr->redraw_flags & FORCE_REDRAW)) {
if (canvasPtr->flags & BBOX_NOT_EMPTY) {
if (itemPtr->x1 <= canvasPtr->redrawX1) {
canvasPtr->redrawX1 = itemPtr->x1;
}
if (itemPtr->y1 <= canvasPtr->redrawY1) {
canvasPtr->redrawY1 = itemPtr->y1;
}
if (itemPtr->x2 >= canvasPtr->redrawX2) {
canvasPtr->redrawX2 = itemPtr->x2;
}
if (itemPtr->y2 >= canvasPtr->redrawY2) {
canvasPtr->redrawY2 = itemPtr->y2;
}
} else {
canvasPtr->redrawX1 = itemPtr->x1;
canvasPtr->redrawY1 = itemPtr->y1;
canvasPtr->redrawX2 = itemPtr->x2;
canvasPtr->redrawY2 = itemPtr->y2;
canvasPtr->flags |= BBOX_NOT_EMPTY;
}
itemPtr->redraw_flags |= FORCE_REDRAW;
}
while (itemPtr->group) {
itemPtr = itemPtr->group;
itemPtr->redraw_flags |= NEEDS_DISPLAY;
}
if (!(canvasPtr->flags & REDRAW_PENDING)) {
Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
canvasPtr->flags |= REDRAW_PENDING;
}
}
/*
*--------------------------------------------------------------
*
* Tk_CreateItemType --
*
* This procedure may be invoked to add a new kind of canvas
* element to the core item types supported by Tk.
*
* Results:
* None.
*
* Side effects:
* From now on, the new item type will be useable in canvas
* widgets (e.g. typePtr->name can be used as the item type
* in "create" widget commands). If there was already a
* type with the same name as in typePtr, it is replaced with
* the new type.
*
*--------------------------------------------------------------
*/
void
Tk_CreateItemType(typePtr)
Tk_ItemType *typePtr; /* Information about item type;
* storage must be statically
* allocated (must live forever). */
{
Tk_ItemType *typePtr2, *prevPtr;
if (typeList == NULL) {
InitCanvas();
}
/*
* If there's already an item type with the given name, remove it.
*/
for (typePtr2 = typeList, prevPtr = NULL; typePtr2 != NULL;
prevPtr = typePtr2, typePtr2 = typePtr2->nextPtr) {
if (strcmp(typePtr2->name, typePtr->name) == 0) {
if (prevPtr == NULL) {
typeList = typePtr2->nextPtr;
} else {
prevPtr->nextPtr = typePtr2->nextPtr;
}
break;
}
}
typePtr->nextPtr = typeList;
typeList = typePtr;
}
/*
*----------------------------------------------------------------------
*
* Tk_GetItemTypes --
*
* This procedure returns a pointer to the list of all item
* types.
*
* Results:
* The return value is a pointer to the first in the list
* of item types currently supported by canvases.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
Tk_ItemType *
Tk_GetItemTypes()
{
if (typeList == NULL) {
InitCanvas();
}
return typeList;
}
/*
*--------------------------------------------------------------
*
* InitCanvas --
*
* This procedure is invoked to perform once-only-ever
* initialization for the module, such as setting up
* the type table.
*
* Results:
* None.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
static void
InitCanvas()
{
if (typeList != NULL) {
return;
}
typeList = &tkRectangleType;
tkRectangleType.nextPtr = &tkTextType;
tkTextType.nextPtr = &tkLineType;
tkLineType.nextPtr = &tkPolygonType;
tkPolygonType.nextPtr = &tkImageType;
tkImageType.nextPtr = &tkOvalType;
tkOvalType.nextPtr = &tkBitmapType;
tkBitmapType.nextPtr = &tkArcType;
tkArcType.nextPtr = &tkWindowType;
tkWindowType.nextPtr = NULL;
#ifndef USE_OLD_TAG_SEARCH
allUid = Tk_GetUid("all");
currentUid = Tk_GetUid("current");
andUid = Tk_GetUid("&&");
orUid = Tk_GetUid("||");
xorUid = Tk_GetUid("^");
parenUid = Tk_GetUid("(");
endparenUid = Tk_GetUid(")");
negparenUid = Tk_GetUid("!(");
tagvalUid = Tk_GetUid("!!");
negtagvalUid = Tk_GetUid("!");
#endif /* USE_OLD_TAG_SEARCH */
}
#ifdef USE_OLD_TAG_SEARCH
/*
*--------------------------------------------------------------
*
* StartTagSearch --
*
* This procedure is called to initiate an enumeration of
* all items in a given canvas that contain a given tag.
*
* Results:
* The return value is a pointer to the first item in
* canvasPtr that matches tag, or NULL if there is no
* such item. The information at *searchPtr is initialized
* such that successive calls to NextItem will return
* successive items that match tag.
*
* Side effects:
* SearchPtr is linked into a list of searches in progress
* on canvasPtr, so that elements can safely be deleted
* while the search is in progress. EndTagSearch must be
* called at the end of the search to unlink searchPtr from
* this list.
*
*--------------------------------------------------------------
*/
static Tk_Item *
StartTagSearch(canvasPtr, tagObj, searchPtr)
TkCanvas *canvasPtr; /* Canvas whose items are to be
* searched. */
Tcl_Obj *tagObj; /* Object giving tag value. */
TagSearch *searchPtr; /* Record describing tag search;
* will be initialized here. */
{
int id;
Tk_Item *itemPtr, *lastPtr;
Tk_Uid *tagPtr;
Tk_Uid uid;
char *tag = Tcl_GetString(tagObj);
int count;
TkWindow *tkwin;
TkDisplay *dispPtr;
tkwin = (TkWindow *) canvasPtr->tkwin;
dispPtr = tkwin->dispPtr;
/*
* Initialize the search.
*/
searchPtr->canvasPtr = canvasPtr;
searchPtr->searchOver = 0;
/*
* Find the first matching item in one of several ways. If the tag
* is a number then it selects the single item with the matching
* identifier. In this case see if the item being requested is the
* hot item, in which case the search can be skipped.
*/
if (isdigit(UCHAR(*tag))) {
char *end;
Tcl_HashEntry *entryPtr;
dispPtr->numIdSearches++;
id = strtoul(tag, &end, 0);
if (*end == 0) {
itemPtr = canvasPtr->hotPtr;
lastPtr = canvasPtr->hotPrevPtr;
if ((itemPtr == NULL) || (itemPtr->id != id) || (lastPtr == NULL)
|| (lastPtr->nextPtr != itemPtr)) {
dispPtr->numSlowSearches++;
entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) id);
if (entryPtr != NULL) {
itemPtr = (Tk_Item *)Tcl_GetHashValue(entryPtr);
lastPtr = itemPtr->prevPtr;
} else {
lastPtr = itemPtr = NULL;
}
}
searchPtr->lastPtr = lastPtr;
searchPtr->searchOver = 1;
canvasPtr->hotPtr = itemPtr;
canvasPtr->hotPrevPtr = lastPtr;
return itemPtr;
}
}
searchPtr->tag = uid = Tk_GetUid(tag);
if (uid == Tk_GetUid("all")) {
/*
* All items match.
*/
searchPtr->tag = NULL;
searchPtr->lastPtr = NULL;
searchPtr->currentPtr = canvasPtr->firstItemPtr;
return canvasPtr->firstItemPtr;
}
/*
* None of the above. Search for an item with a matching tag.
*/
for (lastPtr = NULL, itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
count > 0; tagPtr++, count--) {
if (*tagPtr == uid) {
searchPtr->lastPtr = lastPtr;
searchPtr->currentPtr = itemPtr;
return itemPtr;
}
}
}
searchPtr->lastPtr = lastPtr;
searchPtr->searchOver = 1;
return NULL;
}
/*
*--------------------------------------------------------------
*
* NextItem --
*
* This procedure returns successive items that match a given
* tag; it should be called only after StartTagSearch has been
* used to begin a search.
*
* Results:
* The return value is a pointer to the next item that matches
* the tag specified to StartTagSearch, or NULL if no such
* item exists. *SearchPtr is updated so that the next call
* to this procedure will return the next item.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
static Tk_Item *
NextItem(searchPtr)
TagSearch *searchPtr; /* Record describing search in
* progress. */
{
Tk_Item *itemPtr, *lastPtr;
int count;
Tk_Uid uid;
Tk_Uid *tagPtr;
/*
* Find next item in list (this may not actually be a suitable
* one to return), and return if there are no items left.
*/
lastPtr = searchPtr->lastPtr;
if (lastPtr == NULL) {
itemPtr = searchPtr->canvasPtr->firstItemPtr;
} else {
itemPtr = lastPtr->nextPtr;
}
if ((itemPtr == NULL) || (searchPtr->searchOver)) {
searchPtr->searchOver = 1;
return NULL;
}
if (itemPtr != searchPtr->currentPtr) {
/*
* The structure of the list has changed. Probably the
* previously-returned item was removed from the list.
* In this case, don't advance lastPtr; just return
* its new successor (i.e. do nothing here).
*/
} else {
lastPtr = itemPtr;
itemPtr = lastPtr->nextPtr;
}
/*
* Handle special case of "all" search by returning next item.
*/
uid = searchPtr->tag;
if (uid == NULL) {
searchPtr->lastPtr = lastPtr;
searchPtr->currentPtr = itemPtr;
return itemPtr;
}
/*
* Look for an item with a particular tag.
*/
for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
count > 0; tagPtr++, count--) {
if (*tagPtr == uid) {
searchPtr->lastPtr = lastPtr;
searchPtr->currentPtr = itemPtr;
return itemPtr;
}
}
}
searchPtr->lastPtr = lastPtr;
searchPtr->searchOver = 1;
return NULL;
}
#else /* USE_OLD_TAG_SEARCH */
/*
*--------------------------------------------------------------
*
* TagSearchExprInit --
*
* This procedure allocates and initializes one TagSearchExpr struct.
*
* Results:
*
* Side effects:
*
*--------------------------------------------------------------
*/
static void
TagSearchExprInit(exprPtrPtr)
TagSearchExpr **exprPtrPtr;
{
TagSearchExpr* expr = *exprPtrPtr;
if (! expr) {
expr = (TagSearchExpr *) ckalloc(sizeof(TagSearchExpr));
expr->allocated = 0;
expr->uids = NULL;
expr->next = NULL;
}
expr->uid = NULL;
expr->index = 0;
expr->length = 0;
*exprPtrPtr = expr;
}
/*
*--------------------------------------------------------------
*
* TagSearchExprDestroy --
*
* This procedure destroys one TagSearchExpr structure.
*
* Results:
*
* Side effects:
*
*--------------------------------------------------------------
*/
static void
TagSearchExprDestroy(expr)
TagSearchExpr *expr;
{
if (expr) {
if (expr->uids) {
ckfree((char *)expr->uids);
}
ckfree((char *)expr);
}
}
/*
*--------------------------------------------------------------
*
* TagSearchScan --
*
* This procedure is called to initiate an enumeration of
* all items in a given canvas that contain a tag that matches
* the tagOrId expression.
*
* Results:
* The return value indicates if the tagOrId expression
* was successfully scanned (syntax).
* The information at *searchPtr is initialized
* such that a call to TagSearchFirst, followed by
* successive calls to TagSearchNext will return items
* that match tag.
*
* Side effects:
* SearchPtr is linked into a list of searches in progress
* on canvasPtr, so that elements can safely be deleted
* while the search is in progress.
*
*--------------------------------------------------------------
*/
static int
TagSearchScan(canvasPtr, tagObj, searchPtrPtr)
TkCanvas *canvasPtr; /* Canvas whose items are to be
* searched. */
Tcl_Obj *tagObj; /* Object giving tag value. */
TagSearch **searchPtrPtr; /* Record describing tag search;
* will be initialized here. */
{
char *tag = Tcl_GetStringFromObj(tagObj,NULL);
int i;
TagSearch *searchPtr;
/*
* Initialize the search.
*/
if (*searchPtrPtr) {
searchPtr = *searchPtrPtr;
} else {
/* Allocate primary search struct on first call */
*searchPtrPtr = searchPtr = (TagSearch *) ckalloc(sizeof(TagSearch));
searchPtr->expr = NULL;
/* Allocate buffer for rewritten tags (after de-escaping) */
searchPtr->rewritebufferAllocated = 100;
searchPtr->rewritebuffer =
ckalloc(searchPtr->rewritebufferAllocated);
}
TagSearchExprInit(&(searchPtr->expr));
/* How long is the tagOrId ? */
searchPtr->stringLength = strlen(tag);
/* Make sure there is enough buffer to hold rewritten tags */
if ((unsigned int)searchPtr->stringLength >=
searchPtr->rewritebufferAllocated) {
searchPtr->rewritebufferAllocated = searchPtr->stringLength + 100;
searchPtr->rewritebuffer =
ckrealloc(searchPtr->rewritebuffer,
searchPtr->rewritebufferAllocated);
}
/* Initialize search */
searchPtr->canvasPtr = canvasPtr;
searchPtr->searchOver = 0;
searchPtr->type = 0;
/*
* Find the first matching item in one of several ways. If the tag
* is a number then it selects the single item with the matching
* identifier. In this case see if the item being requested is the
* hot item, in which case the search can be skipped.
*/
if (searchPtr->stringLength && isdigit(UCHAR(*tag))) {
char *end;
searchPtr->id = strtoul(tag, &end, 0);
if (*end == 0) {
searchPtr->type = 1;
return TCL_OK;
}
}
/*
* For all other tags and tag expressions convert to a UID.
* This UID is kept forever, but this should be thought of
* as a cache rather than as a memory leak.
*/
searchPtr->expr->uid = Tk_GetUid(tag);
/* short circuit impossible searches for null tags */
if (searchPtr->stringLength == 0) {
return TCL_OK;
}
/*
* Pre-scan tag for at least one unquoted "&&" "||" "^" "!"
* if not found then use string as simple tag
*/
for (i = 0; i < searchPtr->stringLength ; i++) {
if (tag[i] == '"') {
i++;
for ( ; i < searchPtr->stringLength; i++) {
if (tag[i] == '\\') {
i++;
continue;
}
if (tag[i] == '"') {
break;
}
}
} else {
if ((tag[i] == '&' && tag[i+1] == '&')
|| (tag[i] == '|' && tag[i+1] == '|')
|| (tag[i] == '^')
|| (tag[i] == '!')) {
searchPtr->type = 4;
break;
}
}
}
searchPtr->string = tag;
searchPtr->stringIndex = 0;
if (searchPtr->type == 4) {
/*
* an operator was found in the prescan, so
* now compile the tag expression into array of Tk_Uid
* flagging any syntax errors found
*/
if (TagSearchScanExpr(canvasPtr->interp, searchPtr, searchPtr->expr) != TCL_OK) {
/* Syntax error in tag expression */
/* Result message set by TagSearchScanExpr */
return TCL_ERROR;
}
searchPtr->expr->length = searchPtr->expr->index;
} else {
if (searchPtr->expr->uid == allUid) {
/*
* All items match.
*/
searchPtr->type = 2;
} else {
/*
* Optimized single-tag search
*/
searchPtr->type = 3;
}
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* TagSearchDestroy --
*
* This procedure destroys any dynamic structures that
* may have been allocated by TagSearchScan.
*
* Results:
*
* Side effects:
*
*--------------------------------------------------------------
*/
static void
TagSearchDestroy(searchPtr)
TagSearch *searchPtr; /* Record describing tag search */
{
if (searchPtr) {
TagSearchExprDestroy(searchPtr->expr);
ckfree((char *)searchPtr->rewritebuffer);
ckfree((char *)searchPtr);
}
}
/*
*--------------------------------------------------------------
*
* TagSearchScanExpr --
*
* This recursive procedure is called to scan a tag expression
* and compile it into an array of Tk_Uids.
*
* Results:
* The return value indicates if the tagOrId expression
* was successfully scanned (syntax).
* The information at *searchPtr is initialized
* such that a call to TagSearchFirst, followed by
* successive calls to TagSearchNext will return items
* that match tag.
*
* Side effects:
*
*--------------------------------------------------------------
*/
static int
TagSearchScanExpr(interp, searchPtr, expr)
Tcl_Interp *interp; /* Current interpreter. */
TagSearch *searchPtr; /* Search data */
TagSearchExpr *expr; /* compiled expression result */
{
int looking_for_tag; /* When true, scanner expects
* next char(s) to be a tag,
* else operand expected */
int found_tag; /* One or more tags found */
int found_endquote; /* For quoted tag string parsing */
int negate_result; /* Pending negation of next tag value */
char *tag; /* tag from tag expression string */
char c;
negate_result = 0;
found_tag = 0;
looking_for_tag = 1;
while (searchPtr->stringIndex < searchPtr->stringLength) {
c = searchPtr->string[searchPtr->stringIndex++];
if (expr->allocated == expr->index) {
expr->allocated += 15;
if (expr->uids) {
expr->uids =
(Tk_Uid *) ckrealloc((char *)(expr->uids),
(expr->allocated)*sizeof(Tk_Uid));
} else {
expr->uids =
(Tk_Uid *) ckalloc((expr->allocated)*sizeof(Tk_Uid));
}
}
if (looking_for_tag) {
switch (c) {
case ' ' : /* ignore unquoted whitespace */
case '\t' :
case '\n' :
case '\r' :
break;
case '!' : /* negate next tag or subexpr */
if (looking_for_tag > 1) {
Tcl_AppendResult(interp,
"Too many '!' in tag search expression",
(char *) NULL);
return TCL_ERROR;
}
looking_for_tag++;
negate_result = 1;
break;
case '(' : /* scan (negated) subexpr recursively */
if (negate_result) {
expr->uids[expr->index++] = negparenUid;
negate_result = 0;
} else {
expr->uids[expr->index++] = parenUid;
}
if (TagSearchScanExpr(interp, searchPtr, expr) != TCL_OK) {
/* Result string should be already set
* by nested call to tag_expr_scan() */
return TCL_ERROR;
}
looking_for_tag = 0;
found_tag = 1;
break;
case '"' : /* quoted tag string */
if (negate_result) {
expr->uids[expr->index++] = negtagvalUid;
negate_result = 0;
} else {
expr->uids[expr->index++] = tagvalUid;
}
tag = searchPtr->rewritebuffer;
found_endquote = 0;
while (searchPtr->stringIndex < searchPtr->stringLength) {
c = searchPtr->string[searchPtr->stringIndex++];
if (c == '\\') {
c = searchPtr->string[searchPtr->stringIndex++];
}
if (c == '"') {
found_endquote = 1;
break;
}
*tag++ = c;
}
if (! found_endquote) {
Tcl_AppendResult(interp,
"Missing endquote in tag search expression",
(char *) NULL);
return TCL_ERROR;
}
if (! (tag - searchPtr->rewritebuffer)) {
Tcl_AppendResult(interp,
"Null quoted tag string in tag search expression",
(char *) NULL);
return TCL_ERROR;
}
*tag++ = '\0';
expr->uids[expr->index++] =
Tk_GetUid(searchPtr->rewritebuffer);
looking_for_tag = 0;
found_tag = 1;
break;
case '&' : /* illegal chars when looking for tag */
case '|' :
case '^' :
case ')' :
Tcl_AppendResult(interp,
"Unexpected operator in tag search expression",
(char *) NULL);
return TCL_ERROR;
default : /* unquoted tag string */
if (negate_result) {
expr->uids[expr->index++] = negtagvalUid;
negate_result = 0;
} else {
expr->uids[expr->index++] = tagvalUid;
}
tag = searchPtr->rewritebuffer;
*tag++ = c;
/* copy rest of tag, including any embedded whitespace */
while (searchPtr->stringIndex < searchPtr->stringLength) {
c = searchPtr->string[searchPtr->stringIndex];
if (c == '!' || c == '&' || c == '|' || c == '^'
|| c == '(' || c == ')' || c == '"') {
break;
}
*tag++ = c;
searchPtr->stringIndex++;
}
/* remove trailing whitespace */
while (1) {
c = *--tag;
/* there must have been one non-whitespace char,
* so this will terminate */
if (c != ' ' && c != '\t' && c != '\n' && c != '\r') {
break;
}
}
*++tag = '\0';
expr->uids[expr->index++] =
Tk_GetUid(searchPtr->rewritebuffer);
looking_for_tag = 0;
found_tag = 1;
}
} else { /* ! looking_for_tag */
switch (c) {
case ' ' : /* ignore whitespace */
case '\t' :
case '\n' :
case '\r' :
break;
case '&' : /* AND operator */
c = searchPtr->string[searchPtr->stringIndex++];
if (c != '&') {
Tcl_AppendResult(interp,
"Singleton '&' in tag search expression",
(char *) NULL);
return TCL_ERROR;
}
expr->uids[expr->index++] = andUid;
looking_for_tag = 1;
break;
case '|' : /* OR operator */
c = searchPtr->string[searchPtr->stringIndex++];
if (c != '|') {
Tcl_AppendResult(interp,
"Singleton '|' in tag search expression",
(char *) NULL);
return TCL_ERROR;
}
expr->uids[expr->index++] = orUid;
looking_for_tag = 1;
break;
case '^' : /* XOR operator */
expr->uids[expr->index++] = xorUid;
looking_for_tag = 1;
break;
case ')' : /* end subexpression */
expr->uids[expr->index++] = endparenUid;
goto breakwhile;
default : /* syntax error */
Tcl_AppendResult(interp,
"Invalid boolean operator in tag search expression",
(char *) NULL);
return TCL_ERROR;
}
}
}
breakwhile:
if (found_tag && ! looking_for_tag) {
return TCL_OK;
}
Tcl_AppendResult(interp, "Missing tag in tag search expression",
(char *) NULL);
return TCL_ERROR;
}
/*
*--------------------------------------------------------------
*
* TagSearchEvalExpr --
*
* This recursive procedure is called to eval a tag expression.
*
* Results:
* The return value indicates if the tagOrId expression
* successfully matched the tags of the current item.
*
* Side effects:
*
*--------------------------------------------------------------
*/
static int
TagSearchEvalExpr(expr, itemPtr)
TagSearchExpr *expr; /* Search expression */
Tk_Item *itemPtr; /* Item being test for match */
{
int looking_for_tag; /* When true, scanner expects
* next char(s) to be a tag,
* else operand expected */
int negate_result; /* Pending negation of next tag value */
Tk_Uid uid;
Tk_Uid *tagPtr;
int count;
int result; /* Value of expr so far */
int parendepth;
result = 0; /* just to keep the compiler quiet */
negate_result = 0;
looking_for_tag = 1;
while (expr->index < expr->length) {
uid = expr->uids[expr->index++];
if (looking_for_tag) {
if (uid == tagvalUid) {
/*
* assert(expr->index < expr->length);
*/
uid = expr->uids[expr->index++];
result = 0;
/*
* set result 1 if tag is found in item's tags
*/
for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
count > 0; tagPtr++, count--) {
if (*tagPtr == uid) {
result = 1;
break;
}
}
} else if (uid == negtagvalUid) {
negate_result = ! negate_result;
/*
* assert(expr->index < expr->length);
*/
uid = expr->uids[expr->index++];
result = 0;
/*
* set result 1 if tag is found in item's tags
*/
for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
count > 0; tagPtr++, count--) {
if (*tagPtr == uid) {
result = 1;
break;
}
}
} else if (uid == parenUid) {
/*
* evaluate subexpressions with recursion
*/
result = TagSearchEvalExpr(expr, itemPtr);
} else if (uid == negparenUid) {
negate_result = ! negate_result;
/*
* evaluate subexpressions with recursion
*/
result = TagSearchEvalExpr(expr, itemPtr);
/*
* } else {
* assert(0);
*/
}
if (negate_result) {
result = ! result;
negate_result = 0;
}
looking_for_tag = 0;
} else { /* ! looking_for_tag */
if (((uid == andUid) && (!result)) || ((uid == orUid) && result)) {
/*
* short circuit expression evaluation
*
* if result before && is 0, or result before || is 1,
* then the expression is decided and no further
* evaluation is needed.
*/
parendepth = 0;
while (expr->index < expr->length) {
uid = expr->uids[expr->index++];
if (uid == tagvalUid || uid == negtagvalUid) {
expr->index++;
continue;
}
if (uid == parenUid || uid == negparenUid) {
parendepth++;
continue;
}
if (uid == endparenUid) {
parendepth--;
if (parendepth < 0) {
break;
}
}
}
return result;
} else if (uid == xorUid) {
/*
* if the previous result was 1
* then negate the next result
*/
negate_result = result;
} else if (uid == endparenUid) {
return result;
/*
* } else {
* assert(0);
*/
}
looking_for_tag = 1;
}
}
/*
* assert(! looking_for_tag);
*/
return result;
}
/*
*--------------------------------------------------------------
*
* TagSearchFirst --
*
* This procedure is called to get the first item
* item that matches a preestablished search predicate
* that was set by TagSearchScan.
*
* Results:
* The return value is a pointer to the first item, or NULL
* if there is no such item. The information at *searchPtr
* is updated such that successive calls to TagSearchNext
* will return successive items.
*
* Side effects:
* SearchPtr is linked into a list of searches in progress
* on canvasPtr, so that elements can safely be deleted
* while the search is in progress.
*
*--------------------------------------------------------------
*/
static Tk_Item *
TagSearchFirst(searchPtr)
TagSearch *searchPtr; /* Record describing tag search */
{
Tk_Item *itemPtr, *lastPtr;
Tk_Uid uid, *tagPtr;
int count;
/* short circuit impossible searches for null tags */
if (searchPtr->stringLength == 0) {
return NULL;
}
/*
* Find the first matching item in one of several ways. If the tag
* is a number then it selects the single item with the matching
* identifier. In this case see if the item being requested is the
* hot item, in which case the search can be skipped.
*/
if (searchPtr->type == 1) {
Tcl_HashEntry *entryPtr;
itemPtr = searchPtr->canvasPtr->hotPtr;
lastPtr = searchPtr->canvasPtr->hotPrevPtr;
if ((itemPtr == NULL) || (itemPtr->id != searchPtr->id) || (lastPtr == NULL)
|| (lastPtr->nextPtr != itemPtr)) {
entryPtr = Tcl_FindHashEntry(&searchPtr->canvasPtr->idTable,
(char *) searchPtr->id);
if (entryPtr != NULL) {
itemPtr = (Tk_Item *)Tcl_GetHashValue(entryPtr);
lastPtr = itemPtr->prevPtr;
} else {
lastPtr = itemPtr = NULL;
}
}
searchPtr->lastPtr = lastPtr;
searchPtr->searchOver = 1;
searchPtr->canvasPtr->hotPtr = itemPtr;
searchPtr->canvasPtr->hotPrevPtr = lastPtr;
return itemPtr;
}
if (searchPtr->type == 2) {
/*
* All items match.
*/
searchPtr->lastPtr = NULL;
searchPtr->currentPtr = searchPtr->canvasPtr->firstItemPtr;
return searchPtr->canvasPtr->firstItemPtr;
}
if (searchPtr->type == 3) {
/*
* Optimized single-tag search
*/
uid = searchPtr->expr->uid;
for (lastPtr = NULL, itemPtr = searchPtr->canvasPtr->firstItemPtr;
itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
count > 0; tagPtr++, count--) {
if (*tagPtr == uid) {
searchPtr->lastPtr = lastPtr;
searchPtr->currentPtr = itemPtr;
return itemPtr;
}
}
}
} else {
/*
* None of the above. Search for an item matching the tag expression.
*/
for (lastPtr = NULL, itemPtr = searchPtr->canvasPtr->firstItemPtr;
itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
searchPtr->expr->index = 0;
if (TagSearchEvalExpr(searchPtr->expr, itemPtr)) {
searchPtr->lastPtr = lastPtr;
searchPtr->currentPtr = itemPtr;
return itemPtr;
}
}
}
searchPtr->lastPtr = lastPtr;
searchPtr->searchOver = 1;
return NULL;
}
/*
*--------------------------------------------------------------
*
* TagSearchNext --
*
* This procedure returns successive items that match a given
* tag; it should be called only after TagSearchFirst has been
* used to begin a search.
*
* Results:
* The return value is a pointer to the next item that matches
* the tag expr specified to TagSearchScan, or NULL if no such
* item exists. *SearchPtr is updated so that the next call
* to this procedure will return the next item.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
static Tk_Item *
TagSearchNext(searchPtr)
TagSearch *searchPtr; /* Record describing search in
* progress. */
{
Tk_Item *itemPtr, *lastPtr;
Tk_Uid uid, *tagPtr;
int count;
/*
* Find next item in list (this may not actually be a suitable
* one to return), and return if there are no items left.
*/
lastPtr = searchPtr->lastPtr;
if (lastPtr == NULL) {
itemPtr = searchPtr->canvasPtr->firstItemPtr;
} else {
itemPtr = lastPtr->nextPtr;
}
if ((itemPtr == NULL) || (searchPtr->searchOver)) {
searchPtr->searchOver = 1;
return NULL;
}
if (itemPtr != searchPtr->currentPtr) {
/*
* The structure of the list has changed. Probably the
* previously-returned item was removed from the list.
* In this case, don't advance lastPtr; just return
* its new successor (i.e. do nothing here).
*/
} else {
lastPtr = itemPtr;
itemPtr = lastPtr->nextPtr;
}
if (searchPtr->type == 2) {
/*
* All items match.
*/
searchPtr->lastPtr = lastPtr;
searchPtr->currentPtr = itemPtr;
return itemPtr;
}
if (searchPtr->type == 3) {
/*
* Optimized single-tag search
*/
uid = searchPtr->expr->uid;
for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
count > 0; tagPtr++, count--) {
if (*tagPtr == uid) {
searchPtr->lastPtr = lastPtr;
searchPtr->currentPtr = itemPtr;
return itemPtr;
}
}
}
searchPtr->lastPtr = lastPtr;
searchPtr->searchOver = 1;
return NULL;
}
/*
* Else.... evaluate tag expression
*/
for ( ; itemPtr != NULL; lastPtr = itemPtr, itemPtr = itemPtr->nextPtr) {
searchPtr->expr->index = 0;
if (TagSearchEvalExpr(searchPtr->expr, itemPtr)) {
searchPtr->lastPtr = lastPtr;
searchPtr->currentPtr = itemPtr;
return itemPtr;
}
}
searchPtr->lastPtr = lastPtr;
searchPtr->searchOver = 1;
return NULL;
}
#endif /* USE_OLD_TAG_SEARCH */
/*
*--------------------------------------------------------------
*
* DoItem --
*
* This is a utility procedure called by FindItems. It
* either adds itemPtr's id to the result forming in interp,
* or it adds a new tag to itemPtr, depending on the value
* of tag.
*
* Results:
* None.
*
* Side effects:
* If tag is NULL then itemPtr's id is added as a list element
* to the interp's result; otherwise tag is added to itemPtr's
* list of tags.
*
*--------------------------------------------------------------
*/
static void
DoItem(interp, itemPtr, tag)
Tcl_Interp *interp; /* Interpreter in which to (possibly)
* record item id. */
Tk_Item *itemPtr; /* Item to (possibly) modify. */
Tk_Uid tag; /* Tag to add to those already
* present for item, or NULL. */
{
Tk_Uid *tagPtr;
int count;
/*
* Handle the "add-to-result" case and return, if appropriate.
*/
if (tag == NULL) {
Tcl_IntResults(interp, 1, 1, itemPtr->id);
return;
}
for (tagPtr = itemPtr->tagPtr, count = itemPtr->numTags;
count > 0; tagPtr++, count--) {
if (tag == *tagPtr) {
return;
}
}
/*
* Grow the tag space if there's no more room left in the current
* block.
*/
if (itemPtr->tagSpace == itemPtr->numTags) {
Tk_Uid *newTagPtr;
itemPtr->tagSpace += 5;
newTagPtr = (Tk_Uid *) ckalloc((unsigned)
(itemPtr->tagSpace * sizeof(Tk_Uid)));
memcpy((VOID *) newTagPtr, (VOID *) itemPtr->tagPtr,
(itemPtr->numTags * sizeof(Tk_Uid)));
if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
ckfree((char *) itemPtr->tagPtr);
}
itemPtr->tagPtr = newTagPtr;
tagPtr = &itemPtr->tagPtr[itemPtr->numTags];
}
/*
* Add in the new tag.
*/
*tagPtr = tag;
itemPtr->numTags++;
}
/*
*--------------------------------------------------------------
*
* FindItems --
*
* This procedure does all the work of implementing the
* "find" and "addtag" options of the canvas widget command,
* which locate items that have certain features (location,
* tags, position in display list, etc.).
*
* Results:
* A standard Tcl return value. If newTag is NULL, then a
* list of ids from all the items that match argc/argv is
* returned in the interp's result. If newTag is NULL, then
* the normal the interp's result is an empty string. If an error
* occurs, then the interp's result will hold an error message.
*
* Side effects:
* If newTag is non-NULL, then all the items that match the
* information in argc/argv have that tag added to their
* lists of tags.
*
*--------------------------------------------------------------
*/
static int
#ifdef USE_OLD_TAG_SEARCH
FindItems(interp, canvasPtr, argc, objv, newTag, first)
#else /* USE_OLD_TAG_SEARCH */
FindItems(interp, canvasPtr, argc, objv, newTag, first, searchPtrPtr)
#endif /* USE_OLD_TAG_SEARCH */
Tcl_Interp *interp; /* Interpreter for error reporting. */
TkCanvas *canvasPtr; /* Canvas whose items are to be
* searched. */
int argc; /* Number of entries in argv. Must be
* greater than zero. */
Tcl_Obj *CONST *objv; /* Arguments that describe what items
* to search for (see user doc on
* "find" and "addtag" options). */
Tcl_Obj *newTag; /* If non-NULL, gives new tag to set
* on all found items; if NULL, then
* ids of found items are returned
* in the interp's result. */
int first; /* For error messages: gives number
* of elements of argv which are already
* handled. */
#ifndef USE_OLD_TAG_SEARCH
TagSearch **searchPtrPtr; /* From CanvasWidgetCmd local vars*/
#endif /* not USE_OLD_TAG_SEARCH */
{
#ifdef USE_OLD_TAG_SEARCH
TagSearch search;
#endif /* USE_OLD_TAG_SEARCH */
Tk_Item *itemPtr;
Tk_Uid uid;
int index;
static CONST char *optionStrings[] = {
"above", "all", "below", "closest",
"enclosed", "overlapping", "withtag", "ingroup", NULL
};
enum options {
CANV_ABOVE, CANV_ALL, CANV_BELOW, CANV_CLOSEST,
CANV_ENCLOSED, CANV_OVERLAPPING, CANV_WITHTAG,
CANV_INGROUP
};
if (newTag != NULL) {
uid = Tk_GetUid(Tcl_GetStringFromObj(newTag, NULL));
} else {
uid = NULL;
}
if (Tcl_GetIndexFromObj(interp, objv[first], optionStrings, "search command", 0,
&index) != TCL_OK) {
return TCL_ERROR;
}
switch ((enum options) index) {
case CANV_ABOVE: {
Tk_Item *lastPtr = NULL;
if (argc != first+2) {
Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId");
return TCL_ERROR;
}
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[first+1], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if (TagSearchScan(canvasPtr, objv[first+1], searchPtrPtr) != TCL_OK) {
return TCL_ERROR;
}
for (itemPtr = TagSearchFirst(*searchPtrPtr);
itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
lastPtr = itemPtr;
}
if ((lastPtr != NULL) && (lastPtr->nextPtr != NULL)) {
DoItem(interp, lastPtr->nextPtr, uid);
}
break;
}
case CANV_ALL: {
if (argc != first+1) {
Tcl_WrongNumArgs(interp, first+1, argv, (char *) NULL);
return TCL_ERROR;
}
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
itemPtr = itemPtr->nextPtr) {
DoItem(interp, itemPtr, uid);
}
break;
}
case CANV_INGROUP: {
if (argc != first+1) {
Tcl_WrongNumArgs(interp, first+1, argv, (char *) NULL);
return TCL_ERROR;
}
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
itemPtr = itemPtr->nextPtr) {
if (itemPtr->group == canvasPtr->activeGroup) {
DoItem(interp, itemPtr, uid);
}
}
break;
}
case CANV_BELOW: {
Tk_Item *itemPtr;
if (argc != first+2) {
Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId");
return TCL_ERROR;
}
#ifdef USE_OLD_TAG_SEARCH
itemPtr = StartTagSearch(canvasPtr, objv[first+1], &search);
#else /* USE_OLD_TAG_SEARCH */
if (TagSearchScan(canvasPtr, objv[first+1], searchPtrPtr) != TCL_OK) {
return TCL_ERROR;
}
itemPtr = TagSearchFirst(*searchPtrPtr);
#endif /* USE_OLD_TAG_SEARCH */
if (itemPtr != NULL) {
if (itemPtr->prevPtr != NULL) {
DoItem(interp, itemPtr->prevPtr, uid);
}
}
break;
}
case CANV_CLOSEST: {
double closestDist;
Tk_Item *startPtr, *closestPtr;
double coords[2], halo;
int x1, y1, x2, y2;
if ((argc < first+3) || (argc > first+5)) {
Tcl_WrongNumArgs(interp, first+1, argv, "x y ?halo? ?start?");
return TCL_ERROR;
}
if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, objv[first+1],
&coords[0]) != TCL_OK) || (Tk_CanvasGetCoordFromObj(interp,
(Tk_Canvas) canvasPtr, objv[first+2], &coords[1]) != TCL_OK)) {
return TCL_ERROR;
}
if (argc > first+3) {
if (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, objv[first+3],
&halo) != TCL_OK) {
return TCL_ERROR;
}
if (halo < 0.0) {
Tcl_AppendResult(interp, "can't have negative halo value \"",
Tcl_GetString(objv[first+3]), "\"", (char *) NULL);
return TCL_ERROR;
}
} else {
halo = 0.0;
}
/*
* Find the item at which to start the search.
*/
startPtr = canvasPtr->firstItemPtr;
if (argc == first+5) {
#ifdef USE_OLD_TAG_SEARCH
itemPtr = StartTagSearch(canvasPtr, objv[first+4], &search);
#else /* USE_OLD_TAG_SEARCH */
if (TagSearchScan(canvasPtr, objv[first+4], searchPtrPtr) != TCL_OK) {
return TCL_ERROR;
}
itemPtr = TagSearchFirst(*searchPtrPtr);
#endif /* USE_OLD_TAG_SEARCH */
if (itemPtr != NULL) {
startPtr = itemPtr;
}
}
/*
* The code below is optimized so that it can eliminate most
* items without having to call their item-specific procedures.
* This is done by keeping a bounding box (x1, y1, x2, y2) that
* an item's bbox must overlap if the item is to have any
* chance of being closer than the closest so far.
*/
itemPtr = startPtr;
while (itemPtr && ItemHidden(canvasPtr, itemPtr, 1) ) {
itemPtr = itemPtr->nextPtr;
}
if (itemPtr == NULL) {
return TCL_OK;
}
closestDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
itemPtr, coords) - halo;
if (closestDist < 0.0) {
closestDist = 0.0;
}
while (1) {
double newDist;
/*
* Update the bounding box using itemPtr, which is the
* new closest item.
*/
x1 = (int) (coords[0] - closestDist - halo - 1);
y1 = (int) (coords[1] - closestDist - halo - 1);
x2 = (int) (coords[0] + closestDist + halo + 1);
y2 = (int) (coords[1] + closestDist + halo + 1);
closestPtr = itemPtr;
/*
* Search for an item that beats the current closest one.
* Work circularly through the canvas's item list until
* getting back to the starting item.
*/
while (1) {
itemPtr = itemPtr->nextPtr;
if (itemPtr == NULL) {
itemPtr = canvasPtr->firstItemPtr;
}
if (itemPtr == startPtr) {
DoItem(interp, closestPtr, uid);
return TCL_OK;
}
if (ItemHidden(canvasPtr, itemPtr, 1)) {
continue;
}
if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
|| (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
continue;
}
newDist = (*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
itemPtr, coords) - halo;
if (newDist < 0.0) {
newDist = 0.0;
}
if (newDist <= closestDist) {
closestDist = newDist;
break;
}
}
}
break;
}
case CANV_ENCLOSED: {
if (argc != first+5) {
Tcl_WrongNumArgs(interp, first+1, argv, "x1 y1 x2 y2");
return TCL_ERROR;
}
return FindArea(interp, canvasPtr, argv+first+1, uid, 1);
}
case CANV_OVERLAPPING: {
if (argc != first+5) {
Tcl_WrongNumArgs(interp, first+1, argv, "x1 y1 x2 y2");
return TCL_ERROR;
}
return FindArea(interp, canvasPtr, argv+first+1, uid, 0);
}
case CANV_WITHTAG: {
if (argc != first+2) {
Tcl_WrongNumArgs(interp, first+1, argv, "tagOrId");
return TCL_ERROR;
}
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, objv[first+1], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if (TagSearchScan(canvasPtr, objv[first+1], searchPtrPtr) != TCL_OK) {
return TCL_ERROR;
}
for (itemPtr = TagSearchFirst(*searchPtrPtr);
itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
DoItem(interp, itemPtr, uid);
}
}
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* FindArea --
*
* This procedure implements area searches for the "find"
* and "addtag" options.
*
* Results:
* A standard Tcl return value. If newTag is NULL, then a
* list of ids from all the items overlapping or enclosed
* by the rectangle given by argc is returned in the interp's result.
* If newTag is NULL, then the normal the interp's result is an
* empty string. If an error occurs, then the interp's result will
* hold an error message.
*
* Side effects:
* If uid is non-NULL, then all the items overlapping
* or enclosed by the area in argv have that tag added to
* their lists of tags.
*
*--------------------------------------------------------------
*/
static int
FindArea(interp, canvasPtr, args, uid, enclosed)
Tcl_Interp *interp; /* Interpreter for error reporting
* and result storing. */
TkCanvas *canvasPtr; /* Canvas whose items are to be
* searched. */
Tcl_Obj *CONST *args; /* Array of four arguments that
* give the coordinates of the
* rectangular area to search. */
Tk_Uid uid; /* If non-NULL, gives new tag to set
* on all found items; if NULL, then
* ids of found items are returned
* in the interp's result. */
int enclosed; /* 0 means overlapping or enclosed
* items are OK, 1 means only enclosed
* items are OK. */
{
double rect[4], tmp;
int x1, y1, x2, y2;
Tk_Item *itemPtr;
if ((Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, args[0],
&rect[0]) != TCL_OK)
|| (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, args[1],
&rect[1]) != TCL_OK)
|| (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, args[2],
&rect[2]) != TCL_OK)
|| (Tk_CanvasGetCoordFromObj(interp, (Tk_Canvas) canvasPtr, args[3],
&rect[3]) != TCL_OK)) {
return TCL_ERROR;
}
if (rect[0] > rect[2]) {
tmp = rect[0]; rect[0] = rect[2]; rect[2] = tmp;
}
if (rect[1] > rect[3]) {
tmp = rect[1]; rect[1] = rect[3]; rect[3] = tmp;
}
/*
* Use an integer bounding box for a quick test, to avoid
* calling item-specific code except for items that are close.
*/
x1 = (int) (rect[0]-1.0);
y1 = (int) (rect[1]-1.0);
x2 = (int) (rect[2]+1.0);
y2 = (int) (rect[3]+1.0);
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
itemPtr = itemPtr->nextPtr) {
if (ItemHidden(canvasPtr, itemPtr, 1)) {
continue;
}
if ((itemPtr->x1 >= x2) || (itemPtr->x2 <= x1)
|| (itemPtr->y1 >= y2) || (itemPtr->y2 <= y1)) {
continue;
}
if ((*itemPtr->typePtr->areaProc)((Tk_Canvas) canvasPtr, itemPtr, rect)
>= enclosed) {
DoItem(interp, itemPtr, uid);
}
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* RelinkItems --
*
* Move one or more items to a different place in the
* display order for a canvas.
*
* Results:
* None.
*
* Side effects:
* The items identified by "tag" are moved so that they
* are all together in the display list and immediately
* after prevPtr. The order of the moved items relative
* to each other is not changed.
*
*--------------------------------------------------------------
*/
#ifdef USE_OLD_TAG_SEARCH
static void
RelinkItems(canvasPtr, tag, prevPtr)
#else /* USE_OLD_TAG_SEARCH */
static int
RelinkItems(canvasPtr, tag, prevPtr, searchPtrPtr)
#endif /* USE_OLD_TAG_SEARCH */
TkCanvas *canvasPtr; /* Canvas to be modified. */
Tcl_Obj *tag; /* Tag identifying items to be moved
* in the redisplay list. */
Tk_Item *prevPtr; /* Reposition the items so that they
* go just after this item (NULL means
* put at beginning of list). */
#ifndef USE_OLD_TAG_SEARCH
TagSearch **searchPtrPtr; /* From CanvasWidgetCmd local vars */
#endif /* not USE_OLD_TAG_SEARCH */
{
Tk_Item *itemPtr;
#ifdef USE_OLD_TAG_SEARCH
TagSearch search;
#endif /* USE_OLD_TAG_SEARCH */
Tk_Item *firstMovePtr, *lastMovePtr;
/*
* Find all of the items to be moved and remove them from
* the list, making an auxiliary list running from firstMovePtr
* to lastMovePtr. Record their areas for redisplay.
*/
firstMovePtr = lastMovePtr = NULL;
#ifdef USE_OLD_TAG_SEARCH
for (itemPtr = StartTagSearch(canvasPtr, tag, &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
#else /* USE_OLD_TAG_SEARCH */
if (TagSearchScan(canvasPtr, tag, searchPtrPtr) != TCL_OK) {
return TCL_ERROR;
}
for (itemPtr = TagSearchFirst(*searchPtrPtr);
itemPtr != NULL; itemPtr = TagSearchNext(*searchPtrPtr)) {
#endif /* USE_OLD_TAG_SEARCH */
if (itemPtr == prevPtr) {
/*
* Item after which insertion is to occur is being
* moved! Switch to insert after its predecessor.
*/
prevPtr = prevPtr->prevPtr;
}
if (itemPtr->prevPtr == NULL) {
if (itemPtr->nextPtr != NULL) {
itemPtr->nextPtr->prevPtr = NULL;
}
canvasPtr->firstItemPtr = itemPtr->nextPtr;
} else {
if (itemPtr->nextPtr != NULL) {
itemPtr->nextPtr->prevPtr = itemPtr->prevPtr;
}
itemPtr->prevPtr->nextPtr = itemPtr->nextPtr;
}
if (canvasPtr->lastItemPtr == itemPtr) {
canvasPtr->lastItemPtr = itemPtr->prevPtr;
}
if (firstMovePtr == NULL) {
itemPtr->prevPtr = NULL;
firstMovePtr = itemPtr;
} else {
itemPtr->prevPtr = lastMovePtr;
lastMovePtr->nextPtr = itemPtr;
}
lastMovePtr = itemPtr;
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
canvasPtr->flags |= REPICK_NEEDED;
}
/*
* Insert the list of to-be-moved items back into the canvas's
* at the desired position.
*/
if (firstMovePtr == NULL) {
#ifdef USE_OLD_TAG_SEARCH
return;
#else /* USE_OLD_TAG_SEARCH */
return TCL_OK;
#endif /* USE_OLD_TAG_SEARCH */
}
if (prevPtr == NULL) {
if (canvasPtr->firstItemPtr != NULL) {
canvasPtr->firstItemPtr->prevPtr = lastMovePtr;
}
lastMovePtr->nextPtr = canvasPtr->firstItemPtr;
canvasPtr->firstItemPtr = firstMovePtr;
} else {
if (prevPtr->nextPtr != NULL) {
prevPtr->nextPtr->prevPtr = lastMovePtr;
}
lastMovePtr->nextPtr = prevPtr->nextPtr;
if (firstMovePtr != NULL) {
firstMovePtr->prevPtr = prevPtr;
}
prevPtr->nextPtr = firstMovePtr;
}
if (canvasPtr->lastItemPtr == prevPtr) {
canvasPtr->lastItemPtr = lastMovePtr;
}
#ifndef USE_OLD_TAG_SEARCH
return TCL_OK;
#endif /* not USE_OLD_TAG_SEARCH */
}
/*
*--------------------------------------------------------------
*
* CanvasBindProc --
*
* This procedure is invoked by the Tk dispatcher to handle
* events associated with bindings on items.
*
* Results:
* None.
*
* Side effects:
* Depends on the command invoked as part of the binding
* (if there was any).
*
*--------------------------------------------------------------
*/
static void
CanvasBindProc(clientData, eventPtr)
ClientData clientData; /* Pointer to canvas structure. */
XEvent *eventPtr; /* Pointer to X event that just
* happened. */
{
TkCanvas *canvasPtr = (TkCanvas *) clientData;
Tcl_Preserve((ClientData) canvasPtr);
/*
* This code below keeps track of the current modifier state in
* canvasPtr>state. This information is used to defer repicks of
* the current item while buttons are down.
*/
if ((eventPtr->type == ButtonPress) || (eventPtr->type == ButtonRelease)) {
int mask;
switch (eventPtr->xbutton.button) {
case Button1:
mask = Button1Mask;
break;
case Button2:
mask = Button2Mask;
break;
case Button3:
mask = Button3Mask;
break;
case Button4:
mask = Button4Mask;
break;
case Button5:
mask = Button5Mask;
break;
default:
mask = 0;
break;
}
/*
* For button press events, repick the current item using the
* button state before the event, then process the event. For
* button release events, first process the event, then repick
* the current item using the button state *after* the event
* (the button has logically gone up before we change the
* current item).
*/
if (eventPtr->type == ButtonPress) {
/*
* On a button press, first repick the current item using
* the button state before the event, the process the event.
*/
canvasPtr->state = eventPtr->xbutton.state;
PickCurrentItem(canvasPtr, eventPtr);
canvasPtr->state ^= mask;
CanvasDoEvent(canvasPtr, eventPtr);
} else {
/*
* Button release: first process the event, with the button
* still considered to be down. Then repick the current
* item under the assumption that the button is no longer down.
*/
canvasPtr->state = eventPtr->xbutton.state;
CanvasDoEvent(canvasPtr, eventPtr);
eventPtr->xbutton.state ^= mask;
canvasPtr->state = eventPtr->xbutton.state;
PickCurrentItem(canvasPtr, eventPtr);
eventPtr->xbutton.state ^= mask;
}
goto done;
} else if ((eventPtr->type == EnterNotify)
|| (eventPtr->type == LeaveNotify)) {
canvasPtr->state = eventPtr->xcrossing.state;
PickCurrentItem(canvasPtr, eventPtr);
goto done;
} else if (eventPtr->type == MotionNotify) {
canvasPtr->state = eventPtr->xmotion.state;
PickCurrentItem(canvasPtr, eventPtr);
}
CanvasDoEvent(canvasPtr, eventPtr);
done:
Tcl_Release((ClientData) canvasPtr);
}
/*
*--------------------------------------------------------------
*
* PickCurrentItem --
*
* Find the topmost item in a canvas that contains a given
* location and mark the the current item. If the current
* item has changed, generate a fake exit event on the old
* current item, a fake enter event on the new current item
* item and force a redraw of the two items. Canvas items
* that are hidden or disabled are ignored.
*
* Results:
* None.
*
* Side effects:
* The current item for canvasPtr may change. If it does,
* then the commands associated with item entry and exit
* could do just about anything. A binding script could
* delete the canvas, so callers should protect themselves
* with Tcl_Preserve and Tcl_Release.
*
*--------------------------------------------------------------
*/
static void
PickCurrentItem(canvasPtr, eventPtr)
TkCanvas *canvasPtr; /* Canvas widget in which to select
* current item. */
XEvent *eventPtr; /* Event describing location of
* mouse cursor. Must be EnterWindow,
* LeaveWindow, ButtonRelease, or
* MotionNotify. */
{
double coords[2];
int buttonDown;
Tk_Item *prevItemPtr;
/*
* Check whether or not a button is down. If so, we'll log entry
* and exit into and out of the current item, but not entry into
* any other item. This implements a form of grabbing equivalent
* to what the X server does for windows.
*/
buttonDown = canvasPtr->state
& (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask);
if (!buttonDown) {
canvasPtr->flags &= ~LEFT_GRABBED_ITEM;
}
/*
* Save information about this event in the canvas. The event in
* the canvas is used for two purposes:
*
* 1. Event bindings: if the current item changes, fake events are
* generated to allow item-enter and item-leave bindings to trigger.
* 2. Reselection: if the current item gets deleted, can use the
* saved event to find a new current item.
* Translate MotionNotify events into EnterNotify events, since that's
* what gets reported to item handlers.
*/
if (eventPtr != &canvasPtr->pickEvent) {
if ((eventPtr->type == MotionNotify)
|| (eventPtr->type == ButtonRelease)) {
canvasPtr->pickEvent.xcrossing.type = EnterNotify;
canvasPtr->pickEvent.xcrossing.serial = eventPtr->xmotion.serial;
canvasPtr->pickEvent.xcrossing.send_event
= eventPtr->xmotion.send_event;
canvasPtr->pickEvent.xcrossing.display = eventPtr->xmotion.display;
canvasPtr->pickEvent.xcrossing.window = eventPtr->xmotion.window;
canvasPtr->pickEvent.xcrossing.root = eventPtr->xmotion.root;
canvasPtr->pickEvent.xcrossing.subwindow = None;
canvasPtr->pickEvent.xcrossing.time = eventPtr->xmotion.time;
canvasPtr->pickEvent.xcrossing.x = eventPtr->xmotion.x;
canvasPtr->pickEvent.xcrossing.y = eventPtr->xmotion.y;
canvasPtr->pickEvent.xcrossing.x_root = eventPtr->xmotion.x_root;
canvasPtr->pickEvent.xcrossing.y_root = eventPtr->xmotion.y_root;
canvasPtr->pickEvent.xcrossing.mode = NotifyNormal;
canvasPtr->pickEvent.xcrossing.detail = NotifyNonlinear;
canvasPtr->pickEvent.xcrossing.same_screen
= eventPtr->xmotion.same_screen;
canvasPtr->pickEvent.xcrossing.focus = False;
canvasPtr->pickEvent.xcrossing.state = eventPtr->xmotion.state;
} else {
canvasPtr->pickEvent = *eventPtr;
}
}
/*
* If this is a recursive call (there's already a partially completed
* call pending on the stack; it's in the middle of processing a
* Leave event handler for the old current item) then just return;
* the pending call will do everything that's needed.
*/
if (canvasPtr->flags & REPICK_IN_PROGRESS) {
return;
}
/*
* A LeaveNotify event automatically means that there's no current
* object, so the check for closest item can be skipped.
*/
coords[0] = canvasPtr->pickEvent.xcrossing.x + canvasPtr->xOrigin;
coords[1] = canvasPtr->pickEvent.xcrossing.y + canvasPtr->yOrigin;
if (canvasPtr->pickEvent.type != LeaveNotify) {
canvasPtr->newCurrentPtr = CanvasFindClosest(canvasPtr, coords);
} else {
canvasPtr->newCurrentPtr = NULL;
}
if ((canvasPtr->newCurrentPtr == canvasPtr->currentItemPtr)
&& !(canvasPtr->flags & LEFT_GRABBED_ITEM)) {
/*
* Nothing to do: the current item hasn't changed.
*/
return;
}
/*
* Simulate a LeaveNotify event on the previous current item and
* an EnterNotify event on the new current item. Remove the "current"
* tag from the previous current item and place it on the new current
* item.
*/
if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr)
&& (canvasPtr->currentItemPtr != NULL)
&& !(canvasPtr->flags & LEFT_GRABBED_ITEM)) {
XEvent event;
Tk_Item *itemPtr = canvasPtr->currentItemPtr;
int i;
event = canvasPtr->pickEvent;
event.type = LeaveNotify;
/*
* If the event's detail happens to be NotifyInferior the
* binding mechanism will discard the event. To be consistent,
* always use NotifyAncestor.
*/
event.xcrossing.detail = NotifyAncestor;
canvasPtr->flags |= REPICK_IN_PROGRESS;
CanvasDoEvent(canvasPtr, &event);
canvasPtr->flags &= ~REPICK_IN_PROGRESS;
/*
* The check below is needed because there could be an event
* handler for <LeaveNotify> that deletes the current item.
*/
if ((itemPtr == canvasPtr->currentItemPtr) && !buttonDown) {
for (i = itemPtr->numTags-1; i >= 0; i--) {
#ifdef USE_OLD_TAG_SEARCH
if (itemPtr->tagPtr[i] == Tk_GetUid("current")) {
#else /* USE_OLD_TAG_SEARCH */
if (itemPtr->tagPtr[i] == currentUid) {
#endif /* USE_OLD_TAG_SEARCH */
itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
itemPtr->numTags--;
break;
}
}
}
/*
* Note: during CanvasDoEvent above, it's possible that
* canvasPtr->newCurrentPtr got reset to NULL because the
* item was deleted.
*/
}
if ((canvasPtr->newCurrentPtr != canvasPtr->currentItemPtr) && buttonDown) {
canvasPtr->flags |= LEFT_GRABBED_ITEM;
return;
}
/*
* Special note: it's possible that canvasPtr->newCurrentPtr ==
* canvasPtr->currentItemPtr here. This can happen, for example,
* if LEFT_GRABBED_ITEM was set.
*/
prevItemPtr = canvasPtr->currentItemPtr;
canvasPtr->flags &= ~LEFT_GRABBED_ITEM;
canvasPtr->currentItemPtr = canvasPtr->newCurrentPtr;
if (prevItemPtr != NULL && prevItemPtr != canvasPtr->currentItemPtr &&
(prevItemPtr->redraw_flags & TK_ITEM_STATE_DEPENDANT)) {
EventuallyRedrawItem((Tk_Canvas) canvasPtr, prevItemPtr);
(*prevItemPtr->typePtr->configProc)(canvasPtr->interp,
(Tk_Canvas) canvasPtr, prevItemPtr, 0, NULL,
TK_CONFIG_ARGV_ONLY);
}
if (canvasPtr->currentItemPtr != NULL) {
XEvent event;
#ifdef USE_OLD_TAG_SEARCH
DoItem((Tcl_Interp *) NULL, canvasPtr->currentItemPtr,
Tk_GetUid("current"));
#else /* USE_OLD_TAG_SEARCH */
DoItem((Tcl_Interp *) NULL, canvasPtr->currentItemPtr, currentUid);
#endif /* USE_OLD_TAG_SEA */
if ((canvasPtr->currentItemPtr->redraw_flags & TK_ITEM_STATE_DEPENDANT &&
prevItemPtr != canvasPtr->currentItemPtr)) {
(*canvasPtr->currentItemPtr->typePtr->configProc)(canvasPtr->interp,
(Tk_Canvas) canvasPtr, canvasPtr->currentItemPtr, 0, NULL,
TK_CONFIG_ARGV_ONLY);
EventuallyRedrawItem((Tk_Canvas) canvasPtr,
canvasPtr->currentItemPtr);
}
event = canvasPtr->pickEvent;
event.type = EnterNotify;
event.xcrossing.detail = NotifyAncestor;
CanvasDoEvent(canvasPtr, &event);
}
}
/*
*----------------------------------------------------------------------
*
* CanvasFindClosest --
*
* Given x and y coordinates, find the topmost canvas item that
* is "close" to the coordinates. Canvas items that are hidden
* or disabled are ignored.
*
* Results:
* The return value is a pointer to the topmost item that is
* close to (x,y), or NULL if no item is close.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static Tk_Item *
CanvasFindClosest(canvasPtr, coords)
TkCanvas *canvasPtr; /* Canvas widget to search. */
double coords[2]; /* Desired x,y position in canvas,
* not screen, coordinates.) */
{
Tk_Item *itemPtr;
Tk_Item *bestPtr;
int x1, y1, x2, y2;
x1 = (int) (coords[0] - canvasPtr->closeEnough);
y1 = (int) (coords[1] - canvasPtr->closeEnough);
x2 = (int) (coords[0] + canvasPtr->closeEnough);
y2 = (int) (coords[1] + canvasPtr->closeEnough);
bestPtr = NULL;
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
itemPtr = itemPtr->nextPtr) {
if (ItemHidden(canvasPtr, itemPtr, 1)) {
continue;
}
if ((itemPtr->x1 > x2) || (itemPtr->x2 < x1)
|| (itemPtr->y1 > y2) || (itemPtr->y2 < y1)) {
continue;
}
if ((*itemPtr->typePtr->pointProc)((Tk_Canvas) canvasPtr,
itemPtr, coords) <= canvasPtr->closeEnough) {
bestPtr = itemPtr;
}
}
return bestPtr;
}
/*
*--------------------------------------------------------------
*
* CanvasDoEvent --
*
* This procedure is called to invoke binding processing
* for a new event that is associated with the current item
* for a canvas.
*
* Results:
* None.
*
* Side effects:
* Depends on the bindings for the canvas. A binding script
* could delete the canvas, so callers should protect themselves
* with Tcl_Preserve and Tcl_Release.
*
*--------------------------------------------------------------
*/
static void
CanvasDoEvent(canvasPtr, eventPtr)
TkCanvas *canvasPtr; /* Canvas widget in which event
* occurred. */
XEvent *eventPtr; /* Real or simulated X event that
* is to be processed. */
{
#define NUM_STATIC 3
ClientData staticObjects[NUM_STATIC];
ClientData *objectPtr;
int numObjects, i;
Tk_Item *itemPtr;
#ifndef USE_OLD_TAG_SEARCH
TagSearchExpr *expr;
int numExprs;
#endif /* not USE_OLD_TAG_SEARCH */
if (canvasPtr->bindingTable == NULL) {
return;
}
itemPtr = canvasPtr->currentItemPtr;
if ((eventPtr->type == KeyPress) || (eventPtr->type == KeyRelease)) {
itemPtr = canvasPtr->textInfo.focusItemPtr;
}
if (itemPtr == NULL) {
return;
}
#ifdef USE_OLD_TAG_SEARCH
/*
* Set up an array with all the relevant objects for processing
* this event. The relevant objects are (a) the event's item,
* (b) the tags associated with the event's item, and (c) the
* tag "all". If there are a lot of tags then malloc an array
* to hold all of the objects.
*/
numObjects = itemPtr->numTags + 2;
#else /* USE_OLD_TAG_SEARCH */
/*
* Set up an array with all the relevant objects for processing
* this event. The relevant objects are:
* (a) the event's item,
* (b) the tags associated with the event's item,
* (c) the expressions that are true for the event's item's tags, and
* (d) the tag "all".
*
* If there are a lot of tags then malloc an array to hold all of
* the objects.
*/
/*
* flag and count all expressions that match item's tags
*/
numExprs = 0;
expr = canvasPtr->bindTagExprs;
while (expr) {
expr->index = 0;
expr->match = TagSearchEvalExpr(expr, itemPtr);
if (expr->match) {
numExprs++;
}
expr = expr->next;
}
numObjects = itemPtr->numTags + numExprs + 2;
#endif /* not USE_OLD_TAG_SEARCH */
if (numObjects <= NUM_STATIC) {
objectPtr = staticObjects;
} else {
objectPtr = (ClientData *) ckalloc((unsigned)
(numObjects * sizeof(ClientData)));
}
#ifdef USE_OLD_TAG_SEARCH
objectPtr[0] = (ClientData) Tk_GetUid("all");
#else /* USE_OLD_TAG_SEARCH */
objectPtr[0] = (ClientData) allUid;
#endif /* USE_OLD_TAG_SEARCH */
for (i = itemPtr->numTags-1; i >= 0; i--) {
objectPtr[i+1] = (ClientData) itemPtr->tagPtr[i];
}
objectPtr[itemPtr->numTags+1] = (ClientData) itemPtr;
#ifndef USE_OLD_TAG_SEARCH
/*
* copy uids of matching expressions into object array
*/
i = itemPtr->numTags+2;
expr = canvasPtr->bindTagExprs;
while (expr) {
if (expr->match) {
objectPtr[i++] = (int *) expr->uid;
}
expr = expr->next;
}
#endif /* not USE_OLD_TAG_SEARCH */
/*
* Invoke the binding system, then free up the object array if
* it was malloc-ed.
*/
if (canvasPtr->tkwin != NULL) {
Tk_BindEvent(canvasPtr->bindingTable, eventPtr, canvasPtr->tkwin,
numObjects, objectPtr);
}
if (objectPtr != staticObjects) {
ckfree((char *) objectPtr);
}
}
/*
*----------------------------------------------------------------------
*
* CanvasBlinkProc --
*
* This procedure is called as a timer handler to blink the
* insertion cursor off and on.
*
* Results:
* None.
*
* Side effects:
* The cursor gets turned on or off, redisplay gets invoked,
* and this procedure reschedules itself.
*
*----------------------------------------------------------------------
*/
static void
CanvasBlinkProc(clientData)
ClientData clientData; /* Pointer to record describing entry. */
{
TkCanvas *canvasPtr = (TkCanvas *) clientData;
if (!canvasPtr->textInfo.gotFocus || (canvasPtr->insertOffTime == 0)) {
return;
}
if (canvasPtr->textInfo.cursorOn) {
canvasPtr->textInfo.cursorOn = 0;
canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
canvasPtr->insertOffTime, CanvasBlinkProc,
(ClientData) canvasPtr);
} else {
canvasPtr->textInfo.cursorOn = 1;
canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
canvasPtr->insertOnTime, CanvasBlinkProc,
(ClientData) canvasPtr);
}
if (canvasPtr->textInfo.focusItemPtr != NULL) {
EventuallyRedrawItem((Tk_Canvas) canvasPtr,
canvasPtr->textInfo.focusItemPtr);
}
}
/*
*----------------------------------------------------------------------
*
* CanvasFocusProc --
*
* This procedure is called whenever a canvas gets or loses the
* input focus. It's also called whenever the window is
* reconfigured while it has the focus.
*
* Results:
* None.
*
* Side effects:
* The cursor gets turned on or off.
*
*----------------------------------------------------------------------
*/
static void
CanvasFocusProc(canvasPtr, gotFocus)
TkCanvas *canvasPtr; /* Canvas that just got or lost focus. */
int gotFocus; /* 1 means window is getting focus, 0 means
* it's losing it. */
{
Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler);
if (gotFocus) {
canvasPtr->textInfo.gotFocus = 1;
canvasPtr->textInfo.cursorOn = 1;
if (canvasPtr->insertOffTime != 0) {
canvasPtr->insertBlinkHandler = Tcl_CreateTimerHandler(
canvasPtr->insertOffTime, CanvasBlinkProc,
(ClientData) canvasPtr);
}
} else {
canvasPtr->textInfo.gotFocus = 0;
canvasPtr->textInfo.cursorOn = 0;
canvasPtr->insertBlinkHandler = (Tcl_TimerToken) NULL;
}
if (canvasPtr->textInfo.focusItemPtr != NULL) {
EventuallyRedrawItem((Tk_Canvas) canvasPtr,
canvasPtr->textInfo.focusItemPtr);
}
if (canvasPtr->highlightWidth > 0) {
canvasPtr->flags |= REDRAW_BORDERS;
if (!(canvasPtr->flags & REDRAW_PENDING)) {
Tcl_DoWhenIdle(DisplayCanvas, (ClientData) canvasPtr);
canvasPtr->flags |= REDRAW_PENDING;
}
}
}
/*
*----------------------------------------------------------------------
*
* CanvasSelectTo --
*
* Modify the selection by moving its un-anchored end. This could
* make the selection either larger or smaller.
*
* Results:
* None.
*
* Side effects:
* The selection changes.
*
*----------------------------------------------------------------------
*/
static void
CanvasSelectTo(canvasPtr, itemPtr, index)
TkCanvas *canvasPtr; /* Information about widget. */
Tk_Item *itemPtr; /* Item that is to hold selection. */
int index; /* Index of element that is to become the
* "other" end of the selection. */
{
int oldFirst, oldLast;
Tk_Item *oldSelPtr;
oldFirst = canvasPtr->textInfo.selectFirst;
oldLast = canvasPtr->textInfo.selectLast;
oldSelPtr = canvasPtr->textInfo.selItemPtr;
/*
* Grab the selection if we don't own it already.
*/
if (canvasPtr->textInfo.selItemPtr == NULL) {
Tk_OwnSelection(canvasPtr->tkwin, XA_PRIMARY, CanvasLostSelection,
(ClientData) canvasPtr);
} else if (canvasPtr->textInfo.selItemPtr != itemPtr) {
EventuallyRedrawItem((Tk_Canvas) canvasPtr,
canvasPtr->textInfo.selItemPtr);
}
canvasPtr->textInfo.selItemPtr = itemPtr;
if (canvasPtr->textInfo.anchorItemPtr != itemPtr) {
canvasPtr->textInfo.anchorItemPtr = itemPtr;
canvasPtr->textInfo.selectAnchor = index;
}
if (canvasPtr->textInfo.selectAnchor <= index) {
canvasPtr->textInfo.selectFirst = canvasPtr->textInfo.selectAnchor;
canvasPtr->textInfo.selectLast = index;
} else {
canvasPtr->textInfo.selectFirst = index;
canvasPtr->textInfo.selectLast = canvasPtr->textInfo.selectAnchor - 1;
}
if ((canvasPtr->textInfo.selectFirst != oldFirst)
|| (canvasPtr->textInfo.selectLast != oldLast)
|| (itemPtr != oldSelPtr)) {
EventuallyRedrawItem((Tk_Canvas) canvasPtr, itemPtr);
}
}
/*
*--------------------------------------------------------------
*
* CanvasFetchSelection --
*
* This procedure is invoked by Tk to return part or all of
* the selection, when the selection is in a canvas widget.
* This procedure always returns the selection as a STRING.
*
* Results:
* The return value is the number of non-NULL bytes stored
* at buffer. Buffer is filled (or partially filled) with a
* NULL-terminated string containing part or all of the selection,
* as given by offset and maxBytes.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
static int
CanvasFetchSelection(clientData, offset, buffer, maxBytes)
ClientData clientData; /* Information about canvas widget. */
int offset; /* Offset within selection of first
* character to be returned. */
char *buffer; /* Location in which to place
* selection. */
int maxBytes; /* Maximum number of bytes to place
* at buffer, not including terminating
* NULL character. */
{
TkCanvas *canvasPtr = (TkCanvas *) clientData;
if (canvasPtr->textInfo.selItemPtr == NULL) {
return -1;
}
if (canvasPtr->textInfo.selItemPtr->typePtr->selectionProc == NULL) {
return -1;
}
return (*canvasPtr->textInfo.selItemPtr->typePtr->selectionProc)(
(Tk_Canvas) canvasPtr, canvasPtr->textInfo.selItemPtr, offset,
buffer, maxBytes);
}
/*
*----------------------------------------------------------------------
*
* CanvasLostSelection --
*
* This procedure is called back by Tk when the selection is
* grabbed away from a canvas widget.
*
* Results:
* None.
*
* Side effects:
* The existing selection is unhighlighted, and the window is
* marked as not containing a selection.
*
*----------------------------------------------------------------------
*/
static void
CanvasLostSelection(clientData)
ClientData clientData; /* Information about entry widget. */
{
TkCanvas *canvasPtr = (TkCanvas *) clientData;
if (canvasPtr->textInfo.selItemPtr != NULL) {
EventuallyRedrawItem((Tk_Canvas) canvasPtr,
canvasPtr->textInfo.selItemPtr);
}
canvasPtr->textInfo.selItemPtr = NULL;
}
/*
*--------------------------------------------------------------
*
* GridAlign --
*
* Given a coordinate and a grid spacing, this procedure
* computes the location of the nearest grid line to the
* coordinate.
*
* Results:
* The return value is the location of the grid line nearest
* to coord.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
static double
GridAlign(coord, spacing)
double coord; /* Coordinate to grid-align. */
double spacing; /* Spacing between grid lines. If <= 0
* then no alignment is done. */
{
if (spacing <= 0.0) {
return coord;
}
if (coord < 0) {
return -((int) ((-coord)/spacing + 0.5)) * spacing;
}
return ((int) (coord/spacing + 0.5)) * spacing;
}
/*
*----------------------------------------------------------------------
*
* ScrollFractions --
*
* Given the range that's visible in the window and the "100%
* range" for what's in the canvas, return a list of two
* doubles representing the scroll fractions. This procedure
* is used for both x and y scrolling.
*
* Results:
* The memory pointed to by string is modified to hold
* two real numbers containing the scroll fractions (between
* 0 and 1) corresponding to the other arguments.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static Tcl_Obj *
ScrollFractions(screen1, screen2, object1, object2)
int screen1; /* Lowest coordinate visible in the window. */
int screen2; /* Highest coordinate visible in the window. */
int object1; /* Lowest coordinate in the object. */
int object2; /* Highest coordinate in the object. */
{
Tcl_Obj *result = Tcl_NewListObj(0,NULL);
double range, f1, f2;
range = object2 - object1;
if (range <= 0) {
f1 = 0;
f2 = 1.0;
} else {
f1 = (screen1 - object1)/range;
if (f1 < 0) {
f1 = 0.0;
}
f2 = (screen2 - object1)/range;
if (f2 > 1.0) {
f2 = 1.0;
}
if (f2 < f1) {
f2 = f1;
}
}
Tcl_ListObjAppendElement(NULL, result,Tcl_NewDoubleObj(f1));
Tcl_ListObjAppendElement(NULL, result,Tcl_NewDoubleObj(f2));
return result;
}
/*
*--------------------------------------------------------------
*
* CanvasUpdateScrollbars --
*
* This procedure is invoked whenever a canvas has changed in
* a way that requires scrollbars to be redisplayed (e.g. the
* view in the canvas has changed).
*
* Results:
* None.
*
* Side effects:
* If there are scrollbars associated with the canvas, then
* their scrolling commands are invoked to cause them to
* redisplay. If errors occur, additional Tcl commands may
* be invoked to process the errors.
*
*--------------------------------------------------------------
*/
static void
CanvasUpdateScrollbars(canvasPtr)
TkCanvas *canvasPtr; /* Information about canvas. */
{
int result;
Tcl_Interp *interp;
int xOrigin, yOrigin, inset, width, height, scrollX1, scrollX2,
scrollY1, scrollY2;
LangCallback *xScrollCmd, *yScrollCmd;
/*
* Save all the relevant values from the canvasPtr, because it might be
* deleted as part of either of the two calls to Tcl_VarEval below.
*/
interp = canvasPtr->interp;
Tcl_Preserve((ClientData) interp);
xScrollCmd = canvasPtr->xScrollCmd;
if (xScrollCmd != NULL) {
Tcl_Preserve((ClientData) xScrollCmd);
}
yScrollCmd = canvasPtr->yScrollCmd;
if (yScrollCmd != NULL) {
Tcl_Preserve((ClientData) yScrollCmd);
}
xOrigin = canvasPtr->xOrigin;
yOrigin = canvasPtr->yOrigin;
inset = canvasPtr->inset;
width = Tk_Width(canvasPtr->tkwin);
height = Tk_Height(canvasPtr->tkwin);
scrollX1 = canvasPtr->scrollX1;
scrollX2 = canvasPtr->scrollX2;
scrollY1 = canvasPtr->scrollY1;
scrollY2 = canvasPtr->scrollY2;
canvasPtr->flags &= ~UPDATE_SCROLLBARS;
if (canvasPtr->xScrollCmd != NULL) {
Tcl_Obj *fractions = ScrollFractions(xOrigin + inset,
xOrigin + width - inset, scrollX1, scrollX2);
#if 0
result = Tcl_VarEval(interp, xScrollCmd, " ",
Tcl_GetString(fractions), (char *) NULL);
#else
result = LangDoCallback(interp, xScrollCmd, 0, 1, " %L", fractions);
#endif
Tcl_DecrRefCount(fractions);
if (result != TCL_OK) {
Tcl_BackgroundError(interp);
}
Tcl_ResetResult(interp);
Tcl_Release((ClientData) xScrollCmd);
}
if (yScrollCmd != NULL) {
Tcl_Obj *fractions = ScrollFractions(yOrigin + inset,
yOrigin + height - inset, scrollY1, scrollY2);
#if 0
result = Tcl_VarEval(interp, yScrollCmd, " ",
Tcl_GetString(fractions), (char *) NULL);
#else
result = LangDoCallback(interp, yScrollCmd, 0, 1, " %L", fractions);
#endif
Tcl_DecrRefCount(fractions);
if (result != TCL_OK) {
Tcl_BackgroundError(interp);
}
Tcl_ResetResult(interp);
Tcl_Release((ClientData) yScrollCmd);
}
Tcl_Release((ClientData) interp);
}
/*
*--------------------------------------------------------------
*
* CanvasSetOrigin --
*
* This procedure is invoked to change the mapping between
* canvas coordinates and screen coordinates in the canvas
* window.
*
* Results:
* None.
*
* Side effects:
* The canvas will be redisplayed to reflect the change in
* view. In addition, scrollbars will be updated if there
* are any.
*
*--------------------------------------------------------------
*/
static void
CanvasSetOrigin(canvasPtr, xOrigin, yOrigin)
TkCanvas *canvasPtr; /* Information about canvas. */
int xOrigin; /* New X origin for canvas (canvas x-coord
* corresponding to left edge of canvas
* window). */
int yOrigin; /* New Y origin for canvas (canvas y-coord
* corresponding to top edge of canvas
* window). */
{
int left, right, top, bottom, delta;
/*
* If scroll increments have been set, round the window origin
* to the nearest multiple of the increments. Remember, the
* origin is the place just inside the borders, not the upper
* left corner.
*/
if (canvasPtr->xScrollIncrement > 0) {
if (xOrigin >= 0) {
xOrigin += canvasPtr->xScrollIncrement/2;
xOrigin -= (xOrigin + canvasPtr->inset)
% canvasPtr->xScrollIncrement;
} else {
xOrigin = (-xOrigin) + canvasPtr->xScrollIncrement/2;
xOrigin = -(xOrigin - (xOrigin - canvasPtr->inset)
% canvasPtr->xScrollIncrement);
}
}
if (canvasPtr->yScrollIncrement > 0) {
if (yOrigin >= 0) {
yOrigin += canvasPtr->yScrollIncrement/2;
yOrigin -= (yOrigin + canvasPtr->inset)
% canvasPtr->yScrollIncrement;
} else {
yOrigin = (-yOrigin) + canvasPtr->yScrollIncrement/2;
yOrigin = -(yOrigin - (yOrigin - canvasPtr->inset)
% canvasPtr->yScrollIncrement);
}
}
/*
* Adjust the origin if necessary to keep as much as possible of the
* canvas in the view. The variables left, right, etc. keep track of
* how much extra space there is on each side of the view before it
* will stick out past the scroll region. If one side sticks out past
* the edge of the scroll region, adjust the view to bring that side
* back to the edge of the scrollregion (but don't move it so much that
* the other side sticks out now). If scroll increments are in effect,
* be sure to adjust only by full increments.
*/
if ((canvasPtr->confine) && (canvasPtr->regionArg != NULL)) {
left = xOrigin + canvasPtr->inset - canvasPtr->scrollX1;
right = canvasPtr->scrollX2
- (xOrigin + Tk_Width(canvasPtr->tkwin) - canvasPtr->inset);
top = yOrigin + canvasPtr->inset - canvasPtr->scrollY1;
bottom = canvasPtr->scrollY2
- (yOrigin + Tk_Height(canvasPtr->tkwin) - canvasPtr->inset);
if ((left < 0) && (right > 0)) {
delta = (right > -left) ? -left : right;
if (canvasPtr->xScrollIncrement > 0) {
delta -= delta % canvasPtr->xScrollIncrement;
}
xOrigin += delta;
} else if ((right < 0) && (left > 0)) {
delta = (left > -right) ? -right : left;
if (canvasPtr->xScrollIncrement > 0) {
delta -= delta % canvasPtr->xScrollIncrement;
}
xOrigin -= delta;
}
if ((top < 0) && (bottom > 0)) {
delta = (bottom > -top) ? -top : bottom;
if (canvasPtr->yScrollIncrement > 0) {
delta -= delta % canvasPtr->yScrollIncrement;
}
yOrigin += delta;
} else if ((bottom < 0) && (top > 0)) {
delta = (top > -bottom) ? -bottom : top;
if (canvasPtr->yScrollIncrement > 0) {
delta -= delta % canvasPtr->yScrollIncrement;
}
yOrigin -= delta;
}
}
if ((xOrigin == canvasPtr->xOrigin) && (yOrigin == canvasPtr->yOrigin)) {
return;
}
/*
* Tricky point: must redisplay not only everything that's visible
* in the window's final configuration, but also everything that was
* visible in the initial configuration. This is needed because some
* item types, like windows, need to know when they move off-screen
* so they can explicitly undisplay themselves.
*/
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
canvasPtr->xOrigin, canvasPtr->yOrigin,
canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
canvasPtr->xOrigin = xOrigin;
canvasPtr->yOrigin = yOrigin;
canvasPtr->flags |= UPDATE_SCROLLBARS;
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
canvasPtr->xOrigin, canvasPtr->yOrigin,
canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
}
/*
*----------------------------------------------------------------------
*
* GetStringsFromObjs
*
* Results:
* Converts object list into string list.
*
* Side effects:
* Memory is allocated for the argv array, which must
* be freed using ckfree() when no longer needed.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
static CONST char **
GetStringsFromObjs(argc, objv)
int argc;
Tcl_Obj *CONST objv[];
{
register int i;
CONST char **argvv;
if (argc <= 0) {
return NULL;
}
argvv = (CONST char **) ckalloc((argc+1) * sizeof(char *));
for (i = 0; i < argc; i++) {
argvv[i]=Tcl_GetStringFromObj(objv[i], (int *) NULL);
}
argvv[argc] = 0;
return argvv;
}
/*
*----------------------------------------------------------------------
*
* TileChangedProc
*
* Results:
* None.
*
*----------------------------------------------------------------------
*/
/* ARGSUSED */
static void
TileChangedProc(clientData, tile)
ClientData clientData;
Tk_Tile tile;
{
TkCanvas *canvasPtr = (TkCanvas *)clientData;
/* This a bit of a hack - call ConfigureCanvas with no args
for its side effects of setting the GC members
*/
ConfigureCanvas(canvasPtr->interp, canvasPtr, 0, NULL, 0);
/* and then re-draw everything */
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
canvasPtr->xOrigin, canvasPtr->yOrigin,
canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin),
canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin));
}
/*
*--------------------------------------------------------------
*
* Tk_CanvasPsColor --
*
* This procedure is called by individual canvas items when
* they want to set a color value for output. Given information
* about an X color, this procedure will generate Postscript
* commands to set up an appropriate color in Postscript.
*
* Results:
* Returns a standard Tcl return value. If an error occurs
* then an error message will be left in interp->result.
* If no error occurs, then additional Postscript will be
* appended to interp->result.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
int
Tk_CanvasPsColor(interp, canvas, colorPtr)
Tcl_Interp *interp; /* Interpreter for returning Postscript
* or error message. */
Tk_Canvas canvas; /* Information about canvas. */
XColor *colorPtr; /* Information about color. */
{
return Tk_PostscriptColor(interp, ((TkCanvas *) canvas)->psInfo,
colorPtr);
}
/*
*--------------------------------------------------------------
*
* Tk_CanvasPsFont --
*
* This procedure is called by individual canvas items when
* they want to output text. Given information about an X
* font, this procedure will generate Postscript commands
* to set up an appropriate font in Postscript.
*
* Results:
* Returns a standard Tcl return value. If an error occurs
* then an error message will be left in interp->result.
* If no error occurs, then additional Postscript will be
* appended to the interp->result.
*
* Side effects:
* The Postscript font name is entered into psInfoPtr->fontTable
* if it wasn't already there.
*
*--------------------------------------------------------------
*/
int
Tk_CanvasPsFont(interp, canvas, tkfont)
Tcl_Interp *interp; /* Interpreter for returning Postscript
* or error message. */
Tk_Canvas canvas; /* Information about canvas. */
Tk_Font tkfont; /* Information about font in which text
* is to be printed. */
{
return Tk_PostscriptFont(interp, ((TkCanvas *) canvas)->psInfo, tkfont);
}
/*
*--------------------------------------------------------------
*
* Tk_CanvasPsBitmap --
*
* This procedure is called to output the contents of a
* sub-region of a bitmap in proper image data format for
* Postscript (i.e. data between angle brackets, one bit
* per pixel).
*
* Results:
* Returns a standard Tcl return value. If an error occurs
* then an error message will be left in interp->result.
* If no error occurs, then additional Postscript will be
* appended to interp->result.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
int
Tk_CanvasPsBitmap(interp, canvas, bitmap, startX, startY, width, height)
Tcl_Interp *interp; /* Interpreter for returning Postscript
* or error message. */
Tk_Canvas canvas; /* Information about canvas. */
Pixmap bitmap; /* Bitmap for which to generate
* Postscript. */
int startX, startY; /* Coordinates of upper-left corner
* of rectangular region to output. */
int width, height; /* Height of rectangular region. */
{
return Tk_PostscriptBitmap(interp, ((TkCanvas *) canvas)->tkwin,
((TkCanvas *) canvas)->psInfo, bitmap, startX, startY,
width, height);
}
/*
*--------------------------------------------------------------
*
* Tk_CanvasPsStipple --
*
* This procedure is called by individual canvas items when
* they have created a path that they'd like to be filled with
* a stipple pattern. Given information about an X bitmap,
* this procedure will generate Postscript commands to fill
* the current clip region using a stipple pattern defined by the
* bitmap.
*
* Results:
* Returns a standard Tcl return value. If an error occurs
* then an error message will be left in interp->result.
* If no error occurs, then additional Postscript will be
* appended to interp->result.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
int
Tk_CanvasPsStipple(interp, canvas, bitmap)
Tcl_Interp *interp; /* Interpreter for returning Postscript
* or error message. */
Tk_Canvas canvas; /* Information about canvas. */
Pixmap bitmap; /* Bitmap to use for stippling. */
{
return Tk_PostscriptStipple(interp, ((TkCanvas *) canvas)->tkwin,
((TkCanvas *) canvas)->psInfo, bitmap);
}
/*
*--------------------------------------------------------------
*
* Tk_CanvasPsY --
*
* Given a y-coordinate in canvas coordinates, this procedure
* returns a y-coordinate to use for Postscript output.
*
* Results:
* Returns the Postscript coordinate that corresponds to
* "y".
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
double
Tk_CanvasPsY(canvas, y)
Tk_Canvas canvas; /* Token for canvas on whose behalf
* Postscript is being generated. */
double y; /* Y-coordinate in canvas coords. */
{
return Tk_PostscriptY(y, ((TkCanvas *) canvas)->psInfo);
}
/*
*--------------------------------------------------------------
*
* Tk_CanvasPsPath --
*
* Given an array of points for a path, generate Postscript
* commands to create the path.
*
* Results:
* Postscript commands get appended to what's in interp->result.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
void
Tk_CanvasPsPath(interp, canvas, coordPtr, numPoints)
Tcl_Interp *interp; /* Put generated Postscript in this
* interpreter's result field. */
Tk_Canvas canvas; /* Canvas on whose behalf Postscript
* is being generated. */
double *coordPtr; /* Pointer to first in array of
* 2*numPoints coordinates giving
* points for path. */
int numPoints; /* Number of points at *coordPtr. */
{
Tk_PostscriptPath(interp, ((TkCanvas *) canvas)->psInfo,
coordPtr, numPoints);
}
static int
CanvGroupParseProc(clientData, interp, tkwin, value, widgRec, offset)
ClientData clientData;
Tcl_Interp *interp;
Tk_Window tkwin;
Tcl_Obj * value;
char *widgRec;
int offset;
{
Tk_Canvas canvas = (Tk_Canvas) widgRec;
TkCanvas *canvasPtr = (TkCanvas *) canvas;
Tk_Item **valp = (Tk_Item **) (widgRec+offset);
Tk_Item *itemPtr = NULL;
Tcl_HashEntry *entryPtr = NULL;
int id = 0;
if (Tcl_GetIntFromObj(interp,value,&id) == TCL_OK) {
if (id == 0) {
*valp = NULL;
return TCL_OK;
}
entryPtr = Tcl_FindHashEntry(&canvasPtr->idTable, (char *) id);
if (entryPtr != NULL && (itemPtr = (Tk_Item *) Tcl_GetHashValue(entryPtr)) != NULL) {
if (strcmp(itemPtr->typePtr->name,"group") == 0) {
*valp = itemPtr;
return TCL_OK;
} else {
Tcl_AppendResult(interp,"Id ",Tcl_GetString(value)," is \"",
itemPtr->typePtr->name,"\" not \"group\"",NULL);
}
} else {
Tcl_AppendResult(interp,"Id ",Tcl_GetString(value)," is not a valid item id", NULL);
}
}
return TCL_ERROR;
}
static Tcl_Obj *
CanvGroupPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)
ClientData clientData;
Tk_Window tkwin;
char *widgRec;
int offset;
Tcl_FreeProc **freeProcPtr;
{
Tk_Canvas canvas = (Tk_Canvas) widgRec;
Tk_Item **valp = (Tk_Item **) (widgRec+offset);
Tcl_Obj *result = NULL;
if (*valp) {
result = Tcl_NewIntObj((*valp)->id);
}
else {
result = Tcl_NewIntObj(0);
}
return result;
}