]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesel.c
Shrink the private structs of these a bit.
[~andy/gtk] / gtk / gtkfilesel.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 Lesser 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser 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-2000.  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 #include <config.h>
28
29 #include <stdio.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #ifdef HAVE_SYS_PARAM_H
33 #include <sys/param.h>
34 #endif
35 #include <stdlib.h>
36 #ifdef HAVE_UNISTD_H
37 #include <unistd.h>
38 #endif
39 #include <string.h>
40 #include <errno.h>
41 #ifdef HAVE_PWD_H
42 #include <pwd.h>
43 #endif
44
45 #include <glib.h>               /* Include early to get G_OS_WIN32 etc */
46 #include <glib/gstdio.h>
47
48 #if defined(G_PLATFORM_WIN32)
49 #include <ctype.h>
50 #define STRICT
51 #include <windows.h>
52 #undef STRICT
53 #endif /* G_PLATFORM_WIN32 */
54
55 #include "gdk/gdkkeysyms.h"
56 #include "gtkbutton.h"
57 #include "gtkcellrenderertext.h"
58 #include "gtkentry.h"
59 #include "gtkfilesel.h"
60 #include "gtkhbox.h"
61 #include "gtkhbbox.h"
62 #include "gtkintl.h"
63 #include "gtklabel.h"
64 #include "gtkliststore.h"
65 #include "gtkmain.h"
66 #include "gtkprivate.h"
67 #include "gtkscrolledwindow.h"
68 #include "gtkstock.h"
69 #include "gtktreeselection.h"
70 #include "gtktreeview.h"
71 #include "gtkvbox.h"
72 #include "gtkmenu.h"
73 #include "gtkmenuitem.h"
74 #include "gtkdialog.h"
75 #include "gtkmessagedialog.h"
76 #include "gtkdnd.h"
77 #include "gtkeventbox.h"
78
79 #undef GTK_DISABLE_DEPRECATED
80 #include "gtkoptionmenu.h"
81 #define GTK_DISABLE_DEPRECATED
82
83 #define WANT_HPANED 1
84 #include "gtkhpaned.h"
85
86 #include "gtkalias.h"
87
88 #ifdef G_OS_WIN32
89 #include <direct.h>
90 #include <io.h>
91 #ifndef S_ISDIR
92 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
93 #endif
94 #endif /* G_OS_WIN32 */
95
96 #ifdef G_WITH_CYGWIN
97 #include <sys/cygwin.h>         /* For cygwin_conv_to_posix_path */
98 #endif
99
100 #define DIR_LIST_WIDTH   180
101 #define DIR_LIST_HEIGHT  180
102 #define FILE_LIST_WIDTH  180
103 #define FILE_LIST_HEIGHT 180
104
105 /* The Hurd doesn't define either PATH_MAX or MAXPATHLEN, so we put this
106  * in here, since the rest of the code in the file does require some
107  * fixed maximum.
108  */
109 #ifndef MAXPATHLEN
110 #  ifdef PATH_MAX
111 #    define MAXPATHLEN PATH_MAX
112 #  else
113 #    define MAXPATHLEN 2048
114 #  endif
115 #endif
116
117 /* I've put this here so it doesn't get confused with the 
118  * file completion interface */
119 typedef struct _HistoryCallbackArg HistoryCallbackArg;
120
121 struct _HistoryCallbackArg
122 {
123   gchar *directory;
124   GtkWidget *menu_item;
125 };
126
127
128 typedef struct _CompletionState    CompletionState;
129 typedef struct _CompletionDir      CompletionDir;
130 typedef struct _CompletionDirSent  CompletionDirSent;
131 typedef struct _CompletionDirEntry CompletionDirEntry;
132 typedef struct _CompletionUserDir  CompletionUserDir;
133 typedef struct _PossibleCompletion PossibleCompletion;
134
135 /* Non-external file completion decls and structures */
136
137 /* A contant telling PRCS how many directories to cache.  Its actually
138  * kept in a list, so the geometry isn't important. */
139 #define CMPL_DIRECTORY_CACHE_SIZE 10
140
141 /* A constant used to determine whether a substring was an exact
142  * match by first_diff_index()
143  */
144 #define PATTERN_MATCH -1
145 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
146 #define CMPL_ERRNO_DID_NOT_CONVERT ((1<<16)-2)
147
148 /* This structure contains all the useful information about a directory
149  * for the purposes of filename completion.  These structures are cached
150  * in the CompletionState struct.  CompletionDir's are reference counted.
151  */
152 struct _CompletionDirSent
153 {
154 #ifndef G_PLATFORM_WIN32
155   ino_t inode;
156   time_t mtime;
157   dev_t device;
158 #endif
159
160   gint entry_count;
161   struct _CompletionDirEntry *entries;
162 };
163
164 struct _CompletionDir
165 {
166   CompletionDirSent *sent;
167
168   gchar *fullname;
169   gint fullname_len;
170
171   struct _CompletionDir *cmpl_parent;
172   gint cmpl_index;
173   gchar *cmpl_text;
174 };
175
176 /* This structure contains pairs of directory entry names with a flag saying
177  * whether or not they are a valid directory.  NOTE: This information is used
178  * to provide the caller with information about whether to update its completions
179  * or try to open a file.  Since directories are cached by the directory mtime,
180  * a symlink which points to an invalid file (which will not be a directory),
181  * will not be reevaluated if that file is created, unless the containing
182  * directory is touched.  I consider this case to be worth ignoring (josh).
183  */
184 struct _CompletionDirEntry
185 {
186   gboolean is_dir;
187   gchar *entry_name;
188   gchar *sort_key;
189 };
190
191 struct _CompletionUserDir
192 {
193   gchar *login;
194   gchar *homedir;
195 };
196
197 struct _PossibleCompletion
198 {
199   /* accessible fields, all are accessed externally by functions
200    * declared above
201    */
202   gchar *text;
203   gint is_a_completion;
204   gboolean is_directory;
205
206   /* Private fields
207    */
208   gint text_alloc;
209 };
210
211 struct _CompletionState
212 {
213   gint last_valid_char;
214   gchar *updated_text;
215   gint updated_text_len;
216   gint updated_text_alloc;
217   gboolean re_complete;
218
219   gchar *user_dir_name_buffer;
220   gint user_directories_len;
221
222   gchar *last_completion_text;
223
224   gint user_completion_index; /* if >= 0, currently completing ~user */
225
226   struct _CompletionDir *completion_dir; /* directory completing from */
227   struct _CompletionDir *active_completion_dir;
228
229   struct _PossibleCompletion the_completion;
230
231   struct _CompletionDir *reference_dir; /* initial directory */
232
233   GList* directory_storage;
234   GList* directory_sent_storage;
235
236   struct _CompletionUserDir *user_directories;
237 };
238
239 enum {
240   PROP_0,
241   PROP_SHOW_FILEOPS,
242   PROP_FILENAME,
243   PROP_SELECT_MULTIPLE
244 };
245
246 enum {
247   DIR_COLUMN
248 };
249
250 enum {
251   FILE_COLUMN
252 };
253
254 /* File completion functions which would be external, were they used
255  * outside of this file.
256  */
257
258 static CompletionState*    cmpl_init_state        (void);
259 static void                cmpl_free_state        (CompletionState *cmpl_state);
260 static gint                cmpl_state_okay        (CompletionState* cmpl_state);
261 static const gchar*        cmpl_strerror          (gint);
262
263 static PossibleCompletion* cmpl_completion_matches(gchar           *text_to_complete,
264                                                    gchar          **remaining_text,
265                                                    CompletionState *cmpl_state);
266
267 /* Returns a name for consideration, possibly a completion, this name
268  * will be invalid after the next call to cmpl_next_completion.
269  */
270 static char*               cmpl_this_completion   (PossibleCompletion*);
271
272 /* True if this completion matches the given text.  Otherwise, this
273  * output can be used to have a list of non-completions.
274  */
275 static gint                cmpl_is_a_completion   (PossibleCompletion*);
276
277 /* True if the completion is a directory
278  */
279 static gboolean            cmpl_is_directory      (PossibleCompletion*);
280
281 /* Obtains the next completion, or NULL
282  */
283 static PossibleCompletion* cmpl_next_completion   (CompletionState*);
284
285 /* Updating completions: the return value of cmpl_updated_text() will
286  * be text_to_complete completed as much as possible after the most
287  * recent call to cmpl_completion_matches.  For the present
288  * application, this is the suggested replacement for the user's input
289  * string.  You must CALL THIS AFTER ALL cmpl_text_completions have
290  * been received.
291  */
292 static gchar*              cmpl_updated_text       (CompletionState* cmpl_state);
293
294 /* After updating, to see if the completion was a directory, call
295  * this.  If it was, you should consider re-calling completion_matches.
296  */
297 static gboolean            cmpl_updated_dir        (CompletionState* cmpl_state);
298
299 /* Current location: if using file completion, return the current
300  * directory, from which file completion begins.  More specifically,
301  * the cwd concatenated with all exact completions up to the last
302  * directory delimiter('/').
303  */
304 static gchar*              cmpl_reference_position (CompletionState* cmpl_state);
305
306 #if 0
307 /* This doesn't work currently and would require changes
308  * to fnmatch.c to get working.
309  */
310 /* backing up: if cmpl_completion_matches returns NULL, you may query
311  * the index of the last completable character into cmpl_updated_text.
312  */
313 static gint                cmpl_last_valid_char    (CompletionState* cmpl_state);
314 #endif
315
316 /* When the user selects a non-directory, call cmpl_completion_fullname
317  * to get the full name of the selected file.
318  */
319 static gchar*              cmpl_completion_fullname (const gchar*, CompletionState* cmpl_state);
320
321
322 /* Directory operations. */
323 static CompletionDir* open_ref_dir         (gchar* text_to_complete,
324                                             gchar** remaining_text,
325                                             CompletionState* cmpl_state);
326 #ifndef G_PLATFORM_WIN32
327 static gboolean       check_dir            (gchar *dir_name, 
328                                             struct stat *result, 
329                                             gboolean *stat_subdirs);
330 #endif
331 static CompletionDir* open_dir             (gchar* dir_name,
332                                             CompletionState* cmpl_state);
333 #ifdef HAVE_PWD_H
334 static CompletionDir* open_user_dir        (const gchar* text_to_complete,
335                                             CompletionState *cmpl_state);
336 #endif
337 static CompletionDir* open_relative_dir    (gchar* dir_name, CompletionDir* dir,
338                                             CompletionState *cmpl_state);
339 static CompletionDirSent* open_new_dir     (gchar* dir_name, 
340                                             struct stat* sbuf,
341                                             gboolean stat_subdirs);
342 static gint           correct_dir_fullname (CompletionDir* cmpl_dir);
343 static gint           correct_parent       (CompletionDir* cmpl_dir,
344                                             struct stat *sbuf);
345 #ifndef G_PLATFORM_WIN32
346 static gchar*         find_parent_dir_fullname    (gchar* dirname);
347 #endif
348 static CompletionDir* attach_dir           (CompletionDirSent* sent,
349                                             gchar* dir_name,
350                                             CompletionState *cmpl_state);
351 static void           free_dir_sent (CompletionDirSent* sent);
352 static void           free_dir      (CompletionDir  *dir);
353 static void           prune_memory_usage(CompletionState *cmpl_state);
354
355 /* Completion operations */
356 #ifdef HAVE_PWD_H
357 static PossibleCompletion* attempt_homedir_completion(gchar* text_to_complete,
358                                                       CompletionState *cmpl_state);
359 #endif
360 static PossibleCompletion* attempt_file_completion(CompletionState *cmpl_state);
361 static CompletionDir* find_completion_dir(gchar* text_to_complete,
362                                           gchar** remaining_text,
363                                           CompletionState* cmpl_state);
364 static PossibleCompletion* append_completion_text(gchar* text,
365                                                   CompletionState* cmpl_state);
366 #ifdef HAVE_PWD_H
367 static gint get_pwdb(CompletionState* cmpl_state);
368 static gint compare_user_dir(const void* a, const void* b);
369 #endif
370 static gint first_diff_index(gchar* pat, gchar* text);
371 static gint compare_cmpl_dir(const void* a, const void* b);
372 static void update_cmpl(PossibleCompletion* poss,
373                         CompletionState* cmpl_state);
374
375 static void gtk_file_selection_class_init    (GtkFileSelectionClass *klass);
376 static void gtk_file_selection_set_property  (GObject         *object,
377                                               guint            prop_id,
378                                               const GValue    *value,
379                                               GParamSpec      *pspec);
380 static void gtk_file_selection_get_property  (GObject         *object,
381                                               guint            prop_id,
382                                               GValue          *value,
383                                               GParamSpec      *pspec);
384 static void gtk_file_selection_init          (GtkFileSelection      *filesel);
385 static void gtk_file_selection_finalize      (GObject               *object);
386 static void gtk_file_selection_destroy       (GtkObject             *object);
387 static void gtk_file_selection_map           (GtkWidget             *widget);
388 static gint gtk_file_selection_key_press     (GtkWidget             *widget,
389                                               GdkEventKey           *event,
390                                               gpointer               user_data);
391 static gint gtk_file_selection_insert_text   (GtkWidget             *widget,
392                                               const gchar           *new_text,
393                                               gint                   new_text_length,
394                                               gint                  *position,
395                                               gpointer               user_data);
396 static void gtk_file_selection_update_fileops (GtkFileSelection     *filesel);
397
398 static void gtk_file_selection_file_activate (GtkTreeView       *tree_view,
399                                               GtkTreePath       *path,
400                                               GtkTreeViewColumn *column,
401                                               gpointer           user_data);
402 static void gtk_file_selection_file_changed  (GtkTreeSelection  *selection,
403                                               gpointer           user_data);
404 static void gtk_file_selection_dir_activate  (GtkTreeView       *tree_view,
405                                               GtkTreePath       *path,
406                                               GtkTreeViewColumn *column,
407                                               gpointer           user_data);
408
409 static void gtk_file_selection_populate      (GtkFileSelection      *fs,
410                                               gchar                 *rel_path,
411                                               gboolean               try_complete,
412                                               gboolean               reset_entry);
413 static void gtk_file_selection_abort         (GtkFileSelection      *fs);
414
415 static void gtk_file_selection_update_history_menu (GtkFileSelection       *fs,
416                                                     gchar                  *current_dir);
417
418 static void gtk_file_selection_create_dir  (GtkWidget *widget, gpointer data);
419 static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
420 static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
421
422 static void free_selected_names (GPtrArray *names);
423
424 #ifndef G_PLATFORM_WIN32
425
426 #define compare_utf8_filenames(a, b) strcmp(a, b)
427 #define compare_sys_filenames(a, b) strcmp(a, b)
428
429 #else
430
431 static gint
432 compare_utf8_filenames (const gchar *a,
433                         const gchar *b)
434 {
435   gchar *a_folded, *b_folded;
436   gint retval;
437
438   a_folded = g_utf8_strdown (a, -1);
439   b_folded = g_utf8_strdown (b, -1);
440
441   retval = strcmp (a_folded, b_folded);
442
443   g_free (a_folded);
444   g_free (b_folded);
445
446   return retval;
447 }
448
449 static gint
450 compare_sys_filenames (const gchar *a,
451                        const gchar *b)
452 {
453   gchar *a_utf8, *b_utf8;
454   gint retval;
455
456   a_utf8 = g_filename_to_utf8 (a, -1, NULL, NULL, NULL);
457   b_utf8 = g_filename_to_utf8 (b, -1, NULL, NULL, NULL);
458
459   retval = compare_utf8_filenames (a_utf8, b_utf8);
460
461   g_free (a_utf8);
462   g_free (b_utf8);
463
464   return retval;
465 }
466
467 #endif
468
469 static GtkWindowClass *parent_class = NULL;
470
471 /* Saves errno when something cmpl does fails. */
472 static gint cmpl_errno;
473
474 #ifdef G_WITH_CYGWIN
475 /*
476  * Take the path currently in the file selection
477  * entry field and translate as necessary from
478  * a Win32 style to Cygwin style path.  For
479  * instance translate:
480  * x:\somepath\file.jpg
481  * to:
482  * /cygdrive/x/somepath/file.jpg
483  *
484  * Replace the path in the selection text field.
485  * Return a boolean value concerning whether a
486  * translation had to be made.
487  */
488 static int
489 translate_win32_path (GtkFileSelection *filesel)
490 {
491   int updated = 0;
492   const gchar *path;
493   gchar newPath[MAX_PATH];
494
495   /*
496    * Retrieve the current path
497    */
498   path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
499
500   cygwin_conv_to_posix_path (path, newPath);
501   updated = (strcmp (path, newPath) != 0);
502
503   if (updated)
504     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);
505     
506   return updated;
507 }
508 #endif
509
510 GType
511 gtk_file_selection_get_type (void)
512 {
513   static GType file_selection_type = 0;
514
515   if (!file_selection_type)
516     {
517       static const GTypeInfo filesel_info =
518       {
519         sizeof (GtkFileSelectionClass),
520         NULL,           /* base_init */
521         NULL,           /* base_finalize */
522         (GClassInitFunc) gtk_file_selection_class_init,
523         NULL,           /* class_finalize */
524         NULL,           /* class_data */
525         sizeof (GtkFileSelection),
526         0,              /* n_preallocs */
527         (GInstanceInitFunc) gtk_file_selection_init,
528       };
529
530       file_selection_type =
531         g_type_register_static (GTK_TYPE_DIALOG, I_("GtkFileSelection"),
532                                 &filesel_info, 0);
533     }
534
535   return file_selection_type;
536 }
537
538 static void
539 gtk_file_selection_class_init (GtkFileSelectionClass *class)
540 {
541   GObjectClass *gobject_class;
542   GtkObjectClass *object_class;
543   GtkWidgetClass *widget_class;
544
545   gobject_class = (GObjectClass*) class;
546   object_class = (GtkObjectClass*) class;
547   widget_class = (GtkWidgetClass*) class;
548
549   parent_class = g_type_class_peek_parent (class);
550
551   gobject_class->finalize = gtk_file_selection_finalize;
552   gobject_class->set_property = gtk_file_selection_set_property;
553   gobject_class->get_property = gtk_file_selection_get_property;
554    
555   g_object_class_install_property (gobject_class,
556                                    PROP_FILENAME,
557                                    g_param_spec_string ("filename",
558                                                         P_("Filename"),
559                                                         P_("The currently selected filename"),
560                                                         NULL,
561                                                         GTK_PARAM_READWRITE));
562   g_object_class_install_property (gobject_class,
563                                    PROP_SHOW_FILEOPS,
564                                    g_param_spec_boolean ("show-fileops",
565                                                          P_("Show file operations"),
566                                                          P_("Whether buttons for creating/manipulating files should be displayed"),
567                                                          FALSE,
568                                                          GTK_PARAM_READWRITE));
569   g_object_class_install_property (gobject_class,
570                                    PROP_SELECT_MULTIPLE,
571                                    g_param_spec_boolean ("select-multiple",
572                                                          P_("Select Multiple"),
573                                                          P_("Whether to allow multiple files to be selected"),
574                                                          FALSE,
575                                                          GTK_PARAM_READWRITE));
576   object_class->destroy = gtk_file_selection_destroy;
577   widget_class->map = gtk_file_selection_map;
578 }
579
580 static void gtk_file_selection_set_property (GObject         *object,
581                                              guint            prop_id,
582                                              const GValue    *value,
583                                              GParamSpec      *pspec)
584 {
585   GtkFileSelection *filesel;
586
587   filesel = GTK_FILE_SELECTION (object);
588
589   switch (prop_id)
590     {
591     case PROP_FILENAME:
592       gtk_file_selection_set_filename (filesel,
593                                        g_value_get_string (value));
594       break;
595     case PROP_SHOW_FILEOPS:
596       if (g_value_get_boolean (value))
597          gtk_file_selection_show_fileop_buttons (filesel);
598       else
599          gtk_file_selection_hide_fileop_buttons (filesel);
600       break;
601     case PROP_SELECT_MULTIPLE:
602       gtk_file_selection_set_select_multiple (filesel, g_value_get_boolean (value));
603       break;
604     default:
605       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
606       break;
607     }
608 }
609
610 static void gtk_file_selection_get_property (GObject         *object,
611                                              guint            prop_id,
612                                              GValue          *value,
613                                              GParamSpec      *pspec)
614 {
615   GtkFileSelection *filesel;
616
617   filesel = GTK_FILE_SELECTION (object);
618
619   switch (prop_id)
620     {
621     case PROP_FILENAME:
622       g_value_set_string (value,
623                           gtk_file_selection_get_filename(filesel));
624       break;
625
626     case PROP_SHOW_FILEOPS:
627       /* This is a little bit hacky, but doing otherwise would require
628        * adding a field to the object.
629        */
630       g_value_set_boolean (value, (filesel->fileop_c_dir && 
631                                    filesel->fileop_del_file &&
632                                    filesel->fileop_ren_file));
633       break;
634     case PROP_SELECT_MULTIPLE:
635       g_value_set_boolean (value, gtk_file_selection_get_select_multiple (filesel));
636       break;
637     default:
638       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
639       break;
640     }
641 }
642
643 static gboolean
644 grab_default (GtkWidget *widget)
645 {
646   gtk_widget_grab_default (widget);
647   return FALSE;
648 }
649      
650 static void
651 gtk_file_selection_init (GtkFileSelection *filesel)
652 {
653   GtkWidget *entry_vbox;
654   GtkWidget *label;
655   GtkWidget *list_hbox, *list_container;
656   GtkWidget *pulldown_hbox;
657   GtkWidget *scrolled_win;
658   GtkWidget *eventbox;
659   GtkWidget *spacer;
660   GtkDialog *dialog;
661
662   GtkListStore *model;
663   GtkTreeViewColumn *column;
664   
665   gtk_widget_push_composite_child ();
666
667   dialog = GTK_DIALOG (filesel);
668
669   filesel->cmpl_state = cmpl_init_state ();
670
671   /* The dialog-sized vertical box  */
672   filesel->main_vbox = dialog->vbox;
673   gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
674
675   /* The horizontal box containing create, rename etc. buttons */
676   filesel->button_area = gtk_hbutton_box_new ();
677   gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel->button_area), GTK_BUTTONBOX_START);
678   gtk_box_set_spacing (GTK_BOX (filesel->button_area), 0);
679   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area, 
680                       FALSE, FALSE, 0);
681   gtk_widget_show (filesel->button_area);
682   
683   gtk_file_selection_show_fileop_buttons (filesel);
684
685   /* hbox for pulldown menu */
686   pulldown_hbox = gtk_hbox_new (TRUE, 5);
687   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
688   gtk_widget_show (pulldown_hbox);
689   
690   /* Pulldown menu */
691   filesel->history_pulldown = gtk_option_menu_new ();
692   gtk_widget_show (filesel->history_pulldown);
693   gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown, 
694                       FALSE, FALSE, 0);
695     
696   /*  The horizontal box containing the directory and file listboxes  */
697
698   spacer = gtk_hbox_new (FALSE, 0);
699   gtk_widget_set_size_request (spacer, -1, 5);
700   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), spacer, FALSE, FALSE, 0);
701   gtk_widget_show (spacer);
702   
703   list_hbox = gtk_hbox_new (FALSE, 5);
704   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
705   gtk_widget_show (list_hbox);
706   if (WANT_HPANED)
707     list_container = g_object_new (GTK_TYPE_HPANED,
708                                    "visible", TRUE,
709                                    "parent", list_hbox,
710                                    "border_width", 0,
711                                    NULL);
712   else
713     list_container = list_hbox;
714
715   spacer = gtk_hbox_new (FALSE, 0);
716   gtk_widget_set_size_request (spacer, -1, 5);
717   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), spacer, FALSE, FALSE, 0);  
718   gtk_widget_show (spacer);
719   
720   /* The directories list */
721
722   model = gtk_list_store_new (1, G_TYPE_STRING);
723   filesel->dir_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
724   g_object_unref (model);
725
726   column = gtk_tree_view_column_new_with_attributes (_("Folders"),
727                                                      gtk_cell_renderer_text_new (),
728                                                      "text", DIR_COLUMN,
729                                                      NULL);
730   label = gtk_label_new_with_mnemonic (_("Fol_ders"));
731   gtk_label_set_mnemonic_widget (GTK_LABEL (label), filesel->dir_list);
732   gtk_widget_show (label);
733   gtk_tree_view_column_set_widget (column, label);
734   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
735   gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->dir_list), column);
736
737   gtk_widget_set_size_request (filesel->dir_list,
738                                DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
739   g_signal_connect (filesel->dir_list, "row_activated",
740                     G_CALLBACK (gtk_file_selection_dir_activate), filesel);
741
742   /*  gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list)); */
743
744   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
745   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);  
746   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
747   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
748                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
749   gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0);
750   if (GTK_IS_PANED (list_container))
751     gtk_paned_pack1 (GTK_PANED (list_container), scrolled_win, TRUE, TRUE);
752   else
753     gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
754   gtk_widget_show (filesel->dir_list);
755   gtk_widget_show (scrolled_win);
756
757   /* The files list */
758   model = gtk_list_store_new (1, G_TYPE_STRING);
759   filesel->file_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
760   g_object_unref (model);
761
762   column = gtk_tree_view_column_new_with_attributes (_("Files"),
763                                                      gtk_cell_renderer_text_new (),
764                                                      "text", FILE_COLUMN,
765                                                      NULL);
766   label = gtk_label_new_with_mnemonic (_("_Files"));
767   gtk_label_set_mnemonic_widget (GTK_LABEL (label), filesel->file_list);
768   gtk_widget_show (label);
769   gtk_tree_view_column_set_widget (column, label);
770   gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
771   gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->file_list), column);
772
773   gtk_widget_set_size_request (filesel->file_list,
774                                FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
775   g_signal_connect (filesel->file_list, "row_activated",
776                     G_CALLBACK (gtk_file_selection_file_activate), filesel);
777   g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list)), "changed",
778                     G_CALLBACK (gtk_file_selection_file_changed), filesel);
779
780   /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list)); */
781
782   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
783   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);
784   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
785   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
786                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
787   gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0);
788   gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
789   gtk_widget_show (filesel->file_list);
790   gtk_widget_show (scrolled_win);
791
792   /* action area for packing buttons into. */
793   filesel->action_area = gtk_hbox_new (TRUE, 0);
794   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area, 
795                       FALSE, FALSE, 0);
796   gtk_widget_show (filesel->action_area);
797   
798   /*  The OK/Cancel button area */
799
800   /*  The Cancel button  */
801   filesel->cancel_button = gtk_dialog_add_button (dialog,
802                                                   GTK_STOCK_CANCEL,
803                                                   GTK_RESPONSE_CANCEL);
804   /*  The OK button  */
805   filesel->ok_button = gtk_dialog_add_button (dialog,
806                                               GTK_STOCK_OK,
807                                               GTK_RESPONSE_OK);
808
809   gtk_dialog_set_alternative_button_order (dialog,
810                                            GTK_RESPONSE_OK,
811                                            GTK_RESPONSE_CANCEL,
812                                            -1);
813
814   gtk_widget_grab_default (filesel->ok_button);
815
816   /*  The selection entry widget  */
817   entry_vbox = gtk_vbox_new (FALSE, 2);
818   gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2);
819   gtk_widget_show (entry_vbox);
820   
821   eventbox = gtk_event_box_new ();
822   filesel->selection_text = label = gtk_label_new ("");
823   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
824   gtk_container_add (GTK_CONTAINER (eventbox), label);
825   gtk_box_pack_start (GTK_BOX (entry_vbox), eventbox, FALSE, FALSE, 0);
826   gtk_widget_show (label);
827   gtk_widget_show (eventbox);
828
829   filesel->selection_entry = gtk_entry_new ();
830   g_signal_connect (filesel->selection_entry, "key_press_event",
831                     G_CALLBACK (gtk_file_selection_key_press), filesel);
832   g_signal_connect (filesel->selection_entry, "insert_text",
833                     G_CALLBACK (gtk_file_selection_insert_text), NULL);
834   g_signal_connect_swapped (filesel->selection_entry, "changed",
835                             G_CALLBACK (gtk_file_selection_update_fileops), filesel);
836   g_signal_connect_swapped (filesel->selection_entry, "focus_in_event",
837                             G_CALLBACK (grab_default),
838                             filesel->ok_button);
839   g_signal_connect_swapped (filesel->selection_entry, "activate",
840                             G_CALLBACK (gtk_button_clicked),
841                             filesel->ok_button);
842   
843   gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
844   gtk_widget_show (filesel->selection_entry);
845
846   gtk_label_set_mnemonic_widget (GTK_LABEL (filesel->selection_text),
847                                  filesel->selection_entry);
848
849   if (!cmpl_state_okay (filesel->cmpl_state))
850     {
851       gchar err_buf[256];
852
853       g_snprintf (err_buf, sizeof (err_buf), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
854
855       gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
856     }
857   else
858     {
859       gtk_file_selection_populate (filesel, "", FALSE, TRUE);
860     }
861
862   gtk_widget_grab_focus (filesel->selection_entry);
863
864   gtk_widget_pop_composite_child ();
865 }
866
867 static gchar *
868 uri_list_extract_first_uri (const gchar* uri_list)
869 {
870   const gchar *p, *q;
871   
872   g_return_val_if_fail (uri_list != NULL, NULL);
873   
874   p = uri_list;
875   /* We don't actually try to validate the URI according to RFC
876    * 2396, or even check for allowed characters - we just ignore
877    * comments and trim whitespace off the ends.  We also
878    * allow LF delimination as well as the specified CRLF.
879    *
880    * We do allow comments like specified in RFC 2483.
881    */
882   while (p)
883     {
884       if (*p != '#')
885         {
886           while (g_ascii_isspace(*p))
887             p++;
888           
889           q = p;
890           while (*q && (*q != '\n') && (*q != '\r'))
891             q++;
892           
893           if (q > p)
894             {
895               q--;
896               while (q > p && g_ascii_isspace (*q))
897                 q--;
898
899               if (q > p)
900                 return g_strndup (p, q - p + 1);
901             }
902         }
903       p = strchr (p, '\n');
904       if (p)
905         p++;
906     }
907   return NULL;
908 }
909
910 static void
911 dnd_really_drop  (GtkWidget *dialog, gint response_id, GtkFileSelection *fs)
912 {
913   gchar *filename;
914   
915   if (response_id == GTK_RESPONSE_YES)
916     {
917       filename = g_object_get_data (G_OBJECT (dialog), "gtk-fs-dnd-filename");
918
919       gtk_file_selection_set_filename (fs, filename);
920     }
921   
922   gtk_widget_destroy (dialog);
923 }
924
925
926 static void
927 filenames_dropped (GtkWidget        *widget,
928                    GdkDragContext   *context,
929                    gint              x,
930                    gint              y,
931                    GtkSelectionData *selection_data,
932                    guint             info,
933                    guint             time)
934 {
935   char *uri = NULL;
936   char *filename = NULL;
937   char *hostname;
938   const char *this_hostname;
939   GError *error = NULL;
940         
941   if (!selection_data->data)
942     return;
943
944   uri = uri_list_extract_first_uri ((char *)selection_data->data);
945   
946   if (!uri)
947     return;
948
949   filename = g_filename_from_uri (uri, &hostname, &error);
950   g_free (uri);
951   
952   if (!filename)
953     {
954       g_warning ("Error getting dropped filename: %s\n",
955                  error->message);
956       g_error_free (error);
957       return;
958     }
959
960   this_hostname = g_get_host_name ();
961   
962   if ((hostname == NULL) ||
963       (strcmp (hostname, this_hostname) == 0) ||
964       (strcmp (hostname, "localhost") == 0))
965     gtk_file_selection_set_filename (GTK_FILE_SELECTION (widget),
966                                      filename);
967   else
968     {
969       GtkWidget *dialog;
970       gchar *filename_utf8;
971
972       /* Conversion back to UTF-8 should always succeed for the result
973        * of g_filename_from_uri()
974        */
975       filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
976       g_assert (filename_utf8);
977       
978       dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
979                                        GTK_DIALOG_DESTROY_WITH_PARENT,
980                                        GTK_MESSAGE_QUESTION,
981                                        GTK_BUTTONS_YES_NO,
982                                        _("The file \"%s\" resides on another machine (called %s) and may not be available to this program.\n"
983                                          "Are you sure that you want to select it?"), filename_utf8, hostname);
984       g_free (filename_utf8);
985
986       g_object_set_data_full (G_OBJECT (dialog), I_("gtk-fs-dnd-filename"), g_strdup (filename), g_free);
987       
988       g_signal_connect_data (dialog, "response",
989                              (GCallback) dnd_really_drop, 
990                              widget, NULL, 0);
991       
992       gtk_widget_show (dialog);
993     }
994
995   g_free (hostname);
996   g_free (filename);
997 }
998
999 enum
1000 {
1001   TARGET_URILIST,
1002   TARGET_UTF8_STRING,
1003   TARGET_STRING,
1004   TARGET_TEXT,
1005   TARGET_COMPOUND_TEXT
1006 };
1007
1008
1009 static void
1010 filenames_drag_get (GtkWidget        *widget,
1011                     GdkDragContext   *context,
1012                     GtkSelectionData *selection_data,
1013                     guint             info,
1014                     guint             time,
1015                     GtkFileSelection *filesel)
1016 {
1017   const gchar *file;
1018   gchar *uri_list;
1019   const char *hostname;
1020   GError *error;
1021
1022   file = gtk_file_selection_get_filename (filesel);
1023
1024   if (file)
1025     {
1026       if (info == TARGET_URILIST)
1027         {
1028           hostname = g_get_host_name ();
1029           
1030           error = NULL;
1031           uri_list = g_filename_to_uri (file, hostname, &error);
1032           if (!uri_list)
1033             {
1034               g_warning ("Error getting filename: %s\n",
1035                          error->message);
1036               g_error_free (error);
1037               return;
1038             }
1039           
1040           gtk_selection_data_set (selection_data,
1041                                   selection_data->target, 8,
1042                                   (void *)uri_list, strlen((char *)uri_list));
1043           g_free (uri_list);
1044         }
1045       else
1046         {
1047           gchar *filename_utf8 = g_filename_to_utf8 (file, -1, NULL, NULL, NULL);
1048           g_assert (filename_utf8);
1049           gtk_selection_data_set_text (selection_data, filename_utf8, -1);
1050           g_free (filename_utf8);
1051         }
1052     }
1053 }
1054
1055 static void
1056 file_selection_setup_dnd (GtkFileSelection *filesel)
1057 {
1058   GtkWidget *eventbox;
1059   static const GtkTargetEntry drop_types[] = {
1060     { "text/uri-list", 0, TARGET_URILIST}
1061   };
1062   static const gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]);
1063   static const GtkTargetEntry drag_types[] = {
1064     { "text/uri-list", 0, TARGET_URILIST},
1065     { "UTF8_STRING", 0, TARGET_UTF8_STRING },
1066     { "STRING", 0, 0 },
1067     { "TEXT",   0, 0 }, 
1068     { "COMPOUND_TEXT", 0, 0 }
1069   };
1070   static const gint n_drag_types = sizeof(drag_types)/sizeof(drag_types[0]);
1071
1072   gtk_drag_dest_set (GTK_WIDGET (filesel),
1073                      GTK_DEST_DEFAULT_ALL,
1074                      drop_types, n_drop_types,
1075                      GDK_ACTION_COPY);
1076
1077   g_signal_connect (filesel, "drag_data_received",
1078                     G_CALLBACK (filenames_dropped), NULL);
1079
1080   eventbox = gtk_widget_get_parent (filesel->selection_text);
1081   gtk_drag_source_set (eventbox,
1082                        GDK_BUTTON1_MASK,
1083                        drag_types, n_drag_types,
1084                        GDK_ACTION_COPY);
1085
1086   g_signal_connect (eventbox, "drag_data_get",
1087                     G_CALLBACK (filenames_drag_get), filesel);
1088 }
1089
1090 GtkWidget*
1091 gtk_file_selection_new (const gchar *title)
1092 {
1093   GtkFileSelection *filesel;
1094
1095   filesel = g_object_new (GTK_TYPE_FILE_SELECTION, NULL);
1096   gtk_window_set_title (GTK_WINDOW (filesel), title);
1097   gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE);
1098
1099   file_selection_setup_dnd (filesel);
1100   
1101   return GTK_WIDGET (filesel);
1102 }
1103
1104 void
1105 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
1106 {
1107   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1108     
1109   /* delete, create directory, and rename */
1110   if (!filesel->fileop_c_dir) 
1111     {
1112       filesel->fileop_c_dir = gtk_button_new_with_mnemonic (_("_New Folder"));
1113       g_signal_connect (filesel->fileop_c_dir, "clicked",
1114                         G_CALLBACK (gtk_file_selection_create_dir),
1115                         filesel);
1116       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
1117                           filesel->fileop_c_dir, TRUE, TRUE, 0);
1118       gtk_widget_show (filesel->fileop_c_dir);
1119     }
1120         
1121   if (!filesel->fileop_del_file) 
1122     {
1123       filesel->fileop_del_file = gtk_button_new_with_mnemonic (_("De_lete File"));
1124       g_signal_connect (filesel->fileop_del_file, "clicked",
1125                         G_CALLBACK (gtk_file_selection_delete_file),
1126                         filesel);
1127       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
1128                           filesel->fileop_del_file, TRUE, TRUE, 0);
1129       gtk_widget_show (filesel->fileop_del_file);
1130     }
1131
1132   if (!filesel->fileop_ren_file)
1133     {
1134       filesel->fileop_ren_file = gtk_button_new_with_mnemonic (_("_Rename File"));
1135       g_signal_connect (filesel->fileop_ren_file, "clicked",
1136                         G_CALLBACK (gtk_file_selection_rename_file),
1137                         filesel);
1138       gtk_box_pack_start (GTK_BOX (filesel->button_area), 
1139                           filesel->fileop_ren_file, TRUE, TRUE, 0);
1140       gtk_widget_show (filesel->fileop_ren_file);
1141     }
1142   
1143   gtk_file_selection_update_fileops (filesel);
1144   
1145   g_object_notify (G_OBJECT (filesel), "show_fileops");
1146 }
1147
1148 void       
1149 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
1150 {
1151   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1152     
1153   if (filesel->fileop_ren_file)
1154     {
1155       gtk_widget_destroy (filesel->fileop_ren_file);
1156       filesel->fileop_ren_file = NULL;
1157     }
1158
1159   if (filesel->fileop_del_file)
1160     {
1161       gtk_widget_destroy (filesel->fileop_del_file);
1162       filesel->fileop_del_file = NULL;
1163     }
1164
1165   if (filesel->fileop_c_dir)
1166     {
1167       gtk_widget_destroy (filesel->fileop_c_dir);
1168       filesel->fileop_c_dir = NULL;
1169     }
1170   g_object_notify (G_OBJECT (filesel), "show_fileops");
1171 }
1172
1173
1174
1175 /**
1176  * gtk_file_selection_set_filename:
1177  * @filesel: a #GtkFileSelection.
1178  * @filename:  a string to set as the default file name.
1179  * 
1180  * Sets a default path for the file requestor. If @filename includes a
1181  * directory path, then the requestor will open with that path as its
1182  * current working directory.
1183  *
1184  * This has the consequence that in order to open the requestor with a 
1185  * working directory and an empty filename, @filename must have a trailing
1186  * directory separator.
1187  *
1188  * The encoding of @filename is preferred GLib file name encoding, which
1189  * may not be UTF-8. See g_filename_from_utf8().
1190  **/
1191 void
1192 gtk_file_selection_set_filename (GtkFileSelection *filesel,
1193                                  const gchar      *filename)
1194 {
1195   gchar *buf;
1196   const char *name, *last_slash;
1197   char *filename_utf8;
1198
1199   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1200   g_return_if_fail (filename != NULL);
1201
1202   filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
1203   g_return_if_fail (filename_utf8 != NULL);
1204
1205   last_slash = strrchr (filename_utf8, G_DIR_SEPARATOR);
1206
1207   if (!last_slash)
1208     {
1209       buf = g_strdup ("");
1210       name = filename_utf8;
1211     }
1212   else
1213     {
1214       buf = g_strdup (filename_utf8);
1215       buf[last_slash - filename_utf8 + 1] = 0;
1216       name = last_slash + 1;
1217     }
1218
1219   gtk_file_selection_populate (filesel, buf, FALSE, TRUE);
1220
1221   if (filesel->selection_entry)
1222     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
1223   g_free (buf);
1224   g_object_notify (G_OBJECT (filesel), "filename");
1225
1226   g_free (filename_utf8);
1227 }
1228
1229 /**
1230  * gtk_file_selection_get_filename:
1231  * @filesel: a #GtkFileSelection
1232  * 
1233  * This function returns the selected filename in the GLib file name
1234  * encoding. To convert to UTF-8, call g_filename_to_utf8(). The
1235  * returned string points to a statically allocated buffer and should
1236  * be copied if you plan to keep it around.
1237  *
1238  * If no file is selected then the selected directory path is returned.
1239  * 
1240  * Return value: currently-selected filename in the on-disk encoding.
1241  **/
1242 G_CONST_RETURN gchar*
1243 gtk_file_selection_get_filename (GtkFileSelection *filesel)
1244 {
1245   static const gchar nothing[2] = "";
1246   static gchar something[MAXPATHLEN*2+1];
1247   char *sys_filename;
1248   const char *text;
1249
1250   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
1251
1252 #ifdef G_WITH_CYGWIN
1253   translate_win32_path (filesel);
1254 #endif
1255   text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
1256   if (text)
1257     {
1258       gchar *fullname = cmpl_completion_fullname (text, filesel->cmpl_state);
1259       sys_filename = g_filename_from_utf8 (fullname, -1, NULL, NULL, NULL);
1260       g_free (fullname);
1261       if (!sys_filename)
1262         return nothing;
1263       strncpy (something, sys_filename, sizeof (something) - 1);
1264       something[sizeof (something) - 1] = '\0';
1265       g_free (sys_filename);
1266       return something;
1267     }
1268
1269   return nothing;
1270 }
1271
1272 void
1273 gtk_file_selection_complete (GtkFileSelection *filesel,
1274                              const gchar      *pattern)
1275 {
1276   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1277   g_return_if_fail (pattern != NULL);
1278
1279   if (filesel->selection_entry)
1280     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
1281   gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE, TRUE);
1282 }
1283
1284 static void
1285 gtk_file_selection_destroy (GtkObject *object)
1286 {
1287   GtkFileSelection *filesel;
1288   GList *list;
1289   HistoryCallbackArg *callback_arg;
1290   
1291   g_return_if_fail (GTK_IS_FILE_SELECTION (object));
1292   
1293   filesel = GTK_FILE_SELECTION (object);
1294   
1295   if (filesel->fileop_dialog)
1296     {
1297       gtk_widget_destroy (filesel->fileop_dialog);
1298       filesel->fileop_dialog = NULL;
1299     }
1300   
1301   if (filesel->history_list)
1302     {
1303       list = filesel->history_list;
1304       while (list)
1305         {
1306           callback_arg = list->data;
1307           g_free (callback_arg->directory);
1308           g_free (callback_arg);
1309           list = list->next;
1310         }
1311       g_list_free (filesel->history_list);
1312       filesel->history_list = NULL;
1313     }
1314
1315   if (filesel->cmpl_state)
1316     {
1317       cmpl_free_state (filesel->cmpl_state);
1318       filesel->cmpl_state = NULL;
1319     }
1320  
1321   if (filesel->selected_names)
1322     {
1323       free_selected_names (filesel->selected_names);
1324       filesel->selected_names = NULL;
1325     } 
1326
1327   if (filesel->last_selected)
1328     {
1329       g_free (filesel->last_selected);
1330       filesel->last_selected = NULL;
1331     }
1332
1333   GTK_OBJECT_CLASS (parent_class)->destroy (object);
1334 }
1335
1336 static void
1337 gtk_file_selection_map (GtkWidget *widget)
1338 {
1339   GtkFileSelection *filesel = GTK_FILE_SELECTION (widget);
1340
1341   /* Refresh the contents */
1342   gtk_file_selection_populate (filesel, "", FALSE, FALSE);
1343   
1344   GTK_WIDGET_CLASS (parent_class)->map (widget);
1345 }
1346
1347 static void
1348 gtk_file_selection_finalize (GObject *object)
1349 {
1350   GtkFileSelection *filesel = GTK_FILE_SELECTION (object);
1351
1352   g_free (filesel->fileop_file);
1353
1354   G_OBJECT_CLASS (parent_class)->finalize (object);
1355 }
1356
1357 /* Begin file operations callbacks */
1358
1359 static void
1360 gtk_file_selection_fileop_error (GtkFileSelection *fs,
1361                                  gchar            *error_message)
1362 {
1363   GtkWidget *dialog;
1364     
1365   g_return_if_fail (error_message != NULL);
1366
1367   /* main dialog */
1368   dialog = gtk_message_dialog_new (GTK_WINDOW (fs),
1369                                    GTK_DIALOG_DESTROY_WITH_PARENT,
1370                                    GTK_MESSAGE_ERROR,
1371                                    GTK_BUTTONS_OK,
1372                                    "%s", error_message);
1373
1374   /* yes, we free it */
1375   g_free (error_message);
1376
1377   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1378  
1379   g_signal_connect_swapped (dialog, "response",
1380                             G_CALLBACK (gtk_widget_destroy),
1381                             dialog);
1382
1383   gtk_widget_show (dialog);
1384 }
1385
1386 static void
1387 gtk_file_selection_fileop_destroy (GtkWidget *widget,
1388                                    gpointer   data)
1389 {
1390   GtkFileSelection *fs = data;
1391
1392   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1393   
1394   fs->fileop_dialog = NULL;
1395 }
1396
1397 static gboolean
1398 entry_is_empty (GtkEntry *entry)
1399 {
1400   const gchar *text = gtk_entry_get_text (entry);
1401   
1402   return *text == '\0';
1403 }
1404
1405 static void
1406 gtk_file_selection_fileop_entry_changed (GtkEntry   *entry,
1407                                          GtkWidget  *button)
1408 {
1409   gtk_widget_set_sensitive (button, !entry_is_empty (entry));
1410 }
1411
1412 static void
1413 gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
1414                                          gpointer   data)
1415 {
1416   GtkFileSelection *fs = data;
1417   const gchar *dirname;
1418   gchar *path;
1419   gchar *full_path;
1420   gchar *sys_full_path;
1421   gchar *buf;
1422   GError *error = NULL;
1423   CompletionState *cmpl_state;
1424   
1425   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1426
1427   dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1428   cmpl_state = (CompletionState*) fs->cmpl_state;
1429   path = cmpl_reference_position (cmpl_state);
1430   
1431   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
1432   sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1433   if (error)
1434     {
1435       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1436         buf = g_strdup_printf (_("The folder name \"%s\" contains symbols that are not allowed in filenames"), dirname);
1437       else
1438         buf = g_strdup_printf (_("Error creating folder \"%s\": %s\n%s"), dirname, error->message,
1439                                _("You probably used symbols not allowed in filenames."));
1440       gtk_file_selection_fileop_error (fs, buf);
1441       g_error_free (error);
1442       goto out;
1443     }
1444
1445   if (g_mkdir (sys_full_path, 0777) < 0)
1446     {
1447       buf = g_strdup_printf (_("Error creating folder \"%s\": %s\n"), dirname,
1448                              g_strerror (errno));
1449       gtk_file_selection_fileop_error (fs, buf);
1450     }
1451
1452  out:
1453   g_free (full_path);
1454   g_free (sys_full_path);
1455   
1456   gtk_widget_destroy (fs->fileop_dialog);
1457   gtk_file_selection_populate (fs, "", FALSE, FALSE);
1458 }
1459   
1460 static void
1461 gtk_file_selection_create_dir (GtkWidget *widget,
1462                                gpointer   data)
1463 {
1464   GtkFileSelection *fs = data;
1465   GtkWidget *label;
1466   GtkWidget *dialog;
1467   GtkWidget *vbox;
1468   GtkWidget *button;
1469
1470   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1471
1472   if (fs->fileop_dialog)
1473     return;
1474   
1475   /* main dialog */
1476   dialog = gtk_dialog_new ();
1477   fs->fileop_dialog = dialog;
1478   g_signal_connect (dialog, "destroy",
1479                     G_CALLBACK (gtk_file_selection_fileop_destroy),
1480                     fs);
1481   gtk_window_set_title (GTK_WINDOW (dialog), _("New Folder"));
1482   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1483   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1484
1485   /* If file dialog is grabbed, grab option dialog */
1486   /* When option dialog is closed, file dialog will be grabbed again */
1487   if (GTK_WINDOW (fs)->modal)
1488       gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1489
1490   vbox = gtk_vbox_new (FALSE, 0);
1491   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1492   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1493                      FALSE, FALSE, 0);
1494   gtk_widget_show( vbox);
1495   
1496   label = gtk_label_new_with_mnemonic (_("_Folder name:"));
1497   gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
1498   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1499   gtk_widget_show (label);
1500
1501   /*  The directory entry widget  */
1502   fs->fileop_entry = gtk_entry_new ();
1503   gtk_label_set_mnemonic_widget (GTK_LABEL (label), fs->fileop_entry);
1504   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
1505                       TRUE, TRUE, 5);
1506   GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1507   gtk_entry_set_activates_default (GTK_ENTRY (fs->fileop_entry), TRUE); 
1508   gtk_widget_show (fs->fileop_entry);
1509   
1510   /* buttons */
1511   button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
1512                                   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1513   g_signal_connect_swapped (button, "clicked",
1514                             G_CALLBACK (gtk_widget_destroy),
1515                             dialog);
1516
1517   gtk_widget_grab_focus (fs->fileop_entry);
1518
1519   button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
1520                                   _("C_reate"), GTK_RESPONSE_OK);
1521   gtk_widget_set_sensitive (button, FALSE);
1522   g_signal_connect (button, "clicked",
1523                     G_CALLBACK (gtk_file_selection_create_dir_confirmed),
1524                     fs);
1525   g_signal_connect (fs->fileop_entry, "changed",
1526                     G_CALLBACK (gtk_file_selection_fileop_entry_changed),
1527                     button);
1528
1529   gtk_widget_grab_default (button);
1530   
1531   gtk_widget_show (dialog);
1532 }
1533
1534 static void
1535 gtk_file_selection_delete_file_response (GtkDialog *dialog, 
1536                                          gint       response_id,
1537                                          gpointer   data)
1538 {
1539   GtkFileSelection *fs = data;
1540   CompletionState *cmpl_state;
1541   gchar *path;
1542   gchar *full_path;
1543   gchar *sys_full_path;
1544   GError *error = NULL;
1545   gchar *buf;
1546   
1547   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1548
1549   if (response_id != GTK_RESPONSE_OK)
1550     {
1551       gtk_widget_destroy (GTK_WIDGET (dialog));
1552       return;
1553     }
1554
1555   cmpl_state = (CompletionState*) fs->cmpl_state;
1556   path = cmpl_reference_position (cmpl_state);
1557   
1558   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1559   sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1560   if (error)
1561     {
1562       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1563         buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
1564                                fs->fileop_file);
1565       else
1566         buf = g_strdup_printf (_("Error deleting file \"%s\": %s\n%s"),
1567                                fs->fileop_file, error->message,
1568                                _("It probably contains symbols not allowed in filenames."));
1569       
1570       gtk_file_selection_fileop_error (fs, buf);
1571       g_error_free (error);
1572       goto out;
1573     }
1574
1575   if (g_unlink (sys_full_path) < 0) 
1576     {
1577       buf = g_strdup_printf (_("Error deleting file \"%s\": %s"),
1578                              fs->fileop_file, g_strerror (errno));
1579       gtk_file_selection_fileop_error (fs, buf);
1580     }
1581   
1582  out:
1583   g_free (full_path);
1584   g_free (sys_full_path);
1585   
1586   gtk_widget_destroy (fs->fileop_dialog);
1587   gtk_file_selection_populate (fs, "", FALSE, TRUE);
1588 }
1589
1590 static void
1591 gtk_file_selection_delete_file (GtkWidget *widget,
1592                                 gpointer   data)
1593 {
1594   GtkFileSelection *fs = data;
1595   GtkWidget *dialog;
1596   const gchar *filename;
1597   
1598   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1599
1600   if (fs->fileop_dialog)
1601     return;
1602
1603 #ifdef G_WITH_CYGWIN
1604   translate_win32_path (fs);
1605 #endif
1606
1607   filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1608   if (strlen (filename) < 1)
1609     return;
1610
1611   g_free (fs->fileop_file);
1612   fs->fileop_file = g_strdup (filename);
1613   
1614   /* main dialog */
1615   fs->fileop_dialog = dialog = 
1616     gtk_message_dialog_new (GTK_WINDOW (fs),
1617                             GTK_WINDOW (fs)->modal ? GTK_DIALOG_MODAL : 0,
1618                             GTK_MESSAGE_QUESTION,
1619                             GTK_BUTTONS_NONE,
1620                             _("Really delete file \"%s\"?"), filename);
1621
1622   g_signal_connect (dialog, "destroy",
1623                     G_CALLBACK (gtk_file_selection_fileop_destroy),
1624                     fs);
1625   gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1626   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1627   
1628   /* buttons */
1629   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1630                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1631                           GTK_STOCK_DELETE, GTK_RESPONSE_OK,
1632                           NULL);
1633
1634   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
1635
1636   g_signal_connect (dialog, "response",
1637                     G_CALLBACK (gtk_file_selection_delete_file_response),
1638                     fs);
1639   
1640   gtk_widget_show (dialog);
1641 }
1642
1643 static void
1644 gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
1645                                           gpointer   data)
1646 {
1647   GtkFileSelection *fs = data;
1648   gchar *buf;
1649   const gchar *file;
1650   gchar *path;
1651   gchar *new_filename;
1652   gchar *old_filename;
1653   gchar *sys_new_filename;
1654   gchar *sys_old_filename;
1655   CompletionState *cmpl_state;
1656   GError *error = NULL;
1657   
1658   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1659
1660   file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1661   cmpl_state = (CompletionState*) fs->cmpl_state;
1662   path = cmpl_reference_position (cmpl_state);
1663   
1664   new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
1665   old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1666
1667   sys_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, &error);
1668   if (error)
1669     {
1670       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1671         buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), new_filename);
1672       else
1673         buf = g_strdup_printf (_("Error renaming file to \"%s\": %s\n%s"),
1674                                new_filename, error->message,
1675                                _("You probably used symbols not allowed in filenames."));
1676       gtk_file_selection_fileop_error (fs, buf);
1677       g_error_free (error);
1678       goto out1;
1679     }
1680
1681   sys_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, &error);
1682   if (error)
1683     {
1684       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1685         buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), old_filename);
1686       else
1687         buf = g_strdup_printf (_("Error renaming file \"%s\": %s\n%s"),
1688                                old_filename, error->message,
1689                                _("It probably contains symbols not allowed in filenames."));
1690       gtk_file_selection_fileop_error (fs, buf);
1691       g_error_free (error);
1692       goto out2;
1693     }
1694   
1695   if (g_rename (sys_old_filename, sys_new_filename) < 0) 
1696     {
1697       buf = g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
1698                              sys_old_filename, sys_new_filename,
1699                              g_strerror (errno));
1700       gtk_file_selection_fileop_error (fs, buf);
1701       goto out2;
1702     }
1703   
1704   gtk_file_selection_populate (fs, "", FALSE, FALSE);
1705   gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), file);
1706   
1707  out2:
1708   g_free (sys_old_filename);
1709
1710  out1:
1711   g_free (new_filename);
1712   g_free (old_filename);
1713   g_free (sys_new_filename);
1714   
1715   gtk_widget_destroy (fs->fileop_dialog);
1716 }
1717   
1718 static void
1719 gtk_file_selection_rename_file (GtkWidget *widget,
1720                                 gpointer   data)
1721 {
1722   GtkFileSelection *fs = data;
1723   GtkWidget *label;
1724   GtkWidget *dialog;
1725   GtkWidget *vbox;
1726   GtkWidget *button;
1727   gchar *buf;
1728   
1729   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1730
1731   if (fs->fileop_dialog)
1732           return;
1733
1734   g_free (fs->fileop_file);
1735   fs->fileop_file = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1736   if (strlen (fs->fileop_file) < 1)
1737     return;
1738   
1739   /* main dialog */
1740   fs->fileop_dialog = dialog = gtk_dialog_new ();
1741   g_signal_connect (dialog, "destroy",
1742                     G_CALLBACK (gtk_file_selection_fileop_destroy),
1743                     fs);
1744   gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
1745   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1746   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1747
1748   /* If file dialog is grabbed, grab option dialog */
1749   /* When option dialog  closed, file dialog will be grabbed again */
1750   if (GTK_WINDOW (fs)->modal)
1751     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1752   
1753   vbox = gtk_vbox_new (FALSE, 0);
1754   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1755   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1756                       FALSE, FALSE, 0);
1757   gtk_widget_show(vbox);
1758   
1759   buf = g_strdup_printf (_("Rename file \"%s\" to:"), fs->fileop_file);
1760   label = gtk_label_new (buf);
1761   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1762   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1763   gtk_widget_show (label);
1764   g_free (buf);
1765
1766   /* New filename entry */
1767   fs->fileop_entry = gtk_entry_new ();
1768   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
1769                       TRUE, TRUE, 5);
1770   GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1771   gtk_entry_set_activates_default (GTK_ENTRY (fs->fileop_entry), TRUE); 
1772   gtk_widget_show (fs->fileop_entry);
1773   
1774   gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
1775   gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
1776                               0, strlen (fs->fileop_file));
1777
1778   /* buttons */
1779   button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
1780                                   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1781   g_signal_connect_swapped (button, "clicked",
1782                             G_CALLBACK (gtk_widget_destroy),
1783                             dialog);
1784
1785   gtk_widget_grab_focus (fs->fileop_entry);
1786
1787   button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
1788                                   _("_Rename"), GTK_RESPONSE_OK);
1789   g_signal_connect (button, "clicked",
1790                     G_CALLBACK (gtk_file_selection_rename_file_confirmed),
1791                     fs);
1792   g_signal_connect (fs->fileop_entry, "changed",
1793                     G_CALLBACK (gtk_file_selection_fileop_entry_changed),
1794                     button);
1795
1796   gtk_widget_grab_default (button);
1797   
1798   gtk_widget_show (dialog);
1799 }
1800
1801 static gint
1802 gtk_file_selection_insert_text (GtkWidget   *widget,
1803                                 const gchar *new_text,
1804                                 gint         new_text_length,
1805                                 gint        *position,
1806                                 gpointer     user_data)
1807 {
1808   gchar *filename;
1809
1810   filename = g_filename_from_utf8 (new_text, new_text_length, NULL, NULL, NULL);
1811
1812   if (!filename)
1813     {
1814       gdk_display_beep (gtk_widget_get_display (widget));
1815       g_signal_stop_emission_by_name (widget, "insert_text");
1816       return FALSE;
1817     }
1818   
1819   g_free (filename);
1820   
1821   return TRUE;
1822 }
1823
1824 static void
1825 gtk_file_selection_update_fileops (GtkFileSelection *fs)
1826 {
1827   gboolean sensitive;
1828
1829   if (!fs->selection_entry)
1830     return;
1831
1832   sensitive = !entry_is_empty (GTK_ENTRY (fs->selection_entry));
1833
1834   if (fs->fileop_del_file)
1835     gtk_widget_set_sensitive (fs->fileop_del_file, sensitive);
1836   
1837   if (fs->fileop_ren_file)
1838     gtk_widget_set_sensitive (fs->fileop_ren_file, sensitive);
1839 }
1840
1841 static gint
1842 gtk_file_selection_key_press (GtkWidget   *widget,
1843                               GdkEventKey *event,
1844                               gpointer     user_data)
1845 {
1846   GtkFileSelection *fs;
1847   char *text;
1848
1849   g_return_val_if_fail (widget != NULL, FALSE);
1850   g_return_val_if_fail (event != NULL, FALSE);
1851
1852   if ((event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab) &&
1853       (event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
1854     {
1855       fs = GTK_FILE_SELECTION (user_data);
1856 #ifdef G_WITH_CYGWIN
1857       translate_win32_path (fs);
1858 #endif
1859       text = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1860
1861       gtk_file_selection_populate (fs, text, TRUE, TRUE);
1862
1863       g_free (text);
1864
1865       return TRUE;
1866     }
1867
1868   return FALSE;
1869 }
1870
1871 static void
1872 gtk_file_selection_history_callback (GtkWidget *widget,
1873                                      gpointer   data)
1874 {
1875   GtkFileSelection *fs = data;
1876   HistoryCallbackArg *callback_arg;
1877   GList *list;
1878
1879   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1880
1881   list = fs->history_list;
1882   
1883   while (list) {
1884     callback_arg = list->data;
1885     
1886     if (callback_arg->menu_item == widget)
1887       {
1888         gtk_file_selection_populate (fs, callback_arg->directory, FALSE, FALSE);
1889         break;
1890       }
1891     
1892     list = list->next;
1893   }
1894 }
1895
1896 static void 
1897 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1898                                         gchar            *current_directory)
1899 {
1900   HistoryCallbackArg *callback_arg;
1901   GtkWidget *menu_item;
1902   GList *list;
1903   gchar *current_dir;
1904   gint dir_len;
1905   gint i;
1906   
1907   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1908   g_return_if_fail (current_directory != NULL);
1909   
1910   list = fs->history_list;
1911
1912   if (fs->history_menu) 
1913     {
1914       while (list) {
1915         callback_arg = list->data;
1916         g_free (callback_arg->directory);
1917         g_free (callback_arg);
1918         list = list->next;
1919       }
1920       g_list_free (fs->history_list);
1921       fs->history_list = NULL;
1922       
1923       gtk_widget_destroy (fs->history_menu);
1924     }
1925   
1926   fs->history_menu = gtk_menu_new ();
1927
1928   current_dir = g_strdup (current_directory);
1929
1930   dir_len = strlen (current_dir);
1931
1932   for (i = dir_len; i >= 0; i--)
1933     {
1934       /* the i == dir_len is to catch the full path for the first 
1935        * entry. */
1936       if ( (current_dir[i] == G_DIR_SEPARATOR) || (i == dir_len))
1937         {
1938           /* another small hack to catch the full path */
1939           if (i != dir_len) 
1940                   current_dir[i + 1] = '\0';
1941 #ifdef G_WITH_CYGWIN
1942           if (!strcmp (current_dir, "//"))
1943             continue;
1944 #endif
1945           menu_item = gtk_menu_item_new_with_label (current_dir);
1946           
1947           callback_arg = g_new (HistoryCallbackArg, 1);
1948           callback_arg->menu_item = menu_item;
1949           
1950           /* since the autocompletion gets confused if you don't 
1951            * supply a trailing '/' on a dir entry, set the full
1952            * (current) path to "" which just refreshes the filesel */
1953           if (dir_len == i)
1954             {
1955               callback_arg->directory = g_strdup ("");
1956             }
1957           else
1958             {
1959               callback_arg->directory = g_strdup (current_dir);
1960             }
1961           
1962           fs->history_list = g_list_append (fs->history_list, callback_arg);
1963           
1964           g_signal_connect (menu_item, "activate",
1965                             G_CALLBACK (gtk_file_selection_history_callback),
1966                             fs);
1967           gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
1968           gtk_widget_show (menu_item);
1969         }
1970     }
1971
1972   gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown), 
1973                             fs->history_menu);
1974   g_free (current_dir);
1975 }
1976
1977 static gchar *
1978 get_real_filename (gchar    *filename,
1979                    gboolean  free_old)
1980 {
1981 #ifdef G_WITH_CYGWIN
1982   /* Check to see if the selection was a drive selector */
1983   if (isalpha (filename[0]) && (filename[1] == ':'))
1984     {
1985       gchar temp_filename[MAX_PATH];
1986       int len;
1987
1988       cygwin_conv_to_posix_path (filename, temp_filename);
1989
1990       /* we need trailing '/'. */
1991       len = strlen (temp_filename);
1992       if (len > 0 && temp_filename[len-1] != '/')
1993         {
1994           temp_filename[len]   = '/';
1995           temp_filename[len+1] = '\0';
1996         }
1997       
1998       if (free_old)
1999         g_free (filename);
2000
2001       return g_strdup (temp_filename);
2002     }
2003 #endif /* G_WITH_CYGWIN */
2004   return filename;
2005 }
2006
2007 static void
2008 gtk_file_selection_file_activate (GtkTreeView       *tree_view,
2009                                   GtkTreePath       *path,
2010                                   GtkTreeViewColumn *column,
2011                                   gpointer           user_data)
2012 {
2013   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
2014   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
2015   GtkTreeIter iter;  
2016   gchar *filename;
2017   
2018   gtk_tree_model_get_iter (model, &iter, path);
2019   gtk_tree_model_get (model, &iter, FILE_COLUMN, &filename, -1);
2020   filename = get_real_filename (filename, TRUE);
2021   gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
2022   gtk_button_clicked (GTK_BUTTON (fs->ok_button));
2023
2024   g_free (filename);
2025 }
2026
2027 static void
2028 gtk_file_selection_dir_activate (GtkTreeView       *tree_view,
2029                                  GtkTreePath       *path,
2030                                  GtkTreeViewColumn *column,
2031                                  gpointer           user_data)
2032 {
2033   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
2034   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
2035   GtkTreeIter iter;
2036   gchar *filename;
2037
2038   gtk_tree_model_get_iter (model, &iter, path);
2039   gtk_tree_model_get (model, &iter, DIR_COLUMN, &filename, -1);
2040   filename = get_real_filename (filename, TRUE);
2041   gtk_file_selection_populate (fs, filename, FALSE, FALSE);
2042   g_free (filename);
2043 }
2044
2045 #ifdef G_PLATFORM_WIN32
2046
2047 static void
2048 win32_gtk_add_drives_to_dir_list (GtkListStore *model)
2049 {
2050   gchar *textPtr;
2051   gchar buffer[128];
2052   char formatBuffer[128];
2053   GtkTreeIter iter;
2054
2055   /* Get the drives string */
2056   GetLogicalDriveStrings (sizeof (buffer), buffer);
2057
2058   /* Add the drives as necessary */
2059   textPtr = buffer;
2060   while (*textPtr != '\0')
2061     {
2062       /* Ignore floppies (?) */
2063       if (GetDriveType (textPtr) != DRIVE_REMOVABLE)
2064         {
2065           /* Build the actual displayable string */
2066           g_snprintf (formatBuffer, sizeof (formatBuffer), "%c:\\", toupper (textPtr[0]));
2067
2068           /* Add to the list */
2069           gtk_list_store_append (model, &iter);
2070           gtk_list_store_set (model, &iter, DIR_COLUMN, formatBuffer, -1);
2071         }
2072       textPtr += (strlen (textPtr) + 1);
2073     }
2074 }
2075 #endif
2076
2077 static gchar *
2078 escape_underscores (const gchar *str)
2079 {
2080   GString *result = g_string_new (NULL);
2081   while (*str)
2082     {
2083       if (*str == '_')
2084         g_string_append_c (result, '_');
2085
2086       g_string_append_c (result, *str);
2087       str++;
2088     }
2089
2090   return g_string_free (result, FALSE);
2091 }
2092
2093 static void
2094 gtk_file_selection_populate (GtkFileSelection *fs,
2095                              gchar            *rel_path,
2096                              gboolean          try_complete,
2097                              gboolean          reset_entry)
2098 {
2099   CompletionState *cmpl_state;
2100   PossibleCompletion* poss;
2101   GtkTreeIter iter;
2102   GtkListStore *dir_model;
2103   GtkListStore *file_model;
2104   gchar* filename;
2105   gchar* rem_path = rel_path;
2106   gchar* sel_text;
2107   gint did_recurse = FALSE;
2108   gint possible_count = 0;
2109   
2110   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
2111
2112   cmpl_state = (CompletionState*) fs->cmpl_state;
2113   poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
2114
2115   if (!cmpl_state_okay (cmpl_state))
2116     {
2117       /* Something went wrong. */
2118       gtk_file_selection_abort (fs);
2119       return;
2120     }
2121
2122   g_assert (cmpl_state->reference_dir);
2123
2124   dir_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->dir_list)));
2125   file_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->file_list)));
2126
2127   gtk_list_store_clear (dir_model);
2128   gtk_list_store_clear (file_model);
2129
2130   /* Set the dir list to include ./ and ../ */
2131   gtk_list_store_append (dir_model, &iter);
2132   gtk_list_store_set (dir_model, &iter, DIR_COLUMN, "." G_DIR_SEPARATOR_S, -1);
2133   gtk_list_store_append (dir_model, &iter);
2134   gtk_list_store_set (dir_model, &iter, DIR_COLUMN, ".." G_DIR_SEPARATOR_S, -1);
2135
2136   while (poss)
2137     {
2138       if (cmpl_is_a_completion (poss))
2139         {
2140           possible_count += 1;
2141
2142           filename = cmpl_this_completion (poss);
2143
2144           if (cmpl_is_directory (poss))
2145             {
2146               if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
2147                   strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
2148                 {
2149                   gtk_list_store_append (dir_model, &iter);
2150                   gtk_list_store_set (dir_model, &iter, DIR_COLUMN, filename, -1);
2151                 }
2152             }
2153           else
2154             {
2155               gtk_list_store_append (file_model, &iter);
2156               gtk_list_store_set (file_model, &iter, DIR_COLUMN, filename, -1);
2157             }
2158         }
2159
2160       poss = cmpl_next_completion (cmpl_state);
2161     }
2162
2163 #ifdef G_PLATFORM_WIN32
2164   /* For Windows, add drives as potential selections */
2165   win32_gtk_add_drives_to_dir_list (dir_model);
2166 #endif
2167
2168   /* File lists are set. */
2169
2170   g_assert (cmpl_state->reference_dir);
2171
2172   if (try_complete)
2173     {
2174
2175       /* User is trying to complete filenames, so advance the user's input
2176        * string to the updated_text, which is the common leading substring
2177        * of all possible completions, and if its a directory attempt
2178        * attempt completions in it. */
2179
2180       if (cmpl_updated_text (cmpl_state)[0])
2181         {
2182
2183           if (cmpl_updated_dir (cmpl_state))
2184             {
2185               gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
2186
2187               did_recurse = TRUE;
2188
2189               gtk_file_selection_populate (fs, dir_name, TRUE, TRUE);
2190
2191               g_free (dir_name);
2192             }
2193           else
2194             {
2195               if (fs->selection_entry)
2196                       gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
2197                                           cmpl_updated_text (cmpl_state));
2198             }
2199         }
2200       else
2201         {
2202           if (fs->selection_entry)
2203             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
2204         }
2205     }
2206   else if (reset_entry)
2207     {
2208       if (fs->selection_entry)
2209         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2210     }
2211
2212   if (!did_recurse)
2213     {
2214       if (fs->selection_entry && try_complete)
2215         gtk_editable_set_position (GTK_EDITABLE (fs->selection_entry), -1);
2216
2217       if (fs->selection_entry)
2218         {
2219           char *escaped = escape_underscores (cmpl_reference_position (cmpl_state));
2220           sel_text = g_strconcat (_("_Selection: "), escaped, NULL);
2221           g_free (escaped);
2222
2223           gtk_label_set_text_with_mnemonic (GTK_LABEL (fs->selection_text), sel_text);
2224           g_free (sel_text);
2225         }
2226
2227       if (fs->history_pulldown) 
2228         {
2229           gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
2230         }
2231       
2232     }
2233 }
2234
2235 static void
2236 gtk_file_selection_abort (GtkFileSelection *fs)
2237 {
2238   gchar err_buf[256];
2239
2240   g_snprintf (err_buf, sizeof (err_buf), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
2241
2242   /*  BEEP gdk_beep();  */
2243
2244   if (fs->selection_entry)
2245     gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
2246 }
2247
2248 /**
2249  * gtk_file_selection_set_select_multiple:
2250  * @filesel: a #GtkFileSelection
2251  * @select_multiple: whether or not the user is allowed to select multiple
2252  * files in the file list.
2253  *
2254  * Sets whether the user is allowed to select multiple files in the file list.
2255  * Use gtk_file_selection_get_selections () to get the list of selected files.
2256  **/
2257 void
2258 gtk_file_selection_set_select_multiple (GtkFileSelection *filesel,
2259                                         gboolean          select_multiple)
2260 {
2261   GtkTreeSelection *sel;
2262   GtkSelectionMode mode;
2263
2264   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
2265
2266   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2267
2268   mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE;
2269
2270   if (mode != gtk_tree_selection_get_mode (sel))
2271     {
2272       gtk_tree_selection_set_mode (sel, mode);
2273
2274       g_object_notify (G_OBJECT (filesel), "select-multiple");
2275     }
2276 }
2277
2278 /**
2279  * gtk_file_selection_get_select_multiple:
2280  * @filesel: a #GtkFileSelection
2281  *
2282  * Determines whether or not the user is allowed to select multiple files in
2283  * the file list. See gtk_file_selection_set_select_multiple().
2284  *
2285  * Return value: %TRUE if the user is allowed to select multiple files in the
2286  * file list
2287  **/
2288 gboolean
2289 gtk_file_selection_get_select_multiple (GtkFileSelection *filesel)
2290 {
2291   GtkTreeSelection *sel;
2292
2293   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), FALSE);
2294
2295   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2296   return (gtk_tree_selection_get_mode (sel) == GTK_SELECTION_MULTIPLE);
2297 }
2298
2299 static void
2300 multiple_changed_foreach (GtkTreeModel *model,
2301                           GtkTreePath  *path,
2302                           GtkTreeIter  *iter,
2303                           gpointer      data)
2304 {
2305   GPtrArray *names = data;
2306   gchar *filename;
2307
2308   gtk_tree_model_get (model, iter, FILE_COLUMN, &filename, -1);
2309
2310   g_ptr_array_add (names, filename);
2311 }
2312
2313 static void
2314 free_selected_names (GPtrArray *names)
2315 {
2316   gint i;
2317
2318   for (i = 0; i < names->len; i++)
2319     g_free (g_ptr_array_index (names, i));
2320
2321   g_ptr_array_free (names, TRUE);
2322 }
2323
2324 static void
2325 gtk_file_selection_file_changed (GtkTreeSelection *selection,
2326                                  gpointer          user_data)
2327 {
2328   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
2329   GPtrArray *new_names;
2330   gchar *filename;
2331   const gchar *entry;
2332   gint index = -1;
2333
2334   new_names = g_ptr_array_sized_new (8);
2335
2336   gtk_tree_selection_selected_foreach (selection,
2337                                        multiple_changed_foreach,
2338                                        new_names);
2339
2340   /* nothing selected */
2341   if (new_names->len == 0)
2342     {
2343       g_ptr_array_free (new_names, TRUE);
2344
2345       if (fs->selected_names != NULL)
2346         {
2347           free_selected_names (fs->selected_names);
2348           fs->selected_names = NULL;
2349         }
2350
2351       goto maybe_clear_entry;
2352     }
2353
2354   if (new_names->len != 1)
2355     {
2356       GPtrArray *old_names = fs->selected_names;
2357
2358       if (old_names != NULL)
2359         {
2360           /* A common case is selecting a range of files from top to bottom,
2361            * so quickly check for that to avoid looping over the entire list
2362            */
2363           if (compare_utf8_filenames (g_ptr_array_index (old_names, old_names->len - 1),
2364                                       g_ptr_array_index (new_names, new_names->len - 1)) != 0)
2365             index = new_names->len - 1;
2366           else
2367             {
2368               gint i = 0, j = 0, cmp;
2369
2370               /* do a quick diff, stopping at the first file not in the
2371                * old list
2372                */
2373               while (i < old_names->len && j < new_names->len)
2374                 {
2375                   cmp = compare_utf8_filenames (g_ptr_array_index (old_names, i),
2376                                                 g_ptr_array_index (new_names, j));
2377                   if (cmp < 0)
2378                     {
2379                       i++;
2380                     }
2381                   else if (cmp == 0)
2382                     {
2383                       i++;
2384                       j++;
2385                     }
2386                   else if (cmp > 0)
2387                     {
2388                       index = j;
2389                       break;
2390                     }
2391                 }
2392
2393               /* we ran off the end of the old list */
2394               if (index == -1 && i < new_names->len)
2395                 index = j;
2396             }
2397         }
2398       else
2399         {
2400           /* A phantom anchor still exists at the point where the last item
2401            * was selected, which is used for subsequent range selections.
2402            * So search up from there.
2403            */
2404           if (fs->last_selected &&
2405               compare_utf8_filenames (fs->last_selected,
2406                                       g_ptr_array_index (new_names, 0)) == 0)
2407             index = new_names->len - 1;
2408           else
2409             index = 0;
2410         }
2411     }
2412   else
2413     index = 0;
2414
2415   if (fs->selected_names != NULL)
2416     free_selected_names (fs->selected_names);
2417
2418   fs->selected_names = new_names;
2419
2420   if (index != -1)
2421     {
2422       if (fs->last_selected != NULL)
2423         g_free (fs->last_selected);
2424
2425       fs->last_selected = g_strdup (g_ptr_array_index (new_names, index));
2426       filename = get_real_filename (fs->last_selected, FALSE);
2427
2428       gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
2429
2430       if (filename != fs->last_selected)
2431         g_free (filename);
2432       
2433       return;
2434     }
2435   
2436 maybe_clear_entry:
2437
2438   entry = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
2439   if ((entry != NULL) && (fs->last_selected != NULL) &&
2440       (compare_utf8_filenames (entry, fs->last_selected) == 0))
2441     gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2442 }
2443
2444 /**
2445  * gtk_file_selection_get_selections:
2446  * @filesel: a #GtkFileSelection
2447  *
2448  * Retrieves the list of file selections the user has made in the dialog box.
2449  * This function is intended for use when the user can select multiple files
2450  * in the file list. 
2451  *
2452  * The filenames are in the GLib file name encoding. To convert to
2453  * UTF-8, call g_filename_to_utf8() on each string.
2454  *
2455  * Return value: a newly-allocated %NULL-terminated array of strings. Use
2456  * g_strfreev() to free it.
2457  **/
2458 gchar **
2459 gtk_file_selection_get_selections (GtkFileSelection *filesel)
2460 {
2461   GPtrArray *names;
2462   gchar **selections;
2463   gchar *filename, *dirname;
2464   gchar *current, *buf;
2465   gint i, count;
2466   gboolean unselected_entry;
2467
2468   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), NULL);
2469
2470   filename = g_strdup (gtk_file_selection_get_filename (filesel));
2471
2472   if (strlen (filename) == 0)
2473     {
2474       g_free (filename);
2475       return NULL;
2476     }
2477
2478   names = filesel->selected_names;
2479
2480   if (names != NULL)
2481     selections = g_new (gchar *, names->len + 2);
2482   else
2483     selections = g_new (gchar *, 2);
2484
2485   count = 0;
2486   unselected_entry = TRUE;
2487
2488   if (names != NULL)
2489     {
2490       dirname = g_path_get_dirname (filename);
2491
2492       if ((names->len >= 1) && 
2493           (strcmp (gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry)), "") == 0))
2494         { /* multiple files are selected and last selection was removed via ctrl click */
2495           g_free (dirname);
2496           dirname = g_strdup (filename); /* as gtk_file_selection_get_filename returns dir 
2497                                             if no file is selected */
2498           unselected_entry = FALSE;
2499         }
2500
2501       for (i = 0; i < names->len; i++)
2502         {
2503           buf = g_filename_from_utf8 (g_ptr_array_index (names, i), -1,
2504                                       NULL, NULL, NULL);
2505           current = g_build_filename (dirname, buf, NULL);
2506           g_free (buf);
2507
2508           selections[count++] = current;
2509
2510           if (unselected_entry && compare_sys_filenames (current, filename) == 0)
2511             unselected_entry = FALSE;
2512         }
2513
2514       g_free (dirname);
2515     }
2516
2517   if (unselected_entry)
2518     selections[count++] = filename;
2519   else
2520     g_free (filename);
2521
2522   selections[count] = NULL;
2523
2524   return selections;
2525 }
2526
2527 /**********************************************************************/
2528 /*                        External Interface                          */
2529 /**********************************************************************/
2530
2531 /* The four completion state selectors
2532  */
2533 static gchar*
2534 cmpl_updated_text (CompletionState *cmpl_state)
2535 {
2536   return cmpl_state->updated_text;
2537 }
2538
2539 static gboolean
2540 cmpl_updated_dir (CompletionState *cmpl_state)
2541 {
2542   return cmpl_state->re_complete;
2543 }
2544
2545 static gchar*
2546 cmpl_reference_position (CompletionState *cmpl_state)
2547 {
2548   return cmpl_state->reference_dir->fullname;
2549 }
2550
2551 #if 0
2552 /* This doesn't work currently and would require changes
2553  * to fnmatch.c to get working.
2554  */
2555 static gint
2556 cmpl_last_valid_char (CompletionState *cmpl_state)
2557 {
2558   return cmpl_state->last_valid_char;
2559 }
2560 #endif
2561
2562 static gchar*
2563 cmpl_completion_fullname (const gchar     *text,
2564                           CompletionState *cmpl_state)
2565 {
2566   if (!cmpl_state_okay (cmpl_state))
2567     {
2568       return g_strdup ("");
2569     }
2570   else if (g_path_is_absolute (text))
2571     {
2572       return g_strdup (text);
2573     }
2574 #ifdef HAVE_PWD_H
2575   else if (text[0] == '~')
2576     {
2577       CompletionDir* dir;
2578       char* slash;
2579
2580       dir = open_user_dir (text, cmpl_state);
2581
2582       if (dir)
2583         {
2584           slash = strchr (text, G_DIR_SEPARATOR);
2585           
2586           /* slash may be NULL, that works too */
2587           return g_strconcat (dir->fullname, slash, NULL);
2588         }
2589     }
2590 #endif
2591   
2592   return g_build_filename (cmpl_state->reference_dir->fullname,
2593                            text,
2594                            NULL);
2595 }
2596
2597 /* The three completion selectors
2598  */
2599 static gchar*
2600 cmpl_this_completion (PossibleCompletion* pc)
2601 {
2602   return pc->text;
2603 }
2604
2605 static gboolean
2606 cmpl_is_directory (PossibleCompletion* pc)
2607 {
2608   return pc->is_directory;
2609 }
2610
2611 static gint
2612 cmpl_is_a_completion (PossibleCompletion* pc)
2613 {
2614   return pc->is_a_completion;
2615 }
2616
2617 /**********************************************************************/
2618 /*                       Construction, deletion                       */
2619 /**********************************************************************/
2620
2621 /* Get the nearest parent of the current directory for which
2622  * we can convert the filename into UTF-8. With paranoia.
2623  * Returns "." when all goes wrong.
2624  */
2625 static gchar *
2626 get_current_dir_utf8 (void)
2627 {
2628   gchar *dir = g_get_current_dir ();
2629   gchar *dir_utf8 = NULL;
2630
2631   while (TRUE)
2632     {
2633       gchar *last_slash;
2634
2635       dir_utf8 = g_filename_to_utf8 (dir, -1, NULL, NULL, NULL);
2636       if (dir_utf8)
2637         break;
2638
2639       last_slash = strrchr (dir, G_DIR_SEPARATOR);
2640       if (!last_slash)          /* g_get_current_dir() wasn't absolute! */
2641         break;
2642
2643       if (last_slash + 1 == g_path_skip_root (dir)) /* Parent directory is a root directory */
2644         {
2645           if (last_slash[1] == '\0') /* Root misencoded! */
2646             break;
2647           else
2648             last_slash[1] = '\0';
2649         }
2650       else
2651         last_slash[0] = '\0';
2652       
2653       g_assert (last_slash);
2654     }
2655
2656   g_free (dir);
2657   
2658   return dir_utf8 ? dir_utf8 : g_strdup (".");
2659 }
2660
2661 static CompletionState*
2662 cmpl_init_state (void)
2663 {
2664   gchar *utf8_cwd;
2665   CompletionState *new_state;
2666   gint tries = 0;
2667
2668   new_state = g_new (CompletionState, 1);
2669
2670   utf8_cwd = get_current_dir_utf8 ();
2671
2672 tryagain:
2673   tries++;
2674   new_state->reference_dir = NULL;
2675   new_state->completion_dir = NULL;
2676   new_state->active_completion_dir = NULL;
2677   new_state->directory_storage = NULL;
2678   new_state->directory_sent_storage = NULL;
2679   new_state->last_valid_char = 0;
2680   new_state->updated_text = g_new (gchar, MAXPATHLEN);
2681   new_state->updated_text_alloc = MAXPATHLEN;
2682   new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
2683   new_state->the_completion.text_alloc = MAXPATHLEN;
2684   new_state->user_dir_name_buffer = NULL;
2685   new_state->user_directories = NULL;
2686
2687   new_state->reference_dir = open_dir (utf8_cwd, new_state);
2688
2689   if (!new_state->reference_dir)
2690     {
2691       /* Directories changing from underneath us, grumble */
2692       strcpy (utf8_cwd, G_DIR_SEPARATOR_S);
2693       if (tries < 2)
2694         goto tryagain;
2695     }
2696
2697   g_free (utf8_cwd);
2698   return new_state;
2699 }
2700
2701 static void
2702 cmpl_free_dir_list (GList* dp0)
2703 {
2704   GList *dp = dp0;
2705
2706   while (dp)
2707     {
2708       free_dir (dp->data);
2709       dp = dp->next;
2710     }
2711
2712   g_list_free (dp0);
2713 }
2714
2715 static void
2716 cmpl_free_dir_sent_list (GList* dp0)
2717 {
2718   GList *dp = dp0;
2719
2720   while (dp)
2721     {
2722       free_dir_sent (dp->data);
2723       dp = dp->next;
2724     }
2725
2726   g_list_free (dp0);
2727 }
2728
2729 static void
2730 cmpl_free_state (CompletionState* cmpl_state)
2731 {
2732   g_return_if_fail (cmpl_state != NULL);
2733
2734   cmpl_free_dir_list (cmpl_state->directory_storage);
2735   cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
2736
2737   if (cmpl_state->user_dir_name_buffer)
2738     g_free (cmpl_state->user_dir_name_buffer);
2739   if (cmpl_state->user_directories)
2740     g_free (cmpl_state->user_directories);
2741   if (cmpl_state->the_completion.text)
2742     g_free (cmpl_state->the_completion.text);
2743   if (cmpl_state->updated_text)
2744     g_free (cmpl_state->updated_text);
2745
2746   g_free (cmpl_state);
2747 }
2748
2749 static void
2750 free_dir (CompletionDir* dir)
2751 {
2752   g_free (dir->cmpl_text);
2753   g_free (dir->fullname);
2754   g_free (dir);
2755 }
2756
2757 static void
2758 free_dir_sent (CompletionDirSent* sent)
2759 {
2760   gint i;
2761   for (i = 0; i < sent->entry_count; i++)
2762     {
2763       g_free (sent->entries[i].entry_name);
2764       g_free (sent->entries[i].sort_key);
2765     }
2766   g_free (sent->entries);
2767   g_free (sent);
2768 }
2769
2770 static void
2771 prune_memory_usage (CompletionState *cmpl_state)
2772 {
2773   GList* cdsl = cmpl_state->directory_sent_storage;
2774   GList* cdl = cmpl_state->directory_storage;
2775   GList* cdl0 = cdl;
2776   gint len = 0;
2777
2778   for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
2779     cdsl = cdsl->next;
2780
2781   if (cdsl)
2782     {
2783       cmpl_free_dir_sent_list (cdsl->next);
2784       cdsl->next = NULL;
2785     }
2786
2787   cmpl_state->directory_storage = NULL;
2788   while (cdl)
2789     {
2790       if (cdl->data == cmpl_state->reference_dir)
2791         cmpl_state->directory_storage = g_list_prepend (NULL, cdl->data);
2792       else
2793         free_dir (cdl->data);
2794       cdl = cdl->next;
2795     }
2796
2797   g_list_free (cdl0);
2798 }
2799
2800 /**********************************************************************/
2801 /*                        The main entrances.                         */
2802 /**********************************************************************/
2803
2804 static PossibleCompletion*
2805 cmpl_completion_matches (gchar           *text_to_complete,
2806                          gchar          **remaining_text,
2807                          CompletionState *cmpl_state)
2808 {
2809 #ifdef HAVE_PWD_H
2810   gchar* first_slash;
2811 #endif
2812   PossibleCompletion *poss;
2813
2814   prune_memory_usage (cmpl_state);
2815
2816   g_assert (text_to_complete != NULL);
2817
2818   cmpl_state->user_completion_index = -1;
2819   cmpl_state->last_completion_text = text_to_complete;
2820   cmpl_state->the_completion.text[0] = 0;
2821   cmpl_state->last_valid_char = 0;
2822   cmpl_state->updated_text_len = -1;
2823   cmpl_state->updated_text[0] = 0;
2824   cmpl_state->re_complete = FALSE;
2825
2826 #ifdef HAVE_PWD_H
2827   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2828
2829   if (text_to_complete[0] == '~' && !first_slash)
2830     {
2831       /* Text starts with ~ and there is no slash, show all the
2832        * home directory completions.
2833        */
2834       poss = attempt_homedir_completion (text_to_complete, cmpl_state);
2835
2836       update_cmpl (poss, cmpl_state);
2837
2838       return poss;
2839     }
2840 #endif
2841   cmpl_state->reference_dir =
2842     open_ref_dir (text_to_complete, remaining_text, cmpl_state);
2843
2844   if (!cmpl_state->reference_dir)
2845     return NULL;
2846
2847   cmpl_state->completion_dir =
2848     find_completion_dir (*remaining_text, remaining_text, cmpl_state);
2849
2850   cmpl_state->last_valid_char = *remaining_text - text_to_complete;
2851
2852   if (!cmpl_state->completion_dir)
2853     return NULL;
2854
2855   cmpl_state->completion_dir->cmpl_index = -1;
2856   cmpl_state->completion_dir->cmpl_parent = NULL;
2857   cmpl_state->completion_dir->cmpl_text = g_strdup (*remaining_text);
2858
2859   cmpl_state->active_completion_dir = cmpl_state->completion_dir;
2860
2861   cmpl_state->reference_dir = cmpl_state->completion_dir;
2862
2863   poss = attempt_file_completion (cmpl_state);
2864
2865   update_cmpl (poss, cmpl_state);
2866
2867   return poss;
2868 }
2869
2870 static PossibleCompletion*
2871 cmpl_next_completion (CompletionState* cmpl_state)
2872 {
2873   PossibleCompletion* poss = NULL;
2874
2875   cmpl_state->the_completion.text[0] = 0;
2876
2877 #ifdef HAVE_PWD_H
2878   if (cmpl_state->user_completion_index >= 0)
2879     poss = attempt_homedir_completion (cmpl_state->last_completion_text, cmpl_state);
2880   else
2881     poss = attempt_file_completion (cmpl_state);
2882 #else
2883   poss = attempt_file_completion (cmpl_state);
2884 #endif
2885
2886   update_cmpl (poss, cmpl_state);
2887
2888   return poss;
2889 }
2890
2891 /**********************************************************************/
2892 /*                       Directory Operations                         */
2893 /**********************************************************************/
2894
2895 /* Open the directory where completion will begin from, if possible. */
2896 static CompletionDir*
2897 open_ref_dir (gchar           *text_to_complete,
2898               gchar          **remaining_text,
2899               CompletionState *cmpl_state)
2900 {
2901   gchar* first_slash;
2902   CompletionDir *new_dir;
2903
2904   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2905
2906 #ifdef G_WITH_CYGWIN
2907   if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
2908     {
2909       char root_dir[5];
2910       g_snprintf (root_dir, sizeof (root_dir), "//%c", text_to_complete[2]);
2911
2912       new_dir = open_dir (root_dir, cmpl_state);
2913
2914       if (new_dir) {
2915         *remaining_text = text_to_complete + 4;
2916       }
2917     }
2918 #else
2919   if (FALSE)
2920     ;
2921 #endif
2922 #ifdef HAVE_PWD_H
2923   else if (text_to_complete[0] == '~')
2924     {
2925       new_dir = open_user_dir (text_to_complete, cmpl_state);
2926
2927       if (new_dir)
2928         {
2929           if (first_slash)
2930             *remaining_text = first_slash + 1;
2931           else
2932             *remaining_text = text_to_complete + strlen (text_to_complete);
2933         }
2934       else
2935         {
2936           return NULL;
2937         }
2938     }
2939 #endif
2940   else if (g_path_is_absolute (text_to_complete) || !cmpl_state->reference_dir)
2941     {
2942       gchar *tmp = g_strdup (text_to_complete);
2943       gchar *p;
2944
2945       p = tmp;
2946       while (*p && *p != '*' && *p != '?')
2947         p++;
2948
2949       *p = '\0';
2950       p = strrchr (tmp, G_DIR_SEPARATOR);
2951       if (p)
2952         {
2953           if (p + 1 == g_path_skip_root (tmp))
2954             p++;
2955       
2956           *p = '\0';
2957           new_dir = open_dir (tmp, cmpl_state);
2958
2959           if (new_dir)
2960             *remaining_text = text_to_complete + 
2961               ((p == g_path_skip_root (tmp)) ? (p - tmp) : (p + 1 - tmp));
2962         }
2963       else
2964         {
2965           /* If no possible candidates, use the cwd */
2966           gchar *utf8_curdir = get_current_dir_utf8 ();
2967
2968           new_dir = open_dir (utf8_curdir, cmpl_state);
2969
2970           if (new_dir)
2971             *remaining_text = text_to_complete;
2972
2973           g_free (utf8_curdir);
2974         }
2975
2976       g_free (tmp);
2977     }
2978   else
2979     {
2980       *remaining_text = text_to_complete;
2981
2982       new_dir = open_dir (cmpl_state->reference_dir->fullname, cmpl_state);
2983     }
2984
2985   if (new_dir)
2986     {
2987       new_dir->cmpl_index = -1;
2988       new_dir->cmpl_parent = NULL;
2989     }
2990
2991   return new_dir;
2992 }
2993
2994 #ifdef HAVE_PWD_H
2995
2996 /* open a directory by user name */
2997 static CompletionDir*
2998 open_user_dir (const gchar     *text_to_complete,
2999                CompletionState *cmpl_state)
3000 {
3001   CompletionDir *result;
3002   gchar *first_slash;
3003   gint cmp_len;
3004
3005   g_assert (text_to_complete && text_to_complete[0] == '~');
3006
3007   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
3008
3009   if (first_slash)
3010     cmp_len = first_slash - text_to_complete - 1;
3011   else
3012     cmp_len = strlen (text_to_complete + 1);
3013
3014   if (!cmp_len)
3015     {
3016       /* ~/ */
3017       const gchar *homedir = g_get_home_dir ();
3018       gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
3019
3020       if (utf8_homedir)
3021         result = open_dir (utf8_homedir, cmpl_state);
3022       else
3023         result = NULL;
3024       
3025       g_free (utf8_homedir);
3026     }
3027   else
3028     {
3029       /* ~user/ */
3030       gchar* copy = g_new (char, cmp_len + 1);
3031       gchar *utf8_dir;
3032       struct passwd *pwd;
3033
3034       strncpy (copy, text_to_complete + 1, cmp_len);
3035       copy[cmp_len] = 0;
3036       pwd = getpwnam (copy);
3037       g_free (copy);
3038       if (!pwd)
3039         {
3040           cmpl_errno = errno;
3041           return NULL;
3042         }
3043       utf8_dir = g_filename_to_utf8 (pwd->pw_dir, -1, NULL, NULL, NULL);
3044       result = open_dir (utf8_dir, cmpl_state);
3045       g_free (utf8_dir);
3046     }
3047   return result;
3048 }
3049
3050 #endif
3051
3052 /* open a directory relative to the current relative directory */
3053 static CompletionDir*
3054 open_relative_dir (gchar           *dir_name,
3055                    CompletionDir   *dir,
3056                    CompletionState *cmpl_state)
3057 {
3058   CompletionDir *result;
3059   GString *path;
3060
3061   path = g_string_sized_new (dir->fullname_len + strlen (dir_name) + 10);
3062   g_string_assign (path, dir->fullname);
3063
3064   if (dir->fullname_len > 1
3065       && path->str[dir->fullname_len - 1] != G_DIR_SEPARATOR)
3066     g_string_append_c (path, G_DIR_SEPARATOR);
3067   g_string_append (path, dir_name);
3068
3069   result = open_dir (path->str, cmpl_state);
3070
3071   g_string_free (path, TRUE);
3072
3073   return result;
3074 }
3075
3076 /* after the cache lookup fails, really open a new directory */
3077 static CompletionDirSent*
3078 open_new_dir (gchar       *dir_name,
3079               struct stat *sbuf,
3080               gboolean     stat_subdirs)
3081 {
3082   CompletionDirSent *sent;
3083   GDir *directory;
3084   const char *dirent;
3085   GError *error = NULL;
3086   gint entry_count = 0;
3087   gint n_entries = 0;
3088   gint i;
3089   struct stat ent_sbuf;
3090   GString *path;
3091   gchar *sys_dir_name;
3092
3093   sent = g_new (CompletionDirSent, 1);
3094 #ifndef G_PLATFORM_WIN32
3095   sent->mtime = sbuf->st_mtime;
3096   sent->inode = sbuf->st_ino;
3097   sent->device = sbuf->st_dev;
3098 #endif
3099   path = g_string_sized_new (2*MAXPATHLEN + 10);
3100
3101   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
3102   if (!sys_dir_name)
3103     {
3104       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3105       return NULL;
3106     }
3107   
3108   directory = g_dir_open (sys_dir_name, 0, &error);
3109   if (!directory)
3110     {
3111       cmpl_errno = error->code; /* ??? */
3112       g_free (sys_dir_name);
3113       return NULL;
3114     }
3115
3116   while ((dirent = g_dir_read_name (directory)) != NULL)
3117     entry_count++;
3118   entry_count += 2;             /* For ".",".." */
3119
3120   sent->entries = g_new (CompletionDirEntry, entry_count);
3121   sent->entry_count = entry_count;
3122
3123   g_dir_rewind (directory);
3124
3125   for (i = 0; i < entry_count; i += 1)
3126     {
3127       GError *error = NULL;
3128
3129       if (i == 0)
3130         dirent = ".";
3131       else if (i == 1)
3132         dirent = "..";
3133       else
3134         {
3135           dirent = g_dir_read_name (directory);
3136           if (!dirent)          /* Directory changed */
3137             break;
3138         }
3139
3140       sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent, -1, NULL, NULL, &error);
3141       if (sent->entries[n_entries].entry_name == NULL
3142           || !g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL))
3143         {
3144           gchar *escaped_str = g_strescape (dirent, NULL);
3145           g_message (_("The filename \"%s\" couldn't be converted to UTF-8. "
3146                        "(try setting the environment variable G_FILENAME_ENCODING): %s"),
3147                      escaped_str,
3148                      error->message ? error->message : _("Invalid UTF-8"));
3149           g_free (escaped_str);
3150           g_clear_error (&error);
3151           continue;
3152         }
3153       g_clear_error (&error);
3154       
3155       sent->entries[n_entries].sort_key = g_utf8_collate_key (sent->entries[n_entries].entry_name, -1);
3156       
3157       g_string_assign (path, sys_dir_name);
3158       if (path->str[path->len-1] != G_DIR_SEPARATOR)
3159         {
3160           g_string_append_c (path, G_DIR_SEPARATOR);
3161         }
3162       g_string_append (path, dirent);
3163
3164       if (stat_subdirs)
3165         {
3166           /* Here we know path->str is a "system charset" string */
3167           if (g_stat (path->str, &ent_sbuf) >= 0 && S_ISDIR (ent_sbuf.st_mode))
3168             sent->entries[n_entries].is_dir = TRUE;
3169           else
3170             /* stat may fail, and we don't mind, since it could be a
3171              * dangling symlink. */
3172             sent->entries[n_entries].is_dir = FALSE;
3173         }
3174       else
3175         sent->entries[n_entries].is_dir = 1;
3176
3177       n_entries++;
3178     }
3179   sent->entry_count = n_entries;
3180   
3181   g_free (sys_dir_name);
3182   g_string_free (path, TRUE);
3183   qsort (sent->entries, sent->entry_count, sizeof (CompletionDirEntry), compare_cmpl_dir);
3184
3185   g_dir_close (directory);
3186
3187   return sent;
3188 }
3189
3190 #ifndef G_PLATFORM_WIN32
3191
3192 static gboolean
3193 check_dir (gchar       *dir_name,
3194            struct stat *result,
3195            gboolean    *stat_subdirs)
3196 {
3197   /* A list of directories that we know only contain other directories.
3198    * Trying to stat every file in these directories would be very
3199    * expensive.
3200    */
3201
3202   static struct {
3203     const gchar *name;
3204     gboolean present;
3205     struct stat statbuf;
3206   } no_stat_dirs[] = {
3207     { "/afs", FALSE, { 0 } },
3208     { "/net", FALSE, { 0 } }
3209   };
3210
3211   static const gint n_no_stat_dirs = G_N_ELEMENTS (no_stat_dirs);
3212   static gboolean initialized = FALSE;
3213   gchar *sys_dir_name;
3214   gint i;
3215
3216   if (!initialized)
3217     {
3218       initialized = TRUE;
3219       for (i = 0; i < n_no_stat_dirs; i++)
3220         {
3221           if (g_stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
3222             no_stat_dirs[i].present = TRUE;
3223         }
3224     }
3225
3226   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
3227   if (!sys_dir_name)
3228     {
3229       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3230       return FALSE;
3231     }
3232   
3233   if (g_stat (sys_dir_name, result) < 0)
3234     {
3235       g_free (sys_dir_name);
3236       cmpl_errno = errno;
3237       return FALSE;
3238     }
3239   g_free (sys_dir_name);
3240
3241   *stat_subdirs = TRUE;
3242   for (i = 0; i < n_no_stat_dirs; i++)
3243     {
3244       if (no_stat_dirs[i].present &&
3245           (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
3246           (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
3247         {
3248           *stat_subdirs = FALSE;
3249           break;
3250         }
3251     }
3252
3253   return TRUE;
3254 }
3255
3256 #endif
3257
3258 /* open a directory by absolute pathname */
3259 static CompletionDir*
3260 open_dir (gchar           *dir_name,
3261           CompletionState *cmpl_state)
3262 {
3263 #ifndef G_PLATFORM_WIN32
3264   struct stat sbuf;
3265   gboolean stat_subdirs;
3266   GList* cdsl;
3267 #endif
3268   CompletionDirSent *sent;
3269
3270 #ifndef G_PLATFORM_WIN32
3271   if (!check_dir (dir_name, &sbuf, &stat_subdirs))
3272     return NULL;
3273
3274   cdsl = cmpl_state->directory_sent_storage;
3275
3276   while (cdsl)
3277     {
3278       sent = cdsl->data;
3279
3280       if (sent->inode == sbuf.st_ino &&
3281           sent->mtime == sbuf.st_mtime &&
3282           sent->device == sbuf.st_dev)
3283         return attach_dir (sent, dir_name, cmpl_state);
3284
3285       cdsl = cdsl->next;
3286     }
3287
3288   sent = open_new_dir (dir_name, &sbuf, stat_subdirs);
3289 #else
3290   sent = open_new_dir (dir_name, NULL, TRUE);
3291 #endif
3292
3293   if (sent)
3294     {
3295       cmpl_state->directory_sent_storage =
3296         g_list_prepend (cmpl_state->directory_sent_storage, sent);
3297
3298       return attach_dir (sent, dir_name, cmpl_state);
3299     }
3300
3301   return NULL;
3302 }
3303
3304 static CompletionDir*
3305 attach_dir (CompletionDirSent *sent,
3306             gchar             *dir_name,
3307             CompletionState   *cmpl_state)
3308 {
3309   CompletionDir* new_dir;
3310
3311   new_dir = g_new (CompletionDir, 1);
3312
3313   cmpl_state->directory_storage =
3314     g_list_prepend (cmpl_state->directory_storage, new_dir);
3315
3316   new_dir->sent = sent;
3317   new_dir->fullname = g_strdup (dir_name);
3318   new_dir->fullname_len = strlen (dir_name);
3319   new_dir->cmpl_text = NULL;
3320
3321   return new_dir;
3322 }
3323
3324 static gint
3325 correct_dir_fullname (CompletionDir* cmpl_dir)
3326 {
3327   gint length = strlen (cmpl_dir->fullname);
3328   gchar *first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3329   gchar *sys_filename;
3330   struct stat sbuf;
3331
3332   /* Does it end with /. (\.) ? */
3333   if (length >= 2 &&
3334       strcmp (cmpl_dir->fullname + length - 2, G_DIR_SEPARATOR_S ".") == 0)
3335     {
3336       /* Is it just the root directory (on a drive) ? */
3337       if (cmpl_dir->fullname + length - 2 == first_slash)
3338         {
3339           cmpl_dir->fullname[length - 1] = 0;
3340           cmpl_dir->fullname_len = length - 1;
3341           return TRUE;
3342         }
3343       else
3344         {
3345           cmpl_dir->fullname[length - 2] = 0;
3346         }
3347     }
3348
3349   /* Ends with /./ (\.\)? */
3350   else if (length >= 3 &&
3351            strcmp (cmpl_dir->fullname + length - 3,
3352                    G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) == 0)
3353     cmpl_dir->fullname[length - 2] = 0;
3354
3355   /* Ends with /.. (\..) ? */
3356   else if (length >= 3 &&
3357            strcmp (cmpl_dir->fullname + length - 3,
3358                    G_DIR_SEPARATOR_S "..") == 0)
3359     {
3360       /* Is it just /.. (X:\..)? */
3361       if (cmpl_dir->fullname + length - 3 == first_slash)
3362         {
3363           cmpl_dir->fullname[length - 2] = 0;
3364           cmpl_dir->fullname_len = length - 2;
3365           return TRUE;
3366         }
3367
3368       sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3369       if (!sys_filename)
3370         {
3371           cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3372           return FALSE;
3373         }
3374       
3375       if (g_stat (sys_filename, &sbuf) < 0)
3376         {
3377           g_free (sys_filename);
3378           cmpl_errno = errno;
3379           return FALSE;
3380         }
3381       g_free (sys_filename);
3382
3383       cmpl_dir->fullname[length - 3] = 0;
3384
3385       if (!correct_parent (cmpl_dir, &sbuf))
3386         return FALSE;
3387     }
3388
3389   /* Ends with /../ (\..\)? */
3390   else if (length >= 4 &&
3391            strcmp (cmpl_dir->fullname + length - 4,
3392                    G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) == 0)
3393     {
3394       /* Is it just /../ (X:\..\)? */
3395       if (cmpl_dir->fullname + length - 4 == first_slash)
3396         {
3397           cmpl_dir->fullname[length - 3] = 0;
3398           cmpl_dir->fullname_len = length - 3;
3399           return TRUE;
3400         }
3401
3402       sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3403       if (!sys_filename)
3404         {
3405           cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3406           return FALSE;
3407         }
3408       
3409       if (g_stat (sys_filename, &sbuf) < 0)
3410         {
3411           g_free (sys_filename);
3412           cmpl_errno = errno;
3413           return FALSE;
3414         }
3415       g_free (sys_filename);
3416
3417       cmpl_dir->fullname[length - 4] = 0;
3418
3419       if (!correct_parent (cmpl_dir, &sbuf))
3420         return FALSE;
3421     }
3422
3423   cmpl_dir->fullname_len = strlen (cmpl_dir->fullname);
3424
3425   return TRUE;
3426 }
3427
3428 static gint
3429 correct_parent (CompletionDir *cmpl_dir,
3430                 struct stat   *sbuf)
3431 {
3432   struct stat parbuf;
3433   gchar *last_slash;
3434   gchar *first_slash;
3435 #ifndef G_PLATFORM_WIN32
3436   gchar *new_name;
3437 #endif
3438   gchar *sys_filename;
3439   gchar c = 0;
3440
3441   last_slash = strrchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3442   g_assert (last_slash);
3443   first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3444
3445   /* Clever (?) way to check for top-level directory that works also on
3446    * Win32, where there is a drive letter and colon prefixed...
3447    */
3448   if (last_slash != first_slash)
3449     {
3450       last_slash[0] = 0;
3451     }
3452   else
3453     {
3454       c = last_slash[1];
3455       last_slash[1] = 0;
3456     }
3457
3458   sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3459   if (!sys_filename)
3460     {
3461       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3462       if (!c)
3463         last_slash[0] = G_DIR_SEPARATOR;
3464       return FALSE;
3465     }
3466   
3467   if (g_stat (sys_filename, &parbuf) < 0)
3468     {
3469       g_free (sys_filename);
3470       cmpl_errno = errno;
3471       if (!c)
3472         last_slash[0] = G_DIR_SEPARATOR;
3473       return FALSE;
3474     }
3475   g_free (sys_filename);
3476
3477 #ifndef G_PLATFORM_WIN32        /* No inode numbers on Win32 */
3478   if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
3479     /* it wasn't a link */
3480     return TRUE;
3481
3482   if (c)
3483     last_slash[1] = c;
3484   else
3485     last_slash[0] = G_DIR_SEPARATOR;
3486
3487   /* it was a link, have to figure it out the hard way */
3488
3489   new_name = find_parent_dir_fullname (cmpl_dir->fullname);
3490
3491   if (!new_name)
3492     return FALSE;
3493
3494   g_free (cmpl_dir->fullname);
3495
3496   cmpl_dir->fullname = new_name;
3497 #endif
3498
3499   return TRUE;
3500 }
3501
3502 #ifndef G_PLATFORM_WIN32
3503
3504 static gchar*
3505 find_parent_dir_fullname (gchar* dirname)
3506 {
3507   gchar *sys_orig_dir;
3508   gchar *result;
3509   gchar *sys_cwd;
3510   gchar *sys_dirname;
3511
3512   sys_orig_dir = g_get_current_dir ();
3513   sys_dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
3514   if (!sys_dirname)
3515     {
3516       g_free (sys_orig_dir);
3517       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3518       return NULL;
3519     }
3520   
3521   if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
3522     {
3523       cmpl_errno = errno;
3524       chdir (sys_orig_dir);
3525       g_free (sys_dirname);
3526       g_free (sys_orig_dir);
3527       return NULL;
3528     }
3529   g_free (sys_dirname);
3530
3531   sys_cwd = g_get_current_dir ();
3532   result = g_filename_to_utf8 (sys_cwd, -1, NULL, NULL, NULL);
3533   g_free (sys_cwd);
3534
3535   if (chdir (sys_orig_dir) != 0)
3536     {
3537       cmpl_errno = errno;
3538       g_free (sys_orig_dir);
3539       return NULL;
3540     }
3541
3542   g_free (sys_orig_dir);
3543   return result;
3544 }
3545
3546 #endif
3547
3548 /**********************************************************************/
3549 /*                        Completion Operations                       */
3550 /**********************************************************************/
3551
3552 #ifdef HAVE_PWD_H
3553
3554 static PossibleCompletion*
3555 attempt_homedir_completion (gchar           *text_to_complete,
3556                             CompletionState *cmpl_state)
3557 {
3558   gint index;
3559
3560   if (!cmpl_state->user_dir_name_buffer &&
3561       !get_pwdb (cmpl_state))
3562     return NULL;
3563
3564   cmpl_state->user_completion_index += 1;
3565
3566   while (cmpl_state->user_completion_index < cmpl_state->user_directories_len)
3567     {
3568       index = first_diff_index (text_to_complete + 1,
3569                                 cmpl_state->user_directories
3570                                 [cmpl_state->user_completion_index].login);
3571
3572       switch (index)
3573         {
3574         case PATTERN_MATCH:
3575           break;
3576         default:
3577           if (cmpl_state->last_valid_char < (index + 1))
3578             cmpl_state->last_valid_char = index + 1;
3579           cmpl_state->user_completion_index += 1;
3580           continue;
3581         }
3582
3583       cmpl_state->the_completion.is_a_completion = 1;
3584       cmpl_state->the_completion.is_directory = TRUE;
3585
3586       append_completion_text ("~", cmpl_state);
3587
3588       append_completion_text (cmpl_state->
3589                               user_directories[cmpl_state->user_completion_index].login,
3590                               cmpl_state);
3591
3592       return append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3593     }
3594
3595   if (text_to_complete[1]
3596       || cmpl_state->user_completion_index > cmpl_state->user_directories_len)
3597     {
3598       cmpl_state->user_completion_index = -1;
3599       return NULL;
3600     }
3601   else
3602     {
3603       cmpl_state->user_completion_index += 1;
3604       cmpl_state->the_completion.is_a_completion = 1;
3605       cmpl_state->the_completion.is_directory = TRUE;
3606
3607       return append_completion_text ("~" G_DIR_SEPARATOR_S, cmpl_state);
3608     }
3609 }
3610
3611 #endif
3612
3613 #ifdef G_PLATFORM_WIN32
3614 /* FIXME: determine whether we should casefold all Unicode letters
3615  * here, too (and in in first_diff_index() walk through the strings with
3616  * g_utf8_next_char()), or if this folding isn't actually needed at
3617  * all.
3618  */
3619 #define FOLD(c) (tolower(c))
3620 #else
3621 #define FOLD(c) (c)
3622 #endif
3623
3624 /* returns the index (>= 0) of the first differing character,
3625  * PATTERN_MATCH if the completion matches */
3626 static gint
3627 first_diff_index (gchar *pat,
3628                   gchar *text)
3629 {
3630   gint diff = 0;
3631
3632   while (*pat && *text && FOLD (*text) == FOLD (*pat))
3633     {
3634       pat += 1;
3635       text += 1;
3636       diff += 1;
3637     }
3638
3639   if (*pat)
3640     return diff;
3641
3642   return PATTERN_MATCH;
3643 }
3644
3645 static PossibleCompletion*
3646 append_completion_text (gchar           *text,
3647                         CompletionState *cmpl_state)
3648 {
3649   gint len, i = 1;
3650
3651   if (!cmpl_state->the_completion.text)
3652     return NULL;
3653
3654   len = strlen (text) + strlen (cmpl_state->the_completion.text) + 1;
3655
3656   if (cmpl_state->the_completion.text_alloc > len)
3657     {
3658       strcat (cmpl_state->the_completion.text, text);
3659       return &cmpl_state->the_completion;
3660     }
3661
3662   while (i < len)
3663     i <<= 1;
3664
3665   cmpl_state->the_completion.text_alloc = i;
3666
3667   cmpl_state->the_completion.text = (gchar*) g_realloc (cmpl_state->the_completion.text, i);
3668
3669   if (!cmpl_state->the_completion.text)
3670     return NULL;
3671   else
3672     {
3673       strcat (cmpl_state->the_completion.text, text);
3674       return &cmpl_state->the_completion;
3675     }
3676 }
3677
3678 static CompletionDir*
3679 find_completion_dir (gchar          *text_to_complete,
3680                     gchar          **remaining_text,
3681                     CompletionState *cmpl_state)
3682 {
3683   gchar* first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
3684   CompletionDir* dir = cmpl_state->reference_dir;
3685   CompletionDir* next;
3686   *remaining_text = text_to_complete;
3687
3688   while (first_slash)
3689     {
3690       gint len = first_slash - *remaining_text;
3691       gint found = 0;
3692       gchar *found_name = NULL;         /* Quiet gcc */
3693       gint i;
3694       gchar* pat_buf = g_new (gchar, len + 1);
3695
3696       strncpy (pat_buf, *remaining_text, len);
3697       pat_buf[len] = 0;
3698
3699       for (i = 0; i < dir->sent->entry_count; i += 1)
3700         {
3701           if (dir->sent->entries[i].is_dir &&
3702               _gtk_fnmatch (pat_buf, dir->sent->entries[i].entry_name, TRUE))
3703             {
3704               if (found)
3705                 {
3706                   g_free (pat_buf);
3707                   return dir;
3708                 }
3709               else
3710                 {
3711                   found = 1;
3712                   found_name = dir->sent->entries[i].entry_name;
3713                 }
3714             }
3715         }
3716
3717       if (!found)
3718         {
3719           /* Perhaps we are trying to open an automount directory */
3720           found_name = pat_buf;
3721         }
3722
3723       next = open_relative_dir (found_name, dir, cmpl_state);
3724       
3725       if (!next)
3726         {
3727           g_free (pat_buf);
3728           return NULL;
3729 }
3730       
3731       next->cmpl_parent = dir;
3732       
3733       dir = next;
3734       
3735       if (!correct_dir_fullname (dir))
3736         {
3737           g_free (pat_buf);
3738           return NULL;
3739         }
3740       
3741       *remaining_text = first_slash + 1;
3742       first_slash = strchr (*remaining_text, G_DIR_SEPARATOR);
3743
3744       g_free (pat_buf);
3745     }
3746
3747   return dir;
3748 }
3749
3750 static void
3751 update_cmpl (PossibleCompletion *poss,
3752              CompletionState    *cmpl_state)
3753 {
3754   gint cmpl_len;
3755
3756   if (!poss || !cmpl_is_a_completion (poss))
3757     return;
3758
3759   cmpl_len = strlen (cmpl_this_completion (poss));
3760
3761   if (cmpl_state->updated_text_alloc < cmpl_len + 1)
3762     {
3763       cmpl_state->updated_text_alloc = 2*cmpl_len;
3764       cmpl_state->updated_text =
3765         (gchar*)g_realloc (cmpl_state->updated_text,
3766                            cmpl_state->updated_text_alloc);
3767     }
3768
3769   if (cmpl_state->updated_text_len < 0)
3770     {
3771       strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3772       cmpl_state->updated_text_len = cmpl_len;
3773       cmpl_state->re_complete = cmpl_is_directory (poss);
3774     }
3775   else if (cmpl_state->updated_text_len == 0)
3776     {
3777       cmpl_state->re_complete = FALSE;
3778     }
3779   else
3780     {
3781       gint first_diff =
3782         first_diff_index (cmpl_state->updated_text,
3783                           cmpl_this_completion (poss));
3784
3785       cmpl_state->re_complete = FALSE;
3786
3787       if (first_diff == PATTERN_MATCH)
3788         return;
3789
3790       if (first_diff > cmpl_state->updated_text_len)
3791         strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3792
3793       cmpl_state->updated_text_len = first_diff;
3794       cmpl_state->updated_text[first_diff] = 0;
3795     }
3796 }
3797
3798 static PossibleCompletion*
3799 attempt_file_completion (CompletionState *cmpl_state)
3800 {
3801   gchar *pat_buf, *first_slash;
3802   CompletionDir *dir = cmpl_state->active_completion_dir;
3803
3804   dir->cmpl_index += 1;
3805
3806   if (dir->cmpl_index == dir->sent->entry_count)
3807     {
3808       if (dir->cmpl_parent == NULL)
3809         {
3810           cmpl_state->active_completion_dir = NULL;
3811
3812           return NULL;
3813         }
3814       else
3815         {
3816           cmpl_state->active_completion_dir = dir->cmpl_parent;
3817
3818           return attempt_file_completion (cmpl_state);
3819         }
3820     }
3821
3822   g_assert (dir->cmpl_text);
3823
3824   first_slash = strchr (dir->cmpl_text, G_DIR_SEPARATOR);
3825
3826   if (first_slash)
3827     {
3828       gint len = first_slash - dir->cmpl_text;
3829
3830       pat_buf = g_new (gchar, len + 1);
3831       strncpy (pat_buf, dir->cmpl_text, len);
3832       pat_buf[len] = 0;
3833     }
3834   else
3835     {
3836       gint len = strlen (dir->cmpl_text);
3837
3838       pat_buf = g_new (gchar, len + 2);
3839       strcpy (pat_buf, dir->cmpl_text);
3840       /* Don't append a * if the user entered one herself.
3841        * This way one can complete *.h and don't get matches
3842        * on any .help files, for instance.
3843        */
3844       if (strchr (pat_buf, '*') == NULL)
3845         strcpy (pat_buf + len, "*");
3846     }
3847
3848   if (first_slash)
3849     {
3850       if (dir->sent->entries[dir->cmpl_index].is_dir)
3851         {
3852           if (_gtk_fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, TRUE))
3853             {
3854               CompletionDir* new_dir;
3855
3856               new_dir = open_relative_dir (dir->sent->entries[dir->cmpl_index].entry_name,
3857                                            dir, cmpl_state);
3858
3859               if (!new_dir)
3860                 {
3861                   g_free (pat_buf);
3862                   return NULL;
3863                 }
3864
3865               new_dir->cmpl_parent = dir;
3866
3867               new_dir->cmpl_index = -1;
3868               new_dir->cmpl_text = g_strdup (first_slash + 1);
3869
3870               cmpl_state->active_completion_dir = new_dir;
3871
3872               g_free (pat_buf);
3873               return attempt_file_completion (cmpl_state);
3874             }
3875           else
3876             {
3877               g_free (pat_buf);
3878               return attempt_file_completion (cmpl_state);
3879             }
3880         }
3881       else
3882         {
3883           g_free (pat_buf);
3884           return attempt_file_completion (cmpl_state);
3885         }
3886     }
3887   else
3888     {
3889       if (dir->cmpl_parent != NULL)
3890         {
3891           append_completion_text (dir->fullname +
3892                                   strlen (cmpl_state->completion_dir->fullname) + 1,
3893                                   cmpl_state);
3894           append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3895         }
3896
3897       append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
3898
3899       cmpl_state->the_completion.is_a_completion =
3900         _gtk_fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, TRUE);
3901
3902       cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
3903       if (dir->sent->entries[dir->cmpl_index].is_dir)
3904         append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3905
3906       g_free (pat_buf);
3907       return &cmpl_state->the_completion;
3908     }
3909 }
3910
3911 #ifdef HAVE_PWD_H
3912
3913 static gint
3914 get_pwdb (CompletionState* cmpl_state)
3915 {
3916   struct passwd *pwd_ptr;
3917   gchar* buf_ptr;
3918   gchar *utf8;
3919   gint len = 0, i, count = 0;
3920
3921   if (cmpl_state->user_dir_name_buffer)
3922     return TRUE;
3923   setpwent ();
3924
3925   while ((pwd_ptr = getpwent ()) != NULL)
3926     {
3927       utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3928       len += strlen (utf8);
3929       g_free (utf8);
3930       utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3931       len += strlen (utf8);
3932       g_free (utf8);
3933       len += 2;
3934       count += 1;
3935     }
3936
3937   setpwent ();
3938
3939   cmpl_state->user_dir_name_buffer = g_new (gchar, len);
3940   cmpl_state->user_directories = g_new (CompletionUserDir, count);
3941   cmpl_state->user_directories_len = count;
3942
3943   buf_ptr = cmpl_state->user_dir_name_buffer;
3944
3945   for (i = 0; i < count; i += 1)
3946     {
3947       pwd_ptr = getpwent ();
3948       if (!pwd_ptr)
3949         {
3950           cmpl_errno = errno;
3951           goto error;
3952         }
3953
3954       utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3955       strcpy (buf_ptr, utf8);
3956       g_free (utf8);
3957
3958       cmpl_state->user_directories[i].login = buf_ptr;
3959
3960       buf_ptr += strlen (buf_ptr);
3961       buf_ptr += 1;
3962
3963       utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3964       strcpy (buf_ptr, utf8);
3965       g_free (utf8);
3966
3967       cmpl_state->user_directories[i].homedir = buf_ptr;
3968
3969       buf_ptr += strlen (buf_ptr);
3970       buf_ptr += 1;
3971     }
3972
3973   qsort (cmpl_state->user_directories,
3974          cmpl_state->user_directories_len,
3975          sizeof (CompletionUserDir),
3976          compare_user_dir);
3977
3978   endpwent ();
3979
3980   return TRUE;
3981
3982 error:
3983
3984   if (cmpl_state->user_dir_name_buffer)
3985     g_free (cmpl_state->user_dir_name_buffer);
3986   if (cmpl_state->user_directories)
3987     g_free (cmpl_state->user_directories);
3988
3989   cmpl_state->user_dir_name_buffer = NULL;
3990   cmpl_state->user_directories = NULL;
3991
3992   return FALSE;
3993 }
3994
3995 static gint
3996 compare_user_dir (const void *a,
3997                   const void *b)
3998 {
3999   return strcmp ((((CompletionUserDir*)a))->login,
4000                  (((CompletionUserDir*)b))->login);
4001 }
4002
4003 #endif
4004
4005 static gint
4006 compare_cmpl_dir (const void *a,
4007                   const void *b)
4008 {
4009   
4010   return strcmp (((CompletionDirEntry*)a)->sort_key,
4011                  (((CompletionDirEntry*)b))->sort_key);
4012 }
4013
4014 static gint
4015 cmpl_state_okay (CompletionState* cmpl_state)
4016 {
4017   return  cmpl_state && cmpl_state->reference_dir;
4018 }
4019
4020 static const gchar*
4021 cmpl_strerror (gint err)
4022 {
4023   if (err == CMPL_ERRNO_TOO_LONG)
4024     return _("Name too long");
4025   else if (err == CMPL_ERRNO_DID_NOT_CONVERT)
4026     return _("Couldn't convert filename");
4027   else
4028     return g_strerror (err);
4029 }
4030
4031 #ifdef G_OS_WIN32
4032
4033 /* DLL ABI stability backward compatibility versions */
4034
4035 #undef gtk_file_selection_get_filename
4036
4037 G_CONST_RETURN gchar*
4038 gtk_file_selection_get_filename (GtkFileSelection *filesel)
4039 {
4040   static gchar retval[MAXPATHLEN*2+1];
4041   gchar *tem;
4042
4043   tem = g_locale_from_utf8 (gtk_file_selection_get_filename_utf8 (filesel),
4044                             -1, NULL, NULL, NULL);
4045
4046   strncpy (retval, tem, sizeof (retval) - 1);
4047   retval[sizeof (retval) - 1] = '\0';
4048   g_free (tem);
4049
4050   return retval;
4051 }
4052
4053 #undef gtk_file_selection_set_filename
4054
4055 void
4056 gtk_file_selection_set_filename (GtkFileSelection *filesel,
4057                                  const gchar      *filename)
4058 {
4059   gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL);
4060   gtk_file_selection_set_filename_utf8 (filesel, utf8_filename);
4061   g_free (utf8_filename);
4062 }
4063
4064 #undef gtk_file_selection_get_selections
4065
4066 gchar **
4067 gtk_file_selection_get_selections (GtkFileSelection *filesel)
4068 {
4069   int i = 0;
4070   gchar **selections = gtk_file_selection_get_selections_utf8 (filesel);
4071
4072   if (selections != NULL)
4073     while (selections[i] != NULL)
4074       {
4075         gchar *tem = selections[i];
4076         selections[i] = g_locale_from_utf8 (selections[i],
4077                                             -1, NULL, NULL, NULL);
4078         g_free (tem);
4079         i++;
4080       }
4081
4082   return selections;
4083 }
4084
4085 #endif /* G_OS_WIN32 */
4086
4087 #define __GTK_FILESEL_C__
4088 #include "gtkaliasdef.c"