1/**************************************************************************** 2** 3** Copyright (C) 2015 The Qt Company Ltd. 4** Contact: http://www.qt.io/licensing/ 5** 6** This file is part of the QtGui module of the Qt Toolkit. 7** 8** $QT_BEGIN_LICENSE:LGPL$ 9** Commercial License Usage 10** Licensees holding valid commercial Qt licenses may use this file in 11** accordance with the commercial license agreement provided with the 12** Software or, alternatively, in accordance with the terms contained in 13** a written agreement between you and The Qt Company. For licensing terms 14** and conditions see http://www.qt.io/terms-conditions. For further 15** information use the contact form at http://www.qt.io/contact-us. 16** 17** GNU Lesser General Public License Usage 18** Alternatively, this file may be used under the terms of the GNU Lesser 19** General Public License version 2.1 or version 3 as published by the Free 20** Software Foundation and appearing in the file LICENSE.LGPLv21 and 21** LICENSE.LGPLv3 included in the packaging of this file. Please review the 22** following information to ensure the GNU Lesser General Public License 23** requirements will be met: https://www.gnu.org/licenses/lgpl.html and 24** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. 25** 26** As a special exception, The Qt Company gives you certain additional 27** rights. These rights are described in The Qt Company LGPL Exception 28** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. 29** 30** GNU General Public License Usage 31** Alternatively, this file may be used under the terms of the GNU 32** General Public License version 3.0 as published by the Free Software 33** Foundation and appearing in the file LICENSE.GPL included in the 34** packaging of this file. Please review the following information to 35** ensure the GNU General Public License version 3.0 requirements will be 36** met: http://www.gnu.org/copyleft/gpl.html. 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 69QT_BEGIN_NAMESPACE 70 71extern QStringList qt_make_filter_list(const QString &filter); // qfiledialog.cpp 72extern QStringList qt_clean_filter_list(const QString &filter); // qfiledialog.cpp 73extern const char *qt_file_dialog_filter_reg_exp; // qfiledialog.cpp 74extern bool qt_mac_is_macsheet(const QWidget *w); // qwidget_mac.mm 75 76QT_END_NAMESPACE 77 78QT_FORWARD_DECLARE_CLASS(QFileDialogPrivate) 79QT_FORWARD_DECLARE_CLASS(QString) 80QT_FORWARD_DECLARE_CLASS(QStringList) 81QT_FORWARD_DECLARE_CLASS(QWidget) 82QT_FORWARD_DECLARE_CLASS(QAction) 83QT_FORWARD_DECLARE_CLASS(QFileInfo) 84QT_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 if ([mSavePanel respondsToSelector:@selector(setLevel:)]) 158 [mSavePanel setLevel:NSModalPanelWindowLevel]; 159 [mSavePanel setDelegate:self]; 160 mQDirFilter = new QT_PREPEND_NAMESPACE(QDir::Filters)(qDirFilter); 161 mFileOptions = new QT_PREPEND_NAMESPACE(QFileDialog::Options)(fileOptions); 162 mFileMode = fileMode; 163 mConfirmOverwrite = confirm; 164 mReturnCode = -1; 165 mPriv = priv; 166 mLastFilterCheckPath = new QString; 167 mQDirFilterEntryList = new QStringList; 168 mNameFilterDropDownList = new QStringList(priv->nameFilters); 169 QString selectedVisualNameFilter = priv->qFileDialogUi->fileTypeCombo->currentText(); 170 mSelectedNameFilter = new QStringList([self findStrippedFilterWithVisualFilterName:selectedVisualNameFilter]); 171 172 QFileInfo sel(selectFile); 173 if (sel.isDir() && !sel.isBundle()){ 174 mCurrentDir = [qt_mac_QStringToNSString(sel.absoluteFilePath()) retain]; 175 mCurrentSelection = new QString; 176 } else { 177 mCurrentDir = [qt_mac_QStringToNSString(sel.absolutePath()) retain]; 178 mCurrentSelection = new QString(sel.absoluteFilePath()); 179 } 180 181 [mSavePanel setTitle:qt_mac_QStringToNSString(title)]; 182 [self createPopUpButton:selectedVisualNameFilter hideDetails:hideNameFilterDetails]; 183 [self createTextField]; 184 [self createAccessory]; 185 [mSavePanel setAccessoryView:mNameFilterDropDownList->size() > 1 ? mAccessoryView : nil]; 186 187 if (mPriv){ 188 [mSavePanel setPrompt:[self strip:mPriv->acceptLabel]]; 189 if (mPriv->fileNameLabelExplicitlySat) 190 [mSavePanel setNameFieldLabel:[self strip:mPriv->qFileDialogUi->fileNameLabel->text()]]; 191 } 192 193 [self updateProperties]; 194 [mSavePanel retain]; 195 return self; 196} 197 198- (void)dealloc 199{ 200 delete mQDirFilter; 201 delete mFileOptions; 202 delete mLastFilterCheckPath; 203 delete mQDirFilterEntryList; 204 delete mNameFilterDropDownList; 205 delete mSelectedNameFilter; 206 delete mCurrentSelection; 207 208 if ([mSavePanel respondsToSelector:@selector(orderOut:)]) 209 [mSavePanel orderOut:mSavePanel]; 210 [mSavePanel setAccessoryView:nil]; 211 [mPopUpButton release]; 212 [mTextField release]; 213 [mAccessoryView release]; 214 [mSavePanel setDelegate:nil]; 215 [mSavePanel release]; 216 [mCurrentDir release]; 217 [super dealloc]; 218} 219 220- (NSString *)strip:(const QString &)label 221{ 222 QAction a(label, 0); 223 return qt_mac_QStringToNSString(a.iconText()); 224} 225 226- (void)closePanel 227{ 228 *mCurrentSelection = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)([mSavePanel filename]); 229 if ([mSavePanel respondsToSelector:@selector(close)]) 230 [mSavePanel close]; 231 if ([mSavePanel isSheet]) 232 [[NSApplication sharedApplication] endSheet: mSavePanel]; 233} 234 235- (void)showModelessPanel 236{ 237 if (mOpenPanel){ 238 QFileInfo info(*mCurrentSelection); 239 NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName()); 240 NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath()); 241 bool selectable = (mAcceptMode == QFileDialog::AcceptSave) 242 || [self panel:nil shouldShowFilename:filepath]; 243 [mOpenPanel 244 beginForDirectory:mCurrentDir 245 file:selectable ? filename : nil 246 types:nil 247 modelessDelegate:self 248 didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) 249 contextInfo:nil]; 250 } 251} 252 253- (BOOL)runApplicationModalPanel 254{ 255 QFileInfo info(*mCurrentSelection); 256 NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName()); 257 NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath()); 258 bool selectable = (mAcceptMode == QFileDialog::AcceptSave) 259 || [self panel:nil shouldShowFilename:filepath]; 260 mReturnCode = [mSavePanel 261 runModalForDirectory:mCurrentDir 262 file:selectable ? filename : @"untitled"]; 263 264 QAbstractEventDispatcher::instance()->interrupt(); 265 return (mReturnCode == NSOKButton); 266} 267 268- (QT_PREPEND_NAMESPACE(QDialog::DialogCode))dialogResultCode 269{ 270 return (mReturnCode == NSOKButton) ? QT_PREPEND_NAMESPACE(QDialog::Accepted) : QT_PREPEND_NAMESPACE(QDialog::Rejected); 271} 272 273- (void)showWindowModalSheet:(QWidget *)docWidget 274{ 275 Q_UNUSED(docWidget); 276 QFileInfo info(*mCurrentSelection); 277 NSString *filename = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.fileName()); 278 NSString *filepath = QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(info.filePath()); 279 bool selectable = (mAcceptMode == QFileDialog::AcceptSave) 280 || [self panel:nil shouldShowFilename:filepath]; 281 [mSavePanel 282 beginSheetForDirectory:mCurrentDir 283 file:selectable ? filename : nil 284#ifdef QT_MAC_USE_COCOA 285 modalForWindow:QT_PREPEND_NAMESPACE(qt_mac_window_for)(docWidget) 286#else 287 modalForWindow:nil 288#endif 289 modalDelegate:self 290 didEndSelector:@selector(openPanelDidEnd:returnCode:contextInfo:) 291 contextInfo:nil]; 292} 293 294- (BOOL)isHiddenFile:(NSString *)filename isDir:(BOOL)isDir 295{ 296#ifdef QT_MAC_USE_COCOA 297 CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)filename, kCFURLPOSIXPathStyle, isDir); 298 CFBooleanRef isHidden; 299 Boolean errorOrHidden = false; 300 if (!CFURLCopyResourcePropertyForKey(url, kCFURLIsHiddenKey, &isHidden, NULL)) { 301 errorOrHidden = true; 302 } else { 303 if (CFBooleanGetValue(isHidden)) 304 errorOrHidden = true; 305 CFRelease(isHidden); 306 } 307 CFRelease(url); 308 return errorOrHidden; 309#else 310 Q_UNUSED(filename); 311 Q_UNUSED(isDir); 312 return false; 313#endif 314} 315 316- (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename 317{ 318 Q_UNUSED(sender); 319 320 if ([filename length] == 0) 321 return NO; 322 323 // Always accept directories regardless of their names (unless it is a bundle): 324 NSFileManager *fm = [NSFileManager defaultManager]; 325 NSDictionary *fileAttrs = [fm attributesOfItemAtPath:filename error:nil]; 326 if (!fileAttrs) 327 return NO; // Error accessing the file means 'no'. 328 NSString *fileType = [fileAttrs fileType]; 329 bool isDir = [fileType isEqualToString:NSFileTypeDirectory]; 330 if (isDir) { 331 if ([mSavePanel treatsFilePackagesAsDirectories] == NO) { 332 if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:filename] == NO) 333 return YES; 334 } 335 } 336 337 QString qtFileName 338 = QFileInfo(QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)(filename)).fileName(); 339 // No filter means accept everything 340 bool nameMatches = mSelectedNameFilter->isEmpty(); 341 // Check if the current file name filter accepts the file: 342 for (int i = 0; !nameMatches && i < mSelectedNameFilter->size(); ++i) { 343 if (QDir::match(mSelectedNameFilter->at(i), qtFileName)) 344 nameMatches = true; 345 } 346 if (!nameMatches) 347 return NO; 348 349 QDir::Filters filter = *mQDirFilter; 350 if ((!(filter & (QDir::Dirs | QDir::AllDirs)) && isDir) 351 || (!(filter & QDir::Files) && [fileType isEqualToString:NSFileTypeRegular]) 352 || ((filter & QDir::NoSymLinks) && [fileType isEqualToString:NSFileTypeSymbolicLink])) 353 return NO; 354 355 bool filterPermissions = ((filter & QDir::PermissionMask) 356 && (filter & QDir::PermissionMask) != QDir::PermissionMask); 357 if (filterPermissions) { 358 if ((!(filter & QDir::Readable) && [fm isReadableFileAtPath:filename]) 359 || (!(filter & QDir::Writable) && [fm isWritableFileAtPath:filename]) 360 || (!(filter & QDir::Executable) && [fm isExecutableFileAtPath:filename])) 361 return NO; 362 } 363 if (!(filter & QDir::Hidden) 364 && (qtFileName.startsWith(QLatin1Char('.')) || [self isHiddenFile:filename isDir:isDir])) 365 return NO; 366 367 return YES; 368} 369 370- (NSString *)panel:(id)sender userEnteredFilename:(NSString *)filename confirmed:(BOOL)okFlag 371{ 372 Q_UNUSED(sender); 373 if (!okFlag) 374 return filename; 375 if (mConfirmOverwrite) 376 return filename; 377 378 // User has clicked save, and no overwrite confirmation should occur. 379 // To get the latter, we need to change the name we return (hence the prefix): 380 return [@"___qt_very_unlikely_prefix_" stringByAppendingString:filename]; 381} 382 383- (void)setNameFilters:(const QStringList &)filters hideDetails:(BOOL)hideDetails 384{ 385 [mPopUpButton removeAllItems]; 386 *mNameFilterDropDownList = filters; 387 if (filters.size() > 0){ 388 for (int i=0; i<filters.size(); ++i) { 389 QString filter = hideDetails ? [self removeExtensions:filters.at(i)] : filters.at(i); 390 [mPopUpButton addItemWithTitle:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(filter)]; 391 } 392 [mPopUpButton selectItemAtIndex:0]; 393 [mSavePanel setAccessoryView:mAccessoryView]; 394 } else 395 [mSavePanel setAccessoryView:nil]; 396 397 [self filterChanged:self]; 398} 399 400- (void)filterChanged:(id)sender 401{ 402 // This mDelegate function is called when the _name_ filter changes. 403 Q_UNUSED(sender); 404 QString selection = mNameFilterDropDownList->value([mPopUpButton indexOfSelectedItem]); 405 *mSelectedNameFilter = [self findStrippedFilterWithVisualFilterName:selection]; 406 if ([mSavePanel respondsToSelector:@selector(validateVisibleColumns)]) 407 [mSavePanel validateVisibleColumns]; 408 [self updateProperties]; 409 if (mPriv) 410 mPriv->QNSOpenSavePanelDelegate_filterSelected([mPopUpButton indexOfSelectedItem]); 411} 412 413- (QString)currentNameFilter 414{ 415 return mNameFilterDropDownList->value([mPopUpButton indexOfSelectedItem]); 416} 417 418- (QStringList)selectedFiles 419{ 420 if (mOpenPanel) 421 return QT_PREPEND_NAMESPACE(qt_mac_NSArrayToQStringList)([mOpenPanel filenames]); 422 else{ 423 QStringList result; 424 QString filename = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString)([mSavePanel filename]); 425 result << filename.remove(QLatin1String("___qt_very_unlikely_prefix_")); 426 return result; 427 } 428} 429 430- (void)updateProperties 431{ 432 // Call this functions if mFileMode, mFileOptions, 433 // mNameFilterDropDownList or mQDirFilter changes. 434 // The savepanel does not contain the neccessary functions for this. 435 bool chooseFilesOnly = mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFile) 436 || mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFiles); 437 bool chooseDirsOnly = mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::Directory) 438 || mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::DirectoryOnly) 439 || *mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::ShowDirsOnly); 440 441 [mOpenPanel setCanChooseFiles:!chooseDirsOnly]; 442 [mOpenPanel setCanChooseDirectories:!chooseFilesOnly]; 443 [mSavePanel setCanCreateDirectories:!(*mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::ReadOnly))]; 444 [mOpenPanel setAllowsMultipleSelection:(mFileMode == QT_PREPEND_NAMESPACE(QFileDialog::ExistingFiles))]; 445 [mOpenPanel setResolvesAliases:!(*mFileOptions & QT_PREPEND_NAMESPACE(QFileDialog::DontResolveSymlinks))]; 446 447 QStringList ext = [self acceptableExtensionsForSave]; 448 if (mPriv && !ext.isEmpty() && !mPriv->defaultSuffix.isEmpty()) 449 ext.prepend(mPriv->defaultSuffix); 450 [mSavePanel setAllowedFileTypes:ext.isEmpty() ? nil : QT_PREPEND_NAMESPACE(qt_mac_QStringListToNSMutableArray(ext))]; 451 452 if ([mSavePanel respondsToSelector:@selector(isVisible)] && [mSavePanel isVisible]) 453 { 454 if ([mOpenPanel respondsToSelector:@selector(validateVisibleColumns)]) 455 [mOpenPanel validateVisibleColumns]; 456 } 457} 458 459- (void)panelSelectionDidChange:(id)sender 460{ 461 Q_UNUSED(sender); 462 if (mPriv && [mSavePanel isVisible]) { 463 QString selection = QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString([mSavePanel filename])); 464 if (selection != mCurrentSelection) { 465 *mCurrentSelection = selection; 466 mPriv->QNSOpenSavePanelDelegate_selectionChanged(selection); 467 } 468 } 469} 470 471- (void)openPanelDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo 472{ 473 Q_UNUSED(panel); 474 Q_UNUSED(contextInfo); 475 mReturnCode = returnCode; 476 if (mPriv) 477 mPriv->QNSOpenSavePanelDelegate_panelClosed(returnCode == NSOKButton); 478} 479 480- (void)panel:(id)sender directoryDidChange:(NSString *)path 481{ 482 Q_UNUSED(sender); 483 if (!mPriv) 484 return; 485 if ([path isEqualToString:mCurrentDir]) 486 return; 487 488 if ([mSavePanel respondsToSelector:@selector(isVisible)] && ![mSavePanel isVisible]) 489 return; 490 [mCurrentDir release]; 491 mCurrentDir = [path retain]; 492 mPriv->QNSOpenSavePanelDelegate_directoryEntered(QT_PREPEND_NAMESPACE(qt_mac_NSStringToQString(mCurrentDir))); 493} 494 495/* 496 Returns a list of extensions (e.g. "png", "jpg", "gif") 497 for the current name filter. If a filter do not conform 498 to the format *.xyz or * or *.*, an empty list 499 is returned meaning accept everything. 500*/ 501- (QStringList)acceptableExtensionsForSave 502{ 503 QStringList result; 504 for (int i=0; i<mSelectedNameFilter->count(); ++i) { 505 const QString &filter = mSelectedNameFilter->at(i); 506 if (filter.startsWith(QLatin1String("*.")) 507 && !filter.contains(QLatin1Char('?')) 508 && filter.count(QLatin1Char('*')) == 1) { 509 result += filter.mid(2); 510 } else { 511 return QStringList(); // Accept everything 512 } 513 } 514 return result; 515} 516 517- (QString)removeExtensions:(const QString &)filter 518{ 519 QRegExp regExp(QT_PREPEND_NAMESPACE(QString::fromLatin1)(QT_PREPEND_NAMESPACE(qt_file_dialog_filter_reg_exp))); 520 if (regExp.indexIn(filter) != -1) 521 return regExp.cap(1).trimmed(); 522 return filter; 523} 524 525- (void)createTextField 526{ 527 NSRect textRect = { { 0.0, 3.0 }, { 100.0, 25.0 } }; 528 mTextField = [[NSTextField alloc] initWithFrame:textRect]; 529 [[mTextField cell] setFont:[NSFont systemFontOfSize: 530 [NSFont systemFontSizeForControlSize:NSRegularControlSize]]]; 531 [mTextField setAlignment:NSRightTextAlignment]; 532 [mTextField setEditable:false]; 533 [mTextField setSelectable:false]; 534 [mTextField setBordered:false]; 535 [mTextField setDrawsBackground:false]; 536 if (mPriv){ 537 [mTextField setStringValue:[self strip:mPriv->qFileDialogUi->fileTypeLabel->text()]]; 538 } else 539 [mTextField setStringValue:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(QT_PREPEND_NAMESPACE(QFileDialog::tr)("Files of type:"))]; 540} 541 542- (void)createPopUpButton:(const QString &)selectedFilter hideDetails:(BOOL)hideDetails 543{ 544 NSRect popUpRect = { { 100.0, 5.0 }, { 250.0, 25.0 } }; 545 mPopUpButton = [[NSPopUpButton alloc] initWithFrame:popUpRect pullsDown:NO]; 546 [mPopUpButton setTarget:self]; 547 [mPopUpButton setAction:@selector(filterChanged:)]; 548 549 if (mNameFilterDropDownList->size() > 0) { 550 int filterToUse = -1; 551 for (int i=0; i<mNameFilterDropDownList->size(); ++i) { 552 QString currentFilter = mNameFilterDropDownList->at(i); 553 if (selectedFilter == currentFilter || 554 (filterToUse == -1 && currentFilter.startsWith(selectedFilter))) 555 filterToUse = i; 556 QString filter = hideDetails ? [self removeExtensions:currentFilter] : currentFilter; 557 [mPopUpButton addItemWithTitle:QT_PREPEND_NAMESPACE(qt_mac_QStringToNSString)(filter)]; 558 } 559 if (filterToUse != -1) 560 [mPopUpButton selectItemAtIndex:filterToUse]; 561 } 562} 563 564- (QStringList) findStrippedFilterWithVisualFilterName:(QString)name 565{ 566 for (int i=0; i<mNameFilterDropDownList->size(); ++i) { 567 if (mNameFilterDropDownList->at(i).startsWith(name)) 568 return qt_clean_filter_list(mNameFilterDropDownList->at(i)); 569 } 570 return QStringList(); 571} 572 573- (void)createAccessory 574{ 575 NSRect accessoryRect = { { 0.0, 0.0 }, { 450.0, 33.0 } }; 576 mAccessoryView = [[NSView alloc] initWithFrame:accessoryRect]; 577 [mAccessoryView addSubview:mTextField]; 578 [mAccessoryView addSubview:mPopUpButton]; 579} 580 581@end 582 583QT_BEGIN_NAMESPACE 584 585void QFileDialogPrivate::QNSOpenSavePanelDelegate_selectionChanged(const QString &newPath) 586{ 587 emit q_func()->currentChanged(newPath); 588} 589 590void QFileDialogPrivate::QNSOpenSavePanelDelegate_panelClosed(bool accepted) 591{ 592 if (accepted) 593 q_func()->accept(); 594 else 595 q_func()->reject(); 596} 597 598void QFileDialogPrivate::QNSOpenSavePanelDelegate_directoryEntered(const QString &newDir) 599{ 600 setLastVisitedDirectory(newDir); 601 emit q_func()->directoryEntered(newDir); 602} 603 604void QFileDialogPrivate::QNSOpenSavePanelDelegate_filterSelected(int menuIndex) 605{ 606 emit q_func()->filterSelected(nameFilters.at(menuIndex)); 607} 608 609void QFileDialogPrivate::setDirectory_sys(const QString &directory) 610{ 611#ifndef QT_MAC_USE_COCOA 612 if (directory == mCurrentLocation) 613 return; 614 mCurrentLocation = directory; 615 emit q_func()->directoryEntered(mCurrentLocation); 616 617 FSRef fsRef; 618 if (qt_mac_create_fsref(directory, &fsRef) == noErr) { 619 AEDesc desc; 620 if (AECreateDesc(typeFSRef, &fsRef, sizeof(FSRef), &desc) == noErr) 621 NavCustomControl(mDialog, kNavCtlSetLocation, (void*)&desc); 622 } 623#else 624 QMacCocoaAutoReleasePool pool; 625 QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); 626 [delegate->mSavePanel setDirectory:qt_mac_QStringToNSString(directory)]; 627#endif 628} 629 630QString QFileDialogPrivate::directory_sys() const 631{ 632#ifndef QT_MAC_USE_COCOA 633 return mCurrentLocation; 634#else 635 QMacCocoaAutoReleasePool pool; 636 QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); 637 return qt_mac_NSStringToQString([delegate->mSavePanel directory]); 638#endif 639} 640 641void QFileDialogPrivate::selectFile_sys(const QString &filename) 642{ 643 QString filePath = filename; 644 if (QDir::isRelativePath(filePath)) 645 filePath = QFileInfo(directory_sys(), filePath).filePath(); 646 647#ifndef QT_MAC_USE_COCOA 648 // Update the selection list immidiatly, so 649 // subsequent calls to selectedFiles() gets correct: 650 mCurrentSelectionList.clear(); 651 mCurrentSelectionList << filename; 652 if (mCurrentSelection != filename){ 653 mCurrentSelection = filename; 654 emit q_func()->currentChanged(mCurrentSelection); 655 } 656 657 AEDescList descList; 658 if (AECreateList(0, 0, false, &descList) != noErr) 659 return; 660 661 FSRef fsRef; 662 if (qt_mac_create_fsref(filePath, &fsRef) == noErr) { 663 AEDesc desc; 664 if (AECreateDesc(typeFSRef, &fsRef, sizeof(FSRef), &desc) == noErr){ 665 if (AEPutDesc(&descList, 0, &desc) == noErr) 666 NavCustomControl(mDialog, kNavCtlSetSelection, (void*)&descList); 667 } 668 } 669 670 // Type the file name into the save dialog's text field: 671 UInt8 *strBuffer = (UInt8 *)malloc(1024); 672 qt_mac_to_pascal_string(QFileInfo(filename).fileName(), strBuffer); 673 NavCustomControl(mDialog, kNavCtlSetEditFileName, strBuffer); 674 free(strBuffer); 675#else 676 // There seems to no way to select a file once the dialog is running. 677 // So do the next best thing, set the file's directory: 678 setDirectory_sys(QFileInfo(filePath).absolutePath()); 679#endif 680} 681 682QStringList QFileDialogPrivate::selectedFiles_sys() const 683{ 684#ifndef QT_MAC_USE_COCOA 685 if (q_func()->acceptMode() == QFileDialog::AcceptOpen){ 686 return mCurrentSelectionList; 687 } else { 688 return QStringList() << mCurrentLocation + QLatin1Char('/') 689 + QCFString::toQString(NavDialogGetSaveFileName(mDialog)); 690 } 691#else 692 QMacCocoaAutoReleasePool pool; 693 QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); 694 return [delegate selectedFiles]; 695#endif 696} 697 698void QFileDialogPrivate::setNameFilters_sys(const QStringList &filters) 699{ 700#ifndef QT_MAC_USE_COCOA 701 Q_UNUSED(filters); 702#else 703 QMacCocoaAutoReleasePool pool; 704 QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); 705 bool hideDetails = q_func()->testOption(QFileDialog::HideNameFilterDetails); 706 [delegate setNameFilters:filters hideDetails:hideDetails]; 707#endif 708} 709 710void QFileDialogPrivate::setFilter_sys() 711{ 712#ifndef QT_MAC_USE_COCOA 713#else 714 Q_Q(QFileDialog); 715 QMacCocoaAutoReleasePool pool; 716 QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); 717 *(delegate->mQDirFilter) = model->filter(); 718 delegate->mFileMode = fileMode; 719 [delegate->mSavePanel setTitle:qt_mac_QStringToNSString(q->windowTitle())]; 720 [delegate->mSavePanel setPrompt:[delegate strip:acceptLabel]]; 721 if (fileNameLabelExplicitlySat) 722 [delegate->mSavePanel setNameFieldLabel:[delegate strip:qFileDialogUi->fileNameLabel->text()]]; 723 724 [delegate updateProperties]; 725#endif 726} 727 728void QFileDialogPrivate::selectNameFilter_sys(const QString &filter) 729{ 730 int index = nameFilters.indexOf(filter); 731 if (index != -1) { 732#ifndef QT_MAC_USE_COCOA 733 NavMenuItemSpec navSpec; 734 bzero(&navSpec, sizeof(NavMenuItemSpec)); 735 navSpec.menuType = index; 736 NavCustomControl(mDialog, kNavCtlSelectCustomType, &navSpec); 737#else 738 QMacCocoaAutoReleasePool pool; 739 QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); 740 [delegate->mPopUpButton selectItemAtIndex:index]; 741 [delegate filterChanged:nil]; 742#endif 743 } 744} 745 746QString QFileDialogPrivate::selectedNameFilter_sys() const 747{ 748#ifndef QT_MAC_USE_COCOA 749 int index = filterInfo.currentSelection; 750#else 751 QMacCocoaAutoReleasePool pool; 752 QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); 753 int index = [delegate->mPopUpButton indexOfSelectedItem]; 754#endif 755 return index != -1 ? nameFilters.at(index) : QString(); 756} 757 758void QFileDialogPrivate::deleteNativeDialog_sys() 759{ 760#ifndef QT_MAC_USE_COCOA 761 if (mDialog) 762 NavDialogDispose(mDialog); 763 mDialog = 0; 764 mDialogStarted = false; 765#else 766 QMacCocoaAutoReleasePool pool; 767 [reinterpret_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate) release]; 768 mDelegate = 0; 769#endif 770 nativeDialogInUse = false; 771} 772 773bool QFileDialogPrivate::setVisible_sys(bool visible) 774{ 775 Q_Q(QFileDialog); 776 if (!visible == q->isHidden()) 777 return false; 778 779 if (q->windowFlags() & Qt::WindowStaysOnTopHint) { 780 // The native file dialog tries all it can to stay 781 // on the NSModalPanel level. And it might also show 782 // its own "create directory" dialog that we cannot control. 783 // So we need to use the non-native version in this case... 784 return false; 785 } 786 787#ifndef QT_MAC_USE_COCOA 788 return visible ? showCarbonNavServicesDialog() : hideCarbonNavServicesDialog(); 789#else 790 return visible ? showCocoaFilePanel() : hideCocoaFilePanel(); 791#endif 792} 793 794#ifndef QT_MAC_USE_COCOA 795Boolean QFileDialogPrivate::qt_mac_filedialog_filter_proc(AEDesc *theItem, void *info, 796 void *data, NavFilterModes) 797{ 798 QFileDialogPrivate *fileDialogPrivate = static_cast<QFileDialogPrivate *>(data); 799 800 if (!fileDialogPrivate || fileDialogPrivate->filterInfo.filters.isEmpty() 801 || (fileDialogPrivate->filterInfo.currentSelection < 0 802 && fileDialogPrivate->filterInfo.currentSelection 803 >= fileDialogPrivate->filterInfo.filters.size())) 804 return true; 805 806 NavFileOrFolderInfo *theInfo = static_cast<NavFileOrFolderInfo *>(info); 807 QString file; 808 QString path; 809 const QtMacFilterName &fn 810 = fileDialogPrivate->filterInfo.filters.at(fileDialogPrivate->filterInfo.currentSelection); 811 if (theItem->descriptorType == typeFSRef) { 812 FSRef ref; 813 AEGetDescData(theItem, &ref, sizeof(ref)); 814 UInt8 str_buffer[1024]; 815 FSRefMakePath(&ref, str_buffer, 1024); 816 path = QString::fromUtf8(reinterpret_cast<const char *>(str_buffer)); 817 int slsh = path.lastIndexOf(QLatin1Char('/')); 818 if (slsh != -1) 819 file = path.right(path.length() - slsh - 1); 820 else 821 file = path; 822 } 823 QStringList reg = fn.regexp.split(QLatin1String(";")); 824 for (QStringList::const_iterator it = reg.constBegin(); it != reg.constEnd(); ++it) { 825 QRegExp rg(*it, Qt::CaseInsensitive, QRegExp::Wildcard); 826#ifdef DEBUG_FILEDIALOG_FILTERS 827 qDebug("QFileDialogPrivate::qt_mac_filedialog_filter_proc:%d, asked to filter.. %s (%s)", __LINE__, 828 qPrintable(file), qPrintable(*it)); 829#endif 830 if (rg.exactMatch(file)) 831 return true; 832 } 833 834 if (theInfo->isFolder) { 835 if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:qt_mac_QStringToNSString(path)]) 836 return false; 837 return true; 838 } 839 return false; 840} 841 842void QFileDialogPrivate::qt_mac_filedialog_event_proc(const NavEventCallbackMessage msg, 843 NavCBRecPtr p, NavCallBackUserData data) 844{ 845 QFileDialogPrivate *fileDialogPrivate = static_cast<QFileDialogPrivate *>(data); 846 847 switch(msg) { 848 case kNavCBPopupMenuSelect: { 849 NavMenuItemSpec *s = static_cast<NavMenuItemSpec *>(p->eventData.eventDataParms.param); 850 if (int(s->menuType) != fileDialogPrivate->filterInfo.currentSelection) { 851 fileDialogPrivate->filterInfo.currentSelection = s->menuType; 852 emit fileDialogPrivate->q_func()->filterSelected(fileDialogPrivate->nameFilters.at(s->menuType)); 853 } 854 if (fileDialogPrivate->acceptMode == QFileDialog::AcceptSave) { 855 QString base = QCFString::toQString(NavDialogGetSaveFileName(p->context)); 856 QFileInfo fi(base); 857 base = fi.completeBaseName(); 858 const QtMacFilterName &fn = fileDialogPrivate->filterInfo.filters.at( 859 fileDialogPrivate->filterInfo.currentSelection); 860 QStringList reg = fn.regexp.split(QLatin1String(";"), QString::SkipEmptyParts); 861 if (reg.count()) { 862 QString r = reg.first(); 863 r = r.right(r.length()-1); // Strip the * 864 base += r; //"." + QString::number(s->menuType); 865 } 866 NavDialogSetSaveFileName(p->context, QCFString::toCFStringRef(base)); 867 } 868#ifdef DEBUG_FILEDIALOG_FILTERS 869 qDebug("QFileDialogPrivate::qt_mac_filedialog_event_proc:%d - Selected a filter: %ld", __LINE__, s->menuType); 870#endif 871 break; } 872 case kNavCBStart:{ 873 fileDialogPrivate->mDialogStarted = true; 874 // Set selected file: 875 QModelIndexList indexes = fileDialogPrivate->qFileDialogUi->listView->selectionModel()->selectedRows(); 876 QString selected; 877 if (!indexes.isEmpty()) 878 selected = indexes.at(0).data(QFileSystemModel::FilePathRole).toString(); 879 else 880 selected = fileDialogPrivate->typedFiles().value(0); 881 fileDialogPrivate->selectFile_sys(selected); 882 fileDialogPrivate->selectNameFilter_sys(fileDialogPrivate->qFileDialogUi->fileTypeCombo->currentText()); 883 break; } 884 case kNavCBSelectEntry:{ 885 // Event: Current selection has changed. 886 QStringList prevSelectionList = fileDialogPrivate->mCurrentSelectionList; 887 fileDialogPrivate->mCurrentSelectionList.clear(); 888 QString fileNameToEmit; 889 890 AEDescList *descList = (AEDescList *)p->eventData.eventDataParms.param; 891 // Get the number of files selected: 892 UInt8 strBuffer[1024]; 893 long count; 894 OSErr err = AECountItems(descList, &count); 895 if (err != noErr || !count) 896 break; 897 898 for (long index=1; index<=count; ++index) { 899 FSRef ref; 900 err = AEGetNthPtr(descList, index, typeFSRef, 0, 0, &ref, sizeof(ref), 0); 901 if (err != noErr) 902 break; 903 FSRefMakePath(&ref, strBuffer, 1024); 904 QString selected = QString::fromUtf8((const char *)strBuffer); 905 fileDialogPrivate->mCurrentSelectionList << selected; 906 if (!prevSelectionList.contains(selected)) 907 fileNameToEmit = selected; 908 } 909 910 if (!fileNameToEmit.isEmpty() && fileNameToEmit != fileDialogPrivate->mCurrentSelection) 911 emit fileDialogPrivate->q_func()->currentChanged(fileNameToEmit); 912 fileDialogPrivate->mCurrentSelection = fileNameToEmit; 913 break; } 914 case kNavCBShowDesktop: 915 case kNavCBNewLocation:{ 916 // Event: Current directory has changed. 917 AEDesc *desc = (AEDesc *)p->eventData.eventDataParms.param; 918 FSRef ref; 919 AEGetDescData(desc, &ref, sizeof(ref)); 920 UInt8 *strBuffer = (UInt8 *)malloc(1024); 921 FSRefMakePath(&ref, strBuffer, 1024); 922 QString newLocation = QString::fromUtf8((const char *)strBuffer); 923 free(strBuffer); 924 if (fileDialogPrivate->mCurrentLocation != newLocation){ 925 fileDialogPrivate->mCurrentLocation = newLocation; 926 QFileDialog::FileMode mode = fileDialogPrivate->fileMode; 927 if (mode == QFileDialog::AnyFile || mode == QFileDialog::ExistingFile 928 || mode == QFileDialog::ExistingFiles){ 929 // When changing directory, the current selection is cleared if 930 // we are supposed to be selecting files only: 931 if (!fileDialogPrivate->mCurrentSelection.isEmpty()){ 932 fileDialogPrivate->mCurrentSelectionList.clear(); 933 fileDialogPrivate->mCurrentSelection.clear(); 934 emit fileDialogPrivate->q_func()->currentChanged(fileDialogPrivate->mCurrentSelection); 935 } 936 } 937 fileDialogPrivate->setLastVisitedDirectory(newLocation); 938 emit fileDialogPrivate->q_func()->directoryEntered(newLocation); 939 } 940 break; } 941 case kNavCBAccept: 942 fileDialogPrivate->mDialogClosed = true; 943 fileDialogPrivate->q_func()->accept(); 944 break; 945 case kNavCBCancel: 946 fileDialogPrivate->mDialogClosed = true; 947 fileDialogPrivate->q_func()->reject(); 948 break; 949 } 950} 951 952static QFileDialogPrivate::QtMacFilterName qt_mac_extract_filter(const QString &rawFilter, bool showDetails) 953{ 954 QFileDialogPrivate::QtMacFilterName ret; 955 ret.filter = rawFilter; 956 QString result = rawFilter; 957 QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp)); 958 int index = r.indexIn(result); 959 if (index >= 0) 960 result = r.cap(2); 961 962 if (showDetails) { 963 ret.description = rawFilter; 964 } else { 965 if (index >= 0) 966 ret.description = r.cap(1).trimmed(); 967 if (ret.description.isEmpty()) 968 ret.description = result; 969 } 970 ret.regexp = result.replace(QLatin1Char(' '), QLatin1Char(';')); 971 return ret; 972} 973 974static QList<QFileDialogPrivate::QtMacFilterName> qt_mac_make_filters_list(const QString &filter, bool showDetails) 975{ 976#ifdef DEBUG_FILEDIALOG_FILTERS 977 qDebug("QFileDialog:%d - Got filter (%s)", __LINE__, filter.latin1()); 978#endif 979 980 QList<QFileDialogPrivate::QtMacFilterName> ret; 981 QString f(filter); 982 if (f.isEmpty()) 983 f = QFileDialog::tr("All Files (*)"); 984 if (f.isEmpty()) 985 return ret; 986 QStringList filts = qt_make_filter_list(f); 987 for (QStringList::const_iterator it = filts.constBegin(); it != filts.constEnd(); ++it) { 988 QFileDialogPrivate::QtMacFilterName filter = qt_mac_extract_filter(*it, showDetails); 989#ifdef DEBUG_FILEDIALOG_FILTERS 990 qDebug("QFileDialog:%d Split out filter (%d) '%s' '%s' [%s]", __LINE__, ret.count(), 991 filter->regxp.latin1(), filter->description.latin1(), (*it).latin1()); 992#endif 993 ret.append(filter); 994 } 995 return ret; 996} 997 998void QFileDialogPrivate::createNavServicesDialog() 999{ 1000 Q_Q(QFileDialog); 1001 if (mDialog) 1002 deleteNativeDialog_sys(); 1003 1004 NavDialogCreationOptions navOptions; 1005 NavGetDefaultDialogCreationOptions(&navOptions); 1006 1007 // Translate QFileDialog settings into NavDialog options: 1008 if (qt_mac_is_macsheet(q)) { 1009 navOptions.modality = kWindowModalityWindowModal; 1010 navOptions.parentWindow = qt_mac_window_for(q->parentWidget()); 1011 } else if (q->windowModality() == Qt::ApplicationModal) 1012 navOptions.modality = kWindowModalityAppModal; 1013 else 1014 navOptions.modality = kWindowModalityNone; 1015 navOptions.optionFlags |= kNavSupportPackages; 1016 if (q->testOption(QFileDialog::DontConfirmOverwrite)) 1017 navOptions.optionFlags |= kNavDontConfirmReplacement; 1018 if (fileMode != QFileDialog::ExistingFiles) 1019 navOptions.optionFlags &= ~kNavAllowMultipleFiles; 1020 1021 navOptions.windowTitle = QCFString::toCFStringRef(q->windowTitle()); 1022 1023 navOptions.location.h = -1; 1024 navOptions.location.v = -1; 1025 1026 QWidget *parent = q->parentWidget(); 1027 if (parent && parent->isVisible()) { 1028 WindowClass wclass; 1029 GetWindowClass(qt_mac_window_for(parent), &wclass); 1030 parent = parent->window(); 1031 QString s = parent->windowTitle(); 1032 navOptions.clientName = QCFString::toCFStringRef(s); 1033 } 1034 1035 filterInfo.currentSelection = 0; 1036 filterInfo.filters = qt_mac_make_filters_list(nameFilters.join(QLatin1String(";;")), q->isNameFilterDetailsVisible()); 1037 QCFType<CFArrayRef> filterArray; 1038 if (filterInfo.filters.size() > 1) { 1039 int i = 0; 1040 CFStringRef *cfstringArray = static_cast<CFStringRef *>(malloc(sizeof(CFStringRef) 1041 * filterInfo.filters.size())); 1042 for (i = 0; i < filterInfo.filters.size(); ++i) { 1043 cfstringArray[i] = QCFString::toCFStringRef(filterInfo.filters.at(i).description); 1044 } 1045 filterArray = CFArrayCreate(kCFAllocatorDefault, 1046 reinterpret_cast<const void **>(cfstringArray), filterInfo.filters.size(), 1047 &kCFTypeArrayCallBacks); 1048 navOptions.popupExtension = filterArray; 1049 free(cfstringArray); 1050 } 1051 1052 if (q->acceptMode() == QFileDialog::AcceptSave) { 1053 if (NavCreatePutFileDialog(&navOptions, 'cute', kNavGenericSignature, 1054 QFileDialogPrivate::qt_mac_filedialog_event_proc, this, &mDialog)) { 1055 qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__); 1056 return; 1057 } 1058 } else if (fileMode == QFileDialog::DirectoryOnly || fileMode == QFileDialog::Directory) { 1059 if (NavCreateChooseFolderDialog(&navOptions, 1060 QFileDialogPrivate::qt_mac_filedialog_event_proc, 0, this, &mDialog)) { 1061 qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__); 1062 return; 1063 } 1064 } else { 1065 if (NavCreateGetFileDialog(&navOptions, 0, 1066 QFileDialogPrivate::qt_mac_filedialog_event_proc, 0, 1067 QFileDialogPrivate::qt_mac_filedialog_filter_proc, this, &mDialog)) { 1068 qDebug("Shouldn't happen %s:%d", __FILE__, __LINE__); 1069 return; 1070 } 1071 } 1072 1073 // Set start-up directory: 1074 if (mCurrentLocation.isEmpty()) 1075 mCurrentLocation = rootPath(); 1076 FSRef fsRef; 1077 if (qt_mac_create_fsref(mCurrentLocation, &fsRef) == noErr) { 1078 AEDesc desc; 1079 if (AECreateDesc(typeFSRef, &fsRef, sizeof(FSRef), &desc) == noErr) 1080 NavCustomControl(mDialog, kNavCtlSetLocation, (void*)&desc); 1081 } 1082} 1083 1084bool QFileDialogPrivate::showCarbonNavServicesDialog() 1085{ 1086 Q_Q(QFileDialog); 1087 if (q->acceptMode() == QFileDialog::AcceptSave && q->windowModality() == Qt::NonModal) 1088 return false; // cannot do native no-modal save dialogs. 1089 createNavServicesDialog(); 1090 mDialogClosed = false; 1091 if (q->windowModality() != Qt::ApplicationModal) 1092 NavDialogRun(mDialog); 1093 return true; 1094} 1095 1096bool QFileDialogPrivate::hideCarbonNavServicesDialog() 1097{ 1098 if (!mDialogClosed){ 1099 mDialogClosed = true; 1100 NavCustomControl(mDialog, kNavCtlCancel, 0); 1101 } 1102 return true; 1103} 1104 1105#else // Cocoa 1106 1107void QFileDialogPrivate::createNSOpenSavePanelDelegate() 1108{ 1109 Q_Q(QFileDialog); 1110 if (mDelegate) 1111 return; 1112 1113 bool selectDir = q->selectedFiles().isEmpty(); 1114 QString selection(selectDir ? q->directory().absolutePath() : q->selectedFiles().value(0)); 1115 QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = [[QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) alloc] 1116 initWithAcceptMode:acceptMode 1117 title:q->windowTitle() 1118 hideNameFilterDetails:q->testOption(QFileDialog::HideNameFilterDetails) 1119 qDirFilter:model->filter() 1120 fileOptions:opts 1121 fileMode:fileMode 1122 selectFile:selection 1123 confirmOverwrite:!q->testOption(QFileDialog::DontConfirmOverwrite) 1124 priv:this]; 1125 1126 mDelegate = delegate; 1127} 1128 1129bool QFileDialogPrivate::showCocoaFilePanel() 1130{ 1131 Q_Q(QFileDialog); 1132 QMacCocoaAutoReleasePool pool; 1133 createNSOpenSavePanelDelegate(); 1134 QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); 1135 if (qt_mac_is_macsheet(q)) 1136 [delegate showWindowModalSheet:q->parentWidget()]; 1137 else if (!q->testAttribute(Qt::WA_ShowModal)) 1138 [delegate showModelessPanel]; 1139 return true; 1140} 1141 1142bool QFileDialogPrivate::hideCocoaFilePanel() 1143{ 1144 if (!mDelegate){ 1145 // Nothing to do. We return false to leave the question 1146 // open regarding whether or not to go native: 1147 return false; 1148 } else { 1149 QMacCocoaAutoReleasePool pool; 1150 QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); 1151 [delegate closePanel]; 1152 // Even when we hide it, we are still using a 1153 // native dialog, so return true: 1154 return true; 1155 } 1156} 1157 1158#endif 1159 1160void QFileDialogPrivate::mac_nativeDialogModalHelp() 1161{ 1162 // Do a queued meta-call to open the native modal dialog so it opens after the new 1163 // event loop has started to execute (in QDialog::exec). Using a timer rather than 1164 // a queued meta call is intentional to ensure that the call is only delivered when 1165 // [NSApplication run] runs (timers are handeled special in cocoa). If NSApplication is not 1166 // running (which is the case if e.g a top-most QEventLoop has been 1167 // interrupted, and the second-most event loop has not yet been reactivated (regardless 1168 // if [NSApplication run] is still on the stack)), showing a native modal dialog will fail. 1169 if (nativeDialogInUse){ 1170 Q_Q(QFileDialog); 1171 QTimer::singleShot(1, q, SLOT(_q_macRunNativeAppModalPanel())); 1172 } 1173} 1174 1175void QFileDialogPrivate::_q_macRunNativeAppModalPanel() 1176{ 1177 QBoolBlocker nativeDialogOnTop(QApplicationPrivate::native_modal_dialog_active); 1178#ifndef QT_MAC_USE_COCOA 1179 NavDialogRun(mDialog); 1180#else 1181 Q_Q(QFileDialog); 1182 QMacCocoaAutoReleasePool pool; 1183 QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); 1184 [delegate runApplicationModalPanel]; 1185 dialogResultCode_sys() == QDialog::Accepted ? q->accept() : q->reject(); 1186#endif 1187} 1188 1189QDialog::DialogCode QFileDialogPrivate::dialogResultCode_sys() 1190{ 1191#ifndef QT_MAC_USE_COCOA 1192 NavUserAction result = NavDialogGetUserAction(mDialog); 1193 if (result == kNavUserActionCancel || result == kNavUserActionNone) 1194 return QDialog::Rejected; 1195 else 1196 return QDialog::Accepted; 1197#else 1198 QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *delegate = static_cast<QT_MANGLE_NAMESPACE(QNSOpenSavePanelDelegate) *>(mDelegate); 1199 return [delegate dialogResultCode]; 1200#endif 1201} 1202 1203 1204QT_END_NAMESPACE 1205 1206#endif // QT_NO_FILEDIALOG 1207 1208