PageRenderTime 44ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/ExtLibs/wxWidgets/src/osx/cocoa/filedlg.mm

https://bitbucket.org/lennonchan/cafu
Objective C++ | 664 lines | 507 code | 100 blank | 57 comment | 97 complexity | 64560e90dda3be9272af2fea52488039 MD5 | raw file
  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name: src/cocoa/filedlg.mm
  3. // Purpose: wxFileDialog for wxCocoa
  4. // Author: Ryan Norton
  5. // Modified by:
  6. // Created: 2004-10-02
  7. // RCS-ID: $Id$
  8. // Copyright: (c) Ryan Norton
  9. // Licence: wxWindows licence
  10. /////////////////////////////////////////////////////////////////////////////
  11. // ============================================================================
  12. // declarations
  13. // ============================================================================
  14. // ----------------------------------------------------------------------------
  15. // headers
  16. // ----------------------------------------------------------------------------
  17. // For compilers that support precompilation, includes "wx.h".
  18. #include "wx/wxprec.h"
  19. #if wxUSE_FILEDLG
  20. #include "wx/filedlg.h"
  21. #ifndef WX_PRECOMP
  22. #include "wx/msgdlg.h"
  23. #include "wx/app.h"
  24. #include "wx/sizer.h"
  25. #include "wx/stattext.h"
  26. #include "wx/choice.h"
  27. #endif
  28. #include "wx/filename.h"
  29. #include "wx/tokenzr.h"
  30. #include "wx/osx/private.h"
  31. #include "wx/sysopt.h"
  32. // ============================================================================
  33. // implementation
  34. // ============================================================================
  35. // Open Items:
  36. // - parameter support for descending into packages as directories (setTreatsFilePackagesAsDirectories)
  37. // - as setAllowedFileTypes is only functional for NSOpenPanel on 10.6+, on earlier systems, the file
  38. // type choice will not be shown, but all possible file items will be shown, if a popup must be working
  39. // then the delegate method - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename will have to
  40. // be implemented
  41. @interface wxOpenPanelDelegate : NSObject wxOSX_10_6_AND_LATER(<NSOpenSavePanelDelegate>)
  42. {
  43. wxFileDialog* _dialog;
  44. }
  45. - (wxFileDialog*) fileDialog;
  46. - (void) setFileDialog:(wxFileDialog*) dialog;
  47. - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename;
  48. @end
  49. @implementation wxOpenPanelDelegate
  50. - (id) init
  51. {
  52. self = [super init];
  53. _dialog = NULL;
  54. return self;
  55. }
  56. - (wxFileDialog*) fileDialog
  57. {
  58. return _dialog;
  59. }
  60. - (void) setFileDialog:(wxFileDialog*) dialog
  61. {
  62. _dialog = dialog;
  63. }
  64. - (BOOL)panel:(id)sender shouldShowFilename:(NSString *)filename
  65. {
  66. BOOL showObject = YES;
  67. NSString* resolvedLink = [[NSFileManager defaultManager] pathContentOfSymbolicLinkAtPath:filename];
  68. if ( resolvedLink != nil )
  69. filename = resolvedLink;
  70. NSDictionary* fileAttribs = [[NSFileManager defaultManager]
  71. fileAttributesAtPath:filename traverseLink:YES];
  72. if (fileAttribs)
  73. {
  74. // check for packages
  75. if ([NSFileTypeDirectory isEqualTo:[fileAttribs objectForKey:NSFileType]])
  76. {
  77. if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:filename] == NO)
  78. showObject = YES; // it's a folder, OK to show
  79. else
  80. {
  81. // it's a packaged directory, apply check
  82. wxCFStringRef filecf([filename retain]);
  83. showObject = _dialog->CheckFile(filecf.AsString());
  84. }
  85. }
  86. else
  87. {
  88. // the code above only solves links, not aliases, do this here:
  89. NSString* resolvedAlias = nil;
  90. CFURLRef url = CFURLCreateWithFileSystemPath (kCFAllocatorDefault,
  91. (CFStringRef)filename,
  92. kCFURLPOSIXPathStyle,
  93. NO);
  94. if (url != NULL)
  95. {
  96. FSRef fsRef;
  97. if (CFURLGetFSRef(url, &fsRef))
  98. {
  99. Boolean targetIsFolder, wasAliased;
  100. OSErr err = FSResolveAliasFile (&fsRef, true, &targetIsFolder, &wasAliased);
  101. if ((err == noErr) && wasAliased)
  102. {
  103. CFURLRef resolvedUrl = CFURLCreateFromFSRef(kCFAllocatorDefault, &fsRef);
  104. if (resolvedUrl != NULL)
  105. {
  106. resolvedAlias = (NSString*) CFURLCopyFileSystemPath(resolvedUrl,
  107. kCFURLPOSIXPathStyle);
  108. CFRelease(resolvedUrl);
  109. }
  110. }
  111. }
  112. CFRelease(url);
  113. }
  114. if (resolvedAlias != nil)
  115. {
  116. // recursive call
  117. [resolvedAlias autorelease];
  118. showObject = [self panel:sender shouldShowFilename:resolvedAlias];
  119. }
  120. else
  121. {
  122. wxCFStringRef filecf([filename retain]);
  123. showObject = _dialog->CheckFile(filecf.AsString());
  124. }
  125. }
  126. }
  127. return showObject;
  128. }
  129. @end
  130. IMPLEMENT_CLASS(wxFileDialog, wxFileDialogBase)
  131. wxFileDialog::wxFileDialog(
  132. wxWindow *parent, const wxString& message,
  133. const wxString& defaultDir, const wxString& defaultFileName, const wxString& wildCard,
  134. long style, const wxPoint& pos, const wxSize& sz, const wxString& name)
  135. : wxFileDialogBase(parent, message, defaultDir, defaultFileName, wildCard, style, pos, sz, name)
  136. {
  137. m_filterIndex = -1;
  138. m_sheetDelegate = [[ModalDialogDelegate alloc] init];
  139. [(ModalDialogDelegate*)m_sheetDelegate setImplementation: this];
  140. }
  141. wxFileDialog::~wxFileDialog()
  142. {
  143. [m_sheetDelegate release];
  144. }
  145. bool wxFileDialog::SupportsExtraControl() const
  146. {
  147. return true;
  148. }
  149. NSArray* GetTypesFromExtension( const wxString extensiongroup, wxArrayString& extensions )
  150. {
  151. NSMutableArray* types = nil;
  152. extensions.Clear();
  153. wxStringTokenizer tokenizer( extensiongroup, wxT(";") ) ;
  154. while ( tokenizer.HasMoreTokens() )
  155. {
  156. wxString extension = tokenizer.GetNextToken() ;
  157. // Remove leading '*'
  158. if ( extension.length() && (extension.GetChar(0) == '*') )
  159. extension = extension.Mid( 1 );
  160. // Remove leading '.'
  161. if ( extension.length() && (extension.GetChar(0) == '.') )
  162. extension = extension.Mid( 1 );
  163. // Remove leading '*', this is for handling *.*
  164. if ( extension.length() && (extension.GetChar(0) == '*') )
  165. extension = extension.Mid( 1 );
  166. if ( extension.IsEmpty() )
  167. {
  168. extensions.Clear();
  169. [types release];
  170. types = nil;
  171. return nil;
  172. }
  173. if ( types == nil )
  174. types = [[NSMutableArray alloc] init];
  175. extensions.Add(extension.Lower());
  176. wxCFStringRef cfext(extension);
  177. [types addObject: (NSString*)cfext.AsNSString() ];
  178. #if 0
  179. // add support for classic fileType / creator here
  180. wxUint32 fileType, creator;
  181. // extension -> mactypes
  182. #endif
  183. }
  184. [types autorelease];
  185. return types;
  186. }
  187. NSArray* GetTypesFromFilter( const wxString& filter, wxArrayString& names, wxArrayString& extensiongroups )
  188. {
  189. NSMutableArray* types = nil;
  190. bool allowAll = false;
  191. names.Clear();
  192. extensiongroups.Clear();
  193. if ( !filter.empty() )
  194. {
  195. wxStringTokenizer tokenizer( filter, wxT("|") );
  196. int numtokens = (int)tokenizer.CountTokens();
  197. if(numtokens == 1)
  198. {
  199. // we allow for compatibility reason to have a single filter expression (like *.*) without
  200. // an explanatory text, in that case the first part is name and extension at the same time
  201. wxString extension = tokenizer.GetNextToken();
  202. names.Add( extension );
  203. extensiongroups.Add( extension );
  204. }
  205. else
  206. {
  207. int numextensions = numtokens / 2;
  208. for(int i = 0; i < numextensions; i++)
  209. {
  210. wxString name = tokenizer.GetNextToken();
  211. wxString extension = tokenizer.GetNextToken();
  212. names.Add( name );
  213. extensiongroups.Add( extension );
  214. }
  215. }
  216. const size_t extCount = extensiongroups.GetCount();
  217. wxArrayString extensions;
  218. for ( size_t i = 0 ; i < extCount; i++ )
  219. {
  220. NSArray* exttypes = GetTypesFromExtension(extensiongroups[i], extensions);
  221. if ( exttypes != nil )
  222. {
  223. if ( allowAll == false )
  224. {
  225. if ( types == nil )
  226. types = [[NSMutableArray alloc] init];
  227. [types addObjectsFromArray:exttypes];
  228. }
  229. }
  230. else
  231. {
  232. allowAll = true;
  233. [types release];
  234. types = nil;
  235. }
  236. }
  237. }
  238. [types autorelease];
  239. return types;
  240. }
  241. void wxFileDialog::ShowWindowModal()
  242. {
  243. wxCFStringRef cf( m_message );
  244. wxCFStringRef dir( m_dir );
  245. wxCFStringRef file( m_fileName );
  246. wxNonOwnedWindow* parentWindow = NULL;
  247. m_modality = wxDIALOG_MODALITY_WINDOW_MODAL;
  248. if (GetParent())
  249. parentWindow = dynamic_cast<wxNonOwnedWindow*>(wxGetTopLevelParent(GetParent()));
  250. wxASSERT_MSG(parentWindow, "Window modal display requires parent.");
  251. NSArray* types = GetTypesFromFilter( m_wildCard, m_filterNames, m_filterExtensions ) ;
  252. if ( HasFlag(wxFD_SAVE) )
  253. {
  254. NSSavePanel* sPanel = [NSSavePanel savePanel];
  255. SetupExtraControls(sPanel);
  256. // makes things more convenient:
  257. [sPanel setCanCreateDirectories:YES];
  258. [sPanel setMessage:cf.AsNSString()];
  259. // if we should be able to descend into pacakges we must somehow
  260. // be able to pass this in
  261. [sPanel setTreatsFilePackagesAsDirectories:NO];
  262. [sPanel setCanSelectHiddenExtension:YES];
  263. [sPanel setAllowedFileTypes:types];
  264. [sPanel setAllowsOtherFileTypes:NO];
  265. NSWindow* nativeParent = parentWindow->GetWXWindow();
  266. [sPanel beginSheetForDirectory:dir.AsNSString() file:file.AsNSString()
  267. modalForWindow: nativeParent modalDelegate: m_sheetDelegate
  268. didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
  269. contextInfo: nil];
  270. }
  271. else
  272. {
  273. NSOpenPanel* oPanel = [NSOpenPanel openPanel];
  274. SetupExtraControls(oPanel);
  275. [oPanel setTreatsFilePackagesAsDirectories:NO];
  276. [oPanel setCanChooseDirectories:NO];
  277. [oPanel setResolvesAliases:YES];
  278. [oPanel setCanChooseFiles:YES];
  279. [oPanel setMessage:cf.AsNSString()];
  280. [oPanel setAllowsMultipleSelection: (HasFlag(wxFD_MULTIPLE) ? YES : NO )];
  281. NSWindow* nativeParent = parentWindow->GetWXWindow();
  282. [oPanel beginSheetForDirectory:dir.AsNSString() file:file.AsNSString()
  283. types: types modalForWindow: nativeParent
  284. modalDelegate: m_sheetDelegate
  285. didEndSelector: @selector(sheetDidEnd:returnCode:contextInfo:)
  286. contextInfo: nil];
  287. }
  288. }
  289. // Create a panel with the file type drop down list
  290. // If extra controls need to be added (see wxFileDialog::SetExtraControlCreator), add
  291. // them to the panel as well
  292. // Returns the newly created wxPanel
  293. wxWindow* wxFileDialog::CreateFilterPanel(wxWindow *extracontrol)
  294. {
  295. wxPanel *extrapanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize);
  296. wxBoxSizer *verticalSizer = new wxBoxSizer(wxVERTICAL);
  297. extrapanel->SetSizer(verticalSizer);
  298. // the file type control
  299. {
  300. wxBoxSizer *horizontalSizer = new wxBoxSizer(wxHORIZONTAL);
  301. verticalSizer->Add(horizontalSizer, 0, wxEXPAND, 0);
  302. wxStaticText *stattext = new wxStaticText( extrapanel, wxID_ANY, _("File type:") );
  303. horizontalSizer->Add(stattext, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5);
  304. m_filterChoice = new wxChoice(extrapanel, wxID_ANY);
  305. horizontalSizer->Add(m_filterChoice, 1, wxALIGN_CENTER_VERTICAL|wxALL, 5);
  306. m_filterChoice->Append(m_filterNames);
  307. if( m_filterNames.GetCount() > 0)
  308. {
  309. if ( m_firstFileTypeFilter >= 0 )
  310. m_filterChoice->SetSelection(m_firstFileTypeFilter);
  311. }
  312. m_filterChoice->Connect(wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler(wxFileDialog::OnFilterSelected), NULL, this);
  313. }
  314. if(extracontrol)
  315. {
  316. wxBoxSizer *horizontalSizer = new wxBoxSizer(wxHORIZONTAL);
  317. verticalSizer->Add(horizontalSizer, 0, wxEXPAND, 0);
  318. extracontrol->Reparent(extrapanel);
  319. horizontalSizer->Add(extracontrol);
  320. }
  321. verticalSizer->Layout();
  322. verticalSizer->SetSizeHints(extrapanel);
  323. return extrapanel;
  324. }
  325. void wxFileDialog::DoOnFilterSelected(int index)
  326. {
  327. NSArray* types = GetTypesFromExtension(m_filterExtensions[index],m_currentExtensions);
  328. NSSavePanel* panel = (NSSavePanel*) GetWXWindow();
  329. if ( m_delegate )
  330. [panel validateVisibleColumns];
  331. else
  332. [panel setAllowedFileTypes:types];
  333. }
  334. // An item has been selected in the file filter wxChoice:
  335. void wxFileDialog::OnFilterSelected( wxCommandEvent &WXUNUSED(event) )
  336. {
  337. DoOnFilterSelected( m_filterChoice->GetSelection() );
  338. }
  339. bool wxFileDialog::CheckFile( const wxString& filename )
  340. {
  341. if ( m_currentExtensions.GetCount() == 0 )
  342. return true;
  343. wxString ext = filename.AfterLast('.').Lower();
  344. for ( size_t i = 0; i < m_currentExtensions.GetCount(); ++i )
  345. {
  346. if ( ext == m_currentExtensions[i] )
  347. return true;
  348. }
  349. return false;
  350. }
  351. void wxFileDialog::SetupExtraControls(WXWindow nativeWindow)
  352. {
  353. NSSavePanel* panel = (NSSavePanel*) nativeWindow;
  354. wxNonOwnedWindow::Create( GetParent(), nativeWindow );
  355. wxWindow* extracontrol = NULL;
  356. if ( HasExtraControlCreator() )
  357. {
  358. CreateExtraControl();
  359. extracontrol = GetExtraControl();
  360. }
  361. NSView* accView = nil;
  362. m_delegate = nil;
  363. if ( m_useFileTypeFilter )
  364. {
  365. m_filterPanel = CreateFilterPanel(extracontrol);
  366. accView = m_filterPanel->GetHandle();
  367. if( HasFlag(wxFD_OPEN) )
  368. {
  369. if ( UMAGetSystemVersion() < 0x1060 )
  370. {
  371. wxOpenPanelDelegate* del = [[wxOpenPanelDelegate alloc]init];
  372. [del setFileDialog:this];
  373. [panel setDelegate:del];
  374. m_delegate = del;
  375. }
  376. }
  377. }
  378. else
  379. {
  380. m_filterPanel = NULL;
  381. m_filterChoice = NULL;
  382. if ( extracontrol != nil )
  383. accView = extracontrol->GetHandle();
  384. }
  385. if ( accView != nil )
  386. {
  387. [accView removeFromSuperview];
  388. [panel setAccessoryView:accView];
  389. }
  390. else
  391. {
  392. [panel setAccessoryView:nil];
  393. }
  394. }
  395. int wxFileDialog::ShowModal()
  396. {
  397. wxMacAutoreleasePool autoreleasepool;
  398. wxCFStringRef cf( m_message );
  399. wxCFStringRef dir( m_dir );
  400. wxCFStringRef file( m_fileName );
  401. m_path = wxEmptyString;
  402. m_fileNames.Clear();
  403. m_paths.Clear();
  404. wxNonOwnedWindow* parentWindow = NULL;
  405. int returnCode = -1;
  406. if (GetParent())
  407. {
  408. parentWindow = dynamic_cast<wxNonOwnedWindow*>(wxGetTopLevelParent(GetParent()));
  409. }
  410. NSArray* types = GetTypesFromFilter( m_wildCard, m_filterNames, m_filterExtensions ) ;
  411. m_useFileTypeFilter = m_filterExtensions.GetCount() > 1;
  412. if( HasFlag(wxFD_OPEN) )
  413. {
  414. if ( !(wxSystemOptions::HasOption( wxOSX_FILEDIALOG_ALWAYS_SHOW_TYPES ) && (wxSystemOptions::GetOptionInt( wxOSX_FILEDIALOG_ALWAYS_SHOW_TYPES ) == 1)) )
  415. m_useFileTypeFilter = false;
  416. }
  417. m_firstFileTypeFilter = -1;
  418. if ( m_useFileTypeFilter
  419. && m_filterIndex >= 0 && m_filterIndex < m_filterExtensions.GetCount() )
  420. {
  421. m_firstFileTypeFilter = m_filterIndex;
  422. }
  423. else if ( m_useFileTypeFilter )
  424. {
  425. types = nil;
  426. bool useDefault = true;
  427. for ( size_t i = 0; i < m_filterExtensions.GetCount(); ++i )
  428. {
  429. types = GetTypesFromExtension(m_filterExtensions[i], m_currentExtensions);
  430. if ( m_currentExtensions.GetCount() == 0 )
  431. {
  432. useDefault = false;
  433. m_firstFileTypeFilter = i;
  434. break;
  435. }
  436. for ( size_t j = 0; j < m_currentExtensions.GetCount(); ++j )
  437. {
  438. if ( m_fileName.EndsWith(m_currentExtensions[j]) )
  439. {
  440. m_firstFileTypeFilter = i;
  441. useDefault = false;
  442. break;
  443. }
  444. }
  445. if ( !useDefault )
  446. break;
  447. }
  448. if ( useDefault )
  449. {
  450. types = GetTypesFromExtension(m_filterExtensions[0], m_currentExtensions);
  451. m_firstFileTypeFilter = 0;
  452. }
  453. }
  454. if ( HasFlag(wxFD_SAVE) )
  455. {
  456. NSSavePanel* sPanel = [NSSavePanel savePanel];
  457. SetupExtraControls(sPanel);
  458. // makes things more convenient:
  459. [sPanel setCanCreateDirectories:YES];
  460. [sPanel setMessage:cf.AsNSString()];
  461. // if we should be able to descend into pacakges we must somehow
  462. // be able to pass this in
  463. [sPanel setTreatsFilePackagesAsDirectories:NO];
  464. [sPanel setCanSelectHiddenExtension:YES];
  465. [sPanel setAllowedFileTypes:types];
  466. [sPanel setAllowsOtherFileTypes:NO];
  467. if ( HasFlag(wxFD_OVERWRITE_PROMPT) )
  468. {
  469. }
  470. /*
  471. Let the file dialog know what file type should be used initially.
  472. If this is not done then when setting the filter index
  473. programmatically to 1 the file will still have the extension
  474. of the first file type instead of the second one. E.g. when file
  475. types are foo and bar, a filename "myletter" with SetDialogIndex(1)
  476. would result in saving as myletter.foo, while we want myletter.bar.
  477. */
  478. if(m_firstFileTypeFilter > 0)
  479. {
  480. DoOnFilterSelected(m_firstFileTypeFilter);
  481. }
  482. returnCode = [sPanel runModalForDirectory: m_dir.IsEmpty() ? nil : dir.AsNSString() file:file.AsNSString() ];
  483. ModalFinishedCallback(sPanel, returnCode);
  484. }
  485. else
  486. {
  487. NSOpenPanel* oPanel = [NSOpenPanel openPanel];
  488. SetupExtraControls(oPanel);
  489. [oPanel setTreatsFilePackagesAsDirectories:NO];
  490. [oPanel setCanChooseDirectories:NO];
  491. [oPanel setResolvesAliases:YES];
  492. [oPanel setCanChooseFiles:YES];
  493. [oPanel setMessage:cf.AsNSString()];
  494. [oPanel setAllowsMultipleSelection: (HasFlag(wxFD_MULTIPLE) ? YES : NO )];
  495. if ( UMAGetSystemVersion() < 0x1060 )
  496. {
  497. returnCode = [oPanel runModalForDirectory:m_dir.IsEmpty() ? nil : dir.AsNSString()
  498. file:file.AsNSString() types:(m_delegate == nil ? types : nil)];
  499. }
  500. else
  501. {
  502. [oPanel setAllowedFileTypes: (m_delegate == nil ? types : nil)];
  503. if ( !m_dir.IsEmpty() )
  504. [oPanel setDirectoryURL:[NSURL fileURLWithPath:dir.AsNSString()
  505. isDirectory:YES]];
  506. returnCode = [oPanel runModal];
  507. }
  508. ModalFinishedCallback(oPanel, returnCode);
  509. }
  510. return GetReturnCode();
  511. }
  512. void wxFileDialog::ModalFinishedCallback(void* panel, int returnCode)
  513. {
  514. int result = wxID_CANCEL;
  515. if (HasFlag(wxFD_SAVE))
  516. {
  517. if (returnCode == NSOKButton )
  518. {
  519. NSSavePanel* sPanel = (NSSavePanel*)panel;
  520. result = wxID_OK;
  521. m_path = wxCFStringRef::AsString([sPanel filename]);
  522. m_fileName = wxFileNameFromPath(m_path);
  523. m_dir = wxPathOnly( m_path );
  524. if (m_filterChoice)
  525. {
  526. m_filterIndex = m_filterChoice->GetSelection();
  527. }
  528. }
  529. }
  530. else
  531. {
  532. NSOpenPanel* oPanel = (NSOpenPanel*)panel;
  533. if (returnCode == NSOKButton )
  534. {
  535. panel = oPanel;
  536. result = wxID_OK;
  537. NSArray* filenames = [oPanel filenames];
  538. for ( size_t i = 0 ; i < [filenames count] ; ++ i )
  539. {
  540. wxString fnstr = wxCFStringRef::AsString([filenames objectAtIndex:i]);
  541. m_paths.Add( fnstr );
  542. m_fileNames.Add( wxFileNameFromPath(fnstr) );
  543. if ( i == 0 )
  544. {
  545. m_path = fnstr;
  546. m_fileName = wxFileNameFromPath(fnstr);
  547. m_dir = wxPathOnly( fnstr );
  548. }
  549. }
  550. }
  551. if ( m_delegate )
  552. {
  553. [oPanel setDelegate:nil];
  554. [m_delegate release];
  555. m_delegate = nil;
  556. }
  557. }
  558. SetReturnCode(result);
  559. if (GetModality() == wxDIALOG_MODALITY_WINDOW_MODAL)
  560. SendWindowModalDialogEvent ( wxEVT_WINDOW_MODAL_DIALOG_CLOSED );
  561. UnsubclassWin();
  562. [(NSSavePanel*) panel setAccessoryView:nil];
  563. }
  564. #endif // wxUSE_FILEDLG