diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /src/gui/dialogs/qfiledialog_mac.mm | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'src/gui/dialogs/qfiledialog_mac.mm')
-rw-r--r-- | src/gui/dialogs/qfiledialog_mac.mm | 1113 |
1 files changed, 1113 insertions, 0 deletions
diff --git a/src/gui/dialogs/qfiledialog_mac.mm b/src/gui/dialogs/qfiledialog_mac.mm new file mode 100644 index 0000000..4c13d01 --- /dev/null +++ b/src/gui/dialogs/qfiledialog_mac.mm @@ -0,0 +1,1113 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the QtGui module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#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 "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"]; + 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); + QString qtFileName = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)(filename); + QFileInfo info(qtFileName); + 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()); + + static const int w = 450, h = 350; + navOptions.location.h = 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); + navOptions.location.h = (parent->x() + (parent->width() / 2)) - (w / 2); + navOptions.location.v = (parent->y() + (parent->height() / 2)) - (h / 2); + + QRect r = QApplication::desktop()->screenGeometry( + QApplication::desktop()->screenNumber(parent)); + const int border = 10; + if (navOptions.location.h + w > r.right()) + navOptions.location.h -= (navOptions.location.h + w) - r.right() + border; + if (navOptions.location.v + h > r.bottom()) + navOptions.location.v -= (navOptions.location.v + h) - r.bottom() + border; + if (navOptions.location.h < r.left()) + navOptions.location.h = r.left() + border; + if (navOptions.location.v < r.top()) + navOptions.location.v = r.top() + border; + } + + 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 + |