/*
* tkCanvUtil.c --
*
* This procedure contains a collection of utility procedures
* used by the implementations of various canvas item types.
*
* Copyright (c) 1994 Sun Microsystems, Inc.
* Copyright (c) 1994 Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
* RCS: @(#) $Id: tkCanvUtil.c,v 1.10 2003/01/17 19:54:09 drh Exp $
*/
#include "tkPort.h"
#include "tkInt.h"
#include "tkCanvases.h"
/*
* Custom option for handling "-tags" options for canvas items:
*/
#include "tkCanvas.h"
#include <assert.h>
/*
*----------------------------------------------------------------------
*
* Tk_CanvasTkwin --
*
* Given a token for a canvas, this procedure returns the
* widget that represents the canvas.
*
* Results:
* The return value is a handle for the widget.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
Tk_Window
Tk_CanvasTkwin(canvas)
Tk_Canvas canvas; /* Token for the canvas. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
return canvasPtr->tkwin;
}
/*
*----------------------------------------------------------------------
*
* Tk_CanvasDrawableCoords --
*
* Given an (x,y) coordinate pair within a canvas, this procedure
* returns the corresponding coordinates at which the point should
* be drawn in the drawable used for display.
*
* Results:
* There is no return value. The values at *drawableXPtr and
* *drawableYPtr are filled in with the coordinates at which
* x and y should be drawn. These coordinates are clipped
* to fit within a "short", since this is what X uses in
* most cases for drawing.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
Tk_CanvasDrawableCoords(canvas, x, y, drawableXPtr, drawableYPtr)
Tk_Canvas canvas; /* Token for the canvas. */
double x, y; /* Coordinates in canvas space. */
short *drawableXPtr, *drawableYPtr; /* Screen coordinates are stored
* here. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
double tmp;
tmp = x - canvasPtr->drawableXOrigin;
if (tmp > 0) {
tmp += 0.5;
} else {
tmp -= 0.5;
}
if (tmp > 32767) {
*drawableXPtr = 32767;
} else if (tmp < -32768) {
*drawableXPtr = -32768;
} else {
*drawableXPtr = (short) tmp;
}
tmp = y - canvasPtr->drawableYOrigin;
if (tmp > 0) {
tmp += 0.5;
} else {
tmp -= 0.5;
}
if (tmp > 32767) {
*drawableYPtr = 32767;
} else if (tmp < -32768) {
*drawableYPtr = -32768;
} else {
*drawableYPtr = (short) tmp;
}
}
/*
*----------------------------------------------------------------------
*
* Tk_CanvasWindowCoords --
*
* Given an (x,y) coordinate pair within a canvas, this procedure
* returns the corresponding coordinates in the canvas's window.
*
* Results:
* There is no return value. The values at *screenXPtr and
* *screenYPtr are filled in with the coordinates at which
* (x,y) appears in the canvas's window. These coordinates
* are clipped to fit within a "short", since this is what X
* uses in most cases for drawing.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
void
Tk_CanvasWindowCoords(canvas, x, y, screenXPtr, screenYPtr)
Tk_Canvas canvas; /* Token for the canvas. */
double x, y; /* Coordinates in canvas space. */
short *screenXPtr, *screenYPtr; /* Screen coordinates are stored
* here. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
double tmp;
tmp = x - canvasPtr->xOrigin;
if (tmp > 0) {
tmp += 0.5;
} else {
tmp -= 0.5;
}
if (tmp > 32767) {
*screenXPtr = 32767;
} else if (tmp < -32768) {
*screenXPtr = -32768;
} else {
*screenXPtr = (short) tmp;
}
tmp = y - canvasPtr->yOrigin;
if (tmp > 0) {
tmp += 0.5;
} else {
tmp -= 0.5;
}
if (tmp > 32767) {
*screenYPtr = 32767;
} else if (tmp < -32768) {
*screenYPtr = -32768;
} else {
*screenYPtr = (short) tmp;
}
}
/*
*--------------------------------------------------------------
*
* Tk_CanvasGetCoord --
*
* Given a string, returns a floating-point canvas coordinate
* corresponding to that string.
*
* Results:
* The return value is a standard Tcl return result. If
* TCL_OK is returned, then everything went well and the
* canvas coordinate is stored at *doublePtr; otherwise
* TCL_ERROR is returned and an error message is left in
* the interp's result.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
int
Tk_CanvasGetCoord(interp, canvas, string, doublePtr)
Tcl_Interp *interp; /* Interpreter for error reporting. */
Tk_Canvas canvas; /* Canvas to which coordinate applies. */
CONST char *string; /* Describes coordinate (any screen
* coordinate form may be used here). */
double *doublePtr; /* Place to store converted coordinate. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
if (Tk_GetScreenMM(canvasPtr->interp, canvasPtr->tkwin, string,
doublePtr) != TCL_OK) {
return TCL_ERROR;
}
*doublePtr *= canvasPtr->pixelsPerMM;
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Tk_CanvasGetCoordFromObj --
*
* Given a string, returns a floating-point canvas coordinate
* corresponding to that string.
*
* Results:
* The return value is a standard Tcl return result. If
* TCL_OK is returned, then everything went well and the
* canvas coordinate is stored at *doublePtr; otherwise
* TCL_ERROR is returned and an error message is left in
* interp->result.
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
int
Tk_CanvasGetCoordFromObj(interp, canvas, obj, doublePtr)
Tcl_Interp *interp; /* Interpreter for error reporting. */
Tk_Canvas canvas; /* Canvas to which coordinate applies. */
Tcl_Obj *obj; /* Describes coordinate (any screen
* coordinate form may be used here). */
double *doublePtr; /* Place to store converted coordinate. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
if (Tk_GetMMFromObj(canvasPtr->interp, canvasPtr->tkwin, obj,
doublePtr) != TCL_OK) {
return TCL_ERROR;
}
*doublePtr *= canvasPtr->pixelsPerMM;
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* Tk_CanvasSetStippleOrigin --
*
* This procedure sets the stipple origin in a graphics context
* so that stipples drawn with the GC will line up with other
* stipples previously drawn in the canvas.
*
* Results:
* None.
*
* Side effects:
* The graphics context is modified.
*
*----------------------------------------------------------------------
*/
void
Tk_CanvasSetStippleOrigin(canvas, gc)
Tk_Canvas canvas; /* Token for a canvas. */
GC gc; /* Graphics context that is about to be
* used to draw a stippled pattern as
* part of redisplaying the canvas. */
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
XSetTSOrigin(canvasPtr->display, gc, -canvasPtr->drawableXOrigin,
-canvasPtr->drawableYOrigin);
}
/*
*----------------------------------------------------------------------
*
* Tk_CanvasSetOffset--
*
* This procedure sets the stipple offset in a graphics
* context so that stipples drawn with the GC will
* line up with other stipples with the same offset.
*
* Results:
* None.
*
* Side effects:
* The graphics context is modified.
*
*----------------------------------------------------------------------
*/
void
Tk_CanvasSetOffset(canvas, gc, offset)
Tk_Canvas canvas; /* Token for a canvas. */
GC gc; /* Graphics context that is about to be
* used to draw a stippled pattern as
* part of redisplaying the canvas. */
Tk_TSOffset *offset; /* offset (may be NULL pointer)*/
{
TkCanvas *canvasPtr = (TkCanvas *) canvas;
int flags = 0;
int x = - canvasPtr->drawableXOrigin;
int y = - canvasPtr->drawableYOrigin;
if (offset != NULL) {
flags = offset->flags;
x += offset->xoffset;
y += offset->yoffset;
}
if ((flags & TK_OFFSET_RELATIVE) && !(flags & TK_OFFSET_INDEX)) {
Tk_SetTSOrigin(canvasPtr->tkwin, gc, x - canvasPtr->xOrigin,
y - canvasPtr->yOrigin);
} else {
XSetTSOrigin(canvasPtr->display, gc, x, y);
}
}
/*
*----------------------------------------------------------------------
*
* Tk_CanvasGetTextInfo --
*
* This procedure returns a pointer to a structure containing
* information about the selection and insertion cursor for
* a canvas widget. Items such as text items save the pointer
* and use it to share access to the information with the generic
* canvas code.
*
* Results:
* The return value is a pointer to the structure holding text
* information for the canvas. Most of the fields should not
* be modified outside the generic canvas code; see the user
* documentation for details.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
Tk_CanvasTextInfo *
Tk_CanvasGetTextInfo(canvas)
Tk_Canvas canvas; /* Token for the canvas widget. */
{
return &((TkCanvas *) canvas)->textInfo;
}
/*
*--------------------------------------------------------------
*
* Tk_CanvasTagsParseProc --
*
* This procedure is invoked during option processing to handle
* "-tags" options for canvas items.
*
* Results:
* A standard Tcl return value.
*
* Side effects:
* The tags for a given item get replaced by those indicated
* in the value argument.
*
*--------------------------------------------------------------
*/
int
Tk_CanvasTagsParseProc(clientData, interp, tkwin, arg, widgRec, offset)
ClientData clientData; /* Not used.*/
Tcl_Interp *interp; /* Used for reporting errors. */
Tk_Window tkwin; /* Window containing canvas widget. */
Tcl_Obj *arg; /* Value of option (list of tag
* names). */
char *widgRec; /* Pointer to record for item. */
int offset; /* Offset into item (ignored). */
{
register Tk_Item *itemPtr = (Tk_Item *) widgRec;
int argc, i;
Tcl_Obj **objv;
Tk_Uid *newPtr;
/*
* Break the value up into the individual tag names.
*/
if (Tcl_ListObjGetElements(interp, arg, &argc, &objv) != TCL_OK) {
return TCL_ERROR;
}
/*
* Make sure that there's enough space in the item to hold the
* tag names.
*/
if (itemPtr->tagSpace < argc) {
newPtr = (Tk_Uid *) ckalloc((unsigned) (argc * sizeof(Tk_Uid)));
for (i = itemPtr->numTags-1; i >= 0; i--) {
newPtr[i] = itemPtr->tagPtr[i];
}
if (itemPtr->tagPtr != itemPtr->staticTagSpace) {
ckfree((char *) itemPtr->tagPtr);
}
itemPtr->tagPtr = newPtr;
itemPtr->tagSpace = argc;
}
itemPtr->numTags = argc;
for (i = 0; i < argc; i++) {
itemPtr->tagPtr[i] = Tk_GetUid(Tcl_GetString(objv[i]));
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Tk_CanvasTagsPrintProc --
*
* This procedure is invoked by the Tk configuration code
* to produce a printable string for the "-tags" configuration
* option for canvas items.
*
* Results:
* The return value is a string describing all the tags for
* the item referred to by "widgRec". In addition, *freeProcPtr
* is filled in with the address of a procedure to call to free
* the result string when it's no longer needed (or NULL to
* indicate that the string doesn't need to be freed).
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
Tcl_Obj *
Tk_CanvasTagsPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)
ClientData clientData; /* Ignored. */
Tk_Window tkwin; /* Window containing canvas widget. */
char *widgRec; /* Pointer to record for item. */
int offset; /* Ignored. */
Tcl_FreeProc **freeProcPtr; /* Pointer to variable to fill in with
* information about how to reclaim
* storage for return string. */
{
register Tk_Item *itemPtr = (Tk_Item *) widgRec;
Tcl_Obj *result = NULL;
int i;
result = Tcl_NewListObj(0,NULL);
for (i=0; i < itemPtr->numTags; i++) {
Tcl_ListObjAppendElement(NULL,result,
Tcl_NewStringObj((char *)itemPtr->tagPtr[i],-1));
}
return result;
}
static int DashConvert _ANSI_ARGS_((char *l, CONST char *p,
int n, double width));
#define ABS(a) ((a>=0)?(a):(-(a)))
/*
*--------------------------------------------------------------
*
* TkCanvasDashParseProc --
*
* This procedure is invoked during option processing to handle
* "-dash", "-activedash" and "-disableddash" options for canvas
* objects.
*
* Results:
* A standard Tcl return value.
*
* Side effects:
* The dash list for a given canvas object gets replaced by
* those indicated in the value argument.
*
*--------------------------------------------------------------
*/
int
TkCanvasDashParseProc(clientData, interp, tkwin, value, widgRec, offset)
ClientData clientData; /* Not used.*/
Tcl_Interp *interp; /* Used for reporting errors. */
Tk_Window tkwin; /* Window containing canvas widget. */
Tcl_Obj * value; /* Value of option. */
char *widgRec; /* Pointer to record for item. */
int offset; /* Offset into item. */
{
return Tk_GetDash(interp, value, (Tk_Dash *)(widgRec+offset));
}
/*
*--------------------------------------------------------------
*
* TkCanvasDashPrintProc --
*
* This procedure is invoked by the Tk configuration code
* to produce a printable string for the "-dash", "-activedash"
* and "-disableddash" configuration options for canvas items.
*
* Results:
* The return value is a string describing all the dash list for
* the item referred to by "widgRec"and "offset". In addition,
* *freeProcPtr is filled in with the address of a procedure to
* call to free the result string when it's no longer needed (or
* NULL to indicate that the string doesn't need to be freed).
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
Tcl_Obj *
TkCanvasDashPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)
ClientData clientData; /* Ignored. */
Tk_Window tkwin; /* Window containing canvas widget. */
char *widgRec; /* Pointer to record for item. */
int offset; /* Offset in record for item. */
Tcl_FreeProc **freeProcPtr; /* Pointer to variable to fill in with
* information about how to reclaim
* storage for return string. */
{
Tk_Dash *dash = (Tk_Dash *) (widgRec+offset);
char *buffer = NULL;
char *p;
int i = dash->number;
Tcl_Obj * result = NULL;
if (i<0) {
p = (-i > (int) sizeof(char *)) ? dash->pattern.pt : dash->pattern.array;
result = Tcl_NewStringObj(p,-i);
return result;
} else if (!i) {
*freeProcPtr = (Tcl_FreeProc *) NULL;
LangSetDefault(&result,"");
return result;
}
result = Tcl_NewListObj(0,NULL);
p = (i > (int) sizeof(char *)) ? dash->pattern.pt : dash->pattern.array;
while(i--) {
Tcl_ListObjAppendElement(NULL,result,Tcl_NewIntObj(*p++ & 0xff));
}
return result;
}
/*
*--------------------------------------------------------------
*
* Tk_CreateSmoothMethod --
*
* This procedure is invoked to add additional values
* for the "-smooth" option to the list.
*
* Results:
* A standard Tcl return value.
*
* Side effects:
* In the future "-smooth <name>" will be accepted as
* smooth method for the line and polygon.
*
*--------------------------------------------------------------
*/
Tk_SmoothMethod tkBezierSmoothMethod = {
"bezier",
TkMakeBezierCurve,
(void (*) _ANSI_ARGS_((Tcl_Interp *interp, Tk_Canvas canvas,
double *coordPtr, int numPoints, int numSteps)))
TkMakeBezierPostscript,
};
static void SmoothMethodCleanupProc _ANSI_ARGS_((ClientData clientData,
Tcl_Interp *interp));
typedef struct SmoothAssocData {
struct SmoothAssocData *nextPtr; /* pointer to next SmoothAssocData */
Tk_SmoothMethod smooth; /* name and functions associated with this
* option */
} SmoothAssocData;
void
Tk_CreateSmoothMethod(interp, smooth)
Tcl_Interp *interp;
Tk_SmoothMethod *smooth;
{
SmoothAssocData *methods, *typePtr2, *prevPtr, *ptr;
methods = (SmoothAssocData *) Tcl_GetAssocData(interp, "smoothMethod",
(Tcl_InterpDeleteProc **) NULL);
/*
* If there's already a smooth method with the given name, remove it.
*/
for (typePtr2 = methods, prevPtr = NULL; typePtr2 != NULL;
prevPtr = typePtr2, typePtr2 = typePtr2->nextPtr) {
if (!strcmp(typePtr2->smooth.name, smooth->name)) {
if (prevPtr == NULL) {
methods = typePtr2->nextPtr;
} else {
prevPtr->nextPtr = typePtr2->nextPtr;
}
ckfree((char *) typePtr2);
break;
}
}
ptr = (SmoothAssocData *) ckalloc(sizeof(SmoothAssocData));
ptr->smooth.name = smooth->name;
ptr->smooth.coordProc = smooth->coordProc;
ptr->smooth.postscriptProc = smooth->postscriptProc;
ptr->nextPtr = methods;
Tcl_SetAssocData(interp, "smoothMethod", SmoothMethodCleanupProc,
(ClientData) ptr);
}
/*
*----------------------------------------------------------------------
*
* SmoothMethodCleanupProc --
*
* This procedure is invoked whenever an interpreter is deleted
* to cleanup the smooth methods.
*
* Results:
* None.
*
* Side effects:
* Smooth methods are removed.
*
*----------------------------------------------------------------------
*/
static void
SmoothMethodCleanupProc(clientData, interp)
ClientData clientData; /* Points to "smoothMethod" AssocData
* for the interpreter. */
Tcl_Interp *interp; /* Interpreter that is being deleted. */
{
SmoothAssocData *ptr, *methods = (SmoothAssocData *) clientData;
while (methods != NULL) {
methods = (ptr = methods)->nextPtr;
ckfree((char *) ptr);
}
}
/*
*--------------------------------------------------------------
*
* TkSmoothParseProc --
*
* This procedure is invoked during option processing to handle
* the "-smooth" option.
*
* Results:
* A standard Tcl return value.
*
* Side effects:
* The smooth option for a given item gets replaced by the value
* indicated in the value argument.
*
*--------------------------------------------------------------
*/
int
TkSmoothParseProc(clientData, interp, tkwin, ovalue, widgRec, offset)
ClientData clientData; /* some flags.*/
Tcl_Interp *interp; /* Used for reporting errors. */
Tk_Window tkwin; /* Window containing canvas widget. */
Tcl_Obj * ovalue; /* Value of option. */
char *widgRec; /* Pointer to record for item. */
int offset; /* Offset into item. */
{
register Tk_SmoothMethod **smoothPtr =
(Tk_SmoothMethod **) (widgRec + offset);
Tk_SmoothMethod *smooth = NULL;
int b;
size_t length;
SmoothAssocData *methods;
char *value = Tcl_GetString(ovalue);
if (value == NULL || *value == 0) {
*smoothPtr = (Tk_SmoothMethod *) NULL;
return TCL_OK;
}
length = strlen(value);
methods = (SmoothAssocData *) Tcl_GetAssocData(interp, "smoothMethod",
(Tcl_InterpDeleteProc **) NULL);
while (methods != (SmoothAssocData *) NULL) {
if (strncmp(value, methods->smooth.name, length) == 0) {
if (smooth != (Tk_SmoothMethod *) NULL) {
Tcl_AppendResult(interp, "ambigeous smooth method \"", value,
"\"", (char *) NULL);
return TCL_ERROR;
}
smooth = &methods->smooth;
}
methods = methods->nextPtr;
}
if (smooth) {
*smoothPtr = smooth;
return TCL_OK;
} else if (strncmp(value, tkBezierSmoothMethod.name, length) == 0) {
/*
* We need to do handle the built-in bezier method.
*/
*smoothPtr = &tkBezierSmoothMethod;
return TCL_OK;
}
if (Tcl_GetBooleanFromObj(interp, ovalue, &b) != TCL_OK) {
return TCL_ERROR;
}
*smoothPtr = b ? &tkBezierSmoothMethod : (Tk_SmoothMethod*) NULL;
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* TkSmoothPrintProc --
*
* This procedure is invoked by the Tk configuration code
* to produce a printable string for the "-smooth"
* configuration option.
*
* Results:
* The return value is a string describing the smooth option for
* the item referred to by "widgRec". In addition, *freeProcPtr
* is filled in with the address of a procedure to call to free
* the result string when it's no longer needed (or NULL to
* indicate that the string doesn't need to be freed).
*
* Side effects:
* None.
*
*--------------------------------------------------------------
*/
Tcl_Obj *
TkSmoothPrintProc(clientData, tkwin, widgRec, offset, freeProcPtr)
ClientData clientData; /* Ignored. */
Tk_Window tkwin; /* Window containing canvas widget. */
char *widgRec; /* Pointer to record for item. */
int offset; /* Offset into item. */
Tcl_FreeProc **freeProcPtr; /* Pointer to variable to fill in with
* information about how to reclaim
* storage for return string. */
{
register Tk_SmoothMethod **smoothPtr = (Tk_SmoothMethod **) (widgRec + offset);
Tcl_Obj * result = NULL;
if (*smoothPtr) {
LangSetDefault(&result,(*smoothPtr)->name);
}
return result;
}
/*
*--------------------------------------------------------------
*
* Tk_GetDash
*
* This procedure is used to parse a string, assuming
* it is dash information.
*
* Results:
* The return value is a standard Tcl result: TCL_OK means
* that the dash information was parsed ok, and
* TCL_ERROR means it couldn't be parsed.
*
* Side effects:
* Dash information in the dash structure is updated.
*
*--------------------------------------------------------------
*/
int
Tk_GetDash(interp, ovalue, dash)
Tcl_Interp *interp; /* Used for error reporting. */
Tcl_Obj * ovalue; /* Textual specification of dash list. */
Tk_Dash *dash; /* Pointer to record in which to
* store dash information. */
{
int argc, i;
Tcl_Obj **largv = NULL;
Tcl_Obj **objv = NULL;
char *pt;
char *value = Tcl_GetString(ovalue);
if ((value==(char *) NULL) || (*value==0) ) {
dash->number = 0;
return TCL_OK;
}
if ((*value == '.') || (*value == ',') ||
(*value == '-') || (*value == '_')) {
i = DashConvert((char *) NULL, value, -1, 0.0);
if (i<0) {
goto badDashList;
}
dash->pattern.pt = pt = (char *) ckalloc(strlen(value)+1);
strcpy( pt, value );
dash->number = -i;
return TCL_OK;
}
if (Tcl_ListObjGetElements(interp, ovalue, &argc, &objv) != TCL_OK ||
argc <= 1) {
Tcl_ResetResult(interp);
badDashList:
Tcl_AppendResult(interp, "bad dash list \"", value,
"\": must be a list of integers or a format like \"-..\"",
(char *) NULL);
syntaxError:
if (ABS(dash->number) > sizeof(char *))
ckfree((char *) dash->pattern.pt);
dash->number = 0;
return TCL_ERROR;
}
if (ABS(dash->number) > sizeof(char *)) {
ckfree((char *) dash->pattern.pt);
}
if (argc > (int) sizeof(char *)) {
dash->pattern.pt = pt = (char *) ckalloc((unsigned int) argc);
} else {
pt = dash->pattern.array;
}
dash->number = argc;
largv = objv;
while(argc>0) {
if (Tcl_GetIntFromObj(interp, *largv, &i) != TCL_OK ||
i < 1 || i>255) {
Tcl_ResetResult(interp);
Tcl_AppendResult(interp, "expected integer in the range 1..255 but got \"",
Tcl_GetString(*largv), "\"", (char *) NULL);
goto syntaxError;
}
*pt++ = i;
argc--; largv++;
}
#ifdef FIXME
if (argv != NULL) {
ckfree((char *) argv);
}
#endif
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* Tk_CreateOutline
*
* This procedure initializes the Tk_Outline structure
* with default values.
*
* Results:
* None
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
void Tk_CreateOutline(outline)
Tk_Outline *outline;
{
outline->gc = None;
outline->width = 1.0;
outline->activeWidth = 0.0;
outline->disabledWidth = 0.0;
outline->offset = 0;
outline->dash.number = 0;
outline->activeDash.number = 0;
outline->disabledDash.number = 0;
outline->tsoffset.flags = 0;
outline->tsoffset.xoffset = 0;
outline->tsoffset.yoffset = 0;
outline->color = NULL;
outline->activeColor = NULL;
outline->disabledColor = NULL;
outline->stipple = None;
outline->activeStipple = None;
outline->disabledStipple = None;
}
/*
*--------------------------------------------------------------
*
* Tk_DeleteOutline
*
* This procedure frees all memory that might be
* allocated and referenced in the Tk_Outline structure.
*
* Results:
* None
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
void Tk_DeleteOutline(display, outline)
Display *display; /* Display containing window */
Tk_Outline *outline;
{
if (outline->gc != None) {
Tk_FreeGC(display, outline->gc);
}
if (ABS(outline->dash.number) > sizeof(char *)) {
ckfree((char *) outline->dash.pattern.pt);
}
if (ABS(outline->activeDash.number) > sizeof(char *)) {
ckfree((char *) outline->activeDash.pattern.pt);
}
if (ABS(outline->disabledDash.number) > sizeof(char *)) {
ckfree((char *) outline->disabledDash.pattern.pt);
}
if (outline->color != NULL) {
Tk_FreeColor(outline->color);
}
if (outline->activeColor != NULL) {
Tk_FreeColor(outline->activeColor);
}
if (outline->disabledColor != NULL) {
Tk_FreeColor(outline->disabledColor);
}
if (outline->stipple != None) {
Tk_FreeBitmap(display, outline->stipple);
}
if (outline->activeStipple != None) {
Tk_FreeBitmap(display, outline->activeStipple);
}
if (outline->disabledStipple != None) {
Tk_FreeBitmap(display, outline->disabledStipple);
}
}
/*
*--------------------------------------------------------------
*
* Tk_ConfigOutlineGC
*
* This procedure should be called in the canvas object
* during the configure command. The graphics context
* description in gcValues is updated according to the
* information in the dash structure, as far as possible.
*
* Results:
* The return-value is a mask, indicating which
* elements of gcValues have been updated.
* 0 means there is no outline.
*
* Side effects:
* GC information in gcValues is updated.
*
*--------------------------------------------------------------
*/
int Tk_ConfigOutlineGC(gcValues, canvas, item, outline)
XGCValues *gcValues;
Tk_Canvas canvas;
Tk_Item *item;
Tk_Outline *outline;
{
int mask = 0;
double width;
Tk_Dash *dash;
XColor *color;
Pixmap stipple;
Tk_State state = item->state;
if (outline->width < 0.0) {
outline->width = 0.0;
}
if (outline->activeWidth < 0.0) {
outline->activeWidth = 0.0;
}
if (outline->disabledWidth < 0) {
outline->disabledWidth = 0.0;
}
if (state==TK_STATE_HIDDEN) {
return 0;
}
width = outline->width;
dash = &(outline->dash);
color = outline->color;
stipple = outline->stipple;
if (state == TK_STATE_NULL) {
state = ((TkCanvas *)canvas)->canvas_state;
}
if (((TkCanvas *)canvas)->currentItemPtr == item) {
if (outline->activeWidth>width) {
width = outline->activeWidth;
}
if (outline->activeDash.number != 0) {
dash = &(outline->activeDash);
}
if (outline->activeColor!=NULL) {
color = outline->activeColor;
}
if (outline->activeStipple!=None) {
stipple = outline->activeStipple;
}
} else if (state==TK_STATE_DISABLED) {
if (outline->disabledWidth>0) {
width = outline->disabledWidth;
}
if (outline->disabledDash.number != 0) {
dash = &(outline->disabledDash);
}
if (outline->disabledColor!=NULL) {
color = outline->disabledColor;
}
if (outline->disabledStipple!=None) {
stipple = outline->disabledStipple;
}
}
if (color==NULL) {
return 0;
}
if (width < 1.0) {
width = 1.0;
}
gcValues->line_width = (int) (width + 0.5);
if (color != NULL) {
gcValues->foreground = color->pixel;
mask = GCForeground|GCLineWidth;
if (stipple != None) {
gcValues->stipple = stipple;
gcValues->fill_style = FillStippled;
mask |= GCStipple|GCFillStyle;
}
}
if (mask && (dash->number != 0)) {
gcValues->line_style = LineOnOffDash;
gcValues->dash_offset = outline->offset;
if (dash->number >= 2) {
gcValues->dashes = 4;
} else {
gcValues->dashes = (char) (4 * width);
}
mask |= GCLineStyle|GCDashList|GCDashOffset;
}
return mask;
}
/*
*--------------------------------------------------------------
*
* Tk_ChangeOutlineGC
*
* Updates the GC to represent the full information of
* the dash structure. Partly this is already done in
* Tk_ConfigOutlineGC().
* This function should be called just before drawing
* the dashed item.
*
* Results:
* 1 if there is a stipple pattern.
* 0 otherwise.
*
* Side effects:
* GC is updated.
*
*--------------------------------------------------------------
*/
int
Tk_ChangeOutlineGC(canvas, item, outline)
Tk_Canvas canvas;
Tk_Item *item;
Tk_Outline *outline;
{
CONST char *p;
double width;
Tk_Dash *dash;
XColor *color;
Pixmap stipple;
Tk_State state = item->state;
XGCValues values;
Tk_Tile tile = outline->tile;
width = outline->width;
if (width < 1.0) {
width = 1.0;
}
dash = &(outline->dash);
color = outline->color;
stipple = outline->stipple;
if (state == TK_STATE_NULL) {
state = ((TkCanvas *)canvas)->canvas_state;
}
if (((TkCanvas *)canvas)->currentItemPtr == item) {
if (outline->activeWidth > width) {
width = outline->activeWidth;
}
if (outline->activeDash.number != 0) {
dash = &(outline->activeDash);
}
if (outline->activeColor != NULL) {
color = outline->activeColor;
}
if (outline->activeStipple != None) {
stipple = outline->activeStipple;
}
} else if (state == TK_STATE_DISABLED) {
if (outline->disabledWidth > width) {
width = outline->disabledWidth;
}
if (outline->disabledDash.number != 0) {
dash = &(outline->disabledDash);
}
if (outline->disabledColor != NULL) {
color = outline->disabledColor;
}
if (outline->disabledStipple != None) {
stipple = outline->disabledStipple;
}
}
if (color==NULL) {
return 0;
}
if (dash->number<=-2) {
char *q;
int i = -dash->number;
p = dash->pattern.pt;
q = (char *) ckalloc(2*(unsigned int)i);
i = DashConvert(q, p, i, width);
XSetDashes(((TkCanvas *)canvas)->display, outline->gc, outline->offset, q, i);
values.line_style = LineOnOffDash;
ckfree(q);
} else if ( dash->number>=2 ) {
p = (char *) (dash->number > (int) sizeof(char *)) ? dash->pattern.pt : dash->pattern.array;
XSetDashes(((TkCanvas *)canvas)->display, outline->gc, outline->offset, p, dash->number);
values.line_style = LineOnOffDash;
} else {
values.line_style = LineSolid;
}
XChangeGC(((TkCanvas *)canvas)->display, outline->gc, GCLineStyle, &values);
if ((tile!= NULL) || (stipple!=None)) {
int w=0; int h=0;
Tk_TSOffset *tsoffset = &outline->tsoffset;
int flags = tsoffset->flags;
if (!(flags & TK_OFFSET_INDEX) && (flags & (TK_OFFSET_CENTER|TK_OFFSET_MIDDLE))) {
Tk_SizeOfBitmap(((TkCanvas *)canvas)->display, stipple, &w, &h);
if (flags & TK_OFFSET_CENTER) {
w /= 2;
} else {
w = 0;
}
if (flags & TK_OFFSET_MIDDLE) {
h /= 2;
} else {
h = 0;
}
}
tsoffset->xoffset -= w;
tsoffset->yoffset -= h;
Tk_CanvasSetOffset(canvas, outline->gc, tsoffset);
tsoffset->xoffset += w;
tsoffset->yoffset += h;
return 1;
}
return 0;
}
/*
*--------------------------------------------------------------
*
* Tk_ResetOutlineGC
*
* Restores the GC to the situation before
* Tk_ChangeDashGC() was called.
* This function should be called just after the dashed
* item is drawn, because the GC is supposed to be
* read-only.
*
* Results:
* 1 if there is a stipple pattern.
* 0 otherwise.
*
* Side effects:
* GC is updated.
*
*--------------------------------------------------------------
*/
int
Tk_ResetOutlineGC(canvas, item, outline)
Tk_Canvas canvas;
Tk_Item *item;
Tk_Outline *outline;
{
char dashList;
double width;
Tk_Dash *dash;
XColor *color;
Pixmap stipple;
Tk_State state = item->state;
width = outline->width;
if (width < 1.0) {
width = 1.0;
}
dash = &(outline->dash);
color = outline->color;
stipple = outline->stipple;
if (state == TK_STATE_NULL) {
state = ((TkCanvas *)canvas)->canvas_state;
}
if (((TkCanvas *)canvas)->currentItemPtr == item) {
if (outline->activeWidth>width) {
width = outline->activeWidth;
}
if (outline->activeDash.number != 0) {
dash = &(outline->activeDash);
}
if (outline->activeColor!=NULL) {
color = outline->activeColor;
}
if (outline->activeStipple!=None) {
stipple = outline->activeStipple;
}
} else if (state==TK_STATE_DISABLED) {
if (outline->disabledWidth>width) {
width = outline->disabledWidth;
}
if (outline->disabledDash.number != 0) {
dash = &(outline->disabledDash);
}
if (outline->disabledColor!=NULL) {
color = outline->disabledColor;
}
if (outline->disabledStipple!=None) {
stipple = outline->disabledStipple;
}
}
if (color==NULL) {
return 0;
}
if ( (dash->number >= 2) || (dash->number <= -2) ) {
if (dash->number < 0) {
dashList = (int) (4 * width + 0.5);
} else {
dashList = 4;
}
XSetDashes(((TkCanvas *)canvas)->display, outline->gc, outline->offset, &dashList , 1);
} else {
XGCValues values;
values.line_style = LineSolid;
XChangeGC(((TkCanvas *)canvas)->display, outline->gc, GCLineStyle, &values);
}
if (stipple != None) {
XSetTSOrigin(((TkCanvas *)canvas)->display, outline->gc, 0, 0);
return 1;
}
return 0;
}
/*
*--------------------------------------------------------------
*
* Tk_CanvasPsOutline
*
* Creates the postscript command for the correct
* Outline-information (width, dash, color and stipple).
*
* Results:
* TCL_OK if succeeded, otherwise TCL_ERROR.
*
* Side effects:
* canvas->interp->result contains the postscript string,
* or an error message if the result was TCL_ERROR.
*
*--------------------------------------------------------------
*/
int
Tk_CanvasPsOutline(canvas, item, outline)
Tk_Canvas canvas;
Tk_Item *item;
Tk_Outline *outline;
{
char string[41];
char pattern[11];
int i;
char *ptr;
char *str = string;
char *lptr = pattern;
Tcl_Interp *interp = ((TkCanvas *)canvas)->interp;
double width;
Tk_Dash *dash;
XColor *color;
Pixmap stipple;
Tk_State state = item->state;
width = outline->width;
dash = &(outline->dash);
color = outline->color;
stipple = outline->stipple;
if (state == TK_STATE_NULL) {
state = ((TkCanvas *)canvas)->canvas_state;
}
if (((TkCanvas *)canvas)->currentItemPtr == item) {
if (outline->activeWidth > width) {
width = outline->activeWidth;
}
if (outline->activeDash.number > 0) {
dash = &(outline->activeDash);
}
if (outline->activeColor != NULL) {
color = outline->activeColor;
}
if (outline->activeStipple != None) {
stipple = outline->activeStipple;
}
} else if (state == TK_STATE_DISABLED) {
if (outline->disabledWidth > 0) {
width = outline->disabledWidth;
}
if (outline->disabledDash.number > 0) {
dash = &(outline->disabledDash);
}
if (outline->disabledColor != NULL) {
color = outline->disabledColor;
}
if (outline->disabledStipple != None) {
stipple = outline->disabledStipple;
}
}
sprintf(string, "%.15g setlinewidth\n", width);
Tcl_AppendResult(interp, string, (char *) NULL);
if (dash->number > 10) {
str = (char *)ckalloc((unsigned int) (1 + 4*dash->number));
} else if (dash->number < -5) {
str = (char *)ckalloc((unsigned int) (1 - 8*dash->number));
lptr = (char *)ckalloc((unsigned int) (1 - 2*dash->number));
}
ptr = (char *) ( dash->number > sizeof(char *)) ? dash->pattern.pt :
( dash->number < 0 ) ? dash->pattern.pt : dash->pattern.array;
if (dash->number > 0) {
char *ptr0 = ptr;
sprintf(str, "[%d", *ptr++ & 0xff);
i = dash->number-1;
while (i--) {
sprintf(str+strlen(str), " %d", *ptr++ & 0xff);
}
Tcl_AppendResult(interp, str, (char *)NULL);
if (dash->number&1) {
Tcl_AppendResult(interp, " ", str+1, (char *)NULL);
}
sprintf(str, "] %d setdash\n", outline->offset);
Tcl_AppendResult(interp, str, (char *)NULL);
ptr = ptr0;
} else if (dash->number < 0) {
if ((i = DashConvert(lptr, ptr, -dash->number, width)) != 0) {
char *lptr0 = lptr;
sprintf(str, "[%d", *lptr++ & 0xff);
while (--i) {
sprintf(str+strlen(str), " %d", *lptr++ & 0xff);
}
Tcl_AppendResult(interp, str, (char *)NULL);
sprintf(str, "] %d setdash\n", outline->offset);
Tcl_AppendResult(interp, str, (char *)NULL);
lptr = lptr0;
} else {
Tcl_AppendResult(interp, "[] 0 setdash\n", (char *)NULL);
}
} else {
Tcl_AppendResult(interp, "[] 0 setdash\n", (char *)NULL);
}
if (str != string) {
ckfree(str);
}
if (lptr != pattern) {
ckfree(lptr);
}
if (Tk_CanvasPsColor(interp, canvas, color) != TCL_OK) {
return TCL_ERROR;
}
if (stipple != None) {
Tcl_AppendResult(interp, "StrokeClip ", (char *) NULL);
if (Tk_CanvasPsStipple(interp, canvas, stipple) != TCL_OK) {
return TCL_ERROR;
}
} else {
Tcl_AppendResult(interp, "stroke\n", (char *) NULL);
}
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* DashConvert
*
* Converts a character-like dash-list (e.g. "-..")
* into an X11-style. l must point to a string that
* holds room to at least 2*n characters. If
* l == NULL, this function can be used for
* syntax checking only.
*
* Results:
* The length of the resulting X11 compatible
* dash-list. -1 if failed.
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
static int
DashConvert (l, p, n, width)
char *l;
CONST char *p;
int n;
double width;
{
int result = 0;
int size, intWidth;
if (n<0) {
n = strlen(p);
}
intWidth = (int) (width + 0.5);
if (intWidth < 1) {
intWidth = 1;
}
while (n-- && *p) {
switch (*p++) {
case ' ':
if (result) {
if (l) {
l[-1] += 2 * intWidth;
}
continue;
} else {
return 0;
}
break;
case '_':
size = 4;
break;
case '-':
size = 3;
break;
case ',':
size = 2;
break;
case '.':
size = 1;
break;
default:
return -1;
}
if (l) {
*l++ = size * intWidth;
*l++ = 2 * intWidth;
}
result += 2;
}
return result;
}
/*
*----------------------------------------------------------------------
*
* translateAndAppendCoords --
*
* This is a helper routine for TkCanvTranslatePath() below.
*
* Given an (x,y) coordinate pair within a canvas, this procedure
* computes the corresponding coordinates at which the point should
* be drawn in the drawable used for display. Those coordinates are
* then written into outArr[numOut*2] and outArr[numOut*2+1].
*
* Results:
* There is no return value.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static void
translateAndAppendCoords(canvPtr, x, y, outArr, numOut)
TkCanvas *canvPtr; /* The canvas. */
double x, y; /* Coordinates in canvas space. */
XPoint *outArr; /* Write results into this array */
int numOut; /* Num of prior entries in outArr[] */
{
double tmp;
tmp = x - canvPtr->drawableXOrigin;
if (tmp > 0) {
tmp += 0.5;
} else {
tmp -= 0.5;
}
outArr[numOut].x = (short) tmp;
tmp = y - canvPtr->drawableYOrigin;
if (tmp > 0) {
tmp += 0.5;
} else {
tmp -= 0.5;
}
outArr[numOut].y = (short) tmp;
}
/*
*--------------------------------------------------------------
*
* TkCanvTranslatePath
*
* Translate a line or polygon path so that all vertices are
* within a rectangle that is 1000 pixels larger than the total
* size of the canvas window. This will prevent pixel coordinates
* from overflowing the 16-bit integer size limitation imposed by
* most windowing systems.
*
* coordPtr must point to an array of doubles, two doubles per
* vertex. There are a total of numVertex vertices, or 2*numVertex
* entries in coordPtr. The result vertices written into outArr
* have their coordinate origin shifted to canvPtr->drawableXOrigin
* by canvPtr->drawableYOrigin. There might be as many as 3 times
* more output vertices than there are input vertices. The calling
* function should allocate space accordingly.
*
* This routine limits the width and height of a canvas window
* to 31767 pixels. At the highest resolution display devices
* available today (210 ppi in Jan 2003) that's a window that is
* over 13 feet wide and tall. Should be enough for the near
* future.
*
* Results:
* Clipped and translated path vertices are written into outArr[].
* There might be as many as twice the vertices in outArr[] as there
* are in coordPtr[]. The return value is the number of vertices
* actually written into outArr[].
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
int
TkCanvTranslatePath (canvPtr, numVertex, coordArr, closedPath, outArr)
TkCanvas *canvPtr; /* The canvas */
int numVertex; /* Number of vertices specified by coordArr[] */
double *coordArr; /* X and Y coordinates for each vertex */
int closedPath; /* True if this is a closed polygon */
XPoint *outArr; /* Write results here, if not NULL */
{
int numOutput = 0; /* Number of output coordinates */
double lft, rgh; /* Left and right sides of the bounding box */
double top, btm; /* Top and bottom sizes of the bounding box */
double *tempArr; /* Temporary storage used by the clipper */
double *a, *b, *t; /* Pointers to parts of the temporary storage */
int i, j; /* Loop counters */
int maxOutput; /* Maximum number of outputs that we will allow */
double limit[4]; /* Boundries at which clipping occurs */
double staticSpace[480]; /* Temp space from the stack */
/*
** Constrain all vertices of the path to be within a box that is no
** larger than 32000 pixels wide or height. The top-left corner of
** this clipping box is 1000 pixels above and to the left of the top
** left corner of the window on which the canvas is displayed.
**
** This means that a canvas will not display properly on a canvas
** window that is larger than 31000 pixels wide or high. That is not
** a problem today, but might someday become a factor for ultra-high
** resolutions displays.
**
** The X11 protocol allows us (in theory) to expand the size of the
** clipping box to 32767 pixels. But we have found experimentally that
** XFree86 sometimes fails to draw lines correctly if they are longer
** than about 32500 pixels. So we have left a little margin in the
** size to mask that bug.
*/
lft = canvPtr->xOrigin - 1000.0;
top = canvPtr->yOrigin - 1000.0;
rgh = lft + 32000.0;
btm = top + 32000.0;
/* Try the common case first - no clipping. Loop over the input
** coordinates and translate them into appropriate output coordinates.
** But if a vertex outside of the bounding box is seen, break out of
** the loop.
**
** Most of the time, no clipping is needed, so this one loop is
** sufficient to do the translation.
*/
for(i=0; i<numVertex; i++){
double x, y;
x = coordArr[i*2];
y = coordArr[i*2+1];
if( x<lft || x>rgh || y<top || y>btm ) break;
translateAndAppendCoords(canvPtr, x, y, outArr, numOutput++);
}
if( i==numVertex ){
assert( numOutput==numVertex );
return numOutput;
}
/* If we reach this point, it means that some clipping is required.
** Begin by allocating some working storage - at least 6 times as much space
** as coordArr[] requires. Divide this space into two separate arrays
** a[] and b[]. Initialize a[] to be equal to coordArr[].
*/
if( numVertex*12 <= (int) (sizeof(staticSpace)/sizeof(staticSpace[0])) ){
tempArr = staticSpace;
} else {
tempArr = (double*)ckalloc( numVertex*12*sizeof(tempArr[0]) );
}
for(i=0; i<numVertex*2; i++){
tempArr[i] = coordArr[i];
}
a = tempArr;
b = &tempArr[numVertex*6];
/* We will make four passes through the input data. On each pass,
** we copy the contents of a[] over into b[]. As we copy, we clip
** any line segments that extend to the right past xClip then we
** rotate the coordinate system 90 degrees clockwise. After each
** pass is complete, we interchange a[] and b[] in preparation for
** the next pass.
**
** Each pass clips line segments that extend beyond a single side
** of the bounding box, and four passes rotate the coordinate system
** back to its original value. I'm not an expert on graphics
** algorithms, but I think this is called Cohen-Sutherland polygon
** clipping.
**
** The limit[] array contains the xClip value used for each of the
** four passes.
*/
limit[0] = rgh;
limit[1] = -top;
limit[2] = -lft;
limit[3] = btm;
/* This is the loop that makes the four passes through the data.
*/
maxOutput = numVertex*3;
for(j=0; j<4; j++){
double xClip = limit[j];
int inside = a[0]<xClip;
double priorY = a[1];
numOutput = 0;
/* Clip everything to the right of xClip. Store the results in
** b[] rotated by 90 degrees clockwise.
*/
for(i=0; i<numVertex; i++){
double x = a[i*2];
double y = a[i*2+1];
if( x>=xClip ){
/* The current vertex is to the right of xClip.
*/
if( inside ){
/* If the current vertex is to the right of xClip but
** the previous vertex was left of xClip, then draw a
** line segment from the previous vertex to until it
** intersects the vertical at xClip.
*/
double x0, y0, yN;
assert( i>0 );
x0 = a[i*2-2];
y0 = a[i*2-1];
yN = y0 + (y - y0)*(xClip-x0)/(x-x0);
b[numOutput*2] = -yN;
b[numOutput*2+1] = xClip;
numOutput++;
assert( numOutput<=maxOutput );
priorY = yN;
inside = 0;
}else if( i==0 ){
/* If the first vertex is to the right of xClip, add
** a vertex that is the projection of the first vertex
** onto the vertical xClip line.
*/
b[0] = -y;
b[1] = xClip;
numOutput = 1;
priorY = y;
}
}else{
/* The current vertex is to the left of xClip
*/
if( !inside ){
/* If the current vertex is on the left of xClip and
** one or more prior vertices where to the right, then
** we have to draw a line segment along xClip that extends
** from the spot where we first crossed from left to right
** to the spot where we cross back from right to left.
*/
double x0, y0, yN;
assert( i>0 );
x0 = a[i*2-2];
y0 = a[i*2-1];
yN = y0 + (y - y0)*(xClip-x0)/(x-x0);
if( yN!=priorY ){
b[numOutput*2] = -yN;
b[numOutput*2+1] = xClip;
numOutput++;
assert( numOutput<=maxOutput );
}
inside = 1;
}
b[numOutput*2] = -y;
b[numOutput*2+1] = x;
numOutput++;
assert( numOutput<=maxOutput );
}
}
/* Interchange a[] and b[] in preparation for the next pass.
*/
t = a;
a = b;
b = t;
numVertex = numOutput;
}
/* All clipping is now finished. Convert the coordinates from doubles
** into XPoints and translate the origin for the drawable.
*/
for(i=0; i<numVertex; i++){
translateAndAppendCoords(canvPtr, a[i*2], a[i*2+1], outArr, i);
}
if( tempArr!=staticSpace ){
ckfree((char *) tempArr);
}
return numOutput;
}