Native file dialog support for Mac
authorMorten Sorvig <morten.sorvig@nokia.com>
Wed, 28 Sep 2011 07:52:15 +0000 (09:52 +0200)
committerQt by Nokia <qt-info@nokia.com>
Thu, 27 Oct 2011 09:12:38 +0000 (11:12 +0200)
* 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>
18 files changed:
src/gui/kernel/qplatformintegration_qpa.cpp
src/gui/kernel/qplatformintegration_qpa.h
src/plugins/platforms/cocoa/cocoa.pro
src/plugins/platforms/cocoa/qcocoafiledialoghelper.h [new file with mode: 0644]
src/plugins/platforms/cocoa/qcocoafiledialoghelper.mm [new file with mode: 0644]
src/plugins/platforms/cocoa/qcocoaintegration.h
src/plugins/platforms/cocoa/qcocoaintegration.mm
src/widgets/dialogs/qdialog.cpp
src/widgets/dialogs/qdialog_p.h
src/widgets/dialogs/qfiledialog.cpp
src/widgets/dialogs/qfiledialog.h
src/widgets/dialogs/qfiledialog.ui
src/widgets/dialogs/qfiledialog_mac.mm
src/widgets/dialogs/qfiledialog_p.h
src/widgets/dialogs/qfiledialog_win.cpp
src/widgets/kernel/kernel.pri
src/widgets/kernel/qplatformdialoghelper_qpa.cpp [new file with mode: 0644]
src/widgets/kernel/qplatformdialoghelper_qpa.h [new file with mode: 0644]

index 97c2445..605cc5d 100644 (file)
@@ -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.
 
index 0ccd9f9..6dd8749 100644 (file)
@@ -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;
 
index d69f08e..ee09f3e 100644 (file)
@@ -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 (file)
index 0000000..06309ab
--- /dev/null
@@ -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 (file)
index 0000000..bdccf5d
--- /dev/null
@@ -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
index 0ebe5d5..3a28710 100644 (file)
@@ -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:
index 0700629..c388d5e 100644 (file)
 #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
index de71e45..38609a9 100644 (file)
@@ -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
index 3ee88f4..04e0d74 100644 (file)
@@ -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
index 22d334a..4c0d50f 100644 (file)
@@ -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
 
index e27b655..df33fdb 100644 (file)
@@ -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)
index 01a49f9..9d61de3 100644 (file)
   <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>
index 62654f5..1a9f368 100644 (file)
@@ -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
index def25f9..4de970d 100644 (file)
@@ -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
 
index 8c3b3cf..390e131 100644 (file)
@@ -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()) {
index 3cae5e4..7f10c24 100644 (file)
@@ -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 (file)
index 0000000..4ad3118
--- /dev/null
@@ -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 (file)
index 0000000..012a6b9
--- /dev/null
@@ -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