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