tools/assistant/compat/lib/qassistantclient.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Assistant of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qassistantclient.h"
       
    43 
       
    44 #include <qtcpsocket.h>
       
    45 #include <qtextstream.h>
       
    46 #include <qtimer.h>
       
    47 #include <qfileinfo.h>
       
    48 #include <qmap.h>
       
    49 
       
    50 QT_BEGIN_NAMESPACE
       
    51 
       
    52 class QAssistantClientPrivate
       
    53 {
       
    54     friend class QAssistantClient;
       
    55     QStringList arguments;
       
    56 };
       
    57 
       
    58 static QMap<const QAssistantClient*,QAssistantClientPrivate*> *dpointers = 0;
       
    59 
       
    60 static QAssistantClientPrivate *data( const QAssistantClient *client, bool create=false )
       
    61 {
       
    62     if( !dpointers )
       
    63         dpointers = new QMap<const QAssistantClient*,QAssistantClientPrivate*>;
       
    64     QAssistantClientPrivate *d = (*dpointers)[client];
       
    65     if( !d && create ) {
       
    66         d = new QAssistantClientPrivate;
       
    67         dpointers->insert( client, d );
       
    68     }
       
    69     return d;
       
    70 }
       
    71 
       
    72 /*!
       
    73     \class QAssistantClient
       
    74     \obsolete
       
    75     \brief The QAssistantClient class provides a means of using Qt
       
    76     Assistant as an application's help tool.
       
    77 
       
    78     \ingroup helpsystem
       
    79 
       
    80     \bold{Note:} \e{This class is obsolete and only required when using
       
    81     the old Qt Assistant, now called assistant_adp. If you want to use
       
    82     the new Qt Assistant as a remote help viewer, simple create a
       
    83     QProcess instance and specify \tt{assistant} as its executable.
       
    84     The following code shows how to start Qt Assistant and request a
       
    85     certain page to be shown:}
       
    86 
       
    87     \snippet doc/src/snippets/code/tools_assistant_compat_lib_qassistantclient.cpp 0
       
    88     
       
    89     \e{For a complete example using the Qt Assistant remotely, see the \l
       
    90     {help/remotecontrol}{Remote Control} example.}
       
    91     
       
    92     In order to make Qt Assistant act as a customized help tool for
       
    93     your application, you must provide your application with a
       
    94     QAssistantClient object in addition to a \l
       
    95     {assistant-manual.html} {Qt Assistant Document Profile} (\c .adp
       
    96     file) and the associated documentation.
       
    97 
       
    98     Note that the QAssistantClient class is not included in the Qt
       
    99     library. To use it you must add the following line to your pro
       
   100     file:
       
   101 
       
   102     \snippet doc/src/snippets/code/tools_assistant_compat_lib_qassistantclient.cpp 1
       
   103 
       
   104     A QAssistantClient instance can open or close Qt Assistant
       
   105     whenever it is required.
       
   106 
       
   107     Once you have created a QAssistantClient instance, specifying the
       
   108     path to the Qt Assistant executable, using Qt Assistant is
       
   109     simple: You can either call the openAssistant() slot to show the
       
   110     defined start page of the documentation, or you can call the
       
   111     showPage() slot to show a particular help page. When you call
       
   112     openAssistant() and showPage(), Qt Assistant will be launched if
       
   113     it isn't already running. When Qt Assistant is running, the
       
   114     isOpen() function returns true.
       
   115 
       
   116     When calling showPage() the Qt Assistant instance will also be
       
   117     brought to the foreground if its hidden. The showPage() slot can
       
   118     be called multiple times, while calling openAssistant() several
       
   119     times without closing the application in between, will have no
       
   120     effect.
       
   121 
       
   122     You can close Qt Assistant at any time using the closeAssistant()
       
   123     slot. When you call openAssistant(), or you call showPage()
       
   124     without a previous call to openAssistant(), the assistantOpened()
       
   125     signal is emitted. Similarly when closeAssistant() is called,
       
   126     assistantClosed() is emitted. In either case, if an error occurs,
       
   127     error() is emitted.
       
   128 
       
   129     One QAssistantClient instance interacts with one Qt Assistant
       
   130     instance, so every time you call openAssistant(), showPage() or
       
   131     closeAssistant() they are applied to the particular Qt Assistant
       
   132     instance associated with the QAssistantClient.
       
   133 
       
   134     Qt Assistant's documentation set can be altered using the command
       
   135     line arguments that are passed to the application when it is
       
   136     launched. When started without any options, Qt Assistant displays
       
   137     a default set of documentation. When Qt is installed, the default
       
   138     documentation set in Qt Assistant contains the Qt reference
       
   139     documentation as well as the tools that come with Qt, such as \QD
       
   140     and \c qmake.
       
   141 
       
   142     Use the setArguments() function to specify the command line
       
   143     arguments. You can add or remove documentation from Qt Assistant
       
   144     by adding and removing the relevant content files: The command
       
   145     line arguments are \c {-addContentFile file.dcf} and \c
       
   146     {-removeContentFile file.dcf} respectively. You can make Qt
       
   147     Assistant run customized documentation sets that are separate from
       
   148     the Qt documentation, by specifying a profile: \c {-profile
       
   149     myapplication.adp}. The profile format can also be used to alter
       
   150     several of Qt Assistant's properties such as its title and
       
   151     startpage.
       
   152 
       
   153     The Documentation Content File (\c .dcf) and Qt Assistant
       
   154     Documentation Profile (\c .adp) formats are documented in the \l
       
   155     {assistant-manual.html}{Qt Assistant Manual}.
       
   156 
       
   157     For a complete example using the QAssistantClient class, see the
       
   158     \e{Simple Text Viewer} example. The example shows how you can make
       
   159     Qt Assistant act as a customized help tool for your application
       
   160     using the QAssistantClient class combined with a Qt Assistant
       
   161     Document Profile.
       
   162 
       
   163     \sa {Qt Assistant Manual}, {Simple Text Viewer Example}
       
   164 */
       
   165 
       
   166 /*!
       
   167     \fn void QAssistantClient::assistantOpened()
       
   168 
       
   169     This signal is emitted when Qt Assistant is opened and the
       
   170     client-server communication is set up.
       
   171 
       
   172     \sa openAssistant(), showPage()
       
   173 */
       
   174 
       
   175 /*!
       
   176     \fn void QAssistantClient::assistantClosed()
       
   177 
       
   178     This signal is emitted when the connection to Qt Assistant is
       
   179     closed. This happens when the user exits Qt Assistant, if an
       
   180     error in the server or client occurs, or if closeAssistant() is
       
   181     called.
       
   182 
       
   183     \sa closeAssistant()
       
   184 */
       
   185 
       
   186 /*!
       
   187     \fn void QAssistantClient::error( const QString &message )
       
   188 
       
   189     This signal is emitted if Qt Assistant cannot be started, or if an
       
   190     error occurs during the initialization of the connection between
       
   191     Qt Assistant and the calling application. The \a message provides an
       
   192     explanation of the error.
       
   193 */
       
   194 
       
   195 /*!
       
   196   Constructs an assistant client with the specified \a parent. For
       
   197   systems other than Mac OS, \a path specifies the path to the Qt
       
   198   Assistant executable. For Mac OS, \a path specifies a directory
       
   199   containing a valid assistant.app bundle. If \a path is the empty
       
   200   string, the system path (\c{%PATH%} or \c $PATH) is used.
       
   201 */
       
   202 QAssistantClient::QAssistantClient( const QString &path, QObject *parent )
       
   203     : QObject( parent ), host ( QLatin1String("localhost") )
       
   204 {
       
   205 #if defined(Q_OS_MAC)
       
   206     const QString assistant = QLatin1String("Assistant_adp");
       
   207 #else
       
   208     const QString assistant = QLatin1String("assistant_adp");
       
   209 #endif
       
   210 
       
   211     if ( path.isEmpty() )
       
   212         assistantCommand = assistant;
       
   213     else {
       
   214         QFileInfo fi( path );
       
   215         if ( fi.isDir() )
       
   216             assistantCommand = path + QLatin1String("/") + assistant;
       
   217         else
       
   218             assistantCommand = path;
       
   219     }
       
   220 
       
   221 #if defined(Q_OS_MAC)
       
   222     assistantCommand += QLatin1String(".app/Contents/MacOS/Assistant_adp");
       
   223 #endif
       
   224 
       
   225     socket = new QTcpSocket( this );
       
   226     connect( socket, SIGNAL(connected()),
       
   227             SLOT(socketConnected()) );
       
   228     connect( socket, SIGNAL(disconnected()),
       
   229             SLOT(socketConnectionClosed()) );
       
   230     connect( socket, SIGNAL(error(QAbstractSocket::SocketError)),
       
   231              SLOT(socketError()) );
       
   232     opened = false;
       
   233     proc = new QProcess( this );
       
   234     port = 0;
       
   235     pageBuffer = QLatin1String("");
       
   236     connect( proc, SIGNAL(readyReadStandardError()),
       
   237              this, SLOT(readStdError()) );
       
   238     connect( proc, SIGNAL(error(QProcess::ProcessError)),
       
   239         this, SLOT(procError(QProcess::ProcessError)) );
       
   240 }
       
   241 
       
   242 /*!
       
   243     Destroys the assistant client object.
       
   244 */
       
   245 QAssistantClient::~QAssistantClient()
       
   246 {
       
   247     if ( proc->state() == QProcess::Running )
       
   248         proc->terminate();
       
   249 
       
   250     if( dpointers ) {
       
   251         QAssistantClientPrivate *d = (*dpointers)[ this ];
       
   252         if ( d ) {
       
   253             dpointers->remove(this);
       
   254             delete d;
       
   255             if( dpointers->isEmpty() ) {
       
   256                 delete dpointers;
       
   257                 dpointers = 0;
       
   258             }
       
   259         }
       
   260     }
       
   261 }
       
   262 
       
   263 /*!
       
   264     Opens Qt Assistant, i.e. sets up the client-server communication
       
   265     between the application and Qt Assistant, and shows the start page
       
   266     specified by the current \l {assistant-manual.html}
       
   267     {Qt Assistant Document Profile}. If there is no specfied profile,
       
   268     and Qt is installed, the default start page is the Qt Reference
       
   269     Documentation's index page.
       
   270 
       
   271     If the connection is already established, this function does
       
   272     nothing. Use the showPage() function to show another page. If an
       
   273     error occurs, the error() signal is emitted.
       
   274 
       
   275     \sa showPage(), assistantOpened()
       
   276 */
       
   277 void QAssistantClient::openAssistant()
       
   278 {
       
   279     if ( proc->state() == QProcess::Running )
       
   280         return;
       
   281 
       
   282     QStringList args;
       
   283     args.append(QLatin1String("-server"));
       
   284     if( !pageBuffer.isEmpty() ) {
       
   285         args.append( QLatin1String("-file") );
       
   286         args.append( pageBuffer );
       
   287     }
       
   288 
       
   289     QAssistantClientPrivate *d = data( this );
       
   290     if( d ) {
       
   291         QStringList::ConstIterator it = d->arguments.constBegin();
       
   292         while( it!=d->arguments.constEnd() ) {
       
   293             args.append( *it );
       
   294             ++it;
       
   295         }
       
   296     }
       
   297 
       
   298     connect( proc, SIGNAL(readyReadStandardOutput()),
       
   299         this, SLOT(readPort()) );
       
   300 
       
   301     proc->start(assistantCommand, args);
       
   302 }
       
   303 
       
   304 void QAssistantClient::procError(QProcess::ProcessError err)
       
   305 {
       
   306     switch (err)
       
   307     {
       
   308     case QProcess::FailedToStart:
       
   309         emit error( tr( "Failed to start Qt Assistant." ) );
       
   310         break;
       
   311     case QProcess::Crashed:
       
   312         emit error( tr( "Qt Assistant crashed." ) );
       
   313         break;
       
   314     default:
       
   315         emit error( tr( "Error while running Qt Assistant." ) );
       
   316     }
       
   317 }
       
   318 
       
   319 void QAssistantClient::readPort()
       
   320 {
       
   321     QString p(QString::fromLatin1(proc->readAllStandardOutput()));
       
   322     quint16 port = p.toUShort();
       
   323     if ( port == 0 ) {
       
   324         emit error( tr( "Cannot connect to Qt Assistant." ) );
       
   325         return;
       
   326     }
       
   327     socket->connectToHost( host, port );
       
   328     disconnect( proc, SIGNAL(readyReadStandardOutput()),
       
   329                 this, SLOT(readPort()) );
       
   330 }
       
   331 
       
   332 /*!
       
   333     Closes the Qt Assistant instance.
       
   334 
       
   335     \sa openAssistant(), assistantClosed()
       
   336 */
       
   337 void QAssistantClient::closeAssistant()
       
   338 {
       
   339     if ( !opened )
       
   340         return;
       
   341 
       
   342     bool blocked = proc->blockSignals(true);
       
   343     proc->terminate();
       
   344     if (!proc->waitForFinished(2000)) {
       
   345         // If the process hasn't died after 2 seconds,
       
   346         // we kill it, causing it to exit immediately.
       
   347         proc->kill();
       
   348     }
       
   349     proc->blockSignals(blocked);
       
   350 }
       
   351 
       
   352 /*!
       
   353     Brings Qt Assistant to the foreground showing the given \a page.
       
   354     The \a page parameter is a path to an HTML file
       
   355     (e.g., QLatin1String("/home/pasquale/superproduct/docs/html/intro.html")).
       
   356 
       
   357     If Qt Assistant hasn't been opened yet, this function will call
       
   358     the openAssistant() slot with the specified page as the start
       
   359     page.
       
   360 
       
   361     \note The first time Qt Assistant is started, its window will open
       
   362     in front of the application's windows. Subsequent calls to this function
       
   363     will only load the specified pages in Qt Assistant and will not display
       
   364     its window in front of the application's windows.
       
   365 
       
   366     \sa openAssistant()
       
   367 */
       
   368 void QAssistantClient::showPage( const QString &page )
       
   369 {
       
   370     if (opened) {
       
   371         QTextStream os( socket );
       
   372         os << page << QLatin1String("\n");
       
   373     } else {
       
   374         pageBuffer = page;
       
   375 
       
   376         if (proc->state() == QProcess::NotRunning) {
       
   377             openAssistant();
       
   378             pageBuffer.clear();
       
   379             return;
       
   380         }
       
   381     }
       
   382 }
       
   383 
       
   384 /*!
       
   385     \property QAssistantClient::open
       
   386     \brief whether Qt Assistant is open
       
   387 
       
   388 */
       
   389 bool QAssistantClient::isOpen() const
       
   390 {
       
   391     return opened;
       
   392 }
       
   393 
       
   394 void QAssistantClient::socketConnected()
       
   395 {
       
   396     opened = true;
       
   397     if ( !pageBuffer.isEmpty() )
       
   398         showPage( pageBuffer );
       
   399     emit assistantOpened();
       
   400 }
       
   401 
       
   402 void QAssistantClient::socketConnectionClosed()
       
   403 {
       
   404     opened = false;
       
   405     emit assistantClosed();
       
   406 }
       
   407 
       
   408 void QAssistantClient::socketError()
       
   409 {
       
   410     QAbstractSocket::SocketError err = socket->error();
       
   411     if (err == QTcpSocket::ConnectionRefusedError)
       
   412         emit error( tr( "Could not connect to Assistant: Connection refused" ) );
       
   413     else if (err == QTcpSocket::HostNotFoundError)
       
   414         emit error( tr( "Could not connect to Assistant: Host not found" ) );
       
   415     else if (err != QTcpSocket::RemoteHostClosedError)
       
   416         emit error( tr( "Communication error" ) );
       
   417 }
       
   418 
       
   419 void QAssistantClient::readStdError()
       
   420 {
       
   421     QString errmsg = QString::fromLatin1(proc->readAllStandardError());
       
   422 
       
   423     if (!errmsg.isEmpty())
       
   424         emit error( errmsg.simplified() );
       
   425 }
       
   426 
       
   427 /*!
       
   428     \fn void QAssistantClient::setArguments(const QStringList &arguments)
       
   429 
       
   430     Sets the command line \a arguments that are passed to Qt Assistant
       
   431     when it is launched.
       
   432 
       
   433     The command line arguments can be used to alter Qt Assistant's
       
   434     documentation set. When started without any options, Qt Assistant
       
   435     displays a default set of documentation. When Qt is installed, the
       
   436     default documentation set in Qt Assistant contains the Qt
       
   437     reference documentation as well as the tools that come with Qt,
       
   438     such as Qt Designer and qmake.
       
   439 */
       
   440 void QAssistantClient::setArguments( const QStringList &args )
       
   441 {
       
   442     QAssistantClientPrivate *d = data( this, true );
       
   443     d->arguments = args;
       
   444 }
       
   445 
       
   446 QT_END_NAMESPACE