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

Last change on this file since 802 was 769, checked in by Dmitry A. Kuminov, 15 years ago

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

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