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