summaryrefslogtreecommitdiff
path: root/src/Fl_Native_File_Chooser_GTK.cxx
blob: bc5bd6862b5c00ceae5276a2f140071bab7e8fe6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
// "$Id$"
//
// FLTK native file chooser widget wrapper for GTK's GtkFileChooserDialog 
//
// Copyright 1998-2014 by Bill Spitzak and others.
// Copyright 2012 IMM
//
// This library is free software. Distribution and use rights are outlined in
// the file "COPYING" which should have been included with this file.  If this
// file is missing or damaged, see the license at:
//
//     http://www.fltk.org/COPYING.php
//
// Please report all bugs and problems to:
//
//     http://www.fltk.org/str.php
//

#include <FL/x.H>
#if HAVE_DLSYM && HAVE_DLFCN_H
#include <dlfcn.h>   // for dlopen et al
#endif
#include <locale.h>  // for setlocale

/* --------------------- Type definitions from GLIB and GTK --------------------- */
/* all of this is from the public gnome API, so unlikely to change */
#ifndef	FALSE
#define	FALSE	(0)
#endif
#ifndef	TRUE
#define	TRUE	(!FALSE)
#endif
typedef void* gpointer;
typedef int    gint;
typedef unsigned int    guint;
typedef unsigned long   gulong;
typedef gint   gboolean;
typedef char   gchar;
typedef struct _GSList GSList;
struct _GSList
{
  gpointer data;
  GSList *next;
};
#define  g_slist_next(slist)	         ((slist) ? (((GSList *)(slist))->next) : NULL)
typedef struct _GtkWidget      GtkWidget;
typedef struct _GtkFileChooser      GtkFileChooser;
typedef struct _GtkDialog        GtkDialog;
typedef struct _GtkWindow          GtkWindow;
typedef struct _GdkDrawable           GdkWindow;
typedef struct _GtkFileFilter     GtkFileFilter;
typedef struct _GtkToggleButton       GtkToggleButton;
typedef enum {
  GTK_FILE_FILTER_FILENAME     = 1 << 0,
  GTK_FILE_FILTER_URI          = 1 << 1,
  GTK_FILE_FILTER_DISPLAY_NAME = 1 << 2,
  GTK_FILE_FILTER_MIME_TYPE    = 1 << 3
} GtkFileFilterFlags;
struct _GtkFileFilterInfo
{
  GtkFileFilterFlags contains;
  
  const gchar *filename;
  const gchar *uri;
  const gchar *display_name;
  const gchar *mime_type;
};
typedef struct _GtkFileFilterInfo GtkFileFilterInfo;
typedef gboolean (*GtkFileFilterFunc) (const GtkFileFilterInfo *filter_info, gpointer data);
typedef void (*GDestroyNotify)(gpointer data);
typedef enum
{
  GTK_FILE_CHOOSER_ACTION_OPEN,
  GTK_FILE_CHOOSER_ACTION_SAVE,
  GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
  GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER
} GtkFileChooserAction;
#define GTK_STOCK_CANCEL           "gtk-cancel"
#define GTK_STOCK_SAVE             "gtk-save"
#define GTK_STOCK_OPEN             "gtk-open"
const int   GTK_RESPONSE_NONE = -1;
const int   GTK_RESPONSE_ACCEPT = -3;
const int   GTK_RESPONSE_DELETE_EVENT = -4;
const int   GTK_RESPONSE_CANCEL = -6;
typedef void  (*GCallback)(void);
#define	G_CALLBACK(f)			 ((GCallback) (f))
typedef int GConnectFlags;
typedef struct _GClosure		 GClosure;
typedef void  (*GClosureNotify)(gpointer data, GClosure	*closure);

/* --------------------- End of Type definitions from GLIB and GTK --------------------- */

int Fl_GTK_File_Chooser::did_find_GTK_libs = 0;

/* These are the GTK/GLib methods we want to load, but not call by name...! */

// void g_free (gpointer mem);
typedef void (*XX_g_free)(gpointer);
static XX_g_free fl_g_free = NULL;

// gpointer g_slist_nth_data (GSList *list, guint n);
typedef gpointer (*XX_g_slist_nth_data) (GSList *, guint);
static XX_g_slist_nth_data fl_g_slist_nth_data = NULL;

// guint g_slist_length (GSList *list);
typedef guint (*XX_g_slist_length) (GSList *);
static XX_g_slist_length fl_g_slist_length = NULL;

