1 /*****************************************************************
2  * gmerlin - a general purpose multimedia framework and applications
3  *
4  * Copyright (c) 2001 - 2011 Members of the Gmerlin project
5  * gmerlin-general@lists.sourceforge.net
6  * http://gmerlin.sourceforge.net
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  * *****************************************************************/
21 
22 
23 #include <config.h>
24 
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 
29 #include <string.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <gtk/gtk.h>
33 #include <gmerlin/pluginregistry.h>
34 #include <gui_gtk/fileselect.h>
35 #include <gui_gtk/question.h>
36 #include <gui_gtk/gtkutils.h>
37 #include <gui_gtk/plugin.h>
38 
39 #include <gmerlin/utils.h>
40 
41 struct bg_gtk_filesel_s
42   {
43   GtkWidget * filesel;
44   GtkWidget * plugin_menu;
45   bg_gtk_plugin_menu_t * plugins;
46   void (*add_files)(char ** files, const char * plugin,
47                     int prefer_edl, void * data);
48 
49   void (*add_dir)(char * dir, int recursive, int subdirs_as_subalbums,
50                   int watch, const char * plugin, int prefer_edl,
51                   void * data);
52 
53   void (*close_notify)(bg_gtk_filesel_t * f, void * data);
54 
55   void * callback_data;
56 
57   char * cwd;
58   int is_modal;
59 
60   int unsensitive;
61 
62   GtkWidget * recursive;
63   GtkWidget * subdirs_as_subalbums;
64   GtkWidget * watch;
65   GtkWidget * prefer_edl;
66   };
67 
add_files(bg_gtk_filesel_t * f)68 static void add_files(bg_gtk_filesel_t * f)
69   {
70   char ** filenames;
71   GSList * file_list;
72   GSList * tmp;
73 
74   const char * plugin = NULL;
75   int num, i;
76 
77   if(f->plugins)
78     plugin = bg_gtk_plugin_menu_get_plugin(f->plugins);
79 
80   file_list =
81     gtk_file_chooser_get_filenames(GTK_FILE_CHOOSER(f->filesel));
82 
83   num = g_slist_length(file_list);
84 
85   filenames = calloc(num+1, sizeof(*filenames));
86 
87   tmp = file_list;
88 
89   for(i = 0; i < num; i++)
90     {
91     filenames[i] = (char*)tmp->data;
92     tmp = tmp->next;
93     }
94 
95   f->unsensitive = 1;
96   gtk_widget_set_sensitive(f->filesel, 0);
97   f->add_files(filenames, plugin,
98                gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(f->prefer_edl)),
99                f->callback_data);
100   gtk_widget_set_sensitive(f->filesel, 1);
101   f->unsensitive = 0;
102 
103   g_slist_foreach(file_list, (GFunc)g_free, NULL);
104   g_slist_free(file_list);
105 
106   free(filenames);
107   }
108 
add_dir(bg_gtk_filesel_t * f)109 static void add_dir(bg_gtk_filesel_t * f)
110   {
111   const char * plugin = NULL;
112   char * tmp =
113     gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(f->filesel));
114 
115   if(f->plugins)
116     plugin = bg_gtk_plugin_menu_get_plugin(f->plugins);
117 
118   f->unsensitive = 1;
119   gtk_widget_set_sensitive(f->filesel, 0);
120   f->add_dir(tmp,
121              gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(f->recursive)),
122              gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(f->subdirs_as_subalbums)),
123              gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(f->watch)),
124              plugin,
125              gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(f->prefer_edl)),
126              f->callback_data);
127   gtk_widget_set_sensitive(f->filesel, 1);
128   f->unsensitive = 0;
129   g_free(tmp);
130   }
131 
132 static void
fileselect_callback(GtkWidget * chooser,gint response_id,gpointer data)133 fileselect_callback(GtkWidget *chooser,
134                     gint       response_id,
135                     gpointer data)
136   {
137   bg_gtk_filesel_t * f;
138   f = (bg_gtk_filesel_t *)data;
139   if(f->unsensitive)
140     return;
141 
142   if(response_id == GTK_RESPONSE_OK)
143     {
144     if(f->add_files)
145       add_files(f);
146     else if(f->add_dir)
147       add_dir(f);
148     }
149   else
150     {
151     gtk_widget_hide(f->filesel);
152     if(f->is_modal)
153       gtk_main_quit();
154 
155     if(f->close_notify)
156       f->close_notify(f, f->callback_data);
157     bg_gtk_filesel_destroy(f);
158     }
159   }
160 
161 #if 0
162 static gboolean delete_callback(GtkWidget * w, GdkEventAny * event,
163                                 gpointer data)
164   {
165   fileselect_callback(w, GTK_RESPONSE_CANCEL, data);
166   return TRUE;
167   }
168 
169 static gboolean destroy_callback(GtkWidget * w, GdkEvent * event,
170                                   gpointer data)
171   {
172   fileselect_callback(w, GTK_RESPONSE_CANCEL, data);
173   return TRUE;
174   }
175 #endif
176 
177 static bg_gtk_filesel_t *
filesel_create(const char * title,void (* add_files)(char ** files,const char * plugin,int prefer_edl,void * data),void (* add_dir)(char * dir,int recursive,int subdirs_as_subalbums,int watch,const char * plugin,int prefer_edl,void * data),void (* close_notify)(bg_gtk_filesel_t *,void * data),void * user_data,GtkWidget * parent_window,bg_plugin_registry_t * plugin_reg,int type_mask,int flag_mask)178 filesel_create(const char * title,
179                void (*add_files)(char ** files, const char * plugin,
180                                  int prefer_edl,
181                                  void * data),
182                void (*add_dir)(char * dir, int recursive,
183                                int subdirs_as_subalbums,
184                                int watch,
185                                const char * plugin,
186                                int prefer_edl,
187                                void * data),
188                void (*close_notify)(bg_gtk_filesel_t *,
189                                     void * data),
190                void * user_data,
191                GtkWidget * parent_window, bg_plugin_registry_t * plugin_reg,
192                int type_mask, int flag_mask)
193   {
194   bg_gtk_filesel_t * ret;
195 
196   GtkWidget * extra = NULL;
197 
198   ret = calloc(1, sizeof(*ret));
199 
200   parent_window = bg_gtk_get_toplevel(parent_window);
201 
202   /* Create fileselection */
203 
204   if(add_files)
205     {
206     ret->filesel =
207       gtk_file_chooser_dialog_new(title,
208                                   GTK_WINDOW(parent_window),
209                                   GTK_FILE_CHOOSER_ACTION_OPEN,
210                                   GTK_STOCK_CLOSE,
211                                   GTK_RESPONSE_CANCEL,
212                                   GTK_STOCK_ADD, GTK_RESPONSE_OK,
213                                   NULL);
214     gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(ret->filesel),
215                                          TRUE);
216     }
217   else if(add_dir)
218     {
219     ret->filesel =
220       gtk_file_chooser_dialog_new(title,
221                                   GTK_WINDOW(parent_window),
222                                   GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
223                                   GTK_STOCK_CLOSE,
224                                   GTK_RESPONSE_CANCEL,
225                                   GTK_STOCK_ADD, GTK_RESPONSE_OK,
226                                   NULL);
227 
228 
229     extra = gtk_vbox_new(FALSE, 5);
230 
231     ret->recursive =
232       gtk_check_button_new_with_label(TR("Recursive"));
233     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ret->recursive), 1);
234 
235     gtk_widget_show(ret->recursive);
236     bg_gtk_box_pack_start_defaults(GTK_BOX(extra),
237                                 ret->recursive);
238 
239     ret->subdirs_as_subalbums =
240       gtk_check_button_new_with_label(TR("Add subdirectories as subalbums"));
241     gtk_widget_show(ret->subdirs_as_subalbums);
242     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(ret->subdirs_as_subalbums), 1);
243 
244     ret->watch =
245       gtk_check_button_new_with_label(TR("Watch directories"));
246     gtk_widget_show(ret->watch);
247 
248     bg_gtk_box_pack_start_defaults(GTK_BOX(extra),
249                                 ret->subdirs_as_subalbums);
250     bg_gtk_box_pack_start_defaults(GTK_BOX(extra),
251                                 ret->watch);
252     }
253 
254   gtk_window_set_default_size(GTK_WINDOW(ret->filesel), 400, 400);
255 
256   /* Create plugin menu */
257 
258   if(plugin_reg)
259     {
260     if(!extra)
261       extra = gtk_vbox_new(FALSE, 5);
262 
263     ret->plugins = bg_gtk_plugin_menu_create(1, plugin_reg, type_mask,
264                                              flag_mask);
265 
266     bg_gtk_box_pack_start_defaults(GTK_BOX(extra),
267                                 bg_gtk_plugin_menu_get_widget(ret->plugins));
268     ret->prefer_edl =
269       gtk_check_button_new_with_label(TR("Prefer EDL"));
270     gtk_widget_show(ret->prefer_edl);
271 
272     bg_gtk_box_pack_start_defaults(GTK_BOX(extra),
273                                    ret->prefer_edl);
274 
275     }
276 
277   if(extra)
278     {
279     gtk_widget_show(extra);
280     gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(ret->filesel), extra);
281     }
282 
283   /* Set callbacks */
284 #if 0
285   g_signal_connect(G_OBJECT(ret->filesel), "delete_event",
286                    G_CALLBACK(delete_callback), ret);
287 #endif
288   g_signal_connect(ret->filesel, "response",
289                    G_CALLBACK(fileselect_callback),
290                    (gpointer)ret);
291 
292   ret->add_files     = add_files;
293   ret->add_dir       = add_dir;
294   ret->close_notify  = close_notify;
295   ret->callback_data = user_data;
296 
297   return ret;
298   }
299 
300 bg_gtk_filesel_t *
bg_gtk_filesel_create(const char * title,void (* add_file)(char ** files,const char * plugin,int prefer_edl,void * data),void (* close_notify)(bg_gtk_filesel_t *,void * data),void * user_data,GtkWidget * parent_window,bg_plugin_registry_t * plugin_reg,int type_mask,int flag_mask)301 bg_gtk_filesel_create(const char * title,
302                       void (*add_file)(char ** files, const char * plugin,
303                                        int prefer_edl,
304                                        void * data),
305                       void (*close_notify)(bg_gtk_filesel_t *,
306                                            void * data),
307                       void * user_data,
308                       GtkWidget * parent_window,
309                       bg_plugin_registry_t * plugin_reg,
310                       int type_mask, int flag_mask)
311   {
312   return filesel_create(title,
313                         add_file,
314                         NULL,
315                         close_notify,
316                         user_data,
317                         parent_window, plugin_reg, type_mask, flag_mask);
318   }
319 
320 bg_gtk_filesel_t *
bg_gtk_dirsel_create(const char * title,void (* add_dir)(char * dir,int recursive,int subdirs_as_subalbums,int watch,const char * plugin,int prefer_edl,void * data),void (* close_notify)(bg_gtk_filesel_t *,void * data),void * user_data,GtkWidget * parent_window,bg_plugin_registry_t * plugin_reg,int type_mask,int flag_mask)321 bg_gtk_dirsel_create(const char * title,
322                      void (*add_dir)(char * dir, int recursive,
323                                      int subdirs_as_subalbums,
324                                      int watch,
325                                      const char * plugin,
326                                      int prefer_edl,
327                                      void * data),
328                      void (*close_notify)(bg_gtk_filesel_t *,
329                                           void * data),
330                      void * user_data,
331                      GtkWidget * parent_window,
332                      bg_plugin_registry_t * plugin_reg,
333                      int type_mask, int flag_mask)
334   {
335   return filesel_create(title,
336                         NULL,
337                         add_dir,
338                         close_notify,
339                         user_data,
340                         parent_window, plugin_reg,
341                         type_mask, flag_mask);
342   }
343 
344 /* Destroy fileselector */
345 
bg_gtk_filesel_destroy(bg_gtk_filesel_t * filesel)346 void bg_gtk_filesel_destroy(bg_gtk_filesel_t * filesel)
347   {
348   if(filesel->cwd)
349     g_free(filesel->cwd);
350   //  g_object_unref(G_OBJECT(filesel));
351   free(filesel);
352   }
353 
354 /* Show the window */
355 
bg_gtk_filesel_run(bg_gtk_filesel_t * filesel,int modal)356 void bg_gtk_filesel_run(bg_gtk_filesel_t * filesel, int modal)
357   {
358   gtk_window_set_modal(GTK_WINDOW(filesel->filesel), modal);
359 
360   gtk_widget_show(filesel->filesel);
361   filesel->is_modal = modal;
362   if(modal)
363     gtk_main();
364 
365   }
366 
367 /* Get the current working directory */
368 
bg_gtk_filesel_set_directory(bg_gtk_filesel_t * filesel,const char * dir)369 void bg_gtk_filesel_set_directory(bg_gtk_filesel_t * filesel,
370                                   const char * dir)
371   {
372   gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(filesel->filesel), dir);
373   }
374 
bg_gtk_filesel_get_directory(bg_gtk_filesel_t * filesel)375 const char * bg_gtk_filesel_get_directory(bg_gtk_filesel_t * filesel)
376   {
377   if(filesel->cwd)
378     g_free(filesel->cwd);
379   filesel->cwd =
380     gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(filesel->filesel));
381   return filesel->cwd;
382   }
383 
384 /*
385  *  Create a temporary fileselector and ask
386  *  for a file to save something
387  *
388  *  Return value should be freed with free();
389  */
390 
391 typedef struct
392   {
393   GtkWidget * w;
394   int answer;
395   } filesel_write_struct;
396 
397 static void
write_callback(GtkWidget * chooser,gint response_id,gpointer data)398 write_callback(GtkWidget *chooser,
399                gint       response_id,
400                gpointer data)
401   {
402   filesel_write_struct * ws;
403 
404   ws = (filesel_write_struct*)data;
405 
406   if(response_id == GTK_RESPONSE_OK)
407     ws->answer = 1;
408 
409   gtk_widget_hide(ws->w);
410   gtk_main_quit();
411   }
412 
write_delete_callback(GtkWidget * w,GdkEventAny * evt,gpointer data)413 static gboolean write_delete_callback(GtkWidget * w,
414                                       GdkEventAny * evt,
415                                       gpointer data)
416   {
417   write_callback(w, GTK_RESPONSE_CANCEL, data);
418   return TRUE;
419   }
420 
bg_gtk_get_filename_write(const char * title,char ** directory,int ask_overwrite,GtkWidget * parent)421 char * bg_gtk_get_filename_write(const char * title,
422                                  char ** directory,
423                                  int ask_overwrite, GtkWidget * parent)
424   {
425   char * ret;
426   char * tmp_string;
427   filesel_write_struct f;
428 
429   ret = NULL;
430 
431   parent = bg_gtk_get_toplevel(parent);
432 
433   f.w =
434     gtk_file_chooser_dialog_new(title,
435                                 GTK_WINDOW(parent),
436                                 GTK_FILE_CHOOSER_ACTION_SAVE,
437                                 GTK_STOCK_CANCEL,
438                                 GTK_RESPONSE_CANCEL,
439                                 GTK_STOCK_OK, GTK_RESPONSE_OK,
440                                 NULL);
441   if(ask_overwrite)
442     gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(f.w),
443                                                    TRUE);
444   /* Set attributes */
445 
446   gtk_window_set_modal(GTK_WINDOW(f.w), 1);
447   f.answer = 0;
448 
449   /* Set callbacks */
450 
451   g_signal_connect(G_OBJECT(f.w), "delete_event",
452                    G_CALLBACK(write_delete_callback),
453                    (gpointer)(&f));
454   g_signal_connect(G_OBJECT(f.w), "response",
455                    G_CALLBACK(write_callback),
456                    (gpointer)(&f));
457 
458 
459   /* Set the current directory */
460 
461   if(directory && *directory)
462     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(f.w),
463                                         *directory);
464 
465   /* Run the widget */
466 
467   gtk_widget_show(f.w);
468   gtk_main();
469 
470   /* Fetch the answer */
471 
472   if(!f.answer)
473     {
474     gtk_widget_destroy(f.w);
475     return NULL;
476     }
477 
478   tmp_string = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(f.w));
479   ret = bg_strdup(NULL, tmp_string);
480   g_free(tmp_string);
481 
482   /* Update current directory */
483 
484   if(directory)
485     {
486     tmp_string = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(f.w));
487     *directory = bg_strdup(*directory, tmp_string);
488     g_free(tmp_string);
489     }
490 
491   return ret;
492   }
493 
bg_gtk_get_filename_read(const char * title,char ** directory,GtkWidget * parent)494 char * bg_gtk_get_filename_read(const char * title,
495                                 char ** directory, GtkWidget * parent)
496   {
497   char * ret;
498   char * tmp_string;
499   filesel_write_struct f;
500 
501   ret = NULL;
502 
503   parent = bg_gtk_get_toplevel(parent);
504 
505   f.w =
506     gtk_file_chooser_dialog_new(title,
507                                 GTK_WINDOW(parent),
508                                 GTK_FILE_CHOOSER_ACTION_OPEN,
509                                 GTK_STOCK_CANCEL,
510                                 GTK_RESPONSE_CANCEL,
511                                 GTK_STOCK_OK, GTK_RESPONSE_OK,
512                                 NULL);
513 
514   /* Set attributes */
515 
516   gtk_window_set_modal(GTK_WINDOW(f.w), 1);
517   f.answer = 0;
518 
519   /* Set callbacks */
520 
521   g_signal_connect(G_OBJECT(f.w), "delete_event",
522                    G_CALLBACK(write_delete_callback),
523                    (gpointer)(&f));
524   g_signal_connect(G_OBJECT(f.w), "response",
525                    G_CALLBACK(write_callback),
526                    (gpointer)(&f));
527 
528   /* Set the current directory */
529 
530   if(directory && *directory)
531     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(f.w),
532                                         *directory);
533 
534   /* Run the widget */
535 
536   gtk_widget_show(f.w);
537   gtk_main();
538 
539   /* Fetch the answer */
540 
541   if(!f.answer)
542     {
543     gtk_widget_destroy(f.w);
544     return NULL;
545     }
546 
547   tmp_string = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(f.w));
548   ret = bg_strdup(NULL, tmp_string);
549   g_free(tmp_string);
550 
551   /* Update current directory */
552 
553   if(directory)
554     {
555     tmp_string = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(f.w));
556     *directory = bg_strdup(*directory, tmp_string);
557     g_free(tmp_string);
558     }
559 
560   return ret;
561   }
562