~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/gui/widgets/qtextbrowser.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the widgets module of the Qt Toolkit.
 
6
**
 
7
** This file may be distributed under the terms of the Q Public License
 
8
** as defined by Trolltech AS of Norway and appearing in the file
 
9
** LICENSE.QPL included in the packaging of this file.
 
10
**
 
11
** This file may be distributed and/or modified under the terms of the
 
12
** GNU General Public License version 2 as published by the Free Software
 
13
** Foundation and appearing in the file LICENSE.GPL included in the
 
14
** packaging of this file.
 
15
**
 
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
17
**   information about Qt Commercial License Agreements.
 
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
#include "qtextbrowser.h"
 
30
#include "qtextedit_p.h"
 
31
 
 
32
#include <qstack.h>
 
33
#include <qapplication.h>
 
34
#include <qevent.h>
 
35
#include <qdesktopwidget.h>
 
36
#include <qdebug.h>
 
37
#include <qabstracttextdocumentlayout.h>
 
38
#include "private/qtextdocumentlayout_p.h"
 
39
#include <qtextcodec.h>
 
40
#include <qpainter.h>
 
41
#include <qdir.h>
 
42
#include <qwhatsthis.h>
 
43
#include <qtextobject.h>
 
44
 
 
45
class QTextBrowserPrivate : public QTextEditPrivate
 
46
{
 
47
    Q_DECLARE_PUBLIC(QTextBrowser)
 
48
public:
 
49
    QTextBrowserPrivate() 
 
50
        : textOrSourceChanged(false), forceLoadOnSourceChange(false),
 
51
          hadSelectionOnMousePress(false) {}
 
52
 
 
53
    void init();
 
54
 
 
55
    struct HistoryEntry {
 
56
        QUrl url;
 
57
        int hpos;
 
58
        int vpos;
 
59
    };
 
60
 
 
61
    QStack<HistoryEntry> stack;
 
62
    QStack<HistoryEntry> forwardStack;
 
63
    QUrl home;
 
64
    QUrl currentURL;
 
65
 
 
66
    QStringList searchPaths;
 
67
 
 
68
    /*flag necessary to give the linkClicked() signal some meaningful
 
69
      semantics when somebody connected to it calls setText() or
 
70
      setSource() */
 
71
    bool textOrSourceChanged;
 
72
 
 
73
    bool forceLoadOnSourceChange;
 
74
 
 
75
    QString findFile(const QUrl &name) const;
 
76
 
 
77
    inline void documentModified()
 
78
    {
 
79
        textOrSourceChanged = true;
 
80
        forceLoadOnSourceChange = true;
 
81
    }
 
82
 
 
83
    void activateAnchor(const QString &href);
 
84
 
 
85
    void setSource(const QUrl &url);
 
86
 
 
87
    QString anchorOnMousePress;
 
88
    bool hadSelectionOnMousePress;
 
89
};
 
90
 
 
91
static bool isAbsoluteFileName(const QString &name)
 
92
{
 
93
    return !name.isEmpty()
 
94
           && (name[0] == '/'
 
95
#if defined(Q_WS_WIN)
 
96
               || (name[0].isLetter() && name[1] == QLatin1Char(':')) || name.startsWith("\\\\")
 
97
#endif
 
98
               || (name[0]  == QLatin1Char(':') && name[1] == QLatin1Char('/'))
 
99
              );
 
100
 
 
101
}
 
102
 
 
103
QString QTextBrowserPrivate::findFile(const QUrl &name) const
 
104
{
 
105
    QString fileName;
 
106
    if (name.scheme() == QLatin1String("qrc"))
 
107
        fileName = QLatin1String(":/") + name.path();
 
108
    else
 
109
        fileName = name.toLocalFile();
 
110
 
 
111
    if (isAbsoluteFileName(fileName))
 
112
        return fileName;
 
113
 
 
114
    QString slash("/");
 
115
 
 
116
    foreach (QString path, searchPaths) {
 
117
        if (!path.endsWith(slash))
 
118
            path.append(slash);
 
119
        path.append(fileName);
 
120
        if (QFileInfo(path).isReadable())
 
121
            return path;
 
122
    }
 
123
 
 
124
    if (stack.isEmpty())
 
125
        return fileName;
 
126
 
 
127
    QFileInfo path(QFileInfo(currentURL.toLocalFile()).absolutePath(), fileName);
 
128
    return path.absoluteFilePath();
 
129
}
 
