1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
30 #include <sys/types.h>
32 #ifdef HAVE_SYS_PARAM_H
33 #include <sys/param.h>
45 #include <glib.h> /* Include early to get G_OS_WIN32 and
48 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
53 #endif /* G_OS_WIN32 || G_WITH_CYGWIN */
55 #include <winsock.h> /* For gethostname */
60 #include "gdk/gdkkeysyms.h"
61 #include "gtkbutton.h"
62 #include "gtkcellrenderertext.h"
64 #include "gtkfilesel.h"
68 #include "gtkliststore.h"
70 #include "gtkscrolledwindow.h"
72 #include "gtksignal.h"
73 #include "gtktreeselection.h"
74 #include "gtktreeview.h"
77 #include "gtkmenuitem.h"
78 #include "gtkoptionmenu.h"
79 #include "gtkdialog.h"
80 #include "gtkmessagedialog.h"
83 #include "gtkeventbox.h"
89 #define mkdir(p,m) _mkdir(p)
91 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
93 #endif /* G_OS_WIN32 */
95 #define DIR_LIST_WIDTH 180
96 #define DIR_LIST_HEIGHT 180
97 #define FILE_LIST_WIDTH 180
98 #define FILE_LIST_HEIGHT 180
100 /* The Hurd doesn't define either PATH_MAX or MAXPATHLEN, so we put this
101 * in here, since the rest of the code in the file does require some
106 # define MAXPATHLEN PATH_MAX
108 # define MAXPATHLEN 2048
112 /* I've put this here so it doesn't get confused with the
113 * file completion interface */
114 typedef struct _HistoryCallbackArg HistoryCallbackArg;
116 struct _HistoryCallbackArg
119 GtkWidget *menu_item;
123 typedef struct _CompletionState CompletionState;
124 typedef struct _CompletionDir CompletionDir;
125 typedef struct _CompletionDirSent CompletionDirSent;
126 typedef struct _CompletionDirEntry CompletionDirEntry;
127 typedef struct _CompletionUserDir CompletionUserDir;
128 typedef struct _PossibleCompletion PossibleCompletion;
130 /* Non-external file completion decls and structures */
132 /* A contant telling PRCS how many directories to cache. Its actually
133 * kept in a list, so the geometry isn't important. */
134 #define CMPL_DIRECTORY_CACHE_SIZE 10
136 /* A constant used to determine whether a substring was an exact
137 * match by first_diff_index()
139 #define PATTERN_MATCH -1
140 /* The arguments used by all fnmatch() calls below
142 #define FNMATCH_FLAGS (FNM_PATHNAME | FNM_PERIOD)
144 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
145 #define CMPL_ERRNO_DID_NOT_CONVERT ((1<<16)-2)
147 /* This structure contains all the useful information about a directory
148 * for the purposes of filename completion. These structures are cached
149 * in the CompletionState struct. CompletionDir's are reference counted.
151 struct _CompletionDirSent
158 struct _CompletionDirEntry *entries;
161 struct _CompletionDir
163 CompletionDirSent *sent;
168 struct _CompletionDir *cmpl_parent;
173 /* This structure contains pairs of directory entry names with a flag saying
174 * whether or not they are a valid directory. NOTE: This information is used
175 * to provide the caller with information about whether to update its completions
176 * or try to open a file. Since directories are cached by the directory mtime,
177 * a symlink which points to an invalid file (which will not be a directory),
178 * will not be reevaluated if that file is created, unless the containing
179 * directory is touched. I consider this case to be worth ignoring (josh).
181 struct _CompletionDirEntry
187 struct _CompletionUserDir
193 struct _PossibleCompletion
195 /* accessible fields, all are accessed externally by functions
199 gint is_a_completion;
200 gboolean is_directory;
207 struct _CompletionState
209 gint last_valid_char;
211 gint updated_text_len;
212 gint updated_text_alloc;
213 gboolean re_complete;
215 gchar *user_dir_name_buffer;
216 gint user_directories_len;
218 gchar *last_completion_text;
220 gint user_completion_index; /* if >= 0, currently completing ~user */
222 struct _CompletionDir *completion_dir; /* directory completing from */
223 struct _CompletionDir *active_completion_dir;
225 struct _PossibleCompletion the_completion;
227 struct _CompletionDir *reference_dir; /* initial directory */
229 GList* directory_storage;
230 GList* directory_sent_storage;
232 struct _CompletionUserDir *user_directories;
250 /* File completion functions which would be external, were they used
251 * outside of this file.
254 static CompletionState* cmpl_init_state (void);
255 static void cmpl_free_state (CompletionState *cmpl_state);
256 static gint cmpl_state_okay (CompletionState* cmpl_state);
257 static const gchar* cmpl_strerror (gint);
259 static PossibleCompletion* cmpl_completion_matches(gchar *text_to_complete,
260 gchar **remaining_text,
261 CompletionState *cmpl_state);
263 /* Returns a name for consideration, possibly a completion, this name
264 * will be invalid after the next call to cmpl_next_completion.
266 static char* cmpl_this_completion (PossibleCompletion*);
268 /* True if this completion matches the given text. Otherwise, this
269 * output can be used to have a list of non-completions.
271 static gint cmpl_is_a_completion (PossibleCompletion*);
273 /* True if the completion is a directory
275 static gboolean cmpl_is_directory (PossibleCompletion*);
277 /* Obtains the next completion, or NULL
279 static PossibleCompletion* cmpl_next_completion (CompletionState*);
281 /* Updating completions: the return value of cmpl_updated_text() will
282 * be text_to_complete completed as much as possible after the most
283 * recent call to cmpl_completion_matches. For the present
284 * application, this is the suggested replacement for the user's input
285 * string. You must CALL THIS AFTER ALL cmpl_text_completions have
288 static gchar* cmpl_updated_text (CompletionState* cmpl_state);
290 /* After updating, to see if the completion was a directory, call
291 * this. If it was, you should consider re-calling completion_matches.
293 static gboolean cmpl_updated_dir (CompletionState* cmpl_state);
295 /* Current location: if using file completion, return the current
296 * directory, from which file completion begins. More specifically,
297 * the cwd concatenated with all exact completions up to the last
298 * directory delimiter('/').
300 static gchar* cmpl_reference_position (CompletionState* cmpl_state);
302 /* backing up: if cmpl_completion_matches returns NULL, you may query
303 * the index of the last completable character into cmpl_updated_text.
305 static gint cmpl_last_valid_char (CompletionState* cmpl_state);
307 /* When the user selects a non-directory, call cmpl_completion_fullname
308 * to get the full name of the selected file.
310 static gchar* cmpl_completion_fullname (const gchar*, CompletionState* cmpl_state);
313 /* Directory operations. */
314 static CompletionDir* open_ref_dir (gchar* text_to_complete,
315 gchar** remaining_text,
316 CompletionState* cmpl_state);
317 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
318 static gboolean check_dir (gchar *dir_name,
320 gboolean *stat_subdirs);
322 static CompletionDir* open_dir (gchar* dir_name,
323 CompletionState* cmpl_state);
325 static CompletionDir* open_user_dir (const gchar* text_to_complete,
326 CompletionState *cmpl_state);
328 static CompletionDir* open_relative_dir (gchar* dir_name, CompletionDir* dir,
329 CompletionState *cmpl_state);
330 static CompletionDirSent* open_new_dir (gchar* dir_name,
332 gboolean stat_subdirs);
333 static gint correct_dir_fullname (CompletionDir* cmpl_dir);
334 static gint correct_parent (CompletionDir* cmpl_dir,
337 static gchar* find_parent_dir_fullname (gchar* dirname);
339 static CompletionDir* attach_dir (CompletionDirSent* sent,
341 CompletionState *cmpl_state);
342 static void free_dir_sent (CompletionDirSent* sent);
343 static void free_dir (CompletionDir *dir);
344 static void prune_memory_usage(CompletionState *cmpl_state);
346 /* Completion operations */
348 static PossibleCompletion* attempt_homedir_completion(gchar* text_to_complete,
349 CompletionState *cmpl_state);
351 static PossibleCompletion* attempt_file_completion(CompletionState *cmpl_state);
352 static CompletionDir* find_completion_dir(gchar* text_to_complete,
353 gchar** remaining_text,
354 CompletionState* cmpl_state);
355 static PossibleCompletion* append_completion_text(gchar* text,
356 CompletionState* cmpl_state);
358 static gint get_pwdb(CompletionState* cmpl_state);
359 static gint compare_user_dir(const void* a, const void* b);
361 static gint first_diff_index(gchar* pat, gchar* text);
362 static gint compare_cmpl_dir(const void* a, const void* b);
363 static void update_cmpl(PossibleCompletion* poss,
364 CompletionState* cmpl_state);
366 static void gtk_file_selection_class_init (GtkFileSelectionClass *klass);
367 static void gtk_file_selection_set_property (GObject *object,
371 static void gtk_file_selection_get_property (GObject *object,
375 static void gtk_file_selection_init (GtkFileSelection *filesel);
376 static void gtk_file_selection_finalize (GObject *object);
377 static void gtk_file_selection_destroy (GtkObject *object);
378 static void gtk_file_selection_map (GtkWidget *widget);
379 static gint gtk_file_selection_key_press (GtkWidget *widget,
382 static gint gtk_file_selection_insert_text (GtkWidget *widget,
383 const gchar *new_text,
384 gint new_text_length,
388 static void gtk_file_selection_file_activate (GtkTreeView *tree_view,
390 GtkTreeViewColumn *column,
392 static void gtk_file_selection_file_changed (GtkTreeSelection *selection,
394 static void gtk_file_selection_dir_activate (GtkTreeView *tree_view,
396 GtkTreeViewColumn *column,
399 static void gtk_file_selection_populate (GtkFileSelection *fs,
401 gboolean try_complete,
402 gboolean reset_entry);
403 static void gtk_file_selection_abort (GtkFileSelection *fs);
405 static void gtk_file_selection_update_history_menu (GtkFileSelection *fs,
408 static void gtk_file_selection_create_dir (GtkWidget *widget, gpointer data);
409 static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
410 static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
412 static void free_selected_names (GPtrArray *names);
414 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
415 #define compare_filenames(a, b) strcmp(a, b)
417 #define compare_filenames(a, b) g_ascii_strcasecmp(a, b)
421 static GtkWindowClass *parent_class = NULL;
423 /* Saves errno when something cmpl does fails. */
424 static gint cmpl_errno;
428 * Take the path currently in the file selection
429 * entry field and translate as necessary from
430 * a WIN32 style to CYGWIN32 style path. For
431 * instance translate:
432 * x:\somepath\file.jpg
434 * //x/somepath/file.jpg
436 * Replace the path in the selection text field.
437 * Return a boolean value concerning whether a
438 * translation had to be made.
441 translate_win32_path (GtkFileSelection *filesel)
447 * Retrieve the current path
449 path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
452 * Translate only if this looks like a DOS-ish
453 * path... First handle any drive letters.
455 if (isalpha (path[0]) && (path[1] == ':')) {
457 * This part kind of stinks... It isn't possible
458 * to know if there is enough space in the current
459 * string for the extra character required in this
460 * conversion. Assume that there isn't enough space
461 * and use the set function on the text field to
462 * set the newly created string.
464 gchar *newPath = g_strdup_printf ("//%c/%s", path[0], (path + 3));
465 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);
472 * Now, replace backslashes with forward slashes
475 if (strchr (path, '\\'))
478 for (index = 0; path[index] != '\0'; index++)
479 if (path[index] == '\\')
490 gtk_file_selection_get_type (void)
492 static GtkType file_selection_type = 0;
494 if (!file_selection_type)
496 static const GtkTypeInfo filesel_info =
499 sizeof (GtkFileSelection),
500 sizeof (GtkFileSelectionClass),
501 (GtkClassInitFunc) gtk_file_selection_class_init,
502 (GtkObjectInitFunc) gtk_file_selection_init,
503 /* reserved_1 */ NULL,
504 /* reserved_2 */ NULL,
505 (GtkClassInitFunc) NULL,
508 file_selection_type = gtk_type_unique (GTK_TYPE_DIALOG, &filesel_info);
511 return file_selection_type;
515 gtk_file_selection_class_init (GtkFileSelectionClass *class)
517 GObjectClass *gobject_class;
518 GtkObjectClass *object_class;
519 GtkWidgetClass *widget_class;
521 gobject_class = (GObjectClass*) class;
522 object_class = (GtkObjectClass*) class;
523 widget_class = (GtkWidgetClass*) class;
525 parent_class = gtk_type_class (GTK_TYPE_DIALOG);
527 gobject_class->finalize = gtk_file_selection_finalize;
528 gobject_class->set_property = gtk_file_selection_set_property;
529 gobject_class->get_property = gtk_file_selection_get_property;
531 g_object_class_install_property (gobject_class,
533 g_param_spec_string ("filename",
535 _("The currently selected filename."),
537 G_PARAM_READABLE | G_PARAM_WRITABLE));
538 g_object_class_install_property (gobject_class,
540 g_param_spec_boolean ("show_fileops",
541 _("Show file operations"),
542 _("Whether buttons for creating/manipulating files should be displayed."),
546 g_object_class_install_property (gobject_class,
547 PROP_SELECT_MULTIPLE,
548 g_param_spec_boolean ("select_multiple",
549 _("Select multiple"),
550 _("Whether to allow multiple files to be selected."),
554 object_class->destroy = gtk_file_selection_destroy;
555 widget_class->map = gtk_file_selection_map;
558 static void gtk_file_selection_set_property (GObject *object,
563 GtkFileSelection *filesel;
565 filesel = GTK_FILE_SELECTION (object);
570 gtk_file_selection_set_filename (filesel,
571 g_value_get_string (value));
573 case PROP_SHOW_FILEOPS:
574 if (g_value_get_boolean (value))
575 gtk_file_selection_show_fileop_buttons (filesel);
577 gtk_file_selection_hide_fileop_buttons (filesel);
579 case PROP_SELECT_MULTIPLE:
580 gtk_file_selection_set_select_multiple (filesel, g_value_get_boolean (value));
583 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
588 static void gtk_file_selection_get_property (GObject *object,
593 GtkFileSelection *filesel;
595 filesel = GTK_FILE_SELECTION (object);
600 g_value_set_string (value,
601 gtk_file_selection_get_filename(filesel));
604 case PROP_SHOW_FILEOPS:
605 /* This is a little bit hacky, but doing otherwise would require
606 * adding a field to the object.
608 g_value_set_boolean (value, (filesel->fileop_c_dir &&
609 filesel->fileop_del_file &&
610 filesel->fileop_ren_file));
612 case PROP_SELECT_MULTIPLE:
613 g_value_set_boolean (value, gtk_file_selection_get_select_multiple (filesel));
616 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
622 grab_default (GtkWidget *widget)
624 gtk_widget_grab_default (widget);
629 gtk_file_selection_init (GtkFileSelection *filesel)
631 GtkWidget *entry_vbox;
633 GtkWidget *list_hbox;
634 GtkWidget *confirm_area;
635 GtkWidget *pulldown_hbox;
636 GtkWidget *scrolled_win;
641 GtkTreeViewColumn *column;
643 gtk_widget_push_composite_child ();
645 dialog = GTK_DIALOG (filesel);
647 filesel->cmpl_state = cmpl_init_state ();
649 /* The dialog-sized vertical box */
650 filesel->main_vbox = dialog->vbox;
651 gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
653 /* The horizontal box containing create, rename etc. buttons */
654 filesel->button_area = gtk_hbutton_box_new ();
655 gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel->button_area), GTK_BUTTONBOX_START);
656 gtk_box_set_spacing (GTK_BOX (filesel->button_area), 0);
657 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area,
659 gtk_widget_show (filesel->button_area);
661 gtk_file_selection_show_fileop_buttons (filesel);
663 /* hbox for pulldown menu */
664 pulldown_hbox = gtk_hbox_new (TRUE, 5);
665 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
666 gtk_widget_show (pulldown_hbox);
669 filesel->history_pulldown = gtk_option_menu_new ();
670 gtk_widget_show (filesel->history_pulldown);
671 gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown,
674 /* The horizontal box containing the directory and file listboxes */
675 list_hbox = gtk_hbox_new (FALSE, 5);
676 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
677 gtk_widget_show (list_hbox);
679 /* The directories list */
681 model = gtk_list_store_new (1, G_TYPE_STRING);
682 filesel->dir_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
683 g_object_unref (model);
685 column = gtk_tree_view_column_new_with_attributes (_("Folders"),
686 gtk_cell_renderer_text_new (),
689 label = gtk_label_new_with_mnemonic (_("Fol_ders"));
690 gtk_label_set_mnemonic_widget (GTK_LABEL (label), filesel->dir_list);
691 gtk_widget_show (label);
692 gtk_tree_view_column_set_widget (column, label);
693 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
694 gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->dir_list), column);
696 gtk_widget_set_usize (filesel->dir_list, DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
697 g_signal_connect (filesel->dir_list, "row_activated",
698 G_CALLBACK (gtk_file_selection_dir_activate), filesel);
700 /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list)); */
702 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
703 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);
704 gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
705 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
706 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
707 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
708 gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
709 gtk_widget_show (filesel->dir_list);
710 gtk_widget_show (scrolled_win);
713 model = gtk_list_store_new (1, G_TYPE_STRING);
714 filesel->file_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
715 g_object_unref (model);
717 column = gtk_tree_view_column_new_with_attributes (_("Files"),
718 gtk_cell_renderer_text_new (),
721 label = gtk_label_new_with_mnemonic (_("_Files"));
722 gtk_label_set_mnemonic_widget (GTK_LABEL (label), filesel->file_list);
723 gtk_widget_show (label);
724 gtk_tree_view_column_set_widget (column, label);
725 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
726 gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->file_list), column);
728 gtk_widget_set_usize (filesel->file_list, FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
729 g_signal_connect (filesel->file_list, "row_activated",
730 G_CALLBACK (gtk_file_selection_file_activate), filesel);
731 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list)), "changed",
732 G_CALLBACK (gtk_file_selection_file_changed), filesel);
734 /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list)); */
736 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
737 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);
738 gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
739 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
740 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
741 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 5);
742 gtk_box_pack_start (GTK_BOX (list_hbox), scrolled_win, TRUE, TRUE, 0);
743 gtk_widget_show (filesel->file_list);
744 gtk_widget_show (scrolled_win);
746 /* action area for packing buttons into. */
747 filesel->action_area = gtk_hbox_new (TRUE, 0);
748 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area,
750 gtk_widget_show (filesel->action_area);
752 /* The OK/Cancel button area */
753 confirm_area = dialog->action_area;
755 /* The Cancel button */
756 filesel->cancel_button = gtk_dialog_add_button (dialog,
758 GTK_RESPONSE_CANCEL);
760 filesel->ok_button = gtk_dialog_add_button (dialog,
764 gtk_widget_grab_default (filesel->ok_button);
766 /* The selection entry widget */
767 entry_vbox = gtk_vbox_new (FALSE, 2);
768 gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2);
769 gtk_widget_show (entry_vbox);
771 eventbox = gtk_event_box_new ();
772 filesel->selection_text = label = gtk_label_new ("");
773 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
774 gtk_container_add (GTK_CONTAINER (eventbox), label);
775 gtk_box_pack_start (GTK_BOX (entry_vbox), eventbox, FALSE, FALSE, 0);
776 gtk_widget_show (label);
777 gtk_widget_show (eventbox);
779 filesel->selection_entry = gtk_entry_new ();
780 gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "key_press_event",
781 (GtkSignalFunc) gtk_file_selection_key_press, filesel);
782 gtk_signal_connect (GTK_OBJECT (filesel->selection_entry), "insert_text",
783 (GtkSignalFunc) gtk_file_selection_insert_text, NULL);
784 gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "focus_in_event",
785 (GtkSignalFunc) grab_default,
786 GTK_OBJECT (filesel->ok_button));
787 gtk_signal_connect_object (GTK_OBJECT (filesel->selection_entry), "activate",
788 (GtkSignalFunc) gtk_button_clicked,
789 GTK_OBJECT (filesel->ok_button));
790 gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
791 gtk_widget_show (filesel->selection_entry);
793 if (!cmpl_state_okay (filesel->cmpl_state))
797 sprintf (err_buf, _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
799 gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
803 gtk_file_selection_populate (filesel, "", FALSE, TRUE);
806 gtk_widget_grab_focus (filesel->selection_entry);
808 gtk_widget_pop_composite_child ();
812 uri_list_extract_first_uri (const gchar* uri_list)
816 g_return_val_if_fail (uri_list != NULL, NULL);
819 /* We don't actually try to validate the URI according to RFC
820 * 2396, or even check for allowed characters - we just ignore
821 * comments and trim whitespace off the ends. We also
822 * allow LF delimination as well as the specified CRLF.
824 * We do allow comments like specified in RFC 2483.
830 while (g_ascii_isspace(*p))
834 while (*q && (*q != '\n') && (*q != '\r'))
840 while (q > p && g_ascii_isspace (*q))
844 return g_strndup (p, q - p + 1);
847 p = strchr (p, '\n');
855 dnd_really_drop (GtkWidget *dialog, gint response_id, GtkFileSelection *fs)
859 if (response_id == GTK_RESPONSE_YES)
861 filename = g_object_get_data (G_OBJECT (dialog), "gtk-fs-dnd-filename");
863 gtk_file_selection_set_filename (fs, filename);
866 gtk_widget_destroy (dialog);
871 filenames_dropped (GtkWidget *widget,
872 GdkDragContext *context,
875 GtkSelectionData *selection_data,
880 char *filename = NULL;
882 char this_hostname[257];
884 GError *error = NULL;
886 if (!selection_data->data)
889 uri = uri_list_extract_first_uri ((char *)selection_data->data);
894 filename = g_filename_from_uri (uri, &hostname, &error);
899 g_warning ("Error getting dropped filename: %s\n",
901 g_error_free (error);
905 res = gethostname (this_hostname, 256);
906 this_hostname[256] = 0;
908 if ((hostname == NULL) ||
909 (res == 0 && strcmp (hostname, this_hostname) == 0) ||
910 (strcmp (hostname, "localhost") == 0))
911 gtk_file_selection_set_filename (GTK_FILE_SELECTION (widget),
917 dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
918 GTK_DIALOG_DESTROY_WITH_PARENT,
919 GTK_MESSAGE_QUESTION,
921 _("The file \"%s\" resides on another machine (called %s) and may not be availible to this program.\n"
922 "Are you sure that you want to select it?"), filename, hostname);
924 g_object_set_data_full (G_OBJECT (dialog), "gtk-fs-dnd-filename", g_strdup (filename), g_free);
926 g_signal_connect_data (dialog, "response",
927 (GCallback) dnd_really_drop,
930 gtk_widget_show (dialog);
948 filenames_drag_get (GtkWidget *widget,
949 GdkDragContext *context,
950 GtkSelectionData *selection_data,
953 GtkFileSelection *filesel)
961 file = gtk_file_selection_get_filename (filesel);
965 if (info == TARGET_URILIST)
967 res = gethostname (hostname, 256);
970 uri_list = g_filename_to_uri (file, (!res)?hostname:NULL, &error);
973 g_warning ("Error getting filename: %s\n",
975 g_error_free (error);
979 gtk_selection_data_set (selection_data,
980 selection_data->target, 8,
981 (void *)uri_list, strlen((char *)uri_list));
986 g_print ("Setting text: '%s'\n", file);
987 gtk_selection_data_set_text (selection_data, file, -1);
993 file_selection_setup_dnd (GtkFileSelection *filesel)
996 static GtkTargetEntry drop_types[] = {
997 { "text/uri-list", 0, TARGET_URILIST}
999 static gint n_drop_types = sizeof(drop_types)/sizeof(drop_types[0]);
1000 static GtkTargetEntry drag_types[] = {
1001 { "text/uri-list", 0, TARGET_URILIST},
1002 { "UTF8_STRING", 0, TARGET_UTF8_STRING },
1005 { "COMPOUND_TEXT", 0, 0 }
1007 static gint n_drag_types = sizeof(drag_types)/sizeof(drag_types[0]);
1009 gtk_drag_dest_set (GTK_WIDGET (filesel),
1010 GTK_DEST_DEFAULT_ALL,
1011 drop_types, n_drop_types,
1014 gtk_signal_connect (GTK_OBJECT(filesel), "drag_data_received",
1015 GTK_SIGNAL_FUNC(filenames_dropped), NULL);
1017 eventbox = gtk_widget_get_parent (filesel->selection_text);
1018 gtk_drag_source_set (eventbox,
1020 drag_types, n_drag_types,
1023 gtk_signal_connect (GTK_OBJECT (eventbox),
1025 GTK_SIGNAL_FUNC (filenames_drag_get),
1030 gtk_file_selection_new (const gchar *title)
1032 GtkFileSelection *filesel;
1034 filesel = gtk_type_new (GTK_TYPE_FILE_SELECTION);
1035 gtk_window_set_title (GTK_WINDOW (filesel), title);
1036 gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE);
1038 file_selection_setup_dnd (filesel);
1040 return GTK_WIDGET (filesel);
1044 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
1046 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1048 /* delete, create directory, and rename */
1049 if (!filesel->fileop_c_dir)
1051 filesel->fileop_c_dir = gtk_button_new_with_mnemonic (_("_New Folder"));
1052 gtk_signal_connect (GTK_OBJECT (filesel->fileop_c_dir), "clicked",
1053 (GtkSignalFunc) gtk_file_selection_create_dir,
1054 (gpointer) filesel);
1055 gtk_box_pack_start (GTK_BOX (filesel->button_area),
1056 filesel->fileop_c_dir, TRUE, TRUE, 0);
1057 gtk_widget_show (filesel->fileop_c_dir);
1060 if (!filesel->fileop_del_file)
1062 filesel->fileop_del_file = gtk_button_new_with_mnemonic (_("De_lete File"));
1063 gtk_signal_connect (GTK_OBJECT (filesel->fileop_del_file), "clicked",
1064 (GtkSignalFunc) gtk_file_selection_delete_file,
1065 (gpointer) filesel);
1066 gtk_box_pack_start (GTK_BOX (filesel->button_area),
1067 filesel->fileop_del_file, TRUE, TRUE, 0);
1068 gtk_widget_show (filesel->fileop_del_file);
1071 if (!filesel->fileop_ren_file)
1073 filesel->fileop_ren_file = gtk_button_new_with_mnemonic (_("_Rename File"));
1074 gtk_signal_connect (GTK_OBJECT (filesel->fileop_ren_file), "clicked",
1075 (GtkSignalFunc) gtk_file_selection_rename_file,
1076 (gpointer) filesel);
1077 gtk_box_pack_start (GTK_BOX (filesel->button_area),
1078 filesel->fileop_ren_file, TRUE, TRUE, 0);
1079 gtk_widget_show (filesel->fileop_ren_file);
1081 g_object_notify (G_OBJECT (filesel), "show_fileops");
1082 gtk_widget_queue_resize (GTK_WIDGET (filesel));
1086 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
1088 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1090 if (filesel->fileop_ren_file)
1092 gtk_widget_destroy (filesel->fileop_ren_file);
1093 filesel->fileop_ren_file = NULL;
1096 if (filesel->fileop_del_file)
1098 gtk_widget_destroy (filesel->fileop_del_file);
1099 filesel->fileop_del_file = NULL;
1102 if (filesel->fileop_c_dir)
1104 gtk_widget_destroy (filesel->fileop_c_dir);
1105 filesel->fileop_c_dir = NULL;
1107 g_object_notify (G_OBJECT (filesel), "show_fileops");
1113 gtk_file_selection_set_filename (GtkFileSelection *filesel,
1114 const gchar *filename)
1117 const char *name, *last_slash;
1119 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1120 g_return_if_fail (filename != NULL);
1122 last_slash = strrchr (filename, G_DIR_SEPARATOR);
1126 buf = g_strdup ("");
1131 buf = g_strdup (filename);
1132 buf[last_slash - filename + 1] = 0;
1133 name = last_slash + 1;
1136 gtk_file_selection_populate (filesel, buf, FALSE, TRUE);
1138 if (filesel->selection_entry)
1139 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
1141 g_object_notify (G_OBJECT (filesel), "filename");
1145 * gtk_file_selection_get_filename:
1146 * @filesel: a #GtkFileSelection
1148 * This function returns the selected filename in encoding of
1149 * g_filename_from_utf8(), which may or may not be the same as that
1150 * used by GTK+ (UTF-8). To convert to UTF-8, call g_filename_to_utf8().
1151 * The returned string points to a statically allocated buffer and
1152 * should be copied if you plan to keep it around.
1154 * Return value: currently-selected filename in locale's encoding
1156 G_CONST_RETURN gchar*
1157 gtk_file_selection_get_filename (GtkFileSelection *filesel)
1159 static gchar nothing[2] = "";
1160 static gchar something[MAXPATHLEN*2];
1164 g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
1166 #ifdef G_WITH_CYGWIN
1167 translate_win32_path (filesel);
1169 text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
1172 sys_filename = g_filename_from_utf8 (cmpl_completion_fullname (text, filesel->cmpl_state), -1, NULL, NULL, NULL);
1175 strncpy (something, sys_filename, sizeof (something));
1176 g_free (sys_filename);
1184 gtk_file_selection_complete (GtkFileSelection *filesel,
1185 const gchar *pattern)
1187 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1188 g_return_if_fail (pattern != NULL);
1190 if (filesel->selection_entry)
1191 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
1192 gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE, TRUE);
1196 gtk_file_selection_destroy (GtkObject *object)
1198 GtkFileSelection *filesel;
1200 HistoryCallbackArg *callback_arg;
1202 g_return_if_fail (GTK_IS_FILE_SELECTION (object));
1204 filesel = GTK_FILE_SELECTION (object);
1206 if (filesel->fileop_dialog)
1208 gtk_widget_destroy (filesel->fileop_dialog);
1209 filesel->fileop_dialog = NULL;
1212 if (filesel->history_list)
1214 list = filesel->history_list;
1217 callback_arg = list->data;
1218 g_free (callback_arg->directory);
1219 g_free (callback_arg);
1222 g_list_free (filesel->history_list);
1223 filesel->history_list = NULL;
1226 if (filesel->cmpl_state)
1228 cmpl_free_state (filesel->cmpl_state);
1229 filesel->cmpl_state = NULL;
1232 if (filesel->selected_names)
1234 free_selected_names (filesel->selected_names);
1235 filesel->selected_names = NULL;
1238 if (filesel->last_selected)
1240 g_free (filesel->last_selected);
1241 filesel->last_selected = NULL;
1244 GTK_OBJECT_CLASS (parent_class)->destroy (object);
1248 gtk_file_selection_map (GtkWidget *widget)
1250 GtkFileSelection *filesel = GTK_FILE_SELECTION (widget);
1252 /* Refresh the contents */
1253 gtk_file_selection_populate (filesel, "", FALSE, FALSE);
1255 GTK_WIDGET_CLASS (parent_class)->map (widget);
1259 gtk_file_selection_finalize (GObject *object)
1261 GtkFileSelection *filesel = GTK_FILE_SELECTION (object);
1263 g_free (filesel->fileop_file);
1265 G_OBJECT_CLASS (parent_class)->finalize (object);
1268 /* Begin file operations callbacks */
1271 gtk_file_selection_fileop_error (GtkFileSelection *fs,
1272 gchar *error_message)
1276 g_return_if_fail (error_message != NULL);
1279 dialog = gtk_message_dialog_new (GTK_WINDOW (fs),
1280 GTK_DIALOG_DESTROY_WITH_PARENT,
1283 "%s", error_message);
1285 /* yes, we free it */
1286 g_free (error_message);
1288 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1290 gtk_signal_connect_object (GTK_OBJECT (dialog), "response",
1291 (GtkSignalFunc) gtk_widget_destroy,
1294 gtk_widget_show (dialog);
1298 gtk_file_selection_fileop_destroy (GtkWidget *widget,
1301 GtkFileSelection *fs = data;
1303 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1305 fs->fileop_dialog = NULL;
1310 gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
1313 GtkFileSelection *fs = data;
1314 const gchar *dirname;
1317 gchar *sys_full_path;
1319 GError *error = NULL;
1320 CompletionState *cmpl_state;
1322 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1324 dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1325 cmpl_state = (CompletionState*) fs->cmpl_state;
1326 path = cmpl_reference_position (cmpl_state);
1328 full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
1329 sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1332 if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1333 buf = g_strdup_printf (_("The folder name \"%s\" contains symbols that are not allowed in filenames"), dirname);
1335 buf = g_strdup_printf (_("Error creating folder \"%s\": %s\n%s"), dirname, error->message,
1336 _("You probably used symbols not allowed in filenames."));
1337 gtk_file_selection_fileop_error (fs, buf);
1338 g_error_free (error);
1342 if (mkdir (sys_full_path, 0755) < 0)
1344 buf = g_strdup_printf (_("Error creating folder \"%s\": %s\n"), dirname,
1345 g_strerror (errno));
1346 gtk_file_selection_fileop_error (fs, buf);
1351 g_free (sys_full_path);
1353 gtk_widget_destroy (fs->fileop_dialog);
1354 gtk_file_selection_populate (fs, "", FALSE, FALSE);
1358 gtk_file_selection_create_dir (GtkWidget *widget,
1361 GtkFileSelection *fs = data;
1367 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1369 if (fs->fileop_dialog)
1373 dialog = gtk_dialog_new ();
1374 fs->fileop_dialog = dialog;
1375 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1376 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1378 gtk_window_set_title (GTK_WINDOW (dialog), _("New Folder"));
1379 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1380 gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1382 /* If file dialog is grabbed, grab option dialog */
1383 /* When option dialog is closed, file dialog will be grabbed again */
1384 if (GTK_WINDOW (fs)->modal)
1385 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1387 vbox = gtk_vbox_new (FALSE, 0);
1388 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1389 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1391 gtk_widget_show( vbox);
1393 label = gtk_label_new_with_mnemonic (_("_Folder name:"));
1394 gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
1395 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1396 gtk_widget_show (label);
1398 /* The directory entry widget */
1399 fs->fileop_entry = gtk_entry_new ();
1400 gtk_label_set_mnemonic_widget (GTK_LABEL (label), fs->fileop_entry);
1401 gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
1403 GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1404 gtk_widget_show (fs->fileop_entry);
1407 button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
1408 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1409 (GtkSignalFunc) gtk_widget_destroy,
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);
1417 gtk_widget_grab_focus (fs->fileop_entry);
1419 button = gtk_button_new_with_label (_("Create"));
1420 gtk_signal_connect (GTK_OBJECT (button), "clicked",
1421 (GtkSignalFunc) gtk_file_selection_create_dir_confirmed,
1423 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1424 button, TRUE, TRUE, 0);
1425 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1426 gtk_widget_show (button);
1428 gtk_widget_show (dialog);
1432 gtk_file_selection_delete_file_response (GtkDialog *dialog,
1436 GtkFileSelection *fs = data;
1437 CompletionState *cmpl_state;
1440 gchar *sys_full_path;
1441 GError *error = NULL;
1444 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1446 if (response_id != GTK_RESPONSE_OK)
1448 gtk_widget_destroy (GTK_WIDGET (dialog));
1452 cmpl_state = (CompletionState*) fs->cmpl_state;
1453 path = cmpl_reference_position (cmpl_state);
1455 full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1456 sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1459 if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1460 buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
1463 buf = g_strdup_printf (_("Error deleting file \"%s\": %s\n%s"),
1464 fs->fileop_file, error->message,
1465 _("It probably contains symbols not allowed in filenames."));
1467 gtk_file_selection_fileop_error (fs, buf);
1468 g_error_free (error);
1472 if (unlink (sys_full_path) < 0)
1474 buf = g_strdup_printf (_("Error deleting file \"%s\": %s"),
1475 fs->fileop_file, g_strerror (errno));
1476 gtk_file_selection_fileop_error (fs, buf);
1481 g_free (sys_full_path);
1483 gtk_widget_destroy (fs->fileop_dialog);
1484 gtk_file_selection_populate (fs, "", FALSE, TRUE);
1488 gtk_file_selection_delete_file (GtkWidget *widget,
1491 GtkFileSelection *fs = data;
1493 const gchar *filename;
1495 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1497 if (fs->fileop_dialog)
1500 #ifdef G_WITH_CYGWIN
1501 translate_win32_path (fs);
1504 filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1505 if (strlen (filename) < 1)
1508 g_free (fs->fileop_file);
1509 fs->fileop_file = g_strdup (filename);
1512 fs->fileop_dialog = dialog =
1513 gtk_message_dialog_new (GTK_WINDOW (fs),
1514 GTK_WINDOW (fs)->modal ? GTK_DIALOG_MODAL : 0,
1515 GTK_MESSAGE_QUESTION,
1517 _("Really delete file \"%s\" ?"), filename);
1519 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1520 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1522 gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1523 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1526 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1527 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1528 GTK_STOCK_DELETE, GTK_RESPONSE_OK,
1531 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
1533 g_signal_connect (G_OBJECT (dialog), "response",
1534 G_CALLBACK (gtk_file_selection_delete_file_response),
1537 gtk_widget_show (dialog);
1541 gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
1544 GtkFileSelection *fs = data;
1548 gchar *new_filename;
1549 gchar *old_filename;
1550 gchar *sys_new_filename;
1551 gchar *sys_old_filename;
1552 CompletionState *cmpl_state;
1553 GError *error = NULL;
1555 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1557 file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1558 cmpl_state = (CompletionState*) fs->cmpl_state;
1559 path = cmpl_reference_position (cmpl_state);
1561 new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
1562 old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1564 sys_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, &error);
1567 if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1568 buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), new_filename);
1570 buf = g_strdup_printf (_("Error renaming file to \"%s\": %s\n%s"),
1571 new_filename, error->message,
1572 _("You probably used symbols not allowed in filenames."));
1573 gtk_file_selection_fileop_error (fs, buf);
1574 g_error_free (error);
1578 sys_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, &error);
1581 if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1582 buf = g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), old_filename);
1584 buf = g_strdup_printf (_("Error renaming file \"%s\": %s\n%s"),
1585 old_filename, error->message,
1586 _("It probably contains symbols not allowed in filenames."));
1587 gtk_file_selection_fileop_error (fs, buf);
1588 g_error_free (error);
1592 if (rename (sys_old_filename, sys_new_filename) < 0)
1594 buf = g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
1595 sys_old_filename, sys_new_filename,
1596 g_strerror (errno));
1597 gtk_file_selection_fileop_error (fs, buf);
1601 gtk_file_selection_populate (fs, "", FALSE, FALSE);
1602 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), file);
1605 g_free (sys_old_filename);
1608 g_free (new_filename);
1609 g_free (old_filename);
1610 g_free (sys_new_filename);
1612 gtk_widget_destroy (fs->fileop_dialog);
1616 gtk_file_selection_rename_file (GtkWidget *widget,
1619 GtkFileSelection *fs = data;
1626 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1628 if (fs->fileop_dialog)
1631 g_free (fs->fileop_file);
1632 fs->fileop_file = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1633 if (strlen (fs->fileop_file) < 1)
1637 fs->fileop_dialog = dialog = gtk_dialog_new ();
1638 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
1639 (GtkSignalFunc) gtk_file_selection_fileop_destroy,
1641 gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
1642 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1643 gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1645 /* If file dialog is grabbed, grab option dialog */
1646 /* When option dialog closed, file dialog will be grabbed again */
1647 if (GTK_WINDOW (fs)->modal)
1648 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1650 vbox = gtk_vbox_new (FALSE, 0);
1651 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1652 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1654 gtk_widget_show(vbox);
1656 buf = g_strdup_printf (_("Rename file \"%s\" to:"), fs->fileop_file);
1657 label = gtk_label_new (buf);
1658 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1659 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1660 gtk_widget_show (label);
1663 /* New filename entry */
1664 fs->fileop_entry = gtk_entry_new ();
1665 gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
1667 GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1668 gtk_widget_show (fs->fileop_entry);
1670 gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
1671 gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
1672 0, strlen (fs->fileop_file));
1675 button = gtk_button_new_from_stock (GTK_STOCK_CANCEL);
1676 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1677 (GtkSignalFunc) gtk_widget_destroy,
1679 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1680 button, TRUE, TRUE, 0);
1681 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1682 gtk_widget_grab_default (button);
1683 gtk_widget_show (button);
1685 gtk_widget_grab_focus (fs->fileop_entry);
1687 button = gtk_button_new_with_label (_("Rename"));
1688 gtk_signal_connect (GTK_OBJECT (button), "clicked",
1689 (GtkSignalFunc) gtk_file_selection_rename_file_confirmed,
1691 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area),
1692 button, TRUE, TRUE, 0);
1693 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1694 gtk_widget_show (button);
1696 gtk_widget_show (dialog);
1700 gtk_file_selection_insert_text (GtkWidget *widget,
1701 const gchar *new_text,
1702 gint new_text_length,
1708 filename = g_filename_from_utf8 (new_text, new_text_length, NULL, NULL, NULL);
1713 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "insert_text");
1723 gtk_file_selection_key_press (GtkWidget *widget,
1727 GtkFileSelection *fs;
1730 g_return_val_if_fail (widget != NULL, FALSE);
1731 g_return_val_if_fail (event != NULL, FALSE);
1733 if ((event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab) &&
1734 (event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
1736 fs = GTK_FILE_SELECTION (user_data);
1737 #ifdef G_WITH_CYGWIN
1738 translate_win32_path (fs);
1740 text = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1742 gtk_file_selection_populate (fs, text, TRUE, TRUE);
1753 gtk_file_selection_history_callback (GtkWidget *widget,
1756 GtkFileSelection *fs = data;
1757 HistoryCallbackArg *callback_arg;
1760 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1762 list = fs->history_list;
1765 callback_arg = list->data;
1767 if (callback_arg->menu_item == widget)
1769 gtk_file_selection_populate (fs, callback_arg->directory, FALSE, FALSE);
1778 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1779 gchar *current_directory)
1781 HistoryCallbackArg *callback_arg;
1782 GtkWidget *menu_item;
1788 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1789 g_return_if_fail (current_directory != NULL);
1791 list = fs->history_list;
1793 if (fs->history_menu)
1796 callback_arg = list->data;
1797 g_free (callback_arg->directory);
1798 g_free (callback_arg);
1801 g_list_free (fs->history_list);
1802 fs->history_list = NULL;
1804 gtk_widget_destroy (fs->history_menu);
1807 fs->history_menu = gtk_menu_new ();
1809 current_dir = g_strdup (current_directory);
1811 dir_len = strlen (current_dir);
1813 for (i = dir_len; i >= 0; i--)
1815 /* the i == dir_len is to catch the full path for the first
1817 if ( (current_dir[i] == G_DIR_SEPARATOR) || (i == dir_len))
1819 /* another small hack to catch the full path */
1821 current_dir[i + 1] = '\0';
1822 #ifdef G_WITH_CYGWIN
1823 if (!strcmp (current_dir, "//"))
1826 menu_item = gtk_menu_item_new_with_label (current_dir);
1828 callback_arg = g_new (HistoryCallbackArg, 1);
1829 callback_arg->menu_item = menu_item;
1831 /* since the autocompletion gets confused if you don't
1832 * supply a trailing '/' on a dir entry, set the full
1833 * (current) path to "" which just refreshes the filesel */
1836 callback_arg->directory = g_strdup ("");
1840 callback_arg->directory = g_strdup (current_dir);
1843 fs->history_list = g_list_append (fs->history_list, callback_arg);
1845 gtk_signal_connect (GTK_OBJECT (menu_item), "activate",
1846 (GtkSignalFunc) gtk_file_selection_history_callback,
1848 gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
1849 gtk_widget_show (menu_item);
1853 gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown),
1855 g_free (current_dir);
1859 get_real_filename (gchar *filename,
1862 #ifdef G_WITH_CYGWIN
1863 /* Check to see if the selection was a drive selector */
1864 if (isalpha (filename[0]) && (filename[1] == ':'))
1866 /* It is... map it to a CYGWIN32 drive */
1867 gchar *temp_filename = g_strdup_printf ("//%c/", tolower (filename[0]));
1872 return temp_filename;
1876 #endif /* G_WITH_CYGWIN */
1880 gtk_file_selection_file_activate (GtkTreeView *tree_view,
1882 GtkTreeViewColumn *column,
1885 GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
1886 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
1890 gtk_tree_model_get_iter (model, &iter, path);
1891 gtk_tree_model_get (model, &iter, FILE_COLUMN, &filename, -1);
1892 filename = get_real_filename (filename, TRUE);
1894 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1895 gtk_button_clicked (GTK_BUTTON (fs->ok_button));
1901 gtk_file_selection_dir_activate (GtkTreeView *tree_view,
1903 GtkTreeViewColumn *column,
1906 GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
1907 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
1911 gtk_tree_model_get_iter (model, &iter, path);
1912 gtk_tree_model_get (model, &iter, DIR_COLUMN, &filename, -1);
1913 gtk_file_selection_populate (fs, filename, FALSE, FALSE);
1917 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
1920 win32_gtk_add_drives_to_dir_list (GtkListStore *model)
1924 char formatBuffer[128];
1927 /* Get the drives string */
1928 GetLogicalDriveStrings (sizeof (buffer), buffer);
1930 /* Add the drives as necessary */
1932 while (*textPtr != '\0')
1934 /* Ignore floppies (?) */
1935 if ((tolower (textPtr[0]) != 'a') && (tolower (textPtr[0]) != 'b'))
1937 /* Build the actual displayable string */
1938 sprintf (formatBuffer, "%c:\\", toupper (textPtr[0]));
1940 /* Add to the list */
1941 gtk_list_store_append (model, &iter);
1942 gtk_list_store_set (model, &iter, DIR_COLUMN, formatBuffer, -1);
1944 textPtr += (strlen (textPtr) + 1);
1950 gtk_file_selection_populate (GtkFileSelection *fs,
1952 gboolean try_complete,
1953 gboolean reset_entry)
1955 CompletionState *cmpl_state;
1956 PossibleCompletion* poss;
1958 GtkListStore *dir_model;
1959 GtkListStore *file_model;
1961 gchar* rem_path = rel_path;
1963 gint did_recurse = FALSE;
1964 gint possible_count = 0;
1965 gint selection_index = -1;
1967 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1969 cmpl_state = (CompletionState*) fs->cmpl_state;
1970 poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
1972 if (!cmpl_state_okay (cmpl_state))
1974 /* Something went wrong. */
1975 gtk_file_selection_abort (fs);
1979 g_assert (cmpl_state->reference_dir);
1981 dir_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->dir_list)));
1982 file_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->file_list)));
1984 gtk_list_store_clear (dir_model);
1985 gtk_list_store_clear (file_model);
1987 /* Set the dir list to include ./ and ../ */
1988 gtk_list_store_append (dir_model, &iter);
1989 gtk_list_store_set (dir_model, &iter, DIR_COLUMN, "." G_DIR_SEPARATOR_S, -1);
1990 gtk_list_store_append (dir_model, &iter);
1991 gtk_list_store_set (dir_model, &iter, DIR_COLUMN, ".." G_DIR_SEPARATOR_S, -1);
1995 if (cmpl_is_a_completion (poss))
1997 possible_count += 1;
1999 filename = cmpl_this_completion (poss);
2001 if (cmpl_is_directory (poss))
2003 if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
2004 strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
2006 gtk_list_store_append (dir_model, &iter);
2007 gtk_list_store_set (dir_model, &iter, DIR_COLUMN, filename, -1);
2012 gtk_list_store_append (file_model, &iter);
2013 gtk_list_store_set (file_model, &iter, DIR_COLUMN, filename, -1);
2017 poss = cmpl_next_completion (cmpl_state);
2020 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
2021 /* For Windows, add drives as potential selections */
2022 win32_gtk_add_drives_to_dir_list (dir_model);
2025 /* File lists are set. */
2027 g_assert (cmpl_state->reference_dir);
2032 /* User is trying to complete filenames, so advance the user's input
2033 * string to the updated_text, which is the common leading substring
2034 * of all possible completions, and if its a directory attempt
2035 * attempt completions in it. */
2037 if (cmpl_updated_text (cmpl_state)[0])
2040 if (cmpl_updated_dir (cmpl_state))
2042 gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
2046 gtk_file_selection_populate (fs, dir_name, TRUE, TRUE);
2052 if (fs->selection_entry)
2053 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
2054 cmpl_updated_text (cmpl_state));
2059 selection_index = cmpl_last_valid_char (cmpl_state) -
2060 (strlen (rel_path) - strlen (rem_path));
2061 if (fs->selection_entry)
2062 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
2065 else if (reset_entry)
2067 if (fs->selection_entry)
2068 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2073 if (fs->selection_entry)
2074 gtk_entry_set_position (GTK_ENTRY (fs->selection_entry), selection_index);
2076 if (fs->selection_entry)
2078 sel_text = g_strconcat (_("Selection: "),
2079 cmpl_reference_position (cmpl_state),
2082 gtk_label_set_text (GTK_LABEL (fs->selection_text), sel_text);
2086 if (fs->history_pulldown)
2088 gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
2095 gtk_file_selection_abort (GtkFileSelection *fs)
2099 sprintf (err_buf, _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
2101 /* BEEP gdk_beep(); */
2103 if (fs->selection_entry)
2104 gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
2108 * gtk_file_selection_set_select_multiple:
2109 * @filesel: a #GtkFileSelection
2110 * @select_multiple: whether or not the user is allowed to select multiple
2111 * files in the file list.
2113 * Sets whether the user is allowed to select multiple files in the file list.
2114 * Use gtk_file_selection_get_selections () to get the list of selected files.
2117 gtk_file_selection_set_select_multiple (GtkFileSelection *filesel,
2118 gboolean select_multiple)
2120 GtkTreeSelection *sel;
2121 GtkSelectionMode mode;
2123 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
2125 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2127 mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE;
2129 if (mode != gtk_tree_selection_get_mode (sel))
2131 gtk_tree_selection_set_mode (sel, mode);
2133 g_object_notify (G_OBJECT (filesel), "select-multiple");
2138 * gtk_file_selection_get_select_multiple:
2139 * @filesel: a #GtkFileSelection
2141 * Determines whether or not the user is allowed to select multiple files in
2142 * the file list. See gtk_file_selection_set_select_multiple().
2144 * Return value: %TRUE if the user is allowed to select multiple files in the
2148 gtk_file_selection_get_select_multiple (GtkFileSelection *filesel)
2150 GtkTreeSelection *sel;
2152 g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), FALSE);
2154 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2155 return (gtk_tree_selection_get_mode (sel) == GTK_SELECTION_MULTIPLE);
2159 multiple_changed_foreach (GtkTreeModel *model,
2164 GPtrArray *names = data;
2167 gtk_tree_model_get (model, iter, FILE_COLUMN, &filename, -1);
2169 g_ptr_array_add (names, filename);
2173 free_selected_names (GPtrArray *names)
2177 for (i = 0; i < names->len; i++)
2178 g_free (g_ptr_array_index (names, i));
2180 g_ptr_array_free (names, TRUE);
2184 gtk_file_selection_file_changed (GtkTreeSelection *selection,
2187 GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
2188 GPtrArray *new_names;
2193 new_names = g_ptr_array_sized_new (8);
2195 gtk_tree_selection_selected_foreach (selection,
2196 multiple_changed_foreach,
2199 /* nothing selected */
2200 if (new_names->len == 0)
2202 g_ptr_array_free (new_names, TRUE);
2204 if (fs->selected_names != NULL)
2206 free_selected_names (fs->selected_names);
2207 fs->selected_names = NULL;
2210 goto maybe_clear_entry;
2213 if (new_names->len != 1)
2215 GPtrArray *old_names = fs->selected_names;
2217 if (old_names != NULL)
2219 /* A common case is selecting a range of files from top to bottom,
2220 * so quickly check for that to avoid looping over the entire list
2222 if (compare_filenames (g_ptr_array_index (old_names, old_names->len - 1),
2223 g_ptr_array_index (new_names, new_names->len - 1)) != 0)
2224 index = new_names->len - 1;
2227 gint i = 0, j = 0, cmp;
2229 /* do a quick diff, stopping at the first file not in the
2232 while (i < old_names->len && j < new_names->len)
2234 cmp = compare_filenames (g_ptr_array_index (old_names, i),
2235 g_ptr_array_index (new_names, j));
2252 /* we ran off the end of the old list */
2253 if (index == -1 && i < new_names->len)
2259 /* A phantom anchor still exists at the point where the last item
2260 * was selected, which is used for subsequent range selections.
2261 * So search up from there.
2263 if (compare_filenames (fs->last_selected,
2264 g_ptr_array_index (new_names, 0)) == 0)
2265 index = new_names->len - 1;
2273 if (fs->selected_names != NULL)
2274 free_selected_names (fs->selected_names);
2276 fs->selected_names = new_names;
2280 if (fs->last_selected != NULL)
2281 g_free (fs->last_selected);
2283 fs->last_selected = g_strdup (g_ptr_array_index (new_names, index));
2284 filename = get_real_filename (fs->last_selected, FALSE);
2286 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
2288 if (filename != fs->last_selected)
2296 entry = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
2297 if ((entry != NULL) && (fs->last_selected != NULL) &&
2298 (compare_filenames (entry, fs->last_selected) == 0))
2299 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2303 * gtk_file_selection_get_selections:
2304 * @filesel: a #GtkFileSelection
2306 * Retrieves the list of file selections the user has made in the dialog box.
2307 * This function is intended for use when the user can select multiple files
2308 * in the file list. The first file in the list is equivalent to what
2309 * gtk_file_selection_get_filename() would return.
2311 * The filenames are in the encoding of g_filename_from_utf8, which may or may
2312 * not be the same as that used by GTK+ (UTF-8). To convert to UTF-8, call
2313 * g_filename_to_utf8() on each string.
2315 * Return value: a newly-allocated %NULL-terminated array of strings. Use
2316 * g_strfreev() to free it.
2319 gtk_file_selection_get_selections (GtkFileSelection *filesel)
2323 gchar *filename, *dirname;
2324 gchar *current, *buf;
2327 g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), NULL);
2329 filename = g_strdup (gtk_file_selection_get_filename (filesel));
2331 if (strlen (filename) == 0)
2337 names = filesel->selected_names;
2340 selections = g_new (gchar *, names->len + 2);
2342 selections = g_new (gchar *, 2);
2345 selections[count++] = filename;
2349 dirname = g_path_get_dirname (filename);
2351 for (i = 0; i < names->len; i++)
2353 buf = g_filename_from_utf8 (g_ptr_array_index (names, i), -1,
2355 current = g_build_filename (dirname, buf, NULL);
2358 if (compare_filenames (current, filename) != 0)
2359 selections[count++] = current;
2365 selections[count] = NULL;
2370 /**********************************************************************/
2371 /* External Interface */
2372 /**********************************************************************/
2374 /* The four completion state selectors
2377 cmpl_updated_text (CompletionState *cmpl_state)
2379 return cmpl_state->updated_text;
2383 cmpl_updated_dir (CompletionState *cmpl_state)
2385 return cmpl_state->re_complete;
2389 cmpl_reference_position (CompletionState *cmpl_state)
2391 return cmpl_state->reference_dir->fullname;
2395 cmpl_last_valid_char (CompletionState *cmpl_state)
2397 return cmpl_state->last_valid_char;
2401 cmpl_completion_fullname (const gchar *text,
2402 CompletionState *cmpl_state)
2404 static char nothing[2] = "";
2406 if (!cmpl_state_okay (cmpl_state))
2410 else if (g_path_is_absolute (text))
2412 strcpy (cmpl_state->updated_text, text);
2415 else if (text[0] == '~')
2420 dir = open_user_dir (text, cmpl_state);
2424 /* spencer says just return ~something, so
2425 * for now just do it. */
2426 strcpy (cmpl_state->updated_text, text);
2431 strcpy (cmpl_state->updated_text, dir->fullname);
2433 slash = strchr (text, G_DIR_SEPARATOR);
2436 strcat (cmpl_state->updated_text, slash);
2442 strcpy (cmpl_state->updated_text, cmpl_state->reference_dir->fullname);
2443 if (cmpl_state->updated_text[strlen (cmpl_state->updated_text) - 1] != G_DIR_SEPARATOR)
2444 strcat (cmpl_state->updated_text, G_DIR_SEPARATOR_S);
2445 strcat (cmpl_state->updated_text, text);
2448 return cmpl_state->updated_text;
2451 /* The three completion selectors
2454 cmpl_this_completion (PossibleCompletion* pc)
2460 cmpl_is_directory (PossibleCompletion* pc)
2462 return pc->is_directory;
2466 cmpl_is_a_completion (PossibleCompletion* pc)
2468 return pc->is_a_completion;
2471 /**********************************************************************/
2472 /* Construction, deletion */
2473 /**********************************************************************/
2475 static CompletionState*
2476 cmpl_init_state (void)
2478 gchar *sys_getcwd_buf;
2480 CompletionState *new_state;
2482 new_state = g_new (CompletionState, 1);
2484 /* g_get_current_dir() returns a string in the "system" charset */
2485 sys_getcwd_buf = g_get_current_dir ();
2486 utf8_cwd = g_filename_to_utf8 (sys_getcwd_buf, -1, NULL, NULL, NULL);
2487 g_free (sys_getcwd_buf);
2491 new_state->reference_dir = NULL;
2492 new_state->completion_dir = NULL;
2493 new_state->active_completion_dir = NULL;
2494 new_state->directory_storage = NULL;
2495 new_state->directory_sent_storage = NULL;
2496 new_state->last_valid_char = 0;
2497 new_state->updated_text = g_new (gchar, MAXPATHLEN);
2498 new_state->updated_text_alloc = MAXPATHLEN;
2499 new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
2500 new_state->the_completion.text_alloc = MAXPATHLEN;
2501 new_state->user_dir_name_buffer = NULL;
2502 new_state->user_directories = NULL;
2504 new_state->reference_dir = open_dir (utf8_cwd, new_state);
2506 if (!new_state->reference_dir)
2508 /* Directories changing from underneath us, grumble */
2509 strcpy (utf8_cwd, G_DIR_SEPARATOR_S);
2518 cmpl_free_dir_list (GList* dp0)
2524 free_dir (dp->data);
2532 cmpl_free_dir_sent_list (GList* dp0)
2538 free_dir_sent (dp->data);
2546 cmpl_free_state (CompletionState* cmpl_state)
2548 g_return_if_fail (cmpl_state != NULL);
2550 cmpl_free_dir_list (cmpl_state->directory_storage);
2551 cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
2553 if (cmpl_state->user_dir_name_buffer)
2554 g_free (cmpl_state->user_dir_name_buffer);
2555 if (cmpl_state->user_directories)
2556 g_free (cmpl_state->user_directories);
2557 if (cmpl_state->the_completion.text)
2558 g_free (cmpl_state->the_completion.text);
2559 if (cmpl_state->updated_text)
2560 g_free (cmpl_state->updated_text);
2562 g_free (cmpl_state);
2566 free_dir (CompletionDir* dir)
2568 g_free (dir->cmpl_text);
2569 g_free (dir->fullname);
2574 free_dir_sent (CompletionDirSent* sent)
2577 for (i = 0; i < sent->entry_count; i++)
2578 g_free (sent->entries[i].entry_name);
2579 g_free (sent->entries);
2584 prune_memory_usage (CompletionState *cmpl_state)
2586 GList* cdsl = cmpl_state->directory_sent_storage;
2587 GList* cdl = cmpl_state->directory_storage;
2591 for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
2596 cmpl_free_dir_sent_list (cdsl->next);
2600 cmpl_state->directory_storage = NULL;
2603 if (cdl->data == cmpl_state->reference_dir)
2604 cmpl_state->directory_storage = g_list_prepend (NULL, cdl->data);
2606 free_dir (cdl->data);
2613 /**********************************************************************/
2614 /* The main entrances. */
2615 /**********************************************************************/
2617 static PossibleCompletion*
2618 cmpl_completion_matches (gchar *text_to_complete,
2619 gchar **remaining_text,
2620 CompletionState *cmpl_state)
2623 PossibleCompletion *poss;
2625 prune_memory_usage (cmpl_state);
2627 g_assert (text_to_complete != NULL);
2629 cmpl_state->user_completion_index = -1;
2630 cmpl_state->last_completion_text = text_to_complete;
2631 cmpl_state->the_completion.text[0] = 0;
2632 cmpl_state->last_valid_char = 0;
2633 cmpl_state->updated_text_len = -1;
2634 cmpl_state->updated_text[0] = 0;
2635 cmpl_state->re_complete = FALSE;
2638 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2640 if (text_to_complete[0] == '~' && !first_slash)
2642 /* Text starts with ~ and there is no slash, show all the
2643 * home directory completions.
2645 poss = attempt_homedir_completion (text_to_complete, cmpl_state);
2647 update_cmpl (poss, cmpl_state);
2652 cmpl_state->reference_dir =
2653 open_ref_dir (text_to_complete, remaining_text, cmpl_state);
2655 if (!cmpl_state->reference_dir)
2658 cmpl_state->completion_dir =
2659 find_completion_dir (*remaining_text, remaining_text, cmpl_state);
2661 cmpl_state->last_valid_char = *remaining_text - text_to_complete;
2663 if (!cmpl_state->completion_dir)
2666 cmpl_state->completion_dir->cmpl_index = -1;
2667 cmpl_state->completion_dir->cmpl_parent = NULL;
2668 cmpl_state->completion_dir->cmpl_text = g_strdup (*remaining_text);
2670 cmpl_state->active_completion_dir = cmpl_state->completion_dir;
2672 cmpl_state->reference_dir = cmpl_state->completion_dir;
2674 poss = attempt_file_completion (cmpl_state);
2676 update_cmpl (poss, cmpl_state);
2681 static PossibleCompletion*
2682 cmpl_next_completion (CompletionState* cmpl_state)
2684 PossibleCompletion* poss = NULL;
2686 cmpl_state->the_completion.text[0] = 0;
2689 if (cmpl_state->user_completion_index >= 0)
2690 poss = attempt_homedir_completion (cmpl_state->last_completion_text, cmpl_state);
2692 poss = attempt_file_completion (cmpl_state);
2694 poss = attempt_file_completion (cmpl_state);
2697 update_cmpl (poss, cmpl_state);
2702 /**********************************************************************/
2703 /* Directory Operations */
2704 /**********************************************************************/
2706 /* Open the directory where completion will begin from, if possible. */
2707 static CompletionDir*
2708 open_ref_dir (gchar *text_to_complete,
2709 gchar **remaining_text,
2710 CompletionState *cmpl_state)
2713 CompletionDir *new_dir;
2715 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2717 #ifdef G_WITH_CYGWIN
2718 if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
2721 sprintf (root_dir, "//%c", text_to_complete[2]);
2723 new_dir = open_dir (root_dir, cmpl_state);
2726 *remaining_text = text_to_complete + 4;
2734 else if (text_to_complete[0] == '~')
2736 new_dir = open_user_dir (text_to_complete, cmpl_state);
2741 *remaining_text = first_slash + 1;
2743 *remaining_text = text_to_complete + strlen (text_to_complete);
2751 else if (g_path_is_absolute (text_to_complete) || !cmpl_state->reference_dir)
2753 gchar *tmp = g_strdup (text_to_complete);
2757 while (*p && *p != '*' && *p != '?')
2761 p = strrchr (tmp, G_DIR_SEPARATOR);
2769 new_dir = open_dir (tmp, cmpl_state);
2772 *remaining_text = text_to_complete +
2773 ((p == tmp + 1) ? (p - tmp) : (p + 1 - tmp));
2777 /* If no possible candidates, use the cwd */
2778 gchar *sys_curdir = g_get_current_dir ();
2779 gchar *utf8_curdir = g_filename_to_utf8 (sys_curdir, -1, NULL, NULL, NULL);
2781 g_free (sys_curdir);
2783 new_dir = open_dir (utf8_curdir, cmpl_state);
2786 *remaining_text = text_to_complete;
2788 g_free (utf8_curdir);
2795 *remaining_text = text_to_complete;
2797 new_dir = open_dir (cmpl_state->reference_dir->fullname, cmpl_state);
2802 new_dir->cmpl_index = -1;
2803 new_dir->cmpl_parent = NULL;
2811 /* open a directory by user name */
2812 static CompletionDir*
2813 open_user_dir (const gchar *text_to_complete,
2814 CompletionState *cmpl_state)
2816 CompletionDir *result;
2820 g_assert (text_to_complete && text_to_complete[0] == '~');
2822 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2825 cmp_len = first_slash - text_to_complete - 1;
2827 cmp_len = strlen (text_to_complete + 1);
2832 const gchar *homedir = g_get_home_dir ();
2833 gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
2836 result = open_dir (utf8_homedir, cmpl_state);
2840 g_free (utf8_homedir);
2845 gchar* copy = g_new (char, cmp_len + 1);
2849 strncpy (copy, text_to_complete + 1, cmp_len);
2851 pwd = getpwnam (copy);
2858 utf8_dir = g_filename_to_utf8 (pwd->pw_dir, -1, NULL, NULL, NULL);
2859 result = open_dir (utf8_dir, cmpl_state);
2867 /* open a directory relative the the current relative directory */
2868 static CompletionDir*
2869 open_relative_dir (gchar *dir_name,
2871 CompletionState *cmpl_state)
2873 CompletionDir *result;
2876 path = g_string_sized_new (dir->fullname_len + strlen (dir_name) + 10);
2877 g_string_assign (path, dir->fullname);
2879 if (dir->fullname_len > 1
2880 && path->str[dir->fullname_len - 1] != G_DIR_SEPARATOR)
2881 g_string_append_c (path, G_DIR_SEPARATOR);
2882 g_string_append (path, dir_name);
2884 result = open_dir (path->str, cmpl_state);
2886 g_string_free (path, TRUE);
2891 /* after the cache lookup fails, really open a new directory */
2892 static CompletionDirSent*
2893 open_new_dir (gchar *dir_name,
2895 gboolean stat_subdirs)
2897 CompletionDirSent *sent;
2901 gint entry_count = 0;
2904 struct stat ent_sbuf;
2906 gchar *sys_dir_name;
2908 sent = g_new (CompletionDirSent, 1);
2909 sent->mtime = sbuf->st_mtime;
2910 sent->inode = sbuf->st_ino;
2911 sent->device = sbuf->st_dev;
2913 path = g_string_sized_new (2*MAXPATHLEN + 10);
2915 sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
2918 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
2922 directory = g_dir_open (sys_dir_name, 0, &error);
2925 cmpl_errno = error->code; /* ??? */
2926 g_free (sys_dir_name);
2930 while ((dirent = g_dir_read_name (directory)) != NULL)
2933 sent->entries = g_new (CompletionDirEntry, entry_count);
2934 sent->entry_count = entry_count;
2936 g_dir_rewind (directory);
2938 for (i = 0; i < entry_count; i += 1)
2940 dirent = g_dir_read_name (directory);
2944 g_warning ("Failure reading folder '%s'", sys_dir_name);
2945 g_dir_close (directory);
2946 g_free (sys_dir_name);
2950 sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent, -1, NULL, NULL, NULL);
2951 if (!g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL))
2953 g_warning (_("The filename %s couldn't be converted to UTF-8. Try setting the environment variable G_BROKEN_FILENAMES."), dirent);
2957 g_string_assign (path, sys_dir_name);
2958 if (path->str[path->len-1] != G_DIR_SEPARATOR)
2960 g_string_append_c (path, G_DIR_SEPARATOR);
2962 g_string_append (path, dirent);
2966 /* Here we know path->str is a "system charset" string */
2967 if (stat (path->str, &ent_sbuf) >= 0 && S_ISDIR (ent_sbuf.st_mode))
2968 sent->entries[n_entries].is_dir = TRUE;
2970 /* stat may fail, and we don't mind, since it could be a
2971 * dangling symlink. */
2972 sent->entries[n_entries].is_dir = FALSE;
2975 sent->entries[n_entries].is_dir = 1;
2979 sent->entry_count = n_entries;
2981 g_free (sys_dir_name);
2982 g_string_free (path, TRUE);
2983 qsort (sent->entries, sent->entry_count, sizeof (CompletionDirEntry), compare_cmpl_dir);
2985 g_dir_close (directory);
2990 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
2993 check_dir (gchar *dir_name,
2994 struct stat *result,
2995 gboolean *stat_subdirs)
2997 /* A list of directories that we know only contain other directories.
2998 * Trying to stat every file in these directories would be very
3005 struct stat statbuf;
3006 } no_stat_dirs[] = {
3007 { "/afs", FALSE, { 0 } },
3008 { "/net", FALSE, { 0 } }
3011 static const gint n_no_stat_dirs = G_N_ELEMENTS (no_stat_dirs);
3012 static gboolean initialized = FALSE;
3013 gchar *sys_dir_name;
3019 for (i = 0; i < n_no_stat_dirs; i++)
3021 if (stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
3022 no_stat_dirs[i].present = TRUE;
3026 sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
3029 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3033 if (stat (sys_dir_name, result) < 0)
3035 g_free (sys_dir_name);
3039 g_free (sys_dir_name);
3041 *stat_subdirs = TRUE;
3042 for (i = 0; i < n_no_stat_dirs; i++)
3044 if (no_stat_dirs[i].present &&
3045 (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
3046 (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
3048 *stat_subdirs = FALSE;
3058 /* open a directory by absolute pathname */
3059 static CompletionDir*
3060 open_dir (gchar *dir_name,
3061 CompletionState *cmpl_state)
3064 gboolean stat_subdirs;
3065 CompletionDirSent *sent;
3068 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3069 if (!check_dir (dir_name, &sbuf, &stat_subdirs))
3072 cdsl = cmpl_state->directory_sent_storage;
3078 if (sent->inode == sbuf.st_ino &&
3079 sent->mtime == sbuf.st_mtime &&
3080 sent->device == sbuf.st_dev)
3081 return attach_dir (sent, dir_name, cmpl_state);
3086 stat_subdirs = TRUE;
3089 sent = open_new_dir (dir_name, &sbuf, stat_subdirs);
3093 cmpl_state->directory_sent_storage =
3094 g_list_prepend (cmpl_state->directory_sent_storage, sent);
3096 return attach_dir (sent, dir_name, cmpl_state);
3102 static CompletionDir*
3103 attach_dir (CompletionDirSent *sent,
3105 CompletionState *cmpl_state)
3107 CompletionDir* new_dir;
3109 new_dir = g_new (CompletionDir, 1);
3111 cmpl_state->directory_storage =
3112 g_list_prepend (cmpl_state->directory_storage, new_dir);
3114 new_dir->sent = sent;
3115 new_dir->fullname = g_strdup (dir_name);
3116 new_dir->fullname_len = strlen (dir_name);
3117 new_dir->cmpl_text = NULL;
3123 correct_dir_fullname (CompletionDir* cmpl_dir)
3125 gint length = strlen (cmpl_dir->fullname);
3126 gchar *first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3127 gchar *sys_filename;
3130 /* Does it end with /. (\.) ? */
3132 strcmp (cmpl_dir->fullname + length - 2, G_DIR_SEPARATOR_S ".") == 0)
3134 /* Is it just the root directory (on a drive) ? */
3135 if (cmpl_dir->fullname + length - 2 == first_slash)
3137 cmpl_dir->fullname[length - 1] = 0;
3138 cmpl_dir->fullname_len = length - 1;
3143 cmpl_dir->fullname[length - 2] = 0;
3147 /* Ends with /./ (\.\)? */
3148 else if (length >= 3 &&
3149 strcmp (cmpl_dir->fullname + length - 3,
3150 G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) == 0)
3151 cmpl_dir->fullname[length - 2] = 0;
3153 /* Ends with /.. (\..) ? */
3154 else if (length >= 3 &&
3155 strcmp (cmpl_dir->fullname + length - 3,
3156 G_DIR_SEPARATOR_S "..") == 0)
3158 /* Is it just /.. (X:\..)? */
3159 if (cmpl_dir->fullname + length - 3 == first_slash)
3161 cmpl_dir->fullname[length - 2] = 0;
3162 cmpl_dir->fullname_len = length - 2;
3166 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3169 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3173 if (stat (sys_filename, &sbuf) < 0)
3175 g_free (sys_filename);
3179 g_free (sys_filename);
3181 cmpl_dir->fullname[length - 3] = 0;
3183 if (!correct_parent (cmpl_dir, &sbuf))
3187 /* Ends with /../ (\..\)? */
3188 else if (length >= 4 &&
3189 strcmp (cmpl_dir->fullname + length - 4,
3190 G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) == 0)
3192 /* Is it just /../ (X:\..\)? */
3193 if (cmpl_dir->fullname + length - 4 == first_slash)
3195 cmpl_dir->fullname[length - 3] = 0;
3196 cmpl_dir->fullname_len = length - 3;
3200 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3203 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3207 if (stat (sys_filename, &sbuf) < 0)
3209 g_free (sys_filename);
3213 g_free (sys_filename);
3215 cmpl_dir->fullname[length - 4] = 0;
3217 if (!correct_parent (cmpl_dir, &sbuf))
3221 cmpl_dir->fullname_len = strlen (cmpl_dir->fullname);
3227 correct_parent (CompletionDir *cmpl_dir,
3234 gchar *sys_filename;
3237 last_slash = strrchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3238 g_assert (last_slash);
3239 first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3241 /* Clever (?) way to check for top-level directory that works also on
3242 * Win32, where there is a drive letter and colon prefixed...
3244 if (last_slash != first_slash)
3254 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3257 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3259 last_slash[0] = G_DIR_SEPARATOR;
3263 if (stat (sys_filename, &parbuf) < 0)
3265 g_free (sys_filename);
3268 last_slash[0] = G_DIR_SEPARATOR;
3271 g_free (sys_filename);
3273 #ifndef G_OS_WIN32 /* No inode numbers on Win32 */
3274 if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
3275 /* it wasn't a link */
3281 last_slash[0] = G_DIR_SEPARATOR;
3283 /* it was a link, have to figure it out the hard way */
3285 new_name = find_parent_dir_fullname (cmpl_dir->fullname);
3290 g_free (cmpl_dir->fullname);
3292 cmpl_dir->fullname = new_name;
3301 find_parent_dir_fullname (gchar* dirname)
3303 gchar *sys_orig_dir;
3308 sys_orig_dir = g_get_current_dir ();
3309 sys_dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
3312 g_free (sys_orig_dir);
3313 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3317 if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
3319 g_free (sys_dirname);
3320 g_free (sys_orig_dir);
3324 g_free (sys_dirname);
3326 sys_cwd = g_get_current_dir ();
3327 result = g_filename_to_utf8 (sys_cwd, -1, NULL, NULL, NULL);
3330 if (chdir (sys_orig_dir) != 0)
3333 g_free (sys_orig_dir);
3337 g_free (sys_orig_dir);
3343 /**********************************************************************/
3344 /* Completion Operations */
3345 /**********************************************************************/
3349 static PossibleCompletion*
3350 attempt_homedir_completion (gchar *text_to_complete,
3351 CompletionState *cmpl_state)
3355 if (!cmpl_state->user_dir_name_buffer &&
3356 !get_pwdb (cmpl_state))
3358 length = strlen (text_to_complete) - 1;
3360 cmpl_state->user_completion_index += 1;
3362 while (cmpl_state->user_completion_index < cmpl_state->user_directories_len)
3364 index = first_diff_index (text_to_complete + 1,
3365 cmpl_state->user_directories
3366 [cmpl_state->user_completion_index].login);
3373 if (cmpl_state->last_valid_char < (index + 1))
3374 cmpl_state->last_valid_char = index + 1;
3375 cmpl_state->user_completion_index += 1;
3379 cmpl_state->the_completion.is_a_completion = 1;
3380 cmpl_state->the_completion.is_directory = TRUE;
3382 append_completion_text ("~", cmpl_state);
3384 append_completion_text (cmpl_state->
3385 user_directories[cmpl_state->user_completion_index].login,
3388 return append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3391 if (text_to_complete[1]
3392 || cmpl_state->user_completion_index > cmpl_state->user_directories_len)
3394 cmpl_state->user_completion_index = -1;
3399 cmpl_state->user_completion_index += 1;
3400 cmpl_state->the_completion.is_a_completion = 1;
3401 cmpl_state->the_completion.is_directory = TRUE;
3403 return append_completion_text ("~" G_DIR_SEPARATOR_S, cmpl_state);
3409 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
3410 #define FOLD(c) (tolower(c))
3415 /* returns the index (>= 0) of the first differing character,
3416 * PATTERN_MATCH if the completion matches */
3418 first_diff_index (gchar *pat,
3423 while (*pat && *text && FOLD (*text) == FOLD (*pat))
3433 return PATTERN_MATCH;
3436 static PossibleCompletion*
3437 append_completion_text (gchar *text,
3438 CompletionState *cmpl_state)
3442 if (!cmpl_state->the_completion.text)
3445 len = strlen (text) + strlen (cmpl_state->the_completion.text) + 1;
3447 if (cmpl_state->the_completion.text_alloc > len)
3449 strcat (cmpl_state->the_completion.text, text);
3450 return &cmpl_state->the_completion;
3456 cmpl_state->the_completion.text_alloc = i;
3458 cmpl_state->the_completion.text = (gchar*) g_realloc (cmpl_state->the_completion.text, i);
3460 if (!cmpl_state->the_completion.text)
3464 strcat (cmpl_state->the_completion.text, text);
3465 return &cmpl_state->the_completion;
3469 static CompletionDir*
3470 find_completion_dir (gchar *text_to_complete,
3471 gchar **remaining_text,
3472 CompletionState *cmpl_state)
3474 gchar* first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
3475 CompletionDir* dir = cmpl_state->reference_dir;
3476 CompletionDir* next;
3477 *remaining_text = text_to_complete;
3481 gint len = first_slash - *remaining_text;
3483 gchar *found_name = NULL; /* Quiet gcc */
3485 gchar* pat_buf = g_new (gchar, len + 1);
3487 strncpy (pat_buf, *remaining_text, len);
3490 for (i = 0; i < dir->sent->entry_count; i += 1)
3492 if (dir->sent->entries[i].is_dir &&
3493 fnmatch (pat_buf, dir->sent->entries[i].entry_name,
3494 FNMATCH_FLAGS)!= FNM_NOMATCH)
3504 found_name = dir->sent->entries[i].entry_name;
3511 /* Perhaps we are trying to open an automount directory */
3512 found_name = pat_buf;
3515 next = open_relative_dir (found_name, dir, cmpl_state);
3523 next->cmpl_parent = dir;
3527 if (!correct_dir_fullname (dir))
3533 *remaining_text = first_slash + 1;
3534 first_slash = strchr (*remaining_text, G_DIR_SEPARATOR);
3543 update_cmpl (PossibleCompletion *poss,
3544 CompletionState *cmpl_state)
3548 if (!poss || !cmpl_is_a_completion (poss))
3551 cmpl_len = strlen (cmpl_this_completion (poss));
3553 if (cmpl_state->updated_text_alloc < cmpl_len + 1)
3555 cmpl_state->updated_text =
3556 (gchar*)g_realloc (cmpl_state->updated_text,
3557 cmpl_state->updated_text_alloc);
3558 cmpl_state->updated_text_alloc = 2*cmpl_len;
3561 if (cmpl_state->updated_text_len < 0)
3563 strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3564 cmpl_state->updated_text_len = cmpl_len;
3565 cmpl_state->re_complete = cmpl_is_directory (poss);
3567 else if (cmpl_state->updated_text_len == 0)
3569 cmpl_state->re_complete = FALSE;
3574 first_diff_index (cmpl_state->updated_text,
3575 cmpl_this_completion (poss));
3577 cmpl_state->re_complete = FALSE;
3579 if (first_diff == PATTERN_MATCH)
3582 if (first_diff > cmpl_state->updated_text_len)
3583 strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3585 cmpl_state->updated_text_len = first_diff;
3586 cmpl_state->updated_text[first_diff] = 0;
3590 static PossibleCompletion*
3591 attempt_file_completion (CompletionState *cmpl_state)
3593 gchar *pat_buf, *first_slash;
3594 CompletionDir *dir = cmpl_state->active_completion_dir;
3596 dir->cmpl_index += 1;
3598 if (dir->cmpl_index == dir->sent->entry_count)
3600 if (dir->cmpl_parent == NULL)
3602 cmpl_state->active_completion_dir = NULL;
3608 cmpl_state->active_completion_dir = dir->cmpl_parent;
3610 return attempt_file_completion (cmpl_state);
3614 g_assert (dir->cmpl_text);
3616 first_slash = strchr (dir->cmpl_text, G_DIR_SEPARATOR);
3620 gint len = first_slash - dir->cmpl_text;
3622 pat_buf = g_new (gchar, len + 1);
3623 strncpy (pat_buf, dir->cmpl_text, len);
3628 gint len = strlen (dir->cmpl_text);
3630 pat_buf = g_new (gchar, len + 2);
3631 strcpy (pat_buf, dir->cmpl_text);
3632 /* Don't append a * if the user entered one herself.
3633 * This way one can complete *.h and don't get matches
3634 * on any .help files, for instance.
3636 if (strchr (pat_buf, '*') == NULL)
3637 strcpy (pat_buf + len, "*");
3642 if (dir->sent->entries[dir->cmpl_index].is_dir)
3644 if (fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3645 FNMATCH_FLAGS) != FNM_NOMATCH)
3647 CompletionDir* new_dir;
3649 new_dir = open_relative_dir (dir->sent->entries[dir->cmpl_index].entry_name,
3658 new_dir->cmpl_parent = dir;
3660 new_dir->cmpl_index = -1;
3661 new_dir->cmpl_text = g_strdup (first_slash + 1);
3663 cmpl_state->active_completion_dir = new_dir;
3666 return attempt_file_completion (cmpl_state);
3671 return attempt_file_completion (cmpl_state);
3677 return attempt_file_completion (cmpl_state);
3682 if (dir->cmpl_parent != NULL)
3684 append_completion_text (dir->fullname +
3685 strlen (cmpl_state->completion_dir->fullname) + 1,
3687 append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3690 append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
3692 cmpl_state->the_completion.is_a_completion =
3693 fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name,
3694 FNMATCH_FLAGS) != FNM_NOMATCH;
3696 cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
3697 if (dir->sent->entries[dir->cmpl_index].is_dir)
3698 append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3701 return &cmpl_state->the_completion;
3708 get_pwdb (CompletionState* cmpl_state)
3710 struct passwd *pwd_ptr;
3713 gint len = 0, i, count = 0;
3715 if (cmpl_state->user_dir_name_buffer)
3719 while ((pwd_ptr = getpwent ()) != NULL)
3721 utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3722 len += strlen (utf8);
3724 utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3725 len += strlen (utf8);
3733 cmpl_state->user_dir_name_buffer = g_new (gchar, len);
3734 cmpl_state->user_directories = g_new (CompletionUserDir, count);
3735 cmpl_state->user_directories_len = count;
3737 buf_ptr = cmpl_state->user_dir_name_buffer;
3739 for (i = 0; i < count; i += 1)
3741 pwd_ptr = getpwent ();
3748 utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3749 strcpy (buf_ptr, utf8);
3752 cmpl_state->user_directories[i].login = buf_ptr;
3754 buf_ptr += strlen (buf_ptr);
3757 utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3758 strcpy (buf_ptr, utf8);
3761 cmpl_state->user_directories[i].homedir = buf_ptr;
3763 buf_ptr += strlen (buf_ptr);
3767 qsort (cmpl_state->user_directories,
3768 cmpl_state->user_directories_len,
3769 sizeof (CompletionUserDir),
3778 if (cmpl_state->user_dir_name_buffer)
3779 g_free (cmpl_state->user_dir_name_buffer);
3780 if (cmpl_state->user_directories)
3781 g_free (cmpl_state->user_directories);
3783 cmpl_state->user_dir_name_buffer = NULL;
3784 cmpl_state->user_directories = NULL;
3790 compare_user_dir (const void *a,
3793 return strcmp ((((CompletionUserDir*)a))->login,
3794 (((CompletionUserDir*)b))->login);
3800 compare_cmpl_dir (const void *a,
3803 return compare_filenames ((((CompletionDirEntry*)a))->entry_name,
3804 (((CompletionDirEntry*)b))->entry_name);
3808 cmpl_state_okay (CompletionState* cmpl_state)
3810 return cmpl_state && cmpl_state->reference_dir;
3814 cmpl_strerror (gint err)
3816 if (err == CMPL_ERRNO_TOO_LONG)
3817 return _("Name too long");
3818 else if (err == CMPL_ERRNO_DID_NOT_CONVERT)
3819 return _("Couldn't convert filename");
3821 return g_strerror (err);