// void g_slist_free (GSList *list);
typedef void (*XX_g_slist_free) (GSList *);
static XX_g_slist_free fl_g_slist_free = NULL;

// gboolean gtk_init_check (int *argc, char ***argv);
typedef gboolean (*XX_gtk_init_check)(int *, char ***);
static XX_gtk_init_check fl_gtk_init_check = NULL;

// void gtk_widget_destroy (GtkWidget *widget);
typedef void (*XX_gtk_widget_destroy) (GtkWidget *);
static XX_gtk_widget_destroy fl_gtk_widget_destroy = NULL;

// void gtk_file_chooser_set_select_multiple(GtkFileChooser *chooser, gboolean select_multiple);
typedef void (*XX_gtk_file_chooser_set_select_multiple)(GtkFileChooser *, gboolean);
static XX_gtk_file_chooser_set_select_multiple fl_gtk_file_chooser_set_select_multiple = NULL;

// void gtk_file_chooser_set_do_overwrite_confirmation(GtkFileChooser *chooser, gboolean do_overwrite_confirmation);
typedef void (*XX_gtk_file_chooser_set_do_overwrite_confirmation)(GtkFileChooser *, gboolean);
static XX_gtk_file_chooser_set_do_overwrite_confirmation fl_gtk_file_chooser_set_do_overwrite_confirmation = NULL;

// void gtk_file_chooser_set_current_name (GtkFileChooser *chooser, const gchar *name);
typedef void (*XX_gtk_file_chooser_set_current_name)(GtkFileChooser *, const gchar *);
static XX_gtk_file_chooser_set_current_name fl_gtk_file_chooser_set_current_name = NULL;

// void gtk_file_chooser_set_current_folder (GtkFileChooser *chooser, const gchar *name);
typedef void (*XX_gtk_file_chooser_set_current_folder)(GtkFileChooser *, const gchar *);
static XX_gtk_file_chooser_set_current_folder fl_gtk_file_chooser_set_current_folder = NULL;

// void gtk_file_chooser_set_create_folders (GtkFileChooser *chooser, gboolean create_folders);
typedef void (*XX_gtk_file_chooser_set_create_folders) (GtkFileChooser *, gboolean);
static XX_gtk_file_chooser_set_create_folders fl_gtk_file_chooser_set_create_folders = NULL;

// gboolean gtk_file_chooser_get_select_multiple(GtkFileChooser *chooser);
typedef gboolean (*XX_gtk_file_chooser_get_select_multiple)(GtkFileChooser *);
static XX_gtk_file_chooser_get_select_multiple fl_gtk_file_chooser_get_select_multiple = NULL;

// void gtk_widget_hide(GtkWidget *widget);
typedef void (*XX_gtk_widget_hide)(GtkWidget *);
static XX_gtk_widget_hide fl_gtk_widget_hide = NULL;

// gchar * gtk_file_chooser_get_filename(GtkFileChooser *chooser);
typedef gchar* (*XX_gtk_file_chooser_get_filename)(GtkFileChooser *);
static XX_gtk_file_chooser_get_filename fl_gtk_file_chooser_get_filename = NULL;

// GSList * gtk_file_chooser_get_filenames(GtkFileChooser *chooser);
typedef GSList* (*XX_gtk_file_chooser_get_filenames)(GtkFileChooser *chooser);
static XX_gtk_file_chooser_get_filenames fl_gtk_file_chooser_get_filenames = NULL;

// gboolean gtk_main_iteration(void);
typedef gboolean (*XX_gtk_main_iteration)(void);
static XX_gtk_main_iteration fl_gtk_main_iteration = NULL;

// gboolean gtk_events_pending(void);
typedef gboolean (*XX_gtk_events_pending)(void);
static XX_gtk_events_pending fl_gtk_events_pending = NULL;

// GtkWidget * gtk_file_chooser_dialog_new(const gchar *title, GtkWindow *parent, GtkFileChooserAction action, const gchar *first_button_text, ...);
typedef GtkWidget* (*XX_gtk_file_chooser_dialog_new)(const gchar *, GtkWindow *, GtkFileChooserAction, const gchar *, ...);
static XX_gtk_file_chooser_dialog_new fl_gtk_file_chooser_dialog_new = NULL;