130
 
 
131
void QTextBrowserPrivate::activateAnchor(const QString &href)
 
132
{
 
133
    if (href.isEmpty())
 
134
        return;
 
135
    Q_Q(QTextBrowser);
 
136
 
 
137
    textOrSourceChanged = false;
 
138
 
 
139
    const QUrl url = currentURL.resolved(href);
 
140
    emit q->anchorClicked(url);
 
141
 
 
142
    if (!textOrSourceChanged)
 
143
        q->setSource(url);
 
144
}
 
145
 
 
146
void QTextBrowserPrivate::setSource(const QUrl &url)
 
147
{
 
148
    Q_Q(QTextBrowser);
 
149
    if (q->isVisible())
 
150
        qApp->setOverrideCursor(Qt::WaitCursor);
 
151
 
 
152
    textOrSourceChanged = true;
 
153
 
 
154
    QString txt;
 
155
 
 
156
    bool doSetText = false;
 
157
 
 
158
    QUrl currentUrlWithoutFragment = currentURL;
 
159
    currentUrlWithoutFragment.setFragment(QString());
 
160
    QUrl urlWithoutFragment = url;
 
161
    urlWithoutFragment.setFragment(QString());
 
162
 
 
163
    if (url.isValid()
 
164
        && (urlWithoutFragment != currentUrlWithoutFragment || forceLoadOnSourceChange)) {
 
165
        QVariant data = q->loadResource(QTextDocument::HtmlResource, url);
 
166
        if (data.type() == QVariant::String) {
 
167
            txt = data.toString();
 
168
        } else if (data.type() == QVariant::ByteArray) {
 
169
            QByteArray ba = data.toByteArray();
 
170
            QTextCodec *codec = Qt::codecForHtml(ba);
 
171
            txt = codec->toUnicode(ba);
 
172
        }
 
173
        if (txt.isEmpty())
 
174
            qWarning("QTextBrowser: no document for %s", url.toString().toLatin1().constData());
 
175
 
 
176
        if (q->isVisible()) {
 
177
            QString firstTag = txt.left(txt.indexOf('>') + 1);
 
178
            if (firstTag.left(3) == "<qt" && firstTag.contains("type") && firstTag.contains("detail")) {
 
179
                qApp->restoreOverrideCursor();
 
180
                QWhatsThis::showText(QCursor::pos(), txt, q);
 
181
                return;
 
182
            }
 
183
        }
 
184
 
 
185
        currentURL = url;
 
186
        doSetText = true;
 
187
    }
 
188
 
 
189
    if (!home.isValid())
 
190
        home = url;
 
191
 
 
192
    if (doSetText)
 
193
        q->QTextEdit::setHtml(txt);
 
194
 
 
195
    forceLoadOnSourceChange = false;
 
196
 
 
197
    if (!url.fragment().isEmpty()) {
 
198
        q->scrollToAnchor(url.fragment());
 
199
    } else {
 
200
        hbar->setValue(0);
 
201
        vbar->setValue(0);
 
202
    }
 
203
 
 
204
    if (q->isVisible())
 
205
        qApp->restoreOverrideCursor();
 
206
 
 
207
    emit q->sourceChanged(url);
 
208
}
 
