summaryrefslogtreecommitdiff
path: root/tix/generic/tixMethod.c
diff options
context:
space:
mode:
Diffstat (limited to 'tix/generic/tixMethod.c')
-rw-r--r--tix/generic/tixMethod.c604
1 files changed, 604 insertions, 0 deletions
diff --git a/tix/generic/tixMethod.c b/tix/generic/tixMethod.c
new file mode 100644
index 00000000000..e09d5f371c8
--- /dev/null
+++ b/tix/generic/tixMethod.c
@@ -0,0 +1,604 @@
+/*
+ * tixMethod.c --
+ *
+ * Handle the calling of class methods.
+ *
+ * Implements the basic OOP class mechanism for the Tix Intrinsics.
+ *
+ * Copyright (c) 1996, Expert Interface Technologies
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ */
+
+
+/* ToDo:
+ *
+ * 1) Tix_CallMethod() needs to be re-written
+ *
+ */
+#include <tclInt.h>
+#include <tk.h>
+#include <tixPort.h>
+#include <tixInt.h>
+#include <tixItcl.h>
+
+#define GetMethodTable(interp) (_TixGetHashTable(interp, "tixMethodTab", MethodTableDeleteProc))
+
+static int Tix_CallMethodByContext _ANSI_ARGS_((
+ Tcl_Interp * interp, char * context,
+ char * widRec, char * method, int argc,
+ char ** argv));
+static void Tix_RestoreContext _ANSI_ARGS_((
+ Tcl_Interp * interp, char * widRec,
+ char * oldContext));
+static void Tix_SetContext _ANSI_ARGS_((
+ Tcl_Interp * interp, char * widRec,
+ char * newContext));
+static char * Tix_SaveContext _ANSI_ARGS_((Tcl_Interp * interp,
+ char * widRec));
+static void MethodTableDeleteProc _ANSI_ARGS_((
+ ClientData clientData, Tcl_Interp *interp));
+
+/*
+ *
+ * argv[1] = widget record
+ * argv[2] = method
+ * argv[3+] = args
+ *
+ */
+TIX_DEFINE_CMD(Tix_CallMethodCmd)
+{
+ char * context;
+ char * newContext;
+ char * widRec = argv[1];
+ char * method = argv[2];
+ int result;
+
+ if (argc<3) {
+ return Tix_ArgcError(interp, argc, argv, 1, "w method ...");
+ }
+
+ if ((context = GET_RECORD(interp, widRec, "className")) == NULL) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "invalid object reference \"", widRec,
+ "\"", (char*)NULL);
+ return TCL_ERROR;
+ }
+
+ newContext = Tix_FindMethod(interp, context, method);
+
+ if (newContext) {
+ result = Tix_CallMethodByContext(interp, newContext, widRec, method,
+ argc-3, argv+3);
+ } else {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "cannot call method \"", method,
+ "\" for context \"", context, "\".", (char*)NULL);
+ Tcl_SetVar(interp, "errorInfo", interp->result, TCL_GLOBAL_ONLY);
+ result = TCL_ERROR;
+ }
+
+ return result;
+}
+
+/*
+ *
+ * argv[1] = widget record
+ * argv[2] = method
+ * argv[3+] = args
+ *
+ */
+TIX_DEFINE_CMD(Tix_ChainMethodCmd)
+{
+ char * context;
+ char * superClassContext;
+ char * newContext;
+ char * widRec = argv[1];
+ char * method = argv[2];
+ int result;
+
+ if (argc<3) {
+ return Tix_ArgcError(interp, argc, argv, 1, "w method ...");
+ }
+
+ if ((context = Tix_GetContext(interp, widRec)) == NULL) {
+ return TCL_ERROR;
+ }
+
+ if (Tix_SuperClass(interp, context, &superClassContext) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ if (superClassContext == NULL) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "no superclass exists for context \"",
+ context, "\".", (char*)NULL);
+ result = TCL_ERROR;
+ goto done;
+ }
+
+ newContext = Tix_FindMethod(interp, superClassContext, method);
+
+ if (newContext) {
+ result = Tix_CallMethodByContext(interp, newContext, widRec,
+ method, argc-3, argv+3);
+ } else {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "cannot chain method \"", method,
+ "\" for context \"", context, "\".", (char*)NULL);
+ Tcl_SetVar(interp, "errorInfo", interp->result, TCL_GLOBAL_ONLY);
+ result = TCL_ERROR;
+ goto done;
+ }
+
+ done:
+ return result;
+}
+
+/*
+ *
+ * argv[1] = widget record
+ * argv[2] = class (context)
+ * argv[3] = method
+ *
+ */
+TIX_DEFINE_CMD(Tix_GetMethodCmd)
+{
+ char * newContext;
+ char * context= argv[2];
+ char * method = argv[3];
+ char * cmdName;
+
+ if (argc!=4) {
+ return Tix_ArgcError(interp, argc, argv, 1, "w class method");
+ }
+
+ newContext = Tix_FindMethod(interp, context, method);
+
+ if (newContext) {
+ cmdName = Tix_GetMethodFullName(newContext, method);
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, cmdName, NULL);
+ ckfree(cmdName);
+ } else {
+ Tcl_SetResult(interp, "", TCL_STATIC);
+ }
+
+ return TCL_OK;
+}
+
+/*----------------------------------------------------------------------
+ * Tix_FindMethod
+ *
+ * Starting with class "context", find the first class that defines
+ * the method. This class must be the same as the class "context" or
+ * a superclass of the class "context".
+ */
+char *
+Tix_FindMethod(interp, context, method)
+ Tcl_Interp * interp;
+ char * context;
+ char * method;
+{
+ char * theContext;
+ int isNew;
+ char * key;
+ Tcl_HashEntry *hashPtr;
+
+ key = Tix_GetMethodFullName(context, method);
+ hashPtr = Tcl_CreateHashEntry(GetMethodTable(interp), key, &isNew);
+ ckfree(key);
+
+ if (!isNew) {
+ theContext = (char *) Tcl_GetHashValue(hashPtr);
+ } else {
+ for (theContext = context; theContext;) {
+ if (Tix_ExistMethod(interp, theContext, method)) {
+ break;
+ }
+ /* Go to its superclass and see if it has the method */
+ if (Tix_SuperClass(interp, theContext, &theContext) != TCL_OK) {
+ return NULL;
+ }
+ if (theContext == NULL) {
+ return NULL;
+ }
+ }
+
+ if (theContext != NULL) {
+ /*
+ * theContext may point to the stack. We have to put it
+ * in some more permanent place.
+ */
+ theContext = (char*)tixStrDup(theContext);
+ }
+ Tcl_SetHashValue(hashPtr, (char*)theContext);
+ }
+
+ return theContext;
+}
+
+/*----------------------------------------------------------------------
+ * Tix_CallMethod
+ *
+ * Starting with class "context", find the first class that defines
+ * the method. Call this method.
+ */
+int Tix_CallMethod(interp, context, widRec, method, argc, argv)
+ Tcl_Interp * interp;
+ char * context;
+ char * widRec;
+ char * method;
+ int argc;
+ char ** argv;
+{
+ char * targetContext;
+
+ targetContext = Tix_FindMethod(interp, context, method);
+ if (targetContext != NULL) {
+ return Tix_CallMethodByContext(interp, targetContext, widRec, method,
+ argc, argv);
+ }
+ else {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "cannot call method \"", method,
+ "\" for context \"", context, "\".", (char*)NULL);
+ Tcl_SetVar(interp, "errorInfo", interp->result, TCL_GLOBAL_ONLY);
+ return TCL_ERROR;
+ }
+}
+
+/*----------------------------------------------------------------------
+ * Tix_FindConfigSpec
+ *
+ * Starting with class "classRec", find the first class that defines
+ * the option flag. This class must be the same as the class "classRec" or
+ * a superclass of the class "classRec".
+ */
+
+/* save the old context: calling a method of a superclass will
+ * change the context of a widget.
+ */
+static char * Tix_SaveContext(interp, widRec)
+ Tcl_Interp * interp;
+ char * widRec;
+{
+ char * context;
+
+ if ((context = GET_RECORD(interp, widRec, "context")) == NULL) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "invalid object reference \"", widRec,
+ "\"", (char*)NULL);
+ return NULL;
+ }
+ else {
+ return (char*)tixStrDup(context);
+ }
+}
+
+static void Tix_RestoreContext(interp, widRec, oldContext)
+ Tcl_Interp * interp;
+ char * widRec;
+ char * oldContext;
+{
+ SET_RECORD(interp, widRec, "context", oldContext);
+ ckfree(oldContext);
+}
+
+static void Tix_SetContext(interp, widRec, newContext)
+ Tcl_Interp * interp;
+ char * widRec;
+ char * newContext;
+{
+ SET_RECORD(interp, widRec, "context", newContext);
+}
+
+
+char * Tix_GetContext(interp, widRec)
+ Tcl_Interp * interp;
+ char * widRec;
+{
+ char * context;
+
+ if ((context = GET_RECORD(interp, widRec, "context")) == NULL) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "invalid object reference \"", widRec,
+ "\"", (char*)NULL);
+ return NULL;
+ } else {
+ return context;
+ }
+}
+
+int Tix_SuperClass(interp, class, superClass_ret)
+ Tcl_Interp * interp;
+ char * class;
+ char ** superClass_ret;
+{
+ char * superclass;
+
+ if ((superclass = GET_RECORD(interp, class, "superClass")) == NULL) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "invalid class \"", class,
+ "\"; ", (char*)NULL);
+ return TCL_ERROR;
+ }
+
+ if (strlen(superclass) == 0) {
+ *superClass_ret = (char*) NULL;
+ } else {
+ *superClass_ret = superclass;
+ }
+
+ return TCL_OK;
+}
+
+char * Tix_GetMethodFullName(context, method)
+ char * context;
+ char * method;
+{
+ char * buff;
+ int max;
+ int conLen;
+
+ conLen = strlen(context);
+ max = conLen + strlen(method) + 3;
+ buff = (char*)ckalloc(max * sizeof(char));
+
+ strcpy(buff, context);
+ strcpy(buff+conLen, ":");
+ strcpy(buff+conLen+1, method);
+
+ return buff;
+}
+
+#undef ITCL_2
+
+#if !defined(ITCL_2) && !defined(TK_8_0_OR_LATER)
+
+#define Tix_GetCommandInfo Tcl_GetCommandInfo
+
+#else
+/*
+ *----------------------------------------------------------------------
+ *
+ * Tix_GetCommandInfo --
+ *
+ * Returns various information about a Tcl command. Modified from
+ * Tcl_GetCommandInfo to work with ITcl 2.0. Always work in the global
+ * name space.
+ *
+ * Results:
+ * If cmdName exists in interp, then *infoPtr is modified to
+ * hold information about cmdName and 1 is returned. If the
+ * command doesn't exist then 0 is returned and *infoPtr isn't
+ * modified.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+int Tix_GetCommandInfo(interp, cmdName, infoPtr)
+ Tcl_Interp *interp;
+ char *cmdName;
+ Tcl_CmdInfo *infoPtr;
+{
+ register Interp *iPtr = (Interp *) interp;
+ int result;
+ DECLARE_ITCL_NAMESP(nameSp, interp);
+
+ result = TixItclSetGlobalNameSp(&nameSp, interp);
+
+ if (result != 0) {
+ result = Tcl_GetCommandInfo(interp, cmdName, infoPtr);
+ }
+
+ TixItclRestoreGlobalNameSp(&nameSp, interp);
+ return result;
+}
+#endif
+
+int Tix_ExistMethod(interp, context, method)
+ Tcl_Interp * interp;
+ char * context;
+ char * method;
+{
+ char * cmdName;
+ Tcl_CmdInfo dummy;
+ int exist;
+
+ cmdName = Tix_GetMethodFullName(context, method);
+ exist = Tix_GetCommandInfo(interp, cmdName, &dummy);
+
+ if (!exist) {
+ if (Tix_GlobalVarEval(interp, "auto_load ", cmdName,
+ (char*)NULL)!= TCL_OK) {
+ goto done;
+ }
+ if (strcmp(interp->result, "1") == 0) {
+ exist = 1;
+ }
+ }
+
+ done:
+ ckfree(cmdName);
+ Tcl_SetResult(interp, NULL, TCL_STATIC);
+ return exist;
+}
+
+/* %% There is a dirty version that uses the old argv, without having to
+ * malloc a new argv.
+ */
+static int Tix_CallMethodByContext(interp, context, widRec, method, argc, argv)
+ Tcl_Interp * interp;
+ char * context;
+ char * widRec;
+ char * method;
+ int argc;
+ char ** argv;
+{
+ char * cmdName;
+ int i, result;
+ char * oldContext;
+ char ** newArgv;
+
+ if ((oldContext = Tix_SaveContext(interp, widRec)) == NULL) {
+ return TCL_ERROR;
+ }
+ Tix_SetContext(interp, widRec, context);
+
+ cmdName = Tix_GetMethodFullName(context, method);
+
+ /* Create a new argv list */
+ newArgv = (char**)ckalloc((argc+2)*sizeof(char*));
+ newArgv[0] = cmdName;
+ newArgv[1] = widRec;
+ for (i=0; i< argc; i++) {
+ newArgv[i+2] = argv[i];
+ }
+ result = Tix_EvalArgv(interp, argc+2, newArgv);
+
+ Tix_RestoreContext(interp, widRec, oldContext);
+ ckfree((char*)newArgv);
+ ckfree(cmdName);
+
+ return result;
+}
+
+#ifndef ITCL_2
+
+#define Tix_GlobalEvalArgv(interp, cmdInfoPtr, argc, argv) \
+ (*(cmdInfoPtr)->proc)((cmdInfoPtr)->clientData, interp, argc, argv)
+
+#else
+
+EXTERN int Tix_GlobalEvalArgv _ANSI_ARGS_((Tcl_Interp * interp,
+ Tcl_CmdInfo * cmdInfoPtr, int argc));
+
+int
+Tix_GlobalEvalArgv(interp, cmdInfoPtr, argc, argv)
+ Tcl_Interp * interp;
+ Tcl_CmdInfo * cmdInfoPtr;
+ int argc;
+ char ** argv;
+{
+ register Interp *iPtr = (Interp *) interp;
+ int result;
+ CallFrame *savedVarFramePtr;
+ Itcl_ActiveNamespace nsToken;
+
+ savedVarFramePtr = iPtr->varFramePtr;
+ iPtr->varFramePtr = NULL;
+
+ nsToken = Itcl_ActivateNamesp(interp, (Itcl_Namespace)iPtr->globalNs);
+ if (nsToken == NULL) {
+ result = TCL_ERROR;
+ }
+ else {
+ result = (*cmdInfoPtr->proc)(cmdInfoPtr->clientData,interp,argc,argv);
+ Itcl_DeactivateNamesp(interp, nsToken);
+ }
+
+ iPtr->varFramePtr = savedVarFramePtr;
+ return result;
+}
+#endif /* ITCL_2 */
+
+int Tix_EvalArgv(interp, argc, argv)
+ Tcl_Interp * interp;
+ int argc;
+ char ** argv;
+{
+ Tcl_CmdInfo cmdInfo;
+
+ if (!Tix_GetCommandInfo(interp, argv[0], &cmdInfo)) {
+ char * cmdArgv[2];
+
+ /*
+ * This comand is not defined yet -- looks like we have to auto-load it
+ */
+ if (!Tix_GetCommandInfo(interp, "auto_load", &cmdInfo)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "cannot execute command \"auto_load\"",
+ NULL);
+ return TCL_ERROR;
+ }
+
+ cmdArgv[0] = "auto_load";
+ cmdArgv[1] = argv[0];
+
+ if ((*cmdInfo.proc)(cmdInfo.clientData, interp, 2, cmdArgv)!= TCL_OK){
+ return TCL_ERROR;
+ }
+
+ if (!Tix_GetCommandInfo(interp, argv[0], &cmdInfo)) {
+ Tcl_ResetResult(interp);
+ Tcl_AppendResult(interp, "cannot autoload command \"",
+ argv[0], "\"",NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ return Tix_GlobalEvalArgv(interp, &cmdInfo, argc, argv);
+}
+
+char *
+Tix_FindPublicMethod(interp, cPtr, method)
+ Tcl_Interp * interp;
+ TixClassRecord * cPtr;
+ char * method;
+{
+ int i;
+ int len = strlen(method);
+
+ for (i=0; i<cPtr->nMethods; i++) {
+ if (cPtr->methods[i][0] == method[0] &&
+ strncmp(cPtr->methods[i], method, len)==0) {
+ return cPtr->methods[i];
+ }
+ }
+ return 0;
+}
+
+/*
+ *----------------------------------------------------------------------
+ * MethodTableDeleteProc --
+ *
+ * This procedure is called when the interp is about to
+ * be deleted. It cleans up the hash entries and destroys the hash
+ * table.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * All class method contexts are deleted for this interpreter.
+ *----------------------------------------------------------------------
+ */
+
+static void
+MethodTableDeleteProc(clientData, interp)
+ ClientData clientData;
+ Tcl_Interp *interp;
+{
+ Tcl_HashTable * methodTablePtr = (Tcl_HashTable*)clientData;
+ Tcl_HashSearch hashSearch;
+ Tcl_HashEntry *hashPtr;
+ char * context;
+
+ for (hashPtr = Tcl_FirstHashEntry(methodTablePtr, &hashSearch);
+ hashPtr;
+ hashPtr = Tcl_NextHashEntry(&hashSearch)) {
+
+ context = (char*)Tcl_GetHashValue(hashPtr);
+ if (context) {
+ ckfree(context);
+ }
+ Tcl_DeleteHashEntry(hashPtr);
+ }
+ Tcl_DeleteHashTable(methodTablePtr);
+ ckfree((char*)methodTablePtr);
+}