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 }