209
 
 
210
/*!
 
211
    \class QTextBrowser qtextbrowser.h
 
212
    \brief The QTextBrowser class provides a rich text browser with hypertext navigation.
 
213
 
 
214
    \ingroup text
 
215
 
 
216
    This class extends QTextEdit (in read-only mode), adding some
 
217
    navigation functionality so that users can follow links in
 
218
    hypertext documents. The contents of QTextEdit are set with
 
219
    setHtml() or setPlainText(), but QTextBrowser also implements the
 
220
    setSource() function, making it possible to set the text to a named
 
221
    document. The name is looked up in a list of search paths and in the
 
222
    directory of the current document factory. If a document name ends with
 
223
    an anchor (for example, "\c #anchor"), the text browser automatically
 
224
    scrolls to that position (using scrollToAnchor()). When the user clicks
 
225
    on a hyperlink, the browser will call setSource() itself with the link's
 
226
    \c href value as argument. You can track the current source by connecting
 
227
    to the sourceChanged() signal.
 
228
 
 
229
    QTextBrowser provides backward() and forward() slots which you can
 
230
    use to implement Back and Forward buttons. The home() slot sets
 
231
    the text to the very first document displayed. The anchorClicked()
 
232
    signal is emitted when the user clicks an anchor.
 
233
 
 
234
    If you want to provide your users with editable rich text use
 
235
    QTextEdit. If you want a text browser without hypertext navigation
 
236
    use QTextEdit, and use QTextEdit::setReadOnly() to disable
 
237
    editing. If you just need to display a small piece of rich text
 
238
    use QLabel.
 
239
*/
 
240
 
 
241
/*!
 
242
    \property QTextBrowser::modified
 
243
    \brief whether the contents of the text browser have been modified
 
244
*/
 
245
 
 
246
/*!
 
247
    \property QTextBrowser::readOnly
 
248
    \brief whether the text browser is read-only
 
249
*/
 
250
 
 
251
/*!
 
252
    \property QTextBrowser::undoRedoEnabled
 
253
    \brief whether the text browser supports undo/redo operations
 
254
*/
 
255
 
 
256
void QTextBrowserPrivate::init()
 
257
{
 
258
    Q_Q(QTextBrowser);
 
259
    q->setReadOnly(true);
 
260
    q->setUndoRedoEnabled(false);
 
261
    viewport->setMouseTracking(true);
 
262
    QObject::connect(q->document(), SIGNAL(contentsChanged()), q, SLOT(documentModified()));
 
263
}
 
264
 
 
265
/*!
 
266
    Constructs an empty QTextBrowser with parent \a parent.
 
267
*/
 
268
QTextBrowser::QTextBrowser(QWidget *parent)
 
269
    : QTextEdit(*new QTextBrowserPrivate, parent)
 
270
{
 
271
    Q_D(QTextBrowser);
 
272
    d->init();
 
273
}
 
274
 
 
275
#ifdef QT3_SUPPORT
 
276
/*!
 
277
    Use one of the constructors that doesn't take the \a name
 
278
    argument and then use setObjectName() instead.
 
279
*/
 
280
QTextBrowser::QTextBrowser(QWidget *parent, const char *name)
 
281
    : QTextEdit(*new QTextBrowserPrivate, parent)
 
282
{
 
283
    setObjectName(name);
 
284
    Q_D(QTextBrowser);
 
285
    d->init();
 
286
}
 
287
#endif
 
288
 
 
289
/*!
 
290
    \internal
 
291
*/
 
292
QTextBrowser::~QTextBrowser()
 
293
{
 
294
}
 
295
 
 
296
/*!
 
297
    \property QTextBrowser::source
 
298
    \brief the name of the displayed document.
 
299
 
 
300
    This is a an invalid url if no document is displayed or if the
 
301
    source is unknown.
 
302
 
 
303
    When setting this property QTextBrowser tries to find a document
 
304
    with the specified name in the paths of the searchPaths property
 
305
    and directory of the current source, unless the value is an absolute
 
306
    file path. It also checks for optional anchors and scrolls the document
 
307
    accordingly
 
308
 
 
309
    If the first tag in the document is \c{<qt type=detail>}, the
 
310
    document is displayed as a popup rather than as new document in
 
311
    the browser window itself. Otherwise, the document is displayed
 
312
    normally in the text browser with the text set to the contents of
 
313
    the named document with setHtml().
 
314
*/
 
