169 if (!url.isRelative())
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);
184 QFileInfo fi(currentURL.toLocalFile());
186 return QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(url);
202 const QUrl url = resolveUrl(href);
205 emit q->anchorClicked(url);
211#ifndef QT_NO_DESKTOPSERVICES
213 url.scheme() ==
"file"_L1
214#if defined(Q_OS_ANDROID)
215 || url.scheme() ==
"assets"_L1
217 || url.scheme() ==
"qrc"_L1;
218 if ((openExternalLinks && !isFileScheme && !url.isRelative())
219 || (url.isRelative() && !currentURL.isRelative() && !isFileScheme)) {
220 QDesktopServices::openUrl(url);
225 emit q->anchorClicked(url);
257 QGuiApplication::setOverrideCursor(Qt::WaitCursor);
263 bool doSetText =
false;
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;
278 type = QTextDocument::HtmlResource;
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())
293 decoder = QStringDecoder(QStringDecoder::Utf8);
296 txt = QString::fromUtf8(ba);
299 if (Q_UNLIKELY(txt.isEmpty()))
300 qWarning(
"QTextBrowser: No document for %s", url.toString().toLatin1().constData());
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)) {
306 QGuiApplication::restoreOverrideCursor();
308#if QT_CONFIG(whatsthis)
309 QWhatsThis::showText(QCursor::pos(), txt, q);
315 currentURL = resolveUrl(url);
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);
336#ifndef QT_NO_TEXTHTMLPARSER
337 q->QTextEdit::setHtml(txt);
339 q->QTextEdit::setPlainText(txt);
346 if (!url.fragment().isEmpty()) {
347 q->scrollToAnchor(url.fragment());
355 QGuiApplication::restoreOverrideCursor();
357 emit q->sourceChanged(url);
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();
369 const QTextCursor cursor = control->textCursor();
370 if (control->cursorIsFocusIndicator()
371 && cursor.hasSelection()) {
464 control->setTextInteractionFlags(Qt::TextBrowserInteraction);
468 q->setAttribute(Qt::WA_InputMethodEnabled, shouldEnableInputMethod(q));
469 q->setUndoRedoEnabled(
false);
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),
606void QTextBrowser::doSetSource(
const QUrl &url, QTextDocument::ResourceType type)
610 const QTextBrowserPrivate::HistoryEntry historyEntry = d->createHistoryEntry();
612 d->setSource(url, type);
618 if (!d->stack.isEmpty() && d->stack.top().url == url)
621 if (!d->stack.isEmpty())
622 d->stack.top() = historyEntry;
624 QTextBrowserPrivate::HistoryEntry entry;
626 entry.type = d->currentType;
627 entry.title = documentTitle();
630 d->stack.push(entry);
632 emit backwardAvailable(d->stack.size() > 1);
634 if (!d->forwardStack.isEmpty() && d->forwardStack.top().url == url) {
635 d->forwardStack.pop();
636 emit forwardAvailable(d->forwardStack.size() > 0);
638 d->forwardStack.clear();
639 emit forwardAvailable(
false);
642 emit historyChanged();
729void QTextBrowser::forward()
732 if (d->forwardStack.isEmpty())
734 if (!d->stack.isEmpty()) {
736 d->stack.top() = d->createHistoryEntry();
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();
std::array< QMetaObject::Connection, 3 > connections
bool forceLoadOnSourceChange
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
virtual QUrl resolveUrl(const QUrl &url) const override
void setSource(const QUrl &url, QTextDocument::ResourceType type)
static bool shouldEnableInputMethod(QTextBrowser *texbrowser)
Q_DECLARE_TYPEINFO(QTextBrowserPrivate::HistoryEntry, Q_RELOCATABLE_TYPE)
int focusIndicatorPosition
QTextDocument::ResourceType type