// void gtk_file_chooser_add_filter(GtkFileChooser*, GtkFileFilter*);
typedef void (*XX_gtk_file_chooser_add_filter)(GtkFileChooser*, GtkFileFilter*);
static XX_gtk_file_chooser_add_filter fl_gtk_file_chooser_add_filter = NULL;

// GtkFileFilter* gtk_file_chooser_get_filter(GtkFileChooser*);
typedef GtkFileFilter* (*XX_gtk_file_chooser_get_filter)(GtkFileChooser*);
static XX_gtk_file_chooser_get_filter fl_gtk_file_chooser_get_filter = NULL;

// void gtk_file_chooser_set_filter(GtkFileChooser*, GtkFileFilter*);
typedef void (*XX_gtk_file_chooser_set_filter)(GtkFileChooser*, GtkFileFilter*);
static XX_gtk_file_chooser_set_filter fl_gtk_file_chooser_set_filter = NULL;

// GtkFileFilter * gtk_file_filter_new();
typedef GtkFileFilter* (*XX_gtk_file_filter_new)(void);
static XX_gtk_file_filter_new fl_gtk_file_filter_new = NULL;

// void gtk_file_filter_add_pattern(GtkFileFilter*, const gchar*);
typedef void (*XX_gtk_file_filter_add_pattern)(GtkFileFilter*, const gchar*);
static XX_gtk_file_filter_add_pattern fl_gtk_file_filter_add_pattern = NULL;

// void gtk_file_filter_add_custom(GtkFileFilter *filter, GtkFileFilterFlags needed,
//  GtkFileFilterFunc func, gpointer data, GDestroyNotify notify);
typedef void (*XX_gtk_file_filter_add_custom)(GtkFileFilter *filter, GtkFileFilterFlags needed,
					      GtkFileFilterFunc func, gpointer data, 
					      GDestroyNotify notify);
static XX_gtk_file_filter_add_custom fl_gtk_file_filter_add_custom = NULL;

// void gtk_file_filter_set_name(GtkFileFilter*, const gchar*);
typedef void (*XX_gtk_file_filter_set_name)(GtkFileFilter*, const gchar*);
static XX_gtk_file_filter_set_name fl_gtk_file_filter_set_name = NULL;

// const gchar* gtk_file_filter_get_name(GtkFileFilter*);
typedef const gchar* (*XX_gtk_file_filter_get_name)(GtkFileFilter*);
static XX_gtk_file_filter_get_name fl_gtk_file_filter_get_name = NULL;

// void gtk_file_chooser_set_extra_widget(GtkFileChooser *, GtkWidget *);
typedef void (*XX_gtk_file_chooser_set_extra_widget)(GtkFileChooser *, GtkWidget *);
static XX_gtk_file_chooser_set_extra_widget fl_gtk_file_chooser_set_extra_widget = NULL;

// void gtk_widget_show_now(GtkWidget *);
typedef void (*XX_gtk_widget_show_now)(GtkWidget *);
static XX_gtk_widget_show_now fl_gtk_widget_show_now = NULL;

// GdkWindow* gtk_widget_get_window(GtkWidget *);
typedef GdkWindow* (*XX_gtk_widget_get_window)(GtkWidget *);
static XX_gtk_widget_get_window fl_gtk_widget_get_window = NULL;

// Window gdk_x11_drawable_get_xid(GdkWindow *);
typedef Window (*XX_gdk_x11_drawable_get_xid)(GdkWindow *);
static XX_gdk_x11_drawable_get_xid fl_gdk_x11_drawable_get_xid = NULL;

// GtkWidget *gtk_check_button_new_with_label(const gchar *);
typedef GtkWidget* (*XX_gtk_check_button_new_with_label)(const gchar *);
static XX_gtk_check_button_new_with_label fl_gtk_check_button_new_with_label = NULL;

// gulong g_signal_connect_data(gpointer, const gchar *, GCallback, gpointer, GClosureNotify, GConnectFlags);
typedef gulong (*XX_g_signal_connect_data)(gpointer, const gchar *, GCallback, gpointer, GClosureNotify, GConnectFlags);
static XX_g_signal_connect_data fl_g_signal_connect_data = NULL;

// gboolean gtk_toggle_button_get_active(GtkToggleButton *);
typedef gboolean (*XX_gtk_toggle_button_get_active)(GtkToggleButton*);
static XX_gtk_toggle_button_get_active fl_gtk_toggle_button_get_active = NULL;

