summaryrefslogtreecommitdiff
path: root/src/plugins/help/macwebkithelpviewer.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/help/macwebkithelpviewer.mm')
-rw-r--r--src/plugins/help/macwebkithelpviewer.mm812
1 files changed, 812 insertions, 0 deletions
diff --git a/src/plugins/help/macwebkithelpviewer.mm b/src/plugins/help/macwebkithelpviewer.mm
new file mode 100644
index 0000000000..64fd77b6eb
--- /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