OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k/trunk/insight/itcl/itk/generic
    from Rev 578 to Rev 1765
    Reverse comparison

Rev 578 → Rev 1765

/itk_option.c
0,0 → 1,586
/*
* ------------------------------------------------------------------------
* PACKAGE: [incr Tk]
* DESCRIPTION: Building mega-widgets with [incr Tcl]
*
* [incr Tk] provides a framework for building composite "mega-widgets"
* using [incr Tcl] classes. It defines a set of base classes that are
* specialized to create all other widgets.
*
* This file defines procedures used to manage mega-widget options
* specified within class definitions.
*
* ========================================================================
* AUTHOR: Michael J. McLennan
* Bell Labs Innovations for Lucent Technologies
* mmclennan@lucent.com
* http://www.tcltk.com/itcl
*
* RCS: $Id: itk_option.c,v 1.1.1.1 2002-01-16 10:24:47 markom Exp $
* ========================================================================
* Copyright (c) 1993-1998 Lucent Technologies, Inc.
* ------------------------------------------------------------------------
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*/
#include "itk.h"
 
/*
* FORWARD DECLARATIONS
*/
static char* ItkTraceClassDestroy _ANSI_ARGS_((ClientData cdata,
Tcl_Interp *interp, char *name1, char *name2, int flags));
static Tcl_HashTable* ItkGetClassesWithOptInfo _ANSI_ARGS_((
Tcl_Interp *interp));
static void ItkFreeClassesWithOptInfo _ANSI_ARGS_((ClientData cdata,
Tcl_Interp *interp));
 
/*
* ------------------------------------------------------------------------
* Itk_ClassOptionDefineCmd()
*
* Invoked when a class definition is being parse to handle an
* itk_option declaration. Adds a new option to a mega-widget
* declaration, with some code that will be executed whenever the
* option is changed via "configure". If there is already an existing
* option by that name, then this new option is folded into the
* existing option, but the <init> value is ignored. The X11 resource
* database names must be consistent with the existing option.
*
* Handles the following syntax:
*
* itk_option define <switch> <resName> <resClass> <init> ?<config>?
*
* Returns TCL_OK/TCL_ERROR to indicate success/failure.
* ------------------------------------------------------------------------
*/
/* ARGSUSED */
int
Itk_ClassOptionDefineCmd(clientData, interp, objc, objv)
ClientData clientData; /* class parser info */
Tcl_Interp *interp; /* current interpreter */
int objc; /* number of arguments */
Tcl_Obj *CONST objv[]; /* argument objects */
{
ItclObjectInfo *info = (ItclObjectInfo*)clientData;
ItclClass *cdefn = (ItclClass*)Itcl_PeekStack(&info->cdefnStack);
 
int newEntry;
char *switchName, *resName, *resClass, *init, *config;
ItkClassOptTable *optTable;
Tcl_HashEntry *entry;
ItkClassOption *opt;
 
/*
* Make sure that the arguments look right. The option switch
* name must start with a '-'.
*/
if (objc < 5 || objc > 6) {
Tcl_WrongNumArgs(interp, 1, objv,
"-switch resourceName resourceClass init ?config?");
return TCL_ERROR;
}
 
switchName = Tcl_GetStringFromObj(objv[1], (int*)NULL);
if (*switchName != '-') {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"bad option name \"", switchName, "\": should be -", switchName,
(char*)NULL);
return TCL_ERROR;
}
if (strstr(switchName, ".")) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"bad option name \"", switchName, "\": illegal character \".\"",
(char*)NULL);
return TCL_ERROR;
}
 
resName = Tcl_GetStringFromObj(objv[2], (int*)NULL);
if (!islower((int)*resName)) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"bad resource name \"", resName,
"\": should start with a lower case letter",
(char*)NULL);
return TCL_ERROR;
}
 
resClass = Tcl_GetStringFromObj(objv[3], (int*)NULL);
if (!isupper((int)*resClass)) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"bad resource class \"", resClass,
"\": should start with an upper case letter",
(char*)NULL);
return TCL_ERROR;
}
 
/*
* Make sure that this option has not already been defined in
* the context of this class. Options can be redefined in
* other classes, but can only be defined once in a given
* class. This ensures that there will be no confusion about
* which option is being referenced if the configuration code
* is redefined by a subsequent "body" command.
*/
optTable = Itk_CreateClassOptTable(interp, cdefn);
entry = Tcl_CreateHashEntry(&optTable->options, switchName, &newEntry);
 
if (!newEntry) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"option \"", switchName, "\" already defined in class \"",
cdefn->fullname, "\"",
(char*)NULL);
return TCL_ERROR;
}
 
/*
* Create a new option record and add it to the table for this
* class.
*/
init = Tcl_GetStringFromObj(objv[4], (int*)NULL);
 
if (objc == 6) {
config = Tcl_GetStringFromObj(objv[5], (int*)NULL);
} else {
config = NULL;
}
 
if (Itk_CreateClassOption(interp, cdefn, switchName, resName, resClass,
init, config, &opt) != TCL_OK) {
return TCL_ERROR;
}
 
Tcl_SetHashValue(entry, (ClientData)opt);
Itk_OptListAdd(&optTable->order, entry);
return TCL_OK;
}
 
/*
* ------------------------------------------------------------------------
* Itk_ClassOptionIllegalCmd()
*
* Invoked when a class definition is being parse to handle an
* itk_option declaration. Handles an "illegal" declaration like
* "add" or "remove", which can only be used after a widget has
* been created. Returns TCL_ERROR along with an error message.
* ------------------------------------------------------------------------
*/
/* ARGSUSED */
int
Itk_ClassOptionIllegalCmd(clientData, interp, objc, objv)
ClientData clientData; /* class parser info */
Tcl_Interp *interp; /* current interpreter */
int objc; /* number of arguments */
Tcl_Obj *CONST objv[]; /* argument objects */
{
char *op = Tcl_GetStringFromObj(objv[0], (int*)NULL);
 
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"can only ", op, " options for a specific widget\n",
"(move this command into the constructor)",
(char*)NULL);
 
return TCL_ERROR;
}
 
/*
* ------------------------------------------------------------------------
* Itk_ConfigClassOption()
*
* Invoked whenever a class-based configuration option has been
* configured with a new value. If the option has any extra code
* associated with it, the code is invoked at this point to bring
* the widget up-to-date.
*
* Returns TCL_OK on success, or TCL_ERROR (along with an error
* message in the interpreter) if anything goes wrong.
* ------------------------------------------------------------------------
*/
/* ARGSUSED */
int
Itk_ConfigClassOption(interp, contextObj, cdata, newval)
Tcl_Interp *interp; /* interpreter managing the class */
ItclObject *contextObj; /* object being configured */
ClientData cdata; /* class option */
char *newval; /* new value for this option */
{
ItkClassOption *opt = (ItkClassOption*)cdata;
int result = TCL_OK;
ItclMemberCode *mcode;
 
/*
* If the option has any config code, execute it now.
* Make sure that the namespace context is set up correctly.
*/
mcode = opt->member->code;
if (mcode && mcode->procPtr->bodyPtr) {
result = Itcl_EvalMemberCode(interp, (ItclMemberFunc*)NULL,
opt->member, contextObj, 0, (Tcl_Obj**)NULL);
}
return result;
}
 
