Help: Provide native WebView backend on Mac

Run Qt Creator with environment variable QTC_HELPVIEWER_BACKEND to
* 'native' to get the WebView based one on Mac
* 'textbrowser' to get the QTextBrowser based one

Defaults to use QWebView if QtWebKit is available, or QTextBrowser if
not, like before.

Change-Id: If0660782b18ff3d89301fa7bcaf4e2e2fb69627d
Reviewed-by: Eike Ziller <eike.ziller@digia.com>
This commit is contained in:
Eike Ziller
2014-05-14 13:15:01 +02:00
parent 62a83b2b3f
commit c5be32fae7
15 changed files with 1032 additions and 49 deletions

View File

@@ -82,7 +82,9 @@ QWidget *GeneralSettingsPage::widget()
m_ui->sizeComboBox->setEditable(false);
m_ui->styleComboBox->setEditable(false);
m_font = qvariant_cast<QFont>(HelpManager::customValue(QLatin1String("font"), m_font));
QVariant fontSetting = LocalHelpManager::engineFontSettings();
if (fontSetting.isValid())
m_font = fontSetting.value<QFont>();
updateFontSize();
updateFontStyle();
@@ -155,7 +157,7 @@ void GeneralSettingsPage::apply()
if (newFont != m_font) {
m_font = newFont;
HelpManager::setCustomValue(QLatin1String("font"), newFont);
HelpManager::setCustomValue(Constants::FontKey, newFont);
emit fontChanged();
}

View File

@@ -68,5 +68,14 @@ FORMS += docsettingspage.ui \
generalsettingspage.ui \
remotehelpfilter.ui
macx:minQtVersion(5, 2, 0) {
DEFINES += QTC_MAC_NATIVE_HELPVIEWER
QT += macextras
HEADERS += macwebkithelpviewer.h
OBJECTIVE_SOURCES += macwebkithelpviewer.mm
LIBS += -framework WebKit -framework AppKit
}
RESOURCES += help.qrc
include(../../shared/help/help.pri)

View File

@@ -54,6 +54,7 @@ static const QLatin1String DefaultZoomFactor("0.0");
static const QLatin1String AboutBlank("about:blank");
static const QLatin1String WeAddedFilterKey("UnfilteredFilterInserted");
static const QLatin1String PreviousFilterNameKey("UnfilteredFilterName");
static const QLatin1String FontKey("font");
const int P_MODE_HELP = 70;
const char ID_MODE_HELP [] = "Help";

View File

@@ -48,6 +48,10 @@
#include "searchtaskhandler.h"
#include "textbrowserhelpviewer.h"
#ifdef QTC_MAC_NATIVE_HELPVIEWER
#include "macwebkithelpviewer.h"
#endif
#include <bookmarkmanager.h>
#include <contentwindow.h>
#include <indexwindow.h>
@@ -638,14 +642,27 @@ void HelpPlugin::resetRightPaneScale()
HelpViewer *HelpPlugin::createHelpViewer(qreal zoom)
{
#ifndef QT_NO_WEBKIT
if (qgetenv("QTC_FORCE_TEXTBROWSER").isEmpty())
return new QtWebKitHelpViewer(zoom);
else
return new TextBrowserHelpViewer(zoom);
#else
return new TextBrowserHelpViewer(zoom);
HelpViewer *viewer = 0;
const QString backend = QLatin1String(qgetenv("QTC_HELPVIEWER_BACKEND"));
if (backend.compare(QLatin1String("native"), Qt::CaseInsensitive) == 0) {
#ifdef QTC_MAC_NATIVE_HELPVIEWER
viewer = new MacWebKitHelpViewer(zoom);
#endif
} else if (backend.compare(QLatin1String("textbrowser"), Qt::CaseInsensitive) == 0) {
viewer = new TextBrowserHelpViewer(zoom);
} else {
#ifndef QT_NO_WEBKIT
viewer = new QtWebKitHelpViewer(zoom);
#else
viewer = new TextBrowserHelpViewer(zoom);
#endif
}
// initialize font
QVariant fontSetting = LocalHelpManager::engineFontSettings();
if (fontSetting.isValid())
viewer->setViewerFont(fontSetting.value<QFont>());
return viewer;
}
void HelpPlugin::activateHelpMode()
@@ -729,11 +746,11 @@ void HelpPlugin::fontChanged()
if (!m_helpViewerForSideBar)
createRightPaneContextViewer();
const QHelpEngine &engine = LocalHelpManager::helpEngine();
QFont font = qvariant_cast<QFont>(engine.customValue(QLatin1String("font"),
m_helpViewerForSideBar->viewerFont()));
QVariant fontSetting = LocalHelpManager::engineFontSettings();
QFont font = fontSetting.isValid() ? fontSetting.value<QFont>()
: m_helpViewerForSideBar->viewerFont();
m_helpViewerForSideBar->setFont(font);
m_helpViewerForSideBar->setViewerFont(font);
const int count = OpenPagesManager::instance().pageCount();
for (int i = 0; i < count; ++i) {
if (HelpViewer *viewer = CentralWidget::instance()->viewerAt(i))

View File

@@ -60,7 +60,6 @@ public:
virtual qreal scale() const = 0;
virtual QString title() const = 0;
virtual void setTitle(const QString &title) = 0;
virtual QUrl source() const = 0;
virtual void setSource(const QUrl &url) = 0;

View File

@@ -29,7 +29,8 @@
#include "localhelpmanager.h"
#include <bookmarkmanager.h>
#include "bookmarkmanager.h"
#include "helpconstants.h"
#include <app/app_version.h>
#include <coreplugin/helpmanager.h>
@@ -40,6 +41,8 @@
using namespace Help::Internal;
static LocalHelpManager *m_instance = 0;
QMutex LocalHelpManager::m_guiMutex;
QHelpEngine* LocalHelpManager::m_guiEngine = 0;
@@ -51,6 +54,7 @@ LocalHelpManager::LocalHelpManager(QObject *parent)
, m_guiNeedsSetup(true)
, m_needsCollectionFile(true)
{
m_instance = this;
}
LocalHelpManager::~LocalHelpManager()
@@ -65,6 +69,11 @@ LocalHelpManager::~LocalHelpManager()
m_guiEngine = 0;
}
LocalHelpManager *LocalHelpManager::instance()
{
return m_instance;
}
void LocalHelpManager::setupGuiHelpEngine()
{
if (m_needsCollectionFile) {
@@ -110,3 +119,16 @@ BookmarkManager& LocalHelpManager::bookmarkManager()
}
return *m_bookmarkManager;
}
QVariant LocalHelpManager::engineFontSettings()
{
return helpEngine().customValue(Constants::FontKey, QVariant());
}
QByteArray LocalHelpManager::helpData(const QUrl &url)
{
const QHelpEngineCore &engine = helpEngine();
return engine.findFile(url).isValid() ? engine.fileData(url)
: tr("Could not load \"%1\".").arg(url.toString()).toUtf8();
}