315
QUrl QTextBrowser::source() const
 
316
{
 
317
    Q_D(const QTextBrowser);
 
318
    if (d->stack.isEmpty())
 
319
        return QUrl();
 
320
    else
 
321
        return d->stack.top().url;
 
322
}
 
323
 
 
324
/*!
 
325
    \property QTextBrowser::searchPaths
 
326
    \brief the search paths used by the text browser to find supporting
 
327
    content
 
328
 
 
329
    QTextBrowser uses this list to locate images and documents.
 
330
*/
 
331
 
 
332
QStringList QTextBrowser::searchPaths() const
 
333
{
 
334
    Q_D(const QTextBrowser);
 
335
    return d->searchPaths;
 
336
}
 
337
 
 
338
void QTextBrowser::setSearchPaths(const QStringList &paths)
 
339
{
 
340
    Q_D(QTextBrowser);
 
341
    d->searchPaths = paths;
 
342
}
 
343
 
 
344
/*!
 
345
    Reloads the current set source.
 
346
*/
 
347
void QTextBrowser::reload()
 
348
{
 
349
    Q_D(QTextBrowser);
 
350
    QUrl s = d->currentURL;
 
351
    d->currentURL = QUrl();
 
352
    setSource(s);
 
353
}
 
354
 
 
355
void QTextBrowser::setSource(const QUrl &url)
 
356
{
 
357
    Q_D(QTextBrowser);
 
358
 
 
359
    int hpos = d->hbar->value();
 
360
    int vpos = d->vbar->value();
 
361
 
 
362
    d->setSource(url);
 
363
 
 
364
    QUrl currentUrlWithoutFragment = d->currentURL;
 
365
    currentUrlWithoutFragment.setFragment(QString());
 
366
    QUrl urlWithoutFragment = url;
 
367
    urlWithoutFragment.setFragment(QString());
 
368
 
 
369
    if (url.isValid()
 
370
        && (urlWithoutFragment == currentUrlWithoutFragment)) {
 
371
        if (!d->stack.isEmpty() && d->stack.top().url == url) {
 
372
            // the same url you are already watching
 
373
        } else {
 
374
            if (!d->stack.isEmpty()) {
 
375
                d->stack.top().hpos = hpos;
 
376
                d->stack.top().vpos = vpos;
 
377
            }
 
378
            QTextBrowserPrivate::HistoryEntry entry;
 
379
            entry.url = url;
 
380
            entry.hpos = 0;
 
381
            entry.vpos = 0;
 
382
            d->stack.push(entry);
 
383
 
 
384
            emit backwardAvailable(d->stack.count() > 1);
 
385
 
 
386
            if (!d->forwardStack.isEmpty() && d->forwardStack.top().url == url) {
 
387
                d->forwardStack.pop();
 
388
                emit forwardAvailable(d->forwardStack.count() > 0);
 
389
            } else {
 
390
                d->forwardStack.clear();
 
391
                emit forwardAvailable(false);
 
392
            }
 
393
        }
 
394
    }
 
395
}
 
396
 
 
397
/*!
 
398
    \fn void QTextBrowser::backwardAvailable(bool available)
 
399
 
 
400
    This signal is emitted when the availability of backward()
 
401
    changes. \a available is false when the user is at home();
 
402
    otherwise it is true.
 
403
*/
 
404
 
 
405
/*!
 
406
    \fn void QTextBrowser::forwardAvailable(bool available)
 
407
 
 
408
    This signal is emitted when the availability of forward() changes.
 
409
    \a available is true after the user navigates backward() and false
 
410
    when the user navigates or goes forward().
 
411
*/
 
412
 
 
413
/*!
 
414
    \fn void QTextBrowser::sourceChanged(const QUrl &src)
 
415
 
 
416
    This signal is emitted when the source has changed, \a src
 
417
    being the new source.
 
418
 
 
419
    Source changes happen both programmatically when calling
 
420
    setSource(), forward(), backword() or home() or when the user
 
421
    clicks on links or presses the equivalent key sequences.
 
422
*/
 
