diff options
author | Morten Sorvig <morten.sorvig@nokia.com> | 2011-09-28 09:52:15 +0200 |
---|---|---|
committer | Qt by Nokia <qt-info@nokia.com> | 2011-10-27 11:12:38 +0200 |
commit | 8f912c5c0f3f89e6f3042c0a948ccd155e57e216 (patch) | |
tree | 8b0ebe25160243e43ef1dc83bc669714779ffb68 /src | |
parent | a94c960b75453ef2eb9dca0be1db426de7a1091d (diff) |
Native file dialog support for Mac
* New API: QPlatformDialogHelper to support native dialog on QPA.
(Currently, It supports only file dialog.)
* Modify QDialog* and QFileDialog* to support native dialog.
* Add native file dialog support to cocoa platform plugin.
Change-Id: I957f046748a27a33fd9f8af3c525feabd1b0f582
Reviewed-by: Morten Johan Sørvig <morten.sorvig@nokia.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/gui/kernel/qplatformintegration_qpa.cpp | 12 | ||||
-rw-r--r-- | src/gui/kernel/qplatformintegration_qpa.h | 4 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/cocoa.pro | 3 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoafiledialoghelper.h | 88 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm | 758 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaintegration.h | 3 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoaintegration.mm | 26 | ||||
-rw-r--r-- | src/widgets/dialogs/qdialog.cpp | 17 | ||||
-rw-r--r-- | src/widgets/dialogs/qdialog_p.h | 9 | ||||
-rw-r--r-- | src/widgets/dialogs/qfiledialog.cpp | 46 | ||||
-rw-r--r-- | src/widgets/dialogs/qfiledialog.h | 5 | ||||
-rw-r--r-- | src/widgets/dialogs/qfiledialog.ui | 10 | ||||
-rw-r--r-- | src/widgets/dialogs/qfiledialog_mac.mm | 2 | ||||
-rw-r--r-- | src/widgets/dialogs/qfiledialog_p.h | 118 | ||||
-rw-r--r-- | src/widgets/dialogs/qfiledialog_win.cpp | 5 | ||||
-rw-r--r-- | src/widgets/kernel/kernel.pri | 6 | ||||
-rw-r--r-- | src/widgets/kernel/qplatformdialoghelper_qpa.cpp | 51 | ||||
-rw-r--r-- | src/widgets/kernel/qplatformdialoghelper_qpa.h | 82 |
18 files changed, 1171 insertions, 74 deletions
diff --git a/src/gui/kernel/qplatformintegration_qpa.cpp b/src/gui/kernel/qplatformintegration_qpa.cpp index 97c2445529..605cc5de59 100644 --- a/src/gui/kernel/qplatformintegration_qpa.cpp +++ b/src/gui/kernel/qplatformintegration_qpa.cpp @@ -250,6 +250,18 @@ QPlatformMenuBar *QPlatformIntegration::createPlatformMenuBar(QMenuBar *menuBar) return 0; } +bool QPlatformIntegration::usePlatformNativeDialog(QDialog *dialog) const +{ + Q_UNUSED(dialog); + return false; +} + +QPlatformDialogHelper * QPlatformIntegration::createPlatformDialogHelper(QDialog *dialog) const +{ + Q_UNUSED(dialog); + return 0; +} + /*! Should be called by the implementation whenever a new screen is added. diff --git a/src/gui/kernel/qplatformintegration_qpa.h b/src/gui/kernel/qplatformintegration_qpa.h index 0ccd9f96d3..6dd87495b4 100644 --- a/src/gui/kernel/qplatformintegration_qpa.h +++ b/src/gui/kernel/qplatformintegration_qpa.h @@ -68,6 +68,7 @@ class QMenuBar; class QPlatformMenu; class QPlatformMenuBar; class QPlatformAccessibility; +class QPlatformDialogHelper; class Q_GUI_EXPORT QPlatformIntegration { @@ -104,6 +105,9 @@ public: virtual QPlatformMenuBar *createPlatformMenuBar(QMenuBar *menuBar = 0) const; virtual QPlatformAccessibility *accessibility() const; + virtual bool usePlatformNativeDialog(QDialog *dialog = 0) const; + virtual QPlatformDialogHelper *createPlatformDialogHelper(QDialog *dialog = 0) const; + // Access native handles. The window handle is already available from Wid; virtual QPlatformNativeInterface *nativeInterface() const; diff --git a/src/plugins/platforms/cocoa/cocoa.pro b/src/plugins/platforms/cocoa/cocoa.pro index d69f08e86e..ee09f3e3f9 100644 --- a/src/plugins/platforms/cocoa/cocoa.pro +++ b/src/plugins/platforms/cocoa/cocoa.pro @@ -22,6 +22,7 @@ OBJECTIVE_SOURCES += main.mm \ qmultitouch_mac.mm \ qcocoaaccessibilityelement.mm \ qcocoaaccessibility.mm \ + qcocoafiledialoghelper.mm \ HEADERS += qcocoaintegration.h \ qcocoabackingstore.h \ @@ -41,7 +42,9 @@ HEADERS += qcocoaintegration.h \ qmultitouch_mac_p.h \ qcocoaaccessibilityelement.h \ qcocoaaccessibility.h \ + qcocoafiledialoghelper.h \ +FORMS += $$PWD/../../../widgets/dialogs/qfiledialog.ui RESOURCES += qcocoaresources.qrc #add libz for freetype. diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.h b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.h new file mode 100644 index 0000000000..06309ab62b --- /dev/null +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the plugins 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 QCOCOAFILEDIALOGHELPER_H +#define QCOCOAFILEDIALOGHELPER_H + +#include <QObject> +#include <qplatformdialoghelper_qpa.h> +class QFileDialog; +class QFileDialogPrivate; + +class QCocoaFileDialogHelper : public QPlatformDialogHelper +{ +public: + QCocoaFileDialogHelper(QFileDialog *dialog); + virtual ~QCocoaFileDialogHelper(); + + void platformNativeDialogModalHelp(); + void _q_platformRunNativeAppModalPanel(); + + bool defaultNameFilterDisables() const; + + void deleteNativeDialog_sys(); + bool setVisible_sys(bool visible); + QDialog::DialogCode dialogResultCode_sys(); + void setDirectory_sys(const QString &directory); + QString directory_sys() const; + void selectFile_sys(const QString &filename); + QStringList selectedFiles_sys() const; + void setFilter_sys(); + void setNameFilters_sys(const QStringList &filters); + void selectNameFilter_sys(const QString &filter); + QString selectedNameFilter_sys() const; + +public: + bool showCocoaFilePanel(); + bool hideCocoaFilePanel(); + + void createNSOpenSavePanelDelegate(); + void QNSOpenSavePanelDelegate_selectionChanged(const QString &newPath); + void QNSOpenSavePanelDelegate_panelClosed(bool accepted); + void QNSOpenSavePanelDelegate_directoryEntered(const QString &newDir); + void QNSOpenSavePanelDelegate_filterSelected(int menuIndex); + +private: + QFileDialog *qtFileDialog; + void *mDelegate; +}; + +#endif // QCOCOAFILEDIALOGHELPER_H diff --git a/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm new file mode 100644 index 0000000000..bdccf5dfa5 --- /dev/null +++ b/src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm @@ -0,0 +1,758 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** 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 "qcocoafiledialoghelper.h" + +#ifndef QT_NO_FILEDIALOG + +/***************************************************************************** + QFileDialog debug facilities + *****************************************************************************/ +//#define DEBUG_FILEDIALOG_FILTERS + +#include <qapplication.h> +#include <private/qapplication_p.h> +#include <private/qfiledialog_p.h> +#include <private/qt_mac_p.h> +#include <private/qt_cocoa_helpers_mac_p.h> +#include <qregexp.h> +#include <qbuffer.h> +#include <qdebug.h> +#include <qstringlist.h> +#include <qaction.h> +#include <qtextcodec.h> +#include <qvarlengtharray.h> +#include <qdesktopwidget.h> +#include <stdlib.h> +#include <qabstracteventdispatcher.h> +#import <AppKit/NSSavePanel.h> +#include "ui_qfiledialog.h" + +QT_FORWARD_DECLARE_CLASS(QFileDialogPrivate) +QT_FORWARD_DECLARE_CLASS(QString) +QT_FORWARD_DECLARE_CLASS(QStringList) +QT_FORWARD_DECLARE_CLASS(QWidget) +QT_FORWARD_DECLARE_CLASS(QAction) +QT_FORWARD_DECLARE_CLASS(QFileInfo) +QT_USE_NAMESPACE + +@class QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate); + +@interface QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 + : NSObject<NSOpenSavePanelDelegate> +#else + : NSObject +#endif +{ + @public + NSOpenPanel *mOpenPanel; + NSSavePanel *mSavePanel; + NSView *mAccessoryView; + NSPopUpButton *mPopUpButton; + NSTextField *mTextField; + QFileDialogPrivate *mPriv; + QCocoaFileDialogHelper *mHelper; + NSString *mCurrentDir; + bool mConfirmOverwrite; + int mReturnCode; + + QT_PREPEND_NAMESPACE(QFileDialog::AcceptMode) mAcceptMode; + QT_PREPEND_NAMESPACE(QDir::Filters) *mQDirFilter; + QT_PREPEND_NAMESPACE(QFileDialog::FileMode) mFileMode; + QT_PREPEND_NAMESPACE(QFileDialog::Options) *mFileOptions; + + QString *mLastFilterCheckPath; + QString *mCurrentSelection; + QStringList *mQDirFilterEntryList; + QStringList *mNameFilterDropDownList; + QStringList *mSelectedNameFilter; +} + +- (NSString *)strip:(const QString &)label; +- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename; +- (void)filterChanged:(id)sender; +- (void)showModelessPanel; +- (BOOL)runApplicationModalPanel; +- (void)showWindowModalSheet:(QWidget *)docWidget; +- (void)updateProperties; +- (QStringList)acceptableExtensionsForSave; +- (QString)removeExtensions:(const QString &)filter; +- (void)createTextField; +- (void)createPopUpButton:(const QString &)selectedFilter hideDetails:(BOOL)hideDetails; +- (QStringList)findStrippedFilterWithVisualFilterName:(QString)name; +- (void)createAccessory; + +@end + +@implementation QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) + +- (id)initWithAcceptMode:(QT_PREPEND_NAMESPACE(QFileDialog::AcceptMode))acceptMode + title:(const QString &)title + hideNameFilterDetails:(bool)hideNameFilterDetails + qDirFilter:(QT_PREPEND_NAMESPACE(QDir::Filters))qDirFilter + fileOptions:(QT_PREPEND_NAMESPACE(QFileDialog::Options))fileOptions + fileMode:(QT_PREPEND_NAMESPACE(QFileDialog::FileMode))fileMode + selectFile:(const QString &)selectFile + confirmOverwrite:(bool)confirm + priv:(QFileDialogPrivate *)priv + helper:(QCocoaFileDialogHelper *)helper +{ + self = [super init]; + + mAcceptMode = acceptMode; + if (mAcceptMode == QT_PREPEND_NAMESPACE(QFileDialog::AcceptOpen)){ + mOpenPanel = [NSOpenPanel openPanel]; + mSavePanel = mOpenPanel; + } else { + mSavePanel = [NSSavePanel savePanel]; + mOpenPanel = 0; + } + + [mSavePanel setLevel:NSModalPanelWindowLevel]; + [mSavePanel setDelegate:self]; + mQDirFilter = new QT_PREPEND_NAMESPACE(QDir::Filters)(qDirFilter); + mFileOptions = new QT_PREPEND_NAMESPACE(QFileDialog::Options)(fileOptions); + mFileMode = fileMode; + mConfirmOverwrite = confirm; + mReturnCode = -1; + mPriv = priv; + mHelper = helper; + mLastFilterCheckPath = new QString; + mQDirFilterEntryList = new QStringList; + mNameFilterDropDownList = new QStringList(priv->nameFilters); + QString selectedVisualNameFilter = priv->qFileDialogUi->fileTypeCombo->currentText(); + mSelectedNameFilter = new QStringList([self findStrippedFilterWithVisualFilterName:selectedVisualNameFilter]); + + QFileInfo sel(selectFile); + if (sel.isDir()){ + mCurrentDir = [qt_mac_QStringToNSString(sel.absoluteFilePath()) retain]; + mCurrentSelection = new QString; + } else { + mCurrentDir = [qt_mac_QStringToNSString(sel.absolutePath()) retain]; + mCurrentSelection = new QString(sel.absoluteFilePath()); + } + + [mSavePanel setTitle:qt_mac_QStringToNSString(title)]; + [self createPopUpButton:selectedVisualNameFilter hideDetails:hideNameFilterDetails]; + [self createTextField]; + [self createAccessory]; + [mSavePanel setAccessoryView:mNameFilterDropDownList->size() > 1 ? mAccessoryView : nil]; + + if (mPriv){ + [mSavePanel setPrompt:[self strip:mPriv->acceptLabel]]; + if (mPriv->fileNameLabelExplicitlySat) + [mSavePanel setNameFieldLabel:[self strip:mPriv->qFileDialogUi->fileNameLabel->text()]]; + } + + [self updateProperties]; + [mSavePanel retain]; + return self; +} + +- (void)dealloc +{ + delete mQDirFilter; + delete mFileOptions; + delete mLastFilterCheckPath; + delete mQDirFilterEntryList; + delete mNameFilterDropDownList; + delete mSelectedNameFilter; + delete mCurrentSelection; + + [mSavePanel orderOut:mSavePanel]; + [mSavePanel setAccessoryView:nil]; + [mPopUpButton release]; + [mTextField release]; + [mAccessoryView release]; + [mSavePanel setDelegate:nil]; + [mSavePanel release]; + [mCurrentDir release]; + [super dealloc]; +} + +- (NSString *)strip:(const QString &)label +{ + QAction a(label, 0); + return qt_mac_QStringToNSString(a.iconText()); +} + +- (void)closePanel +{ + *mCurrentSelection = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)([mSavePanel filename]); + [mSavePanel close]; +} + +- (void)showModelessPanel +{ + if (mOpenPanel){ + QFileInfo info(*mCurrentSelection); + NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName()); + NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath()); + bool selectable = (mAcceptMode == QFileDialog::AcceptSave) + || [self panel:nil shouldShowFilename:filepath]; + [mOpenPanel + beginForDirectory:mCurrentDir + file:selectable ? filename : nil + types:nil + modelessDelegate:self + didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) + contextInfo:nil]; + } +} + +- (BOOL)runApplicationModalPanel +{ + QFileInfo info(*mCurrentSelection); + NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName()); + NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath()); + bool selectable = (mAcceptMode == QFileDialog::AcceptSave) + || [self panel:nil shouldShowFilename:filepath]; + mReturnCode = [mSavePanel + runModalForDirectory:mCurrentDir + file:selectable ? filename : @"untitled"]; + + QAbstractEventDispatcher::instance()->interrupt(); + return (mReturnCode == NSOKButton); +} + +- (QT_PREPEND_NAMESPACE(QDialog::DialogCode))dialogResultCode +{ + return (mReturnCode == NSOKButton) ? QT_PREPEND_NAMESPACE(QDialog::Accepted) : QT_PREPEND_NAMESPACE(QDialog::Rejected); +} + +- (void)showWindowModalSheet:(QWidget *)docWidget +{ + Q_UNUSED(docWidget); + QFileInfo info(*mCurrentSelection); + NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName()); + NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath()); + bool selectable = (mAcceptMode == QFileDialog::AcceptSave) + || [self panel:nil shouldShowFilename:filepath]; + [mSavePanel + beginSheetForDirectory:mCurrentDir + file:selectable ? filename : nil + modalForWindow:nil + modalDelegate:self + didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) + contextInfo:nil]; +} + +- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename +{ + Q_UNUSED(sender); + + if ([filename length] == 0) + return NO; + + // Always accept directories regardless of their names (unless it is a bundle): + BOOL isDir; + if ([[NSFileManager defaultManager] fileExistsAtPath:filename isDirectory:&isDir] && isDir) { + if ([mSavePanel treatsFilePackagesAsDirectories] == NO) { + if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:filename] == NO) + return YES; + } + } + + QString qtFileName = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)(filename); + QFileInfo info(qtFileName.normalized(QT_PREPEND_NAMESPACE(QString::NormalizationForm_C))); + QString path = info.absolutePath(); + if (path != *mLastFilterCheckPath){ + *mLastFilterCheckPath = path; + *mQDirFilterEntryList = info.dir().entryList(*mQDirFilter); + } + // Check if the QDir filter accepts the file: + if (!mQDirFilterEntryList->contains(info.fileName())) + return NO; + + // No filter means accept everything + if (mSelectedNameFilter->isEmpty()) + return YES; + // Check if the current file name filter accepts the file: + for (int i=0; i<mSelectedNameFilter->size(); ++i) { + if (QDir::match(mSelectedNameFilter->at(i), qtFileName)) + return YES; + } + return NO; +} + +- (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag +{ + Q_UNUSED(sender); + if (!okFlag) + return filename; + if (mConfirmOverwrite) + return filename; + + // User has clicked save, and no overwrite confirmation should occur. + // To get the latter, we need to change the name we return (hence the prefix): + return [@"___qt_very_unlikely_prefix_" stringByAppendingString:filename]; +} + +- (void)setNameFilters:(const QStringList &)filters hideDetails:(BOOL)hideDetails +{ + [mPopUpButton removeAllItems]; + *mNameFilterDropDownList = filters; + if (filters.size() > 0){ + for (int i=0; i<filters.size(); ++i) { + QString filter = hideDetails ? [self removeExtensions:filters.at(i)] : filters.at(i); + [mPopUpButton addItemWithTitle:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(filter)]; + } + [mPopUpButton selectItemAtIndex:0]; + [mSavePanel setAccessoryView:mAccessoryView]; + } else + [mSavePanel setAccessoryView:nil]; + + [self filterChanged:self]; +} + +- (void)filterChanged:(id)sender +{ + // This mDelegate function is called when the _name_ filter changes. + Q_UNUSED(sender); + QString selection = mNameFilterDropDownList->value([mPopUpButton indexOfSelectedItem]); + *mSelectedNameFilter = [self findStrippedFilterWithVisualFilterName:selection]; + [mSavePanel validateVisibleColumns]; + [self updateProperties]; + if (mHelper) + mHelper->QNSOpenSavePanelDelegate_filterSelected([mPopUpButton indexOfSelectedItem]); +} + +- (QString)currentNameFilter +{ + return mNameFilterDropDownList->value([mPopUpButton indexOfSelectedItem]); +} + +- (QStringList)selectedFiles +{ + if (mOpenPanel) + return QT_PREPEND_NAMESPACE(qt_mac_NSArrayToQStringList)([mOpenPanel filenames]); + else{ + QStringList result; + QString filename = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)([mSavePanel filename]); + result << filename.remove(QLatin1String("___qt_very_unlikely_prefix_")); + return result; + } +} + +- (void)updateProperties +{ + // Call this functions if mFileMode, mFileOptions, + // mNameFilterDropDownList or mQDirFilter changes. + // The savepanel does not contain the neccessary functions for this. + bool chooseFilesOnly = mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFile) + || mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFiles); + bool chooseDirsOnly = mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::Directory) + || mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::DirectoryOnly) + || *mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::ShowDirsOnly); + + [mOpenPanel setCanChooseFiles:!chooseDirsOnly]; + [mOpenPanel setCanChooseDirectories:!chooseFilesOnly]; + [mSavePanel setCanCreateDirectories:!(*mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::ReadOnly))]; + [mOpenPanel setAllowsMultipleSelection:(mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFiles))]; + [mOpenPanel setResolvesAliases:!(*mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::DontResolveSymlinks))]; + + QStringList ext = [self acceptableExtensionsForSave]; + if (mPriv && !ext.isEmpty() && !mPriv->defaultSuffix.isEmpty()) + ext.prepend(mPriv->defaultSuffix); + [mSavePanel setAllowedFileTypes:ext.isEmpty() ? nil : QT_PREPEND_NAMESPACE(qt_mac_QStringListToNSMutableArray(ext))]; + + if ([mSavePanel isVisible]) + [mOpenPanel validateVisibleColumns]; +} + +- (void)panelSelectionDidChange:(id)sender +{ + Q_UNUSED(sender); + if (mHelper) { + QString selection = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString([mSavePanel filename])); + if (selection != mCurrentSelection) { + *mCurrentSelection = selection; + mHelper->QNSOpenSavePanelDelegate_selectionChanged(selection); + } + } +} + +- (void)openPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo +{ + Q_UNUSED(panel); + Q_UNUSED(contextInfo); + mReturnCode = returnCode; + if (mHelper) + mHelper->QNSOpenSavePanelDelegate_panelClosed(returnCode == NSOKButton); +} + +- (void)panel:(id)sender directoryDidChange:(NSString *)path +{ + Q_UNUSED(sender); + if (!mHelper) + return; + if ([path isEqualToString:mCurrentDir]) + return; + + [mCurrentDir release]; + mCurrentDir = [path retain]; + mHelper->QNSOpenSavePanelDelegate_directoryEntered(QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString(mCurrentDir))); +} + +/* + Returns a list of extensions (e.g. "png", "jpg", "gif") + for the current name filter. If a filter do not conform + to the format *.xyz or * or *.*, an empty list + is returned meaning accept everything. +*/ +- (QStringList)acceptableExtensionsForSave +{ + QStringList result; + for (int i=0; i<mSelectedNameFilter->count(); ++i) { + const QString &filter = mSelectedNameFilter->at(i); + if (filter.startsWith(QLatin1String("*.")) + && !filter.contains(QLatin1Char('?')) + && filter.count(QLatin1Char('*')) == 1) { + result += filter.mid(2); + } else { + return QStringList(); // Accept everything + } + } + return result; +} + +- (QString)removeExtensions:(const QString &)filter +{ + QRegExp regExp(QT_PREPEND_NAMESPACE(QString::fromLatin1)(QT_PREPEND_NAMESPACE(QFileDialogPrivate::qt_file_dialog_filter_reg_exp))); + if (regExp.indexIn(filter) != -1) + return regExp.cap(1).trimmed(); + return filter; +} + +- (void)createTextField +{ + NSRect textRect = { { 0.0, 3.0 }, { 100.0, 25.0 } }; + mTextField = [[NSTextField alloc] initWithFrame:textRect]; + [[mTextField cell] setFont:[NSFont systemFontOfSize: + [NSFont systemFontSizeForControlSize:NSRegularControlSize]]]; + [mTextField setAlignment:NSRightTextAlignment]; + [mTextField setEditable:false]; + [mTextField setSelectable:false]; + [mTextField setBordered:false]; + [mTextField setDrawsBackground:false]; + if (mPriv){ + [mTextField setStringValue:[self strip:mPriv->qFileDialogUi->fileTypeLabel->text()]]; + } else + [mTextField setStringValue:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(QT_PREPEND_NAMESPACE(QFileDialog::tr)("Files of type:"))]; +} + +- (void)createPopUpButton:(const QString &)selectedFilter hideDetails:(BOOL)hideDetails +{ + NSRect popUpRect = { { 100.0, 5.0 }, { 250.0, 25.0 } }; + mPopUpButton = [[NSPopUpButton alloc] initWithFrame:popUpRect pullsDown:NO]; + [mPopUpButton setTarget:self]; + [mPopUpButton setAction:@selector(filterChanged:)]; + + QStringList *filters = mNameFilterDropDownList; + if (filters->size() > 0){ + for (int i=0; i<mNameFilterDropDownList->size(); ++i) { + QString filter = hideDetails ? [self removeExtensions:filters->at(i)] : filters->at(i); + [mPopUpButton addItemWithTitle:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(filter)]; + if (filters->at(i).startsWith(selectedFilter)) + [mPopUpButton selectItemAtIndex:i]; + } + } +} + +- (QStringList) findStrippedFilterWithVisualFilterName:(QString)name +{ + for (int i=0; i<mNameFilterDropDownList->size(); ++i) { + if (mNameFilterDropDownList->at(i).startsWith(name)) + return QFileDialogPrivate::qt_clean_filter_list(mNameFilterDropDownList->at(i)); + } + return QStringList(); +} + +- (void)createAccessory +{ + NSRect accessoryRect = { { 0.0, 0.0 }, { 450.0, 33.0 } }; + mAccessoryView = [[NSView alloc] initWithFrame:accessoryRect]; + [mAccessoryView addSubview:mTextField]; + [mAccessoryView addSubview:mPopUpButton]; +} + +@end + +QT_BEGIN_NAMESPACE + +static bool qt_mac_is_macsheet(const QWidget *w) +{ + if (!w) + return false; + + Qt::WindowModality modality = w->windowModality(); + if (modality == Qt::ApplicationModal) + return false; + return w->parentWidget() && (modality == Qt::WindowModal || w->windowType() == Qt::Sheet); +} + +QCocoaFileDialogHelper::QCocoaFileDialogHelper(QFileDialog *dialog) : + qtFileDialog(dialog), mDelegate(0) +{ +} + +QCocoaFileDialogHelper::~QCocoaFileDialogHelper() +{ + +} + +void QCocoaFileDialogHelper::QNSOpenSavePanelDelegate_selectionChanged(const QString &newPath) +{ + qtFileDialog->metaObject()->invokeMethod(qtFileDialog, "currentChanged", Q_ARG(QString, newPath)); +} + +void QCocoaFileDialogHelper::QNSOpenSavePanelDelegate_panelClosed(bool accepted) +{ + if (accepted) + qtFileDialog->metaObject()->invokeMethod(qtFileDialog, "accept"); + else + qtFileDialog->metaObject()->invokeMethod(qtFileDialog, "reject"); +} + +void QCocoaFileDialogHelper::QNSOpenSavePanelDelegate_directoryEntered(const QString &newDir) +{ + QFileDialogPrivate *priv = static_cast<QFileDialogPrivate*>(d_ptr); + priv->setLastVisitedDirectory(newDir); + qtFileDialog->metaObject()->invokeMethod(qtFileDialog, "directoryEntered", Q_ARG(QString, newDir)); +} + +void QCocoaFileDialogHelper::QNSOpenSavePanelDelegate_filterSelected(int menuIndex) +{ + QFileDialogPrivate *priv = static_cast<QFileDialogPrivate*>(d_ptr); + qtFileDialog->metaObject()->invokeMethod(qtFileDialog, "filterSelected", Q_ARG(QString, priv->nameFilters.at(menuIndex))); +} + +extern OSErr qt_mac_create_fsref(const QString &, FSRef *); // qglobal.cpp +extern void qt_mac_to_pascal_string(QString s, Str255 str, TextEncoding encoding=0, int len=-1); // qglobal.cpp + +void QCocoaFileDialogHelper::setDirectory_sys(const QString &directory) +{ + QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + [delegate->mSavePanel setDirectory:qt_mac_QStringToNSString(directory)]; +} + +QString QCocoaFileDialogHelper::directory_sys() const +{ + QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + return qt_mac_NSStringToQString([delegate->mSavePanel directory]); +} + +void QCocoaFileDialogHelper::selectFile_sys(const QString &filename) +{ + QString filePath = filename; + if (QDir::isRelativePath(filePath)) + filePath = QFileInfo(directory_sys(), filePath).filePath(); + + // There seems to no way to select a file once the dialog is running. + // So do the next best thing, set the file's directory: + setDirectory_sys(QFileInfo(filePath).absolutePath()); +} + +QStringList QCocoaFileDialogHelper::selectedFiles_sys() const +{ + QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + return [delegate selectedFiles]; +} + +void QCocoaFileDialogHelper::setNameFilters_sys(const QStringList &filters) +{ + QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + bool hideDetails = qtFileDialog->testOption(QFileDialog::HideNameFilterDetails); + [delegate setNameFilters:filters hideDetails:hideDetails]; +} + +void QCocoaFileDialogHelper::setFilter_sys() +{ + QFileDialogPrivate *priv = static_cast<QFileDialogPrivate*>(d_ptr); + QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + *(delegate->mQDirFilter) = priv->model->filter(); + delegate->mFileMode = priv->fileMode; + [delegate->mSavePanel setTitle:qt_mac_QStringToNSString(qtFileDialog->windowTitle())]; + [delegate->mSavePanel setPrompt:[delegate strip:priv->acceptLabel]]; + if (priv->fileNameLabelExplicitlySat) + [delegate->mSavePanel setNameFieldLabel:[delegate strip:priv->qFileDialogUi->fileNameLabel->text()]]; + + [delegate updateProperties]; +} + +void QCocoaFileDialogHelper::selectNameFilter_sys(const QString &filter) +{ + QFileDialogPrivate *priv = static_cast<QFileDialogPrivate*>(d_ptr); + int index = priv->nameFilters.indexOf(filter); + if (index != -1) { + QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + [delegate->mPopUpButton selectItemAtIndex:index]; + [delegate filterChanged:nil]; + } +} + +QString QCocoaFileDialogHelper::selectedNameFilter_sys() const +{ + QFileDialogPrivate *priv = static_cast<QFileDialogPrivate*>(d_ptr); + QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + int index = [delegate->mPopUpButton indexOfSelectedItem]; + return index != -1 ? priv->nameFilters.at(index) : QString(); +} + +void QCocoaFileDialogHelper::deleteNativeDialog_sys() +{ + QFileDialogPrivate *priv = static_cast<QFileDialogPrivate*>(d_ptr); + [reinterpret_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate) release]; + mDelegate = 0; + priv->nativeDialogInUse = false; +} + +bool QCocoaFileDialogHelper::setVisible_sys(bool visible) +{ +// Q_Q(QFileDialog); + if (!visible == qtFileDialog->isHidden()) + return false; + + if (qtFileDialog->windowFlags() & Qt::WindowStaysOnTopHint) { + // The native file dialog tries all it can to stay + // on the NSModalPanel level. And it might also show + // its own "create directory" dialog that we cannot control. + // So we need to use the non-native version in this case... + return false; + } + + return visible ? showCocoaFilePanel() : hideCocoaFilePanel(); +} + +void QCocoaFileDialogHelper::createNSOpenSavePanelDelegate() +{ + QFileDialogPrivate *priv = static_cast<QFileDialogPrivate*>(d_ptr); + if (mDelegate) + return; + + bool selectDir = qtFileDialog->selectedFiles().isEmpty(); + QString selection(selectDir ? qtFileDialog->directory().absolutePath() : qtFileDialog->selectedFiles().value(0)); + QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = [[QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) alloc] + initWithAcceptMode:priv->acceptMode + title:qtFileDialog->windowTitle() + hideNameFilterDetails:qtFileDialog->testOption(QFileDialog::HideNameFilterDetails) + qDirFilter:priv->model->filter() + fileOptions:priv->opts + fileMode:priv->fileMode + selectFile:selection + confirmOverwrite:!qtFileDialog->testOption(QFileDialog::DontConfirmOverwrite) + priv:priv + helper:this]; + + mDelegate = delegate; +} + +bool QCocoaFileDialogHelper::showCocoaFilePanel() +{ +// Q_Q(QFileDialog); + createNSOpenSavePanelDelegate(); + QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + if (qt_mac_is_macsheet(qtFileDialog)) + [delegate showWindowModalSheet:qtFileDialog->parentWidget()]; + else + [delegate showModelessPanel]; + return true; +} + +bool QCocoaFileDialogHelper::hideCocoaFilePanel() +{ + if (!mDelegate){ + // Nothing to do. We return false to leave the question + // open regarding whether or not to go native: + return false; + } else { + QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + [delegate closePanel]; + // Even when we hide it, we are still using a + // native dialog, so return true: + return true; + } +} + +void QCocoaFileDialogHelper::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. + QFileDialogPrivate *priv = static_cast<QFileDialogPrivate*>(d_ptr); + if (priv->nativeDialogInUse){ + QTimer::singleShot(1, qtFileDialog, SLOT(_q_platformRunNativeAppModalPanel())); + } +} + +void QCocoaFileDialogHelper::_q_platformRunNativeAppModalPanel() +{ + // TODO: +#if 0 + QBoolBlocker nativeDialogOnTop(QApplicationPrivate::native_modal_dialog_active); +#endif + QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + [delegate runApplicationModalPanel]; + if (dialogResultCode_sys() == QDialog::Accepted) + qtFileDialog->metaObject()->invokeMethod(qtFileDialog, "accept"); + else + qtFileDialog->metaObject()->invokeMethod(qtFileDialog, "reject"); +} + +QDialog::DialogCode QCocoaFileDialogHelper::dialogResultCode_sys() +{ + QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); + return [delegate dialogResultCode]; +} + +bool QCocoaFileDialogHelper::defaultNameFilterDisables() const +{ + return true; +} + +QT_END_NAMESPACE + +#endif // QT_NO_FILEDIALOG diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.h b/src/plugins/platforms/cocoa/qcocoaintegration.h index 0ebe5d5af1..3a287109c1 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.h +++ b/src/plugins/platforms/cocoa/qcocoaintegration.h @@ -85,6 +85,9 @@ public: QPlatformMenu *createPlatformMenu(QMenu *menu = 0) const; QPlatformMenuBar *createPlatformMenuBar(QMenuBar *menuBar = 0) const; + bool usePlatformNativeDialog(QDialog *dialog = 0) const; + QPlatformDialogHelper *createPlatformDialogHelper(QDialog *dialog = 0) const; + QPlatformNativeInterface *nativeInterface() const; QPlatformAccessibility *accessibility() const; private: diff --git a/src/plugins/platforms/cocoa/qcocoaintegration.mm b/src/plugins/platforms/cocoa/qcocoaintegration.mm index 07006291ba..c388d5e5bb 100644 --- a/src/plugins/platforms/cocoa/qcocoaintegration.mm +++ b/src/plugins/platforms/cocoa/qcocoaintegration.mm @@ -50,10 +50,14 @@ #include "qcocoaapplication.h" #include "qcocoaapplicationdelegate.h" #include "qmenu_mac.h" +#include "qcocoafiledialoghelper.h" #include <QtGui/qplatformaccessibility_qpa.h> #include <QtCore/qcoreapplication.h> +#include <QtWidgets/QDialog> +#include <QtWidgets/QFileDialog> + #include <QtPlatformSupport/private/qbasicfontdatabase_p.h> QT_BEGIN_NAMESPACE @@ -192,4 +196,26 @@ QPlatformAccessibility *QCocoaIntegration::accessibility() const return mAccessibility; } +bool QCocoaIntegration::usePlatformNativeDialog(QDialog *dialog) const +{ + Q_UNUSED(dialog); + return true; +#if 0 + QFileDialog *fileDialog = qobject_cast<QFileDialog*>(dialog); + if (fileDialog) { + return true; + } + return false; +#endif +} + +QPlatformDialogHelper * QCocoaIntegration::createPlatformDialogHelper(QDialog *dialog) const +{ + QFileDialog *fileDialog = qobject_cast<QFileDialog*>(dialog); + if (fileDialog) { + return new QCocoaFileDialogHelper(fileDialog); + } + return 0; +} + QT_END_NAMESPACE diff --git a/src/widgets/dialogs/qdialog.cpp b/src/widgets/dialogs/qdialog.cpp index de71e451a7..38609a97ed 100644 --- a/src/widgets/dialogs/qdialog.cpp +++ b/src/widgets/dialogs/qdialog.cpp @@ -52,6 +52,7 @@ #include "qmenu.h" #include "qcursor.h" #include "private/qdialog_p.h" +#include "private/qguiapplication_p.h" #ifndef QT_NO_ACCESSIBILITY #include "qaccessible.h" #endif @@ -251,6 +252,10 @@ QDialog::QDialog(QWidget *parent, Qt::WindowFlags f) : QWidget(*new QDialogPrivate, parent, f | ((f & Qt::WindowType_Mask) == 0 ? Qt::Dialog : Qt::WindowType(0))) { + Q_D(QDialog); + d->platformHelper = QGuiApplicationPrivate::platformIntegration()->createPlatformDialogHelper(this); + if (d->platformHelper) + d->platformHelper->d_ptr = d_func(); #ifdef Q_WS_WINCE if (!qt_wince_is_smartphone()) setWindowFlags(windowFlags() | Qt::WindowOkButtonHint | QFlag(qt_wince_is_mobile() ? 0 : Qt::WindowCancelButtonHint)); @@ -265,7 +270,6 @@ QDialog::QDialog(QWidget *parent, Qt::WindowFlags f) #endif } - /*! \overload \internal @@ -361,7 +365,7 @@ void QDialogPrivate::resetModalitySetByOpen() // open() changed the window modality and the user didn't touch it afterwards; restore it q->setWindowModality(Qt::WindowModality(resetModalityTo)); q->setAttribute(Qt::WA_SetWindowModality, wasModalitySet); -#ifdef Q_WS_MAC +#ifdef Q_OS_MAC Q_ASSERT(resetModalityTo != Qt::WindowModal); q->setParent(q->parentWidget(), Qt::Dialog); #endif @@ -449,7 +453,7 @@ void QDialog::open() d->wasModalitySet = testAttribute(Qt::WA_SetWindowModality); setWindowModality(Qt::WindowModal); setAttribute(Qt::WA_SetWindowModality, false); -#ifdef Q_WS_MAC +#ifdef Q_OS_MAC setParent(parentWidget(), Qt::Sheet); #endif } @@ -511,9 +515,8 @@ int QDialog::exec() } show(); -#ifdef Q_WS_MAC - d->mac_nativeDialogModalHelp(); -#endif + if (d->platformHelper) + d->platformHelper->platformNativeDialogModalHelp(); QEventLoop eventLoop; d->eventLoop = &eventLoop; @@ -635,7 +638,7 @@ void QDialog::keyPressEvent(QKeyEvent *e) // Calls reject() if Escape is pressed. Simulates a button // click for the default button if Enter is pressed. Move focus // for the arrow keys. Ignore the rest. -#ifdef Q_WS_MAC +#ifdef Q_OS_MAC if(e->modifiers() == Qt::ControlModifier && e->key() == Qt::Key_Period) { reject(); } else diff --git a/src/widgets/dialogs/qdialog_p.h b/src/widgets/dialogs/qdialog_p.h index 3ee88f4703..04e0d7401c 100644 --- a/src/widgets/dialogs/qdialog_p.h +++ b/src/widgets/dialogs/qdialog_p.h @@ -58,6 +58,7 @@ #include "QtCore/qpointer.h" #include "QtWidgets/qdialog.h" #include "QtWidgets/qpushbutton.h" +#include "QtWidgets/qplatformdialoghelper_qpa.h" QT_BEGIN_NAMESPACE @@ -74,7 +75,7 @@ public: resizer(0), sizeGripEnabled(false), #endif - rescode(0), resetModalityTo(-1), wasModalitySet(true), eventLoop(0) + rescode(0), resetModalityTo(-1), wasModalitySet(true), eventLoop(0), platformHelper(0) {} QPointer<QPushButton> mainDef; @@ -97,15 +98,13 @@ public: void _q_doneAction(); #endif -#ifdef Q_WS_MAC - virtual void mac_nativeDialogModalHelp() {} -#endif - int rescode; int resetModalityTo; bool wasModalitySet; QPointer<QEventLoop> eventLoop; + + QPlatformDialogHelper *platformHelper; }; QT_END_NAMESPACE diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp index 22d334a1ec..4c0d50f55b 100644 --- a/src/widgets/dialogs/qfiledialog.cpp +++ b/src/widgets/dialogs/qfiledialog.cpp @@ -45,6 +45,7 @@ #ifndef QT_NO_FILEDIALOG #include "qfiledialog_p.h" +#include <private/qguiapplication_p.h> #include <qfontmetrics.h> #include <qaction.h> #include <qheaderview.h> @@ -70,6 +71,7 @@ extern bool qt_priv_ptr_valid; #include <pwd.h> #endif #endif +#include "qplatformdialoghelper_qpa.h" QT_BEGIN_NAMESPACE @@ -295,9 +297,9 @@ Q_WIDGETS_EXPORT _qt_filedialog_save_filename_hook qt_filedialog_save_filename_h This signal is emitted when the user selects a \a filter. */ -#if defined(Q_WS_WIN) || defined(Q_WS_MAC) -bool Q_WIDGETS_EXPORT qt_use_native_dialogs = true; // for the benefit of testing tools, until we have a proper API -#endif +//#if defined(Q_WS_WIN) || defined(Q_WS_MAC) +//bool Q_WIDGETS_EXPORT qt_use_native_dialogs = true; // for the benefit of testing tools, until we have a proper API +//#endif QT_BEGIN_INCLUDE_NAMESPACE #ifdef Q_WS_WIN @@ -400,7 +402,7 @@ QList<QUrl> QFileDialog::sidebarUrls() const static const qint32 QFileDialogMagic = 0xbe; -const char *qt_file_dialog_filter_reg_exp = +const char *QFileDialogPrivate::qt_file_dialog_filter_reg_exp = "^(.*)\\(([a-zA-Z0-9_.*? +;#\\-\\[\\]@\\{\\}/!<>\\$%&=^~:\\|]*)\\)$"; /*! @@ -515,9 +517,6 @@ QFileDialogPrivate::QFileDialogPrivate() defaultFileTypes(true), fileNameLabelExplicitlySat(false), nativeDialogInUse(false), -#ifdef Q_WS_MAC - mDelegate(0), -#endif qFileDialogUi(0) { } @@ -1066,7 +1065,7 @@ bool QFileDialog::isNameFilterDetailsVisible() const QStringList qt_strip_filters(const QStringList &filters) { QStringList strippedFilters; - QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp)); + QRegExp r(QString::fromLatin1(QFileDialogPrivate::qt_file_dialog_filter_reg_exp)); for (int i = 0; i < filters.count(); ++i) { QString filterName; int index = r.indexIn(filters[i]); @@ -1361,7 +1360,7 @@ void QFileDialog::setAcceptMode(QFileDialog::AcceptMode mode) d->qFileDialogUi->lookInCombo->setEditable(false); } d->retranslateWindowTitle(); -#if defined(Q_WS_MAC) +#if defined(Q_OS_MAC) d->deleteNativeDialog_sys(); setAttribute(Qt::WA_DontShowOnScreen, false); #endif @@ -1761,7 +1760,7 @@ QString QFileDialog::getOpenFileName(QWidget *parent, args.mode = ExistingFile; args.options = options; #if defined(Q_WS_WIN) - if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog)) { + if (QGuiApplicationPrivate::platformIntegration()->usePlatformNativeDialog() && !(args.options & DontUseNativeDialog)) { return qt_win_get_open_file_name(args, &(args.directory), selectedFilter); } #endif @@ -1854,7 +1853,7 @@ QStringList QFileDialog::getOpenFileNames(QWidget *parent, args.options = options; #if defined(Q_WS_WIN) - if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog)) { + if (QGuiApplicationPrivate::platformIntegration()->usePlatformNativeDialog() && !(args.options & DontUseNativeDialog)) { return qt_win_get_open_file_names(args, &(args.directory), selectedFilter); } #endif @@ -1948,7 +1947,7 @@ QString QFileDialog::getSaveFileName(QWidget *parent, args.options = options; #if defined(Q_WS_WIN) - if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog)) { + if (QGuiApplicationPrivate::platformIntegration()->usePlatformNativeDialog() && !(args.options & DontUseNativeDialog)) { return qt_win_get_save_file_name(args, &(args.directory), selectedFilter); } #endif @@ -2028,7 +2027,7 @@ QString QFileDialog::getExistingDirectory(QWidget *parent, args.options = options; #if defined(Q_WS_WIN) - if (qt_use_native_dialogs && !(args.options & DontUseNativeDialog) && (options & ShowDirsOnly) + if (QGuiApplicationPrivate::platformIntegration()->usePlatformNativeDialog() && !(args.options & DontUseNativeDialog) && (options & ShowDirsOnly) #if defined(Q_WS_WINCE) && qt_priv_ptr_valid #endif @@ -2224,6 +2223,10 @@ void QFileDialogPrivate::init(const QString &directory, const QString &nameFilte const QString &caption) { Q_Q(QFileDialog); + platformHelper = QGuiApplicationPrivate::platformIntegration()->createPlatformDialogHelper(q); + if (platformHelper) + platformHelper->d_ptr = this; + if (!caption.isEmpty()) { useDefaultCaption = false; setWindowTitle = caption; @@ -2270,11 +2273,10 @@ void QFileDialogPrivate::createWidgets() Q_Q(QFileDialog); model = new QFileSystemModel(q); model->setObjectName(QLatin1String("qt_filesystem_model")); -#ifdef Q_WS_MAC - model->setNameFilterDisables(true); -#else - model->setNameFilterDisables(false); -#endif + if (platformHelper) + model->setNameFilterDisables(platformHelper->defaultNameFilterDisables()); + else + model->setNameFilterDisables(false); model->d_func()->disableRecursiveSort = true; QFileDialog::connect(model, SIGNAL(fileRenamed(QString,QString,QString)), q, SLOT(_q_fileRenamed(QString,QString,QString))); QFileDialog::connect(model, SIGNAL(rootPathChanged(QString)), @@ -3013,7 +3015,7 @@ void QFileDialogPrivate::_q_goToDirectory(const QString &path) } // Makes a list of filters from a normal filter string "Image Files (*.png *.jpg)" -QStringList qt_clean_filter_list(const QString &filter) +QStringList QFileDialogPrivate::qt_clean_filter_list(const QString &filter) { QRegExp regexp(QString::fromLatin1(qt_file_dialog_filter_reg_exp)); QString f = filter; @@ -3126,6 +3128,12 @@ void QFileDialogPrivate::_q_fileRenamed(const QString &path, const QString oldNa } } +void QFileDialogPrivate::_q_platformRunNativeAppModalPanel() +{ + if (platformHelper) + platformHelper->_q_platformRunNativeAppModalPanel(); +} + /*! \internal diff --git a/src/widgets/dialogs/qfiledialog.h b/src/widgets/dialogs/qfiledialog.h index e27b655ba1..df33fdb265 100644 --- a/src/widgets/dialogs/qfiledialog.h +++ b/src/widgets/dialogs/qfiledialog.h @@ -261,9 +261,8 @@ private: Q_PRIVATE_SLOT(d_func(), void _q_rowsInserted(const QModelIndex & parent)) Q_PRIVATE_SLOT(d_func(), void _q_fileRenamed(const QString &path, const QString oldName, const QString newName)) -#if defined(Q_WS_MAC) - Q_PRIVATE_SLOT(d_func(), void _q_macRunNativeAppModalPanel()) -#endif + Q_PRIVATE_SLOT(d_func(), void _q_platformRunNativeAppModalPanel()) + friend class QPlatformDialogHelper; }; inline void QFileDialog::setDirectory(const QDir &adirectory) diff --git a/src/widgets/dialogs/qfiledialog.ui b/src/widgets/dialogs/qfiledialog.ui index 01a49f975e..9d61de351f 100644 --- a/src/widgets/dialogs/qfiledialog.ui +++ b/src/widgets/dialogs/qfiledialog.ui @@ -313,27 +313,27 @@ <customwidget> <class>QFileDialogTreeView</class> <extends>QTreeView</extends> - <header>qfiledialog_p.h</header> + <header>private/qfiledialog_p.h</header> </customwidget> <customwidget> <class>QFileDialogListView</class> <extends>QListView</extends> - <header>qfiledialog_p.h</header> + <header>private/qfiledialog_p.h</header> </customwidget> <customwidget> <class>QSidebar</class> <extends>QListWidget</extends> - <header>qsidebar_p.h</header> + <header>private/qsidebar_p.h</header> </customwidget> <customwidget> <class>QFileDialogLineEdit</class> <extends>QLineEdit</extends> - <header>qfiledialog_p.h</header> + <header>private/qfiledialog_p.h</header> </customwidget> <customwidget> <class>QFileDialogComboBox</class> <extends>QComboBox</extends> - <header>qfiledialog_p.h</header> + <header>private/qfiledialog_p.h</header> </customwidget> </customwidgets> <tabstops> diff --git a/src/widgets/dialogs/qfiledialog_mac.mm b/src/widgets/dialogs/qfiledialog_mac.mm index 62654f5a0e..1a9f36892d 100644 --- a/src/widgets/dialogs/qfiledialog_mac.mm +++ b/src/widgets/dialogs/qfiledialog_mac.mm @@ -703,7 +703,7 @@ bool QFileDialogPrivate::hideCocoaFilePanel() } -void QFileDialogPrivate::mac_nativeDialogModalHelp() +void QFileDialogPrivate::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 diff --git a/src/widgets/dialogs/qfiledialog_p.h b/src/widgets/dialogs/qfiledialog_p.h index def25f9b14..4de970d4b2 100644 --- a/src/widgets/dialogs/qfiledialog_p.h +++ b/src/widgets/dialogs/qfiledialog_p.h @@ -92,7 +92,7 @@ class QGridLayout; class QCompleter; class QHBoxLayout; class Ui_QFileDialog; - +class QPlatformDialogHelper; struct QFileDialogArgs { @@ -109,7 +109,7 @@ struct QFileDialogArgs #define UrlRole (Qt::UserRole + 1) -class Q_AUTOTEST_EXPORT QFileDialogPrivate : public QDialogPrivate +class Q_WIDGETS_EXPORT QFileDialogPrivate : public QDialogPrivate { Q_DECLARE_PUBLIC(QFileDialog) @@ -223,6 +223,10 @@ public: void _q_autoCompleteFileName(const QString &); void _q_rowsInserted(const QModelIndex & parent); void _q_fileRenamed(const QString &path, const QString oldName, const QString newName); + void _q_platformRunNativeAppModalPanel(); + + static QStringList qt_clean_filter_list(const QString &filter); + static const char *qt_file_dialog_filter_reg_exp; // layout #ifndef QT_NO_PROXYMODEL @@ -276,19 +280,6 @@ public: QString selectedNameFilter_sys() const; ////////////////////////////////////////////// -#if defined(Q_WS_MAC) - void *mDelegate; - bool showCocoaFilePanel(); - bool hideCocoaFilePanel(); - void createNSOpenSavePanelDelegate(); - void QNSOpenSavePanelDelegate_selectionChanged(const QString &newPath); - void QNSOpenSavePanelDelegate_panelClosed(bool accepted); - void QNSOpenSavePanelDelegate_directoryEntered(const QString &newDir); - void QNSOpenSavePanelDelegate_filterSelected(int menuIndex); - void _q_macRunNativeAppModalPanel(); - void mac_nativeDialogModalHelp(); -#endif - QScopedPointer<Ui_QFileDialog> qFileDialogUi; QString acceptLabel; @@ -376,20 +367,89 @@ inline QString QFileDialogPrivate::rootPath() const { return model->rootPath(); } -#ifndef Q_WS_MAC - // Dummies for platforms that don't use native dialogs: - inline void QFileDialogPrivate::deleteNativeDialog_sys() { qt_guiPlatformPlugin()->fileDialogDelete(q_func()); } - inline bool QFileDialogPrivate::setVisible_sys(bool visible) { return qt_guiPlatformPlugin()->fileDialogSetVisible(q_func(), visible); } - inline QDialog::DialogCode QFileDialogPrivate::dialogResultCode_sys(){ return qt_guiPlatformPlugin()->fileDialogResultCode(q_func()); } - inline void QFileDialogPrivate::setDirectory_sys(const QString &directory) { qt_guiPlatformPlugin()->fileDialogSetDirectory(q_func(), directory); } - inline QString QFileDialogPrivate::directory_sys() const { return qt_guiPlatformPlugin()->fileDialogDirectory(q_func()); } - inline void QFileDialogPrivate::selectFile_sys(const QString &filename) { qt_guiPlatformPlugin()->fileDialogSelectFile(q_func(), filename); } - inline QStringList QFileDialogPrivate::selectedFiles_sys() const { return qt_guiPlatformPlugin()->fileDialogSelectedFiles(q_func()); } - inline void QFileDialogPrivate::setFilter_sys() { qt_guiPlatformPlugin()->fileDialogSetFilter(q_func()); } - inline void QFileDialogPrivate::setNameFilters_sys(const QStringList &filters) { qt_guiPlatformPlugin()->fileDialogSetNameFilters(q_func(), filters); } - inline void QFileDialogPrivate::selectNameFilter_sys(const QString &filter) { qt_guiPlatformPlugin()->fileDialogSelectNameFilter(q_func(), filter); } - inline QString QFileDialogPrivate::selectedNameFilter_sys() const { return qt_guiPlatformPlugin()->fileDialogSelectedNameFilter(q_func()); } -#endif +// Dummies for platforms that don't use native dialogs: +inline void QFileDialogPrivate::deleteNativeDialog_sys() +{ + if (platformHelper) + platformHelper->deleteNativeDialog_sys(); + else + qt_guiPlatformPlugin()->fileDialogDelete(q_func()); +} + +inline bool QFileDialogPrivate::setVisible_sys(bool visible) +{ + if (platformHelper) + return platformHelper->setVisible_sys(visible); + return qt_guiPlatformPlugin()->fileDialogSetVisible(q_func(), visible); +} + +inline QDialog::DialogCode QFileDialogPrivate::dialogResultCode_sys() +{ + if (platformHelper) + return platformHelper->dialogResultCode_sys(); + return qt_guiPlatformPlugin()->fileDialogResultCode(q_func()); +} + +inline void QFileDialogPrivate::setDirectory_sys(const QString &directory) +{ + if (platformHelper) + platformHelper->setDirectory_sys(directory); + else + qt_guiPlatformPlugin()->fileDialogSetDirectory(q_func(), directory); +} + +inline QString QFileDialogPrivate::directory_sys() const +{ + if (platformHelper) + return platformHelper->directory_sys(); + return qt_guiPlatformPlugin()->fileDialogDirectory(q_func()); +} + +inline void QFileDialogPrivate::selectFile_sys(const QString &filename) +{ + if (platformHelper) + platformHelper->selectFile_sys(filename); + else + qt_guiPlatformPlugin()->fileDialogSelectFile(q_func(), filename); +} + +inline QStringList QFileDialogPrivate::selectedFiles_sys() const +{ + if (platformHelper) + return platformHelper->selectedFiles_sys(); + return qt_guiPlatformPlugin()->fileDialogSelectedFiles(q_func()); +} + +inline void QFileDialogPrivate::setFilter_sys() +{ + if (platformHelper) + platformHelper->setFilter_sys(); + else + qt_guiPlatformPlugin()->fileDialogSetFilter(q_func()); +} + +inline void QFileDialogPrivate::setNameFilters_sys(const QStringList &filters) +{ + if (platformHelper) + platformHelper->setNameFilters_sys(filters); + else + qt_guiPlatformPlugin()->fileDialogSetNameFilters(q_func(), filters); +} + +inline void QFileDialogPrivate::selectNameFilter_sys(const QString &filter) +{ + if (platformHelper) + platformHelper->selectNameFilter_sys(filter); + else + qt_guiPlatformPlugin()->fileDialogSelectNameFilter(q_func(), filter); +} + +inline QString QFileDialogPrivate::selectedNameFilter_sys() const +{ + if (platformHelper) + return platformHelper->selectedNameFilter_sys(); + return qt_guiPlatformPlugin()->fileDialogSelectedNameFilter(q_func()); +} QT_END_NAMESPACE diff --git a/src/widgets/dialogs/qfiledialog_win.cpp b/src/widgets/dialogs/qfiledialog_win.cpp index 8c3b3cfda7..390e13183c 100644 --- a/src/widgets/dialogs/qfiledialog_win.cpp +++ b/src/widgets/dialogs/qfiledialog_win.cpp @@ -116,7 +116,6 @@ static void qt_win_resolve_libs() } } -extern const char* qt_file_dialog_filter_reg_exp; // defined in qfiledialog.cpp extern QStringList qt_make_filter_list(const QString &filter); const int maxNameLen = 1023; @@ -126,7 +125,7 @@ const int maxMultiLen = 65535; static QString qt_win_extract_filter(const QString &rawFilter) { QString result = rawFilter; - QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp)); + QRegExp r(QString::fromLatin1(QFileDialogPrivate::qt_file_dialog_filter_reg_exp)); int index = r.indexIn(result); if (index >= 0) result = r.cap(2); @@ -156,7 +155,7 @@ static QString qt_win_filter(const QString &filter, bool hideFiltersDetails) QStringList filterLst = qt_win_make_filters_list(filter); QStringList::Iterator it = filterLst.begin(); QString winfilters; - QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp)); + QRegExp r(QString::fromLatin1(QFileDialogPrivate::qt_file_dialog_filter_reg_exp)); for (; it != filterLst.end(); ++it) { QString subfilter = *it; if (!subfilter.isEmpty()) { diff --git a/src/widgets/kernel/kernel.pri b/src/widgets/kernel/kernel.pri index 3cae5e477b..7f10c24bb0 100644 --- a/src/widgets/kernel/kernel.pri +++ b/src/widgets/kernel/kernel.pri @@ -44,7 +44,8 @@ HEADERS += \ kernel/qguiplatformplugin_p.h \ kernel/qdesktopwidget_qpa_p.h \ kernel/qwidgetwindow_qpa_p.h \ - kernel/qplatformmenu_qpa.h + kernel/qplatformmenu_qpa.h \ + kernel/qplatformdialoghelper_qpa.h SOURCES += \ kernel/qaction.cpp \ @@ -80,7 +81,8 @@ SOURCES += \ kernel/qdesktopwidget_qpa.cpp \ kernel/qwidget_qpa.cpp \ kernel/qwidgetwindow_qpa.cpp \ - kernel/qplatformmenu_qpa.cpp + kernel/qplatformmenu_qpa.cpp \ + kernel/qplatformdialoghelper_qpa.cpp # TODO false:!x11:mac { diff --git a/src/widgets/kernel/qplatformdialoghelper_qpa.cpp b/src/widgets/kernel/qplatformdialoghelper_qpa.cpp new file mode 100644 index 0000000000..4ad3118053 --- /dev/null +++ b/src/widgets/kernel/qplatformdialoghelper_qpa.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** + ** + ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + ** All rights reserved. + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** 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 "qplatformdialoghelper_qpa.h" + +QPlatformDialogHelper::QPlatformDialogHelper() : + d_ptr(0) +{ +} + +QPlatformDialogHelper::~QPlatformDialogHelper() +{ +} diff --git a/src/widgets/kernel/qplatformdialoghelper_qpa.h b/src/widgets/kernel/qplatformdialoghelper_qpa.h new file mode 100644 index 0000000000..012a6b9e7e --- /dev/null +++ b/src/widgets/kernel/qplatformdialoghelper_qpa.h @@ -0,0 +1,82 @@ +/**************************************************************************** + ** + ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). + ** All rights reserved. + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** 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 QPLATFORMDIALOGHELPER_H +#define QPLATFORMDIALOGHELPER_H + +#include <qglobal.h> +#include <qdialog.h> + +QT_BEGIN_NAMESPACE + +class QString; +class QObjectPrivate; + +class Q_WIDGETS_EXPORT QPlatformDialogHelper +{ +public: + QPlatformDialogHelper(); + virtual ~QPlatformDialogHelper(); + + virtual void platformNativeDialogModalHelp() = 0; + virtual void _q_platformRunNativeAppModalPanel() = 0; + + virtual bool defaultNameFilterDisables() const = 0; + + virtual void deleteNativeDialog_sys() = 0; + virtual bool setVisible_sys(bool visible) = 0; + virtual QDialog::DialogCode dialogResultCode_sys() = 0; + + virtual void setDirectory_sys(const QString &directory) = 0; + virtual QString directory_sys() const = 0; + virtual void selectFile_sys(const QString &filename) = 0; + virtual QStringList selectedFiles_sys() const = 0; + virtual void setFilter_sys() = 0; + virtual void setNameFilters_sys(const QStringList &filters) = 0; + virtual void selectNameFilter_sys(const QString &filter) = 0; + virtual QString selectedNameFilter_sys() const = 0; + + QObjectPrivate *d_ptr; +}; + +QT_END_NAMESPACE + +#endif // QPLATFORMDIALOGHELPER_H |