View File

@@ -48,12 +48,18 @@ public:
LocalHelpManager(QObject *parent = 0);
~LocalHelpManager();
static LocalHelpManager *instance();
void setupGuiHelpEngine();
void setEngineNeedsUpdate();
static QHelpEngine& helpEngine();
static BookmarkManager& bookmarkManager();
static QVariant engineFontSettings();
Q_INVOKABLE QByteArray helpData(const QUrl &url);
private:
bool m_guiNeedsSetup;
bool m_needsCollectionFile;

View File

@@ -0,0 +1,140 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#ifndef MACWEBKITHELPVIEWER_H
#define MACWEBKITHELPVIEWER_H
#include "helpviewer.h"
#include <QMacCocoaViewContainer>
Q_FORWARD_DECLARE_OBJC_CLASS(DOMNode);
Q_FORWARD_DECLARE_OBJC_CLASS(DOMRange);
Q_FORWARD_DECLARE_OBJC_CLASS(NSString);
Q_FORWARD_DECLARE_OBJC_CLASS(WebView);
namespace Help {
namespace Internal {
class MacWebKitHelpViewer;
class MacWebKitHelpWidgetPrivate;
class MacResponderHack : public QObject
{
Q_OBJECT
public:
MacResponderHack(QObject *parent);
private slots:
void responderHack(QWidget *old, QWidget *now);
};
class MacWebKitHelpWidget : public QMacCocoaViewContainer
{
Q_OBJECT
public:
MacWebKitHelpWidget(MacWebKitHelpViewer *parent);
~MacWebKitHelpWidget();
void setOpenInNewWindowActionVisible(bool visible);
WebView *webView() const;
protected:
void hideEvent(QHideEvent *);
void showEvent(QShowEvent *);
private:
MacWebKitHelpWidgetPrivate *d;
};
class MacWebKitHelpViewer : public HelpViewer
{
Q_OBJECT
public:
explicit MacWebKitHelpViewer(qreal zoom, QWidget *parent = 0);
~MacWebKitHelpViewer();
QFont viewerFont() const;
void setViewerFont(const QFont &font);
void scaleUp();
void scaleDown();
void resetScale();
qreal scale() const;
QString title() const;
QUrl source() const;
void setSource(const QUrl &url);
void scrollToAnchor(const QString &anchor);
void highlightId(const QString &id) { Q_UNUSED(id) }
void setHtml(const QString &html);
QString selectedText() const;
bool isForwardAvailable() const;
bool isBackwardAvailable() const;
void addBackHistoryItems(QMenu *backMenu);
void addForwardHistoryItems(QMenu *forwardMenu);
void setOpenInNewWindowActionVisible(bool visible);
bool findText(const QString &text, Core::FindFlags flags,
bool incremental, bool fromSearch, bool *wrapped = 0);
MacWebKitHelpWidget *widget() const { return m_widget; }
public slots:
void copy();
void stop();
void forward();
void backward();
void print(QPrinter *printer);
public slots:
void slotLoadStarted();
void slotLoadFinished();
private slots:
void goToHistoryItem();
private:
DOMRange *findText(NSString *text, bool forward, bool caseSensitive, DOMNode *startNode,
int startOffset);
MacWebKitHelpWidget *m_widget;
};
} // namespace Internal
} // namespace Help
#endif // MACWEBKITHELPVIEWER_H

