diff options
author | Eike Ziller <[email protected]> | 2014-05-14 13:15:01 +0200 |
---|---|---|
committer | Eike Ziller <[email protected]> | 2014-05-30 09:37:04 +0200 |
commit | c5be32fae7ac528f4d0c1aa89d2845fb49335141 (patch) | |
tree | 117ad4adaefb4cfaadb6dead7009aaf46d086ad5 /src/plugins | |
parent | 62a83b2b3fb2201a72b5d7501a9ad1ea0440a794 (diff) |
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 <[email protected]>
Diffstat (limited to 'src/plugins')
-rw-r--r-- | src/plugins/help/generalsettingspage.cpp | 6 | ||||
-rw-r--r-- | src/plugins/help/help.pro | 9 | ||||
-rw-r--r-- | src/plugins/help/helpconstants.h | 1 | ||||
-rw-r--r-- | src/plugins/help/helpplugin.cpp | 35 | ||||
-rw-r--r-- | src/plugins/help/helpviewer.h | 1 | ||||
-rw-r--r-- | src/plugins/help/localhelpmanager.cpp | 24 | ||||
-rw-r--r-- | src/plugins/help/localhelpmanager.h | 6 | ||||
-rw-r--r-- | src/plugins/help/macwebkithelpviewer.h | 140 | ||||
-rw-r--r-- | src/plugins/help/macwebkithelpviewer.mm | 812 | ||||
-rw-r--r-- | src/plugins/help/openpagesmanager.cpp | 2 | ||||
-rw-r--r-- | src/plugins/help/openpagesmodel.cpp | 3 | ||||
-rw-r--r-- | src/plugins/help/qtwebkithelpviewer.cpp | 14 | ||||
-rw-r--r-- | src/plugins/help/qtwebkithelpviewer.h | 1 | ||||
-rw-r--r-- | src/plugins/help/textbrowserhelpviewer.cpp | 22 | ||||
-rw-r--r-- | src/plugins/help/textbrowserhelpviewer.h | 1 |
15 files changed, 1030 insertions, 47 deletions
diff --git a/src/plugins/help/generalsettingspage.cpp b/src/plugins/help/generalsettingspage.cpp index 46a30e4bf3e..ed252da08c5 100644 --- a/src/plugins/help/generalsettingspage.cpp +++ b/src/plugins/help/generalsettingspage.cpp @@ -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(); } diff --git a/src/plugins/help/help.pro b/src/plugins/help/help.pro index 4fb13c424e7..4236715677d 100644 --- a/src/plugins/help/help.pro +++ b/src/plugins/help/help.pro @@ -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) diff --git a/src/plugins/help/helpconstants.h b/src/plugins/help/helpconstants.h index 8c80f008d74..f9cfdf70f2a 100644 --- a/src/plugins/help/helpconstants.h +++ b/src/plugins/help/helpconstants.h @@ -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"; diff --git a/src/plugins/help/helpplugin.cpp b/src/plugins/help/helpplugin.cpp index 33d490b3d22..0876d5138f9 100644 --- a/src/plugins/help/helpplugin.cpp +++ b/src/plugins/help/helpplugin.cpp @@ -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) { + 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 - if (qgetenv("QTC_FORCE_TEXTBROWSER").isEmpty()) - return new QtWebKitHelpViewer(zoom); - else - return new TextBrowserHelpViewer(zoom); + viewer = new QtWebKitHelpViewer(zoom); #else - return new TextBrowserHelpViewer(zoom); + 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)) diff --git a/src/plugins/help/helpviewer.h b/src/plugins/help/helpviewer.h index a50ed91cc3a..e2ed30627bb 100644 --- a/src/plugins/help/helpviewer.h +++ b/src/plugins/help/helpviewer.h @@ -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; diff --git a/src/plugins/help/localhelpmanager.cpp b/src/plugins/help/localhelpmanager.cpp index 9bce86ce87d..424bdbe52f4 100644 --- a/src/plugins/help/localhelpmanager.cpp +++ b/src/plugins/help/localhelpmanager.cpp @@ -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(); +} diff --git a/src/plugins/help/localhelpmanager.h b/src/plugins/help/localhelpmanager.h index 3f8b720499a..fdb0ab80d6b 100644 --- a/src/plugins/help/localhelpmanager.h +++ b/src/plugins/help/localhelpmanager.h @@ -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; diff --git a/src/plugins/help/macwebkithelpviewer.h b/src/plugins/help/macwebkithelpviewer.h new file mode 100644 index 00000000000..17e05c0a34d --- /dev/null +++ b/src/plugins/help/macwebkithelpviewer.h @@ -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 diff --git a/src/plugins/help/macwebkithelpviewer.mm b/src/plugins/help/macwebkithelpviewer.mm new file mode 100644 index 00000000000..64fd77b6eb0 --- /dev/null +++ b/src/plugins/help/macwebkithelpviewer.mm @@ -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 diff --git a/src/plugins/help/openpagesmanager.cpp b/src/plugins/help/openpagesmanager.cpp index 4298b3f9a91..563030d4610 100644 --- a/src/plugins/help/openpagesmanager.cpp +++ b/src/plugins/help/openpagesmanager.cpp @@ -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); diff --git a/src/plugins/help/openpagesmodel.cpp b/src/plugins/help/openpagesmodel.cpp index a834769f3b6..57661d8942f 100644 --- a/src/plugins/help/openpagesmodel.cpp +++ b/src/plugins/help/openpagesmodel.cpp @@ -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) diff --git a/src/plugins/help/qtwebkithelpviewer.cpp b/src/plugins/help/qtwebkithelpviewer.cpp index 0d1c0164610..777077154bc 100644 --- a/src/plugins/help/qtwebkithelpviewer.cpp +++ b/src/plugins/help/qtwebkithelpviewer.cpp @@ -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(); diff --git a/src/plugins/help/qtwebkithelpviewer.h b/src/plugins/help/qtwebkithelpviewer.h index b6e9437545d..f8c65bc315d 100644 --- a/src/plugins/help/qtwebkithelpviewer.h +++ b/src/plugins/help/qtwebkithelpviewer.h @@ -59,7 +59,6 @@ public: qreal scale() const; QString title() const; - void setTitle(const QString &title); QUrl source() const; void setSource(const QUrl &url); diff --git a/src/plugins/help/textbrowserhelpviewer.cpp b/src/plugins/help/textbrowserhelpviewer.cpp index 5e7f5c07a80..4b8735c7f48 100644 --- a/src/plugins/help/textbrowserhelpviewer.cpp +++ b/src/plugins/help/textbrowserhelpviewer.cpp @@ -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(); diff --git a/src/plugins/help/textbrowserhelpviewer.h b/src/plugins/help/textbrowserhelpviewer.h index f4a120923c9..b3f9577737e 100644 --- a/src/plugins/help/textbrowserhelpviewer.h +++ b/src/plugins/help/textbrowserhelpviewer.h @@ -59,7 +59,6 @@ public: qreal scale() const; QString title() const; - void setTitle(const QString &title); QUrl source() const; void setSource(const QUrl &url); |