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