Salome HOME
Merge branch 'V8_0_0_BR'
[modules/gui.git] / src / Qtx / QtxWebBrowser.cxx
1 // Copyright (C) 2007-2015  CEA/DEN, EDF R&D, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  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, or (at your option) any later version.
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 // File:      QtxWebBrowser.cxx
24 // Author:    Roman NIKOLAEV
25
26 #include "QtxWebBrowser.h"
27 #include "QtxResourceMgr.h"
28 #include "QtxSearchTool.h"
29
30 #include <QApplication>
31 #include <QButtonGroup>
32 #include <QCheckBox>
33 #include <QFileDialog>
34 #include <QFileInfo>
35 #include <QGridLayout>
36 #include <QHBoxLayout>
37 #include <QLabel>
38 #include <QMenu>
39 #include <QMenuBar>
40 #include <QMessageBox>
41 #include <QPushButton>
42 #include <QRadioButton>
43 #include <QStatusBar>
44 #include <QToolBar>
45 #include <QVBoxLayout>
46 #include <QWebView>
47 #include <QProcess>
48
49 namespace
50 {
51   bool isLocalFile( const QUrl& url )
52   {
53     QFileInfo fi( url.path() );
54     return fi.exists();
55   }
56 }
57
58 /*!
59   \class QtxWebBrowser::Searcher
60   \brief A class is used with QtxSearchTool in order to search text within the web page 
61   \internal
62 */
63
64 class QtxWebBrowser::Searcher : public QtxSearchTool::Searcher
65 {
66 public:
67   Searcher( QWebView* );
68   ~Searcher();
69
70   bool find( const QString&, QtxSearchTool* );
71   bool findNext( const QString&, QtxSearchTool* );
72   bool findPrevious( const QString&, QtxSearchTool* );
73   bool findFirst( const QString&, QtxSearchTool* );
74   bool findLast( const QString&, QtxSearchTool* );
75
76 private:
77   QWebView* myView;
78 };
79
80 /*!
81   \brief Constructor
82   \param view web view
83   \internal
84 */
85 QtxWebBrowser::Searcher::Searcher( QWebView* view ) : myView( view )
86 {
87 }
88
89 /*!
90   \brief Destructor
91   \internal
92 */
93 QtxWebBrowser::Searcher::~Searcher()
94 {
95 }
96
97 /*!
98   \brief Find specified text
99   \param text text being searched
100   \param st search tool
101   \return \c true if text has been found or \c false otherwise
102   \internal
103 */
104 bool QtxWebBrowser::Searcher::find( const QString& text, QtxSearchTool* st )
105 {
106   QWebPage::FindFlags fl = 0;
107   if ( st->isCaseSensitive() ) fl = fl | QWebPage::FindCaseSensitively;
108   if ( st->isSearchWrapped() ) fl = fl | QWebPage::FindWrapsAroundDocument;
109   return myView->findText( text, fl );
110 }
111
112 /*!
113   \brief Find next entry of specified text starting from the current position
114   \param text text being searched
115   \param st search tool
116   \return \c true if text has been found or \c false otherwise
117   \internal
118 */
119 bool QtxWebBrowser::Searcher::findNext( const QString& text, QtxSearchTool* st )
120 {
121   return find( text, st );
122 }
123
124 /*!
125   \brief Find previous entry of specified text starting from the current position
126   \param text text being searched
127   \param st search tool
128   \return \c true if text has been found or \c false otherwise
129   \internal
130 */
131 bool QtxWebBrowser::Searcher::findPrevious( const QString& text, QtxSearchTool* st )
132 {
133   QWebPage::FindFlags fl = QWebPage::FindBackward;
134   if ( st->isCaseSensitive() ) fl = fl | QWebPage::FindCaseSensitively;
135   if ( st->isSearchWrapped() ) fl = fl | QWebPage::FindWrapsAroundDocument;
136   return myView->findText( text, fl );
137 }
138
139 /*!
140   \brief Find first entry of specified text; does nothing in this implementation
141   \param text text being searched
142   \param st search tool
143   \return \c true if text has been found or \c false otherwise
144   \internal
145 */
146 bool QtxWebBrowser::Searcher::findFirst( const QString& /*text*/, QtxSearchTool* /*st*/ )
147 {
148   return false;
149 }
150
151 /*!
152   \brief Find last entry of specified text; does nothing in this implementation
153   \param text text being searched
154   \param st search tool
155   \return \c true if text has been found or \c false otherwise
156   \internal
157 */
158 bool QtxWebBrowser::Searcher::findLast( const QString& /*text*/, QtxSearchTool* /*st*/)
159 {
160   return false;
161 }
162
163
164 /*!
165   \class QtxWebBrowser::Downloader
166   \brief A dialog box that is used to process file links
167   \internal
168 */
169
170 /*!
171   \brief Constructor
172   \param fileName name of the file being opened
173   \param action default action to be used for the file
174   \param program default program to be used to open the file
175   \param parent parent widget
176   \internal
177 */
178 QtxWebBrowser::Downloader::Downloader( const QString& fileName, int action, const QString& program, QWidget* parent )
179   : QDialog( parent ), myProgram( program )
180 {
181   setModal( true );
182   setWindowTitle( QtxWebBrowser::tr( "Open URL" ) );
183   setSizeGripEnabled( true );
184
185   myFileName = new QLabel( this );
186   QRadioButton* rbOpen = new QRadioButton( QtxWebBrowser::tr( "Open in" ), this );
187   QRadioButton* rbSave = new QRadioButton( QtxWebBrowser::tr( "Save file" ), this );
188   myBrowse = new QPushButton( QtxWebBrowser::tr( "&Browse..." ),     this );
189   myRepeat = new QCheckBox( QtxWebBrowser::tr( "Use this program for all files of this type" ), this );
190
191   myAction = new QButtonGroup( this );
192   myAction->addButton( rbOpen, mOpen );
193   myAction->addButton( rbSave, mSave );
194
195   QPushButton* btnOk     = new QPushButton( QtxWebBrowser::tr( "&OK" ),     this );
196   QPushButton* btnCancel = new QPushButton( QtxWebBrowser::tr( "&Cancel" ), this );
197
198   QFont f = myFileName->font(); f.setBold( true ); myFileName->setFont( f );
199
200   QHBoxLayout* btnLayout = new QHBoxLayout;
201   btnLayout->addWidget( btnOk );
202   btnLayout->addStretch();
203   btnLayout->addWidget( btnCancel );
204
205   QGridLayout* l = new QGridLayout( this );
206   l->addWidget( new QLabel( QtxWebBrowser::tr( "You are opening the file" ), this ), 
207                             0, 0, 1, 4 );
208   l->addWidget( myFileName, 1, 1, 1, 3 );
209   l->addWidget( new QLabel( QtxWebBrowser::tr( "Please choose the action to be done" ), this ), 
210                             3, 0, 1, 4 );
211   l->addWidget( rbOpen,     4, 1, 1, 1 );
212   l->addWidget( myBrowse,   4, 2, 1, 1 );
213   l->addWidget( rbSave,     5, 1, 1, 3 );
214   l->addWidget( myRepeat,   6, 1, 1, 3 );
215   l->addLayout( btnLayout,  7, 0, 1, 4 );
216   l->setRowMinimumHeight( 2, 10 );
217
218   connect( myAction,  SIGNAL( buttonClicked( int ) ), this, SLOT( setAction( int ) ) );
219   connect( myBrowse,  SIGNAL( clicked() ), this, SLOT( browse() ) );
220   connect( btnOk,     SIGNAL( clicked() ), this, SLOT( accept() ) );
221   connect( btnCancel, SIGNAL( clicked() ), this, SLOT( reject() ) );
222
223   myFileName->setText( QFileInfo( fileName ).fileName() );
224   myAction->button( action )->click();
225 }
226
227 /*!
228   \brief Destructor
229 */
230 QtxWebBrowser::Downloader::~Downloader()
231 {
232 }
233
234 /*!
235   \brief Get action selected by the user
236   \return action being selected:
237   - 0: open file
238   - 1: save file
239 */
240 int QtxWebBrowser::Downloader::action() const
241 {
242   return myAction->checkedId();
243 }
244
245 /*!
246   \brief Get "repeat action for all such files" flag status
247   \return \c true if chosen action should be automatically done for all files of given type
248   or \c false otherwise
249 */
250 bool QtxWebBrowser::Downloader::isRepeatAction() const
251 {
252   return myRepeat->isChecked();
253 }
254
255 /*!
256   \brief Get program to be used to open chosen file
257   \return path to the program
258 */
259 QString QtxWebBrowser::Downloader::program() const
260 {
261   return myProgram;
262 }
263
264 /*!
265   \brief Set current action
266   \param action action to be done for the file:
267   - 0: open file
268   - 1: save file
269 */
270 void QtxWebBrowser::Downloader::setAction( int action )
271 {
272   myBrowse->setEnabled( action == mOpen );
273 }
274
275 /*!
276   \brief Browse program to be used to open the file
277 */
278 void QtxWebBrowser::Downloader::browse()
279 {
280   QString program = QFileDialog::getOpenFileName( this, QtxWebBrowser::tr( "Choose program" ), myProgram );
281   if ( !program.isEmpty() ) myProgram = program;
282 }
283
284
285 /*!
286   \class QtxWebBrowser
287
288   \brief The QtxWebBrowser provides a window that can display html pages from local file system.
289   
290   Only one instance of the QtxWebBrowser class can be created. To access the browser 
291   window, use static method QtxWebBrowser::webBrowser(), which creates an
292   instance of the QtxWebBrowser widget (if it is not yet created) and returns a
293   pointer to it.
294
295   You should not destroy this instance - it is done automatically after
296   closing of the browser window. To close window programmatically use 
297   method close().
298
299   Optionally resource manager can be specified to automatically store
300   action (open/save) and program to be used to download files in the
301   application preferences.
302
303   The following sample demonstrates how to use web browser.
304   In this code the browser window is created, /data/index.html file is opened
305   and scrolled to the "anchor1" anchor on this page.
306
307   \code
308   // initialize application
309   // - set resource manager
310   QtxWebBrowser::setResourceMgr(myResourceMgr);
311   // ...
312   // show HTML page
313   QtxWebBrowser::loadUrl("file:///data/index.html", "anchor1");
314   \endcode
315 */
316
317 //! The only one instance of web browser
318 QtxWebBrowser* QtxWebBrowser::myBrowser = 0;
319
320 //! Resources manager
321 QtxResourceMgr* QtxWebBrowser::myResourceMgr = 0;
322
323 /*!
324   \brief Constructor.
325   Construct the web browser.
326 */
327 QtxWebBrowser::QtxWebBrowser( ) : QMainWindow( 0 )
328 {
329   Q_INIT_RESOURCE( Qtx );
330
331   setAttribute( Qt::WA_DeleteOnClose );
332   statusBar();
333
334   QWidget* frame = new QWidget( this );
335
336   myWebView = new QWebView( frame );
337
338   myWebView->pageAction( QWebPage::Copy )->setShortcut( QKeySequence::Copy );
339   myWebView->addAction( myWebView->pageAction( QWebPage::Copy ) );
340   myWebView->pageAction( QWebPage::OpenLinkInNewWindow )->setVisible( false );
341   myWebView->pageAction( QWebPage::Back )->setText( tr( "Go Back" ) );
342   myWebView->pageAction( QWebPage::Forward )->setText( tr( "Go Forward" ) );
343   myWebView->pageAction( QWebPage::Reload )->setText( tr( "Refresh" ) );
344
345   myWebView->page()->setLinkDelegationPolicy( QWebPage::DelegateAllLinks );
346
347   myFindPanel = new QtxSearchTool( frame, myWebView,
348                                    QtxSearchTool::Basic | QtxSearchTool::Case | QtxSearchTool::Wrap, 
349                                    Qt::Horizontal );
350   myFindPanel->setFrameStyle( QFrame::NoFrame | QFrame::Plain );
351   myFindPanel->setActivators( QtxSearchTool::SlashKey );
352   myFindPanel->setSearcher( new Searcher( myWebView ) );
353   myFindPanel->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
354
355   QToolBar* toolbar = addToolBar( tr( "Navigation" ) );
356   toolbar->addAction( myWebView->pageAction( QWebPage::Back ) );
357   toolbar->addAction( myWebView->pageAction( QWebPage::Forward ) );
358   toolbar->addAction( myWebView->pageAction( QWebPage::Reload ) );
359
360   QMenu* fileMenu = menuBar()->addMenu( tr( "&File" ) );
361   fileMenu->addAction( QPixmap( ":/images/open.png" ), tr( "&Open..." ), 
362                        this, SLOT( open() ),
363                        QKeySequence( QKeySequence::Open ) );
364   fileMenu->addSeparator();
365   fileMenu->addAction( myWebView->pageAction( QWebPage::Back ) );
366   fileMenu->addAction( myWebView->pageAction( QWebPage::Forward ) );
367   fileMenu->addAction( myWebView->pageAction( QWebPage::Reload ) );
368   fileMenu->addSeparator();
369   fileMenu->addAction( tr( "&Find in text..." ),
370                        myFindPanel, SLOT( find() ),
371                        QKeySequence( QKeySequence::Find ) );
372   fileMenu->addAction( tr( "&Find next" ),
373                        myFindPanel, SLOT( findNext() ),
374                        QKeySequence( QKeySequence::FindNext ) );
375   fileMenu->addAction( tr( "&Find previous" ),
376                        myFindPanel, SLOT( findPrevious() ),
377                        QKeySequence( QKeySequence::FindPrevious ) );
378   fileMenu->addSeparator();
379   fileMenu->addAction( QPixmap( ":/images/close.png" ), tr( "&Close" ),
380                        this, SLOT( close() ) );
381
382   QMenu* helpMenu = menuBar()->addMenu( tr( "&Help" ) );
383   helpMenu->addAction( tr( "&About..." ),
384                        this, SLOT( about() ) );
385   
386   QVBoxLayout* main = new QVBoxLayout( frame );
387   main->addWidget( myWebView );
388   main->addWidget( myFindPanel );
389   main->setMargin( 0 );
390   main->setSpacing( 3 );
391
392   connect( myWebView, SIGNAL( titleChanged( QString ) ), SLOT( adjustTitle() ) ); 
393   connect( myWebView, SIGNAL( loadFinished( bool ) ),    SLOT( finished( bool ) ) ); 
394   connect( myWebView, SIGNAL( linkClicked( QUrl ) ),     SLOT( linkClicked( QUrl ) ) ); 
395   connect( myWebView->page(), SIGNAL( linkHovered( QString, QString, QString ) ), 
396            SLOT( linkHovered( QString, QString, QString ) ) ); 
397   connect( myWebView->pageAction( QWebPage::DownloadLinkToDisk ), SIGNAL( triggered() ),
398            SLOT( linkAction() ) );
399   disconnect( myWebView->pageAction( QWebPage::OpenLink ), 0, 0, 0 );
400   connect( myWebView->pageAction( QWebPage::OpenLink ), SIGNAL( triggered() ),
401            SLOT( linkAction() ) );
402   
403   setCentralWidget( frame );
404   setFocusProxy( myWebView );
405   setWindowIcon( QPixmap( ":/images/appicon.png" ) );
406   adjustTitle();
407 }
408
409 /*!
410   \brief Destructor.
411 */
412 QtxWebBrowser::~QtxWebBrowser()
413 {
414   myBrowser = 0;
415 }
416
417 /*!
418   \brief Return the only instance of the QtxWebBrowser
419   \return instance of the QtxWebBrowser
420 */
421 QtxWebBrowser* QtxWebBrowser::webBrowser()
422 {
423   if ( !myBrowser )
424     myBrowser = new QtxWebBrowser();
425   return myBrowser;
426 }
427
428 /*!
429   \brief Load given url address and optional scroll to the specified anchor
430   \param url an url address to load
431   \param anchor an anchor to scroll page to
432 */
433 void QtxWebBrowser::loadUrl( const QString& url, const QString& anchor )
434 {
435   QString anUrl = url;
436   if( !anchor.isEmpty() ) anUrl += "#" + anchor;
437
438   Qtx::alignWidget( webBrowser(), (QWidget*)QApplication::desktop(), Qtx::AlignCenter );
439
440   QtxWebBrowser* browser = webBrowser();
441   browser->show();
442   browser->load( anUrl );
443   browser->setFocus();
444   browser->activateWindow();
445   browser->raise();
446 }
447
448 /*!
449   \brief Shutdown help browser
450 */
451 void QtxWebBrowser::shutdown()
452 {
453   if ( myBrowser )
454     myBrowser->close();
455 }
456
457 /*!
458   \brief Set resource manager
459   \param resMgr resource manager
460 */
461 void QtxWebBrowser::setResourceMgr( QtxResourceMgr* resMgr )
462 {
463   myResourceMgr = resMgr;
464 }
465
466 /*!
467   \brief Get resource manager
468   \return resource manager
469 */
470 QtxResourceMgr* QtxWebBrowser::resourceMgr() const
471 {
472   return myResourceMgr;
473 }
474
475 /*!
476   Shows About dialog box
477 */
478 void QtxWebBrowser::about()
479 {
480   QMessageBox::about( this, tr( "About %1" ).arg( tr( "Help Browser" ) ),
481                       QString( "SALOME %1" ).arg( tr( "Help Browser" ) ) );
482 }
483
484 /*!
485   \brief Called when users activated any link at the page
486   \param url URL being clicked
487   \internal
488 */
489 void QtxWebBrowser::linkClicked( const QUrl& url )
490 {
491   myWebView->page()->setLinkDelegationPolicy( QWebPage::DontDelegateLinks );
492   myWebView->load( url );
493   myWebView->page()->setLinkDelegationPolicy( QWebPage::DelegateAllLinks );
494 }
495
496 /*!
497   \brief Called when link is hovered
498   \param link link being hovered
499   \param title link title (if it is specified in the markup)
500   \param content provides text within the link element, e.g., text inside an HTML anchor tag
501   \internal
502 */
503 void QtxWebBrowser::linkHovered( const QString& link, const QString& /*title*/, const QString& /*context*/ )
504 {
505   QUrl url = link;
506   if ( !link.isEmpty() && isLocalFile( url ) ) myLastUrl = url;
507   statusBar()->showMessage( link );
508 }
509
510 /*!
511   \brief Update title of the window
512   \internal
513 */
514 void QtxWebBrowser::adjustTitle()
515 {
516   QString title = tr( "Help Browser" );
517   if ( !myWebView->title().isEmpty() ) title += QString( " [%1]" ).arg( myWebView->title() );
518   setWindowTitle( title );
519 }
520
521 /*
522   \brief Called when link is processed by browser
523   \param ok operation status: \c true is URL is correctly processed, \c false otherwise
524 */
525 void QtxWebBrowser::finished( bool ok )
526 {
527   if ( !ok && !myLastUrl.isEmpty() ) {
528     if ( isLocalFile( myLastUrl ) ) {
529       QString filename = myLastUrl.path();
530       QString extension = QFileInfo( filename ).suffix();
531       if ( extension == "html" || extension == "htm" ) return;
532       openLink( filename );
533     }
534   }
535 }
536
537 /*
538   \brief Called when link is processed from browser via popup menu actions
539 */
540 void QtxWebBrowser::linkAction()
541 {
542   QObject* s = sender();
543   if ( s == myWebView->pageAction( QWebPage::DownloadLinkToDisk ) ) {
544     saveLink( myLastUrl.path() );
545   }
546   else if ( s == myWebView->pageAction( QWebPage::OpenLink ) ) {
547     QString fileName  = myLastUrl.path();
548     QString extension = QFileInfo( fileName ).suffix();
549     if ( extension != "html" && extension != "htm" ) {
550       openLink( fileName, true );
551     }
552     else {
553       linkClicked( myLastUrl );
554     }
555   }
556 }
557
558 /*!
559   \brief Open file
560   \param fileName link to the file being opened
561   Opens dialog box to allow the user to choose the program to be used to open the file.
562 */
563 void QtxWebBrowser::openLink( const QString& fileName, bool force )
564 {
565   QString extension = QFileInfo( fileName ).suffix();
566   int defAction = Downloader::mOpen;
567   bool defRepeat = false;
568   QString defProgram;
569   QString resSection   = QString( "%1:%2" ).arg( "web_browser" ).arg( extension );
570   QString actionParam  = "action";
571   QString programParam = "program";
572   QString repeatParam  = "repeat";
573   
574   if ( !extension.isEmpty() && myResourceMgr ) {
575     defAction  = myResourceMgr->integerValue( resSection, actionParam, defAction );
576     defRepeat  = myResourceMgr->booleanValue( resSection, repeatParam, defRepeat );
577     defProgram = myResourceMgr->stringValue( resSection, programParam, defProgram );
578   }
579   
580   if ( force || !defRepeat || ( defAction == Downloader::mOpen && defProgram.isEmpty() ) ) {
581     Downloader downloader( fileName, defAction, defProgram, this );
582     if ( !downloader.exec() ) return;
583     defAction  = downloader.action();
584     defRepeat  = downloader.isRepeatAction();
585     defProgram = downloader.program();
586     if ( myResourceMgr ) {
587       myResourceMgr->setValue( resSection, actionParam, defAction );
588       myResourceMgr->setValue( resSection, repeatParam, defRepeat );
589       if ( defAction == Downloader::mOpen )
590         myResourceMgr->setValue( resSection, programParam, defProgram );
591     }
592   }
593   switch( defAction ) {
594   case Downloader::mOpen:
595     if ( !defProgram.isEmpty() ) {
596       QStringList parameters;
597 #ifdef WIN32
598       QString cmd = defProgram;
599 #else
600       // If Salome Qt version is lower than the system one, on KDE an unresolved symbol is raised
601       // In this case, we can try to launch the pdf viewer after unsetting the LD_LIBRARY_PATH environnement variable
602       QString cmd = "env";
603       parameters << "LD_LIBRARY_PATH=/usr/lib:/usr/lib64";
604       parameters << defProgram;
605 #endif
606       parameters << QFileInfo( myLastUrl.path() ).absoluteFilePath();
607       QProcess::startDetached( cmd, parameters );
608     }
609     break;
610   case Downloader::mSave:
611     {
612       saveLink( fileName );
613     }
614     break;
615   default:
616     break;
617   }
618 }
619
620 /*!
621   \brief Load URL
622   \param url path to the file to be opened in the browser
623 */
624 void QtxWebBrowser::load( const QString& link )
625 {
626   QString linkPath = link;
627   linkPath.replace('\\', '/');
628   QUrl url = linkPath;
629   if ( !url.isEmpty() ) {
630     if ( url.scheme().isEmpty() ) url.setScheme( "file" );
631     myWebView->load( url );
632   }
633 }
634
635 /*!
636   \brief Save file
637   \param fileName link to the file being saved
638   Shows "Save file" standard dialog box to allow user to choose where to save the file.
639 */
640 void QtxWebBrowser::saveLink( const QString& fileName )
641 {
642   QString newFileName = QFileDialog::getSaveFileName( this, tr( "Save file" ), fileName, 
643                                                       QString( "*.%1" ).arg( QFileInfo( fileName ).suffix() ) );
644   if ( !newFileName.isEmpty() && 
645        QFileInfo( newFileName ).canonicalFilePath() != QFileInfo( fileName ).canonicalFilePath() ) {
646     QFile toFile( newFileName );
647     QFile fromFile( fileName );
648     if ( toFile.exists() && !toFile.remove() || !fromFile.copy( newFileName ) )
649       QMessageBox::warning( this, tr( "Error"), tr( "Can't save file:\n%1" ).arg( newFileName ) );
650   }
651 }
652
653 /*!
654   \brief Open file
655   Shows "Open file" standard dialog box to allow user to choose file to open.
656 */
657 void QtxWebBrowser::open()
658 {
659   QString url;
660   if ( isLocalFile( myWebView->url() ) ) url = myWebView->url().path();
661   url = QFileDialog::getOpenFileName( this, tr( "Open file" ), url, "HTML files (*.html *.htm);; All files (*)" );
662   if ( !url.isEmpty() ) load( url );
663 }