/* * NodeScript.cpp * * Copyright (C) 1999 Stephen F. White * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program (see the file "COPYING" for details); if * not, write to the Free Software Foundation, Inc., 675 Mass Ave, * Cambridge, MA 02139, USA. */ #include #include "stdafx.h" #include "NodeScript.h" #include "Scene.h" #include "Proto.h" #include "FieldValue.h" #include "MFString.h" #include "SFBool.h" #include "ExposedField.h" #include "Field.h" #include "EventIn.h" #include "EventOut.h" #include "DuneApp.h" #include "RouteCommand.h" #include "UnRouteCommand.h" ProtoScript::ProtoScript(Scene *scene) : Proto(scene, "Script") { url.set( addExposedField(MFSTRING, "url", new MFString(), FF_HIDDEN | FF_URL, NULL)); directOutput.set( addField(SFBOOL, "directOutput", new SFBool(false), FF_HIDDEN, NULL)); mustEvaluate.set( addField(SFBOOL, "mustEvaluate", new SFBool(false), FF_HIDDEN, NULL)); } Node * ProtoScript::create(Scene *scene) { return new NodeScript(scene); } int NodeScript::getUrlField() { return url_Index(); } NodeScript::NodeScript(Scene *scene) : Node(scene, new ProtoScript(scene)) { _interface = new InterfaceArray; _routeList = NULL; } NodeScript::~NodeScript() { delete _proto; delete _interface; } int NodeScript::write(int f, int indent) { TheApp->checkSelectionLinenumberCounting(_scene, this); RET_ONERROR( indentf(f, indent) ) if (getFlag(NODE_FLAG_DEFED)) { RET_ONERROR( mywritestr(f ,"USE ") ) RET_ONERROR( mywritestr(f ,(const char *) _name) ) RET_ONERROR( mywritestr(f ,"\n") ) TheApp->incSelectionLinenumber(); } else { if (needsDEF()) { if (!_name[0]) _scene->generateUniqueNodeName(this); RET_ONERROR( mywritestr(f,"DEF ") ) RET_ONERROR( mywritestr(f, (const char *) _name) ) RET_ONERROR( mywritestr(f," ") ) } setFlag(NODE_FLAG_DEFED); RET_ONERROR( mywritestr(f, "Script ") ) if (!TheApp->GetkrFormating()) { RET_ONERROR( mywritestr(f, "\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( indentf(f, indent + TheApp->GetIndent()) ) } RET_ONERROR( mywritestr(f, "{\n") ) TheApp->incSelectionLinenumber(); RET_ONERROR( _proto->writeWithoutFields(f, indent + TheApp->GetIndent()) ) RET_ONERROR( writeFields(f, indent + TheApp->GetIndent()) ) if (!TheApp->GetkrFormating()) RET_ONERROR( indentf(f, indent + TheApp->GetIndent()) ) else RET_ONERROR( indentf(f, indent) ) RET_ONERROR( mywritestr(f, "}\n") ) TheApp->incSelectionLinenumber(); if (indent==0) { RET_ONERROR( mywritestr(f ,"\n") ) TheApp->incSelectionLinenumber(); } RET_ONERROR( writeRoutes(f, indent) ) setFlag(NODE_FLAG_TOUCHED); } return(0); } int NodeScript::writeField(int f, int indent, int fieldIndex) { const char *oldBase = _scene->getURL(); const char *newBase = _scene->getNewURL(); bool tempSave = _scene->isTempSave(); Field *field = _proto->getField(fieldIndex); FieldValue *value = _fields[fieldIndex]; if (field->getFlags() & FF_HIDDEN) { if (value && !value->equals(field->getDefault())) { RET_ONERROR( indentf(f, indent) ) RET_ONERROR( mywritestr(f ,(const char *) field->getName()) ) RET_ONERROR( mywritestr(f ," ") ) if ((field->getFlags() & FF_URL) && (!TheApp->GetKeepURLs())) { value = rewriteField(value, oldBase, newBase); RET_ONERROR( value->write(f, indent) ) if (!tempSave) { setField(fieldIndex, value); } else { delete value; } } else if (field->getFlags() & FF_HIDDEN) { RET_ONERROR( value->write(f, indent) ) } else { RET_ONERROR( write(f, indent) ) } } } else { RET_ONERROR( indentf(f, indent) ) RET_ONERROR( mywritestr(f ,"field ") ) RET_ONERROR( mywritestr(f ,typeEnumToString(field->getType())) ) RET_ONERROR( mywritestr(f ," ") ) RET_ONERROR( mywritestr(f ,(const char *) field->getName()) ) RET_ONERROR( mywritestr(f ," ") ) if (value) RET_ONERROR( value->write(f, indent) ) } return(0); } int NodeScript::writeFields(int f, int indent) { if (!_proto) return(0); int i; int urlIndex = -1; for (i = 0; i < _numFields; i++) if (strcmp(_proto->getField(i)->getName(), "url")==0) urlIndex = i; else RET_ONERROR( writeField(f, indent, i) ) // scriptnode without "url" field ? assert (urlIndex != -1); RET_ONERROR( writeField(f, indent, urlIndex) ) MFString *url = (MFString *) getField(urlIndex); for (i=0; i < url->getSize() ; i++) if (url != NULL) { // count end of line characters in url char* string= (char*) ((const char*) url->getValue(i)); while ((string=strchr(string, '\n')) !=NULL) { TheApp->incSelectionLinenumber(); string++; } } return(0); } // When a eventIn is deleted, the routes must be updated. // Routes are handled via "sockets", a list which contain (for eventIns) // the node of source of a route and a integer index to the event/field // of the source route void NodeScript::updateEventIn(int newIndex, int oldIndex) { CommandList* deleteList = new CommandList(); for (SocketList::Iterator *i = _inputs[oldIndex].first(); i != NULL; i = i->next()) { Socket outS = i->item(); for (SocketList::Iterator *j = outS._node->getOutput(outS._index).first(); j != NULL; j = j->next()) { Socket inS = j->item(); if ((inS._node == this) && (inS._index == oldIndex)) { // delete old route deleteList->append(new UnRouteCommand(outS._node, outS._index, this, oldIndex)); // add new route to _routeList if (!_routeList) _routeList = new CommandList(); _routeList->append(new RouteCommand(outS._node, outS._index, this, newIndex)); } } } _scene->execute(deleteList); } // When a eventOut is deleted, the routes must be updated. // Routes are handled via "sockets", a list which contain (for eventOut) // the node of target of a route and a integer index to the event/field // of the target route void NodeScript::updateEventOut(int newIndex, int oldIndex) { CommandList* deleteList = new CommandList(); for (SocketList::Iterator *i = _outputs[oldIndex].first(); i != NULL; i = i->next()) { Socket inS = i->item(); for (SocketList::Iterator *j = inS._node->getInput(inS._index) .first(); j != NULL; j = j->next()) { Socket outS = j->item(); if ((outS._node == this) && (outS._index == oldIndex)) { // delete old route deleteList->append(new UnRouteCommand( this, oldIndex,inS._node, inS._index)); // add new route to _routeList if (!_routeList) _routeList = new CommandList(); _routeList->append(new RouteCommand( this, newIndex,inS._node, inS._index)); } } } _scene->execute(deleteList); } // since scripts can have dynamically-added fields, we need to update // the node's fields after the script is defined. // Same problem for the scriptinterfacebuilder (ScriptDialog) (add or delete) // // It is important, that only one item (field, eventIn or eventOut) can // be added or deleted during this call of update. void NodeScript::update() { int i; int newNumFields = _proto->getNumFields(); // add if (_numFields < newNumFields) { FieldValue **newFields = new FieldValue *[newNumFields]; for (i = 0; i < _numFields; i++) { newFields[i] = _fields[i]; } for (i = _numFields; i < newNumFields; i++) { newFields[i] = _proto->getField(i)->getDefault(); if (newFields[i]) newFields[i]->ref(); } // avoid delete of _fields[something] for (i = 0 ; i < _numFields ; i++) _fields[i] = NULL; delete [] _fields; _fields = newFields; _numFields = newNumFields; } else { // delete ? for (i = 0; i < _numFields; i++) if ((_proto->getField(i)->getFlags() & FF_DELETED) != 0) { // delete ! newNumFields = _numFields-1; FieldValue **newFields = new FieldValue *[newNumFields]; int newIndex=0; for (int j = 0; j < _numFields; j++) if (j != i) newFields[newIndex++] = _fields[j]; _proto->_fields.remove(i); // avoid delete of _fields[something] for (i = 0 ; i < _numFields ; i++) _fields[i] = NULL; delete [] _fields; _fields = newFields; _numFields = newNumFields; break; } } int newNumEventIns = _proto->getNumEventIns(); // add if (_numEventIns < newNumEventIns) { SocketList *newInputs = new SocketList[newNumEventIns]; for (i = 0; i < _numEventIns; i++) { newInputs[i] = _inputs[i]; } // avoid delete of _inputs[something] for (i = 0 ; i < _numEventIns ; i++) _inputs[i] = SocketList(); delete [] _inputs; _inputs = newInputs; _numEventIns = newNumEventIns; } else { // delete ? for (i = 0; i < _numEventIns; i++) if ((_proto->getEventIn(i)->getFlags() & FF_DELETED) != 0) { // delete ! newNumEventIns = _numEventIns-1; SocketList *newInputs = new SocketList[newNumEventIns]; int newIndex=0; for (int j = 0; j < _numEventIns; j++) { if (newIndex != j) updateEventIn(newIndex, j); if (j != i) newInputs[newIndex++] = _inputs[j]; } _proto->_eventIns.remove(i); // avoid delete of _inputs[something!=i] for (int k = 0 ; k < _numEventIns ; k++) if (k != i) _inputs[k] = SocketList(); delete [] _inputs; _inputs = newInputs; _numEventIns = newNumEventIns; // execute addRouteCommands from updateEventIn() if (_routeList) _scene->execute(_routeList); _routeList = NULL; break; } } int newNumEventOuts = _proto->getNumEventOuts(); // add if (_numEventOuts < newNumEventOuts) { SocketList *newOutputs = new SocketList[newNumEventOuts]; for (i = 0; i < _numEventOuts; i++) { newOutputs[i] = _outputs[i]; } // avoid delete of _outputs[something] for (i = 0 ; i < _numEventOuts ; i++) _outputs[i] = SocketList(); delete [] _outputs; _outputs = newOutputs; _numEventOuts = newNumEventOuts; } else { // delete ? for (i = 0; i < _numEventOuts; i++) if ((_proto->getEventOut(i)->getFlags() & FF_DELETED) != 0) { // delete ! newNumEventOuts = _numEventOuts-1; SocketList *newOutputs = new SocketList[newNumEventOuts]; int newIndex=0; for (int j = 0; j < _numEventOuts; j++) { if (newIndex != j) updateEventOut(newIndex, j); if (j != i) newOutputs[newIndex++] = _outputs[j]; } _proto->_eventOuts.remove(i); // avoid delete of _outputs[something!=i] for (int k = 0 ; k < _numEventIns ; k++) if (k != i) _outputs[k] = SocketList(); delete [] _outputs; _outputs = newOutputs; _numEventOuts = newNumEventOuts; // execute addRouteCommands from updateEventIn() if (_routeList) _scene->execute(_routeList); _routeList = NULL; break; } } } // collect array of dynamic field's, eventIn's and eventOut's of ScriptNode void NodeScript::buildInterfaceData(void) { int i; _interface->resize(0); Proto* proto = getProto(); if (proto == NULL) return; for (i=0; i < proto->getNumFields(); i++) if (!(proto->getField(i)->getFlags() && (FF_HIDDEN || FF_URL))) _interface->append(new InterfaceData(EL_FIELD_DEF, i)); for (i=0; i < proto->getNumEventOuts(); i++) { // ignore Exposedfields (eg. "url") bool isExposedField = false; for (int j=0; j < proto->_exposedFields.size(); j++) { MyString exposedName = strdup(proto->getExposedField(j)->getName()); exposedName += "_changed"; if (strcmp(proto->getEventOut(i)->getName(), exposedName) == 0) isExposedField = true; } if (isExposedField) continue; _interface->append(new InterfaceData(EL_EVENT_OUT, i)); } for (i=0; i < proto->getNumEventIns(); i++) { // ignore Exposedfields (eg. "url") bool isExposedField = false; for (int j=0; j < proto->_exposedFields.size(); j++) { MyString exposedName = "set_"; exposedName += proto->getExposedField(j)->getName(); if (strcmp(proto->getEventIn(i)->getName(), exposedName) == 0) isExposedField = true; } if (isExposedField) continue; _interface->append(new InterfaceData(EL_EVENT_IN, i)); } }