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