/*
* ------------------------------------------------------------------------
* Itk_CreateClassOptTable()
*
* Finds or creates an option table which will contain all of the
* class-based configuration options for a mega-widget. These are
* the options included in the class definition which add new behavior
* to the mega-widget.
*
* This table is automatically deleted by ItkTraceClassDestroy
* whenever the class namespace is destroyed. The "unset" operation
* of a private class variable is used to detect the destruction of
* the namespace.
*
* Returns a pointer to an option table which will contain pointers to
* ItkClassOption records.
* ------------------------------------------------------------------------
*/
ItkClassOptTable*
Itk_CreateClassOptTable(interp, cdefn)
Tcl_Interp *interp; /* interpreter managing the class */
ItclClass *cdefn; /* class definition */
{
int newEntry, result;
Tcl_HashTable *itkClasses;
Tcl_HashEntry *entry;
ItkClassOptTable *optTable;
Tcl_CallFrame frame;
 
/*
* Look for the specified class definition in the table.
* If it does not yet exist, then create a new slot for it.
* When a table is created for the first time, add a
* special sentinel variable "_itk_option_data" to the
* class namespace, and put a trace on this variable.
* Whenever it is destroyed, have it delete the option table
* for this class.
*/
itkClasses = ItkGetClassesWithOptInfo(interp);
 
entry = Tcl_CreateHashEntry(itkClasses, (char*)cdefn, &newEntry);
if (newEntry) {
optTable = (ItkClassOptTable*)ckalloc(sizeof(ItkClassOptTable));
Tcl_InitHashTable(&optTable->options, TCL_STRING_KEYS);
Itk_OptListInit(&optTable->order, &optTable->options);
 
Tcl_SetHashValue(entry, (ClientData)optTable);
 
result = Tcl_PushCallFrame(interp, &frame,
cdefn->namesp, /* isProcCallFrame */ 0);
 
if (result == TCL_OK) {
Tcl_TraceVar(interp, "_itk_option_data",
(TCL_TRACE_UNSETS | TCL_NAMESPACE_ONLY),
ItkTraceClassDestroy, (ClientData)cdefn);
Tcl_PopCallFrame(interp);
}
}
else {
optTable = (ItkClassOptTable*)Tcl_GetHashValue(entry);
}
return optTable;
}
 
/*
* ------------------------------------------------------------------------
* Itk_FindClassOptTable()
*
* Looks for an option table containing all of the class-based
* configuration options for a mega-widget. These are the options
* included in a class definition which add new behavior to the
* mega-widget.
*
* Returns a pointer to an option table which will contain pointers to
* Itk_ClassOption records. If a table does not exist for this class,
* this returns NULL.
* ------------------------------------------------------------------------
*/
ItkClassOptTable*
Itk_FindClassOptTable(cdefn)
ItclClass *cdefn; /* class definition */
{
Tcl_HashTable *itkClasses;
Tcl_HashEntry *entry;
 
/*
* Look for the specified class definition in the table.
*/
itkClasses = ItkGetClassesWithOptInfo(cdefn->interp);
entry = Tcl_FindHashEntry(itkClasses, (char*)cdefn);
if (entry) {
return (ItkClassOptTable*)Tcl_GetHashValue(entry);
}
return NULL;
}
 
/*
* ------------------------------------------------------------------------
* ItkTraceClassDestroy()
*
* Invoked automatically whenever the "_itk_option_data" variable
* is destroyed within a class namespace. This should be a signal
* that the namespace is being destroyed.
*
* Releases any option data that exists for the class.
*
* Returns NULL on success, or a pointer to a string describing any
* error that is encountered.
* ------------------------------------------------------------------------
*/
/* ARGSUSED */
static char*
ItkTraceClassDestroy(cdata, interp, name1, name2, flags)
ClientData cdata; /* class definition data */
Tcl_Interp *interp; /* interpreter managing the class */
char *name1; /* name of variable involved in trace */
char *name2; /* name of array element within variable */
int flags; /* flags describing trace */
{
ItclClass *cdefn = (ItclClass*)cdata;
 
Tcl_HashTable *itkClasses;
Tcl_HashEntry *entry;
ItkClassOptTable *optTable;
Tcl_HashSearch place;
ItkClassOption *opt;
 
/*
* Look for the specified class definition in the table.
* If it is found, delete all the option records and tear
* down the table.
*/
itkClasses = ItkGetClassesWithOptInfo(cdefn->interp);
entry = Tcl_FindHashEntry(itkClasses, (char*)cdefn);
if (entry) {
optTable = (ItkClassOptTable*)Tcl_GetHashValue(entry);
Tcl_DeleteHashEntry(entry);
 
entry = Tcl_FirstHashEntry(&optTable->options, &place);
while (entry) {
opt = (ItkClassOption*)Tcl_GetHashValue(entry);
Itk_DelClassOption(opt);
entry = Tcl_NextHashEntry(&place);
}
Tcl_DeleteHashTable(&optTable->options);
Itk_OptListFree(&optTable->order);
ckfree((char*)optTable);
}
return NULL;
}
 
/*
* ------------------------------------------------------------------------
* Itk_CreateClassOption()
*
* Creates the data representing a configuration option for an
* Archetype mega-widget. This record represents an option included
* in the class definition. It adds new behavior to the mega-widget
* class.
*
* If successful, returns TCL_OK along with a pointer to the option
* record. Returns TCL_ERROR (along with an error message in the
* interpreter) if anything goes wrong.
* ------------------------------------------------------------------------
*/
int
Itk_CreateClassOption(interp, cdefn, switchName, resName, resClass,
defVal, config, optPtr)
 
Tcl_Interp *interp; /* interpreter managing the class */
ItclClass *cdefn; /* class containing this option */
char *switchName; /* name of command-line switch */
char *resName; /* resource name in X11 database */
char *resClass; /* resource class name in X11 database */
char *defVal; /* last-resort default value */
char *config; /* configuration code */
ItkClassOption **optPtr; /* returns: option record */
{
ItkClassOption *opt;
ItclMemberCode *mcode;
 
/*
* If this option has any "config" code, then try to create
* an implementation for it.
*/
if (config) {
if (Itcl_CreateMemberCode(interp, cdefn, (char*)NULL, config,
&mcode) != TCL_OK) {
 
return TCL_ERROR;
}
Itcl_PreserveData((ClientData)mcode);
Itcl_EventuallyFree((ClientData)mcode, Itcl_DeleteMemberCode);
}
else {
mcode = NULL;
}
 
/*
* Create the record to represent this option.
*/
opt = (ItkClassOption*)ckalloc(sizeof(ItkClassOption));
opt->member = Itcl_CreateMember(interp, cdefn, switchName);
opt->member->code = mcode;
 
opt->resName = (char*)ckalloc((unsigned)(strlen(resName)+1));
strcpy(opt->resName, resName);
 
opt->resClass = (char*)ckalloc((unsigned)(strlen(resClass)+1));
strcpy(opt->resClass, resClass);
 
opt->init = (char*)ckalloc((unsigned)(strlen(defVal)+1));
strcpy(opt->init, defVal);
 
*optPtr = opt;
return TCL_OK;
}
/*
* ------------------------------------------------------------------------
* Itk_FindClassOption()
*
* Searches for a class-based configuration option for an Archetype
* mega-widget. The specified name is treated as the "switch" name
* (e.g., "-option"), but this procedure will recognize it even without
* the leading "-".
*
* If an option is found that was defined in the specified class,
* then this procedure returns a pointer to the option definition.
* Otherwise, it returns NULL.
* ------------------------------------------------------------------------
*/
ItkClassOption*
Itk_FindClassOption(cdefn, switchName)
ItclClass *cdefn; /* class containing this option */
char *switchName; /* name of command-line switch */
{
ItkClassOption *opt = NULL;
 
Tcl_DString buffer;
ItkClassOptTable *optTable;
Tcl_HashEntry *entry;
 
/*
* If the switch does not have a leading "-", add it on.
*/
Tcl_DStringInit(&buffer);
if (*switchName != '-') {
Tcl_DStringAppend(&buffer, "-", -1);
Tcl_DStringAppend(&buffer, switchName, -1);
switchName = Tcl_DStringValue(&buffer);
}
 
/*
* Look for the option table for the specified class, and check
* for the requested switch.
*/
optTable = Itk_FindClassOptTable(cdefn);
if (optTable) {
entry = Tcl_FindHashEntry(&optTable->options, switchName);
if (entry) {
opt = (ItkClassOption*)Tcl_GetHashValue(entry);
}
}
Tcl_DStringFree(&buffer);
return opt;
}
/*
* ------------------------------------------------------------------------
* Itk_DelClassOption()
*
* Destroys a configuration option previously created by
* Itk_CreateClassOption().
* ------------------------------------------------------------------------
*/
void
Itk_DelClassOption(opt)
ItkClassOption *opt; /* pointer to option data */
{
Itcl_DeleteMember(opt->member);
ckfree(opt->resName);
ckfree(opt->resClass);
ckfree(opt->init);
 
ckfree((char*)opt);
}
 