423
 
 
424
/*!  \fn void QTextBrowser::highlighted(const QUrl &link)
 
425
 
 
426
    This signal is emitted when the user has selected but not
 
427
    activated an anchor in the document. The URL referred to by the
 
428
    anchor is passed in \a link.
 
429
*/
 
430
 
 
431
/*!  \fn void QTextBrowser::highlighted(const QString &link)
 
432
     \overload
 
433
 
 
434
     Convenience signal that allows connecting to a slot
 
435
     that takes just a QString, like for example QStatusBar's
 
436
     message().
 
437
*/
 
438
 
 
439
 
 
440
/*!
 
441
    \fn void QTextBrowser::anchorClicked(const QUrl &link)
 
442
 
 
443
    This signal is emitted when the user clicks an anchor. The
 
444
    URL referred to by the anchor is passed in \a link.
 
445
*/
 
446
 
 
447
/*!
 
448
    Changes the document displayed to the previous document in the
 
449
    list of documents built by navigating links. Does nothing if there
 
450
    is no previous document.
 
451
 
 
452
    \sa forward(), backwardAvailable()
 
453
*/
 
454
void QTextBrowser::backward()
 
455
{
 
456
    Q_D(QTextBrowser);
 
457
    if (d->stack.count() <= 1)
 
458
        return;
 
459
    d->forwardStack.push(d->stack.pop());
 
460
    d->forwardStack.top().hpos = d->hbar->value();
 
461
    d->forwardStack.top().vpos = d->vbar->value();
 
462
    d->setSource(d->stack.top().url);
 
463
    d->hbar->setValue(d->stack.top().hpos);
 
464
    d->vbar->setValue(d->stack.top().vpos);
 
465
    emit backwardAvailable(d->stack.count() > 1);
 
466
    emit forwardAvailable(true);
 
467
}
 
468
 
 
469
/*!
 
470
    Changes the document displayed to the next document in the list of
 
471
    documents built by navigating links. Does nothing if there is no
 
472
    next document.
 
473
 
 
474
    \sa backward(), forwardAvailable()
 
475
*/
 
476
void QTextBrowser::forward()
 
477
{
 
478
    Q_D(QTextBrowser);
 
479
    if (d->forwardStack.isEmpty())
 
480
        return;
 
481
    if (!d->stack.isEmpty()) {
 
482
        d->stack.top().hpos = d->hbar->value();
 
483
        d->stack.top().vpos = d->vbar->value();
 
484
    }
 
485
    d->stack.push(d->forwardStack.pop());
 
486
    setSource(d->stack.top().url);
 
487
    d->hbar->setValue(d->stack.top().hpos);
 
488
    d->vbar->setValue(d->stack.top().vpos);
 
489
    emit backwardAvailable(true);
 
490
    emit forwardAvailable(!d->forwardStack.isEmpty());
 
491
}
 
492
 
 
493
/*!
 
494
    Changes the document displayed to be the first document the
 
495
    browser displayed.
 
496
*/
 
497
void QTextBrowser::home()
 
498
{
 
499
    Q_D(QTextBrowser);
 
500
    if (d->home.isValid())
 
501
        setSource(d->home);
 
502
}
 
503
 
 
504
/*!
 
505
    The event \a ev is used to provide the following keyboard shortcuts:
 
506
    \table
 
507
    \header \i Keypress            \i Action
 
508
    \row \i Alt+Left Arrow  \i \l backward()
 
509
    \row \i Alt+Right Arrow \i \l forward()
 
510
    \row \i Alt+Up Arrow    \i \l home()
 
511
    \endtable
 
512
*/
 
513
void QTextBrowser::keyPressEvent(QKeyEvent *ev)
 
