HelpView.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
00003  *                                                                         *
00004  *   This file is part of the FreeCAD CAx development system.              *
00005  *                                                                         *
00006  *   This library is free software; you can redistribute it and/or         *
00007  *   modify it under the terms of the GNU Library General Public           *
00008  *   License as published by the Free Software Foundation; either          *
00009  *   version 2 of the License, or (at your option) any later version.      *
00010  *                                                                         *
00011  *   This library  is distributed in the hope that it will be useful,      *
00012  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00014  *   GNU Library General Public License for more details.                  *
00015  *                                                                         *
00016  *   You should have received a copy of the GNU Library General Public     *
00017  *   License along with this library; see the file COPYING.LIB. If not,    *
00018  *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
00019  *   Suite 330, Boston, MA  02111-1307, USA                                *
00020  *                                                                         *
00021  ***************************************************************************/
00022 
00023 
00024 #include "PreCompiled.h"
00025 #ifndef _PreComp_
00026 # include <QApplication>
00027 # include <QContextMenuEvent>
00028 # include <QDropEvent>
00029 # include <QGroupBox>
00030 # include <QHBoxLayout>
00031 # include <QHttp>
00032 # include <QLabel>
00033 # include <QMenu>
00034 # include <QMessageBox>
00035 # include <QMimeData>
00036 # include <QProcess>
00037 # include <QTimerEvent>
00038 # include <QToolButton>
00039 # include <QToolTip>
00040 # include <QWhatsThis>
00041 #endif
00042 
00043 #include "HelpView.h"
00044 #include "Application.h"
00045 #include "BitmapFactory.h"
00046 #include "FileDialog.h"
00047 #include "WhatsThis.h"
00048 #include "Action.h"
00049 #include "Command.h"
00050 
00051 
00052 using namespace Gui;
00053 using namespace Gui::DockWnd;
00054 
00055 namespace Gui {
00056 namespace DockWnd {
00057 
00058 struct TextBrowserResources
00059 {
00060   QUrl url;
00061   int type;
00062 };
00063 
00064 class TextBrowserPrivate
00065 {
00066 public:
00067   bool bw, fw;
00068   int toolTipId;
00069   QString toolTip;
00070   QHttp* http;
00071   QUrl source;
00072   QList<TextBrowserResources> resources;
00073 
00074   TextBrowserPrivate() : bw(false), fw(false), toolTipId(0)
00075   {
00076     http = new QHttp;
00077   }
00078   
00079   ~TextBrowserPrivate()
00080   {
00081     delete http;
00082   }
00083 };
00084 
00085 } // namespace DockWnd
00086 } // namespace Gui
00087 
00088 /* TRANSLATOR Gui::DockWnd::TextBrowser */
00089 
00090 TextBrowser::TextBrowser(QWidget * parent)
00091   : QTextBrowser(parent)
00092 {
00093   d = new TextBrowserPrivate;
00094 
00095   setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00096   setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
00097 
00098   setAcceptDrops( TRUE );
00099   viewport()->setAcceptDrops( TRUE );
00100 
00101   connect( d->http, SIGNAL(done(bool)), this, SLOT(done(bool)));
00102   connect( d->http, SIGNAL(stateChanged(int)), this, SLOT(onStateChanged(int)));
00103   connect( d->http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)), this, SLOT(onResponseHeaderReceived(const QHttpResponseHeader &)));
00104   connect( this, SIGNAL(highlighted(const QString&)), this, SLOT(onHighlighted(const QString&)));
00105   connect( this, SIGNAL(backwardAvailable(bool)), this, SLOT(setBackwardAvailable(bool)));
00106   connect( this, SIGNAL(forwardAvailable (bool)), this, SLOT(setForwardAvailable (bool)));
00107 }
00108 
00109 TextBrowser::~TextBrowser()
00110 {
00111   delete d;
00112 }
00113 
00114 QString TextBrowser::findUrl(const QUrl &name) const
00115 {
00116   QString fileName = name.toLocalFile();
00117   QFileInfo fi(fileName);
00118   if (fi.isAbsolute())
00119     return fileName;
00120 
00121   QString slash(QLatin1String("/"));
00122   QStringList spaths = searchPaths();
00123   for (QStringList::ConstIterator it = spaths.begin(); it != spaths.end(); ++it) {
00124     QString path = *it;
00125     if (!path.endsWith(slash))
00126       path.append(slash);
00127     path.append(fileName);
00128     fi.setFile(path);
00129     if (fi.isReadable())
00130       return path;
00131   }
00132 
00133   QUrl src = source();
00134   if (src.isEmpty())
00135     return fileName;
00136 
00137   QFileInfo path(QFileInfo(src.toLocalFile()).absolutePath(), fileName);
00138   return path.absoluteFilePath();
00139 }
00140 
00145 QVariant TextBrowser::loadResource ( int type, const QUrl& url )
00146 {
00147   QString name = url.toString();
00148   if (url.scheme() == QLatin1String("http") ||
00149       d->source.scheme() == QLatin1String("http")) {
00150     return loadHttpResource(type, url);
00151   } else { // file scheme
00152     return loadFileResource(type, url);
00153   }
00154 }
00155 
00160 void TextBrowser::setSource (const QUrl& url)
00161 {
00162   bool relativeUrl = url.isRelative();
00163   if (!relativeUrl)
00164     d->source = url; // last set absolute url
00165   QString name = url.toString();
00166   if (url.scheme() == QLatin1String("http")) {
00167     // start the download but do not call setSource() of the base
00168     // class because we must wait until the data are available.
00169     // The slot done() is invoked automatically then. 
00170     d->http->setHost(url.host());
00171     d->http->get(url.path(), 0);
00172   } else if (d->source.scheme() == QLatin1String("http")) {
00173     // relative hyperlink in previously downloaded a HTML page 
00174     d->source = d->source.resolved(url);
00175     d->http->get(url.path(), 0);
00176   } else {
00177     QUrl resolved = url;
00178 #if defined (Q_OS_WIN)
00179     if (url.scheme() == QLatin1String("file") && !url.isRelative()) {
00180       QString auth = url.authority();
00181       QString path = url.path();
00182       //If we click on a hyperlink with a reference to an absolute file name
00183       //then we get a string that cannot be used to open the file. So we try 
00184       //to reproduce the original url.
00185       if (!auth.isEmpty() && !path.isEmpty()) {
00186         QString fileName = auth + QLatin1Char(':') + path;
00187         resolved = QUrl::fromLocalFile(fileName);
00188       }
00189     }
00190 #endif
00191     QTextBrowser::setSource(resolved);
00192   }
00193 }
00194 
00199 QVariant TextBrowser::loadFileResource(int type, const QUrl& name)
00200 {
00201   QVariant data;
00202   QUrl resolved = name;
00203   if (!QFileInfo(name.toLocalFile()).isAbsolute() && QFileInfo(d->source.toLocalFile()).isAbsolute())
00204     resolved = d->source.resolved(name);
00205   QString fileName = findUrl(resolved);
00206   QFile file(fileName);
00207   if (file.open(QFile::ReadOnly)) {
00208     data = file.readAll();
00209     file.close();
00210   } else if (type == QTextDocument::HtmlResource) {
00211       data = QString::fromAscii(
00212       "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">"
00213       "<html>"
00214       "<head>"
00215       "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-16\">"
00216       "<title>Error</title>"
00217       "</head>"
00218       "<body>"
00219       "<h1>%1</h1>"
00220       "<div><p><strong>%2</strong></p>"
00221       "</div></body>"
00222       "</html>").arg(tr("Could not open file.")).arg(tr("You tried to access the address %1 which is currently unavailable. "
00223       "Please make sure that the URL exists and try reloading the page.").arg(name.toString()));
00224   } else if (type == QTextDocument::ImageResource) {
00225     static const char * empty_xpm[] = {
00226           "24 24 1 1",
00227           ". c #C0C0C0",
00228           "........................",
00229           "........................",
00230           "........................",
00231           "........................",
00232           "........................",
00233           "........................",
00234           "........................",
00235           "........................",
00236           "........................",
00237           "........................",
00238           "........................",
00239           "........................",
00240           "........................",
00241           "........................",
00242           "........................",
00243           "........................",
00244           "........................",
00245           "........................",
00246           "........................",
00247           "........................",
00248           "........................",
00249           "........................",
00250           "........................",
00251           "........................"};
00252     QPixmap px(empty_xpm);
00253     data.setValue<QPixmap>(px);
00254   }
00255 
00256   return data;
00257 }
00258 
00264 QVariant TextBrowser::loadHttpResource(int type, const QUrl& name)
00265 {
00266   QVariant data;
00267   if (type == QTextDocument::ImageResource) {
00268     TextBrowserResources res;
00269     res.url = name;
00270     res.type = type;
00271     d->resources.push_back(res);
00272     
00273     static const char * empty_xpm[] = {
00274           "24 24 1 1",
00275           ". c #C0C0C0",
00276           "........................",
00277           "........................",
00278           "........................",
00279           "........................",
00280           "........................",
00281           "........................",
00282           "........................",
00283           "........................",
00284           "........................",
00285           "........................",
00286           "........................",
00287           "........................",
00288           "........................",
00289           "........................",
00290           "........................",
00291           "........................",
00292           "........................",
00293           "........................",
00294           "........................",
00295           "........................",
00296           "........................",
00297           "........................",
00298           "........................",
00299           "........................"};
00300     QPixmap px(empty_xpm);
00301     data.setValue<QPixmap>(px);
00302     return data;
00303   }
00304 
00305   if (d->http->error() == QHttp::NoError) {
00306     return d->http->readAll();
00307   } else {
00308     if (type == QTextDocument::HtmlResource) {
00309         data = QString::fromAscii(
00310         "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">"
00311         "<html>"
00312         "<head>"
00313         "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-16\">"
00314         "<title>Error</title>"
00315         "</head>"
00316         "<body>"
00317         "<h1>%1</h1>"
00318         "<div><p><strong>%2</strong></p>"
00319         "</div></body>"
00320         "</html>").arg(d->http->errorString()).arg(tr("You tried to access the address %1 which is currently unavailable. "
00321         "Please make sure that the URL exists and try reloading the page.").arg(name.toString()));
00322     }
00323   }
00324 
00325   return data;
00326 }
00327 
00331 void TextBrowser::done( bool /*err*/ )
00332 {
00333   if (d->resources.isEmpty()/* && d->requestId == d->http->currentId()*/) {
00334     // set the HTML text
00335     QTextBrowser::setSource(d->source);
00336   } else {
00337     // add the referenced resource to the document
00338     TextBrowserResources res = d->resources.front();
00339 
00340     QVariant data(d->http->readAll());
00341     document()->addResource(res.type, res.url, data);
00342     viewport()->repaint();
00343     d->resources.pop_front();
00344   }
00345 
00346   // if we need to download further resources start a http request
00347   if (!d->resources.isEmpty()) {
00348     TextBrowserResources res = d->resources.front();
00349     d->http->get(res.url.toString());
00350   } else {
00351     stateChanged(d->source.toString());
00352   }
00353 }
00354 
00358 void TextBrowser::onStateChanged ( int state )
00359 {
00360   switch (state) {
00361     case QHttp::Connecting:
00362       stateChanged(tr("Connecting to %1").arg(d->source.host()));
00363       break;
00364     case QHttp::Sending:
00365       stateChanged(tr("Sending to %1").arg(d->source.host()));
00366       break;
00367     case QHttp::Reading:
00368       stateChanged(tr("Reading from %1").arg(d->source.host()));
00369       break;
00370     case QHttp::Closing:
00371     case QHttp::Unconnected:
00372       if (d->http->error() == QHttp::NoError)
00373         stateChanged(d->source.toString());
00374       else
00375         stateChanged(d->http->errorString());
00376       break;
00377     default:
00378       break;
00379   }
00380 }
00381 
00385 void TextBrowser::onResponseHeaderReceived(const QHttpResponseHeader &responseHeader)
00386 {
00387   if (responseHeader.statusCode() != 200) {
00388     stateChanged(tr("Download failed: %1.").arg(responseHeader.reasonPhrase()));
00389     d->http->abort();
00390   }
00391 }
00392 
00393 void TextBrowser::onHighlighted(const QString& url)
00394 {
00395   if (url.isEmpty() && d->toolTipId != 0) {
00396     killTimer(d->toolTipId);
00397     d->toolTipId = 0;
00398   } else if (!url.isEmpty()) {
00399     d->toolTip = url;
00400     d->toolTipId = startTimer(1000);
00401   } else {
00402     QToolTip::showText(QCursor::pos(), url, this);
00403   }
00404 }
00405 
00406 void TextBrowser::backward()
00407 {
00408   QTextBrowser::backward();
00409   reload();
00410 }
00411 
00412 void TextBrowser::forward()
00413 {
00414   QTextBrowser::forward();
00415   reload();
00416 }
00417 
00418 void TextBrowser::setBackwardAvailable( bool b )
00419 {
00420   d->bw = b;
00421 }
00422 
00423 void TextBrowser::setForwardAvailable( bool b )
00424 {
00425   d->fw = b;
00426 }
00427 
00428 void TextBrowser::timerEvent ( QTimerEvent * e )
00429 {
00430   if (d->toolTipId == e->timerId()) {
00431     QToolTip::showText(QCursor::pos(), d->toolTip, this);
00432     killTimer(d->toolTipId);
00433     d->toolTipId = 0;
00434   }
00435 }
00436 
00437 void TextBrowser::contextMenuEvent ( QContextMenuEvent * e )
00438 {
00439   QMenu* menu = new QMenu(this);
00440 
00441   QAction* prev = menu->addAction(Gui::BitmapFactory().pixmap("back_pixmap"), tr("Previous"), this, SLOT(backward()));
00442   prev->setEnabled(d->bw);
00443 
00444   QAction* next = menu->addAction(Gui::BitmapFactory().pixmap("forward_pixmap"), tr("Forward"), this, SLOT(forward()));
00445   next->setEnabled(d->fw);
00446 
00447   menu->addSeparator();
00448   menu->addAction(Gui::BitmapFactory().pixmap("home_pixmap"), tr("Home"), this, SLOT(home()));
00449   menu->addAction(tr("Refresh"), this, SLOT(reload()));
00450   menu->addSeparator();
00451   menu->addAction(tr("Copy"), this, SLOT(copy()));
00452   menu->addAction(tr("Select all"), this, SLOT(selectAll()));
00453 
00454   menu->exec(e->globalPos());
00455   delete menu;
00456 }
00457 
00458 void TextBrowser::dropEvent(QDropEvent  * e)
00459 {
00460   const QMimeData* mimeData = e->mimeData();
00461   if ( mimeData->hasFormat(QLatin1String("text/x-action-items")) ) {
00462     QByteArray itemData = mimeData->data(QLatin1String("text/x-action-items"));
00463     QDataStream dataStream(&itemData, QIODevice::ReadOnly);
00464 
00465     int ctActions; dataStream >> ctActions;
00466 
00467     // handle the first item only
00468     QString action;
00469     dataStream >> action;
00470 
00471     CommandManager& rclMan = Application::Instance->commandManager();
00472     Command* pCmd = rclMan.getCommandByName(action.toAscii());
00473     if ( pCmd ) {
00474       QString info = pCmd->getAction()->whatsThis();
00475       if ( !info.isEmpty() ) {
00476         // cannot show help to this command
00477         info = QString::fromAscii(
00478         "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">"
00479         "<html>"
00480         "<body bgcolor=white text=black alink=red link=darkblue vlink=darkmagenta>"
00481         "%1"
00482         "</body>"
00483         "</html>" ).arg( info );
00484       } else {
00485         info = QString::fromAscii(
00486         "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">"
00487         "<html>"
00488         "<body bgcolor=white text=black alink=red link=darkblue vlink=darkmagenta>"
00489         "<h2>"
00490         "  %1 '%2'"
00491         "</h2>"
00492         "<hr>"
00493         "</body>"
00494         "</html>" ).arg(tr("No description for")).arg(action);
00495       }
00496 
00497       setHtml( info );
00498     }
00499 
00500     e->setDropAction(Qt::CopyAction);
00501     e->accept();
00502   } else if ( mimeData->hasUrls() ) {
00503     QList<QUrl> uri = mimeData->urls();
00504     QUrl url = uri.front();
00505     setSource(url);
00506 
00507     e->setDropAction(Qt::CopyAction);
00508     e->accept();
00509   } else {
00510     e->ignore();
00511   }
00512 }
00513 
00514 void TextBrowser::dragEnterEvent  (QDragEnterEvent * e)
00515 {
00516   const QMimeData* mimeData = e->mimeData();
00517   if ( mimeData->hasFormat(QLatin1String("text/x-action-items")) )
00518     e->accept();
00519   else if (mimeData->hasUrls())
00520     e->accept();
00521   else
00522     e->ignore();
00523 }
00524 
00525 void TextBrowser::dragMoveEvent( QDragMoveEvent *e )
00526 {
00527   const QMimeData* mimeData = e->mimeData();
00528   if ( mimeData->hasFormat(QLatin1String("text/x-action-items")) )
00529     e->accept();
00530   else if (mimeData->hasUrls())
00531     e->accept();
00532   else
00533     e->ignore();
00534 }
00535 
00536 
00537 // --------------------------------------------------------------------
00538 
00539 /* TRANSLATOR Gui::DockWnd::HelpView */
00540 
00545 HelpView::HelpView( const QString& start,  QWidget* parent )
00546   : QWidget(parent)
00547 {
00548   TextBrowser* browser = new TextBrowser(this);
00549   browser->setFrameStyle( QFrame::Panel | QFrame::Sunken );
00550   // set the start page now
00551   if (!start.isEmpty())
00552     browser->setSource(QUrl::fromLocalFile(start));
00553   
00554   QHBoxLayout* layout = new QHBoxLayout();
00555   layout->setAlignment(Qt::AlignTop);
00556   layout->setSpacing(1);
00557   layout->setMargin (1);
00558 
00559   // create the group box for the layout
00560   QGroupBox* groupBox = new QGroupBox(this);
00561 
00562   // the 'Backward' button
00563   QToolButton* back = new QToolButton( groupBox );
00564   back->setIcon( Gui::BitmapFactory().pixmap("back_pixmap") );
00565   back->setAutoRaise(true);
00566   back->setToolTip(tr("Previous"));
00567 
00568   // the 'Forward' button
00569   QToolButton* forward = new QToolButton( groupBox );
00570   forward->setIcon( Gui::BitmapFactory().pixmap("forward_pixmap") );
00571   forward->setAutoRaise(true);
00572   forward->setToolTip(tr("Next"));
00573 
00574   // the 'Home' button
00575   QToolButton* home = new QToolButton( groupBox );
00576   home->setIcon( Gui::BitmapFactory().pixmap("home_pixmap") );
00577   home->setAutoRaise(true);
00578   home->setToolTip(tr("Home"));
00579 
00580   // the 'Open' button
00581   QToolButton* open = new QToolButton( groupBox );
00582   open->setIcon( Gui::BitmapFactory().pixmap("helpopen") );
00583   open->setAutoRaise(true);
00584   open->setToolTip(tr("Open"));
00585 
00586   QGridLayout* formLayout = new QGridLayout( this );
00587   formLayout->setSpacing( 1 );
00588   formLayout->setMargin ( 1 );
00589 
00590   // add the buttons and the space
00591   layout->addWidget( back );
00592   layout->addWidget( forward );
00593   layout->addWidget( home );
00594   layout->addWidget( open );
00595   QSpacerItem* spacer = new QSpacerItem( 0, 20, QSizePolicy::Expanding, QSizePolicy::Minimum );
00596   layout->addItem( spacer );
00597   groupBox->setLayout(layout);
00598 
00599   label = new QLabel(this);
00600   label->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
00601   label->setText(start);
00602 
00603   // add the button group with its elements and the browser to the layout
00604   formLayout->addWidget( groupBox, 0, 0 );
00605   formLayout->addWidget( browser, 1, 0 );
00606   formLayout->addWidget( label, 2, 0 );
00607 
00608   connect( this, SIGNAL(setSource( const QUrl& )), browser, SLOT(setSource( const QUrl& )));
00609   connect( browser, SIGNAL(stateChanged(const QString&)), this, SLOT(onStateChanged(const QString&)));
00610   connect( browser, SIGNAL(backwardAvailable(bool)), back, SLOT(setEnabled(bool)));
00611   connect( browser, SIGNAL(forwardAvailable (bool)), forward, SLOT(setEnabled(bool)));
00612   connect( browser, SIGNAL(startExternalBrowser(const QString&)), 
00613            this,        SLOT  (startExternalBrowser(const QString&)));
00614   connect( back,    SIGNAL(clicked()), browser, SLOT(backward()));
00615   connect( browser, SIGNAL(backwardAvailable(bool) ), back, SLOT(setEnabled(bool) ) );
00616   connect( forward, SIGNAL(clicked()), browser, SLOT(forward()));
00617   connect( browser, SIGNAL(forwardAvailable(bool) ), forward, SLOT(setEnabled(bool) ) );
00618   connect( home,    SIGNAL(clicked()), browser, SLOT(home()));
00619   connect( open,    SIGNAL(clicked()), this, SLOT(openHelpFile()));
00620   forward->setEnabled( false );
00621   back->setEnabled( false );
00622   qApp->installEventFilter(this);
00623 }
00624 
00628 HelpView::~HelpView()
00629 {
00630   // no need to delete child widgets, Qt does it all for us
00631   qApp->removeEventFilter(this);
00632 }
00633 
00637 void HelpView::setFileSource( const QString& src )
00638 {
00639   setSource( src );
00640 }
00641 
00645 void HelpView::openHelpFile()
00646 {
00647   QString fn = QFileDialog::getOpenFileName(this, tr("Open file"), QString(), tr("All HTML files (*.html *.htm)"));
00648   if ( !fn.isEmpty() )
00649     setSource( QUrl::fromLocalFile(fn) );
00650 }
00651 
00656 void HelpView::startExternalBrowser( const QString& url )
00657 {
00658   ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath
00659       ("User parameter:BaseApp/Preferences/OnlineHelp");
00660   QString browser = QString::fromUtf8(hGrp->GetASCII( "ExternalBrowser", "" ).c_str());
00661 
00662   if (browser.isEmpty())
00663   {
00664     QMessageBox::critical( this, tr("External browser"), tr("No external browser found. Specify in preferences, please") );
00665     return;
00666   }
00667 
00668   // create the command to execute
00669   QStringList arguments;
00670   arguments << url;
00671   
00672   if (!QProcess::startDetached(browser, arguments))
00673   {
00674     QMessageBox::critical( this, tr("External browser"), tr("Starting of %1 failed").arg( browser ) );
00675   }
00676 }
00677 
00678 void HelpView::onStateChanged(const QString& state)
00679 {
00680   label->setText(state);
00681 }
00682 
00683 bool HelpView::eventFilter ( QObject* o, QEvent* e )
00684 {
00685   // Handles What's This click events
00686   if (e->type() == QEvent::WhatsThisClicked) {
00687     QString url = static_cast<QWhatsThisClickedEvent*>(e)->href();
00688     setSource(QUrl::fromLocalFile(url));
00689     QWhatsThis::hideText();
00690     return true;
00691   }
00692 
00693   return QWidget::eventFilter( o, e );
00694 }
00695 
00696 #include "moc_HelpView.cpp"
00697 

Generated on Wed Nov 23 19:00:17 2011 for FreeCAD by  doxygen 1.6.1