📄 qtextbrowser.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.**** This file is part of the QtGui module of the Qt Toolkit.**** This file may be used under the terms of the GNU General Public** License version 2.0 as published by the Free Software Foundation** and appearing in the file LICENSE.GPL included in the packaging of** this file. Please review the following information to ensure GNU** General Public Licensing requirements will be met:** http://www.trolltech.com/products/qt/opensource.html**** If you are unsure which license is appropriate for your use, please** review the following information:** http://www.trolltech.com/products/qt/licensing.html or contact the** sales department at sales@trolltech.com.**** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.******************************************************************************/#include "qtextbrowser.h"#include "qtextedit_p.h"#ifndef QT_NO_TEXTBROWSER#include <qstack.h>#include <qapplication.h>#include <qevent.h>#include <qdesktopwidget.h>#include <qdebug.h>#include <qabstracttextdocumentlayout.h>#include "private/qtextdocumentlayout_p.h"#include <qtextcodec.h>#include <qpainter.h>#include <qdir.h>#include <qwhatsthis.h>#include <qtextobject.h>class QTextBrowserPrivate : public QTextEditPrivate{ Q_DECLARE_PUBLIC(QTextBrowser)public: QTextBrowserPrivate() : textOrSourceChanged(false), forceLoadOnSourceChange(false), hadSelectionOnMousePress(false) {} void init(); struct HistoryEntry { QUrl url; int hpos; int vpos; }; QStack<HistoryEntry> stack; QStack<HistoryEntry> forwardStack; QUrl home; QUrl currentURL; QStringList searchPaths; /*flag necessary to give the linkClicked() signal some meaningful semantics when somebody connected to it calls setText() or setSource() */ bool textOrSourceChanged; bool forceLoadOnSourceChange;#ifndef QT_NO_CURSOR QCursor oldCursor;#endif QString findFile(const QUrl &name) const; inline void _q_documentModified() { textOrSourceChanged = true; forceLoadOnSourceChange = true; } void activateAnchor(const QString &href); void setSource(const QUrl &url); bool findNextPrevAnchor(bool next, int &start, int &end); QString anchorOnMousePress; bool hadSelectionOnMousePress;#ifdef QT_KEYPAD_NAVIGATION void keypadMove(bool next); int lastPos;#endif};static bool isAbsoluteFileName(const QString &name){ return !name.isEmpty() && (name[0] == '/'#if defined(Q_WS_WIN) || (name[0].isLetter() && name[1] == QLatin1Char(':')) || name.startsWith("\\\\")#endif || (name[0] == QLatin1Char(':') && name[1] == QLatin1Char('/')) );}QString QTextBrowserPrivate::findFile(const QUrl &name) const{ QString fileName; if (name.scheme() == QLatin1String("qrc")) fileName = QLatin1String(":/") + name.path(); else fileName = name.toLocalFile(); if (isAbsoluteFileName(fileName)) return fileName; QString slash("/"); foreach (QString path, searchPaths) { if (!path.endsWith(slash)) path.append(slash); path.append(fileName); if (QFileInfo(path).isReadable()) return path; } if (stack.isEmpty()) return fileName; QFileInfo path(QFileInfo(currentURL.toLocalFile()).absolutePath(), fileName); return path.absoluteFilePath();}void QTextBrowserPrivate::activateAnchor(const QString &href){ if (href.isEmpty()) return; Q_Q(QTextBrowser); textOrSourceChanged = false; const QUrl url = isAbsoluteFileName(currentURL.toLocalFile()) ? currentURL.resolved(href) : QUrl(href); emit q->anchorClicked(url); if (!textOrSourceChanged) q->setSource(url);}void QTextBrowserPrivate::setSource(const QUrl &url){ Q_Q(QTextBrowser);#ifndef QT_NO_CURSOR if (q->isVisible()) qApp->setOverrideCursor(Qt::WaitCursor);#endif textOrSourceChanged = true; QString txt; bool doSetText = false; QUrl currentUrlWithoutFragment = currentURL; currentUrlWithoutFragment.setFragment(QString()); QUrl newUrlWithoutFragment = currentURL.resolved(url); newUrlWithoutFragment.setFragment(QString()); if (url.isValid() && (newUrlWithoutFragment != currentUrlWithoutFragment || forceLoadOnSourceChange)) { QVariant data = q->loadResource(QTextDocument::HtmlResource, url); if (data.type() == QVariant::String) { txt = data.toString(); } else if (data.type() == QVariant::ByteArray) {#ifndef QT_NO_TEXTCODEC QByteArray ba = data.toByteArray(); QTextCodec *codec = Qt::codecForHtml(ba); txt = codec->toUnicode(ba);#else txt = data.toString();#endif } if (txt.isEmpty()) qWarning("QTextBrowser: no document for %s", url.toString().toLatin1().constData()); if (q->isVisible()) { QString firstTag = txt.left(txt.indexOf('>') + 1); if (firstTag.left(3) == "<qt" && firstTag.contains("type") && firstTag.contains("detail")) {#ifndef QT_NO_CURSOR qApp->restoreOverrideCursor();#endif#ifndef QT_NO_WHATSTHIS QWhatsThis::showText(QCursor::pos(), txt, q);#endif return; } } currentURL = currentURL.resolved(url); doSetText = true; } if (!home.isValid()) home = url; if (doSetText) q->QTextEdit::setHtml(txt); forceLoadOnSourceChange = false; if (!url.fragment().isEmpty()) { q->scrollToAnchor(url.fragment()); } else { hbar->setValue(0); vbar->setValue(0); }#ifndef QT_NO_CURSOR if (q->isVisible()) qApp->restoreOverrideCursor();#endif emit q->sourceChanged(url);}bool QTextBrowserPrivate::findNextPrevAnchor(bool next, int &start, int &end){ if (!focusIndicator.hasSelection()) { focusIndicator = QTextCursor(doc); if (next) focusIndicator.movePosition(QTextCursor::Start); else focusIndicator.movePosition(QTextCursor::End); } Q_ASSERT(!focusIndicator.isNull()); int anchorStart = -1; int anchorEnd = -1; if (next) { const int startPos = focusIndicator.selectionEnd(); QTextBlock block = doc->findBlock(startPos); QTextBlock::Iterator it = block.begin(); while (!it.atEnd() && it.fragment().position() < startPos) ++it; while (block.isValid()) { anchorStart = -1; // find next anchor for (; !it.atEnd(); ++it) { const QTextFragment fragment = it.fragment(); const QTextCharFormat fmt = fragment.charFormat(); if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) { anchorStart = fragment.position(); break; } } if (anchorStart != -1) { anchorEnd = -1; // find next non-anchor fragment for (; !it.atEnd(); ++it) { const QTextFragment fragment = it.fragment(); const QTextCharFormat fmt = fragment.charFormat(); if (!fmt.isAnchor()) { anchorEnd = fragment.position(); break; } } if (anchorEnd == -1) anchorEnd = block.position() + block.length() - 1; // make found selection break; } block = block.next(); it = block.begin(); } } else { int startPos = focusIndicator.selectionStart(); if (startPos > 0) --startPos; QTextBlock block = doc->findBlock(startPos); QTextBlock::Iterator blockStart = block.begin(); QTextBlock::Iterator it = block.end(); if (startPos == block.position()) { it = block.begin(); } else { do { if (it == blockStart) { it = QTextBlock::Iterator(); block = QTextBlock(); } else { --it; } } while (!it.atEnd() && it.fragment().position() + it.fragment().length() - 1 > startPos); } while (block.isValid()) { anchorStart = -1; if (!it.atEnd()) { do { const QTextFragment fragment = it.fragment(); const QTextCharFormat fmt = fragment.charFormat(); if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) { anchorStart = fragment.position() + fragment.length(); break; } if (it == blockStart) it = QTextBlock::Iterator(); else --it; } while (!it.atEnd()); } if (anchorStart != -1 && !it.atEnd()) { anchorEnd = -1; do { const QTextFragment fragment = it.fragment(); const QTextCharFormat fmt = fragment.charFormat(); if (!fmt.isAnchor()) { anchorEnd = fragment.position() + fragment.length(); break; } if (it == blockStart) it = QTextBlock::Iterator(); else --it; } while (!it.atEnd()); if (anchorEnd == -1) anchorEnd = qMax(0, block.position()); break; } block = block.previous(); it = block.end(); if (it != block.begin()) --it; blockStart = block.begin(); } } if (anchorStart != -1 && anchorEnd != -1) { start = anchorStart; end = anchorEnd; return true; } return false;}#ifdef QT_KEYPAD_NAVIGATIONvoid QTextBrowserPrivate::keypadMove(bool next){ Q_Q(QTextBrowser); const int height = viewport->height(); int anchorStart, anchorEnd; if (findNextPrevAnchor(next, anchorStart, anchorEnd)) { QTextBlock block = doc->findBlock(next ? anchorEnd : anchorStart); const int yOffset = vbar->value(); const int cursYOffset = (int)block.layout()->position().y(); const int overlap = 20; if (next) { if (cursYOffset > yOffset + height) { vbar->setValue(yOffset + height - overlap); if (cursYOffset > vbar->value() + height) { emit q->highlighted(QUrl()); emit q->highlighted(QString()); return; } } else if (cursYOffset < yOffset) { if (yOffset < vbar->maximum()) vbar->setValue(yOffset + height - overlap); else vbar->setValue(0); emit q->highlighted(QUrl()); emit q->highlighted(QString()); return; } } else { qDebug("found anchor"); if (cursYOffset < yOffset) { qDebug("1"); vbar->setValue(yOffset - height + overlap); if (cursYOffset < vbar->value()) { emit q->highlighted(QUrl()); emit q->highlighted(QString()); return; } } else if (cursYOffset > yOffset + height) { qDebug("2"); if (yOffset > 0) vbar->setValue(yOffset - height + overlap); else vbar->setValue(vbar->maximum()); emit q->highlighted(QUrl()); emit q->highlighted(QString()); return; } } focusIndicator.setPosition(anchorStart); if(next) focusIndicator.setPosition(anchorEnd, QTextCursor::KeepAnchor); QTextCharFormat charFmt; charFmt = focusIndicator.charFormat(); emit q->highlighted(QUrl(charFmt.anchorHref())); emit q->highlighted(charFmt.anchorHref()); if(!next) focusIndicator.setPosition(anchorEnd, QTextCursor::KeepAnchor); } else { const int yOffset = vbar->value(); const int overlap = 20; if (next) { if (yOffset == vbar->maximum()) vbar->setValue(0); else vbar->setValue(yOffset + height - overlap); } else { if (yOffset == 0) vbar->setValue(vbar->maximum()); else vbar->setValue(yOffset - height + overlap); } focusIndicator.clearSelection(); emit q->highlighted(QUrl()); emit q->highlighted(QString()); } if (focusIndicator.hasSelection()) { qSwap(focusIndicator, cursor); q->ensureCursorVisible(); qSwap(focusIndicator, cursor); viewport->update(); } else { viewport->update(); }}#endif/*! \class QTextBrowser qtextbrowser.h \brief The QTextBrowser class provides a rich text browser with hypertext navigation. \ingroup text This class extends QTextEdit (in read-only mode), adding some navigation functionality so that users can follow links in hypertext documents. The contents of QTextEdit are set with setHtml() or setPlainText(), but QTextBrowser also implements the setSource() function, making it possible to set the text to a named document. The name is looked up in a list of search paths and in the directory of the current document factory. If a document name ends with an anchor (for example, "\c #anchor"), the text browser automatically scrolls to that position (using scrollToAnchor()). When the user clicks on a hyperlink, the browser will call setSource() itself with the link's \c href value as argument. You can track the current source by connecting to the sourceChanged() signal. QTextBrowser provides backward() and forward() slots which you can use to implement Back and Forward buttons. The home() slot sets the text to the very first document displayed. The anchorClicked() signal is emitted when the user clicks an anchor. To override the default navigation behavior of the browser, call the setSource() function to supply new document text in a slot connected to this signal. If you want to provide your users with editable rich text use QTextEdit. If you want a text browser without hypertext navigation use QTextEdit, and use QTextEdit::setReadOnly() to disable editing. If you just need to display a small piece of rich text use QLabel. If you want to load documents stored in the Qt resource system use qrc as the scheme in the URL to load. For example, for the document resource path \c{:/docs/index.html} use \c{qrc:/docs/index.html} as the URL with setSource(). \sa QTextEdit, QTextDocument*//*! \property QTextBrowser::modified \brief whether the contents of the text browser have been modified*//*! \property QTextBrowser::readOnly \brief whether the text browser is read-only*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -