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