// void gtk_file_chooser_set_show_hidden(GtkFileChooser *, gboolean);
typedef void (*XX_gtk_file_chooser_set_show_hidden)(GtkFileChooser *, gboolean);
static XX_gtk_file_chooser_set_show_hidden fl_gtk_file_chooser_set_show_hidden = NULL;

// gboolean gtk_file_chooser_get_show_hidden(GtkFileChooser *);
typedef gboolean (*XX_gtk_file_chooser_get_show_hidden)(GtkFileChooser *);
static XX_gtk_file_chooser_get_show_hidden fl_gtk_file_chooser_get_show_hidden = NULL;

// void gtk_toggle_button_set_active(GtkToggleButton *, gboolean);
typedef void (*XX_gtk_toggle_button_set_active)(GtkToggleButton *, gboolean);
static XX_gtk_toggle_button_set_active fl_gtk_toggle_button_set_active = NULL;


Fl_GTK_File_Chooser::Fl_GTK_File_Chooser(int val) : Fl_FLTK_File_Chooser(-1)
{
  gtkw_ptr   = NULL;    // used to hold a GtkWidget* 
  gtkw_slist = NULL;    // will hold the returned file names in a multi-selection...
  gtkw_count = 0;       // How many items were selected?
  gtkw_filename = NULL; // holds the last name we read back in a single file selection...
  gtkw_title = NULL;    // dialog title
  _btype = val;
  previous_filter = NULL;
}

Fl_GTK_File_Chooser::~Fl_GTK_File_Chooser()
{
  // Should free up resources taken for...
  if(gtkw_ptr) { 
    fl_gtk_widget_destroy (gtkw_ptr);
    gtkw_ptr = NULL;
  }
  if(gtkw_filename) {
    fl_g_free(gtkw_filename);
    gtkw_filename = NULL;
  }
  if(gtkw_slist) {
    GSList *iter = (GSList *)gtkw_slist;
    while(iter) {
      if(iter->data) fl_g_free(iter->data);
      iter = g_slist_next(iter);
    }
    fl_g_slist_free((GSList *)gtkw_slist);
    gtkw_slist = NULL;
  }
  gtkw_count = 0; // assume we have no files selected now
  gtkw_title = strfree(gtkw_title);
}

void Fl_GTK_File_Chooser::type(int val) {
  _btype = val;
}

int Fl_GTK_File_Chooser::count() const {
  return gtkw_count;
}

const char *Fl_GTK_File_Chooser::filename() const
{
  if(gtkw_ptr) {
    if(fl_gtk_file_chooser_get_select_multiple((GtkFileChooser *)gtkw_ptr) == FALSE) {
      return gtkw_filename;
    }
    else {
      GSList *iter = (GSList *)gtkw_slist;
      char *nm = (char *)iter->data;
      return nm;
    }
  }
  return("");
}

const char *Fl_GTK_File_Chooser::filename(int i) const
{
  if(fl_gtk_file_chooser_get_select_multiple((GtkFileChooser *)gtkw_ptr) == FALSE) {
    return gtkw_filename;
  }
  else {
    if ((unsigned)i < gtkw_count) {
      GSList *iter = (GSList *)gtkw_slist;
      char *nm = (char *)fl_g_slist_nth_data(iter, i);
      return nm;
    }
  }
  return("");
}

void Fl_GTK_File_Chooser::title(const char *val)
{
  strfree(gtkw_title);
  gtkw_title = strnew(val);
}

const char* Fl_GTK_File_Chooser::title() const
{
  return gtkw_title;
}

/* changes the extension of the outfile in the chooser according to newly selected filter */
void Fl_GTK_File_Chooser::changed_output_type(const char *filter)
{
  if ( !(options()&Fl_Native_File_Chooser::USE_FILTER_EXT) ) return;
  if (strchr(filter, '(') || strchr(filter, '{') || strchr(filter+1, '*') || strncmp(filter, "*.", 2)) return;
  const char *p = fl_gtk_file_chooser_get_filename((GtkFileChooser*)gtkw_ptr);
  if (!p) return;
  p = fl_filename_name(p);
  const char *q = strrchr(p, '.');
  if (!q) q = p + strlen(p);
  char *r = new char[strlen(p) + strlen(filter)];
  strcpy(r, p);
  strcpy(r + (q - p), filter + 1);
  fl_gtk_file_chooser_set_current_name((GtkFileChooser*)gtkw_ptr, r);
  delete[] r;
}

