stk/Tk/generic/tkCanvas.c

3854 lines
115 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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-1995 Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* SCCS: @(#) tkCanvas.c 1.126 97/07/31 09:05:52
*/
#include "default.h"
#include "tkInt.h"
#include "tkPort.h"
#include "tkCanvas.h"
/*
* See tkCanvas.h for key data structures used to implement canvases.
*/
/*
* The structure defined below is used to keep track of a tag search
* in progress. Only the "prevPtr" 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 *prevPtr; /* Item just before last one found (or NULL
* if last one found was first in the item
* list of canvasPtr). */
Tk_Item *currentPtr; /* Pointer to last item returned. */
int searchOver; /* Non-zero means NextItem should always
* return NULL. */
} TagSearch;
/*
* Information used for argv parsing.
*/
static Tk_ConfigSpec configSpecs[] = {
{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_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_RELIEF, "-relief", "relief", "Relief",
DEF_CANVAS_RELIEF, Tk_Offset(TkCanvas, relief), 0},
{TK_CONFIG_STRING, "-scrollregion", "scrollRegion", "ScrollRegion",
DEF_CANVAS_SCROLL_REGION, Tk_Offset(TkCanvas, regionString),
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},
#ifdef SCM_CODE
{TK_CONFIG_CLOSURE, "-takefocus", "takeFocus", "TakeFocus",
#else
{TK_CONFIG_STRING, "-takefocus", "takeFocus", "TakeFocus",
#endif
DEF_CANVAS_TAKE_FOCUS, Tk_Offset(TkCanvas, takeFocus),
TK_CONFIG_NULL_OK},
{TK_CONFIG_PIXELS, "-width", "width", "Width",
DEF_CANVAS_WIDTH, Tk_Offset(TkCanvas, width), 0},
#ifdef SCM_CODE
{TK_CONFIG_CLOSURE, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
#else
{TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
#endif
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},
#ifdef SCM_CODE
{TK_CONFIG_CLOSURE, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
#else
{TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
#endif
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. */
/*
* Standard item types provided by Tk:
*/
extern Tk_ItemType tkArcType, tkBitmapType, tkImageType, tkLineType;
extern Tk_ItemType tkOvalType, tkPolygonType;
extern Tk_ItemType tkRectangleType, tkTextType, tkWindowType;
/*
* Various Tk_Uid's used by this module (set up during initialization):
*/
static Tk_Uid allUid = NULL;
static Tk_Uid currentUid = NULL;
/*
* Statistics counters:
*/
static int numIdSearches;
static int numSlowSearches;
/*
* 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, char **argv));
static void CanvasWorldChanged _ANSI_ARGS_((
ClientData instanceData));
static int ConfigureCanvas _ANSI_ARGS_((Tcl_Interp *interp,
TkCanvas *canvasPtr, int argc, char **argv,
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 int FindItems _ANSI_ARGS_((Tcl_Interp *interp,
TkCanvas *canvasPtr, int argc, char **argv,
char *newTag, char *cmdName, char *option));
static int FindArea _ANSI_ARGS_((Tcl_Interp *interp,
TkCanvas *canvasPtr, char **argv, Tk_Uid uid,
int enclosed));
static double GridAlign _ANSI_ARGS_((double coord, double spacing));
static void InitCanvas _ANSI_ARGS_((void));
static Tk_Item * NextItem _ANSI_ARGS_((TagSearch *searchPtr));
static void PickCurrentItem _ANSI_ARGS_((TkCanvas *canvasPtr,
XEvent *eventPtr));
static void PrintScrollFractions _ANSI_ARGS_((int screen1,
int screen2, int object1, int object2,
char *string));
static void RelinkItems _ANSI_ARGS_((TkCanvas *canvasPtr,
char *tag, Tk_Item *prevPtr));
static Tk_Item * StartTagSearch _ANSI_ARGS_((TkCanvas *canvasPtr,
char *tag, TagSearch *searchPtr));
/*
* The structure below defines canvas class behavior by means of procedures
* that can be invoked from generic window code.
*/
static TkClassProcs canvasClass = {
NULL, /* createProc. */
CanvasWorldChanged, /* geometryProc. */
NULL /* modalProc. */
};
/*
*--------------------------------------------------------------
*
* Tk_CanvasCmd --
*
* 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_CanvasCmd(clientData, interp, argc, argv)
ClientData clientData; /* Main window associated with
* interpreter. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
Tk_Window tkwin = (Tk_Window) clientData;
TkCanvas *canvasPtr;
Tk_Window new;
if (typeList == NULL) {
InitCanvas();
}
if (argc < 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " pathName ?options?\"", (char *) NULL);
return TCL_ERROR;
}
new = Tk_CreateWindowFromPath(interp, tkwin, argv[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 = Tcl_CreateCommand(interp,
Tk_PathName(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->regionString = 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->psInfoPtr = NULL;
Tk_SetClass(canvasPtr->tkwin, "Canvas");
TkSetClassProcs(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, argv+2, 0) != TCL_OK) {
goto error;
}
#ifdef STk_CODE
STk_sharp_dot_result(interp, Tk_PathName(canvasPtr->tkwin));
#else
interp->result = Tk_PathName(canvasPtr->tkwin);
#endif
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, argc, argv)
ClientData clientData; /* Information about canvas
* widget. */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
TkCanvas *canvasPtr = (TkCanvas *) clientData;
size_t length;
int c, result;
Tk_Item *itemPtr = NULL; /* Initialization needed only to
* prevent compiler warning. */
TagSearch search;
if (argc < 2) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " option ?arg arg ...?\"", (char *) NULL);
return TCL_ERROR;
}
Tcl_Preserve((ClientData) canvasPtr);
result = TCL_OK;
c = argv[1][0];
length = strlen(argv[1]);
if ((c == 'a') && (strncmp(argv[1], "addtag", length) == 0)) {
if (argc < 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " addtags tag searchCommand ?arg arg ...?\"",
(char *) NULL);
goto error;
}
result = FindItems(interp, canvasPtr, argc-3, argv+3, argv[2], argv[0],
" addtag tag");
} else if ((c == 'b') && (strncmp(argv[1], "bbox", length) == 0)
&& (length >= 2)) {
int i, gotAny;
int x1 = 0, y1 = 0, x2 = 0, y2 = 0; /* Initializations needed
* only to prevent compiler
* warnings. */
if (argc < 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " bbox tagOrId ?tagOrId ...?\"",
(char *) NULL);
goto error;
}
gotAny = 0;
for (i = 2; i < argc; i++) {
for (itemPtr = StartTagSearch(canvasPtr, argv[i], &search);
itemPtr != NULL; itemPtr = NextItem(&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) {
sprintf(interp->result, "%d %d %d %d", x1, y1, x2, y2);
}
} else if ((c == 'b') && (strncmp(argv[1], "bind", length) == 0)
&& (length >= 2)) {
ClientData object;
if ((argc < 3) || (argc > 5)) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " bind tagOrId ?sequence? ?command?\"",
(char *) NULL);
goto error;
}
/*
* Figure out what object to use for the binding (individual
* item vs. tag).
*/
object = 0;
if (isdigit(UCHAR(argv[2][0]))) {
int id;
char *end;
id = strtoul(argv[2], &end, 0);
if (*end != 0) {
goto bindByTag;
}
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
itemPtr = itemPtr->nextPtr) {
if (itemPtr->id == id) {
object = (ClientData) itemPtr;
break;
}
}
if (object == 0) {
Tcl_AppendResult(interp, "item \"", argv[2],
"\" doesn't exist", (char *) NULL);
goto error;
}
} else {
bindByTag:
object = (ClientData) Tk_GetUid(argv[2]);
}
/*
* Make a binding table if the canvas doesn't already have
* one.
*/
if (canvasPtr->bindingTable == NULL) {
canvasPtr->bindingTable = Tk_CreateBindingTable(interp);
}
if (argc == 5) {
#ifndef SCM_CODE
int append = 0;
#endif
unsigned long mask;
if (argv[4][0] == 0) {
result = Tk_DeleteBinding(interp, canvasPtr->bindingTable,
object, argv[3]);
goto done;
}
#ifndef SCM_CODE
if (argv[4][0] == '+') {
argv[4]++;
append = 1;
}
#endif
#ifndef SCM_CODE
mask = Tk_CreateBinding(interp, canvasPtr->bindingTable,
object, argv[3], argv[4], append);
#else
mask = Tk_CreateBinding(interp, canvasPtr->bindingTable,
object, argv[3], argv[4], argv[0], argv[2]);
#endif
if (mask == 0) {
goto error;
}
if (mask & (unsigned) ~(ButtonMotionMask|Button1MotionMask
|Button2MotionMask|Button3MotionMask|Button4MotionMask
|Button5MotionMask|ButtonPressMask|ButtonReleaseMask
|EnterWindowMask|LeaveWindowMask|KeyPressMask
|KeyReleaseMask|PointerMotionMask|VirtualEventMask)) {
Tk_DeleteBinding(interp, canvasPtr->bindingTable,
object, argv[3]);
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "requested illegal events; ",
"only key, button, motion, enter, leave, and virtual ",
"events may be used", (char *) NULL);
goto error;
}
} else if (argc == 4) {
char *command;
command = Tk_GetBinding(interp, canvasPtr->bindingTable,
object, argv[3]);
if (command == NULL) {
goto error;
}
interp->result = command;
} else {
Tk_GetAllBindings(interp, canvasPtr->bindingTable, object);
}
} else if ((c == 'c') && (strcmp(argv[1], "canvasx") == 0)) {
int x;
double grid;
if ((argc < 3) || (argc > 4)) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " canvasx screenx ?gridspacing?\"",
(char *) NULL);
goto error;
}
if (Tk_GetPixels(interp, canvasPtr->tkwin, argv[2], &x) != TCL_OK) {
goto error;
}
if (argc == 4) {
if (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[3],
&grid) != TCL_OK) {
goto error;
}
} else {
grid = 0.0;
}
x += canvasPtr->xOrigin;
Tcl_PrintDouble(interp, GridAlign((double) x, grid), interp->result);
} else if ((c == 'c') && (strcmp(argv[1], "canvasy") == 0)) {
int y;
double grid;
if ((argc < 3) || (argc > 4)) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " canvasy screeny ?gridspacing?\"",
(char *) NULL);
goto error;
}
if (Tk_GetPixels(interp, canvasPtr->tkwin, argv[2], &y) != TCL_OK) {
goto error;
}
if (argc == 4) {
if (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr,
argv[3], &grid) != TCL_OK) {
goto error;
}
} else {
grid = 0.0;
}
y += canvasPtr->yOrigin;
Tcl_PrintDouble(interp, GridAlign((double) y, grid), interp->result);
} else if ((c == 'c') && (strncmp(argv[1], "cget", length) == 0)
&& (length >= 2)) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " cget option\"",
(char *) NULL);
goto error;
}
result = Tk_ConfigureValue(interp, canvasPtr->tkwin, configSpecs,
(char *) canvasPtr, argv[2], 0);
} else if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
&& (length >= 3)) {
if (argc == 2) {
result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
(char *) canvasPtr, (char *) NULL, 0);
} else if (argc == 3) {
result = Tk_ConfigureInfo(interp, canvasPtr->tkwin, configSpecs,
(char *) canvasPtr, argv[2], 0);
} else {
result = ConfigureCanvas(interp, canvasPtr, argc-2, argv+2,
TK_CONFIG_ARGV_ONLY);
}
} else if ((c == 'c') && (strncmp(argv[1], "coords", length) == 0)
&& (length >= 3)) {
if (argc < 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " coords tagOrId ?x y x y ...?\"",
(char *) NULL);
goto error;
}
itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
if (itemPtr != NULL) {
if (argc != 3) {
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
}
if (itemPtr->typePtr->coordProc != NULL) {
result = (*itemPtr->typePtr->coordProc)(interp,
(Tk_Canvas) canvasPtr, itemPtr, argc-3, argv+3);
}
if (argc != 3) {
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
}
}
} else if ((c == 'c') && (strncmp(argv[1], "create", length) == 0)
&& (length >= 2)) {
Tk_ItemType *typePtr;
Tk_ItemType *matchPtr = NULL;
Tk_Item *itemPtr;
if (argc < 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " create type ?arg arg ...?\"", (char *) NULL);
goto error;
}
c = argv[2][0];
length = strlen(argv[2]);
for (typePtr = typeList; typePtr != NULL; typePtr = typePtr->nextPtr) {
if ((c == typePtr->name[0])
&& (strncmp(argv[2], typePtr->name, length) == 0)) {
if (matchPtr != NULL) {
badType:
Tcl_AppendResult(interp,
"unknown or ambiguous item type \"",
argv[2], "\"", (char *) NULL);
goto error;
}
matchPtr = typePtr;
}
}
if (matchPtr == NULL) {
goto badType;
}
typePtr = matchPtr;
itemPtr = (Tk_Item *) ckalloc((unsigned) typePtr->itemSize);
itemPtr->id = canvasPtr->nextId;
canvasPtr->nextId++;
itemPtr->tagPtr = itemPtr->staticTagSpace;
itemPtr->tagSpace = TK_TAG_SPACE;
itemPtr->numTags = 0;
itemPtr->typePtr = typePtr;
if ((*typePtr->createProc)(interp, (Tk_Canvas) canvasPtr,
itemPtr, argc-3, argv+3) != TCL_OK) {
ckfree((char *) itemPtr);
goto error;
}
itemPtr->nextPtr = NULL;
canvasPtr->hotPtr = itemPtr;
canvasPtr->hotPrevPtr = canvasPtr->lastItemPtr;
if (canvasPtr->lastItemPtr == NULL) {
canvasPtr->firstItemPtr = itemPtr;
} else {
canvasPtr->lastItemPtr->nextPtr = itemPtr;
}
canvasPtr->lastItemPtr = itemPtr;
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
canvasPtr->flags |= REPICK_NEEDED;
sprintf(interp->result, "%d", itemPtr->id);
} else if ((c == 'd') && (strncmp(argv[1], "dchars", length) == 0)
&& (length >= 2)) {
int first, last;
if ((argc != 4) && (argc != 5)) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " dchars tagOrId first ?last?\"",
(char *) NULL);
goto error;
}
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
if ((itemPtr->typePtr->indexProc == NULL)
|| (itemPtr->typePtr->dCharsProc == NULL)) {
continue;
}
if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
itemPtr, argv[3], &first) != TCL_OK) {
goto error;
}
if (argc == 5) {
if ((*itemPtr->typePtr->indexProc)(interp,
(Tk_Canvas) canvasPtr, itemPtr, argv[4], &last)
!= TCL_OK) {
goto error;
}
} 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.
*/
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
(*itemPtr->typePtr->dCharsProc)((Tk_Canvas) canvasPtr,
itemPtr, first, last);
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
}
} else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)
&& (length >= 2)) {
int i;
for (i = 2; i < argc; i++) {
for (itemPtr = StartTagSearch(canvasPtr, argv[i], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
if (canvasPtr->bindingTable != NULL) {
Tk_DeleteAllBindings(canvasPtr->bindingTable,
(ClientData) itemPtr);
}
(*itemPtr->typePtr->deleteProc)((Tk_Canvas) canvasPtr, itemPtr,
canvasPtr->display);
if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
ckfree((char *) itemPtr->tagPtr);
}
if (search.prevPtr == NULL) {
canvasPtr->firstItemPtr = itemPtr->nextPtr;
if (canvasPtr->firstItemPtr == NULL) {
canvasPtr->lastItemPtr = NULL;
}
} else {
search.prevPtr->nextPtr = itemPtr->nextPtr;
}
if (canvasPtr->lastItemPtr == itemPtr) {
canvasPtr->lastItemPtr = search.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;
}
}
}
} else if ((c == 'd') && (strncmp(argv[1], "dtag", length) == 0)
&& (length >= 2)) {
Tk_Uid tag;
int i;
if ((argc != 3) && (argc != 4)) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " dtag tagOrId ?tagToDelete?\"",
(char *) NULL);
goto error;
}
if (argc == 4) {
tag = Tk_GetUid(argv[3]);
} else {
tag = Tk_GetUid(argv[2]);
}
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
for (i = itemPtr->numTags-1; i >= 0; i--) {
if (itemPtr->tagPtr[i] == tag) {
itemPtr->tagPtr[i] = itemPtr->tagPtr[itemPtr->numTags-1];
itemPtr->numTags--;
}
}
}
} else if ((c == 'f') && (strncmp(argv[1], "find", length) == 0)
&& (length >= 2)) {
if (argc < 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " find searchCommand ?arg arg ...?\"",
(char *) NULL);
goto error;
}
result = FindItems(interp, canvasPtr, argc-2, argv+2, (char *) NULL,
argv[0]," find");
} else if ((c == 'f') && (strncmp(argv[1], "focus", length) == 0)
&& (length >= 2)) {
if (argc > 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " focus ?tagOrId?\"",
(char *) NULL);
goto error;
}
itemPtr = canvasPtr->textInfo.focusItemPtr;
if (argc == 2) {
if (itemPtr != NULL) {
sprintf(interp->result, "%d", itemPtr->id);
}
goto done;
}
if ((itemPtr != NULL) && (canvasPtr->textInfo.gotFocus)) {
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
}
if (argv[2][0] == 0) {
canvasPtr->textInfo.focusItemPtr = NULL;
goto done;
}
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
if (itemPtr->typePtr->icursorProc != NULL) {
break;
}
}
if (itemPtr == NULL) {
goto done;
}
canvasPtr->textInfo.focusItemPtr = itemPtr;
if (canvasPtr->textInfo.gotFocus) {
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
}
} else if ((c == 'g') && (strncmp(argv[1], "gettags", length) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " gettags tagOrId\"", (char *) NULL);
goto error;
}
itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
if (itemPtr != NULL) {
int i;
#ifdef SCM_CODE
Tcl_AppendElement(interp, "(");
for (i = 0; i < itemPtr->numTags; i++) {
Tcl_AppendResult(interp, " \"", (char *) itemPtr->tagPtr[i],
"\"", NULL);
}
Tcl_AppendElement(interp, ")");
#else
for (i = 0; i < itemPtr->numTags; i++) {
Tcl_AppendElement(interp, (char *) itemPtr->tagPtr[i]);
}
#endif
}
} else if ((c == 'i') && (strncmp(argv[1], "icursor", length) == 0)
&& (length >= 2)) {
int index;
if (argc != 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " icursor tagOrId index\"",
(char *) NULL);
goto error;
}
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
if ((itemPtr->typePtr->indexProc == NULL)
|| (itemPtr->typePtr->icursorProc == NULL)) {
goto done;
}
if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
itemPtr, argv[3], &index) != TCL_OK) {
goto error;
}
(*itemPtr->typePtr->icursorProc)((Tk_Canvas) canvasPtr, itemPtr,
index);
if ((itemPtr == canvasPtr->textInfo.focusItemPtr)
&& (canvasPtr->textInfo.cursorOn)) {
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
}
}
} else if ((c == 'i') && (strncmp(argv[1], "index", length) == 0)
&& (length >= 3)) {
int index;
if (argc != 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " index tagOrId string\"",
(char *) NULL);
goto error;
}
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
if (itemPtr->typePtr->indexProc != NULL) {
break;
}
}
if (itemPtr == NULL) {
Tcl_AppendResult(interp, "can't find an indexable item \"",
argv[2], "\"", (char *) NULL);
goto error;
}
if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
itemPtr, argv[3], &index) != TCL_OK) {
goto error;
}
sprintf(interp->result, "%d", index);
} else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)
&& (length >= 3)) {
int beforeThis;
if (argc != 5) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " insert tagOrId beforeThis string\"",
(char *) NULL);
goto error;
}
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
if ((itemPtr->typePtr->indexProc == NULL)
|| (itemPtr->typePtr->insertProc == NULL)) {
continue;
}
if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
itemPtr, argv[3], &beforeThis) != TCL_OK) {
goto error;
}
/*
* 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.
*/
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
(*itemPtr->typePtr->insertProc)((Tk_Canvas) canvasPtr,
itemPtr, beforeThis, argv[4]);
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr, itemPtr->x1,
itemPtr->y1, itemPtr->x2, itemPtr->y2);
}
} else if ((c == 'i') && (strncmp(argv[1], "itemcget", length) == 0)
&& (length >= 6)) {
if (argc != 4) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " itemcget tagOrId option\"",
(char *) NULL);
return TCL_ERROR;
}
itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
if (itemPtr != NULL) {
result = Tk_ConfigureValue(canvasPtr->interp, canvasPtr->tkwin,
itemPtr->typePtr->configSpecs, (char *) itemPtr,
argv[3], 0);
}
} else if ((c == 'i') && (strncmp(argv[1], "itemconfigure", length) == 0)
&& (length >= 6)) {
if (argc < 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " itemconfigure tagOrId ?option value ...?\"",
(char *) NULL);
goto error;
}
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
if (argc == 3) {
result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
itemPtr->typePtr->configSpecs, (char *) itemPtr,
(char *) NULL, 0);
} else if (argc == 4) {
result = Tk_ConfigureInfo(canvasPtr->interp, canvasPtr->tkwin,
itemPtr->typePtr->configSpecs, (char *) itemPtr,
argv[3], 0);
} else {
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
result = (*itemPtr->typePtr->configProc)(interp,
(Tk_Canvas) canvasPtr, itemPtr, argc-3, argv+3,
TK_CONFIG_ARGV_ONLY);
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
canvasPtr->flags |= REPICK_NEEDED;
}
if ((result != TCL_OK) || (argc < 5)) {
break;
}
}
} else if ((c == 'l') && (strncmp(argv[1], "lower", length) == 0)) {
Tk_Item *prevPtr;
if ((argc != 3) && (argc != 4)) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " lower tagOrId ?belowThis?\"",
(char *) NULL);
goto error;
}
/*
* First find the item just after which we'll insert the
* named items.
*/
if (argc == 3) {
prevPtr = NULL;
} else {
prevPtr = StartTagSearch(canvasPtr, argv[3], &search);
if (prevPtr != NULL) {
prevPtr = search.prevPtr;
} else {
Tcl_AppendResult(interp, "tag \"", argv[3],
"\" doesn't match any items", (char *) NULL);
goto error;
}
}
RelinkItems(canvasPtr, argv[2], prevPtr);
} else if ((c == 'm') && (strncmp(argv[1], "move", length) == 0)) {
double xAmount, yAmount;
if (argc != 5) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " move tagOrId xAmount yAmount\"",
(char *) NULL);
goto error;
}
if ((Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr, argv[3],
&xAmount) != TCL_OK) || (Tk_CanvasGetCoord(interp,
(Tk_Canvas) canvasPtr, argv[4], &yAmount) != TCL_OK)) {
goto error;
}
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
(void) (*itemPtr->typePtr->translateProc)((Tk_Canvas) canvasPtr,
itemPtr, xAmount, yAmount);
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
canvasPtr->flags |= REPICK_NEEDED;
}
} else if ((c == 'p') && (strncmp(argv[1], "postscript", length) == 0)) {
result = TkCanvPostscriptCmd(canvasPtr, interp, argc, argv);
#ifdef SCM_CODE
/* Return the content of interp->result as a string since newlines
* and case are significant in PostScript.
*/
# ifdef STk_CODE
STk_stringify_result(interp, interp->result);
# else
SCM_stringify_result(interp, interp->result);
# endif
#endif
} else if ((c == 'r') && (strncmp(argv[1], "raise", length) == 0)) {
Tk_Item *prevPtr;
if ((argc != 3) && (argc != 4)) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " raise tagOrId ?aboveThis?\"",
(char *) NULL);
goto error;
}
/*
* First find the item just after which we'll insert the
* named items.
*/
if (argc == 3) {
prevPtr = canvasPtr->lastItemPtr;
} else {
prevPtr = NULL;
for (itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
prevPtr = itemPtr;
}
if (prevPtr == NULL) {
Tcl_AppendResult(interp, "tagOrId \"", argv[3],
"\" doesn't match any items", (char *) NULL);
goto error;
}
}
RelinkItems(canvasPtr, argv[2], prevPtr);
} else if ((c == 's') && (strncmp(argv[1], "scale", length) == 0)
&& (length >= 3)) {
double xOrigin, yOrigin, xScale, yScale;
if (argc != 7) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " scale tagOrId xOrigin yOrigin xScale yScale\"",
(char *) NULL);
goto error;
}
if ((Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr,
argv[3], &xOrigin) != TCL_OK)
|| (Tk_CanvasGetCoord(interp, (Tk_Canvas) canvasPtr,
argv[4], &yOrigin) != TCL_OK)
|| (Tcl_GetDouble(interp, argv[5], &xScale) != TCL_OK)
|| (Tcl_GetDouble(interp, argv[6], &yScale) != TCL_OK)) {
goto error;
}
if ((xScale == 0.0) || (yScale == 0.0)) {
interp->result = "scale factor cannot be zero";
goto error;
}
for (itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
itemPtr != NULL; itemPtr = NextItem(&search)) {
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
(void) (*itemPtr->typePtr->scaleProc)((Tk_Canvas) canvasPtr,
itemPtr, xOrigin, yOrigin, xScale, yScale);
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
itemPtr->x1, itemPtr->y1, itemPtr->x2, itemPtr->y2);
canvasPtr->flags |= REPICK_NEEDED;
}
} else if ((c == 's') && (strncmp(argv[1], "scan", length) == 0)
&& (length >= 3)) {
int x, y;
if (argc != 5) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " scan mark|dragto x y\"", (char *) NULL);
goto error;
}
if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
|| (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)){
goto error;
}
if ((argv[2][0] == 'm')
&& (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
canvasPtr->scanX = x;
canvasPtr->scanXOrigin = canvasPtr->xOrigin;
canvasPtr->scanY = y;
canvasPtr->scanYOrigin = canvasPtr->yOrigin;
} else if ((argv[2][0] == 'd')
&& (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
int newXOrigin, newYOrigin, tmp;
/*
* Compute a new view origin for the canvas, amplifying the
* mouse motion.
*/
tmp = canvasPtr->scanXOrigin - 10*(x - canvasPtr->scanX)
- canvasPtr->scrollX1;
newXOrigin = canvasPtr->scrollX1 + tmp;
tmp = canvasPtr->scanYOrigin - 10*(y - canvasPtr->scanY)
- canvasPtr->scrollY1;
newYOrigin = canvasPtr->scrollY1 + tmp;
CanvasSetOrigin(canvasPtr, newXOrigin, newYOrigin);
} else {
Tcl_AppendResult(interp, "bad scan option \"", argv[2],
"\": must be mark or dragto", (char *) NULL);
goto error;
}
} else if ((c == 's') && (strncmp(argv[1], "select", length) == 0)
&& (length >= 2)) {
int index;
if (argc < 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " select option ?tagOrId? ?arg?\"", (char *) NULL);
goto error;
}
if (argc >= 4) {
for (itemPtr = StartTagSearch(canvasPtr, argv[3], &search);
itemPtr != NULL; itemPtr = NextItem(&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 \"",
argv[3], "\"", (char *) NULL);
goto error;
}
}
if (argc == 5) {
if ((*itemPtr->typePtr->indexProc)(interp, (Tk_Canvas) canvasPtr,
itemPtr, argv[4], &index) != TCL_OK) {
goto error;
}
}
length = strlen(argv[2]);
c = argv[2][0];
if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
if (argc != 5) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " select adjust tagOrId index\"",
(char *) NULL);
goto error;
}
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);
} else if ((c == 'c') && (argv[2] != NULL)
&& (strncmp(argv[2], "clear", length) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " select clear\"", (char *) NULL);
goto error;
}
if (canvasPtr->textInfo.selItemPtr != NULL) {
Tk_CanvasEventuallyRedraw((Tk_Canvas) canvasPtr,
canvasPtr->textInfo.selItemPtr->x1,
canvasPtr->textInfo.selItemPtr->y1,
canvasPtr->textInfo.selItemPtr->x2,
canvasPtr->textInfo.selItemPtr->y2);
canvasPtr->textInfo.selItemPtr = NULL;
}
goto done;
} else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
if (argc != 5) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " select from tagOrId index\"",
(char *) NULL);
goto error;
}
canvasPtr->textInfo.anchorItemPtr = itemPtr;
canvasPtr->textInfo.selectAnchor = index;
} else if ((c == 'i') && (strncmp(argv[2], "item", length) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " select item\"", (char *) NULL);
goto error;
}
if (canvasPtr->textInfo.selItemPtr != NULL) {
sprintf(interp->result, "%d",
canvasPtr->textInfo.selItemPtr->id);
}
} else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
if (argc != 5) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " select to tagOrId index\"",
(char *) NULL);
goto error;
}
CanvasSelectTo(canvasPtr, itemPtr, index);
} else {
Tcl_AppendResult(interp, "bad select option \"", argv[2],
"\": must be adjust, clear, from, item, or to",
(char *) NULL);
goto error;
}
} else if ((c == 't') && (strncmp(argv[1], "type", length) == 0)) {
if (argc != 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
argv[0], " type tag\"", (char *) NULL);
goto error;
}
itemPtr = StartTagSearch(canvasPtr, argv[2], &search);
if (itemPtr != NULL) {
interp->result = itemPtr->typePtr->name;
}
} else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
int count, type;
int newX = 0; /* Initialization needed only to prevent
* gcc warnings. */
double fraction;
if (argc == 2) {
PrintScrollFractions(canvasPtr->xOrigin + canvasPtr->inset,
canvasPtr->xOrigin + Tk_Width(canvasPtr->tkwin)
- canvasPtr->inset, canvasPtr->scrollX1,
canvasPtr->scrollX2, interp->result);
} else {
type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
switch (type) {
case TK_SCROLL_ERROR:
goto error;
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);
}
} else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
int count, type;
int newY = 0; /* Initialization needed only to prevent
* gcc warnings. */
double fraction;
if (argc == 2) {
PrintScrollFractions(canvasPtr->yOrigin + canvasPtr->inset,
canvasPtr->yOrigin + Tk_Height(canvasPtr->tkwin)
- canvasPtr->inset, canvasPtr->scrollY1,
canvasPtr->scrollY2, interp->result);
} else {
type = Tk_GetScrollInfo(interp, argc, argv, &fraction, &count);
switch (type) {
case TK_SCROLL_ERROR:
goto error;
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);
}
} else {
Tcl_AppendResult(interp, "bad option \"", argv[1],
"\": must be 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, or yview",
(char *) NULL);
goto error;
}
done:
Tcl_Release((ClientData) canvasPtr);
return result;
error:
Tcl_Release((ClientData) canvasPtr);
return TCL_ERROR;
}
/*
*----------------------------------------------------------------------
*
* 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;
/*
* Free up all of the items in the canvas.
*/
for (itemPtr = canvasPtr->firstItemPtr; itemPtr != NULL;
itemPtr = canvasPtr->firstItemPtr) {
canvasPtr->firstItemPtr = itemPtr->nextPtr;
(*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.
*/
if (canvasPtr->pixmapGC != None) {
Tk_FreeGC(canvasPtr->display, canvasPtr->pixmapGC);
}
Tcl_DeleteTimerHandler(canvasPtr->insertBlinkHandler);
if (canvasPtr->bindingTable != NULL) {
Tk_DeleteBindingTable(canvasPtr->bindingTable);
}
Tk_FreeOptions(configSpecs, (char *) canvasPtr, canvasPtr->display, 0);
ckfree((char *) canvasPtr);
}
/*
*----------------------------------------------------------------------
*
* ConfigureCanvas --
*
* This procedure is called to process an argv/argc 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 interp->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, argc, argv, flags)
Tcl_Interp *interp; /* Used for error reporting. */
TkCanvas *canvasPtr; /* Information about widget; may or may
* not already have values for some fields. */
int argc; /* Number of valid entries in argv. */
char **argv; /* Arguments. */
int flags; /* Flags to pass to Tk_ConfigureWidget. */
{
XGCValues gcValues;
GC new;
if (Tk_ConfigureWidget(interp, canvasPtr->tkwin, configSpecs,
argc, argv, (char *) canvasPtr, flags) != 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;
gcValues.function = GXcopy;
gcValues.foreground = Tk_3DBorderColor(canvasPtr->bgBorder)->pixel;
gcValues.graphics_exposures = False;
new = Tk_GetGC(canvasPtr->tkwin,
GCFunction|GCForeground|GCGraphicsExposures, &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->regionString != NULL) {
int argc2;
char **argv2;
if (Tcl_SplitList(canvasPtr->interp, canvasPtr->regionString,
&argc2, &argv2) != TCL_OK) {
return TCL_ERROR;
}
if (argc2 != 4) {
Tcl_AppendResult(interp, "bad scrollRegion \"",
canvasPtr->regionString, "\"", (char *) NULL);
badRegion:
ckfree(canvasPtr->regionString);
ckfree((char *) argv2);
canvasPtr->regionString = NULL;
return TCL_ERROR;
}
if ((Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
argv2[0], &canvasPtr->scrollX1) != TCL_OK)
|| (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
argv2[1], &canvasPtr->scrollY1) != TCL_OK)
|| (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
argv2[2], &canvasPtr->scrollX2) != TCL_OK)
|| (Tk_GetPixels(canvasPtr->interp, canvasPtr->tkwin,
argv2[3], &canvasPtr->scrollY2) != TCL_OK)) {
goto badRegion;
}
ckfree((char *) argv2);
}
/*
* 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));
}
/*
*--------------------------------------------------------------
*
* 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;
if (canvasPtr->tkwin == NULL) {
return;
}
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;
}
}
/*
* 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.
*/
canvasPtr->drawableXOrigin = screenX1 - 30;
canvasPtr->drawableYOrigin = screenY1 - 30;
pixmap = Tk_GetPixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
(screenX2 + 30 - canvasPtr->drawableXOrigin),
(screenY2 + 30 - canvasPtr->drawableYOrigin),
Tk_Depth(tkwin));
/*
* Clear the area to be redrawn.
*/
width = screenX2 - screenX1;
height = screenY2 - screenY1;
XFillRectangle(Tk_Display(tkwin), pixmap, canvasPtr->pixmapGC,
screenX1 - canvasPtr->drawableXOrigin,
screenY1 - canvasPtr->drawableYOrigin, (unsigned int) width,
(unsigned int) height);
/*
* 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
|| (itemPtr->x1 >= canvasPtr->redrawX2)
|| (itemPtr->y1 >= canvasPtr->redrawY2)
|| (itemPtr->x2 < canvasPtr->redrawX1)
|| (itemPtr->y2 < canvasPtr->redrawY1)) {
continue;
}
}
(*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 gc;
if (canvasPtr->textInfo.gotFocus) {
gc = Tk_GCForColor(canvasPtr->highlightColorPtr,
Tk_WindowId(tkwin));
} else {
gc = Tk_GCForColor(canvasPtr->highlightBgColorPtr,
Tk_WindowId(tkwin));
}
Tk_DrawFocusHighlight(tkwin, gc, canvasPtr->highlightWidth,
Tk_WindowId(tkwin));
}
}
done:
canvasPtr->flags &= ~REDRAW_PENDING;
canvasPtr->redrawX1 = canvasPtr->redrawX2 = 0;
canvasPtr->redrawY1 = canvasPtr->redrawY2 = 0;
if (canvasPtr->flags & UPDATE_SCROLLBARS) {
CanvasUpdateScrollbars(canvasPtr);
}
}
/*
*--------------------------------------------------------------
*
* 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) {
canvasPtr->tkwin = NULL;
Tcl_DeleteCommandFromToken(canvasPtr->interp,
canvasPtr->widgetCmd);
}
if (canvasPtr->flags & REDRAW_PENDING) {
Tcl_CancelIdleCall(DisplayCanvas, (ClientData) canvasPtr);
}
Tcl_EventuallyFree((ClientData) canvasPtr, 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) {
(*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 ((x1 == x2) || (y1 == y2)) {
return;
}
if (canvasPtr->flags & REDRAW_PENDING) {
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;
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;
allUid = Tk_GetUid("all");
currentUid = Tk_GetUid("current");
}
/*
*--------------------------------------------------------------
*
* 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, tag, searchPtr)
TkCanvas *canvasPtr; /* Canvas whose items are to be
* searched. */
char *tag; /* String giving tag value. */
TagSearch *searchPtr; /* Record describing tag search;
* will be initialized here. */
{
int id;
Tk_Item *itemPtr, *prevPtr;
Tk_Uid *tagPtr;
Tk_Uid uid;
int count;
/*
* 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;
numIdSearches++;
id = strtoul(tag, &end, 0);
if (*end == 0) {
itemPtr = canvasPtr->hotPtr;
prevPtr = canvasPtr->hotPrevPtr;
if ((itemPtr == NULL) || (itemPtr->id != id) || (prevPtr == NULL)
|| (prevPtr->nextPtr != itemPtr)) {
numSlowSearches++;
for (prevPtr = NULL, itemPtr =