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