Avoid macro re-definition warning for QT_NO_EXCEPTIONS
[profile/ivi/qtbase.git] / src / widgets / dialogs / qfiledialog_mac.mm
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qfiledialog.h"
43
44 #ifndef QT_NO_FILEDIALOG
45
46 /*****************************************************************************
47   QFileDialog debug facilities
48  *****************************************************************************/
49 //#define DEBUG_FILEDIALOG_FILTERS
50
51 #include <qapplication.h>
52 #include <private/qapplication_p.h>
53 #include <private/qfiledialog_p.h>
54 #include <private/qt_mac_p.h>
55 #include <private/qt_cocoa_helpers_mac_p.h>
56 #include <qregexp.h>
57 #include <qbuffer.h>
58 #include <qdebug.h>
59 #include <qstringlist.h>
60 #include <qaction.h>
61 #include <qtextcodec.h>
62 #include <qvarlengtharray.h>
63 #include <qdesktopwidget.h>
64 #include <stdlib.h>
65 #include <qabstracteventdispatcher.h>
66 #import <AppKit/NSSavePanel.h>
67 #include "ui_qfiledialog.h"
68
69 QT_BEGIN_NAMESPACE
70
71 extern QStringList qt_make_filter_list(const QString &filter); // qfiledialog.cpp
72 extern QStringList qt_clean_filter_list(const QString &filter); // qfiledialog.cpp
73 extern const char *qt_file_dialog_filter_reg_exp; // qfiledialog.cpp
74 extern bool qt_mac_is_macsheet(const QWidget *w); // qwidget_mac.mm
75
76 QT_END_NAMESPACE
77
78 QT_FORWARD_DECLARE_CLASS(QFileDialogPrivate)
79 QT_FORWARD_DECLARE_CLASS(QString)
80 QT_FORWARD_DECLARE_CLASS(QStringList)
81 QT_FORWARD_DECLARE_CLASS(QWidget)
82 QT_FORWARD_DECLARE_CLASS(QAction)
83 QT_FORWARD_DECLARE_CLASS(QFileInfo)
84 QT_USE_NAMESPACE
85
86 @class QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate);
87
88 @interface QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate)
89 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
90     : NSObject<NSOpenSavePanelDelegate>
91 #else
92     : NSObject
93 #endif
94 {
95     @public
96     NSOpenPanel *mOpenPanel;
97     NSSavePanel *mSavePanel;
98     NSView *mAccessoryView;
99     NSPopUpButton *mPopUpButton;
100     NSTextField *mTextField;
101     QFileDialogPrivate *mPriv;
102     NSString *mCurrentDir;
103     bool mConfirmOverwrite;
104     int mReturnCode;
105
106     QT_PREPEND_NAMESPACE(QFileDialog::AcceptMode) mAcceptMode;
107     QT_PREPEND_NAMESPACE(QDir::Filters) *mQDirFilter;
108     QT_PREPEND_NAMESPACE(QFileDialog::FileMode) mFileMode;
109     QT_PREPEND_NAMESPACE(QFileDialog::Options) *mFileOptions;
110
111     QString *mLastFilterCheckPath;
112     QString *mCurrentSelection;
113     QStringList *mQDirFilterEntryList;
114     QStringList *mNameFilterDropDownList;
115     QStringList *mSelectedNameFilter;
116 }
117
118 - (NSString *)strip:(const QString &)label;
119 - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename;
120 - (void)filterChanged:(id)sender;
121 - (void)showModelessPanel;
122 - (BOOL)runApplicationModalPanel;
123 - (void)showWindowModalSheet:(QWidget *)docWidget;
124 - (void)updateProperties;
125 - (QStringList)acceptableExtensionsForSave;
126 - (QString)removeExtensions:(const QString &)filter;
127 - (void)createTextField;
128 - (void)createPopUpButton:(const QString &)selectedFilter hideDetails:(BOOL)hideDetails;
129 - (QStringList)findStrippedFilterWithVisualFilterName:(QString)name;
130 - (void)createAccessory;
131
132 @end
133
134 @implementation QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate)
135
136 - (id)initWithAcceptMode:(QT_PREPEND_NAMESPACE(QFileDialog::AcceptMode))acceptMode
137     title:(const QString &)title
138     hideNameFilterDetails:(bool)hideNameFilterDetails
139     qDirFilter:(QT_PREPEND_NAMESPACE(QDir::Filters))qDirFilter
140     fileOptions:(QT_PREPEND_NAMESPACE(QFileDialog::Options))fileOptions
141     fileMode:(QT_PREPEND_NAMESPACE(QFileDialog::FileMode))fileMode
142     selectFile:(const QString &)selectFile
143     confirmOverwrite:(bool)confirm
144     priv:(QFileDialogPrivate *)priv
145 {
146     self = [super init];
147
148     mAcceptMode = acceptMode;
149     if (mAcceptMode == QT_PREPEND_NAMESPACE(QFileDialog::AcceptOpen)){
150         mOpenPanel = [NSOpenPanel openPanel];
151         mSavePanel = mOpenPanel;
152     } else {
153         mSavePanel = [NSSavePanel savePanel];
154         mOpenPanel = 0;
155     }
156
157     [mSavePanel setLevel:NSModalPanelWindowLevel];
158     [mSavePanel setDelegate:self];
159     mQDirFilter = new QT_PREPEND_NAMESPACE(QDir::Filters)(qDirFilter);
160     mFileOptions = new QT_PREPEND_NAMESPACE(QFileDialog::Options)(fileOptions);
161     mFileMode = fileMode;
162     mConfirmOverwrite = confirm;
163     mReturnCode = -1;
164     mPriv = priv;
165     mLastFilterCheckPath = new QString;
166     mQDirFilterEntryList = new QStringList;
167     mNameFilterDropDownList = new QStringList(priv->nameFilters);
168     QString selectedVisualNameFilter = priv->qFileDialogUi->fileTypeCombo->currentText();
169     mSelectedNameFilter = new QStringList([self findStrippedFilterWithVisualFilterName:selectedVisualNameFilter]);
170
171     QFileInfo sel(selectFile);
172     if (sel.isDir()){
173         mCurrentDir = [qt_mac_QStringToNSString(sel.absoluteFilePath()) retain];
174         mCurrentSelection = new QString;
175     } else {
176         mCurrentDir = [qt_mac_QStringToNSString(sel.absolutePath()) retain];
177         mCurrentSelection = new QString(sel.absoluteFilePath());
178     }
179
180     [mSavePanel setTitle:qt_mac_QStringToNSString(title)];
181     [self createPopUpButton:selectedVisualNameFilter hideDetails:hideNameFilterDetails];
182     [self createTextField];
183     [self createAccessory];
184     [mSavePanel setAccessoryView:mNameFilterDropDownList->size() > 1 ? mAccessoryView : nil];
185
186     if (mPriv){
187         [mSavePanel setPrompt:[self strip:mPriv->acceptLabel]];
188         if (mPriv->fileNameLabelExplicitlySat)
189             [mSavePanel setNameFieldLabel:[self strip:mPriv->qFileDialogUi->fileNameLabel->text()]];
190     }
191
192     [self updateProperties];
193     [mSavePanel retain];
194     return self;
195 }
196
197 - (void)dealloc
198 {
199     delete mQDirFilter;
200     delete mFileOptions;
201     delete mLastFilterCheckPath;
202     delete mQDirFilterEntryList;
203     delete mNameFilterDropDownList;
204     delete mSelectedNameFilter;
205     delete mCurrentSelection;
206
207     [mSavePanel orderOut:mSavePanel];
208     [mSavePanel setAccessoryView:nil];
209     [mPopUpButton release];
210     [mTextField release];
211     [mAccessoryView release];
212     [mSavePanel setDelegate:nil];
213     [mSavePanel release];
214     [mCurrentDir release];
215     [super dealloc];
216 }
217
218 - (NSString *)strip:(const QString &)label
219 {
220     QAction a(label, 0);
221     return qt_mac_QStringToNSString(a.iconText());
222 }
223
224 - (void)closePanel
225 {
226     *mCurrentSelection = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)([mSavePanel filename]);
227     [mSavePanel close];
228 }
229
230 - (void)showModelessPanel
231 {
232     if (mOpenPanel){
233         QFileInfo info(*mCurrentSelection);
234         NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName());
235         NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath());
236         bool selectable = (mAcceptMode == QFileDialog::AcceptSave)
237             || [self panel:nil shouldShowFilename:filepath];
238         [mOpenPanel 
239             beginForDirectory:mCurrentDir
240             file:selectable ? filename : nil
241             types:nil
242             modelessDelegate:self
243             didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:)
244             contextInfo:nil];
245     }
246 }
247
248 - (BOOL)runApplicationModalPanel
249 {
250     QFileInfo info(*mCurrentSelection);
251     NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName());
252     NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath());
253     bool selectable = (mAcceptMode == QFileDialog::AcceptSave)
254         || [self panel:nil shouldShowFilename:filepath];
255     mReturnCode = [mSavePanel 
256         runModalForDirectory:mCurrentDir
257         file:selectable ? filename : @"untitled"];
258
259     QAbstractEventDispatcher::instance()->interrupt();
260     return (mReturnCode == NSOKButton);
261 }
262
263 - (QT_PREPEND_NAMESPACE(QDialog::DialogCode))dialogResultCode
264 {
265     return (mReturnCode == NSOKButton) ? QT_PREPEND_NAMESPACE(QDialog::Accepted) : QT_PREPEND_NAMESPACE(QDialog::Rejected);
266 }
267
268 - (void)showWindowModalSheet:(QWidget *)docWidget
269 {
270     Q_UNUSED(docWidget);
271     QFileInfo info(*mCurrentSelection);
272     NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName());
273     NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath());
274     bool selectable = (mAcceptMode == QFileDialog::AcceptSave)
275         || [self panel:nil shouldShowFilename:filepath];
276     [mSavePanel 
277         beginSheetForDirectory:mCurrentDir
278         file:selectable ? filename : nil
279         modalForWindow:QT_PREPEND_NAMESPACE(qt_mac_window_for)(docWidget)
280         modalDelegate:self
281         didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:)
282         contextInfo:nil];
283 }
284
285 - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename
286 {
287     Q_UNUSED(sender);
288
289     if ([filename length] == 0)
290         return NO;
291
292     // Always accept directories regardless of their names (unless it is a bundle):
293     BOOL isDir;
294     if ([[NSFileManager defaultManager] fileExistsAtPath:filename isDirectory:&isDir] && isDir) {
295         if ([mSavePanel treatsFilePackagesAsDirectories] == NO) {
296             if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:filename] == NO)
297                 return YES;
298         }
299     }
300
301     QString qtFileName = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)(filename);
302     QFileInfo info(qtFileName.normalized(QT_PREPEND_NAMESPACE(QString::NormalizationForm_C)));
303     QString path = info.absolutePath();
304     if (path != *mLastFilterCheckPath){
305         *mLastFilterCheckPath = path;
306         *mQDirFilterEntryList = info.dir().entryList(*mQDirFilter);
307     }
308     // Check if the QDir filter accepts the file:
309     if (!mQDirFilterEntryList->contains(info.fileName()))
310         return NO;
311
312     // No filter means accept everything
313     if (mSelectedNameFilter->isEmpty())
314         return YES;
315     // Check if the current file name filter accepts the file:
316     for (int i=0; i<mSelectedNameFilter->size(); ++i) {
317         if (QDir::match(mSelectedNameFilter->at(i), qtFileName))
318             return YES;
319     }
320     return NO;
321 }
322
323 - (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag
324 {
325     Q_UNUSED(sender);
326     if (!okFlag)
327         return filename;
328     if (mConfirmOverwrite)
329         return filename;
330
331     // User has clicked save, and no overwrite confirmation should occur.
332     // To get the latter, we need to change the name we return (hence the prefix):
333     return [@"___qt_very_unlikely_prefix_" stringByAppendingString:filename];
334 }
335
336 - (void)setNameFilters:(const QStringList &)filters hideDetails:(BOOL)hideDetails
337 {
338     [mPopUpButton removeAllItems];
339     *mNameFilterDropDownList = filters;
340     if (filters.size() > 0){
341         for (int i=0; i<filters.size(); ++i) {
342             QString filter = hideDetails ? [self removeExtensions:filters.at(i)] : filters.at(i);
343             [mPopUpButton addItemWithTitle:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(filter)];
344         }
345         [mPopUpButton selectItemAtIndex:0];
346         [mSavePanel setAccessoryView:mAccessoryView];
347     } else
348         [mSavePanel setAccessoryView:nil];
349
350     [self filterChanged:self];
351 }
352
353 - (void)filterChanged:(id)sender
354 {
355     // This mDelegate function is called when the _name_ filter changes.
356     Q_UNUSED(sender);
357     QString selection = mNameFilterDropDownList->value([mPopUpButton indexOfSelectedItem]);
358     *mSelectedNameFilter = [self findStrippedFilterWithVisualFilterName:selection];
359     [mSavePanel validateVisibleColumns];
360     [self updateProperties];
361     if (mPriv)
362         mPriv->QNSOpenSavePanelDelegate_filterSelected([mPopUpButton indexOfSelectedItem]);
363 }
364
365 - (QString)currentNameFilter
366 {
367     return mNameFilterDropDownList->value([mPopUpButton indexOfSelectedItem]);
368 }
369
370 - (QStringList)selectedFiles
371 {
372     if (mOpenPanel)
373         return QT_PREPEND_NAMESPACE(qt_mac_NSArrayToQStringList)([mOpenPanel filenames]);
374     else{
375         QStringList result;
376         QString filename = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)([mSavePanel filename]);
377         result << filename.remove(QLatin1String("___qt_very_unlikely_prefix_"));
378         return result;
379     }
380 }
381
382 - (void)updateProperties
383 {
384     // Call this functions if mFileMode, mFileOptions,
385     // mNameFilterDropDownList or mQDirFilter changes.
386     // The savepanel does not contain the neccessary functions for this.
387     bool chooseFilesOnly = mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFile)
388         || mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFiles);
389     bool chooseDirsOnly = mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::Directory)
390         || mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::DirectoryOnly)
391         || *mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::ShowDirsOnly);
392
393     [mOpenPanel setCanChooseFiles:!chooseDirsOnly];
394     [mOpenPanel setCanChooseDirectories:!chooseFilesOnly];
395     [mSavePanel setCanCreateDirectories:!(*mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::ReadOnly))];
396     [mOpenPanel setAllowsMultipleSelection:(mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFiles))];
397     [mOpenPanel setResolvesAliases:!(*mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::DontResolveSymlinks))];
398
399     QStringList ext = [self acceptableExtensionsForSave];
400     if (mPriv && !ext.isEmpty() && !mPriv->defaultSuffix.isEmpty())
401         ext.prepend(mPriv->defaultSuffix);
402     [mSavePanel setAllowedFileTypes:ext.isEmpty() ? nil : QT_PREPEND_NAMESPACE(qt_mac_QStringListToNSMutableArray(ext))];
403
404     if ([mSavePanel isVisible])
405         [mOpenPanel validateVisibleColumns];
406 }
407
408 - (void)panelSelectionDidChange:(id)sender
409 {
410     Q_UNUSED(sender);
411     if (mPriv) {
412         QString selection = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString([mSavePanel filename]));
413         if (selection != mCurrentSelection) {
414             *mCurrentSelection = selection;
415             mPriv->QNSOpenSavePanelDelegate_selectionChanged(selection);
416         }
417     }
418 }
419
420 - (void)openPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode  contextInfo:(void *)contextInfo
421 {
422     Q_UNUSED(panel);
423     Q_UNUSED(contextInfo);
424     mReturnCode = returnCode;
425     if (mPriv)
426         mPriv->QNSOpenSavePanelDelegate_panelClosed(returnCode == NSOKButton);
427 }
428
429 - (void)panel:(id)sender directoryDidChange:(NSString *)path
430 {
431     Q_UNUSED(sender);
432     if (!mPriv)
433         return;
434     if ([path isEqualToString:mCurrentDir])
435         return;
436
437     [mCurrentDir release];
438     mCurrentDir = [path retain];
439     mPriv->QNSOpenSavePanelDelegate_directoryEntered(QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString(mCurrentDir)));
440 }
441
442 /*
443     Returns a list of extensions (e.g. "png", "jpg", "gif")
444     for the current name filter. If a filter do not conform
445     to the format *.xyz or * or *.*, an empty list
446     is returned meaning accept everything.
447 */
448 - (QStringList)acceptableExtensionsForSave
449 {
450     QStringList result;
451     for (int i=0; i<mSelectedNameFilter->count(); ++i) {
452         const QString &filter = mSelectedNameFilter->at(i);
453         if (filter.startsWith(QLatin1String("*."))
454                 && !filter.contains(QLatin1Char('?'))
455                 && filter.count(QLatin1Char('*')) == 1) {
456             result += filter.mid(2);
457         } else {
458             return QStringList(); // Accept everything
459         }
460     }
461     return result;
462 }
463
464 - (QString)removeExtensions:(const QString &)filter
465 {
466     QRegExp regExp(QT_PREPEND_NAMESPACE(QString::fromLatin1)(QT_PREPEND_NAMESPACE(qt_file_dialog_filter_reg_exp)));
467     if (regExp.indexIn(filter) != -1)
468         return regExp.cap(1).trimmed();
469     return filter;
470 }
471
472 - (void)createTextField
473 {
474     NSRect textRect = { { 0.0, 3.0 }, { 100.0, 25.0 } };
475     mTextField = [[NSTextField alloc] initWithFrame:textRect];
476     [[mTextField cell] setFont:[NSFont systemFontOfSize:
477             [NSFont systemFontSizeForControlSize:NSRegularControlSize]]];
478     [mTextField setAlignment:NSRightTextAlignment];
479     [mTextField setEditable:false];
480     [mTextField setSelectable:false];
481     [mTextField setBordered:false];
482     [mTextField setDrawsBackground:false];
483     if (mPriv){
484         [mTextField setStringValue:[self strip:mPriv->qFileDialogUi->fileTypeLabel->text()]];
485     } else
486         [mTextField setStringValue:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(QT_PREPEND_NAMESPACE(QFileDialog::tr)("Files of type:"))];
487 }
488
489 - (void)createPopUpButton:(const QString &)selectedFilter hideDetails:(BOOL)hideDetails
490 {
491     NSRect popUpRect = { { 100.0, 5.0 }, { 250.0, 25.0 } };
492     mPopUpButton = [[NSPopUpButton alloc] initWithFrame:popUpRect pullsDown:NO];
493     [mPopUpButton setTarget:self];
494     [mPopUpButton setAction:@selector(filterChanged:)];
495
496     QStringList *filters = mNameFilterDropDownList;
497     if (filters->size() > 0){
498         for (int i=0; i<mNameFilterDropDownList->size(); ++i) {
499             QString filter = hideDetails ? [self removeExtensions:filters->at(i)] : filters->at(i);
500             [mPopUpButton addItemWithTitle:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(filter)];
501             if (filters->at(i).startsWith(selectedFilter))
502                 [mPopUpButton selectItemAtIndex:i];
503         }
504     }
505 }
506
507 - (QStringList) findStrippedFilterWithVisualFilterName:(QString)name
508 {
509     for (int i=0; i<mNameFilterDropDownList->size(); ++i) {
510         if (mNameFilterDropDownList->at(i).startsWith(name))
511             return qt_clean_filter_list(mNameFilterDropDownList->at(i));
512     }
513     return QStringList();
514 }
515
516 - (void)createAccessory
517 {
518     NSRect accessoryRect = { { 0.0, 0.0 }, { 450.0, 33.0 } };
519     mAccessoryView = [[NSView alloc] initWithFrame:accessoryRect];
520     [mAccessoryView addSubview:mTextField];
521     [mAccessoryView addSubview:mPopUpButton];
522 }
523
524 @end
525
526 QT_BEGIN_NAMESPACE
527
528 void QFileDialogPrivate::QNSOpenSavePanelDelegate_selectionChanged(const QString &newPath)
529 {
530     emit q_func()->currentChanged(newPath);
531 }
532
533 void QFileDialogPrivate::QNSOpenSavePanelDelegate_panelClosed(bool accepted)
534 {
535     if (accepted)
536         q_func()->accept();
537     else
538         q_func()->reject();
539 }
540
541 void QFileDialogPrivate::QNSOpenSavePanelDelegate_directoryEntered(const QString &newDir)
542 {
543     setLastVisitedDirectory(newDir);
544     emit q_func()->directoryEntered(newDir);
545 }
546
547 void QFileDialogPrivate::QNSOpenSavePanelDelegate_filterSelected(int menuIndex)
548 {
549     emit q_func()->filterSelected(nameFilters.at(menuIndex));
550 }
551
552 extern OSErr qt_mac_create_fsref(const QString &, FSRef *); // qglobal.cpp
553 extern void qt_mac_to_pascal_string(QString s, Str255 str, TextEncoding encoding=0, int len=-1); // qglobal.cpp
554
555 void QFileDialogPrivate::setDirectory_sys(const QString &directory)
556 {
557     QMacCocoaAutoReleasePool pool;
558     QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate);
559     [delegate->mSavePanel setDirectory:qt_mac_QStringToNSString(directory)];
560 }
561
562 QString QFileDialogPrivate::directory_sys() const
563 {
564     QMacCocoaAutoReleasePool pool;
565     QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate);
566     return qt_mac_NSStringToQString([delegate->mSavePanel directory]);
567 }
568
569 void QFileDialogPrivate::selectFile_sys(const QString &filename)
570 {
571     QString filePath = filename;
572     if (QDir::isRelativePath(filePath))
573         filePath = QFileInfo(directory_sys(), filePath).filePath();
574
575     // There seems to no way to select a file once the dialog is running.
576     // So do the next best thing, set the file's directory:
577     setDirectory_sys(QFileInfo(filePath).absolutePath());
578 }
579
580 QStringList QFileDialogPrivate::selectedFiles_sys() const
581 {
582     QMacCocoaAutoReleasePool pool;
583     QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate);
584     return [delegate selectedFiles];
585 }
586
587 void QFileDialogPrivate::setNameFilters_sys(const QStringList &filters)
588 {
589     QMacCocoaAutoReleasePool pool;
590     QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate);
591     bool hideDetails = q_func()->testOption(QFileDialog::HideNameFilterDetails);
592     [delegate setNameFilters:filters hideDetails:hideDetails];
593 }
594
595 void QFileDialogPrivate::setFilter_sys()
596 {
597     Q_Q(QFileDialog);
598     QMacCocoaAutoReleasePool pool;
599     QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate);
600     *(delegate->mQDirFilter) = model->filter();
601     delegate->mFileMode = fileMode;
602     [delegate->mSavePanel setTitle:qt_mac_QStringToNSString(q->windowTitle())];
603     [delegate->mSavePanel setPrompt:[delegate strip:acceptLabel]];
604     if (fileNameLabelExplicitlySat)
605         [delegate->mSavePanel setNameFieldLabel:[delegate strip:qFileDialogUi->fileNameLabel->text()]];
606
607     [delegate updateProperties];
608 }
609
610 void QFileDialogPrivate::selectNameFilter_sys(const QString &filter)
611 {
612     int index = nameFilters.indexOf(filter);
613     if (index != -1) {
614         QMacCocoaAutoReleasePool pool;
615         QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate);
616         [delegate->mPopUpButton selectItemAtIndex:index];
617         [delegate filterChanged:nil];
618     }
619 }
620
621 QString QFileDialogPrivate::selectedNameFilter_sys() const
622 {
623     QMacCocoaAutoReleasePool pool;
624     QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate);
625     int index = [delegate->mPopUpButton indexOfSelectedItem];
626     return index != -1 ? nameFilters.at(index) : QString();
627 }
628
629 void QFileDialogPrivate::deleteNativeDialog_sys()
630 {
631     QMacCocoaAutoReleasePool pool;
632     [reinterpret_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate) release];
633     mDelegate = 0;
634     nativeDialogInUse = false;
635 }
636
637 bool QFileDialogPrivate::setVisible_sys(bool visible)
638 {
639     Q_Q(QFileDialog);
640     if (!visible == q->isHidden())
641         return false;
642
643     if (q->windowFlags() & Qt::WindowStaysOnTopHint) {
644         // The native file dialog tries all it can to stay
645         // on the NSModalPanel level. And it might also show
646         // its own "create directory" dialog that we cannot control.
647         // So we need to use the non-native version in this case...
648         return false;
649     }
650
651     return visible ? showCocoaFilePanel() : hideCocoaFilePanel();
652 }
653
654 void QFileDialogPrivate::createNSOpenSavePanelDelegate()
655 {
656     Q_Q(QFileDialog);
657     if (mDelegate)
658         return;
659
660     bool selectDir = q->selectedFiles().isEmpty();
661     QString selection(selectDir ? q->directory().absolutePath() : q->selectedFiles().value(0));
662     QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = [[QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) alloc]
663         initWithAcceptMode:acceptMode
664         title:q->windowTitle()
665         hideNameFilterDetails:q->testOption(QFileDialog::HideNameFilterDetails)
666         qDirFilter:model->filter()
667         fileOptions:opts
668         fileMode:fileMode
669         selectFile:selection
670         confirmOverwrite:!q->testOption(QFileDialog::DontConfirmOverwrite)
671         priv:this];
672
673     mDelegate = delegate;
674 }
675
676 bool QFileDialogPrivate::showCocoaFilePanel()
677 {
678     Q_Q(QFileDialog);
679     QMacCocoaAutoReleasePool pool;
680     createNSOpenSavePanelDelegate();
681     QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate);
682     if (qt_mac_is_macsheet(q))
683         [delegate showWindowModalSheet:q->parentWidget()];
684     else
685         [delegate showModelessPanel];
686     return true;
687 }
688
689 bool QFileDialogPrivate::hideCocoaFilePanel()
690 {
691     if (!mDelegate){
692         // Nothing to do. We return false to leave the question
693         // open regarding whether or not to go native:
694         return false;
695     } else {
696         QMacCocoaAutoReleasePool pool;
697         QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate);
698         [delegate closePanel];
699         // Even when we hide it, we are still using a
700         // native dialog, so return true:
701         return true;
702     }
703 }
704
705
706 void QFileDialogPrivate::platformNativeDialogModalHelp()
707 {
708     // Do a queued meta-call to open the native modal dialog so it opens after the new
709     // event loop has started to execute (in QDialog::exec). Using a timer rather than
710     // a queued meta call is intentional to ensure that the call is only delivered when
711     // [NSApp run] runs (timers are handeled special in cocoa). If NSApp is not
712     // running (which is the case if e.g a top-most QEventLoop has been
713     // interrupted, and the second-most event loop has not yet been reactivated (regardless
714     // if [NSApp run] is still on the stack)), showing a native modal dialog will fail.
715     if (nativeDialogInUse){
716         Q_Q(QFileDialog);
717         QTimer::singleShot(1, q, SLOT(_q_macRunNativeAppModalPanel()));
718     }
719 }
720
721 void QFileDialogPrivate::_q_macRunNativeAppModalPanel()
722 {
723     QBoolBlocker nativeDialogOnTop(QApplicationPrivate::native_modal_dialog_active);
724     Q_Q(QFileDialog);
725     QMacCocoaAutoReleasePool pool;
726     QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate);
727     [delegate runApplicationModalPanel];
728     dialogResultCode_sys() == QDialog::Accepted ? q->accept() : q->reject();
729 }
730
731 QDialog::DialogCode QFileDialogPrivate::dialogResultCode_sys()
732 {
733     QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate);
734     return [delegate dialogResultCode];
735 }
736
737
738 QT_END_NAMESPACE
739
740 #endif // QT_NO_FILEDIALOG
741