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