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