blob: 61012d5eaa1443b6a8970d0f39b1152a61f67356 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#import <private/qcocoaview_mac_p.h>
#ifdef QT_MAC_USE_COCOA
#include <private/qwidget_p.h>
#include <private/qt_mac_p.h>
#include <private/qapplication_p.h>
#include <private/qabstractscrollarea_p.h>
#include <private/qt_cocoa_helpers_mac_p.h>
#include <private/qdnd_p.h>
#include <private/qmacinputcontext_p.h>
#include <private/qmultitouch_mac_p.h>
#include <private/qevent_p.h>
#include <private/qbackingstore_p.h>
#include <qscrollarea.h>
#include <qhash.h>
#include <qtextformat.h>
#include <qpaintengine.h>
#include <QUrl>
#include <QAccessible>
#include <QFileInfo>
#include <QFile>
#include <qdebug.h>
@interface NSEvent (Qt_Compile_Leopard_DeviceDelta)
- (CGFloat)deviceDeltaX;
- (CGFloat)deviceDeltaY;
- (CGFloat)deviceDeltaZ;
@end
@interface NSEvent (Qt_Compile_Leopard_Gestures)
- (CGFloat)magnification;
@end
QT_BEGIN_NAMESPACE
Q_GLOBAL_STATIC(DnDParams, qMacDnDParams);
extern void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos); // qcursor_mac.mm
extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp
extern OSViewRef qt_mac_nativeview_for(const QWidget *w); // qwidget_mac.mm
extern QPointer<QWidget> qt_mouseover; //qapplication_mac.mm
extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum);
struct dndenum_mapper
{
NSDragOperation mac_code;
Qt::DropAction qt_code;
bool Qt2Mac;
};
static dndenum_mapper dnd_enums[] = {
{ NSDragOperationLink, Qt::LinkAction, true },
{ NSDragOperationMove, Qt::MoveAction, true },
{ NSDragOperationCopy, Qt::CopyAction, true },
{ NSDragOperationGeneric, Qt::CopyAction, false },
{ NSDragOperationEvery, Qt::ActionMask, false },
{ NSDragOperationNone, Qt::IgnoreAction, false }
};
static NSDragOperation qt_mac_mapDropAction(Qt::DropAction action)
{
for (int i=0; dnd_enums[i].qt_code; i++) {
if (dnd_enums[i].Qt2Mac && (action & dnd_enums[i].qt_code)) {
return dnd_enums[i].mac_code;
}
}
return NSDragOperationNone;
}
static NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions)
{
NSDragOperation nsActions = NSDragOperationNone;
for (int i=0; dnd_enums[i].qt_code; i++) {
if (dnd_enums[i].Qt2Mac && (actions & dnd_enums[i].qt_code))
nsActions |= dnd_enums[i].mac_code;
}
return nsActions;
}
static Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions)
{
Qt::DropAction action = Qt::IgnoreAction;
for (int i=0; dnd_enums[i].mac_code; i++) {
if (nsActions & dnd_enums[i].mac_code)
return dnd_enums[i].qt_code;
}
return action;
}
static Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
{
Qt::DropActions actions = Qt::IgnoreAction;
for (int i=0; dnd_enums[i].mac_code; i++) {
if (nsActions & dnd_enums[i].mac_code)
actions |= dnd_enums[i].qt_code;
}
return actions;
}
static QColor colorFrom(NSColor *color)
{
QColor qtColor;
NSString *colorSpace = [color colorSpaceName];
if (colorSpace == NSDeviceCMYKColorSpace) {
CGFloat cyan, magenta, yellow, black, alpha;
[color getCyan:&cyan magenta:&magenta yellow:&yellow black:&black alpha:&alpha];
qtColor.setCmykF(cyan, magenta, yellow, black, alpha);
} else {
NSColor *tmpColor;
tmpColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
CGFloat red, green, blue, alpha;
[tmpColor getRed:&red green:&green blue:&blue alpha:&alpha];
qtColor.setRgbF(red, green, blue, alpha);
}
return qtColor;
}
QT_END_NAMESPACE
QT_FORWARD_DECLARE_CLASS(QMacCocoaAutoReleasePool)
QT_FORWARD_DECLARE_CLASS(QCFString)
QT_FORWARD_DECLARE_CLASS(QDragManager)
QT_FORWARD_DECLARE_CLASS(QMimeData)
QT_FORWARD_DECLARE_CLASS(QPoint)
QT_FORWARD_DECLARE_CLASS(QApplication)
QT_FORWARD_DECLARE_CLASS(QApplicationPrivate)
QT_FORWARD_DECLARE_CLASS(QDragEnterEvent)
QT_FORWARD_DECLARE_CLASS(QDragMoveEvent)
QT_FORWARD_DECLARE_CLASS(QStringList)
QT_FORWARD_DECLARE_CLASS(QString)
QT_FORWARD_DECLARE_CLASS(QRect)
QT_FORWARD_DECLARE_CLASS(QRegion)
QT_FORWARD_DECLARE_CLASS(QAbstractScrollArea)
QT_FORWARD_DECLARE_CLASS(QAbstractScrollAreaPrivate)
QT_FORWARD_DECLARE_CLASS(QPaintEvent)
QT_FORWARD_DECLARE_CLASS(QPainter)
QT_FORWARD_DECLARE_CLASS(QHoverEvent)
QT_FORWARD_DECLARE_CLASS(QCursor)
QT_USE_NAMESPACE
extern "C" {
extern NSString *NSTextInputReplacementRangeAttributeName;
}
#ifdef ALIEN_DEBUG
static int qCocoaViewCount = 0;
#endif
@implementation QT_MANGLE_NAMESPACE(QCocoaView)
- (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate
{
self = [super init];
if (self) {
[self finishInitWithQWidget:widget widgetPrivate:widgetprivate];
}
[self setFocusRingType:NSFocusRingTypeNone];
composingText = new QString();
#ifdef ALIEN_DEBUG
++qCocoaViewCount;
qDebug() << "init: qCocoaViewCount is" << qCocoaViewCount;
#endif
composing = false;
sendKeyEvents = true;
[self setHidden:YES];
return self;
}
- (void) finishInitWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate
{
qwidget = widget;
qwidgetprivate = widgetprivate;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(frameDidChange:)
name:@"NSViewFrameDidChangeNotification"
object:self];
}
- (void)resetCursorRects
{
// [NSView addCursorRect] is slow, so bail out early if we can:
if (NSIsEmptyRect([self visibleRect]))
return;
QWidget *cursorWidget = qwidget;
if (cursorWidget->testAttribute(Qt::WA_TransparentForMouseEvents))
cursorWidget = QApplication::widgetAt(qwidget->mapToGlobal(qwidget->rect().center()));
if (cursorWidget == 0)
return;
if (!cursorWidget->testAttribute(Qt::WA_SetCursor)) {
[super resetCursorRects];
return;
}
QRegion mask = qt_widget_private(cursorWidget)->extra->mask;
NSCursor *nscursor = static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursorWidget->cursor()));
// The mask could have the WA_MouseNoMask attribute set and that means that we have to ignore the mask.
if (mask.isEmpty() || cursorWidget->testAttribute(Qt::WA_MouseNoMask)) {
[self addCursorRect:[qt_mac_nativeview_for(cursorWidget) visibleRect] cursor:nscursor];
} else {
const QVector<QRect> &rects = mask.rects();
for (int i = 0; i < rects.size(); ++i) {
const QRect &rect = rects.at(i);
[self addCursorRect:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()) cursor:nscursor];
}
}
}
- (void)removeDropData
{
if (dropData) {
delete dropData;
dropData = 0;
}
}
- (void)addDropData:(id <NSDraggingInfo>)sender
{
[self removeDropData];
CFStringRef dropPasteboard = (CFStringRef) [[sender draggingPasteboard] name];
dropData = new QCocoaDropData(dropPasteboard);
}
- (void)changeDraggingCursor:(NSDragOperation)newOperation
{
static SEL action = nil;
static bool operationSupported = false;
if (action == nil) {
action = NSSelectorFromString(@"operationNotAllowedCursor");
if ([NSCursor respondsToSelector:action]) {
operationSupported = true;
}
}
if (operationSupported) {
NSCursor *notAllowedCursor = [NSCursor performSelector:action];
bool isNotAllowedCursor = ([NSCursor currentCursor] == notAllowedCursor);
if (newOperation == NSDragOperationNone && !isNotAllowedCursor) {
[notAllowedCursor push];
} else if (newOperation != NSDragOperationNone && isNotAllowedCursor) {
[notAllowedCursor pop];
}
}
}
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
{
// NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly
// from Cocoa. They modify the drag target, and might fake enter/leave events.
NSPoint windowPoint = [sender draggingLocation];
dragEnterSequence = [sender draggingSequenceNumber];
[self addDropData:sender];
QMimeData *mimeData = dropData;
if (QDragManager::self()->source())
mimeData = QDragManager::self()->dragPrivate()->data;
NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint];
NSPoint localPoint = [self convertPoint:windowPoint fromView:nil];
QPoint posDrag(localPoint.x, localPoint.y);
NSDragOperation nsActions = [sender draggingSourceOperationMask];
Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions);
QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions;
Qt::KeyboardModifiers modifiers = Qt::NoModifier;
if ([sender draggingSource] != nil) {
// modifier flags might have changed, update it here since we don't send any input events.
QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]);
modifiers = QApplication::keyboardModifiers();
} else {
// when the source is from another application the above technique will not work.
modifiers = qt_cocoaDragOperation2QtModifiers(nsActions);
}
// send the drag enter event to the widget.
QDragEnterEvent qDEEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers);
QApplication::sendEvent(qwidget, &qDEEvent);
if (!qDEEvent.isAccepted()) {
// widget is not interested in this drag, so ignore this drop data.
[self removeDropData];
[self changeDraggingCursor:NSDragOperationNone];
return NSDragOperationNone;
} else {
// save the mouse position, used by draggingExited handler.
DnDParams *dndParams = [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent];
dndParams->activeDragEnterPos = windowPoint;
// send a drag move event immediately after a drag enter event (as per documentation).
QDragMoveEvent qDMEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers);
qDMEvent.setDropAction(qDEEvent.dropAction());
qDMEvent.accept(); // accept by default, since enter event was accepted.
QApplication::sendEvent(qwidget, &qDMEvent);
if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) {
// since we accepted the drag enter event, the widget expects
// future drage move events.
// ### check if we need to treat this like the drag enter event.
nsActions = NSDragOperationNone;
// Save as ignored in the answer rect.
qDMEvent.setDropAction(Qt::IgnoreAction);
} else {
nsActions = QT_PREPEND_NAMESPACE(qt_mac_mapDropAction)(qDMEvent.dropAction());
}
QT_PREPEND_NAMESPACE(qt_mac_copy_answer_rect)(qDMEvent);
[self changeDraggingCursor:nsActions];
return nsActions;
}
}
- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender
{
// NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly
// from Cocoa. They modify the drag target, and might fake enter/leave events.
NSPoint windowPoint = [sender draggingLocation];
// in cases like QFocusFrame, the view under the mouse might
// not have received the drag enter. Generate a synthetic
// drag enter event for that view.
if (dragEnterSequence != [sender draggingSequenceNumber])
[self draggingEntered:sender];
// drag enter event was rejected, so ignore the move event.
if (dropData == 0) {
[self changeDraggingCursor:NSDragOperationNone];
return NSDragOperationNone;
}
// return last value, if we are still in the answerRect.
NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint];
NSPoint localPoint = [self convertPoint:windowPoint fromView:nil];
NSDragOperation nsActions = [sender draggingSourceOperationMask];
QPoint posDrag(localPoint.x, localPoint.y);
if (qt_mac_mouse_inside_answer_rect(posDrag)
&& QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) == nsActions) {
NSDragOperation operation = QT_PREPEND_NAMESPACE(qt_mac_mapDropActions)(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastAction));
[self changeDraggingCursor:operation];
return operation;
}
// send drag move event to the widget
QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions;
Qt::DropActions qtAllowed = QT_PREPEND_NAMESPACE(qt_mac_mapNSDragOperations)(nsActions);
Qt::KeyboardModifiers modifiers = Qt::NoModifier;
if ([sender draggingSource] != nil) {
// modifier flags might have changed, update it here since we don't send any input events.
QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]);
modifiers = QApplication::keyboardModifiers();
} else {
// when the source is from another application the above technique will not work.
modifiers = qt_cocoaDragOperation2QtModifiers(nsActions);
}
QMimeData *mimeData = dropData;
if (QDragManager::self()->source())
mimeData = QDragManager::self()->dragPrivate()->data;
QDragMoveEvent qDMEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers);
if (QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction != Qt::IgnoreAction
&& QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).buttons == qDMEvent.mouseButtons()
&& QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).modifiers == qDMEvent.keyboardModifiers())
qDMEvent.setDropAction(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction);
qDMEvent.accept();
QApplication::sendEvent(qwidget, &qDMEvent);
NSDragOperation operation = qt_mac_mapDropAction(qDMEvent.dropAction());
if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) {
// ignore this event (we will still receive further notifications)
operation = NSDragOperationNone;
// Save as ignored in the answer rect.
qDMEvent.setDropAction(Qt::IgnoreAction);
}
qt_mac_copy_answer_rect(qDMEvent);
[self changeDraggingCursor:operation];
return operation;
}
- (void)draggingExited:(id < NSDraggingInfo >)sender
{
// NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly
// from Cocoa. They modify the drag target, and might fake enter/leave events.
Q_UNUSED(sender);
dragEnterSequence = -1;
// drag enter event was rejected, so ignore the move event.
if (dropData) {
QDragLeaveEvent de;
QApplication::sendEvent(qwidget, &de);
[self removeDropData];
}
[self changeDraggingCursor:NSDragOperationEvery];
}
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
{
// NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly
// from Cocoa. They modify the drag target, and might fake enter/leave events.
NSPoint windowPoint = [sender draggingLocation];
dragEnterSequence = -1;
[self addDropData:sender];
NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint];
NSPoint localPoint = [self convertPoint:windowPoint fromView:nil];
QPoint posDrop(localPoint.x, localPoint.y);
NSDragOperation nsActions = [sender draggingSourceOperationMask];
Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions);
QMimeData *mimeData = dropData;
if (QDragManager::self()->source())
mimeData = QDragManager::self()->dragPrivate()->data;
// send the drop event to the widget.
QDropEvent de(posDrop, qtAllowed, mimeData,
QApplication::mouseButtons(), QApplication::keyboardModifiers());
if (QDragManager::self()->object)
QDragManager::self()->dragPrivate()->target = qwidget;
QApplication::sendEvent(qwidget, &de);
if (QDragManager::self()->object)
QDragManager::self()->dragPrivate()->executed_action = de.dropAction();
if (!de.isAccepted())
return NO;
else
return YES;
}
- (void)dealloc
{
delete composingText;
[[NSNotificationCenter defaultCenter] removeObserver:self];
#ifdef ALIEN_DEBUG
--qCocoaViewCount;
qDebug() << "qCocoaViewCount is" << qCocoaViewCount;
#endif
[super dealloc];
}
- (BOOL)isOpaque;
{
if (!qwidgetprivate)
return [super isOpaque];
return qwidgetprivate->isOpaque;
}
- (BOOL)isFlipped;
{
return YES;
}
// We preserve the content of the view if WA_StaticContents is defined.
//
// More info in the Cocoa documentation:
// http://developer.apple.com/mac/library/documentation/cocoa/conceptual/CocoaViewsGuide/Optimizing/Optimizing.html
- (BOOL) preservesContentDuringLiveResize
{
return qwidget->testAttribute(Qt::WA_StaticContents);
}
- (void) setFrameSize:(NSSize)newSize
{
[super setFrameSize:newSize];
// A change in size has required the view to be invalidated.
if ([self inLiveResize]) {
NSRect rects[4];
NSInteger count;
[self getRectsExposedDuringLiveResize:rects count:&count];
while (count-- > 0)
{
[self setNeedsDisplayInRect:rects[count]];
}
} else {
[self setNeedsDisplay:YES];
}
// Make sure the opengl context is updated on resize.
if (qwidgetprivate && qwidgetprivate->isGLWidget) {
qwidgetprivate->needWindowChange = true;
QEvent event(QEvent::MacGLWindowChange);
qApp->sendEvent(qwidget, &event);
}
}
// We catch the 'setNeedsDisplay:' message in order to avoid a useless full repaint.
// During the resize, the top of the widget is repainted, probably because of the
// change of coordinate space (Quartz vs Qt). This is then followed by this message:
// -[NSView _setNeedsDisplayIfTopLeftChanged]
// which force a full repaint by sending the message 'setNeedsDisplay:'.
// That is what we are preventing here.
- (void)setNeedsDisplay:(BOOL)flag {
if (![self inLiveResize] || !(qwidget->testAttribute(Qt::WA_StaticContents))) {
[super setNeedsDisplay:flag];
}
}
- (void)drawRect:(NSRect)aRect
{
if (!qwidget)
return;
if (QApplicationPrivate::graphicsSystem() != 0) {
if (qwidgetprivate->maybeBackingStore()) {
// Drawing is handled on the window level
// See qcocoasharedwindowmethods_mac_p.h
if (!qwidget->testAttribute(Qt::WA_PaintOnScreen))
return;
}
}
CGContextRef cg = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
qwidgetprivate->hd = cg;
CGContextSaveGState(cg);
if (qwidget->isVisible() && qwidget->updatesEnabled()) { //process the actual paint event.
if (qwidget->testAttribute(Qt::WA_WState_InPaintEvent))
qWarning("QWidget::repaint: Recursive repaint detected");
const QRect qrect = QRect(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height);
QRegion qrgn;
const NSRect *rects;
NSInteger count;
[self getRectsBeingDrawn:&rects count:&count];
for (int i = 0; i < count; ++i) {
QRect tmpRect = QRect(rects[i].origin.x, rects[i].origin.y, rects[i].size.width, rects[i].size.height);
qrgn += tmpRect;
}
if (!qwidget->isWindow() && !qobject_cast<QAbstractScrollArea *>(qwidget->parent())) {
const QRegion &parentMask = qwidget->window()->mask();
if (!parentMask.isEmpty()) {
const QPoint mappedPoint = qwidget->mapTo(qwidget->window(), qrect.topLeft());
qrgn.translate(mappedPoint);
qrgn &= parentMask;
qrgn.translate(-mappedPoint.x(), -mappedPoint.y());
}
}
QPoint redirectionOffset(0, 0);
//setup the context
qwidget->setAttribute(Qt::WA_WState_InPaintEvent);
QPaintEngine *engine = qwidget->paintEngine();
if (engine)
engine->setSystemClip(qrgn);
if (qwidgetprivate->extra && qwidgetprivate->extra->hasMask) {
CGRect widgetRect = CGRectMake(0, 0, qwidget->width(), qwidget->height());
CGContextTranslateCTM (cg, 0, widgetRect.size.height);
CGContextScaleCTM(cg, 1, -1);
if (qwidget->isWindow())
CGContextClearRect(cg, widgetRect);
CGContextClipToMask(cg, widgetRect, qwidgetprivate->extra->imageMask);
CGContextScaleCTM(cg, 1, -1);
CGContextTranslateCTM (cg, 0, -widgetRect.size.height);
}
if (qwidget->isWindow() && !qwidgetprivate->isOpaque
&& !qwidget->testAttribute(Qt::WA_MacBrushedMetal)) {
CGContextClearRect(cg, NSRectToCGRect(aRect));
}
// Check for alien widgets, use qwidgetPrivate->drawWidget() to draw the widget if this
// is the case. This makes sure child widgets are drawn as well, Cocoa does not know about
// those and wont send them drawRect calls.
if (qwidget->testAttribute(Qt::WA_NativeWindow) && qt_widget_private(qwidget)->hasAlienChildren == false) {
if (engine && !qwidget->testAttribute(Qt::WA_NoSystemBackground)
&& (qwidget->isWindow() || qwidget->autoFillBackground())
|| qwidget->testAttribute(Qt::WA_TintedBackground)
|| qwidget->testAttribute(Qt::WA_StyledBackground)) {
#ifdef DEBUG_WIDGET_PAINT
if(doDebug)
qDebug(" Handling erase for [%s::%s]", qwidget->metaObject()->className(),
qwidget->objectName().local8Bit().data());
#endif
QPainter p(qwidget);
qwidgetprivate->paintBackground(&p, qrgn,
qwidget->isWindow() ? QWidgetPrivate::DrawAsRoot : 0);
p.end();
}
QPaintEvent e(qrgn);
#ifdef QT3_SUPPORT
e.setErased(true);
#endif
qt_sendSpontaneousEvent(qwidget, &e);
} else {
qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false); // QWidgetPrivate::drawWidget sets this
QWidgetPrivate *qwidgetPrivate = qt_widget_private(qwidget);
qwidgetPrivate->drawWidget(qwidget, qrgn, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen | QWidgetPrivate::DrawRecursive, 0);
}
if (!redirectionOffset.isNull())
QPainter::restoreRedirected(qwidget);
if (engine)
engine->setSystemClip(QRegion());
qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false);
if(!qwidget->testAttribute(Qt::WA_PaintOutsidePaintEvent) && qwidget->paintingActive())
qWarning("QWidget: It is dangerous to leave painters active on a"
" widget outside of the PaintEvent");
}
qwidgetprivate->hd = 0;
CGContextRestoreGState(cg);
}
- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
{
if (!qwidget)
return NO;
Q_UNUSED(theEvent);
return !qwidget->testAttribute(Qt::WA_MacNoClickThrough);
}
- (NSView *)hitTest:(NSPoint)aPoint
{
if (!qwidget)
return [super hitTest:aPoint];
if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents))
return nil; // You cannot hit a transparent for mouse event widget.
return [super hitTest:aPoint];
}
- (void)updateTrackingAreas
{
if (!qwidget)
return;
// [NSView addTrackingArea] is slow, so bail out early if we can:
if (NSIsEmptyRect([self visibleRect]))
return;
QMacCocoaAutoReleasePool pool;
if (NSArray *trackingArray = [self trackingAreas]) {
NSUInteger size = [trackingArray count];
for (NSUInteger i = 0; i < size; ++i) {
NSTrackingArea *t = [trackingArray objectAtIndex:i];
[self removeTrackingArea:t];
}
}
// Ideally, we shouldn't have NSTrackingMouseMoved events included below, it should
// only be turned on if mouseTracking, hover is on or a tool tip is set.
// Unfortunately, Qt will send "tooltip" events on mouse moves, so we need to
// turn it on in ALL case. That means EVERY QCocoaView gets to pay the cost of
// mouse moves delivered to it (Apple recommends keeping it OFF because there
// is a performance hit). So it goes.
NSUInteger trackingOptions = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp
| NSTrackingInVisibleRect | NSTrackingMouseMoved;
NSTrackingArea *ta = [[NSTrackingArea alloc] initWithRect:NSMakeRect(0, 0,
qwidget->width(),
qwidget->height())
options:trackingOptions
owner:self
userInfo:nil];
[self addTrackingArea:ta];
[ta release];
}
- (void)mouseEntered:(NSEvent *)event
{
if (!qwidget)
return;
if (qwidgetprivate->data.in_destructor)
return;
if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) {
QEvent enterEvent(QEvent::Enter);
NSPoint windowPoint = [event locationInWindow];
NSPoint globalPoint = [[event window] convertBaseToScreen:windowPoint];
NSPoint viewPoint = [self convertPoint:windowPoint fromView:nil];
QApplication::sendEvent(qwidget, &enterEvent);
qt_mouseover = qwidget;
// Update cursor icon:
qt_mac_update_cursor_at_global_pos(flipPoint(globalPoint).toPoint());
// Send mouse move and hover events as well:
if (!qAppInstance()->activePopupWidget() || qAppInstance()->activePopupWidget() == qwidget->window()) {
// This mouse move event should be sendt, even when mouse
// tracking is switched off (to trigger tooltips):
NSEvent *mouseEvent = [NSEvent mouseEventWithType:NSMouseMoved
location:windowPoint modifierFlags:[event modifierFlags] timestamp:[event timestamp]
windowNumber:[event windowNumber] context:[event context] eventNumber:[event eventNumber]
clickCount:0 pressure:0];
qt_mac_handleMouseEvent(self, mouseEvent, QEvent::MouseMove, Qt::NoButton);
if (qwidget->testAttribute(Qt::WA_Hover)) {
QHoverEvent he(QEvent::HoverEnter, QPoint(viewPoint.x, viewPoint.y), QPoint(-1, -1));
QApplicationPrivate::instance()->notify_helper(qwidget, &he);
}
}
}
}
- (void)mouseExited:(NSEvent *)event
{
if (!qwidget)
return;
QEvent leaveEvent(QEvent::Leave);
NSPoint globalPoint = [[event window] convertBaseToScreen:[event locationInWindow]];
if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) {
QApplication::sendEvent(qwidget, &leaveEvent);
// ### Think about if it is necessary to update the cursor, should only be for a few cases.
qt_mac_update_cursor_at_global_pos(flipPoint(globalPoint).toPoint());
if (qwidget->testAttribute(Qt::WA_Hover)
&& (!qAppInstance()->activePopupWidget() || qAppInstance()->activePopupWidget() == qwidget->window())) {
QHoverEvent he(QEvent::HoverLeave, QPoint(-1, -1),
qwidget->mapFromGlobal(QApplicationPrivate::instance()->hoverGlobalPos));
QApplicationPrivate::instance()->notify_helper(qwidget, &he);
}
}
}
- (void)flagsChanged:(NSEvent *)theEvent
{
if (!qwidget)
return;
QWidget *widgetToGetKey = qwidget;
QWidget *popup = qAppInstance()->activePopupWidget();
if (popup && popup != qwidget->window())
widgetToGetKey = popup->focusWidget() ? popup->focusWidget() : popup;
qt_dispatchModifiersChanged(theEvent, widgetToGetKey);
[super flagsChanged:theEvent];
}
- (void)mouseMoved:(NSEvent *)theEvent
{
if (!qwidget)
return;
// We always enable mouse tracking for all QCocoaView-s. In cases where we have
// child views, we will receive mouseMoved for both parent & the child (if
// mouse is over the child). We need to ignore the parent mouseMoved in such
// cases.
NSPoint windowPoint = [theEvent locationInWindow];
NSView *candidateView = [[[self window] contentView] hitTest:windowPoint];
if (candidateView && candidateView == self) {
qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton);
}
}
- (void)mouseDown:(NSEvent *)theEvent
{
if (!qt_button_down)
qt_button_down = qwidget;
qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, Qt::LeftButton);
// Don't call super here. This prevents us from getting the mouseUp event,
// which we need to send even if the mouseDown event was not accepted.
// (this is standard Qt behavior.)
}
- (void)mouseUp:(NSEvent *)theEvent
{
qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, Qt::LeftButton);
qt_button_down = 0;
}
- (void)rightMouseDown:(NSEvent *)theEvent
{
if (!qt_button_down)
qt_button_down = qwidget;
qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, Qt::RightButton);
}
- (void)rightMouseUp:(NSEvent *)theEvent
{
qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, Qt::RightButton);
qt_button_down = 0;
}
- (void)otherMouseDown:(NSEvent *)theEvent
{
if (!qt_button_down)
qt_button_down = qwidget;
Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]);
qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, mouseButton);
}
- (void)otherMouseUp:(NSEvent *)theEvent
{
Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]);
qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, mouseButton);
qt_button_down = 0;
}
- (void)mouseDragged:(NSEvent *)theEvent
{
qMacDnDParams()->view = self;
qMacDnDParams()->theEvent = theEvent;
qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton);
}
- (void)rightMouseDragged:(NSEvent *)theEvent
{
qMacDnDParams()->view = self;
qMacDnDParams()->theEvent = theEvent;
qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton);
}
- (void)otherMouseDragged:(NSEvent *)theEvent
{
qMacDnDParams()->view = self;
qMacDnDParams()->theEvent = theEvent;
qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton);
}
- (void)scrollWheel:(NSEvent *)theEvent
{
// Give the Input Manager a chance to process the wheel event.
NSInputManager *currentIManager = [NSInputManager currentInputManager];
if (currentIManager && [currentIManager wantsToHandleMouseEvents]) {
[currentIManager handleMouseEvent:theEvent];
}
NSPoint windowPoint = [theEvent locationInWindow];
NSPoint globalPoint = [[theEvent window] convertBaseToScreen:windowPoint];
NSPoint localPoint = [self convertPoint:windowPoint fromView:nil];
QPoint qlocal = QPoint(localPoint.x, localPoint.y);
QPoint qglobal = QPoint(globalPoint.x, flipYCoordinate(globalPoint.y));
Qt::MouseButtons buttons = QApplication::mouseButtons();
bool wheelOK = false;
Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]);
QWidget *widgetToGetMouse = qwidget;
// if popup is open it should get wheel events if the cursor is over the popup,
// otherwise the event should be ignored.
if (QWidget *popup = qAppInstance()->activePopupWidget()) {
if (!popup->geometry().contains(qglobal))
return;
}
int deltaX = 0;
int deltaY = 0;
int deltaZ = 0;
const EventRef carbonEvent = (EventRef)[theEvent eventRef];
const UInt32 carbonEventKind = carbonEvent ? ::GetEventKind(carbonEvent) : 0;
const bool scrollEvent = carbonEventKind == kEventMouseScroll;
if (scrollEvent) {
// The mouse device containts pixel scroll wheel support (Mighty Mouse, Trackpad).
// Since deviceDelta is delivered as pixels rather than degrees, we need to
// convert from pixels to degrees in a sensible manner.
// It looks like 1/4 degrees per pixel behaves most native.
// (NB: Qt expects the unit for delta to be 8 per degree):
const int pixelsToDegrees = 2; // 8 * 1/4
deltaX = [theEvent deviceDeltaX] * pixelsToDegrees;
deltaY = [theEvent deviceDeltaY] * pixelsToDegrees;
deltaZ = [theEvent deviceDeltaZ] * pixelsToDegrees;
} else {
// carbonEventKind == kEventMouseWheelMoved
// Remove acceleration, and use either -120 or 120 as delta:
deltaX = qBound(-120, int([theEvent deltaX] * 10000), 120);
deltaY = qBound(-120, int([theEvent deltaY] * 10000), 120);
deltaZ = qBound(-120, int([theEvent deltaZ] * 10000), 120);
}
#ifndef QT_NO_WHEELEVENT
if (deltaX != 0) {
QWheelEvent qwe(qlocal, qglobal, deltaX, buttons, keyMods, Qt::Horizontal);
qt_sendSpontaneousEvent(widgetToGetMouse, &qwe);
wheelOK = qwe.isAccepted();
if (!wheelOK && QApplicationPrivate::focus_widget
&& QApplicationPrivate::focus_widget != widgetToGetMouse) {
QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal,
deltaX, buttons, keyMods, Qt::Horizontal);
qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2);
wheelOK = qwe2.isAccepted();
}
}
if (deltaY) {
QWheelEvent qwe(qlocal, qglobal, deltaY, buttons, keyMods, Qt::Vertical);
qt_sendSpontaneousEvent(widgetToGetMouse, &qwe);
wheelOK = qwe.isAccepted();
if (!wheelOK && QApplicationPrivate::focus_widget
&& QApplicationPrivate::focus_widget != widgetToGetMouse) {
QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal,
deltaY, buttons, keyMods, Qt::Vertical);
qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2);
wheelOK = qwe2.isAccepted();
}
}
if (deltaZ) {
// Qt doesn't explicitly support wheels with a Z component. In a misguided attempt to
// try to be ahead of the pack, I'm adding this extra value.
QWheelEvent qwe(qlocal, qglobal, deltaZ, buttons, keyMods, (Qt::Orientation)3);
qt_sendSpontaneousEvent(widgetToGetMouse, &qwe);
wheelOK = qwe.isAccepted();
if (!wheelOK && QApplicationPrivate::focus_widget
&& QApplicationPrivate::focus_widget != widgetToGetMouse) {
QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal,
deltaZ, buttons, keyMods, (Qt::Orientation)3);
qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2);
wheelOK = qwe2.isAccepted();
}
}
#endif //QT_NO_WHEELEVENT
}
- (void)tabletProximity:(NSEvent *)tabletEvent
{
qt_dispatchTabletProximityEvent(tabletEvent);
}
- (void)tabletPoint:(NSEvent *)tabletEvent
{
if (!qt_mac_handleTabletEvent(self, tabletEvent))
[super tabletPoint:tabletEvent];
}
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
- (void)touchesBeganWithEvent:(NSEvent *)event;
{
bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all));
}
- (void)touchesMovedWithEvent:(NSEvent *)event;
{
bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all));
}
- (void)touchesEndedWithEvent:(NSEvent *)event;
{
bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all));
}
- (void)touchesCancelledWithEvent:(NSEvent *)event;
{
bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all));
}
#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
- (void)magnifyWithEvent:(NSEvent *)event;
{
if (!QApplicationPrivate::tryModalHelper(qwidget, 0))
return;
#ifndef QT_NO_GESTURES
QNativeGestureEvent qNGEvent;
qNGEvent.gestureType = QNativeGestureEvent::Zoom;
NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
qNGEvent.position = flipPoint(p).toPoint();
qNGEvent.percentage = [event magnification];
qt_sendSpontaneousEvent(qwidget, &qNGEvent);
#endif // QT_NO_GESTURES
}
- (void)rotateWithEvent:(NSEvent *)event;
{
if (!QApplicationPrivate::tryModalHelper(qwidget, 0))
return;
#ifndef QT_NO_GESTURES
QNativeGestureEvent qNGEvent;
qNGEvent.gestureType = QNativeGestureEvent::Rotate;
NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
qNGEvent.position = flipPoint(p).toPoint();
qNGEvent.percentage = -[event rotation];
qt_sendSpontaneousEvent(qwidget, &qNGEvent);
#endif // QT_NO_GESTURES
}
- (void)swipeWithEvent:(NSEvent *)event;
{
if (!QApplicationPrivate::tryModalHelper(qwidget, 0))
return;
#ifndef QT_NO_GESTURES
QNativeGestureEvent qNGEvent;
qNGEvent.gestureType = QNativeGestureEvent::Swipe;
NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
qNGEvent.position = flipPoint(p).toPoint();
if ([event deltaX] == 1)
qNGEvent.angle = 180.0f;
else if ([event deltaX] == -1)
qNGEvent.angle = 0.0f;
else if ([event deltaY] == 1)
qNGEvent.angle = 90.0f;
else if ([event deltaY] == -1)
qNGEvent.angle = 270.0f;
qt_sendSpontaneousEvent(qwidget, &qNGEvent);
#endif // QT_NO_GESTURES
}
- (void)beginGestureWithEvent:(NSEvent *)event;
{
if (!QApplicationPrivate::tryModalHelper(qwidget, 0))
return;
#ifndef QT_NO_GESTURES
QNativeGestureEvent qNGEvent;
qNGEvent.gestureType = QNativeGestureEvent::GestureBegin;
NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
qNGEvent.position = flipPoint(p).toPoint();
qt_sendSpontaneousEvent(qwidget, &qNGEvent);
#endif // QT_NO_GESTURES
}
- (void)endGestureWithEvent:(NSEvent *)event;
{
if (!QApplicationPrivate::tryModalHelper(qwidget, 0))
return;
#ifndef QT_NO_GESTURES
QNativeGestureEvent qNGEvent;
qNGEvent.gestureType = QNativeGestureEvent::GestureEnd;
NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
qNGEvent.position = flipPoint(p).toPoint();
qt_sendSpontaneousEvent(qwidget, &qNGEvent);
#endif // QT_NO_GESTURES
}
- (void)frameDidChange:(NSNotification *)note
{
Q_UNUSED(note);
if (!qwidget)
return;
if (qwidget->isWindow())
return;
NSRect newFrame = [self frame];
QRect newGeo(newFrame.origin.x, newFrame.origin.y, newFrame.size.width, newFrame.size.height);
bool moved = qwidget->testAttribute(Qt::WA_Moved);
bool resized = qwidget->testAttribute(Qt::WA_Resized);
qwidget->setGeometry(newGeo);
qwidget->setAttribute(Qt::WA_Moved, moved);
qwidget->setAttribute(Qt::WA_Resized, resized);
qwidgetprivate->syncCocoaMask();
}
- (BOOL)isEnabled
{
if (!qwidget)
return [super isEnabled];
return [super isEnabled] && qwidget->isEnabled();
}
- (void)setEnabled:(BOOL)flag
{
QMacCocoaAutoReleasePool pool;
[super setEnabled:flag];
if (qwidget && qwidget->isEnabled() != flag)
qwidget->setEnabled(flag);
}
+ (Class)cellClass
{
return [NSActionCell class];
}
- (BOOL)acceptsFirstResponder
{
if (!qwidget)
return NO;
// disabled widget shouldn't get focus even if it's a window.
// hence disabled windows will not get any key or mouse events.
if (!qwidget->isEnabled())
return NO;
// Before accepting the focus for a window, we check that
// the focusWidget (if any) is not contained in the same window.
if (qwidget->isWindow() && !qt_widget_private(qwidget)->topData()->embedded
&& (!qApp->focusWidget() || qApp->focusWidget()->window() != qwidget)) {
return YES; // Always do it, so that windows can accept key press events.
}
return qwidget->focusPolicy() != Qt::NoFocus;
}
- (BOOL)resignFirstResponder
{
if (!qwidget)
return YES;
// Seems like the following test only triggers if this
// view is inside a QMacNativeWidget:
if (qwidget == QApplication::focusWidget())
qwidget->clearFocus();
return YES;
}
- (BOOL)becomeFirstResponder
{
// see the comment in the acceptsFirstResponder - if the window "stole" focus
// let it become the responder, but don't tell Qt
if (qwidget && qt_widget_private(qwidget->window())->topData()->embedded
&& !QApplication::focusWidget() && qwidget->focusPolicy() != Qt::NoFocus)
qwidget->setFocus(Qt::OtherFocusReason);
return YES;
}
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
{
Q_UNUSED(isLocal);
return supportedActions;
}
- (void)setSupportedActions:(NSDragOperation)actions
{
supportedActions = actions;
}
- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
{
Q_UNUSED(anImage);
Q_UNUSED(aPoint);
qMacDnDParams()->performedAction = operation;
if (QDragManager::self()->object
&& QDragManager::self()->dragPrivate()->executed_action != Qt::ActionMask) {
qMacDnDParams()->performedAction =
qt_mac_mapDropAction(QDragManager::self()->dragPrivate()->executed_action);
}
}
- (QWidget *)qt_qwidget
{
return qwidget;
}
- (void) qt_clearQWidget
{
qwidget = 0;
qwidgetprivate = 0;
}
- (BOOL)qt_leftButtonIsRightButton
{
return leftButtonIsRightButton;
}
- (void)qt_setLeftButtonIsRightButton:(BOOL)isSwapped
{
leftButtonIsRightButton = isSwapped;
}
+ (DnDParams*)currentMouseEvent
{
return qMacDnDParams();
}
- (void)keyDown:(NSEvent *)theEvent
{
sendKeyEvents = true;
QWidget *widgetToGetKey = qwidget;
QWidget *popup = qAppInstance()->activePopupWidget();
bool sendToPopup = false;
if (popup && popup != qwidget->window()) {
widgetToGetKey = popup->focusWidget() ? popup->focusWidget() : popup;
sendToPopup = true;
}
if (widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled)
&& !(widgetToGetKey->inputMethodHints() & Qt::ImhDigitsOnly
|| widgetToGetKey->inputMethodHints() & Qt::ImhFormattedNumbersOnly
|| widgetToGetKey->inputMethodHints() & Qt::ImhHiddenText)) {
[qt_mac_nativeview_for(widgetToGetKey) interpretKeyEvents:[NSArray arrayWithObject: theEvent]];
}
if (sendKeyEvents && !composing) {
bool keyOK = qt_dispatchKeyEvent(theEvent, widgetToGetKey);
if (!keyOK && !sendToPopup) {
// find the first responder that is not created by Qt and forward
// the event to it (for example if Qt widget is embedded into native).
QWidget *toplevel = qwidget->window();
if (toplevel && qt_widget_private(toplevel)->topData()->embedded) {
if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview])
[w keyDown:theEvent];
}
}
}
}
- (void)keyUp:(NSEvent *)theEvent
{
if (sendKeyEvents) {
bool keyOK = qt_dispatchKeyEvent(theEvent, qwidget);
if (!keyOK) {
QWidget *toplevel = qwidget->window();
if (toplevel && qt_widget_private(toplevel)->topData()->embedded) {
if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview])
[w keyUp:theEvent];
}
}
}
}
- (void)viewWillMoveToWindow:(NSWindow *)window
{
if (qwidget == 0)
return;
if (qwidget->windowFlags() & Qt::MSWindowsOwnDC
&& (window != [self window])) { // OpenGL Widget
QEvent event(QEvent::MacGLClearDrawable);
qApp->sendEvent(qwidget, &event);
}
}
- (void)viewDidMoveToWindow
{
if (qwidget == 0)
return;
if (qwidget->windowFlags() & Qt::MSWindowsOwnDC && [self window]) {
// call update paint event
qwidgetprivate->needWindowChange = true;
QEvent event(QEvent::MacGLWindowChange);
qApp->sendEvent(qwidget, &event);
}
}
// NSTextInput Protocol implementation
- (void) insertText:(id)aString
{
QString commitText;
if ([aString length]) {
if ([aString isKindOfClass:[NSAttributedString class]]) {
commitText = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
} else {
commitText = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
};
}
if ([aString length] && composing) {
// Send the commit string to the widget.
composing = false;
sendKeyEvents = false;
QInputMethodEvent e;
e.setCommitString(commitText);
qt_sendSpontaneousEvent(qwidget, &e);
} else {
// The key sequence "`q" on a French Keyboard will generate two calls to insertText before
// it returns from interpretKeyEvents. The first call will turn off 'composing' and accept
// the "`" key. The last keyDown event needs to be processed by the widget to get the
// character "q". The string parameter is ignored for the second call.
sendKeyEvents = true;
}
composingText->clear();
}
- (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
{
// Generate the QInputMethodEvent with preedit string and the attributes
// for rendering it. The attributes handled here are 'underline',
// 'underline color' and 'cursor position'.
sendKeyEvents = false;
composing = true;
QString qtText;
// Cursor position is retrived from the range.
QList<QInputMethodEvent::Attribute> attrs;
attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selRange.location, 1, QVariant());
if ([aString isKindOfClass:[NSAttributedString class]]) {
qtText = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
composingLength = qtText.length();
int index = 0;
// Create attributes for individual sections of preedit text
while (index < composingLength) {
NSRange effectiveRange;
NSRange range = NSMakeRange(index, composingLength-index);
NSDictionary *attributes = [aString attributesAtIndex:index
longestEffectiveRange:&effectiveRange
inRange:range];
NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName];
if (underlineStyle) {
QColor clr (Qt::black);
NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName];
if (color) {
clr = colorFrom(color);
}
QTextCharFormat format;
format.setFontUnderline(true);
format.setUnderlineColor(clr);
attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
effectiveRange.location,
effectiveRange.length,
format);
}
index = effectiveRange.location + effectiveRange.length;
}
} else {
// No attributes specified, take only the preedit text.
qtText = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
composingLength = qtText.length();
}
// Make sure that we have at least one text format.
if (attrs.size() <= 1) {
QTextCharFormat format;
format.setFontUnderline(true);
attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
0, composingLength, format);
}
*composingText = qtText;
QInputMethodEvent e(qtText, attrs);
qt_sendSpontaneousEvent(qwidget, &e);
if (!composingLength)
composing = false;
}
- (void) unmarkText
{
if (composing) {
QInputMethodEvent e;
e.setCommitString(*composingText);
qt_sendSpontaneousEvent(qwidget, &e);
}
composingText->clear();
composing = false;
}
- (BOOL) hasMarkedText
{
return (composing ? YES: NO);
}
- (void) doCommandBySelector:(SEL)aSelector
{
Q_UNUSED(aSelector);
}
- (BOOL)isComposing
{
return composing;
}
- (NSInteger) conversationIdentifier
{
// Return a unique identifier fot this ime conversation
return (NSInteger)self;
}
- (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
{
QString selectedText(qwidget->inputMethodQuery(Qt::ImCurrentSelection).toString());
if (!selectedText.isEmpty()) {
QCFString string(selectedText.mid(theRange.location, theRange.length));
const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string);
return [[[NSAttributedString alloc] initWithString:tmpString] autorelease];
} else {
return nil;
}
}
- (NSRange) markedRange
{
NSRange range;
if (composing) {
range.location = 0;
range.length = composingLength;
} else {
range.location = NSNotFound;
range.length = 0;
}
return range;
}
- (NSRange) selectedRange
{
NSRange selRange;
QString selectedText(qwidget->inputMethodQuery(Qt::ImCurrentSelection).toString());
if (!selectedText.isEmpty()) {
// Consider only the selected text.
selRange.location = 0;
selRange.length = selectedText.length();
} else {
// No selected text.
selRange.location = NSNotFound;
selRange.length = 0;
}
return selRange;
}
- (NSRect) firstRectForCharacterRange:(NSRange)theRange
{
Q_UNUSED(theRange);
// The returned rect is always based on the internal cursor.
QRect mr(qwidget->inputMethodQuery(Qt::ImMicroFocus).toRect());
QPoint mp(qwidget->mapToGlobal(QPoint(mr.bottomLeft())));
NSRect rect ;
rect.origin.x = mp.x();
rect.origin.y = flipYCoordinate(mp.y());
rect.size.width = mr.width();
rect.size.height = mr.height();
return rect;
}
- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
{
// We dont support cursor movements using mouse while composing.
Q_UNUSED(thePoint);
return NSNotFound;
}
- (NSArray*) validAttributesForMarkedText
{
if (qwidget == 0)
return nil;
if (!qwidget->testAttribute(Qt::WA_InputMethodEnabled))
return nil; // Not sure if that's correct, but it's saves a malloc.
// Support only underline color/style.
return [NSArray arrayWithObjects:NSUnderlineColorAttributeName,
NSUnderlineStyleAttributeName, nil];
}
@end
QT_BEGIN_NAMESPACE
void QMacInputContext::reset()
{
QWidget *w = QInputContext::focusWidget();
if (w) {
NSView *view = qt_mac_nativeview_for(w);
if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) {
QMacCocoaAutoReleasePool pool;
QT_MANGLE_NAMESPACE(QCocoaView) *qc = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view);
NSInputManager *currentIManager = [NSInputManager currentInputManager];
if (currentIManager) {
[currentIManager markedTextAbandoned:view];
[qc unmarkText];
}
}
}
}
bool QMacInputContext::isComposing() const
{
QWidget *w = QInputContext::focusWidget();
if (w) {
NSView *view = qt_mac_nativeview_for(w);
if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) {
return [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view) isComposing];
}
}
return false;
}
extern bool qt_mac_in_drag;
void * /*NSImage */qt_mac_create_nsimage(const QPixmap &pm);
static const int default_pm_hotx = -2;
static const int default_pm_hoty = -16;
static const char* default_pm[] = {
"13 9 3 1",
". c None",
" c #000000",
"X c #FFFFFF",
"X X X X X X X",
" X X X X X X ",
"X ......... X",
" X.........X ",
"X ......... X",
" X.........X ",
"X ......... X",
" X X X X X X ",
"X X X X X X X",
};
Qt::DropAction QDragManager::drag(QDrag *o)
{
if(qt_mac_in_drag) { //just make sure..
qWarning("Qt: Internal error: WH0A, unexpected condition reached");
return Qt::IgnoreAction;
}
if(object == o)
return Qt::IgnoreAction;
/* At the moment it seems clear that Mac OS X does not want to drag with a non-left button
so we just bail early to prevent it */
if(!(GetCurrentEventButtonState() & kEventMouseButtonPrimary))
return Qt::IgnoreAction;
if(object) {
dragPrivate()->source->removeEventFilter(this);
cancel();
beingCancelled = false;
}
object = o;
dragPrivate()->target = 0;
#ifndef QT_NO_ACCESSIBILITY
QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart);
#endif
// setup the data
QMacPasteboard dragBoard((CFStringRef) NSDragPboard, QMacPasteboardMime::MIME_DND);
dragPrivate()->data->setData(QLatin1String("application/x-qt-mime-type-name"), QByteArray("dummy"));
dragBoard.setMimeData(dragPrivate()->data);
// create the image
QPoint hotspot;
QPixmap pix = dragPrivate()->pixmap;
if(pix.isNull()) {
if(dragPrivate()->data->hasText() || dragPrivate()->data->hasUrls()) {
// get the string
QString s = dragPrivate()->data->hasText() ? dragPrivate()->data->text()
: dragPrivate()->data->urls().first().toString();
if(s.length() > 26)
s = s.left(23) + QChar(0x2026);
if(!s.isEmpty()) {
// draw it
QFont f(qApp->font());
f.setPointSize(12);
QFontMetrics fm(f);
QPixmap tmp(fm.width(s), fm.height());
if(!tmp.isNull()) {
QPainter p(&tmp);
p.fillRect(0, 0, tmp.width(), tmp.height(), Qt::color0);
p.setPen(Qt::color1);
p.setFont(f);
p.drawText(0, fm.ascent(), s);
// save it
pix = tmp;
hotspot = QPoint(tmp.width() / 2, tmp.height() / 2);
}
}
} else {
pix = QPixmap(default_pm);
hotspot = QPoint(default_pm_hotx, default_pm_hoty);
}
} else {
hotspot = dragPrivate()->hotspot;
}
// convert the image to NSImage.
NSImage *image = (NSImage *)qt_mac_create_nsimage(pix);
[image retain];
DnDParams dndParams = *[QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent];
// save supported actions
[dndParams.view setSupportedActions: qt_mac_mapDropActions(dragPrivate()->possible_actions)];
NSPoint imageLoc = {dndParams.localPoint.x - hotspot.x(),
dndParams.localPoint.y + pix.height() - hotspot.y()};
NSSize mouseOffset = {0.0, 0.0};
NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
NSPoint windowPoint = [dndParams.theEvent locationInWindow];
dragPrivate()->executed_action = Qt::ActionMask;
// do the drag
[dndParams.view retain];
[dndParams.view dragImage:image
at:imageLoc
offset:mouseOffset
event:dndParams.theEvent
pasteboard:pboard
source:dndParams.view
slideBack:YES];
// reset the implicit grab widget when drag ends because we will not
// receive the mouse release event when DND is active.
qt_button_down = 0;
[dndParams.view release];
[image release];
if (dragPrivate())
dragPrivate()->executed_action = Qt::IgnoreAction;
object = 0;
Qt::DropAction performedAction(qt_mac_mapNSDragOperation(qMacDnDParams()->performedAction));
// do post drag processing, if required.
if(performedAction != Qt::IgnoreAction) {
// check if the receiver points us to a file location.
// if so, we need to do the file copy/move ourselves.
QCFType<CFURLRef> pasteLocation = 0;
PasteboardCopyPasteLocation(dragBoard.pasteBoard(), &pasteLocation);
if (pasteLocation) {
QList<QUrl> urls = o->mimeData()->urls();
for (int i = 0; i < urls.size(); ++i) {
QUrl fromUrl = urls.at(i);
QString filename = QFileInfo(fromUrl.path()).fileName();
QUrl toUrl(QCFString::toQString(CFURLGetString(pasteLocation)) + filename);
if (performedAction == Qt::MoveAction)
QFile::rename(fromUrl.path(), toUrl.path());
else if (performedAction == Qt::CopyAction)
QFile::copy(fromUrl.path(), toUrl.path());
}
}
}
o->setMimeData(0);
o->deleteLater();
return performedAction;
}
QT_END_NAMESPACE
#endif // QT_MAC_USE_COCOA