Salome HOME
Moved some functionality to VTKViewer_Utilities.h
[modules/kernel.git] / src / RegistryDisplay / RegWidget.cxx
1 //  SALOME RegistryDisplay : GUI for Registry server implementation
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   : RegWidget.cxx
25 //  Author : Pascale NOYRET, EDF
26 //  Module : SALOME
27 //  $Header$
28
29 # include <qpushbutton.h>
30 # include <qlistview.h>
31 # include <qtabwidget.h> 
32 # include <qstatusbar.h>
33 # include <qtextview.h>
34 # include <qtimer.h>
35 # include <qinputdialog.h>
36 # include <qtoolbar.h>
37 # include <qdir.h>
38 # include <qtooltip.h>
39
40 # include "SALOME_NamingService.hxx"
41 # include "ServiceUnreachable.hxx"
42 # include "Utils_SINGLETON.hxx"
43 # include "Utils_CommException.hxx"
44
45 # include "utilities.h"
46 # include "OpUtil.hxx"
47
48 # include "RegWidget.hxx"
49 # include "HelpWindow.hxx" 
50 # include "IntervalWindow.hxx"
51 using namespace std;
52
53 typedef int PIXELS;
54 RegWidget* RegWidget::myRegWidgetPtr = 0;
55 QString addSlash( const QString& );
56 QString findFile( QString filename );
57
58 #define BOLD( text ) ( QString( "<b>" ) + QString( text ) + QString( "</b>" ) )
59
60 /*!
61   Creates components list
62 */
63
64 Registry::Components_var MakeRegistry( CORBA::ORB_var &orb )
65 {
66
67   const char *registryName="Registry" ;
68   
69   SALOME_NamingService &naming = *SINGLETON_<SALOME_NamingService>::Instance() ;
70   naming.init_orb( orb ) ;
71   
72   // Recuperation de la reference de l'objet
73   CORBA::Object_var object = 0 ;
74   try
75     {
76       SCRUTE(registryName) ;
77       object = naming.Resolve( registryName ) ;
78       if(CORBA::is_nil(object)) throw CommException( "unable to find the RegistryService" ) ;
79     }
80   catch( const ServiceUnreachable &ex )
81     {
82       MESSAGE( ex.what() )
83       exit( EXIT_FAILURE ) ;
84     }
85   catch( const CORBA::Exception &exx )
86     {
87       exit( EXIT_FAILURE ) ;
88     }
89   
90   // Specialisation de l'objet generique
91   
92   return Registry::Components::_narrow( object ) ;
93 }
94
95 /*!
96   Only one global registry window should exist at the moment
97   This method creates registry window if necessary and returns it
98   [ static ] 
99 */
100 RegWidget* RegWidget::GetRegWidget( CORBA::ORB_var &orb , QWidget *parent, const char *name )
101 {
102   if ( !myRegWidgetPtr ) 
103     myRegWidgetPtr = new RegWidget( orb, parent, name );
104   return myRegWidgetPtr;
105 }
106
107 /*!
108   Constructor  
109 */
110 RegWidget::RegWidget(CORBA::ORB_var &orb, QWidget *parent, const char *name ) 
111      : QMainWindow( parent, name, WType_TopLevel | WDestructiveClose ),
112        _VarComponents( MakeRegistry(orb) ),
113        _clients(0), _history(0), _parent( parent ),
114        _tabWidget(0), _refresh(0), _interval(0),
115        myInfoWindow(0), myHelpWindow(0), myIntervalWindow(0)
116 {
117    QString aFile = findFile("default.png");
118  /* char* dir = getenv( "CSF_ResourcesDefaults" );
119   QString path( "" );
120   if ( dir ) {
121     QDir qDir( dir );
122     path = qDir.filePath( "default.png" );
123   }*/
124   QPixmap pm ( aFile );
125   if ( !pm.isNull() )
126     setIcon( pm );
127
128   // Buttons definition
129   QToolBar* topbar = new QToolBar( tr("Toolbar"), this );
130   setDockEnabled( topbar, DockTornOff, false );
131   setDockMenuEnabled( false );
132
133   _refresh = new QPushButton( tr( "Refresh" ), topbar );
134   connect( _refresh, SIGNAL( clicked() ), this, SLOT( slotListeSelect() ) );
135   QToolTip::add( _refresh, "", toolTipGroup(), tr("Immediately updates list of components") );
136   
137   /* PAL5540 - this button is needless
138   QPushButton* help = new QPushButton( tr( "Help" ), topbar );
139   connect( help, SIGNAL( clicked() ), this, SLOT( slotHelp() ) );
140   QToolTip::add( help, "", toolTipGroup(), tr("Opens Help window") );
141   */
142   
143   _interval = new QPushButton( tr( "Interval" ), topbar );
144   connect( _interval, SIGNAL( clicked() ), this, SLOT( slotSelectRefresh() ) );
145   QToolTip::add( _interval, "", toolTipGroup(), tr("Changes refresh interval") );
146   
147   // Display area and associated slots definition
148   _tabWidget = new QTabWidget( this );
149   _clients   = new QListView( _tabWidget );
150   SetListe();
151   _history   = new QListView( _tabWidget );
152   SetListeHistory();
153   
154   _tabWidget->addTab( _clients, tr( "Running" ) );
155   _tabWidget->addTab( _history, tr( "History" ) );
156   connect( _tabWidget, SIGNAL( currentChanged( QWidget* )), this, SLOT( slotListeSelect() ) );
157   connect( _clients,   SIGNAL( clicked( QListViewItem* ) ),        this, SLOT( slotClientChanged( QListViewItem* ) ) );
158   connect( _history,   SIGNAL( clicked( QListViewItem* ) ),        this, SLOT( slotHistoryChanged( QListViewItem* ) ) );
159   setCentralWidget( _tabWidget );
160   
161   // Timer definition (used to automaticaly refresh the display area)
162   _counter = new QTimer( this );
163   connect( _counter, SIGNAL( timeout() ), this, SLOT( slotListeSelect() ) );
164   myRefreshInterval = 10;
165   _counter->start( myRefreshInterval * 1000 );
166   
167   PIXELS xpos    = 160 ;
168   PIXELS ypos    = 100 ;
169   PIXELS largeur = 800 ;
170   PIXELS hauteur = 350 ;
171   setGeometry( xpos, ypos, largeur, hauteur ) ;
172   setCaption( name ) ;
173   statusBar()->message("    ");
174 }
175
176 /*!
177   Destructor
178 */
179 RegWidget::~RegWidget()
180 {
181   MESSAGE("Debut du Destructeur");
182   _counter->stop();
183   myRegWidgetPtr = 0;
184 };
185
186 /*!
187   Event filter
188 */
189 bool RegWidget::eventFilter( QObject* object, QEvent* event )
190 {
191   if ( object ) {
192     if ( object == myHelpWindow && event->type() == QEvent::Close ) {
193       myHelpWindow = 0;
194     }
195     else if ( object == myInfoWindow && event->type() == QEvent::Close ) {
196       myInfoWindow = 0;
197     }
198     else if ( object == myIntervalWindow && event->type() == QEvent::Close ) {
199       myIntervalWindow = 0;
200     }
201   }
202   return QMainWindow::eventFilter( object, event );
203 }
204
205 /*!
206   Searches item in the list and returns it's index or -1 if not found
207 */
208 int RegWidget::numitem(const QString &name, const QString &pid, const QString &machine,const Registry::AllInfos *listclient)
209 {
210   BEGIN_OF("numitem");
211   for (CORBA::ULong i=0; i<listclient->length(); i++)
212     {       
213       const Registry::Infos & c_info=(*listclient)[i];
214       ASSERT( c_info.name!=NULL);
215       QString b;
216       b.setNum(int(c_info.pid));
217       if ( (name.compare(QString(c_info.name)) == 0) && 
218           (machine.compare(QString(c_info.machine)) == 0) && 
219           (pid.compare(b) == 0) )
220         {
221           END_OF("numitem");
222           return i;
223         }
224     }
225   END_OF("numitem");
226   return -1;
227 }
228
229 /*!
230   Returns text, containing information about client [ static ]
231 */
232 QString RegWidget::setlongText( const Registry::Infos &c_info)
233 {
234   BEGIN_OF("setlongText");
235   ASSERT( c_info.name != NULL );
236   QString a = QString( "<hr><h2>" ) + tr( "Code" ) + QString( " : " );
237   a.append( QString( c_info.name ) );
238   a.append( "</h1><hr><br>" );
239   a.append( tr( "Process Id" ) + QString( " : " ) );
240   a.append( BOLD( QString::number( int( c_info.pid ) ) ) );
241   a.append( QString( " " ) + tr( "on machine" ) + QString( " " ) );
242   a.append( BOLD( c_info.machine ) ); 
243   a.append( QString( " " ) + tr( "ip address" ) + QString( " : " ) );
244   a.append( BOLD( c_info.adip ) );
245   a.append( "<br>" );
246   
247   a.append( tr( "launched by user" ) + QString( " " ) );
248   a.append( BOLD( c_info.pwname ) );
249   a.append( QString( " ( " ) + tr( "pid" ) + QString( " : " ) );
250   a.append( BOLD( QString::number( int( c_info.uid ) ) ) );
251   a.append( QString( " )<br> " ) + tr( "in directory" ) + QString( " " ));
252   a.append( BOLD( c_info.cdir ) );
253   
254   a.append( QString( "<br>" ) + tr( "begins" ) + QString( " " ) );
255   char * t1 = (char * )duplicate(ctime(&c_info.tc_start));
256   t1 [strlen(t1) -1 ] = ' ';
257   a.append( BOLD( t1 ) ); 
258   delete [] t1;
259   a.append( "<br>" );
260   
261   if (c_info.tc_hello != 0 )
262     {
263       char * t2 = (char * )duplicate(ctime(&c_info.tc_hello));
264       t2 [strlen(t2) -1 ] = ' ';
265       a.append( tr( "last signal" ) + QString(" : ") ); 
266       a.append( BOLD( t2 ) ); 
267       a.append( "<br>" );
268       delete [] t2;
269     }
270   if ((c_info.tc_end - c_info.difftime) != 0)
271     {
272       char * t3 = (char * )duplicate(ctime(&c_info.tc_end));
273       t3 [strlen(t3) -1 ] = ' ';
274       a.append( tr( "ends" ) + QString( " " ) ); 
275       a.append( BOLD( t3 ) ); 
276       a.append( "<br>" );
277       delete [] t3;
278     }
279   else
280     {
281       a.append( tr( "still running" ) + QString( "<br>" ) );
282     }
283   
284   SCRUTE(c_info.difftime);
285   if (c_info.difftime!= 0)
286     {
287       a.append( QString( "(" ) + tr( "Time on" ) + QString( " " ) ); 
288       a.append( BOLD( c_info.machine ) ); 
289       a.append( QString( " " ) + tr( "differs from server's time. The difference is" ) + QString( " " )); 
290       a.append( BOLD( QString::number( int( c_info.difftime ) ) ) );
291       a.append( QString( " " ) + tr( "seconds" ) + QString( ")<br>" ) );
292     }
293   END_OF("setlongText");  
294   return a;
295   
296 }
297
298 /*!
299   Close event
300 */
301 void RegWidget::closeEvent( QCloseEvent *e)
302 {
303   BEGIN_OF("closeEvent");
304   if ( myInfoWindow )
305     myInfoWindow->close();
306   if ( myHelpWindow )
307     myHelpWindow->close();
308   if (myIntervalWindow)
309     myIntervalWindow->close();
310   e->accept();
311   END_OF("closeEvent");
312 };
313
314 /*!
315   Setups Clients list
316 */
317 void RegWidget::SetListe()
318 {
319   BEGIN_OF("SetListe");
320   _clients->addColumn( tr( "Component" ), -1);
321   _clients->addColumn( tr( "PID" ), -1 );
322   _clients->addColumn( tr( "User Name" ), -1 );
323   _clients->addColumn( tr( "Machine" ), -1 );
324   _clients->addColumn( tr( "begins" ), -1 );
325   _clients->addColumn( tr( "hello" ) , -1 );
326   _clients->setColumnAlignment( 1, Qt::AlignRight );
327   END_OF("SetListe");
328 }
329
330 /*!
331   Setups History list
332 */
333 void RegWidget::SetListeHistory()
334 {
335    BEGIN_OF("SetListeHistory")
336   _history->addColumn( tr( "Component" ), -1);
337   _history->addColumn( tr( "PID" ), -1 );
338   _history->addColumn( tr( "User Name" ), -1 );
339   _history->addColumn( tr( "Machine" ), -1 );
340   _history->addColumn( tr( "begins" ), -1 );
341   _history->addColumn( tr( "ends" ), -1 );
342   _history->setColumnAlignment( 1, Qt::AlignRight );
343    END_OF("SetListeHistory")
344 }
345
346 /*!
347   Updates History list
348 */
349 void RegWidget::InfoHistory()
350 {
351
352   BEGIN_OF("InfoHistory")
353     _history->clear();
354   try
355     {
356       _serverhistory = _VarComponents->history();
357       for (CORBA::ULong i=0; i<_serverhistory->length(); i++)
358         {       
359           const Registry::Infos & c_info=(*_serverhistory)[i];
360           ASSERT( c_info.name!=NULL);
361           QString a;
362           a.setNum(int(c_info.pid));
363           char * t1 = (char * )duplicate(ctime(&c_info.tc_start));
364           t1 [strlen(t1) -1 ] = ' ';
365           char * t2 = (char * )duplicate(ctime(&c_info.tc_end));
366           t2 [strlen(t2) -1 ] = ' ';
367           QListViewItem * item = new QListViewItem(_history, QString(c_info.name),\
368                                                    a, QString(c_info.pwname), QString(c_info.machine), \
369                                                    QString(t1), QString(t2));
370           item=0 ;
371           delete [] t1;
372           delete [] t2;
373           
374         }
375     }
376   catch( ... )
377     {
378       _interval->setDisabled( TRUE ) ;
379       _refresh->setDisabled( TRUE ) ;
380       _counter->stop();
381       MESSAGE("Sorry, No more Registry Server") ;
382       statusBar()->message( tr( "Sorry, No more Registry Server" ) ) ;
383     }
384   END_OF("InfoHistory")
385 }
386
387 /*!
388   Updates clients list
389 */
390 void RegWidget::InfoReg()
391 {
392   BEGIN_OF("InfoReg")
393   _clients->clear();
394   try
395     {
396       _serverclients = _VarComponents->getall();
397       for (CORBA::ULong i=0; i<_serverclients->length(); i++)
398         {       
399           const Registry::Infos & c_info=(*_serverclients)[i];
400           ASSERT( c_info.name!=NULL);
401           QString a;
402           a.setNum(int(c_info.pid));
403           char * t1 = (char * )duplicate(ctime(&c_info.tc_start));
404           t1 [strlen(t1) -1 ] = ' ';
405           char * t2 = (char * )duplicate(ctime(&c_info.tc_hello));
406           t2 [strlen(t2) -1 ] = ' ';
407           QListViewItem * item = new QListViewItem(_clients, QString(c_info.name),\
408                                                    a, QString(c_info.pwname), QString(c_info.machine), \
409                                                    QString(t1), QString(t2));
410           item=0 ;
411           delete [] t1;
412           delete [] t2;
413           
414         }
415     }
416   catch( ... )
417     {
418       _interval->setDisabled( TRUE ) ;
419       _refresh->setDisabled( TRUE ) ;
420       _counter->stop();
421       MESSAGE("Sorry, No more Registry Server") ;
422       statusBar()->message( tr( "Sorry, No more Registry Server" ) ) ;
423     }
424   END_OF("InfoReg")
425 }
426
427 /*!
428   Called when <Refresh> button is clicked
429 */
430 void RegWidget::slotListeSelect()
431 {
432   try
433     {
434       ASSERT(_tabWidget->currentPage() != NULL);
435       if (_tabWidget->currentPage () == _clients) InfoReg();
436       else if (_tabWidget->currentPage () == _history) InfoHistory();
437     }
438   catch( ... )
439     {
440       MESSAGE("Sorry, No more Registry Server") ;
441       statusBar()->message( tr( "Sorry, No more Registry Server" ) ) ;
442     }
443 }
444
445 /*!
446   Called when <Interval> button is clicked (changing refresh interval)
447 */
448 void RegWidget::slotSelectRefresh()
449 {
450   BEGIN_OF("slotSelectRefresh");
451   myIntervalWindow = new IntervalWindow(this);
452   myIntervalWindow->installEventFilter( this );
453   myIntervalWindow->setValue(myRefreshInterval);
454   myIntervalWindow->show();
455   connect( myIntervalWindow->Cancel(), SIGNAL( clicked() ), myIntervalWindow, SLOT( close() ) );
456   connect( myIntervalWindow->Ok(), SIGNAL( clicked() ), this, SLOT( slotIntervalOk() ) );
457   END_OF("slotSelectRefresh");
458 }
459
460 void RegWidget::slotIntervalOk()
461 {
462   BEGIN_OF("slotIntervalOk");
463   myRefreshInterval = myIntervalWindow->getValue();
464   _counter->changeInterval( myRefreshInterval * 1000 );
465   SCRUTE(myRefreshInterval);
466   myIntervalWindow->close();
467   END_OF("slotIntervalOk");
468 }
469 /*!
470   Called when <Help> button is clicked
471 */
472 void RegWidget::slotHelp()
473 {
474   BEGIN_OF("slotHelp()");
475
476   if ( !myHelpWindow ) {
477     myHelpWindow  = new HelpWindow( this );
478     myHelpWindow->installEventFilter( this );
479   }
480   myHelpWindow->show();
481   myHelpWindow->raise();
482   myHelpWindow->setActiveWindow();
483   
484   END_OF("slotHelp()") ;
485 }
486
487 /*!
488   Called when user clicks on item in <Running> list
489 */
490 void RegWidget::slotClientChanged( QListViewItem* item )
491 {
492   BEGIN_OF("slotClientChanged()") ;
493
494   if ( item <= 0)
495     return;
496
497   blockSignals( true ); // for sure that item will not be deleted when refreshing
498
499   int numeroItem = numitem(item->text(0), item->text(1), item->text(3), _serverclients);
500   SCRUTE(numeroItem) ;
501   SCRUTE(item->text(1)) ;
502   
503   ASSERT(numeroItem>=0) ;
504   ASSERT((size_t)numeroItem<_serverclients->length()) ;
505   const Registry::Infos & c_info=(*_serverclients)[numeroItem];
506   ASSERT( c_info.name!=NULL);
507   
508   if ( !myInfoWindow ) {
509     myInfoWindow  = new InfoWindow( this );
510     myInfoWindow->installEventFilter( this );
511   }
512   QString a = tr( "More about" ) + QString( " " ) + QString( c_info.name );
513   myInfoWindow->setCaption(a);
514   myInfoWindow->setText( RegWidget::setlongText( c_info) );
515   myInfoWindow->show();
516   myInfoWindow->raise();
517   myInfoWindow->setActiveWindow();
518
519   blockSignals( false ); // enabling signals again
520
521   END_OF("slotClientChanged()") ;
522   return ;
523 }
524
525 /*!
526   Called when user clicks on item in <History> list
527 */
528 void RegWidget::slotHistoryChanged( QListViewItem* item )
529 {
530
531   BEGIN_OF("slotHistoryChanged()") ;
532   
533   if ( item <= 0)
534     return;
535
536   blockSignals( true ); // for sure that item will not be deleted when refreshing
537
538   int numeroItem = numitem(item->text(0), item->text(1), item->text(3), _serverhistory);
539   
540   SCRUTE(numeroItem) ;
541   SCRUTE(item->text(1)) ;
542   ASSERT(numeroItem>=0) ;
543   ASSERT((size_t)numeroItem<_serverhistory->length()) ;
544   const Registry::Infos & c_info=(*_serverhistory)[numeroItem];
545   ASSERT( c_info.name!=NULL);
546   
547   if ( !myInfoWindow ) {
548     myInfoWindow  = new InfoWindow( this );
549     myInfoWindow->installEventFilter( this );
550   }
551   QString a = tr( "More about" ) + QString( " " ) + QString( c_info.name );
552   myInfoWindow->setCaption(a);
553   myInfoWindow->setText( RegWidget::setlongText( c_info ) );
554   myInfoWindow->show();
555   myInfoWindow->raise();
556   myInfoWindow->setActiveWindow();
557
558   blockSignals( false ); // enabling signals again
559
560   END_OF("slotHistoryChanged()") ;
561   return ;
562 }
563
564 /*!
565   Constructor
566 */
567 InfoWindow::InfoWindow( QWidget* parent, const char* name )
568      : QMainWindow( parent, name, WType_TopLevel | WDestructiveClose )
569 {
570   BEGIN_OF("InfoWindow");
571   myTextView = new QTextView( this, "myTextView" );
572   setCentralWidget( myTextView );
573   setMinimumSize( 450, 250 );
574   END_OF("InfoWindow");
575 }
576
577 /*!
578   Sets text
579 */
580 void InfoWindow::setText( const QString& text )
581 {
582   myTextView->setText( text );
583 }
584
585 static const char* SEPARATOR    = ":";
586
587 QString findFile( QString filename )
588 {
589   QString dir;
590   char* cenv;
591   
592   // Try ${HOME}/.salome/resources directory
593   cenv = getenv( "HOME" );
594   if ( cenv ) {
595     dir.sprintf( "%s", cenv );
596     if ( !dir.isEmpty() ) {
597       dir = addSlash(dir) ;
598       dir = dir + ".salome" ;
599       dir = addSlash(dir) ;
600       dir = dir + "resources" ;
601       dir = addSlash(dir) ;
602       QFileInfo fileInfo( dir + filename );
603       if ( fileInfo.isFile() && fileInfo.exists() )
604         return fileInfo.filePath();
605     }
606   }
607   // Try ${SALOME_SITE_DIR}/share/salome/resources directory
608   cenv = getenv( "SALOME_SITE_DIR" );
609   if ( cenv ) {
610     dir.sprintf( "%s", cenv );
611     if ( !dir.isEmpty() ) {
612       dir = addSlash(dir) ;
613       dir = dir + "share" ;
614       dir = addSlash(dir) ;
615       cenv = getenv("SALOME_SITE_NAME");
616       if (cenv)  dir = dir + cenv;
617       else       dir = dir + "salome" ;
618       dir = addSlash(dir) ;
619       dir = dir + "resources" ;
620       dir = addSlash(dir) ;
621       QFileInfo fileInfo( dir + filename );
622       if ( fileInfo.isFile() && fileInfo.exists() )
623         return fileInfo.filePath();
624     }
625   }
626   // Try ${KERNEL_ROOT_DIR}/share/salome/resources directory
627   cenv = getenv( "KERNEL_ROOT_DIR" );
628   if ( cenv ) {
629     dir.sprintf( "%s", cenv );
630     if ( !dir.isEmpty() ) {
631       dir = addSlash(dir) ;
632       dir = dir + "share" ;
633       dir = addSlash(dir) ;
634       dir = dir + "salome" ;
635       dir = addSlash(dir) ;
636       dir = dir + "resources" ;
637       dir = addSlash(dir) ;
638       QFileInfo fileInfo( dir + filename );
639       if ( fileInfo.isFile() && fileInfo.exists() )
640         return fileInfo.filePath();
641     }
642   }
643   // Try CSF_SaloameResources env.var directory ( or directory list )
644   cenv = getenv( "CSF_SalomeResources" );
645   if ( cenv ) {
646     dir.sprintf( "%s", cenv );
647     if ( !dir.isEmpty() ) {
648       QStringList dirList = QStringList::split( SEPARATOR, dir, false ); // skip empty entries
649       for ( int i = 0; i < dirList.count(); i++ ) {
650         QFileInfo fileInfo( addSlash( dirList[ i ] ) + filename );
651         if ( fileInfo.isFile() && fileInfo.exists() )
652           return fileInfo.filePath();
653       }
654     }
655   }
656   return filename;
657 }
658 QString addSlash( const QString& path )
659 {
660   if (!path.isNull()) {
661 #ifdef WNT
662     QChar slash ('\\');
663 #else
664     QChar slash ('/');
665 #endif
666     if ( path.at(path.length()-1) != slash )
667       return path + slash;
668   }
669   return path;
670 }