514
{
 
515
    Q_D(QTextBrowser);
 
516
 
 
517
    if (ev->modifiers() & Qt::AltModifier) {
 
518
        switch (ev->key()) {
 
519
        case Qt::Key_Right:
 
520
            forward();
 
521
            ev->accept();
 
522
            return;
 
523
        case Qt::Key_Left:
 
524
            backward();
 
525
            ev->accept();
 
526
            return;
 
527
        case Qt::Key_Up:
 
528
            home();
 
529
            ev->accept();
 
530
            return;
 
531
        }
 
532
    } else if ((ev->key() == Qt::Key_Return
 
533
                || ev->key() == Qt::Key_Enter)
 
534
               && d->focusIndicator.hasSelection()) {
 
535
 
 
536
        QTextCursor cursor = d->focusIndicator;
 
537
        if (cursor.selectionStart() != cursor.position())
 
538
            cursor.setPosition(cursor.selectionStart());
 
539
        cursor.movePosition(QTextCursor::NextCharacter);
 
540
 
 
541
        ev->accept();
 
542
 
 
543
        const QString href = cursor.charFormat().anchorHref();
 
544
        d->activateAnchor(href);
 
545
        return;
 
546
    }
 
547
    QTextEdit::keyPressEvent(ev);
 
548
}
 
549
 
 
550
/*!
 
551
    \reimp
 
552
*/
 
553
void QTextBrowser::mouseMoveEvent(QMouseEvent *e)
 
554
{
 
555
    Q_D(QTextBrowser);
 
556
    QTextEdit::mouseMoveEvent(e);
 
557
 
 
558
    QString anchor = anchorAt(e->pos());
 
559
    if (anchor.isEmpty()) {
 
560
        d->viewport->setCursor(Qt::ArrowCursor);
 
561
        emit highlighted(QUrl());
 
562
        emit highlighted(QString());
 
563
    } else {
 
564
        d->viewport->setCursor(Qt::PointingHandCursor);
 
565
 
 
566
        QUrl url = QUrl(d->currentURL).resolved(anchor);
 
567
        emit highlighted(url);
 
568
        // convenience to ease connecting to QStatusBar::showMessage(const QString &)
 
569
        emit highlighted(url.toString());
 
570
    }
 
571
 
 
572
}
 
573
 
 
574
/*!
 
575
    \reimp
 
576
*/
 
577
void QTextBrowser::mousePressEvent(QMouseEvent *e)
 
578
{
 
579
    QTextEdit::mousePressEvent(e);
 
580
 
 
581
    Q_D(QTextBrowser);
 
582
    d->anchorOnMousePress = anchorAt(e->pos());
 
583
    d->hadSelectionOnMousePress = d->cursor.hasSelection();
 
584
}
 
585
 
 
586
/*!
 
587
    \reimp
 
588
*/
 
589
void QTextBrowser::mouseReleaseEvent(QMouseEvent *e)
 
590
{
 
591
    Q_D(QTextBrowser);
 
592
    QTextEdit::mouseReleaseEvent(e);
 
593
 
 
594
    if (!(e->button() & Qt::LeftButton))
 
595
        return;
 
596
 
 
597
    const QString anchor = anchorAt(e->pos());
 
598
 
 
599
    if (anchor.isEmpty())
 
600
        return;
 
601
 
 
602
    if (!d->cursor.hasSelection()
 
603
        || (anchor == d->anchorOnMousePress && d->hadSelectionOnMousePress))
 
604
        d->activateAnchor(anchor);
 
605
}
 
606
 
 
607
/*!
 
608
    \reimp
 
609
*/
 
610
void QTextBrowser::focusOutEvent(QFocusEvent *ev)
 
611
{
 
612
    Q_D(QTextBrowser);
 
613
    if (ev->reason() != Qt::ActiveWindowFocusReason
 
614
        && ev->reason() != Qt::PopupFocusReason) {
 
615
        d->focusIndicator.clearSelection();
 
616
        d->viewport->update();
 
617
    }
 
618
    QTextEdit::focusOutEvent(ev);
 
619
}
 
620
 
 
621
/*!
 
622
    \reimp
 
623
*/
 
