src/gui/dialogs/qfiledialog_mac.mm
changeset 0 1918ee327afb
child 3 41300fa6a67c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/gui/dialogs/qfiledialog_mac.mm	Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,1107 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 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$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qfiledialog.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>
+#include "ui_qfiledialog.h"
+
+QT_BEGIN_NAMESPACE
+
+extern QStringList qt_make_filter_list(const QString &filter); // qfiledialog.cpp
+extern QStringList qt_clean_filter_list(const QString &filter); // qfiledialog.cpp
+extern const char *qt_file_dialog_filter_reg_exp; // qfiledialog.cpp
+extern bool qt_mac_is_macsheet(const QWidget *w); // qwidget_mac.mm
+
+QT_END_NAMESPACE
+
+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 QNSOpenSavePanelDelegate;
+
+@interface QNSOpenSavePanelDelegate : NSObject {
+    @public
+    NSOpenPanel *mOpenPanel;
+    NSSavePanel *mSavePanel;
+    NSView *mAccessoryView;
+    NSPopUpButton *mPopUpButton;
+    NSTextField *mTextField;
+    QFileDialogPrivate *mPriv;
+    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;
+- (void)createAccessory;
+
+@end
+
+@implementation QNSOpenSavePanelDelegate
+
+- (id)initWithAcceptMode:(QT_PREPEND_NAMESPACE(QFileDialog::AcceptMode))acceptMode
+    title:(const QString &)title
+    nameFilters:(const QStringList &)nameFilters
+    selectedNameFilter:(const QString &)selectedNameFilter
+    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
+{
+    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;
+    mLastFilterCheckPath = new QString;
+    mQDirFilterEntryList = new QStringList;
+    mNameFilterDropDownList = new QStringList(nameFilters);
+    mSelectedNameFilter = new QStringList(qt_clean_filter_list(selectedNameFilter));
+    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:selectedNameFilter 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
+#ifdef QT_MAC_USE_COCOA
+        modalForWindow:QT_PREPEND_NAMESPACE(qt_mac_window_for)(docWidget)
+#else
+        modalForWindow:nil
+#endif
+        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;
+
+    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;
+
+    // Always accept directories regardless of their names:
+    BOOL isDir;
+    if ([[NSFileManager defaultManager] fileExistsAtPath:filename isDirectory:&isDir] && isDir)
+        return YES;
+
+    // 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 = QT_PREPEND_NAMESPACE(qt_clean_filter_list)(selection);
+    [mSavePanel validateVisibleColumns];
+    [self updateProperties];
+    if (mPriv)
+        mPriv->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);
+    *mCurrentSelection = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString([mSavePanel filename]));
+    if (mPriv)
+        mPriv->QNSOpenSavePanelDelegate_selectionChanged(*mCurrentSelection);
+}
+
+- (void)openPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode  contextInfo:(void *)contextInfo
+{
+    Q_UNUSED(panel);
+    Q_UNUSED(contextInfo);
+    mReturnCode = returnCode;
+    if (mPriv)
+        mPriv->QNSOpenSavePanelDelegate_panelClosed(returnCode == NSOKButton);
+}
+
+- (void)panel:(id)sender directoryDidChange:(NSString *)path
+{
+    Q_UNUSED(sender);
+    if (!mPriv)
+        return;
+    if ([path isEqualToString:mCurrentDir])
+        return;
+
+    [mCurrentDir release];
+    mCurrentDir = [path retain];
+    mPriv->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(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) == selectedFilter)
+                [mPopUpButton selectItemAtIndex:i];
+        }
+    }
+}
+
+- (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
+
+void QFileDialogPrivate::QNSOpenSavePanelDelegate_selectionChanged(const QString &newPath)
+{
+    emit q_func()->currentChanged(newPath);
+}
+
+void QFileDialogPrivate::QNSOpenSavePanelDelegate_panelClosed(bool accepted)
+{
+    if (accepted)
+        q_func()->accept();
+    else
+        q_func()->reject();
+}
+
+void QFileDialogPrivate::QNSOpenSavePanelDelegate_directoryEntered(const QString &newDir)
+{
+    setLastVisitedDirectory(newDir);
+    emit q_func()->directoryEntered(newDir);
+}
+
+void QFileDialogPrivate::QNSOpenSavePanelDelegate_filterSelected(int menuIndex)
+{
+    emit q_func()->filterSelected(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 QFileDialogPrivate::setDirectory_sys(const QString &directory)
+{
+#ifndef QT_MAC_USE_COCOA
+    if (directory == mCurrentLocation)
+        return;
+    mCurrentLocation = directory;
+    emit q_func()->directoryEntered(mCurrentLocation);
+
+    FSRef fsRef;
+    if (qt_mac_create_fsref(directory, &fsRef) == noErr) {
+        AEDesc desc;
+        if (AECreateDesc(typeFSRef, &fsRef, sizeof(FSRef), &desc) == noErr)
+            NavCustomControl(mDialog, kNavCtlSetLocation, (void*)&desc);
+    }
+#else
+    QMacCocoaAutoReleasePool pool;
+    QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+    [delegate->mSavePanel setDirectory:qt_mac_QStringToNSString(directory)];
+#endif
+}
+
+QString QFileDialogPrivate::directory_sys() const
+{
+#ifndef QT_MAC_USE_COCOA
+    return mCurrentLocation;
+#else
+    QMacCocoaAutoReleasePool pool;
+    QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+    return qt_mac_NSStringToQString([delegate->mSavePanel directory]);
+#endif
+}
+
+void QFileDialogPrivate::selectFile_sys(const QString &filename)
+{
+    QString filePath = filename;
+    if (QDir::isRelativePath(filePath))
+        filePath = QFileInfo(directory_sys(), filePath).filePath();
+
+#ifndef QT_MAC_USE_COCOA
+    // Update the selection list immidiatly, so
+    // subsequent calls to selectedFiles() gets correct:
+    mCurrentSelectionList.clear();
+    mCurrentSelectionList << filename;
+    if (mCurrentSelection != filename){
+        mCurrentSelection = filename;
+        emit q_func()->currentChanged(mCurrentSelection);
+    }
+
+    AEDescList descList;
+    if (AECreateList(0, 0, false, &descList) != noErr)
+        return;
+
+    FSRef fsRef;
+    if (qt_mac_create_fsref(filePath, &fsRef) == noErr) {
+        AEDesc desc;
+        if (AECreateDesc(typeFSRef, &fsRef, sizeof(FSRef), &desc) == noErr){
+            if (AEPutDesc(&descList, 0, &desc) == noErr)
+                NavCustomControl(mDialog, kNavCtlSetSelection, (void*)&descList);
+        }
+    }
+
+    // Type the file name into the save dialog's text field:
+    UInt8 *strBuffer = (UInt8 *)malloc(1024);
+    qt_mac_to_pascal_string(QFileInfo(filename).fileName(), strBuffer);
+    NavCustomControl(mDialog, kNavCtlSetEditFileName, strBuffer);
+    free(strBuffer);
+#else
+    // 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());
+#endif
+}
+
+QStringList QFileDialogPrivate::selectedFiles_sys() const
+{
+#ifndef QT_MAC_USE_COCOA
+    if (q_func()->acceptMode() == QFileDialog::AcceptOpen){
+        return mCurrentSelectionList;
+    } else {
+        return QStringList() << mCurrentLocation + QLatin1Char('/')
+                                + QCFString::toQString(NavDialogGetSaveFileName(mDialog));
+    }
+#else
+    QMacCocoaAutoReleasePool pool;
+    QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+    return [delegate selectedFiles];
+#endif
+}
+
+void QFileDialogPrivate::setNameFilters_sys(const QStringList &filters)
+{
+#ifndef QT_MAC_USE_COCOA
+    Q_UNUSED(filters);
+#else
+    QMacCocoaAutoReleasePool pool;
+    QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+    bool hideDetails = q_func()->testOption(QFileDialog::HideNameFilterDetails);
+    [delegate setNameFilters:filters hideDetails:hideDetails];
+#endif
+}
+
+void QFileDialogPrivate::setFilter_sys()
+{
+#ifndef QT_MAC_USE_COCOA
+#else
+    QMacCocoaAutoReleasePool pool;
+    QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+    *(delegate->mQDirFilter) = model->filter();
+    [delegate updateProperties];
+#endif
+}
+
+void QFileDialogPrivate::selectNameFilter_sys(const QString &filter)
+{
+    int index = nameFilters.indexOf(filter);
+    if (index != -1) {
+#ifndef QT_MAC_USE_COCOA
+        NavMenuItemSpec navSpec;
+        bzero(&navSpec, sizeof(NavMenuItemSpec));
+        navSpec.menuType = index;
+        NavCustomControl(mDialog, kNavCtlSelectCustomType, &navSpec);
+#else
+        QMacCocoaAutoReleasePool pool;
+        QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+        [delegate->mPopUpButton selectItemAtIndex:index];
+        [delegate filterChanged:nil];
+#endif
+    }
+}
+
+QString QFileDialogPrivate::selectedNameFilter_sys() const
+{
+#ifndef QT_MAC_USE_COCOA
+    int index = filterInfo.currentSelection;
+#else
+    QMacCocoaAutoReleasePool pool;
+    QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+    int index = [delegate->mPopUpButton indexOfSelectedItem];
+#endif
+    return index != -1 ? nameFilters.at(index) : QString();
+}
+
+void QFileDialogPrivate::deleteNativeDialog_sys()
+{
+#ifndef QT_MAC_USE_COCOA
+    if (mDialog)
+        NavDialogDispose(mDialog);
+    mDialog = 0;
+    mDialogStarted = false;
+#else
+    QMacCocoaAutoReleasePool pool;
+    [reinterpret_cast<QNSOpenSavePanelDelegate *>(mDelegate) release];
+    mDelegate = 0;
+#endif
+    nativeDialogInUse = false;
+}
+
+bool QFileDialogPrivate::setVisible_sys(bool visible)
+{
+    Q_Q(QFileDialog);
+    if (!visible == q->isHidden())
+        return false;
+
+#ifndef QT_MAC_USE_COCOA
+    return visible ? showCarbonNavServicesDialog() : hideCarbonNavServicesDialog();
+#else
+    return visible ? showCocoaFilePanel() : hideCocoaFilePanel();
+#endif
+}
+
+#ifndef QT_MAC_USE_COCOA
+Boolean QFileDialogPrivate::qt_mac_filedialog_filter_proc(AEDesc *theItem, void *info,
+                                                                 void *data, NavFilterModes)
+{
+    QFileDialogPrivate *fileDialogPrivate = static_cast<QFileDialogPrivate *>(data);
+
+    if (!fileDialogPrivate || fileDialogPrivate->filterInfo.filters.isEmpty()
+        || (fileDialogPrivate->filterInfo.currentSelection < 0
+                && fileDialogPrivate->filterInfo.currentSelection
+                        >= fileDialogPrivate->filterInfo.filters.size()))
+        return true;
+
+    NavFileOrFolderInfo *theInfo = static_cast<NavFileOrFolderInfo *>(info);
+    QString file;
+    const QtMacFilterName &fn
+           = fileDialogPrivate->filterInfo.filters.at(fileDialogPrivate->filterInfo.currentSelection);
+    if (theItem->descriptorType == typeFSRef) {
+        FSRef ref;
+        AEGetDescData(theItem, &ref, sizeof(ref));
+        UInt8 str_buffer[1024];
+        FSRefMakePath(&ref, str_buffer, 1024);
+        file = QString::fromUtf8(reinterpret_cast<const char *>(str_buffer));
+        int slsh = file.lastIndexOf(QLatin1Char('/'));
+        if (slsh != -1)
+            file = file.right(file.length() - slsh - 1);
+    }
+    QStringList reg = fn.regexp.split(QLatin1String(";"));
+    for (QStringList::const_iterator it = reg.constBegin(); it != reg.constEnd(); ++it) {
+        QRegExp rg(*it, Qt::CaseInsensitive, QRegExp::Wildcard);
+#ifdef DEBUG_FILEDIALOG_FILTERS
+        qDebug("QFileDialogPrivate::qt_mac_filedialog_filter_proc:%d, asked to filter.. %s (%s)", __LINE__,
+                qPrintable(file), qPrintable(*it));
+#endif
+        if (rg.exactMatch(file))
+            return true;
+    }
+    return (theInfo->isFolder && !file.endsWith(QLatin1String(".app")));
+}
+
+void QFileDialogPrivate::qt_mac_filedialog_event_proc(const NavEventCallbackMessage msg,
+        NavCBRecPtr p, NavCallBackUserData data)
+{
+    QFileDialogPrivate *fileDialogPrivate = static_cast<QFileDialogPrivate *>(data);
+
+    switch(msg) {
+    case kNavCBPopupMenuSelect: {
+        NavMenuItemSpec *s = static_cast<NavMenuItemSpec *>(p->eventData.eventDataParms.param);
+        if (int(s->menuType) != fileDialogPrivate->filterInfo.currentSelection) {
+            fileDialogPrivate->filterInfo.currentSelection = s->menuType;
+            emit fileDialogPrivate->q_func()->filterSelected(fileDialogPrivate->nameFilters.at(s->menuType));
+        }
+        if (fileDialogPrivate->acceptMode == QFileDialog::AcceptSave) {
+            QString base = QCFString::toQString(NavDialogGetSaveFileName(p->context));
+            QFileInfo fi(base);
+            base = fi.completeBaseName();
+            const QtMacFilterName &fn = fileDialogPrivate->filterInfo.filters.at(
+                                                       fileDialogPrivate->filterInfo.currentSelection);
+            QStringList reg = fn.regexp.split(QLatin1String(";"), QString::SkipEmptyParts);
+            QString r = reg.first();
+            r  = r.right(r.length()-1);      // Strip the *
+            base += r;                        //"." + QString::number(s->menuType);
+            NavDialogSetSaveFileName(p->context, QCFString::toCFStringRef(base));
+        }
+#ifdef DEBUG_FILEDIALOG_FILTERS
+        qDebug("QFileDialogPrivate::qt_mac_filedialog_event_proc:%d - Selected a filter: %ld", __LINE__, s->menuType);
+#endif
+        break; }
+    case kNavCBStart:{
+        fileDialogPrivate->mDialogStarted = true;
+        // Set selected file:
+        QModelIndexList indexes = fileDialogPrivate->qFileDialogUi->listView->selectionModel()->selectedRows();
+        QString selected;
+        if (!indexes.isEmpty())
+            selected = indexes.at(0).data(QFileSystemModel::FilePathRole).toString();
+        else
+            selected = fileDialogPrivate->typedFiles().value(0);
+        fileDialogPrivate->selectFile_sys(selected);
+        fileDialogPrivate->selectNameFilter_sys(fileDialogPrivate->qFileDialogUi->fileTypeCombo->currentText());
+        break; }
+    case kNavCBSelectEntry:{
+        // Event: Current selection has changed.
+        QStringList prevSelectionList = fileDialogPrivate->mCurrentSelectionList;
+        fileDialogPrivate->mCurrentSelectionList.clear();
+        QString fileNameToEmit;
+
+        AEDescList *descList = (AEDescList *)p->eventData.eventDataParms.param;
+        // Get the number of files selected:
+        UInt8 strBuffer[1024];
+        long count;
+        OSErr err = AECountItems(descList, &count);
+        if (err != noErr || !count)
+            break;
+
+        for (long index=1; index<=count; ++index) {
+            FSRef ref;
+            err = AEGetNthPtr(descList, index, typeFSRef, 0, 0, &ref, sizeof(ref), 0);
+            if (err != noErr)
+                break;
+            FSRefMakePath(&ref, strBuffer, 1024);
+            QString selected = QString::fromUtf8((const char *)strBuffer);
+            fileDialogPrivate->mCurrentSelectionList << selected;
+            if (!prevSelectionList.contains(selected))
+                fileNameToEmit = selected;
+        }
+
+        if (!fileNameToEmit.isEmpty() && fileNameToEmit != fileDialogPrivate->mCurrentSelection)
+            emit fileDialogPrivate->q_func()->currentChanged(fileNameToEmit);
+        fileDialogPrivate->mCurrentSelection = fileNameToEmit;
+        break; }
+    case kNavCBShowDesktop:
+    case kNavCBNewLocation:{
+        // Event: Current directory has changed.
+        AEDesc *desc = (AEDesc *)p->eventData.eventDataParms.param;
+        FSRef ref;
+        AEGetDescData(desc, &ref, sizeof(ref));
+        UInt8 *strBuffer = (UInt8 *)malloc(1024);
+        FSRefMakePath(&ref, strBuffer, 1024);
+        QString newLocation = QString::fromUtf8((const char *)strBuffer);
+        free(strBuffer);
+        if (fileDialogPrivate->mCurrentLocation != newLocation){
+            fileDialogPrivate->mCurrentLocation = newLocation;
+            QFileDialog::FileMode mode = fileDialogPrivate->fileMode;
+            if (mode == QFileDialog::AnyFile || mode == QFileDialog::ExistingFile
+                    || mode == QFileDialog::ExistingFiles){
+                // When changing directory, the current selection is cleared if
+                // we are supposed to be selecting files only:
+                fileDialogPrivate->mCurrentSelectionList.clear();
+                if (!fileDialogPrivate->mCurrentSelection.isEmpty()){
+                    fileDialogPrivate->mCurrentSelection.clear();
+                    emit fileDialogPrivate->q_func()->currentChanged(fileDialogPrivate->mCurrentSelection);
+                }
+            }
+            fileDialogPrivate->setLastVisitedDirectory(newLocation);
+            emit fileDialogPrivate->q_func()->directoryEntered(newLocation);
+        }
+        break; }
+    case kNavCBAccept:
+        fileDialogPrivate->mDialogClosed = true;
+        fileDialogPrivate->q_func()->accept();
+        break;
+    case kNavCBCancel:
+        fileDialogPrivate->mDialogClosed = true;
+        fileDialogPrivate->q_func()->reject();
+        break;
+    }
+}
+
+static QFileDialogPrivate::QtMacFilterName qt_mac_extract_filter(const QString &rawFilter, bool showDetails)
+{
+    QFileDialogPrivate::QtMacFilterName ret;
+    ret.filter = rawFilter;
+    QString result = rawFilter;
+    QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp));
+    int index = r.indexIn(result);
+    if (index >= 0)
+        result = r.cap(2);
+
+    if (showDetails) {
+        ret.description = rawFilter;
+    } else {
+        if (index >= 0)
+            ret.description = r.cap(1).trimmed();
+        if (ret.description.isEmpty())
+            ret.description = result;
+    }
+    ret.regexp = result.replace(QLatin1Char(' '), QLatin1Char(';'));
+    return ret;
+}
+
+static QList<QFileDialogPrivate::QtMacFilterName> qt_mac_make_filters_list(const QString &filter, bool showDetails)
+{
+#ifdef DEBUG_FILEDIALOG_FILTERS
+    qDebug("QFileDialog:%d - Got filter (%s)", __LINE__, filter.latin1());
+#endif
+
+    QList<QFileDialogPrivate::QtMacFilterName> ret;
+    QString f(filter);
+    if (f.isEmpty())
+        f = QFileDialog::tr("All Files (*)");
+    if (f.isEmpty())
+        return ret;
+    QStringList filts = qt_make_filter_list(f);
+    for (QStringList::const_iterator it = filts.constBegin(); it != filts.constEnd(); ++it) {
+        QFileDialogPrivate::QtMacFilterName filter = qt_mac_extract_filter(*it, showDetails);
+#ifdef DEBUG_FILEDIALOG_FILTERS
+        qDebug("QFileDialog:%d Split out filter (%d) '%s' '%s' [%s]", __LINE__, ret.count(),
+                filter->regxp.latin1(), filter->description.latin1(), (*it).latin1());
+#endif
+        ret.append(filter);
+    }
+    return ret;
+}
+
+void QFileDialogPrivate::createNavServicesDialog()
+{
+    Q_Q(QFileDialog);
+    if (mDialog)
+        deleteNativeDialog_sys();
+
+    NavDialogCreationOptions navOptions;
+    NavGetDefaultDialogCreationOptions(&navOptions);
+
+    // Translate QFileDialog settings into NavDialog options:
+    if (qt_mac_is_macsheet(q)) {
+        navOptions.modality = kWindowModalityWindowModal;
+        navOptions.parentWindow = qt_mac_window_for(q->parentWidget());
+    } else if (q->windowModality() ==  Qt::ApplicationModal)
+        navOptions.modality = kWindowModalityAppModal;
+    else
+        navOptions.modality = kWindowModalityNone;
+    navOptions.optionFlags |= kNavSupportPackages;
+    if (q->testOption(QFileDialog::DontConfirmOverwrite))
+        navOptions.optionFlags |= kNavDontConfirmReplacement;
+    if (fileMode != QFileDialog::ExistingFiles)
+        navOptions.optionFlags &= ~kNavAllowMultipleFiles;
+
+    navOptions.windowTitle = QCFString::toCFStringRef(q->windowTitle());
+
+    navOptions.location.h = -1;
+    navOptions.location.v = -1;
+
+    QWidget *parent = q->parentWidget();
+    if (parent && parent->isVisible()) {
+        WindowClass wclass;
+        GetWindowClass(qt_mac_window_for(parent), &wclass);
+        parent = parent->window();
+        QString s = parent->windowTitle();
+        navOptions.clientName = QCFString::toCFStringRef(s);
+    }
+
+    filterInfo.currentSelection = 0;
+    filterInfo.filters = qt_mac_make_filters_list(nameFilters.join(QLatin1String(";;")), q->isNameFilterDetailsVisible());
+    QCFType<CFArrayRef> filterArray;
+    if (filterInfo.filters.size() > 1) {
+        int i = 0;
+        CFStringRef *cfstringArray = static_cast<CFStringRef *>(malloc(sizeof(CFStringRef)
+                                                                   * filterInfo.filters.size()));
+        for (i = 0; i < filterInfo.filters.size(); ++i) {
+            cfstringArray[i] = QCFString::toCFStringRef(filterInfo.filters.at(i).description);
+        }
+        filterArray = CFArrayCreate(kCFAllocatorDefault,
+                        reinterpret_cast<const void **>(cfstringArray), filterInfo.filters.size(),
+                        &kCFTypeArrayCallBacks);
+        navOptions.popupExtension = filterArray;
+        free(cfstringArray);
+    }
+
+    if (q->acceptMode() == QFileDialog::AcceptSave) {
+        if (NavCreatePutFileDialog(&navOptions, 'cute', kNavGenericSignature,
+                    QFileDialogPrivate::qt_mac_filedialog_event_proc, this, &mDialog)) {
+            qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__);
+            return;
+        }
+    } else if (fileMode == QFileDialog::DirectoryOnly || fileMode == QFileDialog::Directory) {
+        if (NavCreateChooseFolderDialog(&navOptions,
+                    QFileDialogPrivate::qt_mac_filedialog_event_proc, 0, this, &mDialog)) {
+            qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__);
+            return;
+        }
+    } else {
+        if (NavCreateGetFileDialog(&navOptions, 0,
+                    QFileDialogPrivate::qt_mac_filedialog_event_proc, 0,
+                    QFileDialogPrivate::qt_mac_filedialog_filter_proc, this, &mDialog)) {
+            qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__);
+            return;
+        }
+    }
+
+    // Set start-up directory:
+    if (mCurrentLocation.isEmpty())
+        mCurrentLocation = rootPath();
+    FSRef fsRef;
+    if (qt_mac_create_fsref(mCurrentLocation, &fsRef) == noErr) {
+        AEDesc desc;
+        if (AECreateDesc(typeFSRef, &fsRef, sizeof(FSRef), &desc) == noErr)
+            NavCustomControl(mDialog, kNavCtlSetLocation, (void*)&desc);
+    }
+}
+
+bool QFileDialogPrivate::showCarbonNavServicesDialog()
+{
+    Q_Q(QFileDialog);
+    if (q->acceptMode() == QFileDialog::AcceptSave && q->windowModality() == Qt::NonModal)
+        return false; // cannot do native no-modal save dialogs.
+    createNavServicesDialog();
+    mDialogClosed = false;
+    if (q->windowModality() != Qt::ApplicationModal)
+        NavDialogRun(mDialog);
+    return true;
+}
+
+bool QFileDialogPrivate::hideCarbonNavServicesDialog()
+{
+    if (!mDialogClosed){
+        mDialogClosed = true;
+        NavCustomControl(mDialog, kNavCtlCancel, 0);
+    }
+    return true;
+}
+
+#else // Cocoa
+
+void QFileDialogPrivate::createNSOpenSavePanelDelegate()
+{
+    Q_Q(QFileDialog);
+    if (mDelegate)
+        return;
+
+    bool selectDir = q->selectedFiles().isEmpty();
+    QString selection(selectDir ? q->directory().absolutePath() : q->selectedFiles().value(0));
+    QNSOpenSavePanelDelegate *delegate = [[QNSOpenSavePanelDelegate alloc]
+        initWithAcceptMode:acceptMode
+        title:q->windowTitle()
+        nameFilters:q->nameFilters()
+        selectedNameFilter:q->selectedNameFilter()
+        hideNameFilterDetails:q->testOption(QFileDialog::HideNameFilterDetails)
+        qDirFilter:model->filter()
+        fileOptions:opts
+        fileMode:fileMode
+        selectFile:selection
+        confirmOverwrite:!q->testOption(QFileDialog::DontConfirmOverwrite)
+        priv:this];
+
+    mDelegate = delegate;
+}
+
+bool QFileDialogPrivate::showCocoaFilePanel()
+{
+    Q_Q(QFileDialog);
+    QMacCocoaAutoReleasePool pool;
+    createNSOpenSavePanelDelegate();
+    QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+    if (qt_mac_is_macsheet(q))
+        [delegate showWindowModalSheet:q->parentWidget()];
+    else
+        [delegate showModelessPanel];
+    return true;
+}
+
+bool QFileDialogPrivate::hideCocoaFilePanel()
+{
+    if (!mDelegate){
+        // Nothing to do. We return false to leave the question
+        // open regarding whether or not to go native:
+        return false;
+    } else {
+        QMacCocoaAutoReleasePool pool;
+        QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+        [delegate closePanel];
+        // Even when we hide it, we are still using a
+        // native dialog, so return true:
+        return true;
+    }
+}
+
+#endif
+
+void QFileDialogPrivate::mac_nativeDialogModalHelp()
+{
+    // 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.
+    if (nativeDialogInUse){
+        Q_Q(QFileDialog);
+        QTimer::singleShot(1, q, SLOT(_q_macRunNativeAppModalPanel()));
+    }
+}
+
+void QFileDialogPrivate::_q_macRunNativeAppModalPanel()
+{
+    QBoolBlocker nativeDialogOnTop(QApplicationPrivate::native_modal_dialog_active);
+#ifndef QT_MAC_USE_COCOA
+    NavDialogRun(mDialog);
+#else
+    Q_Q(QFileDialog);
+    QMacCocoaAutoReleasePool pool;
+    QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+    [delegate runApplicationModalPanel];
+    dialogResultCode_sys() == QDialog::Accepted ? q->accept() : q->reject();
+#endif
+}
+
+QDialog::DialogCode QFileDialogPrivate::dialogResultCode_sys()
+{
+#ifndef QT_MAC_USE_COCOA
+    NavUserAction result = NavDialogGetUserAction(mDialog);
+    if (result == kNavUserActionCancel || result == kNavUserActionNone)
+        return QDialog::Rejected;
+    else
+        return QDialog::Accepted;
+#else
+    QNSOpenSavePanelDelegate *delegate = static_cast<QNSOpenSavePanelDelegate *>(mDelegate);
+    return [delegate dialogResultCode];
+#endif
+}
+
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_FILEDIALOG
+