1// Licensed GNU LGPL v3 or later: http://www.gnu.org/licenses/lgpl.html 2 3#include "smnativefiledialog.hh" 4#include "smwindow.hh" 5 6#import <Cocoa/Cocoa.h> 7 8using std::string; 9 10namespace SpectMorph 11{ 12 13class MacFileDialog : public NativeFileDialog 14{ 15 enum class State { 16 null, 17 ok, 18 fail 19 } state = State::null; 20 21 string selected_filename; 22public: 23 MacFileDialog (PuglNativeWindow win_id, bool open, const string& title, const FileDialogFormats& formats) 24 { 25 NSString* titleString = [[NSString alloc] 26 initWithBytes:title.c_str() 27 length:title.size() 28 encoding:NSUTF8StringEncoding]; 29 30 NSMutableArray *file_types_array = [NSMutableArray new]; 31 32 // NSOpenPanel doesn't support multiple filters 33 // -> we use only the extensions of the first format 34 // -> to make this usable on macOS, the first format should contain all supported file extensions 35 for (auto extension : formats.formats[0].exts) 36 { 37 [file_types_array addObject:[NSString stringWithUTF8String:extension.c_str()]]; 38 } 39 40 if (open) 41 { 42 NSOpenPanel *panel = [NSOpenPanel openPanel]; 43 44 [panel setCanChooseFiles:YES]; 45 [panel setCanChooseDirectories:NO]; 46 [panel setAllowsMultipleSelection:NO]; 47 [panel setTitle:titleString]; 48 [panel setAllowedFileTypes:file_types_array]; 49 50 [panel beginWithCompletionHandler:^(NSInteger result) { 51 state = State::fail; 52 53 if (result == NSModalResponseOK) 54 { 55 for (NSURL *url in [panel URLs]) 56 { 57 if (![url isFileURL]) continue; 58 59 state = State::ok; 60 selected_filename = [url.path UTF8String]; 61 break; 62 } 63 } 64 }]; 65 } 66 else 67 { 68 NSSavePanel *panel = [NSSavePanel savePanel]; 69 70 [panel setTitle:titleString]; 71 [panel setAllowedFileTypes:file_types_array]; 72 73 [panel beginWithCompletionHandler:^(NSInteger result) { 74 state = State::fail; 75 76 if (result == NSModalResponseOK) 77 { 78 NSURL* url = [panel URL]; 79 if ([url isFileURL]) 80 { 81 state = State::ok; 82 selected_filename = [url.path UTF8String]; 83 } 84 } 85 }]; 86 } 87 } 88 void 89 process_events() override 90 { 91 if (state == State::ok) 92 { 93 signal_file_selected (selected_filename); 94 state = State::null; 95 } 96 else if (state == State::fail) 97 { 98 signal_file_selected (""); 99 state = State::null; 100 } 101 } 102}; 103 104NativeFileDialog * 105NativeFileDialog::create (Window *window, bool open, const string& title, const FileDialogFormats& formats) 106{ 107 return new MacFileDialog (window->native_window(), open, title, formats); 108} 109 110} 111 112