624
bool QTextBrowser::focusNextPrevChild(bool next)
 
625
{
 
626
    Q_D(QTextBrowser);
 
627
 
 
628
    if (!d->readOnly)
 
629
        return false;
 
630
 
 
631
    if (!d->focusIndicator.hasSelection()) {
 
632
        d->focusIndicator = QTextCursor(d->doc);
 
633
        if (next)
 
634
            d->focusIndicator.movePosition(QTextCursor::Start);
 
635
        else
 
636
            d->focusIndicator.movePosition(QTextCursor::End);
 
637
    }
 
638
 
 
639
    Q_ASSERT(!d->focusIndicator.isNull());
 
640
 
 
641
    int anchorStart = -1;
 
642
    int anchorEnd = -1;
 
643
 
 
644
    if (next) {
 
645
        const int startPos = d->focusIndicator.selectionEnd();
 
646
 
 
647
        QTextBlock block = d->doc->findBlock(startPos);
 
648
        QTextBlock::Iterator it = block.begin();
 
649
 
 
650
        while (!it.atEnd() && it.fragment().position() < startPos)
 
651
            ++it;
 
652
 
 
653
        while (block.isValid()) {
 
654
            anchorStart = -1;
 
655
 
 
656
            // find next anchor
 
657
            for (; !it.atEnd(); ++it) {
 
658
                const QTextFragment fragment = it.fragment();
 
659
                const QTextCharFormat fmt = fragment.charFormat();
 
660
 
 
661
                if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
 
662
                    anchorStart = fragment.position();
 
663
                    break;
 
664
                }
 
665
            }
 
666
 
 
667
            if (anchorStart != -1) {
 
668
                anchorEnd = -1;
 
669
 
 
670
                // find next non-anchor fragment
 
671
                for (; !it.atEnd(); ++it) {
 
672
                    const QTextFragment fragment = it.fragment();
 
673
                    const QTextCharFormat fmt = fragment.charFormat();
 
674
 
 
675
                    if (!fmt.isAnchor()) {
 
676
                        anchorEnd = fragment.position();
 
677
                        break;
 
678
                    }
 
679
                }
 
680
 
 
681
                if (anchorEnd == -1)
 
682
                    anchorEnd = block.position() + block.length() - 1;
 
683
 
 
684
                // make found selection
 
685
                break;
 
686
            }
 
687
 
 
688
            block = block.next();
 
689
            it = block.begin();
 
690
        }
 
691
    } else {
 
692
        int startPos = d->focusIndicator.selectionStart();
 
693
        if (startPos > 0)
 
694
            --startPos;
 
695
 
 
696
        QTextBlock block = d->doc->findBlock(startPos);
 
697
        QTextBlock::Iterator blockStart = block.begin();
 
698
        QTextBlock::Iterator it = block.end();
 
699
 
 
700
        if (startPos == block.position()) {
 
701
            it = block.begin();
 
702
        } else {
 
703
            do {
 
704
                if (it == blockStart) {
 
705
                    it = QTextBlock::Iterator();
 
706
                    block = QTextBlock();
 
707
                } else {
 
708
                    --it;
 
709
                }
 
710
            } while (!it.atEnd() && it.fragment().position() + it.fragment().length() - 1 > startPos);
 
711
        }
 
712
 
 
713
        while (block.isValid()) {
 
714
            anchorStart = -1;
 
715
 
 
716
            if (!it.atEnd()) {
 
717
                do {
 
718
                    const QTextFragment fragment = it.fragment();
 
719
                    const QTextCharFormat fmt = fragment.charFormat();
 
720
 
 
721
                    if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
 
722
                        anchorStart = fragment.position() + fragment.length();
 
723
                        break;
 
724
                    }
 
725
 
 
726
                    if (it == blockStart)
 
727
                        it = QTextBlock::Iterator();
 
728
                    else
 
729
                        --it;
 
730
                } while (!it.atEnd());
 
731
            }
 
732
 
 
733
            if (anchorStart != -1 && !it.atEnd()) {
 
734
                anchorEnd = -1;
 
735
 
 
736
                do {
 
737
                    const QTextFragment fragment = it.fragment();
 
738
                    const QTextCharFormat fmt = fragment.charFormat();
 
739
 
 
740
                    if (!fmt.isAnchor()) {
 
741
                        anchorEnd = fragment.position() + fragment.length();
 
742
                        break;
 
743
                    }
 
744
 
 
745
                    if (it == blockStart)
 
746
                        it = QTextBlock::Iterator();
 
747
                    else
 
748
                        --it;
 
749
                } while (!it.atEnd());
 
750
 
 
751
                if (anchorEnd == -1)
 
752
                    anchorEnd = qMax(0, block.position());
 
753
 
 
754
                break;
 
755
            }
 
756
 
 
757
            block = block.previous();
 
758
            it = block.end();
 
759
            if (it != block.begin())
 
760
                --it;
 
761
            blockStart = block.begin();
 
762
        }
 
