diff options
Diffstat (limited to 'src/gui/kernel/qcocoawindowdelegate_mac.mm')
-rw-r--r-- | src/gui/kernel/qcocoawindowdelegate_mac.mm | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/src/gui/kernel/qcocoawindowdelegate_mac.mm b/src/gui/kernel/qcocoawindowdelegate_mac.mm new file mode 100644 index 0000000..8480179 --- /dev/null +++ b/src/gui/kernel/qcocoawindowdelegate_mac.mm @@ -0,0 +1,349 @@ +/**************************************************************************** + ** + ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) + ** + ** This file is part of the QtGui module of the Qt Toolkit. + ** + ** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ + ** + ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + ** + ****************************************************************************/ + +#import "private/qcocoawindowdelegate_mac_p.h" +#ifdef QT_MAC_USE_COCOA +#include <private/qwidget_p.h> +#include <private/qapplication_p.h> +#include <private/qt_cocoa_helpers_mac_p.h> +#include <qevent.h> +#include <qlayout.h> +#include <qcoreapplication.h> +#include <qmenubar.h> + +QT_BEGIN_NAMESPACE +extern QWidgetData *qt_qwidget_data(QWidget *); // qwidget.cpp +extern void onApplicationWindowChangedActivation(QWidget *, bool); //qapplication_mac.mm +extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); // qapplication.cpp +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +static QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) *sharedCocoaWindowDelegate = nil; + +// This is a singleton, but unlike most Cocoa singletons, it lives in a library and could be +// pontentially loaded and unloaded. This means we should at least attempt to do the +// memory management correctly. + +static void cleanupCocoaWindowDelegate() +{ + [sharedCocoaWindowDelegate release]; +} + +@implementation QT_MANGLE_NAMESPACE(QCocoaWindowDelegate) + +- (id)init +{ + self = [super init]; + if (self != nil) { + m_windowHash = new QHash<NSWindow *, QWidget *>(); + m_drawerHash = new QHash<NSDrawer *, QWidget *>(); + } + return self; +} + +- (void)dealloc +{ + sharedCocoaWindowDelegate = nil; + QHash<NSWindow *, QWidget *>::const_iterator windowIt = m_windowHash->constBegin(); + while (windowIt != m_windowHash->constEnd()) { + [windowIt.key() setDelegate:nil]; + ++windowIt; + } + delete m_windowHash; + QHash<NSDrawer *, QWidget *>::const_iterator drawerIt = m_drawerHash->constBegin(); + while (drawerIt != m_drawerHash->constEnd()) { + [drawerIt.key() setDelegate:nil]; + ++drawerIt; + } + delete m_drawerHash; + [super dealloc]; +} + ++ (id)allocWithZone:(NSZone *)zone +{ + @synchronized(self) { + if (sharedCocoaWindowDelegate == nil) { + sharedCocoaWindowDelegate = [super allocWithZone:zone]; + return sharedCocoaWindowDelegate; + qAddPostRoutine(cleanupCocoaWindowDelegate); + } + } + return nil; +} + ++ (QT_MANGLE_NAMESPACE(QCocoaWindowDelegate)*)sharedDelegate +{ + @synchronized(self) { + if (sharedCocoaWindowDelegate == nil) + [[self alloc] init]; + } + return [[sharedCocoaWindowDelegate retain] autorelease]; +} + +-(void)syncSizeForWidget:(QWidget *)qwidget toSize:(const QSize &)newSize fromSize:(const QSize &)oldSize +{ + qt_qwidget_data(qwidget)->crect.setSize(newSize); + // ### static contents optimization needs to go here + const OSViewRef view = qt_mac_nativeview_for(qwidget); + [view setFrameSize:NSMakeSize(newSize.width(), newSize.height())]; + if (!qwidget->isVisible()) { + qwidget->setAttribute(Qt::WA_PendingResizeEvent, true); + } else { + QResizeEvent qre(newSize, oldSize); + qt_sendSpontaneousEvent(qwidget, &qre); + } +} + +- (void)dumpMaximizedStateforWidget:(QWidget*)qwidget window:(NSWindow *)window; +{ + if (!window) + return; // Nothing to do. + QWidgetData *widgetData = qt_qwidget_data(qwidget); + if ((widgetData->window_state & Qt::WindowMaximized) && ![window isZoomed]) { + widgetData->window_state &= ~Qt::WindowMaximized; + QWindowStateChangeEvent e(Qt::WindowState(widgetData->window_state | Qt::WindowMaximized)); + qt_sendSpontaneousEvent(qwidget, &e); + } +} + +- (NSSize)closestAcceptableSizeForWidget:(QWidget *)qwidget window:(NSWindow *)window + withNewSize:(NSSize)proposedSize +{ + [self dumpMaximizedStateforWidget:qwidget window:window]; + QSize newSize = QLayout::closestAcceptableSize(qwidget, + QSize(proposedSize.width, proposedSize.height)); + return [NSWindow frameRectForContentRect: + NSMakeRect(0., 0., newSize.width(), newSize.height()) + styleMask:[window styleMask]].size; +} + +- (NSSize)windowWillResize:(NSWindow *)windowToResize toSize:(NSSize)proposedFrameSize +{ + QWidget *qwidget = m_windowHash->value(windowToResize); + return [self closestAcceptableSizeForWidget:qwidget window:windowToResize + withNewSize:[NSWindow contentRectForFrameRect: + NSMakeRect(0, 0, + proposedFrameSize.width, + proposedFrameSize.height) + styleMask:[windowToResize styleMask]].size]; +} + +- (NSSize)drawerWillResizeContents:(NSDrawer *)sender toSize:(NSSize)contentSize +{ + QWidget *qwidget = m_drawerHash->value(sender); + return [self closestAcceptableSizeForWidget:qwidget window:nil withNewSize:contentSize]; +} + +-(void)windowDidMiniaturize:(NSNotification*)notification +{ + QWidget *qwidget = m_windowHash->value([notification object]); + if (!qwidget->isMinimized()) { + QWidgetData *widgetData = qt_qwidget_data(qwidget); + widgetData->window_state = widgetData->window_state | Qt::WindowMinimized; + QWindowStateChangeEvent e(Qt::WindowStates(widgetData->window_state & ~Qt::WindowMinimized)); + qt_sendSpontaneousEvent(qwidget, &e); + } + // Send hide to match Qt on X11 and Windows + QEvent e(QEvent::Hide); + qt_sendSpontaneousEvent(qwidget, &e); +} + +- (void)windowDidResize:(NSNotification *)notification +{ + NSWindow *window = [notification object]; + QWidget *qwidget = m_windowHash->value(window); + // Just here to handle the is zoomed method. + QWidgetData *widgetData = qt_qwidget_data(qwidget); + if (!(qwidget->windowState() & (Qt::WindowMaximized | Qt::WindowFullScreen)) && [window isZoomed]) { + widgetData->window_state = widgetData->window_state | Qt::WindowMaximized; + QWindowStateChangeEvent e(Qt::WindowStates(widgetData->window_state + & ~Qt::WindowMaximized)); + qt_sendSpontaneousEvent(qwidget, &e); + } + [self checkForMove:[window frame] forWidget:qwidget]; + NSRect rect = [[window contentView] frame]; + const QSize newSize(rect.size.width, rect.size.height); + const QSize &oldSize = widgetData->crect.size(); + if (newSize != oldSize) { + QWidgetPrivate::qt_mac_update_sizer(qwidget); + [self syncSizeForWidget:qwidget toSize:newSize fromSize:oldSize]; + } +} + +- (void)checkForMove:(const NSRect &)newRect forWidget:(QWidget *)qwidget +{ + // newRect's origin is bottom left. + QPoint qtPoint = flipPoint(NSMakePoint(newRect.origin.x, + newRect.origin.y + newRect.size.height)).toPoint(); + const QRect &oldRect = qwidget->frameGeometry(); + if (qtPoint.x() != oldRect.x() || qtPoint.y() != oldRect.y()) { + QWidgetData *widgetData = qt_qwidget_data(qwidget); + QRect oldCRect = widgetData->crect; + QWidgetPrivate *widgetPrivate = qt_widget_private(qwidget); + const QRect &fStrut = widgetPrivate->frameStrut(); + widgetData->crect.moveTo(qtPoint.x() + fStrut.left(), qtPoint.y() + fStrut.top()); + if (!qwidget->isVisible()) { + qwidget->setAttribute(Qt::WA_PendingMoveEvent, true); + } else { + QMoveEvent qme(qtPoint, oldRect.topLeft()); + qt_sendSpontaneousEvent(qwidget, &qme); + } + } +} + +- (void)windowDidMove:(NSNotification *)notification +{ + [self windowDidResize:notification]; +} + +-(BOOL)windowShouldClose:(id)windowThatWantsToClose +{ + QWidget *qwidget = m_windowHash->value(windowThatWantsToClose); + QScopedLoopLevelCounter counter(qt_widget_private(qwidget)->threadData); + return qt_widget_private(qwidget)->close_helper(QWidgetPrivate::CloseWithSpontaneousEvent); +} + +-(void)windowDidDeminiaturize:(NSNotification *)notification +{ + QWidget *qwidget = m_windowHash->value([notification object]); + QWidgetData *widgetData = qt_qwidget_data(qwidget); + Qt::WindowStates currState = Qt::WindowStates(widgetData->window_state); + Qt::WindowStates newState = currState; + if (currState & Qt::WindowMinimized) + newState &= ~Qt::WindowMinimized; + if (!(currState & Qt::WindowActive)) + newState |= Qt::WindowActive; + if (newState != currState) { + widgetData->window_state = newState; + QWindowStateChangeEvent e(currState); + qt_sendSpontaneousEvent(qwidget, &e); + } + QShowEvent qse; + qt_sendSpontaneousEvent(qwidget, &qse); +} + +-(void)windowDidBecomeMain:(NSNotification*)notification +{ + QWidget *qwidget = m_windowHash->value([notification object]); + Q_ASSERT(qwidget); + if (qwidget->isActiveWindow()) + return; // Widget is already active, no need to go through re-activation. + + onApplicationWindowChangedActivation(qwidget, true); +} + +-(void)windowDidResignMain:(NSNotification*)notification +{ + QWidget *qwidget = m_windowHash->value([notification object]); + Q_ASSERT(qwidget); + onApplicationWindowChangedActivation(qwidget, false); +} + +// These are the same as main, but they are probably better to keep separate since there is a +// tiny difference between main and key windows. +-(void)windowDidBecomeKey:(NSNotification*)notification +{ + QWidget *qwidget = m_windowHash->value([notification object]); + Q_ASSERT(qwidget); + if (qwidget->isActiveWindow()) + return; // Widget is already active, no need to go through re-activation + + + onApplicationWindowChangedActivation(qwidget, true); +} + +-(void)windowDidResignKey:(NSNotification*)notification +{ + QWidget *qwidget = m_windowHash->value([notification object]); + Q_ASSERT(qwidget); + onApplicationWindowChangedActivation(qwidget, false); +} + +-(QWidget *)qt_qwidgetForWindow:(NSWindow *)window +{ + return m_windowHash->value(window); +} + +- (NSRect)windowWillUseStandardFrame:(NSWindow *)window defaultFrame:(NSRect)defaultFrame +{ + NSRect frameToReturn = defaultFrame; + QWidget *qwidget = m_windowHash->value(window); + QSizeF size = qwidget->maximumSize(); + frameToReturn.size.width = qMin<CGFloat>(frameToReturn.size.width, size.width()); + frameToReturn.size.height = qMin<CGFloat>(frameToReturn.size.height, size.height()); + return frameToReturn; +} + +- (void)becomeDelegteForWindow:(NSWindow *)window widget:(QWidget *)widget +{ + m_windowHash->insert(window, widget); + [window setDelegate:self]; +} + +- (void)resignDelegateForWindow:(NSWindow *)window +{ + [window setDelegate:nil]; + m_windowHash->remove(window); +} + +- (void)becomeDelegateForDrawer:(NSDrawer *)drawer widget:(QWidget *)widget +{ + m_drawerHash->insert(drawer, widget); + [drawer setDelegate:self]; + NSWindow *window = [[drawer contentView] window]; + [self becomeDelegteForWindow:window widget:widget]; +} + +- (void)resignDelegateForDrawer:(NSDrawer *)drawer +{ + QWidget *widget = m_drawerHash->value(drawer); + [drawer setDelegate:nil]; + if (widget) + [self resignDelegateForWindow:[[drawer contentView] window]]; + m_drawerHash->remove(drawer); +} + +@end +#endif// QT_MAC_USE_COCOA |