|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 /**************************************************************************** |
|
43 ** |
|
44 ** Copyright (c) 2007-2008, Apple, Inc. |
|
45 ** |
|
46 ** All rights reserved. |
|
47 ** |
|
48 ** Redistribution and use in source and binary forms, with or without |
|
49 ** modification, are permitted provided that the following conditions are met: |
|
50 ** |
|
51 ** * Redistributions of source code must retain the above copyright notice, |
|
52 ** this list of conditions and the following disclaimer. |
|
53 ** |
|
54 ** * Redistributions in binary form must reproduce the above copyright notice, |
|
55 ** this list of conditions and the following disclaimer in the documentation |
|
56 ** and/or other materials provided with the distribution. |
|
57 ** |
|
58 ** * Neither the name of Apple, Inc. nor the names of its contributors |
|
59 ** may be used to endorse or promote products derived from this software |
|
60 ** without specific prior written permission. |
|
61 ** |
|
62 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
63 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
64 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
65 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
|
66 ** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
67 ** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
68 ** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
69 ** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
70 ** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
71 ** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
72 ** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
73 ** |
|
74 ****************************************************************************/ |
|
75 |
|
76 #include <private/qcore_mac_p.h> |
|
77 #include <qaction.h> |
|
78 #include <qwidget.h> |
|
79 #include <qdesktopwidget.h> |
|
80 #include <qevent.h> |
|
81 #include <qpixmapcache.h> |
|
82 #include <private/qevent_p.h> |
|
83 #include <private/qt_cocoa_helpers_mac_p.h> |
|
84 #include <private/qt_mac_p.h> |
|
85 #include <private/qapplication_p.h> |
|
86 #include <private/qcocoawindow_mac_p.h> |
|
87 #include <private/qcocoaview_mac_p.h> |
|
88 #include <private/qkeymapper_p.h> |
|
89 #include <private/qwidget_p.h> |
|
90 |
|
91 QT_BEGIN_NAMESPACE |
|
92 |
|
93 Q_GLOBAL_STATIC(QMacWindowFader, macwindowFader); |
|
94 |
|
95 QMacWindowFader::QMacWindowFader() |
|
96 : m_duration(0.250) |
|
97 { |
|
98 } |
|
99 |
|
100 QMacWindowFader *QMacWindowFader::currentFader() |
|
101 { |
|
102 return macwindowFader(); |
|
103 } |
|
104 |
|
105 void QMacWindowFader::registerWindowToFade(QWidget *window) |
|
106 { |
|
107 m_windowsToFade.append(window); |
|
108 } |
|
109 |
|
110 void QMacWindowFader::performFade() |
|
111 { |
|
112 const QWidgetList myWidgetsToFade = m_windowsToFade; |
|
113 const int widgetCount = myWidgetsToFade.count(); |
|
114 #if QT_MAC_USE_COCOA |
|
115 QMacCocoaAutoReleasePool pool; |
|
116 [NSAnimationContext beginGrouping]; |
|
117 [[NSAnimationContext currentContext] setDuration:NSTimeInterval(m_duration)]; |
|
118 #endif |
|
119 |
|
120 for (int i = 0; i < widgetCount; ++i) { |
|
121 QWidget *widget = m_windowsToFade.at(i); |
|
122 OSWindowRef window = qt_mac_window_for(widget); |
|
123 #if QT_MAC_USE_COCOA |
|
124 [[window animator] setAlphaValue:0.0]; |
|
125 QTimer::singleShot(qRound(m_duration * 1000), widget, SLOT(hide())); |
|
126 #else |
|
127 TransitionWindowOptions options = {0, m_duration, 0, 0}; |
|
128 TransitionWindowWithOptions(window, kWindowFadeTransitionEffect, kWindowHideTransitionAction, |
|
129 0, 1, &options); |
|
130 #endif |
|
131 } |
|
132 #if QT_MAC_USE_COCOA |
|
133 [NSAnimationContext endGrouping]; |
|
134 #endif |
|
135 m_duration = 0.250; |
|
136 m_windowsToFade.clear(); |
|
137 } |
|
138 |
|
139 extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); // qapplication.cpp; |
|
140 extern Qt::MouseButton cocoaButton2QtButton(NSInteger buttonNum); // qcocoaview.mm |
|
141 extern QWidget * mac_mouse_grabber; |
|
142 |
|
143 void macWindowFade(void * /*OSWindowRef*/ window, float durationSeconds) |
|
144 { |
|
145 OSWindowRef wnd = static_cast<OSWindowRef>(window); |
|
146 if (wnd) { |
|
147 QWidget *widget; |
|
148 #if QT_MAC_USE_COCOA |
|
149 widget = [wnd QT_MANGLE_NAMESPACE(qt_qwidget)]; |
|
150 #else |
|
151 const UInt32 kWidgetCreatorQt = kEventClassQt; |
|
152 enum { |
|
153 kWidgetPropertyQWidget = 'QWId' //QWidget * |
|
154 }; |
|
155 if (GetWindowProperty(static_cast<WindowRef>(window), kWidgetCreatorQt, kWidgetPropertyQWidget, sizeof(widget), 0, &widget) != noErr) |
|
156 widget = 0; |
|
157 #endif |
|
158 if (widget) { |
|
159 QMacWindowFader::currentFader()->setFadeDuration(durationSeconds); |
|
160 QMacWindowFader::currentFader()->registerWindowToFade(widget); |
|
161 QMacWindowFader::currentFader()->performFade(); |
|
162 } |
|
163 } |
|
164 } |
|
165 |
|
166 bool macWindowIsTextured( void * /*OSWindowRef*/ window ) |
|
167 { |
|
168 OSWindowRef wnd = static_cast<OSWindowRef>(window); |
|
169 #if QT_MAC_USE_COCOA |
|
170 return ( [wnd styleMask] & NSTexturedBackgroundWindowMask ) ? true : false; |
|
171 #else |
|
172 WindowAttributes currentAttributes; |
|
173 GetWindowAttributes(wnd, ¤tAttributes); |
|
174 return (currentAttributes & kWindowMetalAttribute) ? true : false; |
|
175 #endif |
|
176 } |
|
177 |
|
178 void macWindowToolbarShow(const QWidget *widget, bool show ) |
|
179 { |
|
180 OSWindowRef wnd = qt_mac_window_for(widget); |
|
181 #if QT_MAC_USE_COCOA |
|
182 if (NSToolbar *toolbar = [wnd toolbar]) { |
|
183 QMacCocoaAutoReleasePool pool; |
|
184 if (show != [toolbar isVisible]) { |
|
185 [toolbar setVisible:show]; |
|
186 } else { |
|
187 // The toolbar may be in sync, but we are not, update our framestrut. |
|
188 qt_widget_private(const_cast<QWidget *>(widget))->updateFrameStrut(); |
|
189 } |
|
190 } |
|
191 #else |
|
192 ShowHideWindowToolbar(wnd, show, false); |
|
193 #endif |
|
194 } |
|
195 |
|
196 |
|
197 void macWindowToolbarSet( void * /*OSWindowRef*/ window, void *toolbarRef ) |
|
198 { |
|
199 OSWindowRef wnd = static_cast<OSWindowRef>(window); |
|
200 #if QT_MAC_USE_COCOA |
|
201 [wnd setToolbar:static_cast<NSToolbar *>(toolbarRef)]; |
|
202 #else |
|
203 SetWindowToolbar(wnd, static_cast<HIToolbarRef>(toolbarRef)); |
|
204 #endif |
|
205 } |
|
206 |
|
207 bool macWindowToolbarIsVisible( void * /*OSWindowRef*/ window ) |
|
208 { |
|
209 OSWindowRef wnd = static_cast<OSWindowRef>(window); |
|
210 #if QT_MAC_USE_COCOA |
|
211 if (NSToolbar *toolbar = [wnd toolbar]) |
|
212 return [toolbar isVisible]; |
|
213 return false; |
|
214 #else |
|
215 return IsWindowToolbarVisible(wnd); |
|
216 #endif |
|
217 } |
|
218 |
|
219 void macWindowSetHasShadow( void * /*OSWindowRef*/ window, bool hasShadow ) |
|
220 { |
|
221 OSWindowRef wnd = static_cast<OSWindowRef>(window); |
|
222 #if QT_MAC_USE_COCOA |
|
223 [wnd setHasShadow:BOOL(hasShadow)]; |
|
224 #else |
|
225 if (hasShadow) |
|
226 ChangeWindowAttributes(wnd, 0, kWindowNoShadowAttribute); |
|
227 else |
|
228 ChangeWindowAttributes(wnd, kWindowNoShadowAttribute, 0); |
|
229 #endif |
|
230 } |
|
231 |
|
232 void macWindowFlush(void * /*OSWindowRef*/ window) |
|
233 { |
|
234 OSWindowRef wnd = static_cast<OSWindowRef>(window); |
|
235 #if QT_MAC_USE_COCOA |
|
236 [wnd flushWindowIfNeeded]; |
|
237 #else |
|
238 HIWindowFlush(wnd); |
|
239 #endif |
|
240 } |
|
241 |
|
242 void * /*NSImage */qt_mac_create_nsimage(const QPixmap &pm) |
|
243 { |
|
244 QMacCocoaAutoReleasePool pool; |
|
245 if(QCFType<CGImageRef> image = pm.toMacCGImageRef()) { |
|
246 NSImage *newImage = 0; |
|
247 NSRect imageRect = NSMakeRect(0.0, 0.0, CGImageGetWidth(image), CGImageGetHeight(image)); |
|
248 newImage = [[NSImage alloc] initWithSize:imageRect.size]; |
|
249 [newImage lockFocus]; |
|
250 { |
|
251 CGContextRef imageContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; |
|
252 CGContextDrawImage(imageContext, *(CGRect*)&imageRect, image); |
|
253 } |
|
254 [newImage unlockFocus]; |
|
255 return newImage; |
|
256 } |
|
257 return 0; |
|
258 } |
|
259 |
|
260 void qt_mac_update_mouseTracking(QWidget *widget) |
|
261 { |
|
262 #ifdef QT_MAC_USE_COCOA |
|
263 [qt_mac_nativeview_for(widget) updateTrackingAreas]; |
|
264 #else |
|
265 Q_UNUSED(widget); |
|
266 #endif |
|
267 } |
|
268 |
|
269 OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGImageRef inImage) |
|
270 { |
|
271 // Verbatim copy if HIViewDrawCGImage (as shown on Carbon-Dev) |
|
272 OSStatus err = noErr; |
|
273 |
|
274 require_action(inContext != NULL, InvalidContext, err = paramErr); |
|
275 require_action(inBounds != NULL, InvalidBounds, err = paramErr); |
|
276 require_action(inImage != NULL, InvalidImage, err = paramErr); |
|
277 |
|
278 CGContextSaveGState( inContext ); |
|
279 CGContextTranslateCTM (inContext, 0, inBounds->origin.y + CGRectGetMaxY(*inBounds)); |
|
280 CGContextScaleCTM(inContext, 1, -1); |
|
281 |
|
282 CGContextDrawImage(inContext, *inBounds, inImage); |
|
283 |
|
284 CGContextRestoreGState(inContext); |
|
285 InvalidImage: |
|
286 InvalidBounds: |
|
287 InvalidContext: |
|
288 return err; |
|
289 } |
|
290 |
|
291 bool qt_mac_checkForNativeSizeGrip(const QWidget *widget) |
|
292 { |
|
293 #ifndef QT_MAC_USE_COCOA |
|
294 OSViewRef nativeSizeGrip = 0; |
|
295 HIViewFindByID(HIViewGetRoot(HIViewGetWindow(HIViewRef(widget->winId()))), kHIViewWindowGrowBoxID, &nativeSizeGrip); |
|
296 return (nativeSizeGrip != 0); |
|
297 #else |
|
298 return [[reinterpret_cast<NSView *>(widget->winId()) window] showsResizeIndicator]; |
|
299 #endif |
|
300 } |
|
301 struct qt_mac_enum_mapper |
|
302 { |
|
303 int mac_code; |
|
304 int qt_code; |
|
305 #if defined(DEBUG_MOUSE_MAPS) |
|
306 # define QT_MAC_MAP_ENUM(x) x, #x |
|
307 const char *desc; |
|
308 #else |
|
309 # define QT_MAC_MAP_ENUM(x) x |
|
310 #endif |
|
311 }; |
|
312 |
|
313 //mouse buttons |
|
314 static qt_mac_enum_mapper qt_mac_mouse_symbols[] = { |
|
315 { kEventMouseButtonPrimary, QT_MAC_MAP_ENUM(Qt::LeftButton) }, |
|
316 { kEventMouseButtonSecondary, QT_MAC_MAP_ENUM(Qt::RightButton) }, |
|
317 { kEventMouseButtonTertiary, QT_MAC_MAP_ENUM(Qt::MidButton) }, |
|
318 { 4, QT_MAC_MAP_ENUM(Qt::XButton1) }, |
|
319 { 5, QT_MAC_MAP_ENUM(Qt::XButton2) }, |
|
320 { 0, QT_MAC_MAP_ENUM(0) } |
|
321 }; |
|
322 Qt::MouseButtons qt_mac_get_buttons(int buttons) |
|
323 { |
|
324 #ifdef DEBUG_MOUSE_MAPS |
|
325 qDebug("Qt: internal: **Mapping buttons: %d (0x%04x)", buttons, buttons); |
|
326 #endif |
|
327 Qt::MouseButtons ret = Qt::NoButton; |
|
328 for(int i = 0; qt_mac_mouse_symbols[i].qt_code; i++) { |
|
329 if (buttons & (0x01<<(qt_mac_mouse_symbols[i].mac_code-1))) { |
|
330 #ifdef DEBUG_MOUSE_MAPS |
|
331 qDebug("Qt: internal: got button: %s", qt_mac_mouse_symbols[i].desc); |
|
332 #endif |
|
333 ret |= Qt::MouseButtons(qt_mac_mouse_symbols[i].qt_code); |
|
334 } |
|
335 } |
|
336 return ret; |
|
337 } |
|
338 Qt::MouseButton qt_mac_get_button(EventMouseButton button) |
|
339 { |
|
340 #ifdef DEBUG_MOUSE_MAPS |
|
341 qDebug("Qt: internal: **Mapping button: %d (0x%04x)", button, button); |
|
342 #endif |
|
343 Qt::MouseButtons ret = 0; |
|
344 for(int i = 0; qt_mac_mouse_symbols[i].qt_code; i++) { |
|
345 if (button == qt_mac_mouse_symbols[i].mac_code) { |
|
346 #ifdef DEBUG_MOUSE_MAPS |
|
347 qDebug("Qt: internal: got button: %s", qt_mac_mouse_symbols[i].desc); |
|
348 #endif |
|
349 return Qt::MouseButton(qt_mac_mouse_symbols[i].qt_code); |
|
350 } |
|
351 } |
|
352 return Qt::NoButton; |
|
353 } |
|
354 |
|
355 void macSendToolbarChangeEvent(QWidget *widget) |
|
356 { |
|
357 QToolBarChangeEvent ev(!(GetCurrentKeyModifiers() & cmdKey)); |
|
358 qt_sendSpontaneousEvent(widget, &ev); |
|
359 } |
|
360 |
|
361 Q_GLOBAL_STATIC(QMacTabletHash, tablet_hash) |
|
362 QMacTabletHash *qt_mac_tablet_hash() |
|
363 { |
|
364 return tablet_hash(); |
|
365 } |
|
366 |
|
367 #ifdef QT_MAC_USE_COCOA |
|
368 void qt_dispatchTabletProximityEvent(void * /*NSEvent * */ tabletEvent) |
|
369 { |
|
370 NSEvent *proximityEvent = static_cast<NSEvent *>(tabletEvent); |
|
371 // simply construct a Carbon proximity record and handle it all in one spot. |
|
372 TabletProximityRec carbonProximityRec = { [proximityEvent vendorID], |
|
373 [proximityEvent tabletID], |
|
374 [proximityEvent pointingDeviceID], |
|
375 [proximityEvent deviceID], |
|
376 [proximityEvent systemTabletID], |
|
377 [proximityEvent vendorPointingDeviceType], |
|
378 [proximityEvent pointingDeviceSerialNumber], |
|
379 [proximityEvent uniqueID], |
|
380 [proximityEvent capabilityMask], |
|
381 [proximityEvent pointingDeviceType], |
|
382 [proximityEvent isEnteringProximity] }; |
|
383 qt_dispatchTabletProximityEvent(carbonProximityRec); |
|
384 } |
|
385 #endif // QT_MAC_USE_COCOA |
|
386 |
|
387 void qt_dispatchTabletProximityEvent(const ::TabletProximityRec &proxRec) |
|
388 { |
|
389 QTabletDeviceData proximityDevice; |
|
390 proximityDevice.tabletUniqueID = proxRec.uniqueID; |
|
391 proximityDevice.capabilityMask = proxRec.capabilityMask; |
|
392 |
|
393 switch (proxRec.pointerType) { |
|
394 case NSUnknownPointingDevice: |
|
395 default: |
|
396 proximityDevice.tabletPointerType = QTabletEvent::UnknownPointer; |
|
397 break; |
|
398 case NSPenPointingDevice: |
|
399 proximityDevice.tabletPointerType = QTabletEvent::Pen; |
|
400 break; |
|
401 case NSCursorPointingDevice: |
|
402 proximityDevice.tabletPointerType = QTabletEvent::Cursor; |
|
403 break; |
|
404 case NSEraserPointingDevice: |
|
405 proximityDevice.tabletPointerType = QTabletEvent::Eraser; |
|
406 break; |
|
407 } |
|
408 uint bits = proxRec.vendorPointerType; |
|
409 if (bits == 0 && proximityDevice.tabletUniqueID != 0) { |
|
410 // Fallback. It seems that the driver doesn't always include all the information. |
|
411 // High-End Wacom devices store their "type" in the uper bits of the Unique ID. |
|
412 // I'm not sure how to handle it for consumer devices, but I'll test that in a bit. |
|
413 bits = proximityDevice.tabletUniqueID >> 32; |
|
414 } |
|
415 // Defined in the "EN0056-NxtGenImpGuideX" |
|
416 // on Wacom's Developer Website (www.wacomeng.com) |
|
417 if (((bits & 0x0006) == 0x0002) && ((bits & 0x0F06) != 0x0902)) { |
|
418 proximityDevice.tabletDeviceType = QTabletEvent::Stylus; |
|
419 } else { |
|
420 switch (bits & 0x0F06) { |
|
421 case 0x0802: |
|
422 proximityDevice.tabletDeviceType = QTabletEvent::Stylus; |
|
423 break; |
|
424 case 0x0902: |
|
425 proximityDevice.tabletDeviceType = QTabletEvent::Airbrush; |
|
426 break; |
|
427 case 0x0004: |
|
428 proximityDevice.tabletDeviceType = QTabletEvent::FourDMouse; |
|
429 break; |
|
430 case 0x0006: |
|
431 proximityDevice.tabletDeviceType = QTabletEvent::Puck; |
|
432 break; |
|
433 case 0x0804: |
|
434 proximityDevice.tabletDeviceType = QTabletEvent::RotationStylus; |
|
435 break; |
|
436 default: |
|
437 proximityDevice.tabletDeviceType = QTabletEvent::NoDevice; |
|
438 } |
|
439 } |
|
440 // The deviceID is "unique" while in the proximity, it's a key that we can use for |
|
441 // linking up TabletDeviceData to an event (especially if there are two devices in action). |
|
442 bool entering = proxRec.enterProximity; |
|
443 if (entering) { |
|
444 qt_mac_tablet_hash()->insert(proxRec.deviceID, proximityDevice); |
|
445 } else { |
|
446 qt_mac_tablet_hash()->remove(proxRec.deviceID); |
|
447 } |
|
448 |
|
449 QTabletEvent qtabletProximity(entering ? QEvent::TabletEnterProximity |
|
450 : QEvent::TabletLeaveProximity, |
|
451 QPoint(), QPoint(), QPointF(), proximityDevice.tabletDeviceType, |
|
452 proximityDevice.tabletPointerType, 0., 0, 0, 0., 0., 0, 0, |
|
453 proximityDevice.tabletUniqueID); |
|
454 |
|
455 qt_sendSpontaneousEvent(qApp, &qtabletProximity); |
|
456 } |
|
457 |
|
458 #ifdef QT_MAC_USE_COCOA |
|
459 // Use this method to keep all the information in the TextSegment. As long as it is ordered |
|
460 // we are in OK shape, and we can influence that ourselves. |
|
461 struct KeyPair |
|
462 { |
|
463 QChar cocoaKey; |
|
464 Qt::Key qtKey; |
|
465 }; |
|
466 |
|
467 bool operator==(const KeyPair &entry, QChar qchar) |
|
468 { |
|
469 return entry.cocoaKey == qchar; |
|
470 } |
|
471 |
|
472 bool operator<(const KeyPair &entry, QChar qchar) |
|
473 { |
|
474 return entry.cocoaKey < qchar; |
|
475 } |
|
476 |
|
477 bool operator<(QChar qchar, const KeyPair &entry) |
|
478 { |
|
479 return qchar < entry.cocoaKey; |
|
480 } |
|
481 |
|
482 static Qt::Key cocoaKey2QtKey(QChar keyCode) |
|
483 { |
|
484 static const int NumEntries = 57; |
|
485 static const KeyPair entries[NumEntries] = { |
|
486 { NSEnterCharacter, Qt::Key_Enter }, |
|
487 { NSTabCharacter, Qt::Key_Tab }, |
|
488 { NSCarriageReturnCharacter, Qt::Key_Return }, |
|
489 { NSBackTabCharacter, Qt::Key_Backtab }, |
|
490 { kEscapeCharCode, Qt::Key_Escape }, |
|
491 { NSDeleteCharacter, Qt::Key_Backspace }, |
|
492 { NSUpArrowFunctionKey, Qt::Key_Up }, |
|
493 { NSDownArrowFunctionKey, Qt::Key_Down }, |
|
494 { NSLeftArrowFunctionKey, Qt::Key_Left }, |
|
495 { NSRightArrowFunctionKey, Qt::Key_Right }, |
|
496 { NSF1FunctionKey, Qt::Key_F1 }, |
|
497 { NSF2FunctionKey, Qt::Key_F2 }, |
|
498 { NSF3FunctionKey, Qt::Key_F3 }, |
|
499 { NSF4FunctionKey, Qt::Key_F4 }, |
|
500 { NSF5FunctionKey, Qt::Key_F5 }, |
|
501 { NSF6FunctionKey, Qt::Key_F6 }, |
|
502 { NSF7FunctionKey, Qt::Key_F7 }, |
|
503 { NSF8FunctionKey, Qt::Key_F8 }, |
|
504 { NSF9FunctionKey, Qt::Key_F8 }, |
|
505 { NSF10FunctionKey, Qt::Key_F10 }, |
|
506 { NSF11FunctionKey, Qt::Key_F11 }, |
|
507 { NSF12FunctionKey, Qt::Key_F12 }, |
|
508 { NSF13FunctionKey, Qt::Key_F13 }, |
|
509 { NSF14FunctionKey, Qt::Key_F14 }, |
|
510 { NSF15FunctionKey, Qt::Key_F15 }, |
|
511 { NSF16FunctionKey, Qt::Key_F16 }, |
|
512 { NSF17FunctionKey, Qt::Key_F17 }, |
|
513 { NSF18FunctionKey, Qt::Key_F18 }, |
|
514 { NSF19FunctionKey, Qt::Key_F19 }, |
|
515 { NSF20FunctionKey, Qt::Key_F20 }, |
|
516 { NSF21FunctionKey, Qt::Key_F21 }, |
|
517 { NSF22FunctionKey, Qt::Key_F22 }, |
|
518 { NSF23FunctionKey, Qt::Key_F23 }, |
|
519 { NSF24FunctionKey, Qt::Key_F24 }, |
|
520 { NSF25FunctionKey, Qt::Key_F25 }, |
|
521 { NSF26FunctionKey, Qt::Key_F26 }, |
|
522 { NSF27FunctionKey, Qt::Key_F27 }, |
|
523 { NSF28FunctionKey, Qt::Key_F28 }, |
|
524 { NSF29FunctionKey, Qt::Key_F29 }, |
|
525 { NSF30FunctionKey, Qt::Key_F30 }, |
|
526 { NSF31FunctionKey, Qt::Key_F31 }, |
|
527 { NSF32FunctionKey, Qt::Key_F32 }, |
|
528 { NSF33FunctionKey, Qt::Key_F33 }, |
|
529 { NSF34FunctionKey, Qt::Key_F34 }, |
|
530 { NSF35FunctionKey, Qt::Key_F35 }, |
|
531 { NSInsertFunctionKey, Qt::Key_Insert }, |
|
532 { NSDeleteFunctionKey, Qt::Key_Delete }, |
|
533 { NSHomeFunctionKey, Qt::Key_Home }, |
|
534 { NSEndFunctionKey, Qt::Key_End }, |
|
535 { NSPageUpFunctionKey, Qt::Key_PageUp }, |
|
536 { NSPageDownFunctionKey, Qt::Key_PageDown }, |
|
537 { NSPrintScreenFunctionKey, Qt::Key_Print }, |
|
538 { NSScrollLockFunctionKey, Qt::Key_ScrollLock }, |
|
539 { NSPauseFunctionKey, Qt::Key_Pause }, |
|
540 { NSSysReqFunctionKey, Qt::Key_SysReq }, |
|
541 { NSMenuFunctionKey, Qt::Key_Menu }, |
|
542 { NSHelpFunctionKey, Qt::Key_Help }, |
|
543 }; |
|
544 static const KeyPair * const end = entries + NumEntries; |
|
545 const KeyPair *i = qBinaryFind(entries, end, keyCode); |
|
546 if (i == end) |
|
547 return Qt::Key(keyCode.unicode()); |
|
548 return i->qtKey; |
|
549 } |
|
550 |
|
551 Qt::KeyboardModifiers qt_cocoaModifiers2QtModifiers(ulong modifierFlags) |
|
552 { |
|
553 Qt::KeyboardModifiers qtMods =Qt::NoModifier; |
|
554 if (modifierFlags & NSShiftKeyMask) |
|
555 qtMods |= Qt::ShiftModifier; |
|
556 if (modifierFlags & NSControlKeyMask) |
|
557 qtMods |= Qt::MetaModifier; |
|
558 if (modifierFlags & NSAlternateKeyMask) |
|
559 qtMods |= Qt::AltModifier; |
|
560 if (modifierFlags & NSCommandKeyMask) |
|
561 qtMods |= Qt::ControlModifier; |
|
562 if (modifierFlags & NSNumericPadKeyMask) |
|
563 qtMods |= Qt::KeypadModifier; |
|
564 return qtMods; |
|
565 } |
|
566 |
|
567 Qt::KeyboardModifiers qt_cocoaDragOperation2QtModifiers(uint dragOperations) |
|
568 { |
|
569 Qt::KeyboardModifiers qtMods =Qt::NoModifier; |
|
570 if (dragOperations & NSDragOperationLink) |
|
571 qtMods |= Qt::MetaModifier; |
|
572 if (dragOperations & NSDragOperationGeneric) |
|
573 qtMods |= Qt::ControlModifier; |
|
574 if (dragOperations & NSDragOperationCopy) |
|
575 qtMods |= Qt::AltModifier; |
|
576 return qtMods; |
|
577 } |
|
578 |
|
579 static inline QEvent::Type cocoaEvent2QtEvent(NSUInteger eventType) |
|
580 { |
|
581 // Handle the trivial cases that can be determined from the type. |
|
582 switch (eventType) { |
|
583 case NSKeyDown: |
|
584 return QEvent::KeyPress; |
|
585 case NSKeyUp: |
|
586 return QEvent::KeyRelease; |
|
587 case NSLeftMouseDown: |
|
588 case NSRightMouseDown: |
|
589 case NSOtherMouseDown: |
|
590 return QEvent::MouseButtonPress; |
|
591 case NSLeftMouseUp: |
|
592 case NSRightMouseUp: |
|
593 case NSOtherMouseUp: |
|
594 return QEvent::MouseButtonRelease; |
|
595 case NSMouseMoved: |
|
596 case NSLeftMouseDragged: |
|
597 case NSRightMouseDragged: |
|
598 case NSOtherMouseDragged: |
|
599 return QEvent::MouseMove; |
|
600 case NSScrollWheel: |
|
601 return QEvent::Wheel; |
|
602 } |
|
603 return QEvent::None; |
|
604 } |
|
605 |
|
606 static bool mustUseCocoaKeyEvent() |
|
607 { |
|
608 QCFType<TISInputSourceRef> source = TISCopyCurrentKeyboardInputSource(); |
|
609 return TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData) == 0; |
|
610 } |
|
611 |
|
612 bool qt_dispatchKeyEventWithCocoa(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent) |
|
613 { |
|
614 NSEvent *event = static_cast<NSEvent *>(keyEvent); |
|
615 NSString *keyChars = [event charactersIgnoringModifiers]; |
|
616 int keyLength = [keyChars length]; |
|
617 if (keyLength == 0) |
|
618 return false; // Dead Key, nothing to do! |
|
619 bool ignoreText = false; |
|
620 Qt::Key qtKey = Qt::Key_unknown; |
|
621 if (keyLength == 1) { |
|
622 QChar ch([keyChars characterAtIndex:0]); |
|
623 if (ch.isLower()) |
|
624 ch = ch.toUpper(); |
|
625 qtKey = cocoaKey2QtKey(ch); |
|
626 // Do not set the text for Function-Key Unicodes characters (0xF700–0xF8FF). |
|
627 ignoreText = (ch.unicode() >= 0xF700 && ch.unicode() <= 0xF8FF); |
|
628 } |
|
629 Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([event modifierFlags]); |
|
630 QString text; |
|
631 |
|
632 // To quote from the Carbon port: This is actually wrong--but it is the best that |
|
633 // can be done for now because of the Control/Meta mapping issues |
|
634 // (we always get text on the Mac) |
|
635 if (!ignoreText && !(keyMods & (Qt::ControlModifier | Qt::MetaModifier))) |
|
636 text = QCFString::toQString(reinterpret_cast<CFStringRef>(keyChars)); |
|
637 |
|
638 UInt32 macScanCode = 1; |
|
639 QKeyEventEx ke(cocoaEvent2QtEvent([event type]), qtKey, keyMods, text, [event isARepeat], qMax(1, keyLength), |
|
640 macScanCode, [event keyCode], [event modifierFlags]); |
|
641 qt_sendSpontaneousEvent(widgetToGetEvent, &ke); |
|
642 return ke.isAccepted(); |
|
643 } |
|
644 #endif |
|
645 |
|
646 // Helper to share code between QCocoaWindow and QCocoaView |
|
647 bool qt_dispatchKeyEvent(void * /*NSEvent * */ keyEvent, QWidget *widgetToGetEvent) |
|
648 { |
|
649 #ifndef QT_MAC_USE_COCOA |
|
650 Q_UNUSED(keyEvent); |
|
651 Q_UNUSED(widgetToGetEvent); |
|
652 return false; |
|
653 #else |
|
654 NSEvent *event = static_cast<NSEvent *>(keyEvent); |
|
655 EventRef key_event = static_cast<EventRef>(const_cast<void *>([event eventRef])); |
|
656 Q_ASSERT(key_event); |
|
657 if ([event type] == NSKeyDown) { |
|
658 qt_keymapper_private()->updateKeyMap(0, key_event, 0); |
|
659 } |
|
660 if (widgetToGetEvent == 0) |
|
661 return false; |
|
662 |
|
663 if (qt_mac_sendMacEventToWidget(widgetToGetEvent, key_event)) |
|
664 return true; |
|
665 |
|
666 if (mustUseCocoaKeyEvent()) |
|
667 return qt_dispatchKeyEventWithCocoa(keyEvent, widgetToGetEvent); |
|
668 bool isAccepted; |
|
669 qt_keymapper_private()->translateKeyEvent(widgetToGetEvent, 0, key_event, &isAccepted, true); |
|
670 return isAccepted; |
|
671 #endif |
|
672 } |
|
673 |
|
674 void qt_dispatchModifiersChanged(void * /*NSEvent * */flagsChangedEvent, QWidget *widgetToGetEvent) |
|
675 { |
|
676 #ifndef QT_MAC_USE_COCOA |
|
677 Q_UNUSED(flagsChangedEvent); |
|
678 Q_UNUSED(widgetToGetEvent); |
|
679 #else |
|
680 UInt32 modifiers = 0; |
|
681 // Sync modifiers with Qt |
|
682 NSEvent *event = static_cast<NSEvent *>(flagsChangedEvent); |
|
683 EventRef key_event = static_cast<EventRef>(const_cast<void *>([event eventRef])); |
|
684 Q_ASSERT(key_event); |
|
685 GetEventParameter(key_event, kEventParamKeyModifiers, typeUInt32, 0, |
|
686 sizeof(modifiers), 0, &modifiers); |
|
687 extern void qt_mac_send_modifiers_changed(quint32 modifiers, QObject *object); |
|
688 qt_mac_send_modifiers_changed(modifiers, widgetToGetEvent); |
|
689 #endif |
|
690 } |
|
691 |
|
692 |
|
693 QPointF flipPoint(const NSPoint &p) |
|
694 { |
|
695 return QPointF(p.x, flipYCoordinate(p.y)); |
|
696 } |
|
697 |
|
698 NSPoint flipPoint(const QPoint &p) |
|
699 { |
|
700 return NSMakePoint(p.x(), flipYCoordinate(p.y())); |
|
701 } |
|
702 |
|
703 NSPoint flipPoint(const QPointF &p) |
|
704 { |
|
705 return NSMakePoint(p.x(), flipYCoordinate(p.y())); |
|
706 } |
|
707 |
|
708 void qt_mac_dispatchNCMouseMessage(void * /* NSWindow* */eventWindow, void * /* NSEvent* */mouseEvent, |
|
709 QWidget *widgetToGetEvent, bool &leftButtonIsRightButton) |
|
710 { |
|
711 #ifndef QT_MAC_USE_COCOA |
|
712 Q_UNUSED(eventWindow); |
|
713 Q_UNUSED(mouseEvent); |
|
714 Q_UNUSED(widgetToGetEvent); |
|
715 Q_UNUSED(leftButtonIsRightButton); |
|
716 #else |
|
717 if (widgetToGetEvent == 0) |
|
718 return; |
|
719 NSWindow *window = static_cast<NSWindow *>(eventWindow); |
|
720 NSEvent *event = static_cast<NSEvent *>(mouseEvent); |
|
721 NSEventType evtType = [event type]; |
|
722 |
|
723 QPoint qlocalPoint; |
|
724 QPoint qglobalPoint; |
|
725 bool processThisEvent = false; |
|
726 bool fakeNCEvents = false; |
|
727 bool fakeMouseEvents = false; |
|
728 |
|
729 // Check if this is a mouse event. |
|
730 if (evtType == NSLeftMouseDown || evtType == NSLeftMouseUp |
|
731 || evtType == NSRightMouseDown || evtType == NSRightMouseUp |
|
732 || evtType == NSOtherMouseDown || evtType == NSOtherMouseUp |
|
733 || evtType == NSMouseMoved || evtType == NSLeftMouseDragged |
|
734 || evtType == NSRightMouseDragged || evtType == NSOtherMouseDragged) { |
|
735 // Check if we want to pass this message to another window |
|
736 if (mac_mouse_grabber && mac_mouse_grabber != widgetToGetEvent) { |
|
737 NSWindow *grabWindow = static_cast<NSWindow *>(qt_mac_window_for(mac_mouse_grabber)); |
|
738 if (window != grabWindow) { |
|
739 window = grabWindow; |
|
740 widgetToGetEvent = mac_mouse_grabber; |
|
741 fakeNCEvents = true; |
|
742 } |
|
743 } |
|
744 // Dont generate normal NC mouse events for Left Button dragged |
|
745 if(evtType != NSLeftMouseDragged || fakeNCEvents) { |
|
746 NSPoint windowPoint = [event locationInWindow]; |
|
747 NSPoint globalPoint = [[event window] convertBaseToScreen:windowPoint]; |
|
748 NSRect frameRect = [window frame]; |
|
749 if (fakeNCEvents || NSMouseInRect(globalPoint, frameRect, NO)) { |
|
750 NSRect contentRect = [window contentRectForFrameRect:frameRect]; |
|
751 if (fakeNCEvents || !NSMouseInRect(globalPoint, contentRect, NO)) { |
|
752 qglobalPoint = QPoint(flipPoint(globalPoint).toPoint()); |
|
753 qlocalPoint = widgetToGetEvent->mapFromGlobal(qglobalPoint); |
|
754 processThisEvent = true; |
|
755 } |
|
756 } |
|
757 } |
|
758 } |
|
759 // This is not an NC area mouse message. |
|
760 if (!processThisEvent) |
|
761 return; |
|
762 // If the window is frame less, generate fake mouse events instead. (floating QToolBar) |
|
763 if (fakeNCEvents && (widgetToGetEvent->window()->windowFlags() & Qt::FramelessWindowHint)) |
|
764 fakeMouseEvents = true; |
|
765 |
|
766 Qt::MouseButton button; |
|
767 QEvent::Type eventType; |
|
768 // Convert to Qt::Event type |
|
769 switch (evtType) { |
|
770 case NSLeftMouseDown: |
|
771 button = Qt::LeftButton; |
|
772 eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonPress |
|
773 : QEvent::MouseButtonPress; |
|
774 break; |
|
775 case NSLeftMouseUp: |
|
776 button = Qt::LeftButton; |
|
777 eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonRelease |
|
778 : QEvent::MouseButtonRelease; |
|
779 break; |
|
780 case NSRightMouseDown: |
|
781 button = Qt::RightButton; |
|
782 eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonPress |
|
783 : QEvent::MouseButtonPress; |
|
784 break; |
|
785 case NSRightMouseUp: |
|
786 button = Qt::RightButton; |
|
787 eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonRelease |
|
788 : QEvent::MouseButtonRelease; |
|
789 break; |
|
790 case NSOtherMouseDown: |
|
791 button = cocoaButton2QtButton([event buttonNumber]); |
|
792 eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonPress |
|
793 : QEvent::MouseButtonPress; |
|
794 break; |
|
795 case NSOtherMouseUp: |
|
796 button = cocoaButton2QtButton([event buttonNumber]); |
|
797 eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonRelease |
|
798 : QEvent::MouseButtonRelease; |
|
799 break; |
|
800 case NSMouseMoved: |
|
801 button = Qt::NoButton; |
|
802 eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseMove |
|
803 : QEvent::MouseMove; |
|
804 break; |
|
805 case NSLeftMouseDragged: |
|
806 button = Qt::LeftButton; |
|
807 eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseMove |
|
808 : QEvent::MouseMove; |
|
809 break; |
|
810 case NSRightMouseDragged: |
|
811 button = Qt::RightButton; |
|
812 eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseMove |
|
813 : QEvent::MouseMove; |
|
814 break; |
|
815 case NSOtherMouseDragged: |
|
816 button = cocoaButton2QtButton([event buttonNumber]); |
|
817 eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseMove |
|
818 : QEvent::MouseMove; |
|
819 break; |
|
820 default: |
|
821 qWarning("not handled! Non client area mouse message"); |
|
822 return; |
|
823 } |
|
824 |
|
825 Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([event modifierFlags]); |
|
826 if (eventType == QEvent::NonClientAreaMouseButtonPress || eventType == QEvent::MouseButtonPress) { |
|
827 NSInteger clickCount = [event clickCount]; |
|
828 if (clickCount % 2 == 0) |
|
829 eventType = (!fakeMouseEvents) ? QEvent::NonClientAreaMouseButtonDblClick |
|
830 : QEvent::MouseButtonDblClick; |
|
831 if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) { |
|
832 button = Qt::RightButton; |
|
833 leftButtonIsRightButton = true; |
|
834 } |
|
835 } else if (eventType == QEvent::NonClientAreaMouseButtonRelease || eventType == QEvent::MouseButtonRelease) { |
|
836 if (button == Qt::LeftButton && leftButtonIsRightButton) { |
|
837 button = Qt::RightButton; |
|
838 leftButtonIsRightButton = false; |
|
839 } |
|
840 } |
|
841 QMouseEvent qme(eventType, qlocalPoint, qglobalPoint, button, button, keyMods); |
|
842 qt_sendSpontaneousEvent(widgetToGetEvent, &qme); |
|
843 #endif |
|
844 } |
|
845 |
|
846 bool qt_mac_handleMouseEvent(void * /* NSView * */view, void * /* NSEvent * */event, QEvent::Type eventType, Qt::MouseButton button) |
|
847 { |
|
848 #ifndef QT_MAC_USE_COCOA |
|
849 Q_UNUSED(view); |
|
850 Q_UNUSED(event); |
|
851 Q_UNUSED(eventType); |
|
852 Q_UNUSED(button); |
|
853 return false; |
|
854 #else |
|
855 QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view); |
|
856 NSEvent *theEvent = static_cast<NSEvent *>(event); |
|
857 |
|
858 // Give the Input Manager a chance to process the mouse events. |
|
859 NSInputManager *currentIManager = [NSInputManager currentInputManager]; |
|
860 if (currentIManager && [currentIManager wantsToHandleMouseEvents]) { |
|
861 [currentIManager handleMouseEvent:theEvent]; |
|
862 } |
|
863 |
|
864 // Handle tablet events (if any) first. |
|
865 if (qt_mac_handleTabletEvent(theView, theEvent)) { |
|
866 // Tablet event was handled. In Qt we aren't supposed to send the mouse event. |
|
867 return true; |
|
868 } |
|
869 |
|
870 NSPoint windowPoint = [theEvent locationInWindow]; |
|
871 NSPoint globalPoint = [[theEvent window] convertBaseToScreen:windowPoint]; |
|
872 |
|
873 // Find the widget that *should* get the event (e.g., maybe it was a pop-up, |
|
874 // they always get the mouse event). |
|
875 QWidget *qwidget = [theView qt_qwidget]; |
|
876 QWidget *widgetToGetMouse = qwidget; |
|
877 QWidget *popup = qAppInstance()->activePopupWidget(); |
|
878 NSView *tmpView = theView; |
|
879 if (mac_mouse_grabber && mac_mouse_grabber != widgetToGetMouse) { |
|
880 widgetToGetMouse = mac_mouse_grabber; |
|
881 tmpView = qt_mac_nativeview_for(widgetToGetMouse); |
|
882 } |
|
883 |
|
884 if (popup && popup != qwidget->window()) { |
|
885 widgetToGetMouse = popup; |
|
886 tmpView = qt_mac_nativeview_for(popup); |
|
887 windowPoint = [[tmpView window] convertScreenToBase:globalPoint]; |
|
888 |
|
889 QPoint qWindowPoint(windowPoint.x, windowPoint.y); |
|
890 if (widgetToGetMouse->rect().contains(qWindowPoint)) { |
|
891 // Keeping the mouse pressed on a combobox button will make |
|
892 // the popup pop in front of the mouse. But all mouse events |
|
893 // will be sendt to the button. Since we want mouse events |
|
894 // to be sendt to widgets inside the popup, we search for the |
|
895 // widget in front of the mouse: |
|
896 tmpView = [tmpView hitTest:windowPoint]; |
|
897 if (!tmpView) |
|
898 return false; |
|
899 widgetToGetMouse = |
|
900 [static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(tmpView) qt_qwidget]; |
|
901 } |
|
902 } |
|
903 |
|
904 NSPoint localPoint = [tmpView convertPoint:windowPoint fromView:nil]; |
|
905 QPoint qlocalPoint(localPoint.x, localPoint.y); |
|
906 |
|
907 EventRef carbonEvent = static_cast<EventRef>(const_cast<void *>([theEvent eventRef])); |
|
908 if (qt_mac_sendMacEventToWidget(widgetToGetMouse, carbonEvent)) |
|
909 return true; |
|
910 |
|
911 // Yay! All the special cases are handled, it really is just a normal mouse event. |
|
912 Qt::KeyboardModifiers keyMods = qt_cocoaModifiers2QtModifiers([theEvent modifierFlags]); |
|
913 NSInteger clickCount = [theEvent clickCount]; |
|
914 Qt::MouseButtons buttons = 0; |
|
915 { |
|
916 UInt32 mac_buttons; |
|
917 if (GetEventParameter(carbonEvent, kEventParamMouseChord, typeUInt32, 0, |
|
918 sizeof(mac_buttons), 0, &mac_buttons) == noErr) |
|
919 buttons = qt_mac_get_buttons(mac_buttons); |
|
920 } |
|
921 switch (eventType) { |
|
922 default: |
|
923 qWarning("not handled! %d", eventType); |
|
924 break; |
|
925 case QEvent::MouseMove: |
|
926 break; |
|
927 case QEvent::MouseButtonPress: |
|
928 [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->view = theView; |
|
929 [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->theEvent = theEvent; |
|
930 #ifndef QT_NAMESPACE |
|
931 Q_ASSERT(clickCount > 0); |
|
932 #endif |
|
933 if (clickCount % 2 == 0) |
|
934 eventType = QEvent::MouseButtonDblClick; |
|
935 if (button == Qt::LeftButton && (keyMods & Qt::MetaModifier)) { |
|
936 button = Qt::RightButton; |
|
937 [theView qt_setLeftButtonIsRightButton: true]; |
|
938 } |
|
939 break; |
|
940 case QEvent::MouseButtonRelease: |
|
941 if (button == Qt::LeftButton && [theView qt_leftButtonIsRightButton]) { |
|
942 button = Qt::RightButton; |
|
943 [theView qt_setLeftButtonIsRightButton: false]; |
|
944 } |
|
945 break; |
|
946 } |
|
947 [QT_MANGLE_NAMESPACE(QCocoaView) currentMouseEvent]->localPoint = localPoint; |
|
948 QPoint qglobalPoint(flipPoint(globalPoint).toPoint()); |
|
949 QMouseEvent qme(eventType, qlocalPoint, qglobalPoint, button, buttons, keyMods); |
|
950 qt_sendSpontaneousEvent(widgetToGetMouse, &qme); |
|
951 if (eventType == QEvent::MouseButtonPress && button == Qt::RightButton) { |
|
952 QContextMenuEvent qcme(QContextMenuEvent::Mouse, qlocalPoint, qglobalPoint, keyMods); |
|
953 qt_sendSpontaneousEvent(widgetToGetMouse, &qcme); |
|
954 } |
|
955 return qme.isAccepted(); |
|
956 #endif |
|
957 } |
|
958 |
|
959 bool qt_mac_handleTabletEvent(void * /*QCocoaView * */view, void * /*NSEvent * */tabletEvent) |
|
960 { |
|
961 #ifndef QT_MAC_USE_COCOA |
|
962 Q_UNUSED(view); |
|
963 Q_UNUSED(tabletEvent); |
|
964 return false; |
|
965 #else |
|
966 QT_MANGLE_NAMESPACE(QCocoaView) *theView = static_cast<QT_MANGLE_NAMESPACE(QCocoaView) *>(view); |
|
967 NSView *theNSView = static_cast<NSView *>(view); |
|
968 NSEvent *theTabletEvent = static_cast<NSEvent *>(tabletEvent); |
|
969 |
|
970 NSEventType eventType = [theTabletEvent type]; |
|
971 if (eventType != NSTabletPoint && [theTabletEvent subtype] != NSTabletPointEventSubtype) |
|
972 return false; // Not a tablet event. |
|
973 |
|
974 NSPoint windowPoint = [theTabletEvent locationInWindow]; |
|
975 NSPoint globalPoint = [[theTabletEvent window] convertBaseToScreen:windowPoint]; |
|
976 |
|
977 QWidget *qwidget = [theView qt_qwidget]; |
|
978 QWidget *widgetToGetMouse = qwidget; |
|
979 QWidget *popup = qAppInstance()->activePopupWidget(); |
|
980 if (popup && popup != qwidget->window()) |
|
981 widgetToGetMouse = popup; |
|
982 |
|
983 if (qt_mac_sendMacEventToWidget(widgetToGetMouse, |
|
984 static_cast<EventRef>(const_cast<void *>([theTabletEvent eventRef])))) |
|
985 return true; |
|
986 if (widgetToGetMouse != qwidget) { |
|
987 theNSView = qt_mac_nativeview_for(widgetToGetMouse); |
|
988 windowPoint = [[theNSView window] convertScreenToBase:globalPoint]; |
|
989 } |
|
990 NSPoint localPoint = [theNSView convertPoint:windowPoint fromView:nil]; |
|
991 // Tablet events do not handle WA_TransparentForMouseEvents ATM |
|
992 // In theory, people who set the WA_TransparentForMouseEvents attribute won't handle |
|
993 // tablet events either in which case they will fall into the mouse event case and get |
|
994 // them passed on. This will NOT handle the raw events, but that might not be a big problem. |
|
995 |
|
996 const QMacTabletHash *tabletHash = qt_mac_tablet_hash(); |
|
997 if (!tabletHash->contains([theTabletEvent deviceID])) { |
|
998 qWarning("QCocoaView handleTabletEvent: This tablet device is unknown" |
|
999 " (received no proximity event for it). Discarding event."); |
|
1000 return false; |
|
1001 } |
|
1002 const QTabletDeviceData &deviceData = tabletHash->value([theTabletEvent deviceID]); |
|
1003 |
|
1004 |
|
1005 QEvent::Type qType; |
|
1006 switch (eventType) { |
|
1007 case NSLeftMouseDown: |
|
1008 case NSRightMouseDown: |
|
1009 qType = QEvent::TabletPress; |
|
1010 break; |
|
1011 case NSLeftMouseUp: |
|
1012 case NSRightMouseUp: |
|
1013 qType = QEvent::TabletRelease; |
|
1014 break; |
|
1015 case NSMouseMoved: |
|
1016 case NSTabletPoint: |
|
1017 case NSLeftMouseDragged: |
|
1018 case NSRightMouseDragged: |
|
1019 default: |
|
1020 qType = QEvent::TabletMove; |
|
1021 break; |
|
1022 } |
|
1023 |
|
1024 qreal pressure; |
|
1025 if (eventType != NSMouseMoved) { |
|
1026 pressure = [theTabletEvent pressure]; |
|
1027 } else { |
|
1028 pressure = 0.0; |
|
1029 } |
|
1030 |
|
1031 NSPoint tilt = [theTabletEvent tilt]; |
|
1032 int xTilt = qRound(tilt.x * 60.0); |
|
1033 int yTilt = qRound(tilt.y * -60.0); |
|
1034 qreal tangentialPressure = 0; |
|
1035 qreal rotation = 0; |
|
1036 int z = 0; |
|
1037 if (deviceData.capabilityMask & 0x0200) |
|
1038 z = [theTabletEvent absoluteZ]; |
|
1039 |
|
1040 if (deviceData.capabilityMask & 0x0800) |
|
1041 tangentialPressure = [theTabletEvent tangentialPressure]; |
|
1042 |
|
1043 rotation = [theTabletEvent rotation]; |
|
1044 QPointF hiRes = flipPoint(globalPoint); |
|
1045 QTabletEvent qtabletEvent(qType, QPoint(localPoint.x, localPoint.y), |
|
1046 hiRes.toPoint(), hiRes, |
|
1047 deviceData.tabletDeviceType, deviceData.tabletPointerType, |
|
1048 pressure, xTilt, yTilt, tangentialPressure, rotation, z, |
|
1049 qt_cocoaModifiers2QtModifiers([theTabletEvent modifierFlags]), |
|
1050 deviceData.tabletUniqueID); |
|
1051 |
|
1052 qt_sendSpontaneousEvent(widgetToGetMouse, &qtabletEvent); |
|
1053 return qtabletEvent.isAccepted(); |
|
1054 #endif |
|
1055 } |
|
1056 |
|
1057 void qt_mac_updateContentBorderMetricts(void * /*OSWindowRef */window, const ::HIContentBorderMetrics &metrics) |
|
1058 { |
|
1059 OSWindowRef theWindow = static_cast<OSWindowRef>(window); |
|
1060 #if !defined(QT_MAC_USE_COCOA) |
|
1061 # if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 |
|
1062 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { |
|
1063 ::HIWindowSetContentBorderThickness(theWindow, &metrics); |
|
1064 } |
|
1065 # else |
|
1066 Q_UNUSED(window); |
|
1067 Q_UNUSED(metrics); |
|
1068 # endif |
|
1069 #else |
|
1070 if ([theWindow styleMask] & NSTexturedBackgroundWindowMask) |
|
1071 [theWindow setContentBorderThickness:metrics.top forEdge:NSMaxYEdge]; |
|
1072 [theWindow setContentBorderThickness:metrics.bottom forEdge:NSMinYEdge]; |
|
1073 #endif |
|
1074 } |
|
1075 |
|
1076 void qt_mac_showBaseLineSeparator(void * /*OSWindowRef */window, bool show) |
|
1077 { |
|
1078 #if QT_MAC_USE_COCOA |
|
1079 QMacCocoaAutoReleasePool pool; |
|
1080 OSWindowRef theWindow = static_cast<OSWindowRef>(window); |
|
1081 NSToolbar *macToolbar = [theWindow toolbar]; |
|
1082 if (macToolbar) |
|
1083 [macToolbar setShowsBaselineSeparator: show]; |
|
1084 #endif |
|
1085 } |
|
1086 |
|
1087 QStringList qt_mac_NSArrayToQStringList(void *nsarray) |
|
1088 { |
|
1089 QStringList result; |
|
1090 NSArray *array = static_cast<NSArray *>(nsarray); |
|
1091 for (NSUInteger i=0; i<[array count]; ++i) |
|
1092 result << qt_mac_NSStringToQString([array objectAtIndex:i]); |
|
1093 return result; |
|
1094 } |
|
1095 |
|
1096 void *qt_mac_QStringListToNSMutableArrayVoid(const QStringList &list) |
|
1097 { |
|
1098 NSMutableArray *result = [NSMutableArray arrayWithCapacity:list.size()]; |
|
1099 for (int i=0; i<list.size(); ++i){ |
|
1100 [result addObject:reinterpret_cast<const NSString *>(QCFString::toCFStringRef(list[i]))]; |
|
1101 } |
|
1102 return result; |
|
1103 } |
|
1104 |
|
1105 void qt_syncCocoaTitleBarButtons(OSWindowRef window, QWidget *widgetForWindow) |
|
1106 { |
|
1107 if (!widgetForWindow) |
|
1108 return; |
|
1109 |
|
1110 Qt::WindowFlags flags = widgetForWindow->windowFlags(); |
|
1111 bool customize = flags & Qt::CustomizeWindowHint; |
|
1112 |
|
1113 NSButton *btn = [window standardWindowButton:NSWindowZoomButton]; |
|
1114 // BOOL is not an int, so the bitwise AND doesn't work. |
|
1115 bool go = uint(customize && !(flags & Qt::WindowMaximizeButtonHint)) == 0; |
|
1116 [btn setEnabled:go]; |
|
1117 |
|
1118 btn = [window standardWindowButton:NSWindowMiniaturizeButton]; |
|
1119 go = uint(customize && !(flags & Qt::WindowMinimizeButtonHint)) == 0; |
|
1120 [btn setEnabled:go]; |
|
1121 |
|
1122 btn = [window standardWindowButton:NSWindowCloseButton]; |
|
1123 go = uint(customize && !(flags & Qt::WindowSystemMenuHint |
|
1124 || flags & Qt::WindowCloseButtonHint)) == 0; |
|
1125 [btn setEnabled:go]; |
|
1126 |
|
1127 [window setShowsToolbarButton:uint(flags & Qt::MacWindowToolBarButtonHint) != 0]; |
|
1128 } |
|
1129 |
|
1130 // Carbon: Make sure you call QDEndContext on the context when done with it. |
|
1131 CGContextRef qt_mac_graphicsContextFor(QWidget *widget) |
|
1132 { |
|
1133 if (!widget) |
|
1134 return 0; |
|
1135 |
|
1136 #ifndef QT_MAC_USE_COCOA |
|
1137 CGContextRef context; |
|
1138 CGrafPtr port = GetWindowPort(qt_mac_window_for(widget)); |
|
1139 QDBeginCGContext(port, &context); |
|
1140 #else |
|
1141 CGContextRef context = (CGContextRef)[[NSGraphicsContext graphicsContextWithWindow:qt_mac_window_for(widget)] graphicsPort]; |
|
1142 #endif |
|
1143 return context; |
|
1144 } |
|
1145 |
|
1146 CGFloat qt_mac_get_scalefactor() |
|
1147 { |
|
1148 #ifndef QT_MAC_USE_COCOA |
|
1149 return HIGetScaleFactor(); |
|
1150 #else |
|
1151 return [[NSScreen mainScreen] userSpaceScaleFactor]; |
|
1152 #endif |
|
1153 } |
|
1154 |
|
1155 QString qt_mac_get_pasteboardString() |
|
1156 { |
|
1157 QMacCocoaAutoReleasePool pool; |
|
1158 NSPasteboard *pb = [NSPasteboard generalPasteboard]; |
|
1159 NSString *text = [pb stringForType:NSStringPboardType]; |
|
1160 if (text) { |
|
1161 return qt_mac_NSStringToQString(text); |
|
1162 } else { |
|
1163 return QString(); |
|
1164 } |
|
1165 } |
|
1166 |
|
1167 QPixmap qt_mac_convert_iconref(const IconRef icon, int width, int height) |
|
1168 { |
|
1169 QPixmap ret(width, height); |
|
1170 ret.fill(QColor(0, 0, 0, 0)); |
|
1171 |
|
1172 CGRect rect = CGRectMake(0, 0, width, height); |
|
1173 |
|
1174 CGContextRef ctx = qt_mac_cg_context(&ret); |
|
1175 CGAffineTransform old_xform = CGContextGetCTM(ctx); |
|
1176 CGContextConcatCTM(ctx, CGAffineTransformInvert(old_xform)); |
|
1177 CGContextConcatCTM(ctx, CGAffineTransformIdentity); |
|
1178 |
|
1179 ::RGBColor b; |
|
1180 b.blue = b.green = b.red = 255*255; |
|
1181 PlotIconRefInContext(ctx, &rect, kAlignNone, kTransformNone, &b, kPlotIconRefNormalFlags, icon); |
|
1182 CGContextRelease(ctx); |
|
1183 return ret; |
|
1184 } |
|
1185 |
|
1186 void qt_mac_constructQIconFromIconRef(const IconRef icon, const IconRef overlayIcon, QIcon *retIcon, QStyle::StandardPixmap standardIcon) |
|
1187 { |
|
1188 int size = 16; |
|
1189 while (size <= 128) { |
|
1190 |
|
1191 const QString cacheKey = QLatin1String("qt_mac_constructQIconFromIconRef") + QString::number(standardIcon) + QString::number(size); |
|
1192 QPixmap mainIcon; |
|
1193 if (standardIcon >= QStyle::SP_CustomBase) { |
|
1194 mainIcon = qt_mac_convert_iconref(icon, size, size); |
|
1195 } else if (QPixmapCache::find(cacheKey, mainIcon) == false) { |
|
1196 mainIcon = qt_mac_convert_iconref(icon, size, size); |
|
1197 QPixmapCache::insert(cacheKey, mainIcon); |
|
1198 } |
|
1199 |
|
1200 if (overlayIcon) { |
|
1201 int littleSize = size / 2; |
|
1202 QPixmap overlayPix = qt_mac_convert_iconref(overlayIcon, littleSize, littleSize); |
|
1203 QPainter painter(&mainIcon); |
|
1204 painter.drawPixmap(size - littleSize, size - littleSize, overlayPix); |
|
1205 } |
|
1206 |
|
1207 retIcon->addPixmap(mainIcon); |
|
1208 size += size; // 16 -> 32 -> 64 -> 128 |
|
1209 } |
|
1210 } |
|
1211 |
|
1212 void qt_mac_menu_collapseSeparators(void */*NSMenu **/ theMenu, bool collapse) |
|
1213 { |
|
1214 OSMenuRef menu = static_cast<OSMenuRef>(theMenu); |
|
1215 if (collapse) { |
|
1216 bool previousIsSeparator = true; // setting to true kills all the separators placed at the top. |
|
1217 NSMenuItem *previousItem = nil; |
|
1218 |
|
1219 NSArray *itemArray = [menu itemArray]; |
|
1220 for (unsigned int i = 0; i < [itemArray count]; ++i) { |
|
1221 NSMenuItem *item = reinterpret_cast<NSMenuItem *>([itemArray objectAtIndex:i]); |
|
1222 if ([item isSeparatorItem]) { |
|
1223 [item setHidden:previousIsSeparator]; |
|
1224 } |
|
1225 |
|
1226 if (![item isHidden]) { |
|
1227 previousItem = item; |
|
1228 previousIsSeparator = ([previousItem isSeparatorItem]); |
|
1229 } |
|
1230 } |
|
1231 |
|
1232 // We now need to check the final item since we don't want any separators at the end of the list. |
|
1233 if (previousItem && previousIsSeparator) |
|
1234 [previousItem setHidden:YES]; |
|
1235 } else { |
|
1236 NSArray *itemArray = [menu itemArray]; |
|
1237 for (unsigned int i = 0; i < [itemArray count]; ++i) { |
|
1238 NSMenuItem *item = reinterpret_cast<NSMenuItem *>([itemArray objectAtIndex:i]); |
|
1239 if (QAction *action = reinterpret_cast<QAction *>([item tag])) |
|
1240 [item setHidden:!action->isVisible()]; |
|
1241 } |
|
1242 } |
|
1243 } |
|
1244 |
|
1245 #ifdef QT_MAC_USE_COCOA |
|
1246 void qt_cocoaChangeOverrideCursor(const QCursor &cursor) |
|
1247 { |
|
1248 QMacCocoaAutoReleasePool pool; |
|
1249 [static_cast<NSCursor *>(qt_mac_nsCursorForQCursor(cursor)) set]; |
|
1250 } |
|
1251 #endif |
|
1252 |
|
1253 QT_END_NAMESPACE |