/*
* tkoWidget.h --
*
* This file contains the tko widget class.
*
* Copyright (c) 2019 Rene Zaumseil
*
*/
#include "tcl.h"
#include "tclOO.h"
#include "tk.h"
#include "tkInt.h"
#include "tkoWidget.h"
/*
* Widget structure data used in objects.
*/
typedef struct tkoWidget {
Tcl_Interp *interp; /* Interpreter associated with widget. */
Tcl_Object object; /* our own object */
Tk_Window tkWin; /* Window that embodies the canvas. NULL means
* that the window has been destroyed but the
* data structures haven't yet been cleaned
* up.*/
Tcl_Obj *myCmd; /* Objects "my" command. Needed to call internal methods. */
Tcl_Command widgetCmd; /* Token for canvas's widget command. */
Tcl_Obj *optionsArray; /* Name of option array variable */
Tcl_HashTable optionsTable; /* Hash table containing all used options */
} tkoWidget;
/*
* Widget option.
*/
typedef struct WidgetOption {
Tcl_Obj *option; /* Name of option */
Tcl_Obj *dbname; /* Database name or name of synonym option */
Tcl_Obj *dbclass; /* Class name or NULL for synonym options */
Tcl_Obj *defvalue; /* Default value from initialization */
Tcl_Obj *value; /* Contain last known value of option */
int flags; /* see flags in struct tkoWidgetOptionDefine */
} WidgetOption;
/*
* Static tcl objects.
*/
Tk_Uid TkoUid_class = NULL;
tkoObj TkoObj =
{ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
/*
* Methods
*/
static int WidgetConstructor(
ClientData clientData,
Tcl_Interp * interp,
Tcl_ObjectContext context,
int objc,
Tcl_Obj * const objv[]);
static int WidgetDestructor(
ClientData clientData,
Tcl_Interp * interp,
Tcl_ObjectContext context,
int objc,
Tcl_Obj * const objv[]);
static int WidgetMethod_cget(
ClientData clientData,
Tcl_Interp * interp,
Tcl_ObjectContext context,
int objc,
Tcl_Obj * const objv[]);
static int WidgetMethod_configure(
ClientData clientData,
Tcl_Interp * interp,
Tcl_ObjectContext context,
int objc,
Tcl_Obj * const objv[]);
static int WidgetMethod_tko_configure(
ClientData clientData,
Tcl_Interp * interp,
Tcl_ObjectContext context,
int objc,
Tcl_Obj * const objv[]);
/*
* Functions
*/
static char *WidgetOptionTrace(
ClientData clientData,
Tcl_Interp * interp,
const char *name1,
const char *name2,
int flags);
static void WidgetOptionDelEntry(
Tcl_HashEntry * entry);
static void WidgetEventProc(
ClientData clientData,
XEvent * eventPtr);
static int WidgetOptionAdd(
Tcl_Interp * interp,
tkoWidget * widget,
Tcl_Obj * option,
Tcl_Obj * dbname,
Tcl_Obj * dbclass,
Tcl_Obj * defvalue,
Tcl_Obj * flags,
Tcl_Obj * value,
int initmode);
static int WidgetOptionConfigure(
Tcl_Interp * interp,
tkoWidget * widget,
int objc,
Tcl_Obj * const objv[]);
static int WidgetOptionDel(
Tcl_Interp * interp,
tkoWidget * widget,
Tcl_Obj * option);
static int WidgetOptionGet(
Tcl_Interp * interp,
tkoWidget * widget,
Tcl_Obj * option);
static int WidgetOptionSet(
Tcl_Interp * interp,
tkoWidget * widget,
Tcl_Obj * option,
Tcl_Obj * value);
static void WidgetMetaDestroy(
tkoWidget * widget);
static void WidgetMetaDelete(
ClientData clientData);
static int WidgetMethod_(
ClientData clientData,
Tcl_Interp * interp,
Tcl_ObjectContext context,
int objc,
Tcl_Obj * const objv[]);
/*
* tkoWidgetMeta --
*/
Tcl_ObjectMetadataType tkoWidgetMeta = {
TCL_OO_METADATA_VERSION_CURRENT,
"tkoWidgetMeta",
WidgetMetaDelete,
NULL
};
/*
* widgetMethods --
*
*/
static Tcl_MethodType widgetMethods[] = {
{TCL_OO_METHOD_VERSION_CURRENT, NULL, WidgetConstructor, NULL, NULL},
{TCL_OO_METHOD_VERSION_CURRENT, NULL, WidgetDestructor, NULL, NULL},
{TCL_OO_METHOD_VERSION_CURRENT, "cget", WidgetMethod_cget, NULL, NULL},
{TCL_OO_METHOD_VERSION_CURRENT, "configure", WidgetMethod_configure, NULL,
NULL},
{-1, NULL, NULL, NULL, NULL},
{TCL_OO_METHOD_VERSION_CURRENT, "_tko_configure",
WidgetMethod_tko_configure, NULL, NULL},
{-1, NULL, NULL, NULL, NULL}
};
/*
* TkoWidgetWindow --
*
* Results:
*
* Side effects:
*/
Tk_Window *
TkoWidgetWindow(
Tcl_Object object)
{
tkoWidget *widget = (tkoWidget *) Tcl_ObjectGetMetadata(object, &tkoWidgetMeta);
if(widget)
return &widget->tkWin;
return NULL;
}
/*
* TkoWidgetOptionVar --
*
* Results:
*
* Side effects:
*/
Tcl_Obj *
TkoWidgetOptionVar(
Tcl_Object object)
{
tkoWidget *widget = (tkoWidget *) Tcl_ObjectGetMetadata(object, &tkoWidgetMeta);
if(widget)
return widget->optionsArray;
return NULL;
}
/*
* TkoWidgetClassDefine --
*
* Results:
*
* Side effects:
*/
int
TkoWidgetClassDefine(
Tcl_Interp * interp,
Tcl_Class clazz,
Tcl_Obj * classname,
const Tcl_MethodType * methods,
tkoWidgetOptionDefine * options)
{
Tcl_Obj *listPtr;
int i;
Tcl_Obj *option;
Tcl_MethodType *methodPtr;
Tcl_Obj *myObjv[6];
Tcl_Obj *retPtr;
Tcl_Obj *tmpObj;
if(classname == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("missing class name"));
return TCL_ERROR;
}
/*
* Add methods
*/
if(methods) {
/* constructor */
if(methods[0].name == NULL && methods[0].callProc) {
Tcl_ClassSetConstructor(interp, clazz,
Tcl_NewMethod(interp, clazz, NULL, 1, &methods[0], NULL));
}
/* destructor */
if(methods[1].name == NULL && methods[1].callProc) {
Tcl_ClassSetDestructor(interp, clazz,
Tcl_NewMethod(interp, clazz, NULL, 1, &methods[1], NULL));
}
/* public */
for(i = 2; methods[i].name != NULL; i++) {
tmpObj = Tcl_NewStringObj(methods[i].name, -1);
Tcl_IncrRefCount(tmpObj);
Tcl_NewMethod(interp, clazz, tmpObj, 1, &methods[i], NULL);
Tcl_DecrRefCount(tmpObj);
}
i++;
/* private */
for(; methods[i].name != NULL; i++) {
tmpObj = Tcl_NewStringObj(methods[i].name, -1);
Tcl_IncrRefCount(tmpObj);
Tcl_NewMethod(interp, clazz, tmpObj, 0, &methods[i], NULL);
Tcl_DecrRefCount(tmpObj);
}
}
/*
*Add options
*/
if(options) {
/*
* Option definitions will be saved in the tko::options array variable.
* All options of a class are saved as a list under tko::option(classname)
* Here we ensure this variable exists and is a proper list.
*/
retPtr =
Tcl_ObjGetVar2(interp, TkoObj.tko_options, classname,
TCL_GLOBAL_ONLY);
if(retPtr == NULL) {
retPtr = Tcl_NewObj();
Tcl_IncrRefCount(retPtr);
} else {
if(Tcl_ListObjLength(interp, retPtr, &i) != TCL_OK) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("::tko::options(%s) variable is not list",
Tcl_GetString(classname)));
return TCL_ERROR;
}
retPtr = Tcl_DuplicateObj(retPtr);
Tcl_IncrRefCount(retPtr);
}
/* Loop over all option definitions */
for(i = 0;; i++) {
if(options[i].option == NULL)
break; /* end of options */
if(options[i].dbname == NULL) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("wrong option definition: %d", i));
goto error;
}
listPtr = Tcl_NewObj();
option = Tcl_NewStringObj(options[i].option, -1);
Tcl_IncrRefCount(option);
Tcl_ListObjAppendElement(interp, listPtr, option);
Tcl_ListObjAppendElement(interp, listPtr,
Tcl_NewStringObj(options[i].dbname, -1));
/* synonym option, ignore rest */
if(options[i].dbclass == NULL) {
Tcl_ListObjAppendElement(interp, retPtr, listPtr);
Tcl_DecrRefCount(option);
continue;
}
/* normal option */
Tcl_ListObjAppendElement(interp, listPtr,
Tcl_NewStringObj(options[i].dbclass, -1));
if(options[i].defvalue == NULL) {
Tcl_ListObjAppendElement(interp, listPtr, TkoObj.empty);
} else {
Tcl_ListObjAppendElement(interp, listPtr,
Tcl_NewStringObj(options[i].defvalue, -1));
}
Tcl_ListObjAppendElement(interp, listPtr,
Tcl_NewIntObj(options[i].flags));
Tcl_ListObjAppendElement(interp, retPtr, listPtr);
/*
* Now we create the necessary -option method if provided.
* If given we use the code provided in the proc body.
* Or we create the -option method with the given method.
* Or we use the internal implementation of a given type.
* If none of the above are provided it is up to the caller
* to create the necessary -option method.
*/
if(options[i].proc != NULL) {
myObjv[0] = TkoObj.oo_define;
myObjv[1] = classname;
myObjv[2] = TkoObj.method;
myObjv[3] = option;
myObjv[4] = TkoObj.empty;
myObjv[5] = Tcl_NewStringObj(options[i].proc, -1);
Tcl_IncrRefCount(myObjv[5]);
if(Tcl_EvalObjv(interp, 6, myObjv, TCL_EVAL_GLOBAL) != TCL_OK) {
Tcl_DecrRefCount(myObjv[5]);
Tcl_DecrRefCount(option);
goto error;
}
Tcl_DecrRefCount(myObjv[5]);
} else if(options[i].method != NULL) {
methodPtr = (Tcl_MethodType *) ckalloc(sizeof(Tcl_MethodType));
methodPtr->version = TCL_OO_METHOD_VERSION_CURRENT;
methodPtr->name = options[i].option;
methodPtr->callProc = options[i].method;
methodPtr->deleteProc = NULL;
methodPtr->cloneProc = NULL;
Tcl_NewMethod(interp, clazz, option, 0, methodPtr, NULL);
} else if(options[i].type >= 0) {
if (options[i].optionPtr == NULL) {
options[i].optionPtr = option;
Tcl_IncrRefCount(option);
}
methodPtr = (Tcl_MethodType *) ckalloc(sizeof(Tcl_MethodType));
methodPtr->version = TCL_OO_METHOD_VERSION_CURRENT;
methodPtr->name = options[i].option;
methodPtr->callProc = WidgetMethod_;
methodPtr->deleteProc = NULL;
methodPtr->cloneProc = NULL;
Tcl_NewMethod(interp, clazz, option, 0, methodPtr,
(ClientData) & options[i]);
}
Tcl_DecrRefCount(option);
}
Tcl_ObjSetVar2(interp, TkoObj.tko_options, classname, retPtr,
TCL_GLOBAL_ONLY);
Tcl_SetObjResult(interp, retPtr);
}
return TCL_OK;
error:
Tcl_DecrRefCount(retPtr);
return TCL_ERROR;
}
/*
* Tko_Init --
*
* Results:
*
* Side effects:
*/
int
Tko_Init(
Tcl_Interp * interp /* Tcl interpreter. */)
{
/* Needed oo extension */
if(Tcl_OOInitStubs(interp) == NULL) {
return TCL_ERROR;
}
if(Tko_WidgetInit(interp) != TCL_OK) {
return TCL_ERROR;
}
if(Tko_FrameInit(interp) != TCL_OK) {
return TCL_ERROR;
}
if(Tko_VectorInit(interp) != TCL_OK) {
return TCL_ERROR;
}
if(Tko_GraphInit(interp) != TCL_OK) {
return TCL_ERROR;
}
if(Tko_PathInit(interp) != TCL_OK) {
return TCL_ERROR;
}
return TCL_OK;
}
/*
* Tko_WidgetInit --
*
* Results:
*
* Side effects:
*/
int
Tko_WidgetInit(
Tcl_Interp * interp /* Tcl interpreter. */)
{
Tcl_Class clazz;
Tcl_Object object;
/* Create class like tk command and remove oo functions from widget commands */
static const char *initScript =
"namespace eval ::tko {variable options}\n"
"array set ::tko::options {}\n"
"::oo::class create ::tko::widget {\n"
" variable tko\n"
" self unexport new destroy\n"
" unexport new create destroy\n"
"}\n"
"set ::tko::unknown [list self method unknown args {\n"
" if {[set w [lindex $args 0]] eq {}} {return -code error \"wrong # args: should be \\\"[self] pathName ?-option value ...?\\\"\"}\n"
" uplevel #0 [list [self] create $w {} [lrange $args 1 end]]\n"
" $w configure init\n"
" return $w\n"
"}]";
/*
* Internal variables and constants.
*/
TkoUid_class = Tk_GetUid("-class");
if(TkoObj.empty == NULL) {
Tcl_IncrRefCount((TkoObj.empty = Tcl_NewStringObj("", -1)));
Tcl_IncrRefCount((TkoObj.point = Tcl_NewStringObj(".", -1)));
Tcl_IncrRefCount((TkoObj.next = Tcl_NewStringObj("next", -1)));
Tcl_IncrRefCount((TkoObj.uplevel = Tcl_NewStringObj("::uplevel", -1)));
Tcl_IncrRefCount((TkoObj.oo_define =
Tcl_NewStringObj("::oo::define", -1)));
Tcl_IncrRefCount((TkoObj.oo_objdefine =
Tcl_NewStringObj("::oo::objdefine", -1)));
Tcl_IncrRefCount((TkoObj.method = Tcl_NewStringObj("method", -1)));
Tcl_IncrRefCount((TkoObj._tko_configure =
Tcl_NewStringObj("_tko_configure", -1)));
Tcl_IncrRefCount((TkoObj.tko = Tcl_NewStringObj("::tko", -1)));
Tcl_IncrRefCount((TkoObj.tko_options =
Tcl_NewStringObj("::tko::options", -1)));
Tcl_IncrRefCount((TkoObj.lsort = Tcl_NewStringObj("::lsort", -1)));
Tcl_IncrRefCount((TkoObj.tko_widget =
Tcl_NewStringObj("::tko::widget", -1)));
Tcl_IncrRefCount((TkoObj.tko_frame =
Tcl_NewStringObj("::tko::frame", -1)));
Tcl_IncrRefCount((TkoObj.tko_labelframe =
Tcl_NewStringObj("::tko::labelframe", -1)));
Tcl_IncrRefCount((TkoObj.tko_toplevel =
Tcl_NewStringObj("::tko::toplevel", -1)));
Tcl_IncrRefCount((TkoObj.path = Tcl_NewStringObj("::path", -1)));
Tcl_IncrRefCount((TkoObj.graph = Tcl_NewStringObj("::graph", -1)));
Tcl_IncrRefCount((TkoObj._screen = Tcl_NewStringObj("-screen", -1)));
Tcl_IncrRefCount((TkoObj._labelwidget =
Tcl_NewStringObj("-labelwidget", -1)));
Tcl_IncrRefCount((TkoObj._0 = Tcl_NewIntObj(0)));
Tcl_IncrRefCount((TkoObj._1 = Tcl_NewIntObj(1)));
}
/* Create widget class. */
if(Tcl_Eval(interp, initScript) != TCL_OK) {
return TCL_ERROR;
}
/* Get class object */
if((object = Tcl_GetObjectFromObj(interp, TkoObj.tko_widget)) == NULL
|| (clazz = Tcl_GetObjectAsClass(object)) == NULL) {
return TCL_ERROR;
}
/*
* Add methods and options
*/
if(TkoWidgetClassDefine(interp, clazz, Tcl_GetObjectName(interp, object),
widgetMethods, NULL) != TCL_OK) {
return TCL_ERROR;
}
return TCL_OK;
}
/*
* WidgetConstructor --
*
* class create path optiondefs optionargs
* -screen "" -- special arg to place toplevel widgets
*
* Results:
*
* Side effects:
*/
static int
WidgetConstructor(
ClientData clientData,
Tcl_Interp * interp,
Tcl_ObjectContext context,
int objc,
Tcl_Obj * const objv[])
{
Tcl_Object object;
tkoWidget *widget;
Tk_Window tkWin;
Tcl_Namespace *nsPtr;
Tcl_Obj *argPtr;
int argSize;
Tcl_Obj **optionObjv;
int optionObjc;
Tcl_Obj **argObjv;
int argObjc;
int i;
Tcl_Obj *dbclass;
Tcl_Obj *defvalue;
Tcl_Obj *flags;
Tcl_Obj *value;
const char *screenName = NULL;
Tcl_Obj *win;
char *ch = NULL;
int length;
/* Get current object. Should not fail? */
if((object = Tcl_ObjectContextObject(context)) == NULL) {
return TCL_ERROR;
}
/* Check objv[] arguments: ... optionlist arglist */
if(objc - Tcl_ObjectContextSkippedArgs(context) != 2) {
Tcl_WrongNumArgs(interp, 1, objv, "optionlist arglist");
return TCL_ERROR;
}
/* check arguments */
if(Tcl_ListObjGetElements(interp, objv[objc - 2], &optionObjc,
&optionObjv) != TCL_OK) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("could not get list of options"));
return TCL_ERROR;
}
argPtr = objv[objc - 1];
if(Tcl_DictObjSize(interp, argPtr, &argSize) != TCL_OK) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("could not get arglist"));
return TCL_ERROR;
}
/* Check on -screen option indicating creation of toplevel window
*/
Tcl_DictObjGet(interp, argPtr, TkoObj._screen, &value);
if(value) {
for(i = 0; i < optionObjc; i++) {
Tcl_ListObjGetElements(interp, optionObjv[i], &argObjc, &argObjv);
if(strcmp(Tcl_GetString(argObjv[0]), "-screen") == 0) {
screenName = Tcl_GetString(value);
break;
}
}
}
/* check widget name */
if((win = Tcl_GetObjectName(interp, object)) == NULL
|| (ch = TclGetStringFromObj(win, &length)) == NULL
|| length < 4 || ch[0] != ':' || ch[1] != ':' || ch[2] != '.') {
if (ch) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("no pathName"));
}
else {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("wrong pathName: %s", ch));
}
return TCL_ERROR;
}
tkWin =
Tk_CreateWindowFromPath(interp, Tk_MainWindow(interp), &ch[2],
screenName);
if(tkWin == NULL) {
return TCL_ERROR;
}
Tk_MakeWindowExist(tkWin);
widget = (tkoWidget *)ckalloc(sizeof(tkoWidget));
widget->interp = interp;
widget->object = object;
widget->tkWin = tkWin;
widget->widgetCmd = Tcl_GetObjectCommand(object);
Tcl_InitHashTable(&widget->optionsTable, TCL_ONE_WORD_KEYS);
widget->optionsArray = NULL;
/* Create option array variable */
nsPtr = Tcl_GetObjectNamespace(object);
widget->optionsArray = Tcl_NewStringObj(nsPtr->fullName, -1);
Tcl_IncrRefCount(widget->optionsArray);
Tcl_AppendToObj(widget->optionsArray, "::tko", -1);
/* set tko(.) to name of widget */
win = Tcl_NewStringObj(&ch[2], length - 2);
Tcl_IncrRefCount(win);
if(Tcl_ObjSetVar2(interp, widget->optionsArray, TkoObj.point, win,
TCL_GLOBAL_ONLY) == NULL) {
Tcl_DecrRefCount(win);
goto error;
}
Tcl_DecrRefCount(win);
/* Create my command */
widget->myCmd = Tcl_NewStringObj(nsPtr->fullName, -1);
Tcl_IncrRefCount(widget->myCmd);
Tcl_AppendToObj(widget->myCmd, "::my", -1);
Tcl_ObjectSetMetadata(object, &tkoWidgetMeta, (ClientData) widget);
/*
* Add options
*/
for(i = 0; i < optionObjc; i++) {
Tcl_ListObjGetElements(interp, optionObjv[i], &argObjc, &argObjv);
dbclass = defvalue = flags = value = NULL;
switch (argObjc) {
case 2:
break;
case 3:
dbclass = argObjv[2];
break;
case 4:
dbclass = argObjv[2];
defvalue = argObjv[3];
break;
case 5:
dbclass = argObjv[2];
defvalue = argObjv[3];
flags = argObjv[4];
break;
default:
Tcl_SetObjResult(interp, Tcl_ObjPrintf("wrong option def: %s",
Tcl_GetString(optionObjv[i])));
goto error;
}
Tcl_DictObjGet(interp, argPtr, argObjv[0], &value);
if(value) {
Tcl_IncrRefCount(value);
Tcl_DictObjRemove(interp, argPtr, argObjv[0]);
argSize--;
}
if(WidgetOptionAdd(interp, widget, argObjv[0], argObjv[1],
dbclass, defvalue, flags, value, 1) != TCL_OK) {
goto error;
}
}
if(argSize) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("unknown options: %s",
Tcl_GetString(argPtr)));
goto error;
}
Tcl_TraceVar2(interp, Tcl_GetString(widget->optionsArray), NULL,
TCL_TRACE_WRITES | TCL_TRACE_RESULT_OBJECT, WidgetOptionTrace, widget);
Tk_CreateEventHandler(tkWin, StructureNotifyMask,
WidgetEventProc, (ClientData) widget);
return TCL_OK;
error:
Tcl_DeleteCommandFromToken(interp, Tcl_GetObjectCommand(object));
return TCL_ERROR;
}
/*
* WidgetDestructor --
*
* Results:
*
* Side effects:
*/
static int
WidgetDestructor(
ClientData clientData,
Tcl_Interp * interp,
Tcl_ObjectContext context,
int objc,
Tcl_Obj * const objv[])
{
Tcl_Object object;
tkoWidget *widget;
/* Get current object. Should not fail? */
if((object = Tcl_ObjectContextObject(context)) == NULL)
return TCL_ERROR;
if((widget =
(tkoWidget *) Tcl_ObjectGetMetadata(object,
&tkoWidgetMeta)) == NULL)
return TCL_ERROR;
Tcl_Preserve(widget);
if(widget->tkWin) {
Tk_DeleteEventHandler(widget->tkWin, StructureNotifyMask,
WidgetEventProc, (ClientData) widget);
Tk_DestroyWindow(widget->tkWin);
widget->tkWin = NULL;
}
Tcl_ObjectSetMetadata(object, &tkoWidgetMeta, NULL);
Tcl_Release(widget);
return TCL_OK;
}
/*
* WidgetMetaDestroy --
*
* Results:
*
* Side effects:
*/
static void
WidgetMetaDestroy(
tkoWidget * widget)
{
Tcl_HashSearch search;
Tcl_HashEntry *entryPtr;
entryPtr = Tcl_FirstHashEntry(&widget->optionsTable, &search);
while(entryPtr != NULL) {
WidgetOptionDelEntry(entryPtr);
entryPtr = Tcl_NextHashEntry(&search);
}
Tcl_DeleteHashTable(&widget->optionsTable);
if(widget->optionsArray != NULL) {
Tcl_DecrRefCount((widget->optionsArray));
widget->optionsArray = NULL;
}
if(widget->myCmd) {
Tcl_DecrRefCount(widget->myCmd);
widget->myCmd = NULL;
}
ckfree(widget);
}
/*
* WidgetEventProc --
*
* This function 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
WidgetEventProc(
ClientData clientData, /* Information about window. */
XEvent * eventPtr)
{ /* Information about event. */
tkoWidget *widget = (tkoWidget *) clientData;
if(eventPtr->type == DestroyNotify) {
if(widget->tkWin) {
Tk_DeleteEventHandler(widget->tkWin, StructureNotifyMask,
WidgetEventProc, widget);
Tk_DestroyWindow(widget->tkWin);
widget->tkWin = NULL;
Tcl_DeleteCommandFromToken(widget->interp, widget->widgetCmd);
}
}
}
/*
* WidgetMethod_cget --
*
* cget "-option"
*
* Results:
*
* Side effects:
*/
static int
WidgetMethod_cget(
ClientData clientData,
Tcl_Interp * interp,
Tcl_ObjectContext context,
int objc,
Tcl_Obj * const objv[])
{
Tcl_Object object;
tkoWidget *widget; /* widget. */
int skip;
if((object = Tcl_ObjectContextObject(context)) == NULL)
return TCL_ERROR;
if((widget =
(tkoWidget *) Tcl_ObjectGetMetadata(object,
&tkoWidgetMeta)) == NULL)
return TCL_ERROR;
skip = Tcl_ObjectContextSkippedArgs(context);
if(objc - skip != 1) {
Tcl_WrongNumArgs(interp, skip, objv, "option");
return TCL_ERROR;
}
return WidgetOptionGet(interp, widget, objv[skip]);
}
/*
* WidgetMethod_configure --
*
* configure
* configure "-option"
* configure "-option value .."
* configure "add option dbname dbclass ?default?"
* configure "del option"
* configure "after"
*
* set tk(-option) -> WidgetTraceOption() -> my -option $v ..
* Results:
*
* Side effects:
*/
static int
WidgetMethod_configure(
ClientData clientData,
Tcl_Interp * interp,
Tcl_ObjectContext context,
int objc,
Tcl_Obj * const objv[])
{
int result, index;
Tcl_Object object;
tkoWidget *widget; /* widget. */
int skip;
Tcl_Obj *myObjv[2];
static const char *const commandNames[] =
{ "init", "optionadd", "optiondel", "optionhide", "optionshow", "optionvar", NULL };
enum command {
COMMAND_INIT, COMMAND_OPTIONADD, COMMAND_OPTIONDEL,
COMMAND_OPTIONHIDE, COMMAND_OPTIONSHOW,
COMMAND_OPTIONVAR
};
Tcl_Obj *dbclass = NULL;
Tcl_Obj *defvalue = NULL;
Tcl_Obj *flags = NULL;
Tcl_HashSearch search;
Tcl_HashEntry *entryPtr;
WidgetOption *optionPtr;
Tcl_Obj *retPtr;
Tcl_Obj *listPtr;
if((object = Tcl_ObjectContextObject(context)) == NULL
|| (widget = (tkoWidget *)Tcl_ObjectGetMetadata(object,
&tkoWidgetMeta)) == NULL) {
return TCL_ERROR;
}
skip = Tcl_ObjectContextSkippedArgs(context);
if(widget->tkWin == NULL) {
return TCL_ERROR;
}
/* configure */
if(objc - skip == 0) {
retPtr = Tcl_NewObj();
entryPtr = Tcl_FirstHashEntry(&widget->optionsTable, &search);
while(entryPtr != NULL) { /* TODO Tcl_DuplicateObj()? */
optionPtr = (WidgetOption *) Tcl_GetHashValue(entryPtr);
entryPtr = Tcl_NextHashEntry(&search);
/* hidden option, not visible in configure method */
if (optionPtr->flags&TKO_OPTION_HIDE) continue;
listPtr = Tcl_NewObj();
Tcl_ListObjAppendElement(interp, listPtr, optionPtr->option);
Tcl_ListObjAppendElement(interp, listPtr, optionPtr->dbname);
if(optionPtr->dbclass != NULL) {
Tcl_ListObjAppendElement(interp, listPtr, optionPtr->dbclass);
Tcl_ListObjAppendElement(interp, listPtr, optionPtr->defvalue);
Tcl_ListObjAppendElement(interp, listPtr, optionPtr->value);
}
Tcl_ListObjAppendElement(interp, retPtr, listPtr);
}
/* Return sorted list */
myObjv[0] = TkoObj.lsort;
myObjv[1] = retPtr;
return (Tcl_EvalObjv(interp, 2, myObjv, TCL_EVAL_GLOBAL));
}
/* configure "-option ?value? .." */
if(Tcl_GetString(objv[skip])[0] == '-') {
if(objc - skip == 1) { /* configure -option */
entryPtr =
Tcl_FindHashEntry(&widget->optionsTable,
Tk_GetUid(Tcl_GetString(objv[skip])));
if(entryPtr == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("unknown option \"%s\"",
Tcl_GetString(objv[skip])));
return TCL_ERROR;
}
optionPtr = (WidgetOption *) Tcl_GetHashValue(entryPtr);
/* hidden option, not visible in configure method */
if (optionPtr->flags&TKO_OPTION_HIDE) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("hidden option \"%s\"",
Tcl_GetString(objv[skip])));
return TCL_ERROR;
}
if(optionPtr->dbclass == NULL) {
entryPtr =
Tcl_FindHashEntry(&widget->optionsTable,
Tk_GetUid(Tcl_GetString(optionPtr->dbname)));
if(entryPtr == NULL) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("unknown option \"%s\"",
Tcl_GetString(objv[skip])));
return TCL_ERROR;
}
optionPtr = (WidgetOption *) Tcl_GetHashValue(entryPtr);
if(optionPtr->dbclass == NULL) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("unknown option \"%s\"",
Tcl_GetString(objv[skip])));
return TCL_ERROR;
}
}
listPtr = Tcl_NewObj();
Tcl_ListObjAppendElement(interp, listPtr, optionPtr->option);
Tcl_ListObjAppendElement(interp, listPtr, optionPtr->dbname);
Tcl_ListObjAppendElement(interp, listPtr, optionPtr->dbclass);
Tcl_ListObjAppendElement(interp, listPtr, optionPtr->defvalue);
Tcl_ListObjAppendElement(interp, listPtr, optionPtr->value);
Tcl_SetObjResult(interp, listPtr);
return TCL_OK;
}
if((objc - skip) % 2 == 0) { /* configure "-option value .." */
return WidgetOptionConfigure(interp, widget, objc - skip,
&objv[skip]);
}
Tcl_WrongNumArgs(interp, skip, objv, "?-option value ..?");
return TCL_ERROR;
}
/* configure "command .." */
result =
Tcl_GetIndexFromObj(interp, objv[skip], commandNames, "option", 0,
&index);
if(result != TCL_OK) {
return result;
}
switch (index) {
case COMMAND_INIT:
// collect all not readonly options and configure
Tcl_Preserve(widget);
myObjv[0] = widget->myCmd;
entryPtr = Tcl_FirstHashEntry(&widget->optionsTable, &search);
while (entryPtr != NULL) {
optionPtr = Tcl_GetHashValue(entryPtr);
entryPtr = Tcl_NextHashEntry(&search);
if (optionPtr->dbclass == NULL) { /* synonym option */
if (optionPtr->value) {
Tcl_ObjSetVar2(interp, widget->optionsArray,
optionPtr->dbname, optionPtr->value, TCL_GLOBAL_ONLY);
Tcl_DecrRefCount(optionPtr->value);
optionPtr->value = NULL;
}
}
else { /* normal option */
if ((optionPtr->flags & TKO_OPTION_READONLY) == 0) {
myObjv[1] = optionPtr->option;
if (Tcl_EvalObjv(interp, 2, myObjv,
TCL_EVAL_GLOBAL) != TCL_OK) {
retPtr = Tcl_GetObjResult(interp);
Tcl_IncrRefCount(retPtr);
Tcl_Release(widget);
Tcl_DeleteCommandFromToken(interp, widget->widgetCmd);
Tcl_SetObjResult(interp, retPtr);
Tcl_DecrRefCount(retPtr);
return TCL_ERROR;
}
}
}
}
myObjv[1] = TkoObj._tko_configure;
if (Tcl_EvalObjv(interp, 2, myObjv, TCL_EVAL_GLOBAL) != TCL_OK) {
retPtr = Tcl_GetObjResult(interp);
Tcl_IncrRefCount(retPtr);
Tcl_Release(widget);
Tcl_DeleteCommandFromToken(interp, widget->widgetCmd);
Tcl_SetObjResult(interp, retPtr);
Tcl_DecrRefCount(retPtr);
return TCL_ERROR;
}
Tcl_Release(widget);
return TCL_OK;
case COMMAND_OPTIONADD:
dbclass = NULL;
defvalue = NULL;
flags = NULL;
if(objc - skip == 3) { /* configure add option dbname */
;
} else if(objc - skip == 4) { /* configure add option dbname dbclass */
dbclass = objv[skip + 3];
} else if(objc - skip == 5) { /* configure add option dbname dbclass defvalue */
dbclass = objv[skip + 3];
defvalue = objv[skip + 4];
} else if(objc - skip == 6) { /* configure add option dbname dbclass defvalue flags */
dbclass = objv[skip + 3];
defvalue = objv[skip + 4];
flags = objv[skip + 5];
} else {
Tcl_WrongNumArgs(interp, skip + 1, objv,
"option ?synonym?|?dbname dbclass ?default? ?flags??");
return TCL_ERROR;
}
return (WidgetOptionAdd(interp, widget, objv[skip + 1], objv[skip + 2],
dbclass, defvalue, flags, NULL, 0));
case COMMAND_OPTIONDEL:
if(objc - skip == 2) {
return (WidgetOptionDel(interp, widget, objv[skip + 1]));
}
Tcl_WrongNumArgs(interp, skip + 1, objv, "option");
return TCL_ERROR;
case COMMAND_OPTIONHIDE:
/* no further args, return hidden options */
if (objc - skip == 1) {
retPtr = Tcl_NewObj();
entryPtr = Tcl_FirstHashEntry(&widget->optionsTable, &search);
while (entryPtr != NULL) {
optionPtr = Tcl_GetHashValue(entryPtr);
entryPtr = Tcl_NextHashEntry(&search);
if (optionPtr->flags&TKO_OPTION_HIDE) {
Tcl_ListObjAppendElement(interp, retPtr, optionPtr->option);
}
}
/* Return sorted list */
myObjv[0] = TkoObj.lsort;
myObjv[1] = retPtr;
return (Tcl_EvalObjv(interp, 2, myObjv, TCL_EVAL_GLOBAL));
/* hide given options */
} else {
skip++;
while (skip < objc) {
entryPtr = Tcl_FindHashEntry(&widget->optionsTable,
Tk_GetUid(Tcl_GetString(objv[skip])));
if (entryPtr == NULL) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("unknown option \"%s\"", Tcl_GetString(objv[skip])));
return TCL_ERROR;
}
optionPtr = Tcl_GetHashValue(entryPtr);
optionPtr->flags |= TKO_OPTION_HIDE;
skip++;
}
return TCL_OK;
}
case COMMAND_OPTIONSHOW:
/* no further args, return configure'able options */
if (objc - skip == 1) {
retPtr = Tcl_NewObj();
entryPtr = Tcl_FirstHashEntry(&widget->optionsTable, &search);
while (entryPtr != NULL) {
optionPtr = Tcl_GetHashValue(entryPtr);
entryPtr = Tcl_NextHashEntry(&search);
if ((optionPtr->flags & TKO_OPTION_HIDE) == 0) {
Tcl_ListObjAppendElement(interp, retPtr, optionPtr->option);
}
}
/* Return sorted list */
myObjv[0] = TkoObj.lsort;
myObjv[1] = retPtr;
return (Tcl_EvalObjv(interp, 2, myObjv, TCL_EVAL_GLOBAL));
/* make given options configure'able */
} else {
skip++;
while (skip < objc) {
entryPtr = Tcl_FindHashEntry(&widget->optionsTable,
Tk_GetUid(Tcl_GetString(objv[skip])));
if (entryPtr == NULL) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("unknown option \"%s\"", Tcl_GetString(objv[skip])));
return TCL_ERROR;
}
optionPtr = Tcl_GetHashValue(entryPtr);
optionPtr->flags &= ~TKO_OPTION_HIDE;
skip++;
}
return TCL_OK;
}
case COMMAND_OPTIONVAR:
if(objc - skip == 1) {
Tcl_SetObjResult(interp, widget->optionsArray);
return TCL_OK;
}
Tcl_WrongNumArgs(interp, skip + 1, objv, "");
return TCL_ERROR;
}
return TCL_ERROR; /* supress compiler warning */
}
/*
* WidgetOptionAdd --
*
* Results:
*
* Side effects:
*/
static int
WidgetOptionAdd(
Tcl_Interp * interp,
tkoWidget * widget,
Tcl_Obj * option,
Tcl_Obj * dbname,
Tcl_Obj * dbclass,
Tcl_Obj * defvalue,
Tcl_Obj * flags,
Tcl_Obj * value,
int initmode)
{
Tcl_HashEntry *entryPtr;
WidgetOption *optionPtr;
Tk_Uid valueUid;
int isNew;
Tk_Uid optionUid;
Tk_Uid dbnameUid;
Tk_Uid dbclassUid;
int intFlags;
int readonly;
Tcl_Obj *myObjv[2];
const char *ch;
int traceadd = 0; /* if not 0 then readd trace on array variable */
if(option == NULL || (ch = Tcl_GetString(option))[0] != '-') {
Tcl_SetObjResult(interp, Tcl_NewStringObj("missing or wrong option",
-1));
return TCL_ERROR;
}
if(dbname == NULL) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("missing dbname for option \"%s\"", ch));
return TCL_ERROR;
}
/* synonym option check */
if(dbclass == NULL) {
if(Tcl_GetString(dbname)[0] != '-') {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("wrong synonym name for option \"%s\"", ch));
return TCL_ERROR;
}
}
/* int flag */
intFlags = 0;
if(flags != NULL && Tcl_GetIntFromObj(interp, flags, &intFlags) != TCL_OK) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("wrong flags \"%s\" for option \"%s\"",
Tcl_GetString(flags), ch));
return TCL_ERROR;
}
if (intFlags & TKO_OPTION_READONLY) {
intFlags &= ~TKO_OPTION_READONLY;
readonly = TKO_OPTION_READONLY;
}
else {
readonly = 0;
}
/* return if no widget given, all class checks are done */
if(widget == NULL) {
return TCL_OK;
}
optionUid = Tk_GetUid(ch);
dbnameUid = Tk_GetUid(Tcl_GetString(dbname));
entryPtr = Tcl_CreateHashEntry(&widget->optionsTable, optionUid, &isNew);
if(isNew == 0) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("option \"%s\" exists", ch));
return TCL_ERROR;
}
/* create option */
optionPtr = (WidgetOption *) ckalloc(sizeof(WidgetOption));
optionPtr->option = option;
Tcl_IncrRefCount(optionPtr->option);
optionPtr->dbname = dbname;
Tcl_IncrRefCount(optionPtr->dbname);
optionPtr->dbclass = NULL;
optionPtr->defvalue = NULL;
optionPtr->value = NULL;
optionPtr->flags = intFlags;
Tcl_SetHashValue(entryPtr, (char *)optionPtr);
/* synonym options can have init value */
if(dbclass == NULL) {
if(value) {
optionPtr->value = value;
Tcl_IncrRefCount(optionPtr->value);
}
/* normal option */
} else {
dbclassUid = Tk_GetUid(Tcl_GetString(dbclass));
optionPtr->dbclass = dbclass;
Tcl_IncrRefCount(optionPtr->dbclass);
if(defvalue) {
optionPtr->defvalue = defvalue;
} else {
optionPtr->defvalue = TkoObj.empty;
}
Tcl_IncrRefCount(optionPtr->defvalue);
/*
* If value is given use it.
*/
if(value) {
optionPtr->value = value;
} else {
/*
* Get value from option database
*/
if(optionPtr->value == NULL) {
valueUid = Tk_GetOption(widget->tkWin, dbnameUid, dbclassUid);
if(valueUid != NULL) {
optionPtr->value = Tcl_NewStringObj(valueUid, -1);
}
}
/*
* Check for a system-specific default value.
* Do not for -class because Tcl_SetClass was not called.
* When -class is not first option (after -screen) we get a crash!
*/
if(optionPtr->value == NULL && optionUid != TkoUid_class) {
optionPtr->value =
TkpGetSystemDefault(widget->tkWin, dbnameUid, dbclassUid);
}
/*
* Use default value.
*/
if(optionPtr->value == NULL) {
optionPtr->value = defvalue;
}
}
/*
* No given value defaults to empty string.
*/
if(optionPtr->value == NULL) {
optionPtr->value = TkoObj.empty;
}
Tcl_IncrRefCount(optionPtr->value);
/*
* Outside initmode the trace on the array variable needs to be disabled.
*/
if (initmode == 0) {
Tcl_UntraceVar2(interp, Tcl_GetString(widget->optionsArray), NULL,
TCL_TRACE_WRITES | TCL_TRACE_RESULT_OBJECT, WidgetOptionTrace, widget);
traceadd = 1;
}
/*
*Set option array variable
*/
if (Tcl_ObjSetVar2(interp, widget->optionsArray, option,
optionPtr->value, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
goto error;
}
/*
* Do initialization with -option method.
*/
if (readonly || initmode == 0) {
myObjv[0] = widget->myCmd;
myObjv[1] = option;
if (Tcl_EvalObjv(interp, 2, myObjv, TCL_EVAL_GLOBAL) != TCL_OK) {
goto error;
}
/*
* We set the value again because the -option method may have changed it.
*/
if (optionPtr->value) {
Tcl_DecrRefCount(optionPtr->value);
}
optionPtr->value = Tcl_ObjGetVar2(interp, widget->optionsArray, option, TCL_GLOBAL_ONLY); /*TODO flags? */
Tcl_IncrRefCount(optionPtr->value);
/* Now we are ready to set the readonly bit */
if (readonly) {
optionPtr->flags |= TKO_OPTION_READONLY;
}
}
}
if (traceadd) {
Tcl_TraceVar2(interp, Tcl_GetString(widget->optionsArray), NULL,
TCL_TRACE_WRITES | TCL_TRACE_RESULT_OBJECT, WidgetOptionTrace, widget);
}
return TCL_OK;
error:
if (traceadd) {
/* There should be no error and thus we don't need to save the result. */
Tcl_TraceVar2(interp, Tcl_GetString(widget->optionsArray), NULL,
TCL_TRACE_WRITES | TCL_TRACE_RESULT_OBJECT, WidgetOptionTrace, widget);
}
WidgetOptionDelEntry(entryPtr);
return TCL_ERROR;
}
/*
* WidgetOptionConfigure --
*
* Results:
*
* Side effects:
*/
static int
WidgetOptionConfigure(
Tcl_Interp * interp,
tkoWidget * widget,
int objc,
Tcl_Obj * const objv[])
{
int i;
Tcl_Obj *myObjv[2];
if(objc % 2) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("missing value"));
return TCL_ERROR;
}
Tcl_Preserve(widget);
for(i = 0; i < objc; i = i + 2) {
if(WidgetOptionSet(interp, widget, objv[i], objv[i + 1]) != TCL_OK) {
Tcl_Release(widget);
return TCL_ERROR;
}
}
myObjv[0] = widget->myCmd;
myObjv[1] = TkoObj._tko_configure;
if(Tcl_EvalObjv(interp, 2, myObjv, TCL_EVAL_GLOBAL) != TCL_OK) {
Tcl_Release(widget);
return TCL_ERROR;
}
Tcl_Release(widget);
return TCL_OK;
}
/*
* WidgetOptionDel --
*
* Results:
*
* Side effects:
*/
static int
WidgetOptionDel(
Tcl_Interp * interp,
tkoWidget * widget,
Tcl_Obj * option)
{
Tcl_HashEntry *entryPtr;
if(option == NULL) {
return TCL_ERROR;
}
/* delete single option */
entryPtr =
Tcl_FindHashEntry(&widget->optionsTable,
Tk_GetUid(Tcl_GetString(option)));
if(entryPtr == NULL) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("unknown option \"%s\"", Tcl_GetString(option)));
return TCL_ERROR;
}
/* delete with no additional check on synonym option */
Tcl_UnsetVar2(interp, Tcl_GetString(widget->optionsArray),
Tcl_GetString(option), TCL_GLOBAL_ONLY);
WidgetOptionDelEntry(entryPtr);
return TCL_OK;
}
/*
* WidgetOptionGet --
*
* Results:
*
* Side effects:
*/
static int
WidgetOptionGet(
Tcl_Interp * interp,
tkoWidget * widget,
Tcl_Obj * option)
{
Tcl_Obj *retPtr;
Tcl_HashEntry *entryPtr;
WidgetOption *optionPtr;
if(option == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("no option given"));
return TCL_ERROR;
}
entryPtr =
Tcl_FindHashEntry(&widget->optionsTable,
Tk_GetUid(Tcl_GetString(option)));
if(entryPtr == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("unknown option \"%s\"",
Tcl_GetString(option)));
return TCL_ERROR;
}
optionPtr = Tcl_GetHashValue(entryPtr);
/* hidden option, not visible in cget method */
if (optionPtr->flags&TKO_OPTION_HIDE) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("hidden option \"%s\"",
Tcl_GetString(option)));
return TCL_ERROR;
}
/* synonym option */
if(optionPtr->dbclass == NULL) {
entryPtr =
Tcl_FindHashEntry(&widget->optionsTable,
Tk_GetUid(Tcl_GetString(optionPtr->dbname)));
if(entryPtr == NULL) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("unknown synonym option \"%s\"",
Tcl_GetString(option)));
return TCL_ERROR;
}
optionPtr = Tcl_GetHashValue(entryPtr);
if(optionPtr->dbclass == NULL) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("synonym option is synonym \"%s\"",
Tcl_GetString(option)));
return TCL_ERROR;
}
}
retPtr = optionPtr->value;
Tcl_SetObjResult(interp, retPtr);
return TCL_OK;
}
/*
* WidgetOptionSet --
*
* Results:
*
* Side effects:
*/
static int
WidgetOptionSet(
Tcl_Interp * interp,
tkoWidget * widget,
Tcl_Obj * option,
Tcl_Obj * value)
{
Tcl_HashEntry *entryPtr;
WidgetOption *optionPtr;
if(option == NULL || value == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("missing option and/or value"));
return TCL_ERROR;
}
entryPtr =
Tcl_FindHashEntry(&widget->optionsTable,
Tk_GetUid(Tcl_GetString(option)));
if(entryPtr == NULL) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("unknown option \"%s\"", Tcl_GetString(option)));
return TCL_ERROR;
}
optionPtr = Tcl_GetHashValue(entryPtr);
/* hidden option, not visible in cget method */
if (optionPtr->flags&TKO_OPTION_HIDE) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("hidden option \"%s\"",
Tcl_GetString(option)));
return TCL_ERROR;
}
/* synonym option */
if(optionPtr->dbclass == NULL) {
entryPtr =
Tcl_FindHashEntry(&widget->optionsTable,
Tk_GetUid(Tcl_GetString(optionPtr->dbname)));
if(entryPtr == NULL) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("unknown synonym option \"%s\"",
Tcl_GetString(option)));
return TCL_ERROR;
}
optionPtr = Tcl_GetHashValue(entryPtr);
if(optionPtr->dbclass == NULL) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("synonym option is synonym \"%s\"",
Tcl_GetString(option)));
return TCL_ERROR;
}
if(Tcl_ObjSetVar2(interp, widget->optionsArray, optionPtr->option,
value, TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
return TCL_ERROR;
}
} else {
if(Tcl_ObjSetVar2(interp, widget->optionsArray, option, value,
TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG) == NULL) {
return TCL_ERROR;
}
}
return TCL_OK;
}
/*
* TkoWidgetOptionGet --
*
* Results:
*
* Side effects:
*/
Tcl_Obj *
TkoWidgetOptionGet(
Tcl_Interp * interp,
Tcl_Object object,
Tcl_Obj * option)
{
tkoWidget *widget = (tkoWidget *) Tcl_ObjectGetMetadata(object, &tkoWidgetMeta);
if(widget == NULL) {
return NULL;
}
return Tcl_ObjGetVar2(interp, widget->optionsArray, option,
TCL_GLOBAL_ONLY);
}
/*
* TkoWidgetOptionSet --
*
* Results:
*
* Side effects:
*/
int
TkoWidgetOptionSet(
Tcl_Interp * interp,
Tcl_ObjectContext context,
Tcl_Obj * option,
tkoWidgetOptionType type,
Tcl_ObjectMetadataType * meta,
size_t offset)
{
Tcl_Object object;
tkoWidget *widget;
Tcl_Obj *value;
char *address = NULL;
int intVal;
double dblVal;
Colormap colormap;
int *intPtr;
const char *str;
int length;
int pixels[4] = { 0, 0, 0, 0 };
int objc;
Tcl_Obj **objv;
Visual * visual;
XColor * color;
Tk_3DBorder border;
Tk_Anchor anchor;
Tk_Cursor cursor;
Tk_Window newWin;
Tk_Font newFont;
Tk_Justify justify;
if((object = Tcl_ObjectContextObject(context)) == NULL
|| (widget =
(tkoWidget *) Tcl_ObjectGetMetadata(object, &tkoWidgetMeta)) == NULL
|| (value =
Tcl_ObjGetVar2(interp, widget->optionsArray, option,
TCL_GLOBAL_ONLY)) == NULL || widget->tkWin == NULL) {
return TCL_ERROR;
}
if(meta) {
if((address = Tcl_ObjectGetMetadata(object, meta)) == NULL) {
return TCL_ERROR;
}
address += offset;
}
switch (type) {
case TKO_SET_CLASS: /* (Tcl_Obj **)address */
Tk_SetClass(widget->tkWin, Tcl_GetString(value));
if(address) {
if(*((Tcl_Obj **) address) != NULL)
Tcl_DecrRefCount(*((Tcl_Obj **) address));
*((Tcl_Obj **) address) = value;
Tcl_IncrRefCount(value);
}
return TCL_OK;
case TKO_SET_VISUAL: /* (Tcl_Obj **)address */
visual =
Tk_GetVisual(interp, widget->tkWin, Tcl_GetString(value), &intVal,
&colormap);
if(visual == NULL)
return TCL_ERROR;
Tk_SetWindowVisual(widget->tkWin, visual, intVal, colormap);
if(address) {
if(*((Tcl_Obj **) address) != NULL)
Tcl_DecrRefCount(*((Tcl_Obj **) address));
*((Tcl_Obj **) address) = value;
Tcl_IncrRefCount(value);
}
return TCL_OK;
case TKO_SET_COLORMAP: /* (Tcl_Obj **)address */
str = Tcl_GetStringFromObj(value, &length);
if(str && length) {
colormap = Tk_GetColormap(interp, widget->tkWin, str);
if(colormap == None)
return TCL_ERROR;
Tk_SetWindowColormap(widget->tkWin, colormap);
}
if(address) {
if(*((Tcl_Obj **) address) != NULL)
Tcl_DecrRefCount(*((Tcl_Obj **) address));
*((Tcl_Obj **) address) = value;
Tcl_IncrRefCount(value);
}
return TCL_OK;
case TKO_SET_USENULL: /* (Tcl_Obj **)address */
str = Tcl_GetStringFromObj(value, &length);
if(str && length) {
if(TkpUseWindow(interp, widget->tkWin, str) != TCL_OK) {
return TCL_ERROR;
}
}
if(address) {
if(*((Tcl_Obj **) address) != NULL)
Tcl_DecrRefCount(*((Tcl_Obj **) address));
if(length) {
*((Tcl_Obj **) address) = value;
Tcl_IncrRefCount(value);
} else {
*((Tcl_Obj **) address) = NULL;
}
}
return TCL_OK;
case TKO_SET_CONTAINER: /* (int *)address */
if(Tcl_GetBooleanFromObj(interp, value, &intVal) != TCL_OK)
return TCL_ERROR;
if(intVal) {
TkpMakeContainer(widget->tkWin);
Tcl_ObjSetVar2(interp, widget->optionsArray, option, TkoObj._1,
TCL_GLOBAL_ONLY);
} else {
Tcl_ObjSetVar2(interp, widget->optionsArray, option, TkoObj._0,
TCL_GLOBAL_ONLY);
}
if(address) {
*(int *)address = intVal;
}
return TCL_OK;
case TKO_SET_TCLOBJ: /* (Tcl_Obj **)address */
if(address) {
if(*((Tcl_Obj **) address) != NULL)
Tcl_DecrRefCount(*((Tcl_Obj **) address));
*((Tcl_Obj **) address) = value;
Tcl_IncrRefCount(value);
}
return TCL_OK;
case TKO_SET_XCOLOR: /* (Xcolor **)address */
color = Tk_AllocColorFromObj(interp, widget->tkWin, value);
if(color == NULL)
return TCL_ERROR;
if(address) {
if(*((XColor **) address) != NULL) {
Tk_FreeColor(*((XColor **) address));
}
*((XColor **) address) = color;
} else {
Tk_FreeColor(color);
}
return TCL_OK;
case TKO_SET_3DBORDER: /* (Tk_3DBorder *)address */
border = Tk_Alloc3DBorderFromObj(interp, widget->tkWin, value);
if(border == NULL)
return TCL_ERROR;
if(address) {
if(*(Tk_3DBorder *) address != NULL) {
Tk_Free3DBorder(*(Tk_3DBorder *) address);
}
*(Tk_3DBorder *) address = border;
} else {
Tk_Free3DBorder(border);
}
return TCL_OK;
case TKO_SET_PIXEL: /* (int *)address */
if(Tk_GetPixelsFromObj(interp, widget->tkWin, value, &intVal) != TCL_OK) {
return TCL_ERROR;
}
if(address) {
*(int *)address = intVal;
}
Tcl_ObjSetVar2(interp, widget->optionsArray, option,
Tcl_NewIntObj(intVal), TCL_GLOBAL_ONLY);
return TCL_OK;
case TKO_SET_PIXELNONEGATIV: /* (int *)address */
if(Tk_GetPixelsFromObj(interp, widget->tkWin, value, &intVal) != TCL_OK) {
return TCL_ERROR;
}
if(intVal >= SHRT_MAX) {
Tcl_AppendResult(interp, "bad distance \"", Tcl_GetString(value),
"\": ", "too big to represent", (char *)NULL);
return TCL_ERROR;
}
if(intVal < 0) {
Tcl_AppendResult(interp, "bad distance \"", Tcl_GetString(value),
"\": ", "can't be negative", (char *)NULL);
return TCL_ERROR;
}
if(address) {
*(int *)address = intVal;
}
Tcl_ObjSetVar2(interp, widget->optionsArray, option,
Tcl_NewIntObj(intVal), TCL_GLOBAL_ONLY);
return TCL_OK;
case TKO_SET_PIXELPOSITIV: /* (int *)address */
if(Tk_GetPixelsFromObj(interp, widget->tkWin, value, &intVal) != TCL_OK) {
return TCL_ERROR;
}
if(intVal >= SHRT_MAX) {
Tcl_AppendResult(interp, "bad distance \"", Tcl_GetString(value),
"\": ", "too big to represent", (char *)NULL);
return TCL_ERROR;
}
if(intVal <= 0) {
Tcl_AppendResult(interp, "bad distance \"", Tcl_GetString(value),
"\": ", "must be positive", (char *)NULL);
return TCL_ERROR;
}
if(address) {
*(int *)address = intVal;
}
Tcl_ObjSetVar2(interp, widget->optionsArray, option,
Tcl_NewIntObj(intVal), TCL_GLOBAL_ONLY);
return TCL_OK;
case TKO_SET_DOUBLE: /* (double *)address */
if(Tcl_GetDoubleFromObj(interp, value, &dblVal) != TCL_OK) {
return TCL_ERROR;
}
if(address) {
*(double *)address = dblVal;
}
Tcl_ObjSetVar2(interp, widget->optionsArray, option,
Tcl_NewDoubleObj(dblVal), TCL_GLOBAL_ONLY);
return TCL_OK;
case TKO_SET_BOOLEAN: /* (int *)address */
if(Tcl_GetBooleanFromObj(interp, value, &intVal) != TCL_OK) {
return TCL_ERROR;
}
if(intVal) {
Tcl_ObjSetVar2(interp, widget->optionsArray, option, TkoObj._1,
TCL_GLOBAL_ONLY);
} else {
Tcl_ObjSetVar2(interp, widget->optionsArray, option, TkoObj._0,
TCL_GLOBAL_ONLY);
}
if(address) {
*(int *)address = intVal;
}
Tcl_ObjSetVar2(interp, widget->optionsArray, option,
Tcl_NewIntObj(intVal), TCL_GLOBAL_ONLY);
return TCL_OK;
case TKO_SET_CURSOR: /* (Tk_Cursor *)address */
cursor = None;
if(Tcl_GetString(value)[0] != '\0') {
cursor = Tk_AllocCursorFromObj(interp, widget->tkWin, value);
if(cursor == None) {
return TCL_ERROR;
}
Tk_DefineCursor(widget->tkWin, cursor);
}
if(address) {
if(*(Tk_Cursor *) address != None) {
Tk_FreeCursor(Tk_Display(widget->tkWin),
*(Tk_Cursor *) address);
}
*(Tk_Cursor *) address = cursor;
} else {
if(cursor != None) {
Tk_FreeCursor(Tk_Display(widget->tkWin), cursor); /*TODO necessary? */
}
}
return TCL_OK;
case TKO_SET_INT: /* (int *)address */
if(Tcl_GetIntFromObj(interp, value, &intVal) != TCL_OK) {
return TCL_ERROR;
}
if(address) {
*(int *)address = intVal;
}
Tcl_ObjSetVar2(interp, widget->optionsArray, option,
Tcl_NewIntObj(intVal), TCL_GLOBAL_ONLY);
return TCL_OK;
case TKO_SET_RELIEF: /* (int *)address */
if(Tk_GetReliefFromObj(interp, value, &intVal) != TCL_OK) {
return TCL_ERROR;
}
if(address) {
*(int *)address = intVal;
}
return TCL_OK;
case TKO_SET_ANCHOR: /* (Tk_Anchor *)address */
if(Tk_GetAnchorFromObj(interp, value, &anchor) != TCL_OK) {
return TCL_ERROR;
}
if(address) {
*(Tk_Anchor *) address = anchor;
}
return TCL_OK;
case TKO_SET_WINDOW: /* (Tk_Window *)address */
if(value == NULL || Tcl_GetCharLength(value) == 0) {
newWin = None;
} else {
if(TkGetWindowFromObj(interp, widget->tkWin, value,
&newWin) != TCL_OK) {
return TCL_ERROR;
}
}
if(address) {
*(Tk_Window *) address = newWin;
}
return TCL_OK;
case TKO_SET_FONT: /* (Tk_Font *)address */
newFont = Tk_AllocFontFromObj(interp, widget->tkWin, value);
if(newFont == NULL) {
return TCL_ERROR;
}
if(address) {
if(*(Tk_Font *) address != NULL) {
Tk_FreeFont(*(Tk_Font *) address);
}
*(Tk_Font *) address = newFont;
} else {
Tk_FreeFont(newFont);
}
return TCL_OK;
case TKO_SET_STRING: /* (char **)address */
if(address) {
str = Tcl_GetStringFromObj(value, &length);
if(*(char **)address != NULL) {
ckfree(*(char **)address);
}
*(char **)address = ckalloc(length + 1);
memcpy(*(char **)address, str, length + 1);
}
return TCL_OK;
case TKO_SET_STRINGNULL: /* (char **)address */
if(address) {
str = Tcl_GetStringFromObj(value, &length);
if(*(char **)address != NULL) {
ckfree(*(char **)address);
}
if(length == 0) {
*(char **)address = NULL;
} else {
*(char **)address = ckalloc(length + 1);
memcpy(*(char **)address, str, length + 1);
}
}
return TCL_OK;
case TKO_SET_SCROLLREGION: /* (int *[4])address */
if(Tcl_ListObjGetElements(interp, value, &objc, &objv) != TCL_OK) {
return TCL_ERROR;
}
if(objc == 4) {
if(Tk_GetPixelsFromObj(interp, widget->tkWin, objv[0],
&pixels[0]) != TCL_OK
|| Tk_GetPixelsFromObj(interp, widget->tkWin, objv[1],
&pixels[1]) != TCL_OK
|| Tk_GetPixelsFromObj(interp, widget->tkWin, objv[2],
&pixels[2]) != TCL_OK
|| Tk_GetPixelsFromObj(interp, widget->tkWin, objv[3],
&pixels[3]) != TCL_OK) {
return TCL_ERROR;
}
} else if(objc != 0) {
Tcl_SetObjResult(interp,
Tcl_ObjPrintf("found %d instead of 4 values", objc));
return TCL_ERROR;
}
if(address) {
intPtr = (int *)address;
intPtr[0] = pixels[0];
intPtr[1] = pixels[1];
intPtr[2] = pixels[2];
intPtr[3] = pixels[3];
}
return TCL_OK;
case TKO_SET_JUSTIFY: /* (Tk_Justify *)address */
if(Tk_GetJustify(interp, Tk_GetUid(Tcl_GetString(value)),
&justify) != TCL_OK) {
return TCL_ERROR;
}
if(address) {
*(Tk_Justify *) address = justify;
}
return TCL_OK;
}
Tcl_SetObjResult(interp, Tcl_ObjPrintf("unknown type \"%d\"", type));
return TCL_ERROR;
}
/*
* WidgetOptionTrace --
*
* Write trace on option array variable
*
* Results:
*
* Side effects:
*/
static char *
WidgetOptionTrace(
ClientData clientData,
Tcl_Interp * interp,
const char *name1,
const char *name2,
int flags)
{
tkoWidget *widget = (tkoWidget *) clientData;
Tcl_HashEntry *entryPtr;
Tcl_Obj *valuePtr;
// const char *result;
WidgetOption *optionPtr;
Tcl_Obj *myObjv[2];
Tcl_Obj *myRet;
/* get new value */
entryPtr = Tcl_FindHashEntry(&widget->optionsTable, Tk_GetUid(name2));
if(entryPtr == NULL) {
myRet = Tcl_ObjPrintf("option \"%s\" not found", name2);
Tcl_IncrRefCount(myRet);
return (char *)myRet;
}
optionPtr = (WidgetOption *) Tcl_GetHashValue(entryPtr);
if(optionPtr->flags & TKO_OPTION_READONLY) {
myRet = Tcl_ObjPrintf("option \"%s\" is readonly", name2);
Tcl_IncrRefCount(myRet);
return (char *)myRet;
}
myObjv[0] = widget->myCmd;
myObjv[1] = optionPtr->option;
if(Tcl_EvalObjv(interp, 2, myObjv, TCL_EVAL_GLOBAL) != TCL_OK) {
myRet = Tcl_GetObjResult(interp);
Tcl_IncrRefCount(myRet);
/* reset to old value TODO checks? */
if(optionPtr->value != NULL) {
Tcl_ObjSetVar2(interp, widget->optionsArray, optionPtr->option,
optionPtr->value, TCL_GLOBAL_ONLY);
Tcl_EvalObjv(interp, 2, myObjv, TCL_EVAL_GLOBAL);
}
return (char *)myRet;
}
if(optionPtr->value != NULL) {
Tcl_DecrRefCount(optionPtr->value);
}
valuePtr = Tcl_ObjGetVar2(interp, widget->optionsArray, optionPtr->option, TCL_GLOBAL_ONLY); /*TODO flags? */
optionPtr->value = valuePtr;
Tcl_IncrRefCount(optionPtr->value);
return NULL;
}
/*
* WidgetOptionDelEntry --
*
* Results:
*
* Side effects:
*/
static void
WidgetOptionDelEntry(
Tcl_HashEntry * entry)
{
WidgetOption *optionPtr = Tcl_GetHashValue(entry);
if(optionPtr->option)
Tcl_DecrRefCount(optionPtr->option);
if(optionPtr->dbname)
Tcl_DecrRefCount(optionPtr->dbname);
if(optionPtr->dbclass)
Tcl_DecrRefCount(optionPtr->dbclass);
if(optionPtr->defvalue)
Tcl_DecrRefCount(optionPtr->defvalue);
if(optionPtr->value)
Tcl_DecrRefCount(optionPtr->value);
ckfree(optionPtr);
Tcl_DeleteHashEntry(entry);
}
/*
* WidgetMethod_tko_configure --
* Virtual method called after configuring options.
* Should be implemented in derived classes.
*
* Results:
*
* Side effects:
*/
static int
WidgetMethod_tko_configure(
ClientData clientData,
Tcl_Interp * interp,
Tcl_ObjectContext context,
int objc,
Tcl_Obj * const objv[])
{ /* virtual method */
return TCL_OK;
}
/*
* WidgetMetaDelete --
* Delete widget meta data when all preserve calls done.
*
* Results:
*
* Side effects:
*/
static void
WidgetMetaDelete(
ClientData clientData)
{
Tcl_EventuallyFree(clientData, (Tcl_FreeProc *)WidgetMetaDestroy);
}
/*
* WidgetMethod_ --
* Call standard option set method.
*
* Results:
*
* Side effects:
*/
static int
WidgetMethod_(
ClientData clientData,
Tcl_Interp * interp,
Tcl_ObjectContext context,
int objc,
Tcl_Obj * const objv[])
{ /* common option set method */
tkoWidgetOptionDefine *define = (tkoWidgetOptionDefine *)clientData;
return TkoWidgetOptionSet(interp, context, define->optionPtr,
define->type, define->meta, define->offset);
}
/* vim: set ts=4 sw=4 sts=4 ff=unix et : */