Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtextbrowser.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include "qtextbrowser.h"
6#include "qtextedit_p.h"
7
8#include <qstack.h>
9#include <qapplication.h>
10#include <private/qapplication_p.h>
11#include <qevent.h>
12#include <qdebug.h>
13#include <qabstracttextdocumentlayout.h>
14#include "private/qtextdocumentlayout_p.h"
15#include <qpainter.h>
16#include <qdir.h>
17#if QT_CONFIG(whatsthis)
18#include <qwhatsthis.h>
19#endif
20#include <qtextobject.h>
21#include <qdesktopservices.h>
22#include <qstringconverter.h>
23
25
26using namespace Qt::StringLiterals;
27
28static inline bool shouldEnableInputMethod(QTextBrowser *texbrowser)
29{
30#if defined (Q_OS_ANDROID)
31 return !texbrowser->isReadOnly() || (texbrowser->textInteractionFlags() & Qt::TextSelectableByMouse);
32#else
33 return !texbrowser->isReadOnly();
34#endif
35}
36
37Q_STATIC_LOGGING_CATEGORY(lcBrowser, "qt.text.browser")
38
40{
41 Q_DECLARE_PUBLIC(QTextBrowser)
42public:
52
53 void init();
54
66
67 HistoryEntry history(int i) const
68 {
69 if (i <= 0)
70 if (-i < stack.size())
71 return stack[stack.size()+i-1];
72 else
73 return HistoryEntry();
74 else
75 if (i <= forwardStack.size())
76 return forwardStack[forwardStack.size()-i];
77 else
78 return HistoryEntry();
79 }
80
81
84
87 QUrl home;
89
91
92 /*flag necessary to give the linkClicked() signal some meaningful
93 semantics when somebody connected to it calls setText() or
94 setSource() */
97
100
102
103#ifndef QT_NO_CURSOR
105#endif
106
107 QString findFile(const QUrl &name) const;
108
109 inline void documentModified()
110 {
111 textOrSourceChanged = true;
112 forceLoadOnSourceChange = !currentURL.path().isEmpty();
113 }
114
115 void activateAnchor(const QString &href);
116 void highlightLink(const QString &href);
117
118 void setSource(const QUrl &url, QTextDocument::ResourceType type);
119
120 // re-imlemented from QTextEditPrivate
121 virtual QUrl resolveUrl(const QUrl &url) const override;
122 inline QUrl resolveUrl(const QString &url) const
123 { return resolveUrl(QUrl(url)); }
124
125 void emitHighlighted(const QUrl &url)
126 {
127 Q_Q(QTextBrowser);
128 emit q->highlighted(url);
129 }
131};
133
134QString QTextBrowserPrivate::findFile(const QUrl &name) const
135{
136 QString fileName;
137 if (name.scheme() == "qrc"_L1) {
138 fileName = ":/"_L1 + name.path();
139 } else if (name.scheme().isEmpty()) {
140 fileName = name.path();
141 } else {
142#if defined(Q_OS_ANDROID)
143 if (name.scheme() == "assets"_L1)
144 fileName = "assets:"_L1 + name.path();
145 else
146#endif
147 fileName = name.toLocalFile();
148 }
149
150 if (fileName.isEmpty())
151 return fileName;
152
153 if (QFileInfo(fileName).isAbsolute())
154 return fileName;
155
156 for (QString path : std::as_const(searchPaths)) {
157 if (!path.endsWith(u'/'))
158 path.append(u'/');
159 path.append(fileName);
160 if (QFileInfo(path).isReadable())
161 return path;
162 }
163
164 return fileName;
165}
166
167QUrl QTextBrowserPrivate::resolveUrl(const QUrl &url) const
168{
169 if (!url.isRelative())
170 return url;
171
172 // For the second case QUrl can merge "#someanchor" with "foo.html"
173 // correctly to "foo.html#someanchor"
174 if (!(currentURL.isRelative()
175 || (currentURL.scheme() == "file"_L1
176 && !QFileInfo(currentURL.toLocalFile()).isAbsolute()))
177 || (url.hasFragment() && url.path().isEmpty())) {
178 return currentURL.resolved(url);
179 }
180
181 // this is our last resort when current url and new url are both relative
182 // we try to resolve against the current working directory in the local
183 // file system.
184 QFileInfo fi(currentURL.toLocalFile());
185 if (fi.exists()) {
186 return QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(url);
187 }
188
189 return url;
190}
191
192void QTextBrowserPrivate::activateAnchor(const QString &href)
193{
194 if (href.isEmpty())
195 return;
196 Q_Q(QTextBrowser);
197
198#ifndef QT_NO_CURSOR
199 viewport->setCursor(oldCursor);
200#endif
201
202 const QUrl url = resolveUrl(href);
203
204 if (!openLinks) {
205 emit q->anchorClicked(url);
206 return;
207 }
208
209 textOrSourceChanged = false;
210
211#ifndef QT_NO_DESKTOPSERVICES
212 bool isFileScheme =
213 url.scheme() == "file"_L1
214#if defined(Q_OS_ANDROID)
215 || url.scheme() == "assets"_L1
216#endif
217 || url.scheme() == "qrc"_L1;
218 if ((openExternalLinks && !isFileScheme && !url.isRelative())
219 || (url.isRelative() && !currentURL.isRelative() && !isFileScheme)) {
220 QDesktopServices::openUrl(url);
221 return;
222 }
223#endif
224
225 emit q->anchorClicked(url);
226
228 return;
229
230 q->setSource(url);
231}
232
233void QTextBrowserPrivate::highlightLink(const QString &anchor)
234{
235 if (anchor.isEmpty()) {
236#ifndef QT_NO_CURSOR
237 if (viewport->cursor().shape() != Qt::PointingHandCursor)
238 oldCursor = viewport->cursor();
239 viewport->setCursor(oldCursor);
240#endif
241 emitHighlighted(QUrl());
242 } else {
243#ifndef QT_NO_CURSOR
244 viewport->setCursor(Qt::PointingHandCursor);
245#endif
246
247 const QUrl url = resolveUrl(anchor);
249 }
250}
251
252void QTextBrowserPrivate::setSource(const QUrl &url, QTextDocument::ResourceType type)
253{
254 Q_Q(QTextBrowser);
255#ifndef QT_NO_CURSOR
256 if (q->isVisible())
257 QGuiApplication::setOverrideCursor(Qt::WaitCursor);
258#endif
259 textOrSourceChanged = true;
260
261 QString txt;
262
263 bool doSetText = false;
264
265 QUrl currentUrlWithoutFragment = currentURL;
266 currentUrlWithoutFragment.setFragment(QString());
267 QUrl newUrlWithoutFragment = currentURL.resolved(url);
268 newUrlWithoutFragment.setFragment(QString());
269 QString fileName = url.fileName();
270 if (type == QTextDocument::UnknownResource) {
271#if QT_CONFIG(textmarkdownreader)
272 if (fileName.endsWith(".md"_L1) ||
273 fileName.endsWith(".mkd"_L1) ||
274 fileName.endsWith(".markdown"_L1))
275 type = QTextDocument::MarkdownResource;
276 else
277#endif
278 type = QTextDocument::HtmlResource;
279 }
280 currentType = type;
281
282 if (url.isValid()
283 && (newUrlWithoutFragment != currentUrlWithoutFragment || forceLoadOnSourceChange)) {
284 QVariant data = q->loadResource(type, resolveUrl(url));
285 if (data.userType() == QMetaType::QString) {
286 txt = data.toString();
287 } else if (data.userType() == QMetaType::QByteArray) {
288 QByteArray ba = data.toByteArray();
289 if (type == QTextDocument::HtmlResource) {
290 auto decoder = QStringDecoder::decoderForHtml(ba);
291 if (!decoder.isValid())
292 // fall back to utf8
293 decoder = QStringDecoder(QStringDecoder::Utf8);
294 txt = decoder(ba);
295 } else {
296 txt = QString::fromUtf8(ba);
297 }
298 }
299 if (Q_UNLIKELY(txt.isEmpty()))
300 qWarning("QTextBrowser: No document for %s", url.toString().toLatin1().constData());
301
302 if (q->isVisible()) {
303 const QStringView firstTag = QStringView{txt}.left(txt.indexOf(u'>') + 1);
304 if (firstTag.startsWith("<qt"_L1) && firstTag.contains("type"_L1) && firstTag.contains("detail"_L1)) {
305#ifndef QT_NO_CURSOR
306 QGuiApplication::restoreOverrideCursor();
307#endif
308#if QT_CONFIG(whatsthis)
309 QWhatsThis::showText(QCursor::pos(), txt, q);
310#endif
311 return;
312 }
313 }
314
315 currentURL = resolveUrl(url);
316 doSetText = true;
317 }
318
319 if (!home.isValid())
320 home = url;
321
322 if (doSetText) {
323 // Setting the base URL helps QTextDocument::resource() to find resources with relative paths.
324 // But don't set it unless it contains the document's path, because QTextBrowserPrivate::resolveUrl()
325 // can already deal with local files on the filesystem in case the base URL was not set.
326 QUrl baseUrl = currentURL.adjusted(QUrl::RemoveFilename);
327 if (!baseUrl.path().isEmpty())
328 q->document()->setBaseUrl(baseUrl);
329 q->document()->setMetaInformation(QTextDocument::DocumentUrl, currentURL.toString());
330 qCDebug(lcBrowser) << "loading" << currentURL << "base" << q->document()->baseUrl() << "type" << type << txt.size() << "chars";
331#if QT_CONFIG(textmarkdownreader)
332 if (type == QTextDocument::MarkdownResource)
333 q->QTextEdit::setMarkdown(txt);
334 else
335#endif
336#ifndef QT_NO_TEXTHTMLPARSER
337 q->QTextEdit::setHtml(txt);
338#else
339 q->QTextEdit::setPlainText(txt);
340#endif
341
342 }
343
345
346 if (!url.fragment().isEmpty()) {
347 q->scrollToAnchor(url.fragment());
348 } else {
349 hbar->setValue(0);
350 vbar->setValue(0);
351 }
352
353#ifndef QT_NO_CURSOR
354 if (q->isVisible())
355 QGuiApplication::restoreOverrideCursor();
356#endif
357 emit q->sourceChanged(url);
358}
359
361{
362 HistoryEntry entry;
363 entry.url = q_func()->source();
364 entry.type = q_func()->sourceType();
365 entry.title = q_func()->documentTitle();
366 entry.hpos = hbar->value();
367 entry.vpos = vbar->value();
368
369 const QTextCursor cursor = control->textCursor();
370 if (control->cursorIsFocusIndicator()
371 && cursor.hasSelection()) {
372
373 entry.focusIndicatorPosition = cursor.position();
374 entry.focusIndicatorAnchor = cursor.anchor();
375 }
376 return entry;
377}
378
380{
381 setSource(entry.url, entry.type);
382 hbar->setValue(entry.hpos);
383 vbar->setValue(entry.vpos);
384 if (entry.focusIndicatorAnchor != -1 && entry.focusIndicatorPosition != -1) {
385 QTextCursor cursor(control->document());
386 cursor.setPosition(entry.focusIndicatorAnchor);
387 cursor.setPosition(entry.focusIndicatorPosition, QTextCursor::KeepAnchor);
388 control->setTextCursor(cursor);
389 control->setCursorIsFocusIndicator(true);
390 }
391}
392
393/*!
394 \class QTextBrowser
395 \brief The QTextBrowser class provides a rich text browser with hypertext navigation.
396
397 \ingroup richtext-processing
398 \inmodule QtWidgets
399
400 This class extends QTextEdit (in read-only mode), adding some navigation
401 functionality so that users can follow links in hypertext documents.
402
403 If you want to provide your users with an editable rich text editor,
404 use QTextEdit. If you want a text browser without hypertext navigation
405 use QTextEdit, and use QTextEdit::setReadOnly() to disable
406 editing. If you just need to display a small piece of rich text
407 use QLabel.
408
409 \section1 Document Source and Contents
410
411 The contents of QTextEdit are set with setHtml() or setPlainText(),
412 but QTextBrowser also implements the setSource() function, making it
413 possible to use a named document as the source text. The name is looked
414 up in a list of search paths and in the directory of the current document
415 factory.
416
417 If a document name ends with
418 an anchor (for example, "\c #anchor"), the text browser automatically
419 scrolls to that position (using scrollToAnchor()). When the user clicks
420 on a hyperlink, the browser will call setSource() itself with the link's
421 \c href value as argument. You can track the current source by connecting
422 to the sourceChanged() signal.
423
424 \section1 Navigation
425
426 QTextBrowser provides backward() and forward() slots which you can
427 use to implement Back and Forward buttons. The home() slot sets
428 the text to the very first document displayed. The anchorClicked()
429 signal is emitted when the user clicks an anchor. To override the
430 default navigation behavior of the browser, call the setSource()
431 function to supply new document text in a slot connected to this
432 signal.
433
434 If you want to load documents stored in the Qt resource system use
435 \c{qrc} as the scheme in the URL to load. For example, for the document
436 resource path \c{:/docs/index.html} use \c{qrc:/docs/index.html} as
437 the URL with setSource().
438
439 \sa QTextEdit, QTextDocument
440*/
441
442/*!
443 \property QTextBrowser::modified
444 \brief whether the contents of the text browser have been modified
445*/
446
447/*!
448 \property QTextBrowser::readOnly
449 \brief whether the text browser is read-only
450
451 By default, this property is \c true.
452*/
453
454/*!
455 \property QTextBrowser::undoRedoEnabled
456 \brief whether the text browser supports undo/redo operations
457
458 By default, this property is \c false.
459*/
460
462{
463 Q_Q(QTextBrowser);
464 control->setTextInteractionFlags(Qt::TextBrowserInteraction);
465#ifndef QT_NO_CURSOR
466 viewport->setCursor(oldCursor);
467#endif
468 q->setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(q));
469 q->setUndoRedoEnabled(false);
470 viewport->setMouseTracking(true);
471 connections = {
472 QObjectPrivate::connect(q->document(), &QTextDocument::contentsChanged,
473 this, &QTextBrowserPrivate::documentModified),
474 QObjectPrivate::connect(control, &QWidgetTextControl::linkActivated,
475 this, &QTextBrowserPrivate::activateAnchor),
476 QObjectPrivate::connect(control, &QWidgetTextControl::linkHovered,
477 this, &QTextBrowserPrivate::highlightLink),
478 };
479}
480
481/*!
482 Constructs an empty QTextBrowser with parent \a parent.
483*/
484QTextBrowser::QTextBrowser(QWidget *parent)
485 : QTextEdit(*new QTextBrowserPrivate, parent)
486{
487 Q_D(QTextBrowser);
488 d->init();
489}
490
491
492/*!
493 \internal
494*/
495QTextBrowser::~QTextBrowser()
496{
497}
498
499/*!
500 \property QTextBrowser::source
501 \brief the name of the displayed document.
502
503 This is a an invalid url if no document is displayed or if the
504 source is unknown.
505
506 When setting this property QTextBrowser tries to find a document
507 with the specified name in the paths of the searchPaths property
508 and directory of the current source, unless the value is an absolute
509 file path. It also checks for optional anchors and scrolls the document
510 accordingly
511
512 If the first tag in the document is \c{<qt type=detail>}, the
513 document is displayed as a popup rather than as new document in
514 the browser window itself. Otherwise, the document is displayed
515 normally in the text browser with the text set to the contents of
516 the named document with \l QTextDocument::setHtml() or
517 \l QTextDocument::setMarkdown(), depending on whether the filename ends
518 with any of the known Markdown file extensions.
519
520 If you would like to avoid automatic type detection
521 and specify the type explicitly, call setSource() rather than
522 setting this property.
523
524 By default, this property contains an empty URL.
525*/
526QUrl QTextBrowser::source() const
527{
528 Q_D(const QTextBrowser);
529 if (d->stack.isEmpty())
530 return QUrl();
531 else
532 return d->stack.top().url;
533}
534
535/*!
536 \property QTextBrowser::sourceType
537 \brief the type of the displayed document
538
539 This is QTextDocument::UnknownResource if no document is displayed or if
540 the type of the source is unknown. Otherwise it holds the type that was
541 detected, or the type that was specified when setSource() was called.
542*/
543QTextDocument::ResourceType QTextBrowser::sourceType() const
544{
545 Q_D(const QTextBrowser);
546 if (d->stack.isEmpty())
547 return QTextDocument::UnknownResource;
548 else
549 return d->stack.top().type;
550}
551
552/*!
553 \property QTextBrowser::searchPaths
554 \brief the search paths used by the text browser to find supporting
555 content
556
557 QTextBrowser uses this list to locate images and documents.
558
559 By default, this property contains an empty string list.
560*/
561
562QStringList QTextBrowser::searchPaths() const
563{
564 Q_D(const QTextBrowser);
565 return d->searchPaths;
566}
567
568void QTextBrowser::setSearchPaths(const QStringList &paths)
569{
570 Q_D(QTextBrowser);
571 d->searchPaths = paths;
572}
573
574/*!
575 Reloads the current set source.
576*/
577void QTextBrowser::reload()
578{
579 Q_D(QTextBrowser);
580 QUrl s = d->currentURL;
581 d->currentURL = QUrl();
582 setSource(s, d->currentType);
583}
584
585/*!
586 Attempts to load the document at the given \a url with the specified \a type.
587
588 If \a type is \l {QTextDocument::UnknownResource}{UnknownResource}
589 (the default), the document type will be detected: that is, if the url ends
590 with an extension of \c{.md}, \c{.mkd} or \c{.markdown}, the document will be
591 loaded via \l QTextDocument::setMarkdown(); otherwise it will be loaded via
592 \l QTextDocument::setHtml(). This detection can be bypassed by specifying
593 the \a type explicitly.
594*/
595void QTextBrowser::setSource(const QUrl &url, QTextDocument::ResourceType type)
596{
597 doSetSource(url, type);
598}
599
600/*!
601 Attempts to load the document at the given \a url with the specified \a type.
602
603 setSource() calls doSetSource. In Qt 5, setSource(const QUrl &url) was virtual.
604 In Qt 6, doSetSource() is virtual instead, so that it can be overridden in subclasses.
605*/
606void QTextBrowser::doSetSource(const QUrl &url, QTextDocument::ResourceType type)
607{
608 Q_D(QTextBrowser);
609
610 const QTextBrowserPrivate::HistoryEntry historyEntry = d->createHistoryEntry();
611
612 d->setSource(url, type);
613
614 if (!url.isValid())
615 return;
616
617 // the same url you are already watching?
618 if (!d->stack.isEmpty() && d->stack.top().url == url)
619 return;
620
621 if (!d->stack.isEmpty())
622 d->stack.top() = historyEntry;
623
624 QTextBrowserPrivate::HistoryEntry entry;
625 entry.url = url;
626 entry.type = d->currentType;
627 entry.title = documentTitle();
628 entry.hpos = 0;
629 entry.vpos = 0;
630 d->stack.push(entry);
631
632 emit backwardAvailable(d->stack.size() > 1);
633
634 if (!d->forwardStack.isEmpty() && d->forwardStack.top().url == url) {
635 d->forwardStack.pop();
636 emit forwardAvailable(d->forwardStack.size() > 0);
637 } else {
638 d->forwardStack.clear();
639 emit forwardAvailable(false);
640 }
641
642 emit historyChanged();
643}
644
645/*!
646 \fn void QTextBrowser::backwardAvailable(bool available)
647
648 This signal is emitted when the availability of backward()
649 changes. \a available is false when the user is at home();
650 otherwise it is true.
651*/
652
653/*!
654 \fn void QTextBrowser::forwardAvailable(bool available)
655
656 This signal is emitted when the availability of forward() changes.
657 \a available is true after the user navigates backward() and false
658 when the user navigates or goes forward().
659*/
660
661/*!
662 \fn void QTextBrowser::historyChanged()
663 \since 4.4
664
665 This signal is emitted when the history changes.
666
667 \sa historyTitle(), historyUrl()
668*/
669
670/*!
671 \fn void QTextBrowser::sourceChanged(const QUrl &src)
672
673 This signal is emitted when the source has changed, \a src
674 being the new source.
675
676 Source changes happen both programmatically when calling
677 setSource(), forward(), backward() or home() or when the user
678 clicks on links or presses the equivalent key sequences.
679*/
680
681/*! \fn void QTextBrowser::highlighted(const QUrl &link)
682
683 This signal is emitted when the user has selected but not
684 activated an anchor in the document. The URL referred to by the
685 anchor is passed in \a link.
686*/
687
688/*!
689 \fn void QTextBrowser::anchorClicked(const QUrl &link)
690
691 This signal is emitted when the user clicks an anchor. The
692 URL referred to by the anchor is passed in \a link.
693
694 Note that the browser will automatically handle navigation to the
695 location specified by \a link unless the openLinks property
696 is set to false or you call setSource() in a slot connected.
697 This mechanism is used to override the default navigation features of the browser.
698*/
699
700/*!
701 Changes the document displayed to the previous document in the
702 list of documents built by navigating links. Does nothing if there
703 is no previous document.
704
705 \sa forward(), backwardAvailable()
706*/
707void QTextBrowser::backward()
708{
709 Q_D(QTextBrowser);
710 if (d->stack.size() <= 1)
711 return;
712
713 // Update the history entry
714 d->forwardStack.push(d->createHistoryEntry());
715 d->stack.pop(); // throw away the old version of the current entry
716 d->restoreHistoryEntry(d->stack.top()); // previous entry
717 emit backwardAvailable(d->stack.size() > 1);
718 emit forwardAvailable(true);
719 emit historyChanged();
720}
721
722/*!
723 Changes the document displayed to the next document in the list of
724 documents built by navigating links. Does nothing if there is no
725 next document.
726
727 \sa backward(), forwardAvailable()
728*/
729void QTextBrowser::forward()
730{
731 Q_D(QTextBrowser);
732 if (d->forwardStack.isEmpty())
733 return;
734 if (!d->stack.isEmpty()) {
735 // Update the history entry
736 d->stack.top() = d->createHistoryEntry();
737 }
738 d->stack.push(d->forwardStack.pop());
739 d->restoreHistoryEntry(d->stack.top());
740 emit backwardAvailable(true);
741 emit forwardAvailable(!d->forwardStack.isEmpty());
742 emit historyChanged();
743}
744
745/*!
746 Changes the document displayed to be the first document from
747 the history.
748*/
749void QTextBrowser::home()
750{
751 Q_D(QTextBrowser);
752 if (d->home.isValid())
753 setSource(d->home);
754}
755
756/*!
757 The event \a ev is used to provide the following keyboard shortcuts:
758 \table
759 \header \li Keypress \li Action
760 \row \li Alt+Left Arrow \li \l backward()
761 \row \li Alt+Right Arrow \li \l forward()
762 \row \li Alt+Up Arrow \li \l home()
763 \endtable
764*/
765void QTextBrowser::keyPressEvent(QKeyEvent *ev)
766{
767 if (ev->modifiers() & Qt::AltModifier) {
768 switch (ev->key()) {
769 case Qt::Key_Right:
770 forward();
771 ev->accept();
772 return;
773 case Qt::Key_Left:
774 backward();
775 ev->accept();
776 return;
777 case Qt::Key_Up:
778 home();
779 ev->accept();
780 return;
781 }
782 }
783 QTextEdit::keyPressEvent(ev);
784}
785
786/*!
787 \reimp
788*/
789void QTextBrowser::mouseMoveEvent(QMouseEvent *e)
790{
791 QTextEdit::mouseMoveEvent(e);
792}
793
794/*!
795 \reimp
796*/
797void QTextBrowser::mousePressEvent(QMouseEvent *e)
798{
799 QTextEdit::mousePressEvent(e);
800}
801
802/*!
803 \reimp
804*/
805void QTextBrowser::mouseReleaseEvent(QMouseEvent *e)
806{
807 QTextEdit::mouseReleaseEvent(e);
808}
809
810/*!
811 \reimp
812*/
813void QTextBrowser::focusOutEvent(QFocusEvent *ev)
814{
815#ifndef QT_NO_CURSOR
816 Q_D(QTextBrowser);
817 d->viewport->setCursor((!(d->control->textInteractionFlags() & Qt::TextEditable)) ? d->oldCursor : Qt::IBeamCursor);
818#endif
819 QTextEdit::focusOutEvent(ev);
820}
821
822/*!
823 \reimp
824*/
825bool QTextBrowser::focusNextPrevChild(bool next)
826{
827 Q_D(QTextBrowser);
828 if (d->control->setFocusToNextOrPreviousAnchor(next))
829 return true;
830
831 return QTextEdit::focusNextPrevChild(next);
832}
833
834/*!
835 \reimp
836*/
837void QTextBrowser::paintEvent(QPaintEvent *e)
838{
839 Q_D(QTextBrowser);
840 QPainter p(d->viewport);
841 d->paint(&p, e);
842}
843
844/*!
845 This function is called when the document is loaded and for
846 each image in the document. The \a type indicates the type of resource
847 to be loaded. An invalid QVariant is returned if the resource cannot be
848 loaded.
849
850 The default implementation ignores \a type and tries to locate
851 the resources by interpreting \a name as a file name. If it is
852 not an absolute path it tries to find the file in the paths of
853 the \l searchPaths property and in the same directory as the
854 current source. On success, the result is a QVariant that stores
855 a QByteArray with the contents of the file.
856
857 If you reimplement this function, you can return other QVariant
858 types. The table below shows which variant types are supported
859 depending on the resource type:
860
861 \table
862 \header \li ResourceType \li QMetaType::Type
863 \row \li QTextDocument::HtmlResource \li QString or QByteArray
864 \row \li QTextDocument::ImageResource \li QImage, QPixmap or QByteArray
865 \row \li QTextDocument::StyleSheetResource \li QString or QByteArray
866 \row \li QTextDocument::MarkdownResource \li QString or QByteArray
867 \endtable
868*/
869QVariant QTextBrowser::loadResource(int /*type*/, const QUrl &name)
870{
871 Q_D(QTextBrowser);
872
873 QByteArray data;
874 QString fileName = d->findFile(d->resolveUrl(name));
875 if (fileName.isEmpty())
876 return QVariant();
877 QFile f(fileName);
878 if (f.open(QFile::ReadOnly)) {
879 data = f.readAll();
880 f.close();
881 } else {
882 return QVariant();
883 }
884
885 return data;
886}
887
888/*!
889 \since 4.2
890
891 Returns \c true if the text browser can go backward in the document history
892 using backward().
893
894 \sa backwardAvailable(), backward()
895*/
896bool QTextBrowser::isBackwardAvailable() const
897{
898 Q_D(const QTextBrowser);
899 return d->stack.size() > 1;
900}
901
902/*!
903 \since 4.2
904
905 Returns \c true if the text browser can go forward in the document history
906 using forward().
907
908 \sa forwardAvailable(), forward()
909*/
910bool QTextBrowser::isForwardAvailable() const
911{
912 Q_D(const QTextBrowser);
913 return !d->forwardStack.isEmpty();
914}
915
916/*!
917 \since 4.2
918
919 Clears the history of visited documents and disables the forward and
920 backward navigation.
921
922 \sa backward(), forward()
923*/
924void QTextBrowser::clearHistory()
925{
926 Q_D(QTextBrowser);
927 d->forwardStack.clear();
928 if (!d->stack.isEmpty()) {
929 QTextBrowserPrivate::HistoryEntry historyEntry = d->stack.top();
930 d->stack.clear();
931 d->stack.push(historyEntry);
932 d->home = historyEntry.url;
933 }
934 emit forwardAvailable(false);
935 emit backwardAvailable(false);
936 emit historyChanged();
937}
938
939/*!
940 Returns the url of the HistoryItem.
941
942 \table
943 \header \li Input \li Return
944 \row \li \a{i} < 0 \li \l backward() history
945 \row \li\a{i} == 0 \li current, see QTextBrowser::source()
946 \row \li \a{i} > 0 \li \l forward() history
947 \endtable
948
949 \since 4.4
950*/
951QUrl QTextBrowser::historyUrl(int i) const
952{
953 Q_D(const QTextBrowser);
954 return d->history(i).url;
955}
956
957/*!
958 Returns the documentTitle() of the HistoryItem.
959
960 \table
961 \header \li Input \li Return
962 \row \li \a{i} < 0 \li \l backward() history
963 \row \li \a{i} == 0 \li current, see QTextBrowser::source()
964 \row \li \a{i} > 0 \li \l forward() history
965 \endtable
966
967 \snippet code/src_gui_widgets_qtextbrowser.cpp 0
968
969 \since 4.4
970*/
971QString QTextBrowser::historyTitle(int i) const
972{
973 Q_D(const QTextBrowser);
974 return d->history(i).title;
975}
976
977
978/*!
979 Returns the number of locations forward in the history.
980
981 \since 4.4
982*/
983int QTextBrowser::forwardHistoryCount() const
984{
985 Q_D(const QTextBrowser);
986 return d->forwardStack.size();
987}
988
989/*!
990 Returns the number of locations backward in the history.
991
992 \since 4.4
993*/
994int QTextBrowser::backwardHistoryCount() const
995{
996 Q_D(const QTextBrowser);
997 return d->stack.size()-1;
998}
999
1000/*!
1001 \property QTextBrowser::openExternalLinks
1002 \since 4.2
1003
1004 Specifies whether QTextBrowser should automatically open links to external
1005 sources using QDesktopServices::openUrl() instead of emitting the
1006 anchorClicked signal. Links are considered external if their scheme is
1007 neither file or qrc.
1008
1009 The default value is false.
1010*/
1011bool QTextBrowser::openExternalLinks() const
1012{
1013 Q_D(const QTextBrowser);
1014 return d->openExternalLinks;
1015}
1016
1017void QTextBrowser::setOpenExternalLinks(bool open)
1018{
1019 Q_D(QTextBrowser);
1020 d->openExternalLinks = open;
1021}
1022
1023/*!
1024 \property QTextBrowser::openLinks
1025 \since 4.3
1026
1027 This property specifies whether QTextBrowser should automatically open links the user tries to
1028 activate by mouse or keyboard.
1029
1030 Regardless of the value of this property the anchorClicked signal is always emitted.
1031
1032 The default value is true.
1033*/
1034
1035bool QTextBrowser::openLinks() const
1036{
1037 Q_D(const QTextBrowser);
1038 return d->openLinks;
1039}
1040
1041void QTextBrowser::setOpenLinks(bool open)
1042{
1043 Q_D(QTextBrowser);
1044 d->openLinks = open;
1045}
1046
1047/*! \reimp */
1048bool QTextBrowser::event(QEvent *e)
1049{
1050 return QTextEdit::event(e);
1051}
1052
1053QT_END_NAMESPACE
1054
1055#include "moc_qtextbrowser.cpp"
QRect viewport() const
Returns the viewport rectangle.
std::array< QMetaObject::Connection, 3 > connections
QUrl resolveUrl(const QString &url) const
QTextDocument::ResourceType currentType
void restoreHistoryEntry(const HistoryEntry &entry)
void activateAnchor(const QString &href)
void emitHighlighted(const QUrl &url)
void highlightLink(const QString &href)
HistoryEntry history(int i) const
HistoryEntry createHistoryEntry() const
QStack< HistoryEntry > forwardStack
QString findFile(const QUrl &name) const
QStack< HistoryEntry > stack
QStringList searchPaths
virtual QUrl resolveUrl(const QUrl &url) const override
void setSource(const QUrl &url, QTextDocument::ResourceType type)
Combined button and popup list for selecting options.
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
static bool shouldEnableInputMethod(QTextBrowser *texbrowser)
Q_DECLARE_TYPEINFO(QTextBrowserPrivate::HistoryEntry, Q_RELOCATABLE_TYPE)
QTextDocument::ResourceType type