]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/gtkfilesel.c
reorganized about dialog code, added updated 1.5.0 .ico
[xonotic/netradiant.git] / radiant / gtkfilesel.c
1 /*
2    Copyright (C) 1999-2007 id Software, Inc. and contributors.
3    For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5    This file is part of GtkRadiant.
6
7    GtkRadiant is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    GtkRadiant is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with GtkRadiant; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21
22 /* GTK - The GIMP Toolkit
23  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
24  *
25  * This library is free software; you can redistribute it and/or
26  * modify it under the terms of the GNU Library General Public
27  * License as published by the Free Software Foundation; either
28  * version 2 of the License, or (at your option) any later version.
29  *
30  * This library is distributed in the hope that it will be useful,
31  * but WITHOUT ANY WARRANTY; without even the implied warranty of
32  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
33  * Library General Public License for more details.
34  *
35  * You should have received a copy of the GNU Library General Public
36  * License along with this library; if not, write to the
37  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
38  * Boston, MA 02111-1307, USA.
39  */
40
41 /*
42  * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
43  * file for a list of people on the GTK+ Team.  See the ChangeLog
44  * files for a list of changes.  These files are distributed with
45  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
46  */
47
48
49 // leo FIXME: if we keep this file then we'll need to ask permission to the author, this is LGPL
50 // This file is from the Advanced File Selector widget
51 // by Michael Torrie  <torriem@byu.edu>
52 // http://students.cs.byu.edu/~torriem/gtk/
53
54 #include <stdio.h>
55 #include <sys/types.h>
56 #include <sys/stat.h>
57 #include <sys/param.h>
58 #include <dirent.h>
59 #include <stdlib.h>
60 #include <unistd.h>
61 #include <string.h>
62 #include <errno.h>
63 #include <pwd.h>
64 #include "fnmatch.h"
65
66 // leo: added "gtk/"
67 #include "gdk/gdkkeysyms.h"
68 #include "gtk/gtkbutton.h"
69 #include "gtk/gtkentry.h"
70 #include "gtkfilesel.h"
71 #include "gtk/gtkhbox.h"
72 #include "gtk/gtkhbbox.h"
73 #include "gtk/gtklabel.h"
74 #include "gtk/gtklist.h"
75 #include "gtk/gtklistitem.h"
76 #include "gtk/gtkmain.h"
77 #include "gtk/gtkscrolledwindow.h"
78 #include "gtk/gtksignal.h"
79 #include "gtk/gtkvbox.h"
80 #include "gtk/gtkmenu.h"
81 #include "gtk/gtkmenuitem.h"
82 #include "gtk/gtkoptionmenu.h"
83 #include "gtk/gtkclist.h"
84 #include "gtk/gtkdialog.h"
85 #include "gtk/gtkcombo.h"
86 #include "gtk/gtkframe.h"
87
88 // leo: disable NLS
89 //#include "gtk/gtkintl.h"
90 #define _( String ) ( String )
91
92 #define DIR_LIST_WIDTH   180
93 #define DIR_LIST_HEIGHT  180
94 #define FILE_LIST_WIDTH  180
95 #define FILE_LIST_HEIGHT 180
96
97 /* I've put this here so it doesn't get confused with the
98  * file completion interface */
99 typedef struct _HistoryCallbackArg HistoryCallbackArg;
100
101 struct _HistoryCallbackArg
102 {
103         gchar *directory;
104         GtkWidget *menu_item;
105 };
106
107
108 typedef struct _CompletionState CompletionState;
109 typedef struct _CompletionDir CompletionDir;
110 typedef struct _CompletionDirSent CompletionDirSent;
111 typedef struct _CompletionDirEntry CompletionDirEntry;
112 typedef struct _CompletionUserDir CompletionUserDir;
113 typedef struct _PossibleCompletion PossibleCompletion;
114
115 /* Non-external file completion decls and structures */
116
117 /* A contant telling PRCS how many directories to cache.  Its actually
118  * kept in a list, so the geometry isn't important. */
119 #define CMPL_DIRECTORY_CACHE_SIZE 10
120
121 /* A constant used to determine whether a substring was an exact
122  * match by first_diff_index()
123  */
124 #define PATTERN_MATCH -1
125 /* The arguments used by all fnmatch() calls below
126  */
127 #define FNMATCH_FLAGS ( FNM_PATHNAME | FNM_PERIOD )
128
129 #define CMPL_ERRNO_TOO_LONG ( ( 1 << 16 ) - 1 )
130
131 /* This structure contains all the useful information about a directory
132  * for the purposes of filename completion.  These structures are cached
133  * in the CompletionState struct.  CompletionDir's are reference counted.
134  */
135 struct _CompletionDirSent
136 {
137         ino_t inode;
138         time_t mtime;
139         dev_t device;
140
141         gint entry_count;
142         gchar *name_buffer; /* memory segment containing names of all entries */
143
144         struct _CompletionDirEntry *entries;
145 };
146
147 struct _CompletionDir
148 {
149         CompletionDirSent *sent;
150
151         gchar *fullname;
152         gint fullname_len;
153
154         struct _CompletionDir *cmpl_parent;
155         gint cmpl_index;
156         gchar *cmpl_text;
157 };
158
159 /* This structure contains pairs of directory entry names with a flag saying
160  * whether or not they are a valid directory.  NOTE: This information is used
161  * to provide the caller with information about whether to update its completions
162  * or try to open a file.  Since directories are cached by the directory mtime,
163  * a symlink which points to an invalid file (which will not be a directory),
164  * will not be reevaluated if that file is created, unless the containing
165  * directory is touched.  I consider this case to be worth ignoring (josh).
166  */
167 struct _CompletionDirEntry
168 {
169         gint is_dir;
170         gchar *entry_name;
171 };
172
173 struct _CompletionUserDir
174 {
175         gchar *login;
176         gchar *homedir;
177 };
178
179 struct _PossibleCompletion
180 {
181         /* accessible fields, all are accessed externally by functions
182          * declared above
183          */
184         gchar *text;
185         gint is_a_completion;
186         gint is_directory;
187
188         gint file_size;
189         gint file_time;
190         gint uid;
191         gint gid;
192         /* Private fields
193          */
194         gint text_alloc;
195 };
196
197 struct _CompletionState
198 {
199         gint last_valid_char;
200         gchar *updated_text;
201         gint updated_text_len;
202         gint updated_text_alloc;
203         gint re_complete;
204
205         gchar *user_dir_name_buffer;
206         gint user_directories_len;
207
208         gchar *last_completion_text;
209
210         gint user_completion_index; /* if >= 0, currently completing ~user */
211
212         struct _CompletionDir *completion_dir; /* directory completing from */
213         struct _CompletionDir *active_completion_dir;
214
215         struct _PossibleCompletion the_completion;
216
217         struct _CompletionDir *reference_dir; /* initial directory */
218
219         GList* directory_storage;
220         GList* directory_sent_storage;
221
222         struct _CompletionUserDir *user_directories;
223 };
224
225
226 /* File completion functions which would be external, were they used
227  * outside of this file.
228  */
229
230 static CompletionState*    cmpl_init_state( void );
231 static void                cmpl_free_state( CompletionState *cmpl_state );
232 static gint                cmpl_state_okay( CompletionState* cmpl_state );
233 static gchar*              cmpl_strerror( gint );
234
235 static PossibleCompletion* cmpl_completion_matches( gchar           *text_to_complete,
236                                                                                                         gchar          **remaining_text,
237                                                                                                         CompletionState *cmpl_state );
238
239 /* Returns a name for consideration, possibly a completion, this name
240  * will be invalid after the next call to cmpl_next_completion.
241  */
242 static char*               cmpl_this_completion( PossibleCompletion* );
243
244 /* True if this completion matches the given text.  Otherwise, this
245  * output can be used to have a list of non-completions.
246  */
247 static gint                cmpl_is_a_completion( PossibleCompletion* );
248
249 /* True if the completion is a directory
250  */
251 static gint                cmpl_is_directory( PossibleCompletion* );
252
253 /* Obtains the next completion, or NULL
254  */
255 static PossibleCompletion* cmpl_next_completion( CompletionState* );
256
257 /* Updating completions: the return value of cmpl_updated_text() will
258  * be text_to_complete completed as much as possible after the most
259  * recent call to cmpl_completion_matches.  For the present
260  * application, this is the suggested replacement for the user's input
261  * string.  You must CALL THIS AFTER ALL cmpl_text_completions have
262  * been received.
263  */
264 static gchar*              cmpl_updated_text( CompletionState* cmpl_state );
265
266 /* After updating, to see if the completion was a directory, call
267  * this.  If it was, you should consider re-calling completion_matches.
268  */
269 static gint                cmpl_updated_dir( CompletionState* cmpl_state );
270
271 /* Current location: if using file completion, return the current
272  * directory, from which file completion begins.  More specifically,
273  * the cwd concatenated with all exact completions up to the last
274  * directory delimiter('/').
275  */
276 static gchar*              cmpl_reference_position( CompletionState* cmpl_state );
277
278 /* backing up: if cmpl_completion_matches returns NULL, you may query
279  * the index of the last completable character into cmpl_updated_text.
280  */
281 static gint                cmpl_last_valid_char( CompletionState* cmpl_state );
282
283 /* When the user selects a non-directory, call cmpl_completion_fullname
284  * to get the full name of the selected file.
285  */
286 static gchar*              cmpl_completion_fullname( gchar*, CompletionState* cmpl_state );
287
288
289 /* Directory operations. */
290 static CompletionDir* open_ref_dir( gchar* text_to_complete,
291                                                                         gchar** remaining_text,
292                                                                         CompletionState* cmpl_state );
293 static gboolean       check_dir( gchar *dir_name,
294                                                                  struct stat *result,
295                                                                  gboolean *stat_subdirs );
296 static CompletionDir* open_dir( gchar* dir_name,
297                                                                 CompletionState* cmpl_state );
298 static CompletionDir* open_user_dir( gchar* text_to_complete,
299                                                                          CompletionState *cmpl_state );
300 static CompletionDir* open_relative_dir( gchar* dir_name, CompletionDir* dir,
301                                                                                  CompletionState *cmpl_state );
302 static CompletionDirSent* open_new_dir( gchar* dir_name,
303                                                                                 struct stat* sbuf,
304                                                                                 gboolean stat_subdirs );
305 static gint           correct_dir_fullname( CompletionDir* cmpl_dir );
306 static gint           correct_parent( CompletionDir* cmpl_dir,
307                                                                           struct stat *sbuf );
308 static gchar*         find_parent_dir_fullname( gchar* dirname );
309 static CompletionDir* attach_dir( CompletionDirSent* sent,
310                                                                   gchar* dir_name,
311                                                                   CompletionState *cmpl_state );
312 static void           free_dir_sent( CompletionDirSent* sent );
313 static void           free_dir( CompletionDir  *dir );
314 static void           prune_memory_usage( CompletionState *cmpl_state );
315
316 /* Completion operations */
317 static PossibleCompletion* attempt_homedir_completion( gchar* text_to_complete,
318                                                                                                            CompletionState *cmpl_state );
319 static PossibleCompletion* attempt_file_completion( CompletionState *cmpl_state );
320 static CompletionDir* find_completion_dir( gchar* text_to_complete,
321                                                                                    gchar** remaining_text,
322                                                                                    CompletionState* cmpl_state );
323 static PossibleCompletion* append_completion_text( gchar* text,
324                                                                                                    CompletionState* cmpl_state );
325 static gint get_pwdb( CompletionState* cmpl_state );
326 static gint first_diff_index( gchar* pat, gchar* text );
327 static gint compare_user_dir( const void* a, const void* b );
328 static gint compare_cmpl_dir( const void* a, const void* b );
329 static void update_cmpl( PossibleCompletion* poss,
330                                                  CompletionState* cmpl_state );
331
332 static void gtk_file_selection_class_init( GtkFileSelectionClass *klass );
333 static void gtk_file_selection_init( GtkFileSelection      *filesel );
334 static void gtk_file_selection_destroy( GtkObject             *object );
335 static gint gtk_file_selection_key_press( GtkWidget             *widget,
336                                                                                   GdkEventKey           *event,
337                                                                                   gpointer user_data );
338
339 static void gtk_file_selection_file_button( GtkWidget *widget,
340                                                                                         gint row,
341                                                                                         gint column,
342                                                                                         GdkEventButton *bevent,
343                                                                                         gpointer user_data );
344
345 static void gtk_file_selection_dir_button( GtkWidget *widget,
346                                                                                    gint row,
347                                                                                    gint column,
348                                                                                    GdkEventButton *bevent,
349                                                                                    gpointer data );
350
351 static void gtk_file_selection_undir_button( GtkWidget *widget,
352                                                                                          gint row,
353                                                                                          gint column,
354                                                                                          GdkEventButton *bevent,
355                                                                                          gpointer data );
356
357 static void gtk_file_selection_populate( GtkFileSelection      *fs,
358                                                                                  gchar                 *rel_path,
359                                                                                  gint try_complete );
360 static void gtk_file_selection_abort( GtkFileSelection      *fs );
361
362 static void gtk_file_selection_update_history_menu( GtkFileSelection       *fs,
363                                                                                                         gchar                  *current_dir );
364
365 static void gtk_file_selection_create_dir( GtkWidget *widget, gpointer data );
366 static void gtk_file_selection_delete_file( GtkWidget *widget, gpointer data );
367 static void gtk_file_selection_rename_file( GtkWidget *widget, gpointer data );
368
369 static gboolean gtk_file_selection_history_combo_callback( GtkWidget *widget, GdkEventKey *event, gpointer data );
370 static gboolean gtk_file_selection_history_combo_list_key_handler( GtkWidget *widget,
371                                                                                                                                    GdkEventKey *event,
372                                                                                                                                    gpointer user_data );
373 static gboolean gtk_file_selection_history_combo_list_callback( GtkWidget *thelist,
374                                                                                                                                 GdkEventButton *event,
375                                                                                                                                 gpointer user_data );
376 static void gtk_file_selection_mask_entry_callback( GtkWidget *widget, gpointer data );
377 static void gtk_file_selection_create_dir( GtkWidget *widget, gpointer data );
378 static void gtk_file_selection_delete_file( GtkWidget *widget, gpointer data );
379 static void gtk_file_selection_rename_file( GtkWidget *widget, gpointer data );
380 static void gtk_file_selection_home_button( GtkWidget *widget, gpointer data );
381 static void gtk_file_selection_up_button( GtkWidget *widget, gpointer data );
382 static void gtk_file_selection_prev_button( GtkWidget *widget, gpointer data );
383 static void gtk_file_selection_next_button( GtkWidget *widget, gpointer data );
384 static void gtk_file_selection_refresh_button( GtkWidget *widget, gpointer data );
385
386 static gint gtk_file_selection_match_char( gchar, gchar *mask );
387 static gint gtk_file_selection_match_mask( gchar *,gchar * );
388
389
390 static GtkWindowClass *parent_class = NULL;
391
392 /* Saves errno when something cmpl does fails. */
393 static gint cmpl_errno;
394
395 /* General notes:
396  * Make prev and next inactive if their respective *
397  *   histories are empty.
398  * Add facilities for handling hidden files and    *
399  * directories                                     *
400  * Add an api to access the mask, and hidden files *
401  * check box?  (prob not in 1.2.x series)          *
402  */
403
404 /* Routine for applying mask to filenames         *
405  *   Need to be optimized to minimize recursion   *
406  *     help the for loop by looking for the next  *
407  *     instance of the mask character following   *
408  *     the '*'.  ei *.c -- look for '.'           *
409  *     Also, swap all *? pairs (-> ?*), as that   *
410  *     will make it possible to look ahead (?     *
411  *     makes it very nondeterministic as in *?.c  *
412  *     which really is ?*.c                       *
413  *   Allow multiply masks, separted by commas     *
414  *   Allow more flexible [] handling (ie [a-zA-Z] *
415  *                                                *
416  */
417 static gint gtk_file_selection_match_char( gchar text, gchar *mask ){
418         gchar *maskc;
419         gint x;
420         gint s;
421
422         if ( mask[0] == '[' ) {
423                 if ( !strchr( mask,']' ) ) {
424                         return 0;
425                 }
426                 maskc = g_strdup( mask + 1 ); /* get the portion of mask inside []*/
427
428                 ( *( strchr( maskc,']' ) ) ) = 0;
429                 s = strlen( (char *)maskc );
430
431                 for ( x = 0; x < s; x++ ) {
432                         if ( text == maskc[x] ) {
433                                 g_free( maskc );
434                                 return s + 2;
435                         }
436                 }
437                 g_free( maskc );
438                 return 0;
439         }
440
441         if ( mask[0] == '?' ) {
442                 return 1;
443         }
444         if ( mask[0] == text ) {
445                 return 1;
446         }
447
448         return 0;
449 }
450
451
452 static gint gtk_file_selection_match_mask( gchar *text, gchar *mask ){
453
454         int mc;
455         int tc;
456
457         tc = 0; mc = 0;
458
459         if ( mask[0] == 0 && text[0] == 0 ) {
460                 return 1;
461         }
462
463         if ( mask[0] == '*' ) {
464                 for ( tc = 0; tc <= strlen( text ); tc++ )
465                 {
466                         if ( gtk_file_selection_match_mask( text + tc, mask + 1 ) ) {
467                                 return 1;
468                         }
469                 }
470                 return 0;
471         }
472         mc = gtk_file_selection_match_char( text[0], mask );
473
474         if ( mc ) {
475                 return gtk_file_selection_match_mask( text + 1, mask + mc );
476         }
477         else{
478                 return 0;
479         }
480 }
481
482 GtkType
483 gtk_file_selection_get_type( void ){
484         static GtkType file_selection_type = 0;
485
486         if ( !file_selection_type ) {
487                 static const GtkTypeInfo filesel_info =
488                 {
489                         "GtkFileSelection",
490                         sizeof( GtkFileSelection ),
491                         sizeof( GtkFileSelectionClass ),
492                         (GtkClassInitFunc) gtk_file_selection_class_init,
493                         (GtkObjectInitFunc) gtk_file_selection_init,
494                         /* reserved_1 */ NULL,
495                         /* reserved_2 */ NULL,
496                         (GtkClassInitFunc) NULL,
497                 };
498
499                 file_selection_type = gtk_type_unique( GTK_TYPE_WINDOW, &filesel_info );
500         }
501
502         return file_selection_type;
503 }
504
505 static void
506 gtk_file_selection_class_init( GtkFileSelectionClass *class ){
507         GtkObjectClass *object_class;
508
509         object_class = (GtkObjectClass*) class;
510
511         parent_class = gtk_type_class( GTK_TYPE_WINDOW );
512
513         object_class->destroy = gtk_file_selection_destroy;
514 }
515
516 static void
517 gtk_file_selection_init( GtkFileSelection *filesel ){
518         GtkWidget *entry_vbox;
519         GtkWidget *label;
520         GtkWidget *list_hbox;
521         GtkWidget *confirm_area;
522         GtkWidget *vbox;
523         GtkWidget *hbox;
524         GtkWidget *pulldown_hbox;
525         GtkWidget *scrolled_win;
526         GtkWidget *mask_label;
527         GtkWidget *bigframe;
528         GtkWidget *label_lookingin;
529         GtkWidget *up_button;
530         GtkWidget *home_button;
531         GtkWidget *prev_button;
532         GtkWidget *next_button;
533         GtkWidget *refresh_button;
534
535         char *dir_title [2];
536         char *file_title [2];
537
538         filesel->cmpl_state = cmpl_init_state();
539
540         filesel->mask = NULL;
541         filesel->prev_history = NULL;
542         filesel->next_history = NULL;
543         filesel->saved_entry = NULL;
544
545         /* The dialog-sized vertical box  */
546         filesel->main_vbox = gtk_vbox_new( FALSE, 10 );
547         gtk_container_set_border_width( GTK_CONTAINER( filesel ), 10 );
548         gtk_container_add( GTK_CONTAINER( filesel ), filesel->main_vbox );
549         gtk_widget_show( filesel->main_vbox );
550
551         /* The horizontal box containing create, rename etc. buttons */
552         filesel->button_area = gtk_hbutton_box_new();
553         gtk_button_box_set_layout( GTK_BUTTON_BOX( filesel->button_area ), GTK_BUTTONBOX_START );
554         gtk_button_box_set_spacing( GTK_BUTTON_BOX( filesel->button_area ), 0 );
555         gtk_box_pack_start( GTK_BOX( filesel->main_vbox ), filesel->button_area,
556                                                 FALSE, FALSE, 0 );
557         gtk_widget_show( filesel->button_area );
558
559         gtk_file_selection_show_fileop_buttons( filesel );
560
561         /* hbox for pulldown menu */
562         pulldown_hbox = gtk_hbox_new( FALSE, 5 );
563         gtk_box_pack_start( GTK_BOX( filesel->main_vbox ), pulldown_hbox, FALSE, FALSE, 0 );
564         gtk_widget_show( pulldown_hbox );
565
566         /* The combo box that replaces the pulldown menu */
567         label_lookingin = gtk_label_new( _( "Looking in:" ) );
568         gtk_widget_show( label_lookingin );
569         gtk_box_pack_start( GTK_BOX( pulldown_hbox ), label_lookingin, FALSE, FALSE, 0 );
570
571         filesel->history_combo = gtk_combo_new();
572         gtk_widget_show( filesel->history_combo );
573         gtk_combo_set_value_in_list( GTK_COMBO( filesel->history_combo ),FALSE,FALSE );
574         gtk_box_pack_start( GTK_BOX( pulldown_hbox ),filesel->history_combo,
575                                                 TRUE,TRUE, 0 );
576         gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->history_combo )->entry ),"key-press-event",
577                                                 (GtkSignalFunc) gtk_file_selection_history_combo_callback,
578                                                 (gpointer) filesel );
579
580         gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->history_combo )->list ),"button-press-event",
581                                                 (GtkSignalFunc) gtk_file_selection_history_combo_list_callback,
582                                                 (gpointer) filesel );
583
584         gtk_signal_connect( GTK_OBJECT( ( (GtkCombo *)filesel->history_combo )->list ),"key-press-event",
585                                                 (GtkSignalFunc) gtk_file_selection_history_combo_list_key_handler,
586                                                 (gpointer) filesel );
587
588         /*  frame to put the following hbox in  */
589         bigframe = gtk_frame_new( NULL );
590         gtk_widget_show( bigframe );
591         gtk_box_pack_start( GTK_BOX( filesel->main_vbox ), bigframe, TRUE, TRUE, 0 );
592
593         /*  The horizontal box containing the directory and file listboxes  */
594         list_hbox = gtk_hbox_new( FALSE, 5 );
595         gtk_container_add( GTK_CONTAINER( bigframe ), list_hbox );
596         gtk_container_set_border_width( GTK_CONTAINER( list_hbox ), 5 );
597         gtk_widget_show( list_hbox );
598
599         /* vbox to put the buttons and directory listing in  */
600         vbox = gtk_vbox_new( FALSE, 0 );
601         gtk_widget_show( vbox );
602         gtk_box_pack_start( GTK_BOX( list_hbox ), vbox, FALSE, FALSE, 0 );
603
604         hbox = gtk_hbox_new( FALSE, 0 );
605         gtk_widget_show( hbox );
606         gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 0 );
607
608         home_button = gtk_button_new_with_label( _( "Home" ) );
609         gtk_widget_show( home_button );
610         gtk_signal_connect( GTK_OBJECT( home_button ), "clicked",
611                                                 (GtkSignalFunc) gtk_file_selection_home_button,
612                                                 (gpointer) filesel );
613         gtk_box_pack_start( GTK_BOX( hbox ), home_button, TRUE,TRUE, 0 );
614
615         prev_button = gtk_button_new_with_label( _( "Prev" ) );
616         gtk_signal_connect( GTK_OBJECT( prev_button ), "clicked",
617                                                 (GtkSignalFunc) gtk_file_selection_prev_button,
618                                                 (gpointer) filesel );
619         gtk_widget_show( prev_button );
620         gtk_box_pack_start( GTK_BOX( hbox ), prev_button, TRUE,TRUE, 0 );
621
622         up_button = gtk_button_new_with_label( _( "Up" ) );
623         gtk_signal_connect( GTK_OBJECT( up_button ), "clicked",
624                                                 (GtkSignalFunc) gtk_file_selection_up_button,
625                                                 (gpointer) filesel );
626         gtk_widget_show( up_button );
627         gtk_box_pack_start( GTK_BOX( hbox ), up_button, TRUE,TRUE, 0 );
628
629         next_button = gtk_button_new_with_label( _( "Next" ) );
630         gtk_widget_show( next_button );
631         gtk_signal_connect( GTK_OBJECT( next_button ), "clicked",
632                                                 (GtkSignalFunc) gtk_file_selection_next_button,
633                                                 (gpointer) filesel );
634         gtk_box_pack_start( GTK_BOX( hbox ), next_button, TRUE,TRUE, 0 );
635
636         refresh_button = gtk_button_new_with_label( _( "Refresh" ) );
637         gtk_widget_show( refresh_button );
638         gtk_signal_connect( GTK_OBJECT( refresh_button ), "clicked",
639                                                 (GtkSignalFunc) gtk_file_selection_refresh_button,
640                                                 (gpointer) filesel );
641         gtk_box_pack_start( GTK_BOX( hbox ), refresh_button, TRUE, TRUE, 0 );
642
643         /* The directories clist */
644         dir_title[0] = _( "Directories" );
645         dir_title[1] = NULL;
646         filesel->dir_list = gtk_clist_new_with_titles( 1, (gchar**) dir_title );
647         gtk_widget_set_usize( filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT );
648         gtk_signal_connect( GTK_OBJECT( filesel->dir_list ), "select_row",
649                                                 (GtkSignalFunc) gtk_file_selection_dir_button,
650                                                 (gpointer) filesel );
651         gtk_signal_connect( GTK_OBJECT( filesel->dir_list ), "unselect_row",
652                                                 (GtkSignalFunc) gtk_file_selection_undir_button,
653                                                 (gpointer) filesel );
654         gtk_clist_column_titles_passive( GTK_CLIST( filesel->dir_list ) );
655
656         scrolled_win = gtk_scrolled_window_new( NULL, NULL );
657         gtk_container_add( GTK_CONTAINER( scrolled_win ), filesel->dir_list );
658         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_win ),
659                                                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
660         gtk_box_pack_start( GTK_BOX( vbox ), scrolled_win, TRUE,TRUE, 5 );
661         gtk_widget_show( filesel->dir_list );
662         gtk_widget_show( scrolled_win );
663
664         /* vbox area for mask entry and files clist  */
665         vbox = gtk_vbox_new( FALSE, 0 );
666         gtk_widget_show( vbox );
667         gtk_box_pack_start( GTK_BOX( list_hbox ), vbox, TRUE, TRUE, 0 );
668
669         hbox = gtk_hbox_new( FALSE, 5 );
670         gtk_widget_show( hbox );
671         gtk_box_pack_start( GTK_BOX( vbox ), hbox, FALSE, FALSE, 0 );
672
673         mask_label = gtk_label_new( _( "Mask:" ) );
674         gtk_widget_show( mask_label );
675         gtk_box_pack_start( GTK_BOX( hbox ), mask_label, FALSE, FALSE, 0 );
676
677         filesel->mask_entry = gtk_entry_new();
678         gtk_widget_show( filesel->mask_entry );
679         gtk_signal_connect( GTK_OBJECT( filesel->mask_entry ),"activate",
680                                                 (GtkSignalFunc) gtk_file_selection_mask_entry_callback,
681                                                 (gpointer) filesel );
682         gtk_box_pack_start( GTK_BOX( hbox ),filesel->mask_entry, TRUE, TRUE, 0 );
683
684
685         /* The files clist */
686         file_title[0] = _( "Files" );
687         file_title[1] = NULL;
688         filesel->file_list = gtk_clist_new_with_titles( 1, (gchar**) file_title );
689         gtk_widget_set_usize( filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT );
690         gtk_signal_connect( GTK_OBJECT( filesel->file_list ), "select_row",
691                                                 (GtkSignalFunc) gtk_file_selection_file_button,
692                                                 (gpointer) filesel );
693         gtk_clist_column_titles_passive( GTK_CLIST( filesel->file_list ) );
694
695         scrolled_win = gtk_scrolled_window_new( NULL, NULL );
696         gtk_container_add( GTK_CONTAINER( scrolled_win ), filesel->file_list );
697         gtk_scrolled_window_set_policy( GTK_SCROLLED_WINDOW( scrolled_win ),
698                                                                         GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
699         gtk_box_pack_start( GTK_BOX( vbox ), scrolled_win, TRUE, TRUE, 5 );
700         gtk_widget_show( filesel->file_list );
701         gtk_widget_show( scrolled_win );
702
703         /* action area for packing buttons into. */
704         filesel->action_area = gtk_hbox_new( TRUE, 0 );
705         gtk_box_pack_start( GTK_BOX( filesel->main_vbox ), filesel->action_area,
706                                                 FALSE, FALSE, 0 );
707         gtk_widget_show( filesel->action_area );
708
709         /*  The OK/Cancel button area */
710         confirm_area = gtk_hbutton_box_new();
711         gtk_button_box_set_layout( GTK_BUTTON_BOX( confirm_area ), GTK_BUTTONBOX_END );
712         gtk_button_box_set_spacing( GTK_BUTTON_BOX( confirm_area ), 5 );
713         gtk_box_pack_end( GTK_BOX( filesel->main_vbox ), confirm_area, FALSE, FALSE, 0 );
714         gtk_widget_show( confirm_area );
715
716         /*  The OK button  */
717         filesel->ok_button = gtk_button_new_with_label( _( "OK" ) );
718         GTK_WIDGET_SET_FLAGS( filesel->ok_button, GTK_CAN_DEFAULT );
719         gtk_box_pack_start( GTK_BOX( confirm_area ), filesel->ok_button, TRUE, TRUE, 0 );
720         gtk_widget_grab_default( filesel->ok_button );
721         gtk_widget_show( filesel->ok_button );
722
723         /*  The Cancel button  */
724         filesel->cancel_button = gtk_button_new_with_label( _( "Cancel" ) );
725         GTK_WIDGET_SET_FLAGS( filesel->cancel_button, GTK_CAN_DEFAULT );
726         gtk_box_pack_start( GTK_BOX( confirm_area ), filesel->cancel_button, TRUE, TRUE, 0 );
727         gtk_widget_show( filesel->cancel_button );
728
729         /*  The selection entry widget  */
730         entry_vbox = gtk_vbox_new( FALSE, 2 );
731         gtk_box_pack_end( GTK_BOX( filesel->main_vbox ), entry_vbox, FALSE, FALSE, 0 );
732         gtk_widget_show( entry_vbox );
733
734         filesel->selection_text = label = gtk_label_new( "" );
735         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.5 );
736         gtk_box_pack_start( GTK_BOX( entry_vbox ), label, FALSE, FALSE, 0 );
737         gtk_widget_show( label );
738
739         filesel->selection_entry = gtk_entry_new();
740         gtk_signal_connect( GTK_OBJECT( filesel->selection_entry ), "key_press_event",
741                                                 (GtkSignalFunc) gtk_file_selection_key_press, filesel );
742         gtk_signal_connect_object( GTK_OBJECT( filesel->selection_entry ), "focus_in_event",
743                                                            (GtkSignalFunc) gtk_widget_grab_default,
744                                                            GTK_OBJECT( filesel->ok_button ) );
745         gtk_signal_connect_object( GTK_OBJECT( filesel->selection_entry ), "activate",
746                                                            (GtkSignalFunc) gtk_button_clicked,
747                                                            GTK_OBJECT( filesel->ok_button ) );
748         gtk_box_pack_start( GTK_BOX( entry_vbox ), filesel->selection_entry, TRUE, TRUE, 0 );
749         gtk_widget_show( filesel->selection_entry );
750
751         if ( !cmpl_state_okay( filesel->cmpl_state ) ) {
752                 gchar err_buf[256];
753
754                 sprintf( err_buf, _( "Directory unreadable: %s" ), cmpl_strerror( cmpl_errno ) );
755
756                 gtk_label_set_text( GTK_LABEL( filesel->selection_text ), err_buf );
757         }
758         else
759         {
760                 gtk_file_selection_populate( filesel, "", FALSE );
761         }
762
763         gtk_widget_grab_focus( filesel->selection_entry );
764 }
765
766 GtkWidget*
767 gtk_file_selection_new( const gchar *title ){
768         GtkFileSelection *filesel;
769
770         filesel = gtk_type_new( GTK_TYPE_FILE_SELECTION );
771         gtk_window_set_title( GTK_WINDOW( filesel ), title );
772
773         return GTK_WIDGET( filesel );
774 }
775
776 void
777 gtk_file_selection_show_fileop_buttons( GtkFileSelection *filesel ){
778         g_return_if_fail( filesel != NULL );
779         g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
780
781         /* delete, create directory, and rename */
782         if ( !filesel->fileop_c_dir ) {
783                 filesel->fileop_c_dir = gtk_button_new_with_label( _( "Create Dir" ) );
784                 gtk_signal_connect( GTK_OBJECT( filesel->fileop_c_dir ), "clicked",
785                                                         (GtkSignalFunc) gtk_file_selection_create_dir,
786                                                         (gpointer) filesel );
787                 gtk_box_pack_start( GTK_BOX( filesel->button_area ),
788                                                         filesel->fileop_c_dir, TRUE, TRUE, 0 );
789                 gtk_widget_show( filesel->fileop_c_dir );
790         }
791
792         if ( !filesel->fileop_del_file ) {
793                 filesel->fileop_del_file = gtk_button_new_with_label( _( "Delete File" ) );
794                 gtk_signal_connect( GTK_OBJECT( filesel->fileop_del_file ), "clicked",
795                                                         (GtkSignalFunc) gtk_file_selection_delete_file,
796                                                         (gpointer) filesel );
797                 gtk_box_pack_start( GTK_BOX( filesel->button_area ),
798                                                         filesel->fileop_del_file, TRUE, TRUE, 0 );
799                 gtk_widget_show( filesel->fileop_del_file );
800         }
801
802         if ( !filesel->fileop_ren_file ) {
803                 filesel->fileop_ren_file = gtk_button_new_with_label( _( "Rename File" ) );
804                 gtk_signal_connect( GTK_OBJECT( filesel->fileop_ren_file ), "clicked",
805                                                         (GtkSignalFunc) gtk_file_selection_rename_file,
806                                                         (gpointer) filesel );
807                 gtk_box_pack_start( GTK_BOX( filesel->button_area ),
808                                                         filesel->fileop_ren_file, TRUE, TRUE, 0 );
809                 gtk_widget_show( filesel->fileop_ren_file );
810         }
811
812         gtk_widget_queue_resize( GTK_WIDGET( filesel ) );
813 }
814
815 void
816 gtk_file_selection_hide_fileop_buttons( GtkFileSelection *filesel ){
817         g_return_if_fail( filesel != NULL );
818         g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
819
820         if ( filesel->fileop_ren_file ) {
821                 gtk_widget_destroy( filesel->fileop_ren_file );
822                 filesel->fileop_ren_file = NULL;
823         }
824
825         if ( filesel->fileop_del_file ) {
826                 gtk_widget_destroy( filesel->fileop_del_file );
827                 filesel->fileop_del_file = NULL;
828         }
829
830         if ( filesel->fileop_c_dir ) {
831                 gtk_widget_destroy( filesel->fileop_c_dir );
832                 filesel->fileop_c_dir = NULL;
833         }
834 }
835
836
837
838 void
839 gtk_file_selection_set_filename( GtkFileSelection *filesel,
840                                                                  const gchar      *filename ){
841         char buf[MAXPATHLEN];
842         const char *name, *last_slash;
843
844         g_return_if_fail( filesel != NULL );
845         g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
846         g_return_if_fail( filename != NULL );
847
848         last_slash = strrchr( filename, '/' );
849
850         if ( !last_slash ) {
851                 buf[0] = 0;
852                 name = filename;
853         }
854         else
855         {
856                 gint len = MIN( MAXPATHLEN - 1, last_slash - filename + 1 );
857
858                 strncpy( buf, filename, len );
859                 buf[len] = 0;
860
861                 name = last_slash + 1;
862         }
863
864         gtk_file_selection_populate( filesel, buf, FALSE );
865
866         if ( filesel->selection_entry ) {
867                 gtk_entry_set_text( GTK_ENTRY( filesel->selection_entry ), name );
868         }
869 }
870
871 gchar*
872 gtk_file_selection_get_filename( GtkFileSelection *filesel ){
873         static char nothing[2] = "";
874         char *text;
875         char *filename;
876
877         g_return_val_if_fail( filesel != NULL, nothing );
878         g_return_val_if_fail( GTK_IS_FILE_SELECTION( filesel ), nothing );
879
880         text = gtk_entry_get_text( GTK_ENTRY( filesel->selection_entry ) );
881         if ( text ) {
882                 filename = cmpl_completion_fullname( text, filesel->cmpl_state );
883                 return filename;
884         }
885
886         return nothing;
887 }
888
889 void
890 gtk_file_selection_complete( GtkFileSelection *filesel,
891                                                          const gchar      *pattern ){
892         gchar *new_pattern;
893         gint x;
894
895         g_return_if_fail( filesel != NULL );
896         g_return_if_fail( GTK_IS_FILE_SELECTION( filesel ) );
897         g_return_if_fail( pattern != NULL );
898
899         if ( filesel->selection_entry ) {
900                 gtk_entry_set_text( GTK_ENTRY( filesel->selection_entry ), pattern );
901         }
902
903         if ( strchr( pattern,'*' ) || strchr( pattern,'?' ) ) {
904                 for ( x = strlen( pattern ); x >= 0; x-- )
905                 {
906                         if ( pattern[x] == '/' ) {
907                                 break;
908                         }
909                 }
910                 gtk_entry_set_text( GTK_ENTRY( filesel->mask_entry ),g_strdup( pattern + x + 1 ) );
911
912                 if ( filesel->mask ) {
913                         g_free( filesel->mask );
914                 }
915
916                 filesel->mask = g_strdup( pattern + x + 1 );
917                 new_pattern = g_strdup( pattern );
918                 new_pattern[x + 1] = 0;
919                 gtk_file_selection_populate( filesel, (gchar*) new_pattern, TRUE );
920                 g_free( new_pattern );
921         }
922         else
923         {
924                 gtk_file_selection_populate( filesel, (gchar*) pattern, TRUE );
925         }
926 }
927
928 static void
929 gtk_file_selection_destroy( GtkObject *object ){
930         GtkFileSelection *filesel;
931         GList *list;
932
933         g_return_if_fail( object != NULL );
934         g_return_if_fail( GTK_IS_FILE_SELECTION( object ) );
935
936         filesel = GTK_FILE_SELECTION( object );
937
938         if ( filesel->fileop_dialog ) {
939                 gtk_widget_destroy( filesel->fileop_dialog );
940         }
941
942         if ( filesel->next_history ) {
943                 list = filesel->next_history;
944                 while ( list )
945                 {
946                         g_free( list->data );
947                         list = list->next;
948                 }
949         }
950         g_list_free( filesel->next_history );
951         filesel->next_history = NULL;
952
953         if ( filesel->prev_history ) {
954                 list = filesel->prev_history;
955                 while ( list )
956                 {
957                         g_free( list->data );
958                         list = list->next;
959                 }
960         }
961         g_list_free( filesel->prev_history );
962         filesel->prev_history = NULL;
963
964         if ( filesel->mask ) {
965                 g_free( filesel->mask );
966                 filesel->mask = NULL;
967         }
968
969         cmpl_free_state( filesel->cmpl_state );
970         filesel->cmpl_state = NULL;
971
972         if ( GTK_OBJECT_CLASS( parent_class )->destroy ) {
973                 ( *GTK_OBJECT_CLASS( parent_class )->destroy )( object );
974         }
975 }
976
977 /* Begin file operations callbacks */
978
979 static void
980 gtk_file_selection_fileop_error( GtkFileSelection *fs, gchar *error_message ){
981         GtkWidget *label;
982         GtkWidget *vbox;
983         GtkWidget *button;
984         GtkWidget *dialog;
985
986         g_return_if_fail( error_message != NULL );
987
988         /* main dialog */
989         dialog = gtk_dialog_new();
990         /*
991            gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
992                     (GtkSignalFunc) gtk_file_selection_fileop_destroy,
993                     (gpointer) fs);
994          */
995         gtk_window_set_title( GTK_WINDOW( dialog ), _( "Error" ) );
996         gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
997
998         /* If file dialog is grabbed, make this dialog modal too */
999         /* When error dialog is closed, file dialog will be grabbed again */
1000         if ( GTK_WINDOW( fs )->modal ) {
1001                 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
1002         }
1003
1004         vbox = gtk_vbox_new( FALSE, 0 );
1005         gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 );
1006         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox,
1007                                                 FALSE, FALSE, 0 );
1008         gtk_widget_show( vbox );
1009
1010         label = gtk_label_new( error_message );
1011         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 );
1012         gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 );
1013         gtk_widget_show( label );
1014
1015         /* yes, we free it */
1016         g_free( error_message );
1017
1018         /* close button */
1019         button = gtk_button_new_with_label( _( "Close" ) );
1020         gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
1021                                                            (GtkSignalFunc) gtk_widget_destroy,
1022                                                            (gpointer) dialog );
1023         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
1024                                                 button, TRUE, TRUE, 0 );
1025         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
1026         gtk_widget_grab_default( button );
1027         gtk_widget_show( button );
1028
1029         gtk_widget_show( dialog );
1030 }
1031
1032 static void
1033 gtk_file_selection_fileop_destroy( GtkWidget *widget, gpointer data ){
1034         GtkFileSelection *fs = data;
1035
1036         g_return_if_fail( fs != NULL );
1037         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1038
1039         fs->fileop_dialog = NULL;
1040 }
1041
1042
1043 static void
1044 gtk_file_selection_create_dir_confirmed( GtkWidget *widget, gpointer data ){
1045         GtkFileSelection *fs = data;
1046         gchar *dirname;
1047         gchar *path;
1048         gchar *full_path;
1049         gchar *buf;
1050         CompletionState *cmpl_state;
1051
1052         g_return_if_fail( fs != NULL );
1053         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1054
1055         dirname = gtk_entry_get_text( GTK_ENTRY( fs->fileop_entry ) );
1056         cmpl_state = (CompletionState*) fs->cmpl_state;
1057         path = cmpl_reference_position( cmpl_state );
1058
1059         full_path = g_strconcat( path, "/", dirname, NULL );
1060         if ( ( mkdir( full_path, 0755 ) < 0 ) ) {
1061                 buf = g_strconcat( "Error creating directory \"", dirname, "\":  ",
1062                                                    g_strerror( errno ), NULL );
1063                 gtk_file_selection_fileop_error( fs, buf );
1064         }
1065         g_free( full_path );
1066
1067         gtk_widget_destroy( fs->fileop_dialog );
1068         gtk_file_selection_populate( fs, "", FALSE );
1069 }
1070
1071 static void
1072 gtk_file_selection_create_dir( GtkWidget *widget, gpointer data ){
1073         GtkFileSelection *fs = data;
1074         GtkWidget *label;
1075         GtkWidget *dialog;
1076         GtkWidget *vbox;
1077         GtkWidget *button;
1078
1079         g_return_if_fail( fs != NULL );
1080         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1081
1082         if ( fs->fileop_dialog ) {
1083                 return;
1084         }
1085
1086         /* main dialog */
1087         fs->fileop_dialog = dialog = gtk_dialog_new();
1088         gtk_signal_connect( GTK_OBJECT( dialog ), "destroy",
1089                                                 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1090                                                 (gpointer) fs );
1091         gtk_window_set_title( GTK_WINDOW( dialog ), _( "Create Directory" ) );
1092         gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
1093
1094         /* If file dialog is grabbed, grab option dialog */
1095         /* When option dialog is closed, file dialog will be grabbed again */
1096         if ( GTK_WINDOW( fs )->modal ) {
1097                 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
1098         }
1099
1100         vbox = gtk_vbox_new( FALSE, 0 );
1101         gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 );
1102         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox,
1103                                                 FALSE, FALSE, 0 );
1104         gtk_widget_show( vbox );
1105
1106         label = gtk_label_new( _( "Directory name:" ) );
1107         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 );
1108         gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 );
1109         gtk_widget_show( label );
1110
1111         /*  The directory entry widget  */
1112         fs->fileop_entry = gtk_entry_new();
1113         gtk_box_pack_start( GTK_BOX( vbox ), fs->fileop_entry,
1114                                                 TRUE, TRUE, 5 );
1115         GTK_WIDGET_SET_FLAGS( fs->fileop_entry, GTK_CAN_DEFAULT );
1116         gtk_widget_show( fs->fileop_entry );
1117
1118         /* buttons */
1119         button = gtk_button_new_with_label( _( "Create" ) );
1120         gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1121                                                 (GtkSignalFunc) gtk_file_selection_create_dir_confirmed,
1122                                                 (gpointer) fs );
1123         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
1124                                                 button, TRUE, TRUE, 0 );
1125         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
1126         gtk_widget_show( button );
1127
1128         button = gtk_button_new_with_label( _( "Cancel" ) );
1129         gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
1130                                                            (GtkSignalFunc) gtk_widget_destroy,
1131                                                            (gpointer) dialog );
1132         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
1133                                                 button, TRUE, TRUE, 0 );
1134         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
1135         gtk_widget_grab_default( button );
1136         gtk_widget_show( button );
1137
1138         gtk_widget_show( dialog );
1139 }
1140
1141 static void
1142 gtk_file_selection_delete_file_confirmed( GtkWidget *widget, gpointer data ){
1143         GtkFileSelection *fs = data;
1144         CompletionState *cmpl_state;
1145         gchar *path;
1146         gchar *full_path;
1147         gchar *buf;
1148
1149         g_return_if_fail( fs != NULL );
1150         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1151
1152         cmpl_state = (CompletionState*) fs->cmpl_state;
1153         path = cmpl_reference_position( cmpl_state );
1154
1155         full_path = g_strconcat( path, "/", fs->fileop_file, NULL );
1156         if ( ( unlink( full_path ) < 0 ) ) {
1157                 buf = g_strconcat( "Error deleting file \"", fs->fileop_file, "\":  ",
1158                                                    g_strerror( errno ), NULL );
1159                 gtk_file_selection_fileop_error( fs, buf );
1160         }
1161         g_free( full_path );
1162
1163         gtk_widget_destroy( fs->fileop_dialog );
1164         gtk_file_selection_populate( fs, "", FALSE );
1165 }
1166
1167 static void
1168 gtk_file_selection_delete_file( GtkWidget *widget, gpointer data ){
1169         GtkFileSelection *fs = data;
1170         GtkWidget *label;
1171         GtkWidget *vbox;
1172         GtkWidget *button;
1173         GtkWidget *dialog;
1174         gchar *filename;
1175         gchar *buf;
1176
1177         g_return_if_fail( fs != NULL );
1178         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1179
1180         if ( fs->fileop_dialog ) {
1181                 return;
1182         }
1183
1184         filename = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) );
1185         if ( strlen( filename ) < 1 ) {
1186                 return;
1187         }
1188
1189         fs->fileop_file = filename;
1190
1191         /* main dialog */
1192         fs->fileop_dialog = dialog = gtk_dialog_new();
1193         gtk_signal_connect( GTK_OBJECT( dialog ), "destroy",
1194                                                 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1195                                                 (gpointer) fs );
1196         gtk_window_set_title( GTK_WINDOW( dialog ), _( "Delete File" ) );
1197         gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
1198
1199         /* If file dialog is grabbed, grab option dialog */
1200         /* When option dialog is closed, file dialog will be grabbed again */
1201         if ( GTK_WINDOW( fs )->modal ) {
1202                 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
1203         }
1204
1205         vbox = gtk_vbox_new( FALSE, 0 );
1206         gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 );
1207         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox,
1208                                                 FALSE, FALSE, 0 );
1209         gtk_widget_show( vbox );
1210
1211         buf = g_strconcat( "Really delete file \"", filename, "\" ?", NULL );
1212         label = gtk_label_new( buf );
1213         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 );
1214         gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 );
1215         gtk_widget_show( label );
1216         g_free( buf );
1217
1218         /* buttons */
1219         button = gtk_button_new_with_label( _( "Delete" ) );
1220         gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1221                                                 (GtkSignalFunc) gtk_file_selection_delete_file_confirmed,
1222                                                 (gpointer) fs );
1223         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
1224                                                 button, TRUE, TRUE, 0 );
1225         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
1226         gtk_widget_show( button );
1227
1228         button = gtk_button_new_with_label( _( "Cancel" ) );
1229         gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
1230                                                            (GtkSignalFunc) gtk_widget_destroy,
1231                                                            (gpointer) dialog );
1232         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
1233                                                 button, TRUE, TRUE, 0 );
1234         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
1235         gtk_widget_grab_default( button );
1236         gtk_widget_show( button );
1237
1238         gtk_widget_show( dialog );
1239
1240 }
1241
1242 static void
1243 gtk_file_selection_rename_file_confirmed( GtkWidget *widget, gpointer data ){
1244         GtkFileSelection *fs = data;
1245         gchar *buf;
1246         gchar *file;
1247         gchar *path;
1248         gchar *new_filename;
1249         gchar *old_filename;
1250         CompletionState *cmpl_state;
1251
1252         g_return_if_fail( fs != NULL );
1253         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1254
1255         file = gtk_entry_get_text( GTK_ENTRY( fs->fileop_entry ) );
1256         cmpl_state = (CompletionState*) fs->cmpl_state;
1257         path = cmpl_reference_position( cmpl_state );
1258
1259         new_filename = g_strconcat( path, "/", file, NULL );
1260         old_filename = g_strconcat( path, "/", fs->fileop_file, NULL );
1261
1262         if ( ( rename( old_filename, new_filename ) ) < 0 ) {
1263                 buf = g_strconcat( "Error renaming file \"", file, "\":  ",
1264                                                    g_strerror( errno ), NULL );
1265                 gtk_file_selection_fileop_error( fs, buf );
1266         }
1267         g_free( new_filename );
1268         g_free( old_filename );
1269
1270         gtk_widget_destroy( fs->fileop_dialog );
1271         gtk_file_selection_populate( fs, "", FALSE );
1272 }
1273
1274 static void
1275 gtk_file_selection_rename_file( GtkWidget *widget, gpointer data ){
1276         GtkFileSelection *fs = data;
1277         GtkWidget *label;
1278         GtkWidget *dialog;
1279         GtkWidget *vbox;
1280         GtkWidget *button;
1281         gchar *buf;
1282
1283         g_return_if_fail( fs != NULL );
1284         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1285
1286         if ( fs->fileop_dialog ) {
1287                 return;
1288         }
1289
1290         fs->fileop_file = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) );
1291         if ( strlen( fs->fileop_file ) < 1 ) {
1292                 return;
1293         }
1294
1295         /* main dialog */
1296         fs->fileop_dialog = dialog = gtk_dialog_new();
1297         gtk_signal_connect( GTK_OBJECT( dialog ), "destroy",
1298                                                 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1299                                                 (gpointer) fs );
1300         gtk_window_set_title( GTK_WINDOW( dialog ), _( "Rename File" ) );
1301         gtk_window_set_position( GTK_WINDOW( dialog ), GTK_WIN_POS_MOUSE );
1302
1303         /* If file dialog is grabbed, grab option dialog */
1304         /* When option dialog  closed, file dialog will be grabbed again */
1305         if ( GTK_WINDOW( fs )->modal ) {
1306                 gtk_window_set_modal( GTK_WINDOW( dialog ), TRUE );
1307         }
1308
1309         vbox = gtk_vbox_new( FALSE, 0 );
1310         gtk_container_set_border_width( GTK_CONTAINER( vbox ), 8 );
1311         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->vbox ), vbox,
1312                                                 FALSE, FALSE, 0 );
1313         gtk_widget_show( vbox );
1314
1315         buf = g_strconcat( "Rename file \"", fs->fileop_file, "\" to:", NULL );
1316         label = gtk_label_new( buf );
1317         gtk_misc_set_alignment( GTK_MISC( label ), 0.0, 0.0 );
1318         gtk_box_pack_start( GTK_BOX( vbox ), label, FALSE, FALSE, 5 );
1319         gtk_widget_show( label );
1320         g_free( buf );
1321
1322         /* New filename entry */
1323         fs->fileop_entry = gtk_entry_new();
1324         gtk_box_pack_start( GTK_BOX( vbox ), fs->fileop_entry,
1325                                                 TRUE, TRUE, 5 );
1326         GTK_WIDGET_SET_FLAGS( fs->fileop_entry, GTK_CAN_DEFAULT );
1327         gtk_widget_show( fs->fileop_entry );
1328
1329         gtk_entry_set_text( GTK_ENTRY( fs->fileop_entry ), fs->fileop_file );
1330         gtk_editable_select_region( GTK_EDITABLE( fs->fileop_entry ),
1331                                                                 0, strlen( fs->fileop_file ) );
1332
1333         /* buttons */
1334         button = gtk_button_new_with_label( _( "Rename" ) );
1335         gtk_signal_connect( GTK_OBJECT( button ), "clicked",
1336                                                 (GtkSignalFunc) gtk_file_selection_rename_file_confirmed,
1337                                                 (gpointer) fs );
1338         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
1339                                                 button, TRUE, TRUE, 0 );
1340         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
1341         gtk_widget_show( button );
1342
1343         button = gtk_button_new_with_label( _( "Cancel" ) );
1344         gtk_signal_connect_object( GTK_OBJECT( button ), "clicked",
1345                                                            (GtkSignalFunc) gtk_widget_destroy,
1346                                                            (gpointer) dialog );
1347         gtk_box_pack_start( GTK_BOX( GTK_DIALOG( dialog )->action_area ),
1348                                                 button, TRUE, TRUE, 0 );
1349         GTK_WIDGET_SET_FLAGS( button, GTK_CAN_DEFAULT );
1350         gtk_widget_grab_default( button );
1351         gtk_widget_show( button );
1352
1353         gtk_widget_show( dialog );
1354 }
1355
1356
1357 static gint
1358 gtk_file_selection_key_press( GtkWidget   *widget,
1359                                                           GdkEventKey *event,
1360                                                           gpointer user_data ){
1361         GtkFileSelection *fs;
1362         char *text;
1363
1364         g_return_val_if_fail( widget != NULL, FALSE );
1365         g_return_val_if_fail( event != NULL, FALSE );
1366
1367         fs = GTK_FILE_SELECTION( user_data );
1368
1369         if ( event->keyval == GDK_Tab ) {
1370                 text = gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) );
1371
1372                 text = g_strdup( text );
1373
1374                 gtk_file_selection_populate( fs, text, TRUE );
1375
1376                 g_free( text );
1377
1378                 gtk_signal_emit_stop_by_name( GTK_OBJECT( widget ), "key_press_event" );
1379
1380                 return TRUE;
1381         }
1382         if ( fs->saved_entry ) {
1383                 gtk_clist_unselect_all( (GtkCList *) ( fs->dir_list ) );
1384                 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry );
1385                 g_free( fs->saved_entry );
1386                 fs->saved_entry = NULL;
1387         }
1388
1389
1390         return FALSE;
1391 }
1392
1393 static void
1394 gtk_file_selection_home_button( GtkWidget *widget, gpointer data ){
1395         GList *list;
1396
1397         GtkFileSelection *fs = data;
1398
1399         list = fs->next_history;
1400         if ( list ) {
1401                 g_free( list->data );
1402                 list = list->next;
1403         }
1404         g_list_free( fs->next_history );
1405         fs->next_history = NULL;
1406
1407         gtk_file_selection_populate( fs,"~/",FALSE );
1408 }
1409
1410 static void
1411 gtk_file_selection_up_button( GtkWidget *widget, gpointer data ){
1412         GtkFileSelection *fs = data;
1413         GList *list;
1414
1415         g_return_if_fail( fs != NULL );
1416         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1417
1418         list = fs->next_history;
1419         if ( list ) {
1420                 g_free( list->data );
1421                 list = list->next;
1422         }
1423         g_list_free( fs->next_history );
1424         fs->next_history = NULL;
1425
1426         gtk_file_selection_populate( fs, "../", FALSE ); /*change directories. */
1427
1428 }
1429
1430 static void
1431 gtk_file_selection_prev_button( GtkWidget *widget, gpointer data ){
1432         GtkFileSelection *fs = data;
1433         GList *list;
1434         GList *first;
1435         gchar *path;
1436
1437         g_return_if_fail( fs != NULL );
1438         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1439
1440         list = fs->prev_history;
1441
1442         if ( list && g_list_length( list ) > 1 ) {
1443                 first = list;          /* get first element */
1444                 list = list->next;     /* pop off current directory */
1445
1446                 list->prev = NULL;     /* make this the new head. */
1447
1448                 fs->prev_history = list; /* update prev_history list */
1449                 fs->next_history = g_list_prepend( fs->next_history,first->data ); /* put it on next_history */
1450
1451                 first->next = NULL;    /* orphan the old first node */
1452                 g_list_free( first );  /* free the node (data is now in use by next_history) */
1453
1454
1455
1456                 path = g_malloc( strlen( list->data ) + 4 ); /* plenty of space */
1457                 strcpy( path,list->data );           /* get the 2nd path in the history */
1458                 strcat( path,"/" );                  /* append a '/' */
1459                 gtk_file_selection_populate( fs, path, FALSE ); /* change directories. */
1460                 g_free( path );
1461         }
1462 }
1463
1464 static void
1465 gtk_file_selection_next_button( GtkWidget *widget, gpointer data ){
1466         GtkFileSelection *fs = data;
1467         GList *list;
1468         GList *first;
1469         gchar *path;
1470
1471         g_return_if_fail( fs != NULL );
1472         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1473
1474         list = fs->next_history;
1475
1476         if ( list && g_list_length( list ) > 0 ) {
1477                 first = list;          /*get first element*/
1478                 list = list->next;     /*pop off current directory*/
1479
1480                 if ( list ) {
1481                         list->prev = NULL;
1482                 }
1483
1484                 fs->next_history = list;                     /*update prev_history list*/
1485
1486                 path = g_malloc( strlen( first->data ) + 4 ); /*plenty of space*/
1487                 strcpy( path,first->data );
1488                 strcat( path,"/" );                          /*append a /   */
1489                 gtk_file_selection_populate( fs, path, FALSE ); /*change directories.*/
1490                 g_free( path );
1491
1492                 first->next = NULL;   /* orphan the old first node */
1493                 g_list_free( first ); /* free the node (data is now in use by next_history) */
1494
1495         }
1496 }
1497
1498 void static
1499 gtk_file_selection_refresh_button( GtkWidget *widget, gpointer data ){
1500         GtkFileSelection *fs = data;
1501
1502         g_return_if_fail( fs != NULL );
1503         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1504
1505         gtk_file_selection_populate( fs,"",FALSE );
1506 }
1507
1508 static void
1509 gtk_file_selection_mask_entry_callback( GtkWidget *widget, gpointer data ){
1510         GtkFileSelection *fs = data;
1511
1512         if ( fs->mask ) {
1513                 g_free( fs->mask );
1514         }
1515
1516         fs->mask = g_strdup( gtk_entry_get_text( GTK_ENTRY( fs->mask_entry ) ) );
1517
1518         if ( strlen( fs->mask ) == 0 ) {
1519                 g_free( fs->mask );
1520                 fs->mask = NULL;
1521         }
1522
1523         gtk_file_selection_refresh_button( widget,data );
1524 }
1525
1526 static gboolean gtk_file_selection_history_combo_list_key_handler( GtkWidget *widget,
1527                                                                                                                                    GdkEventKey *event,
1528                                                                                                                                    gpointer user_data ){
1529         /*
1530            g_print("Key pressed! \n");
1531          */
1532
1533         return TRUE;
1534 }
1535
1536 static gboolean gtk_file_selection_history_combo_list_callback( GtkWidget *thelist,
1537                                                                                                                                 GdkEventButton *event,
1538                                                                                                                                 gpointer user_data ){
1539
1540         GtkFileSelection *fs = user_data;
1541         GList *list;
1542         gchar *path;
1543
1544         list = fs->next_history;
1545         if ( list ) {
1546                 g_free( list->data );
1547                 list = list->next;
1548         }
1549         g_list_free( fs->next_history );
1550         fs->next_history = NULL;
1551
1552         path = g_malloc( strlen( gtk_entry_get_text( GTK_ENTRY( ( (GtkCombo *)fs->history_combo )->entry ) ) ) + 4 );
1553         strcpy( path,gtk_entry_get_text( GTK_ENTRY( ( (GtkCombo *)fs->history_combo )->entry ) ) );
1554         strcat( path,"/" );
1555
1556         gtk_file_selection_populate( fs,path,TRUE );
1557
1558         g_free( path );
1559
1560         return TRUE;
1561 }
1562
1563 static gboolean
1564 gtk_file_selection_history_combo_callback( GtkWidget *widget, GdkEventKey *event, gpointer data ){
1565         GtkEntry *entry = (GtkEntry *)widget;
1566         GtkFileSelection *fs = data;
1567         GList *list;
1568         gchar *path;
1569
1570         g_return_val_if_fail( fs != NULL,FALSE );
1571         g_return_val_if_fail( GTK_IS_FILE_SELECTION( fs ),FALSE );
1572
1573
1574         if ( event->keyval == GDK_Return ) {
1575                 list = fs->next_history;
1576                 if ( list ) {
1577                         g_free( list->data );
1578                         list = list->next;
1579                 }
1580                 g_list_free( fs->next_history );
1581                 fs->next_history = NULL;
1582
1583                 path = g_malloc( strlen( gtk_entry_get_text( entry ) ) + 4 );
1584                 strcpy( path,gtk_entry_get_text( entry ) );
1585                 strcat( path,"/" );
1586                 gtk_file_selection_populate( fs,path,TRUE );
1587                 g_free( path );
1588                 gtk_signal_emit_stop_by_name( GTK_OBJECT( widget ), "key_press_event" );
1589                 return TRUE;
1590         }
1591         else
1592         {
1593                 return FALSE;
1594         }
1595
1596 }
1597
1598 static void
1599 gtk_file_selection_update_history_menu( GtkFileSelection *fs,
1600                                                                                 gchar *current_directory ){
1601         gchar *current_dir;
1602
1603         g_return_if_fail( fs != NULL );
1604         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1605         g_return_if_fail( current_directory != NULL );
1606
1607         current_dir = g_strdup( current_directory );
1608
1609         if ( fs->prev_history ) {
1610                 if ( strcmp( ( fs->prev_history )->data,current_dir ) ) { /*if this item isn't on the top of the list */
1611                         fs->prev_history = g_list_prepend( fs->prev_history,g_strdup( current_dir ) );
1612                 }
1613         }
1614         else {
1615                 fs->prev_history = g_list_prepend( fs->prev_history,g_strdup( current_dir ) );
1616         }
1617
1618         gtk_combo_set_popdown_strings( GTK_COMBO( fs->history_combo ),fs->prev_history );
1619
1620         g_free( current_dir );
1621 }
1622
1623 static void
1624 gtk_file_selection_file_button( GtkWidget *widget,
1625                                                                 gint row,
1626                                                                 gint column,
1627                                                                 GdkEventButton *bevent,
1628                                                                 gpointer user_data ){
1629         GtkFileSelection *fs = NULL;
1630         gchar *filename, *temp = NULL;
1631
1632         g_return_if_fail( GTK_IS_CLIST( widget ) );
1633
1634         fs = user_data;
1635         g_return_if_fail( fs != NULL );
1636         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1637
1638         gtk_clist_get_text( GTK_CLIST( fs->file_list ), row, 0, &temp );
1639         filename = g_strdup( temp );
1640
1641         if ( filename ) {
1642                 if ( bevent ) {
1643                         switch ( bevent->type )
1644                         {
1645                         case GDK_2BUTTON_PRESS:
1646                                 gtk_button_clicked( GTK_BUTTON( fs->ok_button ) );
1647                                 break;
1648
1649                         default:
1650                                 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename );
1651                                 break;
1652                         }
1653                 }
1654                 else{
1655                         gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename );
1656                 }
1657
1658                 g_free( filename );
1659         }
1660 }
1661
1662 static void
1663 gtk_file_selection_dir_button( GtkWidget *widget,
1664                                                            gint row,
1665                                                            gint column,
1666                                                            GdkEventButton *bevent,
1667                                                            gpointer user_data ){
1668         GList *list;
1669         GtkFileSelection *fs = NULL;
1670         gchar *filename, *temp = NULL;
1671
1672         g_return_if_fail( GTK_IS_CLIST( widget ) );
1673
1674         fs = GTK_FILE_SELECTION( user_data );
1675         g_return_if_fail( fs != NULL );
1676         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1677
1678         gtk_clist_get_text( GTK_CLIST( fs->dir_list ), row, 0, &temp );
1679         filename = g_strdup( temp );
1680
1681         if ( filename ) {
1682                 if ( bevent ) {
1683                         switch ( bevent->type )
1684                         {
1685                         case GDK_2BUTTON_PRESS:
1686                                 list = fs->next_history;
1687                                 if ( list ) {
1688                                         g_free( list->data );
1689                                         list = list->next;
1690                                 }
1691                                 g_list_free( fs->next_history );
1692                                 fs->next_history = NULL;
1693
1694                                 gtk_file_selection_populate( fs, filename, FALSE );
1695                                 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry );
1696                                 g_free( fs->saved_entry );
1697                                 fs->saved_entry = NULL;
1698                                 break;
1699
1700                         default:
1701                                 /* here we need to add the "filename" to the beginning of what's already
1702                                    in the entry.  Save what's in the entry, then restore it on the double click
1703                                  */
1704                                 if ( fs->saved_entry ) {
1705                                         g_free( fs->saved_entry );
1706                                 }
1707                                 fs->saved_entry = g_strdup( gtk_entry_get_text( GTK_ENTRY( fs->selection_entry ) ) );
1708
1709                                 temp = g_strconcat( filename,fs->saved_entry,NULL );
1710                                 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), temp );
1711                                 g_free( temp );
1712
1713                                 break;
1714                         }
1715                 }
1716                 else{
1717                         gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename );
1718                 }
1719
1720                 g_free( filename );
1721         }
1722 }
1723
1724 static void
1725 gtk_file_selection_undir_button( GtkWidget *widget,
1726                                                                  gint row,
1727                                                                  gint column,
1728                                                                  GdkEventButton *bevent,
1729                                                                  gpointer user_data ){
1730         GtkFileSelection *fs = NULL;
1731         gchar *filename, *temp = NULL;
1732
1733         g_return_if_fail( GTK_IS_CLIST( widget ) );
1734
1735         fs = GTK_FILE_SELECTION( user_data );
1736         g_return_if_fail( fs != NULL );
1737         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1738
1739         gtk_clist_get_text( GTK_CLIST( fs->dir_list ), row, 0, &temp );
1740         filename = g_strdup( temp );
1741
1742         if ( filename ) {
1743                 if ( bevent ) {
1744                         switch ( bevent->type )
1745                         {
1746                         default:
1747                                 /* here we need to add the "filename" to the beginning of what's already
1748                                    in the entry.  Save what's in the entry, then restore it on the double click
1749                                  */
1750                                 if ( fs->saved_entry ) {
1751                                         gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),fs->saved_entry );
1752                                         g_free( fs->saved_entry );
1753                                         fs->saved_entry = NULL;
1754                                 }
1755                                 break;
1756                         }
1757                 }
1758                 else{
1759                         gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), filename ); //?????
1760
1761                 }
1762                 g_free( filename );
1763         }
1764 }
1765
1766 static void
1767 gtk_file_selection_populate( GtkFileSelection *fs,
1768                                                          gchar            *rel_path,
1769                                                          gint try_complete ){
1770         CompletionState *cmpl_state;
1771         PossibleCompletion* poss;
1772         gchar* filename;
1773         gint row;
1774         gchar* rem_path = rel_path;
1775         gchar* sel_text;
1776         gchar* text[2];
1777         gint did_recurse = FALSE;
1778         gint possible_count = 0;
1779         gint selection_index = -1;
1780         gint file_list_width;
1781         gint dir_list_width;
1782
1783         g_return_if_fail( fs != NULL );
1784         g_return_if_fail( GTK_IS_FILE_SELECTION( fs ) );
1785
1786         cmpl_state = (CompletionState*) fs->cmpl_state;
1787         poss = cmpl_completion_matches( rel_path, &rem_path, cmpl_state );
1788
1789         if ( !cmpl_state_okay( cmpl_state ) ) {
1790                 /* Something went wrong. */
1791                 gtk_file_selection_abort( fs );
1792                 return;
1793         }
1794
1795         g_assert( cmpl_state->reference_dir );
1796
1797         gtk_clist_freeze( GTK_CLIST( fs->dir_list ) );
1798         gtk_clist_clear( GTK_CLIST( fs->dir_list ) );
1799         gtk_clist_freeze( GTK_CLIST( fs->file_list ) );
1800         gtk_clist_clear( GTK_CLIST( fs->file_list ) );
1801
1802         /* Set the dir_list to include ./ and ../ */
1803         text[1] = NULL;
1804         text[0] = "./";
1805         row = gtk_clist_append( GTK_CLIST( fs->dir_list ), text );
1806
1807         text[0] = "../";
1808         row = gtk_clist_append( GTK_CLIST( fs->dir_list ), text );
1809
1810         /*reset the max widths of the lists*/
1811         dir_list_width = gdk_string_width( fs->dir_list->style->font,"../" );
1812         gtk_clist_set_column_width( GTK_CLIST( fs->dir_list ),0,dir_list_width );
1813         file_list_width = 1;
1814         gtk_clist_set_column_width( GTK_CLIST( fs->file_list ),0,file_list_width );
1815
1816         while ( poss )
1817         {
1818                 if ( cmpl_is_a_completion( poss ) ) {
1819                         possible_count += 1;
1820
1821                         filename = cmpl_this_completion( poss );
1822
1823                         text[0] = filename;
1824
1825                         if ( cmpl_is_directory( poss ) ) {
1826                                 if ( strcmp( filename, "./" ) != 0 &&
1827                                          strcmp( filename, "../" ) != 0 ) {
1828                                         int width = gdk_string_width( fs->dir_list->style->font,
1829                                                                                                   filename );
1830                                         row = gtk_clist_append( GTK_CLIST( fs->dir_list ), text );
1831                                         if ( width > dir_list_width ) {
1832                                                 dir_list_width = width;
1833                                                 gtk_clist_set_column_width( GTK_CLIST( fs->dir_list ),0,
1834                                                                                                         width );
1835                                         }
1836                                 }
1837                         }
1838                         else
1839                         {
1840                                 if ( fs->mask ) {
1841                                         if ( gtk_file_selection_match_mask( filename,fs->mask ) ) {
1842                                                 int width = gdk_string_width( fs->file_list->style->font,
1843                                                                                                           filename );
1844                                                 row = gtk_clist_append( GTK_CLIST( fs->file_list ), text );
1845                                                 if ( width > file_list_width ) {
1846                                                         file_list_width = width;
1847                                                         gtk_clist_set_column_width( GTK_CLIST( fs->file_list ),0,
1848                                                                                                                 width );
1849                                                 }
1850                                         }
1851                                 }
1852                                 else
1853                                 {
1854                                         int width = gdk_string_width( fs->file_list->style->font,
1855                                                                                                   filename );
1856                                         row = gtk_clist_append( GTK_CLIST( fs->file_list ), text );
1857                                         if ( width > file_list_width ) {
1858                                                 file_list_width = width;
1859                                                 gtk_clist_set_column_width( GTK_CLIST( fs->file_list ),0,
1860                                                                                                         width );
1861                                         }
1862                                 }
1863                         }
1864                 }
1865
1866                 poss = cmpl_next_completion( cmpl_state );
1867         }
1868
1869         gtk_clist_thaw( GTK_CLIST( fs->dir_list ) );
1870         gtk_clist_thaw( GTK_CLIST( fs->file_list ) );
1871
1872         /* File lists are set. */
1873
1874         g_assert( cmpl_state->reference_dir );
1875
1876         if ( try_complete ) {
1877
1878                 /* User is trying to complete filenames, so advance the user's input
1879                  * string to the updated_text, which is the common leading substring
1880                  * of all possible completions, and if its a directory attempt
1881                  * attempt completions in it. */
1882
1883                 if ( cmpl_updated_text( cmpl_state )[0] ) {
1884
1885                         if ( cmpl_updated_dir( cmpl_state ) ) {
1886                                 gchar* dir_name = g_strdup( cmpl_updated_text( cmpl_state ) );
1887
1888                                 did_recurse = TRUE;
1889
1890                                 gtk_file_selection_populate( fs, dir_name, TRUE );
1891
1892                                 g_free( dir_name );
1893                         }
1894                         else
1895                         {
1896                                 if ( fs->selection_entry ) {
1897                                         gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ),
1898                                                                                 cmpl_updated_text( cmpl_state ) );
1899                                 }
1900                         }
1901                 }
1902                 else
1903                 {
1904                         selection_index = cmpl_last_valid_char( cmpl_state ) -
1905                                                           ( strlen( rel_path ) - strlen( rem_path ) );
1906                         if ( fs->selection_entry ) {
1907                                 gtk_entry_set_text( GTK_ENTRY( fs->selection_entry ), rem_path );
1908                         }
1909                 }
1910         }
1911         else
1912         {
1913                 if ( fs->selection_entry ) {
1914                         /* Here we need to take the old filename and keep it!*/
1915                         /*gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");*/
1916                         ;
1917                 }
1918         }
1919
1920         if ( !did_recurse ) {
1921                 if ( fs->selection_entry ) {
1922                         gtk_entry_set_position( GTK_ENTRY( fs->selection_entry ), selection_index );
1923                 }
1924
1925                 if ( fs->selection_entry ) {
1926                         sel_text = g_strconcat( _( "Selection: " ),
1927                                                                         cmpl_reference_position( cmpl_state ),
1928                                                                         NULL );
1929
1930                         gtk_label_set_text( GTK_LABEL( fs->selection_text ), sel_text );
1931                         g_free( sel_text );
1932                 }
1933
1934                 gtk_file_selection_update_history_menu( fs, cmpl_reference_position( cmpl_state ) );
1935
1936         }
1937 }
1938
1939 static void
1940 gtk_file_selection_abort( GtkFileSelection *fs ){
1941         gchar err_buf[256];
1942
1943         sprintf( err_buf, _( "Directory unreadable: %s" ), cmpl_strerror( cmpl_errno ) );
1944
1945         /*  BEEP gdk_beep();  */
1946
1947         if ( fs->selection_entry ) {
1948                 gtk_label_set_text( GTK_LABEL( fs->selection_text ), err_buf );
1949         }
1950 }
1951
1952 /**********************************************************************/
1953 /*                        External Interface                          */
1954 /**********************************************************************/
1955
1956 /* The four completion state selectors
1957  */
1958 static gchar*
1959 cmpl_updated_text( CompletionState* cmpl_state ){
1960         return cmpl_state->updated_text;
1961 }
1962
1963 static gint
1964 cmpl_updated_dir( CompletionState* cmpl_state ){
1965         return cmpl_state->re_complete;
1966 }
1967
1968 static gchar*
1969 cmpl_reference_position( CompletionState* cmpl_state ){
1970         return cmpl_state->reference_dir->fullname;
1971 }
1972
1973 static gint
1974 cmpl_last_valid_char( CompletionState* cmpl_state ){
1975         return cmpl_state->last_valid_char;
1976 }
1977
1978 static gchar*
1979 cmpl_completion_fullname( gchar* text, CompletionState* cmpl_state ){
1980         static char nothing[2] = "";
1981
1982         if ( !cmpl_state_okay( cmpl_state ) ) {
1983                 return nothing;
1984         }
1985         else if ( text[0] == '/' ) {
1986                 strcpy( cmpl_state->updated_text, text );
1987         }
1988         else if ( text[0] == '~' ) {
1989                 CompletionDir* dir;
1990                 char* slash;
1991
1992                 dir = open_user_dir( text, cmpl_state );
1993
1994                 if ( !dir ) {
1995                         /* spencer says just return ~something, so
1996                          * for now just do it. */
1997                         strcpy( cmpl_state->updated_text, text );
1998                 }
1999                 else
2000                 {
2001
2002                         strcpy( cmpl_state->updated_text, dir->fullname );
2003
2004                         slash = strchr( text, '/' );
2005
2006                         if ( slash ) {
2007                                 strcat( cmpl_state->updated_text, slash );
2008                         }
2009                 }
2010         }
2011         else
2012         {
2013                 strcpy( cmpl_state->updated_text, cmpl_state->reference_dir->fullname );
2014                 if ( strcmp( cmpl_state->reference_dir->fullname, "/" ) != 0 ) {
2015                         strcat( cmpl_state->updated_text, "/" );
2016                 }
2017                 strcat( cmpl_state->updated_text, text );
2018         }
2019
2020         return cmpl_state->updated_text;
2021 }
2022
2023 /* The three completion selectors
2024  */
2025 static gchar*
2026 cmpl_this_completion( PossibleCompletion* pc ){
2027         return pc->text;
2028 }
2029
2030 static gint
2031 cmpl_is_directory( PossibleCompletion* pc ){
2032         return pc->is_directory;
2033 }
2034
2035 static gint
2036 cmpl_is_a_completion( PossibleCompletion* pc ){
2037         return pc->is_a_completion;
2038 }
2039
2040 /**********************************************************************/
2041 /*                       Construction, deletion                       */
2042 /**********************************************************************/
2043
2044 static CompletionState*
2045 cmpl_init_state( void ){
2046         gchar getcwd_buf[2 * MAXPATHLEN];
2047         CompletionState *new_state;
2048
2049         new_state = g_new( CompletionState, 1 );
2050
2051         /* We don't use getcwd() on SUNOS, because, it does a popen("pwd")
2052          * and, if that wasn't bad enough, hangs in doing so.
2053          */
2054 #if defined( sun ) && !defined( __SVR4 )
2055         if ( !getwd( getcwd_buf ) )
2056 #else
2057         if ( !getcwd( getcwd_buf, MAXPATHLEN ) )
2058 #endif
2059         {
2060                 /* Oh joy, we can't get the current directory. Um..., we should have
2061                  * a root directory, right? Right? (Probably not portable to non-Unix)
2062                  */
2063                 strcpy( getcwd_buf, "/" );
2064         }
2065
2066 tryagain:
2067
2068         new_state->reference_dir = NULL;
2069         new_state->completion_dir = NULL;
2070         new_state->active_completion_dir = NULL;
2071         new_state->directory_storage = NULL;
2072         new_state->directory_sent_storage = NULL;
2073         new_state->last_valid_char = 0;
2074         new_state->updated_text = g_new( gchar, MAXPATHLEN );
2075         new_state->updated_text_alloc = MAXPATHLEN;
2076         new_state->the_completion.text = g_new( gchar, MAXPATHLEN );
2077         new_state->the_completion.text_alloc = MAXPATHLEN;
2078         new_state->user_dir_name_buffer = NULL;
2079         new_state->user_directories = NULL;
2080
2081         new_state->reference_dir =  open_dir( getcwd_buf, new_state );
2082
2083         if ( !new_state->reference_dir ) {
2084                 /* Directories changing from underneath us, grumble */
2085                 strcpy( getcwd_buf, "/" );
2086                 goto tryagain;
2087         }
2088
2089         return new_state;
2090 }
2091
2092 static void
2093 cmpl_free_dir_list( GList* dp0 ){
2094         GList *dp = dp0;
2095
2096         while ( dp ) {
2097                 free_dir( dp->data );
2098                 dp = dp->next;
2099         }
2100
2101         g_list_free( dp0 );
2102 }
2103
2104 static void
2105 cmpl_free_dir_sent_list( GList* dp0 ){
2106         GList *dp = dp0;
2107
2108         while ( dp ) {
2109                 free_dir_sent( dp->data );
2110                 dp = dp->next;
2111         }
2112
2113         g_list_free( dp0 );
2114 }
2115
2116 static void
2117 cmpl_free_state( CompletionState* cmpl_state ){
2118         cmpl_free_dir_list( cmpl_state->directory_storage );
2119         cmpl_free_dir_sent_list( cmpl_state->directory_sent_storage );
2120
2121         if ( cmpl_state->user_dir_name_buffer ) {
2122                 g_free( cmpl_state->user_dir_name_buffer );
2123         }
2124         if ( cmpl_state->user_directories ) {
2125                 g_free( cmpl_state->user_directories );
2126         }
2127         if ( cmpl_state->the_completion.text ) {
2128                 g_free( cmpl_state->the_completion.text );
2129         }
2130         if ( cmpl_state->updated_text ) {
2131                 g_free( cmpl_state->updated_text );
2132         }
2133
2134         g_free( cmpl_state );
2135 }
2136
2137 static void
2138 free_dir( CompletionDir* dir ){
2139         g_free( dir->fullname );
2140         g_free( dir );
2141 }
2142
2143 static void
2144 free_dir_sent( CompletionDirSent* sent ){
2145         g_free( sent->name_buffer );
2146         g_free( sent->entries );
2147         g_free( sent );
2148 }
2149
2150 static void
2151 prune_memory_usage( CompletionState *cmpl_state ){
2152         GList* cdsl = cmpl_state->directory_sent_storage;
2153         GList* cdl = cmpl_state->directory_storage;
2154         GList* cdl0 = cdl;
2155         gint len = 0;
2156
2157         for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1 )
2158                 cdsl = cdsl->next;
2159
2160         if ( cdsl ) {
2161                 cmpl_free_dir_sent_list( cdsl->next );
2162                 cdsl->next = NULL;
2163         }
2164
2165         cmpl_state->directory_storage = NULL;
2166         while ( cdl ) {
2167                 if ( cdl->data == cmpl_state->reference_dir ) {
2168                         cmpl_state->directory_storage = g_list_prepend( NULL, cdl->data );
2169                 }
2170                 else{
2171                         free_dir( cdl->data );
2172                 }
2173                 cdl = cdl->next;
2174         }
2175
2176         g_list_free( cdl0 );
2177 }
2178
2179 /**********************************************************************/
2180 /*                        The main entrances.                         */
2181 /**********************************************************************/
2182
2183 static PossibleCompletion*
2184 cmpl_completion_matches( gchar* text_to_complete,
2185                                                  gchar** remaining_text,
2186                                                  CompletionState* cmpl_state ){
2187         gchar* first_slash;
2188         PossibleCompletion *poss;
2189
2190         prune_memory_usage( cmpl_state );
2191
2192         g_assert( text_to_complete != NULL );
2193
2194         cmpl_state->user_completion_index = -1;
2195         cmpl_state->last_completion_text = text_to_complete;
2196         cmpl_state->the_completion.text[0] = 0;
2197         cmpl_state->last_valid_char = 0;
2198         cmpl_state->updated_text_len = -1;
2199         cmpl_state->updated_text[0] = 0;
2200         cmpl_state->re_complete = FALSE;
2201
2202         first_slash = strchr( text_to_complete, '/' );
2203
2204         if ( text_to_complete[0] == '~' && !first_slash ) {
2205                 /* Text starts with ~ and there is no slash, show all the
2206                  * home directory completions.
2207                  */
2208                 poss = attempt_homedir_completion( text_to_complete, cmpl_state );
2209
2210                 update_cmpl( poss, cmpl_state );
2211
2212                 return poss;
2213         }
2214
2215         cmpl_state->reference_dir =
2216                 open_ref_dir( text_to_complete, remaining_text, cmpl_state );
2217
2218         if ( !cmpl_state->reference_dir ) {
2219                 return NULL;
2220         }
2221
2222         cmpl_state->completion_dir =
2223                 find_completion_dir( *remaining_text, remaining_text, cmpl_state );
2224
2225         cmpl_state->last_valid_char = *remaining_text - text_to_complete;
2226
2227         if ( !cmpl_state->completion_dir ) {
2228                 return NULL;
2229         }
2230
2231         cmpl_state->completion_dir->cmpl_index = -1;
2232         cmpl_state->completion_dir->cmpl_parent = NULL;
2233         cmpl_state->completion_dir->cmpl_text = *remaining_text;
2234
2235         cmpl_state->active_completion_dir = cmpl_state->completion_dir;
2236
2237         cmpl_state->reference_dir = cmpl_state->completion_dir;
2238
2239         poss = attempt_file_completion( cmpl_state );
2240
2241         update_cmpl( poss, cmpl_state );
2242
2243         return poss;
2244 }
2245
2246 static PossibleCompletion*
2247 cmpl_next_completion( CompletionState* cmpl_state ){
2248         PossibleCompletion* poss = NULL;
2249
2250         cmpl_state->the_completion.text[0] = 0;
2251
2252         if ( cmpl_state->user_completion_index >= 0 ) {
2253                 poss = attempt_homedir_completion( cmpl_state->last_completion_text, cmpl_state );
2254         }
2255         else{
2256                 poss = attempt_file_completion( cmpl_state );
2257         }
2258
2259         update_cmpl( poss, cmpl_state );
2260
2261         return poss;
2262 }
2263
2264 /**********************************************************************/
2265 /*                       Directory Operations                         */
2266 /**********************************************************************/
2267
2268 /* Open the directory where completion will begin from, if possible. */
2269 static CompletionDir*
2270 open_ref_dir( gchar* text_to_complete,
2271                           gchar** remaining_text,
2272                           CompletionState* cmpl_state ){
2273         gchar* first_slash;
2274         CompletionDir *new_dir;
2275
2276         first_slash = strchr( text_to_complete, '/' );
2277
2278         if ( text_to_complete[0] == '~' ) {
2279                 new_dir = open_user_dir( text_to_complete, cmpl_state );
2280
2281                 if ( new_dir ) {
2282                         if ( first_slash ) {
2283                                 *remaining_text = first_slash + 1;
2284                         }
2285                         else{
2286                                 *remaining_text = text_to_complete + strlen( text_to_complete );
2287                         }
2288                 }
2289                 else
2290                 {
2291                         return NULL;
2292                 }
2293         }
2294         else if ( text_to_complete[0] == '/' || !cmpl_state->reference_dir ) {
2295                 gchar *tmp = g_strdup( text_to_complete );
2296                 gchar *p;
2297
2298                 p = tmp;
2299                 while ( *p && *p != '*' && *p != '?' )
2300                         p++;
2301
2302                 *p = '\0';
2303                 p = strrchr( tmp, '/' );
2304                 if ( p ) {
2305                         if ( p == tmp ) {
2306                                 p++;
2307                         }
2308
2309                         *p = '\0';
2310
2311                         new_dir = open_dir( tmp, cmpl_state );
2312
2313                         if ( new_dir ) {
2314                                 *remaining_text = text_to_complete +
2315                                                                   ( ( p == tmp + 1 ) ? ( p - tmp ) : ( p + 1 - tmp ) );
2316                         }
2317                 }
2318                 else
2319                 {
2320                         /* If no possible candidates, use the cwd */
2321                         gchar *curdir = g_get_current_dir();
2322
2323                         new_dir = open_dir( curdir, cmpl_state );
2324
2325                         if ( new_dir ) {
2326                                 *remaining_text = text_to_complete;
2327                         }
2328
2329                         g_free( curdir );
2330                 }
2331
2332                 g_free( tmp );
2333         }
2334         else
2335         {
2336                 *remaining_text = text_to_complete;
2337
2338                 new_dir = open_dir( cmpl_state->reference_dir->fullname, cmpl_state );
2339         }
2340
2341         if ( new_dir ) {
2342                 new_dir->cmpl_index = -1;
2343                 new_dir->cmpl_parent = NULL;
2344         }
2345
2346         return new_dir;
2347 }
2348
2349 /* open a directory by user name */
2350 static CompletionDir*
2351 open_user_dir( gchar* text_to_complete,
2352                            CompletionState *cmpl_state ){
2353         gchar *first_slash;
2354         gint cmp_len;
2355
2356         g_assert( text_to_complete && text_to_complete[0] == '~' );
2357
2358         first_slash = strchr( text_to_complete, '/' );
2359
2360         if ( first_slash ) {
2361                 cmp_len = first_slash - text_to_complete - 1;
2362         }
2363         else{
2364                 cmp_len = strlen( text_to_complete + 1 );
2365         }
2366
2367         if ( !cmp_len ) {
2368                 /* ~/ */
2369                 gchar *homedir = g_get_home_dir();
2370
2371                 if ( homedir ) {
2372                         return open_dir( homedir, cmpl_state );
2373                 }
2374                 else{
2375                         return NULL;
2376                 }
2377         }
2378         else
2379         {
2380                 /* ~user/ */
2381                 char* copy = g_new( char, cmp_len + 1 );
2382                 struct passwd *pwd;
2383                 strncpy( copy, text_to_complete + 1, cmp_len );
2384                 copy[cmp_len] = 0;
2385                 pwd = getpwnam( copy );
2386                 g_free( copy );
2387                 if ( !pwd ) {
2388                         cmpl_errno = errno;
2389                         return NULL;
2390                 }
2391
2392                 return open_dir( pwd->pw_dir, cmpl_state );
2393         }
2394 }
2395
2396 /* open a directory relative the the current relative directory */
2397 static CompletionDir*
2398 open_relative_dir( gchar* dir_name,
2399                                    CompletionDir* dir,
2400                                    CompletionState *cmpl_state ){
2401         gchar path_buf[2 * MAXPATHLEN];
2402
2403         if ( dir->fullname_len + strlen( dir_name ) + 2 >= MAXPATHLEN ) {
2404                 cmpl_errno = CMPL_ERRNO_TOO_LONG;
2405                 return NULL;
2406         }
2407
2408         strcpy( path_buf, dir->fullname );
2409
2410         if ( dir->fullname_len > 1 ) {
2411                 path_buf[dir->fullname_len] = '/';
2412                 strcpy( path_buf + dir->fullname_len + 1, dir_name );
2413         }
2414         else
2415         {
2416                 strcpy( path_buf + dir->fullname_len, dir_name );
2417         }
2418
2419         return open_dir( path_buf, cmpl_state );
2420 }
2421
2422 /* after the cache lookup fails, really open a new directory */
2423 static CompletionDirSent*
2424 open_new_dir( gchar* dir_name, struct stat* sbuf, gboolean stat_subdirs ){
2425         CompletionDirSent* sent;
2426         DIR* directory;
2427         gchar *buffer_ptr;
2428         struct dirent *dirent_ptr;
2429         gint buffer_size = 0;
2430         gint entry_count = 0;
2431         gint i;
2432         struct stat ent_sbuf;
2433         char path_buf[MAXPATHLEN * 2];
2434         gint path_buf_len;
2435
2436         sent = g_new( CompletionDirSent, 1 );
2437         sent->mtime = sbuf->st_mtime;
2438         sent->inode = sbuf->st_ino;
2439         sent->device = sbuf->st_dev;
2440
2441         path_buf_len = strlen( dir_name );
2442
2443         if ( path_buf_len > MAXPATHLEN ) {
2444                 cmpl_errno = CMPL_ERRNO_TOO_LONG;
2445                 return NULL;
2446         }
2447
2448         strcpy( path_buf, dir_name );
2449
2450         directory = opendir( dir_name );
2451
2452         if ( !directory ) {
2453                 cmpl_errno = errno;
2454                 return NULL;
2455         }
2456
2457         while ( ( dirent_ptr = readdir( directory ) ) != NULL )
2458         {
2459                 int entry_len = strlen( dirent_ptr->d_name );
2460                 buffer_size += entry_len + 1;
2461                 entry_count += 1;
2462
2463                 if ( path_buf_len + entry_len + 2 >= MAXPATHLEN ) {
2464                         cmpl_errno = CMPL_ERRNO_TOO_LONG;
2465                         closedir( directory );
2466                         return NULL;
2467                 }
2468         }
2469
2470         sent->name_buffer = g_new( gchar, buffer_size );
2471         sent->entries = g_new( CompletionDirEntry, entry_count );
2472         sent->entry_count = entry_count;
2473
2474         buffer_ptr = sent->name_buffer;
2475
2476         rewinddir( directory );
2477
2478         for ( i = 0; i < entry_count; i += 1 )
2479         {
2480                 dirent_ptr = readdir( directory );
2481
2482                 if ( !dirent_ptr ) {
2483                         cmpl_errno = errno;
2484                         closedir( directory );
2485                         return NULL;
2486                 }
2487
2488                 strcpy( buffer_ptr, dirent_ptr->d_name );
2489                 sent->entries[i].entry_name = buffer_ptr;
2490                 buffer_ptr += strlen( dirent_ptr->d_name );
2491                 *buffer_ptr = 0;
2492                 buffer_ptr += 1;
2493
2494                 path_buf[path_buf_len] = '/';
2495                 strcpy( path_buf + path_buf_len + 1, dirent_ptr->d_name );
2496
2497                 if ( stat_subdirs ) {
2498                         if ( stat( path_buf, &ent_sbuf ) >= 0 && S_ISDIR( ent_sbuf.st_mode ) ) {
2499                                 sent->entries[i].is_dir = 1;
2500                         }
2501                         else{
2502                                 /* stat may fail, and we don't mind, since it could be a
2503                                  * dangling symlink. */
2504                                 sent->entries[i].is_dir = 0;
2505                         }
2506                 }
2507                 else{
2508                         sent->entries[i].is_dir = 1;
2509                 }
2510         }
2511
2512         qsort( sent->entries, sent->entry_count, sizeof( CompletionDirEntry ), compare_cmpl_dir );
2513
2514         closedir( directory );
2515
2516         return sent;
2517 }
2518
2519 static gboolean
2520 check_dir( gchar *dir_name, struct stat *result, gboolean *stat_subdirs ){
2521         /* A list of directories that we know only contain other directories.
2522          * Trying to stat every file in these directories would be very
2523          * expensive.
2524          */
2525
2526         static struct {
2527                 gchar *name;
2528                 gboolean present;
2529                 struct stat statbuf;
2530         } no_stat_dirs[] = {
2531                 { "/afs", FALSE, { 0 } },
2532                 { "/net", FALSE, { 0 } }
2533         };
2534
2535         static const gint n_no_stat_dirs = sizeof( no_stat_dirs ) / sizeof( no_stat_dirs[0] );
2536         static gboolean initialized = FALSE;
2537
2538         gint i;
2539
2540         if ( !initialized ) {
2541                 initialized = TRUE;
2542                 for ( i = 0; i < n_no_stat_dirs; i++ )
2543                 {
2544                         if ( stat( no_stat_dirs[i].name, &no_stat_dirs[i].statbuf ) == 0 ) {
2545                                 no_stat_dirs[i].present = TRUE;
2546                         }
2547                 }
2548         }
2549
2550         if ( stat( dir_name, result ) < 0 ) {
2551                 cmpl_errno = errno;
2552                 return FALSE;
2553         }
2554
2555         *stat_subdirs = TRUE;
2556         for ( i = 0; i < n_no_stat_dirs; i++ )
2557         {
2558                 if ( no_stat_dirs[i].present &&
2559                          ( no_stat_dirs[i].statbuf.st_dev == result->st_dev ) &&
2560                          ( no_stat_dirs[i].statbuf.st_ino == result->st_ino ) ) {
2561                         *stat_subdirs = FALSE;
2562                         break;
2563                 }
2564         }
2565
2566         return TRUE;
2567 }
2568
2569 /* open a directory by absolute pathname */
2570 static CompletionDir*
2571 open_dir( gchar* dir_name, CompletionState* cmpl_state ){
2572         struct stat sbuf;
2573         gboolean stat_subdirs;
2574         CompletionDirSent *sent;
2575         GList* cdsl;
2576
2577         if ( !check_dir( dir_name, &sbuf, &stat_subdirs ) ) {
2578                 return NULL;
2579         }
2580
2581         cdsl = cmpl_state->directory_sent_storage;
2582
2583         while ( cdsl )
2584         {
2585                 sent = cdsl->data;
2586
2587                 if ( sent->inode == sbuf.st_ino &&
2588                          sent->mtime == sbuf.st_mtime &&
2589                          sent->device == sbuf.st_dev ) {
2590                         return attach_dir( sent, dir_name, cmpl_state );
2591                 }
2592
2593                 cdsl = cdsl->next;
2594         }
2595
2596         sent = open_new_dir( dir_name, &sbuf, stat_subdirs );
2597
2598         if ( sent ) {
2599                 cmpl_state->directory_sent_storage =
2600                         g_list_prepend( cmpl_state->directory_sent_storage, sent );
2601
2602                 return attach_dir( sent, dir_name, cmpl_state );
2603         }
2604
2605         return NULL;
2606 }
2607
2608 static CompletionDir*
2609 attach_dir( CompletionDirSent* sent, gchar* dir_name, CompletionState *cmpl_state ){
2610         CompletionDir* new_dir;
2611
2612         new_dir = g_new( CompletionDir, 1 );
2613
2614         cmpl_state->directory_storage =
2615                 g_list_prepend( cmpl_state->directory_storage, new_dir );
2616
2617         new_dir->sent = sent;
2618         new_dir->fullname = g_strdup( dir_name );
2619         new_dir->fullname_len = strlen( dir_name );
2620
2621         return new_dir;
2622 }
2623
2624 static gint
2625 correct_dir_fullname( CompletionDir* cmpl_dir ){
2626         gint length = strlen( cmpl_dir->fullname );
2627         struct stat sbuf;
2628
2629         if ( strcmp( cmpl_dir->fullname + length - 2, "/." ) == 0 ) {
2630                 if ( length == 2 ) {
2631                         strcpy( cmpl_dir->fullname, "/" );
2632                         cmpl_dir->fullname_len = 1;
2633                         return TRUE;
2634                 }
2635                 else {
2636                         cmpl_dir->fullname[length - 2] = 0;
2637                 }
2638         }
2639         else if ( strcmp( cmpl_dir->fullname + length - 3, "/./" ) == 0 ) {
2640                 cmpl_dir->fullname[length - 2] = 0;
2641         }
2642         else if ( strcmp( cmpl_dir->fullname + length - 3, "/.." ) == 0 ) {
2643                 if ( length == 3 ) {
2644                         strcpy( cmpl_dir->fullname, "/" );
2645                         cmpl_dir->fullname_len = 1;
2646                         return TRUE;
2647                 }
2648
2649                 if ( stat( cmpl_dir->fullname, &sbuf ) < 0 ) {
2650                         cmpl_errno = errno;
2651                         return FALSE;
2652                 }
2653
2654                 cmpl_dir->fullname[length - 2] = 0;
2655
2656                 if ( !correct_parent( cmpl_dir, &sbuf ) ) {
2657                         return FALSE;
2658                 }
2659         }
2660         else if ( strcmp( cmpl_dir->fullname + length - 4, "/../" ) == 0 ) {
2661                 if ( length == 4 ) {
2662                         strcpy( cmpl_dir->fullname, "/" );
2663                         cmpl_dir->fullname_len = 1;
2664                         return TRUE;
2665                 }
2666
2667                 if ( stat( cmpl_dir->fullname, &sbuf ) < 0 ) {
2668                         cmpl_errno = errno;
2669                         return FALSE;
2670                 }
2671
2672                 cmpl_dir->fullname[length - 3] = 0;
2673
2674                 if ( !correct_parent( cmpl_dir, &sbuf ) ) {
2675                         return FALSE;
2676                 }
2677         }
2678
2679         cmpl_dir->fullname_len = strlen( cmpl_dir->fullname );
2680
2681         return TRUE;
2682 }
2683
2684 static gint
2685 correct_parent( CompletionDir* cmpl_dir, struct stat *sbuf ){
2686         struct stat parbuf;
2687         gchar *last_slash;
2688         gchar *new_name;
2689         gchar c = 0;
2690
2691         last_slash = strrchr( cmpl_dir->fullname, '/' );
2692
2693         g_assert( last_slash );
2694
2695         if ( last_slash != cmpl_dir->fullname ) { /* last_slash[0] = 0; */
2696         }
2697         else
2698         {
2699                 c = last_slash[1];
2700                 last_slash[1] = 0;
2701         }
2702
2703         if ( stat( cmpl_dir->fullname, &parbuf ) < 0 ) {
2704                 cmpl_errno = errno;
2705                 return FALSE;
2706         }
2707
2708         if ( parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev ) {
2709                 /* it wasn't a link */
2710                 return TRUE;
2711         }
2712
2713         if ( c ) {
2714                 last_slash[1] = c;
2715         }
2716         /* else
2717            last_slash[0] = '/'; */
2718
2719         /* it was a link, have to figure it out the hard way */
2720
2721         new_name = find_parent_dir_fullname( cmpl_dir->fullname );
2722
2723         if ( !new_name ) {
2724                 return FALSE;
2725         }
2726
2727         g_free( cmpl_dir->fullname );
2728
2729         cmpl_dir->fullname = new_name;
2730
2731         return TRUE;
2732 }
2733
2734 static gchar*
2735 find_parent_dir_fullname( gchar* dirname ){
2736         gchar buffer[MAXPATHLEN];
2737         gchar buffer2[MAXPATHLEN];
2738
2739 #if defined( sun ) && !defined( __SVR4 )
2740         if ( !getwd( buffer ) )
2741 #else
2742         if ( !getcwd( buffer, MAXPATHLEN ) )
2743 #endif
2744         {
2745                 cmpl_errno = errno;
2746                 return NULL;
2747         }
2748
2749         if ( chdir( dirname ) != 0 || chdir( ".." ) != 0 ) {
2750                 cmpl_errno = errno;
2751                 return NULL;
2752         }
2753
2754 #if defined( sun ) && !defined( __SVR4 )
2755         if ( !getwd( buffer2 ) )
2756 #else
2757         if ( !getcwd( buffer2, MAXPATHLEN ) )
2758 #endif
2759         {
2760                 chdir( buffer );
2761                 cmpl_errno = errno;
2762
2763                 return NULL;
2764         }
2765
2766         if ( chdir( buffer ) != 0 ) {
2767                 cmpl_errno = errno;
2768                 return NULL;
2769         }
2770
2771         return g_strdup( buffer2 );
2772 }
2773
2774 /**********************************************************************/
2775 /*                        Completion Operations                       */
2776 /**********************************************************************/
2777
2778 static PossibleCompletion*
2779 attempt_homedir_completion( gchar* text_to_complete,
2780                                                         CompletionState *cmpl_state ){
2781         gint index, length;
2782
2783         if ( !cmpl_state->user_dir_name_buffer &&
2784                  !get_pwdb( cmpl_state ) ) {
2785                 return NULL;
2786         }
2787         length = strlen( text_to_complete ) - 1;
2788
2789         cmpl_state->user_completion_index += 1;
2790
2791         while ( cmpl_state->user_completion_index < cmpl_state->user_directories_len )
2792         {
2793                 index = first_diff_index( text_to_complete + 1,
2794                                                                   cmpl_state->user_directories
2795                                                                   [cmpl_state->user_completion_index].login );
2796
2797                 switch ( index )
2798                 {
2799                 case PATTERN_MATCH:
2800                         break;
2801                 default:
2802                         if ( cmpl_state->last_valid_char < ( index + 1 ) ) {
2803                                 cmpl_state->last_valid_char = index + 1;
2804                         }
2805                         cmpl_state->user_completion_index += 1;
2806                         continue;
2807                 }
2808
2809                 cmpl_state->the_completion.is_a_completion = 1;
2810                 cmpl_state->the_completion.is_directory = 1;
2811
2812                 append_completion_text( "~", cmpl_state );
2813
2814                 append_completion_text( cmpl_state->
2815                                                                 user_directories[cmpl_state->user_completion_index].login,
2816                                                                 cmpl_state );
2817
2818                 return append_completion_text( "/", cmpl_state );
2819         }
2820
2821         if ( text_to_complete[1] ||
2822                  cmpl_state->user_completion_index > cmpl_state->user_directories_len ) {
2823                 cmpl_state->user_completion_index = -1;
2824                 return NULL;
2825         }
2826         else
2827         {
2828                 cmpl_state->user_completion_index += 1;
2829                 cmpl_state->the_completion.is_a_completion = 1;
2830                 cmpl_state->the_completion.is_directory = 1;
2831
2832                 return append_completion_text( "~/", cmpl_state );
2833         }
2834 }
2835
2836 /* returns the index (>= 0) of the first differing character,
2837  * PATTERN_MATCH if the completion matches */
2838 static gint
2839 first_diff_index( gchar* pat, gchar* text ){
2840         gint diff = 0;
2841
2842         while ( *pat && *text && *text == *pat )
2843         {
2844                 pat += 1;
2845                 text += 1;
2846                 diff += 1;
2847         }
2848
2849         if ( *pat ) {
2850                 return diff;
2851         }
2852
2853         return PATTERN_MATCH;
2854 }
2855
2856 static PossibleCompletion*
2857 append_completion_text( gchar* text, CompletionState* cmpl_state ){
2858         gint len, i = 1;
2859
2860         if ( !cmpl_state->the_completion.text ) {
2861                 return NULL;
2862         }
2863
2864         len = strlen( text ) + strlen( cmpl_state->the_completion.text ) + 1;
2865
2866         if ( cmpl_state->the_completion.text_alloc > len ) {
2867                 strcat( cmpl_state->the_completion.text, text );
2868                 return &cmpl_state->the_completion;
2869         }
2870
2871         while ( i < len ) { i <<= 1; }
2872
2873         cmpl_state->the_completion.text_alloc = i;
2874
2875         cmpl_state->the_completion.text = (gchar*)g_realloc( cmpl_state->the_completion.text, i );
2876
2877         if ( !cmpl_state->the_completion.text ) {
2878                 return NULL;
2879         }
2880         else
2881         {
2882                 strcat( cmpl_state->the_completion.text, text );
2883                 return &cmpl_state->the_completion;
2884         }
2885 }
2886
2887 static CompletionDir*
2888 find_completion_dir( gchar* text_to_complete,
2889                                          gchar** remaining_text,
2890                                          CompletionState* cmpl_state ){
2891         gchar* first_slash = strchr( text_to_complete, '/' );
2892         CompletionDir* dir = cmpl_state->reference_dir;
2893         CompletionDir* next;
2894         *remaining_text = text_to_complete;
2895
2896         while ( first_slash )
2897         {
2898                 gint len = first_slash - *remaining_text;
2899                 gint found = 0;
2900                 gchar *found_name = NULL;       /* Quiet gcc */
2901                 gint i;
2902                 gchar* pat_buf = g_new( gchar, len + 1 );
2903
2904                 strncpy( pat_buf, *remaining_text, len );
2905                 pat_buf[len] = 0;
2906
2907                 for ( i = 0; i < dir->sent->entry_count; i += 1 )
2908                 {
2909                         if ( dir->sent->entries[i].is_dir &&
2910                                  fnmatch( pat_buf, dir->sent->entries[i].entry_name,
2911                                                   FNMATCH_FLAGS ) != FNM_NOMATCH ) {
2912                                 if ( found ) {
2913                                         g_free( pat_buf );
2914                                         return dir;
2915                                 }
2916                                 else
2917                                 {
2918                                         found = 1;
2919                                         found_name = dir->sent->entries[i].entry_name;
2920                                 }
2921                         }
2922                 }
2923
2924                 if ( !found ) {
2925                         /* Perhaps we are trying to open an automount directory */
2926                         found_name = pat_buf;
2927                 }
2928
2929                 next = open_relative_dir( found_name, dir, cmpl_state );
2930
2931                 if ( !next ) {
2932                         g_free( pat_buf );
2933                         return NULL;
2934                 }
2935
2936                 next->cmpl_parent = dir;
2937
2938                 dir = next;
2939
2940                 if ( !correct_dir_fullname( dir ) ) {
2941                         g_free( pat_buf );
2942                         return NULL;
2943                 }
2944
2945                 *remaining_text = first_slash + 1;
2946                 first_slash = strchr( *remaining_text, '/' );
2947
2948                 g_free( pat_buf );
2949         }
2950
2951         return dir;
2952 }
2953
2954 static void
2955 update_cmpl( PossibleCompletion* poss, CompletionState* cmpl_state ){
2956         gint cmpl_len;
2957
2958         if ( !poss || !cmpl_is_a_completion( poss ) ) {
2959                 return;
2960         }
2961
2962         cmpl_len = strlen( cmpl_this_completion( poss ) );
2963
2964         if ( cmpl_state->updated_text_alloc < cmpl_len + 1 ) {
2965                 cmpl_state->updated_text =
2966                         (gchar*)g_realloc( cmpl_state->updated_text,
2967                                                            cmpl_state->updated_text_alloc );
2968                 cmpl_state->updated_text_alloc = 2 * cmpl_len;
2969         }
2970
2971         if ( cmpl_state->updated_text_len < 0 ) {
2972                 strcpy( cmpl_state->updated_text, cmpl_this_completion( poss ) );
2973                 cmpl_state->updated_text_len = cmpl_len;
2974                 cmpl_state->re_complete = cmpl_is_directory( poss );
2975         }
2976         else if ( cmpl_state->updated_text_len == 0 ) {
2977                 cmpl_state->re_complete = FALSE;
2978         }
2979         else
2980         {
2981                 gint first_diff =
2982                         first_diff_index( cmpl_state->updated_text,
2983                                                           cmpl_this_completion( poss ) );
2984
2985                 cmpl_state->re_complete = FALSE;
2986
2987                 if ( first_diff == PATTERN_MATCH ) {
2988                         return;
2989                 }
2990
2991                 if ( first_diff > cmpl_state->updated_text_len ) {
2992                         strcpy( cmpl_state->updated_text, cmpl_this_completion( poss ) );
2993                 }
2994
2995                 cmpl_state->updated_text_len = first_diff;
2996                 cmpl_state->updated_text[first_diff] = 0;
2997         }
2998 }
2999
3000 static PossibleCompletion*
3001 attempt_file_completion( CompletionState *cmpl_state ){
3002         gchar *pat_buf, *first_slash;
3003         CompletionDir *dir = cmpl_state->active_completion_dir;
3004
3005         dir->cmpl_index += 1;
3006
3007         if ( dir->cmpl_index == dir->sent->entry_count ) {
3008                 if ( dir->cmpl_parent == NULL ) {
3009                         cmpl_state->active_completion_dir = NULL;
3010
3011                         return NULL;
3012                 }
3013                 else
3014                 {
3015                         cmpl_state->active_completion_dir = dir->cmpl_parent;
3016
3017                         return attempt_file_completion( cmpl_state );
3018                 }
3019         }
3020
3021         g_assert( dir->cmpl_text );
3022
3023         first_slash = strchr( dir->cmpl_text, '/' );
3024
3025         if ( first_slash ) {
3026                 gint len = first_slash - dir->cmpl_text;
3027
3028                 pat_buf = g_new( gchar, len + 1 );
3029                 strncpy( pat_buf, dir->cmpl_text, len );
3030                 pat_buf[len] = 0;
3031         }
3032         else
3033         {
3034                 gint len = strlen( dir->cmpl_text );
3035
3036                 pat_buf = g_new( gchar, len + 2 );
3037                 strcpy( pat_buf, dir->cmpl_text );
3038                 strcpy( pat_buf + len, "*" );
3039         }
3040
3041         if ( first_slash ) {
3042                 if ( dir->sent->entries[dir->cmpl_index].is_dir ) {
3043                         if ( fnmatch( pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3044                                                   FNMATCH_FLAGS ) != FNM_NOMATCH ) {
3045                                 CompletionDir* new_dir;
3046
3047                                 new_dir = open_relative_dir( dir->sent->entries[dir->cmpl_index].entry_name,
3048                                                                                          dir, cmpl_state );
3049
3050                                 if ( !new_dir ) {
3051                                         g_free( pat_buf );
3052                                         return NULL;
3053                                 }
3054
3055                                 new_dir->cmpl_parent = dir;
3056
3057                                 new_dir->cmpl_index = -1;
3058                                 new_dir->cmpl_text = first_slash + 1;
3059
3060                                 cmpl_state->active_completion_dir = new_dir;
3061
3062                                 g_free( pat_buf );
3063                                 return attempt_file_completion( cmpl_state );
3064                         }
3065                         else
3066                         {
3067                                 g_free( pat_buf );
3068                                 return attempt_file_completion( cmpl_state );
3069                         }
3070                 }
3071                 else
3072                 {
3073                         g_free( pat_buf );
3074                         return attempt_file_completion( cmpl_state );
3075                 }
3076         }
3077         else
3078         {
3079                 if ( dir->cmpl_parent != NULL ) {
3080                         append_completion_text( dir->fullname +
3081                                                                         strlen( cmpl_state->completion_dir->fullname ) + 1,
3082                                                                         cmpl_state );
3083                         append_completion_text( "/", cmpl_state );
3084                 }
3085
3086                 append_completion_text( dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state );
3087
3088                 cmpl_state->the_completion.is_a_completion =
3089                         ( fnmatch( pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3090                                            FNMATCH_FLAGS ) != FNM_NOMATCH );
3091
3092                 cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
3093                 if ( dir->sent->entries[dir->cmpl_index].is_dir ) {
3094                         append_completion_text( "/", cmpl_state );
3095                 }
3096
3097                 g_free( pat_buf );
3098                 return &cmpl_state->the_completion;
3099         }
3100 }
3101
3102
3103 static gint
3104 get_pwdb( CompletionState* cmpl_state ){
3105         struct passwd *pwd_ptr;
3106         gchar* buf_ptr;
3107         gint len = 0, i, count = 0;
3108
3109         if ( cmpl_state->user_dir_name_buffer ) {
3110                 return TRUE;
3111         }
3112         setpwent();
3113
3114         while ( ( pwd_ptr = getpwent() ) != NULL )
3115         {
3116                 len += strlen( pwd_ptr->pw_name );
3117                 len += strlen( pwd_ptr->pw_dir );
3118                 len += 2;
3119                 count += 1;
3120         }
3121
3122         setpwent();
3123
3124         cmpl_state->user_dir_name_buffer = g_new( gchar, len );
3125         cmpl_state->user_directories = g_new( CompletionUserDir, count );
3126         cmpl_state->user_directories_len = count;
3127
3128         buf_ptr = cmpl_state->user_dir_name_buffer;
3129
3130         for ( i = 0; i < count; i += 1 )
3131         {
3132                 pwd_ptr = getpwent();
3133                 if ( !pwd_ptr ) {
3134                         cmpl_errno = errno;
3135                         goto error;
3136                 }
3137
3138                 strcpy( buf_ptr, pwd_ptr->pw_name );
3139                 cmpl_state->user_directories[i].login = buf_ptr;
3140                 buf_ptr += strlen( buf_ptr );
3141                 buf_ptr += 1;
3142                 strcpy( buf_ptr, pwd_ptr->pw_dir );
3143                 cmpl_state->user_directories[i].homedir = buf_ptr;
3144                 buf_ptr += strlen( buf_ptr );
3145                 buf_ptr += 1;
3146         }
3147
3148         qsort( cmpl_state->user_directories,
3149                    cmpl_state->user_directories_len,
3150                    sizeof( CompletionUserDir ),
3151                    compare_user_dir );
3152
3153         endpwent();
3154
3155         return TRUE;
3156
3157 error:
3158
3159         if ( cmpl_state->user_dir_name_buffer ) {
3160                 g_free( cmpl_state->user_dir_name_buffer );
3161         }
3162         if ( cmpl_state->user_directories ) {
3163                 g_free( cmpl_state->user_directories );
3164         }
3165
3166         cmpl_state->user_dir_name_buffer = NULL;
3167         cmpl_state->user_directories = NULL;
3168
3169         return FALSE;
3170 }
3171
3172 static gint
3173 compare_user_dir( const void* a, const void* b ){
3174         return strcmp( ( ( (CompletionUserDir*)a ) )->login,
3175                                    ( ( (CompletionUserDir*)b ) )->login );
3176 }
3177
3178 static gint
3179 compare_cmpl_dir( const void* a, const void* b ){
3180         return strcmp( ( ( (CompletionDirEntry*)a ) )->entry_name,
3181                                    ( ( (CompletionDirEntry*)b ) )->entry_name );
3182 }
3183
3184 static gint
3185 cmpl_state_okay( CompletionState* cmpl_state ){
3186         return cmpl_state && cmpl_state->reference_dir;
3187 }
3188
3189 static gchar*
3190 cmpl_strerror( gint err ){
3191         if ( err == CMPL_ERRNO_TOO_LONG ) {
3192                 return "Name too long";
3193         }
3194         else{
3195                 return g_strerror( err );
3196         }
3197 }
3198
3199
3200 /* Testing area */
3201 #ifdef TORRIE_DEBUG
3202
3203 /* Get the selected filename and print it to the console */
3204 void file_ok_sel( GtkWidget        *w,
3205                                   GtkFileSelection *fs ){
3206         g_print( "%s\n", gtk_file_selection_get_filename( GTK_FILE_SELECTION( fs ) ) );
3207 }
3208
3209 void destroy( GtkWidget *widget,
3210                           gpointer data ){
3211         gtk_main_quit();
3212 }
3213
3214 int main( int argc,
3215                   char *argv[] ){
3216         GtkWidget *filew;
3217
3218         gtk_init( &argc, &argv );
3219
3220         /* Create a new file selection widget */
3221         filew = gtk_file_selection_new( "Michael's Glorious File Selector" );
3222 //    gtk_file_selection_complete(GTK_FILE_SELECTION(filew),"bob");
3223
3224
3225         gtk_signal_connect( GTK_OBJECT( filew ), "destroy",
3226                                                 (GtkSignalFunc) destroy, &filew );
3227         /* Connect the ok_button to file_ok_sel function */
3228         gtk_signal_connect( GTK_OBJECT( GTK_FILE_SELECTION( filew )->ok_button ),
3229                                                 "clicked", (GtkSignalFunc) file_ok_sel, filew );
3230
3231         /* Connect the cancel_button to destroy the widget */
3232         gtk_signal_connect_object( GTK_OBJECT( GTK_FILE_SELECTION
3233                                                                                            ( filew )->cancel_button ),
3234                                                            "clicked", (GtkSignalFunc) gtk_widget_destroy,
3235                                                            GTK_OBJECT( filew ) );
3236
3237
3238         gtk_widget_show( filew );
3239
3240 /*
3241     g_print("%d",gtk_file_selection_match_mask("mask.c","m*.c"));
3242     g_print("%d",gtk_file_selection_match_mask("mask.c","m???.c"));
3243         g_print("%d",gtk_file_selection_match_mask("mask.c","m??*.c"));
3244         g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c"));
3245         g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c???"));
3246         g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c*"));
3247         g_print("%d",gtk_file_selection_match_mask("mask.cout","n*.c???"));
3248         g_print("%d",gtk_file_selection_match_mask("mask.c","[mn]*"));
3249         g_print("%d",gtk_file_selection_match_mask("COPYING","*.xpm"));
3250  */
3251         gtk_main();
3252
3253         return 0;
3254 }
3255 /* example-end */
3256 #endif