Salome HOME
Join modifications from BR_Dev_For_4_0 tag V4_1_1.
[modules/superv.git] / src / SUPERVGUI / SUPERVGUI_Library.cxx
1 //  SUPERV SUPERVGUI : GUI for Supervisor component
2 //
3 //  Copyright (C) 2003  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 //  CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS 
5 // 
6 //  This library is free software; you can redistribute it and/or 
7 //  modify it under the terms of the GNU Lesser General Public 
8 //  License as published by the Free Software Foundation; either 
9 //  version 2.1 of the License. 
10 // 
11 //  This library is distributed in the hope that it will be useful, 
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of 
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
14 //  Lesser General Public License for more details. 
15 // 
16 //  You should have received a copy of the GNU Lesser General Public 
17 //  License along with this library; if not, write to the Free Software 
18 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA 
19 // 
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22 //
23 //
24 //  File   : SUPERVGUI_Library.cxx
25 //  Author : Alexander SLADKOV
26 //  Module : SUPERV
27
28
29 #include "SUPERVGUI_Library.h"
30 #include "SUPERVGUI_Main.h"
31 #include "SUPERVGUI.h"
32
33 #include "SUIT_Application.h"
34 #include "SUIT_MessageBox.h"
35 #include "SUIT_Session.h"
36
37 #include <qlistbox.h>
38 #include <qlayout.h>
39 #include <qgroupbox.h>
40 #include <qpushbutton.h>
41 #include <qfile.h>
42
43 using namespace std;
44
45 #define LIBFILE "/.salome/InLineLibrary.xml" // $HOME+LIBFILE
46 #define DOCTYPE "InLineNodesLibrary"         // XML DocType
47 #define ROOT_ELEMENT "inlinenodeslibrary"    // XML Element and Attribute tags (many)
48 #define NODE_ELEMENT "INode"
49 #define NODE_NAME_ATT "Name"
50 #define NODE_KIND_ATT "Kind"
51 #define PORT_ELEMENT "Port"
52 #define ES_PORT_ELEMENT "ESPort"
53 #define PORT_INPUT_ATT "IsInput"
54 #define PORT_NAME_ATT "Name"
55 #define PORT_TYPE_ATT "Type"
56
57 #define FUNC_NAME_ATT "PyFuncName"           // XML attributes and elements
58 #define FUNC_ELEMENT "PyFunc"                // for Python functions defined
59 #define FUNC_INIT_NAME_ATT "InitFuncName"    // in different types of InLine nodes
60 #define FUNC_INIT_ELEMENT "InitPyFunc"
61 #define FUNC_MORE_NAME_ATT "MoreFuncName"
62 #define FUNC_MORE_ELEMENT "MorePyFunc"
63 #define FUNC_NEXT_NAME_ATT "NextFuncName"
64 #define FUNC_NEXT_ELEMENT "NextPyFunc"
65 #define FUNC_EL_NAME_ATT "ELFuncName"        // EndLoop
66 #define FUNC_EL_ELEMENT "ELPyFunc"
67 #define FUNC_ES_NAME_ATT "ESFuncName"        // EndSwitch
68 #define FUNC_ES_ELEMENT "ESPyFunc"
69
70
71 SUPERVGUI_Library* SUPERVGUI_Library::myLibrary = 0;
72
73 /**
74  * Inline nodes library.  constructor. 
75  */
76 SUPERVGUI_Library::SUPERVGUI_Library() {
77 }
78
79 /**
80  * Returns the XML file name used as InLine nodes repository
81  */
82 const char* SUPERVGUI_Library::GetLibraryFileName() const {
83   string aFileName = getenv( "HOME" );
84   aFileName += LIBFILE;
85   return aFileName.c_str();
86 }
87
88 /**
89  * Creates a new library file, truncates the length to zero, writes an empty 
90  * XML stub to it.  If fails - displays an error message box and returns false
91  */
92 bool SUPERVGUI_Library::createLibFile() const {
93   QFile libFile( GetLibraryFileName() );
94   try {
95     if ( libFile.open( IO_WriteOnly | IO_Truncate ) ) { 
96       QDomDocument doc( DOCTYPE ); // create a simple XML stub
97       doc.appendChild( doc.createElement( ROOT_ELEMENT ) ); // IMPORTANT: do not delete this root element
98       QTextStream stream( &libFile );
99       doc.save( stream, 0 );
100       libFile.close();
101       return true;
102     }
103     else {
104       // mkr : PAL8557
105       QString aFullPath = GetLibraryFileName();
106       QString aDir = aFullPath.left( aFullPath.findRev('/')+1 );
107       const int toCreateDir = SUIT_MessageBox::warn2( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), 
108                                                       tr( "MSG_ERROR_LIB_DIR_NOT_EXIST_RECREATE" ).arg(aDir), tr( "BUT_YES" ), tr( "BUT_NO" ), 1, 0, 0 );
109       if ( toCreateDir ) { // user selected to create a new ./salome subdirectory in HOME directory
110         
111         // create directory
112         QString aCommand = QString("mkdir ") + aDir;
113         int status = system( aCommand.latin1() );
114         
115         if ( status == -1 || status == 217 ) {
116           SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_LIB_RECREATE_DIR" ).arg(aDir), tr( "OK" ) );
117           return false;
118         }
119         
120         // create XML file
121         libFile.open( IO_WriteOnly | IO_Truncate );
122         QDomDocument doc( DOCTYPE ); // create a simple XML stub
123         doc.appendChild( doc.createElement( ROOT_ELEMENT ) ); // IMPORTANT: do not delete this root element
124         QTextStream stream( &libFile );
125         doc.save( stream, 0 );
126         libFile.close();
127         return true;
128       }
129       else // user chose not to create a new ./salome subdirectory in HOME directory
130         return false;
131     }
132   }
133   catch ( ... ) {
134     SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_LIB_IO" ), tr( "OK" ) );
135   }
136   return false;
137 }
138
139 /**
140  * Save Function Name as attribute of given DomElement, and Function strings as its children with CDATASection
141  * saveStrings() is called to save 'main' Py function of a node, and Init(), More(), Next() for Loop node
142  */
143 void saveStrings( QDomDocument doc, QDomElement element, const char* theNameAtt, const char* theFuncName, 
144                   const char* theFuncElem, SUPERV::ListOfStrings_var pyFunc ) {
145   QString pyFuncName( theFuncName );
146   element.setAttribute( theNameAtt, pyFuncName );  // store Py function name
147   
148   // store Py functions as children of 'element'.
149   for ( int i = 0, n = pyFunc->length(); i < n; i++ ) {
150     QString pyFuncStr( pyFunc[i] );
151     QDomCDATASection aCDATA = doc.createCDATASection( pyFuncStr );
152     QDomElement funcElement = doc.createElement( theFuncElem );
153     element.appendChild( funcElement );
154     funcElement.appendChild( aCDATA );
155   }
156 }
157
158 /**
159  * Export an InLine node to Library 
160  */
161 bool SUPERVGUI_Library::Export( SUPERV::INode_var theNode ) const {
162   try {
163     if ( CORBA::is_nil( theNode ) ) {
164       SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_LIB_NIL_NODE" ), tr( "OK" ) );
165       return false; // null node
166     }
167
168     // open existing library file or create a new one and set some empty XML content
169     QFile libFile( GetLibraryFileName() );
170     if ( !libFile.exists() ) { // new library
171       if ( !createLibFile() )
172         return false; // error opening library file for writing.  MB was already displayed
173     }
174
175     // create the main XML document object
176     QString docName( DOCTYPE ) ;
177     QDomDocument doc( docName );
178     bool xmlOk = doc.setContent( &libFile );
179     if ( xmlOk )      
180       xmlOk = ( doc.elementsByTagName( ROOT_ELEMENT ).length() == 1 ); // find "root" element
181     QDomNode rootElement;
182     if ( xmlOk ) {
183       rootElement = doc.elementsByTagName( ROOT_ELEMENT ).item( 0 );
184       xmlOk = ( !rootElement.isNull() );
185     }
186     if ( !xmlOk ) {
187       const int toRecreate = SUIT_MessageBox::error2( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), 
188                                                       tr( "MSG_ERROR_LIB_IS_RECREATE" ), tr( "BUT_YES" ), tr( "BUT_NO" ), 1, 0, 0 );
189       if ( toRecreate ) { // user selected to recreate a bad XML file
190         libFile.close(); // in case it was opened by doc.setContent()
191         
192         // remove old bad file
193         QString aCommand = QString("rm -f ") + libFile.name();
194         system( aCommand.latin1() );
195
196         if ( !createLibFile() )
197           return false; // error opening library file for writing.  MB was already displayed
198         
199         // library file was successfully recreated. now re-set content, init rooElement. No checking - it MUST be OK.
200         libFile.setName( GetLibraryFileName() ); // IMPORTANT: re-read the file
201         doc.setContent( &libFile ); // no checking of setContent() and find root element is done, since we are sure
202         rootElement = doc.elementsByTagName( ROOT_ELEMENT ).item( 0 ); // that in newly created file everything is OK
203       }
204       else // user chose not to recreate a bad library file
205         return false; // library file is corrupt (bad XML structure), don't recreate
206     }
207   
208     // if theNode is EndSwitch or EndLoop -> first export Switch or Loop node
209     if ( theNode->IsEndLoop() || theNode->IsEndSwitch() ) {
210       SUPERV::GNode_var aTmpNode = SUPERV::GNode::_narrow( theNode );
211       theNode = aTmpNode->Coupled();
212       if ( CORBA::is_nil( theNode ) ) {
213         SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_LIB_NIL_COUPLED" ), tr( "OK" ) );
214         return false; // null coupled node
215       }
216     }
217
218     // create element under main document
219     QDomElement element = doc.createElement( NODE_ELEMENT ) ;
220     rootElement.appendChild( element );
221
222     // save node's name and kind
223     element.setAttribute( NODE_NAME_ATT, theNode->Name() );
224     element.setAttribute( NODE_KIND_ATT, theNode->Kind() );
225     // save the 'main' Py function of the node
226     saveStrings( doc, element, FUNC_NAME_ATT, theNode->PyFuncName(), FUNC_ELEMENT, theNode->PyFunction() );
227
228     // create DOM elements for ports
229     SUPERV::ListOfPorts_var aPorts = theNode->Ports();
230     for ( int i = 0, n = aPorts->length(); i < n; i++) {
231       if ( !CORBA::is_nil( aPorts[i] ) && !aPorts[i]->IsGate() ) {
232         QDomElement portElement = doc.createElement( PORT_ELEMENT );
233         portElement.setAttribute( PORT_INPUT_ATT, aPorts[i]->IsInput() );
234         portElement.setAttribute( PORT_NAME_ATT, aPorts[i]->Name() );
235         portElement.setAttribute( PORT_TYPE_ATT, aPorts[i]->Type() );
236         element.appendChild( portElement );
237       }
238     }
239
240     // if the node is Loop -> additionally export Init(), More(), Next() and EndLoop's function
241     if ( theNode->IsLoop() ) {
242       SUPERV::LNode_var aLoopNode = SUPERV::LNode::_narrow( theNode );
243       if ( CORBA::is_nil( aLoopNode ) ) {
244         SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_LIB_BAD_LOOP" ), tr( "OK" ) );
245         return false;
246       } 
247       SUPERV::INode_var aEndLoopNode = aLoopNode->Coupled();
248       if ( CORBA::is_nil( aEndLoopNode ) ) {
249         SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_LIB_BAD_LOOP" ), tr( "OK" ) );
250         return false;
251       } 
252       // save init, more, next, end-loop functions of the Loop node
253       saveStrings( doc, element, FUNC_INIT_NAME_ATT, aLoopNode->PyInitName(), FUNC_INIT_ELEMENT, aLoopNode->PyInit() );
254       saveStrings( doc, element, FUNC_MORE_NAME_ATT, aLoopNode->PyMoreName(), FUNC_MORE_ELEMENT, aLoopNode->PyMore() );
255       saveStrings( doc, element, FUNC_NEXT_NAME_ATT, aLoopNode->PyNextName(), FUNC_NEXT_ELEMENT, aLoopNode->PyNext() );
256       saveStrings( doc, element, FUNC_EL_NAME_ATT, aEndLoopNode->PyFuncName(), FUNC_EL_ELEMENT, aEndLoopNode->PyFunction() );
257     }
258
259     // if the node is Switch -> additionally export EndSwitch's function and ports 
260     if ( theNode->IsSwitch() ) {
261       SUPERV::SNode_var aSwitchNode = SUPERV::SNode::_narrow( theNode );
262       if ( CORBA::is_nil( aSwitchNode ) ) {
263         SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_LIB_BAD_SWITCH" ), tr( "OK" ) );
264         return false;
265       } 
266       SUPERV::INode_var aEndSwitchNode = aSwitchNode->Coupled();
267       if ( CORBA::is_nil( aEndSwitchNode ) ) {
268         SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_LIB_BAD_SWITCH" ), tr( "OK" ) );
269         return false;
270       } 
271       // save EndSwitch function
272       saveStrings( doc, element, FUNC_ES_NAME_ATT, aEndSwitchNode->PyFuncName(), FUNC_ES_ELEMENT, aEndSwitchNode->PyFunction() );
273
274       // save ports of EndSwitch
275       SUPERV::ListOfPorts_var aESPorts = aEndSwitchNode->Ports();
276       for ( int i = 0, n = aESPorts->length(); i < n; i++) {
277         if ( !CORBA::is_nil( aESPorts[i] ) && !aESPorts[i]->IsGate() ) {
278           QDomElement portElement = doc.createElement( ES_PORT_ELEMENT );
279           portElement.setAttribute( PORT_INPUT_ATT, aESPorts[i]->IsInput() );
280           portElement.setAttribute( PORT_NAME_ATT, aESPorts[i]->Name() );
281           portElement.setAttribute( PORT_TYPE_ATT, aESPorts[i]->Type() );
282           element.appendChild( portElement );
283         }
284       }
285     } // end of IsSwitch() 
286
287     // OK, done with file export.  write the document to the file
288     libFile.close(); // it seems that QDomDocument opens the file when doing 
289                      // setContent() and does not close it
290     if ( libFile.open( IO_WriteOnly ) ) { // IO_WriteOnly truncates the file!
291       QTextStream stream( &libFile );
292       doc.save( stream, 0 );
293       libFile.close();
294       return true;
295     }
296     else {  // error opening library file for final writing
297       SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_LIB_WRITE" ), tr( "OK" ) );
298       return false;
299     }
300   } // try
301   catch ( ... ) {
302   }
303
304   // should get here only in case of exception
305   SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_EXPORT_EXCEPTION" ), tr( "OK" ) );
306   return false;
307 }
308
309 /**
310  * Returns in out parameters Py function name and Py function body
311  */
312 void getStrings( QDomElement element, const char* pyFuncName, QString& thePyFuncName, 
313                 const char* theFuncElem, SUPERV::ListOfStrings_var& pyFunc ) {
314   QDomNamedNodeMap aNodeAtt = element.attributes();
315   if ( !aNodeAtt.namedItem( pyFuncName ).isNull() ) { // if pyFuncName is found - fill pyFunc.
316     thePyFuncName = aNodeAtt.namedItem( pyFuncName ).nodeValue();
317
318     // every 'theFuncElem' element has a CDATA child - one line
319     // iterate through all of them twice: 1) to count the number of these items (number of strings)
320     // 2) to fill pyFunc out-parameter
321     int nStrings( 0 );
322     QDomNode n = element.firstChild();
323     while ( !n.isNull() ) {
324       if ( n.isElement() && n.nodeName() == theFuncElem && n.firstChild().isCDATASection() )
325         nStrings++;
326       n = n.nextSibling();
327     }
328   
329     pyFunc->length( nStrings );
330   
331     int i( 0 );
332     n = element.firstChild();
333     while ( !n.isNull() ) {
334       if ( n.isElement() && n.nodeName() == theFuncElem && n.firstChild().isCDATASection() ) {
335         QDomCDATASection aCDATA = n.firstChild().toCDATASection();
336         pyFunc[i++] = aCDATA.nodeValue();
337       }
338       n = n.nextSibling();
339     }
340   } // if ( pyFuncName )
341 }
342 /**
343  * Adds ports stored in Dom node to the CNode
344  */
345 void addPorts( SUPERV::INode_var theINode, QDomElement element, const char* portElement ) {
346   QDomNodeList aPorts = element.elementsByTagName( portElement );
347   for ( int i = 0, n = aPorts.length(); i < n; i++ ) {
348     QDomNode aPort = aPorts.item( i );
349     QDomNamedNodeMap anAtt = aPort.attributes();
350     bool isInput = ( anAtt.namedItem( PORT_INPUT_ATT ).nodeValue() == "1" );
351     QString portName = anAtt.namedItem( PORT_NAME_ATT ).nodeValue();
352     QString portType = anAtt.namedItem( PORT_TYPE_ATT ).nodeValue();
353     if ( isInput )
354       theINode->InPort( portName.latin1(), portType.latin1() );
355     else
356       theINode->OutPort( portName.latin1(), portType.latin1() );
357   }
358 }
359
360 /**
361  * Import an InLine node from Library into the dataflow  
362  */
363 bool SUPERVGUI_Library::Import( SUPERV::Graph_var theDataflow,  SUPERV::INode_var& theNode, 
364                                 SUPERV::INode_var& theEndNode, const int i ) const {
365   try {
366     QDomNodeList aNodes;
367     QDomDocument doc;
368     if ( getNodes( doc, aNodes ) ) { // xml is ok
369       if ( i >=0 && i < aNodes.length() ) { // index is ok
370         // 1. retrieve the node with given index 
371         QDomElement aNode = aNodes.item( i ).toElement();
372         // 2. create an Engines node
373         QDomNamedNodeMap aNodeAtt = aNode.attributes();
374         const int aKind = aNodeAtt.namedItem( NODE_KIND_ATT ).nodeValue().toInt();
375         QString aNodeName = aNodeAtt.namedItem( NODE_NAME_ATT ).nodeValue();
376         switch ( aKind ) {
377         case SUPERV::InLineNode : // PyFunction
378         case SUPERV::GOTONode :
379           {
380             // get all the Python function
381             QString aPyFuncName;
382             SUPERV::ListOfStrings_var aPyFunc = new SUPERV::ListOfStrings();
383             getStrings( aNode, FUNC_NAME_ATT, aPyFuncName, FUNC_ELEMENT, aPyFunc );
384
385             // create the corresponding Engines node 
386             SUPERV::INode_var aINode;
387             if ( aKind == SUPERV::InLineNode ) 
388               aINode = theDataflow->INode( aPyFuncName.latin1(), aPyFunc );
389             else
390               aINode = theDataflow->GNode( aPyFuncName.latin1(), aPyFunc, "" );
391             
392             aINode->SetName( aNodeName.latin1() ); // try to set the same name of node (might be changed by CNode::SetName)
393             addPorts( aINode, aNode, PORT_ELEMENT ); // add stored ports
394
395             theNode = aINode; // init out-parameter
396             return true;
397           }
398         case SUPERV::LoopNode : // PyInit, PyNext, PyMore, PyEndLoopFunction
399           {
400             // get all required Python function
401             QString aInitName, aMoreName, aNextName, aELName;
402             SUPERV::ListOfStrings_var aInitFunc = new SUPERV::ListOfStrings(),
403             aMoreFunc = new SUPERV::ListOfStrings(),
404             aNextFunc = new SUPERV::ListOfStrings(),
405             aELFunc   = new SUPERV::ListOfStrings();
406             getStrings( aNode, FUNC_INIT_NAME_ATT, aInitName, FUNC_INIT_ELEMENT, aInitFunc );
407             getStrings( aNode, FUNC_MORE_NAME_ATT, aMoreName, FUNC_MORE_ELEMENT, aMoreFunc );
408             getStrings( aNode, FUNC_NEXT_NAME_ATT, aNextName, FUNC_NEXT_ELEMENT, aNextFunc );
409             getStrings( aNode, FUNC_EL_NAME_ATT,   aELName, FUNC_EL_ELEMENT,     aELFunc );
410
411             // create Engines Loop node 
412             SUPERV::INode_var aELNode;
413             SUPERV::INode_var aINode = theDataflow->LNode( aInitName.latin1(), aInitFunc,
414                                                           aMoreName.latin1(), aMoreFunc,
415                                                           aNextName.latin1(), aNextFunc, aELNode );
416             // EndLoop node may have or may NOT have pyFunc. set it if it was stored.
417             if ( !aELName.isEmpty() )
418               aELNode->SetPyFunction( aELName.latin1(), aELFunc );
419             
420             aINode->SetName( aNodeName.latin1() );// try to set the same name of node (might be changed by CNode::SetName)
421             addPorts( aINode, aNode, PORT_ELEMENT ); // add stored ports
422           
423             theNode = aINode;  // init out-parameters
424             theEndNode = aELNode;
425             return true;
426           }
427         case SUPERV::SwitchNode : // PyFunction, PyESFunction, ESPorts
428           {
429             // get all required Python function
430             QString aPyFuncName, aESPyFuncName;
431             SUPERV::ListOfStrings_var aPyFunc = new SUPERV::ListOfStrings(),
432             aESPyFunc = new SUPERV::ListOfStrings();
433             getStrings( aNode, FUNC_NAME_ATT,    aPyFuncName,   FUNC_ELEMENT,    aPyFunc );
434             getStrings( aNode, FUNC_ES_NAME_ATT, aESPyFuncName, FUNC_ES_ELEMENT, aESPyFunc );
435
436             // create Engines Switch node 
437             SUPERV::INode_var aESNode;
438             SUPERV::INode_var aINode = theDataflow->SNode( aPyFuncName.latin1(), aPyFunc, aESNode );
439             
440             // EndSwitch node may have or may NOT have pyFunc
441             if ( !aESPyFuncName.isEmpty() )
442               aESNode->SetPyFunction( aESPyFuncName.latin1(), aESPyFunc );
443
444             aINode->SetName( aNodeName.latin1() );// try to set the same name of node (might be changed by CNode::SetName)
445             addPorts( aINode, aNode, PORT_ELEMENT ); // add stored ports
446             addPorts( aESNode, aNode, ES_PORT_ELEMENT ); // add stores ports of EndSwitch
447
448             theNode = aINode;  // init out-parameters
449             theEndNode = aESNode;
450             return true;
451           }
452         default: // wrong kind of node error
453           SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_IMPORT_BAD_KIND_OF_NODE" ), tr( "OK" ) );
454           return false;
455         } // switch ( kind_of_node )
456       } // if ( index >= 0...)
457       else {
458         SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_IMPORT_BAD_INDEX" ), tr( "OK" ) );
459       }
460     } // if ( getNodes() )
461     else {
462       return false; // no MB, getNodes() in case of errors displays MB itself
463     }
464   } // try
465   catch ( ... ) {
466   }
467
468   // Normally we get here ONLY if an exception occured.  All other paths of execution must return before.
469   // But - who knows, maybe we can get here by some other means.. anyway, it's an error and we report it here
470   SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_IMPORT_EXCEPTION" ), tr( "OK" ) );
471   return false;
472 }
473
474 /**
475  * Returns list of NODE_ELEMENT-s and error result (false) if failed (also displays MB)
476  */
477 bool SUPERVGUI_Library::getNodes( QDomDocument& doc, QDomNodeList& theNodes ) const {
478   QFile libFile( GetLibraryFileName() );  // open existing library file
479   if ( !libFile.exists() ) { 
480     SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_IMPORT_LIB_NO_XML" ), tr( "OK" ) );
481     return false;
482   }
483
484   // create the main XML document object
485   QString docName( DOCTYPE ) ;
486   doc = QDomDocument( docName );
487   bool xmlOk = doc.setContent( &libFile );
488   // check XML for validity: 1) find root element with predefined name 2) check xml doctype
489   if ( xmlOk )    
490     xmlOk = ( doc.doctype().name() ==  DOCTYPE && doc.elementsByTagName( ROOT_ELEMENT ).length() == 1 ); 
491   if ( !xmlOk ) {
492     SUIT_MessageBox::error1( (QWidget*)(SUIT_Session::session()->activeApplication()->desktop()), tr( "ERROR" ), tr( "MSG_ERROR_IMPORT_LIB_BAD_XML" ), tr( "OK" ) );
493     return false;
494   }
495
496   theNodes = doc.elementsByTagName( NODE_ELEMENT );
497   return true;
498 }
499
500 /**
501  * Remove an InLine node with given index from Library
502  */
503 bool SUPERVGUI_Library::Remove( const int i ) const {
504   QDomNodeList aNodes;
505   QDomDocument doc;
506   if ( getNodes( doc, aNodes ) && doc.elementsByTagName( ROOT_ELEMENT ).length() == 1 ) { // xml is ok
507     if ( i >=0 && i < aNodes.length() ) {
508       // 1. remove the child element (node) from Dom Document
509       QDomElement root = doc.elementsByTagName( ROOT_ELEMENT ).item( 0 ).toElement();
510       root.removeChild( aNodes.item( i ) );
511       // 2. write the modified document to the file
512       QFile libFile( GetLibraryFileName() );
513       if ( libFile.open( IO_WriteOnly | IO_Truncate ) ) { 
514         QTextStream stream( &libFile );
515         doc.save( stream, 0 );
516         libFile.close();
517         return true;
518       }
519     }
520   }
521   return false;
522 }
523
524 /**
525  * Returns a string descriptor of KindOfNode enumeration
526  */
527 QString getKindStr( const QString& theKind ) {
528   switch ( theKind.toInt() ) {
529   case SUPERV::InLineNode : return "InLine";
530   case SUPERV::LoopNode :   return "Loop";
531   case SUPERV::SwitchNode : return "Switch";
532   case SUPERV::GOTONode :   return "GOTO";
533   case SUPERV::FactoryNode : 
534   case SUPERV::DataFlowGraph : 
535   case SUPERV::ComputingNode : 
536   case SUPERV::EndLoopNode : 
537   case SUPERV::EndSwitchNode :
538   case SUPERV::DataStreamGraph : 
539   case SUPERV::MacroNode : 
540   case SUPERV::UnknownNode : 
541   default:
542     ;
543   }
544   return "INCORRECT kind";
545 }
546
547 /**
548  * returns a list of node names currently stored in the library.  Indexes of the nodes in
549  * this list can be used for futher calls to Import(.., index)
550  */
551 SUPERV::ListOfStrings SUPERVGUI_Library::GetLibraryNodesNames() const {
552   SUPERV::ListOfStrings aNodesNames;
553   aNodesNames.length( 0 );
554   
555   QDomNodeList aNodes;
556   QDomDocument doc;
557   if ( !getNodes( doc, aNodes ) )
558     return aNodesNames;
559
560   const int n = aNodes.length();
561   aNodesNames.length( n );
562   QDomNode aNode;
563   for ( int i = 0; i < n; i++ ) {
564     QString aNodeName( "" );
565     aNode = aNodes.item( i );
566     if ( !aNode.isNull() ) {
567       QDomNode aNameAtt = aNode.attributes().namedItem( NODE_NAME_ATT );
568       QDomNode aTypeAtt = aNode.attributes().namedItem( NODE_KIND_ATT );
569       if ( !aNameAtt.isNull() && !aTypeAtt.isNull() ) {
570         aNodeName = QString( "%1 ( %2 )" ).arg( aNameAtt.toAttr().value() ).arg( 
571                     getKindStr( aTypeAtt.toAttr().value() ) ); 
572       }
573     }
574     // if NodeName attribute was not found or some error (NULL node), 
575     // then an empty string is added for that index in the list
576     aNodesNames[i] = aNodeName.latin1();
577   }
578
579   return aNodesNames;
580 }
581
582 /**
583  * returns status of library: false indicates that library file does not exist or can not be opened
584  */
585 bool SUPERVGUI_Library::CanImport() const {
586   try {
587     QDomNodeList aNodes;
588     QDomDocument doc;
589     return getNodes( doc, aNodes );
590   }
591   catch ( ... ) { 
592   }
593   return false;
594 }
595
596
597
598
599
600 /**
601  * Inline nodes library management dialog.  constructor. 
602  */
603 SUPERVGUI_LibDlg::SUPERVGUI_LibDlg( QWidget* parent, int& theX, int& theY )
604   :QDialog( parent, "SUPERVGUI_LibDlg", true, WStyle_Customize | WStyle_NormalBorder | WStyle_Title | WStyle_SysMenu),
605    myX( theX ), myY( theY )
606 {
607   setSizeGripEnabled( true );
608   setCaption(tr("TIT_LIB_DLG"));
609   resize( 350, 400 );
610   QGridLayout* aMainLayout = new QGridLayout( this, 4, 4, 11, 6 );
611
612   myLB = new QListBox( this );
613
614   QPushButton* anAddBtn = new QPushButton( tr("TIT_ADDFNODE"), this );
615   connect( anAddBtn, SIGNAL( clicked() ), this, SLOT( add() ) );
616   QPushButton* aRemBtn = new QPushButton( tr("BUT_REMOVE"), this );
617   connect( aRemBtn, SIGNAL( clicked() ), this, SLOT( remove() ) );
618
619   aMainLayout->addMultiCellWidget( myLB, 0, 2, 0, 2 );
620   aMainLayout->addWidget( anAddBtn, 0, 3  );
621   aMainLayout->addWidget( aRemBtn, 1, 3 );
622
623   QGroupBox* aBtnBox = new QGroupBox( 0, Qt::Vertical, this );
624   aBtnBox->layout()->setSpacing( 0 ); aBtnBox->layout()->setMargin( 0 );
625   QHBoxLayout* aBtnLayout = new QHBoxLayout( aBtnBox->layout() );
626   aBtnLayout->setAlignment( Qt::AlignTop );
627   aBtnLayout->setSpacing( 6 ); aBtnLayout->setMargin( 11 );
628
629   QPushButton* aCloseBtn = new QPushButton( tr("BUT_CLOSE"), aBtnBox );
630   connect( aCloseBtn, SIGNAL( clicked() ), this, SLOT( reject() ) );
631   aBtnLayout->addStretch();
632   aBtnLayout->addWidget( aCloseBtn );
633   aBtnLayout->addStretch();
634
635   aMainLayout->addMultiCellWidget( aBtnBox, 3, 3, 0, 3 );
636   aMainLayout->setRowStretch( 2, 1 );
637
638   initList();
639 }
640
641 /**
642  * Inline nodes library management dialog.  destructor. 
643  */
644 SUPERVGUI_LibDlg::~SUPERVGUI_LibDlg() {}
645
646 /**
647  * Called when user presses "Add" button.  Add a selected node from library to graph
648  */
649 void SUPERVGUI_LibDlg::add() {
650   const int i = myLB->currentItem();
651   if ( i >= 0 && i < myLB->count() ) {
652     SUPERVGUI* aSupMod = SUPERVGUI::Supervision();
653     if ( !aSupMod ) {
654       MESSAGE("NULL Supervision module!");
655       return;
656     }
657     SUPERV::Graph_var aDataflow = aSupMod->getMain()->getDataflow();
658     SUPERV::INode_var aNode, aEndNode;
659     if ( SUPERVGUI_Library::getLibrary()->Import( aDataflow, aNode, aEndNode, i ) ) {
660       SUPERVGUI_Service::addNode( SUPERV::CNode::_narrow( aNode ), aEndNode, myX, myY );
661       aSupMod->getMain()->sync();
662     }
663     else { // all errors must be reported to user in Import(), MB shown, etc..
664     }      // so we don't need to report errors if Import() returned false.
665   }
666 }
667
668 /**
669  * Called when user presses "Remove" button.  Remove a selected node from library
670  */
671 void SUPERVGUI_LibDlg::remove() {
672   const int i = myLB->currentItem();
673   if ( i >= 0 && i < myLB->count() ) {
674     SUPERVGUI_Library::getLibrary()->Remove( i );
675     initList(); // re-initialize the list to reflect the changes
676   } 
677 }
678
679 /**
680  * Fills the list with names of nodes currently stored in the library.  Indexes in the list
681  * can be used for calls to Library::Import()
682  */
683 void SUPERVGUI_LibDlg::initList() {
684   myLB->clear();
685   SUPERV::ListOfStrings aNodesNames = SUPERVGUI_Library::getLibrary()->GetLibraryNodesNames();
686   for ( int i = 0, n = aNodesNames.length(); i < n; i++ )
687     myLB->insertItem( (const char*)aNodesNames[i] );
688 }
689