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 etc */
46 #include <glib/gstdio.h>
48 #if defined(G_PLATFORM_WIN32)
53 #endif /* G_PLATFORM_WIN32 */
55 #include "gdk/gdkkeysyms.h"
56 #include "gtkbutton.h"
57 #include "gtkcellrenderertext.h"
59 #include "gtkfilesel.h"
64 #include "gtkliststore.h"
66 #include "gtkprivate.h"
67 #include "gtkscrolledwindow.h"
69 #include "gtktreeselection.h"
70 #include "gtktreeview.h"
73 #include "gtkmenuitem.h"
74 #include "gtkdialog.h"
75 #include "gtkmessagedialog.h"
77 #include "gtkeventbox.h"
79 #undef GTK_DISABLE_DEPRECATED
80 #include "gtkoptionmenu.h"
83 #include "gtkhpaned.h"
91 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
93 #endif /* G_OS_WIN32 */
96 #include <sys/cygwin.h> /* For cygwin_conv_to_posix_path */
99 #define DIR_LIST_WIDTH 180
100 #define DIR_LIST_HEIGHT 180
101 #define FILE_LIST_WIDTH 180
102 #define FILE_LIST_HEIGHT 180
104 /* The Hurd doesn't define either PATH_MAX or MAXPATHLEN, so we put this
105 * in here, since the rest of the code in the file does require some
110 # define MAXPATHLEN PATH_MAX
112 # define MAXPATHLEN 2048
116 /* I've put this here so it doesn't get confused with the
117 * file completion interface */
118 typedef struct _HistoryCallbackArg HistoryCallbackArg;
120 struct _HistoryCallbackArg
123 GtkWidget *menu_item;
127 typedef struct _CompletionState CompletionState;
128 typedef struct _CompletionDir CompletionDir;
129 typedef struct _CompletionDirSent CompletionDirSent;
130 typedef struct _CompletionDirEntry CompletionDirEntry;
131 typedef struct _CompletionUserDir CompletionUserDir;
132 typedef struct _PossibleCompletion PossibleCompletion;
134 /* Non-external file completion decls and structures */
136 /* A contant telling PRCS how many directories to cache. Its actually
137 * kept in a list, so the geometry isn't important. */
138 #define CMPL_DIRECTORY_CACHE_SIZE 10
140 /* A constant used to determine whether a substring was an exact
141 * match by first_diff_index()
143 #define PATTERN_MATCH -1
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
153 #ifndef G_PLATFORM_WIN32
160 struct _CompletionDirEntry *entries;
163 struct _CompletionDir
165 CompletionDirSent *sent;
170 struct _CompletionDir *cmpl_parent;
175 /* This structure contains pairs of directory entry names with a flag saying
176 * whether or not they are a valid directory. NOTE: This information is used
177 * to provide the caller with information about whether to update its completions
178 * or try to open a file. Since directories are cached by the directory mtime,
179 * a symlink which points to an invalid file (which will not be a directory),
180 * will not be reevaluated if that file is created, unless the containing
181 * directory is touched. I consider this case to be worth ignoring (josh).
183 struct _CompletionDirEntry
190 struct _CompletionUserDir
196 struct _PossibleCompletion
198 /* accessible fields, all are accessed externally by functions
202 gint is_a_completion;
203 gboolean is_directory;
210 struct _CompletionState
212 gint last_valid_char;
214 gint updated_text_len;
215 gint updated_text_alloc;
216 gboolean re_complete;
218 gchar *user_dir_name_buffer;
219 gint user_directories_len;
221 gchar *last_completion_text;
223 gint user_completion_index; /* if >= 0, currently completing ~user */
225 struct _CompletionDir *completion_dir; /* directory completing from */
226 struct _CompletionDir *active_completion_dir;
228 struct _PossibleCompletion the_completion;
230 struct _CompletionDir *reference_dir; /* initial directory */
232 GList* directory_storage;
233 GList* directory_sent_storage;
235 struct _CompletionUserDir *user_directories;
253 /* File completion functions which would be external, were they used
254 * outside of this file.
257 static CompletionState* cmpl_init_state (void);
258 static void cmpl_free_state (CompletionState *cmpl_state);
259 static gint cmpl_state_okay (CompletionState* cmpl_state);
260 static const gchar* cmpl_strerror (gint);
262 static PossibleCompletion* cmpl_completion_matches(gchar *text_to_complete,
263 gchar **remaining_text,
264 CompletionState *cmpl_state);
266 /* Returns a name for consideration, possibly a completion, this name
267 * will be invalid after the next call to cmpl_next_completion.
269 static char* cmpl_this_completion (PossibleCompletion*);
271 /* True if this completion matches the given text. Otherwise, this
272 * output can be used to have a list of non-completions.
274 static gint cmpl_is_a_completion (PossibleCompletion*);
276 /* True if the completion is a directory
278 static gboolean cmpl_is_directory (PossibleCompletion*);
280 /* Obtains the next completion, or NULL
282 static PossibleCompletion* cmpl_next_completion (CompletionState*);
284 /* Updating completions: the return value of cmpl_updated_text() will
285 * be text_to_complete completed as much as possible after the most
286 * recent call to cmpl_completion_matches. For the present
287 * application, this is the suggested replacement for the user's input
288 * string. You must CALL THIS AFTER ALL cmpl_text_completions have
291 static gchar* cmpl_updated_text (CompletionState* cmpl_state);
293 /* After updating, to see if the completion was a directory, call
294 * this. If it was, you should consider re-calling completion_matches.
296 static gboolean cmpl_updated_dir (CompletionState* cmpl_state);
298 /* Current location: if using file completion, return the current
299 * directory, from which file completion begins. More specifically,
300 * the cwd concatenated with all exact completions up to the last
301 * directory delimiter('/').
303 static gchar* cmpl_reference_position (CompletionState* cmpl_state);
306 /* This doesn't work currently and would require changes
307 * to fnmatch.c to get working.
309 /* backing up: if cmpl_completion_matches returns NULL, you may query
310 * the index of the last completable character into cmpl_updated_text.
312 static gint cmpl_last_valid_char (CompletionState* cmpl_state);
315 /* When the user selects a non-directory, call cmpl_completion_fullname
316 * to get the full name of the selected file.
318 static gchar* cmpl_completion_fullname (const gchar*, CompletionState* cmpl_state);
321 /* Directory operations. */
322 static CompletionDir* open_ref_dir (gchar* text_to_complete,
323 gchar** remaining_text,
324 CompletionState* cmpl_state);
325 #ifndef G_PLATFORM_WIN32
326 static gboolean check_dir (gchar *dir_name,
328 gboolean *stat_subdirs);
330 static CompletionDir* open_dir (gchar* dir_name,
331 CompletionState* cmpl_state);
333 static CompletionDir* open_user_dir (const gchar* text_to_complete,
334 CompletionState *cmpl_state);
336 static CompletionDir* open_relative_dir (gchar* dir_name, CompletionDir* dir,
337 CompletionState *cmpl_state);
338 static CompletionDirSent* open_new_dir (gchar* dir_name,
340 gboolean stat_subdirs);
341 static gint correct_dir_fullname (CompletionDir* cmpl_dir);
342 static gint correct_parent (CompletionDir* cmpl_dir,
344 #ifndef G_PLATFORM_WIN32
345 static gchar* find_parent_dir_fullname (gchar* dirname);
347 static CompletionDir* attach_dir (CompletionDirSent* sent,
349 CompletionState *cmpl_state);
350 static void free_dir_sent (CompletionDirSent* sent);
351 static void free_dir (CompletionDir *dir);
352 static void prune_memory_usage(CompletionState *cmpl_state);
354 /* Completion operations */
356 static PossibleCompletion* attempt_homedir_completion(gchar* text_to_complete,
357 CompletionState *cmpl_state);
359 static PossibleCompletion* attempt_file_completion(CompletionState *cmpl_state);
360 static CompletionDir* find_completion_dir(gchar* text_to_complete,
361 gchar** remaining_text,
362 CompletionState* cmpl_state);
363 static PossibleCompletion* append_completion_text(gchar* text,
364 CompletionState* cmpl_state);
366 static gint get_pwdb(CompletionState* cmpl_state);
367 static gint compare_user_dir(const void* a, const void* b);
369 static gint first_diff_index(gchar* pat, gchar* text);
370 static gint compare_cmpl_dir(const void* a, const void* b);
371 static void update_cmpl(PossibleCompletion* poss,
372 CompletionState* cmpl_state);
374 static void gtk_file_selection_set_property (GObject *object,
378 static void gtk_file_selection_get_property (GObject *object,
382 static void gtk_file_selection_finalize (GObject *object);
383 static void gtk_file_selection_destroy (GtkObject *object);
384 static void gtk_file_selection_map (GtkWidget *widget);
385 static gint gtk_file_selection_key_press (GtkWidget *widget,
388 static gint gtk_file_selection_insert_text (GtkWidget *widget,
389 const gchar *new_text,
390 gint new_text_length,
393 static void gtk_file_selection_update_fileops (GtkFileSelection *filesel);
395 static void gtk_file_selection_file_activate (GtkTreeView *tree_view,
397 GtkTreeViewColumn *column,
399 static void gtk_file_selection_file_changed (GtkTreeSelection *selection,
401 static void gtk_file_selection_dir_activate (GtkTreeView *tree_view,
403 GtkTreeViewColumn *column,
406 static void gtk_file_selection_populate (GtkFileSelection *fs,
408 gboolean try_complete,
409 gboolean reset_entry);
410 static void gtk_file_selection_abort (GtkFileSelection *fs);
412 static void gtk_file_selection_update_history_menu (GtkFileSelection *fs,
415 static void gtk_file_selection_create_dir (GtkWidget *widget, gpointer data);
416 static void gtk_file_selection_delete_file (GtkWidget *widget, gpointer data);
417 static void gtk_file_selection_rename_file (GtkWidget *widget, gpointer data);
419 static void free_selected_names (GPtrArray *names);
421 #ifndef G_PLATFORM_WIN32
423 #define compare_utf8_filenames(a, b) strcmp(a, b)
424 #define compare_sys_filenames(a, b) strcmp(a, b)
429 compare_utf8_filenames (const gchar *a,
432 gchar *a_folded, *b_folded;
435 a_folded = g_utf8_strdown (a, -1);
436 b_folded = g_utf8_strdown (b, -1);
438 retval = strcmp (a_folded, b_folded);
447 compare_sys_filenames (const gchar *a,
450 gchar *a_utf8, *b_utf8;
453 a_utf8 = g_filename_to_utf8 (a, -1, NULL, NULL, NULL);
454 b_utf8 = g_filename_to_utf8 (b, -1, NULL, NULL, NULL);
456 retval = compare_utf8_filenames (a_utf8, b_utf8);
466 /* Saves errno when something cmpl does fails. */
467 static gint cmpl_errno;
471 * Take the path currently in the file selection
472 * entry field and translate as necessary from
473 * a Win32 style to Cygwin style path. For
474 * instance translate:
475 * x:\somepath\file.jpg
477 * /cygdrive/x/somepath/file.jpg
479 * Replace the path in the selection text field.
480 * Return a boolean value concerning whether a
481 * translation had to be made.
484 translate_win32_path (GtkFileSelection *filesel)
488 gchar newPath[MAX_PATH];
491 * Retrieve the current path
493 path = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
495 cygwin_conv_to_posix_path (path, newPath);
496 updated = (strcmp (path, newPath) != 0);
499 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), newPath);
505 G_DEFINE_TYPE (GtkFileSelection, gtk_file_selection, GTK_TYPE_DIALOG)
508 gtk_file_selection_class_init (GtkFileSelectionClass *class)
510 GObjectClass *gobject_class;
511 GtkObjectClass *object_class;
512 GtkWidgetClass *widget_class;
514 gobject_class = (GObjectClass*) class;
515 object_class = (GtkObjectClass*) class;
516 widget_class = (GtkWidgetClass*) class;
518 gobject_class->finalize = gtk_file_selection_finalize;
519 gobject_class->set_property = gtk_file_selection_set_property;
520 gobject_class->get_property = gtk_file_selection_get_property;
522 g_object_class_install_property (gobject_class,
524 g_param_spec_string ("filename",
526 P_("The currently selected filename"),
528 GTK_PARAM_READWRITE));
529 g_object_class_install_property (gobject_class,
531 g_param_spec_boolean ("show-fileops",
532 P_("Show file operations"),
533 P_("Whether buttons for creating/manipulating files should be displayed"),
535 GTK_PARAM_READWRITE));
536 g_object_class_install_property (gobject_class,
537 PROP_SELECT_MULTIPLE,
538 g_param_spec_boolean ("select-multiple",
539 P_("Select Multiple"),
540 P_("Whether to allow multiple files to be selected"),
542 GTK_PARAM_READWRITE));
543 object_class->destroy = gtk_file_selection_destroy;
544 widget_class->map = gtk_file_selection_map;
547 static void gtk_file_selection_set_property (GObject *object,
552 GtkFileSelection *filesel;
554 filesel = GTK_FILE_SELECTION (object);
559 gtk_file_selection_set_filename (filesel,
560 g_value_get_string (value));
562 case PROP_SHOW_FILEOPS:
563 if (g_value_get_boolean (value))
564 gtk_file_selection_show_fileop_buttons (filesel);
566 gtk_file_selection_hide_fileop_buttons (filesel);
568 case PROP_SELECT_MULTIPLE:
569 gtk_file_selection_set_select_multiple (filesel, g_value_get_boolean (value));
572 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
577 static void gtk_file_selection_get_property (GObject *object,
582 GtkFileSelection *filesel;
584 filesel = GTK_FILE_SELECTION (object);
589 g_value_set_string (value,
590 gtk_file_selection_get_filename(filesel));
593 case PROP_SHOW_FILEOPS:
594 /* This is a little bit hacky, but doing otherwise would require
595 * adding a field to the object.
597 g_value_set_boolean (value, (filesel->fileop_c_dir &&
598 filesel->fileop_del_file &&
599 filesel->fileop_ren_file));
601 case PROP_SELECT_MULTIPLE:
602 g_value_set_boolean (value, gtk_file_selection_get_select_multiple (filesel));
605 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
611 grab_default (GtkWidget *widget)
613 gtk_widget_grab_default (widget);
618 gtk_file_selection_init (GtkFileSelection *filesel)
620 GtkWidget *entry_vbox;
622 GtkWidget *list_hbox, *list_container;
623 GtkWidget *pulldown_hbox;
624 GtkWidget *scrolled_win;
630 GtkTreeViewColumn *column;
632 gtk_widget_push_composite_child ();
634 dialog = GTK_DIALOG (filesel);
636 filesel->cmpl_state = cmpl_init_state ();
638 /* The dialog-sized vertical box */
639 filesel->main_vbox = dialog->vbox;
640 gtk_container_set_border_width (GTK_CONTAINER (filesel), 10);
642 /* The horizontal box containing create, rename etc. buttons */
643 filesel->button_area = gtk_hbutton_box_new ();
644 gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel->button_area), GTK_BUTTONBOX_START);
645 gtk_box_set_spacing (GTK_BOX (filesel->button_area), 0);
646 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->button_area,
648 gtk_widget_show (filesel->button_area);
650 gtk_file_selection_show_fileop_buttons (filesel);
652 /* hbox for pulldown menu */
653 pulldown_hbox = gtk_hbox_new (TRUE, 5);
654 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), pulldown_hbox, FALSE, FALSE, 0);
655 gtk_widget_show (pulldown_hbox);
658 filesel->history_pulldown = gtk_option_menu_new ();
659 gtk_widget_show (filesel->history_pulldown);
660 gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown,
663 /* The horizontal box containing the directory and file listboxes */
665 spacer = gtk_hbox_new (FALSE, 0);
666 gtk_widget_set_size_request (spacer, -1, 5);
667 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), spacer, FALSE, FALSE, 0);
668 gtk_widget_show (spacer);
670 list_hbox = gtk_hbox_new (FALSE, 5);
671 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), list_hbox, TRUE, TRUE, 0);
672 gtk_widget_show (list_hbox);
674 list_container = g_object_new (GTK_TYPE_HPANED,
680 list_container = list_hbox;
682 spacer = gtk_hbox_new (FALSE, 0);
683 gtk_widget_set_size_request (spacer, -1, 5);
684 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), spacer, FALSE, FALSE, 0);
685 gtk_widget_show (spacer);
687 /* The directories list */
689 model = gtk_list_store_new (1, G_TYPE_STRING);
690 filesel->dir_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
691 g_object_unref (model);
693 column = gtk_tree_view_column_new_with_attributes (_("Folders"),
694 gtk_cell_renderer_text_new (),
697 label = gtk_label_new_with_mnemonic (_("Fol_ders"));
698 gtk_label_set_mnemonic_widget (GTK_LABEL (label), filesel->dir_list);
699 gtk_widget_show (label);
700 gtk_tree_view_column_set_widget (column, label);
701 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
702 gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->dir_list), column);
704 gtk_widget_set_size_request (filesel->dir_list,
705 DIR_LIST_WIDTH, DIR_LIST_HEIGHT);
706 g_signal_connect (filesel->dir_list, "row_activated",
707 G_CALLBACK (gtk_file_selection_dir_activate), filesel);
709 /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list)); */
711 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
712 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);
713 gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->dir_list);
714 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
715 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
716 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0);
717 if (GTK_IS_PANED (list_container))
718 gtk_paned_pack1 (GTK_PANED (list_container), scrolled_win, TRUE, TRUE);
720 gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
721 gtk_widget_show (filesel->dir_list);
722 gtk_widget_show (scrolled_win);
725 model = gtk_list_store_new (1, G_TYPE_STRING);
726 filesel->file_list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (model));
727 g_object_unref (model);
729 column = gtk_tree_view_column_new_with_attributes (_("Files"),
730 gtk_cell_renderer_text_new (),
733 label = gtk_label_new_with_mnemonic (_("_Files"));
734 gtk_label_set_mnemonic_widget (GTK_LABEL (label), filesel->file_list);
735 gtk_widget_show (label);
736 gtk_tree_view_column_set_widget (column, label);
737 gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
738 gtk_tree_view_append_column (GTK_TREE_VIEW (filesel->file_list), column);
740 gtk_widget_set_size_request (filesel->file_list,
741 FILE_LIST_WIDTH, FILE_LIST_HEIGHT);
742 g_signal_connect (filesel->file_list, "row_activated",
743 G_CALLBACK (gtk_file_selection_file_activate), filesel);
744 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list)), "changed",
745 G_CALLBACK (gtk_file_selection_file_changed), filesel);
747 /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list)); */
749 scrolled_win = gtk_scrolled_window_new (NULL, NULL);
750 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win), GTK_SHADOW_IN);
751 gtk_container_add (GTK_CONTAINER (scrolled_win), filesel->file_list);
752 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
753 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
754 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win), 0);
755 gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
756 gtk_widget_show (filesel->file_list);
757 gtk_widget_show (scrolled_win);
759 /* action area for packing buttons into. */
760 filesel->action_area = gtk_hbox_new (TRUE, 0);
761 gtk_box_pack_start (GTK_BOX (filesel->main_vbox), filesel->action_area,
763 gtk_widget_show (filesel->action_area);
765 /* The OK/Cancel button area */
767 /* The Cancel button */
768 filesel->cancel_button = gtk_dialog_add_button (dialog,
770 GTK_RESPONSE_CANCEL);
772 filesel->ok_button = gtk_dialog_add_button (dialog,
776 gtk_dialog_set_alternative_button_order (dialog,
781 gtk_widget_grab_default (filesel->ok_button);
783 /* The selection entry widget */
784 entry_vbox = gtk_vbox_new (FALSE, 2);
785 gtk_box_pack_end (GTK_BOX (filesel->main_vbox), entry_vbox, FALSE, FALSE, 2);
786 gtk_widget_show (entry_vbox);
788 eventbox = gtk_event_box_new ();
789 filesel->selection_text = label = gtk_label_new ("");
790 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
791 gtk_container_add (GTK_CONTAINER (eventbox), label);
792 gtk_box_pack_start (GTK_BOX (entry_vbox), eventbox, FALSE, FALSE, 0);
793 gtk_widget_show (label);
794 gtk_widget_show (eventbox);
796 filesel->selection_entry = gtk_entry_new ();
797 g_signal_connect (filesel->selection_entry, "key_press_event",
798 G_CALLBACK (gtk_file_selection_key_press), filesel);
799 g_signal_connect (filesel->selection_entry, "insert_text",
800 G_CALLBACK (gtk_file_selection_insert_text), NULL);
801 g_signal_connect_swapped (filesel->selection_entry, "changed",
802 G_CALLBACK (gtk_file_selection_update_fileops), filesel);
803 g_signal_connect_swapped (filesel->selection_entry, "focus_in_event",
804 G_CALLBACK (grab_default),
806 g_signal_connect_swapped (filesel->selection_entry, "activate",
807 G_CALLBACK (gtk_button_clicked),
810 gtk_box_pack_start (GTK_BOX (entry_vbox), filesel->selection_entry, TRUE, TRUE, 0);
811 gtk_widget_show (filesel->selection_entry);
813 gtk_label_set_mnemonic_widget (GTK_LABEL (filesel->selection_text),
814 filesel->selection_entry);
816 if (!cmpl_state_okay (filesel->cmpl_state))
820 g_snprintf (err_buf, sizeof (err_buf), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
822 gtk_label_set_text (GTK_LABEL (filesel->selection_text), err_buf);
826 gtk_file_selection_populate (filesel, "", FALSE, TRUE);
829 gtk_widget_grab_focus (filesel->selection_entry);
831 gtk_widget_pop_composite_child ();
835 dnd_really_drop (GtkWidget *dialog, gint response_id, GtkFileSelection *fs)
839 if (response_id == GTK_RESPONSE_YES)
841 filename = g_object_get_data (G_OBJECT (dialog), "gtk-fs-dnd-filename");
843 gtk_file_selection_set_filename (fs, filename);
846 gtk_widget_destroy (dialog);
850 filenames_dropped (GtkWidget *widget,
851 GdkDragContext *context,
854 GtkSelectionData *selection_data,
859 char *filename = NULL;
861 const char *this_hostname;
862 GError *error = NULL;
864 uris = gtk_selection_data_get_uris (selection_data);
865 if (!uris || !uris[0])
871 filename = g_filename_from_uri (uris[0], &hostname, &error);
876 g_warning ("Error getting dropped filename: %s\n",
878 g_error_free (error);
882 this_hostname = g_get_host_name ();
884 if ((hostname == NULL) ||
885 (strcmp (hostname, this_hostname) == 0) ||
886 (strcmp (hostname, "localhost") == 0))
887 gtk_file_selection_set_filename (GTK_FILE_SELECTION (widget),
892 gchar *filename_utf8;
894 /* Conversion back to UTF-8 should always succeed for the result
895 * of g_filename_from_uri()
897 filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
898 g_assert (filename_utf8);
900 dialog = gtk_message_dialog_new (GTK_WINDOW (widget),
901 GTK_DIALOG_DESTROY_WITH_PARENT,
902 GTK_MESSAGE_QUESTION,
904 _("The file \"%s\" resides on another machine (called %s) and may not be available to this program.\n"
905 "Are you sure that you want to select it?"), filename_utf8, hostname);
906 g_free (filename_utf8);
908 g_object_set_data_full (G_OBJECT (dialog), I_("gtk-fs-dnd-filename"), g_strdup (filename), g_free);
910 g_signal_connect_data (dialog, "response",
911 (GCallback) dnd_really_drop,
914 gtk_widget_show (dialog);
922 filenames_drag_get (GtkWidget *widget,
923 GdkDragContext *context,
924 GtkSelectionData *selection_data,
927 GtkFileSelection *filesel)
930 gchar *filename_utf8;
932 file = gtk_file_selection_get_filename (filesel);
936 if (gtk_targets_include_uri (&selection_data->target, 1))
939 const char *hostname;
943 hostname = g_get_host_name ();
946 file_uri = g_filename_to_uri (file, hostname, &error);
949 g_warning ("Error getting filename: %s\n",
951 g_error_free (error);
957 gtk_selection_data_set_uris (selection_data, uris);
963 filename_utf8 = g_filename_to_utf8 (file, -1, NULL, NULL, NULL);
967 gtk_selection_data_set_text (selection_data, filename_utf8, -1);
968 g_free (filename_utf8);
972 file_selection_setup_dnd (GtkFileSelection *filesel)
976 gtk_drag_dest_set (GTK_WIDGET (filesel),
977 GTK_DEST_DEFAULT_ALL,
980 gtk_drag_dest_add_uri_targets (GTK_WIDGET (filesel));
982 g_signal_connect (filesel, "drag_data_received",
983 G_CALLBACK (filenames_dropped), NULL);
985 eventbox = gtk_widget_get_parent (filesel->selection_text);
986 gtk_drag_source_set (eventbox,
990 gtk_drag_source_add_uri_targets (eventbox);
991 gtk_drag_source_add_text_targets (eventbox);
993 g_signal_connect (eventbox, "drag_data_get",
994 G_CALLBACK (filenames_drag_get), filesel);
998 gtk_file_selection_new (const gchar *title)
1000 GtkFileSelection *filesel;
1002 filesel = g_object_new (GTK_TYPE_FILE_SELECTION, NULL);
1003 gtk_window_set_title (GTK_WINDOW (filesel), title);
1004 gtk_dialog_set_has_separator (GTK_DIALOG (filesel), FALSE);
1006 file_selection_setup_dnd (filesel);
1008 return GTK_WIDGET (filesel);
1012 gtk_file_selection_show_fileop_buttons (GtkFileSelection *filesel)
1014 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1016 /* delete, create directory, and rename */
1017 if (!filesel->fileop_c_dir)
1019 filesel->fileop_c_dir = gtk_button_new_with_mnemonic (_("_New Folder"));
1020 g_signal_connect (filesel->fileop_c_dir, "clicked",
1021 G_CALLBACK (gtk_file_selection_create_dir),
1023 gtk_box_pack_start (GTK_BOX (filesel->button_area),
1024 filesel->fileop_c_dir, TRUE, TRUE, 0);
1025 gtk_widget_show (filesel->fileop_c_dir);
1028 if (!filesel->fileop_del_file)
1030 filesel->fileop_del_file = gtk_button_new_with_mnemonic (_("De_lete File"));
1031 g_signal_connect (filesel->fileop_del_file, "clicked",
1032 G_CALLBACK (gtk_file_selection_delete_file),
1034 gtk_box_pack_start (GTK_BOX (filesel->button_area),
1035 filesel->fileop_del_file, TRUE, TRUE, 0);
1036 gtk_widget_show (filesel->fileop_del_file);
1039 if (!filesel->fileop_ren_file)
1041 filesel->fileop_ren_file = gtk_button_new_with_mnemonic (_("_Rename File"));
1042 g_signal_connect (filesel->fileop_ren_file, "clicked",
1043 G_CALLBACK (gtk_file_selection_rename_file),
1045 gtk_box_pack_start (GTK_BOX (filesel->button_area),
1046 filesel->fileop_ren_file, TRUE, TRUE, 0);
1047 gtk_widget_show (filesel->fileop_ren_file);
1050 gtk_file_selection_update_fileops (filesel);
1052 g_object_notify (G_OBJECT (filesel), "show-fileops");
1056 gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
1058 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1060 if (filesel->fileop_ren_file)
1062 gtk_widget_destroy (filesel->fileop_ren_file);
1063 filesel->fileop_ren_file = NULL;
1066 if (filesel->fileop_del_file)
1068 gtk_widget_destroy (filesel->fileop_del_file);
1069 filesel->fileop_del_file = NULL;
1072 if (filesel->fileop_c_dir)
1074 gtk_widget_destroy (filesel->fileop_c_dir);
1075 filesel->fileop_c_dir = NULL;
1077 g_object_notify (G_OBJECT (filesel), "show-fileops");
1083 * gtk_file_selection_set_filename:
1084 * @filesel: a #GtkFileSelection.
1085 * @filename: a string to set as the default file name.
1087 * Sets a default path for the file requestor. If @filename includes a
1088 * directory path, then the requestor will open with that path as its
1089 * current working directory.
1091 * This has the consequence that in order to open the requestor with a
1092 * working directory and an empty filename, @filename must have a trailing
1093 * directory separator.
1095 * The encoding of @filename is preferred GLib file name encoding, which
1096 * may not be UTF-8. See g_filename_from_utf8().
1099 gtk_file_selection_set_filename (GtkFileSelection *filesel,
1100 const gchar *filename)
1103 const char *name, *last_slash;
1104 char *filename_utf8;
1106 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1107 g_return_if_fail (filename != NULL);
1109 filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
1110 g_return_if_fail (filename_utf8 != NULL);
1112 last_slash = strrchr (filename_utf8, G_DIR_SEPARATOR);
1116 buf = g_strdup ("");
1117 name = filename_utf8;
1121 buf = g_strdup (filename_utf8);
1122 buf[last_slash - filename_utf8 + 1] = 0;
1123 name = last_slash + 1;
1126 gtk_file_selection_populate (filesel, buf, FALSE, TRUE);
1128 if (filesel->selection_entry)
1129 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), name);
1131 g_object_notify (G_OBJECT (filesel), "filename");
1133 g_free (filename_utf8);
1137 * gtk_file_selection_get_filename:
1138 * @filesel: a #GtkFileSelection
1140 * This function returns the selected filename in the GLib file name
1141 * encoding. To convert to UTF-8, call g_filename_to_utf8(). The
1142 * returned string points to a statically allocated buffer and should
1143 * be copied if you plan to keep it around.
1145 * If no file is selected then the selected directory path is returned.
1147 * Return value: currently-selected filename in the on-disk encoding.
1149 G_CONST_RETURN gchar*
1150 gtk_file_selection_get_filename (GtkFileSelection *filesel)
1152 static const gchar nothing[2] = "";
1153 static GString *something;
1157 g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), nothing);
1159 #ifdef G_WITH_CYGWIN
1160 translate_win32_path (filesel);
1162 text = gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry));
1165 gchar *fullname = cmpl_completion_fullname (text, filesel->cmpl_state);
1166 sys_filename = g_filename_from_utf8 (fullname, -1, NULL, NULL, NULL);
1171 something = g_string_new (sys_filename);
1173 g_string_assign (something, sys_filename);
1174 g_free (sys_filename);
1176 return something->str;
1183 gtk_file_selection_complete (GtkFileSelection *filesel,
1184 const gchar *pattern)
1186 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
1187 g_return_if_fail (pattern != NULL);
1189 if (filesel->selection_entry)
1190 gtk_entry_set_text (GTK_ENTRY (filesel->selection_entry), pattern);
1191 gtk_file_selection_populate (filesel, (gchar*) pattern, TRUE, TRUE);
1195 gtk_file_selection_destroy (GtkObject *object)
1197 GtkFileSelection *filesel;
1199 HistoryCallbackArg *callback_arg;
1201 g_return_if_fail (GTK_IS_FILE_SELECTION (object));
1203 filesel = GTK_FILE_SELECTION (object);
1205 if (filesel->fileop_dialog)
1207 gtk_widget_destroy (filesel->fileop_dialog);
1208 filesel->fileop_dialog = NULL;
1211 if (filesel->history_list)
1213 list = filesel->history_list;
1216 callback_arg = list->data;
1217 g_free (callback_arg->directory);
1218 g_free (callback_arg);
1221 g_list_free (filesel->history_list);
1222 filesel->history_list = NULL;
1225 if (filesel->cmpl_state)
1227 cmpl_free_state (filesel->cmpl_state);
1228 filesel->cmpl_state = NULL;
1231 if (filesel->selected_names)
1233 free_selected_names (filesel->selected_names);
1234 filesel->selected_names = NULL;
1237 if (filesel->last_selected)
1239 g_free (filesel->last_selected);
1240 filesel->last_selected = NULL;
1243 GTK_OBJECT_CLASS (gtk_file_selection_parent_class)->destroy (object);
1247 gtk_file_selection_map (GtkWidget *widget)
1249 GtkFileSelection *filesel = GTK_FILE_SELECTION (widget);
1251 /* Refresh the contents */
1252 gtk_file_selection_populate (filesel, "", FALSE, FALSE);
1254 GTK_WIDGET_CLASS (gtk_file_selection_parent_class)->map (widget);
1258 gtk_file_selection_finalize (GObject *object)
1260 GtkFileSelection *filesel = GTK_FILE_SELECTION (object);
1262 g_free (filesel->fileop_file);
1264 G_OBJECT_CLASS (gtk_file_selection_parent_class)->finalize (object);
1267 /* Begin file operations callbacks */
1270 gtk_file_selection_fileop_error (GtkFileSelection *fs,
1271 gchar *error_message)
1275 g_return_if_fail (error_message != NULL);
1278 dialog = gtk_message_dialog_new (GTK_WINDOW (fs),
1279 GTK_DIALOG_DESTROY_WITH_PARENT,
1282 "%s", error_message);
1284 /* yes, we free it */
1285 g_free (error_message);
1287 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1289 g_signal_connect_swapped (dialog, "response",
1290 G_CALLBACK (gtk_widget_destroy),
1293 gtk_widget_show (dialog);
1297 gtk_file_selection_fileop_destroy (GtkWidget *widget,
1300 GtkFileSelection *fs = data;
1302 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1304 fs->fileop_dialog = NULL;
1308 entry_is_empty (GtkEntry *entry)
1310 const gchar *text = gtk_entry_get_text (entry);
1312 return *text == '\0';
1316 gtk_file_selection_fileop_entry_changed (GtkEntry *entry,
1319 gtk_widget_set_sensitive (button, !entry_is_empty (entry));
1323 gtk_file_selection_create_dir_confirmed (GtkWidget *widget,
1326 GtkFileSelection *fs = data;
1327 const gchar *dirname;
1330 gchar *sys_full_path;
1332 GError *error = NULL;
1333 CompletionState *cmpl_state;
1335 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1337 dirname = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1338 cmpl_state = (CompletionState*) fs->cmpl_state;
1339 path = cmpl_reference_position (cmpl_state);
1341 full_path = g_strconcat (path, G_DIR_SEPARATOR_S, dirname, NULL);
1342 sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1345 if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1346 buf = g_strdup_printf (_("The folder name \"%s\" contains symbols that are not allowed in filenames"), dirname);
1348 buf = g_strdup_printf (_("Error creating folder '%s': %s"),
1349 dirname, error->message);
1350 gtk_file_selection_fileop_error (fs, buf);
1351 g_error_free (error);
1355 if (g_mkdir (sys_full_path, 0777) < 0)
1357 buf = g_strdup_printf (_("Error creating folder '%s': %s"),
1358 dirname, g_strerror (errno));
1359 gtk_file_selection_fileop_error (fs, buf);
1364 g_free (sys_full_path);
1366 gtk_widget_destroy (fs->fileop_dialog);
1367 gtk_file_selection_populate (fs, "", FALSE, FALSE);
1371 gtk_file_selection_create_dir (GtkWidget *widget,
1374 GtkFileSelection *fs = data;
1380 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1382 if (fs->fileop_dialog)
1386 dialog = gtk_dialog_new ();
1387 fs->fileop_dialog = dialog;
1388 g_signal_connect (dialog, "destroy",
1389 G_CALLBACK (gtk_file_selection_fileop_destroy),
1391 gtk_window_set_title (GTK_WINDOW (dialog), _("New Folder"));
1392 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1393 gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1395 /* If file dialog is grabbed, grab option dialog */
1396 /* When option dialog is closed, file dialog will be grabbed again */
1397 if (GTK_WINDOW (fs)->modal)
1398 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1400 vbox = gtk_vbox_new (FALSE, 0);
1401 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1402 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1404 gtk_widget_show( vbox);
1406 label = gtk_label_new_with_mnemonic (_("_Folder name:"));
1407 gtk_misc_set_alignment(GTK_MISC (label), 0.0, 0.0);
1408 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1409 gtk_widget_show (label);
1411 /* The directory entry widget */
1412 fs->fileop_entry = gtk_entry_new ();
1413 gtk_label_set_mnemonic_widget (GTK_LABEL (label), fs->fileop_entry);
1414 gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
1416 GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1417 gtk_entry_set_activates_default (GTK_ENTRY (fs->fileop_entry), TRUE);
1418 gtk_widget_show (fs->fileop_entry);
1421 button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1422 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1423 g_signal_connect_swapped (button, "clicked",
1424 G_CALLBACK (gtk_widget_destroy),
1427 gtk_widget_grab_focus (fs->fileop_entry);
1429 button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1430 _("C_reate"), GTK_RESPONSE_OK);
1431 gtk_widget_set_sensitive (button, FALSE);
1432 g_signal_connect (button, "clicked",
1433 G_CALLBACK (gtk_file_selection_create_dir_confirmed),
1435 g_signal_connect (fs->fileop_entry, "changed",
1436 G_CALLBACK (gtk_file_selection_fileop_entry_changed),
1439 gtk_widget_grab_default (button);
1441 gtk_widget_show (dialog);
1445 gtk_file_selection_delete_file_response (GtkDialog *dialog,
1449 GtkFileSelection *fs = data;
1450 CompletionState *cmpl_state;
1453 gchar *sys_full_path;
1454 GError *error = NULL;
1457 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1459 if (response_id != GTK_RESPONSE_OK)
1461 gtk_widget_destroy (GTK_WIDGET (dialog));
1465 cmpl_state = (CompletionState*) fs->cmpl_state;
1466 path = cmpl_reference_position (cmpl_state);
1468 full_path = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1469 sys_full_path = g_filename_from_utf8 (full_path, -1, NULL, NULL, &error);
1472 if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1473 buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
1476 buf = g_strdup_printf (_("Error deleting file '%s': %s"),
1477 fs->fileop_file, error->message);
1479 gtk_file_selection_fileop_error (fs, buf);
1480 g_error_free (error);
1484 if (g_unlink (sys_full_path) < 0)
1486 buf = g_strdup_printf (_("Error deleting file '%s': %s"),
1487 fs->fileop_file, g_strerror (errno));
1488 gtk_file_selection_fileop_error (fs, buf);
1493 g_free (sys_full_path);
1495 gtk_widget_destroy (fs->fileop_dialog);
1496 gtk_file_selection_populate (fs, "", FALSE, TRUE);
1500 gtk_file_selection_delete_file (GtkWidget *widget,
1503 GtkFileSelection *fs = data;
1505 const gchar *filename;
1507 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1509 if (fs->fileop_dialog)
1512 #ifdef G_WITH_CYGWIN
1513 translate_win32_path (fs);
1516 filename = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
1517 if (strlen (filename) < 1)
1520 g_free (fs->fileop_file);
1521 fs->fileop_file = g_strdup (filename);
1524 fs->fileop_dialog = dialog =
1525 gtk_message_dialog_new (GTK_WINDOW (fs),
1526 GTK_WINDOW (fs)->modal ? GTK_DIALOG_MODAL : 0,
1527 GTK_MESSAGE_QUESTION,
1529 _("Really delete file \"%s\"?"), filename);
1531 g_signal_connect (dialog, "destroy",
1532 G_CALLBACK (gtk_file_selection_fileop_destroy),
1534 gtk_window_set_title (GTK_WINDOW (dialog), _("Delete File"));
1535 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1538 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1539 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1540 GTK_STOCK_DELETE, GTK_RESPONSE_OK,
1543 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_CANCEL);
1545 g_signal_connect (dialog, "response",
1546 G_CALLBACK (gtk_file_selection_delete_file_response),
1549 gtk_widget_show (dialog);
1553 gtk_file_selection_rename_file_confirmed (GtkWidget *widget,
1556 GtkFileSelection *fs = data;
1560 gchar *new_filename;
1561 gchar *old_filename;
1562 gchar *sys_new_filename;
1563 gchar *sys_old_filename;
1564 CompletionState *cmpl_state;
1565 GError *error = NULL;
1567 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1569 file = gtk_entry_get_text (GTK_ENTRY (fs->fileop_entry));
1570 cmpl_state = (CompletionState*) fs->cmpl_state;
1571 path = cmpl_reference_position (cmpl_state);
1573 new_filename = g_strconcat (path, G_DIR_SEPARATOR_S, file, NULL);
1574 old_filename = g_strconcat (path, G_DIR_SEPARATOR_S, fs->fileop_file, NULL);
1576 sys_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, &error);
1579 if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1580 buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"), new_filename);
1582 buf = g_strdup_printf (_("Error renaming file to \"%s\": %s"),
1583 new_filename, error->message);
1584 gtk_file_selection_fileop_error (fs, buf);
1585 g_error_free (error);
1589 sys_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, &error);
1592 if (g_error_matches (error, G_CONVERT_ERROR, G_CONVERT_ERROR_ILLEGAL_SEQUENCE))
1593 buf = g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"), old_filename);
1595 buf = g_strdup_printf (_("Error renaming file \"%s\": %s"),
1596 old_filename, error->message);
1597 gtk_file_selection_fileop_error (fs, buf);
1598 g_error_free (error);
1602 if (g_rename (sys_old_filename, sys_new_filename) < 0)
1604 buf = g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
1605 sys_old_filename, sys_new_filename,
1606 g_strerror (errno));
1607 gtk_file_selection_fileop_error (fs, buf);
1611 gtk_file_selection_populate (fs, "", FALSE, FALSE);
1612 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), file);
1615 g_free (sys_old_filename);
1618 g_free (new_filename);
1619 g_free (old_filename);
1620 g_free (sys_new_filename);
1622 gtk_widget_destroy (fs->fileop_dialog);
1626 gtk_file_selection_rename_file (GtkWidget *widget,
1629 GtkFileSelection *fs = data;
1636 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1638 if (fs->fileop_dialog)
1641 g_free (fs->fileop_file);
1642 fs->fileop_file = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1643 if (strlen (fs->fileop_file) < 1)
1647 fs->fileop_dialog = dialog = gtk_dialog_new ();
1648 g_signal_connect (dialog, "destroy",
1649 G_CALLBACK (gtk_file_selection_fileop_destroy),
1651 gtk_window_set_title (GTK_WINDOW (dialog), _("Rename File"));
1652 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1653 gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (fs));
1655 /* If file dialog is grabbed, grab option dialog */
1656 /* When option dialog closed, file dialog will be grabbed again */
1657 if (GTK_WINDOW (fs)->modal)
1658 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
1660 vbox = gtk_vbox_new (FALSE, 0);
1661 gtk_container_set_border_width (GTK_CONTAINER (vbox), 8);
1662 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), vbox,
1664 gtk_widget_show(vbox);
1666 buf = g_strdup_printf (_("Rename file \"%s\" to:"), fs->fileop_file);
1667 label = gtk_label_new (buf);
1668 gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.0);
1669 gtk_box_pack_start (GTK_BOX (vbox), label, FALSE, FALSE, 5);
1670 gtk_widget_show (label);
1673 /* New filename entry */
1674 fs->fileop_entry = gtk_entry_new ();
1675 gtk_box_pack_start (GTK_BOX (vbox), fs->fileop_entry,
1677 GTK_WIDGET_SET_FLAGS (fs->fileop_entry, GTK_CAN_DEFAULT);
1678 gtk_entry_set_activates_default (GTK_ENTRY (fs->fileop_entry), TRUE);
1679 gtk_widget_show (fs->fileop_entry);
1681 gtk_entry_set_text (GTK_ENTRY (fs->fileop_entry), fs->fileop_file);
1682 gtk_editable_select_region (GTK_EDITABLE (fs->fileop_entry),
1683 0, strlen (fs->fileop_file));
1686 button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1687 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1688 g_signal_connect_swapped (button, "clicked",
1689 G_CALLBACK (gtk_widget_destroy),
1692 gtk_widget_grab_focus (fs->fileop_entry);
1694 button = gtk_dialog_add_button (GTK_DIALOG (dialog),
1695 _("_Rename"), GTK_RESPONSE_OK);
1696 g_signal_connect (button, "clicked",
1697 G_CALLBACK (gtk_file_selection_rename_file_confirmed),
1699 g_signal_connect (fs->fileop_entry, "changed",
1700 G_CALLBACK (gtk_file_selection_fileop_entry_changed),
1703 gtk_widget_grab_default (button);
1705 gtk_widget_show (dialog);
1709 gtk_file_selection_insert_text (GtkWidget *widget,
1710 const gchar *new_text,
1711 gint new_text_length,
1717 filename = g_filename_from_utf8 (new_text, new_text_length, NULL, NULL, NULL);
1721 gdk_display_beep (gtk_widget_get_display (widget));
1722 g_signal_stop_emission_by_name (widget, "insert_text");
1732 gtk_file_selection_update_fileops (GtkFileSelection *fs)
1736 if (!fs->selection_entry)
1739 sensitive = !entry_is_empty (GTK_ENTRY (fs->selection_entry));
1741 if (fs->fileop_del_file)
1742 gtk_widget_set_sensitive (fs->fileop_del_file, sensitive);
1744 if (fs->fileop_ren_file)
1745 gtk_widget_set_sensitive (fs->fileop_ren_file, sensitive);
1749 gtk_file_selection_key_press (GtkWidget *widget,
1753 GtkFileSelection *fs;
1756 g_return_val_if_fail (widget != NULL, FALSE);
1757 g_return_val_if_fail (event != NULL, FALSE);
1759 if ((event->keyval == GDK_Tab || event->keyval == GDK_KP_Tab) &&
1760 (event->state & gtk_accelerator_get_default_mod_mask ()) == 0)
1762 fs = GTK_FILE_SELECTION (user_data);
1763 #ifdef G_WITH_CYGWIN
1764 translate_win32_path (fs);
1766 text = g_strdup (gtk_entry_get_text (GTK_ENTRY (fs->selection_entry)));
1768 gtk_file_selection_populate (fs, text, TRUE, TRUE);
1779 gtk_file_selection_history_callback (GtkWidget *widget,
1782 GtkFileSelection *fs = data;
1783 HistoryCallbackArg *callback_arg;
1786 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1788 list = fs->history_list;
1791 callback_arg = list->data;
1793 if (callback_arg->menu_item == widget)
1795 gtk_file_selection_populate (fs, callback_arg->directory, FALSE, FALSE);
1804 gtk_file_selection_update_history_menu (GtkFileSelection *fs,
1805 gchar *current_directory)
1807 HistoryCallbackArg *callback_arg;
1808 GtkWidget *menu_item;
1814 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
1815 g_return_if_fail (current_directory != NULL);
1817 list = fs->history_list;
1819 if (fs->history_menu)
1822 callback_arg = list->data;
1823 g_free (callback_arg->directory);
1824 g_free (callback_arg);
1827 g_list_free (fs->history_list);
1828 fs->history_list = NULL;
1830 gtk_widget_destroy (fs->history_menu);
1833 fs->history_menu = gtk_menu_new ();
1835 current_dir = g_strdup (current_directory);
1837 dir_len = strlen (current_dir);
1839 for (i = dir_len; i >= 0; i--)
1841 /* the i == dir_len is to catch the full path for the first
1843 if ( (current_dir[i] == G_DIR_SEPARATOR) || (i == dir_len))
1845 /* another small hack to catch the full path */
1847 current_dir[i + 1] = '\0';
1848 #ifdef G_WITH_CYGWIN
1849 if (!strcmp (current_dir, "//"))
1852 menu_item = gtk_menu_item_new_with_label (current_dir);
1854 callback_arg = g_new (HistoryCallbackArg, 1);
1855 callback_arg->menu_item = menu_item;
1857 /* since the autocompletion gets confused if you don't
1858 * supply a trailing '/' on a dir entry, set the full
1859 * (current) path to "" which just refreshes the filesel */
1862 callback_arg->directory = g_strdup ("");
1866 callback_arg->directory = g_strdup (current_dir);
1869 fs->history_list = g_list_append (fs->history_list, callback_arg);
1871 g_signal_connect (menu_item, "activate",
1872 G_CALLBACK (gtk_file_selection_history_callback),
1874 gtk_menu_shell_append (GTK_MENU_SHELL (fs->history_menu), menu_item);
1875 gtk_widget_show (menu_item);
1879 gtk_option_menu_set_menu (GTK_OPTION_MENU (fs->history_pulldown),
1881 g_free (current_dir);
1885 get_real_filename (gchar *filename,
1888 #ifdef G_WITH_CYGWIN
1889 /* Check to see if the selection was a drive selector */
1890 if (isalpha (filename[0]) && (filename[1] == ':'))
1892 gchar temp_filename[MAX_PATH];
1895 cygwin_conv_to_posix_path (filename, temp_filename);
1897 /* we need trailing '/'. */
1898 len = strlen (temp_filename);
1899 if (len > 0 && temp_filename[len-1] != '/')
1901 temp_filename[len] = '/';
1902 temp_filename[len+1] = '\0';
1908 return g_strdup (temp_filename);
1910 #endif /* G_WITH_CYGWIN */
1915 gtk_file_selection_file_activate (GtkTreeView *tree_view,
1917 GtkTreeViewColumn *column,
1920 GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
1921 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
1925 gtk_tree_model_get_iter (model, &iter, path);
1926 gtk_tree_model_get (model, &iter, FILE_COLUMN, &filename, -1);
1927 filename = get_real_filename (filename, TRUE);
1928 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
1929 gtk_button_clicked (GTK_BUTTON (fs->ok_button));
1935 gtk_file_selection_dir_activate (GtkTreeView *tree_view,
1937 GtkTreeViewColumn *column,
1940 GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
1941 GtkTreeModel *model = gtk_tree_view_get_model (tree_view);
1945 gtk_tree_model_get_iter (model, &iter, path);
1946 gtk_tree_model_get (model, &iter, DIR_COLUMN, &filename, -1);
1947 filename = get_real_filename (filename, TRUE);
1948 gtk_file_selection_populate (fs, filename, FALSE, FALSE);
1952 #ifdef G_PLATFORM_WIN32
1955 win32_gtk_add_drives_to_dir_list (GtkListStore *model)
1959 char formatBuffer[128];
1962 /* Get the drives string */
1963 GetLogicalDriveStrings (sizeof (buffer), buffer);
1965 /* Add the drives as necessary */
1967 while (*textPtr != '\0')
1969 /* Ignore floppies (?) */
1970 if (GetDriveType (textPtr) != DRIVE_REMOVABLE)
1972 /* Build the actual displayable string */
1973 g_snprintf (formatBuffer, sizeof (formatBuffer), "%c:\\", toupper (textPtr[0]));
1975 /* Add to the list */
1976 gtk_list_store_append (model, &iter);
1977 gtk_list_store_set (model, &iter, DIR_COLUMN, formatBuffer, -1);
1979 textPtr += (strlen (textPtr) + 1);
1985 escape_underscores (const gchar *str)
1987 GString *result = g_string_new (NULL);
1991 g_string_append_c (result, '_');
1993 g_string_append_c (result, *str);
1997 return g_string_free (result, FALSE);
2001 gtk_file_selection_populate (GtkFileSelection *fs,
2003 gboolean try_complete,
2004 gboolean reset_entry)
2006 CompletionState *cmpl_state;
2007 PossibleCompletion* poss;
2009 GtkListStore *dir_model;
2010 GtkListStore *file_model;
2012 gchar* rem_path = rel_path;
2014 gint did_recurse = FALSE;
2015 gint possible_count = 0;
2017 g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
2019 cmpl_state = (CompletionState*) fs->cmpl_state;
2020 poss = cmpl_completion_matches (rel_path, &rem_path, cmpl_state);
2022 if (!cmpl_state_okay (cmpl_state))
2024 /* Something went wrong. */
2025 gtk_file_selection_abort (fs);
2029 g_assert (cmpl_state->reference_dir);
2031 dir_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->dir_list)));
2032 file_model = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs->file_list)));
2034 gtk_list_store_clear (dir_model);
2035 gtk_list_store_clear (file_model);
2037 /* Set the dir list to include ./ and ../ */
2038 gtk_list_store_append (dir_model, &iter);
2039 gtk_list_store_set (dir_model, &iter, DIR_COLUMN, "." G_DIR_SEPARATOR_S, -1);
2040 gtk_list_store_append (dir_model, &iter);
2041 gtk_list_store_set (dir_model, &iter, DIR_COLUMN, ".." G_DIR_SEPARATOR_S, -1);
2045 if (cmpl_is_a_completion (poss))
2047 possible_count += 1;
2049 filename = cmpl_this_completion (poss);
2051 if (cmpl_is_directory (poss))
2053 if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
2054 strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
2056 gtk_list_store_append (dir_model, &iter);
2057 gtk_list_store_set (dir_model, &iter, DIR_COLUMN, filename, -1);
2062 gtk_list_store_append (file_model, &iter);
2063 gtk_list_store_set (file_model, &iter, DIR_COLUMN, filename, -1);
2067 poss = cmpl_next_completion (cmpl_state);
2070 #ifdef G_PLATFORM_WIN32
2071 /* For Windows, add drives as potential selections */
2072 win32_gtk_add_drives_to_dir_list (dir_model);
2075 /* File lists are set. */
2077 g_assert (cmpl_state->reference_dir);
2082 /* User is trying to complete filenames, so advance the user's input
2083 * string to the updated_text, which is the common leading substring
2084 * of all possible completions, and if its a directory attempt
2085 * attempt completions in it. */
2087 if (cmpl_updated_text (cmpl_state)[0])
2090 if (cmpl_updated_dir (cmpl_state))
2092 gchar* dir_name = g_strdup (cmpl_updated_text (cmpl_state));
2096 gtk_file_selection_populate (fs, dir_name, TRUE, TRUE);
2102 if (fs->selection_entry)
2103 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry),
2104 cmpl_updated_text (cmpl_state));
2109 if (fs->selection_entry)
2110 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), rem_path);
2113 else if (reset_entry)
2115 if (fs->selection_entry)
2116 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2121 if (fs->selection_entry && try_complete)
2122 gtk_editable_set_position (GTK_EDITABLE (fs->selection_entry), -1);
2124 if (fs->selection_entry)
2126 char *escaped = escape_underscores (cmpl_reference_position (cmpl_state));
2127 sel_text = g_strconcat (_("_Selection: "), escaped, NULL);
2130 gtk_label_set_text_with_mnemonic (GTK_LABEL (fs->selection_text), sel_text);
2134 if (fs->history_pulldown)
2136 gtk_file_selection_update_history_menu (fs, cmpl_reference_position (cmpl_state));
2143 gtk_file_selection_abort (GtkFileSelection *fs)
2147 g_snprintf (err_buf, sizeof (err_buf), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno));
2149 /* BEEP gdk_beep(); */
2151 if (fs->selection_entry)
2152 gtk_label_set_text (GTK_LABEL (fs->selection_text), err_buf);
2156 * gtk_file_selection_set_select_multiple:
2157 * @filesel: a #GtkFileSelection
2158 * @select_multiple: whether or not the user is allowed to select multiple
2159 * files in the file list.
2161 * Sets whether the user is allowed to select multiple files in the file list.
2162 * Use gtk_file_selection_get_selections () to get the list of selected files.
2165 gtk_file_selection_set_select_multiple (GtkFileSelection *filesel,
2166 gboolean select_multiple)
2168 GtkTreeSelection *sel;
2169 GtkSelectionMode mode;
2171 g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
2173 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2175 mode = select_multiple ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE;
2177 if (mode != gtk_tree_selection_get_mode (sel))
2179 gtk_tree_selection_set_mode (sel, mode);
2181 g_object_notify (G_OBJECT (filesel), "select-multiple");
2186 * gtk_file_selection_get_select_multiple:
2187 * @filesel: a #GtkFileSelection
2189 * Determines whether or not the user is allowed to select multiple files in
2190 * the file list. See gtk_file_selection_set_select_multiple().
2192 * Return value: %TRUE if the user is allowed to select multiple files in the
2196 gtk_file_selection_get_select_multiple (GtkFileSelection *filesel)
2198 GtkTreeSelection *sel;
2200 g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), FALSE);
2202 sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel->file_list));
2203 return (gtk_tree_selection_get_mode (sel) == GTK_SELECTION_MULTIPLE);
2207 multiple_changed_foreach (GtkTreeModel *model,
2212 GPtrArray *names = data;
2215 gtk_tree_model_get (model, iter, FILE_COLUMN, &filename, -1);
2217 g_ptr_array_add (names, filename);
2221 free_selected_names (GPtrArray *names)
2225 for (i = 0; i < names->len; i++)
2226 g_free (g_ptr_array_index (names, i));
2228 g_ptr_array_free (names, TRUE);
2232 gtk_file_selection_file_changed (GtkTreeSelection *selection,
2235 GtkFileSelection *fs = GTK_FILE_SELECTION (user_data);
2236 GPtrArray *new_names;
2241 new_names = g_ptr_array_sized_new (8);
2243 gtk_tree_selection_selected_foreach (selection,
2244 multiple_changed_foreach,
2247 /* nothing selected */
2248 if (new_names->len == 0)
2250 g_ptr_array_free (new_names, TRUE);
2252 if (fs->selected_names != NULL)
2254 free_selected_names (fs->selected_names);
2255 fs->selected_names = NULL;
2258 goto maybe_clear_entry;
2261 if (new_names->len != 1)
2263 GPtrArray *old_names = fs->selected_names;
2265 if (old_names != NULL)
2267 /* A common case is selecting a range of files from top to bottom,
2268 * so quickly check for that to avoid looping over the entire list
2270 if (compare_utf8_filenames (g_ptr_array_index (old_names, old_names->len - 1),
2271 g_ptr_array_index (new_names, new_names->len - 1)) != 0)
2272 index = new_names->len - 1;
2275 gint i = 0, j = 0, cmp;
2277 /* do a quick diff, stopping at the first file not in the
2280 while (i < old_names->len && j < new_names->len)
2282 cmp = compare_utf8_filenames (g_ptr_array_index (old_names, i),
2283 g_ptr_array_index (new_names, j));
2300 /* we ran off the end of the old list */
2301 if (index == -1 && i < new_names->len)
2307 /* A phantom anchor still exists at the point where the last item
2308 * was selected, which is used for subsequent range selections.
2309 * So search up from there.
2311 if (fs->last_selected &&
2312 compare_utf8_filenames (fs->last_selected,
2313 g_ptr_array_index (new_names, 0)) == 0)
2314 index = new_names->len - 1;
2322 if (fs->selected_names != NULL)
2323 free_selected_names (fs->selected_names);
2325 fs->selected_names = new_names;
2329 g_free (fs->last_selected);
2331 fs->last_selected = g_strdup (g_ptr_array_index (new_names, index));
2332 filename = get_real_filename (fs->last_selected, FALSE);
2334 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), filename);
2336 if (filename != fs->last_selected)
2344 entry = gtk_entry_get_text (GTK_ENTRY (fs->selection_entry));
2345 if ((entry != NULL) && (fs->last_selected != NULL) &&
2346 (compare_utf8_filenames (entry, fs->last_selected) == 0))
2347 gtk_entry_set_text (GTK_ENTRY (fs->selection_entry), "");
2351 * gtk_file_selection_get_selections:
2352 * @filesel: a #GtkFileSelection
2354 * Retrieves the list of file selections the user has made in the dialog box.
2355 * This function is intended for use when the user can select multiple files
2358 * The filenames are in the GLib file name encoding. To convert to
2359 * UTF-8, call g_filename_to_utf8() on each string.
2361 * Return value: a newly-allocated %NULL-terminated array of strings. Use
2362 * g_strfreev() to free it.
2365 gtk_file_selection_get_selections (GtkFileSelection *filesel)
2369 gchar *filename, *dirname;
2370 gchar *current, *buf;
2372 gboolean unselected_entry;
2374 g_return_val_if_fail (GTK_IS_FILE_SELECTION (filesel), NULL);
2376 filename = g_strdup (gtk_file_selection_get_filename (filesel));
2378 if (strlen (filename) == 0)
2384 names = filesel->selected_names;
2387 selections = g_new (gchar *, names->len + 2);
2389 selections = g_new (gchar *, 2);
2392 unselected_entry = TRUE;
2396 dirname = g_path_get_dirname (filename);
2398 if ((names->len >= 1) &&
2399 (strcmp (gtk_entry_get_text (GTK_ENTRY (filesel->selection_entry)), "") == 0))
2400 { /* multiple files are selected and last selection was removed via ctrl click */
2402 dirname = g_strdup (filename); /* as gtk_file_selection_get_filename returns dir
2403 if no file is selected */
2404 unselected_entry = FALSE;
2407 for (i = 0; i < names->len; i++)
2409 buf = g_filename_from_utf8 (g_ptr_array_index (names, i), -1,
2411 current = g_build_filename (dirname, buf, NULL);
2414 selections[count++] = current;
2416 if (unselected_entry && compare_sys_filenames (current, filename) == 0)
2417 unselected_entry = FALSE;
2423 if (unselected_entry)
2424 selections[count++] = filename;
2428 selections[count] = NULL;
2433 /**********************************************************************/
2434 /* External Interface */
2435 /**********************************************************************/
2437 /* The four completion state selectors
2440 cmpl_updated_text (CompletionState *cmpl_state)
2442 return cmpl_state->updated_text;
2446 cmpl_updated_dir (CompletionState *cmpl_state)
2448 return cmpl_state->re_complete;
2452 cmpl_reference_position (CompletionState *cmpl_state)
2454 return cmpl_state->reference_dir->fullname;
2458 /* This doesn't work currently and would require changes
2459 * to fnmatch.c to get working.
2462 cmpl_last_valid_char (CompletionState *cmpl_state)
2464 return cmpl_state->last_valid_char;
2469 cmpl_completion_fullname (const gchar *text,
2470 CompletionState *cmpl_state)
2472 if (!cmpl_state_okay (cmpl_state))
2474 return g_strdup ("");
2476 else if (g_path_is_absolute (text))
2478 return g_strdup (text);
2481 else if (text[0] == '~')
2486 dir = open_user_dir (text, cmpl_state);
2490 slash = strchr (text, G_DIR_SEPARATOR);
2492 /* slash may be NULL, that works too */
2493 return g_strconcat (dir->fullname, slash, NULL);
2498 return g_build_filename (cmpl_state->reference_dir->fullname,
2503 /* The three completion selectors
2506 cmpl_this_completion (PossibleCompletion* pc)
2512 cmpl_is_directory (PossibleCompletion* pc)
2514 return pc->is_directory;
2518 cmpl_is_a_completion (PossibleCompletion* pc)
2520 return pc->is_a_completion;
2523 /**********************************************************************/
2524 /* Construction, deletion */
2525 /**********************************************************************/
2527 /* Get the nearest parent of the current directory for which
2528 * we can convert the filename into UTF-8. With paranoia.
2529 * Returns "." when all goes wrong.
2532 get_current_dir_utf8 (void)
2534 gchar *dir = g_get_current_dir ();
2535 gchar *dir_utf8 = NULL;
2541 dir_utf8 = g_filename_to_utf8 (dir, -1, NULL, NULL, NULL);
2545 last_slash = strrchr (dir, G_DIR_SEPARATOR);
2546 if (!last_slash) /* g_get_current_dir() wasn't absolute! */
2549 if (last_slash + 1 == g_path_skip_root (dir)) /* Parent directory is a root directory */
2551 if (last_slash[1] == '\0') /* Root misencoded! */
2554 last_slash[1] = '\0';
2557 last_slash[0] = '\0';
2559 g_assert (last_slash);
2564 return dir_utf8 ? dir_utf8 : g_strdup (".");
2567 static CompletionState*
2568 cmpl_init_state (void)
2571 CompletionState *new_state;
2574 new_state = g_new (CompletionState, 1);
2576 utf8_cwd = get_current_dir_utf8 ();
2580 new_state->reference_dir = NULL;
2581 new_state->completion_dir = NULL;
2582 new_state->active_completion_dir = NULL;
2583 new_state->directory_storage = NULL;
2584 new_state->directory_sent_storage = NULL;
2585 new_state->last_valid_char = 0;
2586 new_state->updated_text = g_new (gchar, MAXPATHLEN);
2587 new_state->updated_text_alloc = MAXPATHLEN;
2588 new_state->the_completion.text = g_new (gchar, MAXPATHLEN);
2589 new_state->the_completion.text_alloc = MAXPATHLEN;
2590 new_state->user_dir_name_buffer = NULL;
2591 new_state->user_directories = NULL;
2593 new_state->reference_dir = open_dir (utf8_cwd, new_state);
2595 if (!new_state->reference_dir)
2597 /* Directories changing from underneath us, grumble */
2598 strcpy (utf8_cwd, G_DIR_SEPARATOR_S);
2608 cmpl_free_dir_list (GList* dp0)
2614 free_dir (dp->data);
2622 cmpl_free_dir_sent_list (GList* dp0)
2628 free_dir_sent (dp->data);
2636 cmpl_free_state (CompletionState* cmpl_state)
2638 g_return_if_fail (cmpl_state != NULL);
2640 cmpl_free_dir_list (cmpl_state->directory_storage);
2641 cmpl_free_dir_sent_list (cmpl_state->directory_sent_storage);
2643 g_free (cmpl_state->user_dir_name_buffer);
2644 g_free (cmpl_state->user_directories);
2645 g_free (cmpl_state->the_completion.text);
2646 g_free (cmpl_state->updated_text);
2648 g_free (cmpl_state);
2652 free_dir (CompletionDir* dir)
2654 g_free (dir->cmpl_text);
2655 g_free (dir->fullname);
2660 free_dir_sent (CompletionDirSent* sent)
2663 for (i = 0; i < sent->entry_count; i++)
2665 g_free (sent->entries[i].entry_name);
2666 g_free (sent->entries[i].sort_key);
2668 g_free (sent->entries);
2673 prune_memory_usage (CompletionState *cmpl_state)
2675 GList* cdsl = cmpl_state->directory_sent_storage;
2676 GList* cdl = cmpl_state->directory_storage;
2680 for (; cdsl && len < CMPL_DIRECTORY_CACHE_SIZE; len += 1)
2685 cmpl_free_dir_sent_list (cdsl->next);
2689 cmpl_state->directory_storage = NULL;
2692 if (cdl->data == cmpl_state->reference_dir)
2693 cmpl_state->directory_storage = g_list_prepend (NULL, cdl->data);
2695 free_dir (cdl->data);
2702 /**********************************************************************/
2703 /* The main entrances. */
2704 /**********************************************************************/
2706 static PossibleCompletion*
2707 cmpl_completion_matches (gchar *text_to_complete,
2708 gchar **remaining_text,
2709 CompletionState *cmpl_state)
2714 PossibleCompletion *poss;
2716 prune_memory_usage (cmpl_state);
2718 g_assert (text_to_complete != NULL);
2720 cmpl_state->user_completion_index = -1;
2721 cmpl_state->last_completion_text = text_to_complete;
2722 cmpl_state->the_completion.text[0] = 0;
2723 cmpl_state->last_valid_char = 0;
2724 cmpl_state->updated_text_len = -1;
2725 cmpl_state->updated_text[0] = 0;
2726 cmpl_state->re_complete = FALSE;
2729 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2731 if (text_to_complete[0] == '~' && !first_slash)
2733 /* Text starts with ~ and there is no slash, show all the
2734 * home directory completions.
2736 poss = attempt_homedir_completion (text_to_complete, cmpl_state);
2738 update_cmpl (poss, cmpl_state);
2743 cmpl_state->reference_dir =
2744 open_ref_dir (text_to_complete, remaining_text, cmpl_state);
2746 if (!cmpl_state->reference_dir)
2749 cmpl_state->completion_dir =
2750 find_completion_dir (*remaining_text, remaining_text, cmpl_state);
2752 cmpl_state->last_valid_char = *remaining_text - text_to_complete;
2754 if (!cmpl_state->completion_dir)
2757 cmpl_state->completion_dir->cmpl_index = -1;
2758 cmpl_state->completion_dir->cmpl_parent = NULL;
2759 cmpl_state->completion_dir->cmpl_text = g_strdup (*remaining_text);
2761 cmpl_state->active_completion_dir = cmpl_state->completion_dir;
2763 cmpl_state->reference_dir = cmpl_state->completion_dir;
2765 poss = attempt_file_completion (cmpl_state);
2767 update_cmpl (poss, cmpl_state);
2772 static PossibleCompletion*
2773 cmpl_next_completion (CompletionState* cmpl_state)
2775 PossibleCompletion* poss = NULL;
2777 cmpl_state->the_completion.text[0] = 0;
2780 if (cmpl_state->user_completion_index >= 0)
2781 poss = attempt_homedir_completion (cmpl_state->last_completion_text, cmpl_state);
2783 poss = attempt_file_completion (cmpl_state);
2785 poss = attempt_file_completion (cmpl_state);
2788 update_cmpl (poss, cmpl_state);
2793 /**********************************************************************/
2794 /* Directory Operations */
2795 /**********************************************************************/
2797 /* Open the directory where completion will begin from, if possible. */
2798 static CompletionDir*
2799 open_ref_dir (gchar *text_to_complete,
2800 gchar **remaining_text,
2801 CompletionState *cmpl_state)
2804 CompletionDir *new_dir;
2806 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2808 #ifdef G_WITH_CYGWIN
2809 if (text_to_complete[0] == '/' && text_to_complete[1] == '/')
2812 g_snprintf (root_dir, sizeof (root_dir), "//%c", text_to_complete[2]);
2814 new_dir = open_dir (root_dir, cmpl_state);
2817 *remaining_text = text_to_complete + 4;
2825 else if (text_to_complete[0] == '~')
2827 new_dir = open_user_dir (text_to_complete, cmpl_state);
2832 *remaining_text = first_slash + 1;
2834 *remaining_text = text_to_complete + strlen (text_to_complete);
2842 else if (g_path_is_absolute (text_to_complete) || !cmpl_state->reference_dir)
2844 gchar *tmp = g_strdup (text_to_complete);
2848 while (*p && *p != '*' && *p != '?')
2852 p = strrchr (tmp, G_DIR_SEPARATOR);
2855 if (p + 1 == g_path_skip_root (tmp))
2859 new_dir = open_dir (tmp, cmpl_state);
2862 *remaining_text = text_to_complete +
2863 ((p == g_path_skip_root (tmp)) ? (p - tmp) : (p + 1 - tmp));
2867 /* If no possible candidates, use the cwd */
2868 gchar *utf8_curdir = get_current_dir_utf8 ();
2870 new_dir = open_dir (utf8_curdir, cmpl_state);
2873 *remaining_text = text_to_complete;
2875 g_free (utf8_curdir);
2882 *remaining_text = text_to_complete;
2884 new_dir = open_dir (cmpl_state->reference_dir->fullname, cmpl_state);
2889 new_dir->cmpl_index = -1;
2890 new_dir->cmpl_parent = NULL;
2898 /* open a directory by user name */
2899 static CompletionDir*
2900 open_user_dir (const gchar *text_to_complete,
2901 CompletionState *cmpl_state)
2903 CompletionDir *result;
2907 g_assert (text_to_complete && text_to_complete[0] == '~');
2909 first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
2912 cmp_len = first_slash - text_to_complete - 1;
2914 cmp_len = strlen (text_to_complete + 1);
2919 const gchar *homedir = g_get_home_dir ();
2920 gchar *utf8_homedir = g_filename_to_utf8 (homedir, -1, NULL, NULL, NULL);
2923 result = open_dir (utf8_homedir, cmpl_state);
2927 g_free (utf8_homedir);
2932 gchar* copy = g_new (char, cmp_len + 1);
2936 strncpy (copy, text_to_complete + 1, cmp_len);
2938 pwd = getpwnam (copy);
2945 utf8_dir = g_filename_to_utf8 (pwd->pw_dir, -1, NULL, NULL, NULL);
2946 result = open_dir (utf8_dir, cmpl_state);
2954 /* open a directory relative to the current relative directory */
2955 static CompletionDir*
2956 open_relative_dir (gchar *dir_name,
2958 CompletionState *cmpl_state)
2960 CompletionDir *result;
2963 path = g_string_sized_new (dir->fullname_len + strlen (dir_name) + 10);
2964 g_string_assign (path, dir->fullname);
2966 if (dir->fullname_len > 1
2967 && path->str[dir->fullname_len - 1] != G_DIR_SEPARATOR)
2968 g_string_append_c (path, G_DIR_SEPARATOR);
2969 g_string_append (path, dir_name);
2971 result = open_dir (path->str, cmpl_state);
2973 g_string_free (path, TRUE);
2978 /* after the cache lookup fails, really open a new directory */
2979 static CompletionDirSent*
2980 open_new_dir (gchar *dir_name,
2982 gboolean stat_subdirs)
2984 CompletionDirSent *sent;
2987 GError *error = NULL;
2988 gint entry_count = 0;
2991 struct stat ent_sbuf;
2993 gchar *sys_dir_name;
2995 sent = g_new (CompletionDirSent, 1);
2996 #ifndef G_PLATFORM_WIN32
2997 sent->mtime = sbuf->st_mtime;
2998 sent->inode = sbuf->st_ino;
2999 sent->device = sbuf->st_dev;
3001 path = g_string_sized_new (2*MAXPATHLEN + 10);
3003 sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
3006 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3010 directory = g_dir_open (sys_dir_name, 0, &error);
3013 cmpl_errno = error->code; /* ??? */
3014 g_free (sys_dir_name);
3018 while ((dirent = g_dir_read_name (directory)) != NULL)
3020 entry_count += 2; /* For ".",".." */
3022 sent->entries = g_new (CompletionDirEntry, entry_count);
3023 sent->entry_count = entry_count;
3025 g_dir_rewind (directory);
3027 for (i = 0; i < entry_count; i += 1)
3029 GError *error = NULL;
3037 dirent = g_dir_read_name (directory);
3038 if (!dirent) /* Directory changed */
3042 sent->entries[n_entries].entry_name = g_filename_to_utf8 (dirent, -1, NULL, NULL, &error);
3043 if (sent->entries[n_entries].entry_name == NULL
3044 || !g_utf8_validate (sent->entries[n_entries].entry_name, -1, NULL))
3046 gchar *escaped_str = g_strescape (dirent, NULL);
3047 g_message (_("The filename \"%s\" couldn't be converted to UTF-8. "
3048 "(try setting the environment variable G_FILENAME_ENCODING): %s"),
3050 error->message ? error->message : _("Invalid UTF-8"));
3051 g_free (escaped_str);
3052 g_clear_error (&error);
3055 g_clear_error (&error);
3057 sent->entries[n_entries].sort_key = g_utf8_collate_key (sent->entries[n_entries].entry_name, -1);
3059 g_string_assign (path, sys_dir_name);
3060 if (path->str[path->len-1] != G_DIR_SEPARATOR)
3062 g_string_append_c (path, G_DIR_SEPARATOR);
3064 g_string_append (path, dirent);
3068 /* Here we know path->str is a "system charset" string */
3069 if (g_stat (path->str, &ent_sbuf) >= 0 && S_ISDIR (ent_sbuf.st_mode))
3070 sent->entries[n_entries].is_dir = TRUE;
3072 /* stat may fail, and we don't mind, since it could be a
3073 * dangling symlink. */
3074 sent->entries[n_entries].is_dir = FALSE;
3077 sent->entries[n_entries].is_dir = 1;
3081 sent->entry_count = n_entries;
3083 g_free (sys_dir_name);
3084 g_string_free (path, TRUE);
3085 qsort (sent->entries, sent->entry_count, sizeof (CompletionDirEntry), compare_cmpl_dir);
3087 g_dir_close (directory);
3092 #ifndef G_PLATFORM_WIN32
3095 check_dir (gchar *dir_name,
3096 struct stat *result,
3097 gboolean *stat_subdirs)
3099 /* A list of directories that we know only contain other directories.
3100 * Trying to stat every file in these directories would be very
3105 const gchar name[5];
3107 struct stat statbuf;
3108 } no_stat_dirs[] = {
3109 { "/afs", FALSE, { 0 } },
3110 { "/net", FALSE, { 0 } }
3113 static const gint n_no_stat_dirs = G_N_ELEMENTS (no_stat_dirs);
3114 static gboolean initialized = FALSE;
3115 gchar *sys_dir_name;
3121 for (i = 0; i < n_no_stat_dirs; i++)
3123 if (g_stat (no_stat_dirs[i].name, &no_stat_dirs[i].statbuf) == 0)
3124 no_stat_dirs[i].present = TRUE;
3128 sys_dir_name = g_filename_from_utf8 (dir_name, -1, NULL, NULL, NULL);
3131 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3135 if (g_stat (sys_dir_name, result) < 0)
3137 g_free (sys_dir_name);
3141 g_free (sys_dir_name);
3143 *stat_subdirs = TRUE;
3144 for (i = 0; i < n_no_stat_dirs; i++)
3146 if (no_stat_dirs[i].present &&
3147 (no_stat_dirs[i].statbuf.st_dev == result->st_dev) &&
3148 (no_stat_dirs[i].statbuf.st_ino == result->st_ino))
3150 *stat_subdirs = FALSE;
3160 /* open a directory by absolute pathname */
3161 static CompletionDir*
3162 open_dir (gchar *dir_name,
3163 CompletionState *cmpl_state)
3165 #ifndef G_PLATFORM_WIN32
3167 gboolean stat_subdirs;
3170 CompletionDirSent *sent;
3172 #ifndef G_PLATFORM_WIN32
3173 if (!check_dir (dir_name, &sbuf, &stat_subdirs))
3176 cdsl = cmpl_state->directory_sent_storage;
3182 if (sent->inode == sbuf.st_ino &&
3183 sent->mtime == sbuf.st_mtime &&
3184 sent->device == sbuf.st_dev)
3185 return attach_dir (sent, dir_name, cmpl_state);
3190 sent = open_new_dir (dir_name, &sbuf, stat_subdirs);
3192 sent = open_new_dir (dir_name, NULL, TRUE);
3197 cmpl_state->directory_sent_storage =
3198 g_list_prepend (cmpl_state->directory_sent_storage, sent);
3200 return attach_dir (sent, dir_name, cmpl_state);
3206 static CompletionDir*
3207 attach_dir (CompletionDirSent *sent,
3209 CompletionState *cmpl_state)
3211 CompletionDir* new_dir;
3213 new_dir = g_new (CompletionDir, 1);
3215 cmpl_state->directory_storage =
3216 g_list_prepend (cmpl_state->directory_storage, new_dir);
3218 new_dir->sent = sent;
3219 new_dir->fullname = g_strdup (dir_name);
3220 new_dir->fullname_len = strlen (dir_name);
3221 new_dir->cmpl_text = NULL;
3227 correct_dir_fullname (CompletionDir* cmpl_dir)
3229 gint length = strlen (cmpl_dir->fullname);
3230 gchar *first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3231 gchar *sys_filename;
3234 /* Does it end with /. (\.) ? */
3236 strcmp (cmpl_dir->fullname + length - 2, G_DIR_SEPARATOR_S ".") == 0)
3238 /* Is it just the root directory (on a drive) ? */
3239 if (cmpl_dir->fullname + length - 2 == first_slash)
3241 cmpl_dir->fullname[length - 1] = 0;
3242 cmpl_dir->fullname_len = length - 1;
3247 cmpl_dir->fullname[length - 2] = 0;
3251 /* Ends with /./ (\.\)? */
3252 else if (length >= 3 &&
3253 strcmp (cmpl_dir->fullname + length - 3,
3254 G_DIR_SEPARATOR_S "." G_DIR_SEPARATOR_S) == 0)
3255 cmpl_dir->fullname[length - 2] = 0;
3257 /* Ends with /.. (\..) ? */
3258 else if (length >= 3 &&
3259 strcmp (cmpl_dir->fullname + length - 3,
3260 G_DIR_SEPARATOR_S "..") == 0)
3262 /* Is it just /.. (X:\..)? */
3263 if (cmpl_dir->fullname + length - 3 == first_slash)
3265 cmpl_dir->fullname[length - 2] = 0;
3266 cmpl_dir->fullname_len = length - 2;
3270 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3273 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3277 if (g_stat (sys_filename, &sbuf) < 0)
3279 g_free (sys_filename);
3283 g_free (sys_filename);
3285 cmpl_dir->fullname[length - 3] = 0;
3287 if (!correct_parent (cmpl_dir, &sbuf))
3291 /* Ends with /../ (\..\)? */
3292 else if (length >= 4 &&
3293 strcmp (cmpl_dir->fullname + length - 4,
3294 G_DIR_SEPARATOR_S ".." G_DIR_SEPARATOR_S) == 0)
3296 /* Is it just /../ (X:\..\)? */
3297 if (cmpl_dir->fullname + length - 4 == first_slash)
3299 cmpl_dir->fullname[length - 3] = 0;
3300 cmpl_dir->fullname_len = length - 3;
3304 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3307 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3311 if (g_stat (sys_filename, &sbuf) < 0)
3313 g_free (sys_filename);
3317 g_free (sys_filename);
3319 cmpl_dir->fullname[length - 4] = 0;
3321 if (!correct_parent (cmpl_dir, &sbuf))
3325 cmpl_dir->fullname_len = strlen (cmpl_dir->fullname);
3331 correct_parent (CompletionDir *cmpl_dir,
3337 #ifndef G_PLATFORM_WIN32
3340 gchar *sys_filename;
3343 last_slash = strrchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3344 g_assert (last_slash);
3345 first_slash = strchr (cmpl_dir->fullname, G_DIR_SEPARATOR);
3347 /* Clever (?) way to check for top-level directory that works also on
3348 * Win32, where there is a drive letter and colon prefixed...
3350 if (last_slash != first_slash)
3360 sys_filename = g_filename_from_utf8 (cmpl_dir->fullname, -1, NULL, NULL, NULL);
3363 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3365 last_slash[0] = G_DIR_SEPARATOR;
3369 if (g_stat (sys_filename, &parbuf) < 0)
3371 g_free (sys_filename);
3374 last_slash[0] = G_DIR_SEPARATOR;
3377 g_free (sys_filename);
3379 #ifndef G_PLATFORM_WIN32 /* No inode numbers on Win32 */
3380 if (parbuf.st_ino == sbuf->st_ino && parbuf.st_dev == sbuf->st_dev)
3381 /* it wasn't a link */
3387 last_slash[0] = G_DIR_SEPARATOR;
3389 /* it was a link, have to figure it out the hard way */
3391 new_name = find_parent_dir_fullname (cmpl_dir->fullname);
3396 g_free (cmpl_dir->fullname);
3398 cmpl_dir->fullname = new_name;
3404 #ifndef G_PLATFORM_WIN32
3407 find_parent_dir_fullname (gchar* dirname)
3409 gchar *sys_orig_dir;
3414 sys_orig_dir = g_get_current_dir ();
3415 sys_dirname = g_filename_from_utf8 (dirname, -1, NULL, NULL, NULL);
3418 g_free (sys_orig_dir);
3419 cmpl_errno = CMPL_ERRNO_DID_NOT_CONVERT;
3423 if (chdir (sys_dirname) != 0 || chdir ("..") != 0)
3426 chdir (sys_orig_dir);
3427 g_free (sys_dirname);
3428 g_free (sys_orig_dir);
3431 g_free (sys_dirname);
3433 sys_cwd = g_get_current_dir ();
3434 result = g_filename_to_utf8 (sys_cwd, -1, NULL, NULL, NULL);
3437 if (chdir (sys_orig_dir) != 0)
3440 g_free (sys_orig_dir);
3444 g_free (sys_orig_dir);
3450 /**********************************************************************/
3451 /* Completion Operations */
3452 /**********************************************************************/
3456 static PossibleCompletion*
3457 attempt_homedir_completion (gchar *text_to_complete,
3458 CompletionState *cmpl_state)
3462 if (!cmpl_state->user_dir_name_buffer &&
3463 !get_pwdb (cmpl_state))
3466 cmpl_state->user_completion_index += 1;
3468 while (cmpl_state->user_completion_index < cmpl_state->user_directories_len)
3470 index = first_diff_index (text_to_complete + 1,
3471 cmpl_state->user_directories
3472 [cmpl_state->user_completion_index].login);
3479 if (cmpl_state->last_valid_char < (index + 1))
3480 cmpl_state->last_valid_char = index + 1;
3481 cmpl_state->user_completion_index += 1;
3485 cmpl_state->the_completion.is_a_completion = 1;
3486 cmpl_state->the_completion.is_directory = TRUE;
3488 append_completion_text ("~", cmpl_state);
3490 append_completion_text (cmpl_state->
3491 user_directories[cmpl_state->user_completion_index].login,
3494 return append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3497 if (text_to_complete[1]
3498 || cmpl_state->user_completion_index > cmpl_state->user_directories_len)
3500 cmpl_state->user_completion_index = -1;
3505 cmpl_state->user_completion_index += 1;
3506 cmpl_state->the_completion.is_a_completion = 1;
3507 cmpl_state->the_completion.is_directory = TRUE;
3509 return append_completion_text ("~" G_DIR_SEPARATOR_S, cmpl_state);
3515 #ifdef G_PLATFORM_WIN32
3516 /* FIXME: determine whether we should casefold all Unicode letters
3517 * here, too (and in in first_diff_index() walk through the strings with
3518 * g_utf8_next_char()), or if this folding isn't actually needed at
3521 #define FOLD(c) (tolower(c))
3526 /* returns the index (>= 0) of the first differing character,
3527 * PATTERN_MATCH if the completion matches */
3529 first_diff_index (gchar *pat,
3534 while (*pat && *text && FOLD (*text) == FOLD (*pat))
3544 return PATTERN_MATCH;
3547 static PossibleCompletion*
3548 append_completion_text (gchar *text,
3549 CompletionState *cmpl_state)
3553 if (!cmpl_state->the_completion.text)
3556 len = strlen (text) + strlen (cmpl_state->the_completion.text) + 1;
3558 if (cmpl_state->the_completion.text_alloc > len)
3560 strcat (cmpl_state->the_completion.text, text);
3561 return &cmpl_state->the_completion;
3567 cmpl_state->the_completion.text_alloc = i;
3569 cmpl_state->the_completion.text = (gchar*) g_realloc (cmpl_state->the_completion.text, i);
3571 if (!cmpl_state->the_completion.text)
3575 strcat (cmpl_state->the_completion.text, text);
3576 return &cmpl_state->the_completion;
3580 static CompletionDir*
3581 find_completion_dir (gchar *text_to_complete,
3582 gchar **remaining_text,
3583 CompletionState *cmpl_state)
3585 gchar* first_slash = strchr (text_to_complete, G_DIR_SEPARATOR);
3586 CompletionDir* dir = cmpl_state->reference_dir;
3587 CompletionDir* next;
3588 *remaining_text = text_to_complete;
3592 gint len = first_slash - *remaining_text;
3594 gchar *found_name = NULL; /* Quiet gcc */
3596 gchar* pat_buf = g_new (gchar, len + 1);
3598 strncpy (pat_buf, *remaining_text, len);
3601 for (i = 0; i < dir->sent->entry_count; i += 1)
3603 if (dir->sent->entries[i].is_dir &&
3604 _gtk_fnmatch (pat_buf, dir->sent->entries[i].entry_name, TRUE))
3614 found_name = dir->sent->entries[i].entry_name;
3621 /* Perhaps we are trying to open an automount directory */
3622 found_name = pat_buf;
3625 next = open_relative_dir (found_name, dir, cmpl_state);
3633 next->cmpl_parent = dir;
3637 if (!correct_dir_fullname (dir))
3643 *remaining_text = first_slash + 1;
3644 first_slash = strchr (*remaining_text, G_DIR_SEPARATOR);
3653 update_cmpl (PossibleCompletion *poss,
3654 CompletionState *cmpl_state)
3658 if (!poss || !cmpl_is_a_completion (poss))
3661 cmpl_len = strlen (cmpl_this_completion (poss));
3663 if (cmpl_state->updated_text_alloc < cmpl_len + 1)
3665 cmpl_state->updated_text_alloc = 2*cmpl_len;
3666 cmpl_state->updated_text =
3667 (gchar*)g_realloc (cmpl_state->updated_text,
3668 cmpl_state->updated_text_alloc);
3671 if (cmpl_state->updated_text_len < 0)
3673 strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3674 cmpl_state->updated_text_len = cmpl_len;
3675 cmpl_state->re_complete = cmpl_is_directory (poss);
3677 else if (cmpl_state->updated_text_len == 0)
3679 cmpl_state->re_complete = FALSE;
3684 first_diff_index (cmpl_state->updated_text,
3685 cmpl_this_completion (poss));
3687 cmpl_state->re_complete = FALSE;
3689 if (first_diff == PATTERN_MATCH)
3692 if (first_diff > cmpl_state->updated_text_len)
3693 strcpy (cmpl_state->updated_text, cmpl_this_completion (poss));
3695 cmpl_state->updated_text_len = first_diff;
3696 cmpl_state->updated_text[first_diff] = 0;
3700 static PossibleCompletion*
3701 attempt_file_completion (CompletionState *cmpl_state)
3703 gchar *pat_buf, *first_slash;
3704 CompletionDir *dir = cmpl_state->active_completion_dir;
3706 dir->cmpl_index += 1;
3708 if (dir->cmpl_index == dir->sent->entry_count)
3710 if (dir->cmpl_parent == NULL)
3712 cmpl_state->active_completion_dir = NULL;
3718 cmpl_state->active_completion_dir = dir->cmpl_parent;
3720 return attempt_file_completion (cmpl_state);
3724 g_assert (dir->cmpl_text);
3726 first_slash = strchr (dir->cmpl_text, G_DIR_SEPARATOR);
3730 gint len = first_slash - dir->cmpl_text;
3732 pat_buf = g_new (gchar, len + 1);
3733 strncpy (pat_buf, dir->cmpl_text, len);
3738 gint len = strlen (dir->cmpl_text);
3740 pat_buf = g_new (gchar, len + 2);
3741 strcpy (pat_buf, dir->cmpl_text);
3742 /* Don't append a * if the user entered one herself.
3743 * This way one can complete *.h and don't get matches
3744 * on any .help files, for instance.
3746 if (strchr (pat_buf, '*') == NULL)
3747 strcpy (pat_buf + len, "*");
3752 if (dir->sent->entries[dir->cmpl_index].is_dir)
3754 if (_gtk_fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, TRUE))
3756 CompletionDir* new_dir;
3758 new_dir = open_relative_dir (dir->sent->entries[dir->cmpl_index].entry_name,
3767 new_dir->cmpl_parent = dir;
3769 new_dir->cmpl_index = -1;
3770 new_dir->cmpl_text = g_strdup (first_slash + 1);
3772 cmpl_state->active_completion_dir = new_dir;
3775 return attempt_file_completion (cmpl_state);
3780 return attempt_file_completion (cmpl_state);
3786 return attempt_file_completion (cmpl_state);
3791 if (dir->cmpl_parent != NULL)
3793 append_completion_text (dir->fullname +
3794 strlen (cmpl_state->completion_dir->fullname) + 1,
3796 append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3799 append_completion_text (dir->sent->entries[dir->cmpl_index].entry_name, cmpl_state);
3801 cmpl_state->the_completion.is_a_completion =
3802 _gtk_fnmatch (pat_buf, dir->sent->entries[dir->cmpl_index].entry_name, TRUE);
3804 cmpl_state->the_completion.is_directory = dir->sent->entries[dir->cmpl_index].is_dir;
3805 if (dir->sent->entries[dir->cmpl_index].is_dir)
3806 append_completion_text (G_DIR_SEPARATOR_S, cmpl_state);
3809 return &cmpl_state->the_completion;
3816 get_pwdb (CompletionState* cmpl_state)
3818 struct passwd *pwd_ptr;
3821 gint len = 0, i, count = 0;
3823 if (cmpl_state->user_dir_name_buffer)
3827 while ((pwd_ptr = getpwent ()) != NULL)
3829 utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3830 len += strlen (utf8);
3832 utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3833 len += strlen (utf8);
3841 cmpl_state->user_dir_name_buffer = g_new (gchar, len);
3842 cmpl_state->user_directories = g_new (CompletionUserDir, count);
3843 cmpl_state->user_directories_len = count;
3845 buf_ptr = cmpl_state->user_dir_name_buffer;
3847 for (i = 0; i < count; i += 1)
3849 pwd_ptr = getpwent ();
3856 utf8 = g_filename_to_utf8 (pwd_ptr->pw_name, -1, NULL, NULL, NULL);
3857 strcpy (buf_ptr, utf8);
3860 cmpl_state->user_directories[i].login = buf_ptr;
3862 buf_ptr += strlen (buf_ptr);
3865 utf8 = g_filename_to_utf8 (pwd_ptr->pw_dir, -1, NULL, NULL, NULL);
3866 strcpy (buf_ptr, utf8);
3869 cmpl_state->user_directories[i].homedir = buf_ptr;
3871 buf_ptr += strlen (buf_ptr);
3875 qsort (cmpl_state->user_directories,
3876 cmpl_state->user_directories_len,
3877 sizeof (CompletionUserDir),
3886 g_free (cmpl_state->user_dir_name_buffer);
3887 g_free (cmpl_state->user_directories);
3889 cmpl_state->user_dir_name_buffer = NULL;
3890 cmpl_state->user_directories = NULL;
3896 compare_user_dir (const void *a,
3899 return strcmp ((((CompletionUserDir*)a))->login,
3900 (((CompletionUserDir*)b))->login);
3906 compare_cmpl_dir (const void *a,
3910 return strcmp (((CompletionDirEntry*)a)->sort_key,
3911 (((CompletionDirEntry*)b))->sort_key);
3915 cmpl_state_okay (CompletionState* cmpl_state)
3917 return cmpl_state && cmpl_state->reference_dir;
3921 cmpl_strerror (gint err)
3923 if (err == CMPL_ERRNO_TOO_LONG)
3924 return _("Name too long");
3925 else if (err == CMPL_ERRNO_DID_NOT_CONVERT)
3926 return _("Couldn't convert filename");
3928 return g_strerror (err);
3933 /* DLL ABI stability backward compatibility versions */
3935 #undef gtk_file_selection_get_filename
3937 G_CONST_RETURN gchar*
3938 gtk_file_selection_get_filename (GtkFileSelection *filesel)
3940 static gchar retval[MAXPATHLEN*2+1];
3943 tem = g_locale_from_utf8 (gtk_file_selection_get_filename_utf8 (filesel),
3944 -1, NULL, NULL, NULL);
3946 strncpy (retval, tem, sizeof (retval) - 1);
3947 retval[sizeof (retval) - 1] = '\0';
3953 #undef gtk_file_selection_set_filename
3956 gtk_file_selection_set_filename (GtkFileSelection *filesel,
3957 const gchar *filename)
3959 gchar *utf8_filename = g_locale_to_utf8 (filename, -1, NULL, NULL, NULL);
3960 gtk_file_selection_set_filename_utf8 (filesel, utf8_filename);
3961 g_free (utf8_filename);
3964 #undef gtk_file_selection_get_selections
3967 gtk_file_selection_get_selections (GtkFileSelection *filesel)
3970 gchar **selections = gtk_file_selection_get_selections_utf8 (filesel);
3972 if (selections != NULL)
3973 while (selections[i] != NULL)
3975 gchar *tem = selections[i];
3976 selections[i] = g_locale_from_utf8 (selections[i],
3977 -1, NULL, NULL, NULL);
3985 #endif /* G_OS_WIN32 */
3987 #define __GTK_FILESEL_C__
3988 #include "gtkaliasdef.c"