/* Filters files before display in chooser. 
 Also used to detect when the filter just changed */
gboolean Fl_GTK_File_Chooser::custom_gtk_filter_function(const GtkFileFilterInfo *info, Fl_GTK_File_Chooser::pair* p)
{
  if (p->running->previous_filter != p->filter) {
    p->running->changed_output_type(p->filter);
    p->running->previous_filter = p->filter;
    }
  return (gboolean)fl_filename_match(fl_filename_name(info->filename), p->filter);
}

void Fl_GTK_File_Chooser::free_pair(Fl_GTK_File_Chooser::pair *p)
{
  delete p;
}

static void hidden_files_cb(GtkToggleButton *togglebutton, gpointer user_data)
{
  gboolean state = fl_gtk_toggle_button_get_active(togglebutton);
  fl_gtk_file_chooser_set_show_hidden((GtkFileChooser*)user_data, state);
}

int Fl_GTK_File_Chooser::show()
{
  // The point here is that after running a GTK dialog, the calling program's current locale is modified.
  // To avoid that, we memorize the calling program's current locale, and the locale as modified
  // by GTK after the first dialog use. We restore the calling program's current locale 
  // before returning, and we set the locale as modified by GTK before subsequent GTK dialog uses.
  static bool first = true;
  char *p;
  char *before = NULL;
  static char *gtk_wants = NULL;
  // record in before the calling program's current locale
  p = setlocale(LC_ALL, NULL);
  if (p) before = strdup(p);
  if (gtk_wants) { // set the locale as GTK 'wants it'
    setlocale(LC_ALL, gtk_wants);
  }
  int retval = fl_gtk_chooser_wrapper(); // may change the locale
  if (first) {
    first = false;
    // record in gtk_wants the locale as modified by the GTK dialog
    p = setlocale(LC_ALL, NULL);
    if (p) gtk_wants = strdup(p);
  }
  if (before) {
    setlocale(LC_ALL, before); // restore calling program's current locale
    free(before);
    }
  return retval;
}

static char *extract_dir_from_path(const char *path)
{
  static char *dir = NULL;
  if (fl_filename_isdir(path)) {
    return (char*)path;
  }
  if (*path != '/') return NULL;
  if (dir) free(dir);
  dir = strdup(path);
  do {
    char *p = strrchr(dir, '/');
    if (p == dir) p++;
    *p = 0;
    }
  while (!fl_filename_isdir(dir));
  return dir;
}

static void run_response_handler(GtkDialog *dialog, gint response_id, gpointer data)
{
  gint *ri = (gint *)data;
  *ri = response_id;
}


