Cocoa: Improve basic window handling.
[profile/ivi/qtbase.git] / src / plugins / platforms / cocoa / qcocoawindow.mm
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #include "qcocoawindow.h"
42 #include "qnswindowdelegate.h"
43 #include "qcocoaautoreleasepool.h"
44 #include "qcocoaglcontext.h"
45 #include "qcocoahelpers.h"
46 #include "qnsview.h"
47 #include <QtCore/private/qcore_mac_p.h>
48 #include <qwindow.h>
49 #include <QWindowSystemInterface>
50 #include <QPlatformScreen>
51
52 #include <Cocoa/Cocoa.h>
53 #include <Carbon/Carbon.h>
54
55 #include <QDebug>
56
57 @implementation QNSWindow
58
59 - (BOOL)canBecomeKeyWindow
60 {
61     // The default implementation returns NO for title-bar less windows,
62     // override and return yes here to make sure popup windows such as
63     // the combobox popup can become the key window.
64     return YES;
65 }
66
67 - (BOOL)canBecomeMainWindow
68 {
69     BOOL canBecomeMain = YES; // By default, windows can become the main window
70
71     // Windows with a transient parent (such as combobox popup windows)
72     // cannot become the main window:
73     if (m_cocoaPlatformWindow->window()->transientParent())
74         canBecomeMain = NO;
75
76     return canBecomeMain;
77 }
78
79
80 @end
81
82 @implementation QNSPanel
83
84 - (BOOL)canBecomeKeyWindow
85 {
86     return NO;
87 }
88
89 @end
90
91 QCocoaWindow::QCocoaWindow(QWindow *tlw)
92     : QPlatformWindow(tlw)
93     , m_glContext(0)
94     , m_inConstructor(true)
95 {
96     QCocoaAutoReleasePool pool;
97
98     m_contentView = [[QNSView alloc] initWithQWindow:tlw platformWindow:this];
99     setGeometry(tlw->geometry());
100
101     m_nsWindow = createNSWindow();
102     setNSWindow(m_nsWindow);
103
104     m_inConstructor = false;
105 }
106
107 QCocoaWindow::~QCocoaWindow()
108 {
109     [m_contentView release];
110     clearNSWindow(m_nsWindow);
111     [m_nsWindow release];
112 }
113
114 void QCocoaWindow::setGeometry(const QRect &rect)
115 {
116     if (geometry() == rect)
117         return;
118 #ifdef QT_COCOA_ENABLE_WINDOW_DEBUG
119     qDebug() << "QCocoaWindow::setGeometry" << this << rect;
120 #endif
121     QPlatformWindow::setGeometry(rect);
122
123     NSRect bounds = qt_mac_flipRect(rect, window());
124     [m_nsWindow setContentSize : bounds.size];
125     [m_nsWindow setFrameOrigin : bounds.origin];
126 }
127
128 void QCocoaWindow::setVisible(bool visible)
129 {
130     QCocoaAutoReleasePool pool;
131 #ifdef QT_COCOA_ENABLE_WINDOW_DEBUG
132     qDebug() << "QCocoaWindow::setVisible" << window() << visible;
133 #endif
134     if (visible) {
135         if (window()->transientParent()) {
136             // The parent window might have moved while this window was hidden,
137             // update the window geometry if there is a parent.
138             setGeometry(window()->geometry());
139
140             // Register popup windows so that the parent window can
141             // close them when needed.
142             if (window()->windowType() == Qt::Popup) {
143                 // qDebug() << "transientParent and popup" << window()->windowType() << Qt::Popup << (window()->windowType() & Qt::Popup);
144
145                 QCocoaWindow *parentCocoaWindow = static_cast<QCocoaWindow *>(window()->transientParent()->handle());
146                 parentCocoaWindow->m_activePopupWindow = window();
147             }
148
149         }
150
151         // Make sure the QWindow has a frame ready before we show the NSWindow.
152         QWindowSystemInterface::handleSynchronousExposeEvent(window(), QRect(QPoint(), geometry().size()));
153
154         if ([m_nsWindow canBecomeKeyWindow])
155             [m_nsWindow makeKeyAndOrderFront:nil];
156         else
157             [m_nsWindow orderFront: nil];
158     } else {
159         // qDebug() << "close" << this;
160         [m_nsWindow orderOut:m_nsWindow];
161     }
162 }
163
164 Qt::WindowFlags QCocoaWindow::setWindowFlags(Qt::WindowFlags flags)
165 {
166     m_windowFlags = flags;
167     return m_windowFlags;
168 }
169
170 void QCocoaWindow::setWindowTitle(const QString &title)
171 {
172     QCocoaAutoReleasePool pool;
173
174     CFStringRef windowTitle = QCFString::toCFStringRef(title);
175     [m_nsWindow setTitle: const_cast<NSString *>(reinterpret_cast<const NSString *>(windowTitle))];
176     CFRelease(windowTitle);
177 }
178
179 void QCocoaWindow::raise()
180 {
181     //qDebug() << "raise" << this;
182     // ### handle spaces (see Qt 4 raise_sys in qwidget_mac.mm)
183     [m_nsWindow orderFront: m_nsWindow];
184 }
185
186 void QCocoaWindow::lower()
187 {
188     [m_nsWindow orderBack: m_nsWindow];
189 }
190
191 void QCocoaWindow::propagateSizeHints()
192 {
193     QCocoaAutoReleasePool pool;
194
195     [m_nsWindow setMinSize : qt_mac_toNSSize(window()->minimumSize())];
196     [m_nsWindow setMaxSize : qt_mac_toNSSize(window()->maximumSize())];
197
198 #ifdef QT_COCOA_ENABLE_WINDOW_DEBUG
199     qDebug() << "QCocoaWindow::propagateSizeHints" << this;
200     qDebug() << "     min/max " << window()->minimumSize() << window()->maximumSize();
201     qDebug() << "     basesize" << window()->baseSize();
202     qDebug() << "     geometry" << geometry();
203 #endif
204
205     if (!window()->sizeIncrement().isNull())
206         [m_nsWindow setResizeIncrements : qt_mac_toNSSize(window()->sizeIncrement())];
207
208     QSize baseSize = window()->baseSize();
209     if (!baseSize.isNull() && baseSize.isValid()) {
210         [m_nsWindow setFrameSize : NSMakeSize(baseSize.width(), baseSize.height()) display : YES];
211     }
212 }
213
214 bool QCocoaWindow::setKeyboardGrabEnabled(bool grab)
215 {
216     if (grab && ![m_nsWindow isKeyWindow])
217         [m_nsWindow makeKeyWindow];
218     else if (!grab && [m_nsWindow isKeyWindow])
219         [m_nsWindow resignKeyWindow];
220     return true;
221 }
222
223 bool QCocoaWindow::setMouseGrabEnabled(bool grab)
224 {
225     if (grab && ![m_nsWindow isKeyWindow])
226         [m_nsWindow makeKeyWindow];
227     else if (!grab && [m_nsWindow isKeyWindow])
228         [m_nsWindow resignKeyWindow];
229     return true;
230 }
231
232 WId QCocoaWindow::winId() const
233 {
234     return WId(m_nsWindow);
235 }
236
237 void QCocoaWindow::setParent(const QPlatformWindow *window)
238 {
239     // recreate the window for compatibility
240     clearNSWindow(m_nsWindow);
241     [m_nsWindow close];
242     [m_nsWindow release];
243
244     m_nsWindow = createNSWindow();
245     setNSWindow(m_nsWindow);
246 }
247
248 NSView *QCocoaWindow::contentView() const
249 {
250     return [m_nsWindow contentView];
251 }
252
253 void QCocoaWindow::windowWillMove()
254 {
255     // Close any open popups on window move
256     if (m_activePopupWindow) {
257         QWindowSystemInterface::handleSynchronousCloseEvent(m_activePopupWindow);
258         m_activePopupWindow = 0;
259     }
260 }
261
262 void QCocoaWindow::windowDidMove()
263 {
264     [m_contentView updateGeometry];
265 }
266
267 void QCocoaWindow::windowDidResize()
268 {
269     NSRect rect = [[m_nsWindow contentView]frame];
270     // Call setFrameSize which will trigger a frameDidChangeNotification on QNSView.
271     [[m_nsWindow contentView] setFrameSize:rect.size];
272 }
273
274 void QCocoaWindow::windowWillClose()
275 {
276     QWindowSystemInterface::handleSynchronousCloseEvent(window());
277 }
278
279 void QCocoaWindow::setCurrentContext(QCocoaGLContext *context)
280 {
281     m_glContext = context;
282 }
283
284 QCocoaGLContext *QCocoaWindow::currentContext() const
285 {
286     return m_glContext;
287 }
288
289 NSWindow * QCocoaWindow::createNSWindow()
290 {
291     QCocoaAutoReleasePool pool;
292
293     NSRect frame = qt_mac_flipRect(window()->geometry(), window());
294
295     Qt::WindowType type = window()->windowType();
296     Qt::WindowFlags flags = window()->windowFlags();
297
298     NSUInteger styleMask;
299     NSWindow *createdWindow = 0;
300
301     // Use NSPanel for popup-type windows. (Popup, Tool, ToolTip, SplashScreen)
302     if ((type & Qt::Popup) == Qt::Popup) {
303         if (type == Qt::Popup || type == Qt::ToolTip || type == Qt::SplashScreen) {
304             styleMask = NSBorderlessWindowMask;
305         } else {
306             styleMask = (NSUtilityWindowMask | NSResizableWindowMask | NSClosableWindowMask |
307                          NSMiniaturizableWindowMask | NSTitledWindowMask);
308         }
309
310         QNSPanel *window;
311         window  = [[QNSPanel alloc] initWithContentRect:frame
312                                          styleMask: styleMask
313                                          backing:NSBackingStoreBuffered
314                                          defer:NO]; // Deferring window creation breaks OpenGL (the GL context is set up
315                                                     // before the window is shown and needs a proper window.).
316         [window setHasShadow:YES];
317         createdWindow = window;
318     } else {
319         styleMask = (NSResizableWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSTitledWindowMask);
320         QNSWindow *window;
321         window  = [[QNSWindow alloc] initWithContentRect:frame
322                                          styleMask: styleMask
323                                          backing:NSBackingStoreBuffered
324                                          defer:NO]; // Deferring window creation breaks OpenGL (the GL context is set up
325                                                     // before the window is shown and needs a proper window.).
326         window->m_cocoaPlatformWindow = this;
327         createdWindow = window;
328     }
329     return createdWindow;
330 }
331
332 void QCocoaWindow::setNSWindow(NSWindow *window)
333 {
334     QNSWindowDelegate *delegate = [[QNSWindowDelegate alloc] initWithQCocoaWindow:this];
335     [window setDelegate:delegate];
336     [window setAcceptsMouseMovedEvents:YES];
337
338     // Prevent Cocoa from releasing the window on close. Qt
339     // handles the close event asynchronously and we want to
340     // make sure that m_nsWindow stays valid until the
341     // QCocoaWindow is deleted by Qt.
342     [window setReleasedWhenClosed : NO];
343
344     [[NSNotificationCenter defaultCenter] addObserver:m_contentView
345                                           selector:@selector(windowDidBecomeKey)
346                                           name:NSWindowDidBecomeKeyNotification
347                                           object:m_nsWindow];
348
349     [[NSNotificationCenter defaultCenter] addObserver:m_contentView
350                                           selector:@selector(windowDidResignKey)
351                                           name:NSWindowDidResignKeyNotification
352                                           object:m_nsWindow];
353
354     [[NSNotificationCenter defaultCenter] addObserver:m_contentView
355                                           selector:@selector(windowDidBecomeMain)
356                                           name:NSWindowDidBecomeMainNotification
357                                           object:m_nsWindow];
358
359     [[NSNotificationCenter defaultCenter] addObserver:m_contentView
360                                           selector:@selector(windowDidResignMain)
361                                           name:NSWindowDidResignMainNotification
362                                           object:m_nsWindow];
363
364
365     // ### Accept touch events by default.
366     // Beware that enabling touch events has a negative impact on the overall performance.
367     // We probably need a QWindowSystemInterface API to enable/disable touch events.
368     [m_contentView setAcceptsTouchEvents:YES];
369
370     [window setContentView:m_contentView];
371 }
372
373 void QCocoaWindow::clearNSWindow(NSWindow *window)
374 {
375     [[NSNotificationCenter defaultCenter] removeObserver:m_contentView];
376 }
377
378 // Returns the current global screen geometry for the nswindow associated with this window.
379 QRect QCocoaWindow::windowGeometry() const
380 {
381     NSRect rect = [m_nsWindow frame];
382     QPlatformScreen *onScreen = QPlatformScreen::platformScreenForWindow(window());
383     int flippedY = onScreen->geometry().height() - rect.origin.y - rect.size.height;  // account for nswindow inverted y.
384     QRect qRect = QRect(rect.origin.x, flippedY, rect.size.width, rect.size.height);
385     return qRect;
386 }
387
388 // Returns a pointer to the parent QCocoaWindow for this window, or 0 if there is none.
389 QCocoaWindow *QCocoaWindow::parentCocoaWindow() const
390 {
391     if (window() && window()->transientParent()) {
392         return static_cast<QCocoaWindow*>(window()->transientParent()->handle());
393     }
394     return 0;
395 }
396