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