1 /*
2  *                           0BSD
3  *
4  *                    BSD Zero Clause License
5  *
6  *  Copyright (c) 2019 Hermann Meyer
7  *
8  * Permission to use, copy, modify, and/or distribute this software for any
9  * purpose with or without fee is hereby granted.
10 
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13  * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  *
19  */
20 
21 
22 /*
23 #ifndef _GNU_SOURCE
24 #define _GNU_SOURCE 1
25 #endif
26 */
27 
28 #include <dirent.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <pwd.h>
35 
36 #include <libgen.h>
37 
38 #include "xfile-dialog.h"
39 #include "xmessage-dialog.h"
40 
41 static void combo_response(void *w_, void* user_data);
42 static void set_selected_file(FileDialog *file_dialog);
43 
draw_window(void * w_,void * user_data)44 static void draw_window(void *w_, void* user_data) {
45     Widget_t *w = (Widget_t*)w_;
46     XWindowAttributes attrs;
47     XGetWindowAttributes(w->app->dpy, (Window)w->widget, &attrs);
48     int width = attrs.width;
49     int height = attrs.height;
50     if (attrs.map_state != IsViewable) return;
51 
52     cairo_rectangle(w->crb,0,0,width,height);
53     set_pattern(w,&w->app->color_scheme->selected,&w->app->color_scheme->normal,BACKGROUND_);
54     cairo_fill (w->crb);
55 
56     //widget_set_scale(w);
57     use_fg_color_scheme(w, NORMAL_);
58     cairo_set_font_size (w->crb, 12.0);
59     cairo_move_to (w->crb, 20, 35);
60     cairo_show_text(w->crb, _("Base Directory"));
61     cairo_move_to (w->crb, 20, 85);
62     cairo_show_text(w->crb, _("Places"));
63     cairo_move_to (w->crb, 130, 85);
64     cairo_show_text(w->crb, _("Entries"));
65     cairo_move_to (w->crb, 20, 330-w->scale.scale_y);
66     cairo_show_text(w->crb, _("Load: "));
67     cairo_move_to (w->crb, 45, 360-w->scale.scale_y);
68     cairo_show_text(w->crb, _("Show hidden files"));
69     cairo_move_to (w->crb, 45, 390-w->scale.scale_y);
70     cairo_show_text(w->crb, _("List view"));
71     cairo_move_to (w->crb, 60, 330-w->scale.scale_y);
72     cairo_show_text(w->crb, w->label);
73     //widget_reset_scale(w);
74     if (w->image) {
75         cairo_set_source_surface (w->crb, w->image, 180, 332-w->scale.scale_y);
76         cairo_paint (w->crb);
77     }
78 }
79 
draw_fd_hslider(void * w_,void * user_data)80 static void draw_fd_hslider(void *w_, void* user_data) {
81     Widget_t *w = (Widget_t*)w_;
82 
83     int width = w->width-2;
84     int height = w->height-2;
85     float center = (float)height/2;
86 
87     float sliderstate = adj_get_state(w->adj_x);
88 
89     use_text_color_scheme(w, get_color_state(w));
90     cairo_move_to (w->crb, 0.0, center);
91     cairo_line_to(w->crb,width,center);
92     cairo_set_line_width(w->crb,center/10);
93     cairo_stroke(w->crb);
94 
95     use_bg_color_scheme(w, get_color_state(w));
96     cairo_rectangle(w->crb, (width-height)*sliderstate,0,height, height);
97     cairo_fill(w->crb);
98     cairo_new_path (w->crb);
99 
100     use_text_color_scheme(w, get_color_state(w));
101     cairo_set_line_width(w->crb,3);
102     cairo_move_to (w->crb,((width-height)*sliderstate)+center, 0.0);
103     cairo_line_to(w->crb,((width-height)*sliderstate)+center,height);
104     cairo_stroke(w->crb);
105     cairo_new_path (w->crb);
106 }
107 
button_quit_callback(void * w_,void * user_data)108 static void button_quit_callback(void *w_, void* user_data) {
109     Widget_t *w = (Widget_t*)w_;
110     FileDialog *file_dialog = (FileDialog *)w->parent_struct;
111 
112     if (w->flags & HAS_POINTER && !adj_get_value(w->adj)){
113         file_dialog->parent->func.dialog_callback(file_dialog->parent,NULL);
114         file_dialog->send_clear_func = false;
115         destroy_widget(file_dialog->w,file_dialog->w->app);
116     }
117 }
118 
set_files(FileDialog * file_dialog)119 static inline int set_files(FileDialog *file_dialog) {
120     if (file_dialog->list_view) {
121         listview_set_list(file_dialog->ft,file_dialog->fp->file_names , (int)file_dialog->fp->file_counter);
122     } else {
123         multi_listview_set_list(file_dialog->ft,file_dialog->fp->file_names , (int)file_dialog->fp->file_counter);
124     }
125     int ret = -1;
126     int i = 0;
127     for (; i<(int)file_dialog->fp->file_counter; i++) {
128         if(file_dialog->fp->selected_file && strcmp(file_dialog->fp->file_names[i],
129           basename(file_dialog->fp->selected_file))==0 )  ret = i;
130     }
131     return ret;
132 }
133 
set_dirs(FileDialog * file_dialog)134 static void set_dirs(FileDialog *file_dialog) {
135     int i = 0;
136     for (; i<(int)file_dialog->fp->dir_counter; i++) {
137         combobox_add_entry(file_dialog->ct,file_dialog->fp->dir_names[i]);
138     }
139 }
140 
file_released_b_callback(void * w_,void * button_,void * user_data)141 static void file_released_b_callback(void *w_, void *button_, void* user_data) {
142     Widget_t *w = (Widget_t*)w_;
143     FileDialog *file_dialog = (FileDialog *)w->parent_struct;
144     XButtonEvent *xbutton = (XButtonEvent*)button_;
145     if(xbutton->button == Button1) {
146         set_selected_file(file_dialog);
147         if(file_dialog->fp->selected_file) {
148             file_dialog->w->label = file_dialog->fp->selected_file;
149             expose_widget(file_dialog->w);
150         }
151     }
152 }
153 
reload_from_dir(FileDialog * file_dialog)154 static void reload_from_dir(FileDialog *file_dialog) {
155     assert(file_dialog->fp->path != NULL);
156     if (file_dialog->list_view) {
157         listview_remove_list(file_dialog->ft);
158     } else {
159         multi_listview_remove_list(file_dialog->ft);
160     }
161     combobox_delete_entrys(file_dialog->ct);
162     int ds = fp_get_files(file_dialog->fp,file_dialog->fp->path, 1, 1);
163     int set_f = set_files(file_dialog);
164     set_dirs(file_dialog);
165     combobox_set_active_entry(file_dialog->ct, ds);
166     if (set_f != -1) {
167         if (file_dialog->list_view) {
168             listview_set_active_entry(file_dialog->ft, set_f);
169         } else {
170             multi_listview_set_active_entry(file_dialog->ft, set_f);
171         }
172     } else {
173         if (file_dialog->list_view) {
174             listview_unset_active_entry(file_dialog->ft);
175         } else {
176             multi_listview_unset_active_entry(file_dialog->ft);
177         }
178     }
179     listview_unset_active_entry(file_dialog->xdg_dirs);
180     expose_widget(file_dialog->ft);
181     expose_widget(file_dialog->ct);
182     expose_widget(file_dialog->xdg_dirs);
183 }
184 
show_preview(FileDialog * file_dialog,const char * file_name)185 static void show_preview(FileDialog *file_dialog, const char* file_name) {
186     Widget_t* w = file_dialog->w;
187     cairo_surface_t *getpng = cairo_image_surface_create_from_png (file_name);
188     int width = cairo_image_surface_get_width(getpng);
189     int height = cairo_image_surface_get_height(getpng);
190     int width_t = 80;
191     int height_t = 80;
192     double x = (double)width_t/(double)width;
193     double y = (double)height_t/(double)height;
194     if (width > 20*height)
195         x = y = (double)width_t/(double)height;
196     cairo_surface_destroy(w->image);
197     w->image = NULL;
198 
199     w->image = cairo_surface_create_similar (w->surface,
200                         CAIRO_CONTENT_COLOR_ALPHA, width_t, height_t);
201     cairo_t *cri = cairo_create (w->image);
202     cairo_scale(cri, x,y);
203     cairo_set_source_surface (cri, getpng,0,0);
204     cairo_paint (cri);
205     cairo_surface_destroy(getpng);
206     cairo_destroy(cri);
207     expose_widget(w);
208 }
209 
set_selected_file(FileDialog * file_dialog)210 static void set_selected_file(FileDialog *file_dialog) {
211     if(adj_get_value(file_dialog->ft->adj)<0 ||
212         adj_get_value(file_dialog->ft->adj) > file_dialog->fp->file_counter) return;
213 
214     struct stat sb;
215     if (stat(file_dialog->fp->file_names[(int)adj_get_value(file_dialog->ft->adj)], &sb) == 0 && S_ISDIR(sb.st_mode)) {
216         asprintf(&file_dialog->fp->path, "%s",file_dialog->fp->file_names[(int)adj_get_value(file_dialog->ft->adj)]);
217         reload_from_dir(file_dialog);
218         return;
219     }
220 
221     Widget_t* menu =  file_dialog->ct->childlist->childs[1];
222     Widget_t* view_port =  menu->childlist->childs[0];
223     ComboBox_t *comboboxlist = (ComboBox_t*)view_port->parent_struct;
224     if ((int)adj_get_value(file_dialog->ct->adj) < 0) return;
225     free(file_dialog->fp->selected_file);
226     file_dialog->fp->selected_file = NULL;
227     if (strlen(comboboxlist->list_names[(int)adj_get_value(file_dialog->ct->adj)]) > 1) {
228         asprintf(&file_dialog->fp->selected_file, "%s/%s",
229             comboboxlist->list_names[(int)adj_get_value(file_dialog->ct->adj)],
230             file_dialog->fp->file_names[(int)adj_get_value(file_dialog->ft->adj)]);
231     } else {
232         asprintf(&file_dialog->fp->selected_file, "/%s",
233             file_dialog->fp->file_names[(int)adj_get_value(file_dialog->ft->adj)]);
234     }
235     assert(file_dialog->fp->selected_file != NULL);
236     if (strstr(file_dialog->fp->selected_file, ".png")) {
237         show_preview(file_dialog, file_dialog->fp->selected_file);
238     } else if (file_dialog->w->image) {
239         cairo_surface_destroy(file_dialog->w->image);
240         file_dialog->w->image = NULL;
241         expose_widget(file_dialog->w);
242     }
243 }
244 
reload_file_entrys(FileDialog * file_dialog)245 static void reload_file_entrys(FileDialog *file_dialog) {
246     if (file_dialog->list_view) {
247         listview_remove_list(file_dialog->ft);
248     } else {
249         multi_listview_remove_list(file_dialog->ft);
250     }
251     fp_get_files(file_dialog->fp,file_dialog->fp->path, 0, 1);
252     if (!file_dialog->fp->file_counter)
253         fp_get_files(file_dialog->fp,file_dialog->fp->path, 1, 1);
254     int set_f = set_files(file_dialog);
255     if (set_f != -1) {
256         if (file_dialog->list_view) {
257             listview_set_active_entry(file_dialog->ft, set_f);
258         } else {
259             multi_listview_set_active_entry(file_dialog->ft, set_f);
260         }
261     } else {
262         if (file_dialog->list_view) {
263             listview_unset_active_entry(file_dialog->ft);
264         } else {
265             multi_listview_unset_active_entry(file_dialog->ft);
266         }
267     }
268     expose_widget(file_dialog->ft);
269 }
270 
combo_response(void * w_,void * user_data)271 static void combo_response(void *w_, void* user_data) {
272     Widget_t *w = (Widget_t*)w_;
273     FileDialog *file_dialog = (FileDialog *)w->parent_struct;
274     Widget_t* menu =  w->childlist->childs[1];
275     Widget_t* view_port =  menu->childlist->childs[0];
276     ComboBox_t *comboboxlist = (ComboBox_t*)view_port->parent_struct;
277     if ((int)adj_get_value(file_dialog->ct->adj) < 0) return;
278     free(file_dialog->fp->path);
279     file_dialog->fp->path = NULL;
280     asprintf(&file_dialog->fp->path, "%s",comboboxlist->list_names[(int)adj_get_value(w->adj)]);
281     assert(file_dialog->fp->path != NULL);
282     reload_from_dir(file_dialog);
283 }
284 
button_ok_callback(void * w_,void * user_data)285 static void button_ok_callback(void *w_, void* user_data) {
286     Widget_t *w = (Widget_t*)w_;
287     FileDialog *file_dialog = (FileDialog *)w->parent_struct;
288     if (w->flags & HAS_POINTER && !*(int*)user_data){
289         if(file_dialog->fp->selected_file) {
290             file_dialog->parent->func.dialog_callback(file_dialog->parent,&file_dialog->fp->selected_file);
291             file_dialog->send_clear_func = false;
292         } else {
293             Widget_t *dia = open_message_dialog(w, INFO_BOX, _("INFO"), _("Please select a file"),NULL);
294             XSetTransientForHint(file_dialog->w->app->dpy, dia->widget, file_dialog->w->widget);
295             return;
296         }
297         destroy_widget(file_dialog->w,file_dialog->w->app);
298    }
299 }
300 
file_double_click_callback(void * w_,void * button,void * user_data)301 static void file_double_click_callback(void *w_, void *button, void* user_data) {
302     Widget_t *w = (Widget_t*)w_;
303     FileDialog *file_dialog = (FileDialog *)w->parent_struct;
304     if(!file_dialog->fp->selected_file) {
305         set_selected_file(file_dialog);
306     }
307     if(file_dialog->fp->selected_file) {
308         file_dialog->parent->func.dialog_callback(file_dialog->parent,&file_dialog->fp->selected_file);
309         file_dialog->send_clear_func = false;
310     } else {
311         Widget_t *dia = open_message_dialog(w, INFO_BOX, _("INFO"), _("Please select a file"),NULL);
312         XSetTransientForHint(file_dialog->w->app->dpy, dia->widget, file_dialog->w->widget);
313         return;
314     }
315     destroy_widget(file_dialog->w,file_dialog->w->app);
316 }
317 
reload_all(FileDialog * file_dialog)318 static void reload_all(FileDialog *file_dialog) {
319     Widget_t* menu =  file_dialog->ct->childlist->childs[1];
320     Widget_t* view_port =  menu->childlist->childs[0];
321     ComboBox_t *comboboxlist = (ComboBox_t*)view_port->parent_struct;
322     if ((int)adj_get_value(file_dialog->ct->adj) < 0) return;
323     free(file_dialog->fp->path);
324     file_dialog->fp->path = NULL;
325     asprintf(&file_dialog->fp->path, "%s",comboboxlist->list_names[(int)adj_get_value(file_dialog->ct->adj)]);
326     assert(file_dialog->fp->path != NULL);
327     if (file_dialog->list_view) {
328         listview_remove_list(file_dialog->ft);
329     } else {
330         multi_listview_remove_list(file_dialog->ft);
331     }
332     combobox_delete_entrys(file_dialog->ct);
333     int ds = fp_get_files(file_dialog->fp,file_dialog->fp->path, 1, 1);
334     int set_f = set_files(file_dialog);
335     set_dirs(file_dialog);
336     combobox_set_active_entry(file_dialog->ct, ds);
337     if (set_f != -1) {
338         if (file_dialog->list_view) {
339             listview_set_active_entry(file_dialog->ft, set_f);
340         } else {
341             multi_listview_set_active_entry(file_dialog->ft, set_f);
342         }
343     } else {
344         if (file_dialog->list_view) {
345             listview_unset_active_entry(file_dialog->ft);
346         } else {
347             multi_listview_unset_active_entry(file_dialog->ft);
348         }
349     }
350     expose_widget(file_dialog->ft);
351 }
352 
set_scale_factor_callback(void * w_,void * user_data)353 static void set_scale_factor_callback(void *w_, void* user_data) {
354     Widget_t *w = (Widget_t*)w_;
355     FileDialog *file_dialog = (FileDialog *)w->parent_struct;
356     float v = adj_get_value(w->adj);
357     if (!file_dialog->list_view) {
358         multi_listview_set_item_size(file_dialog->ft, v);
359     }
360 }
361 
open_dir_callback(void * w_,void * user_data)362 static void open_dir_callback(void *w_, void* user_data) {
363     Widget_t *w = (Widget_t*)w_;
364     FileDialog *file_dialog = (FileDialog *)w->parent_struct;
365     if (w->flags & HAS_POINTER && !*(int*)user_data){
366         reload_from_dir(file_dialog);
367     }
368 }
369 
button_hidden_callback(void * w_,void * user_data)370 static void button_hidden_callback(void *w_, void* user_data) {
371     Widget_t *w = (Widget_t*)w_;
372     FileDialog *file_dialog = (FileDialog *)w->parent_struct;
373     if (w->flags & HAS_POINTER) {
374         file_dialog->fp->show_hidden = adj_get_value(w->adj) ? true : false;
375         reload_all(file_dialog);
376     }
377 }
378 
button_view_callback(void * w_,void * user_data)379 static void button_view_callback(void *w_, void* user_data) {
380     Widget_t *w = (Widget_t*)w_;
381     FileDialog *file_dialog = (FileDialog *)w->parent_struct;
382     if (w->flags & HAS_POINTER) {
383         file_dialog->list_view = adj_get_value(w->adj) ? true : false;
384     }
385     if (file_dialog->list_view) {
386         destroy_widget(file_dialog->ft, w->app);
387         file_dialog->ft = add_listview(file_dialog->w, "", 130, 90, 510, 225);
388         file_dialog->ft->parent_struct = file_dialog;
389         file_dialog->ft->scale.gravity = NORTHWEST;
390         file_dialog->ft->flags |= NO_PROPAGATE;
391         listview_set_check_dir(file_dialog->ft, 1);
392         file_dialog->ft->func.button_release_callback = file_released_b_callback;
393         file_dialog->ft->func.double_click_callback = file_double_click_callback;
394         int set_f = set_files(file_dialog);
395         if (set_f != -1) {
396             listview_set_active_entry(file_dialog->ft, set_f);
397         } else {
398             listview_unset_active_entry(file_dialog->ft);
399         }
400         widget_show_all(file_dialog->ft);
401     } else {
402         destroy_widget(file_dialog->ft, w->app);
403         file_dialog->ft = add_multi_listview(file_dialog->w, "", 130, 90, 510, 225);
404         file_dialog->ft->parent_struct = file_dialog;
405         file_dialog->ft->scale.gravity = NORTHWEST;
406         file_dialog->ft->flags |= NO_PROPAGATE;
407         multi_listview_set_check_dir(file_dialog->ft, 1);
408         file_dialog->ft->func.button_release_callback = file_released_b_callback;
409         file_dialog->ft->func.double_click_callback = file_double_click_callback;
410         int set_f = set_files(file_dialog);
411         if (set_f != -1) {
412             multi_listview_set_active_entry(file_dialog->ft, set_f);
413         } else {
414             multi_listview_unset_active_entry(file_dialog->ft);
415         }
416         multi_listview_set_item_size(file_dialog->ft,adj_get_value(file_dialog->scale_size->adj));
417         widget_show_all(file_dialog->ft);
418     }
419     resize_childs(file_dialog->w);
420 }
421 
set_filter_callback(void * w_,void * user_data)422 static void set_filter_callback(void *w_, void* user_data) {
423     Widget_t *w = (Widget_t*)w_;
424     FileDialog *file_dialog = (FileDialog *)w->parent_struct;
425     if (file_dialog->fp->use_filter != (int)adj_get_value(w->adj)) {
426         file_dialog->fp->use_filter = (int)adj_get_value(w->adj);
427         Widget_t* menu =  w->childlist->childs[1];
428         Widget_t* view_port =  menu->childlist->childs[0];
429         ComboBox_t *comboboxlist = (ComboBox_t*)view_port->parent_struct;
430         if ((int)adj_get_value(file_dialog->ct->adj) < 0) return;
431         free(file_dialog->fp->filter);
432         file_dialog->fp->filter = NULL;
433         asprintf(&file_dialog->fp->filter, "%s",comboboxlist->list_names[(int)adj_get_value(w->adj)]);
434         assert(file_dialog->fp->filter != NULL);
435 
436         reload_file_entrys(file_dialog);
437     }
438 }
439 
fd_mem_free(void * w_,void * user_data)440 static void fd_mem_free(void *w_, void* user_data) {
441     Widget_t *w = (Widget_t*)w_;
442     FileDialog *file_dialog = (FileDialog *)w->parent_struct;
443     if(file_dialog->icon) {
444         XFreePixmap(w->app->dpy, (*file_dialog->icon));
445         file_dialog->icon = NULL;
446     }
447     if(file_dialog->send_clear_func)
448         file_dialog->parent->func.dialog_callback(file_dialog->parent,NULL);
449     fp_free(file_dialog->fp);
450     free(file_dialog->fp);
451     free(file_dialog);
452 }
453 
starts_with(const char * restrict string,const char * restrict prefix)454 static int starts_with(const char *restrict string, const char *restrict prefix) {
455     while(*prefix)
456     {
457         if(*prefix++ != *string++)
458             return 0;
459     }
460 
461     return 1;
462 }
463 
strremove(char * string,const char * restrict find)464 static int strremove(char *string, const char *restrict find){
465     if(strstr(string, find) == NULL) return 0;
466     char *temporaryString = malloc(strlen(strstr(string, find) + strlen(find)) + 1);
467     strcpy(temporaryString, strstr(string, find) + strlen(find));
468     char* rep = strstr(string, find);
469     *rep = '\0';
470     //strcat(string, replaceWith);
471     strcat(string, temporaryString);
472     free(temporaryString);
473     return 1;
474 }
475 
parse_xdg_dirs(FileDialog * file_dialog)476 static void parse_xdg_dirs(FileDialog *file_dialog) {
477 
478     char xdg_dir[204];
479     file_dialog->home_dir = getenv("HOME");
480     if (file_dialog->home_dir == NULL) {
481         file_dialog->home_dir = getpwuid(getuid())->pw_dir;
482     }
483     if (file_dialog->home_dir == NULL) return;
484     sprintf(xdg_dir,"%s/.config/user-dirs.dirs", file_dialog->home_dir);
485     FILE * fp = NULL;
486     char * line = NULL;
487     size_t len = 0;
488     ssize_t read;
489 
490     file_dialog->xdg_user_dirs = (char **)realloc(file_dialog->xdg_user_dirs,
491       (file_dialog->xdg_dir_counter + 1) * sizeof(char *));
492     assert(file_dialog->xdg_user_dirs != NULL);
493     asprintf(&file_dialog->xdg_user_dirs[file_dialog->xdg_dir_counter++], "%s", _("Home"));
494     assert(file_dialog->xdg_user_dirs[file_dialog->xdg_dir_counter-1] != NULL);
495 
496 
497     fp = fopen(xdg_dir, "r");
498     if (fp != NULL) {
499         while ((read = getline(&line, &len, fp)) != -1) {
500             if(starts_with(line,"XDG_")) {
501                 char* xdg = strstr(line, "$HOME/");
502                 if (!strremove(xdg, "$HOME/")) continue;
503                 char* rep = strstr(xdg, "\"");
504                 *rep = '\0';
505                 file_dialog->xdg_user_dirs = (char **)realloc(file_dialog->xdg_user_dirs,
506                   (file_dialog->xdg_dir_counter + 1) * sizeof(char *));
507                 assert(file_dialog->xdg_user_dirs != NULL);
508                 asprintf(&file_dialog->xdg_user_dirs[file_dialog->xdg_dir_counter++], "%s", xdg);
509                 assert(file_dialog->xdg_user_dirs[file_dialog->xdg_dir_counter-1] != NULL);
510             }
511         }
512         fclose(fp);
513     }
514 
515     file_dialog->xdg_user_dirs = (char **)realloc(file_dialog->xdg_user_dirs,
516       (file_dialog->xdg_dir_counter + 1) * sizeof(char *));
517     assert(file_dialog->xdg_user_dirs != NULL);
518     asprintf(&file_dialog->xdg_user_dirs[file_dialog->xdg_dir_counter++], "%s", _("Computer"));
519     assert(file_dialog->xdg_user_dirs[file_dialog->xdg_dir_counter-1] != NULL);
520 
521     if (line)
522         free(line);
523 }
524 
xdg_dir_select_callback(void * w_,void * button,void * user_data)525 static void xdg_dir_select_callback(void *w_, void *button, void* user_data) {
526     Widget_t *w = (Widget_t*)w_;
527     FileDialog *file_dialog = (FileDialog *)w->parent_struct;
528     int v = (int)adj_get_value(w->adj);
529     if (v == 0) {
530         free(file_dialog->fp->path);
531         file_dialog->fp->path = NULL;
532         asprintf(&file_dialog->fp->path, "%s",file_dialog->home_dir);
533         assert(file_dialog->fp->path != NULL);
534     } else if (v == file_dialog->xdg_dir_counter) {
535         free(file_dialog->fp->path);
536         file_dialog->fp->path = NULL;
537         asprintf(&file_dialog->fp->path, "%s",PATH_SEPARATOR);
538         assert(file_dialog->fp->path != NULL);
539     } else {
540         free(file_dialog->fp->path);
541         file_dialog->fp->path = NULL;
542         asprintf(&file_dialog->fp->path, "%s/%s",file_dialog->home_dir,file_dialog->xdg_user_dirs[v]);
543         assert(file_dialog->fp->path != NULL);
544     }
545     reload_from_dir(file_dialog);
546 }
547 
add_xdg_dirs(FileDialog * file_dialog)548 static void add_xdg_dirs(FileDialog *file_dialog) {
549     file_dialog->xdg_dirs = add_listview(file_dialog->w, "", 20, 90, 100, 225);
550     file_dialog->xdg_dirs->parent_struct = file_dialog;
551     file_dialog->xdg_dirs->scale.gravity = EASTNORTH;
552     file_dialog->xdg_dirs->flags |= NO_PROPAGATE;
553     listview_set_list(file_dialog->xdg_dirs, file_dialog->xdg_user_dirs, (int)file_dialog->xdg_dir_counter);
554     file_dialog->xdg_dirs->func.button_release_callback = xdg_dir_select_callback;
555     listview_unset_active_entry(file_dialog->xdg_dirs);
556 }
557 
open_file_dialog(Widget_t * w,const char * path,const char * filter)558 Widget_t *open_file_dialog(Widget_t *w, const char *path, const char *filter) {
559     FileDialog *file_dialog = (FileDialog*)malloc(sizeof(FileDialog));
560 
561     file_dialog->xdg_user_dirs = NULL;
562     file_dialog->xdg_dir_counter = 0;
563     parse_xdg_dirs(file_dialog);
564     file_dialog->fp = (FilePicker*)malloc(sizeof(FilePicker));
565 
566     struct stat sb;
567     if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
568         fp_init(file_dialog->fp, path);
569     } else if (stat(file_dialog->home_dir, &sb) == 0 && S_ISDIR(sb.st_mode)) {
570         fp_init(file_dialog->fp, file_dialog->home_dir);
571     } else {
572         fp_init(file_dialog->fp, "/");
573     }
574 
575     file_dialog->parent = w;
576     file_dialog->send_clear_func = true;
577     file_dialog->icon = NULL;
578     file_dialog->list_view = false;
579 
580     file_dialog->w = create_window(w->app, DefaultRootWindow(w->app->dpy), 0, 0, 660, 415);
581 
582     XSizeHints* win_size_hints;
583     win_size_hints = XAllocSizeHints();
584     win_size_hints->flags =  PMinSize|PBaseSize|PMaxSize|PWinGravity;
585     win_size_hints->min_width = 660;
586     win_size_hints->min_height = 415;
587     win_size_hints->base_width = 660;
588     win_size_hints->base_height = 415;
589     win_size_hints->max_width = 960;
590     win_size_hints->max_height = 865;
591     win_size_hints->win_gravity = CenterGravity;
592     XSetWMNormalHints(file_dialog->w->app->dpy, file_dialog->w->widget, win_size_hints);
593     XFree(win_size_hints);
594 
595     file_dialog->w->flags |= HAS_MEM;
596     file_dialog->w->parent_struct = file_dialog;
597     file_dialog->w->flags |= NO_PROPAGATE;
598     widget_set_title(file_dialog->w, _("File Selector"));
599     file_dialog->w->func.expose_callback = draw_window;
600     file_dialog->w->func.mem_free_callback = fd_mem_free;
601     widget_set_icon_from_png(file_dialog->w,file_dialog->icon,LDVAR(directory_png));
602 
603     file_dialog->ct = add_combobox(file_dialog->w, "", 20, 40, 550, 30);
604     file_dialog->ct->parent_struct = file_dialog;
605     file_dialog->ct->scale.gravity = NORTHEAST;
606     file_dialog->ct->flags |= NO_PROPAGATE;
607     file_dialog->ct->func.value_changed_callback = combo_response;
608 
609     file_dialog->sel_dir = add_button(file_dialog->w, _("Open"), 580, 40, 60, 30);
610     file_dialog->sel_dir->parent_struct = file_dialog;
611     file_dialog->sel_dir->scale.gravity = WESTNORTH;
612     file_dialog->sel_dir->flags |= NO_PROPAGATE;
613     add_tooltip(file_dialog->sel_dir,_("Open sub-directory's"));
614     file_dialog->sel_dir->func.value_changed_callback = open_dir_callback;
615 
616     file_dialog->scale_size = add_hslider(file_dialog->w, "", 580, 10, 60, 15);
617     set_adjustment(file_dialog->scale_size->adj, 0.2, 0.2, 0.1, 0.4, 0.01, CL_CONTINUOS);
618     file_dialog->scale_size->parent_struct = file_dialog;
619     file_dialog->scale_size->scale.gravity = WESTNORTH;
620     file_dialog->scale_size->flags |= NO_PROPAGATE;
621     file_dialog->scale_size->func.expose_callback = draw_fd_hslider;
622     add_tooltip(file_dialog->scale_size,_("Set Icon scale factor"));
623     file_dialog->scale_size->func.value_changed_callback = set_scale_factor_callback;
624 
625     file_dialog->ft = add_multi_listview(file_dialog->w, "", 130, 90, 510, 225);
626     file_dialog->ft->parent_struct = file_dialog;
627     file_dialog->ft->scale.gravity = NORTHWEST;
628     file_dialog->ft->flags |= NO_PROPAGATE;
629     multi_listview_set_check_dir(file_dialog->ft, 1);
630     file_dialog->ft->func.button_release_callback = file_released_b_callback;
631     file_dialog->ft->func.double_click_callback = file_double_click_callback;
632 
633     int ds = fp_get_files(file_dialog->fp,file_dialog->fp->path, 1, 1);
634     int set_f = set_files(file_dialog);
635     set_dirs(file_dialog);
636     combobox_set_active_entry(file_dialog->ct, ds);
637     if (set_f != -1) {
638         multi_listview_set_active_entry(file_dialog->ft, set_f);
639     } else {
640         multi_listview_unset_active_entry(file_dialog->ft);
641     }
642 
643     add_xdg_dirs(file_dialog);
644 
645     file_dialog->w_quit = add_button(file_dialog->w, _("Cancel"), 580, 340, 60, 60);
646     file_dialog->w_quit->parent_struct = file_dialog;
647     file_dialog->w_quit->scale.gravity = SOUTHWEST;
648     file_dialog->w_quit->flags |= NO_PROPAGATE;
649     add_tooltip(file_dialog->w_quit,_("Exit file selector"));
650     file_dialog->w_quit->func.value_changed_callback = button_quit_callback;
651 
652     file_dialog->w_okay = add_button(file_dialog->w, _("Load"), 510, 340, 60, 60);
653     file_dialog->w_okay->parent_struct = file_dialog;
654     file_dialog->w_okay->scale.gravity = SOUTHWEST;
655     file_dialog->w_okay->flags |= NO_PROPAGATE;
656     add_tooltip(file_dialog->w_okay,_("Load selected file"));
657     file_dialog->w_okay->func.value_changed_callback = button_ok_callback;
658 
659     file_dialog->set_filter = add_combobox(file_dialog->w, "", 360, 345, 120, 30);
660     file_dialog->set_filter->parent_struct = file_dialog;
661     file_dialog->set_filter->scale.gravity = SOUTHWEST;
662     file_dialog->set_filter->flags |= NO_PROPAGATE;
663     combobox_add_entry(file_dialog->set_filter,_("all"));
664     combobox_add_entry(file_dialog->set_filter,_("application"));
665     combobox_add_entry(file_dialog->set_filter,_("audio"));
666     combobox_add_entry(file_dialog->set_filter,_("font"));
667     combobox_add_entry(file_dialog->set_filter,_("image"));
668     combobox_add_entry(file_dialog->set_filter,_("text"));
669     combobox_add_entry(file_dialog->set_filter,_("video"));
670     combobox_add_entry(file_dialog->set_filter,_("x-content"));
671     if(filter !=NULL && strlen(filter))
672         combobox_add_entry(file_dialog->set_filter,filter);
673     combobox_set_active_entry(file_dialog->set_filter, 0);
674     file_dialog->set_filter->func.value_changed_callback = set_filter_callback;
675     if(filter !=NULL && strlen(filter))
676         combobox_set_active_entry(file_dialog->set_filter, 8);
677     add_tooltip(file_dialog->set_filter->childlist->childs[0], _("File filter type"));
678 
679     file_dialog->w_hidden = add_check_button(file_dialog->w, "", 20, 345, 20, 20);
680     file_dialog->w_hidden->parent_struct = file_dialog;
681     file_dialog->w_hidden->scale.gravity = EASTWEST;
682     file_dialog->w_hidden->flags |= NO_PROPAGATE;
683     add_tooltip(file_dialog->w_hidden,_("Show hidden files and folders"));
684     file_dialog->w_hidden->func.value_changed_callback = button_hidden_callback;
685 
686     file_dialog->view = add_check_button(file_dialog->w, "", 20, 375, 20, 20);
687     file_dialog->view->parent_struct = file_dialog;
688     file_dialog->view->scale.gravity = EASTWEST;
689     file_dialog->view->flags |= NO_PROPAGATE;
690     add_tooltip(file_dialog->view,_("Show entries in list view"));
691     file_dialog->view->func.value_changed_callback = button_view_callback;
692 
693     widget_show_all(file_dialog->w);
694     return file_dialog->w;
695 }
696 
697 /*---------------------------------------------------------------------
698 -----------------------------------------------------------------------
699 				add_file_button
700 -----------------------------------------------------------------------
701 ----------------------------------------------------------------------*/
702 
fdialog_response(void * w_,void * user_data)703 static void fdialog_response(void *w_, void* user_data) {
704     Widget_t *w = (Widget_t*)w_;
705     FileButton *filebutton = (FileButton *)w->parent_struct;
706     if(user_data !=NULL) {
707         char *tmp = strdup(*(const char**)user_data);
708         free(filebutton->last_path);
709         filebutton->last_path = NULL;
710         filebutton->last_path = strdup(dirname(tmp));
711         filebutton->path = filebutton->last_path;
712         free(tmp);
713     }
714     w->func.user_callback(w,user_data);
715     filebutton->is_active = false;
716     adj_set_value(w->adj,0.0);
717 }
718 
fbutton_callback(void * w_,void * user_data)719 static void fbutton_callback(void *w_, void* user_data) {
720     Widget_t *w = (Widget_t*)w_;
721     FileButton *filebutton = (FileButton *)w->parent_struct;
722     if (w->flags & HAS_POINTER && adj_get_value(w->adj)){
723         filebutton->w = open_file_dialog(w,filebutton->path,filebutton->filter);
724         Atom wmStateAbove = XInternAtom(w->app->dpy, "_NET_WM_STATE_ABOVE", 1 );
725         Atom wmNetWmState = XInternAtom(w->app->dpy, "_NET_WM_STATE", 1 );
726         XChangeProperty(w->app->dpy, filebutton->w->widget, wmNetWmState, XA_ATOM, 32,
727             PropModeReplace, (unsigned char *) &wmStateAbove, 1);
728         filebutton->is_active = true;
729     } else if (w->flags & HAS_POINTER && !adj_get_value(w->adj)){
730         if(filebutton->is_active)
731             destroy_widget(filebutton->w,w->app);
732     }
733 }
734 
fbutton_mem_free(void * w_,void * user_data)735 static void fbutton_mem_free(void *w_, void* user_data) {
736     Widget_t *w = (Widget_t*)w_;
737     FileButton *filebutton = (FileButton *)w->parent_struct;
738     free(filebutton->last_path);
739     filebutton->last_path = NULL;
740     free(filebutton);
741     filebutton = NULL;
742 }
743 
add_file_button(Widget_t * parent,int x,int y,int width,int height,const char * path,const char * filter)744 Widget_t *add_file_button(Widget_t *parent, int x, int y, int width, int height,
745                            const char *path, const char *filter) {
746     FileButton *filebutton = (FileButton*)malloc(sizeof(FileButton));
747     filebutton->path = path;
748     filebutton->filter = filter;
749     filebutton->last_path = NULL;
750     filebutton->w = NULL;
751     filebutton->is_active = false;
752     Widget_t *fbutton = add_image_toggle_button(parent, "", x, y, width, height);
753     fbutton->parent_struct = filebutton;
754     fbutton->flags |= HAS_MEM;
755     widget_get_png(fbutton, LDVAR(directory_open_png));
756     fbutton->scale.gravity = CENTER;
757     fbutton->func.mem_free_callback = fbutton_mem_free;
758     fbutton->func.value_changed_callback = fbutton_callback;
759     fbutton->func.dialog_callback = fdialog_response;
760     return fbutton;
761 }
762