]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/gtkfilesel-linux.c
Adding patch_seam q3map2 regression test. Probably not fixable, but good to
[xonotic/netradiant.git] / radiant / gtkfilesel-linux.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
25  */
26 #define LEO
27
28 #ifdef LEO
29 #define _(a) a
30
31 static char * back_xpm[] = {
32 "14 14 33 1",
33 "       c None",
34 ".      c #000000",
35 "+      c #C6D7C6",
36 "@      c #E7EBE7",
37 "#      c #FFFFFF",
38 "$      c #DEEBDE",
39 "%      c #F7F7F7",
40 "&      c #DEE7DE",
41 "*      c #EFF3EF",
42 "=      c #101810",
43 "-      c #B5C7AD",
44 ";      c #EFEFEF",
45 ">      c #D6E3D6",
46 ",      c #213021",
47 "'      c #315931",
48 ")      c #52824A",
49 "!      c #739A6B",
50 "~      c #84A673",
51 "{      c #7BA673",
52 "]      c #84AA73",
53 "^      c #84AA7B",
54 "/      c #84AE7B",
55 "(      c #63925A",
56 "_      c #526D4A",
57 ":      c #4A7D42",
58 "<      c #739E6B",
59 "[      c #739A63",
60 "}      c #4A7539",
61 "|      c #638E52",
62 "1      c #427139",
63 "2      c #6BA663",
64 "3      c #5A8A52",
65 "4      c #315929",
66 "            ..",
67 "          ..+.",
68 "        ..@#+.",
69 "      ..$#%%+.",
70 "    ..&#%*%%+.",
71 "  .=&#******+.",
72 "..-#;>&@****+,",
73 "..')!~{]^/^/(.",
74 "  .._:<^~^/^(.",
75 "    ..':[]~/(.",
76 "      ..}:[~|.",
77 "        ..123.",
78 "          ..4.",
79 "            .."};
80
81 static char * up_xpm[] = {
82 "14 14 36 1",
83 "       c None",
84 ".      c #000000",
85 "+      c #181C18",
86 "@      c #D6DBD6",
87 "#      c #94AA8C",
88 "$      c #000400",
89 "%      c #DEDFDE",
90 "&      c #94AA84",
91 "*      c #E7E3E7",
92 "=      c #94B28C",
93 "-      c #6B865A",
94 ";      c #EFEBEF",
95 ">      c #9CB694",
96 ",      c #8CA684",
97 "'      c #EFEFEF",
98 ")      c #F7EFF7",
99 "!      c #9CB68C",
100 "~      c #63865A",
101 "{      c #94B684",
102 "]      c #94AE84",
103 "^      c #739263",
104 "/      c #F7F3F7",
105 "(      c #94B284",
106 "_      c #849E73",
107 ":      c #8CAE7B",
108 "<      c #8CAA84",
109 "[      c #7B966B",
110 "}      c #8CA67B",
111 "|      c #DEDBD6",
112 "1      c #E7E7E7",
113 "2      c #8CAE84",
114 "3      c #8CAA7B",
115 "4      c #738E63",
116 "5      c #BDBEB5",
117 "6      c #BDC3BD",
118 "7      c #637D52",
119 "      ..      ",
120 "      ..      ",
121 "     +@#$     ",
122 "     .%&.     ",
123 "    .**=-.    ",
124 "    .;;>,.    ",
125 "   .*')!&~.   ",
126 "   .;)){]^.   ",
127 "  .*')/(]_-.  ",
128 "  .;)//::<[.  ",
129 " .*')//:::}-. ",
130 " .|1;;12]3}4. ",
131 ".556666^^^^-7.",
132 ".............."};
133
134 static char * forward_xpm[] = {
135 "14 14 36 1",
136 "       c None",
137 ".      c #000000",
138 "+      c #E7EBDE",
139 "@      c #FFFFFF",
140 "#      c #F7F7EF",
141 "$      c #D6E3D6",
142 "%      c #F7F7F7",
143 "&      c #EFF3EF",
144 "*      c #CEDFCE",
145 "=      c #CEDBC6",
146 "-      c #E7EFE7",
147 ";      c #181818",
148 ">      c #292829",
149 ",      c #E7EBE7",
150 "'      c #DEE7DE",
151 ")      c #B5C7AD",
152 "!      c #9CBA94",
153 "~      c #8CAE84",
154 "{      c #84AA7B",
155 "]      c #7BA673",
156 "^      c #84A67B",
157 "/      c #739A6B",
158 "(      c #5A824A",
159 "_      c #395931",
160 ":      c #9CBA8C",
161 "<      c #84AE7B",
162 "[      c #739E6B",
163 "}      c #527D4A",
164 "|      c #425942",
165 "1      c #84A673",
166 "2      c #4A7142",
167 "3      c #94B284",
168 "4      c #395D31",
169 "5      c #5A8652",
170 "6      c #315929",
171 "7      c #396531",
172 "..            ",
173 ".+..          ",
174 ".@#$..        ",
175 ".@%&#*..      ",
176 ".@%%&&%=..    ",
177 ".@&&&&&-#=;.  ",
178 ">@&&&&,'$'&)..",
179 ".!~{~{{]^/(_..",
180 ".:{<{^{[}|..  ",
181 ".:<1{/}2..    ",
182 ".31/}4..      ",
183 ".{56..        ",
184 ".7..          ",
185 "..            "};
186
187 static char * refresh_xpm[] = {
188 "16 16 11 1",
189 "       c None",
190 ".      c #000000",
191 "+      c #526942",
192 "@      c #4A6139",
193 "#      c #526542",
194 "$      c #5A7142",
195 "%      c #425531",
196 "&      c #314529",
197 "*      c #425131",
198 "=      c #425931",
199 "-      c #5A754A",
200 "       .        ",
201 "      ..        ",
202 "     .+@...     ",
203 "    .#$##@%..   ",
204 "     .+#...%%.  ",
205 "   .  ..   .&.  ",
206 "  .    .    .&. ",
207 " ..          .. ",
208 " ..          .. ",
209 " .*.    .    .  ",
210 "  .*.   ..  .   ",
211 "  .%@...#=.     ",
212 "   ..##-#@#.    ",
213 "     ...@%.     ",
214 "        ..      ",
215 "        .       "};
216
217 #endif
218
219 #ifndef LEO
220 #include "config.h"
221 #endif
222
223 #include <fcntl.h>
224 #include <stdio.h>
225 #include <sys/types.h>
226 #include <sys/stat.h>
227 #include <sys/param.h>
228 #include <dirent.h>
229 #include <stdlib.h>
230 #include <unistd.h>
231 #include <string.h>
232 #include <errno.h>
233 #include <pwd.h>
234 #include <grp.h>
235 #include <time.h>
236
237 #include "fnmatch.h"
238
239 #if (defined TORRIE_DEBUG || defined LEO)
240 #include <gdk/gdkkeysyms.h>
241 #include <gtk/gtkbutton.h>
242 #include <gtk/gtkentry.h>
243 #include "gtkfilesel-linux.h"
244 #include <gtk/gtkhbox.h>
245 #include <gtk/gtkhbbox.h>
246 #include <gtk/gtklabel.h>
247 #include <gtk/gtklist.h>
248 #include <gtk/gtklistitem.h>
249 #include <gtk/gtkmain.h>
250 #include <gtk/gtkscrolledwindow.h>
251 #include <gtk/gtksignal.h>
252 #include <gtk/gtkvbox.h>
253 #include <gtk/gtkmenu.h>
254 #include <gtk/gtkmenuitem.h>
255 #include <gtk/gtkoptionmenu.h>
256 #include <gtk/gtkclist.h>
257 #include <gtk/gtkdialog.h>
258 #include <gtk/gtkcombo.h>
259 #include <gtk/gtkframe.h>
260 #include <gtk/gtkhpaned.h>
261 #include <gtk/gtktable.h>
262 #include <gtk/gtkpixmap.h>
263 #include <gtk/gtknotebook.h>
264 #include <gtk/gtkhseparator.h>
265 #include <gtk/gtktogglebutton.h>
266 #else
267 #include "gdk/gdkkeysyms.h"
268 #include "gtkbutton.h"
269 #include "gtkentry.h"
270 #include "gtkfilesel.h"
271 #include "gtkhbox.h"
272 #include "gtkhbbox.h"
273 #include "gtklabel.h"
274 #include "gtklist.h"
275 #include "gtklistitem.h"
276 #include "gtkmain.h"
277 #include "gtkscrolledwindow.h"
278 #include "gtksignal.h"
279 #include "gtkvbox.h"
280 #include "gtkmenu.h"
281 #include "gtkmenuitem.h"
282 #include "gtkoptionmenu.h"
283 #include "gtkclist.h"
284 #include "gtkdialog.h"
285 #include "gtkcombo.h"
286 #include "gtkframe.h"
287 #include "gtkhpaned.h"
288 #include "gtktable.h"
289 #include "gtkpixmap.h"
290 #include "gtknotebook.h"
291 #include "gtkhseparator.h"
292 #include "gtktogglebutton.h"
293 #endif
294
295 #ifndef LEO
296 #include "gtkintl.h"
297
298 #include "back.xpm"
299 #include "up.xpm"
300 #include "forward.xpm"
301 #include "refresh.xpm"
302 #endif
303
304 #define DIR_LIST_WIDTH   180
305 #define DIR_LIST_HEIGHT  180
306 #define FILE_LIST_WIDTH  180
307 #define FILE_LIST_HEIGHT 180
308 #define BOOKMARK_FILE "/.gtkfilesel_bookmarks"
309 #define MASK_FILE "/.gtkfilesel_masks"
310 #define TIME_STRING_BUF 50
311
312 /* I've put this here so it doesn't get confused with the
313  * file completion interface */
314 typedef struct _HistoryCallbackArg HistoryCallbackArg;
315
316 struct _HistoryCallbackArg
317 {
318   gchar *directory;
319   GtkWidget *menu_item;
320 };
321
322
323 typedef struct _BookmarkMenuStruct BookmarkMenuStruct;
324 struct _BookmarkMenuStruct {
325   GtkWidget *menu_item;
326   gchar     *desc;
327   gchar     *path;
328 };
329
330 typedef struct _CompletionState    CompletionState;
331 typedef struct _CompletionDir      CompletionDir;
332 typedef struct _CompletionDirSent  CompletionDirSent;
333 typedef struct _CompletionDirEntry CompletionDirEntry;
334 typedef struct _CompletionUserDir  CompletionUserDir;
335 typedef struct _PossibleCompletion PossibleCompletion;
336
337 /* Non-external file completion decls and structures */
338
339 /* A contant telling PRCS how many directories to cache.  Its actually
340  * kept in a list, so the geometry isn't important. */
341 #define CMPL_DIRECTORY_CACHE_SIZE 10
342
343 /* A constant used to determine whether a substring was an exact
344  * match by first_diff_index()
345  */
346 #define PATTERN_MATCH -1
347 /* The arguments used by all fnmatch() calls below
348  */
349 #define FNMATCH_FLAGS (FNM_PATHNAME | FNM_PERIOD)
350
351 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
352
353 /* This structure contains all the useful information about a directory
354  * for the purposes of filename completion.  These structures are cached
355  * in the CompletionState struct.  CompletionDir's are reference counted.
356  */
357 struct _CompletionDirSent
358 {
359   ino_t inode;
360   time_t mtime;
361   dev_t device;
362
363   gint entry_count;
364   gchar *name_buffer; /* memory segment containing names of all entries */
365
366   struct _CompletionDirEntry *entries;
367 };
368
369 struct _CompletionDir
370 {
371   CompletionDirSent *sent;
372
373   gchar *fullname;
374   gint fullname_len;
375
376   struct _CompletionDir *cmpl_parent;
377   gint cmpl_index;
378   gchar *cmpl_text;
379 };
380
381 /* This structure contains pairs of directory entry names with a flag saying
382  * whether or not they are a valid directory.  NOTE: This information is used
383  * to provide the caller with information about whether to update its completions
384  * or try to open a file.  Since directories are cached by the directory mtime,
385  * a symlink which points to an invalid file (which will not be a directory),
386  * will not be reevaluated if that file is created, unless the containing
387  * directory is touched.  I consider this case to be worth ignoring (josh).
388  */
389 struct _CompletionDirEntry
390 {
391   gint is_dir;
392   gchar *entry_name;
393 };
394
395 struct _CompletionUserDir
396 {
397   gchar *login;
398   gchar *homedir;
399 };
400
401 struct _PossibleCompletion
402 {
403   /* accessible fields, all are accessed externally by functions
404    * declared above
405    */
406   gchar *text;
407   gint is_a_completion;
408   gint is_directory;
409
410   gint file_size;
411   gint file_time;
412   gint uid;
413   gint gid;
414   /* Private fields
415    */
416   gint text_alloc;
417 };
418
419 struct _CompletionState
420 {
421   gint last_valid_char;
422   gchar *updated_text;
423   gint updated_text_len;
424   gint updated_text_alloc;
425   gint re_complete;
426
427   gchar *user_dir_name_buffer;
428   gint user_directories_len;
429
430   gchar *last_completion_text;
431
432   gint user_completion_index; /* if >= 0, currently completing ~user */
433
434   struct _CompletionDir *completion_dir; /* directory completing from */
435   struct _CompletionDir *active_completion_dir;
436
437   struct _PossibleCompletion the_completion;
438
439   struct _CompletionDir *reference_dir; /* initial directory */
440
441   GList* directory_storage;
442   GList* directory_sent_storage;
443
444   struct _CompletionUserDir *user_directories;
445 };
446
447 /* Widgets from the Properties Dialog */
448 typedef struct _PropertiesPrivate PropertiesPrivate;
449
450 struct _PropertiesPrivate
451 {
452   GtkWidget *mode_label;
453   GtkWidget *mode_buttons[12];
454 };
455
456 /* pixmap creation function */
457 GtkWidget*                 create_pixmap          (GtkWidget *widget,
458                                                    const gchar *pixmap_char);
459
460 /* File completion functions which would be external, were they used
461  * outside of this file.
462  */
463
464 static CompletionState*    cmpl_init_state        (void);
465 static void                cmpl_free_state        (CompletionState *cmpl_state);
466 static gint                cmpl_state_okay        (CompletionState* cmpl_state);
467 static gchar*              cmpl_strerror          (gint);
468
469 static PossibleCompletion* cmpl_completion_matches(gchar           *text_to_complete,
470                                                    gchar          **remaining_text,
471                                                    CompletionState *cmpl_state);
472
473 /* Returns a name for consideration, possibly a completion, this name
474  * will be invalid after the next call to cmpl_next_completion.
475  */
476 static char*               cmpl_this_completion   (PossibleCompletion*);
477
478 /* True if this completion matches the given text.  Otherwise, this
479  * output can be used to have a list of non-completions.
480  */
481 static gint                cmpl_is_a_completion   (PossibleCompletion*);
482
483 /* True if the completion is a directory
484  */
485 static gint                cmpl_is_directory      (PossibleCompletion*);
486
487 /* Obtains the next completion, or NULL
488  */
489 static PossibleCompletion* cmpl_next_completion   (CompletionState*);
490
491 /* Updating completions: the return value of cmpl_updated_text() will
492  * be text_to_complete completed as much as possible after the most
493  * recent call to cmpl_completion_matches.  For the present
494  * application, this is the suggested replacement for the user's input
495  * string.  You must CALL THIS AFTER ALL cmpl_text_completions have
496  * been received.
497  */
498 static gchar*              cmpl_updated_text       (CompletionState* cmpl_state);
499
500 /* After updating, to see if the completion was a directory, call
501  * this.  If it was, you should consider re-calling completion_matches.
502  */
503 static gint                cmpl_updated_dir        (CompletionState* cmpl_state);
504
505 /* Current location: if using file completion, return the current
506  * directory, from which file completion begins.  More specifically,
507  * the cwd concatenated with all exact completions up to the last
508  * directory delimiter('/').
509  */
510 static gchar*              cmpl_reference_position (CompletionState* cmpl_state);
511
512 /* backing up: if cmpl_completion_matches returns NULL, you may query
513  * the index of the last completable character into cmpl_updated_text.
514  */
515 static gint                cmpl_last_valid_char    (CompletionState* cmpl_state);
516
517 /* When the user selects a non-directory, call cmpl_completion_fullname
518  * to get the full name of the selected file.
519  */
520 static gchar*              cmpl_completion_fullname (gchar*, CompletionState* cmpl_state);
521
522
523 /* Directory operations. */
524 static CompletionDir* open_ref_dir         (gchar* text_to_complete,
525                                             gchar** remaining_text,
526                                             CompletionState* cmpl_state);
527 static gboolean       check_dir            (gchar *dir_name,
528                                             struct stat *result,
529                                             gboolean *stat_subdirs);
530 static CompletionDir* open_dir             (gchar* dir_name,
531                                             CompletionState* cmpl_state);
532 static CompletionDir* open_user_dir        (gchar* text_to_complete,
533                                             CompletionState *cmpl_state);
534 static CompletionDir* open_relative_dir    (gchar* dir_name, CompletionDir* dir,
535                                             CompletionState *cmpl_state);
536 static CompletionDirSent* open_new_dir     (gchar* dir_name,
537                                             struct stat* sbuf,
538                                             gboolean stat_subdirs);
539 static gint           correct_dir_fullname (CompletionDir* cmpl_dir);
540 static gint           correct_parent       (CompletionDir* cmpl_dir,
541                                             struct stat *sbuf);
542 static gchar*         find_parent_dir_fullname    (gchar* dirname);
543 static CompletionDir* attach_dir           (CompletionDirSent* sent,
544                                             gchar* dir_name,
545                                             CompletionState *cmpl_state);
546 static void           free_dir_sent (CompletionDirSent* sent);
547 static void           free_dir      (CompletionDir  *dir);
548 static void           prune_memory_usage(CompletionState *cmpl_state);
549
550 /* Completion operations */
551 static PossibleCompletion* attempt_homedir_completion(gchar* text_to_complete,
552                                                       CompletionState *cmpl_state);
553 static PossibleCompletion* attempt_file_completion(CompletionState *cmpl_state);
554 static CompletionDir* find_completion_dir(gchar* text_to_complete,
555                                           gchar** remaining_text,
556                                           CompletionState* cmpl_state);
557 static PossibleCompletion* append_completion_text(gchar* text,
558                                                   CompletionState* cmpl_state);
559 static gint get_pwdb(CompletionState* cmpl_state);
560 static gint first_diff_index(gchar* pat, gchar* text);
561 static gint compare_user_dir(const void* a, const void* b);
562 static gint compare_cmpl_dir(const void* a, const void* b);
563 static void update_cmpl(PossibleCompletion* poss,
564                         CompletionState* cmpl_state);
565
566 static void gtk_file_selection_class_init    (GtkFileSelectionClass *klass);
567 static void gtk_file_selection_init          (GtkFileSelection      *filesel);
568 static void gtk_file_selection_realize       (GtkWidget             *widget);
569 static void gtk_file_selection_destroy       (GtkObject             *object);
570 static gint gtk_file_selection_key_press     (GtkWidget             *widget,
571                                               GdkEventKey           *event,
572                                               gpointer               user_data);
573
574 static void gtk_file_selection_file_button (GtkWidget *widget,
575                                             gint row,
576                                             gint column,
577                                             GdkEventButton *bevent,
578                                             gpointer user_data);
579
580 static void gtk_file_selection_dir_button (GtkWidget *widget,
581                                            gint row,
582                                            gint column,
583                                            GdkEventButton *bevent,
584                                            gpointer data);
585
586 static void gtk_file_selection_undir_button (GtkWidget *widget,
587                                              gint row,
588                                              gint column,
589                                              GdkEventButton *bevent,
590                                              gpointer data);
591
592 static void gtk_file_selection_populate      (GtkFileSelection      *fs,
593                                               gchar                 *rel_path,
594                                               gint                   try_complete);
595 static void gtk_file_selection_abort         (GtkFileSelection      *fs);
596
597 static void gtk_file_selection_update_history_menu (GtkFileSelection       *fs,
598                                                     gchar                  *current_dir);
599
600 static void gtk_file_selection_create_dir (gpointer data);
601 static void gtk_file_selection_delete_file (gpointer data);
602 static void gtk_file_selection_rename_file (gpointer data);
603 static void gtk_file_selection_properties (gpointer data);
604 static void gtk_file_selection_properties_update_mode (GtkWidget *widget, gpointer data);
605 static mode_t gtk_file_selection_properties_get_mode (PropertiesPrivate* private);
606
607 static gboolean gtk_file_selection_history_combo_callback (GtkWidget *widget, GdkEventKey *event, gpointer data);
608 static gboolean gtk_file_selection_history_combo_list_key_handler(GtkWidget *widget,
609                                                                                                                                                                                         GdkEventKey *event,
610                                                                                                                                                                                         gpointer user_data);
611 static gboolean gtk_file_selection_history_combo_list_callback (GtkWidget *thelist,
612                                                                                                                                                                                         GdkEventButton *event,
613
614                                                                                                                                                                                         gpointer user_data);
615 static void gtk_file_selection_bookmark_callback (GtkWidget *widget, gpointer data);
616 static void gtk_file_selection_mask_entry_callback (GtkWidget *widget, gpointer data);
617 static gint gtk_file_selection_mask_entry_key_callback (GtkWidget *widget, GdkEventKey *event, gpointer data);
618 static gint gtk_file_selection_mask_entry_button_callback (GtkWidget *widget, GdkEventButton *event, gpointer data);
619
620 //static void gtk_file_selection_home_button (GtkWidget *widget, gpointer data);
621 static void gtk_file_selection_bookmark_button (GtkWidget *widget,
622                                    GtkFileSelection *fs);
623
624 static void gtk_file_selection_up_button (GtkWidget *widget, gpointer data);
625 static void gtk_file_selection_prev_button (GtkWidget *widget, gpointer data);
626 static void gtk_file_selection_next_button (GtkWidget *widget, gpointer data);
627 static void gtk_file_selection_refresh_button (GtkWidget *widget, gpointer data);
628
629 static gint gtk_file_selection_files_list_key_callback (GtkWidget *widget, GdkEventKey *event, gpointer data);
630
631
632 static gint gtk_file_selection_match_char (gchar, gchar *mask);
633 static gint gtk_file_selection_match_mask (gchar *,gchar *);
634
635 static void gtk_file_selection_load_bookmarks(GtkFileSelection *fs);
636 static void gtk_file_selection_add_bookmark (GtkFileSelection *fs, gchar *desc, gchar *path);
637 gint gtk_file_selection_save_bookmarks (GtkFileSelection *fs);
638
639 static void gtk_file_selection_load_masks(GtkFileSelection *fs);
640
641 static gint gtk_file_selection_show_fileop_menu (GtkCList *clist,
642                                                  GdkEvent *event,
643                                                  GtkFileSelection *fs);
644
645
646 static GtkWindowClass *parent_class = NULL;
647
648 /* Saves errno when something cmpl does fails. */
649 static gint cmpl_errno;
650
651 #ifdef G_WITH_CYGWIN
652 /*
653  * Take the path currently in the file selection
654  * entry field and translate as necessary from
655  * a WIN32 style to CYGWIN32 style path.  For
656  * instance translate:
657  * x:\somepath\file.jpg
658  * to:
659  * //x/somepath/file.jpg
660  *
661  * Replace the path in the selection text field.
662  * Return a boolean value concerning whether a
663  * translation had to be made.
664  */
665 int
666 translate_win32_path (GtkFileSelection *filesel)
667 {
668   int updated = 0;
669   gchar *path;
670
671   /*
672    * Retrieve the current path
673    */
674   path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
675
676   /*
677    * Translate only if this looks like a DOS-ish
678    * path... First handle any drive letters.
679    */
680   if (isalpha (path[0]) && (path[1] == ':')) {
681     /*
682      * This part kind of stinks... It isn't possible
683      * to know if there is enough space in the current
684      * string for the extra character required in this
685      * conversion.  Assume that there isn't enough space
686      * and use the set function on the text field to
687      * set the newly created string.
688      */
689     gchar *newPath = g_strdup_printf ("//%c/%s", path[0], (path + 3));
690     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);
691
692     path = newPath;
693     updated = 1;
694   }
695
696   /*
697    * Now, replace backslashes with forward slashes
698    * if necessary.
699    */
700   if (strchr (path, '\\'))
701     {
702       int index;
703       for (index = 0; path[index] != '\0'; index++)
704         if (path[index] == '\\')
705           path[index] = '/';
706
707       updated = 1;
708     }
709
710   return updated;
711 }
712 #endif
713
714 /* General notes:
715  * Make prev and next inactive if their respective *
716  *   histories are empty.
717  * Add facilities for handling hidden files and    *
718  * directories                                     *
719  * Add an api to access the mask, and hidden files *
720  * check box?  (prob not in 1.2.x series)          *
721  */
722
723 /* Routine for applying mask to filenames         *
724  *   Need to be optimized to minimize recursion   *
725  *     help the for loop by looking for the next  *
726  *     instance of the mask character following   *
727  *     the '*'.  ei *.c -- look for '.'           *
728  *     Also, swap all *? pairs (-> ?*), as that   *
729  *     will make it possible to look ahead (?     *
730  *     makes it very nondeterministic as in *?.c  *
731  *     which really is ?*.c                       *
732  *                                                *
733  */
734 static gint gtk_file_selection_match_char (gchar text, gchar *mask)
735 {
736   gchar *maskc;
737   gint x;
738   gint s;
739   gchar lastc;
740   gchar nextc;
741
742   if (mask[0] == '[')
743     {
744       if (!strchr (mask,']')) return 0;
745       lastc = 0;
746
747       maskc = g_strdup(mask + 1); /* get the portion of mask inside []*/
748       (*(strchr (maskc + 1,']'))) = 0;
749       s = strlen ((char *)maskc);
750
751       for (x = 0 ; x < s ; x ++){
752         if (maskc[x] == '-')
753           {
754             if (x == s) return 1;
755             nextc = maskc[x + 1];
756
757             if (nextc > lastc)
758             {
759               if ((lastc <= text) && (nextc >= text))
760               {
761                 g_free (maskc);
762                 return s + 2;
763               }
764             }
765             else if ((lastc >= text) && (nextc <= text))
766               {
767                 g_free (maskc);
768                 return s + 2;
769               }
770           }
771         else if (text == maskc[x])
772           {
773             g_free (maskc);
774             return s + 2;
775           }
776         lastc = maskc[x];
777       }
778       g_free (maskc);
779
780       return 0;
781     }
782
783   if (mask[0] == '?') return 1;
784   if (mask[0] == text) return 1;
785
786   return 0;
787 }
788
789
790 static gint gtk_file_selection_match_mask1 (gchar *text, gchar *mask)
791 {
792
793   int mc;
794   int tc;
795
796   tc = 0; mc = 0;
797
798   if (mask[0] == 0 && text[0] == 0) return 1;
799
800   if (mask[0] == '*')
801     {
802       for (tc = 0; tc <= strlen(text); tc++)
803         {
804           if (gtk_file_selection_match_mask1 (text + tc, mask + 1))
805             return 1;
806         }
807       return 0;
808     }
809   mc = gtk_file_selection_match_char (text[0], mask);
810
811   if(mc)
812     return gtk_file_selection_match_mask1 (text + 1, mask + mc);
813   else
814     return 0;
815 }
816
817 static gint gtk_file_selection_match_mask (gchar *text, gchar *mask)
818 {
819   gchar *masks;
820   gchar *bmask;
821   gchar *emask;
822
823   masks=g_strdup(mask);
824
825   emask=strchr(masks,'<');
826   if(emask){
827     bmask=emask+1;
828     emask=strchr(bmask,'>');
829     if(emask){
830       *emask=0;
831     }
832   }else{
833     bmask=masks;
834   }
835
836   do{
837     if((emask=strchr(bmask,',')) || (emask=strchr(bmask,';'))){
838       *emask=0;
839       if (gtk_file_selection_match_mask1 (text, bmask)){
840         g_free(masks);
841         return 1;
842       }
843
844       bmask=emask+1;
845     }
846   }while(emask);
847
848   if(gtk_file_selection_match_mask1 (text, bmask)){
849     g_free(masks);
850     return 1;
851   }
852   g_free(masks);
853   return 0;
854 }
855
856 static void
857 gtk_file_selection_load_bookmarks(GtkFileSelection *fs)
858 {
859   GList *list;
860   gchar *bookmark_file;
861   gchar *bookmark_data;
862   struct stat file_info;
863   gint   file;
864   gint   lp;
865   gint   cp;
866   BookmarkMenuStruct *item;
867
868
869   if(fs->bookmark_list){  //erase
870     list=fs->bookmark_list;
871     while(list){
872       item=list->data;
873       g_free(item->desc);
874       g_free(item->path);
875       g_free(item);
876       list=list->next;
877     }
878     g_list_free (fs->bookmark_list);
879     fs->bookmark_list = NULL;
880     gtk_widget_destroy (fs->bookmark_menu);
881   }
882
883   fs->bookmark_menu=gtk_menu_new();
884
885   /* spacer */
886   item=g_malloc(sizeof(item));
887   item->menu_item = gtk_menu_item_new();
888   gtk_widget_show(item->menu_item);
889   gtk_menu_append (GTK_MENU(fs->bookmark_menu), item->menu_item);
890
891   item=g_malloc(sizeof(item));
892   item->desc=g_strdup("Add bookmark");
893   item->path=g_strdup(".");
894   item->menu_item=gtk_menu_item_new_with_label (item->desc);
895   gtk_widget_show(item->menu_item);
896   //fs->bookmark_list=g_list_append(fs->bookmark_list,item);
897   //set signal here!!
898   gtk_menu_append (GTK_MENU(fs->bookmark_menu), item->menu_item);
899
900   item=g_malloc(sizeof(item));
901   item->desc=g_strdup("Edit bookmark");
902   item->path=g_strdup(".");
903   item->menu_item=gtk_menu_item_new_with_label (item->desc);
904   gtk_widget_show(item->menu_item);
905   //fs->bookmark_list=g_list_append(fs->bookmark_list,item);
906   //set signal here!!
907   gtk_menu_append (GTK_MENU(fs->bookmark_menu), item->menu_item);
908
909   bookmark_file=g_strconcat(g_get_home_dir(), BOOKMARK_FILE ,NULL);
910   if(!stat(bookmark_file,&file_info) && (file = open(bookmark_file,  O_RDONLY )) > 0)
911   {
912     if(file_info.st_size <65536 )
913     {
914       bookmark_data=g_malloc(file_info.st_size);
915
916       if(file && read(file, bookmark_data, file_info.st_size))
917       {
918         cp=lp=0;
919
920         while (cp < file_info.st_size)
921         {
922           while (cp < file_info.st_size && bookmark_data[cp] != '<' )
923             cp++;
924           bookmark_data[cp]=0;
925           item=g_malloc(sizeof(BookmarkMenuStruct));
926           item->desc=g_strdup(bookmark_data+lp);
927           lp=++cp;
928
929           while (cp < file_info.st_size && bookmark_data[cp] != '>' )
930             cp++;
931
932           bookmark_data[cp]=0;
933           //create menu items
934           item->path=g_strdup(bookmark_data+lp);
935           gtk_file_selection_add_bookmark ((gpointer) fs, (gpointer) item->desc, (gpointer) item->path);
936
937           cp++;
938
939           while(cp < file_info.st_size && bookmark_data[cp] < 33 )
940             cp++;
941           lp=cp;
942         }
943       }
944
945       close(file);
946     }
947   } else {
948
949     /* Add some default items, then save off to bookmarks file */
950
951     gtk_file_selection_add_bookmark ((gpointer) fs, "Home", "~/");
952     gtk_file_selection_add_bookmark ((gpointer) fs, "Root", "/");
953
954     gtk_file_selection_save_bookmarks ((gpointer) fs);
955   }
956 }
957
958 static void
959 gtk_file_selection_add_bookmark (GtkFileSelection *fs, gchar *desc, gchar *path)
960 {
961   /* Add item to menu */
962   BookmarkMenuStruct *item;
963   item=g_malloc(sizeof(item));
964   item->desc = (gpointer) desc;
965   item->path = (gpointer) path;
966   item->menu_item=gtk_menu_item_new_with_label (item->desc);
967   gtk_widget_show(item->menu_item);
968   fs->bookmark_list=g_list_append(fs->bookmark_list,item);
969   gtk_signal_connect (GTK_OBJECT(item->menu_item), "activate",
970                       (GtkSignalFunc) gtk_file_selection_bookmark_callback,
971                       (gpointer) fs);
972   gtk_menu_insert (GTK_MENU(fs->bookmark_menu), item->menu_item, g_list_length(fs->bookmark_list) -1);
973 }
974
975 gint
976 gtk_file_selection_save_bookmarks (GtkFileSelection *fs)
977 {
978   BookmarkMenuStruct *item;
979   gchar *bookmark_file;
980   gchar *item_data;
981   gint   file;
982   GList *list;
983
984   bookmark_file=g_strconcat(g_get_home_dir(), BOOKMARK_FILE ,NULL);
985
986   if ((file = open(bookmark_file, O_CREAT | O_WRONLY | O_TRUNC, 0600)) > 0)
987   {
988     for (list = g_list_first (fs->bookmark_list); list != NULL; list = g_list_next(list)) {
989       item = list->data;
990       item_data = g_strconcat(item->desc, " <", item->path, ">\n", NULL);
991       if (write (file, item_data, strlen(item_data)) != strlen(item_data)) {
992         return TRUE;
993       }
994       g_free(item_data);
995     }
996
997     close(file);
998   } else {
999     return TRUE;
1000   }
1001
1002   return FALSE;
1003 }
1004
1005 static void
1006 gtk_file_selection_load_masks(GtkFileSelection *fs)
1007 {
1008   /*
1009   GList *list;
1010   gchar *masks_file;
1011   gchar *masks_data;
1012   struct stat file_info;
1013   gint   file;
1014   gint   lp;
1015   gint   cp;
1016
1017   if(fs->masks){
1018     list=fs->masks;
1019     while(list){
1020       g_free(list->data);
1021       list=list->next;
1022     }
1023     fs->masks = NULL;
1024   }
1025
1026   masks_file=g_strconcat(g_get_home_dir(), MASK_FILE,NULL); //put in #define
1027   if(!stat(masks_file,&file_info))
1028   {
1029     if(file_info.st_size <65536 )
1030     {
1031       masks_data=g_malloc(file_info.st_size);
1032
1033       file = open(masks_file,  O_RDONLY );
1034
1035       if(file && read(file, masks_data, file_info.st_size))
1036       {
1037         cp=lp=0;
1038
1039         while (cp < file_info.st_size)
1040         {
1041           while (cp < file_info.st_size && masks_data[cp] != '>' )
1042             cp++;
1043
1044           masks_data[++cp]=0;
1045           if (masks_data[lp]=='<') { //if there was no description, strip off brackets
1046             lp++;
1047             masks_data[cp-1]=0;
1048           }
1049 //          g_print("%s\n",masks_data+lp);
1050           fs->masks = g_list_append(fs->masks, g_strdup(masks_data+lp));
1051
1052           while(cp < file_info.st_size && masks_data[cp] < 33 )
1053             cp++;
1054           lp=cp;
1055         }
1056       }
1057
1058       close(file);
1059     }
1060   }
1061   */
1062   if (!fs->masks) {
1063     /* masks is still null, fill it with default data... */
1064     /*
1065     fs->masks = g_list_append(fs->masks, "all files <*>");
1066     fs->masks = g_list_append(fs->masks, "mp3s/playlists <*.mp3,*.m3u>");
1067     fs->masks = g_list_append(fs->masks, "src/hdr <*.[CcHh],*.[Cc][Cc],*.[Hh][Hh],*.cpp>");
1068     fs->masks = g_list_append(fs->masks, "html docs <*.html,*.htm,*.HTM,*.php*,*.inc>");
1069     fs->masks = g_list_append(fs->masks, "images <*.png,*.jpg,*.jpeg,*.gif,*.xpm,*.tiff>");
1070     fs->masks = g_list_append(fs->masks, "package <*.rpm,*.deb>");
1071     fs->masks = g_list_append(fs->masks, "archive <*.tgz,*.tb2,*.tar*,*.zip,*.rar>");
1072     fs->masks = g_list_append(fs->masks, "compressed <*.Z,*.gz,*.bz2>");
1073     */
1074   }
1075 }
1076
1077 void gtk_file_selection_clear_masks (GtkFileSelection *filesel)
1078 {
1079   GList *list;
1080
1081   g_return_if_fail (filesel != NULL);
1082   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1083
1084   list = filesel->masks;
1085   while (list)
1086     {
1087       g_free (list->data);
1088       list = list->next;
1089     }
1090   filesel->masks = NULL;
1091
1092   gtk_list_clear_items (GTK_LIST (GTK_COMBO (filesel->mask_entry)->list), 0, -1);
1093 }
1094
1095 void gtk_file_selection_set_masks (GtkFileSelection *filesel, const gchar **masks)
1096 {
1097   g_return_if_fail (filesel != NULL);
1098   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1099
1100   while (*masks)
1101     {
1102       filesel->masks = g_list_append (filesel->masks, (gpointer)*masks);
1103       masks++;
1104     }
1105
1106   if (filesel->masks)
1107     gtk_combo_set_popdown_strings (GTK_COMBO (filesel->mask_entry), filesel->masks);
1108 }
1109
1110 GtkType
1111 gtk_file_selection_get_type (void)
1112 {
1113   static GtkType file_selection_type = 0;
1114
1115   if (!file_selection_type)
1116     {
1117       static const GtkTypeInfo filesel_info =
1118       {
1119         "GtkFileSelection",
1120         sizeof (GtkFileSelection),
1121         sizeof (GtkFileSelectionClass),
1122         (GtkClassInitFunc) gtk_file_selection_class_init,
1123         (GtkObjectInitFunc) gtk_file_selection_init,
1124         /* reserved_1 */ NULL,
1125         /* reserved_2 */ NULL,
1126         (GtkClassInitFunc) NULL,
1127       };
1128
1129       file_selection_type = gtk_type_unique (GTK_TYPE_WINDOW, &filesel_info);
1130     }
1131
1132   return file_selection_type;
1133 }
1134
1135 static void
1136 gtk_file_selection_class_init (GtkFileSelectionClass *class)
1137 {
1138   GtkObjectClass *object_class;
1139   GtkWidgetClass *widget_class;
1140
1141   object_class = (GtkObjectClass*) class;
1142
1143   parent_class = gtk_type_class (GTK_TYPE_WINDOW);
1144
1145   widget_class = GTK_WIDGET_CLASS (class);
1146
1147   widget_class->realize = gtk_file_selection_realize;
1148   object_class->destroy = gtk_file_selection_destroy;
1149 }
1150
1151 static void
1152 gtk_file_selection_init (GtkFileSelection *filesel)
1153 {
1154   GtkWidget *entry_vbox;
1155   GtkWidget *label;
1156   GtkWidget *list_vbox;
1157   GtkWidget *confirm_area;
1158   GtkWidget *vbox;
1159   GtkWidget *hbox;
1160   GtkWidget *hbox2;
1161   GtkWidget *table;
1162   GtkWidget *pulldown_hbox;
1163   GtkWidget *scrolled_win;
1164   GtkWidget *mask_label;
1165   GtkWidget *bigframe;
1166   GtkWidget *button;
1167   GtkWidget *hpaned;
1168   GtkWidget *menu_item;
1169   GtkWidget *pixmap;
1170
1171   char *dir_title [2];
1172   char *file_title [2];
1173
1174   filesel->cmpl_state = cmpl_init_state ();
1175
1176   filesel->mask=NULL;
1177   filesel->prev_history=NULL;
1178   filesel->next_history=NULL;
1179   filesel->saved_entry=NULL;
1180   filesel->bookmark_list=NULL;
1181   filesel->masks=NULL;
1182   filesel->selection_text = NULL;
1183   filesel->fileop_data = NULL;
1184
1185   gtk_file_selection_load_masks(filesel);
1186   gtk_file_selection_load_bookmarks(filesel);
1187
1188   /* The dialog-sized vertical box  */
1189   filesel->main_vbox = gtk_vbox_new (FALSE, 0);
1190   gtk_container_set_border_width (GTK_CONTAINER (filesel), 0);
1191   gtk_container_add (GTK_CONTAINER (filesel), filesel->main_vbox);
1192   gtk_widget_show (filesel->main_vbox);
1193
1194   /* hbox for pulldown menu */
1195   pulldown_hbox = gtk_hbox_new (FALSE, 0);
1196   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
1197   gtk_widget_show (pulldown_hbox);
1198
1199   /* The horizontal box containing create, rename etc. buttons */
1200
1201 /*
1202   filesel->button_area = gtk_hbutton_box_new ();
1203   gtk_button_box_set_layout(GTK_BUTTON_BOX(filesel->button_area), GTK_BUTTONBOX_START);
1204   gtk_button_box_set_spacing(GTK_BUTTON_BOX(filesel->button_area), 0);
1205   gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->button_area,
1206                       FALSE, FALSE, 0);
1207   gtk_button_box_set_child_size(GTK_BUTTON_BOX(filesel->button_area),0,0);
1208   gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX(filesel->button_area),0,0);
1209   */
1210
1211   filesel->button_area = gtk_hbox_new (TRUE,0);
1212   //gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->button_area,
1213   //                  FALSE, FALSE, 0);
1214
1215   //gtk_widget_show (filesel->button_area);
1216
1217   gtk_file_selection_show_fileop_buttons(filesel);
1218   /*  frame to put the following hbox in  */
1219   bigframe = gtk_frame_new (NULL);
1220   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), bigframe, TRUE, TRUE, 0);
1221   gtk_frame_set_shadow_type (GTK_FRAME (bigframe), GTK_SHADOW_OUT);
1222   gtk_widget_show (bigframe);
1223
1224
1225   list_vbox = gtk_vbox_new (FALSE,3);
1226   gtk_widget_show(list_vbox);
1227   gtk_container_add (GTK_CONTAINER(bigframe), list_vbox);
1228   gtk_container_set_border_width (GTK_CONTAINER (list_vbox),2);
1229   gtk_widget_show (list_vbox);
1230
1231   /*  The horizontal box containing the directory and file listboxes  */
1232 //  list_hbox = gtk_hbox_new (FALSE, 3);
1233   //gtk_container_add (GTK_CONTAINER(bigframe), list_hbox);
1234   //gtk_container_set_border_width (GTK_CONTAINER (list_hbox), 3);
1235 //  gtk_box_pack_start(GTK_BOX(list_vbox), list_hbox, FALSE,FALSE,0);
1236 //  gtk_widget_show (list_hbox);
1237
1238   hpaned=gtk_hpaned_new();
1239   gtk_widget_show(hpaned);
1240   gtk_container_set_border_width (GTK_CONTAINER (hpaned), 1);
1241   gtk_paned_set_gutter_size (GTK_PANED (hpaned), 10);
1242   gtk_box_pack_start (GTK_BOX(list_vbox), hpaned,TRUE,TRUE,0);
1243
1244   /* vbox to put the buttons and directory listing in  */
1245   vbox = gtk_vbox_new (FALSE, 3);
1246   gtk_widget_show (vbox);
1247   gtk_container_add(GTK_CONTAINER(hpaned),vbox);
1248   //gtk_box_pack_start (GTK_BOX (hpaned), vbox, FALSE, FALSE, 0);
1249
1250   hbox = gtk_hbox_new (FALSE, 4);
1251   gtk_widget_show (hbox);
1252   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1253
1254 //  home_button = gtk_button_new_with_label (_("Home"));
1255 //  gtk_widget_show (home_button);
1256 //  gtk_signal_connect (GTK_OBJECT (home_button), "clicked",
1257 //                    (GtkSignalFunc) gtk_file_selection_home_button,
1258 //                    (gpointer) filesel);
1259 //  gtk_box_pack_start (GTK_BOX (hbox), home_button, TRUE,TRUE, 0);
1260
1261   /* Here we add the bookmark menu button */
1262   #define If we're going to make bookmark a menu, we don't need
1263   #define   to keep it in the filesel structure
1264   button=gtk_button_new_with_label(_("Bookmarks"));
1265   gtk_widget_show(button);
1266   gtk_box_pack_start (GTK_BOX(hbox), button, FALSE,FALSE,0);
1267   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1268                       (GtkSignalFunc) gtk_file_selection_bookmark_button,
1269                       (gpointer) filesel);
1270
1271   hbox2 = gtk_hbox_new (FALSE, 2);
1272   gtk_box_pack_start (GTK_BOX (hbox), hbox2, FALSE, FALSE, 0);
1273   gtk_widget_show(hbox2);
1274
1275   /* Prev button */
1276   button = gtk_button_new ();
1277   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1278                       (GtkSignalFunc) gtk_file_selection_prev_button,
1279                       (gpointer) filesel);
1280   gtk_widget_show (button);
1281   gtk_box_pack_start (GTK_BOX (hbox2), button, FALSE,FALSE, 0);
1282   pixmap = create_pixmap (filesel->main_vbox, (gpointer) back_xpm);
1283   gtk_widget_show (pixmap);
1284   gtk_container_add (GTK_CONTAINER (button), pixmap);
1285
1286   /* Up button */
1287   button = gtk_button_new ();
1288   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1289                       (GtkSignalFunc) gtk_file_selection_up_button,
1290                       (gpointer) filesel);
1291   gtk_widget_show (button);
1292   gtk_box_pack_start (GTK_BOX (hbox2), button, FALSE,FALSE, 0);
1293   pixmap = create_pixmap (filesel->main_vbox, (gpointer) up_xpm);
1294   gtk_widget_show (pixmap);
1295   gtk_container_add (GTK_CONTAINER (button), pixmap);
1296
1297   /* next button */
1298   button = gtk_button_new ();
1299   gtk_widget_show (button);
1300   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1301                       (GtkSignalFunc) gtk_file_selection_next_button,
1302                       (gpointer) filesel);
1303   gtk_box_pack_start (GTK_BOX (hbox2), button, FALSE,FALSE, 0);
1304   pixmap = create_pixmap (filesel->main_vbox, (gpointer) forward_xpm);
1305   gtk_widget_show (pixmap);
1306   gtk_container_add (GTK_CONTAINER (button), pixmap);
1307
1308   /* refresh button */
1309   button = gtk_button_new ();
1310   gtk_widget_show (button);
1311   gtk_signal_connect (GTK_OBJECT (button), "clicked",
1312                       (GtkSignalFunc) gtk_file_selection_refresh_button,
1313                       (gpointer) filesel);
1314   gtk_box_pack_end (GTK_BOX (hbox), button, FALSE,FALSE, 0);
1315   pixmap = create_pixmap (filesel->main_vbox, (gpointer) refresh_xpm);
1316   gtk_widget_show (pixmap);
1317   gtk_container_add (GTK_CONTAINER (button), pixmap);
1318
1319   /* menu for right click file operations */
1320   filesel->fileop_menu = gtk_menu_new();
1321
1322   menu_item = gtk_menu_item_new_with_label (_("Rename..."));
1323   gtk_widget_show(menu_item);
1324   gtk_signal_connect_object (GTK_OBJECT (menu_item), "activate",
1325                              (GtkSignalFunc) gtk_file_selection_rename_file,
1326                              (gpointer) filesel);
1327   gtk_menu_append (GTK_MENU (filesel->fileop_menu), menu_item);
1328
1329   menu_item = gtk_menu_item_new_with_label (_("Delete"));
1330   gtk_widget_show(menu_item);
1331   gtk_menu_append (GTK_MENU (filesel->fileop_menu), menu_item);
1332   gtk_signal_connect_object (GTK_OBJECT (menu_item), "activate",
1333                              (GtkSignalFunc) gtk_file_selection_delete_file,
1334                              (gpointer) filesel);
1335
1336   menu_item = gtk_menu_item_new ();
1337   gtk_widget_show(menu_item);
1338   gtk_menu_append (GTK_MENU (filesel->fileop_menu), menu_item);
1339
1340   menu_item = gtk_menu_item_new_with_label (_("Create Directory..."));
1341   gtk_signal_connect_object (GTK_OBJECT (menu_item), "activate",
1342                              (GtkSignalFunc) gtk_file_selection_create_dir,
1343                              (gpointer) filesel);
1344   gtk_widget_show(menu_item);
1345   gtk_menu_append (GTK_MENU (filesel->fileop_menu), menu_item);
1346
1347   menu_item = gtk_menu_item_new ();
1348   gtk_menu_append (GTK_MENU (filesel->fileop_menu), menu_item);
1349   gtk_widget_show(menu_item);
1350
1351   menu_item = gtk_menu_item_new_with_label (_("Properties..."));
1352   gtk_signal_connect_object (GTK_OBJECT (menu_item), "activate",
1353                              (GtkSignalFunc) gtk_file_selection_properties,
1354                              (gpointer) filesel);
1355   gtk_menu_append (GTK_MENU (filesel->fileop_menu), menu_item);
1356   gtk_widget_show(menu_item);
1357
1358   /* The directories clist */
1359   dir_title[0] = _("Directories");
1360   dir_title[1] = NULL;
1361   filesel->dir_list = gtk_clist_new_with_titles (1, (gchar**) dir_title);
1362   gtk_widget_set_usize (filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
1363   gtk_signal_connect (GTK_OBJECT (filesel->dir_list), "select_row",
1364                       (GtkSignalFunc) gtk_file_selection_dir_button,
1365                       (gpointer) filesel);
1366   gtk_signal_connect (GTK_OBJECT (filesel->dir_list), "unselect_row",
1367                       (GtkSignalFunc) gtk_file_selection_undir_button,
1368                       (gpointer) filesel);
1369   gtk_signal_connect (GTK_OBJECT (filesel->dir_list), "button_press_event",
1370                       GTK_SIGNAL_FUNC(gtk_file_selection_show_fileop_menu),
1371                       (gpointer) filesel);
1372
1373   gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list));
1374
1375   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
1376   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
1377   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
1378                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1379   gtk_box_pack_start (GTK_BOX (vbox), scrolled_win, TRUE,TRUE, 0);
1380   //gtk_container_add(GTK_CONTAINER(hpaned), scrolled_win);
1381
1382   gtk_widget_show (filesel->dir_list);
1383   gtk_widget_show (scrolled_win);
1384
1385   vbox = gtk_vbox_new (FALSE, 3);
1386   gtk_widget_show (vbox);
1387   gtk_container_add(GTK_CONTAINER(hpaned),vbox);
1388   /* vbox area for mask entry and files clist  */
1389
1390   hbox = gtk_hbox_new (FALSE, 2);
1391   gtk_widget_show (hbox);
1392   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
1393
1394   mask_label = gtk_label_new (_("Mask:"));
1395   gtk_widget_show (mask_label);
1396   gtk_box_pack_start (GTK_BOX (hbox), mask_label, FALSE, FALSE, 2);
1397
1398 /*
1399   filesel->mask_entry = gtk_entry_new ();
1400   gtk_widget_show (filesel->mask_entry);
1401   gtk_signal_connect(GTK_OBJECT(filesel->mask_entry),"activate",
1402                      (GtkSignalFunc) gtk_file_4_mask_entry_callback,
1403                      (gpointer) filesel);
1404   gtk_box_pack_start (GTK_BOX (hbox),filesel->mask_entry, TRUE, TRUE, 0);
1405   */
1406
1407   filesel->mask_entry = gtk_combo_new ();
1408   gtk_widget_show (filesel->mask_entry);
1409   gtk_combo_set_value_in_list(GTK_COMBO(filesel->mask_entry),FALSE,FALSE);
1410   gtk_signal_connect(GTK_OBJECT(GTK_COMBO(filesel->mask_entry)->entry),"activate",
1411                      (GtkSignalFunc) gtk_file_selection_mask_entry_callback,
1412                      (gpointer) filesel);
1413   gtk_signal_connect(GTK_OBJECT(((GtkCombo *)filesel->mask_entry)->entry),"key-press-event",
1414                      (GtkSignalFunc) gtk_file_selection_mask_entry_key_callback,
1415                      (gpointer) filesel);
1416
1417   gtk_signal_connect(GTK_OBJECT(((GtkCombo *)filesel->mask_entry)->list),"button-release-event",
1418                      (GtkSignalFunc) gtk_file_selection_mask_entry_button_callback,
1419                      (gpointer) filesel);
1420   gtk_box_pack_start (GTK_BOX (hbox),filesel->mask_entry, TRUE, TRUE, 0);
1421
1422   if (filesel->masks)
1423     gtk_combo_set_popdown_strings (GTK_COMBO (filesel->mask_entry), filesel->masks);
1424
1425
1426   /* The files clist */
1427   file_title[0] = _("Files");
1428   file_title[1] = NULL;
1429   filesel->file_list = gtk_clist_new_with_titles (1, (gchar**) file_title);
1430   gtk_widget_set_usize (filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
1431   gtk_signal_connect (GTK_OBJECT (filesel->file_list), "select_row",
1432                       (GtkSignalFunc) gtk_file_selection_file_button,
1433                       (gpointer) filesel);
1434   gtk_signal_connect (GTK_OBJECT (filesel->file_list), "key-press-event",
1435                       (GtkSignalFunc) gtk_file_selection_files_list_key_callback,
1436                       (gpointer) filesel);
1437
1438   gtk_signal_connect (GTK_OBJECT (filesel->file_list), "button_press_event",
1439                       GTK_SIGNAL_FUNC(gtk_file_selection_show_fileop_menu),
1440                       (gpointer) filesel);
1441
1442   gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list));
1443
1444   scrolled_win = gtk_scrolled_window_new (NULL, NULL);
1445   gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
1446   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
1447                                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1448   gtk_box_pack_start (GTK_BOX (vbox), scrolled_win, TRUE, TRUE, 0);
1449   gtk_widget_show (filesel->file_list);
1450   gtk_widget_show (scrolled_win);
1451
1452   /* action area for packing buttons into. */
1453   filesel->action_area = gtk_hbox_new (TRUE, 0);
1454   gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area,
1455                       FALSE, FALSE, 2);
1456   gtk_widget_show (filesel->action_area);
1457
1458   /*
1459   hbox=gtk_hbox_new(FALSE,0);
1460   gtk_box_pack_end (GTK_BOX (filesel->main_vbox), hbox, FALSE,FALSE, 0);
1461   gtk_widget_show (hbox);
1462   */
1463
1464   /*  The selection entry widget  */
1465
1466   entry_vbox = gtk_vbox_new (FALSE, 0);
1467   gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 0);
1468   gtk_widget_show (entry_vbox);
1469
1470   table = gtk_table_new ( 2, 2, FALSE );
1471   gtk_box_pack_start (GTK_BOX (entry_vbox), table, TRUE, TRUE, 0);
1472   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
1473   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
1474   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
1475
1476
1477   label = gtk_label_new (_("Selection:"));
1478   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
1479                     (GtkAttachOptions) (0),
1480                     (GtkAttachOptions) (0), 0, 0);
1481   gtk_widget_show (label);
1482
1483
1484   filesel->selection_entry = gtk_entry_new ();
1485   gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
1486                       (GtkSignalFunc) gtk_file_selection_key_press, filesel);
1487   gtk_table_attach (GTK_TABLE (table), filesel->selection_entry, 1, 2, 0, 1,
1488                     (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1489                     (GtkAttachOptions) (0), 0, 0);
1490   gtk_widget_show (filesel->selection_entry);
1491
1492
1493   label = gtk_label_new (_("Directory:"));
1494   gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
1495                     (GtkAttachOptions) (0),
1496                     (GtkAttachOptions) (0), 0, 0);
1497   gtk_widget_show (label);
1498
1499
1500   filesel->history_combo = gtk_combo_new();
1501   gtk_combo_set_value_in_list(GTK_COMBO(filesel->history_combo),FALSE,FALSE);
1502   gtk_table_attach (GTK_TABLE (table), filesel->history_combo, 1, 2, 1, 2,
1503                     (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1504                     (GtkAttachOptions) (0), 0, 0);
1505   gtk_widget_show(filesel->history_combo);
1506
1507   gtk_signal_connect(GTK_OBJECT(((GtkCombo *)filesel->history_combo)->entry),"key-press-event",
1508                      (GtkSignalFunc) gtk_file_selection_history_combo_callback,
1509                      (gpointer) filesel);
1510
1511   gtk_signal_connect(GTK_OBJECT(((GtkCombo *)filesel->history_combo)->list),"button-press-event",
1512                      (GtkSignalFunc) gtk_file_selection_history_combo_list_callback,
1513                      (gpointer) filesel);
1514
1515   gtk_signal_connect(GTK_OBJECT(((GtkCombo *)filesel->history_combo)->list),"key-press-event",
1516                      (GtkSignalFunc) gtk_file_selection_history_combo_list_key_handler,
1517                      (gpointer) filesel);
1518
1519   filesel->selection_text = NULL;
1520
1521
1522   /*  The OK/Cancel button area */
1523   confirm_area = gtk_hbutton_box_new ();
1524   gtk_button_box_set_layout(GTK_BUTTON_BOX(confirm_area), GTK_BUTTONBOX_END);
1525   gtk_button_box_set_spacing(GTK_BUTTON_BOX(confirm_area), 5);
1526   gtk_box_pack_end (GTK_BOX (entry_vbox), confirm_area, FALSE, FALSE, 0);
1527   gtk_widget_show (confirm_area);
1528
1529   /*  The OK button  */
1530   filesel->ok_button = gtk_button_new_with_label (_("OK"));
1531   GTK_WIDGET_SET_FLAGS (filesel->ok_button, GTK_CAN_DEFAULT);
1532   gtk_box_pack_start (GTK_BOX (confirm_area), filesel->ok_button, TRUE, TRUE, 0);
1533   gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
1534                              (GtkSignalFunc) gtk_widget_grab_default,
1535                              GTK_OBJECT (filesel->ok_button));
1536   gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate",
1537                              (GtkSignalFunc) gtk_button_clicked,
1538                              GTK_OBJECT (filesel->ok_button));
1539   gtk_widget_grab_default (filesel->ok_button);
1540   gtk_widget_show (filesel->ok_button);
1541
1542   /*  The Cancel button  */
1543   filesel->cancel_button = gtk_button_new_with_label (_("Cancel"));
1544   GTK_WIDGET_SET_FLAGS (filesel->cancel_button, GTK_CAN_DEFAULT);
1545   gtk_box_pack_start (GTK_BOX (confirm_area), filesel->cancel_button, TRUE, TRUE, 0);
1546   gtk_widget_show (filesel->cancel_button);
1547
1548   gtk_widget_show(table);
1549
1550
1551   /*
1552   filesel->selection_text = label = gtk_label_new ("");
1553   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
1554   gtk_box_pack_start (GTK_BOX (entry_vbox), label, FALSE, FALSE, 0);
1555   gtk_widget_show (label);
1556   */
1557
1558
1559   if (!cmpl_state_okay (filesel->cmpl_state))
1560     {
1561       gchar err_buf[256];
1562
1563       sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
1564
1565       /*
1566       gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
1567       */
1568     }
1569   else
1570     {
1571       gtk_file_selection_populate (filesel, "", FALSE);
1572     }
1573
1574   gtk_widget_grab_focus (filesel->selection_entry);
1575 }
1576
1577 GtkWidget*
1578 gtk_file_selection_new (const gchar *title)
1579 {
1580   GtkFileSelection *filesel;
1581
1582   filesel = gtk_type_new (GTK_TYPE_FILE_SELECTION);
1583   gtk_window_set_title (GTK_WINDOW (filesel), title);
1584   /* !!! put check here to figure out if screen > 640x480, if true
1585      We need to make the file selection dialog bigger. much bigger..
1586      or maybe we should keep it at a certan percentage of the screen
1587      size? */
1588
1589   gtk_window_set_default_size(GTK_WINDOW (filesel), 520, 420);
1590   return GTK_WIDGET (filesel);
1591 }
1592
1593 void
1594 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
1595 {
1596   g_return_if_fail (filesel != NULL);
1597   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1598
1599   return;
1600
1601   /* delete, create directory, and rename */
1602 /*
1603   if (!filesel->fileop_c_dir)
1604     {
1605       filesel->fileop_c_dir = gtk_button_new_with_label (_("MkDir"));
1606       gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked",
1607                           (GtkSignalFunc) gtk_file_selection_create_dir,
1608                           (gpointer) filesel);
1609       gtk_box_pack_start (GTK_BOX (filesel->button_area),
1610                           filesel->fileop_c_dir, TRUE,TRUE, 0);
1611       gtk_widget_show (filesel->fileop_c_dir);
1612     }
1613
1614   if (!filesel->fileop_del_file)
1615     {
1616       filesel->fileop_del_file = gtk_button_new_with_label (_("Delete"));
1617       gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked",
1618                           (GtkSignalFunc) gtk_file_selection_delete_file,
1619                           (gpointer) filesel);
1620       gtk_box_pack_start (GTK_BOX (filesel->button_area),
1621                           filesel->fileop_del_file, TRUE,TRUE, 0);
1622       gtk_widget_show (filesel->fileop_del_file);
1623     }
1624
1625   if (!filesel->fileop_ren_file)
1626     {
1627       filesel->fileop_ren_file = gtk_button_new_with_label (_("Rename"));
1628       gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked",
1629                           (GtkSignalFunc) gtk_file_selection_rename_file,
1630                           (gpointer) filesel);
1631       gtk_box_pack_start (GTK_BOX (filesel->button_area),
1632                           filesel->fileop_ren_file, TRUE,TRUE, 0);
1633       gtk_widget_show (filesel->fileop_ren_file);
1634     }
1635
1636   gtk_widget_queue_resize(GTK_WIDGET(filesel));
1637   */
1638 }
1639
1640 void
1641 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
1642 {
1643   g_return_if_fail (filesel != NULL);
1644   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1645
1646   return;
1647   /*
1648   if (filesel->fileop_ren_file)
1649     {
1650       gtk_widget_destroy (filesel->fileop_ren_file);
1651       filesel->fileop_ren_file = NULL;
1652     }
1653
1654   if (filesel->fileop_del_file)
1655     {
1656       gtk_widget_destroy (filesel->fileop_del_file);
1657       filesel->fileop_del_file = NULL;
1658     }
1659
1660   if (filesel->fileop_c_dir)
1661     {
1662       gtk_widget_destroy (filesel->fileop_c_dir);
1663       filesel->fileop_c_dir = NULL;
1664     }
1665   */
1666 }
1667
1668
1669
1670 void
1671 gtk_file_selection_set_filename (GtkFileSelection *filesel,
1672                                  const gchar      *filename)
1673 {
1674   char  buf[MAXPATHLEN];
1675   const char *name, *last_slash;
1676
1677   g_return_if_fail (filesel != NULL);
1678   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1679   g_return_if_fail (filename != NULL);
1680
1681   last_slash = strrchr (filename, '/');
1682
1683   if (!last_slash)
1684     {
1685       buf[0] = 0;
1686       name = filename;
1687     }
1688   else
1689     {
1690       gint len = MIN (MAXPATHLEN - 1, last_slash - filename + 1);
1691
1692       strncpy (buf, filename, len);
1693       buf[len] = 0;
1694
1695       name = last_slash + 1;
1696     }
1697
1698   gtk_file_selection_populate (filesel, buf, FALSE);
1699
1700   if (filesel->selection_entry)
1701     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
1702 }
1703
1704 gchar*
1705 gtk_file_selection_get_filename (GtkFileSelection *filesel)
1706 {
1707   static char nothing[2] = "";
1708   char *text;
1709   char *filename;
1710
1711   g_return_val_if_fail (filesel != NULL, nothing);
1712   g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
1713
1714   text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
1715   if (text)
1716     {
1717       filename = cmpl_completion_fullname (text, filesel->cmpl_state);
1718       return filename;
1719     }
1720
1721   return nothing;
1722 }
1723
1724 void
1725 gtk_file_selection_complete (GtkFileSelection *filesel,
1726                              const gchar      *pattern)
1727 {
1728   gchar *new_pattern;
1729   gint x;
1730
1731   g_return_if_fail (filesel != NULL);
1732   g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1733   g_return_if_fail (pattern != NULL);
1734
1735   if (filesel->selection_entry)
1736     gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
1737
1738   if(strchr(pattern,'*') || strchr(pattern,'?'))
1739     {
1740       for(x=strlen(pattern);x>=0;x--)
1741         {
1742           if(pattern[x]=='/') break;
1743         }
1744       gtk_entry_set_text(GTK_ENTRY(filesel->mask_entry),g_strdup(pattern+x+1));
1745
1746       if(filesel->mask) g_free(filesel->mask);
1747
1748       filesel->mask=g_strdup(pattern+x+1);
1749       new_pattern=g_strdup(pattern);
1750       new_pattern[x+1]=0;
1751       gtk_file_selection_populate (filesel, (gchar*) new_pattern, TRUE);
1752       g_free(new_pattern);
1753     }
1754   else
1755     {
1756       gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE);
1757     }
1758 }
1759
1760 static void
1761 gtk_file_selection_realize (GtkWidget *widget)
1762 {
1763   GtkFileSelection *filesel;
1764   const gchar *masks[] = { "All Files <*>", NULL };
1765
1766   g_return_if_fail (widget != NULL);
1767   g_return_if_fail (GTK_IS_FILE_SELECTION (widget));
1768
1769   filesel = GTK_FILE_SELECTION (widget);
1770
1771   /* make sure that we have at least one mask */
1772   if (!filesel->masks)
1773     gtk_file_selection_set_masks (filesel, masks);
1774
1775   filesel->mask = g_strdup ((gchar*) filesel->masks->data);
1776   gtk_file_selection_populate (filesel, "", FALSE);
1777
1778
1779   if (GTK_WIDGET_CLASS (parent_class)->realize)
1780     (* GTK_WIDGET_CLASS (parent_class)->realize) (widget);
1781 }
1782
1783 static void
1784 gtk_file_selection_destroy (GtkObject *object)
1785 {
1786   GtkFileSelection *filesel;
1787   GList *list;
1788
1789   g_return_if_fail (object != NULL);
1790   g_return_if_fail (GTK_IS_FILE_SELECTION (object));
1791
1792   filesel = GTK_FILE_SELECTION (object);
1793
1794   if (filesel->fileop_dialog)
1795     gtk_widget_destroy (filesel->fileop_dialog);
1796
1797   if (filesel->next_history)
1798     {
1799       list = filesel->next_history;
1800       while (list)
1801         {
1802           g_free (list->data);
1803           list = list->next;
1804         }
1805     }
1806   g_list_free (filesel->next_history);
1807   filesel->next_history = NULL;
1808
1809   if (filesel->prev_history)
1810     {
1811       list = filesel->prev_history;
1812       while (list)
1813         {
1814           g_free (list->data);
1815           list = list->next;
1816         }
1817     }
1818   g_list_free (filesel->prev_history);
1819   filesel->prev_history = NULL;
1820
1821   if (filesel->mask)
1822     {
1823       g_free (filesel->mask);
1824       filesel->mask = NULL;
1825     }
1826
1827   cmpl_free_state (filesel->cmpl_state);
1828   filesel->cmpl_state = NULL;
1829
1830   if (GTK_OBJECT_CLASS (parent_class)->destroy)
1831     (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
1832 }
1833
1834 /* Begin file operations callbacks */
1835
1836 static gint
1837 gtk_file_selection_show_fileop_menu (GtkCList *clist, GdkEvent *event, GtkFileSelection *fs)
1838 {
1839   GdkEventButton *event_button;
1840
1841   g_return_val_if_fail (clist != NULL, FALSE);
1842   g_return_val_if_fail (GTK_IS_CLIST (clist), FALSE);
1843   g_return_val_if_fail (event != NULL, FALSE);
1844   g_return_val_if_fail (fs != NULL, FALSE);
1845   g_return_val_if_fail (GTK_FILE_SELECTION (fs), FALSE);
1846
1847   if (event->type == GDK_BUTTON_PRESS)
1848     {
1849       event_button = (GdkEventButton *) event;
1850       if (event_button->button == 3)
1851         {
1852
1853           gtk_menu_popup (GTK_MENU (fs->fileop_menu), NULL, NULL, NULL, NULL,
1854                           event_button->button, event_button->time);
1855           return TRUE;
1856         }
1857     }
1858
1859   return FALSE;
1860 }
1861
1862 static void
1863 gtk_file_selection_fileop_error (GtkFileSelection *fs, gchar *error_message)
1864 {
1865   GtkWidget *label;
1866   GtkWidget *vbox;
1867   GtkWidget *button;
1868   GtkWidget *dialog;
1869
1870   g_return_if_fail (error_message != NULL);
1871
1872   /* main dialog */
1873   dialog = gtk_dialog_new ();
1874   /*
1875   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1876                       (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1877                       (gpointer) fs);
1878   */
1879   gtk_window_set_title (GTK_WINDOW (dialog), _("Error"));
1880   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1881
1882   /* If file dialog is grabbed, make this dialog modal too */
1883   /* When error dialog is closed, file dialog will be grabbed again */
1884   if (GTK_WINDOW(fs)->modal)
1885       gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1886
1887   vbox = gtk_vbox_new(FALSE, 0);
1888   gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
1889   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
1890                      FALSE, FALSE, 0);
1891   gtk_widget_show(vbox);
1892
1893   label = gtk_label_new(error_message);
1894   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
1895   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
1896   gtk_widget_show(label);
1897
1898   /* yes, we free it */
1899   g_free (error_message);
1900
1901   /* close button */
1902   button = gtk_button_new_with_label (_("Close"));
1903   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1904                              (GtkSignalFunc) gtk_widget_destroy,
1905                              (gpointer) dialog);
1906   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
1907                      button, TRUE, TRUE, 0);
1908   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1909   gtk_widget_grab_default(button);
1910   gtk_widget_show (button);
1911
1912   gtk_widget_show (dialog);
1913 }
1914
1915 static void
1916 gtk_file_selection_fileop_destroy (GtkWidget *widget, gpointer data)
1917 {
1918   GtkFileSelection *fs = data;
1919
1920   g_return_if_fail (fs != NULL);
1921   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1922
1923   fs->fileop_dialog = NULL;
1924   g_free (fs->fileop_data);
1925   fs->fileop_data = NULL;
1926 }
1927
1928
1929 static void
1930 gtk_file_selection_create_dir_confirmed (GtkWidget *widget, gpointer data)
1931 {
1932   GtkFileSelection *fs = data;
1933   gchar *dirname;
1934   gchar *path;
1935   gchar *full_path;
1936   gchar *buf;
1937   CompletionState *cmpl_state;
1938
1939   g_return_if_fail (fs != NULL);
1940   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1941
1942   dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1943   cmpl_state = (CompletionState*) fs->cmpl_state;
1944   path = cmpl_reference_position (cmpl_state);
1945
1946   full_path = g_strconcat (path, "/", dirname, NULL);
1947   if ( (mkdir (full_path, 0755) < 0) )
1948     {
1949       buf = g_strconcat ("Error creating directory \"", dirname, "\":  ",
1950                          g_strerror(errno), NULL);
1951       gtk_file_selection_fileop_error (fs, buf);
1952     }
1953   g_free (full_path);
1954
1955   gtk_widget_destroy (fs->fileop_dialog);
1956   gtk_file_selection_populate (fs, "", FALSE);
1957 }
1958
1959 static void
1960 gtk_file_selection_create_dir (gpointer data)
1961 {
1962   GtkFileSelection *fs = data;
1963   GtkWidget *label;
1964   GtkWidget *dialog;
1965   GtkWidget *vbox;
1966   GtkWidget *button;
1967
1968   g_return_if_fail (fs != NULL);
1969   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1970
1971   if (fs->fileop_dialog)
1972           return;
1973
1974   /* main dialog */
1975   fs->fileop_dialog = dialog = gtk_dialog_new ();
1976   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1977                       (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1978                       (gpointer) fs);
1979   gtk_window_set_title (GTK_WINDOW (dialog), _("Create Directory"));
1980   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1981
1982   /* If file dialog is grabbed, grab option dialog */
1983   /* When option dialog is closed, file dialog will be grabbed again */
1984   if (GTK_WINDOW(fs)->modal)
1985       gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
1986
1987   vbox = gtk_vbox_new(FALSE, 0);
1988   gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
1989   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
1990                      FALSE, FALSE, 0);
1991   gtk_widget_show(vbox);
1992
1993   label = gtk_label_new(_("Directory name:"));
1994   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
1995   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
1996   gtk_widget_show(label);
1997
1998   /*  The directory entry widget  */
1999   fs->fileop_entry = gtk_entry_new ();
2000   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
2001                       TRUE, TRUE, 5);
2002   GTK_WIDGET_SET_FLAGS(fs->fileop_entry, GTK_CAN_DEFAULT);
2003   gtk_widget_show (fs->fileop_entry);
2004
2005   /* buttons */
2006   button = gtk_button_new_with_label (_("Create"));
2007   gtk_signal_connect (GTK_OBJECT (button), "clicked",
2008                       (GtkSignalFunc) gtk_file_selection_create_dir_confirmed,
2009                       (gpointer) fs);
2010   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
2011                      button, TRUE, TRUE, 0);
2012   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
2013   gtk_widget_show(button);
2014
2015   button = gtk_button_new_with_label (_("Cancel"));
2016   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
2017                              (GtkSignalFunc) gtk_widget_destroy,
2018                              (gpointer) dialog);
2019   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
2020                      button, TRUE, TRUE, 0);
2021   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
2022   gtk_widget_grab_default(button);
2023   gtk_widget_show (button);
2024
2025   gtk_widget_show (dialog);
2026 }
2027
2028 static void
2029 gtk_file_selection_delete_file_confirmed (GtkWidget *widget, gpointer data)
2030 {
2031   GtkFileSelection *fs = data;
2032   CompletionState *cmpl_state;
2033   gchar *path;
2034   gchar *full_path;
2035   gchar *buf;
2036
2037   g_return_if_fail (fs != NULL);
2038   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
2039
2040   cmpl_state = (CompletionState*) fs->cmpl_state;
2041   path = cmpl_reference_position (cmpl_state);
2042
2043   full_path = g_strconcat (path, "/", fs->fileop_file, NULL);
2044   if ( (unlink (full_path) < 0) )
2045     {
2046       buf = g_strconcat ("Error deleting file \"", fs->fileop_file, "\":  ",
2047                          g_strerror(errno), NULL);
2048       gtk_file_selection_fileop_error (fs, buf);
2049     }
2050   g_free (full_path);
2051
2052   gtk_widget_destroy (fs->fileop_dialog);
2053   gtk_file_selection_populate (fs, "", FALSE);
2054 }
2055
2056 static void
2057 gtk_file_selection_delete_file (gpointer data)
2058 {
2059   GtkFileSelection *fs = data;
2060   GtkWidget *label;
2061   GtkWidget *vbox;
2062   GtkWidget *button;
2063   GtkWidget *dialog;
2064   gchar *filename;
2065   gchar *buf;
2066
2067   g_return_if_fail (fs != NULL);
2068   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
2069
2070   if (fs->fileop_dialog)
2071           return;
2072
2073   filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
2074   if (strlen(filename) < 1)
2075           return;
2076
2077   fs->fileop_file = filename;
2078
2079   /* main dialog */
2080   fs->fileop_dialog = dialog = gtk_dialog_new ();
2081   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
2082                       (GtkSignalFunc) gtk_file_selection_fileop_destroy,
2083                       (gpointer) fs);
2084   gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
2085   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
2086
2087   /* If file dialog is grabbed, grab option dialog */
2088   /* When option dialog is closed, file dialog will be grabbed again */
2089   if (GTK_WINDOW(fs)->modal)
2090       gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
2091
2092   vbox = gtk_vbox_new(FALSE, 0);
2093   gtk_container_set_border_width(GTK_CONTAINER(vbox), 8);
2094   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
2095                      FALSE, FALSE, 0);
2096   gtk_widget_show(vbox);
2097
2098   buf = g_strconcat ("Really delete file \"", filename, "\" ?", NULL);
2099   label = gtk_label_new(buf);
2100   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
2101   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
2102   gtk_widget_show(label);
2103   g_free(buf);
2104
2105   /* buttons */
2106   button = gtk_button_new_with_label (_("Delete"));
2107   gtk_signal_connect (GTK_OBJECT (button), "clicked",
2108                       (GtkSignalFunc) gtk_file_selection_delete_file_confirmed,
2109                       (gpointer) fs);
2110   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
2111                      button, TRUE, TRUE, 0);
2112   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
2113   gtk_widget_show(button);
2114
2115   button = gtk_button_new_with_label (_("Cancel"));
2116   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
2117                              (GtkSignalFunc) gtk_widget_destroy,
2118                              (gpointer) dialog);
2119   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
2120                      button, TRUE, TRUE, 0);
2121   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
2122   gtk_widget_grab_default(button);
2123   gtk_widget_show (button);
2124
2125   gtk_widget_show (dialog);
2126
2127 }
2128
2129 static void
2130 gtk_file_selection_rename_file_confirmed (GtkWidget *widget, gpointer data)
2131 {
2132   GtkFileSelection *fs = data;
2133   gchar *buf;
2134   gchar *file;
2135   gchar *path;
2136   gchar *new_filename;
2137   gchar *old_filename;
2138   CompletionState *cmpl_state;
2139
2140   g_return_if_fail (fs != NULL);
2141   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
2142
2143   file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
2144   cmpl_state = (CompletionState*) fs->cmpl_state;
2145   path = cmpl_reference_position (cmpl_state);
2146
2147   new_filename = g_strconcat (path, "/", file, NULL);
2148   old_filename = g_strconcat (path, "/", fs->fileop_file, NULL);
2149
2150   if (strcmp (new_filename, old_filename))
2151     if ((rename (old_filename, new_filename)) < 0)
2152       {
2153         buf = g_strconcat ("Error renaming file \"", file, "\":  ",
2154                            g_strerror(errno), NULL);
2155         gtk_file_selection_fileop_error (fs, buf);
2156       }
2157   g_free (new_filename);
2158   g_free (old_filename);
2159
2160   gtk_widget_destroy (fs->fileop_dialog);
2161   gtk_file_selection_populate (fs, "", FALSE);
2162 }
2163
2164 static void
2165 gtk_file_selection_file_mode_confirmed (GtkWidget *widget, gpointer data)
2166 {
2167   GtkFileSelection *fs = data;
2168   PropertiesPrivate *priv = fs->fileop_data;
2169   CompletionState *cmpl_state;
2170   gchar *filename, *file, *path;
2171   mode_t mode;
2172
2173   mode = gtk_file_selection_properties_get_mode (priv);
2174
2175   file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
2176   cmpl_state = (CompletionState*) fs->cmpl_state;
2177   path = cmpl_reference_position (cmpl_state);
2178
2179   filename = g_strconcat (path, "/", file, NULL);
2180   if (chmod (filename, mode) == -1)
2181     {
2182       gchar *buf = g_strconcat ("Error changing file mode of \"", filename, "\":  ",
2183                                 g_strerror (errno), NULL);
2184       gtk_file_selection_fileop_error (fs, buf);
2185       gtk_widget_destroy (fs->fileop_dialog);
2186       gtk_file_selection_populate (fs, "", FALSE);
2187     }
2188   else
2189     gtk_file_selection_rename_file_confirmed (widget, data);
2190
2191   g_free (filename);
2192 }
2193
2194 static void
2195 gtk_file_selection_rename_file (gpointer data)
2196 {
2197   GtkFileSelection *fs = data;
2198   GtkWidget *label;
2199   GtkWidget *dialog;
2200   GtkWidget *vbox;
2201   GtkWidget *button;
2202   gchar *buf;
2203
2204   g_return_if_fail (fs != NULL);
2205   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
2206
2207   if (fs->fileop_dialog)
2208           return;
2209
2210   fs->fileop_file = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
2211   if (strlen(fs->fileop_file) < 1)
2212           return;
2213
2214   /* main dialog */
2215   fs->fileop_dialog = dialog = gtk_dialog_new ();
2216   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
2217                       (GtkSignalFunc) gtk_file_selection_fileop_destroy,
2218                       (gpointer) fs);
2219   gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
2220   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
2221
2222   /* If file dialog is grabbed, grab option dialog */
2223   /* When option dialog  closed, file dialog will be grabbed again */
2224   if (GTK_WINDOW(fs)->modal)
2225     gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
2226
2227   vbox = gtk_vbox_new(FALSE, 0);
2228   gtk_container_set_border_width (GTK_CONTAINER(vbox), 8);
2229   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox,
2230                      FALSE, FALSE, 0);
2231   gtk_widget_show(vbox);
2232
2233   buf = g_strconcat ("Rename file \"", fs->fileop_file, "\" to:", NULL);
2234   label = gtk_label_new(buf);
2235   gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
2236   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 5);
2237   gtk_widget_show(label);
2238   g_free(buf);
2239
2240   /* New filename entry */
2241   fs->fileop_entry = gtk_entry_new ();
2242   gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
2243                       TRUE, TRUE, 5);
2244   GTK_WIDGET_SET_FLAGS(fs->fileop_entry, GTK_CAN_DEFAULT);
2245   gtk_widget_show (fs->fileop_entry);
2246
2247   gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
2248   gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
2249                               0, strlen (fs->fileop_file));
2250
2251   /* buttons */
2252   button = gtk_button_new_with_label (_("Rename"));
2253   gtk_signal_connect (GTK_OBJECT (button), "clicked",
2254                       (GtkSignalFunc) gtk_file_selection_rename_file_confirmed,
2255                       (gpointer) fs);
2256   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
2257                      button, TRUE, TRUE, 0);
2258   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
2259   gtk_widget_show(button);
2260
2261   button = gtk_button_new_with_label (_("Cancel"));
2262   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
2263                              (GtkSignalFunc) gtk_widget_destroy,
2264                              (gpointer) dialog);
2265   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
2266                      button, TRUE, TRUE, 0);
2267   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
2268   gtk_widget_grab_default(button);
2269   gtk_widget_show (button);
2270
2271   gtk_widget_show (dialog);
2272 }
2273
2274 static mode_t
2275 gtk_file_selection_properties_get_mode (PropertiesPrivate* priv)
2276 {
2277   mode_t mode = 0;
2278
2279   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->mode_buttons[0])))
2280     mode |= S_IRUSR;
2281   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->mode_buttons[1])))
2282     mode |= S_IWUSR;
2283   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->mode_buttons[2])))
2284     mode |= S_IXUSR;
2285   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->mode_buttons[3])))
2286     mode |= S_ISUID;
2287   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->mode_buttons[4])))
2288     mode |= S_IRGRP;
2289   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->mode_buttons[5])))
2290     mode |= S_IWGRP;
2291   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->mode_buttons[6])))
2292     mode |= S_IXGRP;
2293   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->mode_buttons[7])))
2294     mode |= S_ISGID;
2295   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->mode_buttons[8])))
2296     mode |= S_IROTH;
2297   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->mode_buttons[9])))
2298     mode |= S_IWOTH;
2299   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->mode_buttons[10])))
2300     mode |= S_IXOTH;
2301   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->mode_buttons[11])))
2302     mode |= S_ISVTX;
2303
2304   return mode;
2305 }
2306
2307 static void
2308 gtk_file_selection_properties_update_mode (GtkWidget *widget, gpointer data)
2309 {
2310   GtkFileSelection *fs = data;
2311   PropertiesPrivate *priv = fs->fileop_data;
2312   gchar str[8];
2313
2314   sprintf (str, "(%.4o)", gtk_file_selection_properties_get_mode (priv));
2315   gtk_label_set (GTK_LABEL (priv->mode_label), str);
2316 }
2317
2318 static void
2319 gtk_file_selection_properties (gpointer data)
2320 {
2321   GtkFileSelection *fs = data;
2322   GtkWidget *label;
2323   GtkWidget *dialog;
2324   GtkWidget *vbox;
2325   GtkWidget *hbox;
2326   GtkWidget *button;
2327   GtkWidget *notebook;
2328   GtkWidget *table;
2329   GtkWidget *hseparator;
2330   GtkWidget *entry;
2331   GtkWidget *togglebutton;
2332   struct stat statbuf;
2333   struct passwd *pw;
2334   struct group *gp;
2335   gchar *buf;
2336   gchar *path;
2337   gchar *filename;
2338   gchar timeBuf[TIME_STRING_BUF];
2339   gint pagenum = 0;
2340   PropertiesPrivate *priv;
2341   int i;
2342
2343   g_return_if_fail (fs != NULL);
2344   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
2345
2346   if (fs->fileop_dialog)
2347           return;
2348
2349   /* main dialog */
2350   fs->fileop_dialog = dialog = gtk_dialog_new ();
2351   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
2352                       (GtkSignalFunc) gtk_file_selection_fileop_destroy,
2353                       (gpointer) fs);
2354   priv = fs->fileop_data = g_malloc (sizeof (PropertiesPrivate));
2355
2356   gtk_window_set_title (GTK_WINDOW (dialog), ("Properties"));
2357   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
2358
2359   /* If file dialog is grabbed, grab option dialog */
2360   /* When option dialog  closed, file dialog will be grabbed again */
2361   if (GTK_WINDOW(fs)->modal)
2362     gtk_window_set_modal (GTK_WINDOW(dialog), TRUE);
2363
2364   /* Dialog guts go here */
2365   notebook = gtk_notebook_new ();
2366   gtk_widget_show (notebook);
2367   gtk_box_pack_start (GTK_BOX (GTK_DIALOG(dialog)->vbox), notebook, TRUE, TRUE, 0);
2368   gtk_container_set_border_width (GTK_CONTAINER (notebook), 8);
2369
2370   path = cmpl_reference_position(fs->cmpl_state);
2371   fs->fileop_file = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
2372   filename = g_strconcat(path, "/", fs->fileop_file, NULL);
2373   if (strlen(fs->fileop_file) > 0 && !(stat(filename, &statbuf)))
2374   {
2375     /* stats page */
2376     table = gtk_table_new (9, 2, FALSE);
2377     gtk_widget_show (table);
2378     gtk_container_add (GTK_CONTAINER (notebook), table);
2379     gtk_container_set_border_width (GTK_CONTAINER (table), 5);
2380     gtk_table_set_row_spacings (GTK_TABLE (table), 4);
2381     gtk_table_set_col_spacings (GTK_TABLE (table), 6);
2382
2383     label = gtk_label_new (_("Statistics"));
2384     gtk_widget_show (label);
2385     gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), pagenum), label);
2386     pagenum++;
2387     /* path and filename */
2388     label = gtk_label_new (_("Path:"));
2389     gtk_widget_show (label);
2390     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
2391                       (GtkAttachOptions) (GTK_FILL),
2392                       (GtkAttachOptions) (0), 0, 0);
2393     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2394
2395     label = gtk_label_new (_(path));
2396     gtk_widget_show (label);
2397     gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1,
2398                       (GtkAttachOptions) (GTK_FILL),
2399                       (GtkAttachOptions) (0), 0, 0);
2400     gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
2401     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2402
2403     label = gtk_label_new (_("File Name:"));
2404     gtk_widget_show (label);
2405     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
2406                       (GtkAttachOptions) (GTK_FILL),
2407                       (GtkAttachOptions) (0), 0, 0);
2408     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2409
2410     fs->fileop_entry = entry = gtk_entry_new ();
2411     gtk_widget_show (entry);
2412     gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 1, 2,
2413                       (GtkAttachOptions) (GTK_FILL),
2414                       (GtkAttachOptions) (0), 0, 0);
2415     gtk_entry_set_text (GTK_ENTRY (entry), fs->fileop_file);
2416     if (access (filename, W_OK))
2417       gtk_widget_set_sensitive( GTK_WIDGET (entry), FALSE);
2418
2419     hseparator = gtk_hseparator_new ();
2420     gtk_widget_show (hseparator);
2421     gtk_table_attach (GTK_TABLE (table), hseparator, 0, 2, 2, 3,
2422                       (GtkAttachOptions) (GTK_FILL),
2423                       (GtkAttachOptions) (GTK_FILL), 0, 0);
2424
2425     /* file type and size */
2426     label = gtk_label_new (_("Type:"));
2427     gtk_widget_show (label);
2428     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
2429                       (GtkAttachOptions) (GTK_FILL),
2430                       (GtkAttachOptions) (0), 0, 0);
2431     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2432
2433     switch (statbuf.st_mode & S_IFMT)
2434     {
2435     case S_IFSOCK:
2436       buf = g_strdup ("Socket");
2437       break;
2438     case S_IFLNK:
2439       buf = g_strdup ("Symbolic link");
2440       break;
2441     case S_IFREG:
2442       buf = g_strdup ("File");
2443       break;
2444     case S_IFBLK:
2445       buf = g_strdup ("Block device");
2446       break;
2447     case S_IFDIR:
2448       buf = g_strdup ("Directory");
2449       break;
2450     case S_IFCHR:
2451       buf = g_strdup ("Character device");
2452       break;
2453     case S_IFIFO:
2454       buf = g_strdup ("First-in/first-out pipe");
2455       break;
2456     default:
2457       buf = g_strdup ("Unknown");
2458     }
2459
2460
2461     label = gtk_label_new (buf);
2462     gtk_widget_show (label);
2463     gtk_table_attach (GTK_TABLE (table), label, 1, 2, 3, 4,
2464                       (GtkAttachOptions) (GTK_FILL),
2465                       (GtkAttachOptions) (0), 0, 0);
2466     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2467
2468     label = gtk_label_new (_("Size:"));
2469     gtk_widget_show (label);
2470     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 4, 5,
2471                       (GtkAttachOptions) (GTK_FILL),
2472                       (GtkAttachOptions) (0), 0, 0);
2473     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2474
2475     label = gtk_label_new (_(g_strdup_printf ("%ld bytes", statbuf.st_size)));
2476     gtk_widget_show (label);
2477     gtk_table_attach (GTK_TABLE (table), label, 1, 2, 4, 5,
2478                       (GtkAttachOptions) (GTK_FILL),
2479                       (GtkAttachOptions) (0), 0, 0);
2480     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2481
2482     hseparator = gtk_hseparator_new ();
2483     gtk_widget_show (hseparator);
2484     gtk_table_attach (GTK_TABLE (table), hseparator, 0, 2, 5, 6,
2485                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
2486                       (GtkAttachOptions) (GTK_FILL), 0, 0);
2487
2488     /* file dates */
2489     label = gtk_label_new (_("Created:"));
2490     gtk_widget_show (label);
2491     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 6, 7,
2492                       (GtkAttachOptions) (GTK_FILL),
2493                       (GtkAttachOptions) (0), 0, 0);
2494     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2495
2496     strftime (timeBuf, TIME_STRING_BUF, "%a %b %d %X %Y", localtime(&statbuf.st_mtime));
2497     label = gtk_label_new (_(timeBuf));
2498     gtk_widget_show (label);
2499     gtk_table_attach (GTK_TABLE (table), label, 1, 2, 6, 7,
2500                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
2501                       (GtkAttachOptions) (0), 0, 0);
2502     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2503
2504
2505
2506     label = gtk_label_new (_("Modified:"));
2507     gtk_widget_show (label);
2508     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 7, 8,
2509                       (GtkAttachOptions) (GTK_FILL),
2510                       (GtkAttachOptions) (0), 0, 0);
2511     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2512
2513     strftime (timeBuf, TIME_STRING_BUF, "%a %b %d %X %Y", localtime(&statbuf.st_mtime));
2514     label = gtk_label_new (_(timeBuf));
2515     gtk_widget_show (label);
2516     gtk_table_attach (GTK_TABLE (table), label, 1, 2, 7, 8,
2517                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
2518                       (GtkAttachOptions) (0), 0, 0);
2519     gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
2520     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2521
2522
2523     label = gtk_label_new (_("Accessed:"));
2524     gtk_widget_show (label);
2525     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 8, 9,
2526                       (GtkAttachOptions) (GTK_FILL),
2527                       (GtkAttachOptions) (0), 0, 0);
2528     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2529
2530     strftime (timeBuf, TIME_STRING_BUF, "%a %b %d %X %Y", localtime(&statbuf.st_atime));
2531     label = gtk_label_new (_(timeBuf));
2532     gtk_widget_show (label);
2533     gtk_table_attach (GTK_TABLE (table), label, 1, 2, 8, 9,
2534                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
2535                       (GtkAttachOptions) (0), 0, 0);
2536     gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
2537     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2538
2539     /* permissions page */
2540     vbox = gtk_vbox_new (FALSE, 4);
2541     gtk_widget_show (vbox);
2542     gtk_container_add (GTK_CONTAINER (notebook), vbox);
2543     gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
2544
2545     label = gtk_label_new (_("Permissions"));
2546     gtk_widget_show (label);
2547     gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), pagenum), label);
2548     pagenum++;
2549
2550     /* owner / group */
2551     table = gtk_table_new (2, 2, FALSE);
2552     gtk_widget_show (table);
2553     gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, TRUE, 0);
2554     gtk_table_set_row_spacings (GTK_TABLE (table), 2);
2555     gtk_table_set_col_spacings (GTK_TABLE (table), 8);
2556
2557     label = gtk_label_new (_("Owner:"));
2558     gtk_widget_show (label);
2559     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 0, 1,
2560                       (GtkAttachOptions) (GTK_FILL),
2561                       (GtkAttachOptions) (0), 0, 0);
2562     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2563
2564     entry = gtk_entry_new();
2565     gtk_widget_show (entry);
2566     gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 0, 1,
2567                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
2568                       (GtkAttachOptions) (0), 0, 0);
2569     if ((pw = getpwuid(statbuf.st_uid)))
2570       gtk_entry_set_text(GTK_ENTRY (entry), pw->pw_name);
2571     else
2572       gtk_entry_set_text(GTK_ENTRY (entry), (gpointer) statbuf.st_uid);
2573     if (access (filename, W_OK) || (getuid() != 0))
2574       gtk_widget_set_sensitive( GTK_WIDGET (entry), FALSE);
2575
2576
2577     label = gtk_label_new (_("Group:"));
2578     gtk_widget_show (label);
2579     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
2580                       (GtkAttachOptions) (GTK_FILL),
2581                       (GtkAttachOptions) (0), 0, 0);
2582     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2583
2584     entry = gtk_entry_new();
2585     gtk_widget_show (entry);
2586     gtk_table_attach (GTK_TABLE (table), entry, 1, 2, 1, 2,
2587                       (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
2588                       (GtkAttachOptions) (0), 0, 0);
2589     if ((gp = getgrgid(statbuf.st_gid)))
2590       gtk_entry_set_text(GTK_ENTRY (entry), gp->gr_name);
2591     else
2592       gtk_entry_set_text(GTK_ENTRY (entry), (gpointer) statbuf.st_gid);
2593     if (access (filename, W_OK) || (getuid() != 0))
2594       gtk_widget_set_sensitive( GTK_WIDGET (entry), FALSE);
2595
2596
2597     hseparator = gtk_hseparator_new ();
2598     gtk_widget_show (hseparator);
2599     gtk_box_pack_start (GTK_BOX (vbox), hseparator, FALSE, TRUE, 0);
2600
2601     /* permissions */
2602     table = gtk_table_new (4, 5, TRUE);
2603     gtk_widget_show (table);
2604     gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
2605     gtk_table_set_row_spacings (GTK_TABLE (table), 2);
2606     gtk_table_set_col_spacings (GTK_TABLE (table), 4);
2607     if (access (filename, W_OK) || ((getuid() != statbuf.st_uid) && getuid() != 0))
2608       gtk_widget_set_sensitive (GTK_WIDGET (table), FALSE);
2609
2610     hbox = gtk_hbox_new (FALSE, 1);
2611     gtk_widget_show (hbox);
2612     gtk_table_attach (GTK_TABLE (table), hbox, 0, 1, 0, 1,
2613                       (GtkAttachOptions) (GTK_FILL),
2614                       (GtkAttachOptions) (GTK_FILL), 0, 0);
2615
2616     priv->mode_label = label = gtk_label_new ("(0000)");
2617     gtk_widget_show (label);
2618     gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
2619
2620     label = gtk_label_new (_("Read"));
2621     gtk_widget_show (label);
2622     gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1,
2623                       (GtkAttachOptions) (0),
2624                       (GtkAttachOptions) (0), 0, 0);
2625
2626     label = gtk_label_new (_("Write"));
2627     gtk_widget_show (label);
2628     gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
2629                       (GtkAttachOptions) (0),
2630                       (GtkAttachOptions) (0), 0, 0);
2631
2632     label = gtk_label_new (_("Exec"));
2633     gtk_widget_show (label);
2634     gtk_table_attach (GTK_TABLE (table), label, 3, 4, 0, 1,
2635                       (GtkAttachOptions) (0),
2636                       (GtkAttachOptions) (0), 0, 0);
2637
2638     label = gtk_label_new (_("Special"));
2639     gtk_widget_show (label);
2640     gtk_table_attach (GTK_TABLE (table), label, 4, 5, 0, 1,
2641                       (GtkAttachOptions) (0),
2642                       (GtkAttachOptions) (0), 0, 0);
2643
2644
2645     label = gtk_label_new (_("User:"));
2646     gtk_widget_show (label);
2647     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 1, 2,
2648                       (GtkAttachOptions) (GTK_FILL),
2649                       (GtkAttachOptions) (0), 0, 0);
2650     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2651
2652     priv->mode_buttons[0] = togglebutton = gtk_toggle_button_new_with_label ("");
2653     gtk_widget_show (togglebutton);
2654     gtk_table_attach (GTK_TABLE (table), togglebutton, 1, 2, 1, 2,
2655                       (GtkAttachOptions) (GTK_FILL),
2656                       (GtkAttachOptions) (0), 0, 0);
2657     if ((statbuf.st_mode & ~(S_IFMT)) & S_IRUSR)
2658       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (togglebutton), TRUE);
2659
2660     priv->mode_buttons[1] = togglebutton = gtk_toggle_button_new_with_label ("");
2661     gtk_widget_show (togglebutton);
2662     gtk_table_attach (GTK_TABLE (table), togglebutton, 2, 3, 1, 2,
2663                       (GtkAttachOptions) (GTK_FILL),
2664                       (GtkAttachOptions) (0), 0, 0);
2665     if ((statbuf.st_mode & ~(S_IFMT)) & S_IWUSR)
2666       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (togglebutton), TRUE);
2667
2668     priv->mode_buttons[2] = togglebutton = gtk_toggle_button_new_with_label ("");
2669     gtk_widget_show (togglebutton);
2670     gtk_table_attach (GTK_TABLE (table), togglebutton, 3, 4, 1, 2,
2671                       (GtkAttachOptions) (GTK_FILL),
2672                       (GtkAttachOptions) (0), 0, 0);
2673     if ((statbuf.st_mode & ~(S_IFMT)) & S_IXUSR)
2674       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (togglebutton), TRUE);
2675
2676     priv->mode_buttons[3] = togglebutton = gtk_toggle_button_new_with_label ("");
2677     gtk_widget_show (togglebutton);
2678     gtk_table_attach (GTK_TABLE (table), togglebutton, 4, 5, 1, 2,
2679                       (GtkAttachOptions) (GTK_FILL),
2680                       (GtkAttachOptions) (0), 0, 0);
2681     if ((statbuf.st_mode & ~(S_IFMT)) & S_ISUID)
2682       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (togglebutton), TRUE);
2683
2684
2685
2686     label = gtk_label_new (_("Group:"));
2687     gtk_widget_show (label);
2688     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 2, 3,
2689                       (GtkAttachOptions) (GTK_FILL),
2690                       (GtkAttachOptions) (0), 0, 0);
2691     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2692
2693     priv->mode_buttons[4] = togglebutton = gtk_toggle_button_new_with_label ("");
2694     gtk_widget_show (togglebutton);
2695     gtk_table_attach (GTK_TABLE (table), togglebutton, 1, 2, 2, 3,
2696                       (GtkAttachOptions) (GTK_FILL),
2697                       (GtkAttachOptions) (0), 0, 0);
2698     if ((statbuf.st_mode & ~(S_IFMT)) & S_IRGRP)
2699       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (togglebutton), TRUE);
2700
2701     priv->mode_buttons[5] = togglebutton = gtk_toggle_button_new_with_label ("");
2702     gtk_widget_show (togglebutton);
2703     gtk_table_attach (GTK_TABLE (table), togglebutton, 2, 3, 2, 3,
2704                       (GtkAttachOptions) (GTK_FILL),
2705                       (GtkAttachOptions) (0), 0, 0);
2706     if ((statbuf.st_mode & ~(S_IFMT)) & S_IWGRP)
2707       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (togglebutton), TRUE);
2708
2709     priv->mode_buttons[6] = togglebutton = gtk_toggle_button_new_with_label ("");
2710     gtk_widget_show (togglebutton);
2711     gtk_table_attach (GTK_TABLE (table), togglebutton, 3, 4, 2, 3,
2712                       (GtkAttachOptions) (GTK_FILL),
2713                       (GtkAttachOptions) (0), 0, 0);
2714     if ((statbuf.st_mode & ~(S_IFMT)) & S_IXGRP)
2715       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (togglebutton), TRUE);
2716
2717     priv->mode_buttons[7] = togglebutton = gtk_toggle_button_new_with_label ("");
2718     gtk_widget_show (togglebutton);
2719     gtk_table_attach (GTK_TABLE (table), togglebutton, 4, 5, 2, 3,
2720                       (GtkAttachOptions) (GTK_FILL),
2721                       (GtkAttachOptions) (0), 0, 0);
2722     if ((statbuf.st_mode & ~(S_IFMT)) & S_ISGID)
2723       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (togglebutton), TRUE);
2724
2725     label = gtk_label_new (_("Other:"));
2726     gtk_widget_show (label);
2727     gtk_table_attach (GTK_TABLE (table), label, 0, 1, 3, 4,
2728                       (GtkAttachOptions) (GTK_FILL),
2729                       (GtkAttachOptions) (0), 0, 0);
2730     gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
2731
2732     priv->mode_buttons[8] = togglebutton = gtk_toggle_button_new_with_label ("");
2733     gtk_widget_show (togglebutton);
2734     gtk_table_attach (GTK_TABLE (table), togglebutton, 1, 2, 3, 4,
2735                       (GtkAttachOptions) (GTK_FILL),
2736                       (GtkAttachOptions) (0), 0, 0);
2737     if ((statbuf.st_mode & ~(S_IFMT)) & S_IROTH)
2738       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (togglebutton), TRUE);
2739
2740     priv->mode_buttons[9] = togglebutton = gtk_toggle_button_new_with_label ("");
2741     gtk_widget_show (togglebutton);
2742     gtk_table_attach (GTK_TABLE (table), togglebutton, 2, 3, 3, 4,
2743                       (GtkAttachOptions) (GTK_FILL),
2744                       (GtkAttachOptions) (0), 0, 0);
2745     if ((statbuf.st_mode & ~(S_IFMT)) & S_IWOTH)
2746       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (togglebutton), TRUE);
2747
2748     priv->mode_buttons[10] = togglebutton = gtk_toggle_button_new_with_label ("");
2749     gtk_widget_show (togglebutton);
2750     gtk_table_attach (GTK_TABLE (table), togglebutton, 3, 4, 3, 4,
2751                       (GtkAttachOptions) (GTK_FILL),
2752                       (GtkAttachOptions) (0), 0, 0);
2753     if ((statbuf.st_mode & ~(S_IFMT)) & S_IXOTH)
2754       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (togglebutton), TRUE);
2755
2756     priv->mode_buttons[11] = togglebutton = gtk_toggle_button_new_with_label ("");
2757     gtk_widget_show (togglebutton);
2758     gtk_table_attach (GTK_TABLE (table), togglebutton, 4, 5, 3, 4,
2759                       (GtkAttachOptions) (GTK_FILL),
2760                       (GtkAttachOptions) (0), 0, 0);
2761     if ((statbuf.st_mode & ~(S_IFMT)) & S_ISVTX)
2762       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (togglebutton), TRUE);
2763
2764     for (i = 0; i < 12; i++)
2765       gtk_signal_connect (GTK_OBJECT (priv->mode_buttons[i]), "toggled",
2766                           GTK_SIGNAL_FUNC (gtk_file_selection_properties_update_mode), fs);
2767     gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (priv->mode_buttons[0]));
2768   }
2769   /* global page */
2770   vbox = gtk_vbox_new (FALSE, 0);
2771   gtk_widget_show (vbox);
2772   gtk_container_add (GTK_CONTAINER (notebook), vbox);
2773
2774   label = gtk_label_new (_("Global"));
2775   gtk_widget_show (label);
2776   gtk_notebook_set_tab_label (GTK_NOTEBOOK (notebook), gtk_notebook_get_nth_page (GTK_NOTEBOOK (notebook), pagenum), label);
2777   pagenum++;
2778
2779   label = gtk_label_new (_("dialog preferances will go here"));
2780   gtk_widget_show (label);
2781   gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 0);
2782
2783   /* end of dialog guts */
2784
2785   /* buttons */
2786   button = gtk_button_new_with_label (_("OK"));
2787   //  gtk_signal_connect (GTK_OBJECT (button), "clicked",
2788   //                  (GtkSignalFunc) gtk_file_selection_rename_file_confirmed,
2789   //                  (gpointer) fs);
2790   gtk_signal_connect (GTK_OBJECT (button), "clicked",
2791                       (GtkSignalFunc) gtk_file_selection_file_mode_confirmed,
2792                       (gpointer) fs);
2793   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
2794                      button, TRUE, TRUE, 0);
2795   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
2796   gtk_widget_show(button);
2797
2798   button = gtk_button_new_with_label (_("Cancel"));
2799   gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
2800                              (GtkSignalFunc) gtk_widget_destroy,
2801                              (gpointer) dialog);
2802   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area),
2803                      button, TRUE, TRUE, 0);
2804   GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
2805   gtk_widget_grab_default(button);
2806   gtk_widget_show (button);
2807
2808   g_free (filename);
2809   gtk_widget_show (dialog);
2810 }
2811
2812 static gint
2813 gtk_file_selection_key_press (GtkWidget   *widget,
2814                               GdkEventKey *event,
2815                               gpointer     user_data)
2816 {
2817
2818   GtkFileSelection *fs;
2819   char *text;
2820
2821   g_return_val_if_fail (widget != NULL, FALSE);
2822   g_return_val_if_fail (event != NULL, FALSE);
2823
2824   fs = GTK_FILE_SELECTION (user_data);
2825
2826   if (fs->saved_entry)
2827     {
2828       gtk_clist_unselect_all ((GtkCList *) (fs->dir_list));
2829       gtk_entry_set_text(GTK_ENTRY(fs->selection_entry),fs->saved_entry);
2830       g_free (fs->saved_entry);
2831       fs->saved_entry = NULL;
2832     }
2833   if (event->keyval == GDK_Tab)
2834     {
2835       text = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
2836
2837       text = g_strdup (text);
2838
2839       gtk_file_selection_populate (fs, text, TRUE);
2840
2841       g_free (text);
2842
2843       gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
2844
2845       return TRUE;
2846     }
2847
2848
2849   return FALSE;
2850 }
2851
2852 /*
2853 static void
2854 gtk_file_selection_home_button (GtkWidget *widget, gpointer data){
2855   GList *list;
2856
2857   GtkFileSelection *fs=data;
2858
2859   list = fs->next_history;
2860   if (list)
2861     {
2862       g_free (list->data);
2863       list = list->next;
2864     }
2865   g_list_free (fs->next_history);
2866   fs->next_history = NULL;
2867
2868   gtk_file_selection_populate (fs,"~/",FALSE);
2869 }
2870 */
2871 static void
2872 gtk_file_selection_bookmark_button (GtkWidget *widget,
2873                                     GtkFileSelection *fs)
2874 {
2875
2876   g_return_if_fail (fs != NULL);
2877   g_return_if_fail (GTK_FILE_SELECTION (fs));
2878
2879   gtk_menu_popup (GTK_MENU (fs->bookmark_menu), NULL, NULL, NULL, NULL,
2880                   0, 0);
2881
2882 }
2883
2884 static void
2885 gtk_file_selection_up_button (GtkWidget *widget, gpointer data){
2886   GtkFileSelection *fs = data;
2887   GList *list;
2888
2889   g_return_if_fail (fs != NULL);
2890   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
2891
2892   list = fs->next_history;
2893   if (list)
2894     {
2895       g_free (list->data);
2896       list = list->next;
2897     }
2898   g_list_free (fs->next_history);
2899   fs->next_history = NULL;
2900
2901   gtk_file_selection_populate (fs, "../", FALSE); /*change directories. */
2902
2903 }
2904
2905 static void
2906 gtk_file_selection_prev_button (GtkWidget *widget, gpointer data){
2907   GtkFileSelection *fs = data;
2908   GList *list;
2909   GList *first;
2910   gchar *path;
2911
2912   g_return_if_fail (fs != NULL);
2913   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
2914
2915   list = fs->prev_history;
2916
2917   if (list && g_list_length(list) > 1)
2918     {
2919       first = list;            /* get first element */
2920       list = list->next;       /* pop off current directory */
2921
2922       list->prev = NULL;       /* make this the new head. */
2923
2924       fs->prev_history = list; /* update prev_history list */
2925       fs->next_history = g_list_prepend(fs->next_history,first->data); /* put it on next_history */
2926
2927       first->next = NULL;      /* orphan the old first node */
2928       g_list_free (first);     /* free the node (data is now in use by next_history) */
2929
2930
2931
2932       path = g_malloc(strlen(list->data)+4); /* plenty of space */
2933       strcpy(path,list->data);               /* get the 2nd path in the history */
2934       strcat(path,"/");                      /* append a '/' */
2935       gtk_file_selection_populate (fs, path, FALSE); /* change directories. */
2936       g_free (path);
2937     }
2938 }
2939
2940 static void
2941 gtk_file_selection_next_button (GtkWidget *widget, gpointer data){
2942   GtkFileSelection *fs = data;
2943   GList *list;
2944   GList *first;
2945   gchar *path;
2946
2947   g_return_if_fail (fs != NULL);
2948   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
2949
2950   list = fs->next_history;
2951
2952   if (list && g_list_length(list) > 0)
2953     {
2954       first = list;            /*get first element*/
2955       list = list->next;       /*pop off current directory*/
2956
2957       if (list)
2958         list->prev = NULL;
2959
2960       fs->next_history = list;                       /*update prev_history list*/
2961
2962       path = g_malloc(strlen(first->data)+4);        /*plenty of space*/
2963       strcpy(path,first->data);
2964       strcat(path,"/");                              /*append a /   */
2965       gtk_file_selection_populate (fs, path, FALSE); /*change directories.*/
2966       g_free(path);
2967
2968       first->next = NULL;     /* orphan the old first node */
2969       g_list_free (first);    /* free the node (data is now in use by next_history) */
2970
2971     }
2972 }
2973
2974 void static
2975 gtk_file_selection_refresh_button (GtkWidget *widget, gpointer data){
2976   GtkFileSelection *fs = data;
2977
2978   g_return_if_fail (fs != NULL);
2979   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
2980
2981   gtk_file_selection_populate (fs,"",FALSE);
2982 }
2983
2984 static void
2985 gtk_file_selection_mask_entry_callback (GtkWidget *widget, gpointer data){
2986   GtkFileSelection *fs = data;
2987
2988   if(fs->mask)
2989     g_free (fs->mask);
2990
2991   fs->mask = g_strdup(gtk_entry_get_text (GTK_ENTRY(GTK_COMBO(fs->mask_entry)->entry)));
2992
2993   if (strlen(fs->mask) == 0)
2994     {
2995       g_free (fs->mask);
2996       fs->mask = NULL;
2997     }
2998
2999   gtk_file_selection_refresh_button (widget,data);
3000 }
3001
3002 static gint gtk_file_selection_files_list_key_callback (GtkWidget *widget, GdkEventKey *event, gpointer data){
3003   GtkFileSelection *fs=data;
3004   gchar *saved;
3005   gchar key[2];
3006
3007 //  g_print("Key event: %d\n",event->keyval);
3008   //we need some sort of timeout.
3009
3010   //if the key is a normal character then
3011   //add to our saved_entry1
3012   //if it's backspace then remove one character
3013   //otherwise let it through (and erase our buffer.
3014
3015   if(event->keyval > GDK_space && event->keyval <=  GDK_Korean_Won) {
3016     key[1]=0;
3017     key[0]=event->keyval;
3018     saved=fs->saved_entry1;
3019     if(fs->saved_entry1){
3020       fs->saved_entry1=g_strconcat(saved,key,NULL);
3021       g_free(saved);
3022     }else{
3023       fs->saved_entry1=g_strdup(key);
3024     }
3025     g_print("complete: %s\n",fs->saved_entry1);
3026     /*gtk_label_set_text(GTK_LABEL(fs->completion_label), fs->saved_entry1); */
3027
3028     saved=g_strdup(gtk_entry_get_text(GTK_ENTRY(fs->selection_entry)));
3029     gtk_file_selection_complete(fs,fs->saved_entry1);
3030     gtk_entry_set_text(GTK_ENTRY(fs->selection_entry),saved);
3031     g_free(saved);
3032   }else if (event->keyval ==  GDK_BackSpace) {
3033     if(strlen(fs->saved_entry1)){
3034       fs->saved_entry1[strlen(fs->saved_entry1)-1]=0;
3035       g_print("complete: %s\n",fs->saved_entry1);
3036       /*gtk_label_set_text(GTK_LABEL(fs->completion_label),fs->saved_entry1); */
3037       saved=g_strdup(gtk_entry_get_text(GTK_ENTRY(fs->selection_entry)));
3038       gtk_file_selection_complete(fs,fs->saved_entry1);
3039       gtk_entry_set_text(GTK_ENTRY(fs->selection_entry),saved);
3040       g_free(saved);
3041     }
3042   }else if (event->keyval == GDK_Tab) {
3043     saved=g_strdup(gtk_entry_get_text(GTK_ENTRY(fs->selection_entry)));
3044     gtk_file_selection_populate(fs,fs->saved_entry1,TRUE);
3045     g_free(fs->saved_entry1);
3046     fs->saved_entry1=gtk_entry_get_text(GTK_ENTRY(fs->selection_entry));
3047     gtk_entry_set_text(GTK_ENTRY(fs->selection_entry),saved);
3048     g_free(saved);
3049
3050     g_print("complete: %s\n",fs->saved_entry1);
3051     /* gtk_label_set_text(GTK_LABEL(fs->completion_label),fs->saved_entry1);*/
3052
3053     gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
3054   }else {
3055     if(fs->saved_entry1){
3056       g_free(fs->saved_entry1);
3057       fs->saved_entry1=NULL;
3058     }
3059     /* gtk_label_set_text(GTK_LABEL(fs->completion_label)," "); */
3060   }
3061
3062   return TRUE;
3063 }
3064
3065
3066 static gint gtk_file_selection_mask_entry_key_callback (GtkWidget *widget, GdkEventKey *event, gpointer data)
3067 {
3068   GtkEntry *entry=(GtkEntry *)widget;
3069   GtkFileSelection *fs=data;
3070
3071   g_return_val_if_fail (fs != NULL,FALSE);
3072   g_return_val_if_fail (GTK_IS_FILE_SELECTION (fs),FALSE);
3073
3074
3075   if (event->keyval == GDK_Return || event->keyval == GDK_Tab)
3076     {
3077       if(fs->mask)
3078         g_free(fs->mask);
3079
3080       fs->mask=g_strdup(gtk_entry_get_text(entry));
3081       gtk_file_selection_refresh_button(widget,fs);
3082
3083       if (event->keyval == GDK_Return)
3084         gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
3085       return TRUE;
3086     }
3087   else
3088     {
3089       return FALSE;
3090     }
3091 }
3092
3093 static gint gtk_file_selection_mask_entry_button_callback (GtkWidget *widget, GdkEventButton *event, gpointer data)
3094 {
3095   GtkFileSelection *fs = data;
3096
3097   if(fs->mask)
3098     g_free(fs->mask);
3099
3100   fs->mask=g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(fs->mask_entry)->entry)));
3101   gtk_file_selection_refresh_button(widget,fs);
3102
3103   return TRUE;
3104
3105 }
3106
3107 static gboolean gtk_file_selection_history_combo_list_key_handler(GtkWidget *widget,
3108                                                                   GdkEventKey *event,
3109                                                                   gpointer user_data)
3110 {
3111   /*
3112   g_print("Key pressed! \n");
3113   */
3114
3115   return TRUE;
3116 }
3117
3118 static gboolean gtk_file_selection_history_combo_list_callback (GtkWidget *thelist,
3119                                                                 GdkEventButton *event,
3120                                                                 gpointer user_data)
3121 {
3122
3123   GtkFileSelection *fs = user_data;
3124   GList *list;
3125   gchar *path;
3126
3127   list = fs->next_history;
3128   if(list)
3129     {
3130       g_free (list->data);
3131       list = list->next;
3132     }
3133   g_list_free (fs->next_history);
3134   fs->next_history = NULL;
3135
3136   path = g_malloc(strlen(gtk_entry_get_text(GTK_ENTRY (((GtkCombo *)fs->history_combo)->entry)))+4);
3137   strcpy (path,gtk_entry_get_text(GTK_ENTRY( ((GtkCombo *)fs->history_combo)->entry)));
3138   strcat (path,"/");
3139
3140   gtk_file_selection_populate (fs,path,TRUE);
3141
3142   g_free (path);
3143
3144   return TRUE;
3145 }
3146
3147 static gboolean
3148 gtk_file_selection_history_combo_callback (GtkWidget *widget, GdkEventKey *event, gpointer data)
3149 {
3150   GtkEntry *entry=(GtkEntry *)widget;
3151   GtkFileSelection *fs=data;
3152   GList *list;
3153   gchar *path;
3154
3155   g_return_val_if_fail (fs != NULL,FALSE);
3156   g_return_val_if_fail (GTK_IS_FILE_SELECTION (fs),FALSE);
3157
3158
3159   if (event->keyval == GDK_Return)
3160     {
3161       list = fs->next_history;
3162       if (list)
3163         {
3164           g_free (list->data);
3165           list = list->next;
3166         }
3167       g_list_free (fs->next_history);
3168       fs->next_history = NULL;
3169
3170       path = g_malloc(strlen(gtk_entry_get_text(entry))+4);
3171       strcpy (path,gtk_entry_get_text(entry));
3172       strcat (path,"/");
3173       gtk_file_selection_populate (fs,path,TRUE);
3174       g_free (path);
3175       gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
3176       return TRUE;
3177     }
3178   else
3179     {
3180       return FALSE;
3181     }
3182
3183 }
3184
3185
3186 static void gtk_file_selection_bookmark_callback (GtkWidget *widget, gpointer data)
3187 {
3188   GtkFileSelection *fs = data;
3189   BookmarkMenuStruct *item;
3190   GList *list;
3191
3192   g_return_if_fail (fs != NULL);
3193   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
3194
3195 //g_print ("Callback\n");
3196   list = fs->bookmark_list;
3197   while(list) {
3198     item = list->data;
3199     if (item->menu_item == widget) {
3200       if(strcmp(item->path,"./")) {
3201         gtk_file_selection_populate (fs, item->path, FALSE);
3202       }
3203       break;
3204     }
3205     list=list->next;
3206   }
3207 }
3208
3209 static void
3210 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
3211                                         gchar *current_directory)
3212 {
3213   gchar *current_dir;
3214
3215   g_return_if_fail (fs != NULL);
3216   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
3217   g_return_if_fail (current_directory != NULL);
3218
3219   current_dir = g_strdup (current_directory);
3220
3221   if(fs->prev_history)
3222     {
3223       if (strcmp((fs->prev_history)->data,current_dir))
3224         { /*if this item isn't on the top of the list */
3225           fs->prev_history = g_list_prepend(fs->prev_history,g_strdup(current_dir));
3226         }
3227     } else {
3228       fs->prev_history = g_list_prepend(fs->prev_history,g_strdup(current_dir));
3229     }
3230
3231   gtk_combo_set_popdown_strings (GTK_COMBO (fs->history_combo),fs->prev_history);
3232
3233   g_free (current_dir);
3234 }
3235
3236 static void
3237 gtk_file_selection_file_button (GtkWidget *widget,
3238                                gint row,
3239                                gint column,
3240                                GdkEventButton *bevent,
3241                                gpointer user_data)
3242 {
3243   GtkFileSelection *fs = NULL;
3244   gchar *filename, *temp = NULL;
3245
3246   g_return_if_fail (GTK_IS_CLIST (widget));
3247
3248   fs = user_data;
3249   g_return_if_fail (fs != NULL);
3250   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
3251
3252   gtk_clist_get_text (GTK_CLIST (fs->file_list), row, 0, &temp);
3253   filename = g_strdup (temp);
3254
3255   if (filename)
3256     {
3257       if (fs->saved_entry)
3258       {
3259         gtk_clist_unselect_all ((GtkCList *) (fs->dir_list));
3260         gtk_entry_set_text(GTK_ENTRY(fs->selection_entry),fs->saved_entry);
3261         g_free (fs->saved_entry);
3262         fs->saved_entry = NULL;
3263       }
3264       if(fs->saved_entry1){
3265         g_free(fs->saved_entry1);
3266         fs->saved_entry1=NULL;
3267       }
3268       /* gtk_label_set_text(GTK_LABEL(fs->completion_label)," "); */
3269
3270
3271       if (bevent)
3272         switch (bevent->type)
3273           {
3274           case GDK_2BUTTON_PRESS:
3275             gtk_button_clicked (GTK_BUTTON (fs->ok_button));
3276             break;
3277
3278           default:
3279 /*
3280             if (bevent->button && GDK_BUTTON2_MASK)
3281               {
3282                 g_print("Right click! -- %d\n",bevent->button);
3283               }
3284               else
3285               {
3286               */
3287
3288             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
3289               /*}*/
3290             break;
3291           }
3292       else
3293         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
3294
3295       g_free (filename);
3296     }
3297 }
3298
3299 static void
3300 gtk_file_selection_dir_button (GtkWidget *widget,
3301                                gint row,
3302                                gint column,
3303                                GdkEventButton *bevent,
3304                                gpointer user_data)
3305 {
3306   GList *list;
3307   GtkFileSelection *fs = NULL;
3308   gchar *filename, *temp = NULL;
3309
3310   g_return_if_fail (GTK_IS_CLIST (widget));
3311
3312   fs = GTK_FILE_SELECTION (user_data);
3313   g_return_if_fail (fs != NULL);
3314   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
3315
3316   gtk_clist_get_text (GTK_CLIST (fs->dir_list), row, 0, &temp);
3317   filename = g_strdup (temp);
3318
3319   if (filename)
3320     {
3321       if (bevent)
3322         switch (bevent->type)
3323           {
3324           case GDK_2BUTTON_PRESS:
3325             list = fs->next_history;
3326             if (list)
3327               {
3328                 g_free (list->data);
3329                 list = list->next;
3330               }
3331             g_list_free (fs->next_history);
3332             fs->next_history = NULL;
3333
3334             gtk_file_selection_populate (fs, filename, FALSE);
3335             gtk_entry_set_text(GTK_ENTRY(fs->selection_entry),fs->saved_entry);
3336             g_free (fs->saved_entry);
3337             fs->saved_entry = NULL;
3338             break;
3339
3340           default:
3341             /* here we need to add the "filename" to the beginning of what's already
3342                in the entry.  Save what's in the entry, then restore it on the double click
3343             */
3344             if (fs->saved_entry) g_free (fs->saved_entry);
3345             fs->saved_entry=g_strdup(gtk_entry_get_text(GTK_ENTRY (fs->selection_entry)));
3346
3347             temp=g_strconcat(filename,fs->saved_entry,NULL);
3348             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), temp);
3349             g_free (temp);
3350
3351             break;
3352           }
3353       else
3354         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
3355
3356       g_free (filename);
3357     }
3358 }
3359
3360 static void
3361 gtk_file_selection_undir_button (GtkWidget *widget,
3362                                gint row,
3363                                gint column,
3364                                GdkEventButton *bevent,
3365                                gpointer user_data)
3366 {
3367   GtkFileSelection *fs = NULL;
3368   gchar *filename, *temp = NULL;
3369
3370   g_return_if_fail (GTK_IS_CLIST (widget));
3371
3372   fs = GTK_FILE_SELECTION (user_data);
3373   g_return_if_fail (fs != NULL);
3374   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
3375
3376   gtk_clist_get_text (GTK_CLIST (fs->dir_list), row, 0, &temp);
3377   filename = g_strdup (temp);
3378
3379   if (filename)
3380     {
3381       if (bevent)
3382         switch (bevent->type)
3383           {
3384           default:
3385             /* here we need to add the "filename" to the beginning of what's already
3386                in the entry.  Save what's in the entry, then restore it on the double click
3387             */
3388             if (fs->saved_entry)
3389               {
3390                 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),fs->saved_entry);
3391                 g_free (fs->saved_entry);
3392                 fs->saved_entry = NULL;
3393               }
3394             break;
3395           }
3396       else
3397         gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename); //?????
3398
3399       g_free (filename);
3400     }
3401 }
3402
3403 static void
3404 gtk_file_selection_populate (GtkFileSelection *fs,
3405                              gchar            *rel_path,
3406                              gint              try_complete)
3407 {
3408   CompletionState *cmpl_state;
3409   PossibleCompletion* poss;
3410   gchar* filename;
3411   gint row;
3412   gchar* rem_path = rel_path;
3413   gchar* sel_text;
3414   gchar* text[2];
3415   gint did_recurse = FALSE;
3416   gint possible_count = 0;
3417   gint selection_index = -1;
3418   gint file_list_width;
3419   gint dir_list_width;
3420
3421   g_return_if_fail (fs != NULL);
3422   g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
3423
3424   cmpl_state = (CompletionState*) fs->cmpl_state;
3425   poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
3426
3427   if (!cmpl_state_okay (cmpl_state))
3428     {
3429       /* Something went wrong. */
3430       gtk_file_selection_abort (fs);
3431       return;
3432     }
3433
3434   g_assert (cmpl_state->reference_dir);
3435
3436   gtk_clist_freeze (GTK_CLIST (fs->dir_list));
3437   gtk_clist_clear (GTK_CLIST (fs->dir_list));
3438   gtk_clist_freeze (GTK_CLIST (fs->file_list));
3439   gtk_clist_clear (GTK_CLIST (fs->file_list));
3440
3441   /* Set the dir_list to include ./ and ../ */
3442   /* Actually, no let's not.
3443   text[1] = NULL;
3444   text[0] = "./";
3445   row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
3446   */
3447
3448   text[0] = "../";  //Do we need ..?
3449   row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
3450
3451   /*reset the max widths of the lists*/
3452   dir_list_width = gdk_string_width(fs->dir_list->style->font,"../");
3453   gtk_clist_set_column_width(GTK_CLIST(fs->dir_list),0,dir_list_width);
3454   file_list_width = 1;
3455   gtk_clist_set_column_width(GTK_CLIST(fs->file_list),0,file_list_width);
3456
3457   while (poss)
3458     {
3459       if (cmpl_is_a_completion (poss))
3460         {
3461           possible_count += 1;
3462
3463           filename = cmpl_this_completion (poss);
3464
3465           text[0] = filename;
3466
3467           if (cmpl_is_directory (poss))
3468             {
3469               if (strcmp (filename, "./") != 0 &&
3470                   strcmp (filename, "../") != 0)
3471                 {
3472                   int width = gdk_string_width(fs->dir_list->style->font,
3473                                                filename);
3474                   row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
3475                   if(width > dir_list_width)
3476                     {
3477                       dir_list_width = width;
3478                       gtk_clist_set_column_width(GTK_CLIST(fs->dir_list),0,
3479                                                  width);
3480                     }
3481                 }
3482             }
3483           else
3484             {
3485               if(fs->mask)
3486                 {
3487                   if (gtk_file_selection_match_mask(filename,fs->mask))
3488                     {
3489                       int width = gdk_string_width(fs->file_list->style->font,
3490                                                    filename);
3491                       row = gtk_clist_append (GTK_CLIST (fs->file_list), text);
3492                       if(width > file_list_width)
3493                         {
3494                           file_list_width = width;
3495                           gtk_clist_set_column_width(GTK_CLIST(fs->file_list),0,
3496                                                      width);
3497                         }
3498                     }
3499                 }
3500               else
3501                 {
3502                   int width = gdk_string_width(fs->file_list->style->font,
3503                                                filename);
3504                   row = gtk_clist_append (GTK_CLIST (fs->file_list), text);
3505                   if(width > file_list_width)
3506                     {
3507                       file_list_width = width;
3508                       gtk_clist_set_column_width(GTK_CLIST(fs->file_list),0,
3509                                                  width);
3510                     }
3511                 }
3512             }
3513         }
3514
3515       poss = cmpl_next_completion (cmpl_state);
3516     }
3517
3518   gtk_clist_thaw (GTK_CLIST (fs->dir_list));
3519   gtk_clist_thaw (GTK_CLIST (fs->file_list));
3520
3521   /* File lists are set. */
3522
3523   g_assert (cmpl_state->reference_dir);
3524
3525   if (try_complete)
3526     {
3527
3528       /* User is trying to complete filenames, so advance the user's input
3529        * string to the updated_text, which is the common leading substring
3530        * of all possible completions, and if its a directory attempt
3531        * attempt completions in it. */
3532
3533       if (cmpl_updated_text (cmpl_state)[0])
3534         {
3535
3536           if (cmpl_updated_dir (cmpl_state))
3537             {
3538               gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
3539
3540               did_recurse = TRUE;
3541
3542               gtk_file_selection_populate (fs, dir_name, TRUE);
3543
3544               g_free (dir_name);
3545             }
3546           else
3547             {
3548               if (fs->selection_entry)
3549                       gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
3550                                           cmpl_updated_text (cmpl_state));
3551             }
3552         }
3553       else
3554         {
3555           selection_index = cmpl_last_valid_char (cmpl_state) -
3556                             (strlen (rel_path) - strlen (rem_path));
3557           if (fs->selection_entry)
3558             gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
3559         }
3560     }
3561   else
3562     {
3563       if (fs->selection_entry)
3564       /* Here we need to take the old filename and keep it!*/
3565         /*gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");*/
3566         ;
3567     }
3568
3569   if (!did_recurse)
3570     {
3571       if (fs->selection_entry)
3572         gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index);
3573
3574       if (fs->selection_entry)
3575         {
3576           sel_text = g_strconcat (_("Selection: "),
3577                                   cmpl_reference_position (cmpl_state),
3578                                   NULL);
3579
3580 /*
3581                 gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text);
3582 */
3583                 g_free (sel_text);
3584         }
3585
3586   gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
3587
3588     }
3589 }
3590
3591 static void
3592 gtk_file_selection_abort (GtkFileSelection *fs)
3593 {
3594   gchar err_buf[256];
3595
3596   sprintf (err_buf, _("Directory unreadable: %s"), cmpl_strerror (cmpl_errno));
3597
3598   /*  BEEP gdk_beep();  */
3599
3600 /*
3601   if (fs->selection_entry)
3602     gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
3603 */
3604 }
3605
3606 /**********************************************************************/
3607 /*                        External Interface                          */
3608 /**********************************************************************/
3609
3610 /* The four completion state selectors
3611  */
3612 static gchar*
3613 cmpl_updated_text (CompletionState* cmpl_state)
3614 {
3615   return cmpl_state->updated_text;
3616 }
3617
3618 static gint
3619 cmpl_updated_dir (CompletionState* cmpl_state)
3620 {
3621   return cmpl_state->re_complete;
3622 }
3623
3624 static gchar*
3625 cmpl_reference_position (CompletionState* cmpl_state)
3626 {
3627   return cmpl_state->reference_dir->fullname;
3628 }
3629
3630 static gint
3631 cmpl_last_valid_char (CompletionState* cmpl_state)
3632 {
3633   return cmpl_state->last_valid_char;
3634 }
3635
3636 static gchar*
3637 cmpl_completion_fullname (gchar* text, CompletionState* cmpl_state)
3638 {
3639   static char nothing[2] = "";
3640
3641   if (!cmpl_state_okay (cmpl_state))
3642     {
3643       return nothing;
3644     }
3645   else if (text[0] == '/')
3646     {
3647       strcpy (cmpl_state->updated_text, text);
3648     }
3649   else if (text[0] == '~')
3650     {
3651       CompletionDir* dir;
3652       char* slash;
3653
3654       dir = open_user_dir (text, cmpl_state);
3655
3656       if (!dir)
3657         {
3658           /* spencer says just return ~something, so
3659            * for now just do it. */
3660           strcpy (cmpl_state->updated_text, text);
3661         }
3662       else
3663         {
3664
3665           strcpy (cmpl_state->updated_text, dir->fullname);
3666
3667           slash = strchr (text, '/');
3668
3669           if (slash)
3670             strcat (cmpl_state->updated_text, slash);
3671         }
3672     }
3673   else
3674     {
3675       strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname);
3676       if (strcmp (cmpl_state->reference_dir->fullname, "/") != 0)
3677         strcat (cmpl_state->updated_text, "/");
3678       strcat (cmpl_state->updated_text, text);
3679     }
3680
3681   return cmpl_state->updated_text;
3682 }
3683
3684 /* The three completion selectors
3685  */
3686 static gchar*
3687 cmpl_this_completion (PossibleCompletion* pc)
3688 {
3689   return pc->text;
3690 }
3691
3692 static gint
3693 cmpl_is_directory (PossibleCompletion* pc)
3694 {
3695   return pc->is_directory;
3696 }
3697
3698 static gint
3699 cmpl_is_a_completion (PossibleCompletion* pc)
3700 {
3701   return pc->is_a_completion;
3702 }
3703
3704 /**********************************************************************/
3705 /*                       Construction, deletion                       */
3706 /**********************************************************************/
3707
3708 static CompletionState*
3709 cmpl_init_state (void)
3710 {
3711   gchar getcwd_buf[2*MAXPATHLEN];
3712   CompletionState *new_state;
3713
3714   new_state = g_new (CompletionState, 1);
3715
3716   /* We don't use getcwd() on SUNOS, because, it does a popen("pwd")
3717    * and, if that wasn't bad enough, hangs in doing so.
3718    */
3719 #if defined(sun) && !defined(__SVR4)
3720   if (!getwd (getcwd_buf))
3721 #else
3722   if (!getcwd (getcwd_buf, MAXPATHLEN))
3723 #endif
3724     {
3725       /* Oh joy, we can't get the current directory. Um..., we should have
3726        * a root directory, right? Right? (Probably not portable to non-Unix)
3727        */
3728       strcpy (getcwd_buf, "/");
3729     }
3730
3731 tryagain:
3732
3733   new_state->reference_dir = NULL;
3734   new_state->completion_dir = NULL;
3735   new_state->active_completion_dir = NULL;
3736   new_state->directory_storage = NULL;
3737   new_state->directory_sent_storage = NULL;
3738   new_state->last_valid_char = 0;
3739   new_state->updated_text = g_new (gchar, MAXPATHLEN);
3740   new_state->updated_text_alloc = MAXPATHLEN;
3741   new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
3742   new_state->the_completion.text_alloc = MAXPATHLEN;
3743   new_state->user_dir_name_buffer = NULL;
3744   new_state->user_directories = NULL;
3745
3746   new_state->reference_dir =  open_dir (getcwd_buf, new_state);
3747
3748   if (!new_state->reference_dir)
3749     {
3750       /* Directories changing from underneath us, grumble */
3751       strcpy (getcwd_buf, "/");
3752       goto tryagain;
3753     }
3754
3755   return new_state;
3756 }
3757
3758 static void
3759 cmpl_free_dir_list(GList* dp0)
3760 {
3761   GList *dp = dp0;
3762
3763   while (dp) {
3764     free_dir (dp->data);
3765     dp = dp->next;
3766   }
3767
3768   g_list_free(dp0);
3769 }
3770
3771 static void
3772 cmpl_free_dir_sent_list(GList* dp0)
3773 {
3774   GList *dp = dp0;
3775
3776   while (dp) {
3777     free_dir_sent (dp->data);
3778     dp = dp->next;
3779   }
3780
3781   g_list_free(dp0);
3782 }
3783
3784 static void
3785 cmpl_free_state (CompletionState* cmpl_state)
3786 {
3787   cmpl_free_dir_list (cmpl_state->directory_storage);
3788   cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
3789
3790   if (cmpl_state->user_dir_name_buffer)
3791     g_free (cmpl_state->user_dir_name_buffer);
3792   if (cmpl_state->user_directories)
3793     g_free (cmpl_state->user_directories);
3794   if (cmpl_state->the_completion.text)
3795     g_free (cmpl_state->the_completion.text);
3796   if (cmpl_state->updated_text)
3797     g_free (cmpl_state->updated_text);
3798
3799   g_free (cmpl_state);
3800 }
3801
3802 static void
3803 free_dir(CompletionDir* dir)
3804 {
3805   g_free(dir->fullname);
3806   g_free(dir);
3807 }
3808
3809 static void
3810 free_dir_sent(CompletionDirSent* sent)
3811 {
3812   g_free(sent->name_buffer);
3813   g_free(sent->entries);
3814   g_free(sent);
3815 }
3816
3817 static void
3818 prune_memory_usage(CompletionState *cmpl_state)
3819 {
3820   GList* cdsl = cmpl_state->directory_sent_storage;
3821   GList* cdl = cmpl_state->directory_storage;
3822   GList* cdl0 = cdl;
3823   gint len = 0;
3824
3825   for(; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
3826     cdsl = cdsl->next;
3827
3828   if (cdsl) {
3829     cmpl_free_dir_sent_list(cdsl->next);
3830     cdsl->next = NULL;
3831   }
3832
3833   cmpl_state->directory_storage = NULL;
3834   while (cdl) {
3835     if (cdl->data == cmpl_state->reference_dir)
3836       cmpl_state->directory_storage = g_list_prepend(NULL, cdl->data);
3837     else
3838       free_dir (cdl->data);
3839     cdl = cdl->next;
3840   }
3841
3842   g_list_free(cdl0);
3843 }
3844
3845 /**********************************************************************/
3846 /*                        The main entrances.                         */
3847 /**********************************************************************/
3848
3849 static PossibleCompletion*
3850 cmpl_completion_matches (gchar* text_to_complete,
3851                          gchar** remaining_text,
3852                          CompletionState* cmpl_state)
3853 {
3854   gchar* first_slash;
3855   PossibleCompletion *poss;
3856
3857   prune_memory_usage(cmpl_state);
3858
3859   g_assert (text_to_complete != NULL);
3860
3861   cmpl_state->user_completion_index = -1;
3862   cmpl_state->last_completion_text = text_to_complete;
3863   cmpl_state->the_completion.text[0] = 0;
3864   cmpl_state->last_valid_char = 0;
3865   cmpl_state->updated_text_len = -1;
3866   cmpl_state->updated_text[0] = 0;
3867   cmpl_state->re_complete = FALSE;
3868
3869   first_slash = strchr (text_to_complete, '/');
3870
3871   if (text_to_complete[0] == '~' && !first_slash)
3872     {
3873       /* Text starts with ~ and there is no slash, show all the
3874        * home directory completions.
3875        */
3876       poss = attempt_homedir_completion (text_to_complete, cmpl_state);
3877
3878       update_cmpl(poss, cmpl_state);
3879
3880       return poss;
3881     }
3882
3883   cmpl_state->reference_dir =
3884     open_ref_dir (text_to_complete, remaining_text, cmpl_state);
3885
3886   if(!cmpl_state->reference_dir)
3887     return NULL;
3888
3889   cmpl_state->completion_dir =
3890     find_completion_dir (*remaining_text, remaining_text, cmpl_state);
3891
3892   cmpl_state->last_valid_char = *remaining_text - text_to_complete;
3893
3894   if(!cmpl_state->completion_dir)
3895     return NULL;
3896
3897   cmpl_state->completion_dir->cmpl_index = -1;
3898   cmpl_state->completion_dir->cmpl_parent = NULL;
3899   cmpl_state->completion_dir->cmpl_text = *remaining_text;
3900
3901   cmpl_state->active_completion_dir = cmpl_state->completion_dir;
3902
3903   cmpl_state->reference_dir = cmpl_state->completion_dir;
3904
3905   poss = attempt_file_completion(cmpl_state);
3906
3907   update_cmpl(poss, cmpl_state);
3908
3909   return poss;
3910 }
3911
3912 static PossibleCompletion*
3913 cmpl_next_completion (CompletionState* cmpl_state)
3914 {
3915   PossibleCompletion* poss = NULL;
3916
3917   cmpl_state->the_completion.text[0] = 0;
3918
3919   if(cmpl_state->user_completion_index >= 0)
3920     poss = attempt_homedir_completion(cmpl_state->last_completion_text, cmpl_state);
3921   else
3922     poss = attempt_file_completion(cmpl_state);
3923
3924   update_cmpl(poss, cmpl_state);
3925
3926   return poss;
3927 }
3928
3929 /**********************************************************************/
3930 /*                       Directory Operations                         */
3931 /**********************************************************************/
3932
3933 /* Open the directory where completion will begin from, if possible. */
3934 static CompletionDir*
3935 open_ref_dir(gchar* text_to_complete,
3936              gchar** remaining_text,
3937              CompletionState* cmpl_state)
3938 {
3939   gchar* first_slash;
3940   CompletionDir *new_dir;
3941
3942   first_slash = strchr(text_to_complete, '/');
3943
3944   if (text_to_complete[0] == '~')
3945     {
3946       new_dir = open_user_dir(text_to_complete, cmpl_state);
3947
3948       if(new_dir)
3949         {
3950           if(first_slash)
3951             *remaining_text = first_slash + 1;
3952           else
3953             *remaining_text = text_to_complete + strlen(text_to_complete);
3954         }
3955       else
3956         {
3957           return NULL;
3958         }
3959     }
3960   else if (text_to_complete[0] == '/' || !cmpl_state->reference_dir)
3961     {
3962       gchar *tmp = g_strdup(text_to_complete);
3963       gchar *p;
3964
3965       p = tmp;
3966       while (*p && *p != '*' && *p != '?')
3967         p++;
3968
3969       *p = '\0';
3970       p = strrchr(tmp, '/');
3971       if (p)
3972         {
3973           if (p == tmp)
3974             p++;
3975
3976           *p = '\0';
3977
3978           new_dir = open_dir(tmp, cmpl_state);
3979
3980           if(new_dir)
3981             *remaining_text = text_to_complete +
3982               ((p == tmp + 1) ? (p - tmp) : (p + 1 - tmp));
3983         }
3984       else
3985         {
3986           /* If no possible candidates, use the cwd */
3987           gchar *curdir = g_get_current_dir ();
3988
3989           new_dir = open_dir(curdir, cmpl_state);
3990
3991           if (new_dir)
3992             *remaining_text = text_to_complete;
3993
3994           g_free (curdir);
3995         }
3996
3997       g_free (tmp);
3998     }
3999   else
4000     {
4001       *remaining_text = text_to_complete;
4002
4003       new_dir = open_dir(cmpl_state->reference_dir->fullname, cmpl_state);
4004     }
4005
4006   if(new_dir)
4007     {
4008       new_dir->cmpl_index = -1;
4009       new_dir->cmpl_parent = NULL;
4010     }
4011
4012   return new_dir;
4013 }
4014
4015 /* open a directory by user name */
4016 static CompletionDir*
4017 open_user_dir(gchar* text_to_complete,
4018               CompletionState *cmpl_state)
4019 {
4020   gchar *first_slash;
4021   gint cmp_len;
4022
4023   g_assert(text_to_complete && text_to_complete[0] == '~');
4024
4025   first_slash = strchr(text_to_complete, '/');
4026
4027   if (first_slash)
4028     cmp_len = first_slash - text_to_complete - 1;
4029   else
4030     cmp_len = strlen(text_to_complete + 1);
4031
4032   if(!cmp_len)
4033     {
4034       /* ~/ */
4035       gchar *homedir = g_get_home_dir ();
4036
4037       if (homedir)
4038         return open_dir(homedir, cmpl_state);
4039       else
4040         return NULL;
4041     }
4042   else
4043     {
4044       /* ~user/ */
4045       char* copy = g_new(char, cmp_len + 1);
4046       struct passwd *pwd;
4047       strncpy(copy, text_to_complete + 1, cmp_len);
4048       copy[cmp_len] = 0;
4049       pwd = getpwnam(copy);
4050       g_free(copy);
4051       if (!pwd)
4052         {
4053           cmpl_errno = errno;
4054           return NULL;
4055         }
4056
4057       return open_dir(pwd->pw_dir, cmpl_state);
4058     }
4059 }
4060
4061 /* open a directory relative the the current relative directory */
4062 static CompletionDir*
4063 open_relative_dir(gchar* dir_name,
4064                   CompletionDir* dir,
4065                   CompletionState *cmpl_state)
4066 {
4067   gchar path_buf[2*MAXPATHLEN];
4068
4069   if(dir->fullname_len + strlen(dir_name) + 2 >= MAXPATHLEN)
4070     {
4071       cmpl_errno = CMPL_ERRNO_TOO_LONG;
4072       return NULL;
4073     }
4074
4075   strcpy(path_buf, dir->fullname);
4076
4077   if(dir->fullname_len > 1)
4078     {
4079       path_buf[dir->fullname_len] = '/';
4080       strcpy(path_buf + dir->fullname_len + 1, dir_name);
4081     }
4082   else
4083     {
4084       strcpy(path_buf + dir->fullname_len, dir_name);
4085     }
4086
4087   return open_dir(path_buf, cmpl_state);
4088 }
4089
4090 /* after the cache lookup fails, really open a new directory */
4091 static CompletionDirSent*
4092 open_new_dir(gchar* dir_name, struct stat* sbuf, gboolean stat_subdirs)
4093 {
4094   CompletionDirSent* sent;
4095   DIR* directory;
4096   gchar *buffer_ptr;
4097   struct dirent *dirent_ptr;
4098   gint buffer_size = 0;
4099   gint entry_count = 0;
4100   gint i;
4101   struct stat ent_sbuf;
4102   char path_buf[MAXPATHLEN*2];
4103   gint path_buf_len;
4104
4105   sent = g_new(CompletionDirSent, 1);
4106   sent->mtime = sbuf->st_mtime;
4107   sent->inode = sbuf->st_ino;
4108   sent->device = sbuf->st_dev;
4109
4110   path_buf_len = strlen(dir_name);
4111
4112   if (path_buf_len > MAXPATHLEN)
4113     {
4114       cmpl_errno = CMPL_ERRNO_TOO_LONG;
4115       return NULL;
4116     }
4117
4118   strcpy(path_buf, dir_name);
4119
4120   directory = opendir(dir_name);
4121
4122   if(!directory)
4123     {
4124       cmpl_errno = errno;
4125       return NULL;
4126     }
4127
4128   while((dirent_ptr = readdir(directory)) != NULL)
4129     {
4130       int entry_len = strlen(dirent_ptr->d_name);
4131       buffer_size += entry_len + 1;
4132       entry_count += 1;
4133
4134       if(path_buf_len + entry_len + 2 >= MAXPATHLEN)
4135         {
4136           cmpl_errno = CMPL_ERRNO_TOO_LONG;
4137           closedir(directory);
4138           return NULL;
4139         }
4140     }
4141
4142   sent->name_buffer = g_new(gchar, buffer_size);
4143   sent->entries = g_new(CompletionDirEntry, entry_count);
4144   sent->entry_count = entry_count;
4145
4146   buffer_ptr = sent->name_buffer;
4147
4148   rewinddir(directory);
4149
4150   for(i = 0; i < entry_count; i += 1)
4151     {
4152       dirent_ptr = readdir(directory);
4153
4154       if(!dirent_ptr)
4155         {
4156           cmpl_errno = errno;
4157           closedir(directory);
4158           return NULL;
4159         }
4160
4161       strcpy(buffer_ptr, dirent_ptr->d_name);
4162       sent->entries[i].entry_name = buffer_ptr;
4163       buffer_ptr += strlen(dirent_ptr->d_name);
4164       *buffer_ptr = 0;
4165       buffer_ptr += 1;
4166
4167       path_buf[path_buf_len] = '/';
4168       strcpy(path_buf + path_buf_len + 1, dirent_ptr->d_name);
4169
4170       if (stat_subdirs)
4171         {
4172           if(stat(path_buf, &ent_sbuf) >= 0 && S_ISDIR(ent_sbuf.st_mode))
4173             sent->entries[i].is_dir = 1;
4174           else
4175             /* stat may fail, and we don't mind, since it could be a
4176              * dangling symlink. */
4177             sent->entries[i].is_dir = 0;
4178         }
4179       else
4180         sent->entries[i].is_dir = 1;
4181     }
4182
4183   qsort(sent->entries, sent->entry_count, sizeof(CompletionDirEntry), compare_cmpl_dir);
4184
4185   closedir(directory);
4186
4187   return sent;
4188 }
4189
4190 static gboolean
4191 check_dir(gchar *dir_name, struct stat *result, gboolean *stat_subdirs)
4192 {
4193   /* A list of directories that we know only contain other directories.
4194    * Trying to stat every file in these directories would be very
4195    * expensive.
4196    */
4197
4198   static struct {
4199     gchar *name;
4200     gboolean present;
4201     struct stat statbuf;
4202   } no_stat_dirs[] = {
4203     { "/afs", FALSE, { 0 } },
4204     { "/net", FALSE, { 0 } }
4205   };
4206
4207   static const gint n_no_stat_dirs = sizeof(no_stat_dirs) / sizeof(no_stat_dirs[0]);
4208   static gboolean initialized = FALSE;
4209
4210   gint i;
4211
4212   if (!initialized)
4213     {
4214       initialized = TRUE;
4215       for (i = 0; i < n_no_stat_dirs; i++)
4216         {
4217           if (stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
4218             no_stat_dirs[i].present = TRUE;
4219         }
4220     }
4221
4222   if(stat(dir_name, result) < 0)
4223     {
4224       cmpl_errno = errno;
4225       return FALSE;
4226     }
4227
4228   *stat_subdirs = TRUE;
4229   for (i=0; i<n_no_stat_dirs; i++)
4230     {
4231       if (no_stat_dirs[i].present &&
4232           (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
4233           (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
4234         {
4235           *stat_subdirs = FALSE;
4236           break;
4237         }
4238     }
4239
4240   return TRUE;
4241 }
4242
4243 /* open a directory by absolute pathname */
4244 static CompletionDir*
4245 open_dir(gchar* dir_name, CompletionState* cmpl_state)
4246 {
4247   struct stat sbuf;
4248   gboolean stat_subdirs;
4249   CompletionDirSent *sent;
4250   GList* cdsl;
4251
4252   if (!check_dir (dir_name, &sbuf, &stat_subdirs))
4253     return NULL;
4254
4255   cdsl = cmpl_state->directory_sent_storage;
4256
4257   while (cdsl)
4258     {
4259       sent = cdsl->data;
4260
4261       if(sent->inode == sbuf.st_ino &&
4262          sent->mtime == sbuf.st_mtime &&
4263          sent->device == sbuf.st_dev)
4264         return attach_dir(sent, dir_name, cmpl_state);
4265
4266       cdsl = cdsl->next;
4267     }
4268
4269   sent = open_new_dir(dir_name, &sbuf, stat_subdirs);
4270
4271   if (sent) {
4272     cmpl_state->directory_sent_storage =
4273       g_list_prepend(cmpl_state->directory_sent_storage, sent);
4274
4275     return attach_dir(sent, dir_name, cmpl_state);
4276   }
4277
4278   return NULL;
4279 }
4280
4281 static CompletionDir*
4282 attach_dir(CompletionDirSent* sent, gchar* dir_name, CompletionState *cmpl_state)
4283 {
4284   CompletionDir* new_dir;
4285
4286   new_dir = g_new(CompletionDir, 1);
4287
4288   cmpl_state->directory_storage =
4289     g_list_prepend(cmpl_state->directory_storage, new_dir);
4290
4291   new_dir->sent = sent;
4292   new_dir->fullname = g_strdup(dir_name);
4293   new_dir->fullname_len = strlen(dir_name);
4294
4295   return new_dir;
4296 }
4297
4298 static gint
4299 correct_dir_fullname(CompletionDir* cmpl_dir)
4300 {
4301   gint length = strlen(cmpl_dir->fullname);
4302   struct stat sbuf;
4303
4304   if (strcmp(cmpl_dir->fullname + length - 2, "/.") == 0)
4305     {
4306       if (length == 2)
4307         {
4308           strcpy(cmpl_dir->fullname, "/");
4309           cmpl_dir->fullname_len = 1;
4310           return TRUE;
4311         } else {
4312           cmpl_dir->fullname[length - 2] = 0;
4313         }
4314     }
4315   else if (strcmp(cmpl_dir->fullname + length - 3, "/./") == 0)
4316     cmpl_dir->fullname[length - 2] = 0;
4317   else if (strcmp(cmpl_dir->fullname + length - 3, "/..") == 0)
4318     {
4319       if(length == 3)
4320         {
4321           strcpy(cmpl_dir->fullname, "/");
4322           cmpl_dir->fullname_len = 1;
4323           return TRUE;
4324         }
4325
4326       if(stat(cmpl_dir->fullname, &sbuf) < 0)
4327         {
4328           cmpl_errno = errno;
4329           return FALSE;
4330         }
4331
4332       cmpl_dir->fullname[length - 2] = 0;
4333
4334       if(!correct_parent(cmpl_dir, &sbuf))
4335         return FALSE;
4336     }
4337   else if (strcmp(cmpl_dir->fullname + length - 4, "/../") == 0)
4338     {
4339       if(length == 4)
4340         {
4341           strcpy(cmpl_dir->fullname, "/");
4342           cmpl_dir->fullname_len = 1;
4343           return TRUE;
4344         }
4345
4346       if(stat(cmpl_dir->fullname, &sbuf) < 0)
4347         {
4348           cmpl_errno = errno;
4349           return FALSE;
4350         }
4351
4352       cmpl_dir->fullname[length - 3] = 0;
4353
4354       if(!correct_parent(cmpl_dir, &sbuf))
4355         return FALSE;
4356     }
4357
4358   cmpl_dir->fullname_len = strlen(cmpl_dir->fullname);
4359
4360   return TRUE;
4361 }
4362
4363 static gint
4364 correct_parent(CompletionDir* cmpl_dir, struct stat *sbuf)
4365 {
4366   struct stat parbuf;
4367   gchar *last_slash;
4368   gchar *new_name;
4369   gchar c = 0;
4370
4371   last_slash = strrchr(cmpl_dir->fullname, '/');
4372
4373   g_assert(last_slash);
4374
4375   if(last_slash != cmpl_dir->fullname)
4376     { /* last_slash[0] = 0; */ }
4377   else
4378     {
4379       c = last_slash[1];
4380       last_slash[1] = 0;
4381     }
4382
4383   if (stat(cmpl_dir->fullname, &parbuf) < 0)
4384     {
4385       cmpl_errno = errno;
4386       return FALSE;
4387     }
4388
4389   if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
4390     /* it wasn't a link */
4391     return TRUE;
4392
4393   if(c)
4394     last_slash[1] = c;
4395   /* else
4396     last_slash[0] = '/'; */
4397
4398   /* it was a link, have to figure it out the hard way */
4399
4400   new_name = find_parent_dir_fullname(cmpl_dir->fullname);
4401
4402   if (!new_name)
4403     return FALSE;
4404
4405   g_free(cmpl_dir->fullname);
4406
4407   cmpl_dir->fullname = new_name;
4408
4409   return TRUE;
4410 }
4411
4412 static gchar*
4413 find_parent_dir_fullname(gchar* dirname)
4414 {
4415   gchar buffer[MAXPATHLEN];
4416   gchar buffer2[MAXPATHLEN];
4417
4418 #if defined(sun) && !defined(__SVR4)
4419   if(!getwd(buffer))
4420 #else
4421   if(!getcwd(buffer, MAXPATHLEN))
4422 #endif
4423     {
4424       cmpl_errno = errno;
4425       return NULL;
4426     }
4427
4428   if(chdir(dirname) != 0 || chdir("..") != 0)
4429     {
4430       cmpl_errno = errno;
4431       return NULL;
4432     }
4433
4434 #if defined(sun) && !defined(__SVR4)
4435   if(!getwd(buffer2))
4436 #else
4437   if(!getcwd(buffer2, MAXPATHLEN))
4438 #endif
4439     {
4440       chdir(buffer);
4441       cmpl_errno = errno;
4442
4443       return NULL;
4444     }
4445
4446   if(chdir(buffer) != 0)
4447     {
4448       cmpl_errno = errno;
4449       return NULL;
4450     }
4451
4452   return g_strdup(buffer2);
4453 }
4454
4455 /**********************************************************************/
4456 /*                        Completion Operations                       */
4457 /**********************************************************************/
4458
4459 static PossibleCompletion*
4460 attempt_homedir_completion(gchar* text_to_complete,
4461                            CompletionState *cmpl_state)
4462 {
4463   gint index, length;
4464
4465   if (!cmpl_state->user_dir_name_buffer &&
4466       !get_pwdb(cmpl_state))
4467     return NULL;
4468   length = strlen(text_to_complete) - 1;
4469
4470   cmpl_state->user_completion_index += 1;
4471
4472   while(cmpl_state->user_completion_index < cmpl_state->user_directories_len)
4473     {
4474       index = first_diff_index(text_to_complete + 1,
4475                                cmpl_state->user_directories
4476                                [cmpl_state->user_completion_index].login);
4477
4478       switch(index)
4479         {
4480         case PATTERN_MATCH:
4481           break;
4482         default:
4483           if(cmpl_state->last_valid_char < (index + 1))
4484             cmpl_state->last_valid_char = index + 1;
4485           cmpl_state->user_completion_index += 1;
4486           continue;
4487         }
4488
4489       cmpl_state->the_completion.is_a_completion = 1;
4490       cmpl_state->the_completion.is_directory = 1;
4491
4492       append_completion_text("~", cmpl_state);
4493
4494       append_completion_text(cmpl_state->
4495                               user_directories[cmpl_state->user_completion_index].login,
4496                              cmpl_state);
4497
4498       return append_completion_text("/", cmpl_state);
4499     }
4500
4501   if(text_to_complete[1] ||
4502      cmpl_state->user_completion_index > cmpl_state->user_directories_len)
4503     {
4504       cmpl_state->user_completion_index = -1;
4505       return NULL;
4506     }
4507   else
4508     {
4509       cmpl_state->user_completion_index += 1;
4510       cmpl_state->the_completion.is_a_completion = 1;
4511       cmpl_state->the_completion.is_directory = 1;
4512
4513       return append_completion_text("~/", cmpl_state);
4514     }
4515 }
4516
4517 /* returns the index (>= 0) of the first differing character,
4518  * PATTERN_MATCH if the completion matches */
4519 static gint
4520 first_diff_index(gchar* pat, gchar* text)
4521 {
4522   gint diff = 0;
4523
4524   while(*pat && *text && *text == *pat)
4525     {
4526       pat += 1;
4527       text += 1;
4528       diff += 1;
4529     }
4530
4531   if(*pat)
4532     return diff;
4533
4534   return PATTERN_MATCH;
4535 }
4536
4537 static PossibleCompletion*
4538 append_completion_text(gchar* text, CompletionState* cmpl_state)
4539 {
4540   gint len, i = 1;
4541
4542   if(!cmpl_state->the_completion.text)
4543     return NULL;
4544
4545   len = strlen(text) + strlen(cmpl_state->the_completion.text) + 1;
4546
4547   if(cmpl_state->the_completion.text_alloc > len)
4548     {
4549       strcat(cmpl_state->the_completion.text, text);
4550       return &cmpl_state->the_completion;
4551     }
4552
4553   while(i < len) { i <<= 1; }
4554
4555   cmpl_state->the_completion.text_alloc = i;
4556
4557   cmpl_state->the_completion.text = (gchar*)g_realloc(cmpl_state->the_completion.text, i);
4558
4559   if(!cmpl_state->the_completion.text)
4560     return NULL;
4561   else
4562     {
4563       strcat(cmpl_state->the_completion.text, text);
4564       return &cmpl_state->the_completion;
4565     }
4566 }
4567
4568 static CompletionDir*
4569 find_completion_dir(gchar* text_to_complete,
4570                     gchar** remaining_text,
4571                     CompletionState* cmpl_state)
4572 {
4573   gchar* first_slash = strchr(text_to_complete, '/');
4574   CompletionDir* dir = cmpl_state->reference_dir;
4575   CompletionDir* next;
4576   *remaining_text = text_to_complete;
4577
4578   while(first_slash)
4579     {
4580       gint len = first_slash - *remaining_text;
4581       gint found = 0;
4582       gchar *found_name = NULL;         /* Quiet gcc */
4583       gint i;
4584       gchar* pat_buf = g_new (gchar, len + 1);
4585
4586       strncpy(pat_buf, *remaining_text, len);
4587       pat_buf[len] = 0;
4588
4589       for(i = 0; i < dir->sent->entry_count; i += 1)
4590         {
4591           if(dir->sent->entries[i].is_dir &&
4592              fnmatch(pat_buf, dir->sent->entries[i].entry_name,
4593                      FNMATCH_FLAGS)!= FNM_NOMATCH)
4594             {
4595               if(found)
4596                 {
4597                   g_free (pat_buf);
4598                   return dir;
4599                 }
4600               else
4601                 {
4602                   found = 1;
4603                   found_name = dir->sent->entries[i].entry_name;
4604                 }
4605             }
4606         }
4607
4608       if (!found)
4609         {
4610           /* Perhaps we are trying to open an automount directory */
4611           found_name = pat_buf;
4612         }
4613
4614       next = open_relative_dir(found_name, dir, cmpl_state);
4615
4616       if(!next)
4617         {
4618           g_free (pat_buf);
4619           return NULL;
4620         }
4621
4622       next->cmpl_parent = dir;
4623
4624       dir = next;
4625
4626       if(!correct_dir_fullname(dir))
4627         {
4628           g_free(pat_buf);
4629           return NULL;
4630         }
4631
4632       *remaining_text = first_slash + 1;
4633       first_slash = strchr(*remaining_text, '/');
4634
4635       g_free (pat_buf);
4636     }
4637
4638   return dir;
4639 }
4640
4641 static void
4642 update_cmpl(PossibleCompletion* poss, CompletionState* cmpl_state)
4643 {
4644   gint cmpl_len;
4645
4646   if(!poss || !cmpl_is_a_completion(poss))
4647     return;
4648
4649   cmpl_len = strlen(cmpl_this_completion(poss));
4650
4651   if(cmpl_state->updated_text_alloc < cmpl_len + 1)
4652     {
4653       cmpl_state->updated_text =
4654         (gchar*)g_realloc(cmpl_state->updated_text,
4655                           cmpl_state->updated_text_alloc);
4656       cmpl_state->updated_text_alloc = 2*cmpl_len;
4657     }
4658
4659   if(cmpl_state->updated_text_len < 0)
4660     {
4661       strcpy(cmpl_state->updated_text, cmpl_this_completion(poss));
4662       cmpl_state->updated_text_len = cmpl_len;
4663       cmpl_state->re_complete = cmpl_is_directory(poss);
4664     }
4665   else if(cmpl_state->updated_text_len == 0)
4666     {
4667       cmpl_state->re_complete = FALSE;
4668     }
4669   else
4670     {
4671       gint first_diff =
4672         first_diff_index(cmpl_state->updated_text,
4673                          cmpl_this_completion(poss));
4674
4675       cmpl_state->re_complete = FALSE;
4676
4677       if(first_diff == PATTERN_MATCH)
4678         return;
4679
4680       if(first_diff > cmpl_state->updated_text_len)
4681         strcpy(cmpl_state->updated_text, cmpl_this_completion(poss));
4682
4683       cmpl_state->updated_text_len = first_diff;
4684       cmpl_state->updated_text[first_diff] = 0;
4685     }
4686 }
4687
4688 static PossibleCompletion*
4689 attempt_file_completion(CompletionState *cmpl_state)
4690 {
4691   gchar *pat_buf, *first_slash;
4692   CompletionDir *dir = cmpl_state->active_completion_dir;
4693
4694   dir->cmpl_index += 1;
4695
4696   if(dir->cmpl_index == dir->sent->entry_count)
4697     {
4698       if(dir->cmpl_parent == NULL)
4699         {
4700           cmpl_state->active_completion_dir = NULL;
4701
4702           return NULL;
4703         }
4704       else
4705         {
4706           cmpl_state->active_completion_dir = dir->cmpl_parent;
4707
4708           return attempt_file_completion(cmpl_state);
4709         }
4710     }
4711
4712   g_assert(dir->cmpl_text);
4713
4714   first_slash = strchr(dir->cmpl_text, '/');
4715
4716   if(first_slash)
4717     {
4718       gint len = first_slash - dir->cmpl_text;
4719
4720       pat_buf = g_new (gchar, len + 1);
4721       strncpy(pat_buf, dir->cmpl_text, len);
4722       pat_buf[len] = 0;
4723     }
4724   else
4725     {
4726       gint len = strlen(dir->cmpl_text);
4727
4728       pat_buf = g_new (gchar, len + 2);
4729       strcpy(pat_buf, dir->cmpl_text);
4730       strcpy(pat_buf + len, "*");
4731     }
4732
4733   if(first_slash)
4734     {
4735       if(dir->sent->entries[dir->cmpl_index].is_dir)
4736         {
4737           if(fnmatch(pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
4738                      FNMATCH_FLAGS) != FNM_NOMATCH)
4739             {
4740               CompletionDir* new_dir;
4741
4742               new_dir = open_relative_dir(dir->sent->entries[dir->cmpl_index].entry_name,
4743                                           dir, cmpl_state);
4744
4745               if(!new_dir)
4746                 {
4747                   g_free (pat_buf);
4748                   return NULL;
4749                 }
4750
4751               new_dir->cmpl_parent = dir;
4752
4753               new_dir->cmpl_index = -1;
4754               new_dir->cmpl_text = first_slash + 1;
4755
4756               cmpl_state->active_completion_dir = new_dir;
4757
4758               g_free (pat_buf);
4759               return attempt_file_completion(cmpl_state);
4760             }
4761           else
4762             {
4763               g_free (pat_buf);
4764               return attempt_file_completion(cmpl_state);
4765             }
4766         }
4767       else
4768         {
4769           g_free (pat_buf);
4770           return attempt_file_completion(cmpl_state);
4771         }
4772     }
4773   else
4774     {
4775       if(dir->cmpl_parent != NULL)
4776         {
4777           append_completion_text(dir->fullname +
4778                                  strlen(cmpl_state->completion_dir->fullname) + 1,
4779                                  cmpl_state);
4780           append_completion_text("/", cmpl_state);
4781         }
4782
4783       append_completion_text(dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
4784
4785       cmpl_state->the_completion.is_a_completion =
4786         (fnmatch(pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
4787                  FNMATCH_FLAGS) != FNM_NOMATCH);
4788
4789       cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
4790       if(dir->sent->entries[dir->cmpl_index].is_dir)
4791         append_completion_text("/", cmpl_state);
4792
4793       g_free (pat_buf);
4794       return &cmpl_state->the_completion;
4795     }
4796 }
4797
4798
4799 static gint
4800 get_pwdb(CompletionState* cmpl_state)
4801 {
4802   struct passwd *pwd_ptr;
4803   gchar* buf_ptr;
4804   gint len = 0, i, count = 0;
4805
4806   if(cmpl_state->user_dir_name_buffer)
4807     return TRUE;
4808   setpwent ();
4809
4810   while ((pwd_ptr = getpwent()) != NULL)
4811     {
4812       len += strlen(pwd_ptr->pw_name);
4813       len += strlen(pwd_ptr->pw_dir);
4814       len += 2;
4815       count += 1;
4816     }
4817
4818   setpwent ();
4819
4820   cmpl_state->user_dir_name_buffer = g_new(gchar, len);
4821   cmpl_state->user_directories = g_new(CompletionUserDir, count);
4822   cmpl_state->user_directories_len = count;
4823
4824   buf_ptr = cmpl_state->user_dir_name_buffer;
4825
4826   for(i = 0; i < count; i += 1)
4827     {
4828       pwd_ptr = getpwent();
4829       if(!pwd_ptr)
4830         {
4831           cmpl_errno = errno;
4832           goto error;
4833         }
4834
4835       strcpy(buf_ptr, pwd_ptr->pw_name);
4836       cmpl_state->user_directories[i].login = buf_ptr;
4837       buf_ptr += strlen(buf_ptr);
4838       buf_ptr += 1;
4839       strcpy(buf_ptr, pwd_ptr->pw_dir);
4840       cmpl_state->user_directories[i].homedir = buf_ptr;
4841       buf_ptr += strlen(buf_ptr);
4842       buf_ptr += 1;
4843     }
4844
4845   qsort(cmpl_state->user_directories,
4846         cmpl_state->user_directories_len,
4847         sizeof(CompletionUserDir),
4848         compare_user_dir);
4849
4850   endpwent();
4851
4852   return TRUE;
4853
4854 error:
4855
4856   if(cmpl_state->user_dir_name_buffer)
4857     g_free(cmpl_state->user_dir_name_buffer);
4858   if(cmpl_state->user_directories)
4859     g_free(cmpl_state->user_directories);
4860
4861   cmpl_state->user_dir_name_buffer = NULL;
4862   cmpl_state->user_directories = NULL;
4863
4864   return FALSE;
4865 }
4866
4867 static gint
4868 compare_user_dir(const void* a, const void* b)
4869 {
4870   return strcmp((((CompletionUserDir*)a))->login,
4871                 (((CompletionUserDir*)b))->login);
4872 }
4873
4874 static gint
4875 compare_cmpl_dir(const void* a, const void* b)
4876 {
4877   return strcmp((((CompletionDirEntry*)a))->entry_name,
4878                 (((CompletionDirEntry*)b))->entry_name);
4879 }
4880
4881 static gint
4882 cmpl_state_okay(CompletionState* cmpl_state)
4883 {
4884   return  cmpl_state && cmpl_state->reference_dir;
4885 }
4886
4887 static gchar*
4888 cmpl_strerror(gint err)
4889 {
4890   if(err == CMPL_ERRNO_TOO_LONG)
4891     return "Name too long";
4892   else
4893     return g_strerror (err);
4894 }
4895
4896 /* This is an internally used function to create pixmaps. */
4897 GtkWidget*
4898 create_pixmap(GtkWidget *widget, const gchar *pixmap_char)
4899 {
4900   GdkPixmap *gdkpixmap;
4901   GdkBitmap *mask;
4902   GtkWidget *pixmap;
4903   GdkColormap *colormap;
4904
4905   colormap = gtk_widget_get_colormap (widget);
4906
4907   gdkpixmap = gdk_pixmap_colormap_create_from_xpm_d (GTK_WIDGET(widget)->window,
4908                                                      colormap,
4909                                                      &mask,
4910                                                      NULL,
4911                                                      (gpointer) pixmap_char);
4912   if (gdkpixmap == NULL)
4913     {
4914       g_warning ("Error loading pixmap: %s", pixmap_char);
4915       return NULL;
4916     }
4917   pixmap = gtk_pixmap_new (gdkpixmap, mask);
4918   gdk_pixmap_unref (gdkpixmap);
4919   gdk_bitmap_unref (mask);
4920   return pixmap;
4921 }
4922
4923
4924 /* Testing area */
4925 #ifdef TORRIE_DEBUG
4926
4927 /* Get the selected filename and print it to the console */
4928 void file_ok_sel( GtkWidget        *w,
4929                   GtkFileSelection *fs )
4930 {
4931     g_print ("%s\n", gtk_file_selection_get_filename (GTK_FILE_SELECTION (fs)));
4932 }
4933
4934 void destroy( GtkWidget *widget,
4935               gpointer   data )
4936 {
4937     gtk_main_quit ();
4938 }
4939
4940 int main( int   argc,
4941           char *argv[] )
4942 {
4943     GtkWidget *filew;
4944     const gchar *masks[] = { "mp3s/playlists <*.mp3,*.m3u>",
4945                              "src/hdr <*.[CcHh],*.[Cc][Cc],*.[Hh][Hh],*.cpp>",
4946                              NULL };
4947
4948     gtk_init (&argc, &argv);
4949
4950     /* Create a new file selection widget */
4951     filew = gtk_file_selection_new ("Spiffy File Selector");
4952 //    gtk_file_selection_complete(GTK_FILE_SELECTION(filew),"bob");
4953
4954     gtk_file_selection_set_masks (GTK_FILE_SELECTION (filew), masks);
4955
4956     gtk_signal_connect (GTK_OBJECT (filew), "destroy",
4957                         (GtkSignalFunc) destroy, &filew);
4958     /* Connect the ok_button to file_ok_sel function */
4959     gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
4960                         "clicked", (GtkSignalFunc) file_ok_sel, filew );
4961
4962     /* Connect the cancel_button to destroy the widget */
4963     gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION
4964                                             (filew)->cancel_button),
4965                                "clicked", (GtkSignalFunc) gtk_widget_destroy,
4966                                GTK_OBJECT (filew));
4967
4968
4969     gtk_widget_show(filew);
4970
4971 /*
4972     g_print("%d",gtk_file_selection_match_mask("mask.c","m*.c"));
4973     g_print("%d",gtk_file_selection_match_mask("mask.c","m???.c"));
4974                 g_print("%d",gtk_file_selection_match_mask("mask.c","m??*.c"));
4975                 g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c"));
4976                 g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c???"));
4977                 g_print("%d",gtk_file_selection_match_mask("mask.cout","m*.c*"));
4978                 g_print("%d",gtk_file_selection_match_mask("mask.cout","n*.c???"));
4979                 g_print("%d",gtk_file_selection_match_mask("mask.c","[mn]*"));
4980                 g_print("%d",gtk_file_selection_match_mask("COPYING","*.xpm"));
4981 */
4982     gtk_main ();
4983
4984     return 0;
4985 }
4986 /* example-end */
4987 #endif