int Fl_GTK_File_Chooser::fl_gtk_chooser_wrapper()
{
  int result = 1;
  static int have_gtk_init = 0;
  char *p;
  
  if(!have_gtk_init) {
    have_gtk_init = -1;
    int ac = 0;
    fl_gtk_init_check(&ac, NULL);
  }
  
  if(gtkw_ptr) { // discard the previous dialog widget
    fl_gtk_widget_destroy (gtkw_ptr);
    gtkw_ptr = NULL;
  }
  
  // set the dialog action type
  GtkFileChooserAction gtw_action_type;
  switch (_btype) {
    case Fl_Native_File_Chooser::BROWSE_DIRECTORY:
    case Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY:
      gtw_action_type = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER;
      break;
      
    case Fl_Native_File_Chooser::BROWSE_SAVE_FILE:
      gtw_action_type = GTK_FILE_CHOOSER_ACTION_SAVE;
      break;
      
    case Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY:
      gtw_action_type = GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER;
      break;
      
    case Fl_Native_File_Chooser::BROWSE_MULTI_FILE:
    case Fl_Native_File_Chooser::BROWSE_FILE:
    default:
      gtw_action_type = GTK_FILE_CHOOSER_ACTION_OPEN;
      break;
  }
  // create a new dialog
  gtkw_ptr = fl_gtk_file_chooser_dialog_new (gtkw_title,
					     NULL, /* parent_window */
					     gtw_action_type,
					     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
					     gtw_action_type == GTK_FILE_CHOOSER_ACTION_SAVE || gtw_action_type == GTK_FILE_CHOOSER_ACTION_CREATE_FOLDER ? 
					     GTK_STOCK_SAVE : GTK_STOCK_OPEN, 
					     GTK_RESPONSE_ACCEPT,
					     NULL);
  // did we create a valid dialog widget?
  if(!gtkw_ptr) {
    // fail
    return -1;
  }
  
  // set the dialog properties
  switch (_btype) {
    case Fl_Native_File_Chooser::BROWSE_MULTI_DIRECTORY:
    case Fl_Native_File_Chooser::BROWSE_MULTI_FILE:
      fl_gtk_file_chooser_set_select_multiple((GtkFileChooser *)gtkw_ptr, TRUE);
      break;
      
    case Fl_Native_File_Chooser::BROWSE_SAVE_FILE:
      if (_preset_file)fl_gtk_file_chooser_set_current_name ((GtkFileChooser *)gtkw_ptr, fl_filename_name(_preset_file));
      /* FALLTHROUGH */
    case Fl_Native_File_Chooser::BROWSE_SAVE_DIRECTORY:
      fl_gtk_file_chooser_set_create_folders((GtkFileChooser *)gtkw_ptr, TRUE);
      fl_gtk_file_chooser_set_do_overwrite_confirmation ((GtkFileChooser *)gtkw_ptr, (_options & Fl_Native_File_Chooser::SAVEAS_CONFIRM)?TRUE:FALSE);
      break;
      
    case Fl_Native_File_Chooser::BROWSE_DIRECTORY:
    case Fl_Native_File_Chooser::BROWSE_FILE:
    default:
      break;
  }
  
  if (_directory && _directory[0]) {
    p = extract_dir_from_path(_directory);
    if (p) fl_gtk_file_chooser_set_current_folder((GtkFileChooser *)gtkw_ptr, p);
  }
  else if (_preset_file) {
    p = extract_dir_from_path(_preset_file);
    if (p) fl_gtk_file_chooser_set_current_folder((GtkFileChooser *)gtkw_ptr, p);
  }
  
  GtkFileFilter **filter_tab = NULL;
  if (_parsedfilt) {
    filter_tab = new GtkFileFilter*[_nfilters];
    char *filter = strdup(_parsedfilt);
    p = strtok(filter, "\t");
    int count = 0;
    while (p) {
      filter_tab[count] = fl_gtk_file_filter_new();
      fl_gtk_file_filter_set_name(filter_tab[count], p);
      p = strchr(p, '(') + 1;
      char *q = strchr(p, ')'); *q = 0;
      fl_gtk_file_filter_add_custom(filter_tab[count], 
				    GTK_FILE_FILTER_FILENAME, 
				    (GtkFileFilterFunc)Fl_GTK_File_Chooser::custom_gtk_filter_function, 
				    new Fl_GTK_File_Chooser::pair(this, p), 
				    (GDestroyNotify)Fl_GTK_File_Chooser::free_pair);
      fl_gtk_file_chooser_add_filter((GtkFileChooser *)gtkw_ptr, filter_tab[count]);
      p = strtok(NULL, "\t");
      count++;
    }
    free(filter);
    fl_gtk_file_chooser_set_filter((GtkFileChooser *)gtkw_ptr, filter_tab[_filtvalue < _nfilters?_filtvalue:0]);
    previous_filter = NULL;
    if (gtw_action_type == GTK_FILE_CHOOSER_ACTION_OPEN) {
      GtkFileFilter* gfilter = fl_gtk_file_filter_new();
      fl_gtk_file_filter_set_name(gfilter, Fl_File_Chooser::all_files_label);
      fl_gtk_file_filter_add_pattern(gfilter, "*");
      fl_gtk_file_chooser_add_filter((GtkFileChooser *)gtkw_ptr, gfilter);
    }
  }
  
  GtkWidget *toggle = fl_gtk_check_button_new_with_label(Fl_File_Chooser::hidden_label);
  fl_gtk_file_chooser_set_extra_widget((GtkFileChooser *)gtkw_ptr, toggle);
  fl_g_signal_connect_data(toggle, "toggled", G_CALLBACK(hidden_files_cb), gtkw_ptr, NULL, (GConnectFlags) 0);
  Fl_Window* firstw = Fl::first_window();
  fl_gtk_widget_show_now(gtkw_ptr); // map the GTK window on screen
  if (firstw) {
    GdkWindow* gdkw = fl_gtk_widget_get_window(gtkw_ptr);
    Window xw = fl_gdk_x11_drawable_get_xid(gdkw); // get the X11 ref of the GTK window
    XSetTransientForHint(fl_display, xw, fl_xid(firstw)); // set the GTK window transient for the last FLTK win
    }
  gboolean state = fl_gtk_file_chooser_get_show_hidden((GtkFileChooser *)gtkw_ptr);
  fl_gtk_toggle_button_set_active((GtkToggleButton *)toggle, state);
  
  gint response_id = GTK_RESPONSE_NONE;
  fl_g_signal_connect_data(gtkw_ptr, "response", G_CALLBACK(run_response_handler), &response_id, NULL, (GConnectFlags) 0);
  while (response_id == GTK_RESPONSE_NONE) { // loop that shows the GTK dialog window
    fl_gtk_main_iteration(); // one iteration of the GTK event loop
    while (XEventsQueued(fl_display, QueuedAfterReading)) { // emulate modal dialog
      XEvent xevent;
      XNextEvent(fl_display, &xevent);
      Window xid = xevent.xany.window;
      if (xevent.type == ConfigureNotify) xid = xevent.xmaprequest.window;
      if (!fl_find(xid)) continue; // skip events to non-FLTK windows
      // process Expose and ConfigureNotify events
      if ( xevent.type == Expose || xevent.type == ConfigureNotify ) fl_handle(xevent); 
    }
    Fl::flush(); // do the drawings needed after Expose events
  } 
  
  if (response_id == GTK_RESPONSE_ACCEPT) {
    if (_parsedfilt) {
      GtkFileFilter *gfilter = fl_gtk_file_chooser_get_filter((GtkFileChooser *)gtkw_ptr);
      for (_filtvalue = 0; _filtvalue < _nfilters; _filtvalue++) {
	if (filter_tab[_filtvalue] == gfilter) break;
      }
    }
    
    // discard any filenames or lists from previous calls
    if(gtkw_filename) {
      fl_g_free(gtkw_filename);
      gtkw_filename = NULL;
    }
    if(gtkw_slist) {
      GSList *iter = (GSList *)gtkw_slist;
      while(iter) {
        if(iter->data) fl_g_free(iter->data);
        iter = g_slist_next(iter);
      }
      fl_g_slist_free((GSList *)gtkw_slist);
      gtkw_slist = NULL;
    }
    gtkw_count = 0; // assume we have no files selected now
    
    if(fl_gtk_file_chooser_get_select_multiple((GtkFileChooser *)gtkw_ptr) == FALSE) {
      gtkw_filename = fl_gtk_file_chooser_get_filename ((GtkFileChooser *)gtkw_ptr);
      if (gtkw_filename) {
        gtkw_count = 1;
        result = 0;
        //printf("single: %s\n", gtkw_filename);
      }
    }
    else {
      gtkw_slist = fl_gtk_file_chooser_get_filenames((GtkFileChooser *)gtkw_ptr);
      gtkw_count = fl_g_slist_length((GSList *)gtkw_slist);
      if(gtkw_count) result = 0;
      
      //      puts("multiple");
      //      GSList *iter = (GSList *)gtkw_slist;
      //      printf ("Selected %d files\n", gtkw_count);
      //      while(iter) {
      //        char *nm = (char *)iter->data;
      //        printf("%s\n", nm);
      //        iter = g_slist_next(iter);
      //      }
    }
  }
  delete[] filter_tab;
  if ( response_id == GTK_RESPONSE_DELETE_EVENT) gtkw_ptr = NULL;
  else fl_gtk_widget_hide (gtkw_ptr);
  
  // I think this is analogus to doing an Fl::check() - we need this here to make sure
  // the GtkFileChooserDialog is removed from the display correctly
  while (fl_gtk_events_pending ()) fl_gtk_main_iteration (); 
  
  return result;
} // fl_gtk_chooser_wrapper