View File

@@ -0,0 +1,812 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "macwebkithelpviewer.h"
#include "localhelpmanager.h"
#include "openpagesmanager.h"
#include <coreplugin/icore.h>
#include <utils/qtcassert.h>
#include <QApplication>
#include <QClipboard>
#include <QHelpEngine>
#include <QtMac>
#include <QUrl>
#include <QVBoxLayout>
#include <QDebug>
#import <AppKit/NSMenuItem.h>
#import <Foundation/NSURLProtocol.h>
#import <Foundation/NSURLResponse.h>
#import <WebKit/DOMDocument.h>
#import <WebKit/DOMElement.h>
#import <WebKit/DOMHTMLElement.h>
#import <WebKit/DOMNodeFilter.h>
#import <WebKit/DOMNodeIterator.h>
#import <WebKit/DOMRange.h>
#import <WebKit/WebBackForwardList.h>
#import <WebKit/WebDataSource.h>
#import <WebKit/WebDocument.h>
#import <WebKit/WebFrame.h>
#import <WebKit/WebFrameLoadDelegate.h>
#import <WebKit/WebFrameView.h>
#import <WebKit/WebHistoryItem.h>
#import <WebKit/WebPreferences.h>
#import <WebKit/WebUIDelegate.h>
#import <WebKit/WebView.h>
// #pragma mark -- AutoreleasePool
class AutoreleasePool
{
public:
AutoreleasePool();
~AutoreleasePool();
private:
NSAutoreleasePool *pool;
};
AutoreleasePool::AutoreleasePool()
{
pool = [[NSAutoreleasePool alloc] init];
}
AutoreleasePool::~AutoreleasePool()
{
[pool release];
}
// #pragma mark -- DOMNodeIterator (PrivateExtensions)
@interface DOMNodeIterator (PrivateExtensions)
- (BOOL)findNode:(DOMNode *)node;
- (DOMNode *)nextTextNode;
- (DOMNode *)previousTextNode;
- (DOMNode *)gotoEnd;
@end
@implementation DOMNodeIterator (PrivateExtensions)
- (BOOL)findNode:(DOMNode *)node
{
while (DOMNode *next = [self nextNode]) {
if (next == node)
return YES;
}
return NO;
}
- (DOMNode *)nextTextNode
{
DOMNode *node = nil;
do {
node = [self nextNode];
} while (node && node.nodeType != DOM_TEXT_NODE);
return node;
}
- (DOMNode *)previousTextNode
{
DOMNode *node = nil;
do {
node = [self previousNode];
} while (node && node.nodeType != DOM_TEXT_NODE);
return node;
}
- (DOMNode *)gotoEnd
{
DOMNode *previous = nil;
DOMNode *node = nil;
do {
previous = node;
node = [self nextNode];
} while (node);
return previous;
}
@end
// #pragma mark -- QtHelpURLProtocol
@interface QtHelpURLProtocol : NSURLProtocol
{
}
+ (BOOL)canInitWithRequest:(NSURLRequest *)request;
@end
@implementation QtHelpURLProtocol
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
return [[[request URL] scheme] isEqualToString:@"qthelp"];
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
return request;
}
- (void)startLoading
{
const QUrl &url = QUrl::fromNSURL(self.request.URL);
QByteArray data;
Help::Internal::LocalHelpManager *helpManager = Help::Internal::LocalHelpManager::instance();
QMetaObject::invokeMethod(helpManager, "helpData", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(QByteArray, data), Q_ARG(QUrl, url));
QString mtString = Help::Internal::HelpViewer::mimeFromUrl(url);
if (mtString.isEmpty())
mtString = QLatin1String("application/octet-stream");
NSString *mimeType = mtString.toNSString();
NSData *nsdata = QtMac::toNSData(data); // Qt 5.3 has this in QByteArray
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:self.request.URL
MIMEType:mimeType
expectedContentLength:data.length()
textEncodingName:@"UTF8"];
[self.client URLProtocol:self didReceiveResponse:response
cacheStoragePolicy:NSURLCacheStorageNotAllowed];
[self.client URLProtocol:self didLoadData:nsdata];
[self.client URLProtocolDidFinishLoading:self];
[response release];
}
- (void)stopLoading
{
}
@end
static void ensureProtocolHandler()
{
static bool registered = false;
if (!registered) {
[NSURLProtocol registerClass:[QtHelpURLProtocol class]];
registered = true;
}
}
// #pragma mark -- FrameLoadDelegate
@interface FrameLoadDelegate : NSObject
{
WebFrame *mainFrame;
Help::Internal::MacWebKitHelpViewer *viewer;
}
- (id)initWithMainFrame:(WebFrame *)frame viewer:(Help::Internal::MacWebKitHelpViewer *)viewer;
- (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame;
- (void)webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame;
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame;
@end
@implementation FrameLoadDelegate
- (id)initWithMainFrame:(WebFrame *)frame viewer:(Help::Internal::MacWebKitHelpViewer *)helpViewer
{
self = [super init];
if (self) {
mainFrame = frame;
viewer = helpViewer;
}
return self;
}
- (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame
{
Q_UNUSED(sender)
if (frame == mainFrame)
viewer->slotLoadStarted();
}
- (void)webView:(WebView *)sender didReceiveTitle:(NSString *)title forFrame:(WebFrame *)frame
{
Q_UNUSED(sender)
Q_UNUSED(title)
if (frame == mainFrame)
viewer->titleChanged();
}
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
{
Q_UNUSED(sender)
if (frame == mainFrame)
viewer->slotLoadFinished();
}
@end
// #pragma mark -- UIDelegate
@interface UIDelegate : NSObject
{
QWidget *widget;
}
@property (assign) BOOL openInNewWindowActionVisible;
- (id)initWithWidget:(QWidget *)theWidget;
- (void)webView:(WebView *)sender makeFirstResponder:(NSResponder *)responder;
- (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element
defaultMenuItems:(NSArray *)defaultMenuItems;
- (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request;
@end
@implementation UIDelegate
- (id)initWithWidget:(QWidget *)theWidget
{
self = [super init];
if (self) {
widget = theWidget;
self.openInNewWindowActionVisible = YES;
}
return self;
}
- (void)webView:(WebView *)sender makeFirstResponder:(NSResponder *)responder
{
// make the widget get focus
if (responder) {
widget->setFocus();
[sender.window makeFirstResponder:responder];
}
}
- (NSArray *)webView:(WebView *)sender contextMenuItemsForElement:(NSDictionary *)element
defaultMenuItems:(NSArray *)defaultMenuItems
{
Q_UNUSED(sender)
Q_UNUSED(element)
NSMutableArray *ret = [[NSMutableArray alloc] init];
for (NSMenuItem *item in defaultMenuItems) {
switch (item.tag) {
case WebMenuItemTagCopyLinkToClipboard:
case WebMenuItemTagCopyImageToClipboard:
case WebMenuItemTagCopy:
case WebMenuItemTagGoBack:
case WebMenuItemTagGoForward:
case WebMenuItemTagStop:
case WebMenuItemTagReload:
case WebMenuItemTagOther:
case WebMenuItemTagSearchInSpotlight:
case WebMenuItemTagSearchWeb:
case WebMenuItemTagLookUpInDictionary:
case WebMenuItemTagOpenWithDefaultApplication:
[ret addObject:item];
break;
case WebMenuItemTagOpenLinkInNewWindow:
case WebMenuItemTagOpenImageInNewWindow:
if (self.openInNewWindowActionVisible)
[ret addObject:item];
default:
break;
}
}
return [ret autorelease];
}
- (WebView *)webView:(WebView *)sender createWebViewWithRequest:(NSURLRequest *)request
{
Q_UNUSED(sender)
Q_UNUSED(request)
Help::Internal::MacWebKitHelpViewer* viewer
= static_cast<Help::Internal::MacWebKitHelpViewer *>(
Help::Internal::OpenPagesManager::instance().createPage(QUrl()));
return viewer->widget()->webView();
}
@end
// #pragma mark -- MyWebView
@interface MyWebView : WebView
@end
// work around Qt + WebView issue QTBUG-26593, that Qt does not pass mouseMoved: events up the event chain,
// but the Web(HTML)View is only handling mouse moved for hovering etc if the event was passed up
// to the NSWindow (arguably a bug in Web(HTML)View)
@implementation MyWebView
- (void)updateTrackingAreas
{
[super updateTrackingAreas];
if (NSArray *trackingArray = [self trackingAreas]) {
NSUInteger size = [trackingArray count];
for (NSUInteger i = 0; i < size; ++i) {
NSTrackingArea *t = [trackingArray objectAtIndex:i];
[self removeTrackingArea:t];
}
}
NSUInteger trackingOptions = NSTrackingActiveInActiveApp | NSTrackingInVisibleRect
| NSTrackingMouseMoved;
NSTrackingArea *ta = [[[NSTrackingArea alloc] initWithRect:[self frame]
options:trackingOptions
owner:self
userInfo:nil]
autorelease];
[self addTrackingArea:ta];
}
- (void)mouseMoved:(NSEvent *)theEvent
{
[self.window mouseMoved:theEvent];
}
@end
namespace Help {
namespace Internal {
// #pragma mark -- MacWebKitHelpWidget
class MacWebKitHelpWidgetPrivate
{
public:
MacWebKitHelpWidgetPrivate()
: m_savedResponder(nil)
{
}
~MacWebKitHelpWidgetPrivate()
{
[m_webView release];
[m_frameLoadDelegate release];
[m_uiDelegate release];
}
WebView *m_webView;
FrameLoadDelegate *m_frameLoadDelegate;
UIDelegate *m_uiDelegate;
NSResponder *m_savedResponder;
};
// #pragma mark -- MacWebKitHelpWidget
MacWebKitHelpWidget::MacWebKitHelpWidget(MacWebKitHelpViewer *parent)
: QMacCocoaViewContainer(0, parent),
d(new MacWebKitHelpWidgetPrivate)
{
AutoreleasePool pool; Q_UNUSED(pool)
d->m_webView = [[MyWebView alloc] init];
d->m_frameLoadDelegate = [[FrameLoadDelegate alloc] initWithMainFrame:d->m_webView.mainFrame
viewer:parent];
[d->m_webView setFrameLoadDelegate:d->m_frameLoadDelegate];
d->m_uiDelegate = [[UIDelegate alloc] initWithWidget:this];
[d->m_webView setUIDelegate:d->m_uiDelegate];
setCocoaView(d->m_webView);
}
MacWebKitHelpWidget::~MacWebKitHelpWidget()
{
delete d;
}
void MacWebKitHelpWidget::setOpenInNewWindowActionVisible(bool visible)
{
d->m_uiDelegate.openInNewWindowActionVisible = visible;
}
WebView *MacWebKitHelpWidget::webView() const
{
return d->m_webView;
}
void MacWebKitHelpWidget::hideEvent(QHideEvent *)
{
[d->m_webView setHidden:YES];
}
void MacWebKitHelpWidget::showEvent(QShowEvent *)
{
[d->m_webView setHidden:NO];
}
// #pragma mark -- MacWebKitHelpViewer
MacWebKitHelpViewer::MacWebKitHelpViewer(qreal zoom, QWidget *parent)
: HelpViewer(parent),
m_widget(new MacWebKitHelpWidget(this))
{
static bool responderHackInstalled = false;
if (!responderHackInstalled) {
responderHackInstalled = true;
new MacResponderHack(qApp);
}
AutoreleasePool pool; Q_UNUSED(pool)
QVBoxLayout *layout = new QVBoxLayout;
setLayout(layout);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(m_widget, 10);
m_widget->webView().textSizeMultiplier = (zoom == 0.0 ? 1.0 : zoom);
}
MacWebKitHelpViewer::~MacWebKitHelpViewer()
{
}
QFont MacWebKitHelpViewer::viewerFont() const
{
AutoreleasePool pool; Q_UNUSED(pool)
WebPreferences *preferences = m_widget->webView().preferences;
QString family = QString::fromNSString([preferences standardFontFamily]);
int size = [preferences defaultFontSize];
return QFont(family, size);
}
void MacWebKitHelpViewer::setViewerFont(const QFont &font)
{
AutoreleasePool pool; Q_UNUSED(pool)
WebPreferences *preferences = m_widget->webView().preferences;
[preferences setStandardFontFamily:font.family().toNSString()];
[preferences setDefaultFontSize:font.pointSize()];
}
void MacWebKitHelpViewer::scaleUp()
{
AutoreleasePool pool; Q_UNUSED(pool)
m_widget->webView().textSizeMultiplier += 0.1;
}
void MacWebKitHelpViewer::scaleDown()
{
AutoreleasePool pool; Q_UNUSED(pool)
m_widget->webView().textSizeMultiplier = qMax(0.1, m_widget->webView().textSizeMultiplier - 0.1);
}
void MacWebKitHelpViewer::resetScale()
{
AutoreleasePool pool; Q_UNUSED(pool)
m_widget->webView().textSizeMultiplier = 1.0;
}
qreal MacWebKitHelpViewer::scale() const
{
AutoreleasePool pool; Q_UNUSED(pool)
return m_widget->webView().textSizeMultiplier;
}
QString MacWebKitHelpViewer::title() const
{
AutoreleasePool pool; Q_UNUSED(pool)
return QString::fromNSString(m_widget->webView().mainFrameTitle);
}
QUrl MacWebKitHelpViewer::source() const
{
AutoreleasePool pool; Q_UNUSED(pool)
WebDataSource *dataSource = m_widget->webView().mainFrame.dataSource;
if (!dataSource)
dataSource = m_widget->webView().mainFrame.provisionalDataSource;
return QUrl::fromNSURL(dataSource.request.URL);
}
void MacWebKitHelpViewer::setSource(const QUrl &url)
{
AutoreleasePool pool; Q_UNUSED(pool)
ensureProtocolHandler();
[m_widget->webView().mainFrame loadRequest:[NSURLRequest requestWithURL:url.toNSURL()]];
}
void MacWebKitHelpViewer::scrollToAnchor(const QString &anchor)
{
QUrl url = source();
url.setFragment(anchor);
setSource(url);
}
void MacWebKitHelpViewer::setHtml(const QString &html)
{
AutoreleasePool pool; Q_UNUSED(pool)
[m_widget->webView().mainFrame
loadHTMLString:html.toNSString()
baseURL:[NSURL fileURLWithPath:Core::ICore::resourcePath().toNSString()]];
}
QString MacWebKitHelpViewer::selectedText() const
{
AutoreleasePool pool; Q_UNUSED(pool)
return QString::fromNSString(m_widget->webView().selectedDOMRange.text);
}
bool MacWebKitHelpViewer::isForwardAvailable() const
{
AutoreleasePool pool; Q_UNUSED(pool)
return m_widget->webView().canGoForward;
}
bool MacWebKitHelpViewer::isBackwardAvailable() const
{
AutoreleasePool pool; Q_UNUSED(pool)
return m_widget->webView().canGoBack;
}
void MacWebKitHelpViewer::addBackHistoryItems(QMenu *backMenu)
{
AutoreleasePool pool; Q_UNUSED(pool)
WebBackForwardList *list = m_widget->webView().backForwardList;
int backListCount = list.backListCount;
for (int i = 0; i < backListCount; ++i) {
int historyIndex = -(i+1);
QAction *action = new QAction(backMenu);
action->setText(QString::fromNSString([list itemAtIndex:historyIndex].title));
action->setData(historyIndex);
connect(action, SIGNAL(triggered()), this, SLOT(goToHistoryItem()));
backMenu->addAction(action);
}
}
void MacWebKitHelpViewer::addForwardHistoryItems(QMenu *forwardMenu)
{
AutoreleasePool pool; Q_UNUSED(pool)
WebBackForwardList *list = m_widget->webView().backForwardList;
int forwardListCount = list.forwardListCount;
for (int i = 0; i < forwardListCount; ++i) {
int historyIndex = i + 1;
QAction *action = new QAction(forwardMenu);
action->setText(QString::fromNSString([list itemAtIndex:historyIndex].title));
action->setData(historyIndex);
connect(action, SIGNAL(triggered()), this, SLOT(goToHistoryItem()));
forwardMenu->addAction(action);
}
}
void MacWebKitHelpViewer::setOpenInNewWindowActionVisible(bool visible)
{
m_widget->setOpenInNewWindowActionVisible(visible);
}
DOMRange *MacWebKitHelpViewer::findText(NSString *text, bool forward, bool caseSensitive, DOMNode *startNode, int startOffset)
{
QTC_ASSERT(text, return nil);
if (text.length == 0)
return nil;
DOMDocument *document = m_widget->webView().mainFrame.DOMDocument;
// search only the body
DOMNodeIterator *iterator = [document createNodeIterator:document.body whatToShow:DOM_SHOW_ALL
filter:nil expandEntityReferences:NO];
DOMNode *selectionStart = nil;
int selectionStartOffset = 0;
DOMNode *currentNode = startNode;
int currentOffset = startOffset;
NSString *searchTerm = caseSensitive ? text : [text lowercaseString];
int searchTermLength = searchTerm.length;
int indexInSearchTerm = forward ? 0 : searchTerm.length - 1;
if (!currentNode) { // search whole body from end
if (forward)
currentNode = document.body;
else
currentNode = [iterator gotoEnd];
} else { // otherwise find the start node
QTC_ASSERT([iterator findNode:currentNode], return nil);
}
if (!forward) { // findNode leaves iterator behind currentNode, we need to go back
QTC_ASSERT([iterator previousNode] == currentNode, return nil);
}
if (currentNode.nodeType != DOM_TEXT_NODE) { // we only want text nodes
currentNode = forward ? [iterator nextTextNode] : [iterator previousTextNode];
currentOffset = -1; // search whole node
}
while (currentNode) {
NSString *currentText = caseSensitive ? currentNode.nodeValue : [currentNode.nodeValue lowercaseString];
int currentTextLength = currentText.length;
if (currentOffset < 0) // search whole node
currentOffset = forward ? 0 : currentTextLength - 1;
while (currentOffset < currentTextLength/*forward*/ && currentOffset >= 0/*backward*/) {
if ([currentText characterAtIndex:currentOffset] == [searchTerm characterAtIndex:indexInSearchTerm]) {
indexInSearchTerm += forward ? 1 : -1;
if (!selectionStart) {
selectionStart = currentNode;
selectionStartOffset = currentOffset;
}
} else {
indexInSearchTerm = forward ? 0 : searchTerm.length - 1;
selectionStart = nil;
}
currentOffset += forward ? 1 : -1;
if (indexInSearchTerm >= searchTermLength/*forward*/ || indexInSearchTerm < 0/*backward*/) {
// we have found a match!
DOMRange *range = [document createRange];
if (forward) {
[range setStart:selectionStart offset:selectionStartOffset];
[range setEnd:currentNode offset:currentOffset];
} else {
[range setStart:currentNode offset:(currentOffset + 1)]; // was already decreased
[range setEnd:selectionStart offset:(selectionStartOffset + 1)];
}
return range;
}
}
currentNode = forward ? [iterator nextTextNode] : [iterator previousTextNode];
currentOffset = -1; // search whole node
}
return nil;
}
bool MacWebKitHelpViewer::findText(const QString &text, Core::FindFlags flags, bool incremental,
bool fromSearch, bool *wrapped)
{
Q_UNUSED(incremental);
Q_UNUSED(fromSearch);
AutoreleasePool pool; Q_UNUSED(pool)
if (wrapped)
*wrapped = false;
bool forward = !(flags & Core::FindBackward);
bool caseSensitive = (flags & Core::FindCaseSensitively);
WebView *webView = m_widget->webView();
// WebView searchFor:.... grabs first responder, and when losing first responder afterwards,
// it removes the selection and forgets the search state, making it pretty useless for us
// define the start node and offset for the search
DOMNode *start = nil; // default is search whole body
int startOffset = -1;
DOMRange *selection = webView.selectedDOMRange;
if (selection) {
if (QString::fromNSString(selection.text).compare(
text, caseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive) != 0) {
// for incremental search we want to search from the beginning of the selection
start = selection.startContainer;
startOffset = selection.startOffset;
} else {
// search for next occurrence
if (forward) {
start = selection.endContainer;
startOffset = selection.endOffset;
} else {
start = selection.startContainer;
startOffset = selection.startOffset;
}
}
}
DOMRange *newSelection = findText(text.toNSString(), forward, caseSensitive,
start, startOffset);
if (!newSelection && start != nil) { // wrap
start = nil;
startOffset = -1;
newSelection = findText(text.toNSString(), forward, caseSensitive,
start, startOffset);
if (newSelection && wrapped)
*wrapped = true;
}
if (newSelection) {
// found, select and scroll there
[webView setSelectedDOMRange:newSelection affinity:NSSelectionAffinityDownstream];
if (forward)
[newSelection.endContainer.parentElement scrollIntoViewIfNeeded:YES];
else
[newSelection.startContainer.parentElement scrollIntoViewIfNeeded:YES];
return true;
}
return false;
}
void MacWebKitHelpViewer::copy()
{
QApplication::clipboard()->setText(selectedText());
}
void MacWebKitHelpViewer::stop()
{
[m_widget->webView() stopLoading:nil];
}
void MacWebKitHelpViewer::forward()
{
AutoreleasePool pool; Q_UNUSED(pool)
[m_widget->webView() goForward];
emit forwardAvailable(isForwardAvailable());
emit backwardAvailable(isBackwardAvailable());
}
void MacWebKitHelpViewer::backward()
{
AutoreleasePool pool; Q_UNUSED(pool)
[m_widget->webView() goBack];
emit forwardAvailable(isForwardAvailable());
emit backwardAvailable(isBackwardAvailable());
}
void MacWebKitHelpViewer::print(QPrinter *printer)
{
Q_UNUSED(printer)
}
void MacWebKitHelpViewer::slotLoadStarted()
{
HelpViewer::slotLoadStarted();
}
void MacWebKitHelpViewer::slotLoadFinished()
{
HelpViewer::slotLoadFinished();
emit forwardAvailable(isForwardAvailable());
emit backwardAvailable(isBackwardAvailable());
}
void MacWebKitHelpViewer::goToHistoryItem()
{
AutoreleasePool pool; Q_UNUSED(pool)
QAction *action = qobject_cast<QAction *>(sender());
QTC_ASSERT(action, return);
bool ok = false;
int index = action->data().toInt(&ok);
QTC_ASSERT(ok, return);
WebBackForwardList *list = m_widget->webView().backForwardList;
WebHistoryItem *item = [list itemAtIndex:index];
QTC_ASSERT(item, return);
[m_widget->webView() goToBackForwardItem:item];
emit forwardAvailable(isForwardAvailable());
emit backwardAvailable(isBackwardAvailable());
}
// #pragma mark -- MacResponderHack
MacResponderHack::MacResponderHack(QObject *parent)
: QObject(parent)
{
connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)),
this, SLOT(responderHack(QWidget*,QWidget*)));
}
void MacResponderHack::responderHack(QWidget *old, QWidget *now)
{
// On focus change, Qt does not make the corresponding QNSView firstResponder.
// That breaks when embedding native NSView into a Qt hierarchy. When the focus is changed
// by clicking with the mouse into a widget, everything is fine, because Cocoa automatically
// adapts firstResponder in that case, but it breaks when setting the Qt focus from code.
Q_UNUSED(old)
if (!now)
return;
AutoreleasePool pool; Q_UNUSED(pool)
NSView *view;
if (QMacCocoaViewContainer *viewContainer = qobject_cast<QMacCocoaViewContainer *>(now))
view = viewContainer->cocoaView();
else
view = (NSView *)now->effectiveWinId();
[view.window makeFirstResponder:view];
}
} // Internal
} // Help

View File

@@ -196,7 +196,7 @@ HelpViewer *OpenPagesManager::createPageFromSearch(const QUrl &url)
HelpViewer *OpenPagesManager::createPage(const QUrl &url, bool fromSearch)
{
if (HelpViewer::launchWithExternalApp(url))
if (url.isValid() && HelpViewer::launchWithExternalApp(url))
return 0;
m_model->addPage(url);

View File

@@ -77,7 +77,8 @@ void OpenPagesModel::addPage(const QUrl &url, qreal zoom)
connect(page, SIGNAL(titleChanged()), this, SLOT(handleTitleChanged()));
m_pages << page;
endInsertRows();
page->setSource(url);
if (url.isValid())
page->setSource(url);
}
void OpenPagesModel::removePage(int index)

View File

@@ -451,18 +451,13 @@ QtWebKitHelpViewer::QtWebKitHelpViewer(qreal zoom, QWidget *parent)
connect(m_webView->page(), SIGNAL(printRequested(QWebFrame*)), this, SIGNAL(printRequested()));
connect(m_webView, SIGNAL(backwardAvailable(bool)), this, SIGNAL(backwardAvailable(bool)));
connect(m_webView, SIGNAL(forwardAvailable(bool)), this, SIGNAL(forwardAvailable(bool)));
setViewerFont(viewerFont());
}
QFont QtWebKitHelpViewer::viewerFont() const
{
QWebSettings* webSettings = QWebSettings::globalSettings();
QFont font(QApplication::font().family(),
QWebSettings* webSettings = m_webView->settings();
return QFont(webSettings->fontFamily(QWebSettings::StandardFont),
webSettings->fontSize(QWebSettings::DefaultFontSize));
const QHelpEngineCore &engine = LocalHelpManager::helpEngine();
return qvariant_cast<QFont>(engine.customValue(QLatin1String("font"),
font));
}
void QtWebKitHelpViewer::setViewerFont(const QFont &font)
@@ -497,11 +492,6 @@ QString QtWebKitHelpViewer::title() const
return m_webView->title();
}
void QtWebKitHelpViewer::setTitle(const QString &title)
{
Q_UNUSED(title)
}
QUrl QtWebKitHelpViewer::source() const
{
return m_webView->url();

View File

@@ -59,7 +59,6 @@ public:
qreal scale() const;
QString title() const;
void setTitle(const QString &title);
QUrl source() const;
void setSource(const QUrl &url);

View File

@@ -64,11 +64,6 @@ TextBrowserHelpViewer::TextBrowserHelpViewer(qreal zoom, QWidget *parent)
p.color(QPalette::Active, QPalette::HighlightedText));
setPalette(p);
// ???
QFont font = viewerFont();
font.setPointSize(int(font.pointSize() + zoom));
setViewerFont(font);
connect(m_textBrowser, SIGNAL(sourceChanged(QUrl)), this, SIGNAL(titleChanged()));
connect(m_textBrowser, SIGNAL(forwardAvailable(bool)), this, SIGNAL(forwardAvailable(bool)));
connect(m_textBrowser, SIGNAL(backwardAvailable(bool)), this, SIGNAL(backwardAvailable(bool)));
@@ -80,18 +75,14 @@ TextBrowserHelpViewer::~TextBrowserHelpViewer()
QFont TextBrowserHelpViewer::viewerFont() const
{
const QHelpEngineCore &engine = LocalHelpManager::helpEngine();
return qvariant_cast<QFont>(engine.customValue(QLatin1String("font"),
qApp->font()));
return m_textBrowser->font();
}
void TextBrowserHelpViewer::setViewerFont(const QFont &newFont)
{
if (font() != newFont) {
m_textBrowser->forceFont = true;
m_textBrowser->setFont(newFont);
m_textBrowser->forceFont = false;
}
m_textBrowser->forceFont = true;
m_textBrowser->setFont(newFont);
m_textBrowser->forceFont = false;
}
void TextBrowserHelpViewer::scaleUp()
@@ -124,11 +115,6 @@ QString TextBrowserHelpViewer::title() const
return m_textBrowser->documentTitle();
}
void TextBrowserHelpViewer::setTitle(const QString &title)
{
m_textBrowser->setDocumentTitle(title);
}
QUrl TextBrowserHelpViewer::source() const
{
return m_textBrowser->source();

View File

@@ -59,7 +59,6 @@ public:
qreal scale() const;
QString title() const;
void setTitle(const QString &title);
QUrl source() const;
void setSource(const QUrl &url);