blob: b80bee62d8b4d1b108b6f5a57279e1c8e2751d20 [file] [log] [blame]
/****************************************************************************
**
** 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 "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 QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate);
@interface QT_MANGLE_NAMESPACE(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;
- (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
{
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(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
#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;
// 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 (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);
if (mPriv) {
QString selection = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString([mSavePanel filename]));
if (selection != mCurrentSelection) {
*mCurrentSelection = selection;
mPriv->QNSOpenSavePanelDelegate_selectionChanged(selection);
}
}
}
- (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).startsWith(selectedFilter))
[mPopUpButton selectItemAtIndex:i];
}
}
}
- (QStringList) findStrippedFilterWithVisualFilterName:(QString)name
{
for (int i=0; i<mNameFilterDropDownList->size(); ++i) {
if (mNameFilterDropDownList->at(i).startsWith(name))
return 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
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;
QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(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;
QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(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;
QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate);
return [delegate selectedFiles];
#endif
}
void QFileDialogPrivate::setNameFilters_sys(const QStringList &filters)
{
#ifndef QT_MAC_USE_COCOA
Q_UNUSED(filters);
#else
QMacCocoaAutoReleasePool pool;
QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(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
Q_Q(QFileDialog);
QMacCocoaAutoReleasePool pool;
QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate);
*(delegate->mQDirFilter) = model->filter();
delegate->mFileMode = fileMode;
[delegate->mSavePanel setTitle:qt_mac_QStringToNSString(q->windowTitle())];
[delegate->mSavePanel setPrompt:[delegate strip:acceptLabel]];
if (fileNameLabelExplicitlySat)
[delegate->mSavePanel setNameFieldLabel:[delegate strip:qFileDialogUi->fileNameLabel->text()]];
[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;
QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(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;
QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(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<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate) release];
mDelegate = 0;
#endif
nativeDialogInUse = false;
}
bool QFileDialogPrivate::setVisible_sys(bool visible)
{
Q_Q(QFileDialog);
if (!visible == q->isHidden())
return false;
if (q->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;
}
#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;
QString path;
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);
path = QString::fromUtf8(reinterpret_cast<const char *>(str_buffer));
int slsh = path.lastIndexOf(QLatin1Char('/'));
if (slsh != -1)
file = path.right(path.length() - slsh - 1);
else
file = path;
}
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;
}
if (theInfo->isFolder) {
if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:qt_mac_QStringToNSString(path)])
return false;
return true;
}
return false;
}
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);
if (reg.count()) {
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:
if (!fileDialogPrivate->mCurrentSelection.isEmpty()){
fileDialogPrivate->mCurrentSelectionList.clear();
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));
QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = [[QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) alloc]
initWithAcceptMode:acceptMode
title:q->windowTitle()
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();
QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(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;
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;
}
}
#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;
QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(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
QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate);
return [delegate dialogResultCode];
#endif
}
QT_END_NAMESPACE
#endif // QT_NO_FILEDIALOG