📄 qtextbrowser.cpp
字号:
/******************************************************************************** Copyright (C) 1992-2007 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://trolltech.com/products/qt/licenses/licensing/opensource/**** If you are unsure which license is appropriate for your use, please** review the following information:** http://trolltech.com/products/qt/licenses/licensing/licensingoverview** or contact the sales department at sales@trolltech.com.**** In addition, as a special exception, Trolltech gives you certain** additional rights. These rights are described in the Trolltech GPL** Exception version 1.0, which can be found at** http://www.trolltech.com/products/qt/gplexception/ and in the file** GPL_EXCEPTION.txt in this package.**** In addition, as a special exception, Trolltech, as the sole copyright** holder for Qt Designer, grants users of the Qt/Eclipse Integration** plug-in the right for the Qt/Eclipse Integration to link to** functionality provided by Qt Designer and its related libraries.**** Trolltech reserves all rights not expressly granted herein.**** 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>#include <qdesktopservices.h>class QTextBrowserPrivate : public QTextEditPrivate{ Q_DECLARE_PUBLIC(QTextBrowser)public: inline QTextBrowserPrivate() : textOrSourceChanged(false), forceLoadOnSourceChange(false), openExternalLinks(false), openLinks(true)#ifdef QT_KEYPAD_NAVIGATION , lastKeypadScrollValue(-1)#endif {} void init(); struct HistoryEntry { inline HistoryEntry() : hpos(0), vpos(0), focusIndicatorPosition(-1), focusIndicatorAnchor(-1) {} QUrl url; int hpos; int vpos; int focusIndicatorPosition, focusIndicatorAnchor; }; HistoryEntry createHistoryEntry() const; void restoreHistoryEntry(const HistoryEntry entry); 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; bool openExternalLinks; bool openLinks;#ifndef QT_NO_CURSOR QCursor oldCursor;#endif QString findFile(const QUrl &name) const; inline void _q_documentModified() { textOrSourceChanged = true; forceLoadOnSourceChange = !currentURL.path().isEmpty(); } void _q_activateAnchor(const QString &href); void _q_highlightLink(const QString &href); void setSource(const QUrl &url); QUrl resolveUrl(const QUrl &url) const;#ifdef QT_KEYPAD_NAVIGATION void keypadMove(bool next); QTextCursor prevFocus; int lastKeypadScrollValue;#endif};static bool isAbsoluteFileName(const QString &name){ return !name.isEmpty() && (name[0] == QLatin1Char('/')#if defined(Q_WS_WIN) || (name[0].isLetter() && name[1] == QLatin1Char(':')) || name.startsWith(QLatin1String("\\\\"))#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; foreach (QString path, searchPaths) { if (!path.endsWith(QLatin1Char('/'))) path.append(QLatin1Char('/')); path.append(fileName); if (QFileInfo(path).isReadable()) return path; } return fileName;}QUrl QTextBrowserPrivate::resolveUrl(const QUrl &url) const{ if (!url.isRelative()) return url; // For the second case QUrl can merge "#someanchor" with "foo.html" // correctly to "foo.html#someanchor" if (!(currentURL.isRelative() || (currentURL.scheme() == QLatin1String("file") && !isAbsoluteFileName(currentURL.toLocalFile()))) || (url.hasFragment() && url.path().isEmpty())) { return currentURL.resolved(url); } // this is our last resort when current url and new url are both relative // we try to resolve against the current working directory in the local // file system. QFileInfo fi(currentURL.toLocalFile()); if (fi.exists()) { return QUrl::fromLocalFile(fi.absolutePath() + QDir::separator()).resolved(url); } return url;}void QTextBrowserPrivate::_q_activateAnchor(const QString &href){ if (href.isEmpty()) return; Q_Q(QTextBrowser);#ifndef QT_NO_CURSOR viewport->setCursor(oldCursor);#endif const QUrl url = resolveUrl(href); if (!openLinks) { emit q->anchorClicked(url); return; } textOrSourceChanged = false;#ifndef QT_NO_DESKTOPSERVICES if (openExternalLinks && url.scheme() != QLatin1String("file") && url.scheme() != QLatin1String("qrc")) { QDesktopServices::openUrl(url); return; }#endif emit q->anchorClicked(url); if (textOrSourceChanged) return; q->setSource(url);}void QTextBrowserPrivate::_q_highlightLink(const QString &anchor){ Q_Q(QTextBrowser); if (anchor.isEmpty()) {#ifndef QT_NO_CURSOR if (viewport->cursor().shape() != Qt::PointingHandCursor) oldCursor = viewport->cursor(); viewport->setCursor(oldCursor);#endif emit q->highlighted(QUrl()); emit q->highlighted(QString()); } else {#ifndef QT_NO_CURSOR viewport->setCursor(Qt::PointingHandCursor);#endif const QUrl url = resolveUrl(anchor); emit q->highlighted(url); // convenience to ease connecting to QStatusBar::showMessage(const QString &) emit q->highlighted(url.toString()); }}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, resolveUrl(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(QLatin1Char('>')) + 1); if (firstTag.left(3) == QLatin1String("<qt") && firstTag.contains(QLatin1String("type")) && firstTag.contains(QLatin1String("detail"))) {#ifndef QT_NO_CURSOR qApp->restoreOverrideCursor();#endif#ifndef QT_NO_WHATSTHIS QWhatsThis::showText(QCursor::pos(), txt, q);#endif return; } } currentURL = resolveUrl(url); doSetText = true; } if (!home.isValid()) home = url; if (doSetText) { q->QTextEdit::setHtml(txt);#ifdef QT_KEYPAD_NAVIGATION prevFocus.movePosition(QTextCursor::Start);#endif } forceLoadOnSourceChange = false; if (!url.fragment().isEmpty()) { q->scrollToAnchor(url.fragment()); } else { hbar->setValue(0); vbar->setValue(0); }#ifdef QT_KEYPAD_NAVIGATION lastKeypadScrollValue = vbar->value(); #endif #ifndef QT_NO_CURSOR if (q->isVisible()) qApp->restoreOverrideCursor();#endif emit q->sourceChanged(url);}#ifdef QT_KEYPAD_NAVIGATIONvoid QTextBrowserPrivate::keypadMove(bool next){ Q_Q(QTextBrowser); const int height = viewport->height(); const int overlap = qBound(20, height / 5, 40); // XXX arbitrary, but a good balance const int visibleLinkAmount = overlap; // consistent, but maybe not the best choice (?) int yOffset = vbar->value(); int scrollYOffset = qBound(0, next ? yOffset + height - overlap : yOffset - height + overlap, vbar->maximum()); bool foundNextAnchor = false; bool focusIt = false; int focusedPos = -1; QTextCursor anchorToFocus; QRectF viewRect = QRectF(0, yOffset, control->size().width(), height); QRectF newViewRect = QRectF(0, scrollYOffset, control->size().width(), height); QRectF bothViewRects = viewRect.united(newViewRect); // If we don't have a previous anchor, pretend that we had the first/last character // on the screen selected. if (prevFocus.isNull()) { if (next) prevFocus = control->cursorForPosition(QPointF(0, yOffset)); else prevFocus = control->cursorForPosition(QPointF(control->size().width(), yOffset + height)); } // First, check to see if someone has moved the scroll bars independently if (lastKeypadScrollValue != yOffset) { // Someone (user or programmatically) has moved us, so we might // need to start looking from the current position instead of prevFocus bool findOnScreen = true; // If prevFocus is on screen at all, we just use it. if (prevFocus.hasSelection()) { QRectF prevRect = control->selectionRect(prevFocus); if (viewRect.intersects(prevRect)) findOnScreen = false; } // Otherwise, we find a new anchor that's on screen. // Basically, create a cursor with the last/first character // on screen if (findOnScreen) { if (next) prevFocus = control->cursorForPosition(QPointF(0, yOffset)); else prevFocus = control->cursorForPosition(QPointF(control->size().width(), yOffset + height)); } foundNextAnchor = control->findNextPrevAnchor(prevFocus, next, anchorToFocus); } else if (prevFocus.hasSelection()) { // Check the pathological case that the current anchor is higher // than the screen, and just scroll through it in that case QRectF prevRect = control->selectionRect(prevFocus); if ((next && prevRect.bottom() > (yOffset + height)) || (!next && prevRect.top() < yOffset)) { anchorToFocus = prevFocus;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -