1 /* 2 Copyright © 2020, Inochi2D Project 3 Distributed under the 2-Clause BSD License, see LICENSE file. 4 5 Authors: Luna Nielsen 6 */ 7 module creator.io; 8 public import creator.io.psd; 9 public import creator.io.inpexport; 10 public import creator.io.videoexport; 11 public import creator.io.imageexport; 12 13 import tinyfiledialogs; 14 public import tinyfiledialogs : TFD_Filter; 15 import std.string; 16 import i18n; 17 18 import bindbc.sdl; 19 import creator.core; 20 21 version(linux) { 22 import dportals.filechooser; 23 import dportals.promise; 24 } 25 26 private { 27 version(linux) { 28 string getWindowHandle() { 29 SDL_SysWMinfo info; 30 SDL_GetWindowWMInfo(incGetWindowPtr(), &info); 31 if (info.subsystem == SDL_SYSWM_TYPE.SDL_SYSWM_X11) { 32 import std.conv : to; 33 return "x11:"~info.info.x11.window.to!string(16); 34 } 35 return ""; 36 } 37 38 FileFilter[] tfdToFileFilter(const(TFD_Filter)[] filters) { 39 FileFilter[] out_; 40 41 foreach(filter; filters) { 42 auto of = FileFilter( 43 cast(string)filter.description.fromStringz, 44 [] 45 ); 46 47 foreach(i, pattern; filter.patterns) { 48 of.items ~= FileFilterItem( 49 cast(uint)i, 50 cast(string)pattern.fromStringz 51 ); 52 } 53 54 out_ ~= of; 55 } 56 57 return out_; 58 } 59 60 string uriFromPromise(Promise promise) { 61 if (promise.success) { 62 import std.array : replace; 63 string uri = promise.value["uris"].data.array[0].str; 64 uri = uri.replace("%20", " "); 65 return uri[7..$]; 66 } 67 return null; 68 } 69 } 70 } 71 72 string incShowImportDialog(const(TFD_Filter)[] filters) { 73 version(linux) { 74 try { 75 FileOpenOptions op; 76 op.filters = tfdToFileFilter(filters); 77 auto promise = dpFileChooserOpenFile(getWindowHandle(), "Import...", op); 78 promise.await(); 79 return promise.uriFromPromise(); 80 } catch (Throwable ex) { 81 82 // FALLBACK: If xdg-desktop-portal is not available then try tinyfiledialogs. 83 c_str filename = tinyfd_openFileDialog(__("Import..."), "", filters, false); 84 if (filename !is null) { 85 string file = cast(string)filename.fromStringz; 86 return file; 87 } 88 return null; 89 } 90 } else { 91 c_str filename = tinyfd_openFileDialog(__("Import..."), "", filters, false); 92 if (filename !is null) { 93 string file = cast(string)filename.fromStringz; 94 return file; 95 } 96 return null; 97 } 98 } 99 100 string incShowOpenFolderDialog(string title="Open...") { 101 version(linux) { 102 try { 103 FileOpenOptions op; 104 op.directory = true; 105 auto promise = dpFileChooserOpenFile(getWindowHandle(), title, op); 106 promise.await(); 107 return promise.uriFromPromise(); 108 } catch(Throwable _) { 109 110 // FALLBACK: If xdg-desktop-portal is not available then try tinyfiledialogs. 111 c_str filename = tinyfd_selectFolderDialog(title.toStringz, null); 112 if (filename !is null) return cast(string)filename.fromStringz; 113 return null; 114 } 115 } else { 116 c_str filename = tinyfd_selectFolderDialog(title.toStringz, null); 117 if (filename !is null) return cast(string)filename.fromStringz; 118 return null; 119 } 120 } 121 122 string incShowOpenDialog(const(TFD_Filter)[] filters, string title="Open...") { 123 version(linux) { 124 try { 125 FileOpenOptions op; 126 op.filters = tfdToFileFilter(filters); 127 auto promise = dpFileChooserOpenFile(getWindowHandle(), title, op); 128 promise.await(); 129 return promise.uriFromPromise(); 130 } catch(Throwable ex) { 131 132 // FALLBACK: If xdg-desktop-portal is not available then try tinyfiledialogs. 133 c_str filename = tinyfd_openFileDialog(title.toStringz, "", filters, false); 134 if (filename !is null) { 135 string file = cast(string)filename.fromStringz; 136 return file; 137 } 138 return null; 139 } 140 } else { 141 c_str filename = tinyfd_openFileDialog(title.toStringz, "", filters, false); 142 if (filename !is null) { 143 string file = cast(string)filename.fromStringz; 144 return file; 145 } 146 return null; 147 } 148 } 149 150 string incShowSaveDialog(const(TFD_Filter)[] filters, string fname, string title = "Save...") { 151 version(linux) { 152 try { 153 FileSaveOptions op; 154 op.filters = tfdToFileFilter(filters); 155 auto promise = dpFileChooserSaveFile(getWindowHandle(), title, op); 156 promise.await(); 157 return promise.uriFromPromise(); 158 } catch (Throwable ex) { 159 160 // FALLBACK: If xdg-desktop-portal is not available then try tinyfiledialogs. 161 c_str filename = tinyfd_saveFileDialog(title.toStringz, fname.toStringz, filters); 162 if (filename !is null) { 163 string file = cast(string)filename.fromStringz; 164 return file; 165 } 166 return null; 167 } 168 } else { 169 c_str filename = tinyfd_saveFileDialog(title.toStringz, fname.toStringz, filters); 170 if (filename !is null) { 171 string file = cast(string)filename.fromStringz; 172 return file; 173 } 174 return null; 175 } 176 }