apply misc fixes from Markus Fischer and Rambetter
[xonotic/netradiant.git] / radiant / gtkfilesel-darwin.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
27
28 // leo FIXME: if we keep this file then we'll need to ask permission to the author, this is LGPL
29 // This file is from the Advanced File Selector widget 
30 // by Michael Torrie  <torriem@byu.edu>
31 // http://students.cs.byu.edu/~torriem/gtk/
32
33 // common files win32/linux
34 #include <stdio.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <errno.h>
40
41 // TTimo
42 // NOTE: the mkdir stuff etc. is in <direct.h> .. but I don't know what's the best strategy yet.
43 // just including <direct.h> here doesn't cut it
44
45 #if defined (__linux__) || (__APPLE__)
46 #include <sys/param.h>
47 #include <dirent.h>
48 #include <unistd.h>
49 #include <pwd.h>
50 #include "fnmatch.h"
51 #endif
52
53 // leo: added "gtk/"
54 #include "gdk/gdkkeysyms.h"
55 #include "gtk/gtkbutton.h"
56 #include "gtk/gtkentry.h"
57 #include "gtkfilesel-darwin.h"
58 #include "gtk/gtkhbox.h"
59 #include "gtk/gtkhbbox.h"
60 #include "gtk/gtklabel.h"
61 #include "gtk/gtklist.h"
62 #include "gtk/gtklistitem.h"
63 #include "gtk/gtkmain.h"
64 #include "gtk/gtkscrolledwindow.h"
65 #include "gtk/gtksignal.h"
66 #include "gtk/gtkvbox.h"
67 #include "gtk/gtkmenu.h"
68 #include "gtk/gtkmenuitem.h"
69 #include "gtk/gtkoptionmenu.h"
70 #include "gtk/gtkclist.h"
71 #include "gtk/gtkdialog.h"
72 #include "gtk/gtkcombo.h"
73 #include "gtk/gtkframe.h"
74
75 // leo: disable NLS
76 //#include "gtk/gtkintl.h"
77 #define _(String) (String)
78
79 #define DIR_LIST_WIDTH   180
80 #define DIR_LIST_HEIGHT  180
81 #define FILE_LIST_WIDTH  180
82 #define FILE_LIST_HEIGHT 180
83
84 /* I've put this here so it doesn't get confused with the 
85  * file completion interface */
86 typedef struct _HistoryCallbackArg HistoryCallbackArg;
87
88 struct _HistoryCallbackArg
89 {
90   gchar *directory;
91   GtkWidget *menu_item;
92 };
93
94
95 typedef struct _CompletionState    CompletionState;
96 typedef struct _CompletionDir      CompletionDir;
97 typedef struct _CompletionDirSent  CompletionDirSent;
98 typedef struct _CompletionDirEntry CompletionDirEntry;
99 typedef struct _CompletionUserDir  CompletionUserDir;
100 typedef struct _PossibleCompletion PossibleCompletion;
101
102 /* Non-external file completion decls and structures */
103
104 /* A contant telling PRCS how many directories to cache.  Its actually
105  * kept in a list, so the geometry isn't important. */
106 #define CMPL_DIRECTORY_CACHE_SIZE 10
107
108 /* A constant used to determine whether a substring was an exact
109  * match by first_diff_index()
110  */
111 #define PATTERN_MATCH -1
112 /* The arguments used by all fnmatch() calls below
113  */
114 #define FNMATCH_FLAGS (FNM_PATHNAME | FNM_PERIOD)
115
116 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
117
118 /* This structure contains all the useful information about a directory
119  * for the purposes of filename completion.  These structures are cached
120  * in the CompletionState struct.  CompletionDir's are reference counted.
121  */
122 struct _CompletionDirSent
123 {
124   ino_t inode;
125   time_t mtime;
126   dev_t device;
127
128   gint entry_count;
129   gchar *name_buffer; /* memory segment containing names of all entries */
130
131   struct _CompletionDirEntry *entries;
132 };
133
134 struct _CompletionDir
135 {
136   CompletionDirSent *sent;
137
138   gchar *fullname;
139   gint fullname_len;
140
141   struct _CompletionDir *cmpl_parent;
142   gint cmpl_index;
143   gchar *cmpl_text;
144 };
145
146 /* This structure contains pairs of directory entry names with a flag saying
147  * whether or not they are a valid directory.  NOTE: This information is used
148  * to provide the caller with information about whether to update its completions
149  * or try to open a file.  Since directories are cached by the directory mtime,
150  * a symlink which points to an invalid file (which will not be a directory),
151  * will not be reevaluated if that file is created, unless the containing
152  * directory is touched.  I consider this case to be worth ignoring (josh).
153  */
154 struct _CompletionDirEntry
155 {
156   gint is_dir;
157   gchar *entry_name;
158 };
159
160 struct _CompletionUserDir
161 {
162   gchar *login;
163   gchar *homedir;
164 };
165
166 struct _PossibleCompletion
167 {
168   /* accessible fields, all are accessed externally by functions
169    * declared above
170    */
171   gchar *text;
172   gint is_a_completion;
173   gint is_directory;
174
175   gint file_size;
176   gint file_time;
177   gint uid;
178   gint gid;
179   /* Private fields
180    */
181   gint text_alloc;
182 };
183
184 struct _CompletionState
185 {
186   gint last_valid_char;
187   gchar *updated_text;
188   gint updated_text_len;
189   gint updated_text_alloc;
190   gint re_complete;
191
192   gchar *user_dir_name_buffer;
193   gint user_directories_len;
194
195   gchar *last_completion_text;
196
197   gint user_completion_index; /* if >= 0, currently completing ~user */
198
199   struct _CompletionDir *completion_dir; /* directory completing from */
200   struct _CompletionDir *active_completion_dir;
201
202   struct _PossibleCompletion the_completion;
203
204   struct _CompletionDir *reference_dir; /* initial directory */
205
206   GList* directory_storage;
207   GList* directory_sent_storage;
208
209   struct _CompletionUserDir *user_directories;
210 };
211
212
213 /* File completion functions which would be external, were they used
214  * outside of this file.
215  */
216
217 static CompletionState*    cmpl_init_state        (void);
218 static void                cmpl_free_state        (CompletionState *cmpl_state);
219 static gint                cmpl_state_okay        (CompletionState* cmpl_state);
220 static gchar*              cmpl_strerror          (gint);
221
222 static PossibleCompletion* cmpl_completion_matches(gchar           *text_to_complete,
223                                                    gchar          **remaining_text,
224                                                    CompletionState *cmpl_state);
225
226 /* Returns a name for consideration, possibly a completion, this name
227  * will be invalid after the next call to cmpl_next_completion.
228  */
229 static char*               cmpl_this_completion   (PossibleCompletion*);
230
231 /* True if this completion matches the given text.  Otherwise, this
232  * output can be used to have a list of non-completions.
233  */
234 static gint                cmpl_is_a_completion   (PossibleCompletion*);
235
236 /* True if the completion is a directory
237  */
238 static gint                cmpl_is_directory      (PossibleCompletion*);
239
240 /* Obtains the next completion, or NULL
241  */
242 static PossibleCompletion* cmpl_next_completion   (CompletionState*);
243
244 /* Updating completions: the return value of cmpl_updated_text() will
245  * be text_to_complete completed as much as possible after the most
246  * recent call to cmpl_completion_matches.  For the present
247  * application, this is the suggested replacement for the user's input
248  * string.  You must CALL THIS AFTER ALL cmpl_text_completions have
249  * been received.
250  */
251 static gchar*              cmpl_updated_text       (CompletionState* cmpl_state);
252
253 /* After updating, to see if the completion was a directory, call
254  * this.  If it was, you should consider re-calling completion_matches.
255  */
256 static gint                cmpl_updated_dir        (CompletionState* cmpl_state);
257
258 /* Current location: if using file completion, return the current
259  * directory, from which file completion begins.  More specifically,
260  * the cwd concatenated with all exact completions up to the last
261  * directory delimiter('/').
262  */
263 static gchar*              cmpl_reference_position (CompletionState* cmpl_state);
264
265 /* backing up: if cmpl_completion_matches returns NULL, you may query
266  * the index of the last completable character into cmpl_updated_text.
267  */
268 static gint                cmpl_last_valid_char    (CompletionState* cmpl_state);
269
270 /* When the user selects a non-directory, call cmpl_completion_fullname
271  * to get the full name of the selected file.
272  */
273 static gchar*              cmpl_completion_fullname (gchar*, CompletionState* cmpl_state);
274
275
276 /* Directory operations. */
277 static CompletionDir* open_ref_dir         (gchar* text_to_complete,
278                                             gchar** remaining_text,
279                                             CompletionState* cmpl_state);
280 static gboolean       check_dir            (gchar *dir_name, 
281                                             struct stat *result, 
282                                             gboolean *stat_subdirs);
283 static CompletionDir* open_dir             (gchar* dir_name,
284                                             CompletionState* cmpl_state);
285 static CompletionDir* open_user_dir        (gchar* text_to_complete,
286                                             CompletionState *cmpl_state);
287 static CompletionDir* open_relative_dir    (gchar* dir_name, CompletionDir* dir,
288                                             CompletionState *cmpl_state);
289 static CompletionDirSent* open_new_dir     (gchar* dir_name, 
290                                             struct stat* sbuf,
291                                             gboolean stat_subdirs);
292 static gint           correct_dir_fullname (CompletionDir* cmpl_dir);
293 static gint           correct_parent       (CompletionDir* cmpl_dir,
294                                             struct stat *sbuf);
295 static gchar*         find_parent_dir_fullname    (gchar* dirname);
296 static CompletionDir* attach_dir           (CompletionDirSent* sent,
297                                             gchar* dir_name,
298                                             CompletionState *cmpl_state);
299 static void           free_dir_sent (CompletionDirSent* sent);
300 static void           free_dir      (CompletionDir  *dir);
301 static void           prune_memory_usage(CompletionState *cmpl_state);
302
303 /* Completion operations */
304 static PossibleCompletion* attempt_homedir_completion(gchar* text_to_complete,
305                                                       CompletionState *cmpl_state);
306 static PossibleCompletion* attempt_file_completion(CompletionState *cmpl_state);
307 static CompletionDir* find_completion_dir(gchar* text_to_complete,
308                                           gchar** remaining_text,
309                                           CompletionState* cmpl_state);
310 static PossibleCompletion* append_completion_text(gchar* text,
311                                                   CompletionState* cmpl_state);
312 static gint get_pwdb(CompletionState* cmpl_state);
313 static gint first_diff_index(gchar* pat, gchar* text);
314 static gint compare_user_dir(const void* a, const void* b);
315 static gint compare_cmpl_dir(const void* a, const void* b);
316 static void update_cmpl(PossibleCompletion* poss,
317                         CompletionState* cmpl_state);
318
319 static void gtk_file_selection_class_init    (GtkFileSelectionClass *klass);
320 static void gtk_file_selection_init          (GtkFileSelection      *filesel);
321 static void gtk_file_selection_destroy       (GtkObject             *object);
322 static gint gtk_file_selection_key_press     (GtkWidget             *widget,
323                                               GdkEventKey           *event,
324                                               gpointer               user_data);
325
326 static void gtk_file_selection_file_button (GtkWidget *widget,
327                                             gint row, 
328                                             gint column, 
329                                             GdkEventButton *bevent,
330                                             gpointer user_data);
331
332 static void gtk_file_selection_dir_button (GtkWidget *widget,
333                                            gint row,
334                                            gint column,
335                                            GdkEventButton *bevent,
336                                            gpointer data);
337
338 static void gtk_file_selection_undir_button (GtkWidget *widget,
339                                              gint row,
340                                              gint column,
341                                              GdkEventButton *bevent,
342                                              gpointer data);
343
344 static void gtk_file_selection_populate      (GtkFileSelection      *fs,
345                                               gchar                 *rel_path,
346                                               gint                   try_complete);
347 static void gtk_file_selection_abort         (GtkFileSelection      *fs);
348
349 static void gtk_file_selection_update_history_menu (GtkFileSelection       *fs,
350                                                     gchar                  *current_dir);
351
352 static void gtk_file_selection_create_dir (GtkWidget *widget, gpointer data);
353 static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
354 static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
355
356 static gboolean gtk_file_selection_history_combo_callback (GtkWidget *widget, GdkEventKey *event, gpointer data);
357 static gboolean gtk_file_selection_history_combo_list_key_handler(GtkWidget *widget,
358                                                                                                                                                                                         GdkEventKey *event,
359                                                                                                                                                                                         gpointer user_data);
360 static gboolean gtk_file_selection_history_combo_list_callback (GtkWidget *thelist,
361                                                                                                                                                                                         GdkEventButton *event,
362                                                                                                                                                                                         gpointer user_data);
363 static void gtk_file_selection_mask_entry_callback (GtkWidget *widget, gpointer data);
364 static void gtk_file_selection_create_dir (GtkWidget *widget, gpointer data);
365 static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
366 static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
367 static void gtk_file_selection_home_button (GtkWidget *widget, gpointer data);
368 static void gtk_file_selection_up_button (GtkWidget *widget, gpointer data);
369 static void gtk_file_selection_prev_button (GtkWidget *widget, gpointer data);
370 static void gtk_file_selection_next_button (GtkWidget *widget, gpointer data);
371 static void gtk_file_selection_refresh_button (GtkWidget *widget, gpointer data);
372
373 static gint gtk_file_selection_match_char (gchar, gchar *mask);
374 static gint gtk_file_selection_match_mask (gchar *,gchar *);
375
376
377 static GtkWindowClass *parent_class = NULL;
378
379 /* Saves errno when something cmpl does fails. */
380 static gint cmpl_errno;
381
382
383 void gtk_file_selection_clear_masks (GtkFileSelection *filesel)
384 {
385   GList *list;
386
387   g_return_if_fail (filesel != NULL);
388   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
389
390   list = filesel->masks;
391   while (list)
392     {
393       g_free (list->data);
394       list = list->next;
395     }
396   filesel->masks = NULL;
397
398   gtk_list_clear_items (GTK_LIST (GTK_COMBO (filesel->mask_entry)->list), 0, -1);
399 }
400
401 void gtk_file_selection_set_masks (GtkFileSelection *filesel, const gchar **masks)
402 {
403   g_return_if_fail (filesel != NULL);
404   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
405
406   while (*masks)
407     {
408       filesel->masks = g_list_append (filesel->masks, (gpointer)*masks);
409       masks++;
410     }
411
412   if (filesel->masks)
413     gtk_combo_set_popdown_strings (GTK_COMBO (filesel->mask_entry), filesel->masks);
414 }
415
416
417 /* General notes:
418  * Make prev and next inactive if their respective *
419  *   histories are empty.
420  * Add facilities for handling hidden files and    *
421  * directories                                     *
422  * Add an api to access the mask, and hidden files *
423  * check box?  (prob not in 1.2.x series)          *
424  */
425
426 /* Routine for applying mask to filenames         *
427  *   Need to be optimized to minimize recursion   *
428  *     help the for loop by looking for the next  *
429  *     instance of the mask character following   *
430  *     the '*'.  ei *.c -- look for '.'           *
431  *     Also, swap all *? pairs (-> ?*), as that   *
432  *     will make it possible to look ahead (?     *
433  *     makes it very nondeterministic as in *?.c  *
434  *     which really is ?*.c                       *
435  *   Allow multiply masks, separted by commas     *
436  *   Allow more flexible [] handling (ie [a-zA-Z] *
437  *                                                *
438  */
439 static gint gtk_file_selection_match_char (gchar text, gchar *mask){
440   gchar *maskc;
441   gint x;
442   gint s;
443         
444   if (mask[0] == '[')
445     {
446       if (!strchr (mask,']')) return 0;
447       maskc = g_strdup(mask + 1); /* get the portion of mask inside []*/
448                 
449       (*(strchr (maskc,']'))) = 0;
450       s = strlen ((char *)maskc);
451
452       for (x = 0; x < s; x++){
453         if (text == maskc[x])
454           {
455             g_free (maskc);
456             return s + 2;
457           }
458       }
459       g_free (maskc);
460       return 0;
461     }
462
463   if (mask[0] == '?') return 1;
464   if (mask[0] == text) return 1;
465
466   return 0;
467 }
468
469
470 static gint gtk_file_selection_match_mask (gchar *text, gchar *mask){
471
472   int mc;
473   int tc;
474
475   tc = 0; mc = 0;
476         
477   if (mask[0] == 0 && text[0] == 0) return 1;
478         
479   if (mask[0] == '*')
480     {
481       for (tc = 0; tc <= strlen(text); tc++)
482         {
483           if (gtk_file_selection_match_mask (text + tc, mask + 1))
484             return 1;
485         }
486       return 0;
487     }
488   mc = gtk_file_selection_match_char (text[0], mask);
489
490   if(mc)
491     return gtk_file_selection_match_mask (text + 1, mask + mc);
492   else
493     return 0;
494 }
495
496 GtkType
497 gtk_file_selection_get_type (void)
498 {
499   static GtkType file_selection_type = 0;
500
501   if (!file_selection_type)
502     {
503       static const GtkTypeInfo filesel_info =
504       {
505         "GtkFileSelection",
506         sizeof (GtkFileSelection),
507         sizeof (GtkFileSelectionClass),
508         (GtkClassInitFunc) gtk_file_selection_class_init,
509         (GtkObjectInitFunc) gtk_file_selection_init,
510         /* reserved_1 */ NULL,
511         /* reserved_2 */ NULL,
512         (GtkClassInitFunc) NULL,
513       };
514
515       file_selection_type = gtk_type_unique (GTK_TYPE_WINDOW, &filesel_info);
516     }
517
518   return file_selection_type;
519 }
520
521 static void
522 gtk_file_selection_class_init (GtkFileSelectionClass *klass)    //tigital
523 {
524   GtkObjectClass *object_class;
525
526   object_class = (GtkObjectClass*) klass;
527
528   parent_class = gtk_type_class (GTK_TYPE_WINDOW);
529
530   object_class->destroy = gtk_file_selection_destroy;
531 }
532
533 static void
534 gtk_file_selection_init (GtkFileSelection *filesel)
535 {
536   GtkWidget *entry_vbox;
537   GtkWidget *label;
538   GtkWidget *list_hbox;
539   GtkWidget *confirm_area;
540   GtkWidget *vbox;
541   GtkWidget *hbox;
542   GtkWidget *pulldown_hbox;
543   GtkWidget *scrolled_win;
544   GtkWidget *mask_label;
545   GtkWidget *bigframe;
546   GtkWidget *label_lookingin;
547   GtkWidget *up_button;
548   GtkWidget *home_button;
549   GtkWidget *prev_button;
550   GtkWidget *next_button;
551   GtkWidget *refresh_button;
552
553   char *dir_title [2];
554   char *file_title [2];
555   
556   filesel->cmpl_state = cmpl_init_state ();
557
558   filesel->mask=NULL;
559   filesel->prev_history=NULL;
560   filesel->next_history=NULL;
561   filesel->saved_entry=NULL;
562
563   /* The dialog-sized vertical box  */
564   filesel->main_vbox = gtk_vbox_new (FALSE, 10);
565   gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
566   gtk_container_add (GTK_CONTAINER (filesel), filesel->main_vbox);
567   gtk_widget_show (filesel->main_vbox);
568
569   /* The horizontal box containing create, rename etc. buttons */
570   filesel->button_area = gtk_hbutton_box_new ();
571   gtk_button_box_set_layout(GTK_BUTTON_BOX(filesel->button_area), GTK_BUTTONBOX_START);
572   gtk_button_box_set_spacing(GTK_BUTTON_BOX(filesel->button_area), 0);
573   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area, 
574                       FALSE, FALSE, 0);
575   gtk_widget_show (filesel->button_area);
576   
577   gtk_file_selection_show_fileop_buttons(filesel);
578
579   /* hbox for pulldown menu */
580   pulldown_hbox = gtk_hbox_new (FALSE, 5);
581   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
582   gtk_widget_show (pulldown_hbox);
583   
584   /* The combo box that replaces the pulldown menu */
585   label_lookingin = gtk_label_new (_("Looking in:"));
586   gtk_widget_show (label_lookingin);
587   gtk_box_pack_start (GTK_BOX (pulldown_hbox), label_lookingin, FALSE, FALSE, 0);
588
589   filesel->history_combo = gtk_combo_new();
590   gtk_widget_show(filesel->history_combo);
591   gtk_combo_set_value_in_list(GTK_COMBO(filesel->history_combo),FALSE,FALSE);
592   gtk_box_pack_start (GTK_BOX(pulldown_hbox),filesel->history_combo,
593                                 TRUE,TRUE, 0);
594   gtk_signal_connect(GTK_OBJECT(((GtkCombo *)filesel->history_combo)->entry),"key-press-event",                             
595                      (GtkSignalFunc) gtk_file_selection_history_combo_callback,
596                      (gpointer) filesel);
597
598   gtk_signal_connect(GTK_OBJECT(((GtkCombo *)filesel->history_combo)->list),"button-press-event",
599                      (GtkSignalFunc) gtk_file_selection_history_combo_list_callback,
600                      (gpointer) filesel);
601
602   gtk_signal_connect(GTK_OBJECT(((GtkCombo *)filesel->history_combo)->list),"key-press-event",
603                      (GtkSignalFunc) gtk_file_selection_history_combo_list_key_handler,
604                      (gpointer) filesel);
605
606   /*  frame to put the following hbox in  */
607   bigframe = gtk_frame_new (NULL);
608   gtk_widget_show (bigframe);
609   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), bigframe, TRUE, TRUE, 0);
610
611   /*  The horizontal box containing the directory and file listboxes  */
612   list_hbox = gtk_hbox_new (FALSE, 5);
613   gtk_container_add (GTK_CONTAINER(bigframe), list_hbox);
614   gtk_container_set_border_width (GTK_CONTAINER (list_hbox), 5);
615   gtk_widget_show (list_hbox);
616
617   /* vbox to put the buttons and directory listing in  */
618   vbox = gtk_vbox_new (FALSE, 0);
619   gtk_widget_show (vbox);
620   gtk_box_pack_start (GTK_BOX (list_hbox), vbox, FALSE, FALSE, 0);
621
622   hbox = gtk_hbox_new (FALSE, 0);
623   gtk_widget_show (hbox);
624   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
625
626   home_button = gtk_button_new_with_label (_("Home"));
627   gtk_widget_show (home_button);
628   gtk_signal_connect (GTK_OBJECT (home_button), "clicked",
629                       (GtkSignalFunc) gtk_file_selection_home_button,
630                       (gpointer) filesel);
631   gtk_box_pack_start (GTK_BOX (hbox), home_button, TRUE,TRUE, 0);
632
633   prev_button = gtk_button_new_with_label (_("Prev"));
634   gtk_signal_connect (GTK_OBJECT (prev_button), "clicked",
635                       (GtkSignalFunc) gtk_file_selection_prev_button,
636                       (gpointer) filesel);
637   gtk_widget_show (prev_button);
638   gtk_box_pack_start (GTK_BOX (hbox), prev_button, TRUE,TRUE, 0);
639
640   up_button = gtk_button_new_with_label (_("Up"));
641   gtk_signal_connect (GTK_OBJECT (up_button), "clicked",
642                       (GtkSignalFunc) gtk_file_selection_up_button,
643                       (gpointer) filesel);
644   gtk_widget_show (up_button);
645   gtk_box_pack_start (GTK_BOX (hbox), up_button, TRUE,TRUE, 0);
646
647   next_button = gtk_button_new_with_label (_("Next"));
648   gtk_widget_show (next_button);
649   gtk_signal_connect (GTK_OBJECT (next_button), "clicked",
650                       (GtkSignalFunc) gtk_file_selection_next_button,
651                       (gpointer) filesel);
652   gtk_box_pack_start (GTK_BOX (hbox), next_button, TRUE,TRUE, 0);
653
654   refresh_button = gtk_button_new_with_label (_("Refresh"));
655   gtk_widget_show (refresh_button);
656   gtk_signal_connect (GTK_OBJECT (refresh_button), "clicked",
657                       (GtkSignalFunc) gtk_file_selection_refresh_button,
658                       (gpointer) filesel);
659   gtk_box_pack_start (GTK_BOX (hbox), refresh_button, TRUE, TRUE, 0);
660
661   /* The directories clist */
662   dir_title[0] = _("Directories");
663   dir_title[1] = NULL;
664   filesel->dir_list = gtk_clist_new_with_titles (1, (gchar**) dir_title);
665   gtk_widget_set_usize (filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
666   gtk_signal_connect (GTK_OBJECT (filesel->dir_list), "select_row",
667                       (GtkSignalFunc) gtk_file_selection_dir_button,
668                       (gpointer) filesel);
669   gtk_signal_connect (GTK_OBJECT (filesel->dir_list), "unselect_row",
670                       (GtkSignalFunc) gtk_file_selection_undir_button,
671                       (gpointer) filesel);
672   gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list));
673
674   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
675   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
676   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
677                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
678   gtk_box_pack_start (GTK_BOX (vbox), scrolled_win, TRUE,TRUE, 5);
679   gtk_widget_show (filesel->dir_list);
680   gtk_widget_show (scrolled_win);
681
682   /* vbox area for mask entry and files clist  */
683   vbox = gtk_vbox_new (FALSE, 0);
684   gtk_widget_show (vbox);
685   gtk_box_pack_start (GTK_BOX (list_hbox), vbox, TRUE, TRUE, 0);
686
687   hbox = gtk_hbox_new (FALSE, 5);
688   gtk_widget_show (hbox);
689   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
690
691   mask_label = gtk_label_new (_("Mask:"));
692   gtk_widget_show (mask_label);
693   gtk_box_pack_start (GTK_BOX (hbox), mask_label, FALSE, FALSE, 0);
694
695   filesel->mask_entry = gtk_entry_new ();
696   gtk_widget_show (filesel->mask_entry);
697   gtk_signal_connect(GTK_OBJECT(filesel->mask_entry),"activate",
698                      (GtkSignalFunc) gtk_file_selection_mask_entry_callback,
699                      (gpointer) filesel);
700   gtk_box_pack_start (GTK_BOX (hbox),filesel->mask_entry, TRUE, TRUE, 0);
701
702
703   /* The files clist */
704   file_title[0] = _("Files");
705   file_title[1] = NULL;
706   filesel->file_list = gtk_clist_new_with_titles (1, (gchar**) file_title);
707   gtk_widget_set_usize (filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
708   gtk_signal_connect (GTK_OBJECT (filesel->file_list), "select_row",
709                       (GtkSignalFunc) gtk_file_selection_file_button, 
710                       (gpointer) filesel);
711   gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list));
712
713   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
714   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
715   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
716                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
717   gtk_box_pack_start (GTK_BOX (vbox), scrolled_win, TRUE, TRUE, 5);
718   gtk_widget_show (filesel->file_list);
719   gtk_widget_show (scrolled_win);
720
721   /* action area for packing buttons into. */
722   filesel->action_area = gtk_hbox_new (TRUE, 0);
723   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area, 
724                       FALSE, FALSE, 0);
725   gtk_widget_show (filesel->action_area);
726   
727   /*  The OK/Cancel button area */
728   confirm_area = gtk_hbutton_box_new ();
729   gtk_button_box_set_layout(GTK_BUTTON_BOX(confirm_area), GTK_BUTTONBOX_END);
730   gtk_button_box_set_spacing(GTK_BUTTON_BOX(confirm_area), 5);
731   gtk_box_pack_end (GTK_BOX (filesel->main_vbox), confirm_area, FALSE, FALSE, 0);
732   gtk_widget_show (confirm_area);
733
734   /*  The OK button  */
735   filesel->ok_button = gtk_button_new_with_label (_("OK"));
736   GTK_WIDGET_SET_FLAGS (filesel->ok_button, GTK_CAN_DEFAULT);
737   gtk_box_pack_start (GTK_BOX (confirm_area), filesel->ok_button, TRUE, TRUE, 0);
738   gtk_widget_grab_default (filesel->ok_button);
739   gtk_widget_show (filesel->ok_button);
740
741   /*  The Cancel button  */
742   filesel->cancel_button = gtk_button_new_with_label (_("Cancel"));
743   GTK_WIDGET_SET_FLAGS (filesel->cancel_button, GTK_CAN_DEFAULT);
744   gtk_box_pack_start (GTK_BOX (confirm_area), filesel->cancel_button, TRUE, TRUE, 0);
745   gtk_widget_show (filesel->cancel_button);
746
747   /*  The selection entry widget  */
748   entry_vbox = gtk_vbox_new (FALSE, 2);
749   gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 0);
750   gtk_widget_show (entry_vbox);
751
752   filesel->selection_text = label = gtk_label_new ("");
753   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
754   gtk_box_pack_start (GTK_BOX (entry_vbox), label, FALSE, FALSE, 0);
755   gtk_widget_show (label);
756
757   filesel->selection_entry = gtk_entry_new ();
758   gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
759                       (GtkSignalFunc) gtk_file_selection_key_press, filesel);
760   gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
761                              (GtkSignalFunc) gtk_widget_grab_default,
762                              GTK_OBJECT (filesel->ok_button));
763   gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate",
764                              (GtkSignalFunc) gtk_button_clicked,
765                              GTK_OBJECT (filesel->ok_button));
766   gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
767   gtk_widget_show (filesel->selection_entry);
768
769   if (!cmpl_state_okay (filesel->cmpl_state))
770     {
771       gchar err_buf[256];
772
773       sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
774
775       gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
776     }
777   else
778     {
779       gtk_file_selection_populate (filesel, "", FALSE);
780     }
781
782   gtk_widget_grab_focus (filesel->selection_entry);
783 }
784
785 GtkWidget*
786 gtk_file_selection_new (const gchar *title)
787 {
788   GtkFileSelection *filesel;
789
790   filesel = gtk_type_new (GTK_TYPE_FILE_SELECTION);
791   gtk_window_set_title (GTK_WINDOW (filesel), title);
792
793   return GTK_WIDGET (filesel);
794 }
795
796 void
797 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
798 {
799   g_return_if_fail (filesel != NULL);
800   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
801     
802   /* delete, create directory, and rename */
803   if (!filesel->fileop_c_dir) 
804     {
805       filesel->fileop_c_dir = gtk_button_new_with_label (_("Create Dir"));
806       gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked",
807                           (GtkSignalFunc) gtk_file_selection_create_dir, 
808                           (gpointer) filesel);
809       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
810                           filesel->fileop_c_dir, TRUE, TRUE, 0);
811       gtk_widget_show (filesel->fileop_c_dir);
812     }
813         
814   if (!filesel->fileop_del_file) 
815     {
816       filesel->fileop_del_file = gtk_button_new_with_label (_("Delete File"));
817       gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked",
818                           (GtkSignalFunc) gtk_file_selection_delete_file, 
819                           (gpointer) filesel);
820       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
821                           filesel->fileop_del_file, TRUE, TRUE, 0);
822       gtk_widget_show (filesel->fileop_del_file);
823     }
824
825   if (!filesel->fileop_ren_file)
826     {
827       filesel->fileop_ren_file = gtk_button_new_with_label (_("Rename File"));
828       gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked",
829                           (GtkSignalFunc) gtk_file_selection_rename_file, 
830                           (gpointer) filesel);
831       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
832                           filesel->fileop_ren_file, TRUE, TRUE, 0);
833       gtk_widget_show (filesel->fileop_ren_file);
834     }
835
836   gtk_widget_queue_resize(GTK_WIDGET(filesel));
837 }
838
839 void       
840 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
841 {
842   g_return_if_fail (filesel != NULL);
843   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
844     
845   if (filesel->fileop_ren_file) 
846     {
847       gtk_widget_destroy (filesel->fileop_ren_file);
848       filesel->fileop_ren_file = NULL;
849     }
850
851   if (filesel->fileop_del_file)
852     {
853       gtk_widget_destroy (filesel->fileop_del_file);
854       filesel->fileop_del_file = NULL;
855     }
856
857   if (filesel->fileop_c_dir)
858     {
859       gtk_widget_destroy (filesel->fileop_c_dir);
860       filesel->fileop_c_dir = NULL;
861     }
862 }
863
864
865
866 void
867 gtk_file_selection_set_filename (GtkFileSelection *filesel,
868                                  const gchar      *filename)
869 {
870   char  buf[MAXPATHLEN];
871   const char *name, *last_slash;
872
873   g_return_if_fail (filesel != NULL);
874   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
875   g_return_if_fail (filename != NULL);
876
877   last_slash = strrchr (filename, '/');
878
879   if (!last_slash)
880     {
881       buf[0] = 0;
882       name = filename;
883     }
884   else
885     {
886       gint len = MIN (MAXPATHLEN - 1, last_slash - filename + 1);
887
888       strncpy (buf, filename, len);
889       buf[len] = 0;
890
891       name = last_slash + 1;
892     }
893
894   gtk_file_selection_populate (filesel, buf, FALSE);
895
896   if (filesel->selection_entry)
897     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
898 }
899
900 gchar*
901 gtk_file_selection_get_filename (GtkFileSelection *filesel)
902 {
903   static char nothing[2] = "";
904   char *text;
905   char *filename;
906
907   g_return_val_if_fail (filesel != NULL, nothing);
908   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
909
910   text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
911   if (text)
912     {
913       filename = cmpl_completion_fullname (text, filesel->cmpl_state);
914       return filename;
915     }
916
917   return nothing;
918 }
919
920 void
921 gtk_file_selection_complete (GtkFileSelection *filesel,
922                              const gchar      *pattern)
923 {
924   gchar *new_pattern;
925   gint x;
926         
927   g_return_if_fail (filesel != NULL);
928   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
929   g_return_if_fail (pattern != NULL);
930
931   if (filesel->selection_entry)
932     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
933         
934   if(strchr(pattern,'*') || strchr(pattern,'?'))
935     {
936       for(x=strlen(pattern);x>=0;x--)
937         {
938           if(pattern[x]=='/') break;
939         }
940       gtk_entry_set_text(GTK_ENTRY(filesel->mask_entry),g_strdup(pattern+x+1));
941       
942       if(filesel->mask) g_free(filesel->mask);
943       
944       filesel->mask=g_strdup(pattern+x+1);
945       new_pattern=g_strdup(pattern);
946       new_pattern[x+1]=0;
947       gtk_file_selection_populate (filesel, (gchar*) new_pattern, TRUE);
948       g_free(new_pattern);
949     }
950   else
951     {
952       gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE);
953     }
954 }
955
956 static void
957 gtk_file_selection_destroy (GtkObject *object)
958 {
959   GtkFileSelection *filesel;
960   GList *list;
961
962   g_return_if_fail (object != NULL);
963   g_return_if_fail (GTK_IS_FILE_SELECTION (object));
964
965   filesel = GTK_FILE_SELECTION (object);
966   
967   if (filesel->fileop_dialog)
968     gtk_widget_destroy (filesel->fileop_dialog);
969   
970   if (filesel->next_history)
971     {
972       list = filesel->next_history;
973       while (list)
974         {
975           g_free (list->data);
976           list = list->next;
977         }
978     }
979   g_list_free (filesel->next_history);
980   filesel->next_history = NULL;
981
982   if (filesel->prev_history)
983     {
984       list = filesel->prev_history;
985       while (list)
986         {
987           g_free (list->data);
988           list = list->next;
989         }
990     }
991   g_list_free (filesel->prev_history);
992   filesel->prev_history = NULL;
993
994   if (filesel->mask)
995     {
996       g_free (filesel->mask);
997       filesel->mask = NULL;
998     }
999   
1000   cmpl_free_state (filesel->cmpl_state);
1001   filesel->cmpl_state = NULL;
1002
1003   if (GTK_OBJECT_CLASS (parent_class)->destroy)
1004     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
1005 }
1006
1007 /* Begin file operations callbacks */
1008
1009 static void
1010 gtk_file_selection_fileop_error (GtkFileSelection *fs, gchar *error_message)
1011 {
1012   GtkWidget *label;
1013   GtkWidget *vbox;
1014   GtkWidget *button;
1015   GtkWidget *dialog;
1016   
1017   g_return_if_fail (error_message != NULL);
1018   
1019   /* main dialog */
1020   dialog = gtk_dialog_new ();
1021   /*
1022   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1023                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
1024                       (gpointer) fs);
1025   */
1026   gtk_window_set_title (GTK_WINDOW (dialog), _("Error"));
1027   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1028   
1029   /* If file dialog is grabbed, make this dialog modal too */
1030   /* When error dialog is closed, file dialog will be grabbed again */
1031   if (GTK_WINDOW(fs)->modal)
1032       gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1033
1034   vbox = gtk_vbox_new(FALSE, 0);
1035   gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
1036   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
1037                      FALSE, FALSE, 0);
1038   gtk_widget_show(vbox);
1039
1040   label = gtk_label_new(error_message);
1041   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
1042   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
1043   gtk_widget_show(label);
1044
1045   /* yes, we free it */
1046   g_free (error_message);
1047   
1048   /* close button */
1049   button = gtk_button_new_with_label (_("Close"));
1050   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1051                              (GtkSignalFunc) gtk_widget_destroy, 
1052                              (gpointer) dialog);
1053   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1054                      button, TRUE, TRUE, 0);
1055   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1056   gtk_widget_grab_default(button);
1057   gtk_widget_show (button);
1058
1059   gtk_widget_show (dialog);
1060 }
1061
1062 static void
1063 gtk_file_selection_fileop_destroy (GtkWidget *widget, gpointer data)
1064 {
1065   GtkFileSelection *fs = data;
1066
1067   g_return_if_fail (fs != NULL);
1068   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1069   
1070   fs->fileop_dialog = NULL;
1071 }
1072
1073
1074 static void
1075 gtk_file_selection_create_dir_confirmed (GtkWidget *widget, gpointer data)
1076 {
1077   GtkFileSelection *fs = data;
1078   gchar *dirname;
1079   gchar *path;
1080   gchar *full_path;
1081   gchar *buf;
1082   CompletionState *cmpl_state;
1083   
1084   g_return_if_fail (fs != NULL);
1085   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1086
1087   dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1088   cmpl_state = (CompletionState*) fs->cmpl_state;
1089   path = cmpl_reference_position (cmpl_state);
1090   
1091   full_path = g_strconcat (path, "/", dirname, NULL);
1092   if ( (mkdir (full_path, 0755) < 0) ) 
1093     {
1094       buf = g_strconcat ("Error creating directory \"", dirname, "\":  ", 
1095                          g_strerror(errno), NULL);
1096       gtk_file_selection_fileop_error (fs, buf);
1097     }
1098   g_free (full_path);
1099   
1100   gtk_widget_destroy (fs->fileop_dialog);
1101   gtk_file_selection_populate (fs, "", FALSE);
1102 }
1103   
1104 static void
1105 gtk_file_selection_create_dir (GtkWidget *widget, gpointer data)
1106 {
1107   GtkFileSelection *fs = data;
1108   GtkWidget *label;
1109   GtkWidget *dialog;
1110   GtkWidget *vbox;
1111   GtkWidget *button;
1112
1113   g_return_if_fail (fs != NULL);
1114   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1115
1116   if (fs->fileop_dialog)
1117           return;
1118   
1119   /* main dialog */
1120   fs->fileop_dialog = dialog = gtk_dialog_new ();
1121   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1122                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
1123                       (gpointer) fs);
1124   gtk_window_set_title (GTK_WINDOW (dialog), _("Create Directory"));
1125   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1126
1127   /* If file dialog is grabbed, grab option dialog */
1128   /* When option dialog is closed, file dialog will be grabbed again */
1129   if (GTK_WINDOW(fs)->modal)
1130       gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1131
1132   vbox = gtk_vbox_new(FALSE, 0);
1133   gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
1134   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
1135                      FALSE, FALSE, 0);
1136   gtk_widget_show(vbox);
1137   
1138   label = gtk_label_new(_("Directory name:"));
1139   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
1140   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
1141   gtk_widget_show(label);
1142
1143   /*  The directory entry widget  */
1144   fs->fileop_entry = gtk_entry_new ();
1145   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
1146                       TRUE, TRUE, 5);
1147   GTK_WIDGET_SET_FLAGS(fs->fileop_entry, GTK_CAN_DEFAULT);
1148   gtk_widget_show (fs->fileop_entry);
1149   
1150   /* buttons */
1151   button = gtk_button_new_with_label (_("Create"));
1152   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1153                       (GtkSignalFunc) gtk_file_selection_create_dir_confirmed, 
1154                       (gpointer) fs);
1155   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1156                      button, TRUE, TRUE, 0);
1157   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1158   gtk_widget_show(button);
1159   
1160   button = gtk_button_new_with_label (_("Cancel"));
1161   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1162                              (GtkSignalFunc) gtk_widget_destroy, 
1163                              (gpointer) dialog);
1164   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1165                      button, TRUE, TRUE, 0);
1166   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1167   gtk_widget_grab_default(button);
1168   gtk_widget_show (button);
1169
1170   gtk_widget_show (dialog);
1171 }
1172
1173 static void
1174 gtk_file_selection_delete_file_confirmed (GtkWidget *widget, gpointer data)
1175 {
1176   GtkFileSelection *fs = data;
1177   CompletionState *cmpl_state;
1178   gchar *path;
1179   gchar *full_path;
1180   gchar *buf;
1181   
1182   g_return_if_fail (fs != NULL);
1183   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1184
1185   cmpl_state = (CompletionState*) fs->cmpl_state;
1186   path = cmpl_reference_position (cmpl_state);
1187   
1188   full_path = g_strconcat (path, "/", fs->fileop_file, NULL);
1189   if ( (unlink (full_path) < 0) ) 
1190     {
1191       buf = g_strconcat ("Error deleting file \"", fs->fileop_file, "\":  ", 
1192                          g_strerror(errno), NULL);
1193       gtk_file_selection_fileop_error (fs, buf);
1194     }
1195   g_free (full_path);
1196   
1197   gtk_widget_destroy (fs->fileop_dialog);
1198   gtk_file_selection_populate (fs, "", FALSE);
1199 }
1200
1201 static void
1202 gtk_file_selection_delete_file (GtkWidget *widget, gpointer data)
1203 {
1204   GtkFileSelection *fs = data;
1205   GtkWidget *label;
1206   GtkWidget *vbox;
1207   GtkWidget *button;
1208   GtkWidget *dialog;
1209   gchar *filename;
1210   gchar *buf;
1211   
1212   g_return_if_fail (fs != NULL);
1213   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1214
1215   if (fs->fileop_dialog)
1216           return;
1217
1218   filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1219   if (strlen(filename) < 1)
1220           return;
1221
1222   fs->fileop_file = filename;
1223   
1224   /* main dialog */
1225   fs->fileop_dialog = dialog = gtk_dialog_new ();
1226   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1227                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
1228                       (gpointer) fs);
1229   gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1230   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1231
1232   /* If file dialog is grabbed, grab option dialog */
1233   /* When option dialog is closed, file dialog will be grabbed again */
1234   if (GTK_WINDOW(fs)->modal)
1235       gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1236   
1237   vbox = gtk_vbox_new(FALSE, 0);
1238   gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
1239   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
1240                      FALSE, FALSE, 0);
1241   gtk_widget_show(vbox);
1242
1243   buf = g_strconcat ("Really delete file \"", filename, "\" ?", NULL);
1244   label = gtk_label_new(buf);
1245   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
1246   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
1247   gtk_widget_show(label);
1248   g_free(buf);
1249   
1250   /* buttons */
1251   button = gtk_button_new_with_label (_("Delete"));
1252   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1253                       (GtkSignalFunc) gtk_file_selection_delete_file_confirmed, 
1254                       (gpointer) fs);
1255   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1256                      button, TRUE, TRUE, 0);
1257   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1258   gtk_widget_show(button);
1259   
1260   button = gtk_button_new_with_label (_("Cancel"));
1261   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1262                              (GtkSignalFunc) gtk_widget_destroy, 
1263                              (gpointer) dialog);
1264   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1265                      button, TRUE, TRUE, 0);
1266   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1267   gtk_widget_grab_default(button);
1268   gtk_widget_show (button);
1269
1270   gtk_widget_show (dialog);
1271
1272 }
1273
1274 static void
1275 gtk_file_selection_rename_file_confirmed (GtkWidget *widget, gpointer data)
1276 {
1277   GtkFileSelection *fs = data;
1278   gchar *buf;
1279   gchar *file;
1280   gchar *path;
1281   gchar *new_filename;
1282   gchar *old_filename;
1283   CompletionState *cmpl_state;
1284   
1285   g_return_if_fail (fs != NULL);
1286   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1287
1288   file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1289   cmpl_state = (CompletionState*) fs->cmpl_state;
1290   path = cmpl_reference_position (cmpl_state);
1291   
1292   new_filename = g_strconcat (path, "/", file, NULL);
1293   old_filename = g_strconcat (path, "/", fs->fileop_file, NULL);
1294
1295   if ( (rename (old_filename, new_filename)) < 0) 
1296     {
1297       buf = g_strconcat ("Error renaming file \"", file, "\":  ", 
1298                          g_strerror(errno), NULL);
1299       gtk_file_selection_fileop_error (fs, buf);
1300     }
1301   g_free (new_filename);
1302   g_free (old_filename);
1303   
1304   gtk_widget_destroy (fs->fileop_dialog);
1305   gtk_file_selection_populate (fs, "", FALSE);
1306 }
1307   
1308 static void
1309 gtk_file_selection_rename_file (GtkWidget *widget, gpointer data)
1310 {
1311   GtkFileSelection *fs = data;
1312   GtkWidget *label;
1313   GtkWidget *dialog;
1314   GtkWidget *vbox;
1315   GtkWidget *button;
1316   gchar *buf;
1317   
1318   g_return_if_fail (fs != NULL);
1319   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1320
1321   if (fs->fileop_dialog)
1322           return;
1323
1324   fs->fileop_file = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1325   if (strlen(fs->fileop_file) < 1)
1326           return;
1327   
1328   /* main dialog */
1329   fs->fileop_dialog = dialog = gtk_dialog_new ();
1330   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1331                       (GtkSignalFunc) gtk_file_selection_fileop_destroy, 
1332                       (gpointer) fs);
1333   gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
1334   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1335
1336   /* If file dialog is grabbed, grab option dialog */
1337   /* When option dialog  closed, file dialog will be grabbed again */
1338   if (GTK_WINDOW(fs)->modal)
1339     gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1340   
1341   vbox = gtk_vbox_new(FALSE, 0);
1342   gtk_container_set_border_width (GTK_CONTAINER(vbox), 8);
1343   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
1344                      FALSE, FALSE, 0);
1345   gtk_widget_show(vbox);
1346   
1347   buf = g_strconcat ("Rename file \"", fs->fileop_file, "\" to:", NULL);
1348   label = gtk_label_new(buf);
1349   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
1350   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
1351   gtk_widget_show(label);
1352   g_free(buf);
1353
1354   /* New filename entry */
1355   fs->fileop_entry = gtk_entry_new ();
1356   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
1357                       TRUE, TRUE, 5);
1358   GTK_WIDGET_SET_FLAGS(fs->fileop_entry, GTK_CAN_DEFAULT);
1359   gtk_widget_show (fs->fileop_entry);
1360   
1361   gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
1362   gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
1363                               0, strlen (fs->fileop_file));
1364
1365   /* buttons */
1366   button = gtk_button_new_with_label (_("Rename"));
1367   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1368                       (GtkSignalFunc) gtk_file_selection_rename_file_confirmed, 
1369                       (gpointer) fs);
1370   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1371                      button, TRUE, TRUE, 0);
1372   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1373   gtk_widget_show(button);
1374   
1375   button = gtk_button_new_with_label (_("Cancel"));
1376   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1377                              (GtkSignalFunc) gtk_widget_destroy, 
1378                              (gpointer) dialog);
1379   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1380                      button, TRUE, TRUE, 0);
1381   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1382   gtk_widget_grab_default(button);
1383   gtk_widget_show (button);
1384
1385   gtk_widget_show (dialog);
1386 }
1387
1388
1389 static gint
1390 gtk_file_selection_key_press (GtkWidget   *widget,
1391                               GdkEventKey *event,
1392                               gpointer     user_data)
1393 {
1394   GtkFileSelection *fs;
1395   char *text;
1396
1397   g_return_val_if_fail (widget != NULL, FALSE);
1398   g_return_val_if_fail (event != NULL, FALSE);
1399
1400   fs = GTK_FILE_SELECTION (user_data);
1401
1402   if (event->keyval == GDK_Tab)
1403     {
1404       text = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1405
1406       text = g_strdup (text);
1407
1408       gtk_file_selection_populate (fs, text, TRUE);
1409
1410       g_free (text);
1411
1412       gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
1413
1414       return TRUE;
1415     }
1416   if (fs->saved_entry)
1417     {
1418       gtk_clist_unselect_all ((GtkCList *) (fs->dir_list));
1419       gtk_entry_set_text(GTK_ENTRY(fs->selection_entry),fs->saved_entry);
1420       g_free (fs->saved_entry);
1421       fs->saved_entry = NULL;
1422     }
1423
1424
1425   return FALSE;
1426 }
1427
1428 static void
1429 gtk_file_selection_home_button (GtkWidget *widget, gpointer data){
1430   GList *list;
1431         
1432   GtkFileSelection *fs=data;
1433
1434   list = fs->next_history;
1435   if (list)
1436     {
1437       g_free (list->data);
1438       list = list->next;
1439     }
1440   g_list_free (fs->next_history);
1441   fs->next_history = NULL;
1442                 
1443   gtk_file_selection_populate (fs,"~/",FALSE);
1444 }
1445
1446 static void
1447 gtk_file_selection_up_button (GtkWidget *widget, gpointer data){
1448   GtkFileSelection *fs = data;
1449   GList *list;
1450
1451   g_return_if_fail (fs != NULL);
1452   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1453
1454   list = fs->next_history;
1455   if (list)
1456     {
1457       g_free (list->data);
1458       list = list->next;
1459     }
1460   g_list_free (fs->next_history);
1461   fs->next_history = NULL;
1462
1463   gtk_file_selection_populate (fs, "../", FALSE); /*change directories. */
1464                 
1465 }
1466
1467 static void
1468 gtk_file_selection_prev_button (GtkWidget *widget, gpointer data){
1469   GtkFileSelection *fs = data;
1470   GList *list;
1471   GList *first;
1472   gchar *path;
1473
1474   g_return_if_fail (fs != NULL);
1475   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1476
1477   list = fs->prev_history;
1478
1479   if (list && g_list_length(list) > 1)
1480     {
1481       first = list;            /* get first element */
1482       list = list->next;       /* pop off current directory */
1483
1484       list->prev = NULL;       /* make this the new head. */
1485         
1486       fs->prev_history = list; /* update prev_history list */
1487       fs->next_history = g_list_prepend(fs->next_history,first->data); /* put it on next_history */
1488         
1489       first->next = NULL;      /* orphan the old first node */
1490       g_list_free (first);     /* free the node (data is now in use by next_history) */
1491
1492
1493         
1494       path = g_malloc(strlen(list->data)+4); /* plenty of space */
1495       strcpy(path,list->data);               /* get the 2nd path in the history */
1496       strcat(path,"/");                      /* append a '/' */
1497       gtk_file_selection_populate (fs, path, FALSE); /* change directories. */
1498       g_free (path);
1499     }
1500 }       
1501
1502 static void
1503 gtk_file_selection_next_button (GtkWidget *widget, gpointer data){
1504   GtkFileSelection *fs = data;
1505   GList *list;
1506   GList *first;
1507   gchar *path;
1508
1509   g_return_if_fail (fs != NULL);
1510   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1511
1512   list = fs->next_history;
1513
1514   if (list && g_list_length(list) > 0)
1515     {
1516       first = list;            /*get first element*/
1517       list = list->next;       /*pop off current directory*/
1518       
1519       if (list)
1520         list->prev = NULL;
1521       
1522       fs->next_history = list;                       /*update prev_history list*/
1523         
1524       path = g_malloc(strlen(first->data)+4);        /*plenty of space*/
1525       strcpy(path,first->data);
1526       strcat(path,"/");                              /*append a /   */
1527       gtk_file_selection_populate (fs, path, FALSE); /*change directories.*/
1528       g_free(path);
1529         
1530       first->next = NULL;     /* orphan the old first node */
1531       g_list_free (first);    /* free the node (data is now in use by next_history) */
1532       
1533     }
1534 }       
1535
1536 void static
1537 gtk_file_selection_refresh_button (GtkWidget *widget, gpointer data){
1538   GtkFileSelection *fs = data;
1539
1540   g_return_if_fail (fs != NULL);
1541   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1542
1543   gtk_file_selection_populate (fs,"",FALSE);
1544 }
1545
1546 static void
1547 gtk_file_selection_mask_entry_callback (GtkWidget *widget, gpointer data){
1548   GtkFileSelection *fs = data;
1549
1550   if(fs->mask)
1551     g_free (fs->mask);
1552                 
1553   fs->mask = g_strdup(gtk_entry_get_text (GTK_ENTRY(fs->mask_entry)));
1554         
1555   if (strlen(fs->mask) == 0)
1556     {
1557       g_free (fs->mask);
1558       fs->mask = NULL;
1559     }
1560         
1561   gtk_file_selection_refresh_button (widget,data);
1562 }
1563
1564 static gboolean gtk_file_selection_history_combo_list_key_handler(GtkWidget *widget,
1565                                                                   GdkEventKey *event,
1566                                                                   gpointer user_data)
1567 {
1568   /*
1569   g_print("Key pressed! \n");
1570   */
1571         
1572   return TRUE;
1573 }
1574
1575 static gboolean gtk_file_selection_history_combo_list_callback (GtkWidget *thelist,
1576                                                                 GdkEventButton *event,
1577                                                                 gpointer user_data)
1578 {
1579
1580   GtkFileSelection *fs = user_data;
1581   GList *list;
1582   gchar *path;
1583                 
1584   list = fs->next_history;
1585   if(list)
1586     {
1587       g_free (list->data);
1588       list = list->next;
1589     }
1590   g_list_free (fs->next_history);
1591   fs->next_history = NULL;
1592                         
1593   path = g_malloc(strlen(gtk_entry_get_text(GTK_ENTRY (((GtkCombo *)fs->history_combo)->entry)))+4);
1594   strcpy (path,gtk_entry_get_text(GTK_ENTRY( ((GtkCombo *)fs->history_combo)->entry)));
1595   strcat (path,"/");
1596         
1597   gtk_file_selection_populate (fs,path,TRUE);
1598         
1599   g_free (path);
1600
1601   return TRUE;
1602 }
1603
1604 static gboolean
1605 gtk_file_selection_history_combo_callback (GtkWidget *widget, GdkEventKey *event, gpointer data)
1606 {
1607   GtkEntry *entry=(GtkEntry *)widget;
1608   GtkFileSelection *fs=data;
1609   GList *list;
1610   gchar *path;
1611         
1612   g_return_val_if_fail (fs != NULL,FALSE);
1613   g_return_val_if_fail (GTK_IS_FILE_SELECTION (fs),FALSE);
1614         
1615
1616   if (event->keyval == GDK_Return)
1617     {
1618       list = fs->next_history;
1619       if (list)
1620         {
1621           g_free (list->data);
1622           list = list->next;
1623         }
1624       g_list_free (fs->next_history);
1625       fs->next_history = NULL;
1626       
1627       path = g_malloc(strlen(gtk_entry_get_text(entry))+4);
1628       strcpy (path,gtk_entry_get_text(entry));
1629       strcat (path,"/");
1630       gtk_file_selection_populate (fs,path,TRUE);
1631       g_free (path);
1632       gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
1633       return TRUE;
1634     }
1635   else
1636     {
1637       return FALSE;
1638     }
1639
1640 }
1641
1642 static void
1643 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1644                                         gchar *current_directory)
1645 {
1646   gchar *current_dir;
1647
1648   g_return_if_fail (fs != NULL);
1649   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1650   g_return_if_fail (current_directory != NULL);
1651   
1652   current_dir = g_strdup (current_directory);
1653
1654   if(fs->prev_history)
1655     {
1656       if (strcmp((fs->prev_history)->data,current_dir))
1657         { /*if this item isn't on the top of the list */
1658           fs->prev_history = g_list_prepend(fs->prev_history,g_strdup(current_dir));
1659         }
1660     } else {
1661       fs->prev_history = g_list_prepend(fs->prev_history,g_strdup(current_dir));
1662     }
1663   
1664   gtk_combo_set_popdown_strings (GTK_COMBO (fs->history_combo),fs->prev_history);
1665   
1666   g_free (current_dir);
1667 }
1668
1669 static void
1670 gtk_file_selection_file_button (GtkWidget *widget,
1671                                gint row, 
1672                                gint column, 
1673                                GdkEventButton *bevent,
1674                                gpointer user_data)
1675 {
1676   GtkFileSelection *fs = NULL;
1677   gchar *filename, *temp = NULL;
1678   
1679   g_return_if_fail (GTK_IS_CLIST (widget));
1680
1681   fs = user_data;
1682   g_return_if_fail (fs != NULL);
1683   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1684   
1685   gtk_clist_get_text (GTK_CLIST (fs->file_list), row, 0, &temp);
1686   filename = g_strdup (temp);
1687
1688   if (filename)
1689     {
1690       if (bevent)
1691         switch (bevent->type)
1692           {
1693           case GDK_2BUTTON_PRESS:
1694             gtk_button_clicked (GTK_BUTTON (fs->ok_button));
1695             break;
1696             
1697           default:
1698             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1699             break;
1700           }
1701       else
1702         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1703       
1704       g_free (filename);
1705     }
1706 }
1707
1708 static void
1709 gtk_file_selection_dir_button (GtkWidget *widget,
1710                                gint row, 
1711                                gint column, 
1712                                GdkEventButton *bevent,
1713                                gpointer user_data)
1714 {
1715   GList *list;
1716   GtkFileSelection *fs = NULL;
1717   gchar *filename, *temp = NULL;
1718
1719   g_return_if_fail (GTK_IS_CLIST (widget));
1720
1721   fs = GTK_FILE_SELECTION (user_data);
1722   g_return_if_fail (fs != NULL);
1723   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1724
1725   gtk_clist_get_text (GTK_CLIST (fs->dir_list), row, 0, &temp);
1726   filename = g_strdup (temp);
1727
1728   if (filename)
1729     {
1730       if (bevent)
1731         switch (bevent->type)
1732           {
1733           case GDK_2BUTTON_PRESS:
1734             list = fs->next_history;
1735             if (list)
1736               {
1737                 g_free (list->data);
1738                 list = list->next;
1739               }
1740             g_list_free (fs->next_history);
1741             fs->next_history = NULL;
1742         
1743             gtk_file_selection_populate (fs, filename, FALSE);
1744             gtk_entry_set_text(GTK_ENTRY(fs->selection_entry),fs->saved_entry);
1745             g_free (fs->saved_entry);
1746             fs->saved_entry = NULL;
1747             break;
1748             
1749           default:
1750             /* here we need to add the "filename" to the beginning of what's already
1751                in the entry.  Save what's in the entry, then restore it on the double click
1752             */
1753             if (fs->saved_entry) g_free (fs->saved_entry);
1754             fs->saved_entry=g_strdup(gtk_entry_get_text(GTK_ENTRY (fs->selection_entry)));
1755         
1756             temp=g_strconcat(filename,fs->saved_entry,NULL);
1757             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), temp);
1758             g_free (temp);
1759         
1760             break;
1761           }
1762       else
1763         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1764       
1765       g_free (filename);
1766     }
1767 }
1768
1769 static void
1770 gtk_file_selection_undir_button (GtkWidget *widget,
1771                                gint row,
1772                                gint column,
1773                                GdkEventButton *bevent,
1774                                gpointer user_data)
1775 {
1776   GtkFileSelection *fs = NULL;
1777   gchar *filename, *temp = NULL;
1778
1779   g_return_if_fail (GTK_IS_CLIST (widget));
1780
1781   fs = GTK_FILE_SELECTION (user_data);
1782   g_return_if_fail (fs != NULL);
1783   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1784
1785   gtk_clist_get_text (GTK_CLIST (fs->dir_list), row, 0, &temp);
1786   filename = g_strdup (temp);
1787
1788   if (filename)
1789     {
1790       if (bevent)
1791         switch (bevent->type)
1792           {
1793           default:
1794             /* here we need to add the "filename" to the beginning of what's already
1795                in the entry.  Save what's in the entry, then restore it on the double click
1796             */
1797             if (fs->saved_entry)
1798               {
1799                 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),fs->saved_entry);
1800                 g_free (fs->saved_entry);
1801                 fs->saved_entry = NULL;
1802               }
1803             break;
1804           }
1805       else
1806         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename); //?????
1807
1808       g_free (filename);
1809     }
1810 }
1811
1812 static void
1813 gtk_file_selection_populate (GtkFileSelection *fs,
1814                              gchar            *rel_path,
1815                              gint              try_complete)
1816 {
1817   CompletionState *cmpl_state;
1818   PossibleCompletion* poss;
1819   gchar* filename;
1820   gint row;
1821   gchar* rem_path = rel_path;
1822   gchar* sel_text;
1823   gchar* text[2];
1824   gint did_recurse = FALSE;
1825   gint possible_count = 0;
1826   gint selection_index = -1;
1827   gint file_list_width;
1828   gint dir_list_width;
1829   
1830   g_return_if_fail (fs != NULL);
1831   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1832   
1833   cmpl_state = (CompletionState*) fs->cmpl_state;
1834   poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
1835
1836   if (!cmpl_state_okay (cmpl_state))
1837     {
1838       /* Something went wrong. */
1839       gtk_file_selection_abort (fs);
1840       return;
1841     }
1842
1843   g_assert (cmpl_state->reference_dir);
1844
1845   gtk_clist_freeze (GTK_CLIST (fs->dir_list));
1846   gtk_clist_clear (GTK_CLIST (fs->dir_list));
1847   gtk_clist_freeze (GTK_CLIST (fs->file_list));
1848   gtk_clist_clear (GTK_CLIST (fs->file_list));
1849
1850   /* Set the dir_list to include ./ and ../ */
1851   text[1] = NULL;
1852   text[0] = "./";
1853   row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1854
1855   text[0] = "../";
1856   row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1857
1858   /*reset the max widths of the lists*/
1859   dir_list_width = gdk_string_width(fs->dir_list->style->font,"../");
1860   gtk_clist_set_column_width(GTK_CLIST(fs->dir_list),0,dir_list_width);
1861   file_list_width = 1;
1862   gtk_clist_set_column_width(GTK_CLIST(fs->file_list),0,file_list_width);
1863
1864   while (poss)
1865     {
1866       if (cmpl_is_a_completion (poss))
1867         {
1868           possible_count += 1;
1869           
1870           filename = cmpl_this_completion (poss);
1871
1872           text[0] = filename;
1873           
1874           if (cmpl_is_directory (poss))
1875             {
1876               if (strcmp (filename, "./") != 0 &&
1877                   strcmp (filename, "../") != 0)
1878                 {
1879                   int width = gdk_string_width(fs->dir_list->style->font,
1880                                                filename);
1881                   row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
1882                   if(width > dir_list_width)
1883                     {
1884                       dir_list_width = width;
1885                       gtk_clist_set_column_width(GTK_CLIST(fs->dir_list),0,
1886                                                  width);
1887                     }
1888                 }
1889             }
1890           else
1891             {
1892               if(fs->mask)
1893                 {
1894                   if (gtk_file_selection_match_mask(filename,fs->mask))
1895                     {
1896                       int width = gdk_string_width(fs->file_list->style->font,
1897                                                    filename);
1898                       row = gtk_clist_append (GTK_CLIST (fs->file_list), text);
1899                       if(width > file_list_width)
1900                         {
1901                           file_list_width = width;
1902                           gtk_clist_set_column_width(GTK_CLIST(fs->file_list),0,
1903                                                      width);
1904                         }
1905                     }
1906                 }
1907               else
1908                 {
1909                   int width = gdk_string_width(fs->file_list->style->font,
1910                                                filename);
1911                   row = gtk_clist_append (GTK_CLIST (fs->file_list), text);
1912                   if(width > file_list_width)
1913                     {
1914                       file_list_width = width;
1915                       gtk_clist_set_column_width(GTK_CLIST(fs->file_list),0,
1916                                                  width);
1917                     }
1918                 }
1919             }
1920         }
1921
1922       poss = cmpl_next_completion (cmpl_state);
1923     }
1924
1925   gtk_clist_thaw (GTK_CLIST (fs->dir_list));
1926   gtk_clist_thaw (GTK_CLIST (fs->file_list));
1927
1928   /* File lists are set. */
1929
1930   g_assert (cmpl_state->reference_dir);
1931
1932   if (try_complete)
1933     {
1934
1935       /* User is trying to complete filenames, so advance the user's input
1936        * string to the updated_text, which is the common leading substring
1937        * of all possible completions, and if its a directory attempt
1938        * attempt completions in it. */
1939
1940       if (cmpl_updated_text (cmpl_state)[0])
1941         {
1942
1943           if (cmpl_updated_dir (cmpl_state))
1944             {
1945               gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
1946
1947               did_recurse = TRUE;
1948
1949               gtk_file_selection_populate (fs, dir_name, TRUE);
1950
1951               g_free (dir_name);
1952             }
1953           else
1954             {
1955               if (fs->selection_entry)
1956                       gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
1957                                           cmpl_updated_text (cmpl_state));
1958             }
1959         }
1960       else
1961         {
1962           selection_index = cmpl_last_valid_char (cmpl_state) -
1963                             (strlen (rel_path) - strlen (rem_path));
1964           if (fs->selection_entry)
1965             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
1966         }
1967     }
1968   else
1969     {
1970       if (fs->selection_entry)
1971       /* Here we need to take the old filename and keep it!*/
1972         /*gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");*/
1973         ;
1974     }
1975
1976   if (!did_recurse)
1977     {
1978       if (fs->selection_entry)
1979         gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index);
1980
1981       if (fs->selection_entry)
1982         {
1983           sel_text = g_strconcat (_("Selection: "),
1984                                   cmpl_reference_position (cmpl_state),
1985                                   NULL);
1986
1987           gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text);
1988           g_free (sel_text);
1989         }
1990
1991   gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
1992
1993     }
1994 }
1995
1996 static void
1997 gtk_file_selection_abort (GtkFileSelection *fs)
1998 {
1999   gchar err_buf[256];
2000
2001   sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
2002
2003   /*  BEEP gdk_beep();  */
2004
2005   if (fs->selection_entry)
2006     gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
2007 }
2008
2009 /**********************************************************************/
2010 /*                        External Interface                          */
2011 /**********************************************************************/
2012
2013 /* The four completion state selectors
2014  */
2015 static gchar*
2016 cmpl_updated_text (CompletionState* cmpl_state)
2017 {
2018   return cmpl_state->updated_text;
2019 }
2020
2021 static gint
2022 cmpl_updated_dir (CompletionState* cmpl_state)
2023 {
2024   return cmpl_state->re_complete;
2025 }
2026
2027 static gchar*
2028 cmpl_reference_position (CompletionState* cmpl_state)
2029 {
2030   return cmpl_state->reference_dir->fullname;
2031 }
2032
2033 static gint
2034 cmpl_last_valid_char (CompletionState* cmpl_state)
2035 {
2036   return cmpl_state->last_valid_char;
2037 }
2038
2039 static gchar*
2040 cmpl_completion_fullname (gchar* text, CompletionState* cmpl_state)
2041 {
2042   static char nothing[2] = "";
2043
2044   if (!cmpl_state_okay (cmpl_state))
2045     {
2046       return nothing;
2047     }
2048   else if (text[0] == '/')
2049     {
2050       strcpy (cmpl_state->updated_text, text);
2051     }
2052   else if (text[0] == '~')
2053     {
2054       CompletionDir* dir;
2055       char* slash;
2056
2057       dir = open_user_dir (text, cmpl_state);
2058
2059       if (!dir)
2060         {
2061           /* spencer says just return ~something, so
2062            * for now just do it. */
2063           strcpy (cmpl_state->updated_text, text);
2064         }
2065       else
2066         {
2067
2068           strcpy (cmpl_state->updated_text, dir->fullname);
2069
2070           slash = strchr (text, '/');
2071
2072           if (slash)
2073             strcat (cmpl_state->updated_text, slash);
2074         }
2075     }
2076   else
2077     {
2078       strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname);
2079       if (strcmp (cmpl_state->reference_dir->fullname, "/") != 0)
2080         strcat (cmpl_state->updated_text, "/");
2081       strcat (cmpl_state->updated_text, text);
2082     }
2083
2084   return cmpl_state->updated_text;
2085 }
2086
2087 /* The three completion selectors
2088  */
2089 static gchar*
2090 cmpl_this_completion (PossibleCompletion* pc)
2091 {
2092   return pc->text;
2093 }
2094
2095 static gint
2096 cmpl_is_directory (PossibleCompletion* pc)
2097 {
2098   return pc->is_directory;
2099 }
2100
2101 static gint
2102 cmpl_is_a_completion (PossibleCompletion* pc)
2103 {
2104   return pc->is_a_completion;
2105 }
2106
2107 /**********************************************************************/
2108 /*                       Construction, deletion                       */
2109 /**********************************************************************/
2110
2111 static CompletionState*
2112 cmpl_init_state (void)
2113 {
2114   gchar getcwd_buf[2*MAXPATHLEN];
2115   CompletionState *new_state;
2116
2117   new_state = g_new (CompletionState, 1);
2118
2119   /* We don't use getcwd() on SUNOS, because, it does a popen("pwd")
2120    * and, if that wasn't bad enough, hangs in doing so.
2121    */
2122 #if defined(sun) && !defined(__SVR4)
2123   if (!getwd (getcwd_buf))
2124 #else    
2125   if (!getcwd (getcwd_buf, MAXPATHLEN))
2126 #endif    
2127     {
2128       /* Oh joy, we can't get the current directory. Um..., we should have
2129        * a root directory, right? Right? (Probably not portable to non-Unix)
2130        */
2131       strcpy (getcwd_buf, "/");
2132     }
2133
2134 tryagain:
2135
2136   new_state->reference_dir = NULL;
2137   new_state->completion_dir = NULL;
2138   new_state->active_completion_dir = NULL;
2139   new_state->directory_storage = NULL;
2140   new_state->directory_sent_storage = NULL;
2141   new_state->last_valid_char = 0;
2142   new_state->updated_text = g_new (gchar, MAXPATHLEN);
2143   new_state->updated_text_alloc = MAXPATHLEN;
2144   new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
2145   new_state->the_completion.text_alloc = MAXPATHLEN;
2146   new_state->user_dir_name_buffer = NULL;
2147   new_state->user_directories = NULL;
2148
2149   new_state->reference_dir =  open_dir (getcwd_buf, new_state);
2150
2151   if (!new_state->reference_dir)
2152     {
2153       /* Directories changing from underneath us, grumble */
2154       strcpy (getcwd_buf, "/");
2155       goto tryagain;
2156     }
2157
2158   return new_state;
2159 }
2160
2161 static void
2162 cmpl_free_dir_list(GList* dp0)
2163 {
2164   GList *dp = dp0;
2165
2166   while (dp) {
2167     free_dir (dp->data);
2168     dp = dp->next;
2169   }
2170
2171   g_list_free(dp0);
2172 }
2173
2174 static void
2175 cmpl_free_dir_sent_list(GList* dp0)
2176 {
2177   GList *dp = dp0;
2178
2179   while (dp) {
2180     free_dir_sent (dp->data);
2181     dp = dp->next;
2182   }
2183
2184   g_list_free(dp0);
2185 }
2186
2187 static void
2188 cmpl_free_state (CompletionState* cmpl_state)
2189 {
2190   cmpl_free_dir_list (cmpl_state->directory_storage);
2191   cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
2192
2193   if (cmpl_state->user_dir_name_buffer)
2194     g_free (cmpl_state->user_dir_name_buffer);
2195   if (cmpl_state->user_directories)
2196     g_free (cmpl_state->user_directories);
2197   if (cmpl_state->the_completion.text)
2198     g_free (cmpl_state->the_completion.text);
2199   if (cmpl_state->updated_text)
2200     g_free (cmpl_state->updated_text);
2201
2202   g_free (cmpl_state);
2203 }
2204
2205 static void
2206 free_dir(CompletionDir* dir)
2207 {
2208   g_free(dir->fullname);
2209   g_free(dir);
2210 }
2211
2212 static void
2213 free_dir_sent(CompletionDirSent* sent)
2214 {
2215   g_free(sent->name_buffer);
2216   g_free(sent->entries);
2217   g_free(sent);
2218 }
2219
2220 static void
2221 prune_memory_usage(CompletionState *cmpl_state)
2222 {
2223   GList* cdsl = cmpl_state->directory_sent_storage;
2224   GList* cdl = cmpl_state->directory_storage;
2225   GList* cdl0 = cdl;
2226   gint len = 0;
2227
2228   for(; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
2229     cdsl = cdsl->next;
2230
2231   if (cdsl) {
2232     cmpl_free_dir_sent_list(cdsl->next);
2233     cdsl->next = NULL;
2234   }
2235
2236   cmpl_state->directory_storage = NULL;
2237   while (cdl) {
2238     if (cdl->data == cmpl_state->reference_dir)
2239       cmpl_state->directory_storage = g_list_prepend(NULL, cdl->data);
2240     else
2241       free_dir (cdl->data);
2242     cdl = cdl->next;
2243   }
2244
2245   g_list_free(cdl0);
2246 }
2247
2248 /**********************************************************************/
2249 /*                        The main entrances.                         */
2250 /**********************************************************************/
2251
2252 static PossibleCompletion*
2253 cmpl_completion_matches (gchar* text_to_complete,
2254                          gchar** remaining_text,
2255                          CompletionState* cmpl_state)
2256 {
2257   gchar* first_slash;
2258   PossibleCompletion *poss;
2259
2260   prune_memory_usage(cmpl_state);
2261
2262   g_assert (text_to_complete != NULL);
2263
2264   cmpl_state->user_completion_index = -1;
2265   cmpl_state->last_completion_text = text_to_complete;
2266   cmpl_state->the_completion.text[0] = 0;
2267   cmpl_state->last_valid_char = 0;
2268   cmpl_state->updated_text_len = -1;
2269   cmpl_state->updated_text[0] = 0;
2270   cmpl_state->re_complete = FALSE;
2271
2272   first_slash = strchr (text_to_complete, '/');
2273
2274   if (text_to_complete[0] == '~' && !first_slash)
2275     {
2276       /* Text starts with ~ and there is no slash, show all the
2277        * home directory completions.
2278        */
2279       poss = attempt_homedir_completion (text_to_complete, cmpl_state);
2280
2281       update_cmpl(poss, cmpl_state);
2282
2283       return poss;
2284     }
2285
2286   cmpl_state->reference_dir =
2287     open_ref_dir (text_to_complete, remaining_text, cmpl_state);
2288
2289   if(!cmpl_state->reference_dir)
2290     return NULL;
2291
2292   cmpl_state->completion_dir =
2293     find_completion_dir (*remaining_text, remaining_text, cmpl_state);
2294
2295   cmpl_state->last_valid_char = *remaining_text - text_to_complete;
2296
2297   if(!cmpl_state->completion_dir)
2298     return NULL;
2299
2300   cmpl_state->completion_dir->cmpl_index = -1;
2301   cmpl_state->completion_dir->cmpl_parent = NULL;
2302   cmpl_state->completion_dir->cmpl_text = *remaining_text;
2303
2304   cmpl_state->active_completion_dir = cmpl_state->completion_dir;
2305
2306   cmpl_state->reference_dir = cmpl_state->completion_dir;
2307
2308   poss = attempt_file_completion(cmpl_state);
2309
2310   update_cmpl(poss, cmpl_state);
2311
2312   return poss;
2313 }
2314
2315 static PossibleCompletion*
2316 cmpl_next_completion (CompletionState* cmpl_state)
2317 {
2318   PossibleCompletion* poss = NULL;
2319
2320   cmpl_state->the_completion.text[0] = 0;
2321
2322   if(cmpl_state->user_completion_index >= 0)
2323     poss = attempt_homedir_completion(cmpl_state->last_completion_text, cmpl_state);
2324   else
2325     poss = attempt_file_completion(cmpl_state);
2326
2327   update_cmpl(poss, cmpl_state);
2328
2329   return poss;
2330 }
2331
2332 /**********************************************************************/
2333 /*                       Directory Operations                         */
2334 /**********************************************************************/
2335
2336 /* Open the directory where completion will begin from, if possible. */
2337 static CompletionDir*
2338 open_ref_dir(gchar* text_to_complete,
2339              gchar** remaining_text,
2340              CompletionState* cmpl_state)
2341 {
2342   gchar* first_slash;
2343   CompletionDir *new_dir;
2344
2345   first_slash = strchr(text_to_complete, '/');
2346
2347   if (text_to_complete[0] == '~')
2348     {
2349       new_dir = open_user_dir(text_to_complete, cmpl_state);
2350
2351       if(new_dir)
2352         {
2353           if(first_slash)
2354             *remaining_text = first_slash + 1;
2355           else
2356             *remaining_text = text_to_complete + strlen(text_to_complete);
2357         }
2358       else
2359         {
2360           return NULL;
2361         }
2362     }
2363   else if (text_to_complete[0] == '/' || !cmpl_state->reference_dir)
2364     {
2365       gchar *tmp = g_strdup(text_to_complete);
2366       gchar *p;
2367
2368       p = tmp;
2369       while (*p && *p != '*' && *p != '?')
2370         p++;
2371
2372       *p = '\0';
2373       p = strrchr(tmp, '/');
2374       if (p)
2375         {
2376           if (p == tmp)
2377             p++;
2378       
2379           *p = '\0';
2380
2381           new_dir = open_dir(tmp, cmpl_state);
2382
2383           if(new_dir)
2384             *remaining_text = text_to_complete + 
2385               ((p == tmp + 1) ? (p - tmp) : (p + 1 - tmp));
2386         }
2387       else
2388         {
2389           /* If no possible candidates, use the cwd */
2390           gchar *curdir = g_get_current_dir ();
2391           
2392           new_dir = open_dir(curdir, cmpl_state);
2393
2394           if (new_dir)
2395             *remaining_text = text_to_complete;
2396
2397           g_free (curdir);
2398         }
2399
2400       g_free (tmp);
2401     }
2402   else
2403     {
2404       *remaining_text = text_to_complete;
2405
2406       new_dir = open_dir(cmpl_state->reference_dir->fullname, cmpl_state);
2407     }
2408
2409   if(new_dir)
2410     {
2411       new_dir->cmpl_index = -1;
2412       new_dir->cmpl_parent = NULL;
2413     }
2414
2415   return new_dir;
2416 }
2417
2418 /* open a directory by user name */
2419 static CompletionDir*
2420 open_user_dir(gchar* text_to_complete,
2421               CompletionState *cmpl_state)
2422 {
2423   gchar *first_slash;
2424   gint cmp_len;
2425
2426   g_assert(text_to_complete && text_to_complete[0] == '~');
2427
2428   first_slash = strchr(text_to_complete, '/');
2429
2430   if (first_slash)
2431     cmp_len = first_slash - text_to_complete - 1;
2432   else
2433     cmp_len = strlen(text_to_complete + 1);
2434
2435   if(!cmp_len)
2436     {
2437       /* ~/ */
2438       gchar *homedir = g_get_home_dir ();
2439
2440       if (homedir)
2441         return open_dir(homedir, cmpl_state);
2442       else
2443         return NULL;
2444     }
2445   else
2446     {
2447       /* ~user/ */
2448       char* copy = g_new(char, cmp_len + 1);
2449       struct passwd *pwd;
2450       strncpy(copy, text_to_complete + 1, cmp_len);
2451       copy[cmp_len] = 0;
2452       pwd = getpwnam(copy);
2453       g_free(copy);
2454       if (!pwd)
2455         {
2456           cmpl_errno = errno;
2457           return NULL;
2458         }
2459
2460       return open_dir(pwd->pw_dir, cmpl_state);
2461     }
2462 }
2463
2464 /* open a directory relative the the current relative directory */
2465 static CompletionDir*
2466 open_relative_dir(gchar* dir_name,
2467                   CompletionDir* dir,
2468                   CompletionState *cmpl_state)
2469 {
2470   gchar path_buf[2*MAXPATHLEN];
2471
2472   if(dir->fullname_len + strlen(dir_name) + 2 >= MAXPATHLEN)
2473     {
2474       cmpl_errno = CMPL_ERRNO_TOO_LONG;
2475       return NULL;
2476     }
2477
2478   strcpy(path_buf, dir->fullname);
2479
2480   if(dir->fullname_len > 1)
2481     {
2482       path_buf[dir->fullname_len] = '/';
2483       strcpy(path_buf + dir->fullname_len + 1, dir_name);
2484     }
2485   else
2486     {
2487       strcpy(path_buf + dir->fullname_len, dir_name);
2488     }
2489
2490   return open_dir(path_buf, cmpl_state);
2491 }
2492
2493 /* after the cache lookup fails, really open a new directory */
2494 static CompletionDirSent*
2495 open_new_dir(gchar* dir_name, struct stat* sbuf, gboolean stat_subdirs)
2496 {
2497   CompletionDirSent* sent;
2498   DIR* directory;
2499   gchar *buffer_ptr;
2500   struct dirent *dirent_ptr;
2501   gint buffer_size = 0;
2502   gint entry_count = 0;
2503   gint i;
2504   struct stat ent_sbuf;
2505   char path_buf[MAXPATHLEN*2];
2506   gint path_buf_len;
2507
2508   sent = g_new(CompletionDirSent, 1);
2509   sent->mtime = sbuf->st_mtime;
2510   sent->inode = sbuf->st_ino;
2511   sent->device = sbuf->st_dev;
2512
2513   path_buf_len = strlen(dir_name);
2514
2515   if (path_buf_len > MAXPATHLEN)
2516     {
2517       cmpl_errno = CMPL_ERRNO_TOO_LONG;
2518       return NULL;
2519     }
2520
2521   strcpy(path_buf, dir_name);
2522
2523   directory = opendir(dir_name);
2524
2525   if(!directory)
2526     {
2527       cmpl_errno = errno;
2528       return NULL;
2529     }
2530
2531   while((dirent_ptr = readdir(directory)) != NULL)
2532     {
2533       int entry_len = strlen(dirent_ptr->d_name);
2534       buffer_size += entry_len + 1;
2535       entry_count += 1;
2536
2537       if(path_buf_len + entry_len + 2 >= MAXPATHLEN)
2538         {
2539           cmpl_errno = CMPL_ERRNO_TOO_LONG;
2540           closedir(directory);
2541           return NULL;
2542         }
2543     }
2544
2545   sent->name_buffer = g_new(gchar, buffer_size);
2546   sent->entries = g_new(CompletionDirEntry, entry_count);
2547   sent->entry_count = entry_count;
2548
2549   buffer_ptr = sent->name_buffer;
2550
2551   rewinddir(directory);
2552
2553   for(i = 0; i < entry_count; i += 1)
2554     {
2555       dirent_ptr = readdir(directory);
2556
2557       if(!dirent_ptr)
2558         {
2559           cmpl_errno = errno;
2560           closedir(directory);
2561           return NULL;
2562         }
2563
2564       strcpy(buffer_ptr, dirent_ptr->d_name);
2565       sent->entries[i].entry_name = buffer_ptr;
2566       buffer_ptr += strlen(dirent_ptr->d_name);
2567       *buffer_ptr = 0;
2568       buffer_ptr += 1;
2569
2570       path_buf[path_buf_len] = '/';
2571       strcpy(path_buf + path_buf_len + 1, dirent_ptr->d_name);
2572
2573       if (stat_subdirs)
2574         {
2575           if(stat(path_buf, &ent_sbuf) >= 0 && S_ISDIR(ent_sbuf.st_mode))
2576             sent->entries[i].is_dir = 1;
2577           else
2578             /* stat may fail, and we don't mind, since it could be a
2579              * dangling symlink. */
2580             sent->entries[i].is_dir = 0;
2581         }
2582       else
2583         sent->entries[i].is_dir = 1;
2584     }
2585
2586   qsort(sent->entries, sent->entry_count, sizeof(CompletionDirEntry), compare_cmpl_dir);
2587
2588   closedir(directory);
2589
2590   return sent;
2591 }
2592
2593 static gboolean
2594 check_dir(gchar *dir_name, struct stat *result, gboolean *stat_subdirs)
2595 {
2596   /* A list of directories that we know only contain other directories.
2597    * Trying to stat every file in these directories would be very
2598    * expensive.
2599    */
2600
2601   static struct {
2602     gchar *name;
2603     gboolean present;
2604     struct stat statbuf;
2605   } no_stat_dirs[] = {
2606     { "/afs", FALSE, { 0 } },
2607     { "/net", FALSE, { 0 } }
2608   };
2609
2610   static const gint n_no_stat_dirs = sizeof(no_stat_dirs) / sizeof(no_stat_dirs[0]);
2611   static gboolean initialized = FALSE;
2612
2613   gint i;
2614
2615   if (!initialized)
2616     {
2617       initialized = TRUE;
2618       for (i = 0; i < n_no_stat_dirs; i++)
2619         {
2620           if (stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
2621             no_stat_dirs[i].present = TRUE;
2622         }
2623     }
2624
2625   if(stat(dir_name, result) < 0)
2626     {
2627       cmpl_errno = errno;
2628       return FALSE;
2629     }
2630
2631   *stat_subdirs = TRUE;
2632   for (i=0; i<n_no_stat_dirs; i++)
2633     {
2634       if (no_stat_dirs[i].present &&
2635           (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
2636           (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
2637         {
2638           *stat_subdirs = FALSE;
2639           break;
2640         }
2641     }
2642
2643   return TRUE;
2644 }
2645
2646 /* open a directory by absolute pathname */
2647 static CompletionDir*
2648 open_dir(gchar* dir_name, CompletionState* cmpl_state)
2649 {
2650   struct stat sbuf;
2651   gboolean stat_subdirs;
2652   CompletionDirSent *sent;
2653   GList* cdsl;
2654
2655   if (!check_dir (dir_name, &sbuf, &stat_subdirs))
2656     return NULL;
2657
2658   cdsl = cmpl_state->directory_sent_storage;
2659
2660   while (cdsl)
2661     {
2662       sent = cdsl->data;
2663
2664       if(sent->inode == sbuf.st_ino &&
2665          sent->mtime == sbuf.st_mtime &&
2666          sent->device == sbuf.st_dev)
2667         return attach_dir(sent, dir_name, cmpl_state);
2668
2669       cdsl = cdsl->next;
2670     }
2671
2672   sent = open_new_dir(dir_name, &sbuf, stat_subdirs);
2673
2674   if (sent) {
2675     cmpl_state->directory_sent_storage =
2676       g_list_prepend(cmpl_state->directory_sent_storage, sent);
2677
2678     return attach_dir(sent, dir_name, cmpl_state);
2679   }
2680
2681   return NULL;
2682 }
2683
2684 static CompletionDir*
2685 attach_dir(CompletionDirSent* sent, gchar* dir_name, CompletionState *cmpl_state)
2686 {
2687   CompletionDir* new_dir;
2688
2689   new_dir = g_new(CompletionDir, 1);
2690
2691   cmpl_state->directory_storage =
2692     g_list_prepend(cmpl_state->directory_storage, new_dir);
2693
2694   new_dir->sent = sent;
2695   new_dir->fullname = g_strdup(dir_name);
2696   new_dir->fullname_len = strlen(dir_name);
2697
2698   return new_dir;
2699 }
2700
2701 static gint
2702 correct_dir_fullname(CompletionDir* cmpl_dir)
2703 {
2704   gint length = strlen(cmpl_dir->fullname);
2705   struct stat sbuf;
2706
2707   if (strcmp(cmpl_dir->fullname + length - 2, "/.") == 0)
2708     {
2709       if (length == 2) 
2710         {
2711           strcpy(cmpl_dir->fullname, "/");
2712           cmpl_dir->fullname_len = 1;
2713           return TRUE;
2714         } else {
2715           cmpl_dir->fullname[length - 2] = 0;
2716         }
2717     }
2718   else if (strcmp(cmpl_dir->fullname + length - 3, "/./") == 0)
2719     cmpl_dir->fullname[length - 2] = 0;
2720   else if (strcmp(cmpl_dir->fullname + length - 3, "/..") == 0)
2721     {
2722       if(length == 3)
2723         {
2724           strcpy(cmpl_dir->fullname, "/");
2725           cmpl_dir->fullname_len = 1;
2726           return TRUE;
2727         }
2728
2729       if(stat(cmpl_dir->fullname, &sbuf) < 0)
2730         {
2731           cmpl_errno = errno;
2732           return FALSE;
2733         }
2734
2735       cmpl_dir->fullname[length - 2] = 0;
2736
2737       if(!correct_parent(cmpl_dir, &sbuf))
2738         return FALSE;
2739     }
2740   else if (strcmp(cmpl_dir->fullname + length - 4, "/../") == 0)
2741     {
2742       if(length == 4)
2743         {
2744           strcpy(cmpl_dir->fullname, "/");
2745           cmpl_dir->fullname_len = 1;
2746           return TRUE;
2747         }
2748
2749       if(stat(cmpl_dir->fullname, &sbuf) < 0)
2750         {
2751           cmpl_errno = errno;
2752           return FALSE;
2753         }
2754
2755       cmpl_dir->fullname[length - 3] = 0;
2756
2757       if(!correct_parent(cmpl_dir, &sbuf))
2758         return FALSE;
2759     }
2760
2761   cmpl_dir->fullname_len = strlen(cmpl_dir->fullname);
2762
2763   return TRUE;
2764 }
2765
2766 static gint
2767 correct_parent(CompletionDir* cmpl_dir, struct stat *sbuf)
2768 {
2769   struct stat parbuf;
2770   gchar *last_slash;
2771   gchar *new_name;
2772   gchar c = 0;
2773
2774   last_slash = strrchr(cmpl_dir->fullname, '/');
2775
2776   g_assert(last_slash);
2777
2778   if(last_slash != cmpl_dir->fullname)
2779     { /* last_slash[0] = 0; */ }
2780   else
2781     {
2782       c = last_slash[1];
2783       last_slash[1] = 0;
2784     }
2785
2786   if (stat(cmpl_dir->fullname, &parbuf) < 0)
2787     {
2788       cmpl_errno = errno;
2789       return FALSE;
2790     }
2791
2792   if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
2793     /* it wasn't a link */
2794     return TRUE;
2795
2796   if(c)
2797     last_slash[1] = c;
2798   /* else
2799     last_slash[0] = '/'; */
2800
2801   /* it was a link, have to figure it out the hard way */
2802
2803   new_name = find_parent_dir_fullname(cmpl_dir->fullname);
2804
2805   if (!new_name)
2806     return FALSE;
2807
2808   g_free(cmpl_dir->fullname);
2809
2810   cmpl_dir->fullname = new_name;
2811
2812   return TRUE;
2813 }
2814
2815 static gchar*
2816 find_parent_dir_fullname(gchar* dirname)
2817 {
2818   gchar buffer[MAXPATHLEN];
2819   gchar buffer2[MAXPATHLEN];
2820
2821 #if defined(sun) && !defined(__SVR4)
2822   if(!getwd(buffer))
2823 #else
2824   if(!getcwd(buffer, MAXPATHLEN))
2825 #endif    
2826     {
2827       cmpl_errno = errno;
2828       return NULL;
2829     }
2830
2831   if(chdir(dirname) != 0 || chdir("..") != 0)
2832     {
2833       cmpl_errno = errno;
2834       return NULL;
2835     }
2836
2837 #if defined(sun) && !defined(__SVR4)
2838   if(!getwd(buffer2))
2839 #else
2840   if(!getcwd(buffer2, MAXPATHLEN))
2841 #endif
2842     {
2843       chdir(buffer);
2844       cmpl_errno = errno;
2845
2846       return NULL;
2847     }
2848
2849   if(chdir(buffer) != 0)
2850     {
2851       cmpl_errno = errno;
2852       return NULL;
2853     }
2854
2855   return g_strdup(buffer2);
2856 }
2857
2858 /**********************************************************************/
2859 /*                        Completion Operations                       */
2860 /**********************************************************************/
2861
2862 static PossibleCompletion*
2863 attempt_homedir_completion(gchar* text_to_complete,
2864                            CompletionState *cmpl_state)
2865 {
2866   gint index, length;
2867
2868   if (!cmpl_state->user_dir_name_buffer &&
2869       !get_pwdb(cmpl_state))
2870     return NULL;
2871   length = strlen(text_to_complete) - 1;
2872
2873   cmpl_state->user_completion_index += 1;
2874
2875   while(cmpl_state->user_completion_index < cmpl_state->user_directories_len)
2876     {
2877       index = first_diff_index(text_to_complete + 1,
2878                                cmpl_state->user_directories
2879                                [cmpl_state->user_completion_index].login);
2880
2881       switch(index)
2882         {
2883         case PATTERN_MATCH:
2884           break;
2885         default:
2886           if(cmpl_state->last_valid_char < (index + 1))
2887             cmpl_state->last_valid_char = index + 1;
2888           cmpl_state->user_completion_index += 1;
2889           continue;
2890         }
2891
2892       cmpl_state->the_completion.is_a_completion = 1;
2893       cmpl_state->the_completion.is_directory = 1;
2894
2895       append_completion_text("~", cmpl_state);
2896
2897       append_completion_text(cmpl_state->
2898                               user_directories[cmpl_state->user_completion_index].login,
2899                              cmpl_state);
2900
2901       return append_completion_text("/", cmpl_state);
2902     }
2903
2904   if(text_to_complete[1] ||
2905      cmpl_state->user_completion_index > cmpl_state->user_directories_len)
2906     {
2907       cmpl_state->user_completion_index = -1;
2908       return NULL;
2909     }
2910   else
2911     {
2912       cmpl_state->user_completion_index += 1;
2913       cmpl_state->the_completion.is_a_completion = 1;
2914       cmpl_state->the_completion.is_directory = 1;
2915
2916       return append_completion_text("~/", cmpl_state);
2917     }
2918 }
2919
2920 /* returns the index (>= 0) of the first differing character,
2921  * PATTERN_MATCH if the completion matches */
2922 static gint
2923 first_diff_index(gchar* pat, gchar* text)
2924 {
2925   gint diff = 0;
2926
2927   while(*pat && *text && *text == *pat)
2928     {
2929       pat += 1;
2930       text += 1;
2931       diff += 1;
2932     }
2933
2934   if(*pat)
2935     return diff;
2936
2937   return PATTERN_MATCH;
2938 }
2939
2940 static PossibleCompletion*
2941 append_completion_text(gchar* text, CompletionState* cmpl_state)
2942 {
2943   gint len, i = 1;
2944
2945   if(!cmpl_state->the_completion.text)
2946     return NULL;
2947
2948   len = strlen(text) + strlen(cmpl_state->the_completion.text) + 1;
2949
2950   if(cmpl_state->the_completion.text_alloc > len)
2951     {
2952       strcat(cmpl_state->the_completion.text, text);
2953       return &cmpl_state->the_completion;
2954     }
2955
2956   while(i < len) { i <<= 1; }
2957
2958   cmpl_state->the_completion.text_alloc = i;
2959
2960   cmpl_state->the_completion.text = (gchar*)g_realloc(cmpl_state->the_completion.text, i);
2961
2962   if(!cmpl_state->the_completion.text)
2963     return NULL;
2964   else
2965     {
2966       strcat(cmpl_state->the_completion.text, text);
2967       return &cmpl_state->the_completion;
2968     }
2969 }
2970
2971 static CompletionDir*
2972 find_completion_dir(gchar* text_to_complete,
2973                     gchar** remaining_text,
2974                     CompletionState* cmpl_state)
2975 {
2976   gchar* first_slash = strchr(text_to_complete, '/');
2977   CompletionDir* dir = cmpl_state->reference_dir;
2978   CompletionDir* next;
2979   *remaining_text = text_to_complete;
2980
2981   while(first_slash)
2982     {
2983       gint len = first_slash - *remaining_text;
2984       gint found = 0;
2985       gchar *found_name = NULL;         /* Quiet gcc */
2986       gint i;
2987       gchar* pat_buf = g_new (gchar, len + 1);
2988
2989       strncpy(pat_buf, *remaining_text, len);
2990       pat_buf[len] = 0;
2991
2992       for(i = 0; i < dir->sent->entry_count; i += 1)
2993         {
2994           if(dir->sent->entries[i].is_dir &&
2995              fnmatch(pat_buf, dir->sent->entries[i].entry_name,
2996                      FNMATCH_FLAGS)!= FNM_NOMATCH)
2997             {
2998               if(found)
2999                 {
3000                   g_free (pat_buf);
3001                   return dir;
3002                 }
3003               else
3004                 {
3005                   found = 1;
3006                   found_name = dir->sent->entries[i].entry_name;
3007                 }
3008             }
3009         }
3010
3011       if (!found)
3012         {
3013           /* Perhaps we are trying to open an automount directory */
3014           found_name = pat_buf;
3015         }
3016
3017       next = open_relative_dir(found_name, dir, cmpl_state);
3018       
3019       if(!next)
3020         {
3021           g_free (pat_buf);
3022           return NULL;
3023         }
3024       
3025       next->cmpl_parent = dir;
3026       
3027       dir = next;
3028       
3029       if(!correct_dir_fullname(dir))
3030         {
3031           g_free(pat_buf);
3032           return NULL;
3033         }
3034       
3035       *remaining_text = first_slash + 1;
3036       first_slash = strchr(*remaining_text, '/');
3037
3038       g_free (pat_buf);
3039     }
3040
3041   return dir;
3042 }
3043
3044 static void
3045 update_cmpl(PossibleCompletion* poss, CompletionState* cmpl_state)
3046 {
3047   gint cmpl_len;
3048
3049   if(!poss || !cmpl_is_a_completion(poss))
3050     return;
3051
3052   cmpl_len = strlen(cmpl_this_completion(poss));
3053
3054   if(cmpl_state->updated_text_alloc < cmpl_len + 1)
3055     {
3056       cmpl_state->updated_text =
3057         (gchar*)g_realloc(cmpl_state->updated_text,
3058                           cmpl_state->updated_text_alloc);
3059       cmpl_state->updated_text_alloc = 2*cmpl_len;
3060     }
3061
3062   if(cmpl_state->updated_text_len < 0)
3063     {
3064       strcpy(cmpl_state->updated_text, cmpl_this_completion(poss));
3065       cmpl_state->updated_text_len = cmpl_len;
3066       cmpl_state->re_complete = cmpl_is_directory(poss);
3067     }
3068   else if(cmpl_state->updated_text_len == 0)
3069     {
3070       cmpl_state->re_complete = FALSE;
3071     }
3072   else
3073     {
3074       gint first_diff =
3075         first_diff_index(cmpl_state->updated_text,
3076                          cmpl_this_completion(poss));
3077
3078       cmpl_state->re_complete = FALSE;
3079
3080       if(first_diff == PATTERN_MATCH)
3081         return;
3082
3083       if(first_diff > cmpl_state->updated_text_len)
3084         strcpy(cmpl_state->updated_text, cmpl_this_completion(poss));
3085
3086       cmpl_state->updated_text_len = first_diff;
3087       cmpl_state->updated_text[first_diff] = 0;
3088     }
3089 }
3090
3091 static PossibleCompletion*
3092 attempt_file_completion(CompletionState *cmpl_state)
3093 {
3094   gchar *pat_buf, *first_slash;
3095   CompletionDir *dir = cmpl_state->active_completion_dir;
3096
3097   dir->cmpl_index += 1;
3098
3099   if(dir->cmpl_index == dir->sent->entry_count)
3100     {
3101       if(dir->cmpl_parent == NULL)
3102         {
3103           cmpl_state->active_completion_dir = NULL;
3104
3105           return NULL;
3106         }
3107       else
3108         {
3109           cmpl_state->active_completion_dir = dir->cmpl_parent;
3110
3111           return attempt_file_completion(cmpl_state);
3112         }
3113     }
3114
3115   g_assert(dir->cmpl_text);
3116
3117   first_slash = strchr(dir->cmpl_text, '/');
3118
3119   if(first_slash)
3120     {
3121       gint len = first_slash - dir->cmpl_text;
3122
3123       pat_buf = g_new (gchar, len + 1);
3124       strncpy(pat_buf, dir->cmpl_text, len);
3125       pat_buf[len] = 0;
3126     }
3127   else
3128     {
3129       gint len = strlen(dir->cmpl_text);
3130
3131       pat_buf = g_new (gchar, len + 2);
3132       strcpy(pat_buf, dir->cmpl_text);
3133       strcpy(pat_buf + len, "*");
3134     }
3135
3136   if(first_slash)
3137     {
3138       if(dir->sent->entries[dir->cmpl_index].is_dir)
3139         {
3140           if(fnmatch(pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3141                      FNMATCH_FLAGS) != FNM_NOMATCH)
3142             {
3143               CompletionDir* new_dir;
3144
3145               new_dir = open_relative_dir(dir->sent->entries[dir->cmpl_index].entry_name,
3146                                           dir, cmpl_state);
3147
3148               if(!new_dir)
3149                 {
3150                   g_free (pat_buf);
3151                   return NULL;
3152                 }
3153
3154               new_dir->cmpl_parent = dir;
3155
3156               new_dir->cmpl_index = -1;
3157               new_dir->cmpl_text = first_slash + 1;
3158
3159               cmpl_state->active_completion_dir = new_dir;
3160
3161               g_free (pat_buf);
3162               return attempt_file_completion(cmpl_state);
3163             }
3164           else
3165             {
3166               g_free (pat_buf);
3167               return attempt_file_completion(cmpl_state);
3168             }
3169         }
3170       else
3171         {
3172           g_free (pat_buf);
3173           return attempt_file_completion(cmpl_state);
3174         }
3175     }
3176   else
3177     {
3178       if(dir->cmpl_parent != NULL)
3179         {
3180           append_completion_text(dir->fullname +
3181                                  strlen(cmpl_state->completion_dir->fullname) + 1,
3182                                  cmpl_state);
3183           append_completion_text("/", cmpl_state);
3184         }
3185
3186       append_completion_text(dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
3187
3188       cmpl_state->the_completion.is_a_completion =
3189         (fnmatch(pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3190                  FNMATCH_FLAGS) != FNM_NOMATCH);
3191
3192       cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
3193       if(dir->sent->entries[dir->cmpl_index].is_dir)
3194         append_completion_text("/", cmpl_state);
3195
3196       g_free (pat_buf);
3197       return &cmpl_state->the_completion;
3198     }
3199 }
3200
3201
3202 static gint
3203 get_pwdb(CompletionState* cmpl_state)
3204 {
3205   struct passwd *pwd_ptr;
3206   gchar* buf_ptr;
3207   gint len = 0, i, count = 0;
3208
3209   if(cmpl_state->user_dir_name_buffer)
3210     return TRUE;
3211   setpwent ();
3212
3213   while ((pwd_ptr = getpwent()) != NULL)
3214     {
3215       len += strlen(pwd_ptr->pw_name);
3216       len += strlen(pwd_ptr->pw_dir);
3217       len += 2;
3218       count += 1;
3219     }
3220
3221   setpwent ();
3222
3223   cmpl_state->user_dir_name_buffer = g_new(gchar, len);
3224   cmpl_state->user_directories = g_new(CompletionUserDir, count);
3225   cmpl_state->user_directories_len = count;
3226
3227   buf_ptr = cmpl_state->user_dir_name_buffer;
3228
3229   for(i = 0; i < count; i += 1)
3230     {
3231       pwd_ptr = getpwent();
3232       if(!pwd_ptr)
3233         {
3234           cmpl_errno = errno;
3235           goto error;
3236         }
3237
3238       strcpy(buf_ptr, pwd_ptr->pw_name);
3239       cmpl_state->user_directories[i].login = buf_ptr;
3240       buf_ptr += strlen(buf_ptr);
3241       buf_ptr += 1;
3242       strcpy(buf_ptr, pwd_ptr->pw_dir);
3243       cmpl_state->user_directories[i].homedir = buf_ptr;
3244       buf_ptr += strlen(buf_ptr);
3245       buf_ptr += 1;
3246     }
3247
3248   qsort(cmpl_state->user_directories,
3249         cmpl_state->user_directories_len,
3250         sizeof(CompletionUserDir),
3251         compare_user_dir);
3252
3253   endpwent();
3254
3255   return TRUE;
3256
3257 error:
3258
3259   if(cmpl_state->user_dir_name_buffer)
3260     g_free(cmpl_state->user_dir_name_buffer);
3261   if(cmpl_state->user_directories)
3262     g_free(cmpl_state->user_directories);
3263
3264   cmpl_state->user_dir_name_buffer = NULL;
3265   cmpl_state->user_directories = NULL;
3266
3267   return FALSE;
3268 }
3269
3270 static gint
3271 compare_user_dir(const void* a, const void* b)
3272 {
3273   return strcmp((((CompletionUserDir*)a))->login,
3274                 (((CompletionUserDir*)b))->login);
3275 }
3276
3277 static gint
3278 compare_cmpl_dir(const void* a, const void* b)
3279 {
3280   return strcmp((((CompletionDirEntry*)a))->entry_name,
3281                 (((CompletionDirEntry*)b))->entry_name);
3282 }
3283
3284 static gint
3285 cmpl_state_okay(CompletionState* cmpl_state)
3286 {
3287   return  cmpl_state && cmpl_state->reference_dir;
3288 }
3289
3290 static gchar*
3291 cmpl_strerror(gint err)
3292 {
3293   if(err == CMPL_ERRNO_TOO_LONG)
3294     return "Name too long";
3295   else
3296     return g_strerror (err);
3297 }
3298
3299
3300 /* Testing area */
3301 #ifdef TORRIE_DEBUG
3302
3303 /* Get the selected filename and print it to the console */
3304 void file_ok_sel( GtkWidget        *w,
3305                   GtkFileSelection *fs )
3306 {
3307     g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
3308 }
3309
3310 void destroy( GtkWidget *widget,
3311               gpointer   data )
3312 {
3313     gtk_main_quit ();
3314 }
3315
3316 int main( int   argc,
3317           char *argv[] )
3318 {
3319     GtkWidget *filew;
3320
3321     gtk_init (&argc, &argv);
3322
3323     /* Create a new file selection widget */
3324     filew = gtk_file_selection_new ("Michael's Glorious File Selector");
3325 //    gtk_file_selection_complete(GTK_FILE_SELECTION(filew),"bob");
3326
3327                 
3328     gtk_signal_connect (GTK_OBJECT (filew), "destroy",
3329                         (GtkSignalFunc) destroy, &filew);
3330     /* Connect the ok_button to file_ok_sel function */
3331     gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
3332                         "clicked", (GtkSignalFunc) file_ok_sel, filew );
3333
3334     /* Connect the cancel_button to destroy the widget */
3335     gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION
3336                                             (filew)->cancel_button),
3337                                "clicked", (GtkSignalFunc) gtk_widget_destroy,
3338                                GTK_OBJECT (filew));
3339
3340
3341     gtk_widget_show(filew);
3342
3343 /*
3344     g_print("%d",gtk_file_selection_match_mask("mask.c","m*.c"));
3345     g_print("%d",gtk_file_selection_match_mask("mask.c","m???.c"));
3346                 g_print("%d",gtk_file_selection_match_mask("mask.c","m??*.c"));
3347                 g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c"));
3348                 g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c???"));
3349                 g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c*"));
3350                 g_print("%d",gtk_file_selection_match_mask("mask.cout","n*.c???"));
3351                 g_print("%d",gtk_file_selection_match_mask("mask.c","[mn]*"));
3352                 g_print("%d",gtk_file_selection_match_mask("COPYING","*.xpm"));
3353 */      
3354     gtk_main ();
3355
3356     return 0;
3357 }
3358
3359 /* example-end */
3360 #endif