/*
* ------------------------------------------------------------------------
* ItkGetClassesWithOptInfo()
*
* Returns a pointer to a hash table containing the list of registered
* classes in the specified interpreter. If the hash table does not
* already exist, it is created.
* ------------------------------------------------------------------------
*/
static Tcl_HashTable*
ItkGetClassesWithOptInfo(interp)
Tcl_Interp *interp; /* interpreter handling this registration */
{
Tcl_HashTable* classesTable;
 
/*
* If the registration table does not yet exist, then create it.
*/
classesTable = (Tcl_HashTable*)Tcl_GetAssocData(interp,
"itk_classesWithOptInfo", (Tcl_InterpDeleteProc**)NULL);
 
if (!classesTable) {
classesTable = (Tcl_HashTable*)ckalloc(sizeof(Tcl_HashTable));
Tcl_InitHashTable(classesTable, TCL_ONE_WORD_KEYS);
Tcl_SetAssocData(interp, "itk_classesWithOptInfo",
ItkFreeClassesWithOptInfo, (ClientData)classesTable);
}
return classesTable;
}
/*
* ------------------------------------------------------------------------
* ItkFreeClassesWithOptInfo()
*
* When an interpreter is deleted, this procedure is called to
* free up the associated data created by ItkGetClassesWithOptInfo.
* ------------------------------------------------------------------------
*/
static void
ItkFreeClassesWithOptInfo(clientData, interp)
ClientData clientData; /* associated data */
Tcl_Interp *interp; /* interpreter being freed */
{
Tcl_HashTable *tablePtr = (Tcl_HashTable*)clientData;
Tcl_HashSearch place, place2;
Tcl_HashEntry *entry, *entry2;
ItkClassOptTable *optTable;
ItkClassOption *opt;
 
entry = Tcl_FirstHashEntry(tablePtr, &place);
while (entry) {
optTable = (ItkClassOptTable*)Tcl_GetHashValue(entry);
 
entry2 = Tcl_FirstHashEntry(&optTable->options, &place2);
while (entry2) {
opt = (ItkClassOption*)Tcl_GetHashValue(entry2);
Itk_DelClassOption(opt);
entry2 = Tcl_NextHashEntry(&place2);
}
Tcl_DeleteHashTable(&optTable->options);
Itk_OptListFree(&optTable->order);
ckfree((char*)optTable);
 
entry = Tcl_NextHashEntry(&place);
}
 
Tcl_DeleteHashTable(tablePtr);
ckfree((char*)tablePtr);
}
itk_option.c Property changes : Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: itk_util.c =================================================================== --- itk_util.c (nonexistent) +++ itk_util.c (revision 1765) @@ -0,0 +1,200 @@ +/* + * ------------------------------------------------------------------------ + * PACKAGE: [incr Tk] + * DESCRIPTION: Building mega-widgets with [incr Tcl] + * + * [incr Tk] provides a framework for building composite "mega-widgets" + * using [incr Tcl] classes. It defines a set of base classes that are + * specialized to create all other widgets. + * + * This part defines some utility procedures that are useful for + * [incr Tk]. + * + * ======================================================================== + * AUTHOR: Michael J. McLennan + * Bell Labs Innovations for Lucent Technologies + * mmclennan@lucent.com + * http://www.tcltk.com/itcl + * + * RCS: $Id: itk_util.c,v 1.1.1.1 2002-01-16 10:24:47 markom Exp $ + * ======================================================================== + * Copyright (c) 1993-1998 Lucent Technologies, Inc. + * ------------------------------------------------------------------------ + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ +#include "itk.h" + + +/* + * ------------------------------------------------------------------------ + * Itk_OptListInit() + * + * Initializes an ordered option list, allocating a certain amount of + * memory for an initial option list. + * ------------------------------------------------------------------------ + */ +void +Itk_OptListInit(olist, options) + ItkOptList *olist; /* list to be initialized */ + Tcl_HashTable *options; /* table containing the real option entries */ +{ + olist->options = options; + olist->len = 0; + olist->max = 10; + olist->list = (Tcl_HashEntry**)ckalloc( + (unsigned)(olist->max*sizeof(Tcl_HashEntry*)) + ); +} + + +/* + * ------------------------------------------------------------------------ + * Itk_OptListFree() + * + * Frees an ordered option list created by Itk_OptListInit(). + * This only frees the memory associated with the list, not the + * list itself. + * ------------------------------------------------------------------------ + */ +void +Itk_OptListFree(olist) + ItkOptList *olist; /* list to be freed */ +{ + ckfree((char*)olist->list); + olist->len = olist->max = 0; +} + + +/* + * ------------------------------------------------------------------------ + * Itk_OptListAdd() + * + * Adds the hash table entry for an option like '-background' to an + * ordered list of options. The list is kept in alphabetical order, + * so that it can be searched quickly and printed out in order. + * ------------------------------------------------------------------------ + */ +void +Itk_OptListAdd(olist, entry) + ItkOptList *olist; /* ordered list */ + Tcl_HashEntry *entry; /* entry to be added to the list */ +{ + int i, first, last, cmp, pos, size; + Tcl_HashEntry** newOrder; + char *swname, *optname; + + /* + * Make sure that the option list is big enough. Resize + * if needed. + */ + if (olist->len >= olist->max) { + size = olist->max*sizeof(Tcl_HashEntry*); + newOrder = (Tcl_HashEntry**)ckalloc((unsigned)2*size); + memcpy((VOID*)newOrder, (VOID*)olist->list, (size_t)size); + ckfree((char*)olist->list); + + olist->list = newOrder; + olist->max *= 2; + } + + /* + * Perform a binary search to find the option switch quickly. + */ + first = 0; + last = olist->len-1; + swname = Tcl_GetHashKey(olist->options, entry) + 1; + + while (last >= first) { + pos = (first+last)/2; + optname = Tcl_GetHashKey(olist->options, olist->list[pos]) + 1; + if (*swname == *optname) { + cmp = strcmp(swname, optname); + if (cmp == 0) { + break; /* found it! */ + } + } + else if (*swname < *optname) { + cmp = -1; + } + else { + cmp = 1; + } + + if (cmp > 0) + first = pos+1; + else + last = pos-1; + } + + /* + * If a matching entry was not found, then insert one. + */ + if (last < first) { + pos = first; + + for (i=olist->len; i > pos; i--) { + olist->list[i] = olist->list[i-1]; + } + olist->list[pos] = entry; + olist->len++; + } +} + + +/* + * ------------------------------------------------------------------------ + * Itk_OptListRemove() + * + * Removes a hash table entry from an ordered list of options. + * This negates the action of Itk_OptionListAdd(), and is usually + * called when an option is completely removed from a mega-widget. + * This should be called before the entry is removed from the + * real option table. + * ------------------------------------------------------------------------ + */ +void +Itk_OptListRemove(olist, entry) + ItkOptList *olist; /* ordered list */ + Tcl_HashEntry *entry; /* entry to be removed from the list */ +{ + int pos = 0; + int i, first, last, cmp; + char *swname, *optname; + + first = 0; + last = olist->len-1; + swname = Tcl_GetHashKey(olist->options, entry) + 1; + + while (last >= first) { + pos = (first+last)/2; + optname = Tcl_GetHashKey(olist->options, olist->list[pos]) + 1; + if (*swname == *optname) { + cmp = strcmp(swname, optname); + if (cmp == 0) { + break; /* found it! */ + } + } + else if (*swname < *optname) { + cmp = -1; + } + else { + cmp = 1; + } + + if (cmp > 0) + first = pos+1; + else + last = pos-1; + } + + /* + * If a matching entry was found, then remove it. + */ + if (last >= first) { + olist->len--; + for (i=pos; i < olist->len; i++) { + olist->list[i] = olist->list[i+1]; + } + } +}
itk_util.c Property changes : Added: svn:executable ## -0,0 +1 ## +* \ No newline at end of property Index: itk_archetype.c =================================================================== --- itk_archetype.c (nonexistent) +++ itk_archetype.c (revision 1765) @@ -0,0 +1,4172 @@ +/* + * ------------------------------------------------------------------------ + * PACKAGE: [incr Tk] + * DESCRIPTION: Building mega-widgets with [incr Tcl] + * + * [incr Tk] provides a framework for building composite "mega-widgets" + * using [incr Tcl] classes. It defines a set of base classes that are + * specialized to create all other widgets. + * + * This part adds C implementations for some of the methods in the + * base class itk::Archetype. + * + * Itk_ArchComponentCmd <=> itk_component + * Itk_ArchOptionCmd <=> itk_option + * Itk_ArchInitCmd <=> itk_initialize + * Itk_ArchCompAccessCmd <=> component + * Itk_ArchConfigureCmd <=> configure + * Itk_ArchCgetCmd <=> cget + * + * Itk_ArchInitOptsCmd <=> _initOptionInfo (used to set things up) + * Itk_ArchDeleteOptsCmd <=> _deleteOptionInfo (used to clean things up) + * + * ======================================================================== + * AUTHOR: Michael J. McLennan + * Bell Labs Innovations for Lucent Technologies + * mmclennan@lucent.com + * http://www.tcltk.com/itcl + * + * RCS: $Id: itk_archetype.c,v 1.1.1.1 2002-01-16 10:24:47 markom Exp $ + * ======================================================================== + * Copyright (c) 1993-1998 Lucent Technologies, Inc. + * ------------------------------------------------------------------------ + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ +#include +#include "itk.h" + +/* + * Info associated with each Archetype mega-widget: + */ +typedef struct ArchInfo { + ItclObject *itclObj; /* object containing this info */ + Tk_Window tkwin; /* window representing this mega-widget */ + Tcl_HashTable components; /* list of all mega-widget components */ + Tcl_HashTable options; /* list of all mega-widget options */ + ItkOptList order; /* gives ordering of options */ +} ArchInfo; + +/* + * Each component widget in an Archetype mega-widget: + */ +typedef struct ArchComponent { + ItclMember *member; /* contains protection level for this comp */ + Tcl_Command accessCmd; /* access command for component widget */ + Tk_Window tkwin; /* Tk window for this component widget */ +} ArchComponent; + +/* + * Each option in an Archetype mega-widget: + */ +typedef struct ArchOption { + char *switchName; /* command-line switch for this option */ + char *resName; /* resource name in X11 database */ + char *resClass; /* resource class name in X11 database */ + char *init; /* initial value for option */ + int flags; /* flags representing option state */ + Itcl_List parts; /* parts relating to this option */ +} ArchOption; + +/* + * Flag bits for ArchOption state: + */ +#define ITK_ARCHOPT_INIT 0x01 /* option has been initialized */ + +/* + * Various parts of a composite option in an Archetype mega-widget: + */ +typedef int (Itk_ConfigOptionPartProc) _ANSI_ARGS_((Tcl_Interp *interp, + ItclObject *contextObj, ClientData cdata, char* newVal)); + +typedef struct ArchOptionPart { + ClientData clientData; /* data associated with this part */ + Itk_ConfigOptionPartProc *configProc; /* update when new vals arrive */ + Tcl_CmdDeleteProc *deleteProc; /* clean up after clientData */ + + ClientData from; /* token that indicates who + * contributed this option part */ +} ArchOptionPart; + + +/* + * Info kept by the itk::option-parser namespace and shared by + * all option processing commands: + */ +typedef struct ArchMergeInfo { + Tcl_HashTable usualCode; /* usual option handling code for the + * various widget classes */ + + ArchInfo *archInfo; /* internal option info for mega-widget */ + ArchComponent *archComp; /* component being merged into mega-widget */ + Tcl_HashTable *optionTable; /* table of valid configuration options + * for component being merged */ +} ArchMergeInfo; + +/* + * Used to capture component widget configuration options when a + * new component is being merged into a mega-widget: + */ +typedef struct GenericConfigOpt { + char *switchName; /* command-line switch for this option */ + char *resName; /* resource name in X11 database */ + char *resClass; /* resource class name in X11 database */ + char *init; /* initial value for this option */ + char *value; /* current value for this option */ + char **storage; /* storage for above strings */ + + ArchOption *integrated; /* integrated into this mega-widget option */ + ArchOptionPart *optPart; /* integrated as this option part */ +} GenericConfigOpt; + +/* + * Options that are propagated by a "configure" method: + */ +typedef struct ConfigCmdline { + Tcl_Obj *objv[4]; /* objects representing "configure" command */ +} ConfigCmdline; + + +/* + * FORWARD DECLARATIONS + */ +static void Itk_DelMergeInfo _ANSI_ARGS_((char* cdata)); + +static int Itk_ArchInitOptsCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); +static void Itk_DelArchInfo _ANSI_ARGS_((ClientData cdata)); +static int Itk_ArchDeleteOptsCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); + +static int Itk_ArchComponentCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); +static int Itk_ArchCompAddCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); +static int Itk_ArchCompDeleteCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); +static int Itk_ArchOptKeepCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); +static int Itk_ArchOptIgnoreCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); +static int Itk_ArchOptRenameCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); +static int Itk_ArchOptUsualCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); + +static int Itk_ArchInitCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); +static int Itk_ArchOptionCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); +static int Itk_ArchOptionAddCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); +static int Itk_ArchOptionRemoveCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); + +static int Itk_ArchCompAccessCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); +static int Itk_ArchConfigureCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); +static int Itk_ArchCgetCmd _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[])); +static int Itk_PropagateOption _ANSI_ARGS_((Tcl_Interp *interp, + ItclObject *contextObj, ClientData cdata, char *newval)); +static int Itk_PropagatePublicVar _ANSI_ARGS_((Tcl_Interp *interp, + ItclObject *contextObj, ClientData cdata, char *newval)); + +static int Itk_ArchSetOption _ANSI_ARGS_((Tcl_Interp *interp, + ArchInfo *info, char *name, char *value)); +static int Itk_ArchConfigOption _ANSI_ARGS_((Tcl_Interp *interp, + ArchInfo *info, char *name, char *value)); +static void Itk_ArchOptConfigError _ANSI_ARGS_((Tcl_Interp *interp, + ArchInfo *info, ArchOption *archOpt)); +static void Itk_ArchOptAccessError _ANSI_ARGS_((Tcl_Interp *interp, + ArchInfo *info, ArchOption *archOpt)); + +static int Itk_GetArchInfo _ANSI_ARGS_((Tcl_Interp *interp, + ItclObject* contextObj, ArchInfo **infoPtr)); + +static ArchComponent* Itk_CreateArchComponent _ANSI_ARGS_(( + Tcl_Interp *interp, ArchInfo *info, char *name, + ItclClass *cdefn, Tcl_Command accessCmd)); +static void Itk_DelArchComponent _ANSI_ARGS_((ArchComponent *archComp)); + +static int Itk_GetArchOption _ANSI_ARGS_((Tcl_Interp *interp, + ArchInfo *info, char *switchName, char *resName, char *resClass, + char *defVal, char *currVal, ArchOption **aoPtr)); +static void Itk_InitArchOption _ANSI_ARGS_((Tcl_Interp *interp, + ArchInfo *info, ArchOption *archOpt, char *defVal, + char *currVal)); +static void Itk_DelArchOption _ANSI_ARGS_((ArchOption *archOpt)); + +static ArchOptionPart* Itk_CreateOptionPart _ANSI_ARGS_(( + Tcl_Interp *interp, ClientData cdata, Itk_ConfigOptionPartProc* cproc, + Tcl_CmdDeleteProc *dproc, ClientData from)); +static int Itk_AddOptionPart _ANSI_ARGS_((Tcl_Interp *interp, + ArchInfo *info, char *switchName, char *resName, char *resClass, + char *defVal, char *currVal, ArchOptionPart *optPart, + ArchOption **raOpt)); +static ArchOptionPart* Itk_FindArchOptionPart _ANSI_ARGS_(( + ArchInfo *info, char *switchName, ClientData from)); +static int Itk_RemoveArchOptionPart _ANSI_ARGS_((ArchInfo *info, + char *switchName, ClientData from)); +static int Itk_IgnoreArchOptionPart _ANSI_ARGS_((ArchInfo *info, + GenericConfigOpt *opt)); +static void Itk_DelOptionPart _ANSI_ARGS_((ArchOptionPart *optPart)); + +static ConfigCmdline* Itk_CreateConfigCmdline _ANSI_ARGS_(( + Tcl_Interp *interp, Tcl_Command accessCmd, char *switchName)); +static void Itk_DeleteConfigCmdline _ANSI_ARGS_((ClientData cdata)); + +static Tcl_HashTable* Itk_CreateGenericOptTable _ANSI_ARGS_((Tcl_Interp *interp, + char *options)); +static void Itk_DelGenericOptTable _ANSI_ARGS_((Tcl_HashTable *tPtr)); + +static GenericConfigOpt* Itk_CreateGenericOpt _ANSI_ARGS_((Tcl_Interp *interp, + char *switchName, Tcl_Command accessCmd)); +static void Itk_DelGenericOpt _ANSI_ARGS_((GenericConfigOpt* opt)); + +static Tcl_HashTable* ItkGetObjsWithArchInfo _ANSI_ARGS_((Tcl_Interp *interp)); +static void ItkFreeObjsWithArchInfo _ANSI_ARGS_((ClientData cdata, + Tcl_Interp *interp)); + + +/* + * ------------------------------------------------------------------------ + * Itk_ArchetypeInit() + * + * Invoked by Itk_Init() whenever a new interpreter is created to + * declare the procedures used in the itk::Archetype base class. + * ------------------------------------------------------------------------ + */ +int +Itk_ArchetypeInit(interp) + Tcl_Interp *interp; /* interpreter to be updated */ +{ + ArchMergeInfo *mergeInfo; + Tcl_Namespace *parserNs; + + /* + * Declare all of the C routines that are integrated into + * the Archetype base class. + */ + if (Itcl_RegisterObjC(interp, + "Archetype-init", Itk_ArchInitOptsCmd, + (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL) != TCL_OK || + + Itcl_RegisterObjC(interp, + "Archetype-delete", Itk_ArchDeleteOptsCmd, + (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL) != TCL_OK || + + Itcl_RegisterObjC(interp, + "Archetype-itk_component", Itk_ArchComponentCmd, + (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL) != TCL_OK || + + Itcl_RegisterObjC(interp, + "Archetype-itk_option", Itk_ArchOptionCmd, + (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL) != TCL_OK || + + Itcl_RegisterObjC(interp, + "Archetype-itk_initialize", Itk_ArchInitCmd, + (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL) != TCL_OK || + + Itcl_RegisterObjC(interp, + "Archetype-component", Itk_ArchCompAccessCmd, + (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL) != TCL_OK || + + Itcl_RegisterObjC(interp, + "Archetype-configure",Itk_ArchConfigureCmd, + (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL) != TCL_OK || + + Itcl_RegisterObjC(interp, + "Archetype-cget",Itk_ArchCgetCmd, + (ClientData)NULL, (Tcl_CmdDeleteProc*)NULL) != TCL_OK) { + + return TCL_ERROR; + } + + /* + * Create the namespace containing the option parser commands. + */ + mergeInfo = (ArchMergeInfo*)ckalloc(sizeof(ArchMergeInfo)); + Tcl_InitHashTable(&mergeInfo->usualCode, TCL_STRING_KEYS); + mergeInfo->archInfo = NULL; + mergeInfo->archComp = NULL; + mergeInfo->optionTable = NULL; + + parserNs = Tcl_CreateNamespace(interp, "::itk::option-parser", + (ClientData)mergeInfo, Itcl_ReleaseData); + + if (!parserNs) { + Itk_DelMergeInfo((char*)mergeInfo); + Tcl_AddErrorInfo(interp, "\n (while initializing itk)"); + return TCL_ERROR; + } + Itcl_PreserveData((ClientData)mergeInfo); + Itcl_EventuallyFree((ClientData)mergeInfo, Itk_DelMergeInfo); + + Tcl_CreateObjCommand(interp, "::itk::option-parser::keep", + Itk_ArchOptKeepCmd, + (ClientData)mergeInfo, (Tcl_CmdDeleteProc*)NULL); + + Tcl_CreateObjCommand(interp, "::itk::option-parser::ignore", + Itk_ArchOptIgnoreCmd, + (ClientData)mergeInfo, (Tcl_CmdDeleteProc*)NULL); + + Tcl_CreateObjCommand(interp, "::itk::option-parser::rename", + Itk_ArchOptRenameCmd, + (ClientData)mergeInfo, (Tcl_CmdDeleteProc*)NULL); + + Tcl_CreateObjCommand(interp, "::itk::option-parser::usual", + Itk_ArchOptUsualCmd, + (ClientData)mergeInfo, (Tcl_CmdDeleteProc*)NULL); + + /* + * Add the "itk::usual" command to register option handling code. + */ + Tcl_CreateObjCommand(interp, "::itk::usual", Itk_UsualCmd, + (ClientData)mergeInfo, Itcl_ReleaseData); + Itcl_PreserveData((ClientData)mergeInfo); + + return TCL_OK; +} + + +/* + * ------------------------------------------------------------------------ + * Itk_DelMergeInfo() + * + * Destroys the "merge" info record shared by commands in the + * itk::option-parser namespace. Invoked automatically when the + * namespace containing the parsing commands is destroyed and there + * are no more uses of the data. + * ------------------------------------------------------------------------ + */ +static void +Itk_DelMergeInfo(cdata) + char* cdata; /* data to be destroyed */ +{ + ArchMergeInfo *mergeInfo = (ArchMergeInfo*)cdata; + + Tcl_HashEntry *entry; + Tcl_HashSearch place; + Tcl_Obj *codePtr; + + assert(mergeInfo->optionTable == NULL); + + entry = Tcl_FirstHashEntry(&mergeInfo->usualCode, &place); + while (entry) { + codePtr = (Tcl_Obj*)Tcl_GetHashValue(entry); + Tcl_DecrRefCount(codePtr); + entry = Tcl_NextHashEntry(&place); + } + Tcl_DeleteHashTable(&mergeInfo->usualCode); + + ckfree((char*)mergeInfo); +} + + +/* + * ------------------------------------------------------------------------ + * Itk_ArchInitOptsCmd() + * + * Invoked by [incr Tcl] to handle the itk::Archetype::_initOptionInfo + * method. This method should be called out in the constructor for + * each object, to initialize the object so that it can be used with + * the other access methods in this file. Allocates some extra + * data associated with the object at the C-language level. + * + * Returns TCL_OK/TCL_ERROR to indicate success/failure. + * ------------------------------------------------------------------------ + */ +/* ARGSUSED */ +static int +Itk_ArchInitOptsCmd(dummy, interp, objc, objv) + ClientData dummy; /* unused */ + Tcl_Interp *interp; /* current interpreter */ + int objc; /* number of arguments */ + Tcl_Obj *CONST objv[]; /* argument objects */ +{ + int newEntry, result; + ArchInfo *info; + ItclClass *contextClass; + ItclObject *contextObj; + Tcl_HashTable *objsWithArchInfo; + Tcl_HashEntry *entry; + Command *cmdPtr; + + if (objc != 1) { + Tcl_WrongNumArgs(interp, 1, objv, ""); + return TCL_ERROR; + } + + if (Itcl_GetContext(interp, &contextClass, &contextObj) != TCL_OK || + !contextObj) { + + char *token = Tcl_GetStringFromObj(objv[0], (int*)NULL); + Tcl_ResetResult(interp); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "cannot use \"", token, "\" without an object context", + (char*)NULL); + return TCL_ERROR; + } + + /* + * Create some archetype info for the current object and + * register it on the list of all known objects. + */ + objsWithArchInfo = ItkGetObjsWithArchInfo(interp); + + info = (ArchInfo*)ckalloc(sizeof(ArchInfo)); + info->itclObj = contextObj; + info->tkwin = NULL; /* not known yet */ + Tcl_InitHashTable(&info->components, TCL_STRING_KEYS); + Tcl_InitHashTable(&info->options, TCL_STRING_KEYS); + Itk_OptListInit(&info->order, &info->options); + + entry = Tcl_CreateHashEntry(objsWithArchInfo, (char*)contextObj, &newEntry); + if (!newEntry) { + Itk_DelArchInfo( Tcl_GetHashValue(entry) ); + } + Tcl_SetHashValue(entry, (ClientData)info); + + /* + * Make sure that the access command for this object + * resides in the global namespace. If need be, move + * the command. + */ + result = TCL_OK; + cmdPtr = (Command*)contextObj->accessCmd; + + if (cmdPtr->nsPtr != (Namespace*)Tcl_GetGlobalNamespace(interp)) { + Tcl_Obj *oldNamePtr, *newNamePtr; + + oldNamePtr = Tcl_NewStringObj((char*)NULL, 0); + Tcl_GetCommandFullName(interp, contextObj->accessCmd, oldNamePtr); + Tcl_IncrRefCount(oldNamePtr); + + newNamePtr = Tcl_NewStringObj("::", -1); + Tcl_AppendToObj(newNamePtr, + Tcl_GetCommandName(interp, contextObj->accessCmd), -1); + Tcl_IncrRefCount(newNamePtr); + + result = TclRenameCommand(interp, + Tcl_GetStringFromObj(oldNamePtr, (int*)NULL), + Tcl_GetStringFromObj(newNamePtr, (int*)NULL)); + + Tcl_DecrRefCount(oldNamePtr); + Tcl_DecrRefCount(newNamePtr); + } + + return result; +} + + +/* + * ------------------------------------------------------------------------ + * Itk_DelArchInfo() + * + * Invoked when the option info associated with an itk::Archetype + * widget is no longer needed. This usually happens when a widget + * is destroyed. Frees the given bundle of data and removes it + * from the global list of Archetype objects. + * ------------------------------------------------------------------------ + */ +static void +Itk_DelArchInfo(cdata) + ClientData cdata; /* client data for Archetype objects */ +{ + ArchInfo *info = (ArchInfo*)cdata; + + Tcl_HashEntry *entry; + Tcl_HashSearch place; + ArchOption *archOpt; + ArchComponent *archComp; + + /* + * Destroy all component widgets. + */ + entry = Tcl_FirstHashEntry(&info->components, &place); + while (entry) { + archComp = (ArchComponent*)Tcl_GetHashValue(entry); + Itk_DelArchComponent(archComp); + entry = Tcl_NextHashEntry(&place); + } + Tcl_DeleteHashTable(&info->components); + + /* + * Destroy all information associated with configuration options. + */ + entry = Tcl_FirstHashEntry(&info->options, &place); + while (entry) { + archOpt = (ArchOption*)Tcl_GetHashValue(entry); + Itk_DelArchOption(archOpt); + entry = Tcl_NextHashEntry(&place); + } + Tcl_DeleteHashTable(&info->options); + Itk_OptListFree(&info->order); + + ckfree((char*)info); +} + + +/* + * ------------------------------------------------------------------------ + * Itk_ArchDeleteOptsCmd() + * + * Invoked by [incr Tcl] to handle the itk::Archetype::_deleteOptionInfo + * method. This method should be called out in the destructor for each + * object, to clean up data allocated by Itk_ArchInitOptsCmd(). + * + * Returns TCL_OK/TCL_ERROR to indicate success/failure. + * ------------------------------------------------------------------------ + */ +/* ARGSUSED */ +static int +Itk_ArchDeleteOptsCmd(dummy, interp, objc, objv) + ClientData dummy; /* unused */ + Tcl_Interp *interp; /* current interpreter */ + int objc; /* number of arguments */ + Tcl_Obj *CONST objv[]; /* argument objects */ +{ + ItclClass *contextClass; + ItclObject *contextObj; + Tcl_HashTable *objsWithArchInfo; + Tcl_HashEntry *entry; + + if (objc != 1) { + Tcl_WrongNumArgs(interp, 1, objv, ""); + return TCL_ERROR; + } + if (Itcl_GetContext(interp, &contextClass, &contextObj) != TCL_OK || + !contextObj) { + + char *token = Tcl_GetStringFromObj(objv[0], (int*)NULL); + Tcl_ResetResult(interp); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "cannot use \"", token, "\" without an object context", + (char*)NULL); + return TCL_ERROR; + } + + /* + * Find the info associated with this object. + * Destroy the data and remove it from the global list. + */ + objsWithArchInfo = ItkGetObjsWithArchInfo(interp); + entry = Tcl_FindHashEntry(objsWithArchInfo, (char*)contextObj); + + if (entry) { + Itk_DelArchInfo( Tcl_GetHashValue(entry) ); + Tcl_DeleteHashEntry(entry); + } + return TCL_OK; +} + + +/* + * ------------------------------------------------------------------------ + * Itk_ArchComponentCmd() + * + * Invoked by [incr Tcl] to handle the itk::Archetype::itk_component + * method. Handles the following options: + * + * itk_component add ?-protected? ?-private? ?--? \ + * ?? + * + * itk_component delete ?...? + * + * Returns TCL_OK/TCL_ERROR to indicate success/failure. + * ------------------------------------------------------------------------ + */ +/* ARGSUSED */ +static int +Itk_ArchComponentCmd(dummy, interp, objc, objv) + ClientData dummy; /* unused */ + Tcl_Interp *interp; /* current interpreter */ + int objc; /* number of arguments */ + Tcl_Obj *CONST objv[]; /* argument objects */ +{ + char *cmd, *token, c; + int length; + + /* + * Check arguments and handle the various options... + */ + if (objc < 2) { + cmd = Tcl_GetStringFromObj(objv[0], (int*)NULL); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "wrong # args: should be one of...\n", + " ", cmd, " add ?-protected? ?-private? ?--? name createCmds ?optionCmds?\n", + " ", cmd, " delete name ?name name...?", + (char*)NULL); + return TCL_ERROR; + } + + token = Tcl_GetStringFromObj(objv[1], (int*)NULL); + c = *token; + length = strlen(token); + + /* + * Handle: itk_component add... + */ + if (c == 'a' && strncmp(token, "add", length) == 0) { + if (objc < 4) { + Tcl_WrongNumArgs(interp, 1, objv, + "add ?-protected? ?-private? ?--? name createCmds ?optionCmds?"); + return TCL_ERROR; + } + return Itk_ArchCompAddCmd(dummy, interp, objc-1, objv+1); + } + + /* + * Handle: itk_component delete... + */ + else if (c == 'd' && strncmp(token, "delete", length) == 0) { + if (objc < 3) { + Tcl_WrongNumArgs(interp, 1, objv, "delete name ?name name...?"); + return TCL_ERROR; + } + return Itk_ArchCompDeleteCmd(dummy, interp, objc-1, objv+1); + } + + /* + * Flag any errors. + */ + cmd = Tcl_GetStringFromObj(objv[0], (int*)NULL); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "bad option \"", token, + "\": should be one of...\n", + " ", cmd, " add name createCmds ?optionCmds?\n", + " ", cmd, " delete name ?name name...?", + (char*)NULL); + return TCL_ERROR; +} + + +/* + * ------------------------------------------------------------------------ + * Itk_ArchCompAddCmd() + * + * Invoked by [incr Tcl] to handle the itk::Archetype::itk_component + * method. Adds a new component widget into the mega-widget, + * integrating its configuration options into the master list. + * + * itk_component add ?-protected? ?-private? ?--? \ + * + * + * Returns TCL_OK/TCL_ERROR to indicate success/failure. + * ------------------------------------------------------------------------ + */ +/* ARGSUSED */ +static int +Itk_ArchCompAddCmd(dummy, interp, objc, objv) + ClientData dummy; /* unused */ + Tcl_Interp *interp; /* current interpreter */ + int objc; /* number of arguments */ + Tcl_Obj *CONST objv[]; /* argument objects */ +{ + Tcl_HashEntry *entry = NULL; + char *path = NULL; + ArchComponent *archComp = NULL; + ArchMergeInfo *mergeInfo = NULL; + Tcl_Obj *objNamePtr = NULL; + Tcl_Obj *tmpNamePtr = NULL; + Tcl_Obj *winNamePtr = NULL; + Tcl_Obj *hullNamePtr = NULL; + int pLevel = ITCL_PUBLIC; + + int newEntry, result; + char *cmd, *token, *name, *resultStr; + Tcl_Namespace *parserNs; + ItclClass *contextClass, *ownerClass; + ItclObject *contextObj; + ArchInfo *info; + Tcl_CallFrame frame, *uplevelFramePtr, *oldFramePtr; + Tcl_Command accessCmd; + Tcl_Obj *objPtr; + Tcl_DString buffer; + + /* + * Get the Archetype info associated with this widget. + */ + if (Itcl_GetContext(interp, &contextClass, &contextObj) != TCL_OK || + !contextObj) { + + Tcl_ResetResult(interp); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "cannot access components without an object context", + (char*)NULL); + return TCL_ERROR; + } + + if (Itk_GetArchInfo(interp, contextObj, &info) != TCL_OK) { + return TCL_ERROR; + } + + /* + * Look for options like "-protected" or "-private". + */ + cmd = Tcl_GetStringFromObj(objv[0], (int*)NULL); + + while (objc > 1) { + token = Tcl_GetStringFromObj(objv[1], (int*)NULL); + if (*token != '-') { + break; + } + else if (strcmp(token,"-protected") == 0) { + pLevel = ITCL_PROTECTED; + } + else if (strcmp(token,"-private") == 0) { + pLevel = ITCL_PRIVATE; + } + else if (strcmp(token,"--") == 0) { + objc--; + objv++; + break; + } + else { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "bad option \"", token, + "\": should be -private, -protected or --", + (char*)NULL); + return TCL_ERROR; + } + objc--; + objv++; + } + + if (objc < 3 || objc > 4) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "wrong # args: should be \"", cmd, + " ?-protected? ?-private? ?--? name createCmds ?optionCmds?", + (char*)NULL); + return TCL_ERROR; + } + + /* + * See if a component already exists with the symbolic name. + */ + name = Tcl_GetStringFromObj(objv[1], (int*)NULL); + entry = Tcl_CreateHashEntry(&info->components, name, &newEntry); + if (!newEntry) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "component \"", name, "\" already defined", + (char*)NULL); + return TCL_ERROR; + } + + /* + * If this component is the "hull" for the mega-widget, then + * move the object access command out of the way before + * creating the component, so it is not accidentally deleted. + */ + Tcl_DStringInit(&buffer); + + objNamePtr = Tcl_NewStringObj((char*)NULL, 0); + Tcl_GetCommandFullName(contextObj->classDefn->interp, + contextObj->accessCmd, objNamePtr); + Tcl_IncrRefCount(objNamePtr); + + if (strcmp(name, "hull") == 0) { + tmpNamePtr = Tcl_NewStringObj((char*)NULL, 0); + Tcl_GetCommandFullName(contextObj->classDefn->interp, + contextObj->accessCmd, tmpNamePtr); + Tcl_AppendToObj(tmpNamePtr, "-widget-", -1); + Tcl_IncrRefCount(tmpNamePtr); + + result = TclRenameCommand(interp, + Tcl_GetStringFromObj(objNamePtr, (int*)NULL), + Tcl_GetStringFromObj(tmpNamePtr, (int*)NULL)); + + if (result != TCL_OK) { + goto compFail; + } + } + + /* + * Execute the to create the component widget. + * Do this one level up, in the scope of the calling routine. + */ + uplevelFramePtr = _Tcl_GetCallFrame(interp, 1); + oldFramePtr = _Tcl_ActivateCallFrame(interp, uplevelFramePtr); + + /* CYGNUS LOCAL - Fix for Tcl8.1 */ +#if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0 + if (Tcl_EvalObj(interp, objv[2]) != TCL_OK) { +#else + if (Tcl_EvalObj(interp, objv[2], 0) != TCL_OK) { +#endif + /* END CYGNUS LOCAL */ + goto compFail; + } + + /* + * Take the result from the widget creation commands as the + * path name for the new component. Make a local copy of + * this, since the interpreter will get used in the mean time. + */ + resultStr = Tcl_GetStringResult(interp); + path = (char*)ckalloc((unsigned)(strlen(resultStr)+1)); + strcpy(path, resultStr); + + /* + * Look for the access command token in the context of the + * calling namespace. By-pass any protection at this point. + */ + accessCmd = Tcl_FindCommand(interp, path, (Tcl_Namespace*)NULL, + /* flags */ 0); + + if (!accessCmd) { + Tcl_ResetResult(interp); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "cannot find component access command \"", + path, "\" for component \"", name, "\"", + (char*)NULL); + goto compFail; + } + + winNamePtr = Tcl_NewStringObj((char*)NULL, 0); + Tcl_GetCommandFullName(interp, accessCmd, winNamePtr); + Tcl_IncrRefCount(winNamePtr); + + (void) _Tcl_ActivateCallFrame(interp, oldFramePtr); + + /* + * Create the component record. Set the protection level + * according to the "-protected" or "-private" option. + */ + ownerClass = contextClass; + uplevelFramePtr = _Tcl_GetCallFrame(interp, 1); + if (uplevelFramePtr && Itcl_IsClassNamespace(uplevelFramePtr->nsPtr)) { + ownerClass = (ItclClass*)uplevelFramePtr->nsPtr->clientData; + } + + archComp = Itk_CreateArchComponent(interp, info, name, ownerClass, + accessCmd); + + if (!archComp) { + goto compFail; + } + + Tcl_SetHashValue(entry, (ClientData)archComp); + archComp->member->protection = pLevel; + + /* + * If this component is the "hull" for the mega-widget, then + * move the hull widget access command to a different name, + * and move the object access command back into place. This + * way, when the widget name is used as a command, the object + * access command will handle all requests. + */ + if (strcmp(name, "hull") == 0) { + hullNamePtr = Tcl_NewStringObj((char*)NULL, 0); + Tcl_GetCommandFullName(interp, accessCmd, hullNamePtr); + Tcl_AppendToObj(hullNamePtr, "-itk_hull", -1); + Tcl_IncrRefCount(hullNamePtr); + + result = TclRenameCommand(interp, + Tcl_GetStringFromObj(winNamePtr, (int*)NULL), + Tcl_GetStringFromObj(hullNamePtr, (int*)NULL)); + + if (result != TCL_OK) { + goto compFail; + } + + Tcl_DecrRefCount(winNamePtr); /* winNamePtr keeps current name */ + winNamePtr = hullNamePtr; + hullNamePtr = NULL; + + result = TclRenameCommand(interp, + Tcl_GetStringFromObj(tmpNamePtr, (int*)NULL), + Tcl_GetStringFromObj(objNamePtr, (int*)NULL)); + + if (result != TCL_OK) { + goto compFail; + } + } + + /* + * Add a binding onto the new component, so that when its + * window is destroyed, it will automatically remove itself + * from its parent's component list. Avoid doing these things + * for the "hull" component, since it is a special case and + * these things are not really necessary. + */ + else { + Tcl_DStringSetLength(&buffer, 0); + Tcl_DStringAppend(&buffer, "bindtags ", -1); + Tcl_DStringAppend(&buffer, path, -1); + if (Tcl_Eval(interp, Tcl_DStringValue(&buffer)) != TCL_OK) { + goto compFail; + } + + Tcl_DStringSetLength(&buffer, 0); + Tcl_DStringAppend(&buffer, "bind itk-destroy-", -1); + Tcl_DStringAppend(&buffer, path, -1); + Tcl_DStringAppend(&buffer, " [itcl::code ", -1); + + Tcl_DStringAppend(&buffer, + Tcl_GetStringFromObj(objNamePtr,(int*)NULL), -1); + + Tcl_DStringAppend(&buffer, " itk_component delete ", -1); + Tcl_DStringAppend(&buffer, name, -1); + Tcl_DStringAppend(&buffer, "]\n", -1); + Tcl_DStringAppend(&buffer, "bindtags ", -1); + Tcl_DStringAppend(&buffer, path, -1); + Tcl_DStringAppend(&buffer, " {itk-destroy-", -1); + Tcl_DStringAppend(&buffer, path, -1); + Tcl_DStringAppend(&buffer, " ", -1); + Tcl_DStringAppend(&buffer, Tcl_GetStringResult(interp), -1); + Tcl_DStringAppend(&buffer, "}", -1); + if (Tcl_Eval(interp, Tcl_DStringValue(&buffer)) != TCL_OK) { + goto compFail; + } + } + + /* + * Query the list of configuration options for this widget, + * so we will know which ones are valid. Build an option + * table to represent these, so they can be found quickly + * by the option parsing commands in "itk::option-parser". + */ + Tcl_DStringTrunc(&buffer, 0); + Tcl_DStringAppendElement(&buffer, + Tcl_GetStringFromObj(winNamePtr, (int*)NULL)); + Tcl_DStringAppendElement(&buffer, "configure"); + + result = Tcl_Eval(interp, Tcl_DStringValue(&buffer)); + + if (result != TCL_OK) { + goto compFail; + } + Tcl_DStringSetLength(&buffer, 0); + Tcl_DStringAppend(&buffer, Tcl_GetStringResult(interp), -1); + + /* + * Find the "itk::option-parser" namespace and get the data + * record shared by all of the parsing commands. + */ + parserNs = Tcl_FindNamespace(interp, "::itk::option-parser", + (Tcl_Namespace*)NULL, TCL_LEAVE_ERR_MSG); + + if (!parserNs) { + goto compFail; + } + mergeInfo = (ArchMergeInfo*)parserNs->clientData; + assert(mergeInfo); + + /* + * Initialize the data record used by the option parsing commands. + * Store a table of valid configuration options, along with the + * info for the mega-widget that is being updated. + */ + mergeInfo->optionTable = Itk_CreateGenericOptTable(interp, + Tcl_DStringValue(&buffer)); + + if (!mergeInfo->optionTable) { + goto compFail; + } + mergeInfo->archInfo = info; + mergeInfo->archComp = archComp; + + /* + * Execute the option-handling commands in the "itk::option-parser" + * namespace. If there are no option-handling commands, invoke + * the "usual" command instead. + */ + if (objc != 4) { + objPtr = Tcl_NewStringObj("usual", -1); + Tcl_IncrRefCount(objPtr); + } else { + objPtr = objv[3]; + } + + result = Tcl_PushCallFrame(interp, &frame, + parserNs, /* isProcCallFrame */ 0); + + if (result == TCL_OK) { + /* CYGNUS LOCAL - Fix for Tcl8.1 */ +#if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION == 0 + result = Tcl_EvalObj(interp, objPtr); +#else + result = Tcl_EvalObj(interp, objPtr, 0); +#endif + /* END CYGNUS LOCAL */ + Tcl_PopCallFrame(interp); + } + + if (objPtr != objv[3]) { + Tcl_DecrRefCount(objPtr); + } + if (result != TCL_OK) { + goto compFail; + } + + Itk_DelGenericOptTable(mergeInfo->optionTable); + mergeInfo->optionTable = NULL; + mergeInfo->archInfo = NULL; + mergeInfo->archComp = NULL; + + ckfree(path); + + Tcl_DStringFree(&buffer); + if (objNamePtr) { + Tcl_DecrRefCount(objNamePtr); + } + if (tmpNamePtr) { + Tcl_DecrRefCount(tmpNamePtr); + } + if (winNamePtr) { + Tcl_DecrRefCount(winNamePtr); + } + if (hullNamePtr) { + Tcl_DecrRefCount(hullNamePtr); + } + + Tcl_SetResult(interp, name, TCL_VOLATILE); + return TCL_OK; + + /* + * If any errors were encountered, clean up and return. + */ +compFail: + if (archComp) { + Itk_DelArchComponent(archComp); + } + if (entry) { + Tcl_DeleteHashEntry(entry); + } + if (path) { + ckfree(path); + } + if (mergeInfo && mergeInfo->optionTable) { + Itk_DelGenericOptTable(mergeInfo->optionTable); + mergeInfo->optionTable = NULL; + mergeInfo->archInfo = NULL; + mergeInfo->archComp = NULL; + } + + Tcl_DStringFree(&buffer); + if (objNamePtr) { + Tcl_DecrRefCount(objNamePtr); + } + if (tmpNamePtr) { + Tcl_DecrRefCount(tmpNamePtr); + } + if (winNamePtr) { + Tcl_DecrRefCount(winNamePtr); + } + if (hullNamePtr) { + Tcl_DecrRefCount(hullNamePtr); + } + + /* + * Add error info and return. + */ + objPtr = Tcl_NewStringObj((char*)NULL, 0); + Tcl_AppendToObj(objPtr, "\n (while creating component \"", -1); + Tcl_AppendToObj(objPtr, name, -1); + Tcl_AppendToObj(objPtr, "\" for widget \"", -1); + Tcl_GetCommandFullName(contextObj->classDefn->interp, + contextObj->accessCmd, objPtr); + Tcl_AppendToObj(objPtr, "\")", -1); + Tcl_IncrRefCount(objPtr); + + Tcl_AddErrorInfo(interp, Tcl_GetStringFromObj(objPtr, (int*)NULL)); + Tcl_DecrRefCount(objPtr); + + + return TCL_ERROR; +} + + +/* + * ------------------------------------------------------------------------ + * Itk_ArchCompDeleteCmd() + * + * Invoked by [incr Tcl] to handle the itk::Archetype::itk_component + * method. Removes an existing component widget from a mega-widget, + * and removes any configuration options associated with it. + * + * itk_component delete ? ...? + * + * Returns TCL_OK/TCL_ERROR to indicate success/failure. + * ------------------------------------------------------------------------ + */ +/* ARGSUSED */ +static int +Itk_ArchCompDeleteCmd(dummy, interp, objc, objv) + ClientData dummy; /* unused */ + Tcl_Interp *interp; /* current interpreter */ + int objc; /* number of arguments */ + Tcl_Obj *CONST objv[]; /* argument objects */ +{ + int i; + char *token; + ItclClass *contextClass; + ItclObject *contextObj; + ArchInfo *info; + Tcl_HashEntry *entry; + Tcl_HashSearch place; + Itcl_ListElem *elem; + ArchComponent *archComp; + ArchOption *archOpt; + ArchOptionPart *optPart; + + /* + * Get the Archetype info associated with this widget. + */ + if (Itcl_GetContext(interp, &contextClass, &contextObj) != TCL_OK || + !contextObj) { + + Tcl_ResetResult(interp); + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "cannot access components without an object context", + (char*)NULL); + return TCL_ERROR; + } + if (Itk_GetArchInfo(interp, contextObj, &info) != TCL_OK) { + return TCL_ERROR; + } + + /* + * Scan through the list of component names and delete each + * one. Make sure that each component exists. + */ + for (i=1; i < objc; i++) { + token = Tcl_GetStringFromObj(objv[i], (int*)NULL); + entry = Tcl_FindHashEntry(&info->components, token); + if (!entry) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "name \"", token, "\" is not a component", + (char*)NULL); + return TCL_ERROR; + } + archComp = (ArchComponent*)Tcl_GetHashValue(entry); + Tcl_DeleteHashEntry(entry); + + entry = Tcl_FirstHashEntry(&info->options, &place); + while (entry) { + archOpt = (ArchOption*)Tcl_GetHashValue(entry); + elem = Itcl_FirstListElem(&archOpt->parts); + while (elem) { + optPart = (ArchOptionPart*)Itcl_GetListValue(elem); + if (optPart->from == (ClientData)archComp) { + Itk_DelOptionPart(optPart); + elem = Itcl_DeleteListElem(elem); + } + else { + elem = Itcl_NextListElem(elem); + } + } + entry = Tcl_NextHashEntry(&place); + } + + Itk_DelArchComponent(archComp); + } + return TCL_OK; +} + + +/* + * ------------------------------------------------------------------------ + * Itk_ArchOptKeepCmd() + * + * Invoked by [incr Tcl] to handle the "keep" command in the itk + * option parser. Integrates a list of component configuration options + * into a mega-widget, so that whenever the mega-widget is updated, + * the component will be updated as well. + * + * Handles the following syntax: + * + * keep