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