]> Pileus Git - ~andy/gtk/blob - gtk/gtkfilesel.c
Use a GString instead of static buffer to reduce .bss by 8kb. (#336784)
[~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 GString *something;
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       if (!something)
1264         something = g_string_new (sys_filename);
1265       else
1266         g_string_assign (something, sys_filename);
1267       strncpy (something, sys_filename, sizeof (something) - 1);
1268       g_free (sys_filename);
1269
1270       return something->str;
1271     }
1272
1273   return nothing;
1274 }
1275
1276 void
1277 gtk_file_selection_complete (GtkFileSelection *filesel,
1278                              const gchar      *pattern)
1279 {
1280   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1281   g_return_if_fail (pattern != NULL);
1282
1283   if (filesel->selection_entry)
1284     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
1285   gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE, TRUE);
1286 }
1287
1288 static void
1289 gtk_file_selection_destroy (GtkObject *object)
1290 {
1291   GtkFileSelection *filesel;
1292   GList *list;
1293   HistoryCallbackArg *callback_arg;
1294   
1295   g_return_if_fail (GTK_IS_FILE_SELECTION (object));
1296   
1297   filesel = GTK_FILE_SELECTION (object);
1298   
1299   if (filesel->fileop_dialog)
1300     {
1301       gtk_widget_destroy (filesel->fileop_dialog);
1302       filesel->fileop_dialog = NULL;
1303     }
1304   
1305   if (filesel->history_list)
1306     {
1307       list = filesel->history_list;
1308       while (list)
1309         {
1310           callback_arg = list->data;
1311           g_free (callback_arg->directory);
1312           g_free (callback_arg);
1313           list = list->next;
1314         }
1315       g_list_free (filesel->history_list);
1316       filesel->history_list = NULL;
1317     }
1318
1319   if (filesel->cmpl_state)
1320     {
1321       cmpl_free_state (filesel->cmpl_state);
1322       filesel->cmpl_state = NULL;
1323     }
1324  
1325   if (filesel->selected_names)
1326     {
1327       free_selected_names (filesel->selected_names);
1328       filesel->selected_names = NULL;
1329     } 
1330
1331   if (filesel->last_selected)
1332     {
1333       g_free (filesel->last_selected);
1334       filesel->last_selected = NULL;
1335     }
1336
1337   GTK_OBJECT_CLASS (parent_class)->destroy (object);
1338 }
1339
1340 static void
1341 gtk_file_selection_map (GtkWidget *widget)
1342 {
1343   GtkFileSelection *filesel = GTK_FILE_SELECTION (widget);
1344
1345   /* Refresh the contents */
1346   gtk_file_selection_populate (filesel, "", FALSE, FALSE);
1347   
1348   GTK_WIDGET_CLASS (parent_class)->map (widget);
1349 }
1350
1351 static void
1352 gtk_file_selection_finalize (GObject *object)
1353 {
1354   GtkFileSelection *filesel = GTK_FILE_SELECTION (object);
1355
1356   g_free (filesel->fileop_file);
1357
1358   G_OBJECT_CLASS (parent_class)->finalize (object);
1359 }
1360
1361 /* Begin file operations callbacks */
1362
1363 static void
1364 gtk_file_selection_fileop_error (GtkFileSelection *fs,
1365                                  gchar            *error_message)
1366 {
1367   GtkWidget *dialog;
1368     
1369   g_return_if_fail (error_message != NULL);
1370
1371   /* main dialog */
1372   dialog = gtk_message_dialog_new (GTK_WINDOW (fs),
1373                                    GTK_DIALOG_DESTROY_WITH_PARENT,
1374                                    GTK_MESSAGE_ERROR,
1375                                    GTK_BUTTONS_OK,
1376                                    "%s", error_message);
1377
1378   /* yes, we free it */
1379   g_free (error_message);
1380
1381   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1382  
1383   g_signal_connect_swapped (dialog, "response",
1384                             G_CALLBACK (gtk_widget_destroy),
1385                             dialog);
1386
1387   gtk_widget_show (dialog);
1388 }
1389
1390 static void
1391 gtk_file_selection_fileop_destroy (GtkWidget *widget,
1392                                    gpointer   data)
1393 {
1394   GtkFileSelection *fs = data;
1395
1396   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1397   
1398   fs->fileop_dialog = NULL;
1399 }
1400
1401 static gboolean
1402 entry_is_empty (GtkEntry *entry)
1403 {
1404   const gchar *text = gtk_entry_get_text (entry);
1405   
1406   return *text == '\0';
1407 }
1408
1409 static void
1410 gtk_file_selection_fileop_entry_changed (GtkEntry   *entry,
1411                                          GtkWidget  *button)
1412 {
1413   gtk_widget_set_sensitive (button, !entry_is_empty (entry));
1414 }
1415
1416 static void
1417 gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
1418                                          gpointer   data)
1419 {
1420   GtkFileSelection *fs = data;
1421   const gchar *dirname;
1422   gchar *path;
1423   gchar *full_path;
1424   gchar *sys_full_path;
1425   gchar *buf;
1426   GError *error = NULL;
1427   CompletionState *cmpl_state;
1428   
1429   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1430
1431   dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1432   cmpl_state = (CompletionState*) fs->cmpl_state;
1433   path = cmpl_reference_position (cmpl_state);
1434   
1435   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
1436   sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1437   if (error)
1438     {
1439       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1440         buf = g_strdup_printf (_("The folder name \"%s\" contains symbols that are not allowed in filenames"), dirname);
1441       else
1442         buf = g_strdup_printf (_("Error creating folder \"%s\": %s\n%s"), dirname, error->message,
1443                                _("You probably used symbols not allowed in filenames."));
1444       gtk_file_selection_fileop_error (fs, buf);
1445       g_error_free (error);
1446       goto out;
1447     }
1448
1449   if (g_mkdir (sys_full_path, 0777) < 0)
1450     {
1451       buf = g_strdup_printf (_("Error creating folder \"%s\": %s\n"), dirname,
1452                              g_strerror (errno));
1453       gtk_file_selection_fileop_error (fs, buf);
1454     }
1455
1456  out:
1457   g_free (full_path);
1458   g_free (sys_full_path);
1459   
1460   gtk_widget_destroy (fs->fileop_dialog);
1461   gtk_file_selection_populate (fs, "", FALSE, FALSE);
1462 }
1463   
1464 static void
1465 gtk_file_selection_create_dir (GtkWidget *widget,
1466                                gpointer   data)
1467 {
1468   GtkFileSelection *fs = data;
1469   GtkWidget *label;
1470   GtkWidget *dialog;
1471   GtkWidget *vbox;
1472   GtkWidget *button;
1473
1474   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1475
1476   if (fs->fileop_dialog)
1477     return;
1478   
1479   /* main dialog */
1480   dialog = gtk_dialog_new ();
1481   fs->fileop_dialog = dialog;
1482   g_signal_connect (dialog, "destroy",
1483                     G_CALLBACK (gtk_file_selection_fileop_destroy),
1484                     fs);
1485   gtk_window_set_title (GTK_WINDOW (dialog), _("New Folder"));
1486   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1487   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1488
1489   /* If file dialog is grabbed, grab option dialog */
1490   /* When option dialog is closed, file dialog will be grabbed again */
1491   if (GTK_WINDOW (fs)->modal)
1492       gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1493
1494   vbox = gtk_vbox_new (FALSE, 0);
1495   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1496   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1497                      FALSE, FALSE, 0);
1498   gtk_widget_show( vbox);
1499   
1500   label = gtk_label_new_with_mnemonic (_("_Folder name:"));
1501   gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
1502   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1503   gtk_widget_show (label);
1504
1505   /*  The directory entry widget  */
1506   fs->fileop_entry = gtk_entry_new ();
1507   gtk_label_set_mnemonic_widget (GTK_LABEL (label), fs->fileop_entry);
1508   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
1509                       TRUE, TRUE, 5);
1510   GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1511   gtk_entry_set_activates_default (GTK_ENTRY (fs->fileop_entry), TRUE); 
1512   gtk_widget_show (fs->fileop_entry);
1513   
1514   /* buttons */
1515   button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
1516                                   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1517   g_signal_connect_swapped (button, "clicked",
1518                             G_CALLBACK (gtk_widget_destroy),
1519                             dialog);
1520
1521   gtk_widget_grab_focus (fs->fileop_entry);
1522
1523   button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
1524                                   _("C_reate"), GTK_RESPONSE_OK);
1525   gtk_widget_set_sensitive (button, FALSE);
1526   g_signal_connect (button, "clicked",
1527                     G_CALLBACK (gtk_file_selection_create_dir_confirmed),
1528                     fs);
1529   g_signal_connect (fs->fileop_entry, "changed",
1530                     G_CALLBACK (gtk_file_selection_fileop_entry_changed),
1531                     button);
1532
1533   gtk_widget_grab_default (button);
1534   
1535   gtk_widget_show (dialog);
1536 }
1537
1538 static void
1539 gtk_file_selection_delete_file_response (GtkDialog *dialog, 
1540                                          gint       response_id,
1541                                          gpointer   data)
1542 {
1543   GtkFileSelection *fs = data;
1544   CompletionState *cmpl_state;
1545   gchar *path;
1546   gchar *full_path;
1547   gchar *sys_full_path;
1548   GError *error = NULL;
1549   gchar *buf;
1550   
1551   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1552
1553   if (response_id != GTK_RESPONSE_OK)
1554     {
1555       gtk_widget_destroy (GTK_WIDGET (dialog));
1556       return;
1557     }
1558
1559   cmpl_state = (CompletionState*) fs->cmpl_state;
1560   path = cmpl_reference_position (cmpl_state);
1561   
1562   full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1563   sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1564   if (error)
1565     {
1566       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1567         buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
1568                                fs->fileop_file);
1569       else
1570         buf = g_strdup_printf (_("Error deleting file \"%s\": %s\n%s"),
1571                                fs->fileop_file, error->message,
1572                                _("It probably contains symbols not allowed in filenames."));
1573       
1574       gtk_file_selection_fileop_error (fs, buf);
1575       g_error_free (error);
1576       goto out;
1577     }
1578
1579   if (g_unlink (sys_full_path) < 0) 
1580     {
1581       buf = g_strdup_printf (_("Error deleting file \"%s\": %s"),
1582                              fs->fileop_file, g_strerror (errno));
1583       gtk_file_selection_fileop_error (fs, buf);
1584     }
1585   
1586  out:
1587   g_free (full_path);
1588   g_free (sys_full_path);
1589   
1590   gtk_widget_destroy (fs->fileop_dialog);
1591   gtk_file_selection_populate (fs, "", FALSE, TRUE);
1592 }
1593
1594 static void
1595 gtk_file_selection_delete_file (GtkWidget *widget,
1596                                 gpointer   data)
1597 {
1598   GtkFileSelection *fs = data;
1599   GtkWidget *dialog;
1600   const gchar *filename;
1601   
1602   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1603
1604   if (fs->fileop_dialog)
1605     return;
1606
1607 #ifdef G_WITH_CYGWIN
1608   translate_win32_path (fs);
1609 #endif
1610
1611   filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1612   if (strlen (filename) < 1)
1613     return;
1614
1615   g_free (fs->fileop_file);
1616   fs->fileop_file = g_strdup (filename);
1617   
1618   /* main dialog */
1619   fs->fileop_dialog = dialog = 
1620     gtk_message_dialog_new (GTK_WINDOW (fs),
1621                             GTK_WINDOW (fs)->modal ? GTK_DIALOG_MODAL : 0,
1622                             GTK_MESSAGE_QUESTION,
1623                             GTK_BUTTONS_NONE,
1624                             _("Really delete file \"%s\"?"), filename);
1625
1626   g_signal_connect (dialog, "destroy",
1627                     G_CALLBACK (gtk_file_selection_fileop_destroy),
1628                     fs);
1629   gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1630   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1631   
1632   /* buttons */
1633   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1634                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1635                           GTK_STOCK_DELETE, GTK_RESPONSE_OK,
1636                           NULL);
1637
1638   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
1639
1640   g_signal_connect (dialog, "response",
1641                     G_CALLBACK (gtk_file_selection_delete_file_response),
1642                     fs);
1643   
1644   gtk_widget_show (dialog);
1645 }
1646
1647 static void
1648 gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
1649                                           gpointer   data)
1650 {
1651   GtkFileSelection *fs = data;
1652   gchar *buf;
1653   const gchar *file;
1654   gchar *path;
1655   gchar *new_filename;
1656   gchar *old_filename;
1657   gchar *sys_new_filename;
1658   gchar *sys_old_filename;
1659   CompletionState *cmpl_state;
1660   GError *error = NULL;
1661   
1662   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1663
1664   file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1665   cmpl_state = (CompletionState*) fs->cmpl_state;
1666   path = cmpl_reference_position (cmpl_state);
1667   
1668   new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
1669   old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1670
1671   sys_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, &error);
1672   if (error)
1673     {
1674       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1675         buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), new_filename);
1676       else
1677         buf = g_strdup_printf (_("Error renaming file to \"%s\": %s\n%s"),
1678                                new_filename, error->message,
1679                                _("You probably used symbols not allowed in filenames."));
1680       gtk_file_selection_fileop_error (fs, buf);
1681       g_error_free (error);
1682       goto out1;
1683     }
1684
1685   sys_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, &error);
1686   if (error)
1687     {
1688       if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1689         buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), old_filename);
1690       else
1691         buf = g_strdup_printf (_("Error renaming file \"%s\": %s\n%s"),
1692                                old_filename, error->message,
1693                                _("It probably contains symbols not allowed in filenames."));
1694       gtk_file_selection_fileop_error (fs, buf);
1695       g_error_free (error);
1696       goto out2;
1697     }
1698   
1699   if (g_rename (sys_old_filename, sys_new_filename) < 0) 
1700     {
1701       buf = g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
1702                              sys_old_filename, sys_new_filename,
1703                              g_strerror (errno));
1704       gtk_file_selection_fileop_error (fs, buf);
1705       goto out2;
1706     }
1707   
1708   gtk_file_selection_populate (fs, "", FALSE, FALSE);
1709   gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), file);
1710   
1711  out2:
1712   g_free (sys_old_filename);
1713
1714  out1:
1715   g_free (new_filename);
1716   g_free (old_filename);
1717   g_free (sys_new_filename);
1718   
1719   gtk_widget_destroy (fs->fileop_dialog);
1720 }
1721   
1722 static void
1723 gtk_file_selection_rename_file (GtkWidget *widget,
1724                                 gpointer   data)
1725 {
1726   GtkFileSelection *fs = data;
1727   GtkWidget *label;
1728   GtkWidget *dialog;
1729   GtkWidget *vbox;
1730   GtkWidget *button;
1731   gchar *buf;
1732   
1733   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1734
1735   if (fs->fileop_dialog)
1736           return;
1737
1738   g_free (fs->fileop_file);
1739   fs->fileop_file = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1740   if (strlen (fs->fileop_file) < 1)
1741     return;
1742   
1743   /* main dialog */
1744   fs->fileop_dialog = dialog = gtk_dialog_new ();
1745   g_signal_connect (dialog, "destroy",
1746                     G_CALLBACK (gtk_file_selection_fileop_destroy),
1747                     fs);
1748   gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
1749   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1750   gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1751
1752   /* If file dialog is grabbed, grab option dialog */
1753   /* When option dialog  closed, file dialog will be grabbed again */
1754   if (GTK_WINDOW (fs)->modal)
1755     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1756   
1757   vbox = gtk_vbox_new (FALSE, 0);
1758   gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1759   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1760                       FALSE, FALSE, 0);
1761   gtk_widget_show(vbox);
1762   
1763   buf = g_strdup_printf (_("Rename file \"%s\" to:"), fs->fileop_file);
1764   label = gtk_label_new (buf);
1765   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1766   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1767   gtk_widget_show (label);
1768   g_free (buf);
1769
1770   /* New filename entry */
1771   fs->fileop_entry = gtk_entry_new ();
1772   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry, 
1773                       TRUE, TRUE, 5);
1774   GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1775   gtk_entry_set_activates_default (GTK_ENTRY (fs->fileop_entry), TRUE); 
1776   gtk_widget_show (fs->fileop_entry);
1777   
1778   gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
1779   gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
1780                               0, strlen (fs->fileop_file));
1781
1782   /* buttons */
1783   button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
1784                                   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1785   g_signal_connect_swapped (button, "clicked",
1786                             G_CALLBACK (gtk_widget_destroy),
1787                             dialog);
1788
1789   gtk_widget_grab_focus (fs->fileop_entry);
1790
1791   button = gtk_dialog_add_button (GTK_DIALOG (dialog), 
1792                                   _("_Rename"), GTK_RESPONSE_OK);
1793   g_signal_connect (button, "clicked",
1794                     G_CALLBACK (gtk_file_selection_rename_file_confirmed),
1795                     fs);
1796   g_signal_connect (fs->fileop_entry, "changed",
1797                     G_CALLBACK (gtk_file_selection_fileop_entry_changed),
1798                     button);
1799
1800   gtk_widget_grab_default (button);
1801   
1802   gtk_widget_show (dialog);
1803 }
1804
1805 static gint
1806 gtk_file_selection_insert_text (GtkWidget   *widget,
1807                                 const gchar *new_text,
1808                                 gint         new_text_length,
1809                                 gint        *position,
1810                                 gpointer     user_data)
1811 {
1812   gchar *filename;
1813
1814   filename = g_filename_from_utf8 (new_text, new_text_length, NULL, NULL, NULL);
1815
1816   if (!filename)
1817     {
1818       gdk_display_beep (gtk_widget_get_display (widget));
1819       g_signal_stop_emission_by_name (widget, "insert_text");
1820       return FALSE;
1821     }
1822   
1823   g_free (filename);
1824   
1825   return TRUE;
1826 }
1827
1828 static void
1829 gtk_file_selection_update_fileops (GtkFileSelection *fs)
1830 {
1831   gboolean sensitive;
1832
1833   if (!fs->selection_entry)
1834     return;
1835
1836   sensitive = !entry_is_empty (GTK_ENTRY (fs->selection_entry));
1837
1838   if (fs->fileop_del_file)
1839     gtk_widget_set_sensitive (fs->fileop_del_file, sensitive);
1840   
1841   if (fs->fileop_ren_file)
1842     gtk_widget_set_sensitive (fs->fileop_ren_file, sensitive);
1843 }
1844
1845 static gint
1846 gtk_file_selection_key_press (GtkWidget   *widget,
1847                               GdkEventKey *event,
1848                               gpointer     user_data)
1849 {
1850   GtkFileSelection *fs;
1851   char *text;
1852
1853   g_return_val_if_fail (widget != NULL, FALSE);
1854   g_return_val_if_fail (event != NULL, FALSE);
1855
1856   if ((event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab) &&
1857       (event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
1858     {
1859       fs = GTK_FILE_SELECTION (user_data);
1860 #ifdef G_WITH_CYGWIN
1861       translate_win32_path (fs);
1862 #endif
1863       text = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1864
1865       gtk_file_selection_populate (fs, text, TRUE, TRUE);
1866
1867       g_free (text);
1868
1869       return TRUE;
1870     }
1871
1872   return FALSE;
1873 }
1874
1875 static void
1876 gtk_file_selection_history_callback (GtkWidget *widget,
1877                                      gpointer   data)
1878 {
1879   GtkFileSelection *fs = data;
1880   HistoryCallbackArg *callback_arg;
1881   GList *list;
1882
1883   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1884
1885   list = fs->history_list;
1886   
1887   while (list) {
1888     callback_arg = list->data;
1889     
1890     if (callback_arg->menu_item == widget)
1891       {
1892         gtk_file_selection_populate (fs, callback_arg->directory, FALSE, FALSE);
1893         break;
1894       }
1895     
1896     list = list->next;
1897   }
1898 }
1899
1900 static void 
1901 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1902                                         gchar            *current_directory)
1903 {
1904   HistoryCallbackArg *callback_arg;
1905   GtkWidget *menu_item;
1906   GList *list;
1907   gchar *current_dir;
1908   gint dir_len;
1909   gint i;
1910   
1911   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1912   g_return_if_fail (current_directory != NULL);
1913   
1914   list = fs->history_list;
1915
1916   if (fs->history_menu) 
1917     {
1918       while (list) {
1919         callback_arg = list->data;
1920         g_free (callback_arg->directory);
1921         g_free (callback_arg);
1922         list = list->next;
1923       }
1924       g_list_free (fs->history_list);
1925       fs->history_list = NULL;
1926       
1927       gtk_widget_destroy (fs->history_menu);
1928     }
1929   
1930   fs->history_menu = gtk_menu_new ();
1931
1932   current_dir = g_strdup (current_directory);
1933
1934   dir_len = strlen (current_dir);
1935
1936   for (i = dir_len; i >= 0; i--)
1937     {
1938       /* the i == dir_len is to catch the full path for the first 
1939        * entry. */
1940       if ( (current_dir[i] == G_DIR_SEPARATOR) || (i == dir_len))
1941         {
1942           /* another small hack to catch the full path */
1943           if (i != dir_len) 
1944                   current_dir[i + 1] = '\0';
1945 #ifdef G_WITH_CYGWIN
1946           if (!strcmp (current_dir, "//"))
1947             continue;
1948 #endif
1949           menu_item = gtk_menu_item_new_with_label (current_dir);
1950           
1951           callback_arg = g_new (HistoryCallbackArg, 1);
1952           callback_arg->menu_item = menu_item;
1953           
1954           /* since the autocompletion gets confused if you don't 
1955            * supply a trailing '/' on a dir entry, set the full
1956            * (current) path to "" which just refreshes the filesel */
1957           if (dir_len == i)
1958             {
1959               callback_arg->directory = g_strdup ("");
1960             }
1961           else
1962             {
1963               callback_arg->directory = g_strdup (current_dir);
1964             }
1965           
1966           fs->history_list = g_list_append (fs->history_list, callback_arg);
1967           
1968           g_signal_connect (menu_item, "activate",
1969                             G_CALLBACK (gtk_file_selection_history_callback),
1970                             fs);
1971           gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
1972           gtk_widget_show (menu_item);
1973         }
1974     }
1975
1976   gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown), 
1977                             fs->history_menu);
1978   g_free (current_dir);
1979 }
1980
1981 static gchar *
1982 get_real_filename (gchar    *filename,
1983                    gboolean  free_old)
1984 {
1985 #ifdef G_WITH_CYGWIN
1986   /* Check to see if the selection was a drive selector */
1987   if (isalpha (filename[0]) && (filename[1] == ':'))
1988     {
1989       gchar temp_filename[MAX_PATH];
1990       int len;
1991
1992       cygwin_conv_to_posix_path (filename, temp_filename);
1993
1994       /* we need trailing '/'. */
1995       len = strlen (temp_filename);
1996       if (len > 0 && temp_filename[len-1] != '/')
1997         {
1998           temp_filename[len]   = '/';
1999           temp_filename[len+1] = '\0';
2000         }
2001       
2002       if (free_old)
2003         g_free (filename);
2004
2005       return g_strdup (temp_filename);
2006     }
2007 #endif /* G_WITH_CYGWIN */
2008   return filename;
2009 }
2010
2011 static void
2012 gtk_file_selection_file_activate (GtkTreeView       *tree_view,
2013                                   GtkTreePath       *path,
2014                                   GtkTreeViewColumn *column,
2015                                   gpointer           user_data)
2016 {
2017   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
2018   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
2019   GtkTreeIter iter;  
2020   gchar *filename;
2021   
2022   gtk_tree_model_get_iter (model, &iter, path);
2023   gtk_tree_model_get (model, &iter, FILE_COLUMN, &filename, -1);
2024   filename = get_real_filename (filename, TRUE);
2025   gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
2026   gtk_button_clicked (GTK_BUTTON (fs->ok_button));
2027
2028   g_free (filename);
2029 }
2030
2031 static void
2032 gtk_file_selection_dir_activate (GtkTreeView       *tree_view,
2033                                  GtkTreePath       *path,
2034                                  GtkTreeViewColumn *column,
2035                                  gpointer           user_data)
2036 {
2037   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
2038   GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
2039   GtkTreeIter iter;
2040   gchar *filename;
2041
2042   gtk_tree_model_get_iter (model, &iter, path);
2043   gtk_tree_model_get (model, &iter, DIR_COLUMN, &filename, -1);
2044   filename = get_real_filename (filename, TRUE);
2045   gtk_file_selection_populate (fs, filename, FALSE, FALSE);
2046   g_free (filename);
2047 }
2048
2049 #ifdef G_PLATFORM_WIN32
2050
2051 static void
2052 win32_gtk_add_drives_to_dir_list (GtkListStore *model)
2053 {
2054   gchar *textPtr;
2055   gchar buffer[128];
2056   char formatBuffer[128];
2057   GtkTreeIter iter;
2058
2059   /* Get the drives string */
2060   GetLogicalDriveStrings (sizeof (buffer), buffer);
2061
2062   /* Add the drives as necessary */
2063   textPtr = buffer;
2064   while (*textPtr != '\0')
2065     {
2066       /* Ignore floppies (?) */
2067       if (GetDriveType (textPtr) != DRIVE_REMOVABLE)
2068         {
2069           /* Build the actual displayable string */
2070           g_snprintf (formatBuffer, sizeof (formatBuffer), "%c:\\", toupper (textPtr[0]));
2071
2072           /* Add to the list */
2073           gtk_list_store_append (model, &iter);
2074           gtk_list_store_set (model, &iter, DIR_COLUMN, formatBuffer, -1);
2075         }
2076       textPtr += (strlen (textPtr) + 1);
2077     }
2078 }
2079 #endif
2080
2081 static gchar *
2082 escape_underscores (const gchar *str)
2083 {
2084   GString *result = g_string_new (NULL);
2085   while (*str)
2086     {
2087       if (*str == '_')
2088         g_string_append_c (result, '_');
2089
2090       g_string_append_c (result, *str);
2091       str++;
2092     }
2093
2094   return g_string_free (result, FALSE);
2095 }
2096
2097 static void
2098 gtk_file_selection_populate (GtkFileSelection *fs,
2099                              gchar            *rel_path,
2100                              gboolean          try_complete,
2101                              gboolean          reset_entry)
2102 {
2103   CompletionState *cmpl_state;
2104   PossibleCompletion* poss;
2105   GtkTreeIter iter;
2106   GtkListStore *dir_model;
2107   GtkListStore *file_model;
2108   gchar* filename;
2109   gchar* rem_path = rel_path;
2110   gchar* sel_text;
2111   gint did_recurse = FALSE;
2112   gint possible_count = 0;
2113   
2114   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
2115
2116   cmpl_state = (CompletionState*) fs->cmpl_state;
2117   poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
2118
2119   if (!cmpl_state_okay (cmpl_state))
2120     {
2121       /* Something went wrong. */
2122       gtk_file_selection_abort (fs);
2123       return;
2124     }
2125
2126   g_assert (cmpl_state->reference_dir);
2127
2128   dir_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->dir_list)));
2129   file_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->file_list)));
2130
2131   gtk_list_store_clear (dir_model);
2132   gtk_list_store_clear (file_model);
2133
2134   /* Set the dir list to include ./ and ../ */
2135   gtk_list_store_append (dir_model, &iter);
2136   gtk_list_store_set (dir_model, &iter, DIR_COLUMN, "." G_DIR_SEPARATOR_S, -1);
2137   gtk_list_store_append (dir_model, &iter);
2138   gtk_list_store_set (dir_model, &iter, DIR_COLUMN, ".." G_DIR_SEPARATOR_S, -1);
2139
2140   while (poss)
2141     {
2142       if (cmpl_is_a_completion (poss))
2143         {
2144           possible_count += 1;
2145
2146           filename = cmpl_this_completion (poss);
2147
2148           if (cmpl_is_directory (poss))
2149             {
2150               if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
2151                   strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
2152                 {
2153                   gtk_list_store_append (dir_model, &iter);
2154                   gtk_list_store_set (dir_model, &iter, DIR_COLUMN, filename, -1);
2155                 }
2156             }
2157           else
2158             {
2159               gtk_list_store_append (file_model, &iter);
2160               gtk_list_store_set (file_model, &iter, DIR_COLUMN, filename, -1);
2161             }
2162         }
2163
2164       poss = cmpl_next_completion (cmpl_state);
2165     }
2166
2167 #ifdef G_PLATFORM_WIN32
2168   /* For Windows, add drives as potential selections */
2169   win32_gtk_add_drives_to_dir_list (dir_model);
2170 #endif
2171
2172   /* File lists are set. */
2173
2174   g_assert (cmpl_state->reference_dir);
2175
2176   if (try_complete)
2177     {
2178
2179       /* User is trying to complete filenames, so advance the user's input
2180        * string to the updated_text, which is the common leading substring
2181        * of all possible completions, and if its a directory attempt
2182        * attempt completions in it. */
2183
2184       if (cmpl_updated_text (cmpl_state)[0])
2185         {
2186
2187           if (cmpl_updated_dir (cmpl_state))
2188             {
2189               gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
2190
2191               did_recurse = TRUE;
2192
2193               gtk_file_selection_populate (fs, dir_name, TRUE, TRUE);
2194
2195               g_free (dir_name);
2196             }
2197           else
2198             {
2199               if (fs->selection_entry)
2200                       gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
2201                                           cmpl_updated_text (cmpl_state));
2202             }
2203         }
2204       else
2205         {
2206           if (fs->selection_entry)
2207             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
2208         }
2209     }
2210   else if (reset_entry)
2211     {
2212       if (fs->selection_entry)
2213         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2214     }
2215
2216   if (!did_recurse)
2217     {
2218       if (fs->selection_entry && try_complete)
2219         gtk_editable_set_position (GTK_EDITABLE (fs->selection_entry), -1);
2220
2221       if (fs->selection_entry)
2222         {
2223           char *escaped = escape_underscores (cmpl_reference_position (cmpl_state));
2224           sel_text = g_strconcat (_("_Selection: "), escaped, NULL);
2225           g_free (escaped);
2226
2227           gtk_label_set_text_with_mnemonic (GTK_LABEL (fs->selection_text), sel_text);
2228           g_free (sel_text);
2229         }
2230
2231       if (fs->history_pulldown) 
2232         {
2233           gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
2234         }
2235       
2236     }
2237 }
2238
2239 static void
2240 gtk_file_selection_abort (GtkFileSelection *fs)
2241 {
2242   gchar err_buf[256];
2243
2244   g_snprintf (err_buf, sizeof (err_buf), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
2245
2246   /*  BEEP gdk_beep();  */
2247
2248   if (fs->selection_entry)
2249     gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
2250 }
2251
2252 /**
2253  * gtk_file_selection_set_select_multiple:
2254  * @filesel: a #GtkFileSelection
2255  * @select_multiple: whether or not the user is allowed to select multiple
2256  * files in the file list.
2257  *
2258  * Sets whether the user is allowed to select multiple files in the file list.
2259  * Use gtk_file_selection_get_selections () to get the list of selected files.
2260  **/
2261 void
2262 gtk_file_selection_set_select_multiple (GtkFileSelection *filesel,
2263                                         gboolean          select_multiple)
2264 {
2265   GtkTreeSelection *sel;
2266   GtkSelectionMode mode;
2267
2268   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
2269
2270   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2271
2272   mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE;
2273
2274   if (mode != gtk_tree_selection_get_mode (sel))
2275     {
2276       gtk_tree_selection_set_mode (sel, mode);
2277
2278       g_object_notify (G_OBJECT (filesel), "select-multiple");
2279     }
2280 }
2281
2282 /**
2283  * gtk_file_selection_get_select_multiple:
2284  * @filesel: a #GtkFileSelection
2285  *
2286  * Determines whether or not the user is allowed to select multiple files in
2287  * the file list. See gtk_file_selection_set_select_multiple().
2288  *
2289  * Return value: %TRUE if the user is allowed to select multiple files in the
2290  * file list
2291  **/
2292 gboolean
2293 gtk_file_selection_get_select_multiple (GtkFileSelection *filesel)
2294 {
2295   GtkTreeSelection *sel;
2296
2297   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), FALSE);
2298
2299   sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2300   return (gtk_tree_selection_get_mode (sel) == GTK_SELECTION_MULTIPLE);
2301 }
2302
2303 static void
2304 multiple_changed_foreach (GtkTreeModel *model,
2305                           GtkTreePath  *path,
2306                           GtkTreeIter  *iter,
2307                           gpointer      data)
2308 {
2309   GPtrArray *names = data;
2310   gchar *filename;
2311
2312   gtk_tree_model_get (model, iter, FILE_COLUMN, &filename, -1);
2313
2314   g_ptr_array_add (names, filename);
2315 }
2316
2317 static void
2318 free_selected_names (GPtrArray *names)
2319 {
2320   gint i;
2321
2322   for (i = 0; i < names->len; i++)
2323     g_free (g_ptr_array_index (names, i));
2324
2325   g_ptr_array_free (names, TRUE);
2326 }
2327
2328 static void
2329 gtk_file_selection_file_changed (GtkTreeSelection *selection,
2330                                  gpointer          user_data)
2331 {
2332   GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
2333   GPtrArray *new_names;
2334   gchar *filename;
2335   const gchar *entry;
2336   gint index = -1;
2337
2338   new_names = g_ptr_array_sized_new (8);
2339
2340   gtk_tree_selection_selected_foreach (selection,
2341                                        multiple_changed_foreach,
2342                                        new_names);
2343
2344   /* nothing selected */
2345   if (new_names->len == 0)
2346     {
2347       g_ptr_array_free (new_names, TRUE);
2348
2349       if (fs->selected_names != NULL)
2350         {
2351           free_selected_names (fs->selected_names);
2352           fs->selected_names = NULL;
2353         }
2354
2355       goto maybe_clear_entry;
2356     }
2357
2358   if (new_names->len != 1)
2359     {
2360       GPtrArray *old_names = fs->selected_names;
2361
2362       if (old_names != NULL)
2363         {
2364           /* A common case is selecting a range of files from top to bottom,
2365            * so quickly check for that to avoid looping over the entire list
2366            */
2367           if (compare_utf8_filenames (g_ptr_array_index (old_names, old_names->len - 1),
2368                                       g_ptr_array_index (new_names, new_names->len - 1)) != 0)
2369             index = new_names->len - 1;
2370           else
2371             {
2372               gint i = 0, j = 0, cmp;
2373
2374               /* do a quick diff, stopping at the first file not in the
2375                * old list
2376                */
2377               while (i < old_names->len && j < new_names->len)
2378                 {
2379                   cmp = compare_utf8_filenames (g_ptr_array_index (old_names, i),
2380                                                 g_ptr_array_index (new_names, j));
2381                   if (cmp < 0)
2382                     {
2383                       i++;
2384                     }
2385                   else if (cmp == 0)
2386                     {
2387                       i++;
2388                       j++;
2389                     }
2390                   else if (cmp > 0)
2391                     {
2392                       index = j;
2393                       break;
2394                     }
2395                 }
2396
2397               /* we ran off the end of the old list */
2398               if (index == -1 && i < new_names->len)
2399                 index = j;
2400             }
2401         }
2402       else
2403         {
2404           /* A phantom anchor still exists at the point where the last item
2405            * was selected, which is used for subsequent range selections.
2406            * So search up from there.
2407            */
2408           if (fs->last_selected &&
2409               compare_utf8_filenames (fs->last_selected,
2410                                       g_ptr_array_index (new_names, 0)) == 0)
2411             index = new_names->len - 1;
2412           else
2413             index = 0;
2414         }
2415     }
2416   else
2417     index = 0;
2418
2419   if (fs->selected_names != NULL)
2420     free_selected_names (fs->selected_names);
2421
2422   fs->selected_names = new_names;
2423
2424   if (index != -1)
2425     {
2426       if (fs->last_selected != NULL)
2427         g_free (fs->last_selected);
2428
2429       fs->last_selected = g_strdup (g_ptr_array_index (new_names, index));
2430       filename = get_real_filename (fs->last_selected, FALSE);
2431
2432       gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
2433
2434       if (filename != fs->last_selected)
2435         g_free (filename);
2436       
2437       return;
2438     }
2439   
2440 maybe_clear_entry:
2441
2442   entry = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
2443   if ((entry != NULL) && (fs->last_selected != NULL) &&
2444       (compare_utf8_filenames (entry, fs->last_selected) == 0))
2445     gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2446 }
2447
2448 /**
2449  * gtk_file_selection_get_selections:
2450  * @filesel: a #GtkFileSelection
2451  *
2452  * Retrieves the list of file selections the user has made in the dialog box.
2453  * This function is intended for use when the user can select multiple files
2454  * in the file list. 
2455  *
2456  * The filenames are in the GLib file name encoding. To convert to
2457  * UTF-8, call g_filename_to_utf8() on each string.
2458  *
2459  * Return value: a newly-allocated %NULL-terminated array of strings. Use
2460  * g_strfreev() to free it.
2461  **/
2462 gchar **
2463 gtk_file_selection_get_selections (GtkFileSelection *filesel)
2464 {
2465   GPtrArray *names;
2466   gchar **selections;
2467   gchar *filename, *dirname;
2468   gchar *current, *buf;
2469   gint i, count;
2470   gboolean unselected_entry;
2471
2472   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), NULL);
2473
2474   filename = g_strdup (gtk_file_selection_get_filename (filesel));
2475
2476   if (strlen (filename) == 0)
2477     {
2478       g_free (filename);
2479       return NULL;
2480     }
2481
2482   names = filesel->selected_names;
2483
2484   if (names != NULL)
2485     selections = g_new (gchar *, names->len + 2);
2486   else
2487     selections = g_new (gchar *, 2);
2488
2489   count = 0;
2490   unselected_entry = TRUE;
2491
2492   if (names != NULL)
2493     {
2494       dirname = g_path_get_dirname (filename);
2495
2496       if ((names->len >= 1) && 
2497           (strcmp (gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry)), "") == 0))
2498         { /* multiple files are selected and last selection was removed via ctrl click */
2499           g_free (dirname);
2500           dirname = g_strdup (filename); /* as gtk_file_selection_get_filename returns dir 
2501                                             if no file is selected */
2502           unselected_entry = FALSE;
2503         }
2504
2505       for (i = 0; i < names->len; i++)
2506         {
2507           buf = g_filename_from_utf8 (g_ptr_array_index (names, i), -1,
2508                                       NULL, NULL, NULL);
2509           current = g_build_filename (dirname, buf, NULL);
2510           g_free (buf);
2511
2512           selections[count++] = current;
2513
2514           if (unselected_entry && compare_sys_filenames (current, filename) == 0)
2515             unselected_entry = FALSE;
2516         }
2517
2518       g_free (dirname);
2519     }
2520
2521   if (unselected_entry)
2522     selections[count++] = filename;
2523   else
2524     g_free (filename);
2525
2526   selections[count] = NULL;
2527
2528   return selections;
2529 }
2530
2531 /**********************************************************************/
2532 /*                        External Interface                          */
2533 /**********************************************************************/
2534
2535 /* The four completion state selectors
2536  */
2537 static gchar*
2538 cmpl_updated_text (CompletionState *cmpl_state)
2539 {
2540   return cmpl_state->updated_text;
2541 }
2542
2543 static gboolean
2544 cmpl_updated_dir (CompletionState *cmpl_state)
2545 {
2546   return cmpl_state->re_complete;
2547 }
2548
2549 static gchar*
2550 cmpl_reference_position (CompletionState *cmpl_state)
2551 {
2552   return cmpl_state->reference_dir->fullname;
2553 }
2554
2555 #if 0
2556 /* This doesn't work currently and would require changes
2557  * to fnmatch.c to get working.
2558  */
2559 static gint
2560 cmpl_last_valid_char (CompletionState *cmpl_state)
2561 {
2562   return cmpl_state->last_valid_char;
2563 }
2564 #endif
2565
2566 static gchar*
2567 cmpl_completion_fullname (const gchar     *text,
2568                           CompletionState *cmpl_state)
2569 {
2570   if (!cmpl_state_okay (cmpl_state))
2571     {
2572       return g_strdup ("");
2573     }
2574   else if (g_path_is_absolute (text))
2575     {
2576       return g_strdup (text);
2577     }
2578 #ifdef HAVE_PWD_H
2579   else if (text[0] == '~')
2580     {
2581       CompletionDir* dir;
2582       char* slash;
2583
2584       dir = open_user_dir (text, cmpl_state);
2585
2586       if (dir)
2587         {
2588           slash = strchr (text, G_DIR_SEPARATOR);
2589           
2590           /* slash may be NULL, that works too */
2591           return g_strconcat (dir->fullname, slash, NULL);
2592         }
2593     }
2594 #endif
2595   
2596   return g_build_filename (cmpl_state->reference_dir->fullname,
2597                            text,
2598                            NULL);
2599 }
2600
2601 /* The three completion selectors
2602  */
2603 static gchar*
2604 cmpl_this_completion (PossibleCompletion* pc)
2605 {
2606   return pc->text;
2607 }
2608
2609 static gboolean
2610 cmpl_is_directory (PossibleCompletion* pc)
2611 {
2612   return pc->is_directory;
2613 }
2614
2615 static gint
2616 cmpl_is_a_completion (PossibleCompletion* pc)
2617 {
2618   return pc->is_a_completion;
2619 }
2620
2621 /**********************************************************************/
2622 /*                       Construction, deletion                       */
2623 /**********************************************************************/
2624
2625 /* Get the nearest parent of the current directory for which
2626  * we can convert the filename into UTF-8. With paranoia.
2627  * Returns "." when all goes wrong.
2628  */
2629 static gchar *
2630 get_current_dir_utf8 (void)
2631 {
2632   gchar *dir = g_get_current_dir ();
2633   gchar *dir_utf8 = NULL;
2634
2635   while (TRUE)
2636     {
2637       gchar *last_slash;
2638
2639       dir_utf8 = g_filename_to_utf8 (dir, -1, NULL, NULL, NULL);
2640       if (dir_utf8)
2641         break;
2642
2643       last_slash = strrchr (dir, G_DIR_SEPARATOR);
2644       if (!last_slash)          /* g_get_current_dir() wasn't absolute! */
2645         break;
2646
2647       if (last_slash + 1 == g_path_skip_root (dir)) /* Parent directory is a root directory */
2648         {
2649           if (last_slash[1] == '\0') /* Root misencoded! */
2650             break;
2651           else
2652             last_slash[1] = '\0';
2653         }
2654       else
2655         last_slash[0] = '\0';
2656       
2657       g_assert (last_slash);
2658     }
2659
2660   g_free (dir);
2661   
2662   return dir_utf8 ? dir_utf8 : g_strdup (".");
2663 }
2664
2665 static CompletionState*
2666 cmpl_init_state (void)
2667 {
2668   gchar *utf8_cwd;
2669   CompletionState *new_state;
2670   gint tries = 0;
2671
2672   new_state = g_new (CompletionState, 1);
2673
2674   utf8_cwd = get_current_dir_utf8 ();
2675
2676 tryagain:
2677   tries++;
2678   new_state->reference_dir = NULL;
2679   new_state->completion_dir = NULL;
2680   new_state->active_completion_dir = NULL;
2681   new_state->directory_storage = NULL;
2682   new_state->directory_sent_storage = NULL;
2683   new_state->last_valid_char = 0;
2684   new_state->updated_text = g_new (gchar, MAXPATHLEN);
2685   new_state->updated_text_alloc = MAXPATHLEN;
2686   new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
2687   new_state->the_completion.text_alloc = MAXPATHLEN;
2688   new_state->user_dir_name_buffer = NULL;
2689   new_state->user_directories = NULL;
2690
2691   new_state->reference_dir = open_dir (utf8_cwd, new_state);
2692
2693   if (!new_state->reference_dir)
2694     {
2695       /* Directories changing from underneath us, grumble */
2696       strcpy (utf8_cwd, G_DIR_SEPARATOR_S);
2697       if (tries < 2)
2698         goto tryagain;
2699     }
2700
2701   g_free (utf8_cwd);
2702   return new_state;
2703 }
2704
2705 static void
2706 cmpl_free_dir_list (GList* dp0)
2707 {
2708   GList *dp = dp0;
2709
2710   while (dp)
2711     {
2712       free_dir (dp->data);
2713       dp = dp->next;
2714     }
2715
2716   g_list_free (dp0);
2717 }
2718
2719 static void
2720 cmpl_free_dir_sent_list (GList* dp0)
2721 {
2722   GList *dp = dp0;
2723
2724   while (dp)
2725     {
2726       free_dir_sent (dp->data);
2727       dp = dp->next;
2728     }
2729
2730   g_list_free (dp0);
2731 }
2732
2733 static void
2734 cmpl_free_state (CompletionState* cmpl_state)
2735 {
2736   g_return_if_fail (cmpl_state != NULL);
2737
2738   cmpl_free_dir_list (cmpl_state->directory_storage);
2739   cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
2740
2741   if (cmpl_state->user_dir_name_buffer)
2742     g_free (cmpl_state->user_dir_name_buffer);
2743   if (cmpl_state->user_directories)
2744     g_free (cmpl_state->user_directories);
2745   if (cmpl_state->the_completion.text)
2746     g_free (cmpl_state->the_completion.text);
2747   if (cmpl_state->updated_text)
2748     g_free (cmpl_state->updated_text);
2749
2750   g_free (cmpl_state);
2751 }
2752
2753 static void
2754 free_dir (CompletionDir* dir)
2755 {
2756   g_free (dir->cmpl_text);
2757   g_free (dir->fullname);
2758   g_free (dir);
2759 }
2760
2761 static void
2762 free_dir_sent (CompletionDirSent* sent)
2763 {
2764   gint i;
2765   for (i = 0; i < sent->entry_count; i++)
2766     {
2767       g_free (sent->entries[i].entry_name);
2768       g_free (sent->entries[i].sort_key);
2769     }
2770   g_free (sent->entries);
2771   g_free (sent);
2772 }
2773
2774 static void
2775 prune_memory_usage (CompletionState *cmpl_state)
2776 {
2777   GList* cdsl = cmpl_state->directory_sent_storage;
2778   GList* cdl = cmpl_state->directory_storage;
2779   GList* cdl0 = cdl;
2780   gint len = 0;
2781
2782   for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
2783     cdsl = cdsl->next;
2784
2785   if (cdsl)
2786     {
2787       cmpl_free_dir_sent_list (cdsl->next);
2788       cdsl->next = NULL;
2789     }
2790
2791   cmpl_state->directory_storage = NULL;
2792   while (cdl)
2793     {
2794       if (cdl->data == cmpl_state->reference_dir)
2795         cmpl_state->directory_storage = g_list_prepend (NULL, cdl->data);
2796       else
2797         free_dir (cdl->data);
2798       cdl = cdl->next;
2799     }
2800
2801   g_list_free (cdl0);
2802 }
2803
2804 /**********************************************************************/
2805 /*                        The main entrances.                         */
2806 /**********************************************************************/
2807
2808 static PossibleCompletion*
2809 cmpl_completion_matches (gchar           *text_to_complete,
2810                          gchar          **remaining_text,
2811                          CompletionState *cmpl_state)
2812 {
2813 #ifdef HAVE_PWD_H
2814   gchar* first_slash;
2815 #endif
2816   PossibleCompletion *poss;
2817
2818   prune_memory_usage (cmpl_state);
2819
2820   g_assert (text_to_complete != NULL);
2821
2822   cmpl_state->user_completion_index = -1;
2823   cmpl_state->last_completion_text = text_to_complete;
2824   cmpl_state->the_completion.text[0] = 0;
2825   cmpl_state->last_valid_char = 0;
2826   cmpl_state->updated_text_len = -1;
2827   cmpl_state->updated_text[0] = 0;
2828   cmpl_state->re_complete = FALSE;
2829
2830 #ifdef HAVE_PWD_H
2831   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2832
2833   if (text_to_complete[0] == '~' && !first_slash)
2834     {
2835       /* Text starts with ~ and there is no slash, show all the
2836        * home directory completions.
2837        */
2838       poss = attempt_homedir_completion (text_to_complete, cmpl_state);
2839
2840       update_cmpl (poss, cmpl_state);
2841
2842       return poss;
2843     }
2844 #endif
2845   cmpl_state->reference_dir =
2846     open_ref_dir (text_to_complete, remaining_text, cmpl_state);
2847
2848   if (!cmpl_state->reference_dir)
2849     return NULL;
2850
2851   cmpl_state->completion_dir =
2852     find_completion_dir (*remaining_text, remaining_text, cmpl_state);
2853
2854   cmpl_state->last_valid_char = *remaining_text - text_to_complete;
2855
2856   if (!cmpl_state->completion_dir)
2857     return NULL;
2858
2859   cmpl_state->completion_dir->cmpl_index = -1;
2860   cmpl_state->completion_dir->cmpl_parent = NULL;
2861   cmpl_state->completion_dir->cmpl_text = g_strdup (*remaining_text);
2862
2863   cmpl_state->active_completion_dir = cmpl_state->completion_dir;
2864
2865   cmpl_state->reference_dir = cmpl_state->completion_dir;
2866
2867   poss = attempt_file_completion (cmpl_state);
2868
2869   update_cmpl (poss, cmpl_state);
2870
2871   return poss;
2872 }
2873
2874 static PossibleCompletion*
2875 cmpl_next_completion (CompletionState* cmpl_state)
2876 {
2877   PossibleCompletion* poss = NULL;
2878
2879   cmpl_state->the_completion.text[0] = 0;
2880
2881 #ifdef HAVE_PWD_H
2882   if (cmpl_state->user_completion_index >= 0)
2883     poss = attempt_homedir_completion (cmpl_state->last_completion_text, cmpl_state);
2884   else
2885     poss = attempt_file_completion (cmpl_state);
2886 #else
2887   poss = attempt_file_completion (cmpl_state);
2888 #endif
2889
2890   update_cmpl (poss, cmpl_state);
2891
2892   return poss;
2893 }
2894
2895 /**********************************************************************/
2896 /*                       Directory Operations                         */
2897 /**********************************************************************/
2898
2899 /* Open the directory where completion will begin from, if possible. */
2900 static CompletionDir*
2901 open_ref_dir (gchar           *text_to_complete,
2902               gchar          **remaining_text,
2903               CompletionState *cmpl_state)
2904 {
2905   gchar* first_slash;
2906   CompletionDir *new_dir;
2907
2908   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2909
2910 #ifdef G_WITH_CYGWIN
2911   if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
2912     {
2913       char root_dir[5];
2914       g_snprintf (root_dir, sizeof (root_dir), "//%c", text_to_complete[2]);
2915
2916       new_dir = open_dir (root_dir, cmpl_state);
2917
2918       if (new_dir) {
2919         *remaining_text = text_to_complete + 4;
2920       }
2921     }
2922 #else
2923   if (FALSE)
2924     ;
2925 #endif
2926 #ifdef HAVE_PWD_H
2927   else if (text_to_complete[0] == '~')
2928     {
2929       new_dir = open_user_dir (text_to_complete, cmpl_state);
2930
2931       if (new_dir)
2932         {
2933           if (first_slash)
2934             *remaining_text = first_slash + 1;
2935           else
2936             *remaining_text = text_to_complete + strlen (text_to_complete);
2937         }
2938       else
2939         {
2940           return NULL;
2941         }
2942     }
2943 #endif
2944   else if (g_path_is_absolute (text_to_complete) || !cmpl_state->reference_dir)
2945     {
2946       gchar *tmp = g_strdup (text_to_complete);
2947       gchar *p;
2948
2949       p = tmp;
2950       while (*p && *p != '*' && *p != '?')
2951         p++;
2952
2953       *p = '\0';
2954       p = strrchr (tmp, G_DIR_SEPARATOR);
2955       if (p)
2956         {
2957           if (p + 1 == g_path_skip_root (tmp))
2958             p++;
2959       
2960           *p = '\0';
2961           new_dir = open_dir (tmp, cmpl_state);
2962
2963           if (new_dir)
2964             *remaining_text = text_to_complete + 
2965               ((p == g_path_skip_root (tmp)) ? (p - tmp) : (p + 1 - tmp));
2966         }
2967       else
2968         {
2969           /* If no possible candidates, use the cwd */
2970           gchar *utf8_curdir = get_current_dir_utf8 ();
2971
2972           new_dir = open_dir (utf8_curdir, cmpl_state);
2973
2974           if (new_dir)
2975             *remaining_text = text_to_complete;
2976
2977           g_free (utf8_curdir);
2978         }
2979
2980       g_free (tmp);
2981     }
2982   else
2983     {
2984       *remaining_text = text_to_complete;
2985
2986       new_dir = open_dir (cmpl_state->reference_dir->fullname, cmpl_state);
2987     }
2988
2989   if (new_dir)
2990     {
2991       new_dir->cmpl_index = -1;
2992       new_dir->cmpl_parent = NULL;
2993     }
2994
2995   return new_dir;
2996 }
2997
2998 #ifdef HAVE_PWD_H
2999
3000 /* open a directory by user name */
3001 static CompletionDir*
3002 open_user_dir (const gchar     *text_to_complete,
3003                CompletionState *cmpl_state)
3004 {
3005   CompletionDir *result;
3006   gchar *first_slash;
3007   gint cmp_len;
3008
3009   g_assert (text_to_complete && text_to_complete[0] == '~');
3010
3011   first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
3012
3013   if (first_slash)
3014     cmp_len = first_slash - text_to_complete - 1;
3015   else
3016     cmp_len = strlen (text_to_complete + 1);
3017
3018   if (!cmp_len)
3019     {
3020       /* ~/ */
3021       const gchar *homedir = g_get_home_dir ();
3022       gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
3023
3024       if (utf8_homedir)
3025         result = open_dir (utf8_homedir, cmpl_state);
3026       else
3027         result = NULL;
3028       
3029       g_free (utf8_homedir);
3030     }
3031   else
3032     {
3033       /* ~user/ */
3034       gchar* copy = g_new (char, cmp_len + 1);
3035       gchar *utf8_dir;
3036       struct passwd *pwd;
3037
3038       strncpy (copy, text_to_complete + 1, cmp_len);
3039       copy[cmp_len] = 0;
3040       pwd = getpwnam (copy);
3041       g_free (copy);
3042       if (!pwd)
3043         {
3044           cmpl_errno = errno;
3045           return NULL;
3046         }
3047       utf8_dir = g_filename_to_utf8 (pwd->pw_dir, -1, NULL, NULL, NULL);
3048       result = open_dir (utf8_dir, cmpl_state);
3049       g_free (utf8_dir);
3050     }
3051   return result;
3052 }
3053
3054 #endif
3055
3056 /* open a directory relative to the current relative directory */
3057 static CompletionDir*
3058 open_relative_dir (gchar           *dir_name,
3059                    CompletionDir   *dir,
3060                    CompletionState *cmpl_state)
3061 {
3062   CompletionDir *result;
3063   GString *path;
3064
3065   path = g_string_sized_new (dir->fullname_len + strlen (dir_name) + 10);
3066   g_string_assign (path, dir->fullname);
3067
3068   if (dir->fullname_len > 1
3069       && path->str[dir->fullname_len - 1] != G_DIR_SEPARATOR)
3070     g_string_append_c (path, G_DIR_SEPARATOR);
3071   g_string_append (path, dir_name);
3072
3073   result = open_dir (path->str, cmpl_state);
3074
3075   g_string_free (path, TRUE);
3076
3077   return result;
3078 }
3079
3080 /* after the cache lookup fails, really open a new directory */
3081 static CompletionDirSent*
3082 open_new_dir (gchar       *dir_name,
3083               struct stat *sbuf,
3084               gboolean     stat_subdirs)
3085 {
3086   CompletionDirSent *sent;
3087   GDir *directory;
3088   const char *dirent;
3089   GError *error = NULL;
3090   gint entry_count = 0;
3091   gint n_entries = 0;
3092   gint i;
3093   struct stat ent_sbuf;
3094   GString *path;
3095   gchar *sys_dir_name;
3096
3097   sent = g_new (CompletionDirSent, 1);
3098 #ifndef G_PLATFORM_WIN32
3099   sent->mtime = sbuf->st_mtime;
3100   sent->inode = sbuf->st_ino;
3101   sent->device = sbuf->st_dev;
3102 #endif
3103   path = g_string_sized_new (2*MAXPATHLEN + 10);
3104
3105   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
3106   if (!sys_dir_name)
3107     {
3108       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3109       return NULL;
3110     }
3111   
3112   directory = g_dir_open (sys_dir_name, 0, &error);
3113   if (!directory)
3114     {
3115       cmpl_errno = error->code; /* ??? */
3116       g_free (sys_dir_name);
3117       return NULL;
3118     }
3119
3120   while ((dirent = g_dir_read_name (directory)) != NULL)
3121     entry_count++;
3122   entry_count += 2;             /* For ".",".." */
3123
3124   sent->entries = g_new (CompletionDirEntry, entry_count);
3125   sent->entry_count = entry_count;
3126
3127   g_dir_rewind (directory);
3128
3129   for (i = 0; i < entry_count; i += 1)
3130     {
3131       GError *error = NULL;
3132
3133       if (i == 0)
3134         dirent = ".";
3135       else if (i == 1)
3136         dirent = "..";
3137       else
3138         {
3139           dirent = g_dir_read_name (directory);
3140           if (!dirent)          /* Directory changed */
3141             break;
3142         }
3143
3144       sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent, -1, NULL, NULL, &error);
3145       if (sent->entries[n_entries].entry_name == NULL
3146           || !g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL))
3147         {
3148           gchar *escaped_str = g_strescape (dirent, NULL);
3149           g_message (_("The filename \"%s\" couldn't be converted to UTF-8. "
3150                        "(try setting the environment variable G_FILENAME_ENCODING): %s"),
3151                      escaped_str,
3152                      error->message ? error->message : _("Invalid UTF-8"));
3153           g_free (escaped_str);
3154           g_clear_error (&error);
3155           continue;
3156         }
3157       g_clear_error (&error);
3158       
3159       sent->entries[n_entries].sort_key = g_utf8_collate_key (sent->entries[n_entries].entry_name, -1);
3160       
3161       g_string_assign (path, sys_dir_name);
3162       if (path->str[path->len-1] != G_DIR_SEPARATOR)
3163         {
3164           g_string_append_c (path, G_DIR_SEPARATOR);
3165         }
3166       g_string_append (path, dirent);
3167
3168       if (stat_subdirs)
3169         {
3170           /* Here we know path->str is a "system charset" string */
3171           if (g_stat (path->str, &ent_sbuf) >= 0 && S_ISDIR (ent_sbuf.st_mode))
3172             sent->entries[n_entries].is_dir = TRUE;
3173           else
3174             /* stat may fail, and we don't mind, since it could be a
3175              * dangling symlink. */
3176             sent->entries[n_entries].is_dir = FALSE;
3177         }
3178       else
3179         sent->entries[n_entries].is_dir = 1;
3180
3181       n_entries++;
3182     }
3183   sent->entry_count = n_entries;
3184   
3185   g_free (sys_dir_name);
3186   g_string_free (path, TRUE);
3187   qsort (sent->entries, sent->entry_count, sizeof (CompletionDirEntry), compare_cmpl_dir);
3188
3189   g_dir_close (directory);
3190
3191   return sent;
3192 }
3193
3194 #ifndef G_PLATFORM_WIN32
3195
3196 static gboolean
3197 check_dir (gchar       *dir_name,
3198            struct stat *result,
3199            gboolean    *stat_subdirs)
3200 {
3201   /* A list of directories that we know only contain other directories.
3202    * Trying to stat every file in these directories would be very
3203    * expensive.
3204    */
3205
3206   static struct {
3207     const gchar *name;
3208     gboolean present;
3209     struct stat statbuf;
3210   } no_stat_dirs[] = {
3211     { "/afs", FALSE, { 0 } },
3212     { "/net", FALSE, { 0 } }
3213   };
3214
3215   static const gint n_no_stat_dirs = G_N_ELEMENTS (no_stat_dirs);
3216   static gboolean initialized = FALSE;
3217   gchar *sys_dir_name;
3218   gint i;
3219
3220   if (!initialized)
3221     {
3222       initialized = TRUE;
3223       for (i = 0; i < n_no_stat_dirs; i++)
3224         {
3225           if (g_stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
3226             no_stat_dirs[i].present = TRUE;
3227         }
3228     }
3229
3230   sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
3231   if (!sys_dir_name)
3232     {
3233       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3234       return FALSE;
3235     }
3236   
3237   if (g_stat (sys_dir_name, result) < 0)
3238     {
3239       g_free (sys_dir_name);
3240       cmpl_errno = errno;
3241       return FALSE;
3242     }
3243   g_free (sys_dir_name);
3244
3245   *stat_subdirs = TRUE;
3246   for (i = 0; i < n_no_stat_dirs; i++)
3247     {
3248       if (no_stat_dirs[i].present &&
3249           (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
3250           (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
3251         {
3252           *stat_subdirs = FALSE;
3253           break;
3254         }
3255     }
3256
3257   return TRUE;
3258 }
3259
3260 #endif
3261
3262 /* open a directory by absolute pathname */
3263 static CompletionDir*
3264 open_dir (gchar           *dir_name,
3265           CompletionState *cmpl_state)
3266 {
3267 #ifndef G_PLATFORM_WIN32
3268   struct stat sbuf;
3269   gboolean stat_subdirs;
3270   GList* cdsl;
3271 #endif
3272   CompletionDirSent *sent;
3273
3274 #ifndef G_PLATFORM_WIN32
3275   if (!check_dir (dir_name, &sbuf, &stat_subdirs))
3276     return NULL;
3277
3278   cdsl = cmpl_state->directory_sent_storage;
3279
3280   while (cdsl)
3281     {
3282       sent = cdsl->data;
3283
3284       if (sent->inode == sbuf.st_ino &&
3285           sent->mtime == sbuf.st_mtime &&
3286           sent->device == sbuf.st_dev)
3287         return attach_dir (sent, dir_name, cmpl_state);
3288
3289       cdsl = cdsl->next;
3290     }
3291
3292   sent = open_new_dir (dir_name, &sbuf, stat_subdirs);
3293 #else
3294   sent = open_new_dir (dir_name, NULL, TRUE);
3295 #endif
3296
3297   if (sent)
3298     {
3299       cmpl_state->directory_sent_storage =
3300         g_list_prepend (cmpl_state->directory_sent_storage, sent);
3301
3302       return attach_dir (sent, dir_name, cmpl_state);
3303     }
3304
3305   return NULL;
3306 }
3307
3308 static CompletionDir*
3309 attach_dir (CompletionDirSent *sent,
3310             gchar             *dir_name,
3311             CompletionState   *cmpl_state)
3312 {
3313   CompletionDir* new_dir;
3314
3315   new_dir = g_new (CompletionDir, 1);
3316
3317   cmpl_state->directory_storage =
3318     g_list_prepend (cmpl_state->directory_storage, new_dir);
3319
3320   new_dir->sent = sent;
3321   new_dir->fullname = g_strdup (dir_name);
3322   new_dir->fullname_len = strlen (dir_name);
3323   new_dir->cmpl_text = NULL;
3324
3325   return new_dir;
3326 }
3327
3328 static gint
3329 correct_dir_fullname (CompletionDir* cmpl_dir)
3330 {
3331   gint length = strlen (cmpl_dir->fullname);
3332   gchar *first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3333   gchar *sys_filename;
3334   struct stat sbuf;
3335
3336   /* Does it end with /. (\.) ? */
3337   if (length >= 2 &&
3338       strcmp (cmpl_dir->fullname + length - 2, G_DIR_SEPARATOR_S ".") == 0)
3339     {
3340       /* Is it just the root directory (on a drive) ? */
3341       if (cmpl_dir->fullname + length - 2 == first_slash)
3342         {
3343           cmpl_dir->fullname[length - 1] = 0;
3344           cmpl_dir->fullname_len = length - 1;
3345           return TRUE;
3346         }
3347       else
3348         {
3349           cmpl_dir->fullname[length - 2] = 0;
3350         }
3351     }
3352
3353   /* Ends with /./ (\.\)? */
3354   else if (length >= 3 &&
3355            strcmp (cmpl_dir->fullname + length - 3,
3356                    G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) == 0)
3357     cmpl_dir->fullname[length - 2] = 0;
3358
3359   /* Ends with /.. (\..) ? */
3360   else if (length >= 3 &&
3361            strcmp (cmpl_dir->fullname + length - 3,
3362                    G_DIR_SEPARATOR_S "..") == 0)
3363     {
3364       /* Is it just /.. (X:\..)? */
3365       if (cmpl_dir->fullname + length - 3 == first_slash)
3366         {
3367           cmpl_dir->fullname[length - 2] = 0;
3368           cmpl_dir->fullname_len = length - 2;
3369           return TRUE;
3370         }
3371
3372       sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3373       if (!sys_filename)
3374         {
3375           cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3376           return FALSE;
3377         }
3378       
3379       if (g_stat (sys_filename, &sbuf) < 0)
3380         {
3381           g_free (sys_filename);
3382           cmpl_errno = errno;
3383           return FALSE;
3384         }
3385       g_free (sys_filename);
3386
3387       cmpl_dir->fullname[length - 3] = 0;
3388
3389       if (!correct_parent (cmpl_dir, &sbuf))
3390         return FALSE;
3391     }
3392
3393   /* Ends with /../ (\..\)? */
3394   else if (length >= 4 &&
3395            strcmp (cmpl_dir->fullname + length - 4,
3396                    G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) == 0)
3397     {
3398       /* Is it just /../ (X:\..\)? */
3399       if (cmpl_dir->fullname + length - 4 == first_slash)
3400         {
3401           cmpl_dir->fullname[length - 3] = 0;
3402           cmpl_dir->fullname_len = length - 3;
3403           return TRUE;
3404         }
3405
3406       sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3407       if (!sys_filename)
3408         {
3409           cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3410           return FALSE;
3411         }
3412       
3413       if (g_stat (sys_filename, &sbuf) < 0)
3414         {
3415           g_free (sys_filename);
3416           cmpl_errno = errno;
3417           return FALSE;
3418         }
3419       g_free (sys_filename);
3420
3421       cmpl_dir->fullname[length - 4] = 0;
3422
3423       if (!correct_parent (cmpl_dir, &sbuf))
3424         return FALSE;
3425     }
3426
3427   cmpl_dir->fullname_len = strlen (cmpl_dir->fullname);
3428
3429   return TRUE;
3430 }
3431
3432 static gint
3433 correct_parent (CompletionDir *cmpl_dir,
3434                 struct stat   *sbuf)
3435 {
3436   struct stat parbuf;
3437   gchar *last_slash;
3438   gchar *first_slash;
3439 #ifndef G_PLATFORM_WIN32
3440   gchar *new_name;
3441 #endif
3442   gchar *sys_filename;
3443   gchar c = 0;
3444
3445   last_slash = strrchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3446   g_assert (last_slash);
3447   first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3448
3449   /* Clever (?) way to check for top-level directory that works also on
3450    * Win32, where there is a drive letter and colon prefixed...
3451    */
3452   if (last_slash != first_slash)
3453     {
3454       last_slash[0] = 0;
3455     }
3456   else
3457     {
3458       c = last_slash[1];
3459       last_slash[1] = 0;
3460     }
3461
3462   sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3463   if (!sys_filename)
3464     {
3465       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3466       if (!c)
3467         last_slash[0] = G_DIR_SEPARATOR;
3468       return FALSE;
3469     }
3470   
3471   if (g_stat (sys_filename, &parbuf) < 0)
3472     {
3473       g_free (sys_filename);
3474       cmpl_errno = errno;
3475       if (!c)
3476         last_slash[0] = G_DIR_SEPARATOR;
3477       return FALSE;
3478     }
3479   g_free (sys_filename);
3480
3481 #ifndef G_PLATFORM_WIN32        /* No inode numbers on Win32 */
3482   if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
3483     /* it wasn't a link */
3484     return TRUE;
3485
3486   if (c)
3487     last_slash[1] = c;
3488   else
3489     last_slash[0] = G_DIR_SEPARATOR;
3490
3491   /* it was a link, have to figure it out the hard way */
3492
3493   new_name = find_parent_dir_fullname (cmpl_dir->fullname);
3494
3495   if (!new_name)
3496     return FALSE;
3497
3498   g_free (cmpl_dir->fullname);
3499
3500   cmpl_dir->fullname = new_name;
3501 #endif
3502
3503   return TRUE;
3504 }
3505
3506 #ifndef G_PLATFORM_WIN32
3507
3508 static gchar*
3509 find_parent_dir_fullname (gchar* dirname)
3510 {
3511   gchar *sys_orig_dir;
3512   gchar *result;
3513   gchar *sys_cwd;
3514   gchar *sys_dirname;
3515
3516   sys_orig_dir = g_get_current_dir ();
3517   sys_dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
3518   if (!sys_dirname)
3519     {
3520       g_free (sys_orig_dir);
3521       cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3522       return NULL;
3523     }
3524   
3525   if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
3526     {
3527       cmpl_errno = errno;
3528       chdir (sys_orig_dir);
3529       g_free (sys_dirname);
3530       g_free (sys_orig_dir);
3531       return NULL;
3532     }
3533   g_free (sys_dirname);
3534
3535   sys_cwd = g_get_current_dir ();
3536   result = g_filename_to_utf8 (sys_cwd, -1, NULL, NULL, NULL);
3537   g_free (sys_cwd);
3538
3539   if (chdir (sys_orig_dir) != 0)
3540     {
3541       cmpl_errno = errno;
3542       g_free (sys_orig_dir);
3543       return NULL;
3544     }
3545
3546   g_free (sys_orig_dir);
3547   return result;
3548 }
3549
3550 #endif
3551
3552 /**********************************************************************/
3553 /*                        Completion Operations                       */
3554 /**********************************************************************/
3555
3556 #ifdef HAVE_PWD_H
3557
3558 static PossibleCompletion*
3559 attempt_homedir_completion (gchar           *text_to_complete,
3560                             CompletionState *cmpl_state)
3561 {
3562   gint index;
3563
3564   if (!cmpl_state->user_dir_name_buffer &&
3565       !get_pwdb (cmpl_state))
3566     return NULL;
3567
3568   cmpl_state->user_completion_index += 1;
3569
3570   while (cmpl_state->user_completion_index < cmpl_state->user_directories_len)
3571     {
3572       index = first_diff_index (text_to_complete + 1,
3573                                 cmpl_state->user_directories
3574                                 [cmpl_state->user_completion_index].login);
3575
3576       switch (index)
3577         {
3578         case PATTERN_MATCH:
3579           break;
3580         default:
3581           if (cmpl_state->last_valid_char < (index + 1))
3582             cmpl_state->last_valid_char = index + 1;
3583           cmpl_state->user_completion_index += 1;
3584           continue;
3585         }
3586
3587       cmpl_state->the_completion.is_a_completion = 1;
3588       cmpl_state->the_completion.is_directory = TRUE;
3589
3590       append_completion_text ("~", cmpl_state);
3591
3592       append_completion_text (cmpl_state->
3593                               user_directories[cmpl_state->user_completion_index].login,
3594                               cmpl_state);
3595
3596       return append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3597     }
3598
3599   if (text_to_complete[1]
3600       || cmpl_state->user_completion_index > cmpl_state->user_directories_len)
3601     {
3602       cmpl_state->user_completion_index = -1;
3603       return NULL;
3604     }
3605   else
3606     {
3607       cmpl_state->user_completion_index += 1;
3608       cmpl_state->the_completion.is_a_completion = 1;
3609       cmpl_state->the_completion.is_directory = TRUE;
3610
3611       return append_completion_text ("~" G_DIR_SEPARATOR_S, cmpl_state);
3612     }
3613 }
3614
3615 #endif
3616
3617 #ifdef G_PLATFORM_WIN32
3618 /* FIXME: determine whether we should casefold all Unicode letters
3619  * here, too (and in in first_diff_index() walk through the strings with
3620  * g_utf8_next_char()), or if this folding isn't actually needed at
3621  * all.
3622  */
3623 #define FOLD(c) (tolower(c))
3624 #else
3625 #define FOLD(c) (c)
3626 #endif
3627
3628 /* returns the index (>= 0) of the first differing character,
3629  * PATTERN_MATCH if the completion matches */
3630 static gint
3631 first_diff_index (gchar *pat,
3632                   gchar *text)
3633 {
3634   gint diff = 0;
3635
3636   while (*pat && *text && FOLD (*text) == FOLD (*pat))
3637     {
3638       pat += 1;
3639       text += 1;
3640       diff += 1;
3641     }
3642
3643   if (*pat)
3644     return diff;
3645
3646   return PATTERN_MATCH;
3647 }
3648
3649 static PossibleCompletion*
3650 append_completion_text (gchar           *text,
3651                         CompletionState *cmpl_state)
3652 {
3653   gint len, i = 1;
3654
3655   if (!cmpl_state->the_completion.text)
3656     return NULL;
3657
3658   len = strlen (text) + strlen (cmpl_state->the_completion.text) + 1;
3659
3660   if (cmpl_state->the_completion.text_alloc > len)
3661     {
3662       strcat (cmpl_state->the_completion.text, text);
3663       return &cmpl_state->the_completion;
3664     }
3665
3666   while (i < len)
3667     i <<= 1;
3668
3669   cmpl_state->the_completion.text_alloc = i;
3670
3671   cmpl_state->the_completion.text = (gchar*) g_realloc (cmpl_state->the_completion.text, i);
3672
3673   if (!cmpl_state->the_completion.text)
3674     return NULL;
3675   else
3676     {
3677       strcat (cmpl_state->the_completion.text, text);
3678       return &cmpl_state->the_completion;
3679     }
3680 }
3681
3682 static CompletionDir*
3683 find_completion_dir (gchar          *text_to_complete,
3684                     gchar          **remaining_text,
3685                     CompletionState *cmpl_state)
3686 {
3687   gchar* first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
3688   CompletionDir* dir = cmpl_state->reference_dir;
3689   CompletionDir* next;
3690   *remaining_text = text_to_complete;
3691
3692   while (first_slash)
3693     {
3694       gint len = first_slash - *remaining_text;
3695       gint found = 0;
3696       gchar *found_name = NULL;         /* Quiet gcc */
3697       gint i;
3698       gchar* pat_buf = g_new (gchar, len + 1);
3699
3700       strncpy (pat_buf, *remaining_text, len);
3701       pat_buf[len] = 0;
3702
3703       for (i = 0; i < dir->sent->entry_count; i += 1)
3704         {
3705           if (dir->sent->entries[i].is_dir &&
3706               _gtk_fnmatch (pat_buf, dir->sent->entries[i].entry_name, TRUE))
3707             {
3708               if (found)
3709                 {
3710                   g_free (pat_buf);
3711                   return dir;
3712                 }
3713               else
3714                 {
3715                   found = 1;
3716                   found_name = dir->sent->entries[i].entry_name;
3717                 }
3718             }
3719         }
3720
3721       if (!found)
3722         {
3723           /* Perhaps we are trying to open an automount directory */
3724           found_name = pat_buf;
3725         }
3726
3727       next = open_relative_dir (found_name, dir, cmpl_state);
3728       
3729       if (!next)
3730         {
3731           g_free (pat_buf);
3732           return NULL;
3733 }
3734       
3735       next->cmpl_parent = dir;
3736       
3737       dir = next;
3738       
3739       if (!correct_dir_fullname (dir))
3740         {
3741           g_free (pat_buf);
3742           return NULL;
3743         }
3744       
3745       *remaining_text = first_slash + 1;
3746       first_slash = strchr (*remaining_text, G_DIR_SEPARATOR);
3747
3748       g_free (pat_buf);
3749     }
3750
3751   return dir;
3752 }
3753
3754 static void
3755 update_cmpl (PossibleCompletion *poss,
3756              CompletionState    *cmpl_state)
3757 {
3758   gint cmpl_len;
3759
3760   if (!poss || !cmpl_is_a_completion (poss))
3761     return;
3762
3763   cmpl_len = strlen (cmpl_this_completion (poss));
3764
3765   if (cmpl_state->updated_text_alloc < cmpl_len + 1)
3766     {
3767       cmpl_state->updated_text_alloc = 2*cmpl_len;
3768       cmpl_state->updated_text =
3769         (gchar*)g_realloc (cmpl_state->updated_text,
3770                            cmpl_state->updated_text_alloc);
3771     }
3772
3773   if (cmpl_state->updated_text_len < 0)
3774     {
3775       strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3776       cmpl_state->updated_text_len = cmpl_len;
3777       cmpl_state->re_complete = cmpl_is_directory (poss);
3778     }
3779   else if (cmpl_state->updated_text_len == 0)
3780     {
3781       cmpl_state->re_complete = FALSE;
3782     }
3783   else
3784     {
3785       gint first_diff =
3786         first_diff_index (cmpl_state->updated_text,
3787                           cmpl_this_completion (poss));
3788
3789       cmpl_state->re_complete = FALSE;
3790
3791       if (first_diff == PATTERN_MATCH)
3792         return;
3793
3794       if (first_diff > cmpl_state->updated_text_len)
3795         strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3796
3797       cmpl_state->updated_text_len = first_diff;
3798       cmpl_state->updated_text[first_diff] = 0;
3799     }
3800 }
3801
3802 static PossibleCompletion*
3803 attempt_file_completion (CompletionState *cmpl_state)
3804 {
3805   gchar *pat_buf, *first_slash;
3806   CompletionDir *dir = cmpl_state->active_completion_dir;
3807
3808   dir->cmpl_index += 1;
3809
3810   if (dir->cmpl_index == dir->sent->entry_count)
3811     {
3812       if (dir->cmpl_parent == NULL)
3813         {
3814           cmpl_state->active_completion_dir = NULL;
3815
3816           return NULL;
3817         }
3818       else
3819         {
3820           cmpl_state->active_completion_dir = dir->cmpl_parent;
3821
3822           return attempt_file_completion (cmpl_state);
3823         }
3824     }
3825
3826   g_assert (dir->cmpl_text);
3827
3828   first_slash = strchr (dir->cmpl_text, G_DIR_SEPARATOR);
3829
3830   if (first_slash)
3831     {
3832       gint len = first_slash - dir->cmpl_text;
3833
3834       pat_buf = g_new (gchar, len + 1);
3835       strncpy (pat_buf, dir->cmpl_text, len);
3836       pat_buf[len] = 0;
3837     }
3838   else
3839     {
3840       gint len = strlen (dir->cmpl_text);
3841
3842       pat_buf = g_new (gchar, len + 2);
3843       strcpy (pat_buf, dir->cmpl_text);
3844       /* Don't append a * if the user entered one herself.
3845        * This way one can complete *.h and don't get matches
3846        * on any .help files, for instance.
3847        */
3848       if (strchr (pat_buf, '*') == NULL)
3849         strcpy (pat_buf + len, "*");
3850     }
3851
3852   if (first_slash)
3853     {
3854       if (dir->sent->entries[dir->cmpl_index].is_dir)
3855         {
3856           if (_gtk_fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, TRUE))
3857             {
3858               CompletionDir* new_dir;
3859
3860               new_dir = open_relative_dir (dir->sent->entries[dir->cmpl_index].entry_name,
3861                                            dir, cmpl_state);
3862
3863               if (!new_dir)
3864                 {
3865                   g_free (pat_buf);
3866                   return NULL;
3867                 }
3868
3869               new_dir->cmpl_parent = dir;
3870
3871               new_dir->cmpl_index = -1;
3872               new_dir->cmpl_text = g_strdup (first_slash + 1);
3873
3874               cmpl_state->active_completion_dir = new_dir;
3875
3876               g_free (pat_buf);
3877               return attempt_file_completion (cmpl_state);
3878             }
3879           else
3880             {
3881               g_free (pat_buf);
3882               return attempt_file_completion (cmpl_state);
3883             }
3884         }
3885       else
3886         {
3887           g_free (pat_buf);
3888           return attempt_file_completion (cmpl_state);
3889         }
3890     }
3891   else
3892     {
3893       if (dir->cmpl_parent != NULL)
3894         {
3895           append_completion_text (dir->fullname +
3896                                   strlen (cmpl_state->completion_dir->fullname) + 1,
3897                                   cmpl_state);
3898           append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3899         }
3900
3901       append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
3902
3903       cmpl_state->the_completion.is_a_completion =
3904         _gtk_fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, TRUE);
3905
3906       cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
3907       if (dir->sent->entries[dir->cmpl_index].is_dir)
3908         append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3909
3910       g_free (pat_buf);
3911       return &cmpl_state->the_completion;
3912     }
3913 }
3914
3915 #ifdef HAVE_PWD_H
3916
3917 static gint
3918 get_pwdb (CompletionState* cmpl_state)
3919 {
3920   struct passwd *pwd_ptr;
3921   gchar* buf_ptr;
3922   gchar *utf8;
3923   gint len = 0, i, count = 0;
3924
3925   if (cmpl_state->user_dir_name_buffer)
3926     return TRUE;
3927   setpwent ();
3928
3929   while ((pwd_ptr = getpwent ()) != NULL)
3930     {
3931       utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3932       len += strlen (utf8);
3933       g_free (utf8);
3934       utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3935       len += strlen (utf8);
3936       g_free (utf8);
3937       len += 2;
3938       count += 1;
3939     }
3940
3941   setpwent ();
3942
3943   cmpl_state->user_dir_name_buffer = g_new (gchar, len);
3944   cmpl_state->user_directories = g_new (CompletionUserDir, count);
3945   cmpl_state->user_directories_len = count;
3946
3947   buf_ptr = cmpl_state->user_dir_name_buffer;
3948
3949   for (i = 0; i < count; i += 1)
3950     {
3951       pwd_ptr = getpwent ();
3952       if (!pwd_ptr)
3953         {
3954           cmpl_errno = errno;
3955           goto error;
3956         }
3957
3958       utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3959       strcpy (buf_ptr, utf8);
3960       g_free (utf8);
3961
3962       cmpl_state->user_directories[i].login = buf_ptr;
3963
3964       buf_ptr += strlen (buf_ptr);
3965       buf_ptr += 1;
3966
3967       utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3968       strcpy (buf_ptr, utf8);
3969       g_free (utf8);
3970
3971       cmpl_state->user_directories[i].homedir = buf_ptr;
3972
3973       buf_ptr += strlen (buf_ptr);
3974       buf_ptr += 1;
3975     }
3976
3977   qsort (cmpl_state->user_directories,
3978          cmpl_state->user_directories_len,
3979          sizeof (CompletionUserDir),
3980          compare_user_dir);
3981
3982   endpwent ();
3983
3984   return TRUE;
3985
3986 error:
3987
3988   if (cmpl_state->user_dir_name_buffer)
3989     g_free (cmpl_state->user_dir_name_buffer);
3990   if (cmpl_state->user_directories)
3991     g_free (cmpl_state->user_directories);
3992
3993   cmpl_state->user_dir_name_buffer = NULL;
3994   cmpl_state->user_directories = NULL;
3995
3996   return FALSE;
3997 }
3998
3999 static gint
4000 compare_user_dir (const void *a,
4001                   const void *b)
4002 {
4003   return strcmp ((((CompletionUserDir*)a))->login,
4004                  (((CompletionUserDir*)b))->login);
4005 }
4006
4007 #endif
4008
4009 static gint
4010 compare_cmpl_dir (const void *a,
4011                   const void *b)
4012 {
4013   
4014   return strcmp (((CompletionDirEntry*)a)->sort_key,
4015                  (((CompletionDirEntry*)b))->sort_key);
4016 }
4017
4018 static gint
4019 cmpl_state_okay (CompletionState* cmpl_state)
4020 {
4021   return  cmpl_state && cmpl_state->reference_dir;
4022 }
4023
4024 static const gchar*
4025 cmpl_strerror (gint err)
4026 {
4027   if (err == CMPL_ERRNO_TOO_LONG)
4028     return _("Name too long");
4029   else if (err == CMPL_ERRNO_DID_NOT_CONVERT)
4030     return _("Couldn't convert filename");
4031   else
4032     return g_strerror (err);
4033 }
4034
4035 #ifdef G_OS_WIN32
4036
4037 /* DLL ABI stability backward compatibility versions */
4038
4039 #undef gtk_file_selection_get_filename
4040
4041 G_CONST_RETURN gchar*
4042 gtk_file_selection_get_filename (GtkFileSelection *filesel)
4043 {
4044   static gchar retval[MAXPATHLEN*2+1];
4045   gchar *tem;
4046
4047   tem = g_locale_from_utf8 (gtk_file_selection_get_filename_utf8 (filesel),
4048                             -1, NULL, NULL, NULL);
4049
4050   strncpy (retval, tem, sizeof (retval) - 1);
4051   retval[sizeof (retval) - 1] = '\0';
4052   g_free (tem);
4053
4054   return retval;
4055 }
4056
4057 #undef gtk_file_selection_set_filename
4058
4059 void
4060 gtk_file_selection_set_filename (GtkFileSelection *filesel,
4061                                  const gchar      *filename)
4062 {
4063   gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL);
4064   gtk_file_selection_set_filename_utf8 (filesel, utf8_filename);
4065   g_free (utf8_filename);
4066 }
4067
4068 #undef gtk_file_selection_get_selections
4069
4070 gchar **
4071 gtk_file_selection_get_selections (GtkFileSelection *filesel)
4072 {
4073   int i = 0;
4074   gchar **selections = gtk_file_selection_get_selections_utf8 (filesel);
4075
4076   if (selections != NULL)
4077     while (selections[i] != NULL)
4078       {
4079         gchar *tem = selections[i];
4080         selections[i] = g_locale_from_utf8 (selections[i],
4081                                             -1, NULL, NULL, NULL);
4082         g_free (tem);
4083         i++;
4084       }
4085
4086   return selections;
4087 }
4088
4089 #endif /* G_OS_WIN32 */
4090
4091 #define __GTK_FILESEL_C__
4092 #include "gtkaliasdef.c"