1 /*
2  * Copyright (C) 2000-2019 the xine project
3  *
4  * This file is part of xine, a unix video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <stdio.h>
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <X11/keysym.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <dirent.h>
33 #include <unistd.h>
34 #include <pthread.h>
35 #include <errno.h>
36 
37 #include "common.h"
38 
39 #define WINDOW_WIDTH        500
40 #define WINDOW_HEIGHT       390
41 #define MAX_DISP_ENTRIES    10
42 
43 #define MAXFILES            65535
44 
45 #ifndef S_ISLNK
46 #define S_ISLNK(mode)  0
47 #endif
48 #ifndef S_ISFIFO
49 #define S_ISFIFO(mode) 0
50 #endif
51 #ifndef S_ISSOCK
52 #define S_ISSOCK(mode) 0
53 #endif
54 #ifndef S_ISCHR
55 #define S_ISCHR(mode)  0
56 #endif
57 #ifndef S_ISBLK
58 #define S_ISBLK(mode)  0
59 #endif
60 #ifndef S_ISREG
61 #define S_ISREG(mode)  0
62 #endif
63 #if !S_IXUGO
64 #define S_IXUGO        (S_IXUSR | S_IXGRP | S_IXOTH)
65 #endif
66 
67 
68 typedef struct {
69   const char                *name;
70   const char                *ending;
71 } filebrowser_filter_t;
72 
73 static const filebrowser_filter_t __fb_filters[] = {
74   { N_("All files"), "*"                                                           },
75   { N_("All known subtitles"), ".sub .srt .asc .smi .ssa .ass .txt "               },
76   { N_("All known playlists"), ".pls .m3u .sfv .tox .asx .smil .smi .xml .fxd "    },
77   /* media files */
78   { "*.4xm", ".4xm "                                                               },
79   { "*.ac3", ".ac3 "                                                               },
80   { "*.aif, *.aiff", ".aif .aiff "                                                 },
81   {"*.asf, *.wmv, *.wma, *.wvx, *.wax", ".asf .wmv .wma .wvx .wax "                },
82   { "*.aud", ".aud "                                                               },
83   { "*.avi", ".avi "                                                               },
84   { "*.cin", ".cin "                                                               },
85   { "*.cpk, *.cak, *.film", ".cpk .cak .film "                                     },
86   { "*.dv, *.dif", ".dv .dif "                                                     },
87   { "*.fli, *.flc", ".fli .flc "                                                   },
88   { "*.mp3, *.mp2, *.mpa, *.mpega", ".mp3 .mp2 .mpa .mpega "                       },
89   { "*.mjpg", ".mjpg "                                                             },
90   { "*.mov, *.qt, *.mp4", ".mov .qt .mp4 "                                         },
91   { "*.m2p", ".m2p "                                                               },
92   { "*.mpg, *.mpeg", ".mpg .mpeg "                                                 },
93   { "*.mpv", ".mpv "                                                               },
94   { "*.mve, *.mv8", ".mve .mv8 "                                                   },
95   { "*.nsf", ".nsf "                                                               },
96   { "*.nsv", ".nsv "                                                               },
97   { "*.ogg, *.ogm, *.spx", ".ogg .ogm .spx "                                       },
98   { "*.pes", ".pes "                                                               },
99   { "*.png, *.mng", ".png .mng "                                                   },
100   { "*.pva", ".pva "                                                               },
101   { "*.ra", ".ra "                                                                 },
102   { "*.rm, *.ram, *.rmvb ", ".rm .ram .rmvb "                                      },
103   { "*.roq", ".roq "                                                               },
104   { "*.str, *.iki, *.ik2, *.dps, *.dat, *.xa1, *.xa2, *.xas, *.xap, *.xa",
105     ".str .iki .ik2 .dps .dat .xa1 .xa2 .xas .xap .xa "                            },
106   { "*.snd, *.au", ".snd .au "                                                     },
107   { "*.ts, *.m2t, *.trp", ".ts .m2t .trp "                                         },
108   { "*.vqa", ".vqa "                                                               },
109   { "*.vob", ".vob "                                                               },
110   { "*.voc", ".voc "                                                               },
111   { "*.vox", ".vox "                                                               },
112   { "*.wav", ".wav "                                                               },
113   { "*.wve", ".wve "                                                               },
114   { "*.y4m", ".y4m "                                                               },
115   { NULL, NULL                                                                     }
116 };
117 
118 typedef struct {
119   char                  *name;
120 } fileinfo_t;
121 
122 #define DEFAULT_SORT 0
123 #define REVERSE_SORT 1
124 
125 #define NORMAL_CURS  0
126 #define WAIT_CURS    1
127 
128 struct filebrowser_s {
129   gGui_t                         *gui;
130 
131   xitk_window_t                  *xwin;
132 
133   xitk_widget_list_t             *widget_list;
134 
135   xitk_widget_t                  *origin;
136 
137   xitk_widget_t                  *directories_browser;
138   xitk_widget_t                  *directories_sort;
139   int                             directories_sort_direction;
140   xitk_widget_t                  *files_browser;
141   xitk_widget_t                  *files_sort;
142   int                             files_sort_direction;
143 
144   xitk_image_t                   *sort_skin_up;
145   xitk_image_t                   *sort_skin_down;
146 
147   xitk_widget_t                  *rename;
148   xitk_widget_t                  *delete;
149   xitk_widget_t                  *create;
150 
151   xitk_widget_t                  *filters;
152   int                             filter_selected;
153   const char                    **file_filters;
154 
155   xitk_widget_t                  *show_hidden;
156   int                             show_hidden_files;
157 
158   char                            current_dir[XINE_PATH_MAX + 1];
159   char                            filename[XINE_NAME_MAX + 1];
160 
161   fileinfo_t                     *dir_files;
162   char                          **directories;
163   int                             directories_num;
164   int                             directories_sel;
165 
166   fileinfo_t                     *norm_files;
167   char                          **files;
168   int                             files_num;
169 
170   int                             running;
171   int                             visible;
172 
173   xitk_widget_t                  *close;
174 
175   hidden_file_toggle_t            hidden_cb;
176 
177   filebrowser_callback_button_t   cbb[3];
178   xitk_widget_t                  *cb_buttons[2];
179 
180   xitk_register_key_t             widget_key;
181 };
182 
183 
184 typedef struct {
185   xitk_window_t            *xwin;
186   xitk_widget_list_t       *widget_list;
187   xitk_widget_t            *input;
188 
189   xitk_string_callback_t    callback;
190   filebrowser_t            *fb;
191 
192   xitk_widget_t            *button_apply;
193   xitk_widget_t            *button_cancel;
194 
195   xitk_register_key_t       widget_key;
196 } filename_editor_t;
197 
198 /*
199  * Enable/disable widgets in file browser window
200  */
_fb_enability(filebrowser_t * fb,int enable)201 static void _fb_enability(filebrowser_t *fb, int enable) {
202   void (*enability)(xitk_widget_t *) = (enable == 1) ? xitk_enable_widget : xitk_disable_widget;
203 
204   enability(fb->origin);
205   enability(fb->directories_browser);
206   enability(fb->directories_sort);
207   enability(fb->files_browser);
208   enability(fb->files_sort);
209   enability(fb->rename);
210   enability(fb->delete);
211   enability(fb->create);
212   enability(fb->filters);
213   if(fb->cb_buttons[0]) {
214     enability(fb->cb_buttons[0]);
215     if(fb->cb_buttons[1])
216       enability(fb->cb_buttons[1]);
217   }
218   enability(fb->show_hidden);
219   enability(fb->close);
220 }
fb_deactivate(filebrowser_t * fb)221 static void fb_deactivate(filebrowser_t *fb) {
222   _fb_enability(fb, 0);
223 }
fb_reactivate(filebrowser_t * fb)224 static void fb_reactivate(filebrowser_t *fb) {
225   _fb_enability(fb, 1);
226 }
227 
_fb_set_cursor(filebrowser_t * fb,int state)228 static void _fb_set_cursor(filebrowser_t *fb, int state) {
229   if(fb) {
230     if(state == WAIT_CURS)
231       xitk_cursors_define_window_cursor (fb->gui->display, (xitk_window_get_window(fb->xwin)), xitk_cursor_watch);
232     else
233       xitk_cursors_restore_window_cursor (fb->gui->display, (xitk_window_get_window(fb->xwin)));
234   }
235 }
236 /*
237  * **************************************************
238  */
239 static void fb_exit(xitk_widget_t *, void *);
240 
241 /*
242  * ************************* filename editor **************************
243  */
fne_destroy(filename_editor_t * fne)244 static void fne_destroy(filename_editor_t *fne) {
245   if(fne) {
246 
247     xitk_unregister_event_handler(&fne->widget_key);
248 
249     xitk_destroy_widgets(fne->widget_list);
250     xitk_window_destroy_window(gGui->imlib_data, fne->xwin);
251 
252     fne->xwin = NULL;
253     /* xitk_dlist_init (&fne->widget_list->list); */
254 
255     gGui->x_lock_display (gGui->display);
256     XFreeGC(gGui->display, (XITK_WIDGET_LIST_GC(fne->widget_list)));
257     gGui->x_unlock_display (gGui->display);
258 
259     XITK_WIDGET_LIST_FREE(fne->widget_list);
260 
261     free(fne);
262   }
263 }
fne_apply_cb(xitk_widget_t * w,void * data)264 static void fne_apply_cb(xitk_widget_t *w, void *data) {
265   filename_editor_t *fne = (filename_editor_t *) data;
266 
267   if(fne->callback)
268     fne->callback(NULL, (void *)fne->fb, (xitk_inputtext_get_text(fne->input)));
269 
270   fb_reactivate(fne->fb);
271   fne_destroy(fne);
272 }
fne_cancel_cb(xitk_widget_t * w,void * data)273 static void fne_cancel_cb(xitk_widget_t *w, void *data) {
274   filename_editor_t *fne = (filename_editor_t *) data;
275 
276   fb_reactivate(fne->fb);
277   fne_destroy(fne);
278 }
fne_handle_event(XEvent * event,void * data)279 static void fne_handle_event(XEvent *event, void *data) {
280   switch(event->type) {
281 
282   case KeyPress:
283     if(xitk_get_key_pressed(event) == XK_Escape)
284       fne_cancel_cb(NULL, data);
285     else
286       gui_handle_event(event, gGui);
287     break;
288   }
289 }
fb_create_input_window(char * title,char * text,xitk_string_callback_t cb,filebrowser_t * fb)290 static void fb_create_input_window(char *title, char *text,
291 				   xitk_string_callback_t cb, filebrowser_t *fb) {
292   filename_editor_t          *fne;
293   GC                          gc;
294   int                         x, y, w;
295   int                         width = WINDOW_WIDTH;
296   int                         height = 102;
297   xitk_labelbutton_widget_t   lb;
298   xitk_inputtext_widget_t     inp;
299 
300   fb->gui->x_lock_display (fb->gui->display);
301   x = (((DisplayWidth (fb->gui->display, fb->gui->screen))) >> 1) - (width >> 1);
302   y = (((DisplayHeight (fb->gui->display, fb->gui->screen))) >> 1) - (height >> 1);
303   fb->gui->x_unlock_display (fb->gui->display);
304 
305   fne = (filename_editor_t *) calloc(1, sizeof(filename_editor_t));
306 
307   fne->callback = cb;
308   fne->fb = fb;
309 
310   fne->xwin = xitk_window_create_dialog_window (fb->gui->imlib_data, title, x, y, width, height);
311 
312   xitk_set_wm_window_type((xitk_window_get_window(fne->xwin)), WINDOW_TYPE_NORMAL);
313   change_class_name((xitk_window_get_window(fne->xwin)));
314   change_icon((xitk_window_get_window(fne->xwin)));
315 
316   fb->gui->x_lock_display (fb->gui->display);
317   gc = XCreateGC (fb->gui->display, (xitk_window_get_window(fne->xwin)), None, None);
318   fb->gui->x_unlock_display (fb->gui->display);
319 
320   fne->widget_list                = xitk_widget_list_new();
321 
322   xitk_widget_list_set(fne->widget_list,
323 		       WIDGET_LIST_WINDOW, (void *) (xitk_window_get_window(fne->xwin)));
324   xitk_widget_list_set(fne->widget_list, WIDGET_LIST_GC, gc);
325 
326   XITK_WIDGET_INIT(&lb, fb->gui->imlib_data);
327   XITK_WIDGET_INIT(&inp, fb->gui->imlib_data);
328 
329   x = 15;
330   y = 30;
331   w = width - 30;
332 
333   inp.skin_element_name = NULL;
334   inp.text              = text;
335   inp.max_length        = XITK_PATH_MAX + XITK_NAME_MAX + 1;
336   inp.callback          = NULL;
337   inp.userdata          = (void *)fne;
338   fne->input = xitk_noskin_inputtext_create (fne->widget_list, &inp,
339     x, y, w, 20, "Black", "Black", fontname);
340   xitk_add_widget (fne->widget_list, fne->input);
341 
342   xitk_enable_and_show_widget(fne->input);
343 
344   y = height - (23 + 15);
345   x = 15;
346   w = 100;
347 
348   lb.button_type       = CLICK_BUTTON;
349   lb.label             = _("Apply");
350   lb.align             = ALIGN_CENTER;
351   lb.callback          = fne_apply_cb;
352   lb.state_callback    = NULL;
353   lb.userdata          = (void *)fne;
354   lb.skin_element_name = NULL;
355   fne->button_apply = xitk_noskin_labelbutton_create (fne->widget_list,
356     &lb, x, y, w, 23, "Black", "Black", "White", btnfontname);
357   xitk_add_widget (fne->widget_list, fne->button_apply);
358   xitk_enable_and_show_widget(fne->button_apply);
359 
360   x = width - (w + 15);
361 
362   lb.button_type       = CLICK_BUTTON;
363   lb.label             = _("Cancel");
364   lb.align             = ALIGN_CENTER;
365   lb.callback          = fne_cancel_cb;
366   lb.state_callback    = NULL;
367   lb.userdata          = (void *)fne;
368   lb.skin_element_name = NULL;
369   fne->button_cancel = xitk_noskin_labelbutton_create (fne->widget_list,
370     &lb, x, y, w, 23, "Black", "Black", "White", btnfontname);
371   xitk_add_widget (fne->widget_list, fne->button_cancel);
372   xitk_enable_and_show_widget(fne->button_cancel);
373 
374   {
375     char buffer[256];
376     snprintf(buffer, sizeof(buffer), "filenameed%u", (unsigned int) time(NULL));
377 
378     fne->widget_key = xitk_register_event_handler(buffer,
379 						  (xitk_window_get_window(fne->xwin)),
380 						  fne_handle_event,
381 						  NULL,
382 						  NULL,
383 						  fne->widget_list,
384 						  (void *)fne);
385   }
386 
387   fb->gui->x_lock_display (fb->gui->display);
388   XRaiseWindow (fb->gui->display, xitk_window_get_window(fne->xwin));
389   XMapWindow (fb->gui->display, xitk_window_get_window(fne->xwin));
390   if (!fb->gui->use_root_window && fb->gui->video_display == fb->gui->display)
391     XSetTransientForHint (fb->gui->display, xitk_window_get_window (fne->xwin), fb->gui->video_window);
392   fb->gui->x_unlock_display (fb->gui->display);
393   layer_above_video(xitk_window_get_window(fne->xwin));
394 
395   try_to_set_input_focus(xitk_window_get_window(fne->xwin));
396 }
397 /*
398  * ************************** END OF filename editor **************************
399  */
400 
401 /*
402  * **************************** File related funcs **************************
403  */
404 
405 /*
406  * Return 1 if file match with current filter, otherwise 0.
407  */
is_file_match_to_filter(filebrowser_t * fb,char * file)408 static int is_file_match_to_filter(filebrowser_t *fb, char *file) {
409 
410   if(!fb->filter_selected)
411     return 1;
412   else {
413     char *ending;
414 
415     if((ending = strrchr(file, '.'))) {
416       char  ext[strlen(ending) + 2];
417       char *p;
418 
419       sprintf(ext, "%s ", ending);
420 
421       p = ext;
422       while(*p) {
423 	*p = tolower(*p);
424 	p++;
425       }
426 
427       if(strstr(__fb_filters[fb->filter_selected].ending, ext))
428 	return 1;
429 
430     }
431   }
432   return 0;
433 }
434 
fb_update_origin(filebrowser_t * fb)435 static void fb_update_origin(filebrowser_t *fb) {
436   char   buf[XITK_PATH_MAX + XITK_NAME_MAX + 2];
437 
438   if(strcmp(fb->current_dir, "/"))
439     snprintf(buf, sizeof(buf), "%s/%s", fb->current_dir, fb->filename);
440   else
441     snprintf(buf, sizeof(buf), "/%s", fb->filename);
442 
443   xitk_inputtext_change_text(fb->origin, buf);
444 }
445 
fb_extract_path_and_file(filebrowser_t * fb,const char * filepathname)446 static void fb_extract_path_and_file(filebrowser_t *fb, const char *filepathname) {
447   if(filepathname && *filepathname) {
448     char *dirname = NULL;
449     char *filename = NULL;
450     char  _filepathname[XITK_PATH_MAX + XITK_NAME_MAX + 2];
451     char *p;
452 
453     if((*filepathname == '~') && (*(filepathname + 1) == '/' || *(filepathname + 1) == '\0')) {
454       const char *homedir = xine_get_homedir();
455 
456       snprintf(_filepathname, sizeof(_filepathname), "%s%s", homedir, (filepathname + 1));
457     }
458     else {
459       if((*filepathname == '\\') && (*(filepathname + 1) == '~'))
460 	filepathname++;
461       if(*filepathname == '/')
462 	strlcpy(_filepathname, filepathname, sizeof(_filepathname));
463       else
464 	snprintf(_filepathname, sizeof(_filepathname), "%s/%s", fb->current_dir, filepathname);
465     }
466 
467     p = _filepathname + strlen(_filepathname) - 1;
468     while((p > _filepathname) && (*p == '/'))	/* Remove trailing '/' from path name */
469       *p-- = '\0';
470 
471     if(is_a_dir(_filepathname)) {		/* Whole name is a dir name */
472       dirname = _filepathname;
473     }
474     else {
475       filename = strrchr(_filepathname, '/');
476 
477       if(!filename) {				/* Whole name treated as file name */
478 	filename = _filepathname;
479       }
480       else {					/* Split into dir and file part */
481 	*filename++ = '\0';
482 
483 	if(*_filepathname == '\0')
484 	  dirname = "/";			/* Dir part was "/", restore it */
485 	else
486 	  dirname = _filepathname;
487 
488 	p = dirname + strlen(dirname) - 1;
489 	while((p > dirname) && (*p == '/'))	/* Remove trailing '/' from dir name */
490 	  *p-- = '\0';
491 
492 	if(!is_a_dir(dirname))			/* Invalid path, don't change anything */
493 	  return;
494       }
495     }
496 
497     if(dirname)
498       strlcpy(fb->current_dir, dirname, sizeof(fb->current_dir));
499 
500     if(filename)
501       strlcpy(fb->filename, filename, sizeof(fb->filename));
502     else
503       *fb->filename = '\0';
504   }
505 }
506 
507 /*
508  * Sorting function, it comes from GNU fileutils package.
509  */
510 #define S_N        0x0
511 #define S_I        0x4
512 #define S_F        0x8
513 #define S_Z        0xC
514 #define CMP          2
515 #define LEN          3
516 #define ISDIGIT(c)   ((unsigned) (c) - '0' <= 9)
my_strverscmp(const char * s1,const char * s2)517 static int my_strverscmp(const char *s1, const char *s2) {
518   const unsigned char *p1 = (const unsigned char *) s1;
519   const unsigned char *p2 = (const unsigned char *) s2;
520   unsigned char c1, c2;
521   int state;
522   int diff;
523   static const unsigned int next_state[] = {
524     S_N, S_I, S_Z, S_N,
525     S_N, S_I, S_I, S_I,
526     S_N, S_F, S_F, S_F,
527     S_N, S_F, S_Z, S_Z
528   };
529   static const int result_type[] = {
530     CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP,
531     CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
532     CMP,  -1,  -1, CMP,   1, LEN, LEN, CMP,
533       1, LEN, LEN, CMP, CMP, CMP, CMP, CMP,
534     CMP, CMP, CMP, CMP, CMP, LEN, CMP, CMP,
535     CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
536     CMP,   1,   1, CMP,  -1, CMP, CMP, CMP,
537      -1, CMP, CMP, CMP
538   };
539 
540   if(p1 == p2)
541     return 0;
542 
543   c1 = *p1++;
544   c2 = *p2++;
545 
546   state = S_N | ((c1 == '0') + (ISDIGIT(c1) != 0));
547 
548   while((diff = c1 - c2) == 0 && c1 != '\0') {
549     state = next_state[state];
550     c1 = *p1++;
551     c2 = *p2++;
552     state |= (c1 == '0') + (ISDIGIT(c1) != 0);
553   }
554 
555   state = result_type[state << 2 | ((c2 == '0') + (ISDIGIT(c2) != 0))];
556 
557   switch(state) {
558   case CMP:
559     return diff;
560 
561   case LEN:
562     while(ISDIGIT(*p1++))
563       if(!ISDIGIT(*p2++))
564 	return 1;
565 
566     return ISDIGIT(*p2) ? -1 : diff;
567 
568   default:
569     return state;
570   }
571 }
572 
573 /*
574  * Wrapper to my_strverscmp() for qsort() calls, which sort mrl_t type array.
575  */
_sortfiles_default(const fileinfo_t * s1,const fileinfo_t * s2)576 static int _sortfiles_default(const fileinfo_t *s1, const fileinfo_t *s2) {
577   return(my_strverscmp(s1->name, s2->name));
578 }
_sortfiles_reverse(const fileinfo_t * s1,const fileinfo_t * s2)579 static int _sortfiles_reverse(const fileinfo_t *s1, const fileinfo_t *s2) {
580   return(my_strverscmp(s2->name, s1->name));
581 }
582 
sort_files(filebrowser_t * fb)583 static void sort_files(filebrowser_t *fb) {
584   int  (*func) () = NULL;
585 
586   if(fb->files) {
587     int i = 0;
588 
589     while(fb->files[i]) {
590       SAFE_FREE(fb->files[i]);
591       i++;
592     }
593   }
594 
595   if(fb->files_num) {
596     int i;
597 
598     if(fb->files_sort_direction == REVERSE_SORT)
599       func = _sortfiles_reverse;
600     else /*if(fb->files_sort_direction == DEFAULT_SORT)*/
601       func = _sortfiles_default;
602 
603     qsort(fb->norm_files, fb->files_num, sizeof(fileinfo_t), func);
604 
605     fb->files = (char **) realloc(fb->files, sizeof(char *) * (fb->files_num + 2));
606 
607     for(i = 0; i < fb->files_num; i++)
608       fb->files[i] = strdup(fb->norm_files[i].name);
609 
610     fb->files[i] = NULL;
611   }
612 
613   xitk_browser_update_list(fb->files_browser, (const char *const *)fb->files, NULL, fb->files_num, 0);
614 }
615 
sort_directories(filebrowser_t * fb)616 static void sort_directories(filebrowser_t *fb) {
617   int  (*func) () = NULL;
618 
619   if(fb->directories) {
620     int i = 0;
621 
622     while(fb->directories[i]) {
623       SAFE_FREE(fb->directories[i]);
624       i++;
625     }
626   }
627 
628   if(fb->directories_num) {
629     int i;
630 
631     if(fb->directories_sort_direction == REVERSE_SORT)
632       func = _sortfiles_reverse;
633     else /*if(fb->directories_sort_direction == DEFAULT_SORT)*/
634       func = _sortfiles_default;
635 
636     qsort(fb->dir_files, fb->directories_num, sizeof(fileinfo_t), func);
637 
638     fb->directories = (char **) realloc(fb->directories, sizeof(char *) * (fb->directories_num + 2));
639     for(i = 0; i < fb->directories_num; i++) {
640       fb->directories[i] = (char *) malloc(strlen(fb->dir_files[i].name) + 2);
641       sprintf(fb->directories[i], "%s%c", fb->dir_files[i].name, '/');
642     }
643 
644     fb->directories[i] = NULL;
645   }
646 
647   xitk_browser_update_list(fb->directories_browser,
648 			   (const char *const *)fb->directories, NULL, fb->directories_num, 0);
649 }
650 
fb_getdir(filebrowser_t * fb)651 static void fb_getdir(filebrowser_t *fb) {
652   char                  fullfilename[XINE_PATH_MAX + XINE_NAME_MAX + 2];
653   struct dirent        *pdirent;
654   int                   num_dir_files   = 0;
655   int                   num_norm_files  = 0;
656   DIR                  *pdir;
657   int                   num_files       = -1;
658 
659   _fb_set_cursor(fb, WAIT_CURS);
660 
661   if(fb->norm_files) {
662     while(fb->files_num) {
663       free(fb->norm_files[fb->files_num - 1].name);
664       fb->files_num--;
665     }
666   }
667 
668   if(fb->dir_files) {
669     while(fb->directories_num) {
670       free(fb->dir_files[fb->directories_num - 1].name);
671       fb->directories_num--;
672     }
673   }
674 
675   /* Following code relies on the fact that fb->current_dir has no trailing '/' */
676   if((pdir = opendir(fb->current_dir)) == NULL) {
677     char *p = strrchr(fb->current_dir, '/');
678 
679     xine_error(_("Unable to open directory '%s': %s."),
680 	       (p && *(p + 1)) ? p + 1 : fb->current_dir, strerror(errno));
681 
682     /* One step back if dir has a subdir component */
683     if(p && *(p + 1)) {
684       if(p == fb->current_dir) /* we are in the root dir */
685 	*(p + 1) = '\0';
686       else
687 	*p = '\0';
688 
689       fb_update_origin(fb);
690       fb_getdir(fb);
691     }
692     return;
693   }
694 
695   while((pdirent = readdir(pdir)) != NULL) {
696 
697     snprintf(fullfilename, sizeof(fullfilename), "%s/%s", fb->current_dir, pdirent->d_name);
698 
699     if(is_a_dir(fullfilename)) {
700 
701       /* if user don't want to see hidden files, ignore them */
702       if(fb->show_hidden_files == 0 &&
703 	 ((strlen(pdirent->d_name) > 1)
704 	  && (pdirent->d_name[0] == '.' &&  pdirent->d_name[1] != '.'))) {
705 	;
706       }
707       else {
708 	fb->dir_files[num_dir_files].name = strdup(pdirent->d_name);
709 	num_dir_files++;
710       }
711 
712     } /* Hmmmm, an hidden file ? */
713     else if((strlen(pdirent->d_name) > 1)
714 	    && (pdirent->d_name[0] == '.' &&  pdirent->d_name[1] != '.')) {
715 
716       /* if user don't want to see hidden files, ignore them */
717       if(fb->show_hidden_files) {
718 	if(is_file_match_to_filter(fb, pdirent->d_name)) {
719 	  fb->norm_files[num_norm_files].name = strdup(pdirent->d_name);
720 	  num_norm_files++;
721 	}
722       }
723 
724     } /* So a *normal* one. */
725     else {
726       if(is_file_match_to_filter(fb, pdirent->d_name)) {
727 	fb->norm_files[num_norm_files].name = strdup(pdirent->d_name);
728 	num_norm_files++;
729       }
730     }
731 
732     num_files++;
733   }
734 
735   closedir(pdir);
736 
737   /*
738    * Sort arrays
739    */
740   fb->directories_num = num_dir_files;
741   fb->files_num = num_norm_files;
742   sort_directories(fb);
743   sort_files(fb);
744   _fb_set_cursor(fb, NORMAL_CURS);
745 }
746 
747 /*
748  * ****************************** END OF file related funcs ***************************
749  */
750 
751 /*
752  * ***************************** widget callbacks *******************************
753  */
fb_select(xitk_widget_t * w,void * data,int selected)754 static void fb_select(xitk_widget_t *w, void *data, int selected) {
755   filebrowser_t *fb = (filebrowser_t *) data;
756 
757   if(w == fb->files_browser) {
758     strlcpy(fb->filename, fb->norm_files[selected].name, sizeof(fb->filename));
759     fb_update_origin(fb);
760   }
761 }
762 
fb_callback_button_cb(xitk_widget_t * w,void * data)763 static void fb_callback_button_cb(xitk_widget_t *w, void *data) {
764   filebrowser_t *fb = (filebrowser_t *) data;
765 
766   if(w == fb->cb_buttons[0]) {
767     if(fb->cbb[0].need_a_file && (!strlen(fb->filename)))
768       return;
769     fb->cbb[0].callback(fb);
770   }
771   else if(w == fb->cb_buttons[1]) {
772     if(fb->cbb[1].need_a_file && (!strlen(fb->filename)))
773       return;
774     fb->cbb[1].callback(fb);
775   }
776 
777   fb_exit(NULL, (void *)fb);
778 }
779 
780 
fb_dbl_select(xitk_widget_t * w,void * data,int selected)781 static void fb_dbl_select(xitk_widget_t *w, void *data, int selected) {
782   filebrowser_t *fb = (filebrowser_t *) data;
783 
784   if(w == fb->directories_browser) {
785     char buf[XITK_PATH_MAX + XITK_NAME_MAX + 2];
786 
787     /* Want to re-read current dir */
788     if(!strcasecmp(fb->dir_files[selected].name, ".")) {
789       /* NOOP */
790     }
791     else if(!strcasecmp(fb->dir_files[selected].name, "..")) {
792       char *p;
793 
794       strlcpy(buf, fb->current_dir, sizeof(buf));
795       if(strlen(buf) > 1) { /* not '/' directory */
796 
797 	p = &buf[strlen(buf)-1];
798 	while(*p && *p != '/') {
799 	  *p = '\0';
800 	  p--;
801 	}
802 
803 	/* Remove last '/' if current_dir isn't root */
804 	if((strlen(buf) > 1) && *p == '/')
805 	  *p = '\0';
806 
807 	strlcpy(fb->current_dir, buf, sizeof(fb->current_dir));
808       }
809     }
810     else {
811 
812       /* not '/' directory */
813       if(strcasecmp(fb->current_dir, "/")) {
814 	snprintf(buf, sizeof(buf), "%s/%s", fb->current_dir, fb->dir_files[selected].name);
815       }
816       else {
817 	snprintf(buf, sizeof(buf), "/%s", fb->dir_files[selected].name);
818       }
819 
820       if(is_a_dir(buf))
821 	strlcpy(fb->current_dir, buf, sizeof(fb->current_dir));
822 
823     }
824 
825     fb_update_origin(fb);
826     fb_getdir(fb);
827   }
828   else if(w == fb->files_browser) {
829     strlcpy(fb->filename, fb->norm_files[selected].name, sizeof(fb->filename));
830     fb_callback_button_cb(fb->cb_buttons[0], (void *)data);
831   }
832 
833 }
834 
fb_change_origin(xitk_widget_t * w,void * data,const char * currenttext)835 static void fb_change_origin(xitk_widget_t *w, void *data, const char *currenttext) {
836   filebrowser_t *fb = (filebrowser_t *)data;
837 
838   fb_extract_path_and_file(fb, currenttext);
839   fb_update_origin(fb);
840   fb_getdir(fb);
841 }
842 
fb_sort(xitk_widget_t * w,void * data)843 static void fb_sort(xitk_widget_t *w, void *data) {
844   filebrowser_t *fb = (filebrowser_t *) data;
845 
846   if(w == fb->directories_sort) {
847     xitk_image_t *dsimage = xitk_get_widget_foreground_skin(fb->directories_sort);
848 
849     fb->directories_sort_direction = (fb->directories_sort_direction == DEFAULT_SORT) ?
850       REVERSE_SORT : DEFAULT_SORT;
851 
852     xitk_hide_widget(fb->directories_sort);
853 
854     if(fb->directories_sort_direction == DEFAULT_SORT)
855       xitk_image_change_image (fb->gui->imlib_data, fb->sort_skin_down,
856 			      dsimage, dsimage->width, dsimage->height);
857     else
858       xitk_image_change_image (fb->gui->imlib_data, fb->sort_skin_up,
859 			    dsimage, dsimage->width, dsimage->height);
860 
861     xitk_show_widget(fb->directories_sort);
862 
863     sort_directories(fb);
864   }
865   else if(w == fb->files_sort) {
866     xitk_image_t *fsimage = xitk_get_widget_foreground_skin(fb->files_sort);
867 
868     fb->files_sort_direction = (fb->files_sort_direction == DEFAULT_SORT) ?
869       REVERSE_SORT : DEFAULT_SORT;
870 
871     xitk_hide_widget(fb->files_sort);
872 
873     if(fb->files_sort_direction == DEFAULT_SORT)
874       xitk_image_change_image (fb->gui->imlib_data, fb->sort_skin_down,
875 			      fsimage, fsimage->width, fsimage->height);
876     else
877       xitk_image_change_image (fb->gui->imlib_data, fb->sort_skin_up,
878 			    fsimage, fsimage->width, fsimage->height);
879 
880     xitk_show_widget(fb->files_sort);
881 
882     sort_files(fb);
883   }
884 }
885 
fb_exit(xitk_widget_t * w,void * data)886 static void fb_exit(xitk_widget_t *w, void *data) {
887   filebrowser_t *fb = (filebrowser_t *) data;
888 
889   if(fb) {
890     int i;
891 
892     fb->running = 0;
893     fb->visible = 0;
894 
895     xitk_unregister_event_handler(&fb->widget_key);
896 
897     xitk_destroy_widgets(fb->widget_list);
898     xitk_window_destroy_window (fb->gui->imlib_data, fb->xwin);
899 
900     fb->xwin = NULL;
901     /* xitk_dlist_init (&fb->widget_list->list); */
902 
903     fb->gui->x_lock_display (fb->gui->display);
904     XFreeGC (fb->gui->display, (XITK_WIDGET_LIST_GC(fb->widget_list)));
905     fb->gui->x_unlock_display (fb->gui->display);
906 
907     XITK_WIDGET_LIST_FREE(fb->widget_list);
908 
909     if(fb->norm_files) {
910       while(fb->files_num) {
911 	free(fb->norm_files[fb->files_num - 1].name);
912 	fb->files_num--;
913       }
914     }
915     SAFE_FREE(fb->norm_files);
916 
917     if(fb->dir_files) {
918       while(fb->directories_num) {
919 	free(fb->dir_files[fb->directories_num - 1].name);
920 	fb->directories_num--;
921       }
922     }
923     SAFE_FREE(fb->dir_files);
924 
925     if(fb->files) {
926       i = 0;
927 
928       while(fb->files[i]) {
929 	free(fb->files[i]);
930 	i++;
931       }
932       free(fb->files);
933     }
934 
935     if(fb->directories) {
936       i = 0;
937 
938       while(fb->directories[i]) {
939 	free(fb->directories[i]);
940 	i++;
941       }
942       free(fb->directories);
943     }
944 
945     free(fb->file_filters);
946 
947     SAFE_FREE(fb->cbb[0].label);
948     SAFE_FREE(fb->cbb[1].label);
949 
950     xitk_image_free_image (fb->gui->imlib_data, &fb->sort_skin_up);
951     xitk_image_free_image (fb->gui->imlib_data, &fb->sort_skin_down);
952 
953     free(fb);
954     fb = NULL;
955   }
956 }
_fb_exit(xitk_widget_t * w,void * data)957 static void _fb_exit(xitk_widget_t *w, void *data) {
958   filebrowser_t *fb = (filebrowser_t *) data;
959   if(fb->cbb[2].callback)
960     fb->cbb[2].callback(fb);
961   fb_exit(NULL, (void *)fb);
962 }
963 
fb_delete_file_cb(xitk_widget_t * w,void * data,int button)964 static void fb_delete_file_cb(xitk_widget_t *w, void *data, int button) {
965   filebrowser_t *fb = (filebrowser_t *) data;
966 
967   switch(button) {
968   case XITK_WINDOW_ANSWER_YES:
969     {
970       char buf[XITK_PATH_MAX + XITK_NAME_MAX + 2];
971       int sel = xitk_browser_get_current_selected(fb->files_browser);
972 
973       snprintf(buf, sizeof(buf), "%s%s%s",
974 	       fb->current_dir, ((fb->current_dir[0] && strcmp(fb->current_dir, "/")) ? "/" : ""),
975 	       fb->norm_files[sel].name);
976 
977       if((unlink(buf)) == -1)
978 	xine_error(_("Unable to delete file '%s': %s."), buf, strerror(errno));
979       else
980 	fb_getdir(fb);
981 
982     }
983     break;
984   }
985   fb_reactivate(fb);
986 }
987 
fb_delete_file(xitk_widget_t * w,void * data)988 static void fb_delete_file(xitk_widget_t *w, void *data) {
989   filebrowser_t *fb = (filebrowser_t *) data;
990   int            sel;
991 
992   if((sel = xitk_browser_get_current_selected(fb->files_browser)) >= 0) {
993     char buf[256 + XITK_PATH_MAX + XITK_NAME_MAX + 2];
994 
995     snprintf(buf, sizeof(buf), _("Do you really want to delete the file '%s%s%s' ?"),
996 	     fb->current_dir, ((fb->current_dir[0] && strcmp(fb->current_dir, "/")) ? "/" : ""),
997 	     fb->norm_files[sel].name);
998 
999     fb_deactivate(fb);
1000     xitk_window_dialog_yesno (fb->gui->imlib_data, _("Confirm deletion ?"),
1001 			     fb_delete_file_cb,
1002 			     fb_delete_file_cb,
1003 			     (void *)fb, ALIGN_DEFAULT, "%s", buf);
1004   }
1005 }
1006 
fb_rename_file_cb(xitk_widget_t * w,void * data,const char * newname)1007 static void fb_rename_file_cb(xitk_widget_t *w, void *data, const char *newname) {
1008   filebrowser_t *fb = (filebrowser_t *) data;
1009   char buf[XITK_PATH_MAX + XITK_NAME_MAX + 2];
1010   int sel = xitk_browser_get_current_selected(fb->files_browser);
1011 
1012   snprintf(buf, sizeof(buf), "%s%s%s",
1013 	   fb->current_dir, ((fb->current_dir[0] && strcmp(fb->current_dir, "/")) ? "/" : ""),
1014 	   fb->norm_files[sel].name);
1015 
1016   if((rename(buf, newname)) == -1)
1017     xine_error(_("Unable to rename file '%s' to '%s': %s."), buf, newname, strerror(errno));
1018   else
1019     fb_getdir(fb);
1020 
1021 }
fb_rename_file(xitk_widget_t * w,void * data)1022 static void fb_rename_file(xitk_widget_t *w, void *data) {
1023   filebrowser_t *fb = (filebrowser_t *) data;
1024   int            sel;
1025 
1026   if((sel = xitk_browser_get_current_selected(fb->files_browser)) >= 0) {
1027     char buf[XITK_PATH_MAX + XITK_NAME_MAX + 2];
1028 
1029     snprintf(buf, sizeof(buf), "%s%s%s",
1030 	     fb->current_dir, ((fb->current_dir[0] && strcmp(fb->current_dir, "/")) ? "/" : ""),
1031 	     fb->norm_files[sel].name);
1032 
1033     fb_deactivate(fb);
1034     fb_create_input_window(_("Rename file"), buf, fb_rename_file_cb, fb);
1035   }
1036 }
1037 
fb_create_directory_cb(xitk_widget_t * w,void * data,const char * newdir)1038 static void fb_create_directory_cb(xitk_widget_t *w, void *data, const char *newdir) {
1039   filebrowser_t *fb = (filebrowser_t *) data;
1040 
1041   if(!mkdir_safe(newdir))
1042     xine_error(_("Unable to create the directory '%s': %s."), newdir, strerror(errno));
1043   else
1044     fb_getdir(fb);
1045 }
fb_create_directory(xitk_widget_t * w,void * data)1046 static void fb_create_directory(xitk_widget_t *w, void *data) {
1047   filebrowser_t *fb = (filebrowser_t *) data;
1048   char           buf[XITK_PATH_MAX + XITK_NAME_MAX + 2];
1049 
1050   snprintf(buf, sizeof(buf), "%s%s",
1051 	   fb->current_dir, ((fb->current_dir[0] && strcmp(fb->current_dir, "/")) ? "/" : ""));
1052 
1053   fb_deactivate(fb);
1054   fb_create_input_window(_("Create a new directory"), buf, fb_create_directory_cb, fb);
1055 }
1056 
fb_select_file_filter(xitk_widget_t * w,void * data,int selected)1057 static void fb_select_file_filter(xitk_widget_t *w, void *data, int selected) {
1058   filebrowser_t *fb = (filebrowser_t *) data;
1059 
1060   fb->filter_selected = selected;
1061   fb_getdir(fb);
1062 }
1063 
fb_hidden_files(xitk_widget_t * w,void * data,int state)1064 static void fb_hidden_files(xitk_widget_t *w, void *data, int state) {
1065   filebrowser_t *fb = (filebrowser_t *) data;
1066 
1067   fb->show_hidden_files = state;
1068   fb->hidden_cb(1, state);
1069   fb_getdir(fb);
1070 }
fb_lbl_hidden_files(xitk_widget_t * w,void * data)1071 static void fb_lbl_hidden_files(xitk_widget_t *w, void *data) {
1072   filebrowser_t *fb = (filebrowser_t *) data;
1073 
1074   xitk_checkbox_set_state(fb->show_hidden, (!xitk_checkbox_get_state(fb->show_hidden)));
1075   xitk_checkbox_callback_exec(fb->show_hidden);
1076 }
1077 
fb_handle_events(XEvent * event,void * data)1078 static void fb_handle_events(XEvent *event, void *data) {
1079   filebrowser_t *fb = (filebrowser_t *) data;
1080 
1081   switch(event->type) {
1082 
1083   case KeyPress:
1084     if(xitk_get_key_pressed(event) == XK_Escape) {
1085       if(xitk_is_widget_enabled(fb->close)) /* Exit only if close button would exit */
1086 	_fb_exit(NULL, data);
1087     }
1088     else {
1089       int sel = xitk_browser_get_current_selected(fb->files_browser);
1090 
1091       if(sel >= 0)
1092 	fb_select(fb->files_browser, (void *) fb, sel);
1093       else
1094 	gui_handle_event(event, data);
1095     }
1096     break;
1097   }
1098 }
1099 
filebrowser_raise_window(filebrowser_t * fb)1100 void filebrowser_raise_window(filebrowser_t *fb) {
1101   if(fb != NULL)
1102     raise_window(xitk_window_get_window(fb->xwin), fb->visible, fb->running);
1103 }
1104 
filebrowser_end(filebrowser_t * fb)1105 void filebrowser_end(filebrowser_t *fb) {
1106   if(fb)
1107     fb_exit(NULL, (void *)fb);
1108 }
1109 
filebrowser_get_current_dir(filebrowser_t * fb)1110 char *filebrowser_get_current_dir(filebrowser_t *fb) {
1111   char *current_dir = NULL;
1112 
1113   if(fb)
1114     current_dir = strdup(fb->current_dir);
1115 
1116   return current_dir;
1117 }
1118 
filebrowser_get_current_filename(filebrowser_t * fb)1119 char *filebrowser_get_current_filename(filebrowser_t *fb) {
1120   char *filename = NULL;
1121 
1122   if(fb && strlen(fb->filename))
1123     filename = strdup(fb->filename);
1124 
1125   return filename;
1126 }
1127 
filebrowser_get_full_filename(filebrowser_t * fb)1128 char *filebrowser_get_full_filename(filebrowser_t *fb) {
1129   char *fullfilename = NULL;
1130 
1131   if(fb)
1132     fullfilename = strdup(xitk_inputtext_get_text(fb->origin));
1133 
1134   return fullfilename;
1135 }
1136 
filebrowser_get_all_files(filebrowser_t * fb)1137 char **filebrowser_get_all_files(filebrowser_t *fb) {
1138   char **files = NULL;
1139 
1140   if(fb && fb->files_num) {
1141     int i;
1142     files = (char **) calloc((fb->files_num + 2), sizeof(char *));
1143 
1144     for(i = 0; i < fb->files_num; i++)
1145       files[i] = strdup(fb->norm_files[i].name);
1146     files[i] = NULL;
1147   }
1148 
1149   return files;
1150 }
1151 
create_filebrowser(char * window_title,char * filepathname,hidden_file_toggle_t hidden_cb,filebrowser_callback_button_t * cbb1,filebrowser_callback_button_t * cbb2,filebrowser_callback_button_t * cbb_close)1152 filebrowser_t *create_filebrowser(char *window_title, char *filepathname, hidden_file_toggle_t hidden_cb,
1153 				  filebrowser_callback_button_t *cbb1,
1154 				  filebrowser_callback_button_t *cbb2,
1155 				  filebrowser_callback_button_t *cbb_close) {
1156   filebrowser_t              *fb;
1157   GC                          gc;
1158   xitk_labelbutton_widget_t   lb;
1159   xitk_label_widget_t         lbl;
1160   xitk_checkbox_widget_t      cb;
1161   xitk_pixmap_t              *bg;
1162   xitk_browser_widget_t       br;
1163   xitk_inputtext_widget_t     inp;
1164   xitk_button_widget_t        b;
1165   xitk_combo_widget_t         cmb;
1166   xitk_widget_t              *widget;
1167   int                         i, x, y, w, width, height;
1168 
1169   fb = (filebrowser_t *) calloc(1, sizeof(filebrowser_t));
1170   if (!fb)
1171     return NULL;
1172 
1173   fb->gui = gGui;
1174 
1175   if(cbb1 && (strlen(cbb1->label) && cbb1->callback)) {
1176     fb->cbb[0].label = strdup(cbb1->label);
1177     fb->cbb[0].callback = cbb1->callback;
1178     fb->cbb[0].need_a_file = cbb1->need_a_file;
1179     if(cbb2 && (strlen(cbb2->label) && cbb2->callback)) {
1180       fb->cbb[1].label = strdup(cbb2->label);
1181       fb->cbb[1].callback = cbb2->callback;
1182       fb->cbb[1].need_a_file = cbb2->need_a_file;
1183   }
1184     else {
1185       fb->cbb[1].label = NULL;
1186       fb->cbb[1].callback = NULL;
1187     }
1188   }
1189   else {
1190     fb->cbb[0].label = NULL;
1191     fb->cbb[0].callback = NULL;
1192     fb->cbb[1].label = NULL;
1193     fb->cbb[1].callback = NULL;
1194   }
1195 
1196   if(cbb_close)
1197     fb->cbb[2].callback = cbb_close->callback;
1198 
1199   fb->gui->x_lock_display (fb->gui->display);
1200   x = (((DisplayWidth (fb->gui->display, fb->gui->screen))) >> 1) - (WINDOW_WIDTH >> 1);
1201   y = (((DisplayHeight (fb->gui->display, fb->gui->screen))) >> 1) - (WINDOW_HEIGHT >> 1);
1202   fb->gui->x_unlock_display (fb->gui->display);
1203 
1204   /* Create window */
1205   fb->xwin = xitk_window_create_dialog_window (fb->gui->imlib_data,
1206 					      (window_title) ? window_title : _("File Browser"),
1207 					      x, y, WINDOW_WIDTH, WINDOW_HEIGHT);
1208 
1209   xitk_set_wm_window_type((xitk_window_get_window(fb->xwin)), WINDOW_TYPE_NORMAL);
1210   change_class_name((xitk_window_get_window(fb->xwin)));
1211   change_icon((xitk_window_get_window(fb->xwin)));
1212 
1213   fb->directories                = NULL;
1214   fb->directories_num            = 0;
1215   fb->files                      = NULL;
1216   fb->files_num                  = 0;
1217   fb->directories_sort_direction = DEFAULT_SORT;
1218   fb->files_sort_direction       = DEFAULT_SORT;
1219   fb->hidden_cb                  = hidden_cb;
1220   fb->show_hidden_files          = hidden_cb(0, 0);
1221 
1222   strlcpy(fb->current_dir, xine_get_homedir(), sizeof(fb->current_dir));
1223   memset(&fb->filename, 0, sizeof(fb->filename));
1224   fb_extract_path_and_file(fb, filepathname);
1225 
1226   fb->norm_files = (fileinfo_t *) calloc(MAXFILES, sizeof(fileinfo_t));
1227   fb->dir_files = (fileinfo_t *) calloc(MAXFILES, sizeof(fileinfo_t));
1228 
1229   fb->files = (char **) calloc(2, sizeof(char *));
1230   fb->directories = (char **) calloc(2, sizeof(char *));
1231 
1232   fb->file_filters = (const char **) malloc(sizeof(filebrowser_filter_t) * ((sizeof(__fb_filters) / sizeof(__fb_filters[0])) + 1));
1233 
1234   for(i = 0; __fb_filters[i].ending; i++)
1235     fb->file_filters[i] = _(__fb_filters[i].name);
1236 
1237   fb->file_filters[i] = NULL;
1238   fb->filter_selected = 0;
1239 
1240   fb->gui->x_lock_display (fb->gui->display);
1241   gc = XCreateGC (fb->gui->display,
1242 		 (xitk_window_get_window(fb->xwin)), None, None);
1243   fb->gui->x_unlock_display (fb->gui->display);
1244 
1245   fb->widget_list                = xitk_widget_list_new();
1246 
1247   xitk_widget_list_set(fb->widget_list,
1248 		       WIDGET_LIST_WINDOW, (void *) (xitk_window_get_window(fb->xwin)));
1249   xitk_widget_list_set(fb->widget_list, WIDGET_LIST_GC, gc);
1250 
1251   XITK_WIDGET_INIT(&lb, fb->gui->imlib_data);
1252   XITK_WIDGET_INIT(&lbl, fb->gui->imlib_data);
1253   XITK_WIDGET_INIT(&cb, fb->gui->imlib_data);
1254   XITK_WIDGET_INIT(&br, fb->gui->imlib_data);
1255   XITK_WIDGET_INIT(&inp, fb->gui->imlib_data);
1256   XITK_WIDGET_INIT(&cmb, fb->gui->imlib_data);
1257   XITK_WIDGET_INIT(&b, fb->gui->imlib_data);
1258 
1259   xitk_window_get_window_size(fb->xwin, &width, &height);
1260   bg = xitk_image_create_xitk_pixmap (fb->gui->imlib_data, width, height);
1261   fb->gui->x_lock_display (fb->gui->display);
1262   XCopyArea (fb->gui->display, (xitk_window_get_background(fb->xwin)), bg->pixmap,
1263 	    bg->gc, 0, 0, width, height, 0, 0);
1264   fb->gui->x_unlock_display (fb->gui->display);
1265 
1266 
1267   x = 15;
1268   y = 30;
1269   w = WINDOW_WIDTH - 30;
1270 
1271   inp.skin_element_name = NULL;
1272   inp.text              = NULL;
1273   inp.max_length        = XITK_PATH_MAX + XITK_NAME_MAX + 1;
1274   inp.callback          = fb_change_origin;
1275   inp.userdata          = (void *)fb;
1276   fb->origin = xitk_noskin_inputtext_create (fb->widget_list, &inp,
1277     x, y, w, 20, "Black", "Black", fontname);
1278   xitk_add_widget (fb->widget_list, fb->origin);
1279   xitk_enable_and_show_widget(fb->origin);
1280 
1281   fb_update_origin(fb);
1282 
1283   y += 45;
1284   w = (WINDOW_WIDTH - 30 - 10) / 2;
1285 
1286   br.arrow_up.skin_element_name    = NULL;
1287   br.slider.skin_element_name      = NULL;
1288   br.arrow_dn.skin_element_name    = NULL;
1289   br.browser.skin_element_name     = NULL;
1290   br.browser.max_displayed_entries = MAX_DISP_ENTRIES;
1291   br.browser.num_entries           = fb->directories_num;
1292   br.browser.entries               = (const char *const *)fb->directories;
1293   br.callback                      = fb_select;
1294   br.dbl_click_callback            = fb_dbl_select;
1295   br.parent_wlist                  = fb->widget_list;
1296   br.userdata                      = (void *)fb;
1297   fb->directories_browser = xitk_noskin_browser_create (fb->widget_list, &br,
1298     (XITK_WIDGET_LIST_GC(fb->widget_list)), x + 2, y + 2, w - 4 - 12, 20, 12, fontname);
1299   xitk_add_widget (fb->widget_list, fb->directories_browser);
1300   xitk_enable_and_show_widget(fb->directories_browser);
1301 
1302   draw_rectangular_inner_box (fb->gui->imlib_data, bg, x, y,
1303 			     w - 1, xitk_get_widget_height(fb->directories_browser) + 4 - 1);
1304 
1305   y -= 15;
1306 
1307   b.skin_element_name = NULL;
1308   b.callback          = fb_sort;
1309   b.userdata          = (void *)fb;
1310   fb->directories_sort = xitk_noskin_button_create (fb->widget_list, &b, x, y, w, 15);
1311   xitk_add_widget (fb->widget_list, fb->directories_sort);
1312   xitk_enable_and_show_widget(fb->directories_sort);
1313 
1314   x = WINDOW_WIDTH - (w + 15);
1315   y += 15;
1316 
1317   br.arrow_up.skin_element_name    = NULL;
1318   br.slider.skin_element_name      = NULL;
1319   br.arrow_dn.skin_element_name    = NULL;
1320   br.browser.skin_element_name     = NULL;
1321   br.browser.max_displayed_entries = MAX_DISP_ENTRIES;
1322   br.browser.num_entries           = fb->files_num;
1323   br.browser.entries               = (const char *const *)fb->files;
1324   br.callback                      = fb_select;
1325   br.dbl_click_callback            = fb_dbl_select;
1326   br.parent_wlist                  = fb->widget_list;
1327   br.userdata                      = (void *)fb;
1328   fb->files_browser = xitk_noskin_browser_create (fb->widget_list, &br,
1329     (XITK_WIDGET_LIST_GC(fb->widget_list)), x + 2, y + 2, w - 4 - 12, 20, 12, fontname);
1330   xitk_add_widget (fb->widget_list, fb->files_browser);
1331   xitk_enable_and_show_widget(fb->files_browser);
1332 
1333   draw_rectangular_inner_box (fb->gui->imlib_data, bg, x, y,
1334 			     w - 1, xitk_get_widget_height(fb->files_browser) + 4 - 1);
1335 
1336   y -= 15;
1337 
1338   b.skin_element_name = NULL;
1339   b.callback          = fb_sort;
1340   b.userdata          = (void *)fb;
1341   fb->files_sort = xitk_noskin_button_create (fb->widget_list, &b, x, y, w, 15);
1342   xitk_add_widget (fb->widget_list, fb->files_sort);
1343   xitk_enable_and_show_widget(fb->files_sort);
1344 
1345 
1346   {
1347     xitk_image_t *dsimage = xitk_get_widget_foreground_skin(fb->directories_sort);
1348     xitk_image_t *fsimage = xitk_get_widget_foreground_skin(fb->files_sort);
1349     xitk_image_t *image;
1350     XPoint        points[4];
1351     int           i, j, w, offset;
1352     short         x1, x2, x3;
1353     short         y1, y2, y3;
1354 
1355     fb->sort_skin_up = xitk_image_create_image (fb->gui->imlib_data,
1356 					       dsimage->width, dsimage->height);
1357     fb->sort_skin_down = xitk_image_create_image (fb->gui->imlib_data,
1358 						 dsimage->width, dsimage->height);
1359 
1360     draw_bevel_three_state (fb->gui->imlib_data, fb->sort_skin_up);
1361     draw_bevel_three_state (fb->gui->imlib_data, fb->sort_skin_down);
1362 
1363     w = dsimage->width / 3;
1364 
1365     for(j = 0; j < 2; j++) {
1366       if(j == 0)
1367 	image = fb->sort_skin_up;
1368       else
1369 	image = fb->sort_skin_down;
1370 
1371       offset = 0;
1372       for(i = 0; i < 2; i++) {
1373 	draw_rectangular_outter_box (fb->gui->imlib_data, image->image,
1374 				    5, 4 + offset, w - 45, 1);
1375 	draw_rectangular_outter_box (fb->gui->imlib_data, image->image,
1376 				    w - 20, 4 + offset, 10, 1);
1377 	draw_rectangular_outter_box (fb->gui->imlib_data, image->image,
1378 				    w + 5, 4 + offset, w - 45, 1);
1379 	draw_rectangular_outter_box (fb->gui->imlib_data, image->image,
1380 				    (w * 2) - 20, 4 + offset, 10, 1);
1381 	draw_rectangular_outter_box (fb->gui->imlib_data, image->image,
1382 				    (w * 2) + 5 + 1, 4 + 1 + offset, w - 45, 1);
1383 	draw_rectangular_outter_box (fb->gui->imlib_data, image->image,
1384 				    ((w * 3) - 20) + 1, 4 + 1 + offset, 10 + 1, 1);
1385 	offset += 4;
1386       }
1387 
1388       offset = 0;
1389       for(i = 0; i < 3; i++) {
1390 	int k;
1391 
1392 	if(j == 0) {
1393 	  x1 = (w - 30) + offset;
1394 	  y1 = (2);
1395 
1396 	  x2 = (w - 30) - 7 + offset;
1397 	  y2 = (2 + 8);
1398 
1399 	  x3 = (w - 30) + 7 + offset;
1400 	  y3 = (2 + 8);
1401 	}
1402 	else {
1403 	  x1 = (w - 30) + offset;
1404 	  y1 = (2 + 8);
1405 
1406 	  x2 = (w - 30) - 6 + offset;
1407 	  y2 = (3);
1408 
1409 	  x3 = (w - 30) + 6 + offset;
1410 	  y3 = (3);
1411 	}
1412 
1413 	if(i == 2) {
1414 	  x1++; x2++; x3++;
1415 	  y1++; y2++; y3++;
1416 	}
1417 
1418 	points[0].x = x1;
1419 	points[0].y = y1;
1420 	points[1].x = x2;
1421 	points[1].y = y2;
1422 	points[2].x = x3;
1423 	points[2].y = y3;
1424 	points[3].x = x1;
1425 	points[3].y = y1;
1426 
1427 	offset += w;
1428 
1429         fb->gui->x_lock_display (fb->gui->display);
1430         XSetForeground (fb->gui->display, image->image->gc,
1431           xitk_get_pixel_color_lightgray (fb->gui->imlib_data));
1432         XFillPolygon (fb->gui->display, image->image->pixmap, image->image->gc,
1433 		     &points[0], 4, Convex, CoordModeOrigin);
1434         fb->gui->x_unlock_display (fb->gui->display);
1435 
1436         fb->gui->x_lock_display (fb->gui->display);
1437 	for(k = 0; k < 3; k++) {
1438 	  if(k == 0)
1439             XSetForeground (fb->gui->display, image->image->gc,
1440               xitk_get_pixel_color_black (fb->gui->imlib_data));
1441 	  else if(k == 1)
1442             XSetForeground (fb->gui->display, image->image->gc,
1443               xitk_get_pixel_color_darkgray (fb->gui->imlib_data));
1444 	  else
1445             XSetForeground (fb->gui->display, image->image->gc,
1446               xitk_get_pixel_color_white (fb->gui->imlib_data));
1447 
1448           XDrawLine (fb->gui->display, image->image->pixmap, image->image->gc,
1449 		    points[k].x, points[k].y, points[k+1].x, points[k+1].y);
1450 	}
1451         fb->gui->x_unlock_display (fb->gui->display);
1452 
1453       }
1454     }
1455 
1456     xitk_image_change_image (fb->gui->imlib_data, fb->sort_skin_down,
1457 			    dsimage, dsimage->width, dsimage->height);
1458     xitk_image_change_image (fb->gui->imlib_data, fb->sort_skin_down,
1459 			    fsimage, fsimage->width, fsimage->height);
1460   }
1461 
1462   y += xitk_get_widget_height(fb->files_browser) + 15 + 4 + 5;
1463 
1464   cmb.skin_element_name = NULL;
1465   cmb.layer_above       = (is_layer_above());
1466   cmb.parent_wlist      = fb->widget_list;
1467   cmb.entries           = fb->file_filters;
1468   cmb.parent_wkey       = &fb->widget_key;
1469   cmb.callback          = fb_select_file_filter;
1470   cmb.userdata          = (void *)fb;
1471   fb->filters = xitk_noskin_combo_create (fb->widget_list, &cmb, x, y, w, NULL, NULL);
1472   xitk_add_widget (fb->widget_list, fb->filters);
1473   xitk_combo_set_select(fb->filters, fb->filter_selected);
1474   xitk_enable_and_show_widget(fb->filters);
1475 
1476   x = 15;
1477 
1478   cb.skin_element_name = NULL;
1479   cb.callback          = fb_hidden_files;
1480   cb.userdata          = (void *) fb;
1481   fb->show_hidden = xitk_noskin_checkbox_create (fb->widget_list, &cb, x, y+5, 10, 10);
1482   xitk_add_widget (fb->widget_list, fb->show_hidden);
1483   xitk_checkbox_set_state(fb->show_hidden, fb->show_hidden_files);
1484   xitk_enable_and_show_widget(fb->show_hidden);
1485 
1486   lbl.window            = xitk_window_get_window(fb->xwin);
1487   lbl.gc                = (XITK_WIDGET_LIST_GC(fb->widget_list));
1488   lbl.skin_element_name = NULL;
1489   lbl.label             = _("Show hidden file");
1490   lbl.callback          = fb_lbl_hidden_files;
1491   lbl.userdata          = (void *) fb;
1492   widget = xitk_noskin_label_create (fb->widget_list, &lbl, x + 15, y, w - 15, 20, fontname);
1493   xitk_add_widget (fb->widget_list, widget);
1494   xitk_enable_and_show_widget(widget);
1495 
1496   y = WINDOW_HEIGHT - (23 + 15) - (23 + 8);
1497   w = (WINDOW_WIDTH - (4 * 15)) / 3;
1498 
1499   lb.button_type       = CLICK_BUTTON;
1500   lb.label             = _("Rename");
1501   lb.align             = ALIGN_CENTER;
1502   lb.callback          = fb_rename_file;
1503   lb.state_callback    = NULL;
1504   lb.userdata          = (void *)fb;
1505   lb.skin_element_name = NULL;
1506   fb->rename = xitk_noskin_labelbutton_create (fb->widget_list,
1507     &lb, x, y, w, 23, "Black", "Black", "White", btnfontname);
1508   xitk_add_widget (fb->widget_list, fb->rename);
1509   xitk_enable_and_show_widget(fb->rename);
1510 
1511   x = (WINDOW_WIDTH - w) / 2;
1512 
1513   lb.button_type       = CLICK_BUTTON;
1514   lb.label             = _("Delete");
1515   lb.align             = ALIGN_CENTER;
1516   lb.callback          = fb_delete_file;
1517   lb.state_callback    = NULL;
1518   lb.userdata          = (void *)fb;
1519   lb.skin_element_name = NULL;
1520   fb->delete = xitk_noskin_labelbutton_create (fb->widget_list,
1521     &lb, x, y, w, 23, "Black", "Black", "White", btnfontname);
1522   xitk_add_widget (fb->widget_list, fb->delete);
1523   xitk_enable_and_show_widget(fb->delete);
1524 
1525   x = WINDOW_WIDTH - (w + 15);
1526 
1527   lb.button_type       = CLICK_BUTTON;
1528   lb.label             = _("Create a directory");
1529   lb.align             = ALIGN_CENTER;
1530   lb.callback          = fb_create_directory;
1531   lb.state_callback    = NULL;
1532   lb.userdata          = (void *)fb;
1533   lb.skin_element_name = NULL;
1534   fb->create = xitk_noskin_labelbutton_create (fb->widget_list,
1535     &lb, x, y, w, 23, "Black", "Black", "White", btnfontname);
1536   xitk_add_widget (fb->widget_list, fb->create);
1537   xitk_enable_and_show_widget(fb->create);
1538 
1539   fb->cb_buttons[0] = fb->cb_buttons[1] = NULL;
1540 
1541   y = WINDOW_HEIGHT - (23 + 15);
1542 
1543   if(fb->cbb[0].label) {
1544     x = 15;
1545 
1546     lb.button_type       = CLICK_BUTTON;
1547     lb.label             = fb->cbb[0].label;
1548     lb.align             = ALIGN_CENTER;
1549     lb.callback          = fb_callback_button_cb;
1550     lb.state_callback    = NULL;
1551     lb.userdata          = (void *)fb;
1552     lb.skin_element_name = NULL;
1553     fb->cb_buttons[0] = xitk_noskin_labelbutton_create (fb->widget_list,
1554       &lb, x, y, w, 23, "Black", "Black", "White", btnfontname);
1555     xitk_add_widget (fb->widget_list, fb->cb_buttons[0]);
1556 
1557     xitk_enable_and_show_widget(fb->cb_buttons[0]);
1558 
1559     if(fb->cbb[1].label) {
1560       x = (WINDOW_WIDTH - w) / 2;
1561 
1562       lb.button_type       = CLICK_BUTTON;
1563       lb.label             = fb->cbb[1].label;
1564       lb.align             = ALIGN_CENTER;
1565       lb.callback          = fb_callback_button_cb;
1566       lb.state_callback    = NULL;
1567       lb.userdata          = (void *)fb;
1568       lb.skin_element_name = NULL;
1569       fb->cb_buttons[1] = xitk_noskin_labelbutton_create (fb->widget_list,
1570         &lb, x, y, w, 23, "Black", "Black", "White", btnfontname);
1571       xitk_add_widget (fb->widget_list, fb->cb_buttons[1]);
1572       xitk_enable_and_show_widget(fb->cb_buttons[1]);
1573     }
1574   }
1575 
1576   x = WINDOW_WIDTH - (w + 15);
1577 
1578   lb.button_type       = CLICK_BUTTON;
1579   lb.label             = _("Close");
1580   lb.align             = ALIGN_CENTER;
1581   lb.callback          = _fb_exit;
1582   lb.state_callback    = NULL;
1583   lb.userdata          = (void *)fb;
1584   lb.skin_element_name = NULL;
1585   fb->close =  xitk_noskin_labelbutton_create (fb->widget_list,
1586     &lb, x, y, w, 23, "Black", "Black", "White", btnfontname);
1587   xitk_add_widget (fb->widget_list, fb->close);
1588   xitk_enable_and_show_widget(fb->close);
1589 
1590   xitk_window_change_background (fb->gui->imlib_data, fb->xwin, bg->pixmap, width, height);
1591   xitk_image_destroy_xitk_pixmap(bg);
1592 
1593 
1594   if(fb->cb_buttons[0])
1595     xitk_set_focus_to_widget(fb->cb_buttons[0]);
1596 
1597   {
1598     char buffer[256];
1599     snprintf(buffer, sizeof(buffer), "filebrowser%u", (unsigned int) time(NULL));
1600     fb->widget_key = xitk_register_event_handler(buffer,
1601 						 (xitk_window_get_window(fb->xwin)),
1602 						 fb_handle_events,
1603 						 NULL,
1604 						 NULL,
1605 						 fb->widget_list,
1606 						 (void *)fb);
1607   }
1608 
1609   fb->visible = 1;
1610   fb->running = 1;
1611   filebrowser_raise_window(fb);
1612 
1613   fb_getdir(fb);
1614 
1615   layer_above_video(xitk_window_get_window(fb->xwin));
1616   try_to_set_input_focus(xitk_window_get_window(fb->xwin));
1617 
1618   return fb;
1619 }
1620