763
 
 
764
    }
 
765
 
 
766
    if (anchorStart != -1 && anchorEnd != -1) {
 
767
        d->focusIndicator.setPosition(anchorStart);
 
768
        d->focusIndicator.setPosition(anchorEnd, QTextCursor::KeepAnchor);
 
769
    } else {
 
770
        d->focusIndicator.clearSelection();
 
771
    }
 
772
 
 
773
    if (d->focusIndicator.hasSelection()) {
 
774
        qSwap(d->focusIndicator, d->cursor);
 
775
        ensureCursorVisible();
 
776
        qSwap(d->focusIndicator, d->cursor);
 
777
        d->viewport->update();
 
778
        return true;
 
779
    } else {
 
780
        d->viewport->update();
 
781
        return false;
 
782
    }
 
783
}
 
784
 
 
785
/*!
 
786
  \reimp
 
787
*/
 
788
void QTextBrowser::paintEvent(QPaintEvent *e)
 
789
{
 
790
    Q_D(QTextBrowser);
 
791
    QPainter p(d->viewport);
 
792
    d->paint(&p, e);
 
793
}
 
794
 
 
795
/*!
 
796
    This function is called when the document is loaded. The \a type
 
797
    indicates the type of resource to be loaded. For each image in
 
798
    the document, this function is called once.
 
799
 
 
800
    The default implementation ignores \a type and tries to locate
 
801
    the resources by interpreting \a name as a file name. If it is
 
802
    not an absolute path it tries to find the file in the paths of
 
803
    the \l searchPaths property and in the same directory as the
 
804
    current source. On success, the result is a QVariant that stores
 
805
    a QByteArray with the contents of the file.
 
806
 
 
807
    If you reimplement this function, you can return other QVariant
 
808
    types. The table below shows which variant types are supported
 
809
    depending on the resource type:
 
810
 
 
811
    \table
 
812
    \header \i ResourceType  \i QVariant::Type
 
813
    \row    \i QTextDocument::HtmlResource  \i QString or QByteArray
 
814
    \row    \i QTextDocument::ImageResource \i QImage, QPixmap or QByteArray
 
815
    \endtable
 
816
*/
 
817
QVariant QTextBrowser::loadResource(int /*type*/, const QUrl &name)
 
818
{
 
819
    Q_D(QTextBrowser);
 
820
 
 
821
    QByteArray data;
 
822
    QUrl resolved = name;
 
823
    if (!isAbsoluteFileName(name.toLocalFile()))
 
824
        resolved = source().resolved(name);    
 
825
    QString fileName = d->findFile(resolved);
 
826
    QFile f(fileName);
 
827
    if (f.open(QFile::ReadOnly)) {
 
828
        data = f.readAll();
 
829
        f.close();
 
830
    } else {
 
831
        qWarning("QTextBrowser: cannot open '%s' for reading", fileName.toLocal8Bit().data());
 
832
    }
 
833
 
 
834
    return data;
 
835
}
 
836
 
 
837
#include "moc_qtextbrowser.cpp"