1 // ----------------------------------------------------------------------------
2 //
3 // fileselect.cxx -- file selector front end
4 //
5 // Copyright (C) 2008-2009
6 // Stelios Bounanos, M0GLD
7 // Dave Freese, 2015
8 //
9 // This file is part of fldigi.
10 //
11 // Fldigi is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // Fldigi is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with fldigi. If not, see <http://www.gnu.org/licenses/>.
23 // ----------------------------------------------------------------------------
24
25 #include <string>
26 #include <cstdlib>
27 #include <libgen.h>
28
29 #include <FL/fl_ask.H>
30 #include <FL/Fl_Native_File_Chooser.H>
31
32 #include "config.h"
33
34 #include "fileselect.h"
35 #include "debug.h"
36 #include "qrunner.h"
37
38 /**
39 \class Fl_Native_File_Chooser
40
41 This class lets an FLTK application easily and consistently access
42 the operating system's native file chooser. Some operating systems
43 have very complex and specific file choosers that many users want
44 access to specifically, instead of FLTK's default file chooser(s).
45
46 In cases where there is no native file browser, FLTK's own file browser
47 is used instead.
48
49 To use this widget correctly, use the following include in your code:
50 \code
51 #include <FL/Fl_Native_File_Chooser.H>
52 \endcode
53 Do not include the other Fl_Native_File_Choser_XXX.H files in your code;
54 those are platform specific files that will be included automatically
55 depending on your build platform.
56
57 The following example shows how to pick a single file:
58 \code
59 // Create and post the local native file chooser
60 #include <FL/Fl_Native_File_Chooser.H>
61 [..]
62 Fl_Native_File_Chooser fnfc;
63 fnfc.title("Pick a file");
64 fnfc.type(Fl_Native_File_Chooser::BROWSE_FILE);
65 fnfc.filter("Text\t*.txt\n"
66 "C Files\t*.{cxx,h,c}");
67 fnfc.directory("/var/tmp"); // default directory to use
68 // Show native chooser
69 switch ( fnfc.show() ) {
70 case -1: printf("ERROR: %s\n", fnfc.errmsg()); break; // ERROR
71 case 1: printf("CANCEL\n"); break; // CANCEL
72 default: printf("PICKED: %s\n", fnfc.filename()); break; // FILE CHOSEN
73 }
74 \endcode
75
76 <B>Platform Specific Caveats</B>
77
78 - Under X windows, it's best if you call Fl_File_Icon::load_system_icons()
79 at the start of main(), to enable the nicer looking file browser widgets.
80 Use the static public attributes of class Fl_File_Chooser to localize
81 the browser.
82 - Some operating systems support certain OS specific options; see
83 Fl_Native_File_Chooser::options() for a list.
84
85 \image html Fl_Native_File_Chooser.png "The Fl_Native_File_Chooser on different platforms."
86 \image latex Fl_Native_File_Chooser.png "The Fl_Native_File_Chooser on different platforms" width=14cm
87
88 enum Type {
89 BROWSE_FILE = 0, ///< browse files (lets user choose one file)
90 BROWSE_DIRECTORY, ///< browse directories (lets user choose one directory)
91 BROWSE_MULTI_FILE, ///< browse files (lets user choose multiple files)
92 BROWSE_MULTI_DIRECTORY, ///< browse directories (lets user choose multiple directories)
93 BROWSE_SAVE_FILE, ///< browse to save a file
94 BROWSE_SAVE_DIRECTORY ///< browse to save a directory
95 };
96 enum Option {
97 NO_OPTIONS = 0x0000, ///< no options enabled
98 SAVEAS_CONFIRM = 0x0001, ///< Show native 'Save As' overwrite confirm dialog (if supported)
99 NEW_FOLDER = 0x0002, ///< Show 'New Folder' icon (if supported)
100 PREVIEW = 0x0004 ///< enable preview mode
101 };
102
103 IMPORTANT NOTICE:
104
105 The filter type must be terminated with a '\n' on OS X or the application crashes with a Bus timeout
106
107 */
108
109 bool trx_inhibit = false;
110
111 using namespace std;
112
113 namespace FSEL {
114
create(void)115 void create(void) {};
destroy(void)116 void destroy(void) {};
117
118 string filename, stitle, sfilter, sdef, sdirectory;
119 char dirbuf[FL_PATH_MAX + 1] = "";
120 char msg[400];
121
122 // use this function for testing on garbage OS, aka Windows
123 /*
124 void pfile (const char *dir, const char *fname, const char *filt) {
125 char fn[FL_PATH_MAX+1];
126 #ifdef __WIN32__
127 fl_filename_expand(fn, sizeof(fn) -1, "$USERPROFILE/");
128 #else
129 fl_filename_expand(fn, sizeof(fn) -1, "$HOME/");
130 #endif
131 strcat(fn, "pfile.txt");
132 FILE *f = fl_fopen(fn, "a");
133 fprintf(f,"\
134 dir: %s\n\
135 file: %s\n\
136 filter: %s\n", dir, fname, filt);
137 fclose(f);
138 }
139 */
140
dosfname(string & s)141 void dosfname(string &s)
142 {
143 for (size_t i = 0; i < s.length(); i++)
144 if (s[i] == '/') s[i] = '\\';
145 }
146
select(const char * title,const char * filter,const char * def,int * fsel)147 const char* select(const char* title, const char* filter, const char* def, int* fsel)
148 {
149 if (strlen(dirbuf) == 0) {
150 #ifdef __WIN32__
151 fl_filename_expand(dirbuf, sizeof(dirbuf) -1, "$USERPROFILE/");
152 #else
153 fl_filename_expand(dirbuf, sizeof(dirbuf) -1, "$HOME/");
154 #endif
155 }
156
157 size_t p = 0;
158 Fl_Native_File_Chooser native;
159
160 stitle.clear();
161 sfilter.clear();
162 sdef.clear();
163 sdirectory.clear();
164
165 if (title) stitle.assign(title);
166 if (filter) sfilter.assign(filter);
167
168 if (def) {
169 sdef.assign(def);
170 if (!sdef.empty()) {
171 p = sdef.length() - 1;
172 if ((sdef[p] == '/') || (sdef[p] == '\\')) sdef.append("fname");
173 }
174 sdirectory.assign(sdef);
175 p = sdirectory.rfind(fl_filename_name(sdef.c_str()));
176 sdirectory.erase(p);
177 }
178 if (sdirectory.empty()) {
179 sdirectory.assign(dirbuf);
180 }
181 if (sdef.empty()) {
182 sdef.assign(sdirectory);
183 sdef.append("temp");
184 }
185
186 if (!sfilter.empty()) {
187 if (sfilter[sfilter.length()-1] != '\n') sfilter += '\n';
188 native.filter(sfilter.c_str());
189 }
190 native.title(stitle.c_str());
191 #if __WIN32__
192 dosfname(sdef);
193 dosfname(sdirectory);
194 #endif
195 if (!sdef.empty()) native.preset_file(sdef.c_str());
196 if (!sdirectory.empty()) native.directory(sdirectory.c_str());
197
198 native.type(Fl_Native_File_Chooser::BROWSE_FILE);
199 native.options(Fl_Native_File_Chooser::PREVIEW);
200
201 // pfile(sdirectory.c_str(), sdef.c_str(), sfilter.c_str());
202
203 filename.clear();
204
205 trx_inhibit = true;
206
207 switch ( native.show() ) {
208 case -1: // ERROR
209 LOG_ERROR("ERROR: %s\n", native.errmsg()); // Error fall through
210 case 1: // CANCEL
211 filename = "";
212 break;
213 default:
214 if ( native.filename() ) {
215 filename = native.filename();
216 } else {
217 filename = "";
218 }
219 break;
220 }
221
222 trx_inhibit = false;
223
224 if (fsel)
225 *fsel = native.filter_value();
226
227 return filename.c_str();
228 }
229
saveas(const char * title,const char * filter,const char * def,int * fsel)230 const char* saveas(const char* title, const char* filter, const char* def, int* fsel)
231 {
232 if (strlen(dirbuf) == 0) {
233 #ifdef __WIN32__
234 fl_filename_expand(dirbuf, sizeof(dirbuf) -1, "$USERPROFILE/");
235 #else
236 fl_filename_expand(dirbuf, sizeof(dirbuf) -1, "$HOME/");
237 #endif
238 }
239
240 size_t p = 0;
241 Fl_Native_File_Chooser native;
242
243 stitle.clear();
244 sfilter.clear();
245 sdef.clear();
246 sdirectory.clear();
247
248 if (title) stitle.assign(title);
249 if (filter) sfilter.assign(filter);
250
251 if (def) {
252 sdef.assign(def);
253 if (!sdef.empty()) {
254 p = sdef.length() - 1;
255 if ((sdef[p] == '/') || (sdef[p] == '\\')) sdef.append("fname");
256 }
257 sdirectory.assign(sdef);
258 p = sdirectory.rfind(fl_filename_name(sdef.c_str()));
259 sdirectory.erase(p);
260 }
261 if (sdirectory.empty()) {
262 sdirectory.assign(dirbuf);
263 }
264 if (sdef.empty()) {
265 sdef.assign(sdirectory);
266 sdef.append("temp");
267 }
268
269 if (!sfilter.empty()) {
270 if (sfilter[sfilter.length()-1] != '\n') sfilter += '\n';
271 native.filter(sfilter.c_str());
272 }
273 native.title(stitle.c_str());
274 #if __WIN32__
275 dosfname(sdef);
276 dosfname(sdirectory);
277 #endif
278 if (!sdef.empty()) native.preset_file(sdef.c_str());
279 if (!sdirectory.empty()) native.directory(sdirectory.c_str());
280 native.type(Fl_Native_File_Chooser::BROWSE_SAVE_FILE);
281 native.options(Fl_Native_File_Chooser::NEW_FOLDER | Fl_Native_File_Chooser::SAVEAS_CONFIRM);
282
283 // pfile(sdirectory.c_str(), sdef.c_str(), sfilter.c_str());
284
285 filename.clear();
286
287 trx_inhibit = true;
288
289 switch ( native.show() ) {
290 case -1: // ERROR
291 LOG_ERROR("ERROR: %s\n", native.errmsg());
292 break;
293 case 1: // CANCEL
294 filename = "";
295 break;
296 default:
297 if ( native.filename() ) {
298 filename = native.filename();
299 } else {
300 filename = "";
301 }
302 break;
303 }
304
305 trx_inhibit = false;
306
307 if (fsel)
308 *fsel = native.filter_value();
309
310 return filename.c_str();
311
312 }
313
dir_select(const char * title,const char * filter,const char * def)314 const char* dir_select(const char* title, const char* filter, const char* def)
315 {
316 Fl_Native_File_Chooser native;
317
318 stitle.clear();
319 sfilter.clear();
320 sdef.clear();
321 if (title) stitle.assign(title);
322 if (filter) sfilter.assign(filter);
323 if (def) sdef.assign(def);
324 if (!sfilter.empty() && sfilter[sfilter.length()-1] != '\n') sfilter += '\n';
325
326 if (!stitle.empty()) native.title(stitle.c_str());
327 native.type(Fl_Native_File_Chooser::BROWSE_DIRECTORY);
328 if (!sfilter.empty()) native.filter(sfilter.c_str());
329 native.options(Fl_Native_File_Chooser::NO_OPTIONS);
330 #if __WIN32__
331 dosfname(sdef);
332 #endif
333 if (!sdef.empty()) {
334 native.directory(sdef.c_str());
335 sdirectory = sdef;
336 } else
337 sdirectory.clear();
338
339 filename.clear();
340
341 trx_inhibit = true;
342
343 switch ( native.show() ) {
344 case -1: // ERROR
345 LOG_ERROR("ERROR: %s\n", native.errmsg());
346 break;
347 case 1: // CANCEL
348 filename = "";
349 break;
350 default:
351 if ( native.filename() ) {
352 filename = native.filename();
353 } else {
354 filename = "";
355 }
356 break;
357 }
358
359 trx_inhibit = false;
360
361 return filename.c_str();
362 }
363
364 } // FSEL
365