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