source: trunk/src/gui/kernel/qcocoaview_mac.mm@ 858

Last change on this file since 858 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 58.5 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#import <private/qcocoaview_mac_p.h>
43#ifdef QT_MAC_USE_COCOA
44
45#include <private/qwidget_p.h>
46#include <private/qt_mac_p.h>
47#include <private/qapplication_p.h>
48#include <private/qabstractscrollarea_p.h>
49#include <private/qt_cocoa_helpers_mac_p.h>
50#include <private/qdnd_p.h>
51#include <private/qmacinputcontext_p.h>
52#include <private/qmultitouch_mac_p.h>
53#include <private/qevent_p.h>
54#include <private/qbackingstore_p.h>
55
56#include <qscrollarea.h>
57#include <qhash.h>
58#include <qtextformat.h>
59#include <qpaintengine.h>
60#include <QUrl>
61#include <QAccessible>
62#include <QFileInfo>
63#include <QFile>
64
65#include <qdebug.h>
66
67@interface NSEvent (Qt_Compile_Leopard_DeviceDelta)
68 - (CGFloat)deviceDeltaX;
69 - (CGFloat)deviceDeltaY;
70 - (CGFloat)deviceDeltaZ;
71@end
72
73@interface NSEvent (Qt_Compile_Leopard_Gestures)
74 - (CGFloat)magnification;
75@end
76
77QT_BEGIN_NAMESPACE
78
79Q_GLOBAL_STATIC(DnDParams, qMacDnDParams);
80
81extern void qt_mac_update_cursor_at_global_pos(const QPoint &globalPos); // qcursor_mac.mm
82extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp
83extern OSViewRef qt_mac_nativeview_for(const QWidget *w); // qwidget_mac.mm
84extern QPointer<QWidget> qt_mouseover; //qapplication_mac.mm
85extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
86extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum);
87
88struct dndenum_mapper
89{
90 NSDragOperation mac_code;
91 Qt::DropAction qt_code;
92 bool Qt2Mac;
93};
94
95static dndenum_mapper dnd_enums[] = {
96 { NSDragOperationLink, Qt::LinkAction, true },
97 { NSDragOperationMove, Qt::MoveAction, true },
98 { NSDragOperationCopy, Qt::CopyAction, true },
99 { NSDragOperationGeneric, Qt::CopyAction, false },
100 { NSDragOperationEvery, Qt::ActionMask, false },
101 { NSDragOperationNone, Qt::IgnoreAction, false }
102};
103
104static NSDragOperation qt_mac_mapDropAction(Qt::DropAction action)
105{
106 for (int i=0; dnd_enums[i].qt_code; i++) {
107 if (dnd_enums[i].Qt2Mac && (action & dnd_enums[i].qt_code)) {
108 return dnd_enums[i].mac_code;
109 }
110 }
111 return NSDragOperationNone;
112}
113
114static NSDragOperation qt_mac_mapDropActions(Qt::DropActions actions)
115{
116 NSDragOperation nsActions = NSDragOperationNone;
117 for (int i=0; dnd_enums[i].qt_code; i++) {
118 if (dnd_enums[i].Qt2Mac && (actions & dnd_enums[i].qt_code))
119 nsActions |= dnd_enums[i].mac_code;
120 }
121 return nsActions;
122}
123
124static Qt::DropAction qt_mac_mapNSDragOperation(NSDragOperation nsActions)
125{
126 Qt::DropAction action = Qt::IgnoreAction;
127 for (int i=0; dnd_enums[i].mac_code; i++) {
128 if (nsActions & dnd_enums[i].mac_code)
129 return dnd_enums[i].qt_code;
130 }
131 return action;
132}
133
134static Qt::DropActions qt_mac_mapNSDragOperations(NSDragOperation nsActions)
135{
136 Qt::DropActions actions = Qt::IgnoreAction;
137 for (int i=0; dnd_enums[i].mac_code; i++) {
138 if (nsActions & dnd_enums[i].mac_code)
139 actions |= dnd_enums[i].qt_code;
140 }
141 return actions;
142}
143
144static QColor colorFrom(NSColor *color)
145{
146 QColor qtColor;
147 NSString *colorSpace = [color colorSpaceName];
148 if (colorSpace == NSDeviceCMYKColorSpace) {
149 CGFloat cyan, magenta, yellow, black, alpha;
150 [color getCyan:&cyan magenta:&magenta yellow:&yellow black:&black alpha:&alpha];
151 qtColor.setCmykF(cyan, magenta, yellow, black, alpha);
152 } else {
153 NSColor *tmpColor;
154 tmpColor = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
155 CGFloat red, green, blue, alpha;
156 [tmpColor getRed:&red green:&green blue:&blue alpha:&alpha];
157 qtColor.setRgbF(red, green, blue, alpha);
158 }
159 return qtColor;
160}
161
162QT_END_NAMESPACE
163
164QT_FORWARD_DECLARE_CLASS(QMacCocoaAutoReleasePool)
165QT_FORWARD_DECLARE_CLASS(QCFString)
166QT_FORWARD_DECLARE_CLASS(QDragManager)
167QT_FORWARD_DECLARE_CLASS(QMimeData)
168QT_FORWARD_DECLARE_CLASS(QPoint)
169QT_FORWARD_DECLARE_CLASS(QApplication)
170QT_FORWARD_DECLARE_CLASS(QApplicationPrivate)
171QT_FORWARD_DECLARE_CLASS(QDragEnterEvent)
172QT_FORWARD_DECLARE_CLASS(QDragMoveEvent)
173QT_FORWARD_DECLARE_CLASS(QStringList)
174QT_FORWARD_DECLARE_CLASS(QString)
175QT_FORWARD_DECLARE_CLASS(QRect)
176QT_FORWARD_DECLARE_CLASS(QRegion)
177QT_FORWARD_DECLARE_CLASS(QAbstractScrollArea)
178QT_FORWARD_DECLARE_CLASS(QAbstractScrollAreaPrivate)
179QT_FORWARD_DECLARE_CLASS(QPaintEvent)
180QT_FORWARD_DECLARE_CLASS(QPainter)
181QT_FORWARD_DECLARE_CLASS(QHoverEvent)
182QT_FORWARD_DECLARE_CLASS(QCursor)
183QT_USE_NAMESPACE
184extern "C" {
185 extern NSString *NSTextInputReplacementRangeAttributeName;
186}
187
188#ifdef ALIEN_DEBUG
189static int qCocoaViewCount = 0;
190#endif
191
192@implementation QT_MANGLE_NAMESPACE(QCocoaView)
193
194- (id)initWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate
195{
196 self = [super init];
197 if (self) {
198 [self finishInitWithQWidget:widget widgetPrivate:widgetprivate];
199 }
200 [self setFocusRingType:NSFocusRingTypeNone];
201 composingText = new QString();
202
203#ifdef ALIEN_DEBUG
204 ++qCocoaViewCount;
205 qDebug() << "init: qCocoaViewCount is" << qCocoaViewCount;
206#endif
207
208 composing = false;
209 sendKeyEvents = true;
210 [self setHidden:YES];
211 return self;
212}
213
214- (void) finishInitWithQWidget:(QWidget *)widget widgetPrivate:(QWidgetPrivate *)widgetprivate
215{
216 qwidget = widget;
217 qwidgetprivate = widgetprivate;
218 [[NSNotificationCenter defaultCenter] addObserver:self
219 selector:@selector(frameDidChange:)
220 name:@"NSViewFrameDidChangeNotification"
221 object:self];
222}
223
224- (void)resetCursorRects
225{
226 // [NSView addCursorRect] is slow, so bail out early if we can:
227 if (NSIsEmptyRect([self visibleRect]))
228 return;
229
230 QWidget *cursorWidget = qwidget;
231
232 if (cursorWidget->testAttribute(Qt::WA_TransparentForMouseEvents))
233 cursorWidget = QApplication::widgetAt(qwidget->mapToGlobal(qwidget->rect().center()));
234
235 if (cursorWidget == 0)
236 return;
237
238 if (!cursorWidget->testAttribute(Qt::WA_SetCursor)) {
239 [super resetCursorRects];
240 return;
241 }
242
243 QRegion mask = qt_widget_private(cursorWidget)->extra->mask;
244 NSCursor *nscursor = static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursorWidget->cursor()));
245 // The mask could have the WA_MouseNoMask attribute set and that means that we have to ignore the mask.
246 if (mask.isEmpty() || cursorWidget->testAttribute(Qt::WA_MouseNoMask)) {
247 [self addCursorRect:[qt_mac_nativeview_for(cursorWidget) visibleRect] cursor:nscursor];
248 } else {
249 const QVector<QRect> &rects = mask.rects();
250 for (int i = 0; i < rects.size(); ++i) {
251 const QRect &rect = rects.at(i);
252 [self addCursorRect:NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height()) cursor:nscursor];
253 }
254 }
255}
256
257- (void)removeDropData
258{
259 if (dropData) {
260 delete dropData;
261 dropData = 0;
262 }
263}
264
265- (void)addDropData:(id <NSDraggingInfo>)sender
266{
267 [self removeDropData];
268 CFStringRef dropPasteboard = (CFStringRef) [[sender draggingPasteboard] name];
269 dropData = new QCocoaDropData(dropPasteboard);
270}
271
272- (void)changeDraggingCursor:(NSDragOperation)newOperation
273{
274 static SEL action = nil;
275 static bool operationSupported = false;
276 if (action == nil) {
277 action = NSSelectorFromString(@"operationNotAllowedCursor");
278 if ([NSCursor respondsToSelector:action]) {
279 operationSupported = true;
280 }
281 }
282 if (operationSupported) {
283 NSCursor *notAllowedCursor = [NSCursor performSelector:action];
284 bool isNotAllowedCursor = ([NSCursor currentCursor] == notAllowedCursor);
285 if (newOperation == NSDragOperationNone && !isNotAllowedCursor) {
286 [notAllowedCursor push];
287 } else if (newOperation != NSDragOperationNone && isNotAllowedCursor) {
288 [notAllowedCursor pop];
289 }
290
291 }
292}
293
294- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
295{
296 // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly
297 // from Cocoa. They modify the drag target, and might fake enter/leave events.
298 NSPoint windowPoint = [sender draggingLocation];
299 dragEnterSequence = [sender draggingSequenceNumber];
300 [self addDropData:sender];
301 QMimeData *mimeData = dropData;
302 if (QDragManager::self()->source())
303 mimeData = QDragManager::self()->dragPrivate()->data;
304 NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint];
305 NSPoint localPoint = [self convertPoint:windowPoint fromView:nil];
306 QPoint posDrag(localPoint.x, localPoint.y);
307 NSDragOperation nsActions = [sender draggingSourceOperationMask];
308 Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions);
309 QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions;
310 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
311 if ([sender draggingSource] != nil) {
312 // modifier flags might have changed, update it here since we don't send any input events.
313 QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]);
314 modifiers = QApplication::keyboardModifiers();
315 } else {
316 // when the source is from another application the above technique will not work.
317 modifiers = qt_cocoaDragOperation2QtModifiers(nsActions);
318 }
319 // send the drag enter event to the widget.
320 QDragEnterEvent qDEEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers);
321 QApplication::sendEvent(qwidget, &qDEEvent);
322 if (!qDEEvent.isAccepted()) {
323 // widget is not interested in this drag, so ignore this drop data.
324 [self removeDropData];
325 [self changeDraggingCursor:NSDragOperationNone];
326 return NSDragOperationNone;
327 } else {
328 // save the mouse position, used by draggingExited handler.
329 DnDParams *dndParams = [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent];
330 dndParams->activeDragEnterPos = windowPoint;
331 // send a drag move event immediately after a drag enter event (as per documentation).
332 QDragMoveEvent qDMEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers);
333 qDMEvent.setDropAction(qDEEvent.dropAction());
334 qDMEvent.accept(); // accept by default, since enter event was accepted.
335 QApplication::sendEvent(qwidget, &qDMEvent);
336 if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) {
337 // since we accepted the drag enter event, the widget expects
338 // future drage move events.
339 // ### check if we need to treat this like the drag enter event.
340 nsActions = NSDragOperationNone;
341 // Save as ignored in the answer rect.
342 qDMEvent.setDropAction(Qt::IgnoreAction);
343 } else {
344 nsActions = QT_PREPEND_NAMESPACE(qt_mac_mapDropAction)(qDMEvent.dropAction());
345 }
346 QT_PREPEND_NAMESPACE(qt_mac_copy_answer_rect)(qDMEvent);
347 [self changeDraggingCursor:nsActions];
348 return nsActions;
349 }
350 }
351- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender
352{
353 // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly
354 // from Cocoa. They modify the drag target, and might fake enter/leave events.
355 NSPoint windowPoint = [sender draggingLocation];
356 // in cases like QFocusFrame, the view under the mouse might
357 // not have received the drag enter. Generate a synthetic
358 // drag enter event for that view.
359 if (dragEnterSequence != [sender draggingSequenceNumber])
360 [self draggingEntered:sender];
361 // drag enter event was rejected, so ignore the move event.
362 if (dropData == 0) {
363 [self changeDraggingCursor:NSDragOperationNone];
364 return NSDragOperationNone;
365 }
366 // return last value, if we are still in the answerRect.
367 NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint];
368 NSPoint localPoint = [self convertPoint:windowPoint fromView:nil];
369 NSDragOperation nsActions = [sender draggingSourceOperationMask];
370 QPoint posDrag(localPoint.x, localPoint.y);
371 if (qt_mac_mouse_inside_answer_rect(posDrag)
372 && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) == nsActions) {
373 NSDragOperation operation = QT_PREPEND_NAMESPACE(qt_mac_mapDropActions)(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastAction));
374 [self changeDraggingCursor:operation];
375 return operation;
376 }
377 // send drag move event to the widget
378 QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec.lastOperation) = nsActions;
379 Qt::DropActions qtAllowed = QT_PREPEND_NAMESPACE(qt_mac_mapNSDragOperations)(nsActions);
380 Qt::KeyboardModifiers modifiers = Qt::NoModifier;
381 if ([sender draggingSource] != nil) {
382 // modifier flags might have changed, update it here since we don't send any input events.
383 QApplicationPrivate::modifier_buttons = qt_cocoaModifiers2QtModifiers([[NSApp currentEvent] modifierFlags]);
384 modifiers = QApplication::keyboardModifiers();
385 } else {
386 // when the source is from another application the above technique will not work.
387 modifiers = qt_cocoaDragOperation2QtModifiers(nsActions);
388 }
389 QMimeData *mimeData = dropData;
390 if (QDragManager::self()->source())
391 mimeData = QDragManager::self()->dragPrivate()->data;
392 QDragMoveEvent qDMEvent(posDrag, qtAllowed, mimeData, QApplication::mouseButtons(), modifiers);
393 if (QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction != Qt::IgnoreAction
394 && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).buttons == qDMEvent.mouseButtons()
395 && QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).modifiers == qDMEvent.keyboardModifiers())
396 qDMEvent.setDropAction(QT_PREPEND_NAMESPACE(qt_mac_dnd_answer_rec).lastAction);
397 qDMEvent.accept();
398 QApplication::sendEvent(qwidget, &qDMEvent);
399
400 NSDragOperation operation = qt_mac_mapDropAction(qDMEvent.dropAction());
401 if (!qDMEvent.isAccepted() || qDMEvent.dropAction() == Qt::IgnoreAction) {
402 // ignore this event (we will still receive further notifications)
403 operation = NSDragOperationNone;
404 // Save as ignored in the answer rect.
405 qDMEvent.setDropAction(Qt::IgnoreAction);
406 }
407 qt_mac_copy_answer_rect(qDMEvent);
408 [self changeDraggingCursor:operation];
409 return operation;
410}
411
412- (void)draggingExited:(id < NSDraggingInfo >)sender
413{
414 // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly
415 // from Cocoa. They modify the drag target, and might fake enter/leave events.
416 Q_UNUSED(sender);
417 dragEnterSequence = -1;
418 // drag enter event was rejected, so ignore the move event.
419 if (dropData) {
420 QDragLeaveEvent de;
421 QApplication::sendEvent(qwidget, &de);
422 [self removeDropData];
423 }
424 [self changeDraggingCursor:NSDragOperationEvery];
425
426}
427
428- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
429{
430 // NB: This function is called from QCoocaWindow/QCocoaPanel rather than directly
431 // from Cocoa. They modify the drag target, and might fake enter/leave events.
432 NSPoint windowPoint = [sender draggingLocation];
433 dragEnterSequence = -1;
434 [self addDropData:sender];
435
436 NSPoint globalPoint = [[sender draggingDestinationWindow] convertBaseToScreen:windowPoint];
437 NSPoint localPoint = [self convertPoint:windowPoint fromView:nil];
438 QPoint posDrop(localPoint.x, localPoint.y);
439
440 NSDragOperation nsActions = [sender draggingSourceOperationMask];
441 Qt::DropActions qtAllowed = qt_mac_mapNSDragOperations(nsActions);
442 QMimeData *mimeData = dropData;
443 if (QDragManager::self()->source())
444 mimeData = QDragManager::self()->dragPrivate()->data;
445 // send the drop event to the widget.
446 QDropEvent de(posDrop, qtAllowed, mimeData,
447 QApplication::mouseButtons(), QApplication::keyboardModifiers());
448 if (QDragManager::self()->object)
449 QDragManager::self()->dragPrivate()->target = qwidget;
450 QApplication::sendEvent(qwidget, &de);
451 if (QDragManager::self()->object)
452 QDragManager::self()->dragPrivate()->executed_action = de.dropAction();
453 if (!de.isAccepted())
454 return NO;
455 else
456 return YES;
457}
458
459- (void)dealloc
460{
461 delete composingText;
462 [[NSNotificationCenter defaultCenter] removeObserver:self];
463
464#ifdef ALIEN_DEBUG
465 --qCocoaViewCount;
466 qDebug() << "qCocoaViewCount is" << qCocoaViewCount;
467#endif
468
469 [super dealloc];
470}
471
472- (BOOL)isOpaque;
473{
474 if (!qwidgetprivate)
475 return [super isOpaque];
476 return qwidgetprivate->isOpaque;
477}
478
479- (BOOL)isFlipped;
480{
481 return YES;
482}
483
484// We preserve the content of the view if WA_StaticContents is defined.
485//
486// More info in the Cocoa documentation:
487// http://developer.apple.com/mac/library/documentation/cocoa/conceptual/CocoaViewsGuide/Optimizing/Optimizing.html
488- (BOOL) preservesContentDuringLiveResize
489{
490 return qwidget->testAttribute(Qt::WA_StaticContents);
491}
492
493- (void) setFrameSize:(NSSize)newSize
494{
495 [super setFrameSize:newSize];
496
497 // A change in size has required the view to be invalidated.
498 if ([self inLiveResize]) {
499 NSRect rects[4];
500 NSInteger count;
501 [self getRectsExposedDuringLiveResize:rects count:&count];
502 while (count-- > 0)
503 {
504 [self setNeedsDisplayInRect:rects[count]];
505 }
506 } else {
507 [self setNeedsDisplay:YES];
508 }
509
510 // Make sure the opengl context is updated on resize.
511 if (qwidgetprivate && qwidgetprivate->isGLWidget) {
512 qwidgetprivate->needWindowChange = true;
513 QEvent event(QEvent::MacGLWindowChange);
514 qApp->sendEvent(qwidget, &event);
515 }
516}
517
518// We catch the 'setNeedsDisplay:' message in order to avoid a useless full repaint.
519// During the resize, the top of the widget is repainted, probably because of the
520// change of coordinate space (Quartz vs Qt). This is then followed by this message:
521// -[NSView _setNeedsDisplayIfTopLeftChanged]
522// which force a full repaint by sending the message 'setNeedsDisplay:'.
523// That is what we are preventing here.
524- (void)setNeedsDisplay:(BOOL)flag {
525 if (![self inLiveResize] || !(qwidget->testAttribute(Qt::WA_StaticContents))) {
526 [super setNeedsDisplay:flag];
527 }
528}
529
530- (void)drawRect:(NSRect)aRect
531{
532 if (!qwidget)
533 return;
534
535 if (QApplicationPrivate::graphicsSystem() != 0) {
536 if (qwidgetprivate->maybeBackingStore()) {
537 // Drawing is handled on the window level
538 // See qcocoasharedwindowmethods_mac_p.h
539 if (!qwidget->testAttribute(Qt::WA_PaintOnScreen))
540 return;
541 }
542 }
543 CGContextRef cg = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
544 qwidgetprivate->hd = cg;
545 CGContextSaveGState(cg);
546
547 if (qwidget->isVisible() && qwidget->updatesEnabled()) { //process the actual paint event.
548 if (qwidget->testAttribute(Qt::WA_WState_InPaintEvent))
549 qWarning("QWidget::repaint: Recursive repaint detected");
550
551 const QRect qrect = QRect(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height);
552 QRegion qrgn;
553
554 const NSRect *rects;
555 NSInteger count;
556 [self getRectsBeingDrawn:&rects count:&count];
557 for (int i = 0; i < count; ++i) {
558 QRect tmpRect = QRect(rects[i].origin.x, rects[i].origin.y, rects[i].size.width, rects[i].size.height);
559 qrgn += tmpRect;
560 }
561
562 if (!qwidget->isWindow() && !qobject_cast<QAbstractScrollArea *>(qwidget->parent())) {
563 const QRegion &parentMask = qwidget->window()->mask();
564 if (!parentMask.isEmpty()) {
565 const QPoint mappedPoint = qwidget->mapTo(qwidget->window(), qrect.topLeft());
566 qrgn.translate(mappedPoint);
567 qrgn &= parentMask;
568 qrgn.translate(-mappedPoint.x(), -mappedPoint.y());
569 }
570 }
571
572 QPoint redirectionOffset(0, 0);
573 //setup the context
574 qwidget->setAttribute(Qt::WA_WState_InPaintEvent);
575 QPaintEngine *engine = qwidget->paintEngine();
576 if (engine)
577 engine->setSystemClip(qrgn);
578 if (qwidgetprivate->extra && qwidgetprivate->extra->hasMask) {
579 CGRect widgetRect = CGRectMake(0, 0, qwidget->width(), qwidget->height());
580 CGContextTranslateCTM (cg, 0, widgetRect.size.height);
581 CGContextScaleCTM(cg, 1, -1);
582 if (qwidget->isWindow())
583 CGContextClearRect(cg, widgetRect);
584 CGContextClipToMask(cg, widgetRect, qwidgetprivate->extra->imageMask);
585 CGContextScaleCTM(cg, 1, -1);
586 CGContextTranslateCTM (cg, 0, -widgetRect.size.height);
587 }
588
589 if (qwidget->isWindow() && !qwidgetprivate->isOpaque
590 && !qwidget->testAttribute(Qt::WA_MacBrushedMetal)) {
591 CGContextClearRect(cg, NSRectToCGRect(aRect));
592 }
593
594 // Check for alien widgets, use qwidgetPrivate->drawWidget() to draw the widget if this
595 // is the case. This makes sure child widgets are drawn as well, Cocoa does not know about
596 // those and wont send them drawRect calls.
597 if (qwidget->testAttribute(Qt::WA_NativeWindow) && qt_widget_private(qwidget)->hasAlienChildren == false) {
598 if (engine && !qwidget->testAttribute(Qt::WA_NoSystemBackground)
599 && (qwidget->isWindow() || qwidget->autoFillBackground())
600 || qwidget->testAttribute(Qt::WA_TintedBackground)
601 || qwidget->testAttribute(Qt::WA_StyledBackground)) {
602#ifdef DEBUG_WIDGET_PAINT
603 if(doDebug)
604 qDebug(" Handling erase for [%s::%s]", qwidget->metaObject()->className(),
605 qwidget->objectName().local8Bit().data());
606#endif
607 QPainter p(qwidget);
608 qwidgetprivate->paintBackground(&p, qrgn,
609 qwidget->isWindow() ? QWidgetPrivate::DrawAsRoot : 0);
610 p.end();
611 }
612 QPaintEvent e(qrgn);
613#ifdef QT3_SUPPORT
614 e.setErased(true);
615#endif
616 qt_sendSpontaneousEvent(qwidget, &e);
617 } else {
618 qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false); // QWidgetPrivate::drawWidget sets this
619 QWidgetPrivate *qwidgetPrivate = qt_widget_private(qwidget);
620 qwidgetPrivate->drawWidget(qwidget, qrgn, QPoint(), QWidgetPrivate::DrawAsRoot | QWidgetPrivate::DrawPaintOnScreen | QWidgetPrivate::DrawRecursive, 0);
621 }
622
623 if (!redirectionOffset.isNull())
624 QPainter::restoreRedirected(qwidget);
625 if (engine)
626 engine->setSystemClip(QRegion());
627 qwidget->setAttribute(Qt::WA_WState_InPaintEvent, false);
628 if(!qwidget->testAttribute(Qt::WA_PaintOutsidePaintEvent) && qwidget->paintingActive())
629 qWarning("QWidget: It is dangerous to leave painters active on a"
630 " widget outside of the PaintEvent");
631 }
632 qwidgetprivate->hd = 0;
633 CGContextRestoreGState(cg);
634}
635
636- (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
637{
638 if (!qwidget)
639 return NO;
640
641 Q_UNUSED(theEvent);
642 return !qwidget->testAttribute(Qt::WA_MacNoClickThrough);
643}
644
645- (NSView *)hitTest:(NSPoint)aPoint
646{
647 if (!qwidget)
648 return [super hitTest:aPoint];
649
650 if (qwidget->testAttribute(Qt::WA_TransparentForMouseEvents))
651 return nil; // You cannot hit a transparent for mouse event widget.
652 return [super hitTest:aPoint];
653}
654
655- (void)updateTrackingAreas
656{
657 if (!qwidget)
658 return;
659
660 // [NSView addTrackingArea] is slow, so bail out early if we can:
661 if (NSIsEmptyRect([self visibleRect]))
662 return;
663
664 QMacCocoaAutoReleasePool pool;
665 if (NSArray *trackingArray = [self trackingAreas]) {
666 NSUInteger size = [trackingArray count];
667 for (NSUInteger i = 0; i < size; ++i) {
668 NSTrackingArea *t = [trackingArray objectAtIndex:i];
669 [self removeTrackingArea:t];
670 }
671 }
672
673 // Ideally, we shouldn't have NSTrackingMouseMoved events included below, it should
674 // only be turned on if mouseTracking, hover is on or a tool tip is set.
675 // Unfortunately, Qt will send "tooltip" events on mouse moves, so we need to
676 // turn it on in ALL case. That means EVERY QCocoaView gets to pay the cost of
677 // mouse moves delivered to it (Apple recommends keeping it OFF because there
678 // is a performance hit). So it goes.
679 NSUInteger trackingOptions = NSTrackingMouseEnteredAndExited | NSTrackingActiveInActiveApp
680 | NSTrackingInVisibleRect | NSTrackingMouseMoved;
681 NSTrackingArea *ta = [[NSTrackingArea alloc] initWithRect:NSMakeRect(0, 0,
682 qwidget->width(),
683 qwidget->height())
684 options:trackingOptions
685 owner:self
686 userInfo:nil];
687 [self addTrackingArea:ta];
688 [ta release];
689}
690
691- (void)mouseEntered:(NSEvent *)event
692{
693 if (!qwidget)
694 return;
695 if (qwidgetprivate->data.in_destructor)
696 return;
697
698 if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) {
699 QEvent enterEvent(QEvent::Enter);
700 NSPoint windowPoint = [event locationInWindow];
701 NSPoint globalPoint = [[event window] convertBaseToScreen:windowPoint];
702 NSPoint viewPoint = [self convertPoint:windowPoint fromView:nil];
703 QApplication::sendEvent(qwidget, &enterEvent);
704 qt_mouseover = qwidget;
705
706 // Update cursor icon:
707 qt_mac_update_cursor_at_global_pos(flipPoint(globalPoint).toPoint());
708
709 // Send mouse move and hover events as well:
710 if (!qAppInstance()->activePopupWidget() || qAppInstance()->activePopupWidget() == qwidget->window()) {
711 // This mouse move event should be sendt, even when mouse
712 // tracking is switched off (to trigger tooltips):
713 NSEvent *mouseEvent = [NSEvent mouseEventWithType:NSMouseMoved
714 location:windowPoint modifierFlags:[event modifierFlags] timestamp:[event timestamp]
715 windowNumber:[event windowNumber] context:[event context] eventNumber:[event eventNumber]
716 clickCount:0 pressure:0];
717 qt_mac_handleMouseEvent(self, mouseEvent, QEvent::MouseMove, Qt::NoButton);
718
719 if (qwidget->testAttribute(Qt::WA_Hover)) {
720 QHoverEvent he(QEvent::HoverEnter, QPoint(viewPoint.x, viewPoint.y), QPoint(-1, -1));
721 QApplicationPrivate::instance()->notify_helper(qwidget, &he);
722 }
723 }
724 }
725}
726
727- (void)mouseExited:(NSEvent *)event
728{
729 if (!qwidget)
730 return;
731
732 QEvent leaveEvent(QEvent::Leave);
733 NSPoint globalPoint = [[event window] convertBaseToScreen:[event locationInWindow]];
734 if (!qAppInstance()->activeModalWidget() || QApplicationPrivate::tryModalHelper(qwidget, 0)) {
735 QApplication::sendEvent(qwidget, &leaveEvent);
736
737 // ### Think about if it is necessary to update the cursor, should only be for a few cases.
738 qt_mac_update_cursor_at_global_pos(flipPoint(globalPoint).toPoint());
739 if (qwidget->testAttribute(Qt::WA_Hover)
740 && (!qAppInstance()->activePopupWidget() || qAppInstance()->activePopupWidget() == qwidget->window())) {
741 QHoverEvent he(QEvent::HoverLeave, QPoint(-1, -1),
742 qwidget->mapFromGlobal(QApplicationPrivate::instance()->hoverGlobalPos));
743 QApplicationPrivate::instance()->notify_helper(qwidget, &he);
744 }
745 }
746}
747
748- (void)flagsChanged:(NSEvent *)theEvent
749{
750 if (!qwidget)
751 return;
752
753 QWidget *widgetToGetKey = qwidget;
754
755 QWidget *popup = qAppInstance()->activePopupWidget();
756 if (popup && popup != qwidget->window())
757 widgetToGetKey = popup->focusWidget() ? popup->focusWidget() : popup;
758 qt_dispatchModifiersChanged(theEvent, widgetToGetKey);
759 [super flagsChanged:theEvent];
760}
761
762- (void)mouseMoved:(NSEvent *)theEvent
763{
764 if (!qwidget)
765 return;
766
767 // We always enable mouse tracking for all QCocoaView-s. In cases where we have
768 // child views, we will receive mouseMoved for both parent & the child (if
769 // mouse is over the child). We need to ignore the parent mouseMoved in such
770 // cases.
771 NSPoint windowPoint = [theEvent locationInWindow];
772 NSView *candidateView = [[[self window] contentView] hitTest:windowPoint];
773 if (candidateView && candidateView == self) {
774 qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton);
775 }
776}
777
778- (void)mouseDown:(NSEvent *)theEvent
779{
780 if (!qt_button_down)
781 qt_button_down = qwidget;
782
783 qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, Qt::LeftButton);
784 // Don't call super here. This prevents us from getting the mouseUp event,
785 // which we need to send even if the mouseDown event was not accepted.
786 // (this is standard Qt behavior.)
787}
788
789
790- (void)mouseUp:(NSEvent *)theEvent
791{
792 qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, Qt::LeftButton);
793
794 qt_button_down = 0;
795}
796
797- (void)rightMouseDown:(NSEvent *)theEvent
798{
799 if (!qt_button_down)
800 qt_button_down = qwidget;
801
802 qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, Qt::RightButton);
803}
804
805- (void)rightMouseUp:(NSEvent *)theEvent
806{
807 qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, Qt::RightButton);
808
809 qt_button_down = 0;
810}
811
812- (void)otherMouseDown:(NSEvent *)theEvent
813{
814 if (!qt_button_down)
815 qt_button_down = qwidget;
816
817 Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]);
818 qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonPress, mouseButton);
819}
820
821- (void)otherMouseUp:(NSEvent *)theEvent
822{
823 Qt::MouseButton mouseButton = cocoaButton2QtButton([theEvent buttonNumber]);
824 qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseButtonRelease, mouseButton);
825
826 qt_button_down = 0;
827}
828
829- (void)mouseDragged:(NSEvent *)theEvent
830{
831 qMacDnDParams()->view = self;
832 qMacDnDParams()->theEvent = theEvent;
833 qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton);
834}
835
836- (void)rightMouseDragged:(NSEvent *)theEvent
837{
838 qMacDnDParams()->view = self;
839 qMacDnDParams()->theEvent = theEvent;
840 qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton);
841}
842
843- (void)otherMouseDragged:(NSEvent *)theEvent
844{
845 qMacDnDParams()->view = self;
846 qMacDnDParams()->theEvent = theEvent;
847 qt_mac_handleMouseEvent(self, theEvent, QEvent::MouseMove, Qt::NoButton);
848}
849
850- (void)scrollWheel:(NSEvent *)theEvent
851{
852 // Give the Input Manager a chance to process the wheel event.
853 NSInputManager *currentIManager = [NSInputManager currentInputManager];
854 if (currentIManager && [currentIManager wantsToHandleMouseEvents]) {
855 [currentIManager handleMouseEvent:theEvent];
856 }
857
858 NSPoint windowPoint = [theEvent locationInWindow];
859 NSPoint globalPoint = [[theEvent window] convertBaseToScreen:windowPoint];
860 NSPoint localPoint = [self convertPoint:windowPoint fromView:nil];
861 QPoint qlocal = QPoint(localPoint.x, localPoint.y);
862 QPoint qglobal = QPoint(globalPoint.x, flipYCoordinate(globalPoint.y));
863 Qt::MouseButtons buttons = QApplication::mouseButtons();
864 bool wheelOK = false;
865 Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]);
866 QWidget *widgetToGetMouse = qwidget;
867 // if popup is open it should get wheel events if the cursor is over the popup,
868 // otherwise the event should be ignored.
869 if (QWidget *popup = qAppInstance()->activePopupWidget()) {
870 if (!popup->geometry().contains(qglobal))
871 return;
872 }
873
874 int deltaX = 0;
875 int deltaY = 0;
876 int deltaZ = 0;
877
878 const EventRef carbonEvent = (EventRef)[theEvent eventRef];
879 const UInt32 carbonEventKind = carbonEvent ? ::GetEventKind(carbonEvent) : 0;
880 const bool scrollEvent = carbonEventKind == kEventMouseScroll;
881
882 if (scrollEvent) {
883 // The mouse device containts pixel scroll wheel support (Mighty Mouse, Trackpad).
884 // Since deviceDelta is delivered as pixels rather than degrees, we need to
885 // convert from pixels to degrees in a sensible manner.
886 // It looks like 1/4 degrees per pixel behaves most native.
887 // (NB: Qt expects the unit for delta to be 8 per degree):
888 const int pixelsToDegrees = 2; // 8 * 1/4
889 deltaX = [theEvent deviceDeltaX] * pixelsToDegrees;
890 deltaY = [theEvent deviceDeltaY] * pixelsToDegrees;
891 deltaZ = [theEvent deviceDeltaZ] * pixelsToDegrees;
892 } else {
893 // carbonEventKind == kEventMouseWheelMoved
894 // Remove acceleration, and use either -120 or 120 as delta:
895 deltaX = qBound(-120, int([theEvent deltaX] * 10000), 120);
896 deltaY = qBound(-120, int([theEvent deltaY] * 10000), 120);
897 deltaZ = qBound(-120, int([theEvent deltaZ] * 10000), 120);
898 }
899
900#ifndef QT_NO_WHEELEVENT
901 if (deltaX != 0) {
902 QWheelEvent qwe(qlocal, qglobal, deltaX, buttons, keyMods, Qt::Horizontal);
903 qt_sendSpontaneousEvent(widgetToGetMouse, &qwe);
904 wheelOK = qwe.isAccepted();
905 if (!wheelOK && QApplicationPrivate::focus_widget
906 && QApplicationPrivate::focus_widget != widgetToGetMouse) {
907 QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal,
908 deltaX, buttons, keyMods, Qt::Horizontal);
909 qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2);
910 wheelOK = qwe2.isAccepted();
911 }
912 }
913
914 if (deltaY) {
915 QWheelEvent qwe(qlocal, qglobal, deltaY, buttons, keyMods, Qt::Vertical);
916 qt_sendSpontaneousEvent(widgetToGetMouse, &qwe);
917 wheelOK = qwe.isAccepted();
918 if (!wheelOK && QApplicationPrivate::focus_widget
919 && QApplicationPrivate::focus_widget != widgetToGetMouse) {
920 QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal,
921 deltaY, buttons, keyMods, Qt::Vertical);
922 qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2);
923 wheelOK = qwe2.isAccepted();
924 }
925 }
926
927 if (deltaZ) {
928 // Qt doesn't explicitly support wheels with a Z component. In a misguided attempt to
929 // try to be ahead of the pack, I'm adding this extra value.
930 QWheelEvent qwe(qlocal, qglobal, deltaZ, buttons, keyMods, (Qt::Orientation)3);
931 qt_sendSpontaneousEvent(widgetToGetMouse, &qwe);
932 wheelOK = qwe.isAccepted();
933 if (!wheelOK && QApplicationPrivate::focus_widget
934 && QApplicationPrivate::focus_widget != widgetToGetMouse) {
935 QWheelEvent qwe2(QApplicationPrivate::focus_widget->mapFromGlobal(qglobal), qglobal,
936 deltaZ, buttons, keyMods, (Qt::Orientation)3);
937 qt_sendSpontaneousEvent(QApplicationPrivate::focus_widget, &qwe2);
938 wheelOK = qwe2.isAccepted();
939 }
940 }
941#endif //QT_NO_WHEELEVENT
942}
943
944- (void)tabletProximity:(NSEvent *)tabletEvent
945{
946 qt_dispatchTabletProximityEvent(tabletEvent);
947}
948
949- (void)tabletPoint:(NSEvent *)tabletEvent
950{
951 if (!qt_mac_handleTabletEvent(self, tabletEvent))
952 [super tabletPoint:tabletEvent];
953}
954
955#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
956- (void)touchesBeganWithEvent:(NSEvent *)event;
957{
958 bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
959 qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all));
960}
961
962- (void)touchesMovedWithEvent:(NSEvent *)event;
963{
964 bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
965 qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all));
966}
967
968- (void)touchesEndedWithEvent:(NSEvent *)event;
969{
970 bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
971 qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all));
972}
973
974- (void)touchesCancelledWithEvent:(NSEvent *)event;
975{
976 bool all = qwidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents);
977 qt_translateRawTouchEvent(qwidget, QTouchEvent::TouchPad, QCocoaTouch::getCurrentTouchPointList(event, all));
978}
979#endif // MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
980
981- (void)magnifyWithEvent:(NSEvent *)event;
982{
983 if (!QApplicationPrivate::tryModalHelper(qwidget, 0))
984 return;
985
986#ifndef QT_NO_GESTURES
987 QNativeGestureEvent qNGEvent;
988 qNGEvent.gestureType = QNativeGestureEvent::Zoom;
989 NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
990 qNGEvent.position = flipPoint(p).toPoint();
991 qNGEvent.percentage = [event magnification];
992 qt_sendSpontaneousEvent(qwidget, &qNGEvent);
993#endif // QT_NO_GESTURES
994}
995
996- (void)rotateWithEvent:(NSEvent *)event;
997{
998 if (!QApplicationPrivate::tryModalHelper(qwidget, 0))
999 return;
1000
1001#ifndef QT_NO_GESTURES
1002 QNativeGestureEvent qNGEvent;
1003 qNGEvent.gestureType = QNativeGestureEvent::Rotate;
1004 NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
1005 qNGEvent.position = flipPoint(p).toPoint();
1006 qNGEvent.percentage = -[event rotation];
1007 qt_sendSpontaneousEvent(qwidget, &qNGEvent);
1008#endif // QT_NO_GESTURES
1009}
1010
1011- (void)swipeWithEvent:(NSEvent *)event;
1012{
1013 if (!QApplicationPrivate::tryModalHelper(qwidget, 0))
1014 return;
1015
1016#ifndef QT_NO_GESTURES
1017 QNativeGestureEvent qNGEvent;
1018 qNGEvent.gestureType = QNativeGestureEvent::Swipe;
1019 NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
1020 qNGEvent.position = flipPoint(p).toPoint();
1021 if ([event deltaX] == 1)
1022 qNGEvent.angle = 180.0f;
1023 else if ([event deltaX] == -1)
1024 qNGEvent.angle = 0.0f;
1025 else if ([event deltaY] == 1)
1026 qNGEvent.angle = 90.0f;
1027 else if ([event deltaY] == -1)
1028 qNGEvent.angle = 270.0f;
1029 qt_sendSpontaneousEvent(qwidget, &qNGEvent);
1030#endif // QT_NO_GESTURES
1031}
1032
1033- (void)beginGestureWithEvent:(NSEvent *)event;
1034{
1035 if (!QApplicationPrivate::tryModalHelper(qwidget, 0))
1036 return;
1037
1038#ifndef QT_NO_GESTURES
1039 QNativeGestureEvent qNGEvent;
1040 qNGEvent.gestureType = QNativeGestureEvent::GestureBegin;
1041 NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
1042 qNGEvent.position = flipPoint(p).toPoint();
1043 qt_sendSpontaneousEvent(qwidget, &qNGEvent);
1044#endif // QT_NO_GESTURES
1045}
1046
1047- (void)endGestureWithEvent:(NSEvent *)event;
1048{
1049 if (!QApplicationPrivate::tryModalHelper(qwidget, 0))
1050 return;
1051
1052#ifndef QT_NO_GESTURES
1053 QNativeGestureEvent qNGEvent;
1054 qNGEvent.gestureType = QNativeGestureEvent::GestureEnd;
1055 NSPoint p = [[event window] convertBaseToScreen:[event locationInWindow]];
1056 qNGEvent.position = flipPoint(p).toPoint();
1057 qt_sendSpontaneousEvent(qwidget, &qNGEvent);
1058#endif // QT_NO_GESTURES
1059}
1060
1061- (void)frameDidChange:(NSNotification *)note
1062{
1063 Q_UNUSED(note);
1064 if (!qwidget)
1065 return;
1066 if (qwidget->isWindow())
1067 return;
1068 NSRect newFrame = [self frame];
1069 QRect newGeo(newFrame.origin.x, newFrame.origin.y, newFrame.size.width, newFrame.size.height);
1070 bool moved = qwidget->testAttribute(Qt::WA_Moved);
1071 bool resized = qwidget->testAttribute(Qt::WA_Resized);
1072 qwidget->setGeometry(newGeo);
1073 qwidget->setAttribute(Qt::WA_Moved, moved);
1074 qwidget->setAttribute(Qt::WA_Resized, resized);
1075 qwidgetprivate->syncCocoaMask();
1076}
1077
1078- (BOOL)isEnabled
1079{
1080 if (!qwidget)
1081 return [super isEnabled];
1082 return [super isEnabled] && qwidget->isEnabled();
1083}
1084
1085- (void)setEnabled:(BOOL)flag
1086{
1087 QMacCocoaAutoReleasePool pool;
1088 [super setEnabled:flag];
1089 if (qwidget && qwidget->isEnabled() != flag)
1090 qwidget->setEnabled(flag);
1091}
1092
1093+ (Class)cellClass
1094{
1095 return [NSActionCell class];
1096}
1097
1098- (BOOL)acceptsFirstResponder
1099{
1100 if (!qwidget)
1101 return NO;
1102 // disabled widget shouldn't get focus even if it's a window.
1103 // hence disabled windows will not get any key or mouse events.
1104 if (!qwidget->isEnabled())
1105 return NO;
1106 // Before accepting the focus for a window, we check that
1107 // the focusWidget (if any) is not contained in the same window.
1108 if (qwidget->isWindow() && !qt_widget_private(qwidget)->topData()->embedded
1109 && (!qApp->focusWidget() || qApp->focusWidget()->window() != qwidget)) {
1110 return YES; // Always do it, so that windows can accept key press events.
1111 }
1112 return qwidget->focusPolicy() != Qt::NoFocus;
1113}
1114
1115- (BOOL)resignFirstResponder
1116{
1117 if (!qwidget)
1118 return NO;
1119 // Seems like the following test only triggers if this
1120 // view is inside a QMacNativeWidget:
1121 if (qwidget == QApplication::focusWidget())
1122 qwidget->clearFocus();
1123 return YES;
1124}
1125
1126- (BOOL)becomeFirstResponder
1127{
1128 // see the comment in the acceptsFirstResponder - if the window "stole" focus
1129 // let it become the responder, but don't tell Qt
1130 if (qwidget && qt_widget_private(qwidget->window())->topData()->embedded
1131 && !QApplication::focusWidget() && qwidget->focusPolicy() != Qt::NoFocus)
1132 qwidget->setFocus(Qt::OtherFocusReason);
1133 return YES;
1134}
1135
1136- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
1137{
1138 Q_UNUSED(isLocal);
1139 return supportedActions;
1140}
1141
1142- (void)setSupportedActions:(NSDragOperation)actions
1143{
1144 supportedActions = actions;
1145}
1146
1147- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
1148{
1149 Q_UNUSED(anImage);
1150 Q_UNUSED(aPoint);
1151 qMacDnDParams()->performedAction = operation;
1152 if (QDragManager::self()->object
1153 && QDragManager::self()->dragPrivate()->executed_action != Qt::ActionMask) {
1154 qMacDnDParams()->performedAction =
1155 qt_mac_mapDropAction(QDragManager::self()->dragPrivate()->executed_action);
1156 }
1157}
1158
1159- (QWidget *)qt_qwidget
1160{
1161 return qwidget;
1162}
1163
1164- (void) qt_clearQWidget
1165{
1166 qwidget = 0;
1167 qwidgetprivate = 0;
1168}
1169
1170- (BOOL)qt_leftButtonIsRightButton
1171{
1172 return leftButtonIsRightButton;
1173}
1174
1175- (void)qt_setLeftButtonIsRightButton:(BOOL)isSwapped
1176{
1177 leftButtonIsRightButton = isSwapped;
1178}
1179
1180+ (DnDParams*)currentMouseEvent
1181{
1182 return qMacDnDParams();
1183}
1184
1185- (void)keyDown:(NSEvent *)theEvent
1186{
1187 sendKeyEvents = true;
1188
1189 QWidget *widgetToGetKey = qwidget;
1190
1191 QWidget *popup = qAppInstance()->activePopupWidget();
1192 bool sendToPopup = false;
1193 if (popup && popup != qwidget->window()) {
1194 widgetToGetKey = popup->focusWidget() ? popup->focusWidget() : popup;
1195 sendToPopup = true;
1196 }
1197
1198 if (widgetToGetKey->testAttribute(Qt::WA_InputMethodEnabled)
1199 && !(widgetToGetKey->inputMethodHints() & Qt::ImhDigitsOnly
1200 || widgetToGetKey->inputMethodHints() & Qt::ImhFormattedNumbersOnly
1201 || widgetToGetKey->inputMethodHints() & Qt::ImhHiddenText)) {
1202 [qt_mac_nativeview_for(widgetToGetKey) interpretKeyEvents:[NSArray arrayWithObject: theEvent]];
1203 }
1204 if (sendKeyEvents && !composing) {
1205 bool keyOK = qt_dispatchKeyEvent(theEvent, widgetToGetKey);
1206 if (!keyOK && !sendToPopup) {
1207 // find the first responder that is not created by Qt and forward
1208 // the event to it (for example if Qt widget is embedded into native).
1209 QWidget *toplevel = qwidget->window();
1210 if (toplevel && qt_widget_private(toplevel)->topData()->embedded) {
1211 if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview])
1212 [w keyDown:theEvent];
1213 }
1214 }
1215 }
1216}
1217
1218
1219- (void)keyUp:(NSEvent *)theEvent
1220{
1221 if (sendKeyEvents) {
1222 bool keyOK = qt_dispatchKeyEvent(theEvent, qwidget);
1223 if (!keyOK) {
1224 QWidget *toplevel = qwidget->window();
1225 if (toplevel && qt_widget_private(toplevel)->topData()->embedded) {
1226 if (NSResponder *w = [qt_mac_nativeview_for(toplevel) superview])
1227 [w keyUp:theEvent];
1228 }
1229 }
1230 }
1231}
1232
1233- (void)viewWillMoveToWindow:(NSWindow *)window
1234{
1235 if (qwidget == 0)
1236 return;
1237
1238 if (qwidget->windowFlags() & Qt::MSWindowsOwnDC
1239 && (window != [self window])) { // OpenGL Widget
1240 QEvent event(QEvent::MacGLClearDrawable);
1241 qApp->sendEvent(qwidget, &event);
1242 }
1243}
1244
1245- (void)viewDidMoveToWindow
1246{
1247 if (qwidget == 0)
1248 return;
1249
1250 if (qwidget->windowFlags() & Qt::MSWindowsOwnDC && [self window]) {
1251 // call update paint event
1252 qwidgetprivate->needWindowChange = true;
1253 QEvent event(QEvent::MacGLWindowChange);
1254 qApp->sendEvent(qwidget, &event);
1255 }
1256}
1257
1258
1259// NSTextInput Protocol implementation
1260
1261- (void) insertText:(id)aString
1262{
1263 QString commitText;
1264 if ([aString length]) {
1265 if ([aString isKindOfClass:[NSAttributedString class]]) {
1266 commitText = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
1267 } else {
1268 commitText = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
1269 };
1270 }
1271
1272 if ([aString length] && composing) {
1273 // Send the commit string to the widget.
1274 composing = false;
1275 sendKeyEvents = false;
1276 QInputMethodEvent e;
1277 e.setCommitString(commitText);
1278 qt_sendSpontaneousEvent(qwidget, &e);
1279 } else {
1280 // The key sequence "`q" on a French Keyboard will generate two calls to insertText before
1281 // it returns from interpretKeyEvents. The first call will turn off 'composing' and accept
1282 // the "`" key. The last keyDown event needs to be processed by the widget to get the
1283 // character "q". The string parameter is ignored for the second call.
1284 sendKeyEvents = true;
1285 }
1286
1287 composingText->clear();
1288}
1289
1290- (void) setMarkedText:(id)aString selectedRange:(NSRange)selRange
1291{
1292 // Generate the QInputMethodEvent with preedit string and the attributes
1293 // for rendering it. The attributes handled here are 'underline',
1294 // 'underline color' and 'cursor position'.
1295 sendKeyEvents = false;
1296 composing = true;
1297 QString qtText;
1298 // Cursor position is retrived from the range.
1299 QList<QInputMethodEvent::Attribute> attrs;
1300 attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::Cursor, selRange.location, 1, QVariant());
1301 if ([aString isKindOfClass:[NSAttributedString class]]) {
1302 qtText = QCFString::toQString(reinterpret_cast<CFStringRef>([aString string]));
1303 composingLength = qtText.length();
1304 int index = 0;
1305 // Create attributes for individual sections of preedit text
1306 while (index < composingLength) {
1307 NSRange effectiveRange;
1308 NSRange range = NSMakeRange(index, composingLength-index);
1309 NSDictionary *attributes = [aString attributesAtIndex:index
1310 longestEffectiveRange:&effectiveRange
1311 inRange:range];
1312 NSNumber *underlineStyle = [attributes objectForKey:NSUnderlineStyleAttributeName];
1313 if (underlineStyle) {
1314 QColor clr (Qt::black);
1315 NSColor *color = [attributes objectForKey:NSUnderlineColorAttributeName];
1316 if (color) {
1317 clr = colorFrom(color);
1318 }
1319 QTextCharFormat format;
1320 format.setFontUnderline(true);
1321 format.setUnderlineColor(clr);
1322 attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
1323 effectiveRange.location,
1324 effectiveRange.length,
1325 format);
1326 }
1327 index = effectiveRange.location + effectiveRange.length;
1328 }
1329 } else {
1330 // No attributes specified, take only the preedit text.
1331 qtText = QCFString::toQString(reinterpret_cast<CFStringRef>(aString));
1332 composingLength = qtText.length();
1333 }
1334 // Make sure that we have at least one text format.
1335 if (attrs.size() <= 1) {
1336 QTextCharFormat format;
1337 format.setFontUnderline(true);
1338 attrs<<QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat,
1339 0, composingLength, format);
1340 }
1341 *composingText = qtText;
1342 QInputMethodEvent e(qtText, attrs);
1343 qt_sendSpontaneousEvent(qwidget, &e);
1344 if (!composingLength)
1345 composing = false;
1346}
1347
1348- (void) unmarkText
1349{
1350 if (composing) {
1351 QInputMethodEvent e;
1352 e.setCommitString(*composingText);
1353 qt_sendSpontaneousEvent(qwidget, &e);
1354 }
1355 composingText->clear();
1356 composing = false;
1357}
1358
1359- (BOOL) hasMarkedText
1360{
1361 return (composing ? YES: NO);
1362}
1363
1364- (void) doCommandBySelector:(SEL)aSelector
1365{
1366 Q_UNUSED(aSelector);
1367}
1368
1369- (BOOL)isComposing
1370{
1371 return composing;
1372}
1373
1374- (NSInteger) conversationIdentifier
1375{
1376 // Return a unique identifier fot this ime conversation
1377 return (NSInteger)self;
1378}
1379
1380- (NSAttributedString *) attributedSubstringFromRange:(NSRange)theRange
1381{
1382 QString selectedText(qwidget->inputMethodQuery(Qt::ImCurrentSelection).toString());
1383 if (!selectedText.isEmpty()) {
1384 QCFString string(selectedText.mid(theRange.location, theRange.length));
1385 const NSString *tmpString = reinterpret_cast<const NSString *>((CFStringRef)string);
1386 return [[[NSAttributedString alloc] initWithString:tmpString] autorelease];
1387 } else {
1388 return nil;
1389 }
1390}
1391
1392- (NSRange) markedRange
1393{
1394 NSRange range;
1395 if (composing) {
1396 range.location = 0;
1397 range.length = composingLength;
1398 } else {
1399 range.location = NSNotFound;
1400 range.length = 0;
1401 }
1402 return range;
1403}
1404
1405- (NSRange) selectedRange
1406{
1407 NSRange selRange;
1408 QString selectedText(qwidget->inputMethodQuery(Qt::ImCurrentSelection).toString());
1409 if (!selectedText.isEmpty()) {
1410 // Consider only the selected text.
1411 selRange.location = 0;
1412 selRange.length = selectedText.length();
1413 } else {
1414 // No selected text.
1415 selRange.location = NSNotFound;
1416 selRange.length = 0;
1417 }
1418 return selRange;
1419
1420}
1421
1422- (NSRect) firstRectForCharacterRange:(NSRange)theRange
1423{
1424 Q_UNUSED(theRange);
1425 // The returned rect is always based on the internal cursor.
1426 QRect mr(qwidget->inputMethodQuery(Qt::ImMicroFocus).toRect());
1427 QPoint mp(qwidget->mapToGlobal(QPoint(mr.bottomLeft())));
1428 NSRect rect ;
1429 rect.origin.x = mp.x();
1430 rect.origin.y = flipYCoordinate(mp.y());
1431 rect.size.width = mr.width();
1432 rect.size.height = mr.height();
1433 return rect;
1434}
1435
1436- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
1437{
1438 // We dont support cursor movements using mouse while composing.
1439 Q_UNUSED(thePoint);
1440 return NSNotFound;
1441}
1442
1443- (NSArray*) validAttributesForMarkedText
1444{
1445 if (qwidget == 0)
1446 return nil;
1447
1448 if (!qwidget->testAttribute(Qt::WA_InputMethodEnabled))
1449 return nil; // Not sure if that's correct, but it's saves a malloc.
1450
1451 // Support only underline color/style.
1452 return [NSArray arrayWithObjects:NSUnderlineColorAttributeName,
1453 NSUnderlineStyleAttributeName, nil];
1454}
1455@end
1456
1457QT_BEGIN_NAMESPACE
1458void QMacInputContext::reset()
1459{
1460 QWidget *w = QInputContext::focusWidget();
1461 if (w) {
1462 NSView *view = qt_mac_nativeview_for(w);
1463 if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) {
1464 QMacCocoaAutoReleasePool pool;
1465 QT_MANGLE_NAMESPACE(QCocoaView) *qc = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view);
1466 NSInputManager *currentIManager = [NSInputManager currentInputManager];
1467 if (currentIManager) {
1468 [currentIManager markedTextAbandoned:view];
1469 [qc unmarkText];
1470 }
1471 }
1472 }
1473}
1474
1475bool QMacInputContext::isComposing() const
1476{
1477 QWidget *w = QInputContext::focusWidget();
1478 if (w) {
1479 NSView *view = qt_mac_nativeview_for(w);
1480 if ([view isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]]) {
1481 return [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view) isComposing];
1482 }
1483 }
1484 return false;
1485}
1486
1487extern bool qt_mac_in_drag;
1488void * /*NSImage */qt_mac_create_nsimage(const QPixmap &pm);
1489static const int default_pm_hotx = -2;
1490static const int default_pm_hoty = -16;
1491static const char* default_pm[] = {
1492 "13 9 3 1",
1493 ". c None",
1494 " c #000000",
1495 "X c #FFFFFF",
1496 "X X X X X X X",
1497 " X X X X X X ",
1498 "X ......... X",
1499 " X.........X ",
1500 "X ......... X",
1501 " X.........X ",
1502 "X ......... X",
1503 " X X X X X X ",
1504 "X X X X X X X",
1505};
1506
1507Qt::DropAction QDragManager::drag(QDrag *o)
1508{
1509 if(qt_mac_in_drag) { //just make sure..
1510 qWarning("Qt: Internal error: WH0A, unexpected condition reached");
1511 return Qt::IgnoreAction;
1512 }
1513 if(object == o)
1514 return Qt::IgnoreAction;
1515 /* At the moment it seems clear that Mac OS X does not want to drag with a non-left button
1516 so we just bail early to prevent it */
1517 if(!(GetCurrentEventButtonState() & kEventMouseButtonPrimary))
1518 return Qt::IgnoreAction;
1519
1520 if(object) {
1521 dragPrivate()->source->removeEventFilter(this);
1522 cancel();
1523 beingCancelled = false;
1524 }
1525
1526 object = o;
1527 dragPrivate()->target = 0;
1528
1529#ifndef QT_NO_ACCESSIBILITY
1530 QAccessible::updateAccessibility(this, 0, QAccessible::DragDropStart);
1531#endif
1532
1533 // setup the data
1534 QMacPasteboard dragBoard((CFStringRef) NSDragPboard, QMacPasteboardMime::MIME_DND);
1535 dragPrivate()->data->setData(QLatin1String("application/x-qt-mime-type-name"), QByteArray("dummy"));
1536 dragBoard.setMimeData(dragPrivate()->data);
1537
1538 // create the image
1539 QPoint hotspot;
1540 QPixmap pix = dragPrivate()->pixmap;
1541 if(pix.isNull()) {
1542 if(dragPrivate()->data->hasText() || dragPrivate()->data->hasUrls()) {
1543 // get the string
1544 QString s = dragPrivate()->data->hasText() ? dragPrivate()->data->text()
1545 : dragPrivate()->data->urls().first().toString();
1546 if(s.length() > 26)
1547 s = s.left(23) + QChar(0x2026);
1548 if(!s.isEmpty()) {
1549 // draw it
1550 QFont f(qApp->font());
1551 f.setPointSize(12);
1552 QFontMetrics fm(f);
1553 QPixmap tmp(fm.width(s), fm.height());
1554 if(!tmp.isNull()) {
1555 QPainter p(&tmp);
1556 p.fillRect(0, 0, tmp.width(), tmp.height(), Qt::color0);
1557 p.setPen(Qt::color1);
1558 p.setFont(f);
1559 p.drawText(0, fm.ascent(), s);
1560 // save it
1561 pix = tmp;
1562 hotspot = QPoint(tmp.width() / 2, tmp.height() / 2);
1563 }
1564 }
1565 } else {
1566 pix = QPixmap(default_pm);
1567 hotspot = QPoint(default_pm_hotx, default_pm_hoty);
1568 }
1569 } else {
1570 hotspot = dragPrivate()->hotspot;
1571 }
1572 // convert the image to NSImage.
1573 NSImage *image = (NSImage *)qt_mac_create_nsimage(pix);
1574 [image retain];
1575 DnDParams dndParams = *[QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent];
1576 // save supported actions
1577 [dndParams.view setSupportedActions: qt_mac_mapDropActions(dragPrivate()->possible_actions)];
1578 NSPoint imageLoc = {dndParams.localPoint.x - hotspot.x(),
1579 dndParams.localPoint.y + pix.height() - hotspot.y()};
1580 NSSize mouseOffset = {0.0, 0.0};
1581 NSPasteboard *pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
1582 NSPoint windowPoint = [dndParams.theEvent locationInWindow];
1583 dragPrivate()->executed_action = Qt::ActionMask;
1584 // do the drag
1585 [dndParams.view retain];
1586 [dndParams.view dragImage:image
1587 at:imageLoc
1588 offset:mouseOffset
1589 event:dndParams.theEvent
1590 pasteboard:pboard
1591 source:dndParams.view
1592 slideBack:YES];
1593 // reset the implicit grab widget when drag ends because we will not
1594 // receive the mouse release event when DND is active.
1595 qt_button_down = 0;
1596 [dndParams.view release];
1597 [image release];
1598 if (dragPrivate())
1599 dragPrivate()->executed_action = Qt::IgnoreAction;
1600 object = 0;
1601 Qt::DropAction performedAction(qt_mac_mapNSDragOperation(qMacDnDParams()->performedAction));
1602 // do post drag processing, if required.
1603 if(performedAction != Qt::IgnoreAction) {
1604 // check if the receiver points us to a file location.
1605 // if so, we need to do the file copy/move ourselves.
1606 QCFType<CFURLRef> pasteLocation = 0;
1607 PasteboardCopyPasteLocation(dragBoard.pasteBoard(), &pasteLocation);
1608 if (pasteLocation) {
1609 QList<QUrl> urls = o->mimeData()->urls();
1610 for (int i = 0; i < urls.size(); ++i) {
1611 QUrl fromUrl = urls.at(i);
1612 QString filename = QFileInfo(fromUrl.path()).fileName();
1613 QUrl toUrl(QCFString::toQString(CFURLGetString(pasteLocation)) + filename);
1614 if (performedAction == Qt::MoveAction)
1615 QFile::rename(fromUrl.path(), toUrl.path());
1616 else if (performedAction == Qt::CopyAction)
1617 QFile::copy(fromUrl.path(), toUrl.path());
1618 }
1619 }
1620 }
1621 o->setMimeData(0);
1622 o->deleteLater();
1623 return performedAction;
1624}
1625
1626QT_END_NAMESPACE
1627
1628#endif // QT_MAC_USE_COCOA
Note: See TracBrowser for help on using the repository browser.