2014-05-14 13:15:01 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
|
|
|
|
** 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);
|
2014-06-17 15:37:40 +02:00
|
|
|
Help::Internal::LocalHelpManager::HelpData data;
|
2014-05-14 13:15:01 +02:00
|
|
|
Help::Internal::LocalHelpManager *helpManager = Help::Internal::LocalHelpManager::instance();
|
|
|
|
|
|
|
|
|
|
QMetaObject::invokeMethod(helpManager, "helpData", Qt::BlockingQueuedConnection,
|
2014-06-17 15:37:40 +02:00
|
|
|
Q_RETURN_ARG(Help::Internal::LocalHelpManager::HelpData, data),
|
|
|
|
|
Q_ARG(QUrl, url));
|
2014-05-14 13:15:01 +02:00
|
|
|
|
2014-06-17 15:37:40 +02:00
|
|
|
NSURL *resolvedURL = data.resolvedUrl.toNSURL();
|
|
|
|
|
NSString *mimeType = data.mimeType.toNSString();
|
|
|
|
|
NSData *nsdata = QtMac::toNSData(data.data); // Qt 5.3 has this in QByteArray
|
|
|
|
|
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:resolvedURL
|
2014-05-14 13:15:01 +02:00
|
|
|
MIMEType:mimeType
|
2014-06-17 15:37:40 +02:00
|
|
|
expectedContentLength:data.data.length()
|
2014-05-14 13:15:01 +02:00
|
|
|
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
|