PageRenderTime 29ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/fltk/src/Fl_Native_File_Chooser_FLTK.cxx

http://luafltk.googlecode.com/
C++ | 504 lines | 239 code | 47 blank | 218 comment | 47 complexity | 9b05776218ff2eb4867c5b766f0b0a6d MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-3.0, 0BSD
  1. // "$Id: Fl_Native_File_Chooser_FLTK.cxx 7015 2010-01-17 17:09:00Z greg.ercolano $"
  2. //
  3. // FLTK native OS file chooser widget
  4. //
  5. // Copyright 1998-2005 by Bill Spitzak and others.
  6. // Copyright 2004 Greg Ercolano.
  7. // API changes + filter improvements by Nathan Vander Wilt 2005
  8. //
  9. // This library is free software; you can redistribute it and/or
  10. // modify it under the terms of the GNU Library General Public
  11. // License as published by the Free Software Foundation; either
  12. // version 2 of the License, or (at your option) any later version.
  13. //
  14. // This library is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. // Library General Public License for more details.
  18. //
  19. // You should have received a copy of the GNU Library General Public
  20. // License along with this library; if not, write to the Free Software
  21. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  22. // USA.
  23. //
  24. // Please report all bugs and problems to:
  25. //
  26. // http://www.fltk.org/str.php
  27. //
  28. #include <FL/Fl_Native_File_Chooser.H>
  29. #include <FL/Fl_File_Icon.H>
  30. #define FLTK_CHOOSER_SINGLE Fl_File_Chooser::SINGLE
  31. #define FLTK_CHOOSER_DIRECTORY Fl_File_Chooser::DIRECTORY
  32. #define FLTK_CHOOSER_MULTI Fl_File_Chooser::MULTI
  33. #define FLTK_CHOOSER_CREATE Fl_File_Chooser::CREATE
  34. #include "Fl_Native_File_Chooser_common.cxx"
  35. #include <sys/stat.h>
  36. #include <string.h>
  37. /**
  38. The constructor. Internally allocates the native widgets.
  39. Optional \p val presets the type of browser this will be,
  40. which can also be changed with type().
  41. */
  42. Fl_Native_File_Chooser::Fl_Native_File_Chooser(int val) {
  43. //// CANT USE THIS -- MESSES UP LINKING/CREATES DEPENDENCY ON fltk_images.
  44. //// Have app call this from main() instead.
  45. ////
  46. //// static int init = 0; // 'first time' initialize flag
  47. //// if ( init == 0 ) {
  48. //// // Initialize when instanced for first time
  49. //// Fl_File_Icon::load_system_icons();
  50. //// init = 1;
  51. //// }
  52. _btype = val;
  53. _options = NO_OPTIONS;
  54. _filter = NULL;
  55. _filtvalue = 0;
  56. _parsedfilt = NULL;
  57. _preset_file = NULL;
  58. _prevvalue = NULL;
  59. _directory = NULL;
  60. _errmsg = NULL;
  61. _file_chooser = new Fl_File_Chooser(NULL, NULL, 0, NULL);
  62. type(val); // do this after _file_chooser created
  63. _nfilters = 0;
  64. // Added by MG
  65. Fl_Button *b = _file_chooser->previewButton;
  66. Fl_Window *w = b->window();
  67. Fl_Group::current(w); // adds a "Show hidden files" check button in _file_chooser's window
  68. show_hidden = new Fl_Check_Button(b->x() + b->w() + 10, b->y(), 145, b->h(), "Show hidden files");
  69. show_hidden->callback((Fl_Callback*)show_hidden_cb, this);
  70. my_fileList = _file_chooser->browser();
  71. _old_dir = 0; // to detect directory changes
  72. prev_filtervalue = _file_chooser->filter_value(); // to detect filter changes
  73. }
  74. /**
  75. Destructor.
  76. Deallocates any resources allocated to this widget.
  77. */
  78. Fl_Native_File_Chooser::~Fl_Native_File_Chooser() {
  79. delete _file_chooser;
  80. _filter = strfree(_filter);
  81. _parsedfilt = strfree(_parsedfilt);
  82. _preset_file = strfree(_preset_file);
  83. _prevvalue = strfree(_prevvalue);
  84. _directory = strfree(_directory);
  85. _errmsg = strfree(_errmsg);
  86. _old_dir = strfree(_old_dir);
  87. }
  88. // PRIVATE: SET ERROR MESSAGE
  89. void Fl_Native_File_Chooser::errmsg(const char *msg) {
  90. _errmsg = strfree(_errmsg);
  91. _errmsg = strnew(msg);
  92. }
  93. // PRIVATE: translate Native types to Fl_File_Chooser types
  94. int Fl_Native_File_Chooser::type_fl_file(int val) {
  95. switch (val) {
  96. case BROWSE_FILE:
  97. return(FLTK_CHOOSER_SINGLE);
  98. case BROWSE_DIRECTORY:
  99. return(FLTK_CHOOSER_SINGLE | FLTK_CHOOSER_DIRECTORY);
  100. case BROWSE_MULTI_FILE:
  101. return(FLTK_CHOOSER_MULTI);
  102. case BROWSE_MULTI_DIRECTORY:
  103. return(FLTK_CHOOSER_DIRECTORY | FLTK_CHOOSER_MULTI);
  104. case BROWSE_SAVE_FILE:
  105. return(FLTK_CHOOSER_SINGLE | FLTK_CHOOSER_CREATE);
  106. case BROWSE_SAVE_DIRECTORY:
  107. return(FLTK_CHOOSER_DIRECTORY | FLTK_CHOOSER_MULTI | FLTK_CHOOSER_CREATE);
  108. default:
  109. return(FLTK_CHOOSER_SINGLE);
  110. }
  111. }
  112. /**
  113. Sets the current Fl_Native_File_Chooser::Type of browser.
  114. */
  115. void Fl_Native_File_Chooser::type(int val) {
  116. _btype = val;
  117. _file_chooser->type(type_fl_file(val));
  118. }
  119. /**
  120. Gets the current Fl_Native_File_Chooser::Type of browser.
  121. */
  122. int Fl_Native_File_Chooser::type() const {
  123. return(_btype);
  124. }
  125. /**
  126. Sets the platform specific chooser options to \p val.
  127. \p val is expected to be one or more Fl_Native_File_Chooser::Option flags ORed together.
  128. Some platforms have OS-specific functions that can be enabled/disabled via this method.
  129. <P>
  130. \code
  131. Flag Description Win Mac Other
  132. -------------- ----------------------------------------------- ------- ------- -------
  133. NEW_FOLDER Shows the 'New Folder' button. Ignored Used Used
  134. PREVIEW Enables the 'Preview' mode by default. Ignored Ignored Used
  135. SAVEAS_CONFIRM Confirm dialog if BROWSE_SAVE_FILE file exists. Ignored Used Used
  136. \endcode
  137. */
  138. void Fl_Native_File_Chooser::options(int val) {
  139. _options = val;
  140. }
  141. /**
  142. Gets the platform specific Fl_Native_File_Chooser::Option flags.
  143. */
  144. int Fl_Native_File_Chooser::options() const {
  145. return(_options);
  146. }
  147. /**
  148. Post the chooser's dialog. Blocks until dialog has been completed or cancelled.
  149. \returns
  150. - 0 -- user picked a file
  151. - 1 -- user cancelled
  152. - -1 -- failed; errmsg() has reason
  153. */
  154. int Fl_Native_File_Chooser::show() {
  155. // FILTER
  156. if ( _parsedfilt ) {
  157. _file_chooser->filter(_parsedfilt);
  158. }
  159. // FILTER VALUE
  160. // Set this /after/ setting the filter
  161. //
  162. _file_chooser->filter_value(_filtvalue);
  163. // DIRECTORY
  164. if ( _directory && _directory[0] ) {
  165. _file_chooser->directory(_directory);
  166. } else {
  167. _file_chooser->directory(_prevvalue);
  168. }
  169. // PRESET FILE
  170. if ( _preset_file ) {
  171. _file_chooser->value(_preset_file);
  172. }
  173. // OPTIONS: PREVIEW
  174. _file_chooser->preview( (options() & PREVIEW) ? 1 : 0);
  175. // OPTIONS: NEW FOLDER
  176. if ( options() & NEW_FOLDER )
  177. _file_chooser->type(_file_chooser->type() | FLTK_CHOOSER_CREATE); // on
  178. // SHOW
  179. _file_chooser->show();
  180. // BLOCK WHILE BROWSER SHOWN
  181. while ( _file_chooser->shown() ) {
  182. if (_old_dir==0 || strcmp(_old_dir, _file_chooser->directory()) != 0) {
  183. _old_dir = strfree(_old_dir);
  184. _old_dir = strnew(_file_chooser->directory());
  185. if (!show_hidden->value()) remove_hidden_files(my_fileList);
  186. } else if (prev_filtervalue != _file_chooser->filter_value() ) {
  187. prev_filtervalue = _file_chooser->filter_value();
  188. if (!show_hidden->value() ) remove_hidden_files(my_fileList);
  189. }
  190. Fl::wait();
  191. }
  192. if ( _file_chooser->value() && _file_chooser->value()[0] ) {
  193. _prevvalue = strfree(_prevvalue);
  194. _prevvalue = strnew(_file_chooser->value());
  195. _filtvalue = _file_chooser->filter_value(); // update filter value
  196. // HANDLE SHOWING 'SaveAs' CONFIRM
  197. if ( options() & SAVEAS_CONFIRM && type() == BROWSE_SAVE_FILE ) {
  198. struct stat buf;
  199. if ( stat(_file_chooser->value(), &buf) != -1 ) {
  200. if ( buf.st_mode & S_IFREG ) { // Regular file + exists?
  201. if ( exist_dialog() == 0 ) {
  202. return(1);
  203. }
  204. }
  205. }
  206. }
  207. }
  208. if ( _file_chooser->count() ) return(0);
  209. else return(1);
  210. }
  211. /**
  212. Returns a system dependent error message for the last method that failed.
  213. This message should at least be flagged to the user in a dialog box, or to some kind of error log.
  214. Contents will be valid only for methods that document errmsg() will have info on failures.
  215. */
  216. const char *Fl_Native_File_Chooser::errmsg() const {
  217. return(_errmsg ? _errmsg : "No error");
  218. }
  219. /**
  220. Return the filename the user choose.
  221. Use this if only expecting a single filename.
  222. If more than one filename is expected, use filename(int) instead.
  223. Return value may be "" if no filename was chosen (eg. user cancelled).
  224. */
  225. const char* Fl_Native_File_Chooser::filename() const {
  226. if ( _file_chooser->count() > 0 ) return(_file_chooser->value());
  227. return("");
  228. }
  229. /**
  230. Return one of the filenames the user selected.
  231. Use count() to determine how many filenames the user selected.
  232. <P>
  233. \b Example:
  234. \code
  235. if ( fnfc->show() == 0 ) {
  236. // Print all filenames user selected
  237. for (int n=0; n<fnfc->count(); n++ ) {
  238. printf("%d) '%s'\n", n, fnfc->filename(n));
  239. }
  240. }
  241. \endcode
  242. */
  243. const char* Fl_Native_File_Chooser::filename(int i) const {
  244. if ( i < _file_chooser->count() )
  245. return(_file_chooser->value(i+1)); // convert fltk 1 based to our 0 based
  246. return("");
  247. }
  248. /**
  249. Set the title of the file chooser's dialog window.
  250. Can be NULL if no title desired.
  251. The default title varies according to the platform, so you are advised to set the title explicitly.
  252. */
  253. void Fl_Native_File_Chooser::title(const char *val) {
  254. _file_chooser->label(val);
  255. }
  256. /**
  257. Get the title of the file chooser's dialog window.
  258. Return value may be NULL if no title was set.
  259. */
  260. const char *Fl_Native_File_Chooser::title() const {
  261. return(_file_chooser->label());
  262. }
  263. /**
  264. Sets the filename filters used for browsing.
  265. The default is NULL, which browses all files.
  266. <P>
  267. The filter string can be any of:
  268. <P>
  269. - A single wildcard (eg. "*.txt")
  270. - Multiple wildcards (eg. "*.{cxx,h,H}")
  271. - A descriptive name followed by a "\t" and a wildcard (eg. "Text Files\t*.txt")
  272. - A list of separate wildcards with a "\n" between each (eg. "*.{cxx,H}\n*.txt")
  273. - A list of descriptive names and wildcards (eg. "C++ Files\t*.{cxx,H}\nTxt Files\t*.txt")
  274. <P>
  275. The format of each filter is a wildcard, or an optional user description
  276. followed by '\\t' and the wildcard.
  277. <P>
  278. On most platforms, each filter is available to the user via a pulldown menu
  279. in the file chooser. The 'All Files' option is always available to the user.
  280. */
  281. void Fl_Native_File_Chooser::filter(const char *val) {
  282. _filter = strfree(_filter);
  283. _filter = strnew(val);
  284. parse_filter();
  285. }
  286. /**
  287. Returns the filter string last set.
  288. Can be NULL if no filter was set.
  289. */
  290. const char *Fl_Native_File_Chooser::filter() const {
  291. return(_filter);
  292. }
  293. /**
  294. Gets how many filters were available, not including "All Files"
  295. */
  296. int Fl_Native_File_Chooser::filters() const {
  297. return(_nfilters);
  298. }
  299. /**
  300. Sets which filter will be initially selected.
  301. The first filter is indexed as 0.
  302. If filter_value()==filters(), then "All Files" was chosen.
  303. If filter_value() > filters(), then a custom filter was set.
  304. */
  305. void Fl_Native_File_Chooser::filter_value(int val) {
  306. _filtvalue = val;
  307. }
  308. /**
  309. Returns which filter value was last selected by the user.
  310. This is only valid if the chooser returns success.
  311. */
  312. int Fl_Native_File_Chooser::filter_value() const {
  313. return(_filtvalue);
  314. }
  315. /**
  316. Returns the number of filenames (or directory names) the user selected.
  317. <P>
  318. \b Example:
  319. \code
  320. if ( fnfc->show() == 0 ) {
  321. // Print all filenames user selected
  322. for (int n=0; n<fnfc->count(); n++ ) {
  323. printf("%d) '%s'\n", n, fnfc->filename(n));
  324. }
  325. }
  326. \endcode
  327. */
  328. int Fl_Native_File_Chooser::count() const {
  329. return(_file_chooser->count());
  330. }
  331. /**
  332. Preset the directory the browser will show when opened.
  333. If \p val is NULL, or no directory is specified, the chooser will attempt
  334. to use the last non-cancelled folder.
  335. */
  336. void Fl_Native_File_Chooser::directory(const char *val) {
  337. _directory = strfree(_directory);
  338. _directory = strnew(val);
  339. }
  340. /**
  341. Returns the current preset directory() value.
  342. */
  343. const char *Fl_Native_File_Chooser::directory() const {
  344. return(_directory);
  345. }
  346. // PRIVATE: Convert our filter format to fltk's chooser format
  347. // FROM TO (FLTK)
  348. // ------------------------- --------------------------
  349. // "*.cxx" "*.cxx Files(*.cxx)"
  350. // "C Files\t*.{cxx,h}" "C Files(*.{cxx,h})"
  351. // "C Files\t*.{cxx,h}\nText Files\t*.txt" "C Files(*.{cxx,h})\tText Files(*.txt)"
  352. //
  353. // Returns a modified version of the filter that the caller is responsible
  354. // for freeing with strfree().
  355. //
  356. void Fl_Native_File_Chooser::parse_filter() {
  357. _parsedfilt = strfree(_parsedfilt); // clear previous parsed filter (if any)
  358. _nfilters = 0;
  359. char *in = _filter;
  360. if ( !in ) return;
  361. int has_name = strchr(in, '\t') ? 1 : 0;
  362. char mode = has_name ? 'n' : 'w'; // parse mode: n=title, w=wildcard
  363. char wildcard[1024] = ""; // parsed wildcard
  364. char name[1024] = "";
  365. // Parse filter user specified
  366. for ( ; 1; in++ ) {
  367. /*** DEBUG
  368. printf("WORKING ON '%c': mode=<%c> name=<%s> wildcard=<%s>\n",
  369. *in, mode, name, wildcard);
  370. ***/
  371. switch (*in) {
  372. // FINISHED PARSING NAME?
  373. case '\t':
  374. if ( mode != 'n' ) goto regchar;
  375. mode = 'w';
  376. break;
  377. // ESCAPE NEXT CHAR
  378. case '\\':
  379. ++in;
  380. goto regchar;
  381. // FINISHED PARSING ONE OF POSSIBLY SEVERAL FILTERS?
  382. case '\r':
  383. case '\n':
  384. case '\0':
  385. // APPEND NEW FILTER TO LIST
  386. if ( wildcard[0] ) {
  387. // OUT: "name(wild)\tname(wild)"
  388. char comp[2048];
  389. sprintf(comp, "%s%.511s(%.511s)", ((_parsedfilt)?"\t":""),
  390. name, wildcard);
  391. _parsedfilt = strapp(_parsedfilt, comp);
  392. _nfilters++;
  393. //DEBUG printf("DEBUG: PARSED FILT NOW <%s>\n", _parsedfilt);
  394. }
  395. // RESET
  396. wildcard[0] = name[0] = '\0';
  397. mode = strchr(in, '\t') ? 'n' : 'w';
  398. // DONE?
  399. if ( *in == '\0' ) return; // done
  400. else continue; // not done yet, more filters
  401. // Parse all other chars
  402. default: // handle all non-special chars
  403. regchar: // handle regular char
  404. switch ( mode ) {
  405. case 'n': chrcat(name, *in); continue;
  406. case 'w': chrcat(wildcard, *in); continue;
  407. }
  408. break;
  409. }
  410. }
  411. //NOTREACHED
  412. }
  413. /**
  414. Sets the default filename for the chooser.
  415. Use directory() to set the default directory.
  416. Mainly used to preset the filename for save dialogs,
  417. and on most platforms can be used for opening files as well.
  418. */
  419. void Fl_Native_File_Chooser::preset_file(const char* val) {
  420. _preset_file = strfree(_preset_file);
  421. _preset_file = strnew(val);
  422. }
  423. /**
  424. Get the preset filename.
  425. */
  426. const char* Fl_Native_File_Chooser::preset_file() const {
  427. return(_preset_file);
  428. }
  429. void Fl_Native_File_Chooser::show_hidden_cb(Fl_Check_Button *o, void *data)
  430. {
  431. Fl_Native_File_Chooser *mychooser = (Fl_Native_File_Chooser *)data;
  432. if (o->value()) {
  433. mychooser->my_fileList->load(mychooser->_file_chooser->directory());
  434. } else {
  435. remove_hidden_files(mychooser->my_fileList);
  436. mychooser->my_fileList->redraw();
  437. }
  438. }
  439. // PRIVATE: Don't show hidden files
  440. void Fl_Native_File_Chooser::remove_hidden_files(Fl_File_Browser *my_fileList)
  441. {
  442. int count = my_fileList->size();
  443. for(int num = count; num >= 1; num--) {
  444. const char *p = my_fileList->text(num);
  445. if (*p == '.' && strcmp(p, "../") != 0) my_fileList->remove(num);
  446. }
  447. my_fileList->topline(1);
  448. }
  449. // PRIVATE: Don't show hidden files
  450. int Fl_Native_File_Chooser::exist_dialog() {
  451. return(fl_choice("File exists. Are you sure you want to overwrite?",
  452. "Cancel", " OK ", NULL));
  453. }
  454. //
  455. // End of "$Id: Fl_Native_File_Chooser_FLTK.cxx 7015 2010-01-17 17:09:00Z greg.ercolano $".
  456. //