Fixed crash in GL 2 paint engine on Intel Atom.
[profile/ivi/qtbase.git] / src / widgets / platforms / mac / qcocoawindowdelegate_mac.mm
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 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 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #import "private/qcocoawindowdelegate_mac_p.h"
43 #include <private/qwidget_p.h>
44 #include <private/qapplication_p.h>
45 #include <private/qt_cocoa_helpers_mac_p.h>
46 #include <qevent.h>
47 #include <qlayout.h>
48 #include <qcoreapplication.h>
49 #include <qmenubar.h>
50 #include <QMainWindow>
51 #include <QToolBar>
52 #include <private/qmainwindowlayout_p.h>
53
54 QT_BEGIN_NAMESPACE
55 extern QWidgetData *qt_qwidget_data(QWidget *); // qwidget.cpp
56 extern void onApplicationWindowChangedActivation(QWidget *, bool); //qapplication_mac.mm
57 extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp
58 QT_END_NAMESPACE
59
60 QT_USE_NAMESPACE
61
62 static QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) *sharedCocoaWindowDelegate = nil;
63
64 // This is a singleton, but unlike most Cocoa singletons, it lives in a library and could be
65 // pontentially loaded and unloaded. This means we should at least attempt to do the
66 // memory management correctly.
67
68 static void cleanupCocoaWindowDelegate()
69 {
70     [sharedCocoaWindowDelegate release];
71 }
72
73 @implementation QT_MANGLE_NAMESPACE(QCocoaWindowDelegate)
74
75 - (id)init
76 {
77     self = [super init];
78     if (self != nil) {
79         m_windowHash = new QHash<NSWindow *, QWidget *>();
80         m_drawerHash = new QHash<NSDrawer *, QWidget *>();
81     }
82     return self;
83 }
84
85 - (void)dealloc
86 {
87     sharedCocoaWindowDelegate = nil;
88     QHash<NSWindow *, QWidget *>::const_iterator windowIt = m_windowHash->constBegin();
89     while (windowIt != m_windowHash->constEnd()) {
90         [windowIt.key() setDelegate:nil];
91         ++windowIt;
92     }
93     delete m_windowHash;
94     QHash<NSDrawer *, QWidget *>::const_iterator drawerIt = m_drawerHash->constBegin();
95     while (drawerIt != m_drawerHash->constEnd()) {
96         [drawerIt.key() setDelegate:nil];
97         ++drawerIt;
98     }
99     delete m_drawerHash;
100     [super dealloc];
101 }
102
103 + (id)allocWithZone:(NSZone *)zone
104 {
105     @synchronized(self) {
106         if (sharedCocoaWindowDelegate == nil) {
107             sharedCocoaWindowDelegate = [super allocWithZone:zone];
108             return sharedCocoaWindowDelegate;
109             qAddPostRoutine(cleanupCocoaWindowDelegate);
110         }
111     }
112     return nil;
113 }
114
115 + (QT_MANGLE_NAMESPACE(QCocoaWindowDelegate)*)sharedDelegate
116 {
117     @synchronized(self) {
118         if (sharedCocoaWindowDelegate == nil)
119             [[self alloc] init];
120     }
121     return [[sharedCocoaWindowDelegate retain] autorelease];
122 }
123
124 -(void)syncSizeForWidget:(QWidget *)qwidget toSize:(const QSize &)newSize fromSize:(const QSize &)oldSize
125 {
126     qt_qwidget_data(qwidget)->crect.setSize(newSize);
127     // ### static contents optimization needs to go here
128     const OSViewRef view = qt_mac_nativeview_for(qwidget);
129     [view setFrameSize:NSMakeSize(newSize.width(), newSize.height())];
130     if (!qwidget->isVisible()) {
131         qwidget->setAttribute(Qt::WA_PendingResizeEvent, true);
132     } else {
133         QResizeEvent qre(newSize, oldSize);
134         if (qwidget->testAttribute(Qt::WA_PendingResizeEvent)) {
135             qwidget->setAttribute(Qt::WA_PendingResizeEvent, false);
136             QApplication::sendEvent(qwidget, &qre);
137         } else {
138             qt_sendSpontaneousEvent(qwidget, &qre);
139         }
140     }
141 }
142
143 - (void)dumpMaximizedStateforWidget:(QWidget*)qwidget window:(NSWindow *)window
144 {
145     if (!window)
146         return; // Nothing to do.
147     QWidgetData *widgetData = qt_qwidget_data(qwidget);
148     if ((widgetData->window_state & Qt::WindowMaximized) && ![window isZoomed]) {
149         widgetData->window_state &= ~Qt::WindowMaximized;
150         QWindowStateChangeEvent e(Qt::WindowState(widgetData->window_state | Qt::WindowMaximized));
151         qt_sendSpontaneousEvent(qwidget, &e);
152     }
153 }
154
155 - (NSSize)closestAcceptableSizeForWidget:(QWidget *)qwidget window:(NSWindow *)window
156                              withNewSize:(NSSize)proposedSize
157 {
158     [self dumpMaximizedStateforWidget:qwidget window:window];
159     QSize newSize = QLayout::closestAcceptableSize(qwidget, 
160                                                    QSize(proposedSize.width, proposedSize.height));
161     return [NSWindow frameRectForContentRect:
162             NSMakeRect(0., 0., newSize.width(), newSize.height())
163                                    styleMask:[window styleMask]].size;
164 }
165
166 - (NSSize)windowWillResize:(NSWindow *)windowToResize toSize:(NSSize)proposedFrameSize
167 {
168     QWidget *qwidget = m_windowHash->value(windowToResize);
169     return [self closestAcceptableSizeForWidget:qwidget window:windowToResize
170                                     withNewSize:[NSWindow contentRectForFrameRect:
171                                                  NSMakeRect(0, 0, 
172                                                             proposedFrameSize.width,
173                                                             proposedFrameSize.height) 
174                                                     styleMask:[windowToResize styleMask]].size];
175 }
176
177 - (NSSize)drawerWillResizeContents:(NSDrawer *)sender toSize:(NSSize)contentSize
178 {
179     QWidget *qwidget = m_drawerHash->value(sender);
180     return [self closestAcceptableSizeForWidget:qwidget window:nil withNewSize:contentSize];
181 }
182
183 -(void)windowDidMiniaturize:(NSNotification*)notification
184 {
185     QWidget *qwidget = m_windowHash->value([notification object]);
186     if (!qwidget->isMinimized()) {
187         QWidgetData *widgetData = qt_qwidget_data(qwidget);
188         widgetData->window_state = widgetData->window_state | Qt::WindowMinimized;
189         QWindowStateChangeEvent e(Qt::WindowStates(widgetData->window_state & ~Qt::WindowMinimized));
190         qt_sendSpontaneousEvent(qwidget, &e);
191     }
192     // Send hide to match Qt on X11 and Windows
193     QEvent e(QEvent::Hide);
194     qt_sendSpontaneousEvent(qwidget, &e);
195 }
196
197 - (void)windowDidResize:(NSNotification *)notification
198 {
199     NSWindow *window = [notification object];
200     QWidget *qwidget = m_windowHash->value(window);
201     QWidgetData *widgetData = qt_qwidget_data(qwidget);
202     if (!(qwidget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) && [window isZoomed]) {
203         widgetData->window_state = widgetData->window_state | Qt::WindowMaximized;
204         QWindowStateChangeEvent e(Qt::WindowStates(widgetData->window_state
205                                                    & ~Qt::WindowMaximized));
206         qt_sendSpontaneousEvent(qwidget, &e);
207     } else {
208         widgetData->window_state = widgetData->window_state & ~Qt::WindowMaximized;
209         QWindowStateChangeEvent e(Qt::WindowStates(widgetData->window_state
210                                                    | Qt::WindowMaximized));
211         qt_sendSpontaneousEvent(qwidget, &e);
212     }
213     NSRect rect = [[window contentView] frame];
214     const QSize newSize(rect.size.width, rect.size.height);
215     const QSize &oldSize = widgetData->crect.size();
216     if (newSize != oldSize) {
217         QWidgetPrivate::qt_mac_update_sizer(qwidget);
218         [self syncSizeForWidget:qwidget toSize:newSize fromSize:oldSize];
219     }
220
221     // We force the repaint to be synchronized with the resize of the window.
222     // Otherwise, the resize looks sluggish because we paint one event loop later.
223     if ([[window contentView] inLiveResize]) {
224         qwidget->repaint();
225
226         // We need to repaint the toolbar as well.
227         QMainWindow* mWindow = qobject_cast<QMainWindow*>(qwidget->window());
228         if (mWindow) {
229             QMainWindowLayout *mLayout = qobject_cast<QMainWindowLayout*>(mWindow->layout());
230             QList<QToolBar *> toolbarList = mLayout->qtoolbarsInUnifiedToolbarList;
231
232             for (int i = 0; i < toolbarList.size(); ++i) {
233                 QToolBar* toolbar = toolbarList.at(i);
234                 toolbar->repaint();
235             }
236         }
237     }
238 }
239
240 - (void)windowDidMove:(NSNotification *)notification
241 {
242     // The code underneath needs to translate the window location
243     // from bottom left (which is the origin used by Cocoa) to
244     // upper left (which is the origin used by Qt):
245     NSWindow *window = [notification object];
246     NSRect newRect = [window frame];
247     QWidget *qwidget = m_windowHash->value(window);
248     QPoint qtPoint = flipPoint(NSMakePoint(newRect.origin.x,
249                                            newRect.origin.y + newRect.size.height)).toPoint();
250     const QRect &oldRect = qwidget->frameGeometry();
251
252     if (qtPoint.x() != oldRect.x() || qtPoint.y() != oldRect.y()) {
253         QWidgetData *widgetData = qt_qwidget_data(qwidget);
254         QRect oldCRect = widgetData->crect;
255         QWidgetPrivate *widgetPrivate = qt_widget_private(qwidget);
256         const QRect &fStrut = widgetPrivate->frameStrut();
257         widgetData->crect.moveTo(qtPoint.x() + fStrut.left(), qtPoint.y() + fStrut.top());
258         if (!qwidget->isVisible()) {
259             qwidget->setAttribute(Qt::WA_PendingMoveEvent, true);
260         } else {
261             QMoveEvent qme(qtPoint, oldRect.topLeft());
262             qt_sendSpontaneousEvent(qwidget, &qme);
263         }
264     }
265 }
266
267 -(BOOL)windowShouldClose:(id)windowThatWantsToClose
268 {
269     QWidget *qwidget = m_windowHash->value(windowThatWantsToClose);
270     QScopedLoopLevelCounter counter(qt_widget_private(qwidget)->threadData);
271     return qt_widget_private(qwidget)->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent);
272 }
273
274 -(void)windowDidDeminiaturize:(NSNotification *)notification
275 {
276     QWidget *qwidget = m_windowHash->value([notification object]);
277     QWidgetData *widgetData = qt_qwidget_data(qwidget);
278     Qt::WindowStates currState = Qt::WindowStates(widgetData->window_state);
279     Qt::WindowStates newState = currState;
280     if (currState & Qt::WindowMinimized)
281         newState &= ~Qt::WindowMinimized;
282     if (!(currState & Qt::WindowActive))
283         newState |= Qt::WindowActive;
284     if (newState != currState) {
285         widgetData->window_state = newState;
286         QWindowStateChangeEvent e(currState);
287         qt_sendSpontaneousEvent(qwidget, &e);
288     }
289     QShowEvent qse;
290     qt_sendSpontaneousEvent(qwidget, &qse);
291 }
292
293 -(void)windowDidBecomeMain:(NSNotification*)notification
294 {
295     QWidget *qwidget = m_windowHash->value([notification object]);
296     Q_ASSERT(qwidget);
297     onApplicationWindowChangedActivation(qwidget, true);
298 }
299
300 -(void)windowDidResignMain:(NSNotification*)notification
301 {
302     QWidget *qwidget = m_windowHash->value([notification object]);
303     Q_ASSERT(qwidget);
304     onApplicationWindowChangedActivation(qwidget, false);
305 }
306
307 // These are the same as main, but they are probably better to keep separate since there is a
308 // tiny difference between main and key windows.
309 -(void)windowDidBecomeKey:(NSNotification*)notification
310 {
311     QWidget *qwidget = m_windowHash->value([notification object]);
312     Q_ASSERT(qwidget);
313     onApplicationWindowChangedActivation(qwidget, true);
314 }
315
316 -(void)windowDidResignKey:(NSNotification*)notification
317 {
318     QWidget *qwidget = m_windowHash->value([notification object]);
319     Q_ASSERT(qwidget);
320     onApplicationWindowChangedActivation(qwidget, false);
321 }
322
323 -(QWidget *)qt_qwidgetForWindow:(NSWindow *)window
324 {
325     return m_windowHash->value(window);
326 }
327
328 - (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)newFrame
329 {
330     Q_UNUSED(newFrame);
331     // saving the current window geometry before the window is maximized
332     QWidget *qwidget = m_windowHash->value(window);
333     QWidgetPrivate *widgetPrivate = qt_widget_private(qwidget);
334     if (qwidget->isWindow()) {
335         if(qwidget->windowState() & Qt::WindowMaximized) {
336             // Restoring
337             widgetPrivate->topData()->wasMaximized = false;
338         } else {
339             // Maximizing
340             widgetPrivate->topData()->normalGeometry = qwidget->geometry();
341             // If the window was maximized we need to update the coordinates since now it will start at 0,0.
342             // We do this in a special field that is only used when not restoring but manually resizing the window.
343             // Since the coordinates are fixed we just set a boolean flag.
344             widgetPrivate->topData()->wasMaximized = true;
345         }
346     }
347     return YES;
348 }
349
350 - (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)defaultFrame
351 {
352     NSRect frameToReturn = defaultFrame;
353     QWidget *qwidget = m_windowHash->value(window);
354     QSizeF size = qwidget->maximumSize();
355     NSRect windowFrameRect = [window frame];
356     NSRect viewFrameRect = [[window contentView] frame];
357     // consider additional size required for titlebar & frame
358     frameToReturn.size.width = qMin<CGFloat>(frameToReturn.size.width,
359             size.width()+(windowFrameRect.size.width - viewFrameRect.size.width));
360     frameToReturn.size.height = qMin<CGFloat>(frameToReturn.size.height,
361             size.height()+(windowFrameRect.size.height - viewFrameRect.size.height));
362     return frameToReturn;
363 }
364
365 - (void)becomeDelegteForWindow:(NSWindow *)window  widget:(QWidget *)widget
366 {
367     m_windowHash->insert(window, widget);
368     [window setDelegate:self];
369 }
370
371 - (void)resignDelegateForWindow:(NSWindow *)window
372 {
373     [window setDelegate:nil];
374     m_windowHash->remove(window);
375 }
376
377 - (void)becomeDelegateForDrawer:(NSDrawer *)drawer widget:(QWidget *)widget
378 {
379     m_drawerHash->insert(drawer, widget);
380     [drawer setDelegate:self];
381     NSWindow *window = [[drawer contentView] window];
382     [self becomeDelegteForWindow:window widget:widget];
383 }
384
385 - (void)resignDelegateForDrawer:(NSDrawer *)drawer
386 {
387     QWidget *widget = m_drawerHash->value(drawer);
388     [drawer setDelegate:nil];
389     if (widget)
390         [self resignDelegateForWindow:[[drawer contentView] window]];
391     m_drawerHash->remove(drawer);
392 }
393
394 - (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu
395 {
396     Q_UNUSED(menu);
397     QWidget *qwidget = m_windowHash->value(window);
398     if (qwidget && !qwidget->windowFilePath().isEmpty()) {
399         return YES;
400     }
401     return NO;
402 }
403
404 - (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event
405                                                           from:(NSPoint)dragImageLocation
406                                                 withPasteboard:(NSPasteboard *)pasteboard
407 {
408     Q_UNUSED(event);
409     Q_UNUSED(dragImageLocation);
410     Q_UNUSED(pasteboard);
411     QWidget *qwidget = m_windowHash->value(window);
412     if (qwidget && !qwidget->windowFilePath().isEmpty()) {
413         return YES;
414     }
415     return NO;
416 }
417
418 - (void)syncContentViewFrame: (NSNotification *)notification
419 {
420     NSView *cView = [notification object];
421     if (cView) {
422         NSWindow *window = [cView window];
423         QWidget *qwidget = m_windowHash->value(window);
424         if (qwidget) {
425             QWidgetData *widgetData = qt_qwidget_data(qwidget);
426             NSRect rect = [cView frame];
427             const QSize newSize(rect.size.width, rect.size.height);
428             const QSize &oldSize = widgetData->crect.size();
429             if (newSize != oldSize) {
430                 [self syncSizeForWidget:qwidget toSize:newSize fromSize:oldSize];
431             }
432         }
433
434     }
435 }
436
437 @end