source: trunk/src/gui/kernel/qwidget_mac.mm@ 677

Last change on this file since 677 was 651, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 192.3 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/****************************************************************************
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/qt_mac_p.h>
77#include <private/qeventdispatcher_mac_p.h>
78
79#include "qapplication.h"
80#include "qapplication_p.h"
81#include "qbitmap.h"
82#include "qcursor.h"
83#include "qdesktopwidget.h"
84#include "qevent.h"
85#include "qfileinfo.h"
86#include "qimage.h"
87#include "qlayout.h"
88#include "qmenubar.h"
89#include <private/qbackingstore_p.h>
90#include <private/qwindowsurface_mac_p.h>
91#include <private/qpaintengine_mac_p.h>
92#include "qpainter.h"
93#include "qstyle.h"
94#include "qtimer.h"
95#include "qfocusframe.h"
96#include "qdebug.h"
97#include <private/qmainwindowlayout_p.h>
98
99#include <private/qabstractscrollarea_p.h>
100#include <qabstractscrollarea.h>
101#include <ApplicationServices/ApplicationServices.h>
102#include <limits.h>
103#include <private/qt_cocoa_helpers_mac_p.h>
104#include <private/qcocoaview_mac_p.h>
105#include <private/qcocoawindow_mac_p.h>
106#include <private/qcocoawindowdelegate_mac_p.h>
107#include <private/qcocoapanel_mac_p.h>
108
109#include "qwidget_p.h"
110#include "qevent_p.h"
111#include "qdnd_p.h"
112#include <QtGui/qgraphicsproxywidget.h>
113#include "qmainwindow.h"
114
115QT_BEGIN_NAMESPACE
116
117#define XCOORD_MAX 16383
118#define WRECT_MAX 8191
119
120#ifndef QT_MAC_USE_COCOA
121
122extern "C" {
123 extern OSStatus _HIViewScrollRectWithOptions(HIViewRef, const HIRect *, CGFloat, CGFloat,
124 OptionBits) __attribute__ ((weak));
125}
126#define kHIViewScrollRectAdjustInvalid 1
127#define kHIViewScrollRectDontInvalidateRevealedArea 2
128#endif
129
130
131/*****************************************************************************
132 QWidget debug facilities
133 *****************************************************************************/
134//#define DEBUG_WINDOW_RGNS
135//#define DEBUG_WINDOW_CREATE
136//#define DEBUG_WINDOW_STATE
137//#define DEBUG_WIDGET_PAINT
138
139/*****************************************************************************
140 QWidget globals
141 *****************************************************************************/
142#ifndef QT_MAC_USE_COCOA
143typedef QHash<Qt::WindowFlags, WindowGroupRef> WindowGroupHash;
144Q_GLOBAL_STATIC(WindowGroupHash, qt_mac_window_groups)
145const UInt32 kWidgetCreatorQt = kEventClassQt;
146enum {
147 kWidgetPropertyQWidget = 'QWId' //QWidget *
148};
149#endif
150
151static bool qt_mac_raise_process = true;
152static OSWindowRef qt_root_win = 0;
153QWidget *mac_mouse_grabber = 0;
154QWidget *mac_keyboard_grabber = 0;
155
156#ifndef QT_MAC_USE_COCOA
157#ifdef QT_NAMESPACE
158
159// produce the string "com.trolltech.qt-namespace.widget", where "namespace" is the contents of QT_NAMESPACE.
160#define SS(x) #x
161#define S0(x) SS(x)
162#define S "com.trolltech.qt-" S0(QT_NAMESPACE) ".widget"
163
164static CFStringRef kObjectQWidget = CFSTR(S);
165
166#undef SS
167#undef S0
168#undef S
169
170#else
171static CFStringRef kObjectQWidget = CFSTR("com.trolltech.qt.widget");
172#endif // QT_NAMESPACE
173#endif // QT_MAC_USE_COCOA
174
175/*****************************************************************************
176 Externals
177 *****************************************************************************/
178extern QWidget *qt_mac_modal_blocked(QWidget *); //qapplication_mac.mm
179extern void qt_event_request_activate(QWidget *); //qapplication_mac.mm
180extern bool qt_event_remove_activate(); //qapplication_mac.mm
181extern void qt_mac_event_release(QWidget *w); //qapplication_mac.mm
182extern void qt_event_request_showsheet(QWidget *); //qapplication_mac.mm
183extern void qt_event_request_window_change(QWidget *); //qapplication_mac.mm
184extern QPointer<QWidget> qt_mouseover; //qapplication_mac.mm
185extern IconRef qt_mac_create_iconref(const QPixmap &); //qpixmap_mac.cpp
186extern void qt_mac_set_cursor(const QCursor *, const QPoint &); //qcursor_mac.mm
187extern void qt_mac_update_cursor(); //qcursor_mac.mm
188extern bool qt_nograb();
189extern CGImageRef qt_mac_create_cgimage(const QPixmap &, bool); //qpixmap_mac.cpp
190extern RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
191extern QRegion qt_mac_convert_mac_region(RgnHandle rgn); //qregion_mac.cpp
192
193/*****************************************************************************
194 QWidget utility functions
195 *****************************************************************************/
196void Q_GUI_EXPORT qt_mac_set_raise_process(bool b) { qt_mac_raise_process = b; }
197static QSize qt_mac_desktopSize()
198{
199 int w = 0, h = 0;
200 CGDisplayCount cg_count;
201 CGGetActiveDisplayList(0, 0, &cg_count);
202 QVector<CGDirectDisplayID> displays(cg_count);
203 CGGetActiveDisplayList(cg_count, displays.data(), &cg_count);
204 Q_ASSERT(cg_count == (CGDisplayCount)displays.size());
205 for(int i = 0; i < (int)cg_count; ++i) {
206 CGRect r = CGDisplayBounds(displays.at(i));
207 w = qMax<int>(w, qRound(r.origin.x + r.size.width));
208 h = qMax<int>(h, qRound(r.origin.y + r.size.height));
209 }
210 return QSize(w, h);
211}
212
213#ifdef QT_MAC_USE_COCOA
214static NSDrawer *qt_mac_drawer_for(const QWidget *widget)
215{
216 // This only goes one level below the content view so start with the window.
217 // This works fine for straight Qt stuff, but runs into problems if we are
218 // embedding, but if that's the case, they probably want to be using
219 // NSDrawer directly.
220 NSView *widgetView = reinterpret_cast<NSView *>(widget->window()->winId());
221 NSArray *windows = [NSApp windows];
222 for (NSWindow *window in windows) {
223 NSArray *drawers = [window drawers];
224 for (NSDrawer *drawer in drawers) {
225 NSArray *views = [[drawer contentView] subviews];
226 for (NSView *view in views) {
227 if (view == widgetView)
228 return drawer;
229 }
230 }
231 }
232 return 0;
233}
234#endif
235
236static void qt_mac_destructView(OSViewRef view)
237{
238#ifdef QT_MAC_USE_COCOA
239 [view removeFromSuperview];
240 [view release];
241#else
242 HIViewRemoveFromSuperview(view);
243 CFRelease(view);
244#endif
245}
246
247static void qt_mac_destructWindow(OSWindowRef window)
248{
249#ifdef QT_MAC_USE_COCOA
250 if ([window isVisible] && [window isSheet]){
251 [NSApp endSheet:window];
252 [window orderOut:window];
253 }
254
255 [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] resignDelegateForWindow:window];
256 [window release];
257#else
258 // Remove property to clean up memory:
259 RemoveWindowProperty(window, kWidgetCreatorQt, kWidgetPropertyQWidget);
260 CFRelease(window);
261#endif
262}
263
264static void qt_mac_destructDrawer(NSDrawer *drawer)
265{
266#ifdef QT_MAC_USE_COCOA
267 [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] resignDelegateForDrawer:drawer];
268 [drawer release];
269#else
270 Q_UNUSED(drawer);
271#endif
272}
273
274bool qt_mac_can_clickThrough(const QWidget *w)
275{
276 static int qt_mac_carbon_clickthrough = -1;
277 if (qt_mac_carbon_clickthrough < 0)
278 qt_mac_carbon_clickthrough = !qgetenv("QT_MAC_NO_COCOA_CLICKTHROUGH").isEmpty();
279 bool ret = !qt_mac_carbon_clickthrough;
280 for ( ; w; w = w->parentWidget()) {
281 if (w->testAttribute(Qt::WA_MacNoClickThrough)) {
282 ret = false;
283 break;
284 }
285 }
286 return ret;
287}
288
289bool qt_mac_is_macsheet(const QWidget *w)
290{
291 if (!w)
292 return false;
293
294 Qt::WindowModality modality = w->windowModality();
295 if (modality == Qt::ApplicationModal)
296 return false;
297 return w->parentWidget() && (modality == Qt::WindowModal || w->windowType() == Qt::Sheet);
298}
299
300bool qt_mac_is_macdrawer(const QWidget *w)
301{
302 return (w && w->parentWidget() && w->windowType() == Qt::Drawer);
303}
304
305bool qt_mac_insideKeyWindow(const QWidget *w)
306{
307#ifdef QT_MAC_USE_COCOA
308 return [[reinterpret_cast<NSView *>(w->winId()) window] isKeyWindow];
309#else
310 Q_UNUSED(w);
311#endif
312 return false;
313}
314
315bool qt_mac_set_drawer_preferred_edge(QWidget *w, Qt::DockWidgetArea where) //users of Qt for Mac OS X can use this..
316{
317 if(!qt_mac_is_macdrawer(w))
318 return false;
319
320#if QT_MAC_USE_COCOA
321 NSDrawer *drawer = qt_mac_drawer_for(w);
322 if (!drawer)
323 return false;
324 NSRectEdge edge;
325 if (where & Qt::LeftDockWidgetArea)
326 edge = NSMinXEdge;
327 else if (where & Qt::RightDockWidgetArea)
328 edge = NSMaxXEdge;
329 else if (where & Qt::TopDockWidgetArea)
330 edge = NSMaxYEdge;
331 else if (where & Qt::BottomDockWidgetArea)
332 edge = NSMinYEdge;
333 else
334 return false;
335
336 if (edge == [drawer preferredEdge]) //no-op
337 return false;
338
339 if (w->isVisible()) {
340 [drawer close];
341 [drawer openOnEdge:edge];
342 }
343 [drawer setPreferredEdge:edge];
344#else
345 OSWindowRef window = qt_mac_window_for(w);
346 OptionBits edge;
347 if(where & Qt::LeftDockWidgetArea)
348 edge = kWindowEdgeLeft;
349 else if(where & Qt::RightDockWidgetArea)
350 edge = kWindowEdgeRight;
351 else if(where & Qt::TopDockWidgetArea)
352 edge = kWindowEdgeTop;
353 else if(where & Qt::BottomDockWidgetArea)
354 edge = kWindowEdgeBottom;
355 else
356 return false;
357
358 if(edge == GetDrawerPreferredEdge(window)) //no-op
359 return false;
360
361 //do it
362 SetDrawerPreferredEdge(window, edge);
363 if(w->isVisible()) {
364 CloseDrawer(window, false);
365 OpenDrawer(window, edge, true);
366 }
367#endif
368 return true;
369}
370
371#ifndef QT_MAC_USE_COCOA
372Q_GUI_EXPORT
373#endif
374QPoint qt_mac_posInWindow(const QWidget *w)
375{
376 QPoint ret = w->data->wrect.topLeft();
377 while(w && !w->isWindow()) {
378 ret += w->pos();
379 w = w->parentWidget();
380 }
381 return ret;
382}
383
384//find a QWidget from a OSWindowRef
385QWidget *qt_mac_find_window(OSWindowRef window)
386{
387#ifdef QT_MAC_USE_COCOA
388 return [window QT_MANGLE_NAMESPACE(qt_qwidget)];
389#else
390 if(!window)
391 return 0;
392
393 QWidget *ret;
394 if(GetWindowProperty(window, kWidgetCreatorQt, kWidgetPropertyQWidget, sizeof(ret), 0, &ret) == noErr)
395 return ret;
396 return 0;
397#endif
398}
399
400inline static void qt_mac_set_fullscreen_mode(bool b)
401{
402 extern bool qt_mac_app_fullscreen; //qapplication_mac.mm
403 if(qt_mac_app_fullscreen == b)
404 return;
405 qt_mac_app_fullscreen = b;
406 if (b) {
407 SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
408 } else {
409 SetSystemUIMode(kUIModeNormal, 0);
410 }
411}
412
413Q_GUI_EXPORT OSViewRef qt_mac_nativeview_for(const QWidget *w)
414{
415 return reinterpret_cast<OSViewRef>(w->data->winid);
416}
417
418Q_GUI_EXPORT OSViewRef qt_mac_get_contentview_for(OSWindowRef w)
419{
420#ifdef QT_MAC_USE_COCOA
421 return [w contentView];
422#else
423 HIViewRef contentView = 0;
424 OSStatus err = GetRootControl(w, &contentView); // Returns the window's content view (Apple QA1214)
425 if (err == errUnknownControl) {
426 contentView = HIViewGetRoot(w);
427 } else if (err != noErr) {
428 qWarning("Qt:Could not get content or root view of window! %s:%d [%ld]",
429 __FILE__, __LINE__, err);
430 }
431 return contentView;
432#endif
433}
434
435bool qt_mac_sendMacEventToWidget(QWidget *widget, EventRef ref)
436{
437 return widget->macEvent(0, ref);
438}
439
440Q_GUI_EXPORT OSWindowRef qt_mac_window_for(OSViewRef view)
441{
442#ifdef QT_MAC_USE_COCOA
443 if (view)
444 return [view window];
445 return 0;
446#else
447 return HIViewGetWindow(view);
448#endif
449}
450
451static bool qt_isGenuineQWidget(OSViewRef ref)
452{
453#ifdef QT_MAC_USE_COCOA
454 return [ref isKindOfClass:[QT_MANGLE_NAMESPACE(QCocoaView) class]];
455#else
456 return HIObjectIsOfClass(HIObjectRef(ref), kObjectQWidget);
457#endif
458}
459
460bool qt_isGenuineQWidget(const QWidget *window)
461{
462 return window && qt_isGenuineQWidget(OSViewRef(window->winId()));
463}
464
465Q_GUI_EXPORT OSWindowRef qt_mac_window_for(const QWidget *w)
466{
467 OSViewRef hiview = qt_mac_nativeview_for(w);
468 if (hiview){
469 OSWindowRef window = qt_mac_window_for(hiview);
470 if (!window && qt_isGenuineQWidget(hiview)) {
471 QWidget *myWindow = w->window();
472 // This is a workaround for NSToolbar. When a widget is hidden
473 // by clicking the toolbar button, Cocoa reparents the widgets
474 // to another window (but Qt doesn't know about it).
475 // When we start showing them, it reparents back,
476 // but at this point it's window is nil, but the window it's being brought
477 // into (the Qt one) is for sure created.
478 // This stops the hierarchy moving under our feet.
479 if (myWindow != w && qt_mac_window_for(qt_mac_nativeview_for(myWindow)))
480 return qt_mac_window_for(qt_mac_nativeview_for(myWindow));
481
482 myWindow->d_func()->createWindow_sys();
483 // Reget the hiview since the "create window could potentially move the view (I guess).
484 hiview = qt_mac_nativeview_for(w);
485 window = qt_mac_window_for(hiview);
486 }
487 return window;
488 }
489 return 0;
490}
491#ifndef QT_MAC_USE_COCOA
492/* Checks if the current group is a 'stay on top' group. If so, the
493 group gets removed from the hash table */
494static void qt_mac_release_stays_on_top_group(WindowGroupRef group)
495{
496 for (WindowGroupHash::iterator it = qt_mac_window_groups()->begin(); it != qt_mac_window_groups()->end(); ++it) {
497 if (it.value() == group) {
498 qt_mac_window_groups()->remove(it.key());
499 return;
500 }
501 }
502}
503
504/* Use this function instead of ReleaseWindowGroup, this will be sure to release the
505 stays on top window group (created with qt_mac_get_stays_on_top_group below) */
506static void qt_mac_release_window_group(WindowGroupRef group)
507{
508 ReleaseWindowGroup(group);
509 if (GetWindowGroupRetainCount(group) == 0)
510 qt_mac_release_stays_on_top_group(group);
511}
512#define ReleaseWindowGroup(x) Are you sure you wanted to do that? (you wanted qt_mac_release_window_group)
513
514SInt32 qt_mac_get_group_level(WindowClass wclass)
515{
516 SInt32 group_level;
517 CGWindowLevel tmpLevel;
518 GetWindowGroupLevelOfType(GetWindowGroupOfClass(wclass), kWindowGroupLevelActive, &tmpLevel);
519 group_level = tmpLevel;
520 return group_level;
521}
522#endif
523
524#ifndef QT_MAC_USE_COCOA
525static void qt_mac_set_window_group(OSWindowRef window, Qt::WindowFlags flags, int level)
526{
527 WindowGroupRef group = 0;
528 if (qt_mac_window_groups()->contains(flags)) {
529 group = qt_mac_window_groups()->value(flags);
530 RetainWindowGroup(group);
531 } else {
532 CreateWindowGroup(kWindowActivationScopeNone, &group);
533 SetWindowGroupLevel(group, level);
534 SetWindowGroupParent(group, GetWindowGroupOfClass(kAllWindowClasses));
535 qt_mac_window_groups()->insert(flags, group);
536 }
537 SetWindowGroup(window, group);
538}
539
540inline static void qt_mac_set_window_group_to_stays_on_top(OSWindowRef window, Qt::WindowType type)
541{
542 // We create one static stays on top window group so that
543 // all stays on top (aka popups) will fall into the same
544 // group and be able to be raise()'d with releation to one another (from
545 // within the same window group).
546 qt_mac_set_window_group(window, type|Qt::WindowStaysOnTopHint, qt_mac_get_group_level(kOverlayWindowClass));
547}
548
549inline static void qt_mac_set_window_group_to_tooltip(OSWindowRef window)
550{
551 // Since new groups are created for 'stays on top' windows, the
552 // same must be done for tooltips. Otherwise, tooltips would be drawn
553 // below 'stays on top' widgets even tough they are on the same level.
554 // Also, add 'two' to the group level to make sure they also get on top of popups.
555 qt_mac_set_window_group(window, Qt::ToolTip, qt_mac_get_group_level(kHelpWindowClass)+2);
556}
557
558inline static void qt_mac_set_window_group_to_popup(OSWindowRef window)
559{
560 // In Qt, a popup is seen as a 'stay on top' window.
561 // Since new groups are created for 'stays on top' windows, the
562 // same must be done for popups. Otherwise, popups would be drawn
563 // below 'stays on top' windows. Add 1 to get above pure stay-on-top windows.
564 qt_mac_set_window_group(window, Qt::Popup, qt_mac_get_group_level(kOverlayWindowClass)+1);
565}
566#endif
567
568inline static bool updateRedirectedToGraphicsProxyWidget(QWidget *widget, const QRect &rect)
569{
570 if (!widget)
571 return false;
572
573#ifndef QT_NO_GRAPHICSVIEW
574 QWidget *tlw = widget->window();
575 QWExtra *extra = qt_widget_private(tlw)->extra;
576 if (extra && extra->proxyWidget) {
577 extra->proxyWidget->update(rect.translated(widget->mapTo(tlw, QPoint())));
578 return true;
579 }
580#endif
581
582 return false;
583}
584
585inline static bool updateRedirectedToGraphicsProxyWidget(QWidget *widget, const QRegion &rgn)
586{
587 if (!widget)
588 return false;
589
590#ifndef QT_NO_GRAPHICSVIEW
591 QWidget *tlw = widget->window();
592 QWExtra *extra = qt_widget_private(tlw)->extra;
593 if (extra && extra->proxyWidget) {
594 const QPoint offset(widget->mapTo(tlw, QPoint()));
595 const QVector<QRect> rects = rgn.rects();
596 for (int i = 0; i < rects.size(); ++i)
597 extra->proxyWidget->update(rects.at(i).translated(offset));
598 return true;
599 }
600#endif
601
602 return false;
603}
604
605void QWidgetPrivate::macUpdateIsOpaque()
606{
607 Q_Q(QWidget);
608 if (!q->testAttribute(Qt::WA_WState_Created))
609 return;
610#ifndef QT_MAC_USE_COCOA
611 HIViewFeatures bits;
612 HIViewRef hiview = qt_mac_nativeview_for(q);
613 HIViewGetFeatures(hiview, &bits);
614 if ((bits & kHIViewIsOpaque) == isOpaque)
615 return;
616 if (isOpaque) {
617 HIViewChangeFeatures(hiview, kHIViewIsOpaque, 0);
618 } else {
619 HIViewChangeFeatures(hiview, 0, kHIViewIsOpaque);
620 }
621 if (q->isVisible())
622 HIViewReshapeStructure(qt_mac_nativeview_for(q));
623#else
624 if (isRealWindow() && !q->testAttribute(Qt::WA_MacBrushedMetal)) {
625 bool opaque = isOpaque;
626 if (extra && extra->imageMask)
627 opaque = false; // we are never opaque when we have a mask.
628 [qt_mac_window_for(q) setOpaque:opaque];
629 }
630#endif
631}
632#ifdef QT_MAC_USE_COCOA
633static OSWindowRef qt_mac_create_window(QWidget *widget, WindowClass wclass,
634 NSUInteger wattr, const QRect &crect)
635{
636 // Determine if we need to add in our "custom window" attribute. Cocoa is rather clever
637 // in deciding if we need the maximize button or not (i.e., it's resizeable, so you
638 // must need a maximize button). So, the only buttons we have control over are the
639 // close and minimize buttons. If someone wants to customize and NOT have the maximize
640 // button, then we have to do our hack. We only do it for these cases because otherwise
641 // the window looks different when activated. This "QtMacCustomizeWindow" attribute is
642 // intruding on a public space and WILL BREAK in the future.
643 // One can hope that there is a more public API available by that time.
644 Qt::WindowFlags flags = widget ? widget->windowFlags() : Qt::WindowFlags(0);
645 if ((flags & Qt::CustomizeWindowHint)) {
646 if ((flags & (Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint
647 | Qt::WindowMinimizeButtonHint | Qt::WindowTitleHint))
648 && !(flags & Qt::WindowMaximizeButtonHint))
649 wattr |= QtMacCustomizeWindow;
650 }
651
652 // If we haven't created the desktop widget, you have to pass the rectangle
653 // in "cocoa coordinates" (i.e., top points to the lower left coordinate).
654 // Otherwise, we do the conversion for you. Since we are the only ones that
655 // create the desktop widget, this is OK (but confusing).
656 NSRect geo = NSMakeRect(crect.left(),
657 (qt_root_win != 0) ? flipYCoordinate(crect.bottom() + 1) : crect.top(),
658 crect.width(), crect.height());
659 QMacCocoaAutoReleasePool pool;
660 OSWindowRef window;
661 switch (wclass) {
662 case kMovableModalWindowClass:
663 case kModalWindowClass:
664 case kSheetWindowClass:
665 case kFloatingWindowClass:
666 case kOverlayWindowClass:
667 case kHelpWindowClass: {
668 NSPanel *panel;
669 BOOL needFloating = NO;
670 BOOL worksWhenModal = widget && (widget->windowType() == Qt::Popup);
671 // Add in the extra flags if necessary.
672 switch (wclass) {
673 case kSheetWindowClass:
674 wattr |= NSDocModalWindowMask;
675 break;
676 case kFloatingWindowClass:
677 case kHelpWindowClass:
678 needFloating = YES;
679 wattr |= NSUtilityWindowMask;
680 break;
681 default:
682 break;
683 }
684 panel = [[QT_MANGLE_NAMESPACE(QCocoaPanel) alloc] QT_MANGLE_NAMESPACE(qt_initWithQWidget):widget contentRect:geo styleMask:wattr];
685 [panel setFloatingPanel:needFloating];
686 [panel setWorksWhenModal:worksWhenModal];
687 window = panel;
688 break;
689 }
690 case kDrawerWindowClass: {
691 NSDrawer *drawer = [[NSDrawer alloc] initWithContentSize:geo.size preferredEdge:NSMinXEdge];
692 [[QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) sharedDelegate] becomeDelegateForDrawer:drawer widget:widget];
693 QWidget *parentWidget = widget->parentWidget();
694 if (parentWidget)
695 [drawer setParentWindow:qt_mac_window_for(parentWidget)];
696 [drawer setLeadingOffset:0.0];
697 [drawer setTrailingOffset:25.0];
698 window = [[drawer contentView] window]; // Just to make sure we actually return a window
699 break;
700 }
701 default:
702 window = [[QT_MANGLE_NAMESPACE(QCocoaWindow) alloc] QT_MANGLE_NAMESPACE(qt_initWithQWidget):widget contentRect:geo styleMask:wattr];
703 break;
704 }
705 qt_syncCocoaTitleBarButtons(window, widget);
706 return window;
707}
708#else
709static OSWindowRef qt_mac_create_window(QWidget *, WindowClass wclass, WindowAttributes wattr,
710 const QRect &crect)
711{
712 OSWindowRef window;
713 Rect geo;
714 SetRect(&geo, crect.left(), crect.top(), crect.right() + 1, crect.bottom() + 1);
715 OSStatus err;
716 if(geo.right <= geo.left) geo.right = geo.left + 1;
717 if(geo.bottom <= geo.top) geo.bottom = geo.top + 1;
718 Rect null_rect;
719 SetRect(&null_rect, 0, 0, 1, 1);
720 err = CreateNewWindow(wclass, wattr, &null_rect, &window);
721 if(err == noErr) {
722 err = SetWindowBounds(window, kWindowContentRgn, &geo);
723 if(err != noErr)
724 qWarning("QWidget: Internal error (%s:%d)", __FILE__, __LINE__);
725 }
726 return window;
727}
728
729#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
730/* We build the release package against the 10.4 SDK.
731 So, to enable gestures for applications running on
732 10.6+, we define the missing constants here: */
733enum {
734 kEventClassGesture = 'gest',
735 kEventGestureStarted = 1,
736 kEventGestureEnded = 2,
737 kEventGestureMagnify = 4,
738 kEventGestureSwipe = 5,
739 kEventGestureRotate = 6,
740 kEventParamRotationAmount = 'rota',
741 kEventParamSwipeDirection = 'swip',
742 kEventParamMagnificationAmount = 'magn'
743};
744#endif
745
746// window events
747static EventTypeSpec window_events[] = {
748 { kEventClassWindow, kEventWindowClose },
749 { kEventClassWindow, kEventWindowExpanded },
750 { kEventClassWindow, kEventWindowHidden },
751 { kEventClassWindow, kEventWindowZoom },
752 { kEventClassWindow, kEventWindowZoomed },
753 { kEventClassWindow, kEventWindowCollapsed },
754 { kEventClassWindow, kEventWindowToolbarSwitchMode },
755 { kEventClassWindow, kEventWindowProxyBeginDrag },
756 { kEventClassWindow, kEventWindowProxyEndDrag },
757 { kEventClassWindow, kEventWindowResizeCompleted },
758 { kEventClassWindow, kEventWindowBoundsChanging },
759 { kEventClassWindow, kEventWindowGetRegion },
760 { kEventClassWindow, kEventWindowGetClickModality },
761 { kEventClassWindow, kEventWindowTransitionCompleted },
762 { kEventClassGesture, kEventGestureStarted },
763 { kEventClassGesture, kEventGestureEnded },
764 { kEventClassGesture, kEventGestureMagnify },
765 { kEventClassGesture, kEventGestureSwipe },
766 { kEventClassGesture, kEventGestureRotate },
767 { kEventClassMouse, kEventMouseDown }
768};
769static EventHandlerUPP mac_win_eventUPP = 0;
770static void cleanup_win_eventUPP()
771{
772 DisposeEventHandlerUPP(mac_win_eventUPP);
773 mac_win_eventUPP = 0;
774}
775static const EventHandlerUPP make_win_eventUPP()
776{
777 if(mac_win_eventUPP)
778 return mac_win_eventUPP;
779 qAddPostRoutine(cleanup_win_eventUPP);
780 return mac_win_eventUPP = NewEventHandlerUPP(QWidgetPrivate::qt_window_event);
781}
782OSStatus QWidgetPrivate::qt_window_event(EventHandlerCallRef er, EventRef event, void *)
783{
784 QScopedLoopLevelCounter loopLevelCounter(qApp->d_func()->threadData);
785 bool handled_event = true;
786 UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
787 switch(eclass) {
788 case kEventClassWindow: {
789 WindowRef wid = 0;
790 GetEventParameter(event, kEventParamDirectObject, typeWindowRef, 0,
791 sizeof(WindowRef), 0, &wid);
792 QWidget *widget = qt_mac_find_window(wid);
793 if(!widget) {
794 handled_event = false;
795 } else if(ekind == kEventWindowGetClickModality) {
796 // Carbon will send us kEventWindowGetClickModality before every
797 // mouse press / release event. By returning 'true', we tell Carbon
798 // that we would like the event target to receive the mouse event even
799 // if the target is modally shaddowed. In Qt, this makes sense when we
800 // e.g. have a popup showing, as the popup will grab the event
801 // and perhaps use it to close itself.
802 // By also setting the current modal window back into the event, we
803 // help Carbon determining which window is supposed to be raised.
804 handled_event = qApp->activePopupWidget() ? true : false;
805 } else if(ekind == kEventWindowClose) {
806 widget->d_func()->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent);
807 QMenuBar::macUpdateMenuBar();
808 } else if (ekind == kEventWindowTransitionCompleted) {
809 WindowTransitionAction transitionAction;
810 GetEventParameter(event, kEventParamWindowTransitionAction, typeWindowTransitionAction,
811 0, sizeof(transitionAction), 0, &transitionAction);
812 if (transitionAction == kWindowHideTransitionAction)
813 widget->hide();
814 } else if(ekind == kEventWindowExpanded) {
815 Qt::WindowStates currState = Qt::WindowStates(widget->data->window_state);
816 Qt::WindowStates newState = currState;
817 if (currState & Qt::WindowMinimized)
818 newState &= ~Qt::WindowMinimized;
819 if (!(currState & Qt::WindowActive))
820 newState |= Qt::WindowActive;
821 if (newState != currState) {
822 // newState will differ from currState if the window
823 // was expanded after clicking on the jewels (as opposed
824 // to calling QWidget::setWindowState)
825 widget->data->window_state = newState;
826 QWindowStateChangeEvent e(currState);
827 QApplication::sendSpontaneousEvent(widget, &e);
828 }
829
830 QShowEvent qse;
831 QApplication::sendSpontaneousEvent(widget, &qse);
832 } else if(ekind == kEventWindowZoom) {
833 widget->d_func()->topData()->normalGeometry = widget->geometry();
834 handled_event = false;
835 } else if(ekind == kEventWindowZoomed) {
836 WindowPartCode windowPart;
837 GetEventParameter(event, kEventParamWindowPartCode,
838 typeWindowPartCode, 0, sizeof(windowPart), 0, &windowPart);
839 if(windowPart == inZoomIn && widget->isMaximized()) {
840
841 widget->data->window_state = widget->data->window_state & ~Qt::WindowMaximized;
842 QWindowStateChangeEvent e(Qt::WindowStates(widget->data->window_state | Qt::WindowMaximized));
843 QApplication::sendSpontaneousEvent(widget, &e);
844 } else if(windowPart == inZoomOut && !widget->isMaximized()) {
845 widget->data->window_state = widget->data->window_state | Qt::WindowMaximized;
846 QWindowStateChangeEvent e(Qt::WindowStates(widget->data->window_state
847 & ~Qt::WindowMaximized));
848 QApplication::sendSpontaneousEvent(widget, &e);
849 }
850 extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
851 qt_button_down = 0;
852 } else if(ekind == kEventWindowCollapsed) {
853 if (!widget->isMinimized()) {
854 widget->data->window_state = widget->data->window_state | Qt::WindowMinimized;
855 QWindowStateChangeEvent e(Qt::WindowStates(widget->data->window_state & ~Qt::WindowMinimized));
856 QApplication::sendSpontaneousEvent(widget, &e);
857 }
858
859 // Deactivate this window:
860 if (widget->isActiveWindow() && !(widget->windowType() == Qt::Popup)) {
861 QWidget *w = 0;
862 if (widget->parentWidget())
863 w = widget->parentWidget()->window();
864 if (!w || (!w->isVisible() && !w->isMinimized())) {
865 for (WindowPtr wp = GetFrontWindowOfClass(kDocumentWindowClass, true);
866 wp; wp = GetNextWindowOfClass(wp, kDocumentWindowClass, true)) {
867 if ((w = qt_mac_find_window(wp)))
868 break;
869 }
870 }
871 if(!(w && w->isVisible() && !w->isMinimized()))
872 qApp->setActiveWindow(0);
873 }
874
875 //we send a hide to be like X11/Windows
876 QEvent e(QEvent::Hide);
877 QApplication::sendSpontaneousEvent(widget, &e);
878 extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
879 qt_button_down = 0;
880 } else if(ekind == kEventWindowToolbarSwitchMode) {
881 macSendToolbarChangeEvent(widget);
882 HIToolbarRef toolbar;
883 if (GetWindowToolbar(wid, &toolbar) == noErr) {
884 if (toolbar) {
885 // Let HIToolbar do its thang, but things like the OpenGL context
886 // needs to know about it.
887 CallNextEventHandler(er, event);
888 qt_event_request_window_change(widget);
889 widget->data->fstrut_dirty = true;
890 }
891 }
892 } else if(ekind == kEventWindowGetRegion) {
893 WindowRef window;
894 GetEventParameter(event, kEventParamDirectObject, typeWindowRef, 0,
895 sizeof(window), 0, &window);
896 WindowRegionCode wcode;
897 GetEventParameter(event, kEventParamWindowRegionCode, typeWindowRegionCode, 0,
898 sizeof(wcode), 0, &wcode);
899 if (wcode != kWindowOpaqueRgn){
900 // If the region is kWindowOpaqueRgn, don't call next
901 // event handler cause this will make the shadow of
902 // masked windows become offset. Unfortunately, we're not sure why.
903 CallNextEventHandler(er, event);
904 }
905 RgnHandle rgn;
906 GetEventParameter(event, kEventParamRgnHandle, typeQDRgnHandle, 0,
907 sizeof(rgn), 0, &rgn);
908
909 if(QWidgetPrivate::qt_widget_rgn(qt_mac_find_window(window), wcode, rgn, false))
910 SetEventParameter(event, kEventParamRgnHandle, typeQDRgnHandle, sizeof(rgn), &rgn);
911 } else if(ekind == kEventWindowProxyBeginDrag) {
912 QIconDragEvent e;
913 QApplication::sendSpontaneousEvent(widget, &e);
914 } else if(ekind == kEventWindowResizeCompleted) {
915 // Create a mouse up event, since such an event is not send by carbon to the
916 // application event handler (while a mouse down <b>is</b> on kEventWindowResizeStarted)
917 EventRef mouseUpEvent;
918 CreateEvent(0, kEventClassMouse, kEventMouseUp, 0, kEventAttributeUserEvent, &mouseUpEvent);
919 UInt16 mbutton = kEventMouseButtonPrimary;
920 SetEventParameter(mouseUpEvent, kEventParamMouseButton, typeMouseButton, sizeof(mbutton), &mbutton);
921 WindowRef window;
922 GetEventParameter(event, kEventParamDirectObject, typeWindowRef, 0, sizeof(window), 0, &window);
923 Rect dragRect;
924 GetWindowBounds(window, kWindowGrowRgn, &dragRect);
925 Point pos = {dragRect.bottom, dragRect.right};
926 SetEventParameter(mouseUpEvent, kEventParamMouseLocation, typeQDPoint, sizeof(pos), &pos);
927 SendEventToApplication(mouseUpEvent);
928 ReleaseEvent(mouseUpEvent);
929 } else if(ekind == kEventWindowBoundsChanging) {
930 UInt32 flags = 0;
931 GetEventParameter(event, kEventParamAttributes, typeUInt32, 0,
932 sizeof(flags), 0, &flags);
933 Rect nr;
934 GetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, 0,
935 sizeof(nr), 0, &nr);
936
937 QRect newRect(nr.left, nr.top, nr.right - nr.left, nr.bottom - nr.top);
938
939 QTLWExtra * const tlwExtra = widget->d_func()->maybeTopData();
940 if (tlwExtra && tlwExtra->isSetGeometry == 1) {
941 widget->d_func()->setGeometry_sys_helper(newRect.left(), newRect.top(), newRect.width(), newRect.height(), tlwExtra->isMove);
942 } else {
943 //implicitly removes the maximized bit
944 if((widget->data->window_state & Qt::WindowMaximized) &&
945 IsWindowInStandardState(wid, 0, 0)) {
946 widget->data->window_state &= ~Qt::WindowMaximized;
947 QWindowStateChangeEvent e(Qt::WindowStates(widget->data->window_state
948 | Qt::WindowMaximized));
949 QApplication::sendSpontaneousEvent(widget, &e);
950
951 }
952
953 handled_event = false;
954 const QRect oldRect = widget->data->crect;
955 if((flags & kWindowBoundsChangeOriginChanged)) {
956 if(nr.left != oldRect.x() || nr.top != oldRect.y()) {
957 widget->data->crect.moveTo(nr.left, nr.top);
958 QMoveEvent qme(widget->data->crect.topLeft(), oldRect.topLeft());
959 QApplication::sendSpontaneousEvent(widget, &qme);
960 }
961 }
962 if((flags & kWindowBoundsChangeSizeChanged)) {
963 if (widget->isWindow()) {
964 QSize newSize = QLayout::closestAcceptableSize(widget, newRect.size());
965 int dh = newSize.height() - newRect.height();
966 int dw = newSize.width() - newRect.width();
967 if (dw != 0 || dh != 0) {
968 handled_event = true; // We want to change the bounds, so we handle the event
969
970 // set the rect, so we can also do the resize down below (yes, we need to resize).
971 newRect.setBottom(newRect.bottom() + dh);
972 newRect.setRight(newRect.right() + dw);
973
974 nr.left = newRect.x();
975 nr.top = newRect.y();
976 nr.right = nr.left + newRect.width();
977 nr.bottom = nr.top + newRect.height();
978 SetEventParameter(event, kEventParamCurrentBounds, typeQDRectangle, sizeof(Rect), &nr);
979 }
980 }
981
982 if (oldRect.width() != newRect.width() || oldRect.height() != newRect.height()) {
983 widget->data->crect.setSize(newRect.size());
984 HIRect bounds = CGRectMake(0, 0, newRect.width(), newRect.height());
985
986 // If the WA_StaticContents attribute is set we can optimize the resize
987 // by only repainting the newly exposed area. We do this by disabling
988 // painting when setting the size of the view. The OS will invalidate
989 // the newly exposed area for us.
990 const bool staticContents = widget->testAttribute(Qt::WA_StaticContents);
991 const HIViewRef view = qt_mac_nativeview_for(widget);
992 if (staticContents)
993 HIViewSetDrawingEnabled(view, false);
994 HIViewSetFrame(view, &bounds);
995 if (staticContents)
996 HIViewSetDrawingEnabled(view, true);
997
998 QResizeEvent qre(newRect.size(), oldRect.size());
999 QApplication::sendSpontaneousEvent(widget, &qre);
1000 qt_event_request_window_change(widget);
1001 }
1002 }
1003 }
1004 } else if (ekind == kEventWindowHidden) {
1005 // Make sure that we also hide any visible sheets on our window.
1006 // Cocoa does the right thing for us.
1007 const QObjectList children = widget->children();
1008 const int childCount = children.count();
1009 for (int i = 0; i < childCount; ++i) {
1010 QObject *obj = children.at(i);
1011 if (obj->isWidgetType()) {
1012 QWidget *widget = static_cast<QWidget *>(obj);
1013 if (qt_mac_is_macsheet(widget) && widget->isVisible())
1014 widget->hide();
1015 }
1016 }
1017 } else {
1018 handled_event = false;
1019 }
1020 break; }
1021 case kEventClassMouse: {
1022#if 0
1023 return SendEventToApplication(event);
1024#endif
1025
1026 bool send_to_app = false;
1027 {
1028 WindowPartCode wpc;
1029 if (GetEventParameter(event, kEventParamWindowPartCode, typeWindowPartCode, 0,
1030 sizeof(wpc), 0, &wpc) == noErr && wpc != inContent)
1031 send_to_app = true;
1032 }
1033 if(!send_to_app) {
1034 WindowRef window;
1035 if(GetEventParameter(event, kEventParamWindowRef, typeWindowRef, 0,
1036 sizeof(window), 0, &window) == noErr) {
1037 HIViewRef hiview;
1038 if(HIViewGetViewForMouseEvent(HIViewGetRoot(window), event, &hiview) == noErr) {
1039 if(QWidget *w = QWidget::find((WId)hiview)) {
1040#if 0
1041 send_to_app = !w->isActiveWindow();
1042#else
1043 Q_UNUSED(w);
1044 send_to_app = true;
1045#endif
1046 }
1047 }
1048 }
1049 }
1050 if(send_to_app)
1051 return SendEventToApplication(event);
1052 handled_event = false;
1053 break; }
1054
1055 case kEventClassGesture: {
1056 // First, find the widget that was under
1057 // the mouse when the gesture happened:
1058 HIPoint screenLocation;
1059 if (GetEventParameter(event, kEventParamMouseLocation, typeHIPoint, 0,
1060 sizeof(screenLocation), 0, &screenLocation) != noErr) {
1061 handled_event = false;
1062 break;
1063 }
1064 QWidget *widget = QApplication::widgetAt(screenLocation.x, screenLocation.y);
1065 if (!widget) {
1066 handled_event = false;
1067 break;
1068 }
1069
1070 QNativeGestureEvent qNGEvent;
1071 qNGEvent.position = QPoint(screenLocation.x, screenLocation.y);
1072
1073 switch (ekind) {
1074 case kEventGestureStarted:
1075 qNGEvent.gestureType = QNativeGestureEvent::GestureBegin;
1076 break;
1077 case kEventGestureEnded:
1078 qNGEvent.gestureType = QNativeGestureEvent::GestureEnd;
1079 break;
1080 case kEventGestureRotate: {
1081 CGFloat amount;
1082 if (GetEventParameter(event, kEventParamRotationAmount, 'cgfl', 0,
1083 sizeof(amount), 0, &amount) != noErr) {
1084 handled_event = false;
1085 break;
1086 }
1087 qNGEvent.gestureType = QNativeGestureEvent::Rotate;
1088 qNGEvent.percentage = float(-amount);
1089 break; }
1090 case kEventGestureSwipe: {
1091 HIPoint swipeDirection;
1092 if (GetEventParameter(event, kEventParamSwipeDirection, typeHIPoint, 0,
1093 sizeof(swipeDirection), 0, &swipeDirection) != noErr) {
1094 handled_event = false;
1095 break;
1096 }
1097 qNGEvent.gestureType = QNativeGestureEvent::Swipe;
1098 if (swipeDirection.x == 1)
1099 qNGEvent.angle = 180.0f;
1100 else if (swipeDirection.x == -1)
1101 qNGEvent.angle = 0.0f;
1102 else if (swipeDirection.y == 1)
1103 qNGEvent.angle = 90.0f;
1104 else if (swipeDirection.y == -1)
1105 qNGEvent.angle = 270.0f;
1106 break; }
1107 case kEventGestureMagnify: {
1108 CGFloat amount;
1109 if (GetEventParameter(event, kEventParamMagnificationAmount, 'cgfl', 0,
1110 sizeof(amount), 0, &amount) != noErr) {
1111 handled_event = false;
1112 break;
1113 }
1114 qNGEvent.gestureType = QNativeGestureEvent::Zoom;
1115 qNGEvent.percentage = float(amount);
1116 break; }
1117 }
1118
1119 QApplication::sendSpontaneousEvent(widget, &qNGEvent);
1120 break; }
1121
1122 default:
1123 handled_event = false;
1124 }
1125 if(!handled_event) //let the event go through
1126 return eventNotHandledErr;
1127 return noErr; //we eat the event
1128}
1129
1130// widget events
1131static HIObjectClassRef widget_class = 0;
1132static EventTypeSpec widget_events[] = {
1133 { kEventClassHIObject, kEventHIObjectConstruct },
1134 { kEventClassHIObject, kEventHIObjectDestruct },
1135
1136 { kEventClassControl, kEventControlDraw },
1137 { kEventClassControl, kEventControlInitialize },
1138 { kEventClassControl, kEventControlGetPartRegion },
1139 { kEventClassControl, kEventControlGetClickActivation },
1140 { kEventClassControl, kEventControlSetFocusPart },
1141 { kEventClassControl, kEventControlDragEnter },
1142 { kEventClassControl, kEventControlDragWithin },
1143 { kEventClassControl, kEventControlDragLeave },
1144 { kEventClassControl, kEventControlDragReceive },
1145 { kEventClassControl, kEventControlOwningWindowChanged },
1146 { kEventClassControl, kEventControlBoundsChanged },
1147 { kEventClassControl, kEventControlGetSizeConstraints },
1148 { kEventClassControl, kEventControlVisibilityChanged },
1149
1150 { kEventClassMouse, kEventMouseDown },
1151 { kEventClassMouse, kEventMouseUp },
1152 { kEventClassMouse, kEventMouseMoved },
1153 { kEventClassMouse, kEventMouseDragged }
1154};
1155static EventHandlerUPP mac_widget_eventUPP = 0;
1156static void cleanup_widget_eventUPP()
1157{
1158 DisposeEventHandlerUPP(mac_widget_eventUPP);
1159 mac_widget_eventUPP = 0;
1160}
1161static const EventHandlerUPP make_widget_eventUPP()
1162{
1163 if(mac_widget_eventUPP)
1164 return mac_widget_eventUPP;
1165 qAddPostRoutine(cleanup_widget_eventUPP);
1166 return mac_widget_eventUPP = NewEventHandlerUPP(QWidgetPrivate::qt_widget_event);
1167}
1168OSStatus QWidgetPrivate::qt_widget_event(EventHandlerCallRef er, EventRef event, void *)
1169{
1170 QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
1171
1172 bool handled_event = true;
1173 UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
1174 switch(eclass) {
1175 case kEventClassHIObject: {
1176 HIViewRef view = 0;
1177 GetEventParameter(event, kEventParamHIObjectInstance, typeHIObjectRef,
1178 0, sizeof(view), 0, &view);
1179 if(ekind == kEventHIObjectConstruct) {
1180 if(view) {
1181 HIViewChangeFeatures(view, kHIViewAllowsSubviews, 0);
1182 SetEventParameter(event, kEventParamHIObjectInstance,
1183 typeVoidPtr, sizeof(view), &view);
1184 }
1185 } else if(ekind == kEventHIObjectDestruct) {
1186 //nothing to really do.. or is there?
1187 } else {
1188 handled_event = false;
1189 }
1190 break; }
1191 case kEventClassControl: {
1192 QWidget *widget = 0;
1193 HIViewRef hiview = 0;
1194 if(GetEventParameter(event, kEventParamDirectObject, typeControlRef,
1195 0, sizeof(hiview), 0, &hiview) == noErr)
1196 widget = QWidget::find((WId)hiview);
1197 if (widget && widget->macEvent(er, event))
1198 return noErr;
1199 if(ekind == kEventControlDraw) {
1200 if(widget && qt_isGenuineQWidget(hiview)) {
1201
1202 // if there is a window change event pending for any gl child wigets,
1203 // send it immediately. (required for flicker-free resizing)
1204 extern void qt_mac_send_posted_gl_updates(QWidget *widget);
1205 qt_mac_send_posted_gl_updates(widget);
1206
1207 if (QApplicationPrivate::graphicsSystem() && !widget->d_func()->paintOnScreen()) {
1208 widget->d_func()->syncBackingStore();
1209 widget->d_func()->dirtyOnWidget = QRegion();
1210 return noErr;
1211 }
1212
1213 //requested rgn
1214 RgnHandle rgn;
1215 GetEventParameter(event, kEventParamRgnHandle, typeQDRgnHandle, 0, sizeof(rgn), 0, &rgn);
1216 QRegion qrgn(qt_mac_convert_mac_region(rgn));
1217
1218 //update handles
1219 GrafPtr qd = 0;
1220 CGContextRef cg = 0;
1221 if(GetEventParameter(event, kEventParamCGContextRef, typeCGContextRef, 0, sizeof(cg), 0, &cg) != noErr) {
1222 Q_ASSERT(false);
1223 }
1224 widget->d_func()->hd = cg;
1225 widget->d_func()->qd_hd = qd;
1226 CGContextSaveGState(cg);
1227
1228#ifdef DEBUG_WIDGET_PAINT
1229 const bool doDebug = true;
1230 if(doDebug) {
1231 qDebug("asked to draw %p[%p] [%s::%s] %p[%p] [%d] [%dx%d]", widget, hiview, widget->metaObject()->className(),
1232 widget->objectName().local8Bit().data(), widget->parentWidget(),
1233 (HIViewRef)(widget->parentWidget() ? qt_mac_nativeview_for(widget->parentWidget()) : (HIViewRef)0),
1234 HIViewIsCompositingEnabled(hiview), qt_mac_posInWindow(widget).x(), qt_mac_posInWindow(widget).y());
1235#if 0
1236 QVector<QRect> region_rects = qrgn.rects();
1237 qDebug("Region! %d", region_rects.count());
1238 for(int i = 0; i < region_rects.count(); i++)
1239 qDebug("%d %d %d %d", region_rects[i].x(), region_rects[i].y(),
1240 region_rects[i].width(), region_rects[i].height());
1241 region_rects = widget->d_func()->clp.rects();
1242 qDebug("Widget Region! %d", region_rects.count());
1243 for(int i = 0; i < region_rects.count(); i++)
1244 qDebug("%d %d %d %d", region_rects[i].x(), region_rects[i].y(),
1245 region_rects[i].width(), region_rects[i].height());
1246#endif
1247 }
1248#endif
1249 if (widget->isVisible() && widget->updatesEnabled()) { //process the actual paint event.
1250 if(widget->testAttribute(Qt::WA_WState_InPaintEvent))
1251 qWarning("QWidget::repaint: Recursive repaint detected");
1252
1253 QPoint redirectionOffset(0, 0);
1254 QWidget *tl = widget->window();
1255 if (tl) {
1256 Qt::WindowFlags flags = tl->windowFlags();
1257 if (flags & Qt::FramelessWindowHint
1258 || (flags & Qt::CustomizeWindowHint && !(flags & Qt::WindowTitleHint))) {
1259 if(tl->d_func()->extra && !tl->d_func()->extra->mask.isEmpty())
1260 redirectionOffset += tl->d_func()->extra->mask.boundingRect().topLeft();
1261 }
1262 }
1263
1264 //setup the context
1265 widget->setAttribute(Qt::WA_WState_InPaintEvent);
1266 QPaintEngine *engine = widget->paintEngine();
1267 if (engine)
1268 engine->setSystemClip(qrgn);
1269
1270 //handle the erase
1271 if (engine && (!widget->testAttribute(Qt::WA_NoSystemBackground)
1272 && (widget->isWindow() || widget->autoFillBackground())
1273 || widget->testAttribute(Qt::WA_TintedBackground)
1274 || widget->testAttribute(Qt::WA_StyledBackground))) {
1275#ifdef DEBUG_WIDGET_PAINT
1276 if(doDebug)
1277 qDebug(" Handling erase for [%s::%s]", widget->metaObject()->className(),
1278 widget->objectName().local8Bit().data());
1279#endif
1280 if (!redirectionOffset.isNull())
1281 widget->d_func()->setRedirected(widget, redirectionOffset);
1282
1283 bool was_unclipped = widget->testAttribute(Qt::WA_PaintUnclipped);
1284 widget->setAttribute(Qt::WA_PaintUnclipped, false);
1285 QPainter p(widget);
1286 p.setClipping(false);
1287 if(was_unclipped)
1288 widget->setAttribute(Qt::WA_PaintUnclipped);
1289 widget->d_func()->paintBackground(&p, qrgn, widget->isWindow() ? DrawAsRoot : 0);
1290 if (widget->testAttribute(Qt::WA_TintedBackground)) {
1291 QColor tint = widget->palette().window().color();
1292 tint.setAlphaF(.6);
1293 const QVector<QRect> &rects = qrgn.rects();
1294 for (int i = 0; i < rects.size(); ++i)
1295 p.fillRect(rects.at(i), tint);
1296 }
1297 p.end();
1298 if (!redirectionOffset.isNull())
1299 widget->d_func()->restoreRedirected();
1300 }
1301
1302 if (widget->isWindow() && !widget->d_func()->isOpaque
1303 && !widget->testAttribute(Qt::WA_MacBrushedMetal)) {
1304 QRect qrgnRect = qrgn.boundingRect();
1305 CGContextClearRect(cg, CGRectMake(qrgnRect.x(), qrgnRect.y(), qrgnRect.width(), qrgnRect.height()));
1306 }
1307
1308
1309 if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget))
1310 CallNextEventHandler(er, event);
1311
1312 //send the paint
1313 redirectionOffset += widget->data->wrect.topLeft(); // Map from system to qt coordinates
1314 if (!redirectionOffset.isNull())
1315 widget->d_func()->setRedirected(widget, redirectionOffset);
1316 qrgn.translate(redirectionOffset);
1317 QPaintEvent e(qrgn);
1318 widget->d_func()->dirtyOnWidget = QRegion();
1319#ifdef QT3_SUPPORT
1320 e.setErased(true);
1321#endif
1322 QApplication::sendSpontaneousEvent(widget, &e);
1323 if (!redirectionOffset.isNull())
1324 widget->d_func()->restoreRedirected();
1325
1326 //cleanup
1327 if (engine)
1328 engine->setSystemClip(QRegion());
1329
1330 widget->setAttribute(Qt::WA_WState_InPaintEvent, false);
1331 if(!widget->testAttribute(Qt::WA_PaintOutsidePaintEvent) && widget->paintingActive())
1332 qWarning("QWidget: It is dangerous to leave painters active on a widget outside of the PaintEvent");
1333 }
1334
1335 widget->d_func()->hd = 0;
1336 widget->d_func()->qd_hd = 0;
1337 CGContextRestoreGState(cg);
1338 } else if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget)) {
1339 CallNextEventHandler(er, event);
1340 }
1341 } else if(ekind == kEventControlInitialize) {
1342 if(HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget)) {
1343 UInt32 features = kControlSupportsDragAndDrop | kControlSupportsClickActivation | kControlSupportsFocus;
1344 SetEventParameter(event, kEventParamControlFeatures, typeUInt32, sizeof(features), &features);
1345 } else {
1346 handled_event = false;
1347 }
1348 } else if(ekind == kEventControlSetFocusPart) {
1349 if(widget) {
1350 ControlPartCode part;
1351 GetEventParameter(event, kEventParamControlPart, typeControlPartCode, 0,
1352 sizeof(part), 0, &part);
1353 if(part == kControlFocusNoPart){
1354 if (widget->hasFocus())
1355 QApplicationPrivate::setFocusWidget(0, Qt::OtherFocusReason);
1356 } else
1357 widget->setFocus();
1358 }
1359 if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget))
1360 CallNextEventHandler(er, event);
1361 } else if(ekind == kEventControlGetClickActivation) {
1362 ClickActivationResult clickT = kActivateAndIgnoreClick;
1363 SetEventParameter(event, kEventParamClickActivation, typeClickActivationResult,
1364 sizeof(clickT), &clickT);
1365 } else if(ekind == kEventControlGetPartRegion) {
1366 handled_event = false;
1367 if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget) && CallNextEventHandler(er, event) == noErr) {
1368 handled_event = true;
1369 break;
1370 }
1371 if(widget && !widget->isWindow()) {
1372 ControlPartCode part;
1373 GetEventParameter(event, kEventParamControlPart, typeControlPartCode, 0,
1374 sizeof(part), 0, &part);
1375 if(part == kControlClickableMetaPart && widget->testAttribute(Qt::WA_TransparentForMouseEvents)) {
1376 RgnHandle rgn;
1377 GetEventParameter(event, kEventParamControlRegion, typeQDRgnHandle, 0,
1378 sizeof(rgn), 0, &rgn);
1379 SetEmptyRgn(rgn);
1380 handled_event = true;
1381 } else if(part == kControlStructureMetaPart || part == kControlClickableMetaPart) {
1382 RgnHandle rgn;
1383 GetEventParameter(event, kEventParamControlRegion, typeQDRgnHandle, 0,
1384 sizeof(rgn), 0, &rgn);
1385 SetRectRgn(rgn, 0, 0, widget->width(), widget->height());
1386 if(QWidgetPrivate::qt_widget_rgn(widget, kWindowStructureRgn, rgn, false))
1387 handled_event = true;
1388 } else if(part == kControlOpaqueMetaPart) {
1389 if(widget->d_func()->isOpaque) {
1390 RgnHandle rgn;
1391 GetEventParameter(event, kEventParamControlRegion, typeQDRgnHandle, 0,
1392 sizeof(RgnHandle), 0, &rgn);
1393 SetRectRgn(rgn, 0, 0, widget->width(), widget->height());
1394 QWidgetPrivate::qt_widget_rgn(widget, kWindowStructureRgn, rgn, false);
1395 SetEventParameter(event, kEventParamControlRegion, typeQDRgnHandle,
1396 sizeof(RgnHandle), &rgn);
1397 handled_event = true;
1398 }
1399 }
1400 }
1401 } else if(ekind == kEventControlOwningWindowChanged) {
1402 if(!HIObjectIsOfClass((HIObjectRef)hiview, kObjectQWidget))
1403 CallNextEventHandler(er, event);
1404 if(widget && qt_mac_window_for(hiview)) {
1405 WindowRef foo = 0;
1406 GetEventParameter(event, kEventParamControlCurrentOwningWindow, typeWindowRef, 0,
1407 sizeof(foo), 0, &foo);
1408 widget->d_func()->initWindowPtr();
1409 }
1410 if (widget)
1411 qt_event_request_window_change(widget);
1412 } else if(ekind == kEventControlDragEnter || ekind == kEventControlDragWithin ||
1413 ekind == kEventControlDragLeave || ekind == kEventControlDragReceive) {
1414 // dnd are really handled in qdnd_mac.cpp,
1415 // just modularize the code a little...
1416 DragRef drag;
1417 GetEventParameter(event, kEventParamDragRef, typeDragRef, 0, sizeof(drag), 0, &drag);
1418 handled_event = false;
1419 bool drag_allowed = false;
1420
1421 QWidget *dropWidget = widget;
1422 if (qobject_cast<QFocusFrame *>(widget)){
1423 // We might shadow widgets underneath the focus
1424 // frame, so stay interrested, and let the dnd through
1425 drag_allowed = true;
1426 handled_event = true;
1427 Point where;
1428 GetDragMouse(drag, &where, 0);
1429 dropWidget = QApplication::widgetAt(QPoint(where.h, where.v));
1430
1431 if (dropWidget != QDragManager::self()->currentTarget()) {
1432 // We have to 'fake' enter and leave events for the shaddowed widgets:
1433 if (ekind == kEventControlDragEnter) {
1434 if (QDragManager::self()->currentTarget())
1435 QDragManager::self()->currentTarget()->d_func()->qt_mac_dnd_event(kEventControlDragLeave, drag);
1436 if (dropWidget) {
1437 dropWidget->d_func()->qt_mac_dnd_event(kEventControlDragEnter, drag);
1438 }
1439 // Set dropWidget to zero, so qt_mac_dnd_event
1440 // doesn't get called a second time below:
1441 dropWidget = 0;
1442 } else if (ekind == kEventControlDragLeave) {
1443 dropWidget = QDragManager::self()->currentTarget();
1444 if (dropWidget) {
1445 dropWidget->d_func()->qt_mac_dnd_event(kEventControlDragLeave, drag);
1446 }
1447 // Set dropWidget to zero, so qt_mac_dnd_event
1448 // doesn't get called a second time below:
1449 dropWidget = 0;
1450 }
1451 }
1452 }
1453
1454 // Send the dnd event to the widget:
1455 if (dropWidget && dropWidget->d_func()->qt_mac_dnd_event(ekind, drag)) {
1456 drag_allowed = true;
1457 handled_event = true;
1458 }
1459
1460 if (ekind == kEventControlDragEnter) {
1461 // If we don't accept the enter event, we will
1462 // receive no more drag events for this widget
1463 const Boolean wouldAccept = drag_allowed ? true : false;
1464 SetEventParameter(event, kEventParamControlWouldAcceptDrop, typeBoolean,
1465 sizeof(wouldAccept), &wouldAccept);
1466 }
1467 } else if (ekind == kEventControlBoundsChanged) {
1468 if (!widget || widget->isWindow() || widget->testAttribute(Qt::WA_Moved) || widget->testAttribute(Qt::WA_Resized)) {
1469 handled_event = false;
1470 } else {
1471 // Sync our view in case some other (non-Qt) view is controlling us.
1472 handled_event = true;
1473 Rect newBounds;
1474 GetEventParameter(event, kEventParamCurrentBounds,
1475 typeQDRectangle, 0, sizeof(Rect), 0, &newBounds);
1476 QRect rect(newBounds.left, newBounds.top,
1477 newBounds.right - newBounds.left, newBounds.bottom - newBounds.top);
1478
1479 bool moved = widget->testAttribute(Qt::WA_Moved);
1480 bool resized = widget->testAttribute(Qt::WA_Resized);
1481 widget->setGeometry(rect);
1482 widget->setAttribute(Qt::WA_Moved, moved);
1483 widget->setAttribute(Qt::WA_Resized, resized);
1484 qt_event_request_window_change(widget);
1485 }
1486 } else if (ekind == kEventControlGetSizeConstraints) {
1487 if (!widget || !qt_isGenuineQWidget(widget)) {
1488 handled_event = false;
1489 } else {
1490 handled_event = true;
1491 QWidgetItem item(widget);
1492 QSize size = item.minimumSize();
1493 HISize hisize = { size.width(), size.height() };
1494 SetEventParameter(event, kEventParamMinimumSize, typeHISize, sizeof(HISize), &hisize);
1495 size = item.maximumSize();
1496 hisize.width = size.width() + 2; // ### shouldn't have to add 2 (but it works).
1497 hisize.height = size.height();
1498 SetEventParameter(event, kEventParamMaximumSize, typeHISize, sizeof(HISize), &hisize);
1499 }
1500 } else if (ekind == kEventControlVisibilityChanged) {
1501 handled_event = false;
1502 if (widget) {
1503 qt_event_request_window_change(widget);
1504 if (!HIViewIsVisible(HIViewRef(widget->winId()))) {
1505 extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
1506 if (widget == qt_button_down)
1507 qt_button_down = 0;
1508 }
1509 }
1510 }
1511 break; }
1512 case kEventClassMouse: {
1513 bool send_to_app = false;
1514 extern QPointer<QWidget> qt_button_down; //qapplication_mac.cpp
1515 if(qt_button_down)
1516 send_to_app = true;
1517 if(send_to_app) {
1518 OSStatus err = SendEventToApplication(event);
1519 if(err != noErr)
1520 handled_event = false;
1521 } else {
1522 CallNextEventHandler(er, event);
1523 }
1524 break; }
1525 default:
1526 handled_event = false;
1527 break;
1528 }
1529 if(!handled_event) //let the event go through
1530 return eventNotHandledErr;
1531 return noErr; //we eat the event
1532}
1533#endif
1534
1535OSViewRef qt_mac_create_widget(QWidget *widget, QWidgetPrivate *widgetPrivate, OSViewRef parent)
1536{
1537#ifdef QT_MAC_USE_COCOA
1538 QMacCocoaAutoReleasePool pool;
1539 QT_MANGLE_NAMESPACE(QCocoaView) *view = [[QT_MANGLE_NAMESPACE(QCocoaView) alloc] initWithQWidget:widget widgetPrivate:widgetPrivate];
1540 if (view && parent)
1541 [parent addSubview:view];
1542 return view;
1543#else
1544 Q_UNUSED(widget);
1545 Q_UNUSED(widgetPrivate);
1546 if(!widget_class) {
1547 OSStatus err = HIObjectRegisterSubclass(kObjectQWidget, kHIViewClassID, 0, make_widget_eventUPP(),
1548 GetEventTypeCount(widget_events), widget_events,
1549 0, &widget_class);
1550 if (err && err != hiObjectClassExistsErr)
1551 qWarning("QWidget: Internal error (%d)", __LINE__);
1552 }
1553 HIViewRef ret = 0;
1554 if(HIObjectCreate(kObjectQWidget, 0, (HIObjectRef*)&ret) != noErr)
1555 qWarning("QWidget: Internal error (%d)", __LINE__);
1556 if(ret && parent)
1557 HIViewAddSubview(parent, ret);
1558 return ret;
1559#endif
1560}
1561
1562void qt_mac_unregister_widget()
1563{
1564#ifndef QT_MAC_USE_COCOA
1565 HIObjectUnregisterClass(widget_class);
1566 widget_class = 0;
1567#endif
1568}
1569
1570void QWidgetPrivate::toggleDrawers(bool visible)
1571{
1572 for (int i = 0; i < children.size(); ++i) {
1573 register QObject *object = children.at(i);
1574 if (!object->isWidgetType())
1575 continue;
1576 QWidget *widget = static_cast<QWidget*>(object);
1577 if(qt_mac_is_macdrawer(widget)) {
1578 if(visible) {
1579 if (!widget->testAttribute(Qt::WA_WState_ExplicitShowHide))
1580 widget->show();
1581 } else {
1582 widget->hide();
1583 widget->setAttribute(Qt::WA_WState_ExplicitShowHide, false);
1584 }
1585 }
1586 }
1587}
1588
1589/*****************************************************************************
1590 QWidgetPrivate member functions
1591 *****************************************************************************/
1592bool QWidgetPrivate::qt_mac_update_sizer(QWidget *w, int up)
1593{
1594 // I'm not sure what "up" is
1595 if(!w || !w->isWindow())
1596 return false;
1597
1598 QTLWExtra *topData = w->d_func()->topData();
1599 QWExtra *extraData = w->d_func()->extraData();
1600 // topData->resizer is only 4 bits, so subtracting -1 from zero causes bad stuff
1601 // to happen, prevent that here (you really want the thing hidden).
1602 if (up >= 0 || topData->resizer != 0)
1603 topData->resizer += up;
1604 OSWindowRef windowRef = qt_mac_window_for(OSViewRef(w->winId()));
1605 {
1606#ifndef QT_MAC_USE_COCOA
1607 WindowClass wclass;
1608 GetWindowClass(windowRef, &wclass);
1609 if(!(GetAvailableWindowAttributes(wclass) & kWindowResizableAttribute))
1610 return true;
1611#endif
1612 }
1613 bool remove_grip = (topData->resizer || (w->windowFlags() & Qt::FramelessWindowHint)
1614 || (extraData->maxw && extraData->maxh &&
1615 extraData->maxw == extraData->minw && extraData->maxh == extraData->minh));
1616#ifndef QT_MAC_USE_COCOA
1617 WindowAttributes attr;
1618 GetWindowAttributes(windowRef, &attr);
1619 if(remove_grip) {
1620 if(attr & kWindowResizableAttribute) {
1621 ChangeWindowAttributes(qt_mac_window_for(w), kWindowNoAttributes,
1622 kWindowResizableAttribute);
1623 ReshapeCustomWindow(qt_mac_window_for(w));