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