src/qt3support/text/q3textbrowser.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 Qt3Support module 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 "q3textbrowser.h"
       
    43 #ifndef QT_NO_TEXTBROWSER
       
    44 #include <private/q3richtext_p.h>
       
    45 
       
    46 #include "qevent.h"
       
    47 #include "qdesktopwidget.h"
       
    48 #include "qapplication.h"
       
    49 #include "qlayout.h"
       
    50 #include "qpainter.h"
       
    51 #include "qstack.h"
       
    52 #include "stdio.h"
       
    53 #include "qfile.h"
       
    54 #include "qtextstream.h"
       
    55 #include "qbitmap.h"
       
    56 #include "qtimer.h"
       
    57 #include "qimage.h"
       
    58 #include "q3simplerichtext.h"
       
    59 #include "q3dragobject.h"
       
    60 #include "qurl.h"
       
    61 #include "qcursor.h"
       
    62 
       
    63 QT_BEGIN_NAMESPACE
       
    64 
       
    65 /*!
       
    66     \class Q3TextBrowser
       
    67     \brief The Q3TextBrowser class provides a rich text browser with hypertext navigation.
       
    68 
       
    69     \compat
       
    70 
       
    71     This class extends Q3TextEdit (in read-only mode), adding some
       
    72     navigation functionality so that users can follow links in
       
    73     hypertext documents. The contents of Q3TextEdit is set with
       
    74     setText(), but Q3TextBrowser has an additional function,
       
    75     setSource(), which makes it possible to set the text to a named
       
    76     document. The name is looked up in the text view's mime source
       
    77     factory. If a document name ends with an anchor (for example, "\c
       
    78     #anchor"), the text browser automatically scrolls to that position
       
    79     (using scrollToAnchor()). When the user clicks on a hyperlink, the
       
    80     browser will call setSource() itself, with the link's \c href
       
    81     value as argument. You can track the current source by connetion
       
    82     to the sourceChanged() signal.
       
    83 
       
    84     Q3TextBrowser provides backward() and forward() slots which you can
       
    85     use to implement Back and Forward buttons. The home() slot sets
       
    86     the text to the very first document displayed. The linkClicked()
       
    87     signal is emitted when the user clicks a link.
       
    88 
       
    89     By using Q3TextEdit::setMimeSourceFactory() you can provide your
       
    90     own subclass of Q3MimeSourceFactory. This makes it possible to
       
    91     access data from anywhere, for example from a network or from a
       
    92     database. See Q3MimeSourceFactory::data() for details.
       
    93 
       
    94     If you intend using the mime factory to read the data directly
       
    95     from the file system, you may have to specify the encoding for the
       
    96     file extension you are using. For example:
       
    97     \snippet doc/src/snippets/code/src_qt3support_text_q3textbrowser.cpp 0
       
    98     This is to ensure that the factory is able to resolve the document
       
    99     names.
       
   100 
       
   101     Q3TextBrowser interprets the tags it processes in accordance with
       
   102     the default style sheet. Change the style sheet with
       
   103     \l{setStyleSheet()}; see QStyleSheet for details.
       
   104 
       
   105     If you want to provide your users with editable rich text use
       
   106     Q3TextEdit. If you want a text browser without hypertext navigation
       
   107     use Q3TextEdit, and use Q3TextEdit::setReadOnly() to disable
       
   108     editing. If you just need to display a small piece of rich text
       
   109     use QSimpleRichText or QLabel.
       
   110 */
       
   111 
       
   112 class Q3TextBrowserData
       
   113 {
       
   114 public:
       
   115     Q3TextBrowserData():textOrSourceChanged(false) {}
       
   116 
       
   117     QStack<QString> stack;
       
   118     QStack<QString> forwardStack;
       
   119     QString home;
       
   120     QString curmain;
       
   121     QString curmark;
       
   122 
       
   123     /*flag necessary to give the linkClicked() signal some meaningful
       
   124       semantics when somebody connected to it calls setText() or
       
   125       setSource() */
       
   126     bool textOrSourceChanged;
       
   127 };
       
   128 
       
   129 
       
   130 /*!
       
   131     Constructs an empty Q3TextBrowser called \a name, with parent \a
       
   132     parent.
       
   133 */
       
   134 Q3TextBrowser::Q3TextBrowser(QWidget *parent, const char *name)
       
   135     : Q3TextEdit(parent, name)
       
   136 {
       
   137     setReadOnly(true);
       
   138     d = new Q3TextBrowserData;
       
   139 
       
   140     viewport()->setMouseTracking(true);
       
   141 }
       
   142 
       
   143 /*!
       
   144     \internal
       
   145 */
       
   146 Q3TextBrowser::~Q3TextBrowser()
       
   147 {
       
   148     delete d;
       
   149 }
       
   150 
       
   151 
       
   152 /*!
       
   153     \property Q3TextBrowser::source
       
   154     \brief the name of the displayed document.
       
   155 
       
   156     This is a an empty string if no document is displayed or if the
       
   157     source is unknown.
       
   158 
       
   159     Setting this property uses the mimeSourceFactory() to lookup the
       
   160     named document. It also checks for optional anchors and scrolls
       
   161     the document accordingly.
       
   162 
       
   163     If the first tag in the document is \c{<qt type=detail>}, the
       
   164     document is displayed as a popup rather than as new document in
       
   165     the browser window itself. Otherwise, the document is displayed
       
   166     normally in the text browser with the text set to the contents of
       
   167     the named document with setText().
       
   168 
       
   169     If you are using the filesystem access capabilities of the mime
       
   170     source factory, you must ensure that the factory knows about the
       
   171     encoding of specified files; otherwise no data will be available.
       
   172     The default factory handles a couple of common file extensions
       
   173     such as \c *.html and \c *.txt with reasonable defaults. See
       
   174     Q3MimeSourceFactory::data() for details.
       
   175 */
       
   176 
       
   177 QString Q3TextBrowser::source() const
       
   178 {
       
   179     if (d->stack.isEmpty())
       
   180         return QString();
       
   181     else
       
   182         return d->stack.top();
       
   183 }
       
   184 
       
   185 /*!
       
   186     Reloads the current set source.
       
   187 */
       
   188 
       
   189 void Q3TextBrowser::reload()
       
   190 {
       
   191     QString s = d->curmain;
       
   192     d->curmain = QLatin1String("");
       
   193     setSource(s);
       
   194 }
       
   195 
       
   196 
       
   197 void Q3TextBrowser::setSource(const QString& name)
       
   198 {
       
   199 #ifndef QT_NO_CURSOR
       
   200     if (isVisible())
       
   201         qApp->setOverrideCursor(Qt::WaitCursor);
       
   202 #endif
       
   203     d->textOrSourceChanged = true;
       
   204     QString source = name;
       
   205     QString mark;
       
   206     int hash = name.indexOf(QLatin1Char('#'));
       
   207     if (hash != -1) {
       
   208         source = name.left(hash);
       
   209         mark = name.mid(hash+1);
       
   210     }
       
   211 
       
   212     if (source.left(5) == QLatin1String("file:"))
       
   213         source = source.mid(6);
       
   214 
       
   215     QString url = mimeSourceFactory()->makeAbsolute(source, context());
       
   216     QString txt;
       
   217     bool dosettext = false;
       
   218 
       
   219     if (!source.isEmpty() && url != d->curmain) {
       
   220         const QMimeSource* m =
       
   221                     mimeSourceFactory()->data(source, context());
       
   222         if (!m){
       
   223             qWarning("Q3TextBrowser: no mimesource for %s", source.latin1());
       
   224         }
       
   225         else {
       
   226             if (!Q3TextDrag::decode(m, txt)) {
       
   227                 qWarning("Q3TextBrowser: cannot decode %s", source.latin1());
       
   228             }
       
   229         }
       
   230         if (isVisible()) {
       
   231             QString firstTag = txt.left(txt.indexOf(QLatin1Char('>')) + 1);
       
   232             if (firstTag.left(3) == QLatin1String("<qt") && firstTag.contains(QLatin1String("type")) && firstTag.contains(QLatin1String("detail"))) {
       
   233                 popupDetail(txt, QCursor::pos());
       
   234 #ifndef QT_NO_CURSOR
       
   235                 qApp->restoreOverrideCursor();
       
   236 #endif
       
   237                 return;
       
   238             }
       
   239         }
       
   240 
       
   241         d->curmain = url;
       
   242         dosettext = true;
       
   243     }
       
   244 
       
   245     d->curmark = mark;
       
   246 
       
   247     if (!mark.isEmpty()) {
       
   248         url += QLatin1Char('#');
       
   249         url += mark;
       
   250     }
       
   251     if (d->home.count() == 0)
       
   252         d->home = url;
       
   253 
       
   254     if (d->stack.isEmpty() || d->stack.top() != url)
       
   255         d->stack.push(url);
       
   256 
       
   257     int stackCount = (int)d->stack.count();
       
   258     if (d->stack.top() == url)
       
   259         stackCount--;
       
   260     emit backwardAvailable(stackCount > 0);
       
   261     stackCount = (int)d->forwardStack.count();
       
   262     if (d->forwardStack.isEmpty() || d->forwardStack.top() == url)
       
   263         stackCount--;
       
   264     emit forwardAvailable(stackCount > 0);
       
   265 
       
   266     if (dosettext)
       
   267         Q3TextEdit::setText(txt, url);
       
   268 
       
   269     if (!mark.isEmpty())
       
   270         scrollToAnchor(mark);
       
   271     else
       
   272         setContentsPos(0, 0);
       
   273 
       
   274 #ifndef QT_NO_CURSOR
       
   275     if (isVisible())
       
   276         qApp->restoreOverrideCursor();
       
   277 #endif
       
   278 
       
   279     emit sourceChanged(url);
       
   280 }
       
   281 
       
   282 /*!
       
   283     \fn void Q3TextBrowser::backwardAvailable(bool available)
       
   284 
       
   285     This signal is emitted when the availability of backward()
       
   286     changes. \a available is false when the user is at home();
       
   287     otherwise it is true.
       
   288 */
       
   289 
       
   290 /*!
       
   291     \fn void Q3TextBrowser::forwardAvailable(bool available)
       
   292 
       
   293     This signal is emitted when the availability of forward() changes.
       
   294     \a available is true after the user navigates backward() and false
       
   295     when the user navigates or goes forward().
       
   296 */
       
   297 
       
   298 /*!
       
   299     \fn void Q3TextBrowser::sourceChanged(const QString& src)
       
   300 
       
   301     This signal is emitted when the mime source has changed, \a src
       
   302     being the new source.
       
   303 
       
   304     Source changes happen both programmatically when calling
       
   305     setSource(), forward(), backword() or home() or when the user
       
   306     clicks on links or presses the equivalent key sequences.
       
   307 */
       
   308 
       
   309 /*!  \fn void Q3TextBrowser::highlighted (const QString &link)
       
   310 
       
   311     This signal is emitted when the user has selected but not
       
   312     activated a link in the document. \a link is the value of the \c
       
   313     href i.e. the name of the target document.
       
   314 */
       
   315 
       
   316 /*!
       
   317     \fn void Q3TextBrowser::linkClicked(const QString& link)
       
   318 
       
   319     This signal is emitted when the user clicks a link. The \a link is
       
   320     the value of the \c href i.e. the name of the target document.
       
   321 
       
   322     The \a link will be the absolute location of the document, based
       
   323     on the value of the anchor's href tag and the current context of
       
   324     the document.
       
   325 
       
   326     \sa anchorClicked()
       
   327 */
       
   328 
       
   329 /*!
       
   330     \fn void Q3TextBrowser::anchorClicked(const QString& name, const QString &link)
       
   331 
       
   332     This signal is emitted when the user clicks an anchor. The \a link is
       
   333     the value of the \c href i.e. the name of the target document.  The \a name
       
   334     is the name of the anchor.
       
   335 
       
   336     \sa linkClicked()
       
   337 */
       
   338 
       
   339 /*!
       
   340     Changes the document displayed to the previous document in the
       
   341     list of documents built by navigating links. Does nothing if there
       
   342     is no previous document.
       
   343 
       
   344     \sa forward(), backwardAvailable()
       
   345 */
       
   346 void Q3TextBrowser::backward()
       
   347 {
       
   348     if (d->stack.count() <= 1)
       
   349         return;
       
   350     d->forwardStack.push(d->stack.pop());
       
   351     setSource(d->stack.pop());
       
   352     emit forwardAvailable(true);
       
   353 }
       
   354 
       
   355 /*!
       
   356     Changes the document displayed to the next document in the list of
       
   357     documents built by navigating links. Does nothing if there is no
       
   358     next document.
       
   359 
       
   360     \sa backward(), forwardAvailable()
       
   361 */
       
   362 void Q3TextBrowser::forward()
       
   363 {
       
   364     if (d->forwardStack.isEmpty())
       
   365         return;
       
   366     setSource(d->forwardStack.pop());
       
   367     emit forwardAvailable(!d->forwardStack.isEmpty());
       
   368 }
       
   369 
       
   370 /*!
       
   371     Changes the document displayed to be the first document the
       
   372     browser displayed.
       
   373 */
       
   374 void Q3TextBrowser::home()
       
   375 {
       
   376     if (!d->home.isNull())
       
   377         setSource(d->home);
       
   378 }
       
   379 
       
   380 /*!
       
   381     The event \a e is used to provide the following keyboard shortcuts:
       
   382     \table
       
   383     \header \i Keypress            \i Action
       
   384     \row \i Alt+Left Arrow  \i \l backward()
       
   385     \row \i Alt+Right Arrow \i \l forward()
       
   386     \row \i Alt+Up Arrow    \i \l home()
       
   387     \endtable
       
   388 */
       
   389 void Q3TextBrowser::keyPressEvent(QKeyEvent * e)
       
   390 {
       
   391     if (e->state() & Qt::AltButton) {
       
   392         switch (e->key()) {
       
   393         case Qt::Key_Right:
       
   394             forward();
       
   395             return;
       
   396         case Qt::Key_Left:
       
   397             backward();
       
   398             return;
       
   399         case Qt::Key_Up:
       
   400             home();
       
   401             return;
       
   402         }
       
   403     }
       
   404     Q3TextEdit::keyPressEvent(e);
       
   405 }
       
   406 
       
   407 class QTextDetailPopup : public QWidget
       
   408 {
       
   409 public:
       
   410     QTextDetailPopup()
       
   411         : QWidget (0, "automatic QText detail widget", Qt::WType_Popup)
       
   412     {
       
   413 	setAttribute(Qt::WA_DeleteOnClose, true);
       
   414     }
       
   415 
       
   416 protected:
       
   417     void mousePressEvent(QMouseEvent *)
       
   418     {
       
   419         close();
       
   420     }
       
   421 };
       
   422 
       
   423 
       
   424 void Q3TextBrowser::popupDetail(const QString& contents, const QPoint& pos)
       
   425 {
       
   426 
       
   427     const int shadowWidth = 6;   // also used as '5' and '6' and even '8' below
       
   428     const int vMargin = 8;
       
   429     const int hMargin = 12;
       
   430 
       
   431     QWidget* popup = new QTextDetailPopup;
       
   432     popup->setAttribute(Qt::WA_NoSystemBackground, true);
       
   433 
       
   434     Q3SimpleRichText* doc = new Q3SimpleRichText(contents, popup->font());
       
   435     doc->adjustSize();
       
   436     QRect r(0, 0, doc->width(), doc->height());
       
   437 
       
   438     int w = r.width() + 2*hMargin;
       
   439     int h = r.height() + 2*vMargin;
       
   440 
       
   441     popup->resize(w + shadowWidth, h + shadowWidth);
       
   442 
       
   443     // okay, now to find a suitable location
       
   444     //###### we need a global fancy popup positioning somewhere
       
   445     popup->move(pos - popup->rect().center());
       
   446     if (popup->geometry().right() > QApplication::desktop()->width())
       
   447         popup->move(QApplication::desktop()->width() - popup->width(),
       
   448                      popup->y());
       
   449     if (popup->geometry().bottom() > QApplication::desktop()->height())
       
   450         popup->move(popup->x(),
       
   451                      QApplication::desktop()->height() - popup->height());
       
   452     if (popup->x() < 0)
       
   453         popup->move(0, popup->y());
       
   454     if (popup->y() < 0)
       
   455         popup->move(popup->x(), 0);
       
   456 
       
   457 
       
   458     popup->show();
       
   459 
       
   460     // now for super-clever shadow stuff.  super-clever mostly in
       
   461     // how many window system problems it skirts around.
       
   462 
       
   463     QPainter p(popup);
       
   464     p.setPen(QApplication::palette().color(QPalette::Active, QPalette::WindowText));
       
   465     p.drawRect(0, 0, w, h);
       
   466     p.setPen(QApplication::palette().color(QPalette::Active, QPalette::Mid));
       
   467     p.setBrush(QColor(255, 255, 240));
       
   468     p.drawRect(1, 1, w-2, h-2);
       
   469     p.setPen(Qt::black);
       
   470 
       
   471     doc->draw(&p, hMargin, vMargin, r, popup->palette(), 0);
       
   472     delete doc;
       
   473 
       
   474     p.drawPoint(w + 5, 6);
       
   475     p.drawLine(w + 3, 6,
       
   476                 w + 5, 8);
       
   477     p.drawLine(w + 1, 6,
       
   478                 w + 5, 10);
       
   479     int i;
       
   480     for(i=7; i < h; i += 2)
       
   481         p.drawLine(w, i,
       
   482                     w + 5, i + 5);
       
   483     for(i = w - i + h; i > 6; i -= 2)
       
   484         p.drawLine(i, h,
       
   485                     i + 5, h + 5);
       
   486     for(; i > 0 ; i -= 2)
       
   487         p.drawLine(6, h + 6 - i,
       
   488                     i + 5, h + 5);
       
   489 }
       
   490 
       
   491 /*!
       
   492     \fn void Q3TextBrowser::setText(const QString &txt)
       
   493 
       
   494     \overload
       
   495 
       
   496     Sets the text to \a txt.
       
   497 */
       
   498 
       
   499 /*!
       
   500     \reimp
       
   501 */
       
   502 
       
   503 void Q3TextBrowser::setText(const QString &txt, const QString &context)
       
   504 {
       
   505     d->textOrSourceChanged = true;
       
   506     d->curmark = QLatin1String("");
       
   507     d->curmain = QLatin1String("");
       
   508     Q3TextEdit::setText(txt, context);
       
   509 }
       
   510 
       
   511 void Q3TextBrowser::emitHighlighted(const QString &s)
       
   512 {
       
   513     emit highlighted(s);
       
   514 }
       
   515 
       
   516 void Q3TextBrowser::emitLinkClicked(const QString &s)
       
   517 {
       
   518     d->textOrSourceChanged = false;
       
   519     emit linkClicked(s);
       
   520     if (!d->textOrSourceChanged)
       
   521         setSource(s);
       
   522 }
       
   523 
       
   524 QT_END_NAMESPACE
       
   525 
       
   526 #endif  // QT_NO_TEXTBROWSER