src/gui/widgets/qtextbrowser.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 QtGui 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 "qtextbrowser.h"
       
    43 #include "qtextedit_p.h"
       
    44 
       
    45 #ifndef QT_NO_TEXTBROWSER
       
    46 
       
    47 #include <qstack.h>
       
    48 #include <qapplication.h>
       
    49 #include <qevent.h>
       
    50 #include <qdesktopwidget.h>
       
    51 #include <qdebug.h>
       
    52 #include <qabstracttextdocumentlayout.h>
       
    53 #include "private/qtextdocumentlayout_p.h"
       
    54 #include <qtextcodec.h>
       
    55 #include <qpainter.h>
       
    56 #include <qdir.h>
       
    57 #include <qwhatsthis.h>
       
    58 #include <qtextobject.h>
       
    59 #include <qdesktopservices.h>
       
    60 
       
    61 QT_BEGIN_NAMESPACE
       
    62 
       
    63 class QTextBrowserPrivate : public QTextEditPrivate
       
    64 {
       
    65     Q_DECLARE_PUBLIC(QTextBrowser)
       
    66 public:
       
    67     inline QTextBrowserPrivate()
       
    68         : textOrSourceChanged(false), forceLoadOnSourceChange(false), openExternalLinks(false),
       
    69           openLinks(true)
       
    70 #ifdef QT_KEYPAD_NAVIGATION
       
    71         , lastKeypadScrollValue(-1)
       
    72 #endif
       
    73     {}
       
    74 
       
    75     void init();
       
    76 
       
    77     struct HistoryEntry {
       
    78         inline HistoryEntry()
       
    79             : hpos(0), vpos(0), focusIndicatorPosition(-1),
       
    80               focusIndicatorAnchor(-1) {}
       
    81         QUrl url;
       
    82         QString title;
       
    83         int hpos;
       
    84         int vpos;
       
    85         int focusIndicatorPosition, focusIndicatorAnchor;
       
    86     };
       
    87 
       
    88     HistoryEntry history(int i) const
       
    89     {
       
    90         if (i <= 0)
       
    91             if (-i < stack.count())
       
    92                 return stack[stack.count()+i-1];
       
    93             else
       
    94                 return HistoryEntry();
       
    95         else
       
    96             if (i <= forwardStack.count())
       
    97                 return forwardStack[forwardStack.count()-i];
       
    98             else
       
    99                 return HistoryEntry();
       
   100     }
       
   101 
       
   102 
       
   103     HistoryEntry createHistoryEntry() const;
       
   104     void restoreHistoryEntry(const HistoryEntry entry);
       
   105 
       
   106     QStack<HistoryEntry> stack;
       
   107     QStack<HistoryEntry> forwardStack;
       
   108     QUrl home;
       
   109     QUrl currentURL;
       
   110 
       
   111     QStringList searchPaths;
       
   112 
       
   113     /*flag necessary to give the linkClicked() signal some meaningful
       
   114       semantics when somebody connected to it calls setText() or
       
   115       setSource() */
       
   116     bool textOrSourceChanged;
       
   117     bool forceLoadOnSourceChange;
       
   118 
       
   119     bool openExternalLinks;
       
   120     bool openLinks;
       
   121 
       
   122 #ifndef QT_NO_CURSOR
       
   123     QCursor oldCursor;
       
   124 #endif
       
   125 
       
   126     QString findFile(const QUrl &name) const;
       
   127 
       
   128     inline void _q_documentModified()
       
   129     {
       
   130         textOrSourceChanged = true;
       
   131         forceLoadOnSourceChange = !currentURL.path().isEmpty();
       
   132     }
       
   133 
       
   134     void _q_activateAnchor(const QString &href);
       
   135     void _q_highlightLink(const QString &href);
       
   136 
       
   137     void setSource(const QUrl &url);
       
   138 
       
   139     // re-imlemented from QTextEditPrivate
       
   140     virtual QUrl resolveUrl(const QUrl &url) const;
       
   141     inline QUrl resolveUrl(const QString &url) const
       
   142     { return resolveUrl(QUrl::fromEncoded(url.toUtf8())); }
       
   143 
       
   144 #ifdef QT_KEYPAD_NAVIGATION
       
   145     void keypadMove(bool next);
       
   146     QTextCursor prevFocus;
       
   147     int lastKeypadScrollValue;
       
   148 #endif
       
   149 };
       
   150 
       
   151 QString QTextBrowserPrivate::findFile(const QUrl &name) const
       
   152 {
       
   153     QString fileName;
       
   154     if (name.scheme() == QLatin1String("qrc"))
       
   155         fileName = QLatin1String(":/") + name.path();
       
   156     else
       
   157         fileName = name.toLocalFile();
       
   158 
       
   159     if (QFileInfo(fileName).isAbsolute())
       
   160         return fileName;
       
   161 
       
   162     foreach (QString path, searchPaths) {
       
   163         if (!path.endsWith(QLatin1Char('/')))
       
   164             path.append(QLatin1Char('/'));
       
   165         path.append(fileName);
       
   166         if (QFileInfo(path).isReadable())
       
   167             return path;
       
   168     }
       
   169 
       
   170     return fileName;
       
   171 }
       
   172 
       
   173 QUrl QTextBrowserPrivate::resolveUrl(const QUrl &url) const
       
   174 {
       
   175     if (!url.isRelative())
       
   176         return url;
       
   177 
       
   178     // For the second case QUrl can merge "#someanchor" with "foo.html"
       
   179     // correctly to "foo.html#someanchor"
       
   180     if (!(currentURL.isRelative()
       
   181           || (currentURL.scheme() == QLatin1String("file")
       
   182               && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
       
   183           || (url.hasFragment() && url.path().isEmpty())) {
       
   184         return currentURL.resolved(url);
       
   185     }
       
   186 
       
   187     // this is our last resort when current url and new url are both relative
       
   188     // we try to resolve against the current working directory in the local
       
   189     // file system.
       
   190     QFileInfo fi(currentURL.toLocalFile());
       
   191     if (fi.exists()) {
       
   192         return QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(url);
       
   193     }
       
   194 
       
   195     return url;
       
   196 }
       
   197 
       
   198 void QTextBrowserPrivate::_q_activateAnchor(const QString &href)
       
   199 {
       
   200     if (href.isEmpty())
       
   201         return;
       
   202     Q_Q(QTextBrowser);
       
   203 
       
   204 #ifndef QT_NO_CURSOR
       
   205     viewport->setCursor(oldCursor);
       
   206 #endif
       
   207 
       
   208     const QUrl url = resolveUrl(href);
       
   209 
       
   210     if (!openLinks) {
       
   211         emit q->anchorClicked(url);
       
   212         return;
       
   213     }
       
   214 
       
   215     textOrSourceChanged = false;
       
   216 
       
   217 #ifndef QT_NO_DESKTOPSERVICES
       
   218     if ((openExternalLinks
       
   219          && url.scheme() != QLatin1String("file")
       
   220          && url.scheme() != QLatin1String("qrc")
       
   221          && !url.isRelative())
       
   222         || (url.isRelative() && !currentURL.isRelative()
       
   223             && currentURL.scheme() != QLatin1String("file")
       
   224             && currentURL.scheme() != QLatin1String("qrc"))) {
       
   225         QDesktopServices::openUrl(url);
       
   226         return;
       
   227     }
       
   228 #endif
       
   229 
       
   230     emit q->anchorClicked(url);
       
   231 
       
   232     if (textOrSourceChanged)
       
   233         return;
       
   234 
       
   235     q->setSource(url);
       
   236 }
       
   237 
       
   238 void QTextBrowserPrivate::_q_highlightLink(const QString &anchor)
       
   239 {
       
   240     Q_Q(QTextBrowser);
       
   241     if (anchor.isEmpty()) {
       
   242 #ifndef QT_NO_CURSOR
       
   243         if (viewport->cursor().shape() != Qt::PointingHandCursor)
       
   244             oldCursor = viewport->cursor();
       
   245         viewport->setCursor(oldCursor);
       
   246 #endif
       
   247         emit q->highlighted(QUrl());
       
   248         emit q->highlighted(QString());
       
   249     } else {
       
   250 #ifndef QT_NO_CURSOR
       
   251         viewport->setCursor(Qt::PointingHandCursor);
       
   252 #endif
       
   253 
       
   254         const QUrl url = resolveUrl(anchor);
       
   255         emit q->highlighted(url);
       
   256         // convenience to ease connecting to QStatusBar::showMessage(const QString &)
       
   257         emit q->highlighted(url.toString());
       
   258     }
       
   259 }
       
   260 
       
   261 void QTextBrowserPrivate::setSource(const QUrl &url)
       
   262 {
       
   263     Q_Q(QTextBrowser);
       
   264 #ifndef QT_NO_CURSOR
       
   265     if (q->isVisible())
       
   266         QApplication::setOverrideCursor(Qt::WaitCursor);
       
   267 #endif
       
   268     textOrSourceChanged = true;
       
   269 
       
   270     QString txt;
       
   271 
       
   272     bool doSetText = false;
       
   273 
       
   274     QUrl currentUrlWithoutFragment = currentURL;
       
   275     currentUrlWithoutFragment.setFragment(QString());
       
   276     QUrl newUrlWithoutFragment = currentURL.resolved(url);
       
   277     newUrlWithoutFragment.setFragment(QString());
       
   278 
       
   279     if (url.isValid()
       
   280         && (newUrlWithoutFragment != currentUrlWithoutFragment || forceLoadOnSourceChange)) {
       
   281         QVariant data = q->loadResource(QTextDocument::HtmlResource, resolveUrl(url));
       
   282         if (data.type() == QVariant::String) {
       
   283             txt = data.toString();
       
   284         } else if (data.type() == QVariant::ByteArray) {
       
   285 #ifndef QT_NO_TEXTCODEC
       
   286             QByteArray ba = data.toByteArray();
       
   287             QTextCodec *codec = Qt::codecForHtml(ba);
       
   288             txt = codec->toUnicode(ba);
       
   289 #else
       
   290 	    txt = data.toString();
       
   291 #endif
       
   292         }
       
   293         if (txt.isEmpty())
       
   294             qWarning("QTextBrowser: No document for %s", url.toString().toLatin1().constData());
       
   295 
       
   296         if (q->isVisible()) {
       
   297             QString firstTag = txt.left(txt.indexOf(QLatin1Char('>')) + 1);
       
   298             if (firstTag.startsWith(QLatin1String("<qt")) && firstTag.contains(QLatin1String("type")) && firstTag.contains(QLatin1String("detail"))) {
       
   299 #ifndef QT_NO_CURSOR
       
   300                 QApplication::restoreOverrideCursor();
       
   301 #endif
       
   302 #ifndef QT_NO_WHATSTHIS
       
   303                 QWhatsThis::showText(QCursor::pos(), txt, q);
       
   304 #endif
       
   305                 return;
       
   306             }
       
   307         }
       
   308 
       
   309         currentURL = resolveUrl(url);
       
   310         doSetText = true;
       
   311     }
       
   312 
       
   313     if (!home.isValid())
       
   314         home = url;
       
   315 
       
   316     if (doSetText) {
       
   317 #ifndef QT_NO_TEXTHTMLPARSER
       
   318         q->QTextEdit::setHtml(txt);
       
   319         q->document()->setMetaInformation(QTextDocument::DocumentUrl, currentURL.toString());
       
   320 #else
       
   321         q->QTextEdit::setPlainText(txt);
       
   322 #endif
       
   323 
       
   324 #ifdef QT_KEYPAD_NAVIGATION
       
   325         prevFocus.movePosition(QTextCursor::Start);
       
   326 #endif
       
   327     }
       
   328 
       
   329     forceLoadOnSourceChange = false;
       
   330 
       
   331     if (!url.fragment().isEmpty()) {
       
   332         q->scrollToAnchor(url.fragment());
       
   333     } else {
       
   334         hbar->setValue(0);
       
   335         vbar->setValue(0);
       
   336     }
       
   337 #ifdef QT_KEYPAD_NAVIGATION
       
   338     lastKeypadScrollValue = vbar->value();
       
   339     emit q->highlighted(QUrl());
       
   340     emit q->highlighted(QString());
       
   341 #endif
       
   342 
       
   343 #ifndef QT_NO_CURSOR
       
   344     if (q->isVisible())
       
   345         QApplication::restoreOverrideCursor();
       
   346 #endif
       
   347     emit q->sourceChanged(url);
       
   348 }
       
   349 
       
   350 #ifdef QT_KEYPAD_NAVIGATION
       
   351 void QTextBrowserPrivate::keypadMove(bool next)
       
   352 {
       
   353     Q_Q(QTextBrowser);
       
   354 
       
   355     const int height = viewport->height();
       
   356     const int overlap = qBound(20, height / 5, 40); // XXX arbitrary, but a good balance
       
   357     const int visibleLinkAmount = overlap; // consistent, but maybe not the best choice (?)
       
   358     int yOffset = vbar->value();
       
   359     int scrollYOffset = qBound(0, next ? yOffset + height - overlap : yOffset - height + overlap, vbar->maximum());
       
   360 
       
   361     bool foundNextAnchor = false;
       
   362     bool focusIt = false;
       
   363     int focusedPos = -1;
       
   364 
       
   365     QTextCursor anchorToFocus;
       
   366 
       
   367     QRectF viewRect = QRectF(0, yOffset, control->size().width(), height);
       
   368     QRectF newViewRect = QRectF(0, scrollYOffset, control->size().width(), height);
       
   369     QRectF bothViewRects = viewRect.united(newViewRect);
       
   370 
       
   371     // If we don't have a previous anchor, pretend that we had the first/last character
       
   372     // on the screen selected.
       
   373     if (prevFocus.isNull()) {
       
   374         if (next)
       
   375             prevFocus = control->cursorForPosition(QPointF(0, yOffset));
       
   376         else
       
   377             prevFocus = control->cursorForPosition(QPointF(control->size().width(), yOffset + height));
       
   378     }
       
   379 
       
   380     // First, check to see if someone has moved the scroll bars independently
       
   381     if (lastKeypadScrollValue != yOffset) {
       
   382         // Someone (user or programmatically) has moved us, so we might
       
   383         // need to start looking from the current position instead of prevFocus
       
   384 
       
   385         bool findOnScreen = true;
       
   386 
       
   387         // If prevFocus is on screen at all, we just use it.
       
   388         if (prevFocus.hasSelection()) {
       
   389             QRectF prevRect = control->selectionRect(prevFocus);
       
   390             if (viewRect.intersects(prevRect))
       
   391                 findOnScreen = false;
       
   392         }
       
   393 
       
   394         // Otherwise, we find a new anchor that's on screen.
       
   395         // Basically, create a cursor with the last/first character
       
   396         // on screen
       
   397         if (findOnScreen) {
       
   398             if (next)
       
   399                 prevFocus = control->cursorForPosition(QPointF(0, yOffset));
       
   400             else
       
   401                 prevFocus = control->cursorForPosition(QPointF(control->size().width(), yOffset + height));
       
   402         }
       
   403         foundNextAnchor = control->findNextPrevAnchor(prevFocus, next, anchorToFocus);
       
   404     } else if (prevFocus.hasSelection()) {
       
   405         // Check the pathological case that the current anchor is higher
       
   406         // than the screen, and just scroll through it in that case
       
   407         QRectF prevRect = control->selectionRect(prevFocus);
       
   408         if ((next && prevRect.bottom() > (yOffset + height)) ||
       
   409                 (!next && prevRect.top() < yOffset)) {
       
   410             anchorToFocus = prevFocus;
       
   411             focusedPos = scrollYOffset;
       
   412             focusIt = true;
       
   413         } else {
       
   414             // This is the "normal" case - no scroll bar adjustments, no large anchors,
       
   415             // and no wrapping.
       
   416             foundNextAnchor = control->findNextPrevAnchor(prevFocus, next, anchorToFocus);
       
   417         }
       
   418     }
       
   419 
       
   420     // If not found yet, see if we need to wrap
       
   421     if (!focusIt && !foundNextAnchor) {
       
   422         if (next) {
       
   423             if (yOffset == vbar->maximum()) {
       
   424                 prevFocus.movePosition(QTextCursor::Start);
       
   425                 yOffset = scrollYOffset = 0;
       
   426 
       
   427                 // Refresh the rectangles
       
   428                 viewRect = QRectF(0, yOffset, control->size().width(), height);
       
   429                 newViewRect = QRectF(0, scrollYOffset, control->size().width(), height);
       
   430                 bothViewRects = viewRect.united(newViewRect);
       
   431             }
       
   432         } else {
       
   433             if (yOffset == 0) {
       
   434                 prevFocus.movePosition(QTextCursor::End);
       
   435                 yOffset = scrollYOffset = vbar->maximum();
       
   436 
       
   437                 // Refresh the rectangles
       
   438                 viewRect = QRectF(0, yOffset, control->size().width(), height);
       
   439                 newViewRect = QRectF(0, scrollYOffset, control->size().width(), height);
       
   440                 bothViewRects = viewRect.united(newViewRect);
       
   441             }
       
   442         }
       
   443 
       
   444         // Try looking now
       
   445         foundNextAnchor = control->findNextPrevAnchor(prevFocus, next, anchorToFocus);
       
   446     }
       
   447 
       
   448     // If we did actually find an anchor to use...
       
   449     if (foundNextAnchor) {
       
   450         QRectF desiredRect = control->selectionRect(anchorToFocus);
       
   451 
       
   452         // XXX This is an arbitrary heuristic
       
   453         // Decide to focus an anchor if it will be at least be
       
   454         // in the middle region of the screen after a scroll.
       
   455         // This can result in partial anchors with focus, but
       
   456         // insisting on links being completely visible before
       
   457         // selecting them causes disparities between links that
       
   458         // take up 90% of the screen height and those that take
       
   459         // up e.g. 110%
       
   460         // Obviously if a link is entirely visible, we still
       
   461         // focus it.
       
   462         if(bothViewRects.contains(desiredRect)
       
   463                 || bothViewRects.adjusted(0, visibleLinkAmount, 0, -visibleLinkAmount).intersects(desiredRect)) {
       
   464             focusIt = true;
       
   465 
       
   466             // We aim to put the new link in the middle of the screen,
       
   467             // unless the link is larger than the screen (we just move to
       
   468             // display the first page of the link)
       
   469             if (desiredRect.height() > height) {
       
   470                 if (next)
       
   471                     focusedPos = (int) desiredRect.top();
       
   472                 else
       
   473                     focusedPos = (int) desiredRect.bottom() - height;
       
   474             } else
       
   475                 focusedPos = (int) ((desiredRect.top() + desiredRect.bottom()) / 2 - (height / 2));
       
   476 
       
   477             // and clamp it to make sure we don't skip content.
       
   478             if (next)
       
   479                 focusedPos = qBound(yOffset, focusedPos, scrollYOffset);
       
   480             else
       
   481                 focusedPos = qBound(scrollYOffset, focusedPos, yOffset);
       
   482         }
       
   483     }
       
   484 
       
   485     // If we didn't get a new anchor, check if the old one is still on screen when we scroll
       
   486     // Note that big (larger than screen height) anchors also have some handling at the
       
   487     // start of this function.
       
   488     if (!focusIt && prevFocus.hasSelection()) {
       
   489         QRectF desiredRect = control->selectionRect(prevFocus);
       
   490         // XXX this may be better off also using the visibleLinkAmount value
       
   491         if(newViewRect.intersects(desiredRect)) {
       
   492             focusedPos = scrollYOffset;
       
   493             focusIt = true;
       
   494             anchorToFocus = prevFocus;
       
   495         }
       
   496     }
       
   497 
       
   498     // setTextCursor ensures that the cursor is visible. save & restore
       
   499     // the scroll bar values therefore
       
   500     const int savedXOffset = hbar->value();
       
   501 
       
   502     // Now actually process our decision
       
   503     if (focusIt && control->setFocusToAnchor(anchorToFocus)) {
       
   504         // Save the focus for next time
       
   505         prevFocus = control->textCursor();
       
   506 
       
   507         // Scroll
       
   508         vbar->setValue(focusedPos);
       
   509         lastKeypadScrollValue = focusedPos;
       
   510         hbar->setValue(savedXOffset);
       
   511 
       
   512         // Ensure that the new selection is highlighted.
       
   513         const QString href = control->anchorAtCursor();
       
   514         QUrl url = resolveUrl(href);
       
   515         emit q->highlighted(url);
       
   516         emit q->highlighted(url.toString());
       
   517     } else {
       
   518         // Scroll
       
   519         vbar->setValue(scrollYOffset);
       
   520         lastKeypadScrollValue = scrollYOffset;
       
   521 
       
   522         // now make sure we don't have a focused anchor
       
   523         QTextCursor cursor = control->textCursor();
       
   524         cursor.clearSelection();
       
   525 
       
   526         control->setTextCursor(cursor);
       
   527 
       
   528         hbar->setValue(savedXOffset);
       
   529         vbar->setValue(scrollYOffset);
       
   530 
       
   531         emit q->highlighted(QUrl());
       
   532         emit q->highlighted(QString());
       
   533     }
       
   534 }
       
   535 #endif
       
   536 
       
   537 QTextBrowserPrivate::HistoryEntry QTextBrowserPrivate::createHistoryEntry() const
       
   538 {
       
   539     HistoryEntry entry;
       
   540     entry.url = q_func()->source();
       
   541     entry.title = q_func()->documentTitle();
       
   542     entry.hpos = hbar->value();
       
   543     entry.vpos = vbar->value();
       
   544 
       
   545     const QTextCursor cursor = control->textCursor();
       
   546     if (control->cursorIsFocusIndicator()
       
   547         && cursor.hasSelection()) {
       
   548 
       
   549         entry.focusIndicatorPosition = cursor.position();
       
   550         entry.focusIndicatorAnchor = cursor.anchor();
       
   551     }
       
   552     return entry;
       
   553 }
       
   554 
       
   555 void QTextBrowserPrivate::restoreHistoryEntry(const HistoryEntry entry)
       
   556 {
       
   557     setSource(entry.url);
       
   558     hbar->setValue(entry.hpos);
       
   559     vbar->setValue(entry.vpos);
       
   560     if (entry.focusIndicatorAnchor != -1 && entry.focusIndicatorPosition != -1) {
       
   561         QTextCursor cursor(control->document());
       
   562         cursor.setPosition(entry.focusIndicatorAnchor);
       
   563         cursor.setPosition(entry.focusIndicatorPosition, QTextCursor::KeepAnchor);
       
   564         control->setTextCursor(cursor);
       
   565         control->setCursorIsFocusIndicator(true);
       
   566     }
       
   567 #ifdef QT_KEYPAD_NAVIGATION
       
   568     lastKeypadScrollValue = vbar->value();
       
   569     prevFocus = control->textCursor();
       
   570 
       
   571     Q_Q(QTextBrowser);
       
   572     const QString href = prevFocus.charFormat().anchorHref();
       
   573     QUrl url = resolveUrl(href);
       
   574     emit q->highlighted(url);
       
   575     emit q->highlighted(url.toString());
       
   576 #endif
       
   577 }
       
   578 
       
   579 /*!
       
   580     \class QTextBrowser
       
   581     \brief The QTextBrowser class provides a rich text browser with hypertext navigation.
       
   582 
       
   583     \ingroup richtext-processing
       
   584 
       
   585     This class extends QTextEdit (in read-only mode), adding some navigation
       
   586     functionality so that users can follow links in hypertext documents.
       
   587 
       
   588     If you want to provide your users with an editable rich text editor,
       
   589     use QTextEdit. If you want a text browser without hypertext navigation
       
   590     use QTextEdit, and use QTextEdit::setReadOnly() to disable
       
   591     editing. If you just need to display a small piece of rich text
       
   592     use QLabel.
       
   593 
       
   594     \section1 Document Source and Contents
       
   595 
       
   596     The contents of QTextEdit are set with setHtml() or setPlainText(),
       
   597     but QTextBrowser also implements the setSource() function, making it
       
   598     possible to use a named document as the source text. The name is looked
       
   599     up in a list of search paths and in the directory of the current document
       
   600     factory.
       
   601 
       
   602     If a document name ends with
       
   603     an anchor (for example, "\c #anchor"), the text browser automatically
       
   604     scrolls to that position (using scrollToAnchor()). When the user clicks
       
   605     on a hyperlink, the browser will call setSource() itself with the link's
       
   606     \c href value as argument. You can track the current source by connecting
       
   607     to the sourceChanged() signal.
       
   608 
       
   609     \section1 Navigation
       
   610 
       
   611     QTextBrowser provides backward() and forward() slots which you can
       
   612     use to implement Back and Forward buttons. The home() slot sets
       
   613     the text to the very first document displayed. The anchorClicked()
       
   614     signal is emitted when the user clicks an anchor. To override the
       
   615     default navigation behavior of the browser, call the setSource()
       
   616     function to supply new document text in a slot connected to this
       
   617     signal.
       
   618 
       
   619     If you want to load documents stored in the Qt resource system use
       
   620     \c{qrc} as the scheme in the URL to load. For example, for the document
       
   621     resource path \c{:/docs/index.html} use \c{qrc:/docs/index.html} as
       
   622     the URL with setSource().
       
   623 
       
   624     \sa QTextEdit, QTextDocument
       
   625 */
       
   626 
       
   627 /*!
       
   628     \property QTextBrowser::modified
       
   629     \brief whether the contents of the text browser have been modified
       
   630 */
       
   631 
       
   632 /*!
       
   633     \property QTextBrowser::readOnly
       
   634     \brief whether the text browser is read-only
       
   635 
       
   636     By default, this property is true.
       
   637 */
       
   638 
       
   639 /*!
       
   640     \property QTextBrowser::undoRedoEnabled
       
   641     \brief whether the text browser supports undo/redo operations
       
   642 
       
   643     By default, this property is false.
       
   644 */
       
   645 
       
   646 void QTextBrowserPrivate::init()
       
   647 {
       
   648     Q_Q(QTextBrowser);
       
   649     control->setTextInteractionFlags(Qt::TextBrowserInteraction);
       
   650 #ifndef QT_NO_CURSOR
       
   651     viewport->setCursor(oldCursor);
       
   652 #endif
       
   653     q->setUndoRedoEnabled(false);
       
   654     viewport->setMouseTracking(true);
       
   655     QObject::connect(q->document(), SIGNAL(contentsChanged()), q, SLOT(_q_documentModified()));
       
   656     QObject::connect(control, SIGNAL(linkActivated(QString)),
       
   657                      q, SLOT(_q_activateAnchor(QString)));
       
   658     QObject::connect(control, SIGNAL(linkHovered(QString)),
       
   659                      q, SLOT(_q_highlightLink(QString)));
       
   660 }
       
   661 
       
   662 /*!
       
   663     Constructs an empty QTextBrowser with parent \a parent.
       
   664 */
       
   665 QTextBrowser::QTextBrowser(QWidget *parent)
       
   666     : QTextEdit(*new QTextBrowserPrivate, parent)
       
   667 {
       
   668     Q_D(QTextBrowser);
       
   669     d->init();
       
   670 }
       
   671 
       
   672 #ifdef QT3_SUPPORT
       
   673 /*!
       
   674     Use one of the constructors that doesn't take the \a name
       
   675     argument and then use setObjectName() instead.
       
   676 */
       
   677 QTextBrowser::QTextBrowser(QWidget *parent, const char *name)
       
   678     : QTextEdit(*new QTextBrowserPrivate, parent)
       
   679 {
       
   680     setObjectName(QString::fromAscii(name));
       
   681     Q_D(QTextBrowser);
       
   682     d->init();
       
   683 }
       
   684 #endif
       
   685 
       
   686 /*!
       
   687     \internal
       
   688 */
       
   689 QTextBrowser::~QTextBrowser()
       
   690 {
       
   691 }
       
   692 
       
   693 /*!
       
   694     \property QTextBrowser::source
       
   695     \brief the name of the displayed document.
       
   696 
       
   697     This is a an invalid url if no document is displayed or if the
       
   698     source is unknown.
       
   699 
       
   700     When setting this property QTextBrowser tries to find a document
       
   701     with the specified name in the paths of the searchPaths property
       
   702     and directory of the current source, unless the value is an absolute
       
   703     file path. It also checks for optional anchors and scrolls the document
       
   704     accordingly
       
   705 
       
   706     If the first tag in the document is \c{<qt type=detail>}, the
       
   707     document is displayed as a popup rather than as new document in
       
   708     the browser window itself. Otherwise, the document is displayed
       
   709     normally in the text browser with the text set to the contents of
       
   710     the named document with setHtml().
       
   711 
       
   712     By default, this property contains an empty URL.
       
   713 */
       
   714 QUrl QTextBrowser::source() const
       
   715 {
       
   716     Q_D(const QTextBrowser);
       
   717     if (d->stack.isEmpty())
       
   718         return QUrl();
       
   719     else
       
   720         return d->stack.top().url;
       
   721 }
       
   722 
       
   723 /*!
       
   724     \property QTextBrowser::searchPaths
       
   725     \brief the search paths used by the text browser to find supporting
       
   726     content
       
   727 
       
   728     QTextBrowser uses this list to locate images and documents.
       
   729 
       
   730     By default, this property contains an empty string list.
       
   731 */
       
   732 
       
   733 QStringList QTextBrowser::searchPaths() const
       
   734 {
       
   735     Q_D(const QTextBrowser);
       
   736     return d->searchPaths;
       
   737 }
       
   738 
       
   739 void QTextBrowser::setSearchPaths(const QStringList &paths)
       
   740 {
       
   741     Q_D(QTextBrowser);
       
   742     d->searchPaths = paths;
       
   743 }
       
   744 
       
   745 /*!
       
   746     Reloads the current set source.
       
   747 */
       
   748 void QTextBrowser::reload()
       
   749 {
       
   750     Q_D(QTextBrowser);
       
   751     QUrl s = d->currentURL;
       
   752     d->currentURL = QUrl();
       
   753     setSource(s);
       
   754 }
       
   755 
       
   756 void QTextBrowser::setSource(const QUrl &url)
       
   757 {
       
   758     Q_D(QTextBrowser);
       
   759 
       
   760     const QTextBrowserPrivate::HistoryEntry historyEntry = d->createHistoryEntry();
       
   761 
       
   762     d->setSource(url);
       
   763 
       
   764     if (!url.isValid())
       
   765         return;
       
   766 
       
   767     // the same url you are already watching?
       
   768     if (!d->stack.isEmpty() && d->stack.top().url == url)
       
   769         return;
       
   770 
       
   771     if (!d->stack.isEmpty())
       
   772         d->stack.top() = historyEntry;
       
   773 
       
   774     QTextBrowserPrivate::HistoryEntry entry;
       
   775     entry.url = url;
       
   776     entry.title = documentTitle();
       
   777     entry.hpos = 0;
       
   778     entry.vpos = 0;
       
   779     d->stack.push(entry);
       
   780 
       
   781     emit backwardAvailable(d->stack.count() > 1);
       
   782 
       
   783     if (!d->forwardStack.isEmpty() && d->forwardStack.top().url == url) {
       
   784         d->forwardStack.pop();
       
   785         emit forwardAvailable(d->forwardStack.count() > 0);
       
   786     } else {
       
   787         d->forwardStack.clear();
       
   788         emit forwardAvailable(false);
       
   789     }
       
   790 
       
   791     emit historyChanged();
       
   792 }
       
   793 
       
   794 /*!
       
   795     \fn void QTextBrowser::backwardAvailable(bool available)
       
   796 
       
   797     This signal is emitted when the availability of backward()
       
   798     changes. \a available is false when the user is at home();
       
   799     otherwise it is true.
       
   800 */
       
   801 
       
   802 /*!
       
   803     \fn void QTextBrowser::forwardAvailable(bool available)
       
   804 
       
   805     This signal is emitted when the availability of forward() changes.
       
   806     \a available is true after the user navigates backward() and false
       
   807     when the user navigates or goes forward().
       
   808 */
       
   809 
       
   810 /*!
       
   811     \fn void QTextBrowser::historyChanged()
       
   812     \since 4.4
       
   813 
       
   814     This signal is emitted when the history changes.
       
   815 
       
   816     \sa historyTitle(), historyUrl()
       
   817 */
       
   818 
       
   819 /*!
       
   820     \fn void QTextBrowser::sourceChanged(const QUrl &src)
       
   821 
       
   822     This signal is emitted when the source has changed, \a src
       
   823     being the new source.
       
   824 
       
   825     Source changes happen both programmatically when calling
       
   826     setSource(), forward(), backword() or home() or when the user
       
   827     clicks on links or presses the equivalent key sequences.
       
   828 */
       
   829 
       
   830 /*!  \fn void QTextBrowser::highlighted(const QUrl &link)
       
   831 
       
   832     This signal is emitted when the user has selected but not
       
   833     activated an anchor in the document. The URL referred to by the
       
   834     anchor is passed in \a link.
       
   835 */
       
   836 
       
   837 /*!  \fn void QTextBrowser::highlighted(const QString &link)
       
   838      \overload
       
   839 
       
   840      Convenience signal that allows connecting to a slot
       
   841      that takes just a QString, like for example QStatusBar's
       
   842      message().
       
   843 */
       
   844 
       
   845 
       
   846 /*!
       
   847     \fn void QTextBrowser::anchorClicked(const QUrl &link)
       
   848 
       
   849     This signal is emitted when the user clicks an anchor. The
       
   850     URL referred to by the anchor is passed in \a link.
       
   851 
       
   852     Note that the browser will automatically handle navigation to the
       
   853     location specified by \a link unless the openLinks property
       
   854     is set to false or you call setSource() in a slot connected.
       
   855     This mechanism is used to override the default navigation features of the browser.
       
   856 */
       
   857 
       
   858 /*!
       
   859     Changes the document displayed to the previous document in the
       
   860     list of documents built by navigating links. Does nothing if there
       
   861     is no previous document.
       
   862 
       
   863     \sa forward(), backwardAvailable()
       
   864 */
       
   865 void QTextBrowser::backward()
       
   866 {
       
   867     Q_D(QTextBrowser);
       
   868     if (d->stack.count() <= 1)
       
   869         return;
       
   870 
       
   871     // Update the history entry
       
   872     d->forwardStack.push(d->createHistoryEntry());
       
   873     d->stack.pop(); // throw away the old version of the current entry
       
   874     d->restoreHistoryEntry(d->stack.top()); // previous entry
       
   875     emit backwardAvailable(d->stack.count() > 1);
       
   876     emit forwardAvailable(true);
       
   877     emit historyChanged();
       
   878 }
       
   879 
       
   880 /*!
       
   881     Changes the document displayed to the next document in the list of
       
   882     documents built by navigating links. Does nothing if there is no
       
   883     next document.
       
   884 
       
   885     \sa backward(), forwardAvailable()
       
   886 */
       
   887 void QTextBrowser::forward()
       
   888 {
       
   889     Q_D(QTextBrowser);
       
   890     if (d->forwardStack.isEmpty())
       
   891         return;
       
   892     if (!d->stack.isEmpty()) {
       
   893         // Update the history entry
       
   894         d->stack.top() = d->createHistoryEntry();
       
   895     }
       
   896     d->stack.push(d->forwardStack.pop());
       
   897     d->restoreHistoryEntry(d->stack.top());
       
   898     emit backwardAvailable(true);
       
   899     emit forwardAvailable(!d->forwardStack.isEmpty());
       
   900     emit historyChanged();
       
   901 }
       
   902 
       
   903 /*!
       
   904     Changes the document displayed to be the first document from
       
   905     the history.
       
   906 */
       
   907 void QTextBrowser::home()
       
   908 {
       
   909     Q_D(QTextBrowser);
       
   910     if (d->home.isValid())
       
   911         setSource(d->home);
       
   912 }
       
   913 
       
   914 /*!
       
   915     The event \a ev is used to provide the following keyboard shortcuts:
       
   916     \table
       
   917     \header \i Keypress            \i Action
       
   918     \row \i Alt+Left Arrow  \i \l backward()
       
   919     \row \i Alt+Right Arrow \i \l forward()
       
   920     \row \i Alt+Up Arrow    \i \l home()
       
   921     \endtable
       
   922 */
       
   923 void QTextBrowser::keyPressEvent(QKeyEvent *ev)
       
   924 {
       
   925 #ifdef QT_KEYPAD_NAVIGATION
       
   926     Q_D(QTextBrowser);
       
   927     switch (ev->key()) {
       
   928     case Qt::Key_Select:
       
   929         if (QApplication::keypadNavigationEnabled()) {
       
   930             if (!hasEditFocus()) {
       
   931                 setEditFocus(true);
       
   932                 return;
       
   933             } else {
       
   934                 QTextCursor cursor = d->control->textCursor();
       
   935                 QTextCharFormat charFmt = cursor.charFormat();
       
   936                 if (!cursor.hasSelection() || charFmt.anchorHref().isEmpty()) {
       
   937                     ev->accept();
       
   938                     return;
       
   939                 }
       
   940             }
       
   941         }
       
   942         break;
       
   943     case Qt::Key_Back:
       
   944         if (QApplication::keypadNavigationEnabled()) {
       
   945             if (hasEditFocus()) {
       
   946                 setEditFocus(false);
       
   947                 ev->accept();
       
   948                 return;
       
   949             }
       
   950         }
       
   951         QTextEdit::keyPressEvent(ev);
       
   952         return;
       
   953     default:
       
   954         if (QApplication::keypadNavigationEnabled() && !hasEditFocus()) {
       
   955             ev->ignore();
       
   956             return;
       
   957         }
       
   958     }
       
   959 #endif
       
   960 
       
   961     if (ev->modifiers() & Qt::AltModifier) {
       
   962         switch (ev->key()) {
       
   963         case Qt::Key_Right:
       
   964             forward();
       
   965             ev->accept();
       
   966             return;
       
   967         case Qt::Key_Left:
       
   968             backward();
       
   969             ev->accept();
       
   970             return;
       
   971         case Qt::Key_Up:
       
   972             home();
       
   973             ev->accept();
       
   974             return;
       
   975         }
       
   976     }
       
   977 #ifdef QT_KEYPAD_NAVIGATION
       
   978     else {
       
   979         if (ev->key() == Qt::Key_Up) {
       
   980             d->keypadMove(false);
       
   981             return;
       
   982         } else if (ev->key() == Qt::Key_Down) {
       
   983             d->keypadMove(true);
       
   984             return;
       
   985         }
       
   986     }
       
   987 #endif
       
   988     QTextEdit::keyPressEvent(ev);
       
   989 }
       
   990 
       
   991 /*!
       
   992     \reimp
       
   993 */
       
   994 void QTextBrowser::mouseMoveEvent(QMouseEvent *e)
       
   995 {
       
   996     QTextEdit::mouseMoveEvent(e);
       
   997 }
       
   998 
       
   999 /*!
       
  1000     \reimp
       
  1001 */
       
  1002 void QTextBrowser::mousePressEvent(QMouseEvent *e)
       
  1003 {
       
  1004     QTextEdit::mousePressEvent(e);
       
  1005 }
       
  1006 
       
  1007 /*!
       
  1008     \reimp
       
  1009 */
       
  1010 void QTextBrowser::mouseReleaseEvent(QMouseEvent *e)
       
  1011 {
       
  1012     QTextEdit::mouseReleaseEvent(e);
       
  1013 }
       
  1014 
       
  1015 /*!
       
  1016     \reimp
       
  1017 */
       
  1018 void QTextBrowser::focusOutEvent(QFocusEvent *ev)
       
  1019 {
       
  1020 #ifndef QT_NO_CURSOR
       
  1021     Q_D(QTextBrowser);
       
  1022     d->viewport->setCursor((!(d->control->textInteractionFlags() & Qt::TextEditable)) ? d->oldCursor : Qt::IBeamCursor);
       
  1023 #endif
       
  1024     QTextEdit::focusOutEvent(ev);
       
  1025 }
       
  1026 
       
  1027 /*!
       
  1028     \reimp
       
  1029 */
       
  1030 bool QTextBrowser::focusNextPrevChild(bool next)
       
  1031 {
       
  1032     Q_D(QTextBrowser);
       
  1033     if (d->control->setFocusToNextOrPreviousAnchor(next)) {
       
  1034 #ifdef QT_KEYPAD_NAVIGATION
       
  1035         // Might need to synthesize a highlight event.
       
  1036         if (d->prevFocus != d->control->textCursor() && d->control->textCursor().hasSelection()) {
       
  1037             const QString href = d->control->anchorAtCursor();
       
  1038             QUrl url = d->resolveUrl(href);
       
  1039             emit highlighted(url);
       
  1040             emit highlighted(url.toString());
       
  1041         }
       
  1042         d->prevFocus = d->control->textCursor();
       
  1043 #endif
       
  1044         return true;
       
  1045     } else {
       
  1046 #ifdef QT_KEYPAD_NAVIGATION
       
  1047         // We assume we have no highlight now.
       
  1048         emit highlighted(QUrl());
       
  1049         emit highlighted(QString());
       
  1050 #endif
       
  1051     }
       
  1052     return QTextEdit::focusNextPrevChild(next);
       
  1053 }
       
  1054 
       
  1055 /*!
       
  1056   \reimp
       
  1057 */
       
  1058 void QTextBrowser::paintEvent(QPaintEvent *e)
       
  1059 {
       
  1060     Q_D(QTextBrowser);
       
  1061     QPainter p(d->viewport);
       
  1062     d->paint(&p, e);
       
  1063 }
       
  1064 
       
  1065 /*!
       
  1066     This function is called when the document is loaded and for
       
  1067     each image in the document. The \a type indicates the type of resource
       
  1068     to be loaded. An invalid QVariant is returned if the resource cannot be
       
  1069     loaded.
       
  1070 
       
  1071     The default implementation ignores \a type and tries to locate
       
  1072     the resources by interpreting \a name as a file name. If it is
       
  1073     not an absolute path it tries to find the file in the paths of
       
  1074     the \l searchPaths property and in the same directory as the
       
  1075     current source. On success, the result is a QVariant that stores
       
  1076     a QByteArray with the contents of the file.
       
  1077 
       
  1078     If you reimplement this function, you can return other QVariant
       
  1079     types. The table below shows which variant types are supported
       
  1080     depending on the resource type:
       
  1081 
       
  1082     \table
       
  1083     \header \i ResourceType  \i QVariant::Type
       
  1084     \row    \i QTextDocument::HtmlResource  \i QString or QByteArray
       
  1085     \row    \i QTextDocument::ImageResource \i QImage, QPixmap or QByteArray
       
  1086     \row    \i QTextDocument::StyleSheetResource \i QString or QByteArray
       
  1087     \endtable
       
  1088 */
       
  1089 QVariant QTextBrowser::loadResource(int /*type*/, const QUrl &name)
       
  1090 {
       
  1091     Q_D(QTextBrowser);
       
  1092 
       
  1093     QByteArray data;
       
  1094     QString fileName = d->findFile(d->resolveUrl(name));
       
  1095     QFile f(fileName);
       
  1096     if (f.open(QFile::ReadOnly)) {
       
  1097         data = f.readAll();
       
  1098         f.close();
       
  1099     } else {
       
  1100         return QVariant();
       
  1101     }
       
  1102 
       
  1103     return data;
       
  1104 }
       
  1105 
       
  1106 /*!
       
  1107     \since 4.2
       
  1108 
       
  1109     Returns true if the text browser can go backward in the document history
       
  1110     using backward().
       
  1111 
       
  1112     \sa backwardAvailable(), backward()
       
  1113 */
       
  1114 bool QTextBrowser::isBackwardAvailable() const
       
  1115 {
       
  1116     Q_D(const QTextBrowser);
       
  1117     return d->stack.count() > 1;
       
  1118 }
       
  1119 
       
  1120 /*!
       
  1121     \since 4.2
       
  1122 
       
  1123     Returns true if the text browser can go forward in the document history
       
  1124     using forward().
       
  1125 
       
  1126     \sa forwardAvailable(), forward()
       
  1127 */
       
  1128 bool QTextBrowser::isForwardAvailable() const
       
  1129 {
       
  1130     Q_D(const QTextBrowser);
       
  1131     return !d->forwardStack.isEmpty();
       
  1132 }
       
  1133 
       
  1134 /*!
       
  1135     \since 4.2
       
  1136 
       
  1137     Clears the history of visited documents and disables the forward and
       
  1138     backward navigation.
       
  1139 
       
  1140     \sa backward(), forward()
       
  1141 */
       
  1142 void QTextBrowser::clearHistory()
       
  1143 {
       
  1144     Q_D(QTextBrowser);
       
  1145     d->forwardStack.clear();
       
  1146     if (!d->stack.isEmpty()) {
       
  1147         QTextBrowserPrivate::HistoryEntry historyEntry = d->stack.top();
       
  1148         d->stack.resize(0);
       
  1149         d->stack.push(historyEntry);
       
  1150         d->home = historyEntry.url;
       
  1151     }
       
  1152     emit forwardAvailable(false);
       
  1153     emit backwardAvailable(false);
       
  1154     emit historyChanged();
       
  1155 }
       
  1156 
       
  1157 /*!
       
  1158    Returns the url of the HistoryItem.
       
  1159 
       
  1160     \table
       
  1161     \header \i Input            \i Return
       
  1162     \row \i \a{i} < 0  \i \l backward() history
       
  1163     \row \i\a{i} == 0 \i current, see QTextBrowser::source()
       
  1164     \row \i \a{i} > 0  \i \l forward() history
       
  1165     \endtable
       
  1166 
       
  1167     \since 4.4
       
  1168 */
       
  1169 QUrl QTextBrowser::historyUrl(int i) const
       
  1170 {
       
  1171     Q_D(const QTextBrowser);
       
  1172     return d->history(i).url;
       
  1173 }
       
  1174 
       
  1175 /*!
       
  1176     Returns the documentTitle() of the HistoryItem.
       
  1177 
       
  1178     \table
       
  1179     \header \i Input            \i Return
       
  1180     \row \i \a{i} < 0  \i \l backward() history
       
  1181     \row \i \a{i} == 0 \i current, see QTextBrowser::source()
       
  1182     \row \i \a{i} > 0  \i \l forward() history
       
  1183     \endtable
       
  1184 
       
  1185     \snippet doc/src/snippets/code/src_gui_widgets_qtextbrowser.cpp 0
       
  1186 
       
  1187     \since 4.4
       
  1188 */
       
  1189 QString QTextBrowser::historyTitle(int i) const
       
  1190 {
       
  1191     Q_D(const QTextBrowser);
       
  1192     return d->history(i).title;
       
  1193 }
       
  1194 
       
  1195 
       
  1196 /*!
       
  1197     Returns the number of locations forward in the history.
       
  1198 
       
  1199     \since 4.4
       
  1200 */
       
  1201 int QTextBrowser::forwardHistoryCount() const
       
  1202 {
       
  1203     Q_D(const QTextBrowser);
       
  1204     return d->forwardStack.count();
       
  1205 }
       
  1206 
       
  1207 /*!
       
  1208     Returns the number of locations backward in the history.
       
  1209 
       
  1210     \since 4.4
       
  1211 */
       
  1212 int QTextBrowser::backwardHistoryCount() const
       
  1213 {
       
  1214     Q_D(const QTextBrowser);
       
  1215     return d->stack.count()-1;
       
  1216 }
       
  1217 
       
  1218 /*!
       
  1219     \property QTextBrowser::openExternalLinks
       
  1220     \since 4.2
       
  1221 
       
  1222     Specifies whether QTextBrowser should automatically open links to external
       
  1223     sources using QDesktopServices::openUrl() instead of emitting the
       
  1224     anchorClicked signal. Links are considered external if their scheme is
       
  1225     neither file or qrc.
       
  1226 
       
  1227     The default value is false.
       
  1228 */
       
  1229 bool QTextBrowser::openExternalLinks() const
       
  1230 {
       
  1231     Q_D(const QTextBrowser);
       
  1232     return d->openExternalLinks;
       
  1233 }
       
  1234 
       
  1235 void QTextBrowser::setOpenExternalLinks(bool open)
       
  1236 {
       
  1237     Q_D(QTextBrowser);
       
  1238     d->openExternalLinks = open;
       
  1239 }
       
  1240 
       
  1241 /*!
       
  1242    \property QTextBrowser::openLinks
       
  1243    \since 4.3
       
  1244 
       
  1245    This property specifies whether QTextBrowser should automatically open links the user tries to
       
  1246    activate by mouse or keyboard.
       
  1247 
       
  1248    Regardless of the value of this property the anchorClicked signal is always emitted.
       
  1249 
       
  1250    The default value is true.
       
  1251 */
       
  1252 
       
  1253 bool QTextBrowser::openLinks() const
       
  1254 {
       
  1255     Q_D(const QTextBrowser);
       
  1256     return d->openLinks;
       
  1257 }
       
  1258 
       
  1259 void QTextBrowser::setOpenLinks(bool open)
       
  1260 {
       
  1261     Q_D(QTextBrowser);
       
  1262     d->openLinks = open;
       
  1263 }
       
  1264 
       
  1265 /*! \reimp */
       
  1266 bool QTextBrowser::event(QEvent *e)
       
  1267 {
       
  1268     return QTextEdit::event(e);
       
  1269 }
       
  1270 
       
  1271 QT_END_NAMESPACE
       
  1272 
       
  1273 #include "moc_qtextbrowser.cpp"
       
  1274 
       
  1275 #endif // QT_NO_TEXTBROWSER