1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
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>
48 #include <qcoreapplication.h>
50 #include <QMainWindow>
52 #include <private/qmainwindowlayout_p.h>
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
62 static QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) *sharedCocoaWindowDelegate = nil;
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.
68 static void cleanupCocoaWindowDelegate()
70 [sharedCocoaWindowDelegate release];
73 @implementation QT_MANGLE_NAMESPACE(QCocoaWindowDelegate)
79 m_windowHash = new QHash<NSWindow *, QWidget *>();
80 m_drawerHash = new QHash<NSDrawer *, QWidget *>();
87 sharedCocoaWindowDelegate = nil;
88 QHash<NSWindow *, QWidget *>::const_iterator windowIt = m_windowHash->constBegin();
89 while (windowIt != m_windowHash->constEnd()) {
90 [windowIt.key() setDelegate:nil];
94 QHash<NSDrawer *, QWidget *>::const_iterator drawerIt = m_drawerHash->constBegin();
95 while (drawerIt != m_drawerHash->constEnd()) {
96 [drawerIt.key() setDelegate:nil];
103 + (id)allocWithZone:(NSZone *)zone
105 @synchronized(self) {
106 if (sharedCocoaWindowDelegate == nil) {
107 sharedCocoaWindowDelegate = [super allocWithZone:zone];
108 return sharedCocoaWindowDelegate;
109 qAddPostRoutine(cleanupCocoaWindowDelegate);
115 + (QT_MANGLE_NAMESPACE(QCocoaWindowDelegate)*)sharedDelegate
117 @synchronized(self) {
118 if (sharedCocoaWindowDelegate == nil)
121 return [[sharedCocoaWindowDelegate retain] autorelease];
124 -(void)syncSizeForWidget:(QWidget *)qwidget toSize:(const QSize &)newSize fromSize:(const QSize &)oldSize
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);
133 QResizeEvent qre(newSize, oldSize);
134 if (qwidget->testAttribute(Qt::WA_PendingResizeEvent)) {
135 qwidget->setAttribute(Qt::WA_PendingResizeEvent, false);
136 QApplication::sendEvent(qwidget, &qre);
138 qt_sendSpontaneousEvent(qwidget, &qre);
143 - (void)dumpMaximizedStateforWidget:(QWidget*)qwidget window:(NSWindow *)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);
155 - (NSSize)closestAcceptableSizeForWidget:(QWidget *)qwidget window:(NSWindow *)window
156 withNewSize:(NSSize)proposedSize
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;
166 - (NSSize)windowWillResize:(NSWindow *)windowToResize toSize:(NSSize)proposedFrameSize
168 QWidget *qwidget = m_windowHash->value(windowToResize);
169 return [self closestAcceptableSizeForWidget:qwidget window:windowToResize
170 withNewSize:[NSWindow contentRectForFrameRect:
172 proposedFrameSize.width,
173 proposedFrameSize.height)
174 styleMask:[windowToResize styleMask]].size];
177 - (NSSize)drawerWillResizeContents:(NSDrawer *)sender toSize:(NSSize)contentSize
179 QWidget *qwidget = m_drawerHash->value(sender);
180 return [self closestAcceptableSizeForWidget:qwidget window:nil withNewSize:contentSize];
183 -(void)windowDidMiniaturize:(NSNotification*)notification
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);
192 // Send hide to match Qt on X11 and Windows
193 QEvent e(QEvent::Hide);
194 qt_sendSpontaneousEvent(qwidget, &e);
197 - (void)windowDidResize:(NSNotification *)notification
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);
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);
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];
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]) {
226 // We need to repaint the toolbar as well.
227 QMainWindow* mWindow = qobject_cast<QMainWindow*>(qwidget->window());
229 QMainWindowLayout *mLayout = qobject_cast<QMainWindowLayout*>(mWindow->layout());
230 QList<QToolBar *> toolbarList = mLayout->qtoolbarsInUnifiedToolbarList;
232 for (int i = 0; i < toolbarList.size(); ++i) {
233 QToolBar* toolbar = toolbarList.at(i);
240 - (void)windowDidMove:(NSNotification *)notification
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();
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);
261 QMoveEvent qme(qtPoint, oldRect.topLeft());
262 qt_sendSpontaneousEvent(qwidget, &qme);
267 -(BOOL)windowShouldClose:(id)windowThatWantsToClose
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);
274 -(void)windowDidDeminiaturize:(NSNotification *)notification
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);
290 qt_sendSpontaneousEvent(qwidget, &qse);
293 -(void)windowDidBecomeMain:(NSNotification*)notification
295 QWidget *qwidget = m_windowHash->value([notification object]);
297 onApplicationWindowChangedActivation(qwidget, true);
300 -(void)windowDidResignMain:(NSNotification*)notification
302 QWidget *qwidget = m_windowHash->value([notification object]);
304 onApplicationWindowChangedActivation(qwidget, false);
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
311 QWidget *qwidget = m_windowHash->value([notification object]);
313 onApplicationWindowChangedActivation(qwidget, true);
316 -(void)windowDidResignKey:(NSNotification*)notification
318 QWidget *qwidget = m_windowHash->value([notification object]);
320 onApplicationWindowChangedActivation(qwidget, false);
323 -(QWidget *)qt_qwidgetForWindow:(NSWindow *)window
325 return m_windowHash->value(window);
328 - (BOOL)windowShouldZoom:(NSWindow *)window toFrame:(NSRect)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) {
337 widgetPrivate->topData()->wasMaximized = false;
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;
350 - (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)defaultFrame
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;
365 - (void)becomeDelegteForWindow:(NSWindow *)window widget:(QWidget *)widget
367 m_windowHash->insert(window, widget);
368 [window setDelegate:self];
371 - (void)resignDelegateForWindow:(NSWindow *)window
373 [window setDelegate:nil];
374 m_windowHash->remove(window);
377 - (void)becomeDelegateForDrawer:(NSDrawer *)drawer widget:(QWidget *)widget
379 m_drawerHash->insert(drawer, widget);
380 [drawer setDelegate:self];
381 NSWindow *window = [[drawer contentView] window];
382 [self becomeDelegteForWindow:window widget:widget];
385 - (void)resignDelegateForDrawer:(NSDrawer *)drawer
387 QWidget *widget = m_drawerHash->value(drawer);
388 [drawer setDelegate:nil];
390 [self resignDelegateForWindow:[[drawer contentView] window]];
391 m_drawerHash->remove(drawer);
394 - (BOOL)window:(NSWindow *)window shouldPopUpDocumentPathMenu:(NSMenu *)menu
397 QWidget *qwidget = m_windowHash->value(window);
398 if (qwidget && !qwidget->windowFilePath().isEmpty()) {
404 - (BOOL)window:(NSWindow *)window shouldDragDocumentWithEvent:(NSEvent *)event
405 from:(NSPoint)dragImageLocation
406 withPasteboard:(NSPasteboard *)pasteboard
409 Q_UNUSED(dragImageLocation);
410 Q_UNUSED(pasteboard);
411 QWidget *qwidget = m_windowHash->value(window);
412 if (qwidget && !qwidget->windowFilePath().isEmpty()) {
418 - (void)syncContentViewFrame: (NSNotification *)notification
420 NSView *cView = [notification object];
422 NSWindow *window = [cView window];
423 QWidget *qwidget = m_windowHash->value(window);
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];