You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
834 lines
24 KiB
834 lines
24 KiB
2 years ago
|
/*=========================================================================
|
||
|
|
||
|
Program: Visualization Toolkit
|
||
|
Module: $RCSfile: vtkTclUtil.cxx,v $
|
||
|
|
||
|
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
|
||
|
All rights reserved.
|
||
|
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
|
||
|
|
||
|
This software is distributed WITHOUT ANY WARRANTY; without even
|
||
|
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||
|
PURPOSE. See the above copyright notice for more information.
|
||
|
|
||
|
=========================================================================*/
|
||
|
|
||
|
#include "vtkObject.h"
|
||
|
#include "vtkTclUtil.h"
|
||
|
#include "vtkSetGet.h"
|
||
|
#include "vtkCallbackCommand.h"
|
||
|
|
||
|
#include <vtkstd/string>
|
||
|
#include <vtksys/SystemTools.hxx>
|
||
|
|
||
|
extern "C"
|
||
|
{
|
||
|
#if (TCL_MAJOR_VERSION == 8) && (TCL_MINOR_VERSION >= 4 && TCL_RELEASE_LEVEL >= TCL_FINAL_RELEASE)
|
||
|
typedef int (*vtkTclCommandType)(ClientData, Tcl_Interp *,int, CONST84 char *[]);
|
||
|
#else
|
||
|
typedef int (*vtkTclCommandType)(ClientData, Tcl_Interp *,int, char *[]);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
vtkTclInterpStruct *vtkGetInterpStruct(Tcl_Interp *interp)
|
||
|
{
|
||
|
vtkTclInterpStruct *is = (vtkTclInterpStruct *)Tcl_GetAssocData(interp,(char *) "vtk",NULL);
|
||
|
if (!is)
|
||
|
{
|
||
|
vtkGenericWarningMacro("unable to find interp struct");
|
||
|
}
|
||
|
return is;
|
||
|
}
|
||
|
|
||
|
VTKTCL_EXPORT int vtkTclInDelete(Tcl_Interp *interp)
|
||
|
{
|
||
|
vtkTclInterpStruct *is = vtkGetInterpStruct(interp);
|
||
|
if (is)
|
||
|
{
|
||
|
return is->InDelete;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
// just another way into DeleteCommand
|
||
|
VTKTCL_EXPORT void vtkTclDeleteObjectFromHash(vtkObject *obj,
|
||
|
unsigned long vtkNotUsed(eventId),
|
||
|
void *cd, void *)
|
||
|
{
|
||
|
vtkTclCommandArgStruct *as = (vtkTclCommandArgStruct *)cd;
|
||
|
char temps[80];
|
||
|
Tcl_HashEntry *entry;
|
||
|
char *temp;
|
||
|
vtkTclInterpStruct *is = vtkGetInterpStruct(as->Interp);
|
||
|
|
||
|
// lookup the objects name
|
||
|
sprintf(temps,"%p",obj);
|
||
|
entry = Tcl_FindHashEntry(&is->PointerLookup,temps);
|
||
|
if (entry)
|
||
|
{
|
||
|
temp = (char *)(Tcl_GetHashValue(entry));
|
||
|
if (temp)
|
||
|
{
|
||
|
Tcl_DeleteCommand(as->Interp,temp);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// we do no error checking in this. We assume that if we were called
|
||
|
// then tcl must have been able to find the command function and object.
|
||
|
VTKTCL_EXPORT void vtkTclGenericDeleteObject(ClientData cd)
|
||
|
{
|
||
|
char temps[80];
|
||
|
Tcl_HashEntry *entry;
|
||
|
int (*command)(ClientData, Tcl_Interp *,int, char *[]);
|
||
|
char *args[2];
|
||
|
char *temp;
|
||
|
vtkObject *tobject;
|
||
|
int error;
|
||
|
vtkTclCommandArgStruct *as = (vtkTclCommandArgStruct *)cd;
|
||
|
Tcl_Interp *interp = as->Interp;
|
||
|
vtkTclInterpStruct *is = vtkGetInterpStruct(interp);
|
||
|
|
||
|
/* set up the args */
|
||
|
args[1] = (char *) "Delete";
|
||
|
|
||
|
// lookup the objects name
|
||
|
sprintf(temps,"%p",as->Pointer);
|
||
|
entry = Tcl_FindHashEntry(&is->PointerLookup,temps);
|
||
|
if (!entry)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
temp = (char *)(Tcl_GetHashValue(entry));
|
||
|
args[0] = temp;
|
||
|
|
||
|
// first we clear the delete callback since we will
|
||
|
// always remove this object from the hash regardless
|
||
|
// of if it has really been freed.
|
||
|
tobject = (vtkObject *)vtkTclGetPointerFromObject(temp,"vtkObject",
|
||
|
interp, error);
|
||
|
tobject->RemoveObserver(as->Tag);
|
||
|
as->Tag = 0;
|
||
|
|
||
|
// get the command function and invoke the delete operation
|
||
|
entry = Tcl_FindHashEntry(&is->CommandLookup,temp);
|
||
|
command = (int (*)(ClientData,Tcl_Interp *,int,char *[]))
|
||
|
Tcl_GetHashValue(entry);
|
||
|
|
||
|
// do we need to delete the c++ obj
|
||
|
if (strncmp(temp,"vtkTemp",7))
|
||
|
{
|
||
|
is->InDelete = 1;
|
||
|
command(cd,interp,2,args);
|
||
|
is->InDelete = 0;
|
||
|
}
|
||
|
|
||
|
// the actual C++ object may not be freed yet. So we
|
||
|
// force it to be free from the hash table.
|
||
|
Tcl_DeleteHashEntry(entry);
|
||
|
entry = Tcl_FindHashEntry(&is->PointerLookup,temps);
|
||
|
Tcl_DeleteHashEntry(entry);
|
||
|
entry = Tcl_FindHashEntry(&is->InstanceLookup,temp);
|
||
|
Tcl_DeleteHashEntry(entry);
|
||
|
delete as;
|
||
|
|
||
|
if (is->DebugOn)
|
||
|
{
|
||
|
vtkGenericWarningMacro("vtkTcl Attempting to free object named " << temp);
|
||
|
}
|
||
|
if (temp)
|
||
|
{
|
||
|
free(temp);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int vtkCreateCommand(ClientData vtkNotUsed(cd), Tcl_Interp *interp, int argc, char *argv[])
|
||
|
{
|
||
|
Tcl_HashEntry *entry;
|
||
|
Tcl_HashSearch search;
|
||
|
char * tmp;
|
||
|
vtkTclInterpStruct *is = vtkGetInterpStruct(interp);
|
||
|
|
||
|
if (argc < 2)
|
||
|
{
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
|
||
|
if (!strcmp(argv[1],"DeleteAllObjects"))
|
||
|
{
|
||
|
for (entry = Tcl_FirstHashEntry(&is->PointerLookup,&search);
|
||
|
entry != NULL;
|
||
|
entry = Tcl_FirstHashEntry(&is->PointerLookup,&search))
|
||
|
{
|
||
|
tmp = strdup((char *)Tcl_GetHashValue(entry));
|
||
|
if (tmp)
|
||
|
{
|
||
|
Tcl_DeleteCommand(interp,tmp);
|
||
|
}
|
||
|
if (tmp)
|
||
|
{
|
||
|
free(tmp);
|
||
|
}
|
||
|
}
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
if (!strcmp(argv[1],"ListAllInstances"))
|
||
|
{
|
||
|
for (entry = Tcl_FirstHashEntry(&is->InstanceLookup,&search);
|
||
|
entry != NULL; entry = Tcl_NextHashEntry(&search))
|
||
|
{
|
||
|
Tcl_AppendResult(interp,
|
||
|
(char *)Tcl_GetHashKey(&is->InstanceLookup,entry),NULL);
|
||
|
Tcl_AppendResult(interp,"\n",NULL);
|
||
|
}
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
if (!strcmp(argv[1],"DebugOn"))
|
||
|
{
|
||
|
is->DebugOn = 1;
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
if (!strcmp(argv[1],"DebugOff"))
|
||
|
{
|
||
|
is->DebugOn = 0;
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
if (!strcmp(argv[1],"DeleteExistingObjectOnNewOn"))
|
||
|
{
|
||
|
is->DeleteExistingObjectOnNew = 1;
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
if (!strcmp(argv[1],"DeleteExistingObjectOnNewOff"))
|
||
|
{
|
||
|
is->DeleteExistingObjectOnNew = 0;
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
if (!strcmp("ListMethods",argv[1]))
|
||
|
{
|
||
|
Tcl_AppendResult(interp,"Methods for vtkCommand:\n",NULL);
|
||
|
Tcl_AppendResult(interp," DebugOn\n",NULL);
|
||
|
Tcl_AppendResult(interp," DebugOff\n",NULL);
|
||
|
Tcl_AppendResult(interp," DeleteAllObjects\n",NULL);
|
||
|
Tcl_AppendResult(interp," ListAllInstances\n",NULL);
|
||
|
Tcl_AppendResult(interp," DeleteExistingObjectOnNewOn\n",NULL);
|
||
|
Tcl_AppendResult(interp," DeleteExistingObjectOnNewOff\n",NULL);
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
|
||
|
Tcl_AppendResult(interp,"invalid method for vtkCommand\n",NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
|
||
|
VTKTCL_EXPORT void
|
||
|
vtkTclUpdateCommand(Tcl_Interp *interp, char *name, vtkObject *temp)
|
||
|
{
|
||
|
Tcl_CmdProc *command = NULL;
|
||
|
|
||
|
// check to see if we can find the command function based on class name
|
||
|
Tcl_CmdInfo cinf;
|
||
|
char *tstr = strdup(temp->GetClassName());
|
||
|
if (Tcl_GetCommandInfo(interp,tstr,&cinf))
|
||
|
{
|
||
|
if (cinf.clientData)
|
||
|
{
|
||
|
vtkTclCommandStruct *cs = (vtkTclCommandStruct *)cinf.clientData;
|
||
|
command = (Tcl_CmdProc *)cs->CommandFunction;
|
||
|
}
|
||
|
}
|
||
|
if (tstr)
|
||
|
{
|
||
|
free(tstr);
|
||
|
}
|
||
|
|
||
|
// if not found then just return
|
||
|
if (!command)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// is the current command the same
|
||
|
Tcl_CmdInfo cinfo;
|
||
|
Tcl_GetCommandInfo(interp, name, &cinfo);
|
||
|
cinfo.proc = command;
|
||
|
Tcl_SetCommandInfo(interp, name, &cinfo);
|
||
|
|
||
|
vtkTclInterpStruct *is = vtkGetInterpStruct(interp);
|
||
|
Tcl_HashEntry *entry = Tcl_FindHashEntry(&is->CommandLookup,name);
|
||
|
Tcl_SetHashValue(entry,(ClientData)command);
|
||
|
}
|
||
|
|
||
|
|
||
|
VTKTCL_EXPORT void
|
||
|
vtkTclGetObjectFromPointer(Tcl_Interp *interp, void *temp1,
|
||
|
const char *targetType)
|
||
|
{
|
||
|
int (*command)(ClientData, Tcl_Interp *,int, char *[]) = 0;
|
||
|
int is_new;
|
||
|
vtkObject *temp = (vtkObject *)temp1;
|
||
|
char temps[80];
|
||
|
char name[80];
|
||
|
Tcl_HashEntry *entry;
|
||
|
vtkTclInterpStruct *is = vtkGetInterpStruct(interp);
|
||
|
|
||
|
/* if it is NULL then return empty string */
|
||
|
if (!temp)
|
||
|
{
|
||
|
Tcl_ResetResult(interp);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* return a pointer to a vtk Object */
|
||
|
if (is->DebugOn)
|
||
|
{
|
||
|
vtkGenericWarningMacro("Looking up name for vtk pointer: " << temp);
|
||
|
}
|
||
|
|
||
|
/* first we must look up the pointer to see if it already exists */
|
||
|
sprintf(temps,"%p",temp);
|
||
|
if ((entry = Tcl_FindHashEntry(&is->PointerLookup,temps)))
|
||
|
{
|
||
|
if (is->DebugOn)
|
||
|
{
|
||
|
vtkGenericWarningMacro("Found name: "
|
||
|
<< (char *)(Tcl_GetHashValue(entry))
|
||
|
<< " for vtk pointer: " << temp);
|
||
|
}
|
||
|
|
||
|
/* while we are at it store the name since it is required anyhow */
|
||
|
Tcl_SetResult(interp, (char *)(Tcl_GetHashValue(entry)), TCL_VOLATILE);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* we must create a new name if it isn't NULL */
|
||
|
sprintf(name,"vtkTemp%i",is->Number);
|
||
|
is->Number++;
|
||
|
|
||
|
if (is->DebugOn)
|
||
|
{
|
||
|
vtkGenericWarningMacro("Created name: " << name
|
||
|
<< " for vtk pointer: " << temp);
|
||
|
}
|
||
|
|
||
|
// check to see if we can find the command function based on class name
|
||
|
Tcl_CmdInfo cinf;
|
||
|
char *tstr = strdup(temp->GetClassName());
|
||
|
if (Tcl_GetCommandInfo(interp,tstr,&cinf))
|
||
|
{
|
||
|
if (cinf.clientData)
|
||
|
{
|
||
|
vtkTclCommandStruct *cs = (vtkTclCommandStruct *)cinf.clientData;
|
||
|
command = cs->CommandFunction;
|
||
|
}
|
||
|
}
|
||
|
// if the class command wasn;t found try the target return type command
|
||
|
if (!command && targetType)
|
||
|
{
|
||
|
if (tstr)
|
||
|
{
|
||
|
free(tstr);
|
||
|
}
|
||
|
tstr = strdup(targetType);
|
||
|
if (Tcl_GetCommandInfo(interp,tstr,&cinf))
|
||
|
{
|
||
|
if (cinf.clientData)
|
||
|
{
|
||
|
vtkTclCommandStruct *cs = (vtkTclCommandStruct *)cinf.clientData;
|
||
|
command = cs->CommandFunction;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// if we still do not havbe a match then try vtkObject
|
||
|
if (!command)
|
||
|
{
|
||
|
if (tstr)
|
||
|
{
|
||
|
free(tstr);
|
||
|
}
|
||
|
tstr = strdup("vtkObject");
|
||
|
if (Tcl_GetCommandInfo(interp,tstr,&cinf))
|
||
|
{
|
||
|
if (cinf.clientData)
|
||
|
{
|
||
|
vtkTclCommandStruct *cs = (vtkTclCommandStruct *)cinf.clientData;
|
||
|
command = cs->CommandFunction;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (tstr)
|
||
|
{
|
||
|
free(tstr);
|
||
|
}
|
||
|
|
||
|
entry = Tcl_CreateHashEntry(&is->InstanceLookup,name,&is_new);
|
||
|
Tcl_SetHashValue(entry,(ClientData)(temp));
|
||
|
entry = Tcl_CreateHashEntry(&is->PointerLookup,temps,&is_new);
|
||
|
Tcl_SetHashValue(entry,(ClientData)(strdup(name)));
|
||
|
vtkTclCommandArgStruct *as = new vtkTclCommandArgStruct;
|
||
|
as->Pointer = (void *)temp;
|
||
|
as->Interp = interp;
|
||
|
Tcl_CreateCommand(interp,name,
|
||
|
reinterpret_cast<vtkTclCommandType>(command),
|
||
|
(ClientData)(as),
|
||
|
(Tcl_CmdDeleteProc *)vtkTclGenericDeleteObject);
|
||
|
entry = Tcl_CreateHashEntry(&is->CommandLookup,name,&is_new);
|
||
|
Tcl_SetHashValue(entry,(ClientData)command);
|
||
|
|
||
|
// setup the delete callback
|
||
|
vtkCallbackCommand *cbc = vtkCallbackCommand::New();
|
||
|
cbc->SetCallback(vtkTclDeleteObjectFromHash);
|
||
|
cbc->SetClientData((void *)as);
|
||
|
as->Tag = temp->AddObserver(vtkCommand::DeleteEvent, cbc);
|
||
|
cbc->Delete();
|
||
|
|
||
|
Tcl_SetResult(interp, (char *)name, TCL_VOLATILE);
|
||
|
}
|
||
|
|
||
|
VTKTCL_EXPORT void *vtkTclGetPointerFromObject(const char *name,
|
||
|
const char *result_type,
|
||
|
Tcl_Interp *interp,
|
||
|
int &error)
|
||
|
{
|
||
|
Tcl_HashEntry *entry;
|
||
|
ClientData temp;
|
||
|
int (*command)(ClientData, Tcl_Interp *,int, char *[]);
|
||
|
char *args[3];
|
||
|
char temps[256];
|
||
|
vtkTclInterpStruct *is = vtkGetInterpStruct(interp);
|
||
|
|
||
|
/* check for empty string, empty string is the same as passing NULL */
|
||
|
if (name[0] == '\0')
|
||
|
{
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// object names cannot start with a number
|
||
|
if ((name[0] >= '0')&&(name[0] <= '9'))
|
||
|
{
|
||
|
error = 1;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if ((entry = Tcl_FindHashEntry(&is->InstanceLookup,name)))
|
||
|
{
|
||
|
temp = (ClientData)Tcl_GetHashValue(entry);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sprintf(temps,"vtk bad argument, could not find object named %s\n", name);
|
||
|
Tcl_AppendResult(interp,temps,NULL);
|
||
|
error = 1;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* now handle the typecasting, get the command proc */
|
||
|
if ((entry = Tcl_FindHashEntry(&is->CommandLookup,name)))
|
||
|
{
|
||
|
command = (int (*)(ClientData,Tcl_Interp *,int,char *[]))Tcl_GetHashValue(entry);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sprintf(temps,"vtk bad argument, could not find command process for %s.\n", name);
|
||
|
Tcl_AppendResult(interp,temps,NULL);
|
||
|
error = 1;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/* set up the args */
|
||
|
args[0] = (char *) "DoTypecasting";
|
||
|
args[1] = strdup(result_type);
|
||
|
args[2] = NULL;
|
||
|
vtkTclCommandArgStruct foo;
|
||
|
foo.Pointer = temp;
|
||
|
foo.Interp = interp;
|
||
|
if (command((ClientData)&foo,(Tcl_Interp *)NULL,3,args) == TCL_OK)
|
||
|
{
|
||
|
free (args[1]);
|
||
|
return (void *)(args[2]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Tcl_Interp *i;
|
||
|
i = Tcl_CreateInterp();
|
||
|
// provide more diagnostic info
|
||
|
args[0] = (char *) "Dummy";
|
||
|
free (args[1]);
|
||
|
args[1] = (char *) "GetClassName";
|
||
|
args[2] = NULL;
|
||
|
command((ClientData)&foo,i,2,args);
|
||
|
|
||
|
sprintf(temps,"vtk bad argument, type conversion failed for object %s.\nCould not type convert %s which is of type %s, to type %s.\n", name, name, i->result, result_type);
|
||
|
Tcl_AppendResult(interp,temps,NULL);
|
||
|
error = 1;
|
||
|
Tcl_DeleteInterp(i);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
VTKTCL_EXPORT void vtkTclVoidFunc(void *arg)
|
||
|
{
|
||
|
int res;
|
||
|
|
||
|
vtkTclVoidFuncArg *arg2;
|
||
|
|
||
|
arg2 = (vtkTclVoidFuncArg *)arg;
|
||
|
|
||
|
#if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION <= 2
|
||
|
res = Tcl_GlobalEval(arg2->interp, arg2->command);
|
||
|
#else
|
||
|
res = Tcl_EvalEx(arg2->interp, arg2->command, -1, TCL_EVAL_GLOBAL);
|
||
|
#endif
|
||
|
|
||
|
if (res == TCL_ERROR)
|
||
|
{
|
||
|
if (Tcl_GetVar(arg2->interp,(char *) "errorInfo",0))
|
||
|
{
|
||
|
vtkGenericWarningMacro("Error returned from vtk/tcl callback:\n" <<
|
||
|
arg2->command << endl <<
|
||
|
Tcl_GetVar(arg2->interp,(char *) "errorInfo",0) <<
|
||
|
" at line number " << arg2->interp->errorLine);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vtkGenericWarningMacro("Error returned from vtk/tcl callback:\n" <<
|
||
|
arg2->command << endl <<
|
||
|
" at line number " << arg2->interp->errorLine);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
VTKTCL_EXPORT void vtkTclVoidFuncArgDelete(void *arg)
|
||
|
{
|
||
|
vtkTclVoidFuncArg *arg2;
|
||
|
|
||
|
arg2 = (vtkTclVoidFuncArg *)arg;
|
||
|
|
||
|
// free the string and then structure
|
||
|
delete [] arg2->command;
|
||
|
delete arg2;
|
||
|
}
|
||
|
|
||
|
VTKTCL_EXPORT void vtkTclListInstances(Tcl_Interp *interp, ClientData arg)
|
||
|
{
|
||
|
Tcl_HashSearch srch;
|
||
|
Tcl_HashEntry *entry;
|
||
|
int first = 1;
|
||
|
vtkTclInterpStruct *is = vtkGetInterpStruct(interp);
|
||
|
|
||
|
// iteratively search hash table for command function
|
||
|
entry = Tcl_FirstHashEntry(&is->CommandLookup, &srch);
|
||
|
if (!entry)
|
||
|
{
|
||
|
Tcl_ResetResult(interp);
|
||
|
return;
|
||
|
}
|
||
|
while (entry)
|
||
|
{
|
||
|
if (Tcl_GetHashValue(entry) == arg)
|
||
|
{
|
||
|
if (first)
|
||
|
{
|
||
|
first = 0;
|
||
|
Tcl_AppendResult(interp,Tcl_GetHashKey(&is->CommandLookup,entry),NULL);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Tcl_AppendResult(interp, " ", Tcl_GetHashKey(&is->CommandLookup,entry),
|
||
|
NULL);
|
||
|
}
|
||
|
}
|
||
|
entry = Tcl_NextHashEntry(&srch);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
int vtkTclNewInstanceCommand(ClientData cd, Tcl_Interp *interp,
|
||
|
int argc, char *argv[])
|
||
|
{
|
||
|
int (*command)(ClientData, Tcl_Interp *,int, char *[]);
|
||
|
Tcl_HashEntry *entry;
|
||
|
int is_new;
|
||
|
char temps[80];
|
||
|
char name[80];
|
||
|
vtkTclCommandStruct *cs = (vtkTclCommandStruct *)cd;
|
||
|
Tcl_CmdInfo cinf;
|
||
|
vtkTclInterpStruct *is = vtkGetInterpStruct(interp);
|
||
|
|
||
|
if (argc != 2)
|
||
|
{
|
||
|
Tcl_SetResult(interp, (char *) "vtk object creation requires one argument, a name, or the special New keyword to instantiate a new name.", TCL_VOLATILE);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
|
||
|
if ((argv[1][0] >= '0')&&(argv[1][0] <= '9'))
|
||
|
{
|
||
|
Tcl_SetResult(interp, argv[1], TCL_VOLATILE);
|
||
|
Tcl_AppendResult(interp, ": vtk object cannot start with a numeric.", NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
|
||
|
if (Tcl_FindHashEntry(&is->InstanceLookup,argv[1]))
|
||
|
{
|
||
|
if (is->DeleteExistingObjectOnNew)
|
||
|
{
|
||
|
Tcl_DeleteCommand(interp, argv[1]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
Tcl_SetResult(interp, argv[1], TCL_VOLATILE);
|
||
|
Tcl_AppendResult(interp,
|
||
|
": a vtk object with that name already exists.",
|
||
|
NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Make sure we are not clobbering a built in command
|
||
|
if (Tcl_GetCommandInfo(interp,argv[1],&cinf))
|
||
|
{
|
||
|
Tcl_SetResult(interp, argv[1], TCL_VOLATILE);
|
||
|
Tcl_AppendResult(interp,
|
||
|
": a tcl/tk command with that name already exists.",
|
||
|
NULL);
|
||
|
return TCL_ERROR;
|
||
|
}
|
||
|
|
||
|
ClientData temp;
|
||
|
if (!strcmp("ListInstances",argv[1]))
|
||
|
{
|
||
|
vtkTclListInstances(interp,(ClientData) cs->CommandFunction);
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
|
||
|
if (!strcmp("New",argv[1]))
|
||
|
{
|
||
|
sprintf(name,"vtkObj%i",is->Number);
|
||
|
is->Number++;
|
||
|
argv[1] = name;
|
||
|
}
|
||
|
|
||
|
temp = cs->NewCommand();
|
||
|
|
||
|
entry = Tcl_CreateHashEntry(&is->InstanceLookup,argv[1],&is_new);
|
||
|
Tcl_SetHashValue(entry,temp);
|
||
|
sprintf(temps,"%p",(void *)temp);
|
||
|
entry = Tcl_CreateHashEntry(&is->PointerLookup,temps,&is_new);
|
||
|
Tcl_SetHashValue(entry,(ClientData)(strdup(argv[1])));
|
||
|
|
||
|
// check to see if we can find the command function based on class name
|
||
|
char *tstr = strdup(((vtkObject *)temp)->GetClassName());
|
||
|
if (Tcl_GetCommandInfo(interp,tstr,&cinf))
|
||
|
{
|
||
|
if (cinf.clientData)
|
||
|
{
|
||
|
vtkTclCommandStruct *cs2 = (vtkTclCommandStruct *)cinf.clientData;
|
||
|
command = cs2->CommandFunction;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
command = cs->CommandFunction;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
command = cs->CommandFunction;
|
||
|
}
|
||
|
if (tstr)
|
||
|
{
|
||
|
free(tstr);
|
||
|
}
|
||
|
|
||
|
vtkTclCommandArgStruct *as = new vtkTclCommandArgStruct;
|
||
|
as->Pointer = (void *)temp;
|
||
|
as->Interp = interp;
|
||
|
Tcl_CreateCommand(interp,argv[1],
|
||
|
reinterpret_cast<vtkTclCommandType>(command),
|
||
|
(ClientData)as,
|
||
|
(Tcl_CmdDeleteProc *)vtkTclGenericDeleteObject);
|
||
|
entry = Tcl_CreateHashEntry(&is->CommandLookup,argv[1],&is_new);
|
||
|
Tcl_SetHashValue(entry,(ClientData)(cs->CommandFunction));
|
||
|
|
||
|
// setup the delete callback
|
||
|
vtkCallbackCommand *cbc = vtkCallbackCommand::New();
|
||
|
cbc->SetCallback(vtkTclDeleteObjectFromHash);
|
||
|
cbc->SetClientData((void *)as);
|
||
|
as->Tag = ((vtkObject *)temp)->AddObserver(vtkCommand::DeleteEvent, cbc);
|
||
|
cbc->Delete();
|
||
|
|
||
|
Tcl_SetResult(interp, argv[1], TCL_VOLATILE);
|
||
|
return TCL_OK;
|
||
|
}
|
||
|
|
||
|
void vtkTclDeleteCommandStruct(ClientData cd)
|
||
|
{
|
||
|
vtkTclCommandStruct *cs = (vtkTclCommandStruct *)cd;
|
||
|
delete cs;
|
||
|
}
|
||
|
|
||
|
void vtkTclCreateNew(Tcl_Interp *interp, const char *cname,
|
||
|
ClientData (*NewCommand)(),
|
||
|
int (*CommandFunction)(ClientData cd,
|
||
|
Tcl_Interp *interp,
|
||
|
int argc, char *argv[]))
|
||
|
{
|
||
|
vtkTclCommandStruct *cs = new vtkTclCommandStruct;
|
||
|
cs->NewCommand = NewCommand;
|
||
|
cs->CommandFunction = CommandFunction;
|
||
|
Tcl_CreateCommand(interp,(char *) cname,
|
||
|
reinterpret_cast<vtkTclCommandType>(
|
||
|
vtkTclNewInstanceCommand),
|
||
|
(ClientData *)cs,
|
||
|
(Tcl_CmdDeleteProc *)vtkTclDeleteCommandStruct);
|
||
|
}
|
||
|
|
||
|
|
||
|
vtkTclCommand::vtkTclCommand()
|
||
|
{
|
||
|
this->Interp = NULL;
|
||
|
this->StringCommand = NULL;
|
||
|
}
|
||
|
|
||
|
vtkTclCommand::~vtkTclCommand()
|
||
|
{
|
||
|
if(this->StringCommand) { delete [] this->StringCommand; }
|
||
|
}
|
||
|
|
||
|
void vtkTclCommand::SetStringCommand(const char *arg)
|
||
|
{
|
||
|
if(this->StringCommand) { delete [] this->StringCommand; }
|
||
|
this->StringCommand = new char[strlen(arg)+1];
|
||
|
strcpy(this->StringCommand, arg);
|
||
|
}
|
||
|
|
||
|
void vtkTclCommand::Execute(vtkObject *, unsigned long, void *)
|
||
|
{
|
||
|
int res;
|
||
|
#if TCL_MAJOR_VERSION == 8 && TCL_MINOR_VERSION <= 2
|
||
|
res = Tcl_GlobalEval(this->Interp, this->StringCommand);
|
||
|
#else
|
||
|
res = Tcl_EvalEx(this->Interp, this->StringCommand, -1, TCL_EVAL_GLOBAL);
|
||
|
#endif
|
||
|
|
||
|
if (res == TCL_ERROR)
|
||
|
{
|
||
|
if (Tcl_GetVar(this->Interp,(char *) "errorInfo",0))
|
||
|
{
|
||
|
vtkGenericWarningMacro("Error returned from vtk/tcl callback:\n" <<
|
||
|
this->StringCommand << endl <<
|
||
|
Tcl_GetVar(this->Interp,(char *) "errorInfo",0) <<
|
||
|
" at line number " << this->Interp->errorLine);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
vtkGenericWarningMacro("Error returned from vtk/tcl callback:\n" <<
|
||
|
this->StringCommand << endl <<
|
||
|
" at line number " <<
|
||
|
this->Interp->errorLine);
|
||
|
}
|
||
|
}
|
||
|
else if (res == -1)
|
||
|
{
|
||
|
this->AbortFlagOn();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void vtkTclApplicationInitExecutable(int vtkNotUsed(argc),
|
||
|
const char* const argv[])
|
||
|
{
|
||
|
vtkstd::string av0 = vtksys::SystemTools::CollapseFullPath(argv[0]);
|
||
|
Tcl_FindExecutable(av0.c_str());
|
||
|
}
|
||
|
|
||
|
// We need two internal Tcl functions. They usually are declared in
|
||
|
// tclIntDecls.h, but UNIX builds do not have access to VTK's
|
||
|
// tkInternals include path. Since the signature has not changed for
|
||
|
// years (at least since 8.2), let's just prototype them.
|
||
|
EXTERN Tcl_Obj* TclGetLibraryPath _ANSI_ARGS_((void));
|
||
|
EXTERN void TclSetLibraryPath _ANSI_ARGS_((Tcl_Obj * pathPtr));
|
||
|
|
||
|
void vtkTclApplicationInitTclTk(Tcl_Interp* interp,
|
||
|
const char* const relative_dirs[])
|
||
|
{
|
||
|
/*
|
||
|
Tcl/Tk requires support files to work (set of tcl files).
|
||
|
When an app is linked against Tcl/Tk shared libraries, the path to
|
||
|
the libraries is used by Tcl/Tk to search for its support files.
|
||
|
For example, on Windows, if bin/tcl84.dll is the shared lib, support
|
||
|
files will be searched in bin/../lib/tcl8.4, which is where they are
|
||
|
usually installed.
|
||
|
If an app is linked against Tcl/Tk *static* libraries, there is no
|
||
|
way for Tcl/Tk to find its support files. In that case, it will
|
||
|
use the TCL_LIBRARY and TK_LIBRARY environment variable (those should
|
||
|
point to the support files dir, ex: c:/tcl/lib/tcl8.4, c:/tk/lib/tcl8.4).
|
||
|
|
||
|
The above code will also make Tcl/Tk search inside VTK's build/install
|
||
|
directory, more precisely inside a TclTk/lib sub dir.
|
||
|
ex: [path to vtk.exe]/TclTk/lib/tcl8.4, [path to vtk.exe]/TclTk/lib/tk8.4
|
||
|
Support files are copied to that location when
|
||
|
VTK_TCL_TK_COPY_SUPPORT_LIBRARY is ON.
|
||
|
*/
|
||
|
|
||
|
int has_tcllibpath_env = getenv("TCL_LIBRARY") ? 1 : 0;
|
||
|
int has_tklibpath_env = getenv("TK_LIBRARY") ? 1 : 0;
|
||
|
vtkstd::string selfdir;
|
||
|
if(!has_tcllibpath_env || !has_tklibpath_env)
|
||
|
{
|
||
|
const char* nameofexec = Tcl_GetNameOfExecutable();
|
||
|
if(nameofexec && vtksys::SystemTools::FileExists(nameofexec))
|
||
|
{
|
||
|
vtkstd::string name = nameofexec;
|
||
|
vtksys::SystemTools::ConvertToUnixSlashes(name);
|
||
|
selfdir = vtksys::SystemTools::GetFilenamePath(name);
|
||
|
}
|
||
|
}
|
||
|
if(selfdir.length() > 0)
|
||
|
{
|
||
|
if(!has_tcllibpath_env)
|
||
|
{
|
||
|
vtkstd::string tdir;
|
||
|
for(const char* const* p = relative_dirs; *p; ++p)
|
||
|
{
|
||
|
tdir = selfdir;
|
||
|
tdir += "/";
|
||
|
tdir += *p;
|
||
|
tdir += "/tcl" TCL_VERSION;
|
||
|
tdir = vtksys::SystemTools::CollapseFullPath(tdir.c_str());
|
||
|
if(vtksys::SystemTools::FileExists(tdir.c_str()) &&
|
||
|
vtksys::SystemTools::FileIsDirectory(tdir.c_str()))
|
||
|
{
|
||
|
// Set the tcl_library Tcl variable.
|
||
|
char tcl_library[1024];
|
||
|
strcpy(tcl_library, tdir.c_str());
|
||
|
Tcl_SetVar(interp, "tcl_library", tcl_library,
|
||
|
TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if(!has_tklibpath_env)
|
||
|
{
|
||
|
vtkstd::string tdir;
|
||
|
for(const char* const* p = relative_dirs; *p; ++p)
|
||
|
{
|
||
|
tdir = selfdir;
|
||
|
tdir += "/";
|
||
|
tdir += *p;
|
||
|
tdir += "/tk" TCL_VERSION;
|
||
|
tdir = vtksys::SystemTools::CollapseFullPath(tdir.c_str());
|
||
|
if(vtksys::SystemTools::FileExists(tdir.c_str()) &&
|
||
|
vtksys::SystemTools::FileIsDirectory(tdir.c_str()))
|
||
|
{
|
||
|
// Set the tk_library Tcl variable.
|
||
|
char tk_library[1024];
|
||
|
strcpy(tk_library, tdir.c_str());
|
||
|
Tcl_SetVar(interp, "tk_library", tk_library,
|
||
|
TCL_GLOBAL_ONLY | TCL_LEAVE_ERR_MSG);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|