#if HAVE_DLSYM && HAVE_DLFCN_H
// macro to help with the symbol loading boilerplate...
#  define GET_SYM(SSS, LLL) \
dlerror();    /* Clear any existing error */  \
fl_##SSS = (XX_##SSS)dlsym(LLL, #SSS);        \
if ((pc_dl_error = dlerror()) != NULL)  {     \
fprintf(stderr, "%s\n", pc_dl_error);       \
did_find_GTK_libs = 0;                      \
return; }

static void* fl_dlopen(const char *filename1, const char *filename2)
{
  void *ptr = dlopen(filename1, RTLD_LAZY | RTLD_GLOBAL);
  if (!ptr) ptr = dlopen(filename2, RTLD_LAZY | RTLD_GLOBAL);
  return ptr;
}
#endif

/* 
 * Use dlopen to see if we can load the gtk dynamic libraries that
 * will allow us to create a GtkFileChooserDialog() on the fly,
 * without linking to the GTK libs at compile time.
 */
void Fl_GTK_File_Chooser::probe_for_GTK_libs(void) {
#if HAVE_DLSYM && HAVE_DLFCN_H
  void *ptr_glib    = NULL;
  void *ptr_gtk     = NULL;
  
#   ifdef __APPLE_CC__ // allows testing on Darwin + X11
  ptr_glib    = dlopen("/sw/lib/libglib-2.0.dylib", RTLD_LAZY | RTLD_GLOBAL);
#   else
  ptr_glib    = fl_dlopen("libglib-2.0.so", "libglib-2.0.so.0");
#   endif
  // Try first with GTK2
#   ifdef __APPLE_CC__ // allows testing on Darwin + X11
  ptr_gtk     = dlopen("/sw/lib/libgtk-x11-2.0.dylib", RTLD_LAZY | RTLD_GLOBAL);
#else
  ptr_gtk     = fl_dlopen("libgtk-x11-2.0.so", "libgtk-x11-2.0.so.0");
#endif
  if (ptr_gtk && ptr_glib) {
#ifdef DEBUG
    puts("selected GTK-2\n");
#endif
  }
  else {// Try then with GTK3
    ptr_gtk     = fl_dlopen("libgtk-3.so", "libgtk-3.so.0");
#ifdef DEBUG
    if (ptr_gtk && ptr_glib) {
      puts("selected GTK-3\n");
    }
#endif
  }
  
  if((!ptr_glib) || (!ptr_gtk)) {
#ifdef DEBUG
    puts("Failure to load libglib or libgtk");
#endif
    did_find_GTK_libs = 0;
    return;
  }
  
  char *pc_dl_error; // used to report errors by the GET_SYM macro...
  // items we need from GLib
  GET_SYM(g_free, ptr_glib);
  GET_SYM(g_slist_nth_data, ptr_glib);
  GET_SYM(g_slist_length, ptr_glib);
  GET_SYM(g_slist_free, ptr_glib);
  // items we need from GTK
  GET_SYM(gtk_init_check, ptr_gtk);
  GET_SYM(gtk_widget_destroy, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_select_multiple, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_do_overwrite_confirmation, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_current_name, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_current_folder, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_create_folders, ptr_gtk);
  GET_SYM(gtk_file_chooser_get_select_multiple, ptr_gtk);
  GET_SYM(gtk_widget_hide, ptr_gtk);
  GET_SYM(gtk_file_chooser_get_filename, ptr_gtk);
  GET_SYM(gtk_file_chooser_get_filenames, ptr_gtk);
  GET_SYM(gtk_main_iteration, ptr_gtk);
  GET_SYM(gtk_events_pending, ptr_gtk);
  GET_SYM(gtk_file_chooser_dialog_new, ptr_gtk);
  GET_SYM(gtk_file_chooser_add_filter, ptr_gtk);
  GET_SYM(gtk_file_chooser_get_filter, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_filter, ptr_gtk);
  GET_SYM(gtk_file_filter_new, ptr_gtk);
  GET_SYM(gtk_file_filter_add_pattern, ptr_gtk);
  GET_SYM(gtk_file_filter_add_custom, ptr_gtk);
  GET_SYM(gtk_file_filter_set_name, ptr_gtk);
  GET_SYM(gtk_file_filter_get_name, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_extra_widget, ptr_gtk);
  GET_SYM(gtk_widget_show_now, ptr_gtk);
  GET_SYM(gtk_widget_get_window, ptr_gtk);
  GET_SYM(gdk_x11_drawable_get_xid, ptr_gtk);
  GET_SYM(gtk_check_button_new_with_label, ptr_gtk);
  GET_SYM(g_signal_connect_data, ptr_gtk);
  GET_SYM(gtk_toggle_button_get_active, ptr_gtk);
  GET_SYM(gtk_file_chooser_set_show_hidden, ptr_gtk);
  GET_SYM(gtk_file_chooser_get_show_hidden, ptr_gtk);
  GET_SYM(gtk_toggle_button_set_active, ptr_gtk);
  
  did_find_GTK_libs = 1;
#endif
} // probe_for_GTK_libs

//
// End of "$Id$".
//