]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/gtkfilesel-linux.c
fix unzip code
[xonotic/netradiant.git] / radiant / gtkfilesel-linux.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26 #define LEO
27
28 #ifdef LEO
29 #define _( a ) a
30
31 static char * back_xpm[] = {
32         "14 14 33 1",
33         "   c None",
34         ".      c #000000",
35         "+      c #C6D7C6",
36         "@      c #E7EBE7",
37         "#      c #FFFFFF",
38         "$      c #DEEBDE",
39         "%      c #F7F7F7",
40         "&      c #DEE7DE",
41         "*      c #EFF3EF",
42         "=      c #101810",
43         "-      c #B5C7AD",
44         ";      c #EFEFEF",
45         ">      c #D6E3D6",
46         ",      c #213021",
47         "'      c #315931",
48         ")      c #52824A",
49         "!      c #739A6B",
50         "~      c #84A673",
51         "{      c #7BA673",
52         "]      c #84AA73",
53         "^      c #84AA7B",
54         "/      c #84AE7B",
55         "(      c #63925A",
56         "_      c #526D4A",
57         ":      c #4A7D42",
58         "<      c #739E6B",
59         "[      c #739A63",
60         "}      c #4A7539",
61         "|      c #638E52",
62         "1      c #427139",
63         "2      c #6BA663",
64         "3      c #5A8A52",
65         "4      c #315929",
66         "            ..",
67         "          ..+.",
68         "        ..@#+.",
69         "      ..$#%%+.",
70         "    ..&#%*%%+.",
71         "  .=&#******+.",
72         "..-#;>&@****+,",
73         "..')!~{]^/^/(.",
74         "  .._:<^~^/^(.",
75         "    ..':[]~/(.",
76         "      ..}:[~|.",
77         "        ..123.",
78         "          ..4.",
79         "            .."
80 };
81
82 static char * up_xpm[] = {
83         "14 14 36 1",
84         "   c None",
85         ".      c #000000",
86         "+      c #181C18",
87         "@      c #D6DBD6",
88         "#      c #94AA8C",
89         "$      c #000400",
90         "%      c #DEDFDE",
91         "&      c #94AA84",
92         "*      c #E7E3E7",
93         "=      c #94B28C",
94         "-      c #6B865A",
95         ";      c #EFEBEF",
96         ">      c #9CB694",
97         ",      c #8CA684",
98         "'      c #EFEFEF",
99         ")      c #F7EFF7",
100         "!      c #9CB68C",
101         "~      c #63865A",
102         "{      c #94B684",
103         "]      c #94AE84",
104         "^      c #739263",
105         "/      c #F7F3F7",
106         "(      c #94B284",
107         "_      c #849E73",
108         ":      c #8CAE7B",
109         "<      c #8CAA84",
110         "[      c #7B966B",
111         "}      c #8CA67B",
112         "|      c #DEDBD6",
113         "1      c #E7E7E7",
114         "2      c #8CAE84",
115         "3      c #8CAA7B",
116         "4      c #738E63",
117         "5      c #BDBEB5",
118         "6      c #BDC3BD",
119         "7      c #637D52",
120         "      ..      ",
121         "      ..      ",
122         "     +@#$     ",
123         "     .%&.     ",
124         "    .**=-.    ",
125         "    .;;>,.    ",
126         "   .*')!&~.   ",
127         "   .;)){]^.   ",
128         "  .*')/(]_-.  ",
129         "  .;)//::<[.  ",
130         " .*')//:::}-. ",
131         " .|1;;12]3}4. ",
132         ".556666^^^^-7.",
133         ".............."
134 };
135
136 static char * forward_xpm[] = {
137         "14 14 36 1",
138         "   c None",
139         ".      c #000000",
140         "+      c #E7EBDE",
141         "@      c #FFFFFF",
142         "#      c #F7F7EF",
143         "$      c #D6E3D6",
144         "%      c #F7F7F7",
145         "&      c #EFF3EF",
146         "*      c #CEDFCE",
147         "=      c #CEDBC6",
148         "-      c #E7EFE7",
149         ";      c #181818",
150         ">      c #292829",
151         ",      c #E7EBE7",
152         "'      c #DEE7DE",
153         ")      c #B5C7AD",
154         "!      c #9CBA94",
155         "~      c #8CAE84",
156         "{      c #84AA7B",
157         "]      c #7BA673",
158         "^      c #84A67B",
159         "/      c #739A6B",
160         "(      c #5A824A",
161         "_      c #395931",
162         ":      c #9CBA8C",
163         "<      c #84AE7B",
164         "[      c #739E6B",
165         "}      c #527D4A",
166         "|      c #425942",
167         "1      c #84A673",
168         "2      c #4A7142",
169         "3      c #94B284",
170         "4      c #395D31",
171         "5      c #5A8652",
172         "6      c #315929",
173         "7      c #396531",
174         "..            ",
175         ".+..          ",
176         ".@#$..        ",
177         ".@%&#*..      ",
178         ".@%%&&%=..    ",
179         ".@&&&&&-#=;.  ",
180         ">@&&&&,'$'&)..",
181         ".!~{~{{]^/(_..",
182         ".:{<{^{[}|..  ",
183         ".:<1{/}2..    ",
184         ".31/}4..      ",
185         ".{56..        ",
186         ".7..          ",
187         "..            "
188 };
189
190 static char * refresh_xpm[] = {
191         "16 16 11 1",
192         "   c None",
193         ".      c #000000",
194         "+      c #526942",
195         "@      c #4A6139",
196         "#      c #526542",
197         "$      c #5A7142",
198         "%      c #425531",
199         "&      c #314529",
200         "*      c #425131",
201         "=      c #425931",
202         "-      c #5A754A",
203         "       .        ",
204         "      ..        ",
205         "     .+@...     ",
206         "    .#$##@%..   ",
207         "     .+#...%%.  ",
208         "   .  ..   .&.  ",
209         "  .    .    .&. ",
210         " ..          .. ",
211         " ..          .. ",
212         " .*.    .    .  ",
213         "  .*.   ..  .   ",
214         "  .%@...#=.     ",
215         "   ..##-#@#.    ",
216         "     ...@%.     ",
217         "        ..      ",
218         "        .       "
219 };
220
221 #endif
222
223 #ifndef LEO
224 #include "config.h"
225 #endif
226
227 #include <fcntl.h>
228 #include <stdio.h>
229 #include <sys/types.h>
230 #include <sys/stat.h>
231 #include <sys/param.h>
232 #include <dirent.h>
233 #include <stdlib.h>
234 #include <unistd.h>
235 #include <string.h>
236 #include <errno.h>
237 #include <pwd.h>
238 #include <grp.h>
239 #include <time.h>
240
241 #include "fnmatch.h"
242
243 #if ( defined TORRIE_DEBUG || defined LEO )
244 #include <gdk/gdkkeysyms.h>
245 #include <gtk/gtkbutton.h>
246 #include <gtk/gtkentry.h>
247 #include "gtkfilesel-linux.h"
248 #include <gtk/gtkhbox.h>
249 #include <gtk/gtkhbbox.h>
250 #include <gtk/gtklabel.h>
251 #include <gtk/gtklist.h>
252 #include <gtk/gtklistitem.h>
253 #include <gtk/gtkmain.h>
254 #include <gtk/gtkscrolledwindow.h>
255 #include <gtk/gtksignal.h>
256 #include <gtk/gtkvbox.h>
257 #include <gtk/gtkmenu.h>
258 #include <gtk/gtkmenuitem.h>
259 #include <gtk/gtkoptionmenu.h>
260 #include <gtk/gtkclist.h>
261 #include <gtk/gtkdialog.h>
262 #include <gtk/gtkcombo.h>
263 #include <gtk/gtkframe.h>
264 #include <gtk/gtkhpaned.h>
265 #include <gtk/gtktable.h>
266 #include <gtk/gtkpixmap.h>
267 #include <gtk/gtknotebook.h>
268 #include <gtk/gtkhseparator.h>
269 #include <gtk/gtktogglebutton.h>
270 #else
271 #include "gdk/gdkkeysyms.h"
272 #include "gtkbutton.h"
273 #include "gtkentry.h"
274 #include "gtkfilesel.h"
275 #include "gtkhbox.h"
276 #include "gtkhbbox.h"
277 #include "gtklabel.h"
278 #include "gtklist.h"
279 #include "gtklistitem.h"
280 #include "gtkmain.h"
281 #include "gtkscrolledwindow.h"
282 #include "gtksignal.h"
283 #include "gtkvbox.h"
284 #include "gtkmenu.h"
285 #include "gtkmenuitem.h"
286 #include "gtkoptionmenu.h"
287 #include "gtkclist.h"
288 #include "gtkdialog.h"
289 #include "gtkcombo.h"
290 #include "gtkframe.h"
291 #include "gtkhpaned.h"
292 #include "gtktable.h"
293 #include "gtkpixmap.h"
294 #include "gtknotebook.h"
295 #include "gtkhseparator.h"
296 #include "gtktogglebutton.h"
297 #endif
298
299 #ifndef LEO
300 #include "gtkintl.h"
301
302 #include "back.xpm"
303 #include "up.xpm"
304 #include "forward.xpm"
305 #include "refresh.xpm"
306 #endif
307
308 #define DIR_LIST_WIDTH   180
309 #define DIR_LIST_HEIGHT  180
310 #define FILE_LIST_WIDTH  180
311 #define FILE_LIST_HEIGHT 180
312 #define BOOKMARK_FILE "/.gtkfilesel_bookmarks"
313 #define MASK_FILE "/.gtkfilesel_masks"
314 #define TIME_STRING_BUF 50
315
316 /* I've put this here so it doesn't get confused with the
317  * file completion interface */
318 typedef struct _HistoryCallbackArg HistoryCallbackArg;
319
320 struct _HistoryCallbackArg
321 {
322         gchar *directory;
323         GtkWidget *menu_item;
324 };
325
326
327 typedef struct _BookmarkMenuStruct BookmarkMenuStruct;
328 struct _BookmarkMenuStruct {
329         GtkWidget *menu_item;
330         gchar     *desc;
331         gchar     *path;
332 };
333
334 typedef struct _CompletionState CompletionState;
335 typedef struct _CompletionDir CompletionDir;
336 typedef struct _CompletionDirSent CompletionDirSent;
337 typedef struct _CompletionDirEntry CompletionDirEntry;
338 typedef struct _CompletionUserDir CompletionUserDir;
339 typedef struct _PossibleCompletion PossibleCompletion;
340
341 /* Non-external file completion decls and structures */
342
343 /* A contant telling PRCS how many directories to cache.  Its actually
344  * kept in a list, so the geometry isn't important. */
345 #define CMPL_DIRECTORY_CACHE_SIZE 10
346
347 /* A constant used to determine whether a substring was an exact
348  * match by first_diff_index()
349  */
350 #define PATTERN_MATCH -1
351 /* The arguments used by all fnmatch() calls below
352  */
353 #define FNMATCH_FLAGS ( FNM_PATHNAME | FNM_PERIOD )
354
355 #define CMPL_ERRNO_TOO_LONG ( ( 1 << 16 ) - 1 )
356
357 /* This structure contains all the useful information about a directory
358  * for the purposes of filename completion.  These structures are cached
359  * in the CompletionState struct.  CompletionDir's are reference counted.
360  */
361 struct _CompletionDirSent
362 {
363         ino_t inode;
364         time_t mtime;
365         dev_t device;
366
367         gint entry_count;
368         gchar *name_buffer; /* memory segment containing names of all entries */
369
370         struct _CompletionDirEntry *entries;
371 };
372
373 struct _CompletionDir
374 {
375         CompletionDirSent *sent;
376
377         gchar *fullname;
378         gint fullname_len;
379
380         struct _CompletionDir *cmpl_parent;
381         gint cmpl_index;
382         gchar *cmpl_text;
383 };
384
385 /* This structure contains pairs of directory entry names with a flag saying
386  * whether or not they are a valid directory.  NOTE: This information is used
387  * to provide the caller with information about whether to update its completions
388  * or try to open a file.  Since directories are cached by the directory mtime,
389  * a symlink which points to an invalid file (which will not be a directory),
390  * will not be reevaluated if that file is created, unless the containing
391  * directory is touched.  I consider this case to be worth ignoring (josh).
392  */
393 struct _CompletionDirEntry
394 {
395         gint is_dir;
396         gchar *entry_name;
397 };
398
399 struct _CompletionUserDir
400 {
401         gchar *login;
402         gchar *homedir;
403 };
404
405 struct _PossibleCompletion
406 {
407         /* accessible fields, all are accessed externally by functions
408          * declared above
409          */
410         gchar *text;
411         gint is_a_completion;
412         gint is_directory;
413
414         gint file_size;
415         gint file_time;
416         gint uid;
417         gint gid;
418         /* Private fields
419          */
420         gint text_alloc;
421 };
422
423 struct _CompletionState
424 {
425         gint last_valid_char;
426         gchar *updated_text;
427         gint updated_text_len;
428         gint updated_text_alloc;
429         gint re_complete;
430
431         gchar *user_dir_name_buffer;
432         gint user_directories_len;
433
434         gchar *last_completion_text;
435
436         gint user_completion_index; /* if >= 0, currently completing ~user */
437
438         struct _CompletionDir *completion_dir; /* directory completing from */
439         struct _CompletionDir *active_completion_dir;
440
441         struct _PossibleCompletion the_completion;
442
443         struct _CompletionDir *reference_dir; /* initial directory */
444
445         GList* directory_storage;
446         GList* directory_sent_storage;
447
448         struct _CompletionUserDir *user_directories;
449 };
450
451 /* Widgets from the Properties Dialog */
452 typedef struct _PropertiesPrivate PropertiesPrivate;
453
454 struct _PropertiesPrivate
455 {
456         GtkWidget *mode_label;
457         GtkWidget *mode_buttons[12];
458 };
459
460 /* pixmap creation function */
461 GtkWidget*                 create_pixmap( GtkWidget *widget,
462                                                                                   const gchar *pixmap_char );
463
464 /* File completion functions which would be external, were they used
465  * outside of this file.
466  */
467
468 static CompletionState*    cmpl_init_state( void );
469 static void                cmpl_free_state( CompletionState *cmpl_state );
470 static gint                cmpl_state_okay( CompletionState* cmpl_state );
471 static gchar*              cmpl_strerror( gint );
472
473 static PossibleCompletion* cmpl_completion_matches( gchar           *text_to_complete,
474                                                                                                         gchar          **remaining_text,
475                                                                                                         CompletionState *cmpl_state );
476
477 /* Returns a name for consideration, possibly a completion, this name
478  * will be invalid after the next call to cmpl_next_completion.
479  */
480 static char*               cmpl_this_completion( PossibleCompletion* );
481
482 /* True if this completion matches the given text.  Otherwise, this
483  * output can be used to have a list of non-completions.
484  */
485 static gint                cmpl_is_a_completion( PossibleCompletion* );
486
487 /* True if the completion is a directory
488  */
489 static gint                cmpl_is_directory( PossibleCompletion* );
490
491 /* Obtains the next completion, or NULL
492  */
493 static PossibleCompletion* cmpl_next_completion( CompletionState* );
494
495 /* Updating completions: the return value of cmpl_updated_text() will
496  * be text_to_complete completed as much as possible after the most
497  * recent call to cmpl_completion_matches.  For the present
498  * application, this is the suggested replacement for the user's input
499  * string.  You must CALL THIS AFTER ALL cmpl_text_completions have
500  * been received.
501  */
502 static gchar*              cmpl_updated_text( CompletionState* cmpl_state );
503
504 /* After updating, to see if the completion was a directory, call
505  * this.  If it was, you should consider re-calling completion_matches.
506  */
507 static gint                cmpl_updated_dir( CompletionState* cmpl_state );
508
509 /* Current location: if using file completion, return the current
510  * directory, from which file completion begins.  More specifically,
511  * the cwd concatenated with all exact completions up to the last
512  * directory delimiter('/').
513  */
514 static gchar*              cmpl_reference_position( CompletionState* cmpl_state );
515
516 /* backing up: if cmpl_completion_matches returns NULL, you may query
517  * the index of the last completable character into cmpl_updated_text.
518  */
519 static gint                cmpl_last_valid_char( CompletionState* cmpl_state );
520
521 /* When the user selects a non-directory, call cmpl_completion_fullname
522  * to get the full name of the selected file.
523  */
524 static gchar*              cmpl_completion_fullname( gchar*, CompletionState* cmpl_state );
525
526
527 /* Directory operations. */
528 static CompletionDir* open_ref_dir( gchar* text_to_complete,
529                                                                         gchar** remaining_text,
530                                                                         CompletionState* cmpl_state );
531 static gboolean       check_dir( gchar *dir_name,
532                                                                  struct stat *result,
533                                                                  gboolean *stat_subdirs );
534 static CompletionDir* open_dir( gchar* dir_name,
535                                                                 CompletionState* cmpl_state );
536 static CompletionDir* open_user_dir( gchar* text_to_complete,
537                                                                          CompletionState *cmpl_state );
538 static CompletionDir* open_relative_dir( gchar* dir_name, CompletionDir* dir,
539                                                                                  CompletionState *cmpl_state );
540 static CompletionDirSent* open_new_dir( gchar* dir_name,
541                                                                                 struct stat* sbuf,
542                                                                                 gboolean stat_subdirs );
543 static gint           correct_dir_fullname( CompletionDir* cmpl_dir );
544 static gint           correct_parent( CompletionDir* cmpl_dir,
545                                                                           struct stat *sbuf );
546 static gchar*         find_parent_dir_fullname( gchar* dirname );
547 static CompletionDir* attach_dir( CompletionDirSent* sent,
548                                                                   gchar* dir_name,
549                                                                   CompletionState *cmpl_state );
550 static void           free_dir_sent( CompletionDirSent* sent );
551 static void           free_dir( CompletionDir  *dir );
552 static void           prune_memory_usage( CompletionState *cmpl_state );
553
554 /* Completion operations */
555 static PossibleCompletion* attempt_homedir_completion( gchar* text_to_complete,
556                                                                                                            CompletionState *cmpl_state );
557 static PossibleCompletion* attempt_file_completion( CompletionState *cmpl_state );
558 static CompletionDir* find_completion_dir( gchar* text_to_complete,
559                                                                                    gchar** remaining_text,
560                                                                                    CompletionState* cmpl_state );
561 static PossibleCompletion* append_completion_text( gchar* text,
562                                                                                                    CompletionState* cmpl_state );
563 static gint get_pwdb( CompletionState* cmpl_state );
564 static gint first_diff_index( gchar* pat, gchar* text );
565 static gint compare_user_dir( const void* a, const void* b );
566 static gint compare_cmpl_dir( const void* a, const void* b );
567 static void update_cmpl( PossibleCompletion* poss,
568                                                  CompletionState* cmpl_state );
569
570 static void gtk_file_selection_class_init( GtkFileSelectionClass *klass );
571 static void gtk_file_selection_init( GtkFileSelection      *filesel );
572 static void gtk_file_selection_realize( GtkWidget             *widget );
573 static void gtk_file_selection_destroy( GtkObject             *object );
574 static gint gtk_file_selection_key_press( GtkWidget             *widget,
575                                                                                   GdkEventKey           *event,
576                                                                                   gpointer user_data );
577
578 static void gtk_file_selection_file_button( GtkWidget *widget,
579                                                                                         gint row,
580                                                                                         gint column,
581                                                                                         GdkEventButton *bevent,
582                                                                                         gpointer user_data );
583
584 static void gtk_file_selection_dir_button( GtkWidget *widget,
585                                                                                    gint row,
586                                                                                    gint column,
587                                                                                    GdkEventButton *bevent,
588                                                                                    gpointer data );
589
590 static void gtk_file_selection_undir_button( GtkWidget *widget,
591                                                                                          gint row,
592                                                                                          gint column,
593                                                                                          GdkEventButton *bevent,
594                                                                                          gpointer data );
595
596 static void gtk_file_selection_populate( GtkFileSelection      *fs,
597                                                                                  gchar                 *rel_path,
598                                                                                  gint try_complete );
599 static void gtk_file_selection_abort( GtkFileSelection      *fs );
600
601 static void gtk_file_selection_update_history_menu( GtkFileSelection       *fs,
602                                                                                                         gchar                  *current_dir );
603
604 static void gtk_file_selection_create_dir( gpointer data );
605 static void gtk_file_selection_delete_file( gpointer data );
606 static void gtk_file_selection_rename_file( gpointer data );
607 static void gtk_file_selection_properties( gpointer data );
608 static void gtk_file_selection_properties_update_mode( GtkWidget *widget, gpointer data );
609 static mode_t gtk_file_selection_properties_get_mode( PropertiesPrivate* private );
610
611 static gboolean gtk_file_selection_history_combo_callback( GtkWidget *widget, GdkEventKey *event, gpointer data );
612 static gboolean gtk_file_selection_history_combo_list_key_handler( GtkWidget *widget,
613                                                                                                                                    GdkEventKey *event,
614                                                                                                                                    gpointer user_data );
615 static gboolean gtk_file_selection_history_combo_list_callback( GtkWidget *thelist,
616                                                                                                                                 GdkEventButton *event,
617
618                                                                                                                                 gpointer user_data );
619 static void gtk_file_selection_bookmark_callback( GtkWidget *widget, gpointer data );
620 static void gtk_file_selection_mask_entry_callback( GtkWidget *widget, gpointer data );
621 static gint gtk_file_selection_mask_entry_key_callback( GtkWidget *widget, GdkEventKey *event, gpointer data );
622 static gint gtk_file_selection_mask_entry_button_callback( GtkWidget *widget, GdkEventButton *event, gpointer data );
623
624 //static void gtk_file_selection_home_button (GtkWidget *widget, gpointer data);
625 static void gtk_file_selection_bookmark_button( GtkWidget *widget,
626                                                                                                 GtkFileSelection *fs );
627
628 static void gtk_file_selection_up_button( GtkWidget *widget, gpointer data );
629 static void gtk_file_selection_prev_button( GtkWidget *widget, gpointer data );
630 static void gtk_file_selection_next_button( GtkWidget *widget, gpointer data );
631 static void gtk_file_selection_refresh_button( GtkWidget *widget, gpointer data );
632
633 static gint gtk_file_selection_files_list_key_callback( GtkWidget *widget, GdkEventKey *event, gpointer data );
634
635
636 static gint gtk_file_selection_match_char( gchar, gchar *mask );
637 static gint gtk_file_selection_match_mask( gchar *,gchar * );
638
639 static void gtk_file_selection_load_bookmarks( GtkFileSelection *fs );
640 static void gtk_file_selection_add_bookmark( GtkFileSelection *fs, gchar *desc, gchar *path );
641 gint gtk_file_selection_save_bookmarks( GtkFileSelection *fs );
642
643 static void gtk_file_selection_load_masks( GtkFileSelection *fs );
644
645 static gint gtk_file_selection_show_fileop_menu( GtkCList *clist,
646                                                                                                  GdkEvent *event,
647                                                                                                  GtkFileSelection *fs );
648
649
650 static GtkWindowClass *parent_class = NULL;
651
652 /* Saves errno when something cmpl does fails. */
653 static gint cmpl_errno;
654
655 #ifdef G_WITH_CYGWIN
656 /*
657  * Take the path currently in the file selection
658  * entry field and translate as necessary from
659  * a WIN32 style to CYGWIN32 style path.  For
660  * instance translate:
661  * x:\somepath\file.jpg
662  * to:
663  * //x/somepath/file.jpg
664  *
665  * Replace the path in the selection text field.
666  * Return a boolean value concerning whether a
667  * translation had to be made.
668  */
669 int
670 translate_win32_path( GtkFileSelection *filesel ){
671         int updated = 0;
672         gchar *path;
673
674         /*
675          * Retrieve the current path
676          */
677         path = gtk_entry_get_text( GTK_ENTRY( filesel->selection_entry ) );
678
679         /*
680          * Translate only if this looks like a DOS-ish
681          * path... First handle any drive letters.
682          */
683         if ( isalpha( path[0] ) && ( path[1] == ':' ) ) {
684                 /*
685                  * This part kind of stinks... It isn't possible
686                  * to know if there is enough space in the current
687                  * string for the extra character required in this
688                  * conversion.  Assume that there isn't enough space
689                  * and use the set function on the text field to
690                  * set the newly created string.
691                  */
692                 gchar *newPath = g_strdup_printf( "//%c/%s", path[0], ( path + 3 ) );
693                 gtk_entry_set_text( GTK_ENTRY( filesel->selection_entry ), newPath );
694
695                 path = newPath;
696                 updated = 1;
697         }
698
699         /*
700          * Now, replace backslashes with forward slashes
701          * if necessary.
702          */
703         if ( strchr( path, '\\' ) ) {
704                 int index;
705                 for ( index = 0; path[index] != '\0'; index++ )
706                         if ( path[index] == '\\' ) {
707                                 path[index] = '/';
708                         }
709
710                 updated = 1;
711         }
712
713         return updated;
714 }
715 #endif
716
717 /* General notes:
718  * Make prev and next inactive if their respective *
719  *   histories are empty.
720  * Add facilities for handling hidden files and    *
721  * directories                                     *
722  * Add an api to access the mask, and hidden files *
723  * check box?  (prob not in 1.2.x series)          *
724  */
725
726 /* Routine for applying mask to filenames         *
727  *   Need to be optimized to minimize recursion   *
728  *     help the for loop by looking for the next  *
729  *     instance of the mask character following   *
730  *     the '*'.  ei *.c -- look for '.'           *
731  *     Also, swap all *? pairs (-> ?*), as that   *
732  *     will make it possible to look ahead (?     *
733  *     makes it very nondeterministic as in *?.c  *
734  *     which really is ?*.c                       *
735  *                                                *
736  */
737 static gint gtk_file_selection_match_char( gchar text, gchar *mask ){
738         gchar *maskc;
739         gint x;
740         gint s;
741         gchar lastc;
742         gchar nextc;
743
744         if ( mask[0] == '[' ) {
745                 if ( !strchr( mask,']' ) ) {
746                         return 0;
747                 }
748                 lastc = 0;
749
750                 maskc = g_strdup( mask + 1 ); /* get the portion of mask inside []*/
751                 ( *( strchr( maskc + 1,']' ) ) ) = 0;
752                 s = strlen( (char *)maskc );
753
754                 for ( x = 0 ; x < s ; x++ ) {
755                         if ( maskc[x] == '-' ) {
756                                 if ( x == s ) {
757                                         return 1;
758                                 }
759                                 nextc = maskc[x + 1];
760
761                                 if ( nextc > lastc ) {
762                                         if ( ( lastc <= text ) && ( nextc >= text ) ) {
763                                                 g_free( maskc );
764                                                 return s + 2;
765                                         }
766                                 }
767                                 else if ( ( lastc >= text ) && ( nextc <= text ) ) {
768                                         g_free( maskc );
769                                         return s + 2;
770                                 }
771                         }
772                         else if ( text == maskc[x] ) {
773                                 g_free( maskc );
774                                 return s + 2;
775                         }
776                         lastc = maskc[x];
777                 }
778                 g_free( maskc );
779
780                 return 0;
781         }
782
783         if ( mask[0] == '?' ) {
784                 return 1;
785         }
786         if ( mask[0] == text ) {
787                 return 1;
788         }
789
790         return 0;
791 }
792
793
794 static gint gtk_file_selection_match_mask1( gchar *text, gchar *mask ){
795
796         int mc;
797         int tc;
798
799         tc = 0; mc = 0;
800
801         if ( mask[0] == 0 && text[0] == 0 ) {
802                 return 1;
803         }
804
805         if ( mask[0] == '*' ) {
806                 for ( tc = 0; tc <= strlen( text ); tc++ )
807                 {
808                         if ( gtk_file_selection_match_mask1( text + tc, mask + 1 ) ) {
809                                 return 1;
810                         }
811                 }
812                 return 0;
813         }
814         mc = gtk_file_selection_match_char( text[0], mask );
815
816         if ( mc ) {
817                 return gtk_file_selection_match_mask1( text + 1, mask + mc );
818         }
819         else{
820                 return 0;
821         }
822 }
823
824 static gint gtk_file_selection_match_mask( gchar *text, gchar *mask ){
825         gchar *masks;
826         gchar *bmask;
827         gchar *emask;
828
829         masks = g_strdup( mask );
830
831         emask = strchr( masks,'<' );
832         if ( emask ) {
833                 bmask = emask + 1;
834                 emask = strchr( bmask,'>' );
835                 if ( emask ) {
836                         *emask = 0;
837                 }
838         }
839         else{
840                 bmask = masks;
841         }
842
843         do {
844                 if ( ( emask = strchr( bmask,',' ) ) || ( emask = strchr( bmask,';' ) ) ) {
845                         *emask = 0;
846                         if ( gtk_file_selection_match_mask1( text, bmask ) ) {
847                                 g_free( masks );
848                                 return 1;
849                         }
850
851                         bmask = emask + 1;
852                 }
853         } while ( emask );
854
855         if ( gtk_file_selection_match_mask1( text, bmask ) ) {
856                 g_free( masks );
857                 return 1;
858         }
859         g_free( masks );
860         return 0;
861 }
862
863 static void
864 gtk_file_selection_load_bookmarks( GtkFileSelection *fs ){
865         GList *list;
866         gchar *bookmark_file;
867         gchar *bookmark_data;
868         struct stat file_info;
869         gint file;
870         gint lp;
871         gint cp;
872         BookmarkMenuStruct *item;
873
874
875         if ( fs->bookmark_list ) { //erase
876                 list = fs->bookmark_list;
877                 while ( list ) {
878                         item = list->data;
879                         g_free( item->desc );
880                         g_free( item->path );
881                         g_free( item );
882                         list = list->next;
883                 }
884                 g_list_free( fs->bookmark_list );
885                 fs->bookmark_list = NULL;
886                 gtk_widget_destroy( fs->bookmark_menu );
887         }
888
889         fs->bookmark_menu = gtk_menu_new();
890
891         /* spacer */
892         item = g_malloc( sizeof( item ) );
893         item->menu_item = gtk_menu_item_new();
894         gtk_widget_show( item->menu_item );
895         gtk_menu_append( GTK_MENU( fs->bookmark_menu ), item->menu_item );
896
897         item = g_malloc( sizeof( item ) );
898         item->desc = g_strdup( "Add bookmark" );
899         item->path = g_strdup( "." );
900         item->menu_item = gtk_menu_item_new_with_label( item->desc );
901         gtk_widget_show( item->menu_item );
902         //fs->bookmark_list=g_list_append(fs->bookmark_list,item);
903         //set signal here!!
904         gtk_menu_append( GTK_MENU( fs->bookmark_menu ), item->menu_item );
905
906         item = g_malloc( sizeof( item ) );
907         item->desc = g_strdup( "Edit bookmark" );
908         item->path = g_strdup( "." );
909         item->menu_item = gtk_menu_item_new_with_label( item->desc );
910         gtk_widget_show( item->menu_item );
911         //fs->bookmark_list=g_list_append(fs->bookmark_list,item);
912         //set signal here!!
913         gtk_menu_append( GTK_MENU( fs->bookmark_menu ), item->menu_item );
914
915         bookmark_file = g_strconcat( g_get_home_dir(), BOOKMARK_FILE,NULL );
916         if ( !stat( bookmark_file,&file_info ) && ( file = open( bookmark_file,  O_RDONLY ) ) > 0 ) {
917                 if ( file_info.st_size < 65536 ) {
918                         bookmark_data = g_malloc( file_info.st_size );
919
920                         if ( file && read( file, bookmark_data, file_info.st_size ) ) {
921                                 cp = lp = 0;
922
923                                 while ( cp < file_info.st_size )
924                                 {
925                                         while ( cp < file_info.st_size && bookmark_data[cp] != '<' )
926                                                 cp++;
927                                         bookmark_data[cp] = 0;
928                                         item = g_malloc( sizeof( BookmarkMenuStruct ) );
929                                         item->desc = g_strdup( bookmark_data + lp );
930                                         lp = ++cp;
931
932                                         while ( cp < file_info.st_size && bookmark_data[cp] != '>' )
933                                                 cp++;
934
935                                         bookmark_data[cp] = 0;
936                                         //create menu items
937                                         item->path = g_strdup( bookmark_data + lp );
938                                         gtk_file_selection_add_bookmark( (gpointer) fs, (gpointer) item->desc, (gpointer) item->path );
939
940                                         cp++;
941
942                                         while ( cp < file_info.st_size && bookmark_data[cp] < 33 )
943                                                 cp++;
944                                         lp = cp;
945                                 }
946                         }
947
948                         close( file );
949                 }
950         }
951         else {
952
953                 /* Add some default items, then save off to bookmarks file */
954
955                 gtk_file_selection_add_bookmark( (gpointer) fs, "Home", "~/" );
956                 gtk_file_selection_add_bookmark( (gpointer) fs, "Root", "/" );
957
958                 gtk_file_selection_save_bookmarks( (gpointer) fs );
959         }
960 }
961
962 static void
963 gtk_file_selection_add_bookmark( GtkFileSelection *fs, gchar *desc, gchar *path ){
964         /* Add item to menu */
965         BookmarkMenuStruct *item;
966         item = g_malloc( sizeof( item ) );
967         item->desc = (gpointer) desc;
968         item->path = (gpointer) path;
969         item->menu_item = gtk_menu_item_new_with_label( item->desc );
970         gtk_widget_show( item->menu_item );
971         fs->bookmark_list = g_list_append( fs->bookmark_list,item );
972         gtk_signal_connect( GTK_OBJECT( item->menu_item ), "activate",
973                                                 (GtkSignalFunc) gtk_file_selection_bookmark_callback,
974                                                 (gpointer) fs );
975         gtk_menu_insert( GTK_MENU( fs->bookmark_menu ), item->menu_item, g_list_length( fs->bookmark_list ) - 1 );
976 }
977
978 gint
979 gtk_file_selection_save_bookmarks( GtkFileSelection *fs ){
980         BookmarkMenuStruct *item;
981         gchar *bookmark_file;
982         gchar *item_data;
983         gint file;
984         GList *list;
985
986         bookmark_file = g_strconcat( g_get_home_dir(), BOOKMARK_FILE,NULL );
987
988         if ( ( file = open( bookmark_file, O_CREAT | O_WRONLY | O_TRUNC, 0600 ) ) > 0 ) {
989                 for ( list = g_list_first( fs->bookmark_list ); list != NULL; list = g_list_next( list ) ) {
990                         item = list->data;
991                         item_data = g_strconcat( item->desc, " <", item->path, ">\n", NULL );
992                         if ( write( file, item_data, strlen( item_data ) ) != strlen( item_data ) ) {
993                                 return TRUE;
994                         }
995                         g_free( item_data );
996                 }
997
998                 close( file );
999         }
1000         else {
1001                 return TRUE;
1002         }
1003
1004         return FALSE;
1005 }
1006
1007 static void
1008 gtk_file_selection_load_masks( GtkFileSelection *fs ){
1009         /*
1010            GList *list;
1011            gchar *masks_file;
1012            gchar *masks_data;
1013            struct stat file_info;
1014            gint   file;
1015            gint   lp;
1016            gint   cp;
1017
1018            if(fs->masks){
1019            list=fs->masks;
1020            while(list){
1021             g_free(list->data);
1022             list=list->next;
1023            }
1024            fs->masks = NULL;
1025            }
1026
1027            masks_file=g_strconcat(g_get_home_dir(), MASK_FILE,NULL); //put in #define
1028            if(!stat(masks_file,&file_info))
1029            {
1030            if(file_info.st_size <65536 )
1031            {
1032             masks_data=g_malloc(file_info.st_size);
1033
1034             file = open(masks_file,  O_RDONLY );
1035
1036             if(file && read(file, masks_data, file_info.st_size))
1037             {
1038               cp=lp=0;
1039
1040               while (cp < file_info.st_size)
1041               {
1042                 while (cp < file_info.st_size && masks_data[cp] != '>' )
1043                   cp++;
1044
1045                 masks_data[++cp]=0;
1046                 if (masks_data[lp]=='<') { //if there was no description, strip off brackets
1047                   lp++;
1048                   masks_data[cp-1]=0;
1049                 }
1050            //          g_print("%s\n",masks_data+lp);
1051                 fs->masks = g_list_append(fs->masks, g_strdup(masks_data+lp));
1052
1053                 while(cp < file_info.st_size && masks_data[cp] < 33 )
1054                   cp++;
1055                 lp=cp;
1056               }
1057             }
1058
1059             close(file);
1060            }
1061            }
1062          */
1063         if ( !fs->masks ) {
1064                 /* masks is still null, fill it with default data... */
1065                 /*
1066                    fs->masks = g_list_append(fs->masks, "all files <*>");
1067                    fs->masks = g_list_append(fs->masks, "mp3s/playlists <*.mp3,*.m3u>");
1068                    fs->masks = g_list_append(fs->masks, "src/hdr <*.[CcHh],*.[Cc][Cc],*.[Hh][Hh],*.cpp>");
1069                    fs->masks = g_list_append(fs->masks, "html docs <*.html,*.htm,*.HTM,*.php*,*.inc>");
1070                    fs->masks = g_list_append(fs->masks, "images <*.png,*.jpg,*.jpeg,*.gif,*.xpm,*.tiff>");
1071                    fs->masks = g_list_append(fs->masks, "package <*.rpm,*.deb>");
1072                    fs->masks = g_list_append(fs->masks, "archive <*.tgz,*.tb2,*.tar*,*.zip,*.rar>");
1073                    fs->masks = g_list_append(fs->masks, "compressed <*.Z,*.gz,*.bz2>");
1074                  */
1075         }
1076 }
1077
1078 void gtk_file_selection_clear_masks( GtkFileSelection *filesel ){
1079         GList *list;
1080
1081         g_return_if_fail( filesel != NULL );
1082         g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
1083
1084         list = filesel->masks;
1085         while ( list )
1086         {
1087                 g_free( list->data );
1088                 list = list->next;
1089         }
1090         filesel->masks = NULL;
1091
1092         gtk_list_clear_items( GTK_LIST( GTK_COMBO( filesel->mask_entry )->list ), 0, -1 );
1093 }
1094
1095 void gtk_file_selection_set_masks( GtkFileSelection *filesel, const gchar **masks ){
1096         g_return_if_fail( filesel != NULL );
1097         g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
1098
1099         while ( *masks )
1100         {
1101                 filesel->masks = g_list_append( filesel->masks, ( gpointer ) * masks );
1102                 masks++;
1103         }
1104
1105         if ( filesel->masks ) {
1106                 gtk_combo_set_popdown_strings( GTK_COMBO( filesel->mask_entry ), filesel->masks );
1107         }
1108 }
1109
1110 GtkType
1111 gtk_file_selection_get_type( void ){
1112         static GtkType file_selection_type = 0;
1113
1114         if ( !file_selection_type ) {
1115                 static const GtkTypeInfo filesel_info =
1116                 {
1117                         "GtkFileSelection",
1118                         sizeof( GtkFileSelection ),
1119                         sizeof( GtkFileSelectionClass ),
1120                         (GtkClassInitFunc) gtk_file_selection_class_init,
1121                         (GtkObjectInitFunc) gtk_file_selection_init,
1122                         /* reserved_1 */ NULL,
1123                         /* reserved_2 */ NULL,
1124                         (GtkClassInitFunc) NULL,
1125                 };
1126
1127                 file_selection_type = gtk_type_unique( GTK_TYPE_WINDOW, &filesel_info );
1128         }
1129
1130         return file_selection_type;
1131 }
1132
1133 static void
1134 gtk_file_selection_class_init( GtkFileSelectionClass *class ){
1135         GtkObjectClass *object_class;
1136         GtkWidgetClass *widget_class;
1137
1138         object_class = (GtkObjectClass*) class;
1139
1140         parent_class = gtk_type_class( GTK_TYPE_WINDOW );
1141
1142         widget_class = GTK_WIDGET_CLASS( class );
1143
1144         widget_class->realize = gtk_file_selection_realize;
1145         object_class->destroy = gtk_file_selection_destroy;
1146 }
1147
1148 static void
1149 gtk_file_selection_init( GtkFileSelection *filesel ){
1150         GtkWidget *entry_vbox;
1151         GtkWidget *label;
1152         GtkWidget *list_vbox;
1153         GtkWidget *confirm_area;
1154         GtkWidget *vbox;
1155         GtkWidget *hbox;
1156         GtkWidget *hbox2;
1157         GtkWidget *table;
1158         GtkWidget *pulldown_hbox;
1159         GtkWidget *scrolled_win;
1160         GtkWidget *mask_label;
1161         GtkWidget *bigframe;
1162         GtkWidget *button;
1163         GtkWidget *hpaned;
1164         GtkWidget *menu_item;
1165         GtkWidget *pixmap;
1166
1167         char *dir_title [2];
1168         char *file_title [2];
1169
1170         filesel->cmpl_state = cmpl_init_state();
1171
1172         filesel->mask = NULL;
1173         filesel->prev_history = NULL;
1174         filesel->next_history = NULL;
1175         filesel->saved_entry = NULL;
1176         filesel->bookmark_list = NULL;
1177         filesel->masks = NULL;
1178         filesel->selection_text = NULL;
1179         filesel->fileop_data = NULL;
1180
1181         gtk_file_selection_load_masks( filesel );
1182         gtk_file_selection_load_bookmarks( filesel );
1183
1184         /* The dialog-sized vertical box  */
1185         filesel->main_vbox = gtk_vbox_new( FALSE, 0 );
1186         gtk_container_set_border_width( GTK_CONTAINER( filesel ), 0 );
1187         gtk_container_add( GTK_CONTAINER( filesel ), filesel->main_vbox );
1188         gtk_widget_show( filesel->main_vbox );
1189
1190         /* hbox for pulldown menu */
1191         pulldown_hbox = gtk_hbox_new( FALSE, 0 );
1192         gtk_box_pack_start( GTK_BOX( filesel->main_vbox ), pulldown_hbox, FALSE, FALSE, 0 );
1193         gtk_widget_show( pulldown_hbox );
1194
1195         /* The horizontal box containing create, rename etc. buttons */
1196
1197 /*
1198    filesel->button_area = gtk_hbutton_box_new ();
1199    gtk_button_box_set_layout(GTK_BUTTON_BOX(filesel->button_area), GTK_BUTTONBOX_START);
1200    gtk_button_box_set_spacing(GTK_BUTTON_BOX(filesel->button_area), 0);
1201    gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->button_area,
1202               FALSE, FALSE, 0);
1203    gtk_button_box_set_child_size(GTK_BUTTON_BOX(filesel->button_area),0,0);
1204    gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX(filesel->button_area),0,0);
1205  */
1206
1207         filesel->button_area = gtk_hbox_new( TRUE,0 );
1208         //gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->button_area,
1209         //                    FALSE, FALSE, 0);
1210
1211         //gtk_widget_show (filesel->button_area);
1212
1213         gtk_file_selection_show_fileop_buttons( filesel );
1214         /*  frame to put the following hbox in  */
1215         bigframe = gtk_frame_new( NULL );
1216         gtk_box_pack_start( GTK_BOX( filesel->main_vbox ), bigframe, TRUE, TRUE, 0 );
1217         gtk_frame_set_shadow_type( GTK_FRAME( bigframe ), GTK_SHADOW_OUT );
1218         gtk_widget_show( bigframe );
1219
1220
1221         list_vbox = gtk_vbox_new( FALSE,3 );
1222         gtk_widget_show( list_vbox );
1223         gtk_container_add( GTK_CONTAINER( bigframe ), list_vbox );
1224         gtk_container_set_border_width( GTK_CONTAINER( list_vbox ),2 );
1225         gtk_widget_show( list_vbox );
1226
1227         /*  The horizontal box containing the directory and file listboxes  */
1228 //  list_hbox = gtk_hbox_new (FALSE, 3);
1229         //gtk_container_add (GTK_CONTAINER(bigframe), list_hbox);
1230         //gtk_container_set_border_width (GTK_CONTAINER (list_hbox), 3);
1231 //  gtk_box_pack_start(GTK_BOX(list_vbox), list_hbox, FALSE,FALSE,0);
1232 //  gtk_widget_show (list_hbox);
1233
1234         hpaned = gtk_hpaned_new();
1235         gtk_widget_show( hpaned );
1236         gtk_container_set_border_width( GTK_CONTAINER( hpaned ), 1 );
1237         gtk_paned_set_gutter_size( GTK_PANED( hpaned ), 10 );
1238         gtk_box_pack_start( GTK_BOX( list_vbox ), hpaned,TRUE,TRUE,0 );
1239
1240         /* vbox to put the buttons and directory listing in  */
1241         vbox = gtk_vbox_new( FALSE, 3 );
1242         gtk_widget_show( vbox );
1243         gtk_container_add( GTK_CONTAINER( hpaned ),vbox );
1244         //gtk_box_pack_start (GTK_BOX (hpaned), vbox, FALSE, FALSE, 0);
1245
1246         hbox = gtk_hbox_new( FALSE, 4 );
1247         gtk_widget_show( hbox );
1248         gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 0 );
1249
1250 //  home_button = gtk_button_new_with_label (_("Home"));
1251 //  gtk_widget_show (home_button);
1252 //  gtk_signal_connect (GTK_OBJECT (home_button), "clicked",
1253 //                    (GtkSignalFunc) gtk_file_selection_home_button,
1254 //                    (gpointer) filesel);
1255 //  gtk_box_pack_start (GTK_BOX (hbox), home_button, TRUE,TRUE, 0);
1256
1257         /* Here we add the bookmark menu button */
1258   #define If we 're going to make bookmark a menu, we don't need
1259   #define   to keep it in the filesel structure
1260         button = gtk_button_new_with_label( _( "Bookmarks" ) );
1261         gtk_widget_show( button );
1262         gtk_box_pack_start( GTK_BOX( hbox ), button, FALSE,FALSE,0 );
1263         gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1264                                                 (GtkSignalFunc) gtk_file_selection_bookmark_button,
1265                                                 (gpointer) filesel );
1266
1267         hbox2 = gtk_hbox_new( FALSE, 2 );
1268         gtk_box_pack_start( GTK_BOX( hbox ), hbox2, FALSE, FALSE, 0 );
1269         gtk_widget_show( hbox2 );
1270
1271         /* Prev button */
1272         button = gtk_button_new();
1273         gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1274                                                 (GtkSignalFunc) gtk_file_selection_prev_button,
1275                                                 (gpointer) filesel );
1276         gtk_widget_show( button );
1277         gtk_box_pack_start( GTK_BOX( hbox2 ), button, FALSE,FALSE, 0 );
1278         pixmap = create_pixmap( filesel->main_vbox, (gpointer) back_xpm );
1279         gtk_widget_show( pixmap );
1280         gtk_container_add( GTK_CONTAINER( button ), pixmap );
1281
1282         /* Up button */
1283         button = gtk_button_new();
1284         gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1285                                                 (GtkSignalFunc) gtk_file_selection_up_button,
1286                                                 (gpointer) filesel );
1287         gtk_widget_show( button );
1288         gtk_box_pack_start( GTK_BOX( hbox2 ), button, FALSE,FALSE, 0 );
1289         pixmap = create_pixmap( filesel->main_vbox, (gpointer) up_xpm );
1290         gtk_widget_show( pixmap );
1291         gtk_container_add( GTK_CONTAINER( button ), pixmap );
1292
1293         /* next button */
1294         button = gtk_button_new();
1295         gtk_widget_show( button );
1296         gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1297                                                 (GtkSignalFunc) gtk_file_selection_next_button,
1298                                                 (gpointer) filesel );
1299         gtk_box_pack_start( GTK_BOX( hbox2 ), button, FALSE,FALSE, 0 );
1300         pixmap = create_pixmap( filesel->main_vbox, (gpointer) forward_xpm );
1301         gtk_widget_show( pixmap );
1302         gtk_container_add( GTK_CONTAINER( button ), pixmap );
1303
1304         /* refresh button */
1305         button = gtk_button_new();
1306         gtk_widget_show( button );
1307         gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1308                                                 (GtkSignalFunc) gtk_file_selection_refresh_button,
1309                                                 (gpointer) filesel );
1310         gtk_box_pack_end( GTK_BOX( hbox ), button, FALSE,FALSE, 0 );
1311         pixmap = create_pixmap( filesel->main_vbox, (gpointer) refresh_xpm );
1312         gtk_widget_show( pixmap );
1313         gtk_container_add( GTK_CONTAINER( button ), pixmap );
1314
1315         /* menu for right click file operations */
1316         filesel->fileop_menu = gtk_menu_new();
1317
1318         menu_item = gtk_menu_item_new_with_label( _( "Rename..." ) );
1319         gtk_widget_show( menu_item );
1320         gtk_signal_connect_object( GTK_OBJECT( menu_item ), "activate",
1321                                                            (GtkSignalFunc) gtk_file_selection_rename_file,
1322                                                            (gpointer) filesel );
1323         gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item );
1324
1325         menu_item = gtk_menu_item_new_with_label( _( "Delete" ) );
1326         gtk_widget_show( menu_item );
1327         gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item );
1328         gtk_signal_connect_object( GTK_OBJECT( menu_item ), "activate",
1329                                                            (GtkSignalFunc) gtk_file_selection_delete_file,
1330                                                            (gpointer) filesel );
1331
1332         menu_item = gtk_menu_item_new();
1333         gtk_widget_show( menu_item );
1334         gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item );
1335
1336         menu_item = gtk_menu_item_new_with_label( _( "Create Directory..." ) );
1337         gtk_signal_connect_object( GTK_OBJECT( menu_item ), "activate",
1338                                                            (GtkSignalFunc) gtk_file_selection_create_dir,
1339                                                            (gpointer) filesel );
1340         gtk_widget_show( menu_item );
1341         gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item );
1342
1343         menu_item = gtk_menu_item_new();
1344         gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item );
1345         gtk_widget_show( menu_item );
1346
1347         menu_item = gtk_menu_item_new_with_label( _( "Properties..." ) );
1348         gtk_signal_connect_object( GTK_OBJECT( menu_item ), "activate",
1349                                                            (GtkSignalFunc) gtk_file_selection_properties,
1350                                                            (gpointer) filesel );
1351         gtk_menu_append( GTK_MENU( filesel->fileop_menu ), menu_item );
1352         gtk_widget_show( menu_item );
1353
1354         /* The directories clist */
1355         dir_title[0] = _( "Directories" );
1356         dir_title[1] = NULL;
1357         filesel->dir_list = gtk_clist_new_with_titles( 1, (gchar**) dir_title );
1358         gtk_widget_set_usize( filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT );
1359         gtk_signal_connect( GTK_OBJECT( filesel->dir_list ), "select_row",
1360                                                 (GtkSignalFunc) gtk_file_selection_dir_button,
1361                                                 (gpointer) filesel );
1362         gtk_signal_connect( GTK_OBJECT( filesel->dir_list ), "unselect_row",
1363                                                 (GtkSignalFunc) gtk_file_selection_undir_button,
1364                                                 (gpointer) filesel );
1365         gtk_signal_connect( GTK_OBJECT( filesel->dir_list ), "button_press_event",
1366                                                 GTK_SIGNAL_FUNC( gtk_file_selection_show_fileop_menu ),
1367                                                 (gpointer) filesel );
1368
1369         gtk_clist_column_titles_passive( GTK_CLIST( filesel->dir_list ) );
1370
1371         scrolled_win = gtk_scrolled_window_new( NULL, NULL );
1372         gtk_container_add( GTK_CONTAINER( scrolled_win ), filesel->dir_list );
1373         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_win ),
1374                                                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1375         gtk_box_pack_start( GTK_BOX( vbox ), scrolled_win, TRUE,TRUE, 0 );
1376         //gtk_container_add(GTK_CONTAINER(hpaned), scrolled_win);
1377
1378         gtk_widget_show( filesel->dir_list );
1379         gtk_widget_show( scrolled_win );
1380
1381         vbox = gtk_vbox_new( FALSE, 3 );
1382         gtk_widget_show( vbox );
1383         gtk_container_add( GTK_CONTAINER( hpaned ),vbox );
1384         /* vbox area for mask entry and files clist  */
1385
1386         hbox = gtk_hbox_new( FALSE, 2 );
1387         gtk_widget_show( hbox );
1388         gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 0 );
1389
1390         mask_label = gtk_label_new( _( "Mask:" ) );
1391         gtk_widget_show( mask_label );
1392         gtk_box_pack_start( GTK_BOX( hbox ), mask_label, FALSE, FALSE, 2 );
1393
1394 /*
1395    filesel->mask_entry = gtk_entry_new ();
1396    gtk_widget_show (filesel->mask_entry);
1397    gtk_signal_connect(GTK_OBJECT(filesel->mask_entry),"activate",
1398              (GtkSignalFunc) gtk_file_4_mask_entry_callback,
1399              (gpointer) filesel);
1400    gtk_box_pack_start (GTK_BOX (hbox),filesel->mask_entry, TRUE, TRUE, 0);
1401  */
1402
1403         filesel->mask_entry = gtk_combo_new();
1404         gtk_widget_show( filesel->mask_entry );
1405         gtk_combo_set_value_in_list( GTK_COMBO( filesel->mask_entry ),FALSE,FALSE );
1406         gtk_signal_connect( GTK_OBJECT( GTK_COMBO( filesel->mask_entry )->entry ),"activate",
1407                                                 (GtkSignalFunc) gtk_file_selection_mask_entry_callback,
1408                                                 (gpointer) filesel );
1409         gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->mask_entry )->entry ),"key-press-event",
1410                                                 (GtkSignalFunc) gtk_file_selection_mask_entry_key_callback,
1411                                                 (gpointer) filesel );
1412
1413         gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->mask_entry )->list ),"button-release-event",
1414                                                 (GtkSignalFunc) gtk_file_selection_mask_entry_button_callback,
1415                                                 (gpointer) filesel );
1416         gtk_box_pack_start( GTK_BOX( hbox ),filesel->mask_entry, TRUE, TRUE, 0 );
1417
1418         if ( filesel->masks ) {
1419                 gtk_combo_set_popdown_strings( GTK_COMBO( filesel->mask_entry ), filesel->masks );
1420         }
1421
1422
1423         /* The files clist */
1424         file_title[0] = _( "Files" );
1425         file_title[1] = NULL;
1426         filesel->file_list = gtk_clist_new_with_titles( 1, (gchar**) file_title );
1427         gtk_widget_set_usize( filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT );
1428         gtk_signal_connect( GTK_OBJECT( filesel->file_list ), "select_row",
1429                                                 (GtkSignalFunc) gtk_file_selection_file_button,
1430                                                 (gpointer) filesel );
1431         gtk_signal_connect( GTK_OBJECT( filesel->file_list ), "key-press-event",
1432                                                 (GtkSignalFunc) gtk_file_selection_files_list_key_callback,
1433                                                 (gpointer) filesel );
1434
1435         gtk_signal_connect( GTK_OBJECT( filesel->file_list ), "button_press_event",
1436                                                 GTK_SIGNAL_FUNC( gtk_file_selection_show_fileop_menu ),
1437                                                 (gpointer) filesel );
1438
1439         gtk_clist_column_titles_passive( GTK_CLIST( filesel->file_list ) );
1440
1441         scrolled_win = gtk_scrolled_window_new( NULL, NULL );
1442         gtk_container_add( GTK_CONTAINER( scrolled_win ), filesel->file_list );
1443         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_win ),
1444                                                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
1445         gtk_box_pack_start( GTK_BOX( vbox ), scrolled_win, TRUE, TRUE, 0 );
1446         gtk_widget_show( filesel->file_list );
1447         gtk_widget_show( scrolled_win );
1448
1449         /* action area for packing buttons into. */
1450         filesel->action_area = gtk_hbox_new( TRUE, 0 );
1451         gtk_box_pack_start( GTK_BOX( filesel->main_vbox ), filesel->action_area,
1452                                                 FALSE, FALSE, 2 );
1453         gtk_widget_show( filesel->action_area );
1454
1455         /*
1456            hbox=gtk_hbox_new(FALSE,0);
1457            gtk_box_pack_end (GTK_BOX (filesel->main_vbox), hbox, FALSE,FALSE, 0);
1458            gtk_widget_show (hbox);
1459          */
1460
1461         /*  The selection entry widget  */
1462
1463         entry_vbox = gtk_vbox_new( FALSE, 0 );
1464         gtk_box_pack_end( GTK_BOX( filesel->main_vbox ), entry_vbox, FALSE, FALSE, 0 );
1465         gtk_widget_show( entry_vbox );
1466
1467         table = gtk_table_new( 2, 2, FALSE );
1468         gtk_box_pack_start( GTK_BOX( entry_vbox ), table, TRUE, TRUE, 0 );
1469         gtk_container_set_border_width( GTK_CONTAINER( table ), 4 );
1470         gtk_table_set_row_spacings( GTK_TABLE( table ), 2 );
1471         gtk_table_set_col_spacings( GTK_TABLE( table ), 4 );
1472
1473
1474         label = gtk_label_new( _( "Selection:" ) );
1475         gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
1476                                           (GtkAttachOptions) ( 0 ),
1477                                           (GtkAttachOptions) ( 0 ), 0, 0 );
1478         gtk_widget_show( label );
1479
1480
1481         filesel->selection_entry = gtk_entry_new();
1482         gtk_signal_connect( GTK_OBJECT( filesel->selection_entry ), "key_press_event",
1483                                                 (GtkSignalFunc) gtk_file_selection_key_press, filesel );
1484         gtk_table_attach( GTK_TABLE( table ), filesel->selection_entry, 1, 2, 0, 1,
1485                                           (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
1486                                           (GtkAttachOptions) ( 0 ), 0, 0 );
1487         gtk_widget_show( filesel->selection_entry );
1488
1489
1490         label = gtk_label_new( _( "Directory:" ) );
1491         gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
1492                                           (GtkAttachOptions) ( 0 ),
1493                                           (GtkAttachOptions) ( 0 ), 0, 0 );
1494         gtk_widget_show( label );
1495
1496
1497         filesel->history_combo = gtk_combo_new();
1498         gtk_combo_set_value_in_list( GTK_COMBO( filesel->history_combo ),FALSE,FALSE );
1499         gtk_table_attach( GTK_TABLE( table ), filesel->history_combo, 1, 2, 1, 2,
1500                                           (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
1501                                           (GtkAttachOptions) ( 0 ), 0, 0 );
1502         gtk_widget_show( filesel->history_combo );
1503
1504         gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->history_combo )->entry ),"key-press-event",
1505                                                 (GtkSignalFunc) gtk_file_selection_history_combo_callback,
1506                                                 (gpointer) filesel );
1507
1508         gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->history_combo )->list ),"button-press-event",
1509                                                 (GtkSignalFunc) gtk_file_selection_history_combo_list_callback,
1510                                                 (gpointer) filesel );
1511
1512         gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->history_combo )->list ),"key-press-event",
1513                                                 (GtkSignalFunc) gtk_file_selection_history_combo_list_key_handler,
1514                                                 (gpointer) filesel );
1515
1516         filesel->selection_text = NULL;
1517
1518
1519         /*  The OK/Cancel button area */
1520         confirm_area = gtk_hbutton_box_new();
1521         gtk_button_box_set_layout( GTK_BUTTON_BOX( confirm_area ), GTK_BUTTONBOX_END );
1522         gtk_button_box_set_spacing( GTK_BUTTON_BOX( confirm_area ), 5 );
1523         gtk_box_pack_end( GTK_BOX( entry_vbox ), confirm_area, FALSE, FALSE, 0 );
1524         gtk_widget_show( confirm_area );
1525
1526         /*  The OK button  */
1527         filesel->ok_button = gtk_button_new_with_label( _( "OK" ) );
1528         GTK_WIDGET_SET_FLAGS( filesel->ok_button, GTK_CAN_DEFAULT );
1529         gtk_box_pack_start( GTK_BOX( confirm_area ), filesel->ok_button, TRUE, TRUE, 0 );
1530         gtk_signal_connect_object( GTK_OBJECT( filesel->selection_entry ), "focus_in_event",
1531                                                            (GtkSignalFunc) gtk_widget_grab_default,
1532                                                            GTK_OBJECT( filesel->ok_button ) );
1533         gtk_signal_connect_object( GTK_OBJECT( filesel->selection_entry ), "activate",
1534                                                            (GtkSignalFunc) gtk_button_clicked,
1535                                                            GTK_OBJECT( filesel->ok_button ) );
1536         gtk_widget_grab_default( filesel->ok_button );
1537         gtk_widget_show( filesel->ok_button );
1538
1539         /*  The Cancel button  */
1540         filesel->cancel_button = gtk_button_new_with_label( _( "Cancel" ) );
1541         GTK_WIDGET_SET_FLAGS( filesel->cancel_button, GTK_CAN_DEFAULT );
1542         gtk_box_pack_start( GTK_BOX( confirm_area ), filesel->cancel_button, TRUE, TRUE, 0 );
1543         gtk_widget_show( filesel->cancel_button );
1544
1545         gtk_widget_show( table );
1546
1547
1548         /*
1549            filesel->selection_text = label = gtk_label_new ("");
1550            gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1551            gtk_box_pack_start (GTK_BOX (entry_vbox), label, FALSE, FALSE, 0);
1552            gtk_widget_show (label);
1553          */
1554
1555
1556         if ( !cmpl_state_okay( filesel->cmpl_state ) ) {
1557                 gchar err_buf[256];
1558
1559                 sprintf( err_buf, _( "Directory unreadable: %s" ), cmpl_strerror( cmpl_errno ) );
1560
1561                 /*
1562                    gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
1563                  */
1564         }
1565         else
1566         {
1567                 gtk_file_selection_populate( filesel, "", FALSE );
1568         }
1569
1570         gtk_widget_grab_focus( filesel->selection_entry );
1571 }
1572
1573 GtkWidget*
1574 gtk_file_selection_new( const gchar *title ){
1575         GtkFileSelection *filesel;
1576
1577         filesel = gtk_type_new( GTK_TYPE_FILE_SELECTION );
1578         gtk_window_set_title( GTK_WINDOW( filesel ), title );
1579         /* !!! put check here to figure out if screen > 640x480, if true
1580            We need to make the file selection dialog bigger. much bigger..
1581            or maybe we should keep it at a certan percentage of the screen
1582            size? */
1583
1584         gtk_window_set_default_size( GTK_WINDOW( filesel ), 520, 420 );
1585         return GTK_WIDGET( filesel );
1586 }
1587
1588 void
1589 gtk_file_selection_show_fileop_buttons( GtkFileSelection *filesel ){
1590         g_return_if_fail( filesel != NULL );
1591         g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
1592
1593         return;
1594
1595         /* delete, create directory, and rename */
1596 /*
1597    if (!filesel->fileop_c_dir)
1598     {
1599       filesel->fileop_c_dir = gtk_button_new_with_label (_("MkDir"));
1600       gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked",
1601               (GtkSignalFunc) gtk_file_selection_create_dir,
1602               (gpointer) filesel);
1603       gtk_box_pack_start (GTK_BOX (filesel->button_area),
1604               filesel->fileop_c_dir, TRUE,TRUE, 0);
1605       gtk_widget_show (filesel->fileop_c_dir);
1606     }
1607
1608    if (!filesel->fileop_del_file)
1609     {
1610       filesel->fileop_del_file = gtk_button_new_with_label (_("Delete"));
1611       gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked",
1612               (GtkSignalFunc) gtk_file_selection_delete_file,
1613               (gpointer) filesel);
1614       gtk_box_pack_start (GTK_BOX (filesel->button_area),
1615               filesel->fileop_del_file, TRUE,TRUE, 0);
1616       gtk_widget_show (filesel->fileop_del_file);
1617     }
1618
1619    if (!filesel->fileop_ren_file)
1620     {
1621       filesel->fileop_ren_file = gtk_button_new_with_label (_("Rename"));
1622       gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked",
1623               (GtkSignalFunc) gtk_file_selection_rename_file,
1624               (gpointer) filesel);
1625       gtk_box_pack_start (GTK_BOX (filesel->button_area),
1626               filesel->fileop_ren_file, TRUE,TRUE, 0);
1627       gtk_widget_show (filesel->fileop_ren_file);
1628     }
1629
1630    gtk_widget_queue_resize(GTK_WIDGET(filesel));
1631  */
1632 }
1633
1634 void
1635 gtk_file_selection_hide_fileop_buttons( GtkFileSelection *filesel ){
1636         g_return_if_fail( filesel != NULL );
1637         g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
1638
1639         return;
1640         /*
1641            if (filesel->fileop_ren_file)
1642            {
1643             gtk_widget_destroy (filesel->fileop_ren_file);
1644             filesel->fileop_ren_file = NULL;
1645            }
1646
1647            if (filesel->fileop_del_file)
1648            {
1649             gtk_widget_destroy (filesel->fileop_del_file);
1650             filesel->fileop_del_file = NULL;
1651            }
1652
1653            if (filesel->fileop_c_dir)
1654            {
1655             gtk_widget_destroy (filesel->fileop_c_dir);
1656             filesel->fileop_c_dir = NULL;
1657            }
1658          */
1659 }
1660
1661
1662
1663 void
1664 gtk_file_selection_set_filename( GtkFileSelection *filesel,
1665                                                                  const gchar      *filename ){
1666         char buf[MAXPATHLEN];
1667         const char *name, *last_slash;
1668
1669         g_return_if_fail( filesel != NULL );
1670         g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
1671         g_return_if_fail( filename != NULL );
1672
1673         last_slash = strrchr( filename, '/' );
1674
1675         if ( !last_slash ) {
1676                 buf[0] = 0;
1677                 name = filename;
1678         }
1679         else
1680         {
1681                 gint len = MIN( MAXPATHLEN - 1, last_slash - filename + 1 );
1682
1683                 strncpy( buf, filename, len );
1684                 buf[len] = 0;
1685
1686                 name = last_slash + 1;
1687         }
1688
1689         gtk_file_selection_populate( filesel, buf, FALSE );
1690
1691         if ( filesel->selection_entry ) {
1692                 gtk_entry_set_text( GTK_ENTRY( filesel->selection_entry ), name );
1693         }
1694 }
1695
1696 gchar*
1697 gtk_file_selection_get_filename( GtkFileSelection *filesel ){
1698         static char nothing[2] = "";
1699         char *text;
1700         char *filename;
1701
1702         g_return_val_if_fail( filesel != NULL, nothing );
1703         g_return_val_if_fail( GTK_IS_FILE_SELECTION( filesel ), nothing );
1704
1705         text = gtk_entry_get_text( GTK_ENTRY( filesel->selection_entry ) );
1706         if ( text ) {
1707                 filename = cmpl_completion_fullname( text, filesel->cmpl_state );
1708                 return filename;
1709         }
1710
1711         return nothing;
1712 }
1713
1714 void
1715 gtk_file_selection_complete( GtkFileSelection *filesel,
1716                                                          const gchar      *pattern ){
1717         gchar *new_pattern;
1718         gint x;
1719
1720         g_return_if_fail( filesel != NULL );
1721         g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
1722         g_return_if_fail( pattern != NULL );
1723
1724         if ( filesel->selection_entry ) {
1725                 gtk_entry_set_text( GTK_ENTRY( filesel->selection_entry ), pattern );
1726         }
1727
1728         if ( strchr( pattern,'*' ) || strchr( pattern,'?' ) ) {
1729                 for ( x = strlen( pattern ); x >= 0; x-- )
1730                 {
1731                         if ( pattern[x] == '/' ) {
1732                                 break;
1733                         }
1734                 }
1735                 gtk_entry_set_text( GTK_ENTRY( filesel->mask_entry ),g_strdup( pattern + x + 1 ) );
1736
1737                 if ( filesel->mask ) {
1738                         g_free( filesel->mask );
1739                 }
1740
1741                 filesel->mask = g_strdup( pattern + x + 1 );
1742                 new_pattern = g_strdup( pattern );
1743                 new_pattern[x + 1] = 0;
1744                 gtk_file_selection_populate( filesel, (gchar*) new_pattern, TRUE );
1745                 g_free( new_pattern );
1746         }
1747         else
1748         {
1749                 gtk_file_selection_populate( filesel, (gchar*) pattern, TRUE );
1750         }
1751 }
1752
1753 static void
1754 gtk_file_selection_realize( GtkWidget *widget ){
1755         GtkFileSelection *filesel;
1756         const gchar *masks[] = { "All Files <*>", NULL };
1757
1758         g_return_if_fail( widget != NULL );
1759         g_return_if_fail( GTK_IS_FILE_SELECTION( widget ) );
1760
1761         filesel = GTK_FILE_SELECTION( widget );
1762
1763         /* make sure that we have at least one mask */
1764         if ( !filesel->masks ) {
1765                 gtk_file_selection_set_masks( filesel, masks );
1766         }
1767
1768         filesel->mask = g_strdup( (gchar*) filesel->masks->data );
1769         gtk_file_selection_populate( filesel, "", FALSE );
1770
1771
1772         if ( GTK_WIDGET_CLASS( parent_class )->realize ) {
1773                 ( *GTK_WIDGET_CLASS( parent_class )->realize )( widget );
1774         }
1775 }
1776
1777 static void
1778 gtk_file_selection_destroy( GtkObject *object ){
1779         GtkFileSelection *filesel;
1780         GList *list;
1781
1782         g_return_if_fail( object != NULL );
1783         g_return_if_fail( GTK_IS_FILE_SELECTION( object ) );
1784
1785         filesel = GTK_FILE_SELECTION( object );
1786
1787         if ( filesel->fileop_dialog ) {
1788                 gtk_widget_destroy( filesel->fileop_dialog );
1789         }
1790
1791         if ( filesel->next_history ) {
1792                 list = filesel->next_history;
1793                 while ( list )
1794                 {
1795                         g_free( list->data );
1796                         list = list->next;
1797                 }
1798         }
1799         g_list_free( filesel->next_history );
1800         filesel->next_history = NULL;
1801
1802         if ( filesel->prev_history ) {
1803                 list = filesel->prev_history;
1804                 while ( list )
1805                 {
1806                         g_free( list->data );
1807                         list = list->next;
1808                 }
1809         }
1810         g_list_free( filesel->prev_history );
1811         filesel->prev_history = NULL;
1812
1813         if ( filesel->mask ) {
1814                 g_free( filesel->mask );
1815                 filesel->mask = NULL;
1816         }
1817
1818         cmpl_free_state( filesel->cmpl_state );
1819         filesel->cmpl_state = NULL;
1820
1821         if ( GTK_OBJECT_CLASS( parent_class )->destroy ) {
1822                 ( *GTK_OBJECT_CLASS( parent_class )->destroy )( object );
1823         }
1824 }
1825
1826 /* Begin file operations callbacks */
1827
1828 static gint
1829 gtk_file_selection_show_fileop_menu( GtkCList *clist, GdkEvent *event, GtkFileSelection *fs ){
1830         GdkEventButton *event_button;
1831
1832         g_return_val_if_fail( clist != NULL, FALSE );
1833         g_return_val_if_fail( GTK_IS_CLIST( clist ), FALSE );
1834         g_return_val_if_fail( event != NULL, FALSE );
1835         g_return_val_if_fail( fs != NULL, FALSE );
1836         g_return_val_if_fail( GTK_FILE_SELECTION( fs ), FALSE );
1837
1838         if ( event->type == GDK_BUTTON_PRESS ) {
1839                 event_button = (GdkEventButton *) event;
1840                 if ( event_button->button == 3 ) {
1841
1842                         gtk_menu_popup( GTK_MENU( fs->fileop_menu ), NULL, NULL, NULL, NULL,
1843                                                         event_button->button, event_button->time );
1844                         return TRUE;
1845                 }
1846         }
1847
1848         return FALSE;
1849 }
1850
1851 static void
1852 gtk_file_selection_fileop_error( GtkFileSelection *fs, gchar *error_message ){
1853         GtkWidget *label;
1854         GtkWidget *vbox;
1855         GtkWidget *button;
1856         GtkWidget *dialog;
1857
1858         g_return_if_fail( error_message != NULL );
1859
1860         /* main dialog */
1861         dialog = gtk_dialog_new();
1862         /*
1863            gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1864                     (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1865                     (gpointer) fs);
1866          */
1867         gtk_window_set_title( GTK_WINDOW( dialog ), _( "Error" ) );
1868         gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
1869
1870         /* If file dialog is grabbed, make this dialog modal too */
1871         /* When error dialog is closed, file dialog will be grabbed again */
1872         if ( GTK_WINDOW( fs )->modal ) {
1873                 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
1874         }
1875
1876         vbox = gtk_vbox_new( FALSE, 0 );
1877         gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 );
1878         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox,
1879                                                 FALSE, FALSE, 0 );
1880         gtk_widget_show( vbox );
1881
1882         label = gtk_label_new( error_message );
1883         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 );
1884         gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 );
1885         gtk_widget_show( label );
1886
1887         /* yes, we free it */
1888         g_free( error_message );
1889
1890         /* close button */
1891         button = gtk_button_new_with_label( _( "Close" ) );
1892         gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
1893                                                            (GtkSignalFunc) gtk_widget_destroy,
1894                                                            (gpointer) dialog );
1895         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
1896                                                 button, TRUE, TRUE, 0 );
1897         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
1898         gtk_widget_grab_default( button );
1899         gtk_widget_show( button );
1900
1901         gtk_widget_show( dialog );
1902 }
1903
1904 static void
1905 gtk_file_selection_fileop_destroy( GtkWidget *widget, gpointer data ){
1906         GtkFileSelection *fs = data;
1907
1908         g_return_if_fail( fs != NULL );
1909         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1910
1911         fs->fileop_dialog = NULL;
1912         g_free( fs->fileop_data );
1913         fs->fileop_data = NULL;
1914 }
1915
1916
1917 static void
1918 gtk_file_selection_create_dir_confirmed( GtkWidget *widget, gpointer data ){
1919         GtkFileSelection *fs = data;
1920         gchar *dirname;
1921         gchar *path;
1922         gchar *full_path;
1923         gchar *buf;
1924         CompletionState *cmpl_state;
1925
1926         g_return_if_fail( fs != NULL );
1927         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1928
1929         dirname = gtk_entry_get_text( GTK_ENTRY( fs->fileop_entry ) );
1930         cmpl_state = (CompletionState*) fs->cmpl_state;
1931         path = cmpl_reference_position( cmpl_state );
1932
1933         full_path = g_strconcat( path, "/", dirname, NULL );
1934         if ( ( mkdir( full_path, 0755 ) < 0 ) ) {
1935                 buf = g_strconcat( "Error creating directory \"", dirname, "\":  ",
1936                                                    g_strerror( errno ), NULL );
1937                 gtk_file_selection_fileop_error( fs, buf );
1938         }
1939         g_free( full_path );
1940
1941         gtk_widget_destroy( fs->fileop_dialog );
1942         gtk_file_selection_populate( fs, "", FALSE );
1943 }
1944
1945 static void
1946 gtk_file_selection_create_dir( gpointer data ){
1947         GtkFileSelection *fs = data;
1948         GtkWidget *label;
1949         GtkWidget *dialog;
1950         GtkWidget *vbox;
1951         GtkWidget *button;
1952
1953         g_return_if_fail( fs != NULL );
1954         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1955
1956         if ( fs->fileop_dialog ) {
1957                 return;
1958         }
1959
1960         /* main dialog */
1961         fs->fileop_dialog = dialog = gtk_dialog_new();
1962         gtk_signal_connect( GTK_OBJECT( dialog ), "destroy",
1963                                                 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1964                                                 (gpointer) fs );
1965         gtk_window_set_title( GTK_WINDOW( dialog ), _( "Create Directory" ) );
1966         gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
1967
1968         /* If file dialog is grabbed, grab option dialog */
1969         /* When option dialog is closed, file dialog will be grabbed again */
1970         if ( GTK_WINDOW( fs )->modal ) {
1971                 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
1972         }
1973
1974         vbox = gtk_vbox_new( FALSE, 0 );
1975         gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 );
1976         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox,
1977                                                 FALSE, FALSE, 0 );
1978         gtk_widget_show( vbox );
1979
1980         label = gtk_label_new( _( "Directory name:" ) );
1981         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 );
1982         gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 );
1983         gtk_widget_show( label );
1984
1985         /*  The directory entry widget  */
1986         fs->fileop_entry = gtk_entry_new();
1987         gtk_box_pack_start( GTK_BOX( vbox ), fs->fileop_entry,
1988                                                 TRUE, TRUE, 5 );
1989         GTK_WIDGET_SET_FLAGS( fs->fileop_entry, GTK_CAN_DEFAULT );
1990         gtk_widget_show( fs->fileop_entry );
1991
1992         /* buttons */
1993         button = gtk_button_new_with_label( _( "Create" ) );
1994         gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1995                                                 (GtkSignalFunc) gtk_file_selection_create_dir_confirmed,
1996                                                 (gpointer) fs );
1997         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
1998                                                 button, TRUE, TRUE, 0 );
1999         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2000         gtk_widget_show( button );
2001
2002         button = gtk_button_new_with_label( _( "Cancel" ) );
2003         gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
2004                                                            (GtkSignalFunc) gtk_widget_destroy,
2005                                                            (gpointer) dialog );
2006         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
2007                                                 button, TRUE, TRUE, 0 );
2008         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2009         gtk_widget_grab_default( button );
2010         gtk_widget_show( button );
2011
2012         gtk_widget_show( dialog );
2013 }
2014
2015 static void
2016 gtk_file_selection_delete_file_confirmed( GtkWidget *widget, gpointer data ){
2017         GtkFileSelection *fs = data;
2018         CompletionState *cmpl_state;
2019         gchar *path;
2020         gchar *full_path;
2021         gchar *buf;
2022
2023         g_return_if_fail( fs != NULL );
2024         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2025
2026         cmpl_state = (CompletionState*) fs->cmpl_state;
2027         path = cmpl_reference_position( cmpl_state );
2028
2029         full_path = g_strconcat( path, "/", fs->fileop_file, NULL );
2030         if ( ( unlink( full_path ) < 0 ) ) {
2031                 buf = g_strconcat( "Error deleting file \"", fs->fileop_file, "\":  ",
2032                                                    g_strerror( errno ), NULL );
2033                 gtk_file_selection_fileop_error( fs, buf );
2034         }
2035         g_free( full_path );
2036
2037         gtk_widget_destroy( fs->fileop_dialog );
2038         gtk_file_selection_populate( fs, "", FALSE );
2039 }
2040
2041 static void
2042 gtk_file_selection_delete_file( gpointer data ){
2043         GtkFileSelection *fs = data;
2044         GtkWidget *label;
2045         GtkWidget *vbox;
2046         GtkWidget *button;
2047         GtkWidget *dialog;
2048         gchar *filename;
2049         gchar *buf;
2050
2051         g_return_if_fail( fs != NULL );
2052         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2053
2054         if ( fs->fileop_dialog ) {
2055                 return;
2056         }
2057
2058         filename = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) );
2059         if ( strlen( filename ) < 1 ) {
2060                 return;
2061         }
2062
2063         fs->fileop_file = filename;
2064
2065         /* main dialog */
2066         fs->fileop_dialog = dialog = gtk_dialog_new();
2067         gtk_signal_connect( GTK_OBJECT( dialog ), "destroy",
2068                                                 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
2069                                                 (gpointer) fs );
2070         gtk_window_set_title( GTK_WINDOW( dialog ), _( "Delete File" ) );
2071         gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
2072
2073         /* If file dialog is grabbed, grab option dialog */
2074         /* When option dialog is closed, file dialog will be grabbed again */
2075         if ( GTK_WINDOW( fs )->modal ) {
2076                 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
2077         }
2078
2079         vbox = gtk_vbox_new( FALSE, 0 );
2080         gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 );
2081         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox,
2082                                                 FALSE, FALSE, 0 );
2083         gtk_widget_show( vbox );
2084
2085         buf = g_strconcat( "Really delete file \"", filename, "\" ?", NULL );
2086         label = gtk_label_new( buf );
2087         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 );
2088         gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 );
2089         gtk_widget_show( label );
2090         g_free( buf );
2091
2092         /* buttons */
2093         button = gtk_button_new_with_label( _( "Delete" ) );
2094         gtk_signal_connect( GTK_OBJECT( button ), "clicked",
2095                                                 (GtkSignalFunc) gtk_file_selection_delete_file_confirmed,
2096                                                 (gpointer) fs );
2097         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
2098                                                 button, TRUE, TRUE, 0 );
2099         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2100         gtk_widget_show( button );
2101
2102         button = gtk_button_new_with_label( _( "Cancel" ) );
2103         gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
2104                                                            (GtkSignalFunc) gtk_widget_destroy,
2105                                                            (gpointer) dialog );
2106         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
2107                                                 button, TRUE, TRUE, 0 );
2108         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2109         gtk_widget_grab_default( button );
2110         gtk_widget_show( button );
2111
2112         gtk_widget_show( dialog );
2113
2114 }
2115
2116 static void
2117 gtk_file_selection_rename_file_confirmed( GtkWidget *widget, gpointer data ){
2118         GtkFileSelection *fs = data;
2119         gchar *buf;
2120         gchar *file;
2121         gchar *path;
2122         gchar *new_filename;
2123         gchar *old_filename;
2124         CompletionState *cmpl_state;
2125
2126         g_return_if_fail( fs != NULL );
2127         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2128
2129         file = gtk_entry_get_text( GTK_ENTRY( fs->fileop_entry ) );
2130         cmpl_state = (CompletionState*) fs->cmpl_state;
2131         path = cmpl_reference_position( cmpl_state );
2132
2133         new_filename = g_strconcat( path, "/", file, NULL );
2134         old_filename = g_strconcat( path, "/", fs->fileop_file, NULL );
2135
2136         if ( strcmp( new_filename, old_filename ) ) {
2137                 if ( ( rename( old_filename, new_filename ) ) < 0 ) {
2138                         buf = g_strconcat( "Error renaming file \"", file, "\":  ",
2139                                                            g_strerror( errno ), NULL );
2140                         gtk_file_selection_fileop_error( fs, buf );
2141                 }
2142         }
2143         g_free( new_filename );
2144         g_free( old_filename );
2145
2146         gtk_widget_destroy( fs->fileop_dialog );
2147         gtk_file_selection_populate( fs, "", FALSE );
2148 }
2149
2150 static void
2151 gtk_file_selection_file_mode_confirmed( GtkWidget *widget, gpointer data ){
2152         GtkFileSelection *fs = data;
2153         PropertiesPrivate *priv = fs->fileop_data;
2154         CompletionState *cmpl_state;
2155         gchar *filename, *file, *path;
2156         mode_t mode;
2157
2158         mode = gtk_file_selection_properties_get_mode( priv );
2159
2160         file = gtk_entry_get_text( GTK_ENTRY( fs->fileop_entry ) );
2161         cmpl_state = (CompletionState*) fs->cmpl_state;
2162         path = cmpl_reference_position( cmpl_state );
2163
2164         filename = g_strconcat( path, "/", file, NULL );
2165         if ( chmod( filename, mode ) == -1 ) {
2166                 gchar *buf = g_strconcat( "Error changing file mode of \"", filename, "\":  ",
2167                                                                   g_strerror( errno ), NULL );
2168                 gtk_file_selection_fileop_error( fs, buf );
2169                 gtk_widget_destroy( fs->fileop_dialog );
2170                 gtk_file_selection_populate( fs, "", FALSE );
2171         }
2172         else{
2173                 gtk_file_selection_rename_file_confirmed( widget, data );
2174         }
2175
2176         g_free( filename );
2177 }
2178
2179 static void
2180 gtk_file_selection_rename_file( gpointer data ){
2181         GtkFileSelection *fs = data;
2182         GtkWidget *label;
2183         GtkWidget *dialog;
2184         GtkWidget *vbox;
2185         GtkWidget *button;
2186         gchar *buf;
2187
2188         g_return_if_fail( fs != NULL );
2189         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2190
2191         if ( fs->fileop_dialog ) {
2192                 return;
2193         }
2194
2195         fs->fileop_file = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) );
2196         if ( strlen( fs->fileop_file ) < 1 ) {
2197                 return;
2198         }
2199
2200         /* main dialog */
2201         fs->fileop_dialog = dialog = gtk_dialog_new();
2202         gtk_signal_connect( GTK_OBJECT( dialog ), "destroy",
2203                                                 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
2204                                                 (gpointer) fs );
2205         gtk_window_set_title( GTK_WINDOW( dialog ), _( "Rename File" ) );
2206         gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
2207
2208         /* If file dialog is grabbed, grab option dialog */
2209         /* When option dialog  closed, file dialog will be grabbed again */
2210         if ( GTK_WINDOW( fs )->modal ) {
2211                 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
2212         }
2213
2214         vbox = gtk_vbox_new( FALSE, 0 );
2215         gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 );
2216         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox,
2217                                                 FALSE, FALSE, 0 );
2218         gtk_widget_show( vbox );
2219
2220         buf = g_strconcat( "Rename file \"", fs->fileop_file, "\" to:", NULL );
2221         label = gtk_label_new( buf );
2222         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 );
2223         gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 );
2224         gtk_widget_show( label );
2225         g_free( buf );
2226
2227         /* New filename entry */
2228         fs->fileop_entry = gtk_entry_new();
2229         gtk_box_pack_start( GTK_BOX( vbox ), fs->fileop_entry,
2230                                                 TRUE, TRUE, 5 );
2231         GTK_WIDGET_SET_FLAGS( fs->fileop_entry, GTK_CAN_DEFAULT );
2232         gtk_widget_show( fs->fileop_entry );
2233
2234         gtk_entry_set_text( GTK_ENTRY( fs->fileop_entry ), fs->fileop_file );
2235         gtk_editable_select_region( GTK_EDITABLE( fs->fileop_entry ),
2236                                                                 0, strlen( fs->fileop_file ) );
2237
2238         /* buttons */
2239         button = gtk_button_new_with_label( _( "Rename" ) );
2240         gtk_signal_connect( GTK_OBJECT( button ), "clicked",
2241                                                 (GtkSignalFunc) gtk_file_selection_rename_file_confirmed,
2242                                                 (gpointer) fs );
2243         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
2244                                                 button, TRUE, TRUE, 0 );
2245         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2246         gtk_widget_show( button );
2247
2248         button = gtk_button_new_with_label( _( "Cancel" ) );
2249         gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
2250                                                            (GtkSignalFunc) gtk_widget_destroy,
2251                                                            (gpointer) dialog );
2252         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
2253                                                 button, TRUE, TRUE, 0 );
2254         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2255         gtk_widget_grab_default( button );
2256         gtk_widget_show( button );
2257
2258         gtk_widget_show( dialog );
2259 }
2260
2261 static mode_t
2262 gtk_file_selection_properties_get_mode( PropertiesPrivate* priv ){
2263         mode_t mode = 0;
2264
2265         if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[0] ) ) ) {
2266                 mode |= S_IRUSR;
2267         }
2268         if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[1] ) ) ) {
2269                 mode |= S_IWUSR;
2270         }
2271         if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[2] ) ) ) {
2272                 mode |= S_IXUSR;
2273         }
2274         if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[3] ) ) ) {
2275                 mode |= S_ISUID;
2276         }
2277         if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[4] ) ) ) {
2278                 mode |= S_IRGRP;
2279         }
2280         if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[5] ) ) ) {
2281                 mode |= S_IWGRP;
2282         }
2283         if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[6] ) ) ) {
2284                 mode |= S_IXGRP;
2285         }
2286         if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[7] ) ) ) {
2287                 mode |= S_ISGID;
2288         }
2289         if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[8] ) ) ) {
2290                 mode |= S_IROTH;
2291         }
2292         if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[9] ) ) ) {
2293                 mode |= S_IWOTH;
2294         }
2295         if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[10] ) ) ) {
2296                 mode |= S_IXOTH;
2297         }
2298         if ( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON( priv->mode_buttons[11] ) ) ) {
2299                 mode |= S_ISVTX;
2300         }
2301
2302         return mode;
2303 }
2304
2305 static void
2306 gtk_file_selection_properties_update_mode( GtkWidget *widget, gpointer data ){
2307         GtkFileSelection *fs = data;
2308         PropertiesPrivate *priv = fs->fileop_data;
2309         gchar str[8];
2310
2311         sprintf( str, "(%.4o)", gtk_file_selection_properties_get_mode( priv ) );
2312         gtk_label_set( GTK_LABEL( priv->mode_label ), str );
2313 }
2314
2315 static void
2316 gtk_file_selection_properties( gpointer data ){
2317         GtkFileSelection *fs = data;
2318         GtkWidget *label;
2319         GtkWidget *dialog;
2320         GtkWidget *vbox;
2321         GtkWidget *hbox;
2322         GtkWidget *button;
2323         GtkWidget *notebook;
2324         GtkWidget *table;
2325         GtkWidget *hseparator;
2326         GtkWidget *entry;
2327         GtkWidget *togglebutton;
2328         struct stat statbuf;
2329         struct passwd *pw;
2330         struct group *gp;
2331         gchar *buf;
2332         gchar *path;
2333         gchar *filename;
2334         gchar timeBuf[TIME_STRING_BUF];
2335         gint pagenum = 0;
2336         PropertiesPrivate *priv;
2337         int i;
2338
2339         g_return_if_fail( fs != NULL );
2340         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2341
2342         if ( fs->fileop_dialog ) {
2343                 return;
2344         }
2345
2346         /* main dialog */
2347         fs->fileop_dialog = dialog = gtk_dialog_new();
2348         gtk_signal_connect( GTK_OBJECT( dialog ), "destroy",
2349                                                 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
2350                                                 (gpointer) fs );
2351         priv = fs->fileop_data = g_malloc( sizeof( PropertiesPrivate ) );
2352
2353         gtk_window_set_title( GTK_WINDOW( dialog ), ( "Properties" ) );
2354         gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
2355
2356         /* If file dialog is grabbed, grab option dialog */
2357         /* When option dialog  closed, file dialog will be grabbed again */
2358         if ( GTK_WINDOW( fs )->modal ) {
2359                 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
2360         }
2361
2362         /* Dialog guts go here */
2363         notebook = gtk_notebook_new();
2364         gtk_widget_show( notebook );
2365         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), notebook, TRUE, TRUE, 0 );
2366         gtk_container_set_border_width( GTK_CONTAINER( notebook ), 8 );
2367
2368         path = cmpl_reference_position( fs->cmpl_state );
2369         fs->fileop_file = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) );
2370         filename = g_strconcat( path, "/", fs->fileop_file, NULL );
2371         if ( strlen( fs->fileop_file ) > 0 && !( stat( filename, &statbuf ) ) ) {
2372                 /* stats page */
2373                 table = gtk_table_new( 9, 2, FALSE );
2374                 gtk_widget_show( table );
2375                 gtk_container_add( GTK_CONTAINER( notebook ), table );
2376                 gtk_container_set_border_width( GTK_CONTAINER( table ), 5 );
2377                 gtk_table_set_row_spacings( GTK_TABLE( table ), 4 );
2378                 gtk_table_set_col_spacings( GTK_TABLE( table ), 6 );
2379
2380                 label = gtk_label_new( _( "Statistics" ) );
2381                 gtk_widget_show( label );
2382                 gtk_notebook_set_tab_label( GTK_NOTEBOOK( notebook ), gtk_notebook_get_nth_page( GTK_NOTEBOOK( notebook ), pagenum ), label );
2383                 pagenum++;
2384                 /* path and filename */
2385                 label = gtk_label_new( _( "Path:" ) );
2386                 gtk_widget_show( label );
2387                 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2388                                                   (GtkAttachOptions) ( GTK_FILL ),
2389                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2390                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2391
2392                 label = gtk_label_new( _( path ) );
2393                 gtk_widget_show( label );
2394                 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 0, 1,
2395                                                   (GtkAttachOptions) ( GTK_FILL ),
2396                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2397                 gtk_label_set_justify( GTK_LABEL( label ), GTK_JUSTIFY_LEFT );
2398                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2399
2400                 label = gtk_label_new( _( "File Name:" ) );
2401                 gtk_widget_show( label );
2402                 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2403                                                   (GtkAttachOptions) ( GTK_FILL ),
2404                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2405                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2406
2407                 fs->fileop_entry = entry = gtk_entry_new();
2408                 gtk_widget_show( entry );
2409                 gtk_table_attach( GTK_TABLE( table ), entry, 1, 2, 1, 2,
2410                                                   (GtkAttachOptions) ( GTK_FILL ),
2411                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2412                 gtk_entry_set_text( GTK_ENTRY( entry ), fs->fileop_file );
2413                 if ( access( filename, W_OK ) ) {
2414                         gtk_widget_set_sensitive( GTK_WIDGET( entry ), FALSE );
2415                 }
2416
2417                 hseparator = gtk_hseparator_new();
2418                 gtk_widget_show( hseparator );
2419                 gtk_table_attach( GTK_TABLE( table ), hseparator, 0, 2, 2, 3,
2420                                                   (GtkAttachOptions) ( GTK_FILL ),
2421                                                   (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
2422
2423                 /* file type and size */
2424                 label = gtk_label_new( _( "Type:" ) );
2425                 gtk_widget_show( label );
2426                 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 3, 4,
2427                                                   (GtkAttachOptions) ( GTK_FILL ),
2428                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2429                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2430
2431                 switch ( statbuf.st_mode & S_IFMT )
2432                 {
2433                 case S_IFSOCK:
2434                         buf = g_strdup( "Socket" );
2435                         break;
2436                 case S_IFLNK:
2437                         buf = g_strdup( "Symbolic link" );
2438                         break;
2439                 case S_IFREG:
2440                         buf = g_strdup( "File" );
2441                         break;
2442                 case S_IFBLK:
2443                         buf = g_strdup( "Block device" );
2444                         break;
2445                 case S_IFDIR:
2446                         buf = g_strdup( "Directory" );
2447                         break;
2448                 case S_IFCHR:
2449                         buf = g_strdup( "Character device" );
2450                         break;
2451                 case S_IFIFO:
2452                         buf = g_strdup( "First-in/first-out pipe" );
2453                         break;
2454                 default:
2455                         buf = g_strdup( "Unknown" );
2456                 }
2457
2458
2459                 label = gtk_label_new( buf );
2460                 gtk_widget_show( label );
2461                 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 3, 4,
2462                                                   (GtkAttachOptions) ( GTK_FILL ),
2463                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2464                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2465
2466                 label = gtk_label_new( _( "Size:" ) );
2467                 gtk_widget_show( label );
2468                 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 4, 5,
2469                                                   (GtkAttachOptions) ( GTK_FILL ),
2470                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2471                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2472
2473                 label = gtk_label_new( _( g_strdup_printf( "%ld bytes", statbuf.st_size ) ) );
2474                 gtk_widget_show( label );
2475                 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 4, 5,
2476                                                   (GtkAttachOptions) ( GTK_FILL ),
2477                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2478                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2479
2480                 hseparator = gtk_hseparator_new();
2481                 gtk_widget_show( hseparator );
2482                 gtk_table_attach( GTK_TABLE( table ), hseparator, 0, 2, 5, 6,
2483                                                   (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2484                                                   (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
2485
2486                 /* file dates */
2487                 label = gtk_label_new( _( "Created:" ) );
2488                 gtk_widget_show( label );
2489                 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 6, 7,
2490                                                   (GtkAttachOptions) ( GTK_FILL ),
2491                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2492                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2493
2494                 strftime( timeBuf, TIME_STRING_BUF, "%a %b %d %X %Y", localtime( &statbuf.st_mtime ) );
2495                 label = gtk_label_new( _( timeBuf ) );
2496                 gtk_widget_show( label );
2497                 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 6, 7,
2498                                                   (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2499                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2500                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2501
2502
2503
2504                 label = gtk_label_new( _( "Modified:" ) );
2505                 gtk_widget_show( label );
2506                 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 7, 8,
2507                                                   (GtkAttachOptions) ( GTK_FILL ),
2508                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2509                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2510
2511                 strftime( timeBuf, TIME_STRING_BUF, "%a %b %d %X %Y", localtime( &statbuf.st_mtime ) );
2512                 label = gtk_label_new( _( timeBuf ) );
2513                 gtk_widget_show( label );
2514                 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 7, 8,
2515                                                   (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2516                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2517                 gtk_label_set_justify( GTK_LABEL( label ), GTK_JUSTIFY_LEFT );
2518                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2519
2520
2521                 label = gtk_label_new( _( "Accessed:" ) );
2522                 gtk_widget_show( label );
2523                 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 8, 9,
2524                                                   (GtkAttachOptions) ( GTK_FILL ),
2525                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2526                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2527
2528                 strftime( timeBuf, TIME_STRING_BUF, "%a %b %d %X %Y", localtime( &statbuf.st_atime ) );
2529                 label = gtk_label_new( _( timeBuf ) );
2530                 gtk_widget_show( label );
2531                 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 8, 9,
2532                                                   (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2533                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2534                 gtk_label_set_justify( GTK_LABEL( label ), GTK_JUSTIFY_LEFT );
2535                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2536
2537                 /* permissions page */
2538                 vbox = gtk_vbox_new( FALSE, 4 );
2539                 gtk_widget_show( vbox );
2540                 gtk_container_add( GTK_CONTAINER( notebook ), vbox );
2541                 gtk_container_set_border_width( GTK_CONTAINER( vbox ), 5 );
2542
2543                 label = gtk_label_new( _( "Permissions" ) );
2544                 gtk_widget_show( label );
2545                 gtk_notebook_set_tab_label( GTK_NOTEBOOK( notebook ), gtk_notebook_get_nth_page( GTK_NOTEBOOK( notebook ), pagenum ), label );
2546                 pagenum++;
2547
2548                 /* owner / group */
2549                 table = gtk_table_new( 2, 2, FALSE );
2550                 gtk_widget_show( table );
2551                 gtk_box_pack_start( GTK_BOX( vbox ), table, FALSE, TRUE, 0 );
2552                 gtk_table_set_row_spacings( GTK_TABLE( table ), 2 );
2553                 gtk_table_set_col_spacings( GTK_TABLE( table ), 8 );
2554
2555                 label = gtk_label_new( _( "Owner:" ) );
2556                 gtk_widget_show( label );
2557                 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 0, 1,
2558                                                   (GtkAttachOptions) ( GTK_FILL ),
2559                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2560                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2561
2562                 entry = gtk_entry_new();
2563                 gtk_widget_show( entry );
2564                 gtk_table_attach( GTK_TABLE( table ), entry, 1, 2, 0, 1,
2565                                                   (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2566                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2567                 if ( ( pw = getpwuid( statbuf.st_uid ) ) ) {
2568                         gtk_entry_set_text( GTK_ENTRY( entry ), pw->pw_name );
2569                 }
2570                 else{
2571                         gtk_entry_set_text( GTK_ENTRY( entry ), (gpointer) statbuf.st_uid );
2572                 }
2573                 if ( access( filename, W_OK ) || ( getuid() != 0 ) ) {
2574                         gtk_widget_set_sensitive( GTK_WIDGET( entry ), FALSE );
2575                 }
2576
2577
2578                 label = gtk_label_new( _( "Group:" ) );
2579                 gtk_widget_show( label );
2580                 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2581                                                   (GtkAttachOptions) ( GTK_FILL ),
2582                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2583                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2584
2585                 entry = gtk_entry_new();
2586                 gtk_widget_show( entry );
2587                 gtk_table_attach( GTK_TABLE( table ), entry, 1, 2, 1, 2,
2588                                                   (GtkAttachOptions) ( GTK_EXPAND | GTK_FILL ),
2589                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2590                 if ( ( gp = getgrgid( statbuf.st_gid ) ) ) {
2591                         gtk_entry_set_text( GTK_ENTRY( entry ), gp->gr_name );
2592                 }
2593                 else{
2594                         gtk_entry_set_text( GTK_ENTRY( entry ), (gpointer) statbuf.st_gid );
2595                 }
2596                 if ( access( filename, W_OK ) || ( getuid() != 0 ) ) {
2597                         gtk_widget_set_sensitive( GTK_WIDGET( entry ), FALSE );
2598                 }
2599
2600
2601                 hseparator = gtk_hseparator_new();
2602                 gtk_widget_show( hseparator );
2603                 gtk_box_pack_start( GTK_BOX( vbox ), hseparator, FALSE, TRUE, 0 );
2604
2605                 /* permissions */
2606                 table = gtk_table_new( 4, 5, TRUE );
2607                 gtk_widget_show( table );
2608                 gtk_box_pack_start( GTK_BOX( vbox ), table, FALSE, FALSE, 0 );
2609                 gtk_table_set_row_spacings( GTK_TABLE( table ), 2 );
2610                 gtk_table_set_col_spacings( GTK_TABLE( table ), 4 );
2611                 if ( access( filename, W_OK ) || ( ( getuid() != statbuf.st_uid ) && getuid() != 0 ) ) {
2612                         gtk_widget_set_sensitive( GTK_WIDGET( table ), FALSE );
2613                 }
2614
2615                 hbox = gtk_hbox_new( FALSE, 1 );
2616                 gtk_widget_show( hbox );
2617                 gtk_table_attach( GTK_TABLE( table ), hbox, 0, 1, 0, 1,
2618                                                   (GtkAttachOptions) ( GTK_FILL ),
2619                                                   (GtkAttachOptions) ( GTK_FILL ), 0, 0 );
2620
2621                 priv->mode_label = label = gtk_label_new( "(0000)" );
2622                 gtk_widget_show( label );
2623                 gtk_box_pack_start( GTK_BOX( hbox ), label, FALSE, FALSE, 0 );
2624
2625                 label = gtk_label_new( _( "Read" ) );
2626                 gtk_widget_show( label );
2627                 gtk_table_attach( GTK_TABLE( table ), label, 1, 2, 0, 1,
2628                                                   (GtkAttachOptions) ( 0 ),
2629                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2630
2631                 label = gtk_label_new( _( "Write" ) );
2632                 gtk_widget_show( label );
2633                 gtk_table_attach( GTK_TABLE( table ), label, 2, 3, 0, 1,
2634                                                   (GtkAttachOptions) ( 0 ),
2635                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2636
2637                 label = gtk_label_new( _( "Exec" ) );
2638                 gtk_widget_show( label );
2639                 gtk_table_attach( GTK_TABLE( table ), label, 3, 4, 0, 1,
2640                                                   (GtkAttachOptions) ( 0 ),
2641                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2642
2643                 label = gtk_label_new( _( "Special" ) );
2644                 gtk_widget_show( label );
2645                 gtk_table_attach( GTK_TABLE( table ), label, 4, 5, 0, 1,
2646                                                   (GtkAttachOptions) ( 0 ),
2647                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2648
2649
2650                 label = gtk_label_new( _( "User:" ) );
2651                 gtk_widget_show( label );
2652                 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 1, 2,
2653                                                   (GtkAttachOptions) ( GTK_FILL ),
2654                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2655                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2656
2657                 priv->mode_buttons[0] = togglebutton = gtk_toggle_button_new_with_label( "" );
2658                 gtk_widget_show( togglebutton );
2659                 gtk_table_attach( GTK_TABLE( table ), togglebutton, 1, 2, 1, 2,
2660                                                   (GtkAttachOptions) ( GTK_FILL ),
2661                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2662                 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IRUSR ) {
2663                         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2664                 }
2665
2666                 priv->mode_buttons[1] = togglebutton = gtk_toggle_button_new_with_label( "" );
2667                 gtk_widget_show( togglebutton );
2668                 gtk_table_attach( GTK_TABLE( table ), togglebutton, 2, 3, 1, 2,
2669                                                   (GtkAttachOptions) ( GTK_FILL ),
2670                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2671                 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IWUSR ) {
2672                         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2673                 }
2674
2675                 priv->mode_buttons[2] = togglebutton = gtk_toggle_button_new_with_label( "" );
2676                 gtk_widget_show( togglebutton );
2677                 gtk_table_attach( GTK_TABLE( table ), togglebutton, 3, 4, 1, 2,
2678                                                   (GtkAttachOptions) ( GTK_FILL ),
2679                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2680                 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IXUSR ) {
2681                         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2682                 }
2683
2684                 priv->mode_buttons[3] = togglebutton = gtk_toggle_button_new_with_label( "" );
2685                 gtk_widget_show( togglebutton );
2686                 gtk_table_attach( GTK_TABLE( table ), togglebutton, 4, 5, 1, 2,
2687                                                   (GtkAttachOptions) ( GTK_FILL ),
2688                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2689                 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_ISUID ) {
2690                         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2691                 }
2692
2693
2694
2695                 label = gtk_label_new( _( "Group:" ) );
2696                 gtk_widget_show( label );
2697                 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 2, 3,
2698                                                   (GtkAttachOptions) ( GTK_FILL ),
2699                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2700                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2701
2702                 priv->mode_buttons[4] = togglebutton = gtk_toggle_button_new_with_label( "" );
2703                 gtk_widget_show( togglebutton );
2704                 gtk_table_attach( GTK_TABLE( table ), togglebutton, 1, 2, 2, 3,
2705                                                   (GtkAttachOptions) ( GTK_FILL ),
2706                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2707                 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IRGRP ) {
2708                         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2709                 }
2710
2711                 priv->mode_buttons[5] = togglebutton = gtk_toggle_button_new_with_label( "" );
2712                 gtk_widget_show( togglebutton );
2713                 gtk_table_attach( GTK_TABLE( table ), togglebutton, 2, 3, 2, 3,
2714                                                   (GtkAttachOptions) ( GTK_FILL ),
2715                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2716                 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IWGRP ) {
2717                         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2718                 }
2719
2720                 priv->mode_buttons[6] = togglebutton = gtk_toggle_button_new_with_label( "" );
2721                 gtk_widget_show( togglebutton );
2722                 gtk_table_attach( GTK_TABLE( table ), togglebutton, 3, 4, 2, 3,
2723                                                   (GtkAttachOptions) ( GTK_FILL ),
2724                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2725                 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IXGRP ) {
2726                         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2727                 }
2728
2729                 priv->mode_buttons[7] = togglebutton = gtk_toggle_button_new_with_label( "" );
2730                 gtk_widget_show( togglebutton );
2731                 gtk_table_attach( GTK_TABLE( table ), togglebutton, 4, 5, 2, 3,
2732                                                   (GtkAttachOptions) ( GTK_FILL ),
2733                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2734                 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_ISGID ) {
2735                         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2736                 }
2737
2738                 label = gtk_label_new( _( "Other:" ) );
2739                 gtk_widget_show( label );
2740                 gtk_table_attach( GTK_TABLE( table ), label, 0, 1, 3, 4,
2741                                                   (GtkAttachOptions) ( GTK_FILL ),
2742                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2743                 gtk_misc_set_alignment( GTK_MISC( label ), 0, 0.5 );
2744
2745                 priv->mode_buttons[8] = togglebutton = gtk_toggle_button_new_with_label( "" );
2746                 gtk_widget_show( togglebutton );
2747                 gtk_table_attach( GTK_TABLE( table ), togglebutton, 1, 2, 3, 4,
2748                                                   (GtkAttachOptions) ( GTK_FILL ),
2749                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2750                 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IROTH ) {
2751                         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2752                 }
2753
2754                 priv->mode_buttons[9] = togglebutton = gtk_toggle_button_new_with_label( "" );
2755                 gtk_widget_show( togglebutton );
2756                 gtk_table_attach( GTK_TABLE( table ), togglebutton, 2, 3, 3, 4,
2757                                                   (GtkAttachOptions) ( GTK_FILL ),
2758                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2759                 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IWOTH ) {
2760                         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2761                 }
2762
2763                 priv->mode_buttons[10] = togglebutton = gtk_toggle_button_new_with_label( "" );
2764                 gtk_widget_show( togglebutton );
2765                 gtk_table_attach( GTK_TABLE( table ), togglebutton, 3, 4, 3, 4,
2766                                                   (GtkAttachOptions) ( GTK_FILL ),
2767                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2768                 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_IXOTH ) {
2769                         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2770                 }
2771
2772                 priv->mode_buttons[11] = togglebutton = gtk_toggle_button_new_with_label( "" );
2773                 gtk_widget_show( togglebutton );
2774                 gtk_table_attach( GTK_TABLE( table ), togglebutton, 4, 5, 3, 4,
2775                                                   (GtkAttachOptions) ( GTK_FILL ),
2776                                                   (GtkAttachOptions) ( 0 ), 0, 0 );
2777                 if ( ( statbuf.st_mode & ~( S_IFMT ) ) & S_ISVTX ) {
2778                         gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON( togglebutton ), TRUE );
2779                 }
2780
2781                 for ( i = 0; i < 12; i++ )
2782                         gtk_signal_connect( GTK_OBJECT( priv->mode_buttons[i] ), "toggled",
2783                                                                 GTK_SIGNAL_FUNC( gtk_file_selection_properties_update_mode ), fs );
2784                 gtk_toggle_button_toggled( GTK_TOGGLE_BUTTON( priv->mode_buttons[0] ) );
2785         }
2786         /* global page */
2787         vbox = gtk_vbox_new( FALSE, 0 );
2788         gtk_widget_show( vbox );
2789         gtk_container_add( GTK_CONTAINER( notebook ), vbox );
2790
2791         label = gtk_label_new( _( "Global" ) );
2792         gtk_widget_show( label );
2793         gtk_notebook_set_tab_label( GTK_NOTEBOOK( notebook ), gtk_notebook_get_nth_page( GTK_NOTEBOOK( notebook ), pagenum ), label );
2794         pagenum++;
2795
2796         label = gtk_label_new( _( "dialog preferances will go here" ) );
2797         gtk_widget_show( label );
2798         gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 0 );
2799
2800         /* end of dialog guts */
2801
2802         /* buttons */
2803         button = gtk_button_new_with_label( _( "OK" ) );
2804         //  gtk_signal_connect (GTK_OBJECT (button), "clicked",
2805         //                    (GtkSignalFunc) gtk_file_selection_rename_file_confirmed,
2806         //                    (gpointer) fs);
2807         gtk_signal_connect( GTK_OBJECT( button ), "clicked",
2808                                                 (GtkSignalFunc) gtk_file_selection_file_mode_confirmed,
2809                                                 (gpointer) fs );
2810         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
2811                                                 button, TRUE, TRUE, 0 );
2812         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2813         gtk_widget_show( button );
2814
2815         button = gtk_button_new_with_label( _( "Cancel" ) );
2816         gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
2817                                                            (GtkSignalFunc) gtk_widget_destroy,
2818                                                            (gpointer) dialog );
2819         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
2820                                                 button, TRUE, TRUE, 0 );
2821         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
2822         gtk_widget_grab_default( button );
2823         gtk_widget_show( button );
2824
2825         g_free( filename );
2826         gtk_widget_show( dialog );
2827 }
2828
2829 static gint
2830 gtk_file_selection_key_press( GtkWidget   *widget,
2831                                                           GdkEventKey *event,
2832                                                           gpointer user_data ){
2833
2834         GtkFileSelection *fs;
2835         char *text;
2836
2837         g_return_val_if_fail( widget != NULL, FALSE );
2838         g_return_val_if_fail( event != NULL, FALSE );
2839
2840         fs = GTK_FILE_SELECTION( user_data );
2841
2842         if ( fs->saved_entry ) {
2843                 gtk_clist_unselect_all( (GtkCList *) ( fs->dir_list ) );
2844                 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry );
2845                 g_free( fs->saved_entry );
2846                 fs->saved_entry = NULL;
2847         }
2848         if ( event->keyval == GDK_Tab ) {
2849                 text = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) );
2850
2851                 text = g_strdup( text );
2852
2853                 gtk_file_selection_populate( fs, text, TRUE );
2854
2855                 g_free( text );
2856
2857                 gtk_signal_emit_stop_by_name( GTK_OBJECT( widget ), "key_press_event" );
2858
2859                 return TRUE;
2860         }
2861
2862
2863         return FALSE;
2864 }
2865
2866 /*
2867    static void
2868    gtk_file_selection_home_button (GtkWidget *widget, gpointer data){
2869    GList *list;
2870
2871    GtkFileSelection *fs=data;
2872
2873    list = fs->next_history;
2874    if (list)
2875     {
2876       g_free (list->data);
2877       list = list->next;
2878     }
2879    g_list_free (fs->next_history);
2880    fs->next_history = NULL;
2881
2882    gtk_file_selection_populate (fs,"~/",FALSE);
2883    }
2884  */
2885 static void
2886 gtk_file_selection_bookmark_button( GtkWidget *widget,
2887                                                                         GtkFileSelection *fs ){
2888
2889         g_return_if_fail( fs != NULL );
2890         g_return_if_fail( GTK_FILE_SELECTION( fs ) );
2891
2892         gtk_menu_popup( GTK_MENU( fs->bookmark_menu ), NULL, NULL, NULL, NULL,
2893                                         0, 0 );
2894
2895 }
2896
2897 static void
2898 gtk_file_selection_up_button( GtkWidget *widget, gpointer data ){
2899         GtkFileSelection *fs = data;
2900         GList *list;
2901
2902         g_return_if_fail( fs != NULL );
2903         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2904
2905         list = fs->next_history;
2906         if ( list ) {
2907                 g_free( list->data );
2908                 list = list->next;
2909         }
2910         g_list_free( fs->next_history );
2911         fs->next_history = NULL;
2912
2913         gtk_file_selection_populate( fs, "../", FALSE ); /*change directories. */
2914
2915 }
2916
2917 static void
2918 gtk_file_selection_prev_button( GtkWidget *widget, gpointer data ){
2919         GtkFileSelection *fs = data;
2920         GList *list;
2921         GList *first;
2922         gchar *path;
2923
2924         g_return_if_fail( fs != NULL );
2925         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2926
2927         list = fs->prev_history;
2928
2929         if ( list && g_list_length( list ) > 1 ) {
2930                 first = list;          /* get first element */
2931                 list = list->next;     /* pop off current directory */
2932
2933                 list->prev = NULL;     /* make this the new head. */
2934
2935                 fs->prev_history = list; /* update prev_history list */
2936                 fs->next_history = g_list_prepend( fs->next_history,first->data ); /* put it on next_history */
2937
2938                 first->next = NULL;    /* orphan the old first node */
2939                 g_list_free( first );  /* free the node (data is now in use by next_history) */
2940
2941
2942
2943                 path = g_malloc( strlen( list->data ) + 4 ); /* plenty of space */
2944                 strcpy( path,list->data );           /* get the 2nd path in the history */
2945                 strcat( path,"/" );                  /* append a '/' */
2946                 gtk_file_selection_populate( fs, path, FALSE ); /* change directories. */
2947                 g_free( path );
2948         }
2949 }
2950
2951 static void
2952 gtk_file_selection_next_button( GtkWidget *widget, gpointer data ){
2953         GtkFileSelection *fs = data;
2954         GList *list;
2955         GList *first;
2956         gchar *path;
2957
2958         g_return_if_fail( fs != NULL );
2959         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2960
2961         list = fs->next_history;
2962
2963         if ( list && g_list_length( list ) > 0 ) {
2964                 first = list;          /*get first element*/
2965                 list = list->next;     /*pop off current directory*/
2966
2967                 if ( list ) {
2968                         list->prev = NULL;
2969                 }
2970
2971                 fs->next_history = list;                     /*update prev_history list*/
2972
2973                 path = g_malloc( strlen( first->data ) + 4 ); /*plenty of space*/
2974                 strcpy( path,first->data );
2975                 strcat( path,"/" );                          /*append a /   */
2976                 gtk_file_selection_populate( fs, path, FALSE ); /*change directories.*/
2977                 g_free( path );
2978
2979                 first->next = NULL;   /* orphan the old first node */
2980                 g_list_free( first ); /* free the node (data is now in use by next_history) */
2981
2982         }
2983 }
2984
2985 void static
2986 gtk_file_selection_refresh_button( GtkWidget *widget, gpointer data ){
2987         GtkFileSelection *fs = data;
2988
2989         g_return_if_fail( fs != NULL );
2990         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
2991
2992         gtk_file_selection_populate( fs,"",FALSE );
2993 }
2994
2995 static void
2996 gtk_file_selection_mask_entry_callback( GtkWidget *widget, gpointer data ){
2997         GtkFileSelection *fs = data;
2998
2999         if ( fs->mask ) {
3000                 g_free( fs->mask );
3001         }
3002
3003         fs->mask = g_strdup( gtk_entry_get_text( GTK_ENTRY( GTK_COMBO( fs->mask_entry )->entry ) ) );
3004
3005         if ( strlen( fs->mask ) == 0 ) {
3006                 g_free( fs->mask );
3007                 fs->mask = NULL;
3008         }
3009
3010         gtk_file_selection_refresh_button( widget,data );
3011 }
3012
3013 static gint gtk_file_selection_files_list_key_callback( GtkWidget *widget, GdkEventKey *event, gpointer data ){
3014         GtkFileSelection *fs = data;
3015         gchar *saved;
3016         gchar key[2];
3017
3018 //  g_print("Key event: %d\n",event->keyval);
3019         //we need some sort of timeout.
3020
3021         //if the key is a normal character then
3022         //add to our saved_entry1
3023         //if it's backspace then remove one character
3024         //otherwise let it through (and erase our buffer.
3025
3026         if ( event->keyval > GDK_space && event->keyval <=  GDK_Korean_Won ) {
3027                 key[1] = 0;
3028                 key[0] = event->keyval;
3029                 saved = fs->saved_entry1;
3030                 if ( fs->saved_entry1 ) {
3031                         fs->saved_entry1 = g_strconcat( saved,key,NULL );
3032                         g_free( saved );
3033                 }
3034                 else{
3035                         fs->saved_entry1 = g_strdup( key );
3036                 }
3037                 g_print( "complete: %s\n",fs->saved_entry1 );
3038                 /*gtk_label_set_text(GTK_LABEL(fs->completion_label), fs->saved_entry1); */
3039
3040                 saved = g_strdup( gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ) );
3041                 gtk_file_selection_complete( fs,fs->saved_entry1 );
3042                 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),saved );
3043                 g_free( saved );
3044         }
3045         else if ( event->keyval ==  GDK_BackSpace ) {
3046                 if ( strlen( fs->saved_entry1 ) ) {
3047                         fs->saved_entry1[strlen( fs->saved_entry1 ) - 1] = 0;
3048                         g_print( "complete: %s\n",fs->saved_entry1 );
3049                         /*gtk_label_set_text(GTK_LABEL(fs->completion_label),fs->saved_entry1); */
3050                         saved = g_strdup( gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ) );
3051                         gtk_file_selection_complete( fs,fs->saved_entry1 );
3052                         gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),saved );
3053                         g_free( saved );
3054                 }
3055         }
3056         else if ( event->keyval == GDK_Tab ) {
3057                 saved = g_strdup( gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ) );
3058                 gtk_file_selection_populate( fs,fs->saved_entry1,TRUE );
3059                 g_free( fs->saved_entry1 );
3060                 fs->saved_entry1 = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) );
3061                 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),saved );
3062                 g_free( saved );
3063
3064                 g_print( "complete: %s\n",fs->saved_entry1 );
3065                 /* gtk_label_set_text(GTK_LABEL(fs->completion_label),fs->saved_entry1);*/
3066
3067                 gtk_signal_emit_stop_by_name( GTK_OBJECT( widget ), "key_press_event" );
3068         }
3069         else {
3070                 if ( fs->saved_entry1 ) {
3071                         g_free( fs->saved_entry1 );
3072                         fs->saved_entry1 = NULL;
3073                 }
3074                 /* gtk_label_set_text(GTK_LABEL(fs->completion_label)," "); */
3075         }
3076
3077         return TRUE;
3078 }
3079
3080
3081 static gint gtk_file_selection_mask_entry_key_callback( GtkWidget *widget, GdkEventKey *event, gpointer data ){
3082         GtkEntry *entry = (GtkEntry *)widget;
3083         GtkFileSelection *fs = data;
3084
3085         g_return_val_if_fail( fs != NULL,FALSE );
3086         g_return_val_if_fail( GTK_IS_FILE_SELECTION( fs ),FALSE );
3087
3088
3089         if ( event->keyval == GDK_Return || event->keyval == GDK_Tab ) {
3090                 if ( fs->mask ) {
3091                         g_free( fs->mask );
3092                 }
3093
3094                 fs->mask = g_strdup( gtk_entry_get_text( entry ) );
3095                 gtk_file_selection_refresh_button( widget,fs );
3096
3097                 if ( event->keyval == GDK_Return ) {
3098                         gtk_signal_emit_stop_by_name( GTK_OBJECT( widget ), "key_press_event" );
3099                 }
3100                 return TRUE;
3101         }
3102         else
3103         {
3104                 return FALSE;
3105         }
3106 }
3107
3108 static gint gtk_file_selection_mask_entry_button_callback( GtkWidget *widget, GdkEventButton *event, gpointer data ){
3109         GtkFileSelection *fs = data;
3110
3111         if ( fs->mask ) {
3112                 g_free( fs->mask );
3113         }
3114
3115         fs->mask = g_strdup( gtk_entry_get_text( GTK_ENTRY( GTK_COMBO( fs->mask_entry )->entry ) ) );
3116         gtk_file_selection_refresh_button( widget,fs );
3117
3118         return TRUE;
3119
3120 }
3121
3122 static gboolean gtk_file_selection_history_combo_list_key_handler( GtkWidget *widget,
3123                                                                                                                                    GdkEventKey *event,
3124                                                                                                                                    gpointer user_data ){
3125         /*
3126            g_print("Key pressed! \n");
3127          */
3128
3129         return TRUE;
3130 }
3131
3132 static gboolean gtk_file_selection_history_combo_list_callback( GtkWidget *thelist,
3133                                                                                                                                 GdkEventButton *event,
3134                                                                                                                                 gpointer user_data ){
3135
3136         GtkFileSelection *fs = user_data;
3137         GList *list;
3138         gchar *path;
3139
3140         list = fs->next_history;
3141         if ( list ) {
3142                 g_free( list->data );
3143                 list = list->next;
3144         }
3145         g_list_free( fs->next_history );
3146         fs->next_history = NULL;
3147
3148         path = g_malloc( strlen( gtk_entry_get_text( GTK_ENTRY( ( (GtkCombo *)fs->history_combo )->entry ) ) ) + 4 );
3149         strcpy( path,gtk_entry_get_text( GTK_ENTRY( ( (GtkCombo *)fs->history_combo )->entry ) ) );
3150         strcat( path,"/" );
3151
3152         gtk_file_selection_populate( fs,path,TRUE );
3153
3154         g_free( path );
3155
3156         return TRUE;
3157 }
3158
3159 static gboolean
3160 gtk_file_selection_history_combo_callback( GtkWidget *widget, GdkEventKey *event, gpointer data ){
3161         GtkEntry *entry = (GtkEntry *)widget;
3162         GtkFileSelection *fs = data;
3163         GList *list;
3164         gchar *path;
3165
3166         g_return_val_if_fail( fs != NULL,FALSE );
3167         g_return_val_if_fail( GTK_IS_FILE_SELECTION( fs ),FALSE );
3168
3169
3170         if ( event->keyval == GDK_Return ) {
3171                 list = fs->next_history;
3172                 if ( list ) {
3173                         g_free( list->data );
3174                         list = list->next;
3175                 }
3176                 g_list_free( fs->next_history );
3177                 fs->next_history = NULL;
3178
3179                 path = g_malloc( strlen( gtk_entry_get_text( entry ) ) + 4 );
3180                 strcpy( path,gtk_entry_get_text( entry ) );
3181                 strcat( path,"/" );
3182                 gtk_file_selection_populate( fs,path,TRUE );
3183                 g_free( path );
3184                 gtk_signal_emit_stop_by_name( GTK_OBJECT( widget ), "key_press_event" );
3185                 return TRUE;
3186         }
3187         else
3188         {
3189                 return FALSE;
3190         }
3191
3192 }
3193
3194
3195 static void gtk_file_selection_bookmark_callback( GtkWidget *widget, gpointer data ){
3196         GtkFileSelection *fs = data;
3197         BookmarkMenuStruct *item;
3198         GList *list;
3199
3200         g_return_if_fail( fs != NULL );
3201         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
3202
3203 //g_print ("Callback\n");
3204         list = fs->bookmark_list;
3205         while ( list ) {
3206                 item = list->data;
3207                 if ( item->menu_item == widget ) {
3208                         if ( strcmp( item->path,"./" ) ) {
3209                                 gtk_file_selection_populate( fs, item->path, FALSE );
3210                         }
3211                         break;
3212                 }
3213                 list = list->next;
3214         }
3215 }
3216
3217 static void
3218 gtk_file_selection_update_history_menu( GtkFileSelection *fs,
3219                                                                                 gchar *current_directory ){
3220         gchar *current_dir;
3221
3222         g_return_if_fail( fs != NULL );
3223         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
3224         g_return_if_fail( current_directory != NULL );
3225
3226         current_dir = g_strdup( current_directory );
3227
3228         if ( fs->prev_history ) {
3229                 if ( strcmp( ( fs->prev_history )->data,current_dir ) ) { /*if this item isn't on the top of the list */
3230                         fs->prev_history = g_list_prepend( fs->prev_history,g_strdup( current_dir ) );
3231                 }
3232         }
3233         else {
3234                 fs->prev_history = g_list_prepend( fs->prev_history,g_strdup( current_dir ) );
3235         }
3236
3237         gtk_combo_set_popdown_strings( GTK_COMBO( fs->history_combo ),fs->prev_history );
3238
3239         g_free( current_dir );
3240 }
3241
3242 static void
3243 gtk_file_selection_file_button( GtkWidget *widget,
3244                                                                 gint row,
3245                                                                 gint column,
3246                                                                 GdkEventButton *bevent,
3247                                                                 gpointer user_data ){
3248         GtkFileSelection *fs = NULL;
3249         gchar *filename, *temp = NULL;
3250
3251         g_return_if_fail( GTK_IS_CLIST( widget ) );
3252
3253         fs = user_data;
3254         g_return_if_fail( fs != NULL );
3255         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
3256
3257         gtk_clist_get_text( GTK_CLIST( fs->file_list ), row, 0, &temp );
3258         filename = g_strdup( temp );
3259
3260         if ( filename ) {
3261                 if ( fs->saved_entry ) {
3262                         gtk_clist_unselect_all( (GtkCList *) ( fs->dir_list ) );
3263                         gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry );
3264                         g_free( fs->saved_entry );
3265                         fs->saved_entry = NULL;
3266                 }
3267                 if ( fs->saved_entry1 ) {
3268                         g_free( fs->saved_entry1 );
3269                         fs->saved_entry1 = NULL;
3270                 }
3271                 /* gtk_label_set_text(GTK_LABEL(fs->completion_label)," "); */
3272
3273
3274                 if ( bevent ) {
3275                         switch ( bevent->type )
3276                         {
3277                         case GDK_2BUTTON_PRESS:
3278                                 gtk_button_clicked( GTK_BUTTON( fs->ok_button ) );
3279                                 break;
3280
3281                         default:
3282 /*
3283         if (bevent->button && GDK_BUTTON2_MASK)
3284           {
3285             g_print("Right click! -- %d\n",bevent->button);
3286           }
3287           else
3288           {
3289  */
3290
3291                                 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename );
3292                                 /*}*/
3293                                 break;
3294                         }
3295                 }
3296                 else{
3297                         gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename );
3298                 }
3299
3300                 g_free( filename );
3301         }
3302 }
3303
3304 static void
3305 gtk_file_selection_dir_button( GtkWidget *widget,
3306                                                            gint row,
3307                                                            gint column,
3308                                                            GdkEventButton *bevent,
3309                                                            gpointer user_data ){
3310         GList *list;
3311         GtkFileSelection *fs = NULL;
3312         gchar *filename, *temp = NULL;
3313
3314         g_return_if_fail( GTK_IS_CLIST( widget ) );
3315
3316         fs = GTK_FILE_SELECTION( user_data );
3317         g_return_if_fail( fs != NULL );
3318         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
3319
3320         gtk_clist_get_text( GTK_CLIST( fs->dir_list ), row, 0, &temp );
3321         filename = g_strdup( temp );
3322
3323         if ( filename ) {
3324                 if ( bevent ) {
3325                         switch ( bevent->type )
3326                         {
3327                         case GDK_2BUTTON_PRESS:
3328                                 list = fs->next_history;
3329                                 if ( list ) {
3330                                         g_free( list->data );
3331                                         list = list->next;
3332                                 }
3333                                 g_list_free( fs->next_history );
3334                                 fs->next_history = NULL;
3335
3336                                 gtk_file_selection_populate( fs, filename, FALSE );
3337                                 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry );
3338                                 g_free( fs->saved_entry );
3339                                 fs->saved_entry = NULL;
3340                                 break;
3341
3342                         default:
3343                                 /* here we need to add the "filename" to the beginning of what's already
3344                                    in the entry.  Save what's in the entry, then restore it on the double click
3345                                  */
3346                                 if ( fs->saved_entry ) {
3347                                         g_free( fs->saved_entry );
3348                                 }
3349                                 fs->saved_entry = g_strdup( gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ) );
3350
3351                                 temp = g_strconcat( filename,fs->saved_entry,NULL );
3352                                 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), temp );
3353                                 g_free( temp );
3354
3355                                 break;
3356                         }
3357                 }
3358                 else{
3359                         gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename );
3360                 }
3361
3362                 g_free( filename );
3363         }
3364 }
3365
3366 static void
3367 gtk_file_selection_undir_button( GtkWidget *widget,
3368                                                                  gint row,
3369                                                                  gint column,
3370                                                                  GdkEventButton *bevent,
3371                                                                  gpointer user_data ){
3372         GtkFileSelection *fs = NULL;
3373         gchar *filename, *temp = NULL;
3374
3375         g_return_if_fail( GTK_IS_CLIST( widget ) );
3376
3377         fs = GTK_FILE_SELECTION( user_data );
3378         g_return_if_fail( fs != NULL );
3379         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
3380
3381         gtk_clist_get_text( GTK_CLIST( fs->dir_list ), row, 0, &temp );
3382         filename = g_strdup( temp );
3383
3384         if ( filename ) {
3385                 if ( bevent ) {
3386                         switch ( bevent->type )
3387                         {
3388                         default:
3389                                 /* here we need to add the "filename" to the beginning of what's already
3390                                    in the entry.  Save what's in the entry, then restore it on the double click
3391                                  */
3392                                 if ( fs->saved_entry ) {
3393                                         gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry );
3394                                         g_free( fs->saved_entry );
3395                                         fs->saved_entry = NULL;
3396                                 }
3397                                 break;
3398                         }
3399                 }
3400                 else{
3401                         gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename ); //?????
3402
3403                 }
3404                 g_free( filename );
3405         }
3406 }
3407
3408 static void
3409 gtk_file_selection_populate( GtkFileSelection *fs,
3410                                                          gchar            *rel_path,
3411                                                          gint try_complete ){
3412         CompletionState *cmpl_state;
3413         PossibleCompletion* poss;
3414         gchar* filename;
3415         gint row;
3416         gchar* rem_path = rel_path;
3417         gchar* sel_text;
3418         gchar* text[2];
3419         gint did_recurse = FALSE;
3420         gint possible_count = 0;
3421         gint selection_index = -1;
3422         gint file_list_width;
3423         gint dir_list_width;
3424
3425         g_return_if_fail( fs != NULL );
3426         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
3427
3428         cmpl_state = (CompletionState*) fs->cmpl_state;
3429         poss = cmpl_completion_matches( rel_path, &rem_path, cmpl_state );
3430
3431         if ( !cmpl_state_okay( cmpl_state ) ) {
3432                 /* Something went wrong. */
3433                 gtk_file_selection_abort( fs );
3434                 return;
3435         }
3436
3437         g_assert( cmpl_state->reference_dir );
3438
3439         gtk_clist_freeze( GTK_CLIST( fs->dir_list ) );
3440         gtk_clist_clear( GTK_CLIST( fs->dir_list ) );
3441         gtk_clist_freeze( GTK_CLIST( fs->file_list ) );
3442         gtk_clist_clear( GTK_CLIST( fs->file_list ) );
3443
3444         /* Set the dir_list to include ./ and ../ */
3445         /* Actually, no let's not.
3446            text[1] = NULL;
3447            text[0] = "./";
3448            row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
3449          */
3450
3451         text[0] = "../"; //Do we need ..?
3452         row = gtk_clist_append( GTK_CLIST( fs->dir_list ), text );
3453
3454         /*reset the max widths of the lists*/
3455         dir_list_width = gdk_string_width( fs->dir_list->style->font,"../" );
3456         gtk_clist_set_column_width( GTK_CLIST( fs->dir_list ),0,dir_list_width );
3457         file_list_width = 1;
3458         gtk_clist_set_column_width( GTK_CLIST( fs->file_list ),0,file_list_width );
3459
3460         while ( poss )
3461         {
3462                 if ( cmpl_is_a_completion( poss ) ) {
3463                         possible_count += 1;
3464
3465                         filename = cmpl_this_completion( poss );
3466
3467                         text[0] = filename;
3468
3469                         if ( cmpl_is_directory( poss ) ) {
3470                                 if ( strcmp( filename, "./" ) != 0 &&
3471                                          strcmp( filename, "../" ) != 0 ) {
3472                                         int width = gdk_string_width( fs->dir_list->style->font,
3473                                                                                                   filename );
3474                                         row = gtk_clist_append( GTK_CLIST( fs->dir_list ), text );
3475                                         if ( width > dir_list_width ) {
3476                                                 dir_list_width = width;
3477                                                 gtk_clist_set_column_width( GTK_CLIST( fs->dir_list ),0,
3478                                                                                                         width );
3479                                         }
3480                                 }
3481                         }
3482                         else
3483                         {
3484                                 if ( fs->mask ) {
3485                                         if ( gtk_file_selection_match_mask( filename,fs->mask ) ) {
3486                                                 int width = gdk_string_width( fs->file_list->style->font,
3487                                                                                                           filename );
3488                                                 row = gtk_clist_append( GTK_CLIST( fs->file_list ), text );
3489                                                 if ( width > file_list_width ) {
3490                                                         file_list_width = width;
3491                                                         gtk_clist_set_column_width( GTK_CLIST( fs->file_list ),0,
3492                                                                                                                 width );
3493                                                 }
3494                                         }
3495                                 }
3496                                 else
3497                                 {
3498                                         int width = gdk_string_width( fs->file_list->style->font,
3499                                                                                                   filename );
3500                                         row = gtk_clist_append( GTK_CLIST( fs->file_list ), text );
3501                                         if ( width > file_list_width ) {
3502                                                 file_list_width = width;
3503                                                 gtk_clist_set_column_width( GTK_CLIST( fs->file_list ),0,
3504                                                                                                         width );
3505                                         }
3506                                 }
3507                         }
3508                 }
3509
3510                 poss = cmpl_next_completion( cmpl_state );
3511         }
3512
3513         gtk_clist_thaw( GTK_CLIST( fs->dir_list ) );
3514         gtk_clist_thaw( GTK_CLIST( fs->file_list ) );
3515
3516         /* File lists are set. */
3517
3518         g_assert( cmpl_state->reference_dir );
3519
3520         if ( try_complete ) {
3521
3522                 /* User is trying to complete filenames, so advance the user's input
3523                  * string to the updated_text, which is the common leading substring
3524                  * of all possible completions, and if its a directory attempt
3525                  * attempt completions in it. */
3526
3527                 if ( cmpl_updated_text( cmpl_state )[0] ) {
3528
3529                         if ( cmpl_updated_dir( cmpl_state ) ) {
3530                                 gchar* dir_name = g_strdup( cmpl_updated_text( cmpl_state ) );
3531
3532                                 did_recurse = TRUE;
3533
3534                                 gtk_file_selection_populate( fs, dir_name, TRUE );
3535
3536                                 g_free( dir_name );
3537                         }
3538                         else
3539                         {
3540                                 if ( fs->selection_entry ) {
3541                                         gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),
3542                                                                                 cmpl_updated_text( cmpl_state ) );
3543                                 }
3544                         }
3545                 }
3546                 else
3547                 {
3548                         selection_index = cmpl_last_valid_char( cmpl_state ) -
3549                                                           ( strlen( rel_path ) - strlen( rem_path ) );
3550                         if ( fs->selection_entry ) {
3551                                 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), rem_path );
3552                         }
3553                 }
3554         }
3555         else
3556         {
3557                 if ( fs->selection_entry ) {
3558                         /* Here we need to take the old filename and keep it!*/
3559                         /*gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");*/
3560                         ;
3561                 }
3562         }
3563
3564         if ( !did_recurse ) {
3565                 if ( fs->selection_entry ) {
3566                         gtk_entry_set_position( GTK_ENTRY( fs->selection_entry ), selection_index );
3567                 }
3568
3569                 if ( fs->selection_entry ) {
3570                         sel_text = g_strconcat( _( "Selection: " ),
3571                                                                         cmpl_reference_position( cmpl_state ),
3572                                                                         NULL );
3573
3574 /*
3575         gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text);
3576  */
3577                         g_free( sel_text );
3578                 }
3579
3580                 gtk_file_selection_update_history_menu( fs, cmpl_reference_position( cmpl_state ) );
3581
3582         }
3583 }
3584
3585 static void
3586 gtk_file_selection_abort( GtkFileSelection *fs ){
3587         gchar err_buf[256];
3588
3589         sprintf( err_buf, _( "Directory unreadable: %s" ), cmpl_strerror( cmpl_errno ) );
3590
3591         /*  BEEP gdk_beep();  */
3592
3593 /*
3594    if (fs->selection_entry)
3595     gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
3596  */
3597 }
3598
3599 /**********************************************************************/
3600 /*                        External Interface                          */
3601 /**********************************************************************/
3602
3603 /* The four completion state selectors
3604  */
3605 static gchar*
3606 cmpl_updated_text( CompletionState* cmpl_state ){
3607         return cmpl_state->updated_text;
3608 }
3609
3610 static gint
3611 cmpl_updated_dir( CompletionState* cmpl_state ){
3612         return cmpl_state->re_complete;
3613 }
3614
3615 static gchar*
3616 cmpl_reference_position( CompletionState* cmpl_state ){
3617         return cmpl_state->reference_dir->fullname;
3618 }
3619
3620 static gint
3621 cmpl_last_valid_char( CompletionState* cmpl_state ){
3622         return cmpl_state->last_valid_char;
3623 }
3624
3625 static gchar*
3626 cmpl_completion_fullname( gchar* text, CompletionState* cmpl_state ){
3627         static char nothing[2] = "";
3628
3629         if ( !cmpl_state_okay( cmpl_state ) ) {
3630                 return nothing;
3631         }
3632         else if ( text[0] == '/' ) {
3633                 strcpy( cmpl_state->updated_text, text );
3634         }
3635         else if ( text[0] == '~' ) {
3636                 CompletionDir* dir;
3637                 char* slash;
3638
3639                 dir = open_user_dir( text, cmpl_state );
3640
3641                 if ( !dir ) {
3642                         /* spencer says just return ~something, so
3643                          * for now just do it. */
3644                         strcpy( cmpl_state->updated_text, text );
3645                 }
3646                 else
3647                 {
3648
3649                         strcpy( cmpl_state->updated_text, dir->fullname );
3650
3651                         slash = strchr( text, '/' );
3652
3653                         if ( slash ) {
3654                                 strcat( cmpl_state->updated_text, slash );
3655                         }
3656                 }
3657         }
3658         else
3659         {
3660                 strcpy( cmpl_state->updated_text, cmpl_state->reference_dir->fullname );
3661                 if ( strcmp( cmpl_state->reference_dir->fullname, "/" ) != 0 ) {
3662                         strcat( cmpl_state->updated_text, "/" );
3663                 }
3664                 strcat( cmpl_state->updated_text, text );
3665         }
3666
3667         return cmpl_state->updated_text;
3668 }
3669
3670 /* The three completion selectors
3671  */
3672 static gchar*
3673 cmpl_this_completion( PossibleCompletion* pc ){
3674         return pc->text;
3675 }
3676
3677 static gint
3678 cmpl_is_directory( PossibleCompletion* pc ){
3679         return pc->is_directory;
3680 }
3681
3682 static gint
3683 cmpl_is_a_completion( PossibleCompletion* pc ){
3684         return pc->is_a_completion;
3685 }
3686
3687 /**********************************************************************/
3688 /*                       Construction, deletion                       */
3689 /**********************************************************************/
3690
3691 static CompletionState*
3692 cmpl_init_state( void ){
3693         gchar getcwd_buf[2 * MAXPATHLEN];
3694         CompletionState *new_state;
3695
3696         new_state = g_new( CompletionState, 1 );
3697
3698         /* We don't use getcwd() on SUNOS, because, it does a popen("pwd")
3699          * and, if that wasn't bad enough, hangs in doing so.
3700          */
3701 #if defined( sun ) && !defined( __SVR4 )
3702         if ( !getwd( getcwd_buf ) )
3703 #else
3704         if ( !getcwd( getcwd_buf, MAXPATHLEN ) )
3705 #endif
3706         {
3707                 /* Oh joy, we can't get the current directory. Um..., we should have
3708                  * a root directory, right? Right? (Probably not portable to non-Unix)
3709                  */
3710                 strcpy( getcwd_buf, "/" );
3711         }
3712
3713 tryagain:
3714
3715         new_state->reference_dir = NULL;
3716         new_state->completion_dir = NULL;
3717         new_state->active_completion_dir = NULL;
3718         new_state->directory_storage = NULL;
3719         new_state->directory_sent_storage = NULL;
3720         new_state->last_valid_char = 0;
3721         new_state->updated_text = g_new( gchar, MAXPATHLEN );
3722         new_state->updated_text_alloc = MAXPATHLEN;
3723         new_state->the_completion.text = g_new( gchar, MAXPATHLEN );
3724         new_state->the_completion.text_alloc = MAXPATHLEN;
3725         new_state->user_dir_name_buffer = NULL;
3726         new_state->user_directories = NULL;
3727
3728         new_state->reference_dir =  open_dir( getcwd_buf, new_state );
3729
3730         if ( !new_state->reference_dir ) {
3731                 /* Directories changing from underneath us, grumble */
3732                 strcpy( getcwd_buf, "/" );
3733                 goto tryagain;
3734         }
3735
3736         return new_state;
3737 }
3738
3739 static void
3740 cmpl_free_dir_list( GList* dp0 ){
3741         GList *dp = dp0;
3742
3743         while ( dp ) {
3744                 free_dir( dp->data );
3745                 dp = dp->next;
3746         }
3747
3748         g_list_free( dp0 );
3749 }
3750
3751 static void
3752 cmpl_free_dir_sent_list( GList* dp0 ){
3753         GList *dp = dp0;
3754
3755         while ( dp ) {
3756                 free_dir_sent( dp->data );
3757                 dp = dp->next;
3758         }
3759
3760         g_list_free( dp0 );
3761 }
3762
3763 static void
3764 cmpl_free_state( CompletionState* cmpl_state ){
3765         cmpl_free_dir_list( cmpl_state->directory_storage );
3766         cmpl_free_dir_sent_list( cmpl_state->directory_sent_storage );
3767
3768         if ( cmpl_state->user_dir_name_buffer ) {
3769                 g_free( cmpl_state->user_dir_name_buffer );
3770         }
3771         if ( cmpl_state->user_directories ) {
3772                 g_free( cmpl_state->user_directories );
3773         }
3774         if ( cmpl_state->the_completion.text ) {
3775                 g_free( cmpl_state->the_completion.text );
3776         }
3777         if ( cmpl_state->updated_text ) {
3778                 g_free( cmpl_state->updated_text );
3779         }
3780
3781         g_free( cmpl_state );
3782 }
3783
3784 static void
3785 free_dir( CompletionDir* dir ){
3786         g_free( dir->fullname );
3787         g_free( dir );
3788 }
3789
3790 static void
3791 free_dir_sent( CompletionDirSent* sent ){
3792         g_free( sent->name_buffer );
3793         g_free( sent->entries );
3794         g_free( sent );
3795 }
3796
3797 static void
3798 prune_memory_usage( CompletionState *cmpl_state ){
3799         GList* cdsl = cmpl_state->directory_sent_storage;
3800         GList* cdl = cmpl_state->directory_storage;
3801         GList* cdl0 = cdl;
3802         gint len = 0;
3803
3804         for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1 )
3805                 cdsl = cdsl->next;
3806
3807         if ( cdsl ) {
3808                 cmpl_free_dir_sent_list( cdsl->next );
3809                 cdsl->next = NULL;
3810         }
3811
3812         cmpl_state->directory_storage = NULL;
3813         while ( cdl ) {
3814                 if ( cdl->data == cmpl_state->reference_dir ) {
3815                         cmpl_state->directory_storage = g_list_prepend( NULL, cdl->data );
3816                 }
3817                 else{
3818                         free_dir( cdl->data );
3819                 }
3820                 cdl = cdl->next;
3821         }
3822
3823         g_list_free( cdl0 );
3824 }
3825
3826 /**********************************************************************/
3827 /*                        The main entrances.                         */
3828 /**********************************************************************/
3829
3830 static PossibleCompletion*
3831 cmpl_completion_matches( gchar* text_to_complete,
3832                                                  gchar** remaining_text,
3833                                                  CompletionState* cmpl_state ){
3834         gchar* first_slash;
3835         PossibleCompletion *poss;
3836
3837         prune_memory_usage( cmpl_state );
3838
3839         g_assert( text_to_complete != NULL );
3840
3841         cmpl_state->user_completion_index = -1;
3842         cmpl_state->last_completion_text = text_to_complete;
3843         cmpl_state->the_completion.text[0] = 0;
3844         cmpl_state->last_valid_char = 0;
3845         cmpl_state->updated_text_len = -1;
3846         cmpl_state->updated_text[0] = 0;
3847         cmpl_state->re_complete = FALSE;
3848
3849         first_slash = strchr( text_to_complete, '/' );
3850
3851         if ( text_to_complete[0] == '~' && !first_slash ) {
3852                 /* Text starts with ~ and there is no slash, show all the
3853                  * home directory completions.
3854                  */
3855                 poss = attempt_homedir_completion( text_to_complete, cmpl_state );
3856
3857                 update_cmpl( poss, cmpl_state );
3858
3859                 return poss;
3860         }
3861
3862         cmpl_state->reference_dir =
3863                 open_ref_dir( text_to_complete, remaining_text, cmpl_state );
3864
3865         if ( !cmpl_state->reference_dir ) {
3866                 return NULL;
3867         }
3868
3869         cmpl_state->completion_dir =
3870                 find_completion_dir( *remaining_text, remaining_text, cmpl_state );
3871
3872         cmpl_state->last_valid_char = *remaining_text - text_to_complete;
3873
3874         if ( !cmpl_state->completion_dir ) {
3875                 return NULL;
3876         }
3877
3878         cmpl_state->completion_dir->cmpl_index = -1;
3879         cmpl_state->completion_dir->cmpl_parent = NULL;
3880         cmpl_state->completion_dir->cmpl_text = *remaining_text;
3881
3882         cmpl_state->active_completion_dir = cmpl_state->completion_dir;
3883
3884         cmpl_state->reference_dir = cmpl_state->completion_dir;
3885
3886         poss = attempt_file_completion( cmpl_state );
3887
3888         update_cmpl( poss, cmpl_state );
3889
3890         return poss;
3891 }
3892
3893 static PossibleCompletion*
3894 cmpl_next_completion( CompletionState* cmpl_state ){
3895         PossibleCompletion* poss = NULL;
3896
3897         cmpl_state->the_completion.text[0] = 0;
3898
3899         if ( cmpl_state->user_completion_index >= 0 ) {
3900                 poss = attempt_homedir_completion( cmpl_state->last_completion_text, cmpl_state );
3901         }
3902         else{
3903                 poss = attempt_file_completion( cmpl_state );
3904         }
3905
3906         update_cmpl( poss, cmpl_state );
3907
3908         return poss;
3909 }
3910
3911 /**********************************************************************/
3912 /*                       Directory Operations                         */
3913 /**********************************************************************/
3914
3915 /* Open the directory where completion will begin from, if possible. */
3916 static CompletionDir*
3917 open_ref_dir( gchar* text_to_complete,
3918                           gchar** remaining_text,
3919                           CompletionState* cmpl_state ){
3920         gchar* first_slash;
3921         CompletionDir *new_dir;
3922
3923         first_slash = strchr( text_to_complete, '/' );
3924
3925         if ( text_to_complete[0] == '~' ) {
3926                 new_dir = open_user_dir( text_to_complete, cmpl_state );
3927
3928                 if ( new_dir ) {
3929                         if ( first_slash ) {
3930                                 *remaining_text = first_slash + 1;
3931                         }
3932                         else{
3933                                 *remaining_text = text_to_complete + strlen( text_to_complete );
3934                         }
3935                 }
3936                 else
3937                 {
3938                         return NULL;
3939                 }
3940         }
3941         else if ( text_to_complete[0] == '/' || !cmpl_state->reference_dir ) {
3942                 gchar *tmp = g_strdup( text_to_complete );
3943                 gchar *p;
3944
3945                 p = tmp;
3946                 while ( *p && *p != '*' && *p != '?' )
3947                         p++;
3948
3949                 *p = '\0';
3950                 p = strrchr( tmp, '/' );
3951                 if ( p ) {
3952                         if ( p == tmp ) {
3953                                 p++;
3954                         }
3955
3956                         *p = '\0';
3957
3958                         new_dir = open_dir( tmp, cmpl_state );
3959
3960                         if ( new_dir ) {
3961                                 *remaining_text = text_to_complete +
3962                                                                   ( ( p == tmp + 1 ) ? ( p - tmp ) : ( p + 1 - tmp ) );
3963                         }
3964                 }
3965                 else
3966                 {
3967                         /* If no possible candidates, use the cwd */
3968                         gchar *curdir = g_get_current_dir();
3969
3970                         new_dir = open_dir( curdir, cmpl_state );
3971
3972                         if ( new_dir ) {
3973                                 *remaining_text = text_to_complete;
3974                         }
3975
3976                         g_free( curdir );
3977                 }
3978
3979                 g_free( tmp );
3980         }
3981         else
3982         {
3983                 *remaining_text = text_to_complete;
3984
3985                 new_dir = open_dir( cmpl_state->reference_dir->fullname, cmpl_state );
3986         }
3987
3988         if ( new_dir ) {
3989                 new_dir->cmpl_index = -1;
3990                 new_dir->cmpl_parent = NULL;
3991         }
3992
3993         return new_dir;
3994 }
3995
3996 /* open a directory by user name */
3997 static CompletionDir*
3998 open_user_dir( gchar* text_to_complete,
3999                            CompletionState *cmpl_state ){
4000         gchar *first_slash;
4001         gint cmp_len;
4002
4003         g_assert( text_to_complete && text_to_complete[0] == '~' );
4004
4005         first_slash = strchr( text_to_complete, '/' );
4006
4007         if ( first_slash ) {
4008                 cmp_len = first_slash - text_to_complete - 1;
4009         }
4010         else{
4011                 cmp_len = strlen( text_to_complete + 1 );
4012         }
4013
4014         if ( !cmp_len ) {
4015                 /* ~/ */
4016                 gchar *homedir = g_get_home_dir();
4017
4018                 if ( homedir ) {
4019                         return open_dir( homedir, cmpl_state );
4020                 }
4021                 else{
4022                         return NULL;
4023                 }
4024         }
4025         else
4026         {
4027                 /* ~user/ */
4028                 char* copy = g_new( char, cmp_len + 1 );
4029                 struct passwd *pwd;
4030                 strncpy( copy, text_to_complete + 1, cmp_len );
4031                 copy[cmp_len] = 0;
4032                 pwd = getpwnam( copy );
4033                 g_free( copy );
4034                 if ( !pwd ) {
4035                         cmpl_errno = errno;
4036                         return NULL;
4037                 }
4038
4039                 return open_dir( pwd->pw_dir, cmpl_state );
4040         }
4041 }
4042
4043 /* open a directory relative the the current relative directory */
4044 static CompletionDir*
4045 open_relative_dir( gchar* dir_name,
4046                                    CompletionDir* dir,
4047                                    CompletionState *cmpl_state ){
4048         gchar path_buf[2 * MAXPATHLEN];
4049
4050         if ( dir->fullname_len + strlen( dir_name ) + 2 >= MAXPATHLEN ) {
4051                 cmpl_errno = CMPL_ERRNO_TOO_LONG;
4052                 return NULL;
4053         }
4054
4055         strcpy( path_buf, dir->fullname );
4056
4057         if ( dir->fullname_len > 1 ) {
4058                 path_buf[dir->fullname_len] = '/';
4059                 strcpy( path_buf + dir->fullname_len + 1, dir_name );
4060         }
4061         else
4062         {
4063                 strcpy( path_buf + dir->fullname_len, dir_name );
4064         }
4065
4066         return open_dir( path_buf, cmpl_state );
4067 }
4068
4069 /* after the cache lookup fails, really open a new directory */
4070 static CompletionDirSent*
4071 open_new_dir( gchar* dir_name, struct stat* sbuf, gboolean stat_subdirs ){
4072         CompletionDirSent* sent;
4073         DIR* directory;
4074         gchar *buffer_ptr;
4075         struct dirent *dirent_ptr;
4076         gint buffer_size = 0;
4077         gint entry_count = 0;
4078         gint i;
4079         struct stat ent_sbuf;
4080         char path_buf[MAXPATHLEN * 2];
4081         gint path_buf_len;
4082
4083         sent = g_new( CompletionDirSent, 1 );
4084         sent->mtime = sbuf->st_mtime;
4085         sent->inode = sbuf->st_ino;
4086         sent->device = sbuf->st_dev;
4087
4088         path_buf_len = strlen( dir_name );
4089
4090         if ( path_buf_len > MAXPATHLEN ) {
4091                 cmpl_errno = CMPL_ERRNO_TOO_LONG;
4092                 return NULL;
4093         }
4094
4095         strcpy( path_buf, dir_name );
4096
4097         directory = opendir( dir_name );
4098
4099         if ( !directory ) {
4100                 cmpl_errno = errno;
4101                 return NULL;
4102         }
4103
4104         while ( ( dirent_ptr = readdir( directory ) ) != NULL )
4105         {
4106                 int entry_len = strlen( dirent_ptr->d_name );
4107                 buffer_size += entry_len + 1;
4108                 entry_count += 1;
4109
4110                 if ( path_buf_len + entry_len + 2 >= MAXPATHLEN ) {
4111                         cmpl_errno = CMPL_ERRNO_TOO_LONG;
4112                         closedir( directory );
4113                         return NULL;
4114                 }
4115         }
4116
4117         sent->name_buffer = g_new( gchar, buffer_size );
4118         sent->entries = g_new( CompletionDirEntry, entry_count );
4119         sent->entry_count = entry_count;
4120
4121         buffer_ptr = sent->name_buffer;
4122
4123         rewinddir( directory );
4124
4125         for ( i = 0; i < entry_count; i += 1 )
4126         {
4127                 dirent_ptr = readdir( directory );
4128
4129                 if ( !dirent_ptr ) {
4130                         cmpl_errno = errno;
4131                         closedir( directory );
4132                         return NULL;
4133                 }
4134
4135                 strcpy( buffer_ptr, dirent_ptr->d_name );
4136                 sent->entries[i].entry_name = buffer_ptr;
4137                 buffer_ptr += strlen( dirent_ptr->d_name );
4138                 *buffer_ptr = 0;
4139                 buffer_ptr += 1;
4140
4141                 path_buf[path_buf_len] = '/';
4142                 strcpy( path_buf + path_buf_len + 1, dirent_ptr->d_name );
4143
4144                 if ( stat_subdirs ) {
4145                         if ( stat( path_buf, &ent_sbuf ) >= 0 && S_ISDIR( ent_sbuf.st_mode ) ) {
4146                                 sent->entries[i].is_dir = 1;
4147                         }
4148                         else{
4149                                 /* stat may fail, and we don't mind, since it could be a
4150                                  * dangling symlink. */
4151                                 sent->entries[i].is_dir = 0;
4152                         }
4153                 }
4154                 else{
4155                         sent->entries[i].is_dir = 1;
4156                 }
4157         }
4158
4159         qsort( sent->entries, sent->entry_count, sizeof( CompletionDirEntry ), compare_cmpl_dir );
4160
4161         closedir( directory );
4162
4163         return sent;
4164 }
4165
4166 static gboolean
4167 check_dir( gchar *dir_name, struct stat *result, gboolean *stat_subdirs ){
4168         /* A list of directories that we know only contain other directories.
4169          * Trying to stat every file in these directories would be very
4170          * expensive.
4171          */
4172
4173         static struct {
4174                 gchar *name;
4175                 gboolean present;
4176                 struct stat statbuf;
4177         } no_stat_dirs[] = {
4178                 { "/afs", FALSE, { 0 } },
4179                 { "/net", FALSE, { 0 } }
4180         };
4181
4182         static const gint n_no_stat_dirs = sizeof( no_stat_dirs ) / sizeof( no_stat_dirs[0] );
4183         static gboolean initialized = FALSE;
4184
4185         gint i;
4186
4187         if ( !initialized ) {
4188                 initialized = TRUE;
4189                 for ( i = 0; i < n_no_stat_dirs; i++ )
4190                 {
4191                         if ( stat( no_stat_dirs[i].name, &no_stat_dirs[i].statbuf ) == 0 ) {
4192                                 no_stat_dirs[i].present = TRUE;
4193                         }
4194                 }
4195         }
4196
4197         if ( stat( dir_name, result ) < 0 ) {
4198                 cmpl_errno = errno;
4199                 return FALSE;
4200         }
4201
4202         *stat_subdirs = TRUE;
4203         for ( i = 0; i < n_no_stat_dirs; i++ )
4204         {
4205                 if ( no_stat_dirs[i].present &&
4206                          ( no_stat_dirs[i].statbuf.st_dev == result->st_dev ) &&
4207                          ( no_stat_dirs[i].statbuf.st_ino == result->st_ino ) ) {
4208                         *stat_subdirs = FALSE;
4209                         break;
4210                 }
4211         }
4212
4213         return TRUE;
4214 }
4215
4216 /* open a directory by absolute pathname */
4217 static CompletionDir*
4218 open_dir( gchar* dir_name, CompletionState* cmpl_state ){
4219         struct stat sbuf;
4220         gboolean stat_subdirs;
4221         CompletionDirSent *sent;
4222         GList* cdsl;
4223
4224         if ( !check_dir( dir_name, &sbuf, &stat_subdirs ) ) {
4225                 return NULL;
4226         }
4227
4228         cdsl = cmpl_state->directory_sent_storage;
4229
4230         while ( cdsl )
4231         {
4232                 sent = cdsl->data;
4233
4234                 if ( sent->inode == sbuf.st_ino &&
4235                          sent->mtime == sbuf.st_mtime &&
4236                          sent->device == sbuf.st_dev ) {
4237                         return attach_dir( sent, dir_name, cmpl_state );
4238                 }
4239
4240                 cdsl = cdsl->next;
4241         }
4242
4243         sent = open_new_dir( dir_name, &sbuf, stat_subdirs );
4244
4245         if ( sent ) {
4246                 cmpl_state->directory_sent_storage =
4247                         g_list_prepend( cmpl_state->directory_sent_storage, sent );
4248
4249                 return attach_dir( sent, dir_name, cmpl_state );
4250         }
4251
4252         return NULL;
4253 }
4254
4255 static CompletionDir*
4256 attach_dir( CompletionDirSent* sent, gchar* dir_name, CompletionState *cmpl_state ){
4257         CompletionDir* new_dir;
4258
4259         new_dir = g_new( CompletionDir, 1 );
4260
4261         cmpl_state->directory_storage =
4262                 g_list_prepend( cmpl_state->directory_storage, new_dir );
4263
4264         new_dir->sent = sent;
4265         new_dir->fullname = g_strdup( dir_name );
4266         new_dir->fullname_len = strlen( dir_name );
4267
4268         return new_dir;
4269 }
4270
4271 static gint
4272 correct_dir_fullname( CompletionDir* cmpl_dir ){
4273         gint length = strlen( cmpl_dir->fullname );
4274         struct stat sbuf;
4275
4276         if ( strcmp( cmpl_dir->fullname + length - 2, "/." ) == 0 ) {
4277                 if ( length == 2 ) {
4278                         strcpy( cmpl_dir->fullname, "/" );
4279                         cmpl_dir->fullname_len = 1;
4280                         return TRUE;
4281                 }
4282                 else {
4283                         cmpl_dir->fullname[length - 2] = 0;
4284                 }
4285         }
4286         else if ( strcmp( cmpl_dir->fullname + length - 3, "/./" ) == 0 ) {
4287                 cmpl_dir->fullname[length - 2] = 0;
4288         }
4289         else if ( strcmp( cmpl_dir->fullname + length - 3, "/.." ) == 0 ) {
4290                 if ( length == 3 ) {
4291                         strcpy( cmpl_dir->fullname, "/" );
4292                         cmpl_dir->fullname_len = 1;
4293                         return TRUE;
4294                 }
4295
4296                 if ( stat( cmpl_dir->fullname, &sbuf ) < 0 ) {
4297                         cmpl_errno = errno;
4298                         return FALSE;
4299                 }
4300
4301                 cmpl_dir->fullname[length - 2] = 0;
4302
4303                 if ( !correct_parent( cmpl_dir, &sbuf ) ) {
4304                         return FALSE;
4305                 }
4306         }
4307         else if ( strcmp( cmpl_dir->fullname + length - 4, "/../" ) == 0 ) {
4308                 if ( length == 4 ) {
4309                         strcpy( cmpl_dir->fullname, "/" );
4310                         cmpl_dir->fullname_len = 1;
4311                         return TRUE;
4312                 }
4313
4314                 if ( stat( cmpl_dir->fullname, &sbuf ) < 0 ) {
4315                         cmpl_errno = errno;
4316                         return FALSE;
4317                 }
4318
4319                 cmpl_dir->fullname[length - 3] = 0;
4320
4321                 if ( !correct_parent( cmpl_dir, &sbuf ) ) {
4322                         return FALSE;
4323                 }
4324         }
4325
4326         cmpl_dir->fullname_len = strlen( cmpl_dir->fullname );
4327
4328         return TRUE;
4329 }
4330
4331 static gint
4332 correct_parent( CompletionDir* cmpl_dir, struct stat *sbuf ){
4333         struct stat parbuf;
4334         gchar *last_slash;
4335         gchar *new_name;
4336         gchar c = 0;
4337
4338         last_slash = strrchr( cmpl_dir->fullname, '/' );
4339
4340         g_assert( last_slash );
4341
4342         if ( last_slash != cmpl_dir->fullname ) { /* last_slash[0] = 0; */
4343         }
4344         else
4345         {
4346                 c = last_slash[1];
4347                 last_slash[1] = 0;
4348         }
4349
4350         if ( stat( cmpl_dir->fullname, &parbuf ) < 0 ) {
4351                 cmpl_errno = errno;
4352                 return FALSE;
4353         }
4354
4355         if ( parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev ) {
4356                 /* it wasn't a link */
4357                 return TRUE;
4358         }
4359
4360         if ( c ) {
4361                 last_slash[1] = c;
4362         }
4363         /* else
4364            last_slash[0] = '/'; */
4365
4366         /* it was a link, have to figure it out the hard way */
4367
4368         new_name = find_parent_dir_fullname( cmpl_dir->fullname );
4369
4370         if ( !new_name ) {
4371                 return FALSE;
4372         }
4373
4374         g_free( cmpl_dir->fullname );
4375
4376         cmpl_dir->fullname = new_name;
4377
4378         return TRUE;
4379 }
4380
4381 static gchar*
4382 find_parent_dir_fullname( gchar* dirname ){
4383         gchar buffer[MAXPATHLEN];
4384         gchar buffer2[MAXPATHLEN];
4385
4386 #if defined( sun ) && !defined( __SVR4 )
4387         if ( !getwd( buffer ) )
4388 #else
4389         if ( !getcwd( buffer, MAXPATHLEN ) )
4390 #endif
4391         {
4392                 cmpl_errno = errno;
4393                 return NULL;
4394         }
4395
4396         if ( chdir( dirname ) != 0 || chdir( ".." ) != 0 ) {
4397                 cmpl_errno = errno;
4398                 return NULL;
4399         }
4400
4401 #if defined( sun ) && !defined( __SVR4 )
4402         if ( !getwd( buffer2 ) )
4403 #else
4404         if ( !getcwd( buffer2, MAXPATHLEN ) )
4405 #endif
4406         {
4407                 chdir( buffer );
4408                 cmpl_errno = errno;
4409
4410                 return NULL;
4411         }
4412
4413         if ( chdir( buffer ) != 0 ) {
4414                 cmpl_errno = errno;
4415                 return NULL;
4416         }
4417
4418         return g_strdup( buffer2 );
4419 }
4420
4421 /**********************************************************************/
4422 /*                        Completion Operations                       */
4423 /**********************************************************************/
4424
4425 static PossibleCompletion*
4426 attempt_homedir_completion( gchar* text_to_complete,
4427                                                         CompletionState *cmpl_state ){
4428         gint index, length;
4429
4430         if ( !cmpl_state->user_dir_name_buffer &&
4431                  !get_pwdb( cmpl_state ) ) {
4432                 return NULL;
4433         }
4434         length = strlen( text_to_complete ) - 1;
4435
4436         cmpl_state->user_completion_index += 1;
4437
4438         while ( cmpl_state->user_completion_index < cmpl_state->user_directories_len )
4439         {
4440                 index = first_diff_index( text_to_complete + 1,
4441                                                                   cmpl_state->user_directories
4442                                                                   [cmpl_state->user_completion_index].login );
4443
4444                 switch ( index )
4445                 {
4446                 case PATTERN_MATCH:
4447                         break;
4448                 default:
4449                         if ( cmpl_state->last_valid_char < ( index + 1 ) ) {
4450                                 cmpl_state->last_valid_char = index + 1;
4451                         }
4452                         cmpl_state->user_completion_index += 1;
4453                         continue;
4454                 }
4455
4456                 cmpl_state->the_completion.is_a_completion = 1;
4457                 cmpl_state->the_completion.is_directory = 1;
4458
4459                 append_completion_text( "~", cmpl_state );
4460
4461                 append_completion_text( cmpl_state->
4462                                                                 user_directories[cmpl_state->user_completion_index].login,
4463                                                                 cmpl_state );
4464
4465                 return append_completion_text( "/", cmpl_state );
4466         }
4467
4468         if ( text_to_complete[1] ||
4469                  cmpl_state->user_completion_index > cmpl_state->user_directories_len ) {
4470                 cmpl_state->user_completion_index = -1;
4471                 return NULL;
4472         }
4473         else
4474         {
4475                 cmpl_state->user_completion_index += 1;
4476                 cmpl_state->the_completion.is_a_completion = 1;
4477                 cmpl_state->the_completion.is_directory = 1;
4478
4479                 return append_completion_text( "~/", cmpl_state );
4480         }
4481 }
4482
4483 /* returns the index (>= 0) of the first differing character,
4484  * PATTERN_MATCH if the completion matches */
4485 static gint
4486 first_diff_index( gchar* pat, gchar* text ){
4487         gint diff = 0;
4488
4489         while ( *pat && *text && *text == *pat )
4490         {
4491                 pat += 1;
4492                 text += 1;
4493                 diff += 1;
4494         }
4495
4496         if ( *pat ) {
4497                 return diff;
4498         }
4499
4500         return PATTERN_MATCH;
4501 }
4502
4503 static PossibleCompletion*
4504 append_completion_text( gchar* text, CompletionState* cmpl_state ){
4505         gint len, i = 1;
4506
4507         if ( !cmpl_state->the_completion.text ) {
4508                 return NULL;
4509         }
4510
4511         len = strlen( text ) + strlen( cmpl_state->the_completion.text ) + 1;
4512
4513         if ( cmpl_state->the_completion.text_alloc > len ) {
4514                 strcat( cmpl_state->the_completion.text, text );
4515                 return &cmpl_state->the_completion;
4516         }
4517
4518         while ( i < len ) { i <<= 1; }
4519
4520         cmpl_state->the_completion.text_alloc = i;
4521
4522         cmpl_state->the_completion.text = (gchar*)g_realloc( cmpl_state->the_completion.text, i );
4523
4524         if ( !cmpl_state->the_completion.text ) {
4525                 return NULL;
4526         }
4527         else
4528         {
4529                 strcat( cmpl_state->the_completion.text, text );
4530                 return &cmpl_state->the_completion;
4531         }
4532 }
4533
4534 static CompletionDir*
4535 find_completion_dir( gchar* text_to_complete,
4536                                          gchar** remaining_text,
4537                                          CompletionState* cmpl_state ){
4538         gchar* first_slash = strchr( text_to_complete, '/' );
4539         CompletionDir* dir = cmpl_state->reference_dir;
4540         CompletionDir* next;
4541         *remaining_text = text_to_complete;
4542
4543         while ( first_slash )
4544         {
4545                 gint len = first_slash - *remaining_text;
4546                 gint found = 0;
4547                 gchar *found_name = NULL;       /* Quiet gcc */
4548                 gint i;
4549                 gchar* pat_buf = g_new( gchar, len + 1 );
4550
4551                 strncpy( pat_buf, *remaining_text, len );
4552                 pat_buf[len] = 0;
4553
4554                 for ( i = 0; i < dir->sent->entry_count; i += 1 )
4555                 {
4556                         if ( dir->sent->entries[i].is_dir &&
4557                                  fnmatch( pat_buf, dir->sent->entries[i].entry_name,
4558                                                   FNMATCH_FLAGS ) != FNM_NOMATCH ) {
4559                                 if ( found ) {
4560                                         g_free( pat_buf );
4561                                         return dir;
4562                                 }
4563                                 else
4564                                 {
4565                                         found = 1;
4566                                         found_name = dir->sent->entries[i].entry_name;
4567                                 }
4568                         }
4569                 }
4570
4571                 if ( !found ) {
4572                         /* Perhaps we are trying to open an automount directory */
4573                         found_name = pat_buf;
4574                 }
4575
4576                 next = open_relative_dir( found_name, dir, cmpl_state );
4577
4578                 if ( !next ) {
4579                         g_free( pat_buf );
4580                         return NULL;
4581                 }
4582
4583                 next->cmpl_parent = dir;
4584
4585                 dir = next;
4586
4587                 if ( !correct_dir_fullname( dir ) ) {
4588                         g_free( pat_buf );
4589                         return NULL;
4590                 }
4591
4592                 *remaining_text = first_slash + 1;
4593                 first_slash = strchr( *remaining_text, '/' );
4594
4595                 g_free( pat_buf );
4596         }
4597
4598         return dir;
4599 }
4600
4601 static void
4602 update_cmpl( PossibleCompletion* poss, CompletionState* cmpl_state ){
4603         gint cmpl_len;
4604
4605         if ( !poss || !cmpl_is_a_completion( poss ) ) {
4606                 return;
4607         }
4608
4609         cmpl_len = strlen( cmpl_this_completion( poss ) );
4610
4611         if ( cmpl_state->updated_text_alloc < cmpl_len + 1 ) {
4612                 cmpl_state->updated_text =
4613                         (gchar*)g_realloc( cmpl_state->updated_text,
4614                                                            cmpl_state->updated_text_alloc );
4615                 cmpl_state->updated_text_alloc = 2 * cmpl_len;
4616         }
4617
4618         if ( cmpl_state->updated_text_len < 0 ) {
4619                 strcpy( cmpl_state->updated_text, cmpl_this_completion( poss ) );
4620                 cmpl_state->updated_text_len = cmpl_len;
4621                 cmpl_state->re_complete = cmpl_is_directory( poss );
4622         }
4623         else if ( cmpl_state->updated_text_len == 0 ) {
4624                 cmpl_state->re_complete = FALSE;
4625         }
4626         else
4627         {
4628                 gint first_diff =
4629                         first_diff_index( cmpl_state->updated_text,
4630                                                           cmpl_this_completion( poss ) );
4631
4632                 cmpl_state->re_complete = FALSE;
4633
4634                 if ( first_diff == PATTERN_MATCH ) {
4635                         return;
4636                 }
4637
4638                 if ( first_diff > cmpl_state->updated_text_len ) {
4639                         strcpy( cmpl_state->updated_text, cmpl_this_completion( poss ) );
4640                 }
4641
4642                 cmpl_state->updated_text_len = first_diff;
4643                 cmpl_state->updated_text[first_diff] = 0;
4644         }
4645 }
4646
4647 static PossibleCompletion*
4648 attempt_file_completion( CompletionState *cmpl_state ){
4649         gchar *pat_buf, *first_slash;
4650         CompletionDir *dir = cmpl_state->active_completion_dir;
4651
4652         dir->cmpl_index += 1;
4653
4654         if ( dir->cmpl_index == dir->sent->entry_count ) {
4655                 if ( dir->cmpl_parent == NULL ) {
4656                         cmpl_state->active_completion_dir = NULL;
4657
4658                         return NULL;
4659                 }
4660                 else
4661                 {
4662                         cmpl_state->active_completion_dir = dir->cmpl_parent;
4663
4664                         return attempt_file_completion( cmpl_state );
4665                 }
4666         }
4667
4668         g_assert( dir->cmpl_text );
4669
4670         first_slash = strchr( dir->cmpl_text, '/' );
4671
4672         if ( first_slash ) {
4673                 gint len = first_slash - dir->cmpl_text;
4674
4675                 pat_buf = g_new( gchar, len + 1 );
4676                 strncpy( pat_buf, dir->cmpl_text, len );
4677                 pat_buf[len] = 0;
4678         }
4679         else
4680         {
4681                 gint len = strlen( dir->cmpl_text );
4682
4683                 pat_buf = g_new( gchar, len + 2 );
4684                 strcpy( pat_buf, dir->cmpl_text );
4685                 strcpy( pat_buf + len, "*" );
4686         }
4687
4688         if ( first_slash ) {
4689                 if ( dir->sent->entries[dir->cmpl_index].is_dir ) {
4690                         if ( fnmatch( pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
4691                                                   FNMATCH_FLAGS ) != FNM_NOMATCH ) {
4692                                 CompletionDir* new_dir;
4693
4694                                 new_dir = open_relative_dir( dir->sent->entries[dir->cmpl_index].entry_name,
4695                                                                                          dir, cmpl_state );
4696
4697                                 if ( !new_dir ) {
4698                                         g_free( pat_buf );
4699                                         return NULL;
4700                                 }
4701
4702                                 new_dir->cmpl_parent = dir;
4703
4704                                 new_dir->cmpl_index = -1;
4705                                 new_dir->cmpl_text = first_slash + 1;
4706
4707                                 cmpl_state->active_completion_dir = new_dir;
4708
4709                                 g_free( pat_buf );
4710                                 return attempt_file_completion( cmpl_state );
4711                         }
4712                         else
4713                         {
4714                                 g_free( pat_buf );
4715                                 return attempt_file_completion( cmpl_state );
4716                         }
4717                 }
4718                 else
4719                 {
4720                         g_free( pat_buf );
4721                         return attempt_file_completion( cmpl_state );
4722                 }
4723         }
4724         else
4725         {
4726                 if ( dir->cmpl_parent != NULL ) {
4727                         append_completion_text( dir->fullname +
4728                                                                         strlen( cmpl_state->completion_dir->fullname ) + 1,
4729                                                                         cmpl_state );
4730                         append_completion_text( "/", cmpl_state );
4731                 }
4732
4733                 append_completion_text( dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state );
4734
4735                 cmpl_state->the_completion.is_a_completion =
4736                         ( fnmatch( pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
4737                                            FNMATCH_FLAGS ) != FNM_NOMATCH );
4738
4739                 cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
4740                 if ( dir->sent->entries[dir->cmpl_index].is_dir ) {
4741                         append_completion_text( "/", cmpl_state );
4742                 }
4743
4744                 g_free( pat_buf );
4745                 return &cmpl_state->the_completion;
4746         }
4747 }
4748
4749
4750 static gint
4751 get_pwdb( CompletionState* cmpl_state ){
4752         struct passwd *pwd_ptr;
4753         gchar* buf_ptr;
4754         gint len = 0, i, count = 0;
4755
4756         if ( cmpl_state->user_dir_name_buffer ) {
4757                 return TRUE;
4758         }
4759         setpwent();
4760
4761         while ( ( pwd_ptr = getpwent() ) != NULL )
4762         {
4763                 len += strlen( pwd_ptr->pw_name );
4764                 len += strlen( pwd_ptr->pw_dir );
4765                 len += 2;
4766                 count += 1;
4767         }
4768
4769         setpwent();
4770
4771         cmpl_state->user_dir_name_buffer = g_new( gchar, len );
4772         cmpl_state->user_directories = g_new( CompletionUserDir, count );
4773         cmpl_state->user_directories_len = count;
4774
4775         buf_ptr = cmpl_state->user_dir_name_buffer;
4776
4777         for ( i = 0; i < count; i += 1 )
4778         {
4779                 pwd_ptr = getpwent();
4780                 if ( !pwd_ptr ) {
4781                         cmpl_errno = errno;
4782                         goto error;
4783                 }
4784
4785                 strcpy( buf_ptr, pwd_ptr->pw_name );
4786                 cmpl_state->user_directories[i].login = buf_ptr;
4787                 buf_ptr += strlen( buf_ptr );
4788                 buf_ptr += 1;
4789                 strcpy( buf_ptr, pwd_ptr->pw_dir );
4790                 cmpl_state->user_directories[i].homedir = buf_ptr;
4791                 buf_ptr += strlen( buf_ptr );
4792                 buf_ptr += 1;
4793         }
4794
4795         qsort( cmpl_state->user_directories,
4796                    cmpl_state->user_directories_len,
4797                    sizeof( CompletionUserDir ),
4798                    compare_user_dir );
4799
4800         endpwent();
4801
4802         return TRUE;
4803
4804 error:
4805
4806         if ( cmpl_state->user_dir_name_buffer ) {
4807                 g_free( cmpl_state->user_dir_name_buffer );
4808         }
4809         if ( cmpl_state->user_directories ) {
4810                 g_free( cmpl_state->user_directories );
4811         }
4812
4813         cmpl_state->user_dir_name_buffer = NULL;
4814         cmpl_state->user_directories = NULL;
4815
4816         return FALSE;
4817 }
4818
4819 static gint
4820 compare_user_dir( const void* a, const void* b ){
4821         return strcmp( ( ( (CompletionUserDir*)a ) )->login,
4822                                    ( ( (CompletionUserDir*)b ) )->login );
4823 }
4824
4825 static gint
4826 compare_cmpl_dir( const void* a, const void* b ){
4827         return strcmp( ( ( (CompletionDirEntry*)a ) )->entry_name,
4828                                    ( ( (CompletionDirEntry*)b ) )->entry_name );
4829 }
4830
4831 static gint
4832 cmpl_state_okay( CompletionState* cmpl_state ){
4833         return cmpl_state && cmpl_state->reference_dir;
4834 }
4835
4836 static gchar*
4837 cmpl_strerror( gint err ){
4838         if ( err == CMPL_ERRNO_TOO_LONG ) {
4839                 return "Name too long";
4840         }
4841         else{
4842                 return g_strerror( err );
4843         }
4844 }
4845
4846 /* This is an internally used function to create pixmaps. */
4847 GtkWidget*
4848 create_pixmap( GtkWidget *widget, const gchar *pixmap_char ){
4849         GdkPixmap *gdkpixmap;
4850         GdkBitmap *mask;
4851         GtkWidget *pixmap;
4852         GdkColormap *colormap;
4853
4854         colormap = gtk_widget_get_colormap( widget );
4855
4856         gdkpixmap = gdk_pixmap_colormap_create_from_xpm_d( GTK_WIDGET( widget )->window,
4857                                                                                                            colormap,
4858                                                                                                            &mask,
4859                                                                                                            NULL,
4860                                                                                                            (gpointer) pixmap_char );
4861         if ( gdkpixmap == NULL ) {
4862                 g_warning( "Error loading pixmap: %s", pixmap_char );
4863                 return NULL;
4864         }
4865         pixmap = gtk_pixmap_new( gdkpixmap, mask );
4866         gdk_pixmap_unref( gdkpixmap );
4867         gdk_bitmap_unref( mask );
4868         return pixmap;
4869 }
4870
4871
4872 /* Testing area */
4873 #ifdef TORRIE_DEBUG
4874
4875 /* Get the selected filename and print it to the console */
4876 void file_ok_sel( GtkWidget        *w,
4877                                   GtkFileSelection *fs ){
4878         g_print( "%s\n", gtk_file_selection_get_filename( GTK_FILE_SELECTION( fs ) ) );
4879 }
4880
4881 void destroy( GtkWidget *widget,
4882                           gpointer data ){
4883         gtk_main_quit();
4884 }
4885
4886 int main( int argc,
4887                   char *argv[] ){
4888         GtkWidget *filew;
4889         const gchar *masks[] = { "mp3s/playlists <*.mp3,*.m3u>",
4890                                                          "src/hdr <*.[CcHh],*.[Cc][Cc],*.[Hh][Hh],*.cpp>",
4891                                                          NULL };
4892
4893         gtk_init( &argc, &argv );
4894
4895         /* Create a new file selection widget */
4896         filew = gtk_file_selection_new( "Spiffy File Selector" );
4897 //    gtk_file_selection_complete(GTK_FILE_SELECTION(filew),"bob");
4898
4899         gtk_file_selection_set_masks( GTK_FILE_SELECTION( filew ), masks );
4900
4901         gtk_signal_connect( GTK_OBJECT( filew ), "destroy",
4902                                                 (GtkSignalFunc) destroy, &filew );
4903         /* Connect the ok_button to file_ok_sel function */
4904         gtk_signal_connect( GTK_OBJECT( GTK_FILE_SELECTION( filew )->ok_button ),
4905                                                 "clicked", (GtkSignalFunc) file_ok_sel, filew );
4906
4907         /* Connect the cancel_button to destroy the widget */
4908         gtk_signal_connect_object( GTK_OBJECT( GTK_FILE_SELECTION
4909                                                                                            ( filew )->cancel_button ),
4910                                                            "clicked", (GtkSignalFunc) gtk_widget_destroy,
4911                                                            GTK_OBJECT( filew ) );
4912
4913
4914         gtk_widget_show( filew );
4915
4916 /*
4917     g_print("%d",gtk_file_selection_match_mask("mask.c","m*.c"));
4918     g_print("%d",gtk_file_selection_match_mask("mask.c","m???.c"));
4919         g_print("%d",gtk_file_selection_match_mask("mask.c","m??*.c"));
4920         g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c"));
4921         g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c???"));
4922         g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c*"));
4923         g_print("%d",gtk_file_selection_match_mask("mask.cout","n*.c???"));
4924         g_print("%d",gtk_file_selection_match_mask("mask.c","[mn]*"));
4925         g_print("%d",gtk_file_selection_match_mask("COPYING","*.xpm"));
4926  */
4927         gtk_main();
4928
4929         return 0;
4930 }
4931 /* example-end */
4932 #endif