source: trunk/src/gui/kernel/qcocoawindowdelegate_mac.mm

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

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

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