1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2003-2004 XangXiu Yang
5 * Jean-Hugues Deschenes
6 * 02-02-2005 Patch removing non standard _fn_match calls
8 * This program is free software; you can redistribute it and/or modify
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License Version 2 as
11 * published by the Free Software Foundation;
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
29 #include <sys/types.h>
31 #include <limits.h> // For PATH_MAX
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 */
58 #include "gdk/gdkkeysyms.h"
60 #include <lttvwindow/gtkdirsel.h>
68 #define mkdir(p,m) _mkdir(p)
70 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
72 #endif /* G_OS_WIN32 */
75 #include <sys/cygwin.h> /* For cygwin_conv_to_posix_path */
78 #define DIR_LIST_WIDTH 180
79 #define DIR_LIST_HEIGHT 180
80 #define FILE_LIST_WIDTH 180
81 #define FILE_LIST_HEIGHT 180
83 /* The Hurd doesn't define either PATH_MAX or MAXPATHLEN, so we put this
84 * in here, since the rest of the code in the file does require some
89 # define MAXPATHLEN PATH_MAX
91 # define MAXPATHLEN 2048
95 /* I've put this here so it doesn't get confused with the
96 * file completion interface */
97 typedef struct _HistoryCallbackArg HistoryCallbackArg
;
99 struct _HistoryCallbackArg
102 GtkWidget
*menu_item
;
106 typedef struct _CompletionState CompletionState
;
107 typedef struct _CompletionDir CompletionDir
;
108 typedef struct _CompletionDirSent CompletionDirSent
;
109 typedef struct _CompletionDirEntry CompletionDirEntry
;
110 typedef struct _CompletionUserDir CompletionUserDir
;
111 typedef struct _PossibleCompletion PossibleCompletion
;
113 /* Non-external file completion decls and structures */
115 /* A contant telling PRCS how many directories to cache. Its actually
116 * kept in a list, so the geometry isn't important. */
117 #define CMPL_DIRECTORY_CACHE_SIZE 10
119 /* A constant used to determine whether a substring was an exact
120 * match by first_diff_index()
122 #define PATTERN_MATCH -1
123 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
124 #define CMPL_ERRNO_DID_NOT_CONVERT ((1<<16)-2)
126 /* This structure contains all the useful information about a directory
127 * for the purposes of filename completion. These structures are cached
128 * in the CompletionState struct. CompletionDir's are reference counted.
130 struct _CompletionDirSent
137 struct _CompletionDirEntry
*entries
;
140 struct _CompletionDir
142 CompletionDirSent
*sent
;
147 struct _CompletionDir
*cmpl_parent
;
152 /* This structure contains pairs of directory entry names with a flag saying
153 * whether or not they are a valid directory. NOTE: This information is used
154 * to provide the caller with information about whether to update its completions
155 * or try to open a file. Since directories are cached by the directory mtime,
156 * a symlink which points to an invalid file (which will not be a directory),
157 * will not be reevaluated if that file is created, unless the containing
158 * directory is touched. I consider this case to be worth ignoring (josh).
160 struct _CompletionDirEntry
167 struct _CompletionUserDir
173 struct _PossibleCompletion
175 /* accessible fields, all are accessed externally by functions
179 gint is_a_completion
;
180 gboolean is_directory
;
187 struct _CompletionState
189 gint last_valid_char
;
191 gint updated_text_len
;
192 gint updated_text_alloc
;
193 gboolean re_complete
;
195 gchar
*user_dir_name_buffer
;
196 gint user_directories_len
;
198 gchar
*last_completion_text
;
200 gint user_completion_index
; /* if >= 0, currently completing ~user */
202 struct _CompletionDir
*completion_dir
; /* directory completing from */
203 struct _CompletionDir
*active_completion_dir
;
205 struct _PossibleCompletion the_completion
;
207 struct _CompletionDir
*reference_dir
; /* initial directory */
209 GList
* directory_storage
;
210 GList
* directory_sent_storage
;
212 struct _CompletionUserDir
*user_directories
;
230 /* File completion functions which would be external, were they used
231 * outside of this file.
234 static CompletionState
* cmpl_init_state (void);
235 static void cmpl_free_state (CompletionState
*cmpl_state
);
236 static gint
cmpl_state_okay (CompletionState
* cmpl_state
);
237 static const gchar
* cmpl_strerror (gint
);
239 static PossibleCompletion
* cmpl_completion_matches(gchar
*text_to_complete
,
240 gchar
**remaining_text
,
241 CompletionState
*cmpl_state
);
243 /* Returns a name for consideration, possibly a completion, this name
244 * will be invalid after the next call to cmpl_next_completion.
246 static char* cmpl_this_completion (PossibleCompletion
*);
248 /* True if this completion matches the given text. Otherwise, this
249 * output can be used to have a list of non-completions.
251 static gint
cmpl_is_a_completion (PossibleCompletion
*);
253 /* True if the completion is a directory
255 static gboolean
cmpl_is_directory (PossibleCompletion
*);
257 /* Obtains the next completion, or NULL
259 static PossibleCompletion
* cmpl_next_completion (CompletionState
*);
261 /* Updating completions: the return value of cmpl_updated_text() will
262 * be text_to_complete completed as much as possible after the most
263 * recent call to cmpl_completion_matches. For the present
264 * application, this is the suggested replacement for the user's input
265 * string. You must CALL THIS AFTER ALL cmpl_text_completions have
268 static gchar
* cmpl_updated_text (CompletionState
* cmpl_state
);
270 /* After updating, to see if the completion was a directory, call
271 * this. If it was, you should consider re-calling completion_matches.
273 static gboolean
cmpl_updated_dir (CompletionState
* cmpl_state
);
275 /* Current location: if using file completion, return the current
276 * directory, from which file completion begins. More specifically,
277 * the cwd concatenated with all exact completions up to the last
278 * directory delimiter('/').
280 static gchar
* cmpl_reference_position (CompletionState
* cmpl_state
);
282 /* backing up: if cmpl_completion_matches returns NULL, you may query
283 * the index of the last completable character into cmpl_updated_text.
285 static gint
cmpl_last_valid_char (CompletionState
* cmpl_state
);
287 /* When the user selects a non-directory, call cmpl_completion_fullname
288 * to get the full name of the selected file.
290 static const gchar
* cmpl_completion_fullname (const gchar
*, CompletionState
* cmpl_state
);
293 /* Directory operations. */
294 static CompletionDir
* open_ref_dir (gchar
* text_to_complete
,
295 gchar
** remaining_text
,
296 CompletionState
* cmpl_state
);
297 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
298 static gboolean
check_dir (gchar
*dir_name
,
300 gboolean
*stat_subdirs
);
302 static CompletionDir
* open_dir (gchar
* dir_name
,
303 CompletionState
* cmpl_state
);
305 static CompletionDir
* open_user_dir (const gchar
* text_to_complete
,
306 CompletionState
*cmpl_state
);
308 static CompletionDir
* open_relative_dir (gchar
* dir_name
, CompletionDir
* dir
,
309 CompletionState
*cmpl_state
);
310 static CompletionDirSent
* open_new_dir (gchar
* dir_name
,
312 gboolean stat_subdirs
);
313 static gint
correct_dir_fullname (CompletionDir
* cmpl_dir
);
314 static gint
correct_parent (CompletionDir
* cmpl_dir
,
317 static gchar
* find_parent_dir_fullname (gchar
* dirname
);
319 static CompletionDir
* attach_dir (CompletionDirSent
* sent
,
321 CompletionState
*cmpl_state
);
322 static void free_dir_sent (CompletionDirSent
* sent
);
323 static void free_dir (CompletionDir
*dir
);
324 static void prune_memory_usage(CompletionState
*cmpl_state
);
326 /* Completion operations */
328 static PossibleCompletion
* attempt_homedir_completion(gchar
* text_to_complete
,
329 CompletionState
*cmpl_state
);
331 static PossibleCompletion
* attempt_dir_completion(CompletionState
*cmpl_state
);
332 static CompletionDir
* find_completion_dir(gchar
* text_to_complete
,
333 gchar
** remaining_text
,
334 CompletionState
* cmpl_state
);
335 static PossibleCompletion
* append_completion_text(gchar
* text
,
336 CompletionState
* cmpl_state
);
338 static gint
get_pwdb(CompletionState
* cmpl_state
);
339 static gint
compare_user_dir(const void* a
, const void* b
);
341 static gint
first_diff_index(gchar
* pat
, gchar
* text
);
342 static gint
compare_cmpl_dir(const void* a
, const void* b
);
343 static void update_cmpl(PossibleCompletion
* poss
,
344 CompletionState
* cmpl_state
);
346 static void gtk_dir_selection_class_init (GtkDirSelectionClass
*klass
);
347 static void gtk_dir_selection_set_property (GObject
*object
,
351 static void gtk_dir_selection_get_property (GObject
*object
,
355 static void gtk_dir_selection_init (GtkDirSelection
*filesel
);
356 static void gtk_dir_selection_finalize (GObject
*object
);
357 static void gtk_dir_selection_destroy (GtkObject
*object
);
358 static void gtk_dir_selection_map (GtkWidget
*widget
);
359 static gint
gtk_dir_selection_key_press (GtkWidget
*widget
,
362 static gint
gtk_dir_selection_insert_text (GtkWidget
*widget
,
363 const gchar
*new_text
,
364 gint new_text_length
,
367 static void gtk_dir_selection_update_fileops (GtkDirSelection
*filesel
);
369 static void gtk_dir_selection_file_activate (GtkTreeView
*tree_view
,
371 GtkTreeViewColumn
*column
,
373 static void gtk_dir_selection_file_changed (GtkTreeSelection
*selection
,
375 static void gtk_dir_selection_dir_activate (GtkTreeView
*tree_view
,
377 GtkTreeViewColumn
*column
,
379 static void gtk_dir_selection_dir_changed (GtkTreeSelection
*selection
,
381 static void gtk_dir_selection_populate (GtkDirSelection
*fs
,
383 gboolean try_complete
,
384 gboolean reset_entry
);
385 static void gtk_dir_selection_abort (GtkDirSelection
*fs
);
387 static void gtk_dir_selection_update_history_menu (GtkDirSelection
*fs
,
390 static void gtk_dir_selection_create_dir (GtkWidget
*widget
, gpointer data
);
391 static void gtk_dir_selection_delete_file (GtkWidget
*widget
, gpointer data
);
392 static void gtk_dir_selection_rename_file (GtkWidget
*widget
, gpointer data
);
394 static void free_selected_names (GPtrArray
*names
);
396 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
397 #define compare_filenames(a, b) strcmp(a, b)
399 #define compare_filenames(a, b) g_ascii_strcasecmp(a, b)
403 static GtkWindowClass
*parent_class
= NULL
;
405 /* Saves errno when something cmpl does fails. */
406 static gint cmpl_errno
;
410 * Take the path currently in the file selection
411 * entry field and translate as necessary from
412 * a WIN32 style to CYGWIN32 style path. For
413 * instance translate:
414 * x:\somepath\file.jpg
416 * /cygdrive/x/somepath/file.jpg
418 * Replace the path in the selection text field.
419 * Return a boolean value concerning whether a
420 * translation had to be made.
423 translate_win32_path (GtkDirSelection
*filesel
)
427 gchar newPath
[PATH_MAX
];
430 * Retrieve the current path
432 path
= gtk_entry_get_text (GTK_ENTRY (filesel
->selection_entry
));
434 cygwin_conv_to_posix_path (path
, newPath
);
435 updated
= (strcmp (path
, newPath
) != 0);
438 gtk_entry_set_text (GTK_ENTRY (filesel
->selection_entry
), newPath
);
445 gtk_dir_selection_get_type (void)
447 static GType file_selection_type
= 0;
449 if (!file_selection_type
)
451 static const GTypeInfo filesel_info
=
453 sizeof (GtkDirSelectionClass
),
454 NULL
, /* base_init */
455 NULL
, /* base_finalize */
456 (GClassInitFunc
) gtk_dir_selection_class_init
,
457 NULL
, /* class_finalize */
458 NULL
, /* class_data */
459 sizeof (GtkDirSelection
),
461 (GInstanceInitFunc
) gtk_dir_selection_init
,
465 file_selection_type
=
466 g_type_register_static (GTK_TYPE_DIALOG
, "GtkDirSelection",
470 return file_selection_type
;
474 gtk_dir_selection_class_init (GtkDirSelectionClass
*class)
476 GObjectClass
*gobject_class
;
477 GtkObjectClass
*object_class
;
478 GtkWidgetClass
*widget_class
;
480 gobject_class
= (GObjectClass
*) class;
481 object_class
= (GtkObjectClass
*) class;
482 widget_class
= (GtkWidgetClass
*) class;
484 parent_class
= g_type_class_peek_parent (class);
486 gobject_class
->finalize
= gtk_dir_selection_finalize
;
487 gobject_class
->set_property
= gtk_dir_selection_set_property
;
488 gobject_class
->get_property
= gtk_dir_selection_get_property
;
490 g_object_class_install_property (gobject_class
,
492 g_param_spec_string ("filename",
494 _("The currently selected filename"),
496 G_PARAM_READABLE
| G_PARAM_WRITABLE
));
497 g_object_class_install_property (gobject_class
,
499 g_param_spec_boolean ("show_fileops",
500 _("Show file operations"),
501 _("Whether buttons for creating/manipulating files should be displayed"),
505 g_object_class_install_property (gobject_class
,
506 PROP_SELECT_MULTIPLE
,
507 g_param_spec_boolean ("select_multiple",
508 _("Select multiple"),
509 _("Whether to allow multiple files to be selected"),
513 object_class
->destroy
= gtk_dir_selection_destroy
;
514 widget_class
->map
= gtk_dir_selection_map
;
517 static void gtk_dir_selection_set_property (GObject
*object
,
522 GtkDirSelection
*filesel
;
524 filesel
= GTK_DIR_SELECTION (object
);
529 gtk_dir_selection_set_filename (filesel
,
530 g_value_get_string (value
));
532 case PROP_SHOW_FILEOPS
:
533 if (g_value_get_boolean (value
))
534 gtk_dir_selection_show_fileop_buttons (filesel
);
536 gtk_dir_selection_hide_fileop_buttons (filesel
);
538 case PROP_SELECT_MULTIPLE
:
539 gtk_dir_selection_set_select_multiple (filesel
, g_value_get_boolean (value
));
542 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
547 static void gtk_dir_selection_get_property (GObject
*object
,
552 GtkDirSelection
*filesel
;
554 filesel
= GTK_DIR_SELECTION (object
);
559 g_value_set_string (value
,
560 gtk_dir_selection_get_filename(filesel
));
563 case PROP_SHOW_FILEOPS
:
564 /* This is a little bit hacky, but doing otherwise would require
565 * adding a field to the object.
567 g_value_set_boolean (value
, (filesel
->fileop_c_dir
&&
568 filesel
->fileop_del_file
&&
569 filesel
->fileop_ren_file
));
571 case PROP_SELECT_MULTIPLE
:
572 g_value_set_boolean (value
, gtk_dir_selection_get_select_multiple (filesel
));
575 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
581 grab_default (GtkWidget
*widget
)
583 gtk_widget_grab_default (widget
);
588 gtk_dir_selection_init (GtkDirSelection
*filesel
)
590 GtkWidget
*entry_vbox
;
592 GtkWidget
*list_hbox
, *list_container
;
593 GtkWidget
*confirm_area
;
594 GtkWidget
*pulldown_hbox
;
595 GtkWidget
*scrolled_win
;
601 GtkTreeViewColumn
*column
;
603 gtk_widget_push_composite_child ();
605 dialog
= GTK_DIALOG (filesel
);
607 filesel
->cmpl_state
= cmpl_init_state ();
609 /* The dialog-sized vertical box */
610 filesel
->main_vbox
= dialog
->vbox
;
611 gtk_container_set_border_width (GTK_CONTAINER (filesel
), 10);
613 /* The horizontal box containing create, rename etc. buttons */
614 filesel
->button_area
= gtk_hbutton_box_new ();
615 gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel
->button_area
), GTK_BUTTONBOX_START
);
616 gtk_box_set_spacing (GTK_BOX (filesel
->button_area
), 0);
617 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), filesel
->button_area
,
619 gtk_widget_show (filesel
->button_area
);
621 gtk_dir_selection_show_fileop_buttons (filesel
);
623 /* hbox for pulldown menu */
624 pulldown_hbox
= gtk_hbox_new (TRUE
, 5);
625 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), pulldown_hbox
, FALSE
, FALSE
, 0);
626 gtk_widget_show (pulldown_hbox
);
629 filesel
->history_pulldown
= gtk_option_menu_new ();
630 // gtk_widget_show (filesel->history_pulldown);
631 // gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown,
634 /* The horizontal box containing the directory and file listboxes */
636 spacer
= gtk_hbox_new (FALSE
, 0);
637 gtk_widget_set_size_request (spacer
, -1, 5);
638 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), spacer
, FALSE
, FALSE
, 0);
639 gtk_widget_show (spacer
);
641 list_hbox
= gtk_hbox_new (FALSE
, 5);
642 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), list_hbox
, TRUE
, TRUE
, 0);
643 gtk_widget_show (list_hbox
);
645 list_container
= g_object_new (GTK_TYPE_HPANED
,
651 list_container
= list_hbox
;
653 spacer
= gtk_hbox_new (FALSE
, 0);
654 gtk_widget_set_size_request (spacer
, -1, 5);
655 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), spacer
, FALSE
, FALSE
, 0);
656 gtk_widget_show (spacer
);
658 /* The directories list */
660 model
= gtk_list_store_new (1, G_TYPE_STRING
);
661 filesel
->dir_list
= gtk_tree_view_new_with_model (GTK_TREE_MODEL (model
));
662 g_object_unref (model
);
664 column
= gtk_tree_view_column_new_with_attributes (_("Folders"),
665 gtk_cell_renderer_text_new (),
668 label
= gtk_label_new_with_mnemonic (_("Fol_ders"));
669 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), filesel
->dir_list
);
670 gtk_widget_show (label
);
671 gtk_tree_view_column_set_widget (column
, label
);
672 gtk_tree_view_column_set_sizing (column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
673 gtk_tree_view_append_column (GTK_TREE_VIEW (filesel
->dir_list
), column
);
675 gtk_widget_set_size_request (filesel
->dir_list
,
676 DIR_LIST_WIDTH
, DIR_LIST_HEIGHT
);
677 g_signal_connect (filesel
->dir_list
, "row_activated",
678 G_CALLBACK (gtk_dir_selection_dir_activate
), filesel
);
679 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel
->dir_list
)), "changed",
680 G_CALLBACK (gtk_dir_selection_dir_changed
), filesel
);
682 /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list)); */
684 scrolled_win
= gtk_scrolled_window_new (NULL
, NULL
);
685 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win
), GTK_SHADOW_IN
);
686 gtk_container_add (GTK_CONTAINER (scrolled_win
), filesel
->dir_list
);
687 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win
),
688 GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
);
689 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win
), 0);
690 if (GTK_IS_PANED (list_container
))
691 gtk_paned_pack1 (GTK_PANED (list_container
), scrolled_win
, TRUE
, TRUE
);
693 gtk_container_add (GTK_CONTAINER (list_container
), scrolled_win
);
694 gtk_widget_show (filesel
->dir_list
);
695 gtk_widget_show (scrolled_win
);
698 model
= gtk_list_store_new (1, G_TYPE_STRING
);
699 filesel
->file_list
= gtk_tree_view_new_with_model (GTK_TREE_MODEL (model
));
700 g_object_unref (model
);
702 column
= gtk_tree_view_column_new_with_attributes (_("Files"),
703 gtk_cell_renderer_text_new (),
706 label
= gtk_label_new_with_mnemonic (_("_Files"));
707 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), filesel
->file_list
);
708 gtk_widget_show (label
);
709 gtk_tree_view_column_set_widget (column
, label
);
710 gtk_tree_view_column_set_sizing (column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
711 gtk_tree_view_append_column (GTK_TREE_VIEW (filesel
->file_list
), column
);
713 gtk_widget_set_size_request (filesel
->file_list
,
714 FILE_LIST_WIDTH
, FILE_LIST_HEIGHT
);
715 g_signal_connect (filesel
->file_list
, "row_activated",
716 G_CALLBACK (gtk_dir_selection_file_activate
), filesel
);
717 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel
->file_list
)), "changed",
718 G_CALLBACK (gtk_dir_selection_file_changed
), filesel
);
720 /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list)); */
722 scrolled_win
= gtk_scrolled_window_new (NULL
, NULL
);
723 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win
), GTK_SHADOW_IN
);
724 gtk_container_add (GTK_CONTAINER (scrolled_win
), filesel
->file_list
);
725 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win
),
726 GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
);
727 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win
), 0);
728 // gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
729 // gtk_widget_show (filesel->file_list);
730 // gtk_widget_show (scrolled_win);
732 /* action area for packing buttons into. */
733 filesel
->action_area
= gtk_hbox_new (TRUE
, 0);
734 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), filesel
->action_area
,
736 gtk_widget_show (filesel
->action_area
);
738 /* The OK/Cancel button area */
739 confirm_area
= dialog
->action_area
;
741 /* The Cancel button */
742 filesel
->cancel_button
= gtk_dialog_add_button (dialog
,
744 GTK_RESPONSE_CANCEL
);
746 filesel
->ok_button
= gtk_dialog_add_button (dialog
,
750 gtk_widget_grab_default (filesel
->ok_button
);
752 /* The selection entry widget */
753 entry_vbox
= gtk_vbox_new (FALSE
, 2);
754 gtk_box_pack_end (GTK_BOX (filesel
->main_vbox
), entry_vbox
, FALSE
, FALSE
, 2);
755 gtk_widget_show (entry_vbox
);
757 eventbox
= gtk_event_box_new ();
758 filesel
->selection_text
= label
= gtk_label_new ("");
759 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
760 gtk_container_add (GTK_CONTAINER (eventbox
), label
);
761 gtk_box_pack_start (GTK_BOX (entry_vbox
), eventbox
, FALSE
, FALSE
, 0);
762 gtk_widget_show (label
);
763 gtk_widget_show (eventbox
);
765 filesel
->selection_entry
= gtk_entry_new ();
766 g_signal_connect (filesel
->selection_entry
, "key_press_event",
767 G_CALLBACK (gtk_dir_selection_key_press
), filesel
);
768 g_signal_connect (filesel
->selection_entry
, "insert_text",
769 G_CALLBACK (gtk_dir_selection_insert_text
), NULL
);
770 g_signal_connect_swapped (filesel
->selection_entry
, "changed",
771 G_CALLBACK (gtk_dir_selection_update_fileops
), filesel
);
772 g_signal_connect_swapped (filesel
->selection_entry
, "focus_in_event",
773 G_CALLBACK (grab_default
),
775 g_signal_connect_swapped (filesel
->selection_entry
, "activate",
776 G_CALLBACK (gtk_button_clicked
),
779 gtk_box_pack_start (GTK_BOX (entry_vbox
), filesel
->selection_entry
, TRUE
, TRUE
, 0);
780 gtk_widget_show (filesel
->selection_entry
);
782 gtk_label_set_mnemonic_widget (GTK_LABEL (filesel
->selection_text
),
783 filesel
->selection_entry
);
785 if (!cmpl_state_okay (filesel
->cmpl_state
))
789 g_snprintf (err_buf
, sizeof (err_buf
), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno
));
791 gtk_label_set_text (GTK_LABEL (filesel
->selection_text
), err_buf
);
795 gtk_dir_selection_populate (filesel
, "", FALSE
, TRUE
);
798 gtk_widget_grab_focus (filesel
->selection_entry
);
800 gtk_widget_pop_composite_child ();
804 uri_list_extract_first_uri (const gchar
* uri_list
)
808 g_return_val_if_fail (uri_list
!= NULL
, NULL
);
811 /* We don't actually try to validate the URI according to RFC
812 * 2396, or even check for allowed characters - we just ignore
813 * comments and trim whitespace off the ends. We also
814 * allow LF delimination as well as the specified CRLF.
816 * We do allow comments like specified in RFC 2483.
822 while (g_ascii_isspace(*p
))
826 while (*q
&& (*q
!= '\n') && (*q
!= '\r'))
832 while (q
> p
&& g_ascii_isspace (*q
))
836 return g_strndup (p
, q
- p
+ 1);
839 p
= strchr (p
, '\n');
847 dnd_really_drop (GtkWidget
*dialog
, gint response_id
, GtkDirSelection
*fs
)
851 if (response_id
== GTK_RESPONSE_YES
)
853 filename
= g_object_get_data (G_OBJECT (dialog
), "gtk-fs-dnd-filename");
855 gtk_dir_selection_set_filename (fs
, filename
);
858 gtk_widget_destroy (dialog
);
863 filenames_dropped (GtkWidget
*widget
,
864 GdkDragContext
*context
,
867 GtkSelectionData
*selection_data
,
872 char *filename
= NULL
;
874 char this_hostname
[257];
876 GError
*error
= NULL
;
878 if (!selection_data
->data
)
881 uri
= uri_list_extract_first_uri ((char *)selection_data
->data
);
886 filename
= g_filename_from_uri (uri
, &hostname
, &error
);
891 g_warning ("Error getting dropped filename: %s\n",
893 g_error_free (error
);
897 res
= gethostname (this_hostname
, 256);
898 this_hostname
[256] = 0;
900 if ((hostname
== NULL
) ||
901 (res
== 0 && strcmp (hostname
, this_hostname
) == 0) ||
902 (strcmp (hostname
, "localhost") == 0))
903 gtk_dir_selection_set_filename (GTK_DIR_SELECTION (widget
),
908 gchar
*filename_utf8
;
910 /* Conversion back to UTF-8 should always succeed for the result
911 * of g_filename_from_uri()
913 filename_utf8
= g_filename_to_utf8 (filename
, -1, NULL
, NULL
, NULL
);
914 g_assert (filename_utf8
);
916 dialog
= gtk_message_dialog_new (GTK_WINDOW (widget
),
917 GTK_DIALOG_DESTROY_WITH_PARENT
,
918 GTK_MESSAGE_QUESTION
,
920 _("The file \"%s\" resides on another machine (called %s) and may not be available to this program.\n"
921 "Are you sure that you want to select it?"), filename_utf8
, hostname
);
922 g_free (filename_utf8
);
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 GtkDirSelection
*filesel
)
961 file
= gtk_dir_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 gchar
*filename_utf8
= g_filename_to_utf8 (file
, -1, NULL
, NULL
, NULL
);
987 g_assert (filename_utf8
);
988 gtk_selection_data_set_text (selection_data
, filename_utf8
, -1);
989 g_free (filename_utf8
);
995 file_selection_setup_dnd (GtkDirSelection
*filesel
)
998 static const GtkTargetEntry drop_types
[] = {
999 { "text/uri-list", 0, TARGET_URILIST
}
1001 static gint n_drop_types
= sizeof(drop_types
)/sizeof(drop_types
[0]);
1002 static const GtkTargetEntry drag_types
[] = {
1003 { "text/uri-list", 0, TARGET_URILIST
},
1004 { "UTF8_STRING", 0, TARGET_UTF8_STRING
},
1007 { "COMPOUND_TEXT", 0, 0 }
1009 static gint n_drag_types
= sizeof(drag_types
)/sizeof(drag_types
[0]);
1011 gtk_drag_dest_set (GTK_WIDGET (filesel
),
1012 GTK_DEST_DEFAULT_ALL
,
1013 drop_types
, n_drop_types
,
1016 g_signal_connect (filesel
, "drag_data_received",
1017 G_CALLBACK (filenames_dropped
), NULL
);
1019 eventbox
= gtk_widget_get_parent (filesel
->selection_text
);
1020 gtk_drag_source_set (eventbox
,
1022 drag_types
, n_drag_types
,
1025 g_signal_connect (eventbox
, "drag_data_get",
1026 G_CALLBACK (filenames_drag_get
), filesel
);
1030 gtk_dir_selection_new (const gchar
*title
)
1032 GtkDirSelection
*filesel
;
1034 filesel
= g_object_new (GTK_TYPE_DIR_SELECTION
, NULL
);
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_dir_selection_show_fileop_buttons (GtkDirSelection
*filesel
)
1046 g_return_if_fail (GTK_IS_DIR_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 g_signal_connect (filesel
->fileop_c_dir
, "clicked",
1053 G_CALLBACK (gtk_dir_selection_create_dir
),
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 g_signal_connect (filesel
->fileop_del_file
, "clicked",
1064 G_CALLBACK (gtk_dir_selection_delete_file
),
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 g_signal_connect (filesel
->fileop_ren_file
, "clicked",
1075 G_CALLBACK (gtk_dir_selection_rename_file
),
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);
1082 gtk_dir_selection_update_fileops (filesel
);
1084 g_object_notify (G_OBJECT (filesel
), "show_fileops");
1088 gtk_dir_selection_hide_fileop_buttons (GtkDirSelection
*filesel
)
1090 g_return_if_fail (GTK_IS_DIR_SELECTION (filesel
));
1092 if (filesel
->fileop_ren_file
)
1094 gtk_widget_destroy (filesel
->fileop_ren_file
);
1095 filesel
->fileop_ren_file
= NULL
;
1098 if (filesel
->fileop_del_file
)
1100 gtk_widget_destroy (filesel
->fileop_del_file
);
1101 filesel
->fileop_del_file
= NULL
;
1104 if (filesel
->fileop_c_dir
)
1106 gtk_widget_destroy (filesel
->fileop_c_dir
);
1107 filesel
->fileop_c_dir
= NULL
;
1109 g_object_notify (G_OBJECT (filesel
), "show_fileops");
1115 * gtk_dir_selection_set_filename:
1116 * @filesel: a #GtkDirSelection.
1117 * @filename: a string to set as the default file name.
1119 * Sets a default path for the file requestor. If @filename includes a
1120 * directory path, then the requestor will open with that path as its
1121 * current working directory.
1123 * The encoding of @filename is the on-disk encoding, which
1124 * may not be UTF-8. See g_filename_from_utf8().
1127 gtk_dir_selection_set_filename (GtkDirSelection
*filesel
,
1128 const gchar
*filename
)
1131 const char *name
, *last_slash
;
1132 char *filename_utf8
;
1134 g_return_if_fail (GTK_IS_DIR_SELECTION (filesel
));
1135 g_return_if_fail (filename
!= NULL
);
1137 filename_utf8
= g_filename_to_utf8 (filename
, -1, NULL
, NULL
, NULL
);
1138 g_return_if_fail (filename_utf8
!= NULL
);
1140 last_slash
= strrchr (filename_utf8
, G_DIR_SEPARATOR
);
1144 buf
= g_strdup ("");
1145 name
= filename_utf8
;
1149 buf
= g_strdup (filename_utf8
);
1150 buf
[last_slash
- filename_utf8
+ 1] = 0;
1151 name
= last_slash
+ 1;
1154 gtk_dir_selection_populate (filesel
, buf
, FALSE
, TRUE
);
1156 if (filesel
->selection_entry
)
1157 gtk_entry_set_text (GTK_ENTRY (filesel
->selection_entry
), name
);
1159 g_object_notify (G_OBJECT (filesel
), "filename");
1161 g_free (filename_utf8
);
1165 * gtk_dir_selection_get_filename:
1166 * @filesel: a #GtkDirSelection
1168 * This function returns the selected filename in the on-disk encoding
1169 * (see g_filename_from_utf8()), which may or may not be the same as that
1170 * used by GTK+ (UTF-8). To convert to UTF-8, call g_filename_to_utf8().
1171 * The returned string points to a statically allocated buffer and
1172 * should be copied if you plan to keep it around.
1174 * If no file is selected then the selected directory path is returned.
1176 * Return value: currently-selected filename in the on-disk encoding.
1178 G_CONST_RETURN gchar
*
1179 gtk_dir_selection_get_filename (GtkDirSelection
*filesel
)
1181 static const gchar nothing
[2] = "";
1182 static gchar something
[MAXPATHLEN
*2];
1186 g_return_val_if_fail (GTK_IS_DIR_SELECTION (filesel
), nothing
);
1188 #ifdef G_WITH_CYGWIN
1189 translate_win32_path (filesel
);
1191 text
= gtk_entry_get_text (GTK_ENTRY (filesel
->selection_entry
));
1194 sys_filename
= g_filename_from_utf8 (cmpl_completion_fullname (text
, filesel
->cmpl_state
), -1, NULL
, NULL
, NULL
);
1197 strncpy (something
, sys_filename
, sizeof (something
));
1198 g_free (sys_filename
);
1206 gtk_dir_selection_complete (GtkDirSelection
*filesel
,
1207 const gchar
*pattern
)
1209 g_return_if_fail (GTK_IS_DIR_SELECTION (filesel
));
1210 g_return_if_fail (pattern
!= NULL
);
1212 if (filesel
->selection_entry
)
1213 gtk_entry_set_text (GTK_ENTRY (filesel
->selection_entry
), pattern
);
1214 gtk_dir_selection_populate (filesel
, (gchar
*) pattern
, TRUE
, TRUE
);
1218 gtk_dir_selection_destroy (GtkObject
*object
)
1220 GtkDirSelection
*filesel
;
1222 HistoryCallbackArg
*callback_arg
;
1224 g_return_if_fail (GTK_IS_DIR_SELECTION (object
));
1226 filesel
= GTK_DIR_SELECTION (object
);
1228 if (filesel
->fileop_dialog
)
1230 gtk_widget_destroy (filesel
->fileop_dialog
);
1231 filesel
->fileop_dialog
= NULL
;
1234 if (filesel
->history_list
)
1236 list
= filesel
->history_list
;
1239 callback_arg
= list
->data
;
1240 g_free (callback_arg
->directory
);
1241 g_free (callback_arg
);
1244 g_list_free (filesel
->history_list
);
1245 filesel
->history_list
= NULL
;
1248 if (filesel
->cmpl_state
)
1250 cmpl_free_state (filesel
->cmpl_state
);
1251 filesel
->cmpl_state
= NULL
;
1254 if (filesel
->selected_names
)
1256 free_selected_names (filesel
->selected_names
);
1257 filesel
->selected_names
= NULL
;
1260 if (filesel
->last_selected
)
1262 g_free (filesel
->last_selected
);
1263 filesel
->last_selected
= NULL
;
1266 GTK_OBJECT_CLASS (parent_class
)->destroy (object
);
1270 gtk_dir_selection_map (GtkWidget
*widget
)
1272 GtkDirSelection
*filesel
= GTK_DIR_SELECTION (widget
);
1274 /* Refresh the contents */
1275 gtk_dir_selection_populate (filesel
, "", FALSE
, FALSE
);
1277 GTK_WIDGET_CLASS (parent_class
)->map (widget
);
1281 gtk_dir_selection_finalize (GObject
*object
)
1283 GtkDirSelection
*filesel
= GTK_DIR_SELECTION (object
);
1285 g_free (filesel
->fileop_file
);
1287 G_OBJECT_CLASS (parent_class
)->finalize (object
);
1290 /* Begin file operations callbacks */
1293 gtk_dir_selection_fileop_error (GtkDirSelection
*fs
,
1294 gchar
*error_message
)
1298 g_return_if_fail (error_message
!= NULL
);
1301 dialog
= gtk_message_dialog_new (GTK_WINDOW (fs
),
1302 GTK_DIALOG_DESTROY_WITH_PARENT
,
1305 "%s", error_message
);
1307 /* yes, we free it */
1308 g_free (error_message
);
1310 gtk_window_set_modal (GTK_WINDOW (dialog
), TRUE
);
1312 g_signal_connect_swapped (dialog
, "response",
1313 G_CALLBACK (gtk_widget_destroy
),
1316 gtk_widget_show (dialog
);
1320 gtk_dir_selection_fileop_destroy (GtkWidget
*widget
,
1323 GtkDirSelection
*fs
= data
;
1325 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1327 fs
->fileop_dialog
= NULL
;
1331 entry_is_empty (GtkEntry
*entry
)
1333 const gchar
*text
= gtk_entry_get_text (entry
);
1335 return *text
== '\0';
1339 gtk_dir_selection_fileop_entry_changed (GtkEntry
*entry
,
1342 gtk_widget_set_sensitive (button
, !entry_is_empty (entry
));
1346 gtk_dir_selection_create_dir_confirmed (GtkWidget
*widget
,
1349 GtkDirSelection
*fs
= data
;
1350 const gchar
*dirname
;
1353 gchar
*sys_full_path
;
1355 GError
*error
= NULL
;
1356 CompletionState
*cmpl_state
;
1358 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1360 dirname
= gtk_entry_get_text (GTK_ENTRY (fs
->fileop_entry
));
1361 cmpl_state
= (CompletionState
*) fs
->cmpl_state
;
1362 path
= cmpl_reference_position (cmpl_state
);
1364 full_path
= g_strconcat (path
, G_DIR_SEPARATOR_S
, dirname
, NULL
);
1365 sys_full_path
= g_filename_from_utf8 (full_path
, -1, NULL
, NULL
, &error
);
1368 if (g_error_matches (error
, G_CONVERT_ERROR
, G_CONVERT_ERROR_ILLEGAL_SEQUENCE
))
1369 buf
= g_strdup_printf (_("The folder name \"%s\" contains symbols that are not allowed in filenames"), dirname
);
1371 buf
= g_strdup_printf (_("Error creating folder \"%s\": %s\n%s"), dirname
, error
->message
,
1372 _("You probably used symbols not allowed in filenames."));
1373 gtk_dir_selection_fileop_error (fs
, buf
);
1374 g_error_free (error
);
1378 if (mkdir (sys_full_path
, 0755) < 0)
1380 buf
= g_strdup_printf (_("Error creating folder \"%s\": %s\n"), dirname
,
1381 g_strerror (errno
));
1382 gtk_dir_selection_fileop_error (fs
, buf
);
1387 g_free (sys_full_path
);
1389 gtk_widget_destroy (fs
->fileop_dialog
);
1390 gtk_dir_selection_populate (fs
, "", FALSE
, FALSE
);
1394 gtk_dir_selection_create_dir (GtkWidget
*widget
,
1397 GtkDirSelection
*fs
= data
;
1403 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1405 if (fs
->fileop_dialog
)
1409 dialog
= gtk_dialog_new ();
1410 fs
->fileop_dialog
= dialog
;
1411 g_signal_connect (dialog
, "destroy",
1412 G_CALLBACK (gtk_dir_selection_fileop_destroy
),
1414 gtk_window_set_title (GTK_WINDOW (dialog
), _("New Folder"));
1415 gtk_window_set_position (GTK_WINDOW (dialog
), GTK_WIN_POS_MOUSE
);
1416 gtk_window_set_transient_for (GTK_WINDOW (dialog
), GTK_WINDOW (fs
));
1418 /* If file dialog is grabbed, grab option dialog */
1419 /* When option dialog is closed, file dialog will be grabbed again */
1420 if (GTK_WINDOW (fs
)->modal
)
1421 gtk_window_set_modal (GTK_WINDOW (dialog
), TRUE
);
1423 vbox
= gtk_vbox_new (FALSE
, 0);
1424 gtk_container_set_border_width (GTK_CONTAINER (vbox
), 8);
1425 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), vbox
,
1427 gtk_widget_show( vbox
);
1429 label
= gtk_label_new_with_mnemonic (_("_Folder name:"));
1430 gtk_misc_set_alignment(GTK_MISC (label
), 0.0, 0.0);
1431 gtk_box_pack_start (GTK_BOX (vbox
), label
, FALSE
, FALSE
, 5);
1432 gtk_widget_show (label
);
1434 /* The directory entry widget */
1435 fs
->fileop_entry
= gtk_entry_new ();
1436 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), fs
->fileop_entry
);
1437 gtk_box_pack_start (GTK_BOX (vbox
), fs
->fileop_entry
,
1439 GTK_WIDGET_SET_FLAGS (fs
->fileop_entry
, GTK_CAN_DEFAULT
);
1440 gtk_widget_show (fs
->fileop_entry
);
1443 button
= gtk_button_new_from_stock (GTK_STOCK_CANCEL
);
1444 g_signal_connect_swapped (button
, "clicked",
1445 G_CALLBACK (gtk_widget_destroy
),
1447 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->action_area
),
1448 button
, TRUE
, TRUE
, 0);
1449 GTK_WIDGET_SET_FLAGS (button
, GTK_CAN_DEFAULT
);
1450 gtk_widget_grab_default (button
);
1451 gtk_widget_show (button
);
1453 gtk_widget_grab_focus (fs
->fileop_entry
);
1455 button
= gtk_button_new_with_mnemonic (_("C_reate"));
1456 gtk_widget_set_sensitive (button
, FALSE
);
1457 g_signal_connect (button
, "clicked",
1458 G_CALLBACK (gtk_dir_selection_create_dir_confirmed
),
1460 g_signal_connect (fs
->fileop_entry
, "changed",
1461 G_CALLBACK (gtk_dir_selection_fileop_entry_changed
),
1464 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->action_area
),
1465 button
, TRUE
, TRUE
, 0);
1466 GTK_WIDGET_SET_FLAGS (button
, GTK_CAN_DEFAULT
);
1467 gtk_widget_show (button
);
1469 gtk_widget_show (dialog
);
1473 gtk_dir_selection_delete_dir_response (GtkDialog
*dialog
,
1477 GtkDirSelection
*fs
= data
;
1478 CompletionState
*cmpl_state
;
1481 gchar
*sys_full_path
;
1482 GError
*error
= NULL
;
1485 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1487 if (response_id
!= GTK_RESPONSE_OK
)
1489 gtk_widget_destroy (GTK_WIDGET (dialog
));
1493 cmpl_state
= (CompletionState
*) fs
->cmpl_state
;
1494 path
= cmpl_reference_position (cmpl_state
);
1496 full_path
= g_strconcat (path
, G_DIR_SEPARATOR_S
, fs
->fileop_file
, NULL
);
1497 sys_full_path
= g_filename_from_utf8 (full_path
, -1, NULL
, NULL
, &error
);
1500 if (g_error_matches (error
, G_CONVERT_ERROR
, G_CONVERT_ERROR_ILLEGAL_SEQUENCE
))
1501 buf
= g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
1504 buf
= g_strdup_printf (_("Error deleting file \"%s\": %s\n%s"),
1505 fs
->fileop_file
, error
->message
,
1506 _("It probably contains symbols not allowed in filenames."));
1508 gtk_dir_selection_fileop_error (fs
, buf
);
1509 g_error_free (error
);
1513 if (unlink (sys_full_path
) < 0)
1515 buf
= g_strdup_printf (_("Error deleting file \"%s\": %s"),
1516 fs
->fileop_file
, g_strerror (errno
));
1517 gtk_dir_selection_fileop_error (fs
, buf
);
1522 g_free (sys_full_path
);
1524 gtk_widget_destroy (fs
->fileop_dialog
);
1525 gtk_dir_selection_populate (fs
, "", FALSE
, TRUE
);
1529 gtk_dir_selection_delete_file (GtkWidget
*widget
,
1532 GtkDirSelection
*fs
= data
;
1534 const gchar
*filename
;
1536 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1538 if (fs
->fileop_dialog
)
1541 #ifdef G_WITH_CYGWIN
1542 translate_win32_path (fs
);
1545 filename
= gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
));
1546 if (strlen (filename
) < 1)
1549 g_free (fs
->fileop_file
);
1550 fs
->fileop_file
= g_strdup (filename
);
1553 fs
->fileop_dialog
= dialog
=
1554 gtk_message_dialog_new (GTK_WINDOW (fs
),
1555 GTK_WINDOW (fs
)->modal
? GTK_DIALOG_MODAL
: 0,
1556 GTK_MESSAGE_QUESTION
,
1558 _("Really delete file \"%s\" ?"), filename
);
1560 g_signal_connect (dialog
, "destroy",
1561 G_CALLBACK (gtk_dir_selection_fileop_destroy
),
1563 gtk_window_set_title (GTK_WINDOW (dialog
), _("Delete File"));
1564 gtk_window_set_position (GTK_WINDOW (dialog
), GTK_WIN_POS_MOUSE
);
1567 gtk_dialog_add_buttons (GTK_DIALOG (dialog
),
1568 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1569 GTK_STOCK_DELETE
, GTK_RESPONSE_OK
,
1572 gtk_dialog_set_default_response (GTK_DIALOG (dialog
), GTK_RESPONSE_CANCEL
);
1574 g_signal_connect (dialog
, "response",
1575 G_CALLBACK (gtk_dir_selection_delete_dir_response
),
1578 gtk_widget_show (dialog
);
1582 gtk_dir_selection_rename_dir_confirmed (GtkWidget
*widget
,
1585 GtkDirSelection
*fs
= data
;
1589 gchar
*new_filename
;
1590 gchar
*old_filename
;
1591 gchar
*sys_new_filename
;
1592 gchar
*sys_old_filename
;
1593 CompletionState
*cmpl_state
;
1594 GError
*error
= NULL
;
1596 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1598 file
= gtk_entry_get_text (GTK_ENTRY (fs
->fileop_entry
));
1599 cmpl_state
= (CompletionState
*) fs
->cmpl_state
;
1600 path
= cmpl_reference_position (cmpl_state
);
1602 new_filename
= g_strconcat (path
, G_DIR_SEPARATOR_S
, file
, NULL
);
1603 old_filename
= g_strconcat (path
, G_DIR_SEPARATOR_S
, fs
->fileop_file
, NULL
);
1605 sys_new_filename
= g_filename_from_utf8 (new_filename
, -1, NULL
, NULL
, &error
);
1608 if (g_error_matches (error
, G_CONVERT_ERROR
, G_CONVERT_ERROR_ILLEGAL_SEQUENCE
))
1609 buf
= g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), new_filename
);
1611 buf
= g_strdup_printf (_("Error renaming file to \"%s\": %s\n%s"),
1612 new_filename
, error
->message
,
1613 _("You probably used symbols not allowed in filenames."));
1614 gtk_dir_selection_fileop_error (fs
, buf
);
1615 g_error_free (error
);
1619 sys_old_filename
= g_filename_from_utf8 (old_filename
, -1, NULL
, NULL
, &error
);
1622 if (g_error_matches (error
, G_CONVERT_ERROR
, G_CONVERT_ERROR_ILLEGAL_SEQUENCE
))
1623 buf
= g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), old_filename
);
1625 buf
= g_strdup_printf (_("Error renaming file \"%s\": %s\n%s"),
1626 old_filename
, error
->message
,
1627 _("It probably contains symbols not allowed in filenames."));
1628 gtk_dir_selection_fileop_error (fs
, buf
);
1629 g_error_free (error
);
1633 if (rename (sys_old_filename
, sys_new_filename
) < 0)
1635 buf
= g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
1636 sys_old_filename
, sys_new_filename
,
1637 g_strerror (errno
));
1638 gtk_dir_selection_fileop_error (fs
, buf
);
1642 gtk_dir_selection_populate (fs
, "", FALSE
, FALSE
);
1643 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), file
);
1646 g_free (sys_old_filename
);
1649 g_free (new_filename
);
1650 g_free (old_filename
);
1651 g_free (sys_new_filename
);
1653 gtk_widget_destroy (fs
->fileop_dialog
);
1657 gtk_dir_selection_rename_file (GtkWidget
*widget
,
1660 GtkDirSelection
*fs
= data
;
1667 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1669 if (fs
->fileop_dialog
)
1672 g_free (fs
->fileop_file
);
1673 fs
->fileop_file
= g_strdup (gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
)));
1674 if (strlen (fs
->fileop_file
) < 1)
1678 fs
->fileop_dialog
= dialog
= gtk_dialog_new ();
1679 g_signal_connect (dialog
, "destroy",
1680 G_CALLBACK (gtk_dir_selection_fileop_destroy
),
1682 gtk_window_set_title (GTK_WINDOW (dialog
), _("Rename File"));
1683 gtk_window_set_position (GTK_WINDOW (dialog
), GTK_WIN_POS_MOUSE
);
1684 gtk_window_set_transient_for (GTK_WINDOW (dialog
), GTK_WINDOW (fs
));
1686 /* If file dialog is grabbed, grab option dialog */
1687 /* When option dialog closed, file dialog will be grabbed again */
1688 if (GTK_WINDOW (fs
)->modal
)
1689 gtk_window_set_modal (GTK_WINDOW (dialog
), TRUE
);
1691 vbox
= gtk_vbox_new (FALSE
, 0);
1692 gtk_container_set_border_width (GTK_CONTAINER (vbox
), 8);
1693 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), vbox
,
1695 gtk_widget_show(vbox
);
1697 buf
= g_strdup_printf (_("Rename file \"%s\" to:"), fs
->fileop_file
);
1698 label
= gtk_label_new (buf
);
1699 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.0);
1700 gtk_box_pack_start (GTK_BOX (vbox
), label
, FALSE
, FALSE
, 5);
1701 gtk_widget_show (label
);
1704 /* New filename entry */
1705 fs
->fileop_entry
= gtk_entry_new ();
1706 gtk_box_pack_start (GTK_BOX (vbox
), fs
->fileop_entry
,
1708 GTK_WIDGET_SET_FLAGS (fs
->fileop_entry
, GTK_CAN_DEFAULT
);
1709 gtk_widget_show (fs
->fileop_entry
);
1711 gtk_entry_set_text (GTK_ENTRY (fs
->fileop_entry
), fs
->fileop_file
);
1712 gtk_editable_select_region (GTK_EDITABLE (fs
->fileop_entry
),
1713 0, strlen (fs
->fileop_file
));
1716 button
= gtk_button_new_from_stock (GTK_STOCK_CANCEL
);
1717 g_signal_connect_swapped (button
, "clicked",
1718 G_CALLBACK (gtk_widget_destroy
),
1720 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->action_area
),
1721 button
, TRUE
, TRUE
, 0);
1722 GTK_WIDGET_SET_FLAGS (button
, GTK_CAN_DEFAULT
);
1723 gtk_widget_grab_default (button
);
1724 gtk_widget_show (button
);
1726 gtk_widget_grab_focus (fs
->fileop_entry
);
1728 button
= gtk_button_new_with_mnemonic (_("_Rename"));
1729 g_signal_connect (button
, "clicked",
1730 G_CALLBACK (gtk_dir_selection_rename_dir_confirmed
),
1732 g_signal_connect (fs
->fileop_entry
, "changed",
1733 G_CALLBACK (gtk_dir_selection_fileop_entry_changed
),
1736 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->action_area
),
1737 button
, TRUE
, TRUE
, 0);
1738 GTK_WIDGET_SET_FLAGS (button
, GTK_CAN_DEFAULT
);
1739 gtk_widget_show (button
);
1741 gtk_widget_show (dialog
);
1745 gtk_dir_selection_insert_text (GtkWidget
*widget
,
1746 const gchar
*new_text
,
1747 gint new_text_length
,
1753 filename
= g_filename_from_utf8 (new_text
, new_text_length
, NULL
, NULL
, NULL
);
1757 gdk_display_beep (gtk_widget_get_display (widget
));
1758 g_signal_stop_emission_by_name (widget
, "insert_text");
1768 gtk_dir_selection_update_fileops (GtkDirSelection
*fs
)
1772 if (!fs
->selection_entry
)
1775 sensitive
= !entry_is_empty (GTK_ENTRY (fs
->selection_entry
));
1777 if (fs
->fileop_del_file
)
1778 gtk_widget_set_sensitive (fs
->fileop_del_file
, sensitive
);
1780 if (fs
->fileop_ren_file
)
1781 gtk_widget_set_sensitive (fs
->fileop_ren_file
, sensitive
);
1785 gtk_dir_selection_key_press (GtkWidget
*widget
,
1789 GtkDirSelection
*fs
;
1792 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1793 g_return_val_if_fail (event
!= NULL
, FALSE
);
1795 if ((event
->keyval
== GDK_Tab
|| event
->keyval
== GDK_KP_Tab
) &&
1796 (event
->state
& gtk_accelerator_get_default_mod_mask ()) == 0)
1798 fs
= GTK_DIR_SELECTION (user_data
);
1799 #ifdef G_WITH_CYGWIN
1800 translate_win32_path (fs
);
1802 text
= g_strdup (gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
)));
1804 gtk_dir_selection_populate (fs
, text
, TRUE
, TRUE
);
1815 gtk_dir_selection_history_callback (GtkWidget
*widget
,
1818 GtkDirSelection
*fs
= data
;
1819 HistoryCallbackArg
*callback_arg
;
1822 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1824 list
= fs
->history_list
;
1827 callback_arg
= list
->data
;
1829 if (callback_arg
->menu_item
== widget
)
1831 gtk_dir_selection_populate (fs
, callback_arg
->directory
, FALSE
, FALSE
);
1840 gtk_dir_selection_update_history_menu (GtkDirSelection
*fs
,
1841 gchar
*current_directory
)
1843 HistoryCallbackArg
*callback_arg
;
1844 GtkWidget
*menu_item
;
1850 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1851 g_return_if_fail (current_directory
!= NULL
);
1853 list
= fs
->history_list
;
1855 if (fs
->history_menu
)
1858 callback_arg
= list
->data
;
1859 g_free (callback_arg
->directory
);
1860 g_free (callback_arg
);
1863 g_list_free (fs
->history_list
);
1864 fs
->history_list
= NULL
;
1866 gtk_widget_destroy (fs
->history_menu
);
1869 fs
->history_menu
= gtk_menu_new ();
1871 current_dir
= g_strdup (current_directory
);
1873 dir_len
= strlen (current_dir
);
1875 for (i
= dir_len
; i
>= 0; i
--)
1877 /* the i == dir_len is to catch the full path for the first
1879 if ( (current_dir
[i
] == G_DIR_SEPARATOR
) || (i
== dir_len
))
1881 /* another small hack to catch the full path */
1883 current_dir
[i
+ 1] = '\0';
1884 #ifdef G_WITH_CYGWIN
1885 if (!strcmp (current_dir
, "//"))
1888 menu_item
= gtk_menu_item_new_with_label (current_dir
);
1890 callback_arg
= g_new (HistoryCallbackArg
, 1);
1891 callback_arg
->menu_item
= menu_item
;
1893 /* since the autocompletion gets confused if you don't
1894 * supply a trailing '/' on a dir entry, set the full
1895 * (current) path to "" which just refreshes the filesel */
1898 callback_arg
->directory
= g_strdup ("");
1902 callback_arg
->directory
= g_strdup (current_dir
);
1905 fs
->history_list
= g_list_append (fs
->history_list
, callback_arg
);
1907 g_signal_connect (menu_item
, "activate",
1908 G_CALLBACK (gtk_dir_selection_history_callback
),
1910 gtk_menu_shell_append (GTK_MENU_SHELL (fs
->history_menu
), menu_item
);
1911 gtk_widget_show (menu_item
);
1915 gtk_option_menu_set_menu (GTK_OPTION_MENU (fs
->history_pulldown
),
1917 g_free (current_dir
);
1921 get_real_filename (gchar
*filename
,
1924 #ifdef G_WITH_CYGWIN
1925 /* Check to see if the selection was a drive selector */
1926 if (isalpha (filename
[0]) && (filename
[1] == ':'))
1928 gchar temp_filename
[PATH_MAX
];
1931 cygwin_conv_to_posix_path (filename
, temp_filename
);
1933 /* we need trailing '/'. */
1934 len
= strlen (temp_filename
);
1935 if (len
> 0 && temp_filename
[len
-1] != '/')
1937 temp_filename
[len
] = '/';
1938 temp_filename
[len
+1] = '\0';
1944 return g_strdup (temp_filename
);
1946 #endif /* G_WITH_CYGWIN */
1951 gtk_dir_selection_file_activate (GtkTreeView
*tree_view
,
1953 GtkTreeViewColumn
*column
,
1956 GtkDirSelection
*fs
= GTK_DIR_SELECTION (user_data
);
1957 GtkTreeModel
*model
= gtk_tree_view_get_model (tree_view
);
1961 gtk_tree_model_get_iter (model
, &iter
, path
);
1962 gtk_tree_model_get (model
, &iter
, FILE_COLUMN
, &filename
, -1);
1963 filename
= get_real_filename (filename
, TRUE
);
1964 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), filename
);
1965 gtk_button_clicked (GTK_BUTTON (fs
->ok_button
));
1971 gtk_dir_selection_dir_activate (GtkTreeView
*tree_view
,
1973 GtkTreeViewColumn
*column
,
1976 GtkDirSelection
*fs
= GTK_DIR_SELECTION (user_data
);
1977 GtkTreeModel
*model
= gtk_tree_view_get_model (tree_view
);
1981 gtk_tree_model_get_iter (model
, &iter
, path
);
1982 gtk_tree_model_get (model
, &iter
, DIR_COLUMN
, &filename
, -1);
1983 filename
= get_real_filename (filename
, TRUE
);
1984 gtk_dir_selection_populate (fs
, filename
, FALSE
, FALSE
);
1988 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
1991 win32_gtk_add_drives_to_dir_list (GtkListStore
*model
)
1995 char formatBuffer
[128];
1998 /* Get the drives string */
1999 GetLogicalDriveStrings (sizeof (buffer
), buffer
);
2001 /* Add the drives as necessary */
2003 while (*textPtr
!= '\0')
2005 /* Ignore floppies (?) */
2006 if ((tolower (textPtr
[0]) != 'a') && (tolower (textPtr
[0]) != 'b'))
2008 /* Build the actual displayable string */
2009 g_snprintf (formatBuffer
, sizeof (formatBuffer
), "%c:\\", toupper (textPtr
[0]));
2011 /* Add to the list */
2012 gtk_list_store_append (model
, &iter
);
2013 gtk_list_store_set (model
, &iter
, DIR_COLUMN
, formatBuffer
, -1);
2015 textPtr
+= (strlen (textPtr
) + 1);
2021 escape_underscores (const gchar
*str
)
2023 GString
*result
= g_string_new (NULL
);
2027 g_string_append_c (result
, '_');
2029 g_string_append_c (result
, *str
);
2033 return g_string_free (result
, FALSE
);
2037 gtk_dir_selection_populate (GtkDirSelection
*fs
,
2039 gboolean try_complete
,
2040 gboolean reset_entry
)
2042 CompletionState
*cmpl_state
;
2043 PossibleCompletion
* poss
;
2045 GtkListStore
*dir_model
;
2046 GtkListStore
*file_model
;
2048 gchar
* rem_path
= rel_path
;
2050 gint did_recurse
= FALSE
;
2051 gint possible_count
= 0;
2052 gint selection_index
= -1;
2054 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
2056 cmpl_state
= (CompletionState
*) fs
->cmpl_state
;
2057 poss
= cmpl_completion_matches (rel_path
, &rem_path
, cmpl_state
);
2059 if (!cmpl_state_okay (cmpl_state
))
2061 /* Something went wrong. */
2062 gtk_dir_selection_abort (fs
);
2066 g_assert (cmpl_state
->reference_dir
);
2068 dir_model
= GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs
->dir_list
)));
2069 file_model
= GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs
->file_list
)));
2071 gtk_list_store_clear (dir_model
);
2072 gtk_list_store_clear (file_model
);
2074 /* Set the dir list to include ./ and ../ */
2075 gtk_list_store_append (dir_model
, &iter
);
2076 gtk_list_store_set (dir_model
, &iter
, DIR_COLUMN
, "." G_DIR_SEPARATOR_S
, -1);
2077 gtk_list_store_append (dir_model
, &iter
);
2078 gtk_list_store_set (dir_model
, &iter
, DIR_COLUMN
, ".." G_DIR_SEPARATOR_S
, -1);
2082 if (cmpl_is_a_completion (poss
))
2084 possible_count
+= 1;
2086 filename
= cmpl_this_completion (poss
);
2088 if (cmpl_is_directory (poss
))
2090 if (strcmp (filename
, "." G_DIR_SEPARATOR_S
) != 0 &&
2091 strcmp (filename
, ".." G_DIR_SEPARATOR_S
) != 0)
2093 gtk_list_store_append (dir_model
, &iter
);
2094 gtk_list_store_set (dir_model
, &iter
, DIR_COLUMN
, filename
, -1);
2099 gtk_list_store_append (file_model
, &iter
);
2100 gtk_list_store_set (file_model
, &iter
, DIR_COLUMN
, filename
, -1);
2104 poss
= cmpl_next_completion (cmpl_state
);
2107 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
2108 /* For Windows, add drives as potential selections */
2109 win32_gtk_add_drives_to_dir_list (dir_model
);
2112 /* File lists are set. */
2114 g_assert (cmpl_state
->reference_dir
);
2119 /* User is trying to complete filenames, so advance the user's input
2120 * string to the updated_text, which is the common leading substring
2121 * of all possible completions, and if its a directory attempt
2122 * attempt completions in it. */
2124 if (cmpl_updated_text (cmpl_state
)[0])
2127 if (cmpl_updated_dir (cmpl_state
))
2129 gchar
* dir_name
= g_strdup (cmpl_updated_text (cmpl_state
));
2133 gtk_dir_selection_populate (fs
, dir_name
, TRUE
, TRUE
);
2139 if (fs
->selection_entry
)
2140 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
),
2141 cmpl_updated_text (cmpl_state
));
2146 selection_index
= cmpl_last_valid_char (cmpl_state
) -
2147 (strlen (rel_path
) - strlen (rem_path
));
2148 if (fs
->selection_entry
)
2149 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), rem_path
);
2152 else if (reset_entry
)
2154 if (fs
->selection_entry
)
2155 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), "");
2160 if (fs
->selection_entry
)
2161 gtk_editable_set_position (GTK_EDITABLE (fs
->selection_entry
),
2164 if (fs
->selection_entry
)
2166 char *escaped
= escape_underscores (cmpl_reference_position (cmpl_state
));
2167 sel_text
= g_strconcat (_("_Selection: "), escaped
, NULL
);
2170 gtk_label_set_text_with_mnemonic (GTK_LABEL (fs
->selection_text
), sel_text
);
2171 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), cmpl_reference_position (cmpl_state
));
2175 if (fs
->history_pulldown
)
2177 gtk_dir_selection_update_history_menu (fs
, cmpl_reference_position (cmpl_state
));
2184 gtk_dir_selection_abort (GtkDirSelection
*fs
)
2188 g_snprintf (err_buf
, sizeof (err_buf
), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno
));
2190 /* BEEP gdk_beep(); */
2192 if (fs
->selection_entry
)
2193 gtk_label_set_text (GTK_LABEL (fs
->selection_text
), err_buf
);
2197 * gtk_dir_selection_set_select_multiple:
2198 * @filesel: a #GtkDirSelection
2199 * @select_multiple: whether or not the user is allowed to select multiple
2200 * files in the file list.
2202 * Sets whether the user is allowed to select multiple files in the file list.
2203 * Use gtk_dir_selection_get_selections () to get the list of selected files.
2206 gtk_dir_selection_set_select_multiple (GtkDirSelection
*filesel
,
2207 gboolean select_multiple
)
2209 GtkTreeSelection
*sel
;
2210 GtkSelectionMode mode
;
2212 g_return_if_fail (GTK_IS_DIR_SELECTION (filesel
));
2214 sel
= gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel
->file_list
));
2216 mode
= select_multiple
? GTK_SELECTION_MULTIPLE
: GTK_SELECTION_SINGLE
;
2218 if (mode
!= gtk_tree_selection_get_mode (sel
))
2220 gtk_tree_selection_set_mode (sel
, mode
);
2222 g_object_notify (G_OBJECT (filesel
), "select-multiple");
2227 * gtk_dir_selection_get_select_multiple:
2228 * @filesel: a #GtkDirSelection
2230 * Determines whether or not the user is allowed to select multiple files in
2231 * the file list. See gtk_dir_selection_set_select_multiple().
2233 * Return value: %TRUE if the user is allowed to select multiple files in the
2237 gtk_dir_selection_get_select_multiple (GtkDirSelection
*filesel
)
2239 GtkTreeSelection
*sel
;
2241 g_return_val_if_fail (GTK_IS_DIR_SELECTION (filesel
), FALSE
);
2243 sel
= gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel
->file_list
));
2244 return (gtk_tree_selection_get_mode (sel
) == GTK_SELECTION_MULTIPLE
);
2248 multiple_changed_foreach (GtkTreeModel
*model
,
2253 GPtrArray
*names
= data
;
2256 gtk_tree_model_get (model
, iter
, FILE_COLUMN
, &filename
, -1);
2258 g_ptr_array_add (names
, filename
);
2262 free_selected_names (GPtrArray
*names
)
2266 for (i
= 0; i
< names
->len
; i
++)
2267 g_free (g_ptr_array_index (names
, i
));
2269 g_ptr_array_free (names
, TRUE
);
2273 gtk_dir_selection_file_changed (GtkTreeSelection
*selection
,
2276 GtkDirSelection
*fs
= GTK_DIR_SELECTION (user_data
);
2277 GPtrArray
*new_names
;
2282 new_names
= g_ptr_array_sized_new (8);
2284 gtk_tree_selection_selected_foreach (selection
,
2285 multiple_changed_foreach
,
2288 /* nothing selected */
2289 if (new_names
->len
== 0)
2291 g_ptr_array_free (new_names
, TRUE
);
2293 if (fs
->selected_names
!= NULL
)
2295 free_selected_names (fs
->selected_names
);
2296 fs
->selected_names
= NULL
;
2299 goto maybe_clear_entry
;
2302 if (new_names
->len
!= 1)
2304 GPtrArray
*old_names
= fs
->selected_names
;
2306 if (old_names
!= NULL
)
2308 /* A common case is selecting a range of files from top to bottom,
2309 * so quickly check for that to avoid looping over the entire list
2311 if (compare_filenames (g_ptr_array_index (old_names
, old_names
->len
- 1),
2312 g_ptr_array_index (new_names
, new_names
->len
- 1)) != 0)
2313 index
= new_names
->len
- 1;
2319 /* do a quick diff, stopping at the first file not in the
2322 while (i
< old_names
->len
&& j
< new_names
->len
)
2324 cmp
= compare_filenames (g_ptr_array_index (old_names
, i
),
2325 g_ptr_array_index (new_names
, j
));
2342 /* we ran off the end of the old list */
2343 if (index
== -1 && i
< new_names
->len
)
2349 /* A phantom anchor still exists at the point where the last item
2350 * was selected, which is used for subsequent range selections.
2351 * So search up from there.
2353 if (fs
->last_selected
&&
2354 compare_filenames (fs
->last_selected
,
2355 g_ptr_array_index (new_names
, 0)) == 0)
2356 index
= new_names
->len
- 1;
2364 if (fs
->selected_names
!= NULL
)
2365 free_selected_names (fs
->selected_names
);
2367 fs
->selected_names
= new_names
;
2371 if (fs
->last_selected
!= NULL
)
2372 g_free (fs
->last_selected
);
2374 fs
->last_selected
= g_strdup (g_ptr_array_index (new_names
, index
));
2375 filename
= get_real_filename (fs
->last_selected
, FALSE
);
2377 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), filename
);
2379 if (filename
!= fs
->last_selected
)
2387 entry
= gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
));
2388 if ((entry
!= NULL
) && (fs
->last_selected
!= NULL
) &&
2389 (compare_filenames (entry
, fs
->last_selected
) == 0))
2390 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), "");
2394 gtk_dir_selection_dir_changed (GtkTreeSelection
*selection
,
2397 GtkDirSelection
*fs
= GTK_DIR_SELECTION (user_data
);
2398 GPtrArray
*new_names
;
2403 new_names
= g_ptr_array_sized_new (8);
2405 gtk_tree_selection_selected_foreach (selection
,
2406 multiple_changed_foreach
,
2409 /* nothing selected */
2410 if (new_names
->len
== 0)
2412 g_ptr_array_free (new_names
, TRUE
);
2414 if (fs
->selected_names
!= NULL
)
2416 free_selected_names (fs
->selected_names
);
2417 fs
->selected_names
= NULL
;
2420 goto maybe_clear_entry
;
2423 if (new_names
->len
!= 1)
2425 GPtrArray
*old_names
= fs
->selected_names
;
2427 if (old_names
!= NULL
)
2429 /* A common case is selecting a range of files from top to bottom,
2430 * so quickly check for that to avoid looping over the entire list
2432 if (compare_filenames (g_ptr_array_index (old_names
, old_names
->len
- 1),
2433 g_ptr_array_index (new_names
, new_names
->len
- 1)) != 0)
2434 index
= new_names
->len
- 1;
2440 /* do a quick diff, stopping at the first file not in the
2443 while (i
< old_names
->len
&& j
< new_names
->len
)
2445 cmp
= compare_filenames (g_ptr_array_index (old_names
, i
),
2446 g_ptr_array_index (new_names
, j
));
2463 /* we ran off the end of the old list */
2464 if (index
== -1 && i
< new_names
->len
)
2470 /* A phantom anchor still exists at the point where the last item
2471 * was selected, which is used for subsequent range selections.
2472 * So search up from there.
2474 if (fs
->last_selected
&&
2475 compare_filenames (fs
->last_selected
,
2476 g_ptr_array_index (new_names
, 0)) == 0)
2477 index
= new_names
->len
- 1;
2485 if (fs
->selected_names
!= NULL
)
2486 free_selected_names (fs
->selected_names
);
2488 fs
->selected_names
= new_names
;
2494 err
= gtk_label_get_text (GTK_LABEL (fs
->selection_text
));
2495 err
+= 11; //pass over "Selection: "
2496 sprintf(str
,"%s",err
);
2499 if (fs
->last_selected
!= NULL
)
2500 g_free (fs
->last_selected
);
2502 fs
->last_selected
= g_strdup (g_ptr_array_index (new_names
, index
));
2503 filename
= get_real_filename (fs
->last_selected
, FALSE
);
2506 strcat(str
,filename
);
2507 str
[strlen(str
)-1] = '\0';
2509 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), str
);
2511 if (filename
!= fs
->last_selected
)
2519 entry
= gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
));
2520 if ((entry
!= NULL
) && (fs
->last_selected
!= NULL
) &&
2521 (compare_filenames (entry
, fs
->last_selected
) == 0))
2522 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), "");
2526 * gtk_dir_selection_get_selections:
2527 * @filesel: a #GtkDirSelection
2529 * Retrieves the list of file selections the user has made in the dialog box.
2530 * This function is intended for use when the user can select multiple files
2531 * in the file list. The first file in the list is equivalent to what
2532 * gtk_dir_selection_get_filename() would return.
2534 * The filenames are in the encoding of g_filename_from_utf8(), which may or
2535 * may not be the same as that used by GTK+ (UTF-8). To convert to UTF-8, call
2536 * g_filename_to_utf8() on each string.
2538 * Return value: a newly-allocated %NULL-terminated array of strings. Use
2539 * g_strfreev() to free it.
2542 gtk_dir_selection_get_selections (GtkDirSelection
*filesel
)
2546 gchar
*filename
, *dirname
;
2547 gchar
*current
, *buf
;
2549 gboolean unselected_entry
;
2551 g_return_val_if_fail (GTK_IS_DIR_SELECTION (filesel
), NULL
);
2553 filename
= g_strdup (gtk_dir_selection_get_filename (filesel
));
2555 if (strlen (filename
) == 0)
2561 names
= filesel
->selected_names
;
2564 selections
= g_new (gchar
*, names
->len
+ 2);
2566 selections
= g_new (gchar
*, 2);
2569 unselected_entry
= TRUE
;
2573 dirname
= g_path_get_dirname (filename
);
2575 for (i
= 0; i
< names
->len
; i
++)
2577 buf
= g_filename_from_utf8 (g_ptr_array_index (names
, i
), -1,
2579 current
= g_build_filename (dirname
, buf
, NULL
);
2582 selections
[count
++] = current
;
2584 if (unselected_entry
&& compare_filenames (current
, filename
) == 0)
2585 unselected_entry
= FALSE
;
2591 if (unselected_entry
)
2592 selections
[count
++] = filename
;
2596 selections
[count
] = NULL
;
2601 /**********************************************************************/
2602 /* External Interface */
2603 /**********************************************************************/
2605 /* The four completion state selectors
2608 cmpl_updated_text (CompletionState
*cmpl_state
)
2610 return cmpl_state
->updated_text
;
2614 cmpl_updated_dir (CompletionState
*cmpl_state
)
2616 return cmpl_state
->re_complete
;
2620 cmpl_reference_position (CompletionState
*cmpl_state
)
2622 return cmpl_state
->reference_dir
->fullname
;
2626 cmpl_last_valid_char (CompletionState
*cmpl_state
)
2628 return cmpl_state
->last_valid_char
;
2632 cmpl_completion_fullname (const gchar
*text
,
2633 CompletionState
*cmpl_state
)
2635 static const char nothing
[2] = "";
2637 if (!cmpl_state_okay (cmpl_state
))
2641 else if (g_path_is_absolute (text
))
2643 strcpy (cmpl_state
->updated_text
, text
);
2646 else if (text
[0] == '~')
2651 dir
= open_user_dir (text
, cmpl_state
);
2655 /* spencer says just return ~something, so
2656 * for now just do it. */
2657 strcpy (cmpl_state
->updated_text
, text
);
2662 strcpy (cmpl_state
->updated_text
, dir
->fullname
);
2664 slash
= strchr (text
, G_DIR_SEPARATOR
);
2667 strcat (cmpl_state
->updated_text
, slash
);
2673 strcpy (cmpl_state
->updated_text
, cmpl_state
->reference_dir
->fullname
);
2674 if (cmpl_state
->updated_text
[strlen (cmpl_state
->updated_text
) - 1] != G_DIR_SEPARATOR
)
2675 strcat (cmpl_state
->updated_text
, G_DIR_SEPARATOR_S
);
2676 strcat (cmpl_state
->updated_text
, text
);
2679 return cmpl_state
->updated_text
;
2682 /* The three completion selectors
2685 cmpl_this_completion (PossibleCompletion
* pc
)
2691 cmpl_is_directory (PossibleCompletion
* pc
)
2693 return pc
->is_directory
;
2697 cmpl_is_a_completion (PossibleCompletion
* pc
)
2699 return pc
->is_a_completion
;
2702 /**********************************************************************/
2703 /* Construction, deletion */
2704 /**********************************************************************/
2706 static CompletionState
*
2707 cmpl_init_state (void)
2709 gchar
*sys_getcwd_buf
;
2711 CompletionState
*new_state
;
2713 new_state
= g_new (CompletionState
, 1);
2715 /* g_get_current_dir() returns a string in the "system" charset */
2716 sys_getcwd_buf
= g_get_current_dir ();
2717 utf8_cwd
= g_filename_to_utf8 (sys_getcwd_buf
, -1, NULL
, NULL
, NULL
);
2718 g_free (sys_getcwd_buf
);
2722 new_state
->reference_dir
= NULL
;
2723 new_state
->completion_dir
= NULL
;
2724 new_state
->active_completion_dir
= NULL
;
2725 new_state
->directory_storage
= NULL
;
2726 new_state
->directory_sent_storage
= NULL
;
2727 new_state
->last_valid_char
= 0;
2728 new_state
->updated_text
= g_new (gchar
, MAXPATHLEN
);
2729 new_state
->updated_text_alloc
= MAXPATHLEN
;
2730 new_state
->the_completion
.text
= g_new (gchar
, MAXPATHLEN
);
2731 new_state
->the_completion
.text_alloc
= MAXPATHLEN
;
2732 new_state
->user_dir_name_buffer
= NULL
;
2733 new_state
->user_directories
= NULL
;
2735 new_state
->reference_dir
= open_dir (utf8_cwd
, new_state
);
2737 if (!new_state
->reference_dir
)
2739 /* Directories changing from underneath us, grumble */
2740 strcpy (utf8_cwd
, G_DIR_SEPARATOR_S
);
2749 cmpl_free_dir_list (GList
* dp0
)
2755 free_dir (dp
->data
);
2763 cmpl_free_dir_sent_list (GList
* dp0
)
2769 free_dir_sent (dp
->data
);
2777 cmpl_free_state (CompletionState
* cmpl_state
)
2779 g_return_if_fail (cmpl_state
!= NULL
);
2781 cmpl_free_dir_list (cmpl_state
->directory_storage
);
2782 cmpl_free_dir_sent_list (cmpl_state
->directory_sent_storage
);
2784 if (cmpl_state
->user_dir_name_buffer
)
2785 g_free (cmpl_state
->user_dir_name_buffer
);
2786 if (cmpl_state
->user_directories
)
2787 g_free (cmpl_state
->user_directories
);
2788 if (cmpl_state
->the_completion
.text
)
2789 g_free (cmpl_state
->the_completion
.text
);
2790 if (cmpl_state
->updated_text
)
2791 g_free (cmpl_state
->updated_text
);
2793 g_free (cmpl_state
);
2797 free_dir (CompletionDir
* dir
)
2799 g_free (dir
->cmpl_text
);
2800 g_free (dir
->fullname
);
2805 free_dir_sent (CompletionDirSent
* sent
)
2808 for (i
= 0; i
< sent
->entry_count
; i
++)
2810 g_free (sent
->entries
[i
].entry_name
);
2811 g_free (sent
->entries
[i
].sort_key
);
2813 g_free (sent
->entries
);
2818 prune_memory_usage (CompletionState
*cmpl_state
)
2820 GList
* cdsl
= cmpl_state
->directory_sent_storage
;
2821 GList
* cdl
= cmpl_state
->directory_storage
;
2825 for (; cdsl
&& len
< CMPL_DIRECTORY_CACHE_SIZE
; len
+= 1)
2830 cmpl_free_dir_sent_list (cdsl
->next
);
2834 cmpl_state
->directory_storage
= NULL
;
2837 if (cdl
->data
== cmpl_state
->reference_dir
)
2838 cmpl_state
->directory_storage
= g_list_prepend (NULL
, cdl
->data
);
2840 free_dir (cdl
->data
);
2847 /**********************************************************************/
2848 /* The main entrances. */
2849 /**********************************************************************/
2851 static PossibleCompletion
*
2852 cmpl_completion_matches (gchar
*text_to_complete
,
2853 gchar
**remaining_text
,
2854 CompletionState
*cmpl_state
)
2856 PossibleCompletion
*poss
;
2858 prune_memory_usage (cmpl_state
);
2860 g_assert (text_to_complete
!= NULL
);
2862 cmpl_state
->user_completion_index
= -1;
2863 cmpl_state
->last_completion_text
= text_to_complete
;
2864 cmpl_state
->the_completion
.text
[0] = 0;
2865 cmpl_state
->last_valid_char
= 0;
2866 cmpl_state
->updated_text_len
= -1;
2867 cmpl_state
->updated_text
[0] = 0;
2868 cmpl_state
->re_complete
= FALSE
;
2871 first_slash
= strchr (text_to_complete
, G_DIR_SEPARATOR
);
2873 if (text_to_complete
[0] == '~' && !first_slash
)
2875 /* Text starts with ~ and there is no slash, show all the
2876 * home directory completions.
2878 poss
= attempt_homedir_completion (text_to_complete
, cmpl_state
);
2880 update_cmpl (poss
, cmpl_state
);
2885 cmpl_state
->reference_dir
=
2886 open_ref_dir (text_to_complete
, remaining_text
, cmpl_state
);
2888 if (!cmpl_state
->reference_dir
)
2891 cmpl_state
->completion_dir
=
2892 find_completion_dir (*remaining_text
, remaining_text
, cmpl_state
);
2894 cmpl_state
->last_valid_char
= *remaining_text
- text_to_complete
;
2896 if (!cmpl_state
->completion_dir
)
2899 cmpl_state
->completion_dir
->cmpl_index
= -1;
2900 cmpl_state
->completion_dir
->cmpl_parent
= NULL
;
2901 cmpl_state
->completion_dir
->cmpl_text
= g_strdup (*remaining_text
);
2903 cmpl_state
->active_completion_dir
= cmpl_state
->completion_dir
;
2905 cmpl_state
->reference_dir
= cmpl_state
->completion_dir
;
2907 poss
= attempt_dir_completion (cmpl_state
);
2909 update_cmpl (poss
, cmpl_state
);
2914 static PossibleCompletion
*
2915 cmpl_next_completion (CompletionState
* cmpl_state
)
2917 PossibleCompletion
* poss
= NULL
;
2919 cmpl_state
->the_completion
.text
[0] = 0;
2922 if (cmpl_state
->user_completion_index
>= 0)
2923 poss
= attempt_homedir_completion (cmpl_state
->last_completion_text
, cmpl_state
);
2925 poss
= attempt_dir_completion (cmpl_state
);
2927 poss
= attempt_dir_completion (cmpl_state
);
2930 update_cmpl (poss
, cmpl_state
);
2935 /**********************************************************************/
2936 /* Directory Operations */
2937 /**********************************************************************/
2939 /* Open the directory where completion will begin from, if possible. */
2940 static CompletionDir
*
2941 open_ref_dir (gchar
*text_to_complete
,
2942 gchar
**remaining_text
,
2943 CompletionState
*cmpl_state
)
2946 CompletionDir
*new_dir
;
2948 first_slash
= strchr (text_to_complete
, G_DIR_SEPARATOR
);
2950 #ifdef G_WITH_CYGWIN
2951 if (text_to_complete
[0] == '/' && text_to_complete
[1] == '/')
2954 g_snprintf (root_dir
, sizeof (root_dir
), "//%c", text_to_complete
[2]);
2956 new_dir
= open_dir (root_dir
, cmpl_state
);
2959 *remaining_text
= text_to_complete
+ 4;
2967 else if (text_to_complete
[0] == '~')
2969 new_dir
= open_user_dir (text_to_complete
, cmpl_state
);
2974 *remaining_text
= first_slash
+ 1;
2976 *remaining_text
= text_to_complete
+ strlen (text_to_complete
);
2984 else if (g_path_is_absolute (text_to_complete
) || !cmpl_state
->reference_dir
)
2986 gchar
*tmp
= g_strdup (text_to_complete
);
2990 while (*p
&& *p
!= '*' && *p
!= '?')
2994 p
= strrchr (tmp
, G_DIR_SEPARATOR
);
3002 new_dir
= open_dir (tmp
, cmpl_state
);
3005 *remaining_text
= text_to_complete
+
3006 ((p
== tmp
+ 1) ? (p
- tmp
) : (p
+ 1 - tmp
));
3010 /* If no possible candidates, use the cwd */
3011 gchar
*sys_curdir
= g_get_current_dir ();
3012 gchar
*utf8_curdir
= g_filename_to_utf8 (sys_curdir
, -1, NULL
, NULL
, NULL
);
3014 g_free (sys_curdir
);
3016 new_dir
= open_dir (utf8_curdir
, cmpl_state
);
3019 *remaining_text
= text_to_complete
;
3021 g_free (utf8_curdir
);
3028 *remaining_text
= text_to_complete
;
3030 new_dir
= open_dir (cmpl_state
->reference_dir
->fullname
, cmpl_state
);
3035 new_dir
->cmpl_index
= -1;
3036 new_dir
->cmpl_parent
= NULL
;
3044 /* open a directory by user name */
3045 static CompletionDir
*
3046 open_user_dir (const gchar
*text_to_complete
,
3047 CompletionState
*cmpl_state
)
3049 CompletionDir
*result
;
3053 g_assert (text_to_complete
&& text_to_complete
[0] == '~');
3055 first_slash
= strchr (text_to_complete
, G_DIR_SEPARATOR
);
3058 cmp_len
= first_slash
- text_to_complete
- 1;
3060 cmp_len
= strlen (text_to_complete
+ 1);
3065 const gchar
*homedir
= g_get_home_dir ();
3066 gchar
*utf8_homedir
= g_filename_to_utf8 (homedir
, -1, NULL
, NULL
, NULL
);
3069 result
= open_dir (utf8_homedir
, cmpl_state
);
3073 g_free (utf8_homedir
);
3078 gchar
* copy
= g_new (char, cmp_len
+ 1);
3082 strncpy (copy
, text_to_complete
+ 1, cmp_len
);
3084 pwd
= getpwnam (copy
);
3091 utf8_dir
= g_filename_to_utf8 (pwd
->pw_dir
, -1, NULL
, NULL
, NULL
);
3092 result
= open_dir (utf8_dir
, cmpl_state
);
3100 /* open a directory relative the the current relative directory */
3101 static CompletionDir
*
3102 open_relative_dir (gchar
*dir_name
,
3104 CompletionState
*cmpl_state
)
3106 CompletionDir
*result
;
3109 path
= g_string_sized_new (dir
->fullname_len
+ strlen (dir_name
) + 10);
3110 g_string_assign (path
, dir
->fullname
);
3112 if (dir
->fullname_len
> 1
3113 && path
->str
[dir
->fullname_len
- 1] != G_DIR_SEPARATOR
)
3114 g_string_append_c (path
, G_DIR_SEPARATOR
);
3115 g_string_append (path
, dir_name
);
3117 result
= open_dir (path
->str
, cmpl_state
);
3119 g_string_free (path
, TRUE
);
3124 /* after the cache lookup fails, really open a new directory */
3125 static CompletionDirSent
*
3126 open_new_dir (gchar
*dir_name
,
3128 gboolean stat_subdirs
)
3130 CompletionDirSent
*sent
;
3133 GError
*error
= NULL
;
3134 gint entry_count
= 0;
3137 struct stat ent_sbuf
;
3139 gchar
*sys_dir_name
;
3141 sent
= g_new (CompletionDirSent
, 1);
3142 sent
->mtime
= sbuf
->st_mtime
;
3143 sent
->inode
= sbuf
->st_ino
;
3144 sent
->device
= sbuf
->st_dev
;
3146 path
= g_string_sized_new (2*MAXPATHLEN
+ 10);
3148 sys_dir_name
= g_filename_from_utf8 (dir_name
, -1, NULL
, NULL
, NULL
);
3151 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3155 directory
= g_dir_open (sys_dir_name
, 0, &error
);
3158 cmpl_errno
= error
->code
; /* ??? */
3159 g_free (sys_dir_name
);
3163 while ((dirent
= g_dir_read_name (directory
)) != NULL
)
3166 entry_count
+= 2; /* For ".",".." */
3168 sent
->entries
= g_new (CompletionDirEntry
, entry_count
);
3169 sent
->entry_count
= entry_count
;
3171 g_dir_rewind (directory
);
3173 for (i
= 0; i
< entry_count
; i
+= 1)
3181 dirent
= g_dir_read_name (directory
);
3182 if (!dirent
) /* Directory changed */
3186 sent
->entries
[n_entries
].entry_name
= g_filename_to_utf8 (dirent
, -1, NULL
, NULL
, &error
);
3187 if (sent
->entries
[n_entries
].entry_name
== NULL
3188 || !g_utf8_validate (sent
->entries
[n_entries
].entry_name
, -1, NULL
))
3190 gchar
*escaped_str
= g_strescape (dirent
, NULL
);
3191 g_message (_("The filename \"%s\" couldn't be converted to UTF-8 "
3192 "(try setting the environment variable G_BROKEN_FILENAMES): %s"),
3194 error
->message
? error
->message
: _("Invalid Utf-8"));
3195 g_free (escaped_str
);
3196 g_clear_error (&error
);
3199 g_clear_error (&error
);
3201 sent
->entries
[n_entries
].sort_key
= g_utf8_collate_key (sent
->entries
[n_entries
].entry_name
, -1);
3203 g_string_assign (path
, sys_dir_name
);
3204 if (path
->str
[path
->len
-1] != G_DIR_SEPARATOR
)
3206 g_string_append_c (path
, G_DIR_SEPARATOR
);
3208 g_string_append (path
, dirent
);
3212 /* Here we know path->str is a "system charset" string */
3213 if (stat (path
->str
, &ent_sbuf
) >= 0 && S_ISDIR (ent_sbuf
.st_mode
))
3214 sent
->entries
[n_entries
].is_dir
= TRUE
;
3216 /* stat may fail, and we don't mind, since it could be a
3217 * dangling symlink. */
3218 sent
->entries
[n_entries
].is_dir
= FALSE
;
3221 sent
->entries
[n_entries
].is_dir
= 1;
3225 sent
->entry_count
= n_entries
;
3227 g_free (sys_dir_name
);
3228 g_string_free (path
, TRUE
);
3229 qsort (sent
->entries
, sent
->entry_count
, sizeof (CompletionDirEntry
), compare_cmpl_dir
);
3231 g_dir_close (directory
);
3236 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3239 check_dir (gchar
*dir_name
,
3240 struct stat
*result
,
3241 gboolean
*stat_subdirs
)
3243 /* A list of directories that we know only contain other directories.
3244 * Trying to stat every file in these directories would be very
3251 struct stat statbuf
;
3252 } no_stat_dirs
[] = {
3253 { "/afs", FALSE
, { 0 } },
3254 { "/net", FALSE
, { 0 } }
3257 static const gint n_no_stat_dirs
= G_N_ELEMENTS (no_stat_dirs
);
3258 static gboolean initialized
= FALSE
;
3259 gchar
*sys_dir_name
;
3265 for (i
= 0; i
< n_no_stat_dirs
; i
++)
3267 if (stat (no_stat_dirs
[i
].name
, &no_stat_dirs
[i
].statbuf
) == 0)
3268 no_stat_dirs
[i
].present
= TRUE
;
3272 sys_dir_name
= g_filename_from_utf8 (dir_name
, -1, NULL
, NULL
, NULL
);
3275 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3279 if (stat (sys_dir_name
, result
) < 0)
3281 g_free (sys_dir_name
);
3285 g_free (sys_dir_name
);
3287 *stat_subdirs
= TRUE
;
3288 for (i
= 0; i
< n_no_stat_dirs
; i
++)
3290 if (no_stat_dirs
[i
].present
&&
3291 (no_stat_dirs
[i
].statbuf
.st_dev
== result
->st_dev
) &&
3292 (no_stat_dirs
[i
].statbuf
.st_ino
== result
->st_ino
))
3294 *stat_subdirs
= FALSE
;
3304 /* open a directory by absolute pathname */
3305 static CompletionDir
*
3306 open_dir (gchar
*dir_name
,
3307 CompletionState
*cmpl_state
)
3310 gboolean stat_subdirs
;
3311 CompletionDirSent
*sent
;
3314 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3315 if (!check_dir (dir_name
, &sbuf
, &stat_subdirs
))
3318 cdsl
= cmpl_state
->directory_sent_storage
;
3324 if (sent
->inode
== sbuf
.st_ino
&&
3325 sent
->mtime
== sbuf
.st_mtime
&&
3326 sent
->device
== sbuf
.st_dev
)
3327 return attach_dir (sent
, dir_name
, cmpl_state
);
3332 stat_subdirs
= TRUE
;
3335 sent
= open_new_dir (dir_name
, &sbuf
, stat_subdirs
);
3339 cmpl_state
->directory_sent_storage
=
3340 g_list_prepend (cmpl_state
->directory_sent_storage
, sent
);
3342 return attach_dir (sent
, dir_name
, cmpl_state
);
3348 static CompletionDir
*
3349 attach_dir (CompletionDirSent
*sent
,
3351 CompletionState
*cmpl_state
)
3353 CompletionDir
* new_dir
;
3355 new_dir
= g_new (CompletionDir
, 1);
3357 cmpl_state
->directory_storage
=
3358 g_list_prepend (cmpl_state
->directory_storage
, new_dir
);
3359 new_dir
->sent
= sent
;
3360 new_dir
->fullname
= g_strdup (dir_name
);
3361 new_dir
->fullname_len
= strlen (dir_name
);
3362 new_dir
->cmpl_text
= NULL
;
3368 correct_dir_fullname (CompletionDir
* cmpl_dir
)
3370 gint length
= strlen (cmpl_dir
->fullname
);
3371 gchar
*first_slash
= strchr (cmpl_dir
->fullname
, G_DIR_SEPARATOR
);
3372 gchar
*sys_filename
;
3375 /* Does it end with /. (\.) ? */
3377 strcmp (cmpl_dir
->fullname
+ length
- 2, G_DIR_SEPARATOR_S
".") == 0)
3379 /* Is it just the root directory (on a drive) ? */
3380 if (cmpl_dir
->fullname
+ length
- 2 == first_slash
)
3382 cmpl_dir
->fullname
[length
- 1] = 0;
3383 cmpl_dir
->fullname_len
= length
- 1;
3388 cmpl_dir
->fullname
[length
- 2] = 0;
3392 /* Ends with /./ (\.\)? */
3393 else if (length
>= 3 &&
3394 strcmp (cmpl_dir
->fullname
+ length
- 3,
3395 G_DIR_SEPARATOR_S
"." G_DIR_SEPARATOR_S
) == 0)
3396 cmpl_dir
->fullname
[length
- 2] = 0;
3398 /* Ends with /.. (\..) ? */
3399 else if (length
>= 3 &&
3400 strcmp (cmpl_dir
->fullname
+ length
- 3,
3401 G_DIR_SEPARATOR_S
"..") == 0)
3403 /* Is it just /.. (X:\..)? */
3404 if (cmpl_dir
->fullname
+ length
- 3 == first_slash
)
3406 cmpl_dir
->fullname
[length
- 2] = 0;
3407 cmpl_dir
->fullname_len
= length
- 2;
3411 sys_filename
= g_filename_from_utf8 (cmpl_dir
->fullname
, -1, NULL
, NULL
, NULL
);
3414 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3418 if (stat (sys_filename
, &sbuf
) < 0)
3420 g_free (sys_filename
);
3424 g_free (sys_filename
);
3426 cmpl_dir
->fullname
[length
- 3] = 0;
3428 if (!correct_parent (cmpl_dir
, &sbuf
))
3432 /* Ends with /../ (\..\)? */
3433 else if (length
>= 4 &&
3434 strcmp (cmpl_dir
->fullname
+ length
- 4,
3435 G_DIR_SEPARATOR_S
".." G_DIR_SEPARATOR_S
) == 0)
3437 /* Is it just /../ (X:\..\)? */
3438 if (cmpl_dir
->fullname
+ length
- 4 == first_slash
)
3440 cmpl_dir
->fullname
[length
- 3] = 0;
3441 cmpl_dir
->fullname_len
= length
- 3;
3445 sys_filename
= g_filename_from_utf8 (cmpl_dir
->fullname
, -1, NULL
, NULL
, NULL
);
3448 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3452 if (stat (sys_filename
, &sbuf
) < 0)
3454 g_free (sys_filename
);
3458 g_free (sys_filename
);
3460 cmpl_dir
->fullname
[length
- 4] = 0;
3462 if (!correct_parent (cmpl_dir
, &sbuf
))
3466 cmpl_dir
->fullname_len
= strlen (cmpl_dir
->fullname
);
3472 correct_parent (CompletionDir
*cmpl_dir
,
3479 gchar
*sys_filename
;
3482 last_slash
= strrchr (cmpl_dir
->fullname
, G_DIR_SEPARATOR
);
3483 g_assert (last_slash
);
3484 first_slash
= strchr (cmpl_dir
->fullname
, G_DIR_SEPARATOR
);
3486 /* Clever (?) way to check for top-level directory that works also on
3487 * Win32, where there is a drive letter and colon prefixed...
3489 if (last_slash
!= first_slash
)
3499 sys_filename
= g_filename_from_utf8 (cmpl_dir
->fullname
, -1, NULL
, NULL
, NULL
);
3502 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3504 last_slash
[0] = G_DIR_SEPARATOR
;
3508 if (stat (sys_filename
, &parbuf
) < 0)
3510 g_free (sys_filename
);
3513 last_slash
[0] = G_DIR_SEPARATOR
;
3516 g_free (sys_filename
);
3518 #ifndef G_OS_WIN32 /* No inode numbers on Win32 */
3519 if (parbuf
.st_ino
== sbuf
->st_ino
&& parbuf
.st_dev
== sbuf
->st_dev
)
3520 /* it wasn't a link */
3526 last_slash
[0] = G_DIR_SEPARATOR
;
3528 /* it was a link, have to figure it out the hard way */
3530 new_name
= find_parent_dir_fullname (cmpl_dir
->fullname
);
3535 g_free (cmpl_dir
->fullname
);
3537 cmpl_dir
->fullname
= new_name
;
3546 find_parent_dir_fullname (gchar
* dirname
)
3548 gchar
*sys_orig_dir
;
3553 sys_orig_dir
= g_get_current_dir ();
3554 sys_dirname
= g_filename_from_utf8 (dirname
, -1, NULL
, NULL
, NULL
);
3557 g_free (sys_orig_dir
);
3558 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3562 if (chdir (sys_dirname
) != 0 || chdir ("..") != 0)
3565 chdir (sys_orig_dir
);
3566 g_free (sys_dirname
);
3567 g_free (sys_orig_dir
);
3570 g_free (sys_dirname
);
3572 sys_cwd
= g_get_current_dir ();
3573 result
= g_filename_to_utf8 (sys_cwd
, -1, NULL
, NULL
, NULL
);
3576 if (chdir (sys_orig_dir
) != 0)
3579 g_free (sys_orig_dir
);
3583 g_free (sys_orig_dir
);
3589 /**********************************************************************/
3590 /* Completion Operations */
3591 /**********************************************************************/
3595 static PossibleCompletion
*
3596 attempt_homedir_completion (gchar
*text_to_complete
,
3597 CompletionState
*cmpl_state
)
3601 if (!cmpl_state
->user_dir_name_buffer
&&
3602 !get_pwdb (cmpl_state
))
3604 length
= strlen (text_to_complete
) - 1;
3606 cmpl_state
->user_completion_index
+= 1;
3608 while (cmpl_state
->user_completion_index
< cmpl_state
->user_directories_len
)
3610 index
= first_diff_index (text_to_complete
+ 1,
3611 cmpl_state
->user_directories
3612 [cmpl_state
->user_completion_index
].login
);
3619 if (cmpl_state
->last_valid_char
< (index
+ 1))
3620 cmpl_state
->last_valid_char
= index
+ 1;
3621 cmpl_state
->user_completion_index
+= 1;
3625 cmpl_state
->the_completion
.is_a_completion
= 1;
3626 cmpl_state
->the_completion
.is_directory
= TRUE
;
3628 append_completion_text ("~", cmpl_state
);
3630 append_completion_text (cmpl_state
->
3631 user_directories
[cmpl_state
->user_completion_index
].login
,
3634 return append_completion_text (G_DIR_SEPARATOR_S
, cmpl_state
);
3637 if (text_to_complete
[1]
3638 || cmpl_state
->user_completion_index
> cmpl_state
->user_directories_len
)
3640 cmpl_state
->user_completion_index
= -1;
3645 cmpl_state
->user_completion_index
+= 1;
3646 cmpl_state
->the_completion
.is_a_completion
= 1;
3647 cmpl_state
->the_completion
.is_directory
= TRUE
;
3649 return append_completion_text ("~" G_DIR_SEPARATOR_S
, cmpl_state
);
3655 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
3656 #define FOLD(c) (tolower(c))
3661 /* returns the index (>= 0) of the first differing character,
3662 * PATTERN_MATCH if the completion matches */
3664 first_diff_index (gchar
*pat
,
3669 while (*pat
&& *text
&& FOLD (*text
) == FOLD (*pat
))
3679 return PATTERN_MATCH
;
3682 static PossibleCompletion
*
3683 append_completion_text (gchar
*text
,
3684 CompletionState
*cmpl_state
)
3688 if (!cmpl_state
->the_completion
.text
)
3691 len
= strlen (text
) + strlen (cmpl_state
->the_completion
.text
) + 1;
3693 if (cmpl_state
->the_completion
.text_alloc
> len
)
3695 strcat (cmpl_state
->the_completion
.text
, text
);
3696 return &cmpl_state
->the_completion
;
3702 cmpl_state
->the_completion
.text_alloc
= i
;
3704 cmpl_state
->the_completion
.text
= (gchar
*) g_realloc (cmpl_state
->the_completion
.text
, i
);
3706 if (!cmpl_state
->the_completion
.text
)
3710 strcat (cmpl_state
->the_completion
.text
, text
);
3711 return &cmpl_state
->the_completion
;
3715 static CompletionDir
*
3716 find_completion_dir (gchar
*text_to_complete
,
3717 gchar
**remaining_text
,
3718 CompletionState
*cmpl_state
)
3720 gchar
* first_slash
= strchr (text_to_complete
, G_DIR_SEPARATOR
);
3721 CompletionDir
* dir
= cmpl_state
->reference_dir
;
3722 CompletionDir
* next
;
3723 *remaining_text
= text_to_complete
;
3727 gint len
= first_slash
- *remaining_text
;
3729 gchar
*found_name
= NULL
; /* Quiet gcc */
3731 gchar
* pat_buf
= g_new (gchar
, len
+ 1);
3732 GtkFileFilter
*filter
= gtk_file_filter_new ();
3733 GtkFileFilterInfo filter_info
= {GTK_FILE_FILTER_FILENAME
,};
3735 strncpy (pat_buf
, *remaining_text
, len
);
3738 gtk_file_filter_add_pattern (filter
, pat_buf
);
3740 for (i
= 0; i
< dir
->sent
->entry_count
; i
+= 1)
3742 filter_info
.filename
= dir
->sent
->entries
[i
].entry_name
;
3743 if (dir
->sent
->entries
[i
].is_dir
&&
3744 gtk_file_filter_filter(filter
, &filter_info
))
3754 found_name
= dir
->sent
->entries
[i
].entry_name
;
3761 /* Perhaps we are trying to open an automount directory */
3762 found_name
= pat_buf
;
3765 next
= open_relative_dir (found_name
, dir
, cmpl_state
);
3773 next
->cmpl_parent
= dir
;
3777 if (!correct_dir_fullname (dir
))
3783 *remaining_text
= first_slash
+ 1;
3784 first_slash
= strchr (*remaining_text
, G_DIR_SEPARATOR
);
3793 update_cmpl (PossibleCompletion
*poss
,
3794 CompletionState
*cmpl_state
)
3798 if (!poss
|| !cmpl_is_a_completion (poss
))
3801 cmpl_len
= strlen (cmpl_this_completion (poss
));
3803 if (cmpl_state
->updated_text_alloc
< cmpl_len
+ 1)
3805 cmpl_state
->updated_text
=
3806 (gchar
*)g_realloc (cmpl_state
->updated_text
,
3807 cmpl_state
->updated_text_alloc
);
3808 cmpl_state
->updated_text_alloc
= 2*cmpl_len
;
3811 if (cmpl_state
->updated_text_len
< 0)
3813 strcpy (cmpl_state
->updated_text
, cmpl_this_completion (poss
));
3814 cmpl_state
->updated_text_len
= cmpl_len
;
3815 cmpl_state
->re_complete
= cmpl_is_directory (poss
);
3817 else if (cmpl_state
->updated_text_len
== 0)
3819 cmpl_state
->re_complete
= FALSE
;
3824 first_diff_index (cmpl_state
->updated_text
,
3825 cmpl_this_completion (poss
));
3827 cmpl_state
->re_complete
= FALSE
;
3829 if (first_diff
== PATTERN_MATCH
)
3832 if (first_diff
> cmpl_state
->updated_text_len
)
3833 strcpy (cmpl_state
->updated_text
, cmpl_this_completion (poss
));
3835 cmpl_state
->updated_text_len
= first_diff
;
3836 cmpl_state
->updated_text
[first_diff
] = 0;
3840 static PossibleCompletion
*
3841 attempt_dir_completion (CompletionState
*cmpl_state
)
3843 gchar
*pat_buf
, *first_slash
;
3844 CompletionDir
*dir
= cmpl_state
->active_completion_dir
;
3845 GtkFileFilter
*filter
= gtk_file_filter_new ();
3846 GtkFileFilterInfo filter_info
= {GTK_FILE_FILTER_FILENAME
,};
3848 dir
->cmpl_index
+= 1;
3850 if (dir
->cmpl_index
== dir
->sent
->entry_count
)
3852 if (dir
->cmpl_parent
== NULL
)
3854 cmpl_state
->active_completion_dir
= NULL
;
3860 cmpl_state
->active_completion_dir
= dir
->cmpl_parent
;
3862 return attempt_dir_completion (cmpl_state
);
3866 g_assert (dir
->cmpl_text
);
3868 first_slash
= strchr (dir
->cmpl_text
, G_DIR_SEPARATOR
);
3872 gint len
= first_slash
- dir
->cmpl_text
;
3874 pat_buf
= g_new (gchar
, len
+ 1);
3875 strncpy (pat_buf
, dir
->cmpl_text
, len
);
3880 gint len
= strlen (dir
->cmpl_text
);
3882 pat_buf
= g_new (gchar
, len
+ 2);
3883 strcpy (pat_buf
, dir
->cmpl_text
);
3884 /* Don't append a * if the user entered one herself.
3885 * This way one can complete *.h and don't get matches
3886 * on any .help files, for instance.
3888 if (strchr (pat_buf
, '*') == NULL
)
3889 strcpy (pat_buf
+ len
, "*");
3892 gtk_file_filter_add_pattern (filter
, pat_buf
);
3896 if (dir
->sent
->entries
[dir
->cmpl_index
].is_dir
)
3898 filter_info
.filename
= dir
->sent
->entries
[dir
->cmpl_index
].entry_name
;
3899 if(gtk_file_filter_filter(filter
, &filter_info
))
3901 CompletionDir
* new_dir
;
3903 new_dir
= open_relative_dir (dir
->sent
->entries
[dir
->cmpl_index
].entry_name
,
3912 new_dir
->cmpl_parent
= dir
;
3914 new_dir
->cmpl_index
= -1;
3915 new_dir
->cmpl_text
= g_strdup (first_slash
+ 1);
3917 cmpl_state
->active_completion_dir
= new_dir
;
3920 return attempt_dir_completion (cmpl_state
);
3925 return attempt_dir_completion (cmpl_state
);
3931 return attempt_dir_completion (cmpl_state
);
3936 if (dir
->cmpl_parent
!= NULL
)
3938 append_completion_text (dir
->fullname
+
3939 strlen (cmpl_state
->completion_dir
->fullname
) + 1,
3941 append_completion_text (G_DIR_SEPARATOR_S
, cmpl_state
);
3944 append_completion_text (dir
->sent
->entries
[dir
->cmpl_index
].entry_name
, cmpl_state
);
3946 filter_info
.filename
= dir
->sent
->entries
[dir
->cmpl_index
].entry_name
;
3947 cmpl_state
->the_completion
.is_a_completion
=
3948 gtk_file_filter_filter(filter
, &filter_info
);
3950 cmpl_state
->the_completion
.is_directory
= dir
->sent
->entries
[dir
->cmpl_index
].is_dir
;
3951 if (dir
->sent
->entries
[dir
->cmpl_index
].is_dir
)
3952 append_completion_text (G_DIR_SEPARATOR_S
, cmpl_state
);
3955 return &cmpl_state
->the_completion
;
3962 get_pwdb (CompletionState
* cmpl_state
)
3964 struct passwd
*pwd_ptr
;
3967 gint len
= 0, i
, count
= 0;
3969 if (cmpl_state
->user_dir_name_buffer
)
3973 while ((pwd_ptr
= getpwent ()) != NULL
)
3975 utf8
= g_filename_to_utf8 (pwd_ptr
->pw_name
, -1, NULL
, NULL
, NULL
);
3976 len
+= strlen (utf8
);
3978 utf8
= g_filename_to_utf8 (pwd_ptr
->pw_dir
, -1, NULL
, NULL
, NULL
);
3979 len
+= strlen (utf8
);
3987 cmpl_state
->user_dir_name_buffer
= g_new (gchar
, len
);
3988 cmpl_state
->user_directories
= g_new (CompletionUserDir
, count
);
3989 cmpl_state
->user_directories_len
= count
;
3991 buf_ptr
= cmpl_state
->user_dir_name_buffer
;
3993 for (i
= 0; i
< count
; i
+= 1)
3995 pwd_ptr
= getpwent ();
4002 utf8
= g_filename_to_utf8 (pwd_ptr
->pw_name
, -1, NULL
, NULL
, NULL
);
4003 strcpy (buf_ptr
, utf8
);
4006 cmpl_state
->user_directories
[i
].login
= buf_ptr
;
4008 buf_ptr
+= strlen (buf_ptr
);
4011 utf8
= g_filename_to_utf8 (pwd_ptr
->pw_dir
, -1, NULL
, NULL
, NULL
);
4012 strcpy (buf_ptr
, utf8
);
4015 cmpl_state
->user_directories
[i
].homedir
= buf_ptr
;
4017 buf_ptr
+= strlen (buf_ptr
);
4021 qsort (cmpl_state
->user_directories
,
4022 cmpl_state
->user_directories_len
,
4023 sizeof (CompletionUserDir
),
4032 if (cmpl_state
->user_dir_name_buffer
)
4033 g_free (cmpl_state
->user_dir_name_buffer
);
4034 if (cmpl_state
->user_directories
)
4035 g_free (cmpl_state
->user_directories
);
4037 cmpl_state
->user_dir_name_buffer
= NULL
;
4038 cmpl_state
->user_directories
= NULL
;
4044 compare_user_dir (const void *a
,
4047 return strcmp ((((CompletionUserDir
*)a
))->login
,
4048 (((CompletionUserDir
*)b
))->login
);
4054 compare_cmpl_dir (const void *a
,
4058 return strcmp (((const CompletionDirEntry
*)a
)->sort_key
,
4059 (((const CompletionDirEntry
*)b
))->sort_key
);
4063 cmpl_state_okay (CompletionState
* cmpl_state
)
4065 return cmpl_state
&& cmpl_state
->reference_dir
;
4069 cmpl_strerror (gint err
)
4071 if (err
== CMPL_ERRNO_TOO_LONG
)
4072 return _("Name too long");
4073 else if (err
== CMPL_ERRNO_DID_NOT_CONVERT
)
4074 return _("Couldn't convert filename");
4076 return g_strerror (err
);
4079 const gchar
* gtk_dir_selection_get_dir (GtkDirSelection
*filesel
)
4081 return gtk_entry_get_text (GTK_ENTRY (filesel
->selection_entry
));