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