From 2907fbd8982203053a16a0cf8a6c4b72b2c61984 Mon Sep 17 00:00:00 2001 From: Christoph Schleifenbaum Date: Tue, 31 Jan 2012 16:15:51 +0100 Subject: Add QCocoaColor- and FontDialogHelper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ie6e648e9e1f4ffbb34d73f9afdd6cc77e86bc3d1 Reviewed-by: Morten Johan Sørvig --- src/plugins/platforms/cocoa/cocoa.pro | 4 + .../platforms/cocoa/qcocoacolordialoghelper.h | 78 ++++ .../platforms/cocoa/qcocoacolordialoghelper.mm | 465 ++++++++++++++++++++ .../platforms/cocoa/qcocoafontdialoghelper.h | 82 ++++ .../platforms/cocoa/qcocoafontdialoghelper.mm | 488 +++++++++++++++++++++ src/plugins/platforms/cocoa/qcocoatheme.mm | 25 +- 6 files changed, 1139 insertions(+), 3 deletions(-) create mode 100644 src/plugins/platforms/cocoa/qcocoacolordialoghelper.h create mode 100644 src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm create mode 100644 src/plugins/platforms/cocoa/qcocoafontdialoghelper.h create mode 100644 src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm (limited to 'src/plugins') diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro index 09b708d9a1..1e803a1c7c 100644 --- a/src/plugins/platforms/cocoa/cocoa.pro +++ b/src/plugins/platforms/cocoa/cocoa.pro @@ -23,7 +23,9 @@ OBJECTIVE_SOURCES += main.mm \ qmultitouch_mac.mm \ qcocoaaccessibilityelement.mm \ qcocoaaccessibility.mm \ + qcocoacolordialoghelper.mm \ qcocoafiledialoghelper.mm \ + qcocoafontdialoghelper.mm \ qcocoacursor.mm \ HEADERS += qcocoaintegration.h \ @@ -45,7 +47,9 @@ HEADERS += qcocoaintegration.h \ qmultitouch_mac_p.h \ qcocoaaccessibilityelement.h \ qcocoaaccessibility.h \ + qcocoacolordialoghelper.h \ qcocoafiledialoghelper.h \ + qcocoafontdialoghelper.h \ qcocoacursor.h \ FORMS += $$PWD/../../../widgets/dialogs/qfiledialog.ui diff --git a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.h b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.h new file mode 100644 index 0000000000..505fd4f111 --- /dev/null +++ b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOCOACOLORDIALOGHELPER_H +#define QCOCOACOLORDIALOGHELPER_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QCocoaColorDialogHelper : public QPlatformColorDialogHelper +{ +public: + QCocoaColorDialogHelper(); + virtual ~QCocoaColorDialogHelper(); + + void platformNativeDialogModalHelp(); + void _q_platformRunNativeAppModalPanel(); + void deleteNativeDialog_sys(); + bool show_sys(QFlags, Qt::WindowFlags, QWindow*); + void hide_sys(); + + DialogCode dialogResultCode_sys(); + void setCurrentColor_sys(const QColor&); + QColor currentColor_sys() const; + +public: + bool showCocoaColorPanel(QWindow *parent); + bool hideCocoaColorPanel(); + + void createNSColorPanelDelegate(); + +private: + void *mDelegate; +}; + +QT_END_NAMESPACE + +#endif // QCOCOACOLORDIALOGHELPER_H diff --git a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm new file mode 100644 index 0000000000..f1d6129e3d --- /dev/null +++ b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm @@ -0,0 +1,465 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcocoacolordialoghelper.h" + +#ifndef QT_NO_COLORDIALOG + +#include +#include +#include +#include + +#include "qcocoahelpers.h" + +#import + +QT_USE_NAMESPACE + +static NSButton *macCreateButton(const char *text, NSView *superview) +{ + static const NSRect buttonFrameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; + + NSButton *button = [[NSButton alloc] initWithFrame:buttonFrameRect]; + [button setButtonType:NSMomentaryLightButton]; + [button setBezelStyle:NSRoundedBezelStyle]; + [button setTitle:(NSString*)(CFStringRef)QCFString(QDialogButtonBox::tr(text) + .remove(QLatin1Char('&')))]; + [[button cell] setFont:[NSFont systemFontOfSize: + [NSFont systemFontSizeForControlSize:NSRegularControlSize]]]; + [superview addSubview:button]; + return button; +} + +@class QT_MANGLE_NAMESPACE(QNSColorPanelDelegate); + +@interface QT_MANGLE_NAMESPACE(QNSColorPanelDelegate) : NSObject +{ + @public + NSColorPanel *mColorPanel; + QCocoaColorDialogHelper *mHelper; + NSView *mStolenContentView; + NSButton *mOkButton; + NSButton *mCancelButton; + QColor mQtColor; + NSInteger mResultCode; + BOOL mDialogIsExecuting; + BOOL mResultSet; +}; +- (void)relayout; +- (void)updateQtColor; +- (void)finishOffWithCode:(NSInteger)code; +@end + +@implementation QT_MANGLE_NAMESPACE(QNSColorPanelDelegate) + +- (id)initWithDialogHelper:(QCocoaColorDialogHelper *)helper +{ + self = [super init]; + mColorPanel = [NSColorPanel sharedColorPanel]; + mHelper = helper; + mResultCode = NSCancelButton; + mDialogIsExecuting = false; + mResultSet = false; + + if (mHelper->options()->testOption(QColorDialogOptions::NoButtons)) { + mStolenContentView = 0; + mOkButton = 0; + mCancelButton = 0; + } else { + // steal the color panel's contents view + mStolenContentView = [mColorPanel contentView]; + [mStolenContentView retain]; + [mColorPanel setContentView:0]; + + // create a new content view and add the stolen one as a subview + NSRect frameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; + NSView *ourContentView = [[NSView alloc] initWithFrame:frameRect]; + [ourContentView addSubview:mStolenContentView]; + + // create OK and Cancel buttons and add these as subviews + mOkButton = macCreateButton("&OK", ourContentView); + mCancelButton = macCreateButton("Cancel", ourContentView); + + [mColorPanel setContentView:ourContentView]; + [mColorPanel setDefaultButtonCell:[mOkButton cell]]; + [self relayout]; + + [mOkButton setAction:@selector(onOkClicked)]; + [mOkButton setTarget:self]; + + [mCancelButton setAction:@selector(onCancelClicked)]; + [mCancelButton setTarget:self]; + } + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(colorChanged:) + name:NSColorPanelColorDidChangeNotification + object:mColorPanel]; + + [mColorPanel retain]; + return self; +} + +- (void)dealloc +{ + if (mOkButton) { + NSView *ourContentView = [mColorPanel contentView]; + + // return stolen stuff to its rightful owner + [mStolenContentView removeFromSuperview]; + [mColorPanel setContentView:mStolenContentView]; + [mOkButton release]; + [mCancelButton release]; + [ourContentView release]; + } + + [mColorPanel setDelegate:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [super dealloc]; +} + +- (void)closePanel +{ + [mColorPanel close]; +} + +- (void)windowDidResize:(NSNotification *)notification +{ + Q_UNUSED(notification); + [self relayout]; +} + +- (void)colorChanged:(NSNotification *)notification +{ + Q_UNUSED(notification); + [self updateQtColor]; + emit mHelper->colorSelected(mQtColor); +} + +- (void)relayout +{ + if (!mOkButton) + return; + + NSRect rect = [[mStolenContentView superview] frame]; + + // should a priori be kept in sync with qfontdialog_mac.mm + const CGFloat ButtonMinWidth = 78.0; // 84.0 for Carbon + const CGFloat ButtonMinHeight = 32.0; + const CGFloat ButtonSpacing = 0.0; + const CGFloat ButtonTopMargin = 0.0; + const CGFloat ButtonBottomMargin = 7.0; + const CGFloat ButtonSideMargin = 9.0; + + [mOkButton sizeToFit]; + NSSize okSizeHint = [mOkButton frame].size; + + [mCancelButton sizeToFit]; + NSSize cancelSizeHint = [mCancelButton frame].size; + + const CGFloat ButtonWidth = qMin(qMax(ButtonMinWidth, + qMax(okSizeHint.width, cancelSizeHint.width)), + CGFloat((rect.size.width - 2.0 * ButtonSideMargin - ButtonSpacing) * 0.5)); + const CGFloat ButtonHeight = qMax(ButtonMinHeight, + qMax(okSizeHint.height, cancelSizeHint.height)); + + NSRect okRect = { { rect.size.width - ButtonSideMargin - ButtonWidth, + ButtonBottomMargin }, + { ButtonWidth, ButtonHeight } }; + [mOkButton setFrame:okRect]; + [mOkButton setNeedsDisplay:YES]; + + NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - ButtonWidth, + ButtonBottomMargin }, + { ButtonWidth, ButtonHeight } }; + [mCancelButton setFrame:cancelRect]; + [mCancelButton setNeedsDisplay:YES]; + + const CGFloat Y = ButtonBottomMargin + ButtonHeight + ButtonTopMargin; + NSRect stolenCVRect = { { 0.0, Y }, + { rect.size.width, rect.size.height - Y } }; + [mStolenContentView setFrame:stolenCVRect]; + [mStolenContentView setNeedsDisplay:YES]; + + [[mStolenContentView superview] setNeedsDisplay:YES]; +} + +- (void)onOkClicked +{ + Q_ASSERT(mHackedPanel); + [mColorPanel close]; + [self updateQtColor]; + [self finishOffWithCode:NSOKButton]; +} + +- (void)onCancelClicked +{ + if (mOkButton) { + [mColorPanel close]; + mQtColor = QColor(); + [self finishOffWithCode:NSCancelButton]; + } +} + +- (void)updateQtColor +{ + NSColor *color = [mColorPanel color]; + NSString *colorSpaceName = [color colorSpaceName]; + if (colorSpaceName == NSDeviceCMYKColorSpace) { + CGFloat cyan = 0, magenta = 0, yellow = 0, black = 0, alpha = 0; + [color getCyan:&cyan magenta:&magenta yellow:&yellow black:&black alpha:&alpha]; + mQtColor.setCmykF(cyan, magenta, yellow, black, alpha); + } else if (colorSpaceName == NSCalibratedRGBColorSpace || colorSpaceName == NSDeviceRGBColorSpace) { + CGFloat red = 0, green = 0, blue = 0, alpha = 0; + [color getRed:&red green:&green blue:&blue alpha:&alpha]; + mQtColor.setRgbF(red, green, blue, alpha); + } else if (colorSpaceName == NSNamedColorSpace) { + NSColor *tmpColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + CGFloat red = 0, green = 0, blue = 0, alpha = 0; + [tmpColor getRed:&red green:&green blue:&blue alpha:&alpha]; + mQtColor.setRgbF(red, green, blue, alpha); + } else { + NSColorSpace *colorSpace = [color colorSpace]; + if ([colorSpace colorSpaceModel] == NSCMYKColorSpaceModel && [color numberOfComponents] == 5){ + CGFloat components[5]; + [color getComponents:components]; + mQtColor.setCmykF(components[0], components[1], components[2], components[3], components[4]); + } else { + NSColor *tmpColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; + CGFloat red = 0, green = 0, blue = 0, alpha = 0; + [tmpColor getRed:&red green:&green blue:&blue alpha:&alpha]; + mQtColor.setRgbF(red, green, blue, alpha); + } + } + emit mHelper->currentColorChanged(mQtColor); +} + +- (void)showModelessPanel +{ + mDialogIsExecuting = false; + [mColorPanel makeKeyAndOrderFront:mColorPanel]; +} + +- (BOOL)runApplicationModalPanel +{ + mDialogIsExecuting = true; + [mColorPanel setDelegate:self]; + [mColorPanel setContinuous:YES]; + [NSApp runModalForWindow:mColorPanel]; + return (mResultCode == NSOKButton); +} + +- (QT_PREPEND_NAMESPACE(QPlatformDialogHelper::DialogCode))dialogResultCode +{ + return (mResultCode == NSOKButton) ? QT_PREPEND_NAMESPACE(QPlatformDialogHelper::Accepted) : QT_PREPEND_NAMESPACE(QPlatformDialogHelper::Rejected); +} + +- (BOOL)windowShouldClose:(id)window +{ + Q_UNUSED(window); + if (!mOkButton) + [self updateQtColor]; + if (mDialogIsExecuting) { + [self finishOffWithCode:NSCancelButton]; + } else { + mResultSet = true; + emit mHelper->reject(); + } + return true; +} + +- (void)finishOffWithCode:(NSInteger)code +{ + mResultCode = code; + if (mDialogIsExecuting) { + // We stop the current modal event loop. The control + // will then return inside -(void)exec below. + // It's important that the modal event loop is stopped before + // we accept/reject QColorDialog, since QColorDialog has its + // own event loop that needs to be stopped last. + [NSApp stopModalWithCode:code]; + } else { + // Since we are not in a modal event loop, we can safely close + // down QColorDialog + // Calling accept() or reject() can in turn call closeCocoaColorPanel. + // This check will prevent any such recursion. + if (!mResultSet) { + mResultSet = true; + if (mResultCode == NSCancelButton) { + emit mHelper->reject(); + } else { + emit mHelper->accept(); + } + } + } +} + +@end + +QT_BEGIN_NAMESPACE + +QCocoaColorDialogHelper::QCocoaColorDialogHelper() : + mDelegate(0) +{ +} + +QCocoaColorDialogHelper::~QCocoaColorDialogHelper() +{ + deleteNativeDialog_sys(); +} + +void QCocoaColorDialogHelper::platformNativeDialogModalHelp() +{ + // Do a queued meta-call to open the native modal dialog so it opens after the new + // event loop has started to execute (in QDialog::exec). Using a timer rather than + // a queued meta call is intentional to ensure that the call is only delivered when + // [NSApp run] runs (timers are handeled special in cocoa). If NSApp is not + // running (which is the case if e.g a top-most QEventLoop has been + // interrupted, and the second-most event loop has not yet been reactivated (regardless + // if [NSApp run] is still on the stack)), showing a native modal dialog will fail. + QTimer::singleShot(1, this, SIGNAL(launchNativeAppModalPanel())); +} + +void QCocoaColorDialogHelper::_q_platformRunNativeAppModalPanel() +{ + // TODO: +#if 0 + QBoolBlocker nativeDialogOnTop(QApplicationPrivate::native_modal_dialog_active); +#endif + QT_MANGLE_NAMESPACE(QNSColorPanelDelegate) *delegate = static_cast(mDelegate); + [delegate runApplicationModalPanel]; + if (dialogResultCode_sys() == QPlatformDialogHelper::Accepted) + emit accept(); + else + emit reject(); +} + +void QCocoaColorDialogHelper::deleteNativeDialog_sys() +{ + if (!mDelegate) + return; + [reinterpret_cast(mDelegate) release]; + mDelegate = 0; +} + +bool QCocoaColorDialogHelper::show_sys(QFlags, Qt::WindowFlags, QWindow *parent) +{ + return showCocoaColorPanel(parent); +} + +void QCocoaColorDialogHelper::hide_sys() +{ + if (!mDelegate) + return; + [reinterpret_cast(mDelegate)->mColorPanel close]; +} + +QCocoaColorDialogHelper::DialogCode QCocoaColorDialogHelper::dialogResultCode_sys() +{ + QT_MANGLE_NAMESPACE(QNSColorPanelDelegate) *delegate = static_cast(mDelegate); + return [delegate dialogResultCode]; +} + +void QCocoaColorDialogHelper::setCurrentColor_sys(const QColor &color) +{ + if (!mDelegate) + createNSColorPanelDelegate(); + QT_MANGLE_NAMESPACE(QNSColorPanelDelegate) *delegate = static_cast(mDelegate); + NSColor *nsColor; + const QColor::Spec spec = color.spec(); + if (spec == QColor::Cmyk) { + nsColor = [NSColor colorWithDeviceCyan:color.cyanF() + magenta:color.magentaF() + yellow:color.yellowF() + black:color.blackF() + alpha:color.alphaF()]; + } else { + nsColor = [NSColor colorWithCalibratedRed:color.redF() + green:color.greenF() + blue:color.blueF() + alpha:color.alphaF()]; + } + delegate->mQtColor = color; + [delegate->mColorPanel setColor:nsColor]; +} + +QColor QCocoaColorDialogHelper::currentColor_sys() const +{ + return reinterpret_cast(mDelegate)->mQtColor; +} + +void QCocoaColorDialogHelper::createNSColorPanelDelegate() +{ + if (mDelegate) + return; + + QT_MANGLE_NAMESPACE(QNSColorPanelDelegate) *delegate = [[QT_MANGLE_NAMESPACE(QNSColorPanelDelegate) alloc] + initWithDialogHelper:this]; + + mDelegate = delegate; +} + +bool QCocoaColorDialogHelper::showCocoaColorPanel(QWindow *parent) +{ + Q_UNUSED(parent); + createNSColorPanelDelegate(); + QT_MANGLE_NAMESPACE(QNSColorPanelDelegate) *delegate = static_cast(mDelegate); + [delegate->mColorPanel setShowsAlpha:options()->testOption(QColorDialogOptions::ShowAlphaChannel)]; + [delegate showModelessPanel]; + return true; +} + +bool QCocoaColorDialogHelper::hideCocoaColorPanel() +{ + if (!mDelegate){ + return false; + } else { + QT_MANGLE_NAMESPACE(QNSColorPanelDelegate) *delegate = static_cast(mDelegate); + [delegate closePanel]; + return true; + } +} + +QT_END_NAMESPACE + +#endif // QT_NO_COLORDIALOG diff --git a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.h b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.h new file mode 100644 index 0000000000..8a914bf632 --- /dev/null +++ b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCOCOAFONTDIALOGHELPER_H +#define QCOCOAFONTDIALOGHELPER_H + +#include +#include + +QT_BEGIN_NAMESPACE + +class QFontDialog; +class QFontDialogPrivate; + +class QCocoaFontDialogHelper : public QPlatformFontDialogHelper +{ +public: + QCocoaFontDialogHelper(); + virtual ~QCocoaFontDialogHelper(); + + void platformNativeDialogModalHelp(); + void _q_platformRunNativeAppModalPanel(); + void deleteNativeDialog_sys(); + + bool show_sys(ShowFlags showFlags, Qt::WindowFlags windowFlags, QWindow *parent); + void hide_sys(); + + QPlatformDialogHelper::DialogCode dialogResultCode_sys(); + + void setCurrentFont_sys(const QFont &); + QFont currentFont_sys() const; + +protected: + void createNSFontPanelDelegate(); + bool showCocoaFontPanel(QWindow *parent); + bool hideCocoaFontPanel(); + +private: + void *mDelegate; +}; + +QT_END_NAMESPACE + +#endif // QCOCOAFONTDIALOGHELPER_H diff --git a/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm new file mode 100644 index 0000000000..5d40e5c48c --- /dev/null +++ b/src/plugins/platforms/cocoa/qcocoafontdialoghelper.mm @@ -0,0 +1,488 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** 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.1, 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. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcocoafontdialoghelper.h" + +#ifndef QT_NO_FONTDIALOG + +#include +#include +#include +#include + +#include +#include +#include + +#include "qcocoahelpers.h" + +#import + +#if !CGFLOAT_DEFINED +typedef float CGFloat; // Should only not be defined on 32-bit platforms +#endif + +QT_USE_NAMESPACE + +// should a priori be kept in sync with qcolordialog_mac.mm +const CGFloat ButtonMinWidth = 78.0; +const CGFloat ButtonMinHeight = 32.0; +const CGFloat ButtonSpacing = 0.0; +const CGFloat ButtonTopMargin = 0.0; +const CGFloat ButtonBottomMargin = 7.0; +const CGFloat ButtonSideMargin = 9.0; + +// looks better with some margins +const CGFloat DialogTopMargin = 7.0; +const CGFloat DialogSideMargin = 9.0; + +static NSButton *macCreateButton(const char *text, NSView *superview) +{ + static const NSRect buttonFrameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; + + NSButton *button = [[NSButton alloc] initWithFrame:buttonFrameRect]; + [button setButtonType:NSMomentaryLightButton]; + [button setBezelStyle:NSRoundedBezelStyle]; + [button setTitle:(NSString*)(CFStringRef)QCFString(QDialogButtonBox::tr(text) + .remove(QLatin1Char('&')))]; + [[button cell] setFont:[NSFont systemFontOfSize: + [NSFont systemFontSizeForControlSize:NSRegularControlSize]]]; + [superview addSubview:button]; + return button; +} + +static QFont qfontForCocoaFont(NSFont *cocoaFont, const QFont &resolveFont) +{ + QFont newFont; + if (cocoaFont) { + int pSize = qRound([cocoaFont pointSize]); + QString family(QCFString::toQString([cocoaFont familyName])); + QString typeface(QCFString::toQString([cocoaFont fontName])); + + int hyphenPos = typeface.indexOf(QLatin1Char('-')); + if (hyphenPos != -1) { + typeface.remove(0, hyphenPos + 1); + } else { + typeface = QLatin1String("Normal"); + } + + newFont = QFontDatabase().font(family, typeface, pSize); + newFont.setUnderline(resolveFont.underline()); + newFont.setStrikeOut(resolveFont.strikeOut()); + + } + return newFont; +} + +@class QT_MANGLE_NAMESPACE(QNSFontPanelDelegate); + +@interface QT_MANGLE_NAMESPACE(QNSFontPanelDelegate) : NSObject +{ + @public + NSFontPanel *mFontPanel; + QCocoaFontDialogHelper *mHelper; + NSView *mStolenContentView; + NSButton *mOkButton; + NSButton *mCancelButton; + QFont mQtFont; + NSInteger mResultCode; + BOOL mDialogIsExecuting; + BOOL mResultSet; +}; +- (void)relayout; +- (void)relayoutToContentSize:(NSSize)frameSize; +- (void)updateQtFont; +- (void)changeFont:(id)sender; +- (void)finishOffWithCode:(NSInteger)code; +@end + +@implementation QT_MANGLE_NAMESPACE(QNSFontPanelDelegate) + +- (id)initWithDialogHelper: + (QCocoaFontDialogHelper *)helper +{ + self = [super init]; + mFontPanel = [NSFontPanel sharedFontPanel]; + mHelper = helper; + mResultCode = NSCancelButton; + mDialogIsExecuting = false; + mResultSet = false; + + [mFontPanel setTitle:QCFString::toNSString(helper->options()->windowTitle())]; + + if (mHelper->options()->testOption(QFontDialogOptions::NoButtons)) { + mStolenContentView = 0; + mOkButton = 0; + mCancelButton = 0; + } else { + // steal the font panel's contents view + mStolenContentView = [mFontPanel contentView]; + [mStolenContentView retain]; + [mFontPanel setContentView:0]; + + // create a new content view and add the stolen one as a subview + NSRect frameRect = { { 0.0, 0.0 }, { 0.0, 0.0 } }; + NSView *ourContentView = [[NSView alloc] initWithFrame:frameRect]; + [ourContentView addSubview:mStolenContentView]; + + // create OK and Cancel buttons and add these as subviews + mOkButton = macCreateButton("&OK", ourContentView); + mCancelButton = macCreateButton("Cancel", ourContentView); + + [mFontPanel setContentView:ourContentView]; + [mFontPanel setDefaultButtonCell:[mOkButton cell]]; + [self relayoutToContentSize:[[mStolenContentView superview] frame].size]; + + [mOkButton setAction:@selector(onOkClicked)]; + [mOkButton setTarget:self]; + + [mCancelButton setAction:@selector(onCancelClicked)]; + [mCancelButton setTarget:self]; + } + + [mFontPanel retain]; + return self; +} + +- (void)dealloc +{ + if (mOkButton) { + NSView *ourContentView = [mFontPanel contentView]; + + // return stolen stuff to its rightful owner + [mStolenContentView removeFromSuperview]; + [mFontPanel setContentView:mStolenContentView]; + [mOkButton release]; + [mCancelButton release]; + [ourContentView release]; + } + + [mFontPanel setDelegate:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self]; + + [super dealloc]; +} + +- (void)closePanel +{ + [mFontPanel close]; +} + +- (void)windowDidResize:(NSNotification *)notification +{ + Q_UNUSED(notification); + if (mOkButton) + [self relayout]; +} + +- (void)relayout +{ + [self relayoutToContentSize:[[mStolenContentView superview] frame].size]; +} + +- (void)relayoutToContentSize:(NSSize)frameSize +{ + Q_ASSERT(mOkButton); + + [mOkButton sizeToFit]; + NSSize okSizeHint = [mOkButton frame].size; + + [mCancelButton sizeToFit]; + NSSize cancelSizeHint = [mCancelButton frame].size; + + const CGFloat ButtonWidth = qMin(qMax(ButtonMinWidth, + qMax(okSizeHint.width, cancelSizeHint.width)), + CGFloat((frameSize.width - 2.0 * ButtonSideMargin - ButtonSpacing) * 0.5)); + const CGFloat ButtonHeight = qMax(ButtonMinHeight, + qMax(okSizeHint.height, cancelSizeHint.height)); + + const CGFloat X = DialogSideMargin; + const CGFloat Y = ButtonBottomMargin + ButtonHeight + ButtonTopMargin; + + NSRect okRect = { { frameSize.width - ButtonSideMargin - ButtonWidth, + ButtonBottomMargin }, + { ButtonWidth, ButtonHeight } }; + [mOkButton setFrame:okRect]; + [mOkButton setNeedsDisplay:YES]; + + NSRect cancelRect = { { okRect.origin.x - ButtonSpacing - ButtonWidth, + ButtonBottomMargin }, + { ButtonWidth, ButtonHeight } }; + [mCancelButton setFrame:cancelRect]; + [mCancelButton setNeedsDisplay:YES]; + + NSRect stolenCVRect = { { X, Y }, + { frameSize.width - X - X, frameSize.height - Y - DialogTopMargin } }; + [mStolenContentView setFrame:stolenCVRect]; + [mStolenContentView setNeedsDisplay:YES]; + + [[mStolenContentView superview] setNeedsDisplay:YES]; +} + + +- (void)onOkClicked +{ + Q_ASSERT(mHackedPanel); + [mFontPanel close]; + [self finishOffWithCode:NSOKButton]; +} + +- (void)onCancelClicked +{ + if (mOkButton) { + [mFontPanel close]; + mQtFont = QFont(); + [self finishOffWithCode:NSCancelButton]; + } +} + +- (void)changeFont:(id)sender +{ + Q_UNUSED(sender); + [self updateQtFont]; +} + +- (void)updateQtFont +{ + // Get selected font + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + NSFont *selectedFont = [fontManager selectedFont]; + if (selectedFont == nil) { + selectedFont = [NSFont systemFontOfSize:[NSFont systemFontSize]]; + } + NSFont *panelFont = [fontManager convertFont:selectedFont]; + mQtFont = qfontForCocoaFont(panelFont, mQtFont); + + emit mHelper->currentFontChanged(mQtFont); +} + +- (void)showModelessPanel +{ + mDialogIsExecuting = false; + [mFontPanel makeKeyAndOrderFront:mFontPanel]; +} + +- (BOOL)runApplicationModalPanel +{ + mDialogIsExecuting = true; + [mFontPanel setDelegate:self]; + [NSApp runModalForWindow:mFontPanel]; + return (mResultCode == NSOKButton); +} + +- (QT_PREPEND_NAMESPACE(QPlatformDialogHelper::DialogCode))dialogResultCode +{ + return (mResultCode == NSOKButton) ? QT_PREPEND_NAMESPACE(QPlatformDialogHelper::Accepted) : QT_PREPEND_NAMESPACE(QPlatformDialogHelper::Rejected); +} + +- (BOOL)windowShouldClose:(id)window +{ + Q_UNUSED(window); + if (!mOkButton) + [self updateQtFont]; + if (mDialogIsExecuting) { + [self finishOffWithCode:NSCancelButton]; + } else { + mResultSet = true; + emit mHelper->reject(); + } + return true; +} + +- (void)finishOffWithCode:(NSInteger)code +{ + mResultCode = code; + if (mDialogIsExecuting) { + // We stop the current modal event loop. The control + // will then return inside -(void)exec below. + // It's important that the modal event loop is stopped before + // we accept/reject QFontDialog, since QFontDialog has its + // own event loop that needs to be stopped last. + [NSApp stopModalWithCode:code]; + } else { + // Since we are not in a modal event loop, we can safely close + // down QFontDialog + // Calling accept() or reject() can in turn call closeCocoaFontPanel. + // This check will prevent any such recursion. + if (!mResultSet) { + mResultSet = true; + if (mResultCode == NSCancelButton) { + emit mHelper->reject(); + } else { + emit mHelper->accept(); + } + } + } +} + +@end + +QT_BEGIN_NAMESPACE + +QCocoaFontDialogHelper::QCocoaFontDialogHelper() : + mDelegate(0) +{ +} + +QCocoaFontDialogHelper::~QCocoaFontDialogHelper() +{ + deleteNativeDialog_sys(); +} + +void QCocoaFontDialogHelper::platformNativeDialogModalHelp() +{ + // Do a queued meta-call to open the native modal dialog so it opens after the new + // event loop has started to execute (in QDialog::exec). Using a timer rather than + // a queued meta call is intentional to ensure that the call is only delivered when + // [NSApp run] runs (timers are handeled special in cocoa). If NSApp is not + // running (which is the case if e.g a top-most QEventLoop has been + // interrupted, and the second-most event loop has not yet been reactivated (regardless + // if [NSApp run] is still on the stack)), showing a native modal dialog will fail. + QTimer::singleShot(1, this, SIGNAL(launchNativeAppModalPanel())); +} + +void QCocoaFontDialogHelper::_q_platformRunNativeAppModalPanel() +{ + // TODO: +#if 0 + QBoolBlocker nativeDialogOnTop(QApplicationPrivate::native_modal_dialog_active); +#endif + QT_MANGLE_NAMESPACE(QNSFontPanelDelegate) *delegate = static_cast(mDelegate); + [delegate runApplicationModalPanel]; + if (dialogResultCode_sys() == QPlatformDialogHelper::Accepted) + emit accept(); + else + emit reject(); +} + +void QCocoaFontDialogHelper::deleteNativeDialog_sys() +{ + if (!mDelegate) + return; + [reinterpret_cast(mDelegate) release]; + mDelegate = 0; +} + +bool QCocoaFontDialogHelper::show_sys(QFlags, Qt::WindowFlags, QWindow *parent) +{ + return showCocoaFontPanel(parent); +} + +void QCocoaFontDialogHelper::hide_sys() +{ + if (!mDelegate) + return; + [reinterpret_cast(mDelegate)->mFontPanel close]; +} + +QCocoaFontDialogHelper::DialogCode QCocoaFontDialogHelper::dialogResultCode_sys() +{ + QT_MANGLE_NAMESPACE(QNSFontPanelDelegate) *delegate = static_cast(mDelegate); + return [delegate dialogResultCode]; +} + +void QCocoaFontDialogHelper::setCurrentFont_sys(const QFont &font) +{ + NSFontManager *mgr = [NSFontManager sharedFontManager]; + const NSFont *nsFont = 0; + + int weight = 5; + NSFontTraitMask mask = 0; + if (font.style() == QFont::StyleItalic) { + mask |= NSItalicFontMask; + } + if (font.weight() == QFont::Bold) { + weight = 9; + mask |= NSBoldFontMask; + } + + QFontInfo fontInfo(font); + nsFont = [mgr fontWithFamily:QCFString::toNSString(fontInfo.family()) + traits:mask + weight:weight + size:fontInfo.pointSize()]; + + if (!mDelegate) + createNSFontPanelDelegate(); + + [mgr setSelectedFont:const_cast(nsFont) isMultiple:NO]; + static_cast(mDelegate)->mQtFont = font; +} + +QFont QCocoaFontDialogHelper::currentFont_sys() const +{ + return reinterpret_cast(mDelegate)->mQtFont; +} + +void QCocoaFontDialogHelper::createNSFontPanelDelegate() +{ + if (mDelegate) + return; + + QT_MANGLE_NAMESPACE(QNSFontPanelDelegate) *delegate = [[QT_MANGLE_NAMESPACE(QNSFontPanelDelegate) alloc] + initWithDialogHelper:this]; + + mDelegate = delegate; +} + +bool QCocoaFontDialogHelper::showCocoaFontPanel(QWindow *parent) +{ + Q_UNUSED(parent); + createNSFontPanelDelegate(); + QT_MANGLE_NAMESPACE(QNSFontPanelDelegate) *delegate = static_cast(mDelegate); + [delegate showModelessPanel]; + return true; +} + +bool QCocoaFontDialogHelper::hideCocoaFontPanel() +{ + if (!mDelegate){ + return false; + } else { + QT_MANGLE_NAMESPACE(QNSFontPanelDelegate) *delegate = static_cast(mDelegate); + [delegate closePanel]; + return true; + } +} + +QT_END_NAMESPACE + +#endif // QT_NO_FONTDIALOG diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm index f0e58abebd..a23faf438d 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.mm +++ b/src/plugins/platforms/cocoa/qcocoatheme.mm @@ -42,8 +42,9 @@ #include "qcocoatheme.h" #include "qmenu_mac.h" +#include "qcocoacolordialoghelper.h" #include "qcocoafiledialoghelper.h" -#include +#include "qcocoafontdialoghelper.h" QT_BEGIN_NAMESPACE @@ -72,15 +73,33 @@ bool QCocoaTheme::usePlatformNativeDialog(DialogType dialogType) const { if (dialogType == QPlatformTheme::FileDialog) return true; +#ifndef QT_NO_COLORDIALOG + if (dialogType == QPlatformTheme::ColorDialog) + return true; +#endif +#ifndef QT_NO_FONTDIALOG + if (dialogType == QPlatformTheme::FontDialog) + return true; +#endif return false; } QPlatformDialogHelper * QCocoaTheme::createPlatformDialogHelper(DialogType dialogType) const { - if (dialogType == QPlatformTheme::FileDialog) { + switch (dialogType) { + case QPlatformTheme::FileDialog: return new QCocoaFileDialogHelper(); +#ifndef QT_NO_COLORDIALOG + case QPlatformTheme::ColorDialog: + return new QCocoaColorDialogHelper(); +#endif +#ifndef QT_NO_FONTDIALOG + case QPlatformTheme::FontDialog: + return new QCocoaFontDialogHelper(); +#endif + default: + return 0; } - return 0; } QT_END_NAMESPACE -- cgit v1.2.3