7 #ifdef HAVE_SYS_PARAM_H
20 #include <glib.h> /* Include early to get G_OS_WIN32 and
23 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
28 #endif /* G_OS_WIN32 || G_WITH_CYGWIN */
30 #include <winsock.h> /* For gethostname */
33 #include "gdk/gdkkeysyms.h"
35 #include <lttv/gtkdirsel.h>
43 #define mkdir(p,m) _mkdir(p)
45 #define S_ISDIR(mode) ((mode)&_S_IFDIR)
47 #endif /* G_OS_WIN32 */
50 #include <sys/cygwin.h> /* For cygwin_conv_to_posix_path */
53 #define DIR_LIST_WIDTH 180
54 #define DIR_LIST_HEIGHT 180
55 #define FILE_LIST_WIDTH 180
56 #define FILE_LIST_HEIGHT 180
58 /* The Hurd doesn't define either PATH_MAX or MAXPATHLEN, so we put this
59 * in here, since the rest of the code in the file does require some
64 # define MAXPATHLEN PATH_MAX
66 # define MAXPATHLEN 2048
70 /* I've put this here so it doesn't get confused with the
71 * file completion interface */
72 typedef struct _HistoryCallbackArg HistoryCallbackArg
;
74 struct _HistoryCallbackArg
81 typedef struct _CompletionState CompletionState
;
82 typedef struct _CompletionDir CompletionDir
;
83 typedef struct _CompletionDirSent CompletionDirSent
;
84 typedef struct _CompletionDirEntry CompletionDirEntry
;
85 typedef struct _CompletionUserDir CompletionUserDir
;
86 typedef struct _PossibleCompletion PossibleCompletion
;
88 /* Non-external file completion decls and structures */
90 /* A contant telling PRCS how many directories to cache. Its actually
91 * kept in a list, so the geometry isn't important. */
92 #define CMPL_DIRECTORY_CACHE_SIZE 10
94 /* A constant used to determine whether a substring was an exact
95 * match by first_diff_index()
97 #define PATTERN_MATCH -1
98 #define CMPL_ERRNO_TOO_LONG ((1<<16)-1)
99 #define CMPL_ERRNO_DID_NOT_CONVERT ((1<<16)-2)
101 /* This structure contains all the useful information about a directory
102 * for the purposes of filename completion. These structures are cached
103 * in the CompletionState struct. CompletionDir's are reference counted.
105 struct _CompletionDirSent
112 struct _CompletionDirEntry
*entries
;
115 struct _CompletionDir
117 CompletionDirSent
*sent
;
122 struct _CompletionDir
*cmpl_parent
;
127 /* This structure contains pairs of directory entry names with a flag saying
128 * whether or not they are a valid directory. NOTE: This information is used
129 * to provide the caller with information about whether to update its completions
130 * or try to open a file. Since directories are cached by the directory mtime,
131 * a symlink which points to an invalid file (which will not be a directory),
132 * will not be reevaluated if that file is created, unless the containing
133 * directory is touched. I consider this case to be worth ignoring (josh).
135 struct _CompletionDirEntry
142 struct _CompletionUserDir
148 struct _PossibleCompletion
150 /* accessible fields, all are accessed externally by functions
154 gint is_a_completion
;
155 gboolean is_directory
;
162 struct _CompletionState
164 gint last_valid_char
;
166 gint updated_text_len
;
167 gint updated_text_alloc
;
168 gboolean re_complete
;
170 gchar
*user_dir_name_buffer
;
171 gint user_directories_len
;
173 gchar
*last_completion_text
;
175 gint user_completion_index
; /* if >= 0, currently completing ~user */
177 struct _CompletionDir
*completion_dir
; /* directory completing from */
178 struct _CompletionDir
*active_completion_dir
;
180 struct _PossibleCompletion the_completion
;
182 struct _CompletionDir
*reference_dir
; /* initial directory */
184 GList
* directory_storage
;
185 GList
* directory_sent_storage
;
187 struct _CompletionUserDir
*user_directories
;
205 /* File completion functions which would be external, were they used
206 * outside of this file.
209 static CompletionState
* cmpl_init_state (void);
210 static void cmpl_free_state (CompletionState
*cmpl_state
);
211 static gint
cmpl_state_okay (CompletionState
* cmpl_state
);
212 static const gchar
* cmpl_strerror (gint
);
214 static PossibleCompletion
* cmpl_completion_matches(gchar
*text_to_complete
,
215 gchar
**remaining_text
,
216 CompletionState
*cmpl_state
);
218 /* Returns a name for consideration, possibly a completion, this name
219 * will be invalid after the next call to cmpl_next_completion.
221 static char* cmpl_this_completion (PossibleCompletion
*);
223 /* True if this completion matches the given text. Otherwise, this
224 * output can be used to have a list of non-completions.
226 static gint
cmpl_is_a_completion (PossibleCompletion
*);
228 /* True if the completion is a directory
230 static gboolean
cmpl_is_directory (PossibleCompletion
*);
232 /* Obtains the next completion, or NULL
234 static PossibleCompletion
* cmpl_next_completion (CompletionState
*);
236 /* Updating completions: the return value of cmpl_updated_text() will
237 * be text_to_complete completed as much as possible after the most
238 * recent call to cmpl_completion_matches. For the present
239 * application, this is the suggested replacement for the user's input
240 * string. You must CALL THIS AFTER ALL cmpl_text_completions have
243 static gchar
* cmpl_updated_text (CompletionState
* cmpl_state
);
245 /* After updating, to see if the completion was a directory, call
246 * this. If it was, you should consider re-calling completion_matches.
248 static gboolean
cmpl_updated_dir (CompletionState
* cmpl_state
);
250 /* Current location: if using file completion, return the current
251 * directory, from which file completion begins. More specifically,
252 * the cwd concatenated with all exact completions up to the last
253 * directory delimiter('/').
255 static gchar
* cmpl_reference_position (CompletionState
* cmpl_state
);
257 /* backing up: if cmpl_completion_matches returns NULL, you may query
258 * the index of the last completable character into cmpl_updated_text.
260 static gint
cmpl_last_valid_char (CompletionState
* cmpl_state
);
262 /* When the user selects a non-directory, call cmpl_completion_fullname
263 * to get the full name of the selected file.
265 static const gchar
* cmpl_completion_fullname (const gchar
*, CompletionState
* cmpl_state
);
268 /* Directory operations. */
269 static CompletionDir
* open_ref_dir (gchar
* text_to_complete
,
270 gchar
** remaining_text
,
271 CompletionState
* cmpl_state
);
272 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
273 static gboolean
check_dir (gchar
*dir_name
,
275 gboolean
*stat_subdirs
);
277 static CompletionDir
* open_dir (gchar
* dir_name
,
278 CompletionState
* cmpl_state
);
280 static CompletionDir
* open_user_dir (const gchar
* text_to_complete
,
281 CompletionState
*cmpl_state
);
283 static CompletionDir
* open_relative_dir (gchar
* dir_name
, CompletionDir
* dir
,
284 CompletionState
*cmpl_state
);
285 static CompletionDirSent
* open_new_dir (gchar
* dir_name
,
287 gboolean stat_subdirs
);
288 static gint
correct_dir_fullname (CompletionDir
* cmpl_dir
);
289 static gint
correct_parent (CompletionDir
* cmpl_dir
,
292 static gchar
* find_parent_dir_fullname (gchar
* dirname
);
294 static CompletionDir
* attach_dir (CompletionDirSent
* sent
,
296 CompletionState
*cmpl_state
);
297 static void free_dir_sent (CompletionDirSent
* sent
);
298 static void free_dir (CompletionDir
*dir
);
299 static void prune_memory_usage(CompletionState
*cmpl_state
);
301 /* Completion operations */
303 static PossibleCompletion
* attempt_homedir_completion(gchar
* text_to_complete
,
304 CompletionState
*cmpl_state
);
306 static PossibleCompletion
* attempt_dir_completion(CompletionState
*cmpl_state
);
307 static CompletionDir
* find_completion_dir(gchar
* text_to_complete
,
308 gchar
** remaining_text
,
309 CompletionState
* cmpl_state
);
310 static PossibleCompletion
* append_completion_text(gchar
* text
,
311 CompletionState
* cmpl_state
);
313 static gint
get_pwdb(CompletionState
* cmpl_state
);
314 static gint
compare_user_dir(const void* a
, const void* b
);
316 static gint
first_diff_index(gchar
* pat
, gchar
* text
);
317 static gint
compare_cmpl_dir(const void* a
, const void* b
);
318 static void update_cmpl(PossibleCompletion
* poss
,
319 CompletionState
* cmpl_state
);
321 static void gtk_dir_selection_class_init (GtkDirSelectionClass
*klass
);
322 static void gtk_dir_selection_set_property (GObject
*object
,
326 static void gtk_dir_selection_get_property (GObject
*object
,
330 static void gtk_dir_selection_init (GtkDirSelection
*filesel
);
331 static void gtk_dir_selection_finalize (GObject
*object
);
332 static void gtk_dir_selection_destroy (GtkObject
*object
);
333 static void gtk_dir_selection_map (GtkWidget
*widget
);
334 static gint
gtk_dir_selection_key_press (GtkWidget
*widget
,
337 static gint
gtk_dir_selection_insert_text (GtkWidget
*widget
,
338 const gchar
*new_text
,
339 gint new_text_length
,
342 static void gtk_dir_selection_update_fileops (GtkDirSelection
*filesel
);
344 static void gtk_dir_selection_file_activate (GtkTreeView
*tree_view
,
346 GtkTreeViewColumn
*column
,
348 static void gtk_dir_selection_file_changed (GtkTreeSelection
*selection
,
350 static void gtk_dir_selection_dir_activate (GtkTreeView
*tree_view
,
352 GtkTreeViewColumn
*column
,
354 static void gtk_dir_selection_dir_changed (GtkTreeSelection
*selection
,
356 static void gtk_dir_selection_populate (GtkDirSelection
*fs
,
358 gboolean try_complete
,
359 gboolean reset_entry
);
360 static void gtk_dir_selection_abort (GtkDirSelection
*fs
);
362 static void gtk_dir_selection_update_history_menu (GtkDirSelection
*fs
,
365 static void gtk_dir_selection_create_dir (GtkWidget
*widget
, gpointer data
);
366 static void gtk_dir_selection_delete_file (GtkWidget
*widget
, gpointer data
);
367 static void gtk_dir_selection_rename_file (GtkWidget
*widget
, gpointer data
);
369 static void free_selected_names (GPtrArray
*names
);
371 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
372 #define compare_filenames(a, b) strcmp(a, b)
374 #define compare_filenames(a, b) g_ascii_strcasecmp(a, b)
378 static GtkWindowClass
*parent_class
= NULL
;
380 /* Saves errno when something cmpl does fails. */
381 static gint cmpl_errno
;
385 * Take the path currently in the file selection
386 * entry field and translate as necessary from
387 * a WIN32 style to CYGWIN32 style path. For
388 * instance translate:
389 * x:\somepath\file.jpg
391 * /cygdrive/x/somepath/file.jpg
393 * Replace the path in the selection text field.
394 * Return a boolean value concerning whether a
395 * translation had to be made.
398 translate_win32_path (GtkDirSelection
*filesel
)
402 gchar newPath
[MAX_PATH
];
405 * Retrieve the current path
407 path
= gtk_entry_get_text (GTK_ENTRY (filesel
->selection_entry
));
409 cygwin_conv_to_posix_path (path
, newPath
);
410 updated
= (strcmp (path
, newPath
) != 0);
413 gtk_entry_set_text (GTK_ENTRY (filesel
->selection_entry
), newPath
);
420 gtk_dir_selection_get_type (void)
422 static GType file_selection_type
= 0;
424 if (!file_selection_type
)
426 static const GTypeInfo filesel_info
=
428 sizeof (GtkDirSelectionClass
),
429 NULL
, /* base_init */
430 NULL
, /* base_finalize */
431 (GClassInitFunc
) gtk_dir_selection_class_init
,
432 NULL
, /* class_finalize */
433 NULL
, /* class_data */
434 sizeof (GtkDirSelection
),
436 (GInstanceInitFunc
) gtk_dir_selection_init
,
439 file_selection_type
=
440 g_type_register_static (GTK_TYPE_DIALOG
, "GtkDirSelection",
444 return file_selection_type
;
448 gtk_dir_selection_class_init (GtkDirSelectionClass
*class)
450 GObjectClass
*gobject_class
;
451 GtkObjectClass
*object_class
;
452 GtkWidgetClass
*widget_class
;
454 gobject_class
= (GObjectClass
*) class;
455 object_class
= (GtkObjectClass
*) class;
456 widget_class
= (GtkWidgetClass
*) class;
458 parent_class
= g_type_class_peek_parent (class);
460 gobject_class
->finalize
= gtk_dir_selection_finalize
;
461 gobject_class
->set_property
= gtk_dir_selection_set_property
;
462 gobject_class
->get_property
= gtk_dir_selection_get_property
;
464 g_object_class_install_property (gobject_class
,
466 g_param_spec_string ("filename",
468 _("The currently selected filename"),
470 G_PARAM_READABLE
| G_PARAM_WRITABLE
));
471 g_object_class_install_property (gobject_class
,
473 g_param_spec_boolean ("show_fileops",
474 _("Show file operations"),
475 _("Whether buttons for creating/manipulating files should be displayed"),
479 g_object_class_install_property (gobject_class
,
480 PROP_SELECT_MULTIPLE
,
481 g_param_spec_boolean ("select_multiple",
482 _("Select multiple"),
483 _("Whether to allow multiple files to be selected"),
487 object_class
->destroy
= gtk_dir_selection_destroy
;
488 widget_class
->map
= gtk_dir_selection_map
;
491 static void gtk_dir_selection_set_property (GObject
*object
,
496 GtkDirSelection
*filesel
;
498 filesel
= GTK_DIR_SELECTION (object
);
503 gtk_dir_selection_set_filename (filesel
,
504 g_value_get_string (value
));
506 case PROP_SHOW_FILEOPS
:
507 if (g_value_get_boolean (value
))
508 gtk_dir_selection_show_fileop_buttons (filesel
);
510 gtk_dir_selection_hide_fileop_buttons (filesel
);
512 case PROP_SELECT_MULTIPLE
:
513 gtk_dir_selection_set_select_multiple (filesel
, g_value_get_boolean (value
));
516 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
521 static void gtk_dir_selection_get_property (GObject
*object
,
526 GtkDirSelection
*filesel
;
528 filesel
= GTK_DIR_SELECTION (object
);
533 g_value_set_string (value
,
534 gtk_dir_selection_get_filename(filesel
));
537 case PROP_SHOW_FILEOPS
:
538 /* This is a little bit hacky, but doing otherwise would require
539 * adding a field to the object.
541 g_value_set_boolean (value
, (filesel
->fileop_c_dir
&&
542 filesel
->fileop_del_file
&&
543 filesel
->fileop_ren_file
));
545 case PROP_SELECT_MULTIPLE
:
546 g_value_set_boolean (value
, gtk_dir_selection_get_select_multiple (filesel
));
549 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, prop_id
, pspec
);
555 grab_default (GtkWidget
*widget
)
557 gtk_widget_grab_default (widget
);
562 gtk_dir_selection_init (GtkDirSelection
*filesel
)
564 GtkWidget
*entry_vbox
;
566 GtkWidget
*list_hbox
, *list_container
;
567 GtkWidget
*confirm_area
;
568 GtkWidget
*pulldown_hbox
;
569 GtkWidget
*scrolled_win
;
575 GtkTreeViewColumn
*column
;
577 gtk_widget_push_composite_child ();
579 dialog
= GTK_DIALOG (filesel
);
581 filesel
->cmpl_state
= cmpl_init_state ();
583 /* The dialog-sized vertical box */
584 filesel
->main_vbox
= dialog
->vbox
;
585 gtk_container_set_border_width (GTK_CONTAINER (filesel
), 10);
587 /* The horizontal box containing create, rename etc. buttons */
588 filesel
->button_area
= gtk_hbutton_box_new ();
589 gtk_button_box_set_layout (GTK_BUTTON_BOX (filesel
->button_area
), GTK_BUTTONBOX_START
);
590 gtk_box_set_spacing (GTK_BOX (filesel
->button_area
), 0);
591 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), filesel
->button_area
,
593 gtk_widget_show (filesel
->button_area
);
595 gtk_dir_selection_show_fileop_buttons (filesel
);
597 /* hbox for pulldown menu */
598 pulldown_hbox
= gtk_hbox_new (TRUE
, 5);
599 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), pulldown_hbox
, FALSE
, FALSE
, 0);
600 gtk_widget_show (pulldown_hbox
);
603 filesel
->history_pulldown
= gtk_option_menu_new ();
604 // gtk_widget_show (filesel->history_pulldown);
605 // gtk_box_pack_start (GTK_BOX (pulldown_hbox), filesel->history_pulldown,
608 /* The horizontal box containing the directory and file listboxes */
610 spacer
= gtk_hbox_new (FALSE
, 0);
611 gtk_widget_set_size_request (spacer
, -1, 5);
612 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), spacer
, FALSE
, FALSE
, 0);
613 gtk_widget_show (spacer
);
615 list_hbox
= gtk_hbox_new (FALSE
, 5);
616 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), list_hbox
, TRUE
, TRUE
, 0);
617 gtk_widget_show (list_hbox
);
619 list_container
= g_object_new (GTK_TYPE_HPANED
,
625 list_container
= list_hbox
;
627 spacer
= gtk_hbox_new (FALSE
, 0);
628 gtk_widget_set_size_request (spacer
, -1, 5);
629 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), spacer
, FALSE
, FALSE
, 0);
630 gtk_widget_show (spacer
);
632 /* The directories list */
634 model
= gtk_list_store_new (1, G_TYPE_STRING
);
635 filesel
->dir_list
= gtk_tree_view_new_with_model (GTK_TREE_MODEL (model
));
636 g_object_unref (model
);
638 column
= gtk_tree_view_column_new_with_attributes (_("Folders"),
639 gtk_cell_renderer_text_new (),
642 label
= gtk_label_new_with_mnemonic (_("Fol_ders"));
643 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), filesel
->dir_list
);
644 gtk_widget_show (label
);
645 gtk_tree_view_column_set_widget (column
, label
);
646 gtk_tree_view_column_set_sizing (column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
647 gtk_tree_view_append_column (GTK_TREE_VIEW (filesel
->dir_list
), column
);
649 gtk_widget_set_size_request (filesel
->dir_list
,
650 DIR_LIST_WIDTH
, DIR_LIST_HEIGHT
);
651 g_signal_connect (filesel
->dir_list
, "row_activated",
652 G_CALLBACK (gtk_dir_selection_dir_activate
), filesel
);
653 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel
->dir_list
)), "changed",
654 G_CALLBACK (gtk_dir_selection_dir_changed
), filesel
);
656 /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list)); */
658 scrolled_win
= gtk_scrolled_window_new (NULL
, NULL
);
659 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win
), GTK_SHADOW_IN
);
660 gtk_container_add (GTK_CONTAINER (scrolled_win
), filesel
->dir_list
);
661 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win
),
662 GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
);
663 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win
), 0);
664 if (GTK_IS_PANED (list_container
))
665 gtk_paned_pack1 (GTK_PANED (list_container
), scrolled_win
, TRUE
, TRUE
);
667 gtk_container_add (GTK_CONTAINER (list_container
), scrolled_win
);
668 gtk_widget_show (filesel
->dir_list
);
669 gtk_widget_show (scrolled_win
);
672 model
= gtk_list_store_new (1, G_TYPE_STRING
);
673 filesel
->file_list
= gtk_tree_view_new_with_model (GTK_TREE_MODEL (model
));
674 g_object_unref (model
);
676 column
= gtk_tree_view_column_new_with_attributes (_("Files"),
677 gtk_cell_renderer_text_new (),
680 label
= gtk_label_new_with_mnemonic (_("_Files"));
681 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), filesel
->file_list
);
682 gtk_widget_show (label
);
683 gtk_tree_view_column_set_widget (column
, label
);
684 gtk_tree_view_column_set_sizing (column
, GTK_TREE_VIEW_COLUMN_AUTOSIZE
);
685 gtk_tree_view_append_column (GTK_TREE_VIEW (filesel
->file_list
), column
);
687 gtk_widget_set_size_request (filesel
->file_list
,
688 FILE_LIST_WIDTH
, FILE_LIST_HEIGHT
);
689 g_signal_connect (filesel
->file_list
, "row_activated",
690 G_CALLBACK (gtk_dir_selection_file_activate
), filesel
);
691 g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel
->file_list
)), "changed",
692 G_CALLBACK (gtk_dir_selection_file_changed
), filesel
);
694 /* gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list)); */
696 scrolled_win
= gtk_scrolled_window_new (NULL
, NULL
);
697 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win
), GTK_SHADOW_IN
);
698 gtk_container_add (GTK_CONTAINER (scrolled_win
), filesel
->file_list
);
699 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win
),
700 GTK_POLICY_AUTOMATIC
, GTK_POLICY_ALWAYS
);
701 gtk_container_set_border_width (GTK_CONTAINER (scrolled_win
), 0);
702 // gtk_container_add (GTK_CONTAINER (list_container), scrolled_win);
703 // gtk_widget_show (filesel->file_list);
704 // gtk_widget_show (scrolled_win);
706 /* action area for packing buttons into. */
707 filesel
->action_area
= gtk_hbox_new (TRUE
, 0);
708 gtk_box_pack_start (GTK_BOX (filesel
->main_vbox
), filesel
->action_area
,
710 gtk_widget_show (filesel
->action_area
);
712 /* The OK/Cancel button area */
713 confirm_area
= dialog
->action_area
;
715 /* The Cancel button */
716 filesel
->cancel_button
= gtk_dialog_add_button (dialog
,
718 GTK_RESPONSE_CANCEL
);
720 filesel
->ok_button
= gtk_dialog_add_button (dialog
,
724 gtk_widget_grab_default (filesel
->ok_button
);
726 /* The selection entry widget */
727 entry_vbox
= gtk_vbox_new (FALSE
, 2);
728 gtk_box_pack_end (GTK_BOX (filesel
->main_vbox
), entry_vbox
, FALSE
, FALSE
, 2);
729 gtk_widget_show (entry_vbox
);
731 eventbox
= gtk_event_box_new ();
732 filesel
->selection_text
= label
= gtk_label_new ("");
733 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.5);
734 gtk_container_add (GTK_CONTAINER (eventbox
), label
);
735 gtk_box_pack_start (GTK_BOX (entry_vbox
), eventbox
, FALSE
, FALSE
, 0);
736 gtk_widget_show (label
);
737 gtk_widget_show (eventbox
);
739 filesel
->selection_entry
= gtk_entry_new ();
740 g_signal_connect (filesel
->selection_entry
, "key_press_event",
741 G_CALLBACK (gtk_dir_selection_key_press
), filesel
);
742 g_signal_connect (filesel
->selection_entry
, "insert_text",
743 G_CALLBACK (gtk_dir_selection_insert_text
), NULL
);
744 g_signal_connect_swapped (filesel
->selection_entry
, "changed",
745 G_CALLBACK (gtk_dir_selection_update_fileops
), filesel
);
746 g_signal_connect_swapped (filesel
->selection_entry
, "focus_in_event",
747 G_CALLBACK (grab_default
),
749 g_signal_connect_swapped (filesel
->selection_entry
, "activate",
750 G_CALLBACK (gtk_button_clicked
),
753 gtk_box_pack_start (GTK_BOX (entry_vbox
), filesel
->selection_entry
, TRUE
, TRUE
, 0);
754 gtk_widget_show (filesel
->selection_entry
);
756 gtk_label_set_mnemonic_widget (GTK_LABEL (filesel
->selection_text
),
757 filesel
->selection_entry
);
759 if (!cmpl_state_okay (filesel
->cmpl_state
))
763 g_snprintf (err_buf
, sizeof (err_buf
), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno
));
765 gtk_label_set_text (GTK_LABEL (filesel
->selection_text
), err_buf
);
769 gtk_dir_selection_populate (filesel
, "", FALSE
, TRUE
);
772 gtk_widget_grab_focus (filesel
->selection_entry
);
774 gtk_widget_pop_composite_child ();
778 uri_list_extract_first_uri (const gchar
* uri_list
)
782 g_return_val_if_fail (uri_list
!= NULL
, NULL
);
785 /* We don't actually try to validate the URI according to RFC
786 * 2396, or even check for allowed characters - we just ignore
787 * comments and trim whitespace off the ends. We also
788 * allow LF delimination as well as the specified CRLF.
790 * We do allow comments like specified in RFC 2483.
796 while (g_ascii_isspace(*p
))
800 while (*q
&& (*q
!= '\n') && (*q
!= '\r'))
806 while (q
> p
&& g_ascii_isspace (*q
))
810 return g_strndup (p
, q
- p
+ 1);
813 p
= strchr (p
, '\n');
821 dnd_really_drop (GtkWidget
*dialog
, gint response_id
, GtkDirSelection
*fs
)
825 if (response_id
== GTK_RESPONSE_YES
)
827 filename
= g_object_get_data (G_OBJECT (dialog
), "gtk-fs-dnd-filename");
829 gtk_dir_selection_set_filename (fs
, filename
);
832 gtk_widget_destroy (dialog
);
837 filenames_dropped (GtkWidget
*widget
,
838 GdkDragContext
*context
,
841 GtkSelectionData
*selection_data
,
846 char *filename
= NULL
;
848 char this_hostname
[257];
850 GError
*error
= NULL
;
852 if (!selection_data
->data
)
855 uri
= uri_list_extract_first_uri ((char *)selection_data
->data
);
860 filename
= g_filename_from_uri (uri
, &hostname
, &error
);
865 g_warning ("Error getting dropped filename: %s\n",
867 g_error_free (error
);
871 res
= gethostname (this_hostname
, 256);
872 this_hostname
[256] = 0;
874 if ((hostname
== NULL
) ||
875 (res
== 0 && strcmp (hostname
, this_hostname
) == 0) ||
876 (strcmp (hostname
, "localhost") == 0))
877 gtk_dir_selection_set_filename (GTK_DIR_SELECTION (widget
),
882 gchar
*filename_utf8
;
884 /* Conversion back to UTF-8 should always succeed for the result
885 * of g_filename_from_uri()
887 filename_utf8
= g_filename_to_utf8 (filename
, -1, NULL
, NULL
, NULL
);
888 g_assert (filename_utf8
);
890 dialog
= gtk_message_dialog_new (GTK_WINDOW (widget
),
891 GTK_DIALOG_DESTROY_WITH_PARENT
,
892 GTK_MESSAGE_QUESTION
,
894 _("The file \"%s\" resides on another machine (called %s) and may not be available to this program.\n"
895 "Are you sure that you want to select it?"), filename_utf8
, hostname
);
896 g_free (filename_utf8
);
898 g_object_set_data_full (G_OBJECT (dialog
), "gtk-fs-dnd-filename", g_strdup (filename
), g_free
);
900 g_signal_connect_data (dialog
, "response",
901 (GCallback
) dnd_really_drop
,
904 gtk_widget_show (dialog
);
922 filenames_drag_get (GtkWidget
*widget
,
923 GdkDragContext
*context
,
924 GtkSelectionData
*selection_data
,
927 GtkDirSelection
*filesel
)
935 file
= gtk_dir_selection_get_filename (filesel
);
939 if (info
== TARGET_URILIST
)
941 res
= gethostname (hostname
, 256);
944 uri_list
= g_filename_to_uri (file
, (!res
)?hostname
:NULL
, &error
);
947 g_warning ("Error getting filename: %s\n",
949 g_error_free (error
);
953 gtk_selection_data_set (selection_data
,
954 selection_data
->target
, 8,
955 (void *)uri_list
, strlen((char *)uri_list
));
960 gchar
*filename_utf8
= g_filename_to_utf8 (file
, -1, NULL
, NULL
, NULL
);
961 g_assert (filename_utf8
);
962 gtk_selection_data_set_text (selection_data
, filename_utf8
, -1);
963 g_free (filename_utf8
);
969 file_selection_setup_dnd (GtkDirSelection
*filesel
)
972 static const GtkTargetEntry drop_types
[] = {
973 { "text/uri-list", 0, TARGET_URILIST
}
975 static gint n_drop_types
= sizeof(drop_types
)/sizeof(drop_types
[0]);
976 static const GtkTargetEntry drag_types
[] = {
977 { "text/uri-list", 0, TARGET_URILIST
},
978 { "UTF8_STRING", 0, TARGET_UTF8_STRING
},
981 { "COMPOUND_TEXT", 0, 0 }
983 static gint n_drag_types
= sizeof(drag_types
)/sizeof(drag_types
[0]);
985 gtk_drag_dest_set (GTK_WIDGET (filesel
),
986 GTK_DEST_DEFAULT_ALL
,
987 drop_types
, n_drop_types
,
990 g_signal_connect (filesel
, "drag_data_received",
991 G_CALLBACK (filenames_dropped
), NULL
);
993 eventbox
= gtk_widget_get_parent (filesel
->selection_text
);
994 gtk_drag_source_set (eventbox
,
996 drag_types
, n_drag_types
,
999 g_signal_connect (eventbox
, "drag_data_get",
1000 G_CALLBACK (filenames_drag_get
), filesel
);
1004 gtk_dir_selection_new (const gchar
*title
)
1006 GtkDirSelection
*filesel
;
1008 filesel
= g_object_new (GTK_TYPE_DIR_SELECTION
, NULL
);
1009 gtk_window_set_title (GTK_WINDOW (filesel
), title
);
1010 gtk_dialog_set_has_separator (GTK_DIALOG (filesel
), FALSE
);
1012 file_selection_setup_dnd (filesel
);
1014 return GTK_WIDGET (filesel
);
1018 gtk_dir_selection_show_fileop_buttons (GtkDirSelection
*filesel
)
1020 g_return_if_fail (GTK_IS_DIR_SELECTION (filesel
));
1022 /* delete, create directory, and rename */
1023 if (!filesel
->fileop_c_dir
)
1025 filesel
->fileop_c_dir
= gtk_button_new_with_mnemonic (_("_New Folder"));
1026 g_signal_connect (filesel
->fileop_c_dir
, "clicked",
1027 G_CALLBACK (gtk_dir_selection_create_dir
),
1029 // gtk_box_pack_start (GTK_BOX (filesel->button_area),
1030 // filesel->fileop_c_dir, TRUE, TRUE, 0);
1031 // gtk_widget_show (filesel->fileop_c_dir);
1034 if (!filesel
->fileop_del_file
)
1036 filesel
->fileop_del_file
= gtk_button_new_with_mnemonic (_("De_lete File"));
1037 g_signal_connect (filesel
->fileop_del_file
, "clicked",
1038 G_CALLBACK (gtk_dir_selection_delete_file
),
1040 // gtk_box_pack_start (GTK_BOX (filesel->button_area),
1041 // filesel->fileop_del_file, TRUE, TRUE, 0);
1042 // gtk_widget_show (filesel->fileop_del_file);
1045 if (!filesel
->fileop_ren_file
)
1047 filesel
->fileop_ren_file
= gtk_button_new_with_mnemonic (_("_Rename File"));
1048 g_signal_connect (filesel
->fileop_ren_file
, "clicked",
1049 G_CALLBACK (gtk_dir_selection_rename_file
),
1051 // gtk_box_pack_start (GTK_BOX (filesel->button_area),
1052 // filesel->fileop_ren_file, TRUE, TRUE, 0);
1053 // gtk_widget_show (filesel->fileop_ren_file);
1056 gtk_dir_selection_update_fileops (filesel
);
1058 g_object_notify (G_OBJECT (filesel
), "show_fileops");
1062 gtk_dir_selection_hide_fileop_buttons (GtkDirSelection
*filesel
)
1064 g_return_if_fail (GTK_IS_DIR_SELECTION (filesel
));
1066 if (filesel
->fileop_ren_file
)
1068 gtk_widget_destroy (filesel
->fileop_ren_file
);
1069 filesel
->fileop_ren_file
= NULL
;
1072 if (filesel
->fileop_del_file
)
1074 gtk_widget_destroy (filesel
->fileop_del_file
);
1075 filesel
->fileop_del_file
= NULL
;
1078 if (filesel
->fileop_c_dir
)
1080 gtk_widget_destroy (filesel
->fileop_c_dir
);
1081 filesel
->fileop_c_dir
= NULL
;
1083 g_object_notify (G_OBJECT (filesel
), "show_fileops");
1089 * gtk_dir_selection_set_filename:
1090 * @filesel: a #GtkDirSelection.
1091 * @filename: a string to set as the default file name.
1093 * Sets a default path for the file requestor. If @filename includes a
1094 * directory path, then the requestor will open with that path as its
1095 * current working directory.
1097 * The encoding of @filename is the on-disk encoding, which
1098 * may not be UTF-8. See g_filename_from_utf8().
1101 gtk_dir_selection_set_filename (GtkDirSelection
*filesel
,
1102 const gchar
*filename
)
1105 const char *name
, *last_slash
;
1106 char *filename_utf8
;
1108 g_return_if_fail (GTK_IS_DIR_SELECTION (filesel
));
1109 g_return_if_fail (filename
!= NULL
);
1111 filename_utf8
= g_filename_to_utf8 (filename
, -1, NULL
, NULL
, NULL
);
1112 g_return_if_fail (filename_utf8
!= NULL
);
1114 last_slash
= strrchr (filename_utf8
, G_DIR_SEPARATOR
);
1118 buf
= g_strdup ("");
1119 name
= filename_utf8
;
1123 buf
= g_strdup (filename_utf8
);
1124 buf
[last_slash
- filename_utf8
+ 1] = 0;
1125 name
= last_slash
+ 1;
1128 gtk_dir_selection_populate (filesel
, buf
, FALSE
, TRUE
);
1130 if (filesel
->selection_entry
)
1131 gtk_entry_set_text (GTK_ENTRY (filesel
->selection_entry
), name
);
1133 g_object_notify (G_OBJECT (filesel
), "filename");
1135 g_free (filename_utf8
);
1139 * gtk_dir_selection_get_filename:
1140 * @filesel: a #GtkDirSelection
1142 * This function returns the selected filename in the on-disk encoding
1143 * (see g_filename_from_utf8()), which may or may not be the same as that
1144 * used by GTK+ (UTF-8). To convert to UTF-8, call g_filename_to_utf8().
1145 * The returned string points to a statically allocated buffer and
1146 * should be copied if you plan to keep it around.
1148 * If no file is selected then the selected directory path is returned.
1150 * Return value: currently-selected filename in the on-disk encoding.
1152 G_CONST_RETURN gchar
*
1153 gtk_dir_selection_get_filename (GtkDirSelection
*filesel
)
1155 static const gchar nothing
[2] = "";
1156 static gchar something
[MAXPATHLEN
*2];
1160 g_return_val_if_fail (GTK_IS_DIR_SELECTION (filesel
), nothing
);
1162 #ifdef G_WITH_CYGWIN
1163 translate_win32_path (filesel
);
1165 text
= gtk_entry_get_text (GTK_ENTRY (filesel
->selection_entry
));
1168 sys_filename
= g_filename_from_utf8 (cmpl_completion_fullname (text
, filesel
->cmpl_state
), -1, NULL
, NULL
, NULL
);
1171 strncpy (something
, sys_filename
, sizeof (something
));
1172 g_free (sys_filename
);
1180 gtk_dir_selection_complete (GtkDirSelection
*filesel
,
1181 const gchar
*pattern
)
1183 g_return_if_fail (GTK_IS_DIR_SELECTION (filesel
));
1184 g_return_if_fail (pattern
!= NULL
);
1186 if (filesel
->selection_entry
)
1187 gtk_entry_set_text (GTK_ENTRY (filesel
->selection_entry
), pattern
);
1188 gtk_dir_selection_populate (filesel
, (gchar
*) pattern
, TRUE
, TRUE
);
1192 gtk_dir_selection_destroy (GtkObject
*object
)
1194 GtkDirSelection
*filesel
;
1196 HistoryCallbackArg
*callback_arg
;
1198 g_return_if_fail (GTK_IS_DIR_SELECTION (object
));
1200 filesel
= GTK_DIR_SELECTION (object
);
1202 if (filesel
->fileop_dialog
)
1204 gtk_widget_destroy (filesel
->fileop_dialog
);
1205 filesel
->fileop_dialog
= NULL
;
1208 if (filesel
->history_list
)
1210 list
= filesel
->history_list
;
1213 callback_arg
= list
->data
;
1214 g_free (callback_arg
->directory
);
1215 g_free (callback_arg
);
1218 g_list_free (filesel
->history_list
);
1219 filesel
->history_list
= NULL
;
1222 if (filesel
->cmpl_state
)
1224 cmpl_free_state (filesel
->cmpl_state
);
1225 filesel
->cmpl_state
= NULL
;
1228 if (filesel
->selected_names
)
1230 free_selected_names (filesel
->selected_names
);
1231 filesel
->selected_names
= NULL
;
1234 if (filesel
->last_selected
)
1236 g_free (filesel
->last_selected
);
1237 filesel
->last_selected
= NULL
;
1240 GTK_OBJECT_CLASS (parent_class
)->destroy (object
);
1244 gtk_dir_selection_map (GtkWidget
*widget
)
1246 GtkDirSelection
*filesel
= GTK_DIR_SELECTION (widget
);
1248 /* Refresh the contents */
1249 gtk_dir_selection_populate (filesel
, "", FALSE
, FALSE
);
1251 GTK_WIDGET_CLASS (parent_class
)->map (widget
);
1255 gtk_dir_selection_finalize (GObject
*object
)
1257 GtkDirSelection
*filesel
= GTK_DIR_SELECTION (object
);
1259 g_free (filesel
->fileop_file
);
1261 G_OBJECT_CLASS (parent_class
)->finalize (object
);
1264 /* Begin file operations callbacks */
1267 gtk_dir_selection_fileop_error (GtkDirSelection
*fs
,
1268 gchar
*error_message
)
1272 g_return_if_fail (error_message
!= NULL
);
1275 dialog
= gtk_message_dialog_new (GTK_WINDOW (fs
),
1276 GTK_DIALOG_DESTROY_WITH_PARENT
,
1279 "%s", error_message
);
1281 /* yes, we free it */
1282 g_free (error_message
);
1284 gtk_window_set_modal (GTK_WINDOW (dialog
), TRUE
);
1286 g_signal_connect_swapped (dialog
, "response",
1287 G_CALLBACK (gtk_widget_destroy
),
1290 gtk_widget_show (dialog
);
1294 gtk_dir_selection_fileop_destroy (GtkWidget
*widget
,
1297 GtkDirSelection
*fs
= data
;
1299 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1301 fs
->fileop_dialog
= NULL
;
1305 entry_is_empty (GtkEntry
*entry
)
1307 const gchar
*text
= gtk_entry_get_text (entry
);
1309 return *text
== '\0';
1313 gtk_dir_selection_fileop_entry_changed (GtkEntry
*entry
,
1316 gtk_widget_set_sensitive (button
, !entry_is_empty (entry
));
1320 gtk_dir_selection_create_dir_confirmed (GtkWidget
*widget
,
1323 GtkDirSelection
*fs
= data
;
1324 const gchar
*dirname
;
1327 gchar
*sys_full_path
;
1329 GError
*error
= NULL
;
1330 CompletionState
*cmpl_state
;
1332 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1334 dirname
= gtk_entry_get_text (GTK_ENTRY (fs
->fileop_entry
));
1335 cmpl_state
= (CompletionState
*) fs
->cmpl_state
;
1336 path
= cmpl_reference_position (cmpl_state
);
1338 full_path
= g_strconcat (path
, G_DIR_SEPARATOR_S
, dirname
, NULL
);
1339 sys_full_path
= g_filename_from_utf8 (full_path
, -1, NULL
, NULL
, &error
);
1342 if (g_error_matches (error
, G_CONVERT_ERROR
, G_CONVERT_ERROR_ILLEGAL_SEQUENCE
))
1343 buf
= g_strdup_printf (_("The folder name \"%s\" contains symbols that are not allowed in filenames"), dirname
);
1345 buf
= g_strdup_printf (_("Error creating folder \"%s\": %s\n%s"), dirname
, error
->message
,
1346 _("You probably used symbols not allowed in filenames."));
1347 gtk_dir_selection_fileop_error (fs
, buf
);
1348 g_error_free (error
);
1352 if (mkdir (sys_full_path
, 0755) < 0)
1354 buf
= g_strdup_printf (_("Error creating folder \"%s\": %s\n"), dirname
,
1355 g_strerror (errno
));
1356 gtk_dir_selection_fileop_error (fs
, buf
);
1361 g_free (sys_full_path
);
1363 gtk_widget_destroy (fs
->fileop_dialog
);
1364 gtk_dir_selection_populate (fs
, "", FALSE
, FALSE
);
1368 gtk_dir_selection_create_dir (GtkWidget
*widget
,
1371 GtkDirSelection
*fs
= data
;
1377 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1379 if (fs
->fileop_dialog
)
1383 dialog
= gtk_dialog_new ();
1384 fs
->fileop_dialog
= dialog
;
1385 g_signal_connect (dialog
, "destroy",
1386 G_CALLBACK (gtk_dir_selection_fileop_destroy
),
1388 gtk_window_set_title (GTK_WINDOW (dialog
), _("New Folder"));
1389 gtk_window_set_position (GTK_WINDOW (dialog
), GTK_WIN_POS_MOUSE
);
1390 gtk_window_set_transient_for (GTK_WINDOW (dialog
), GTK_WINDOW (fs
));
1392 /* If file dialog is grabbed, grab option dialog */
1393 /* When option dialog is closed, file dialog will be grabbed again */
1394 if (GTK_WINDOW (fs
)->modal
)
1395 gtk_window_set_modal (GTK_WINDOW (dialog
), TRUE
);
1397 vbox
= gtk_vbox_new (FALSE
, 0);
1398 gtk_container_set_border_width (GTK_CONTAINER (vbox
), 8);
1399 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), vbox
,
1401 gtk_widget_show( vbox
);
1403 label
= gtk_label_new_with_mnemonic (_("_Folder name:"));
1404 gtk_misc_set_alignment(GTK_MISC (label
), 0.0, 0.0);
1405 gtk_box_pack_start (GTK_BOX (vbox
), label
, FALSE
, FALSE
, 5);
1406 gtk_widget_show (label
);
1408 /* The directory entry widget */
1409 fs
->fileop_entry
= gtk_entry_new ();
1410 gtk_label_set_mnemonic_widget (GTK_LABEL (label
), fs
->fileop_entry
);
1411 gtk_box_pack_start (GTK_BOX (vbox
), fs
->fileop_entry
,
1413 GTK_WIDGET_SET_FLAGS (fs
->fileop_entry
, GTK_CAN_DEFAULT
);
1414 gtk_widget_show (fs
->fileop_entry
);
1417 button
= gtk_button_new_from_stock (GTK_STOCK_CANCEL
);
1418 g_signal_connect_swapped (button
, "clicked",
1419 G_CALLBACK (gtk_widget_destroy
),
1421 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->action_area
),
1422 button
, TRUE
, TRUE
, 0);
1423 GTK_WIDGET_SET_FLAGS (button
, GTK_CAN_DEFAULT
);
1424 gtk_widget_grab_default (button
);
1425 gtk_widget_show (button
);
1427 gtk_widget_grab_focus (fs
->fileop_entry
);
1429 button
= gtk_button_new_with_mnemonic (_("C_reate"));
1430 gtk_widget_set_sensitive (button
, FALSE
);
1431 g_signal_connect (button
, "clicked",
1432 G_CALLBACK (gtk_dir_selection_create_dir_confirmed
),
1434 g_signal_connect (fs
->fileop_entry
, "changed",
1435 G_CALLBACK (gtk_dir_selection_fileop_entry_changed
),
1438 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->action_area
),
1439 button
, TRUE
, TRUE
, 0);
1440 GTK_WIDGET_SET_FLAGS (button
, GTK_CAN_DEFAULT
);
1441 gtk_widget_show (button
);
1443 gtk_widget_show (dialog
);
1447 gtk_dir_selection_delete_dir_response (GtkDialog
*dialog
,
1451 GtkDirSelection
*fs
= data
;
1452 CompletionState
*cmpl_state
;
1455 gchar
*sys_full_path
;
1456 GError
*error
= NULL
;
1459 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1461 if (response_id
!= GTK_RESPONSE_OK
)
1463 gtk_widget_destroy (GTK_WIDGET (dialog
));
1467 cmpl_state
= (CompletionState
*) fs
->cmpl_state
;
1468 path
= cmpl_reference_position (cmpl_state
);
1470 full_path
= g_strconcat (path
, G_DIR_SEPARATOR_S
, fs
->fileop_file
, NULL
);
1471 sys_full_path
= g_filename_from_utf8 (full_path
, -1, NULL
, NULL
, &error
);
1474 if (g_error_matches (error
, G_CONVERT_ERROR
, G_CONVERT_ERROR_ILLEGAL_SEQUENCE
))
1475 buf
= g_strdup_printf (_("The filename \"%s\" contains symbols that are not allowed in filenames"),
1478 buf
= g_strdup_printf (_("Error deleting file \"%s\": %s\n%s"),
1479 fs
->fileop_file
, error
->message
,
1480 _("It probably contains symbols not allowed in filenames."));
1482 gtk_dir_selection_fileop_error (fs
, buf
);
1483 g_error_free (error
);
1487 if (unlink (sys_full_path
) < 0)
1489 buf
= g_strdup_printf (_("Error deleting file \"%s\": %s"),
1490 fs
->fileop_file
, g_strerror (errno
));
1491 gtk_dir_selection_fileop_error (fs
, buf
);
1496 g_free (sys_full_path
);
1498 gtk_widget_destroy (fs
->fileop_dialog
);
1499 gtk_dir_selection_populate (fs
, "", FALSE
, TRUE
);
1503 gtk_dir_selection_delete_file (GtkWidget
*widget
,
1506 GtkDirSelection
*fs
= data
;
1508 const gchar
*filename
;
1510 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1512 if (fs
->fileop_dialog
)
1515 #ifdef G_WITH_CYGWIN
1516 translate_win32_path (fs
);
1519 filename
= gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
));
1520 if (strlen (filename
) < 1)
1523 g_free (fs
->fileop_file
);
1524 fs
->fileop_file
= g_strdup (filename
);
1527 fs
->fileop_dialog
= dialog
=
1528 gtk_message_dialog_new (GTK_WINDOW (fs
),
1529 GTK_WINDOW (fs
)->modal
? GTK_DIALOG_MODAL
: 0,
1530 GTK_MESSAGE_QUESTION
,
1532 _("Really delete file \"%s\" ?"), filename
);
1534 g_signal_connect (dialog
, "destroy",
1535 G_CALLBACK (gtk_dir_selection_fileop_destroy
),
1537 gtk_window_set_title (GTK_WINDOW (dialog
), _("Delete File"));
1538 gtk_window_set_position (GTK_WINDOW (dialog
), GTK_WIN_POS_MOUSE
);
1541 gtk_dialog_add_buttons (GTK_DIALOG (dialog
),
1542 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
1543 GTK_STOCK_DELETE
, GTK_RESPONSE_OK
,
1546 gtk_dialog_set_default_response (GTK_DIALOG (dialog
), GTK_RESPONSE_CANCEL
);
1548 g_signal_connect (dialog
, "response",
1549 G_CALLBACK (gtk_dir_selection_delete_dir_response
),
1552 gtk_widget_show (dialog
);
1556 gtk_dir_selection_rename_dir_confirmed (GtkWidget
*widget
,
1559 GtkDirSelection
*fs
= data
;
1563 gchar
*new_filename
;
1564 gchar
*old_filename
;
1565 gchar
*sys_new_filename
;
1566 gchar
*sys_old_filename
;
1567 CompletionState
*cmpl_state
;
1568 GError
*error
= NULL
;
1570 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1572 file
= gtk_entry_get_text (GTK_ENTRY (fs
->fileop_entry
));
1573 cmpl_state
= (CompletionState
*) fs
->cmpl_state
;
1574 path
= cmpl_reference_position (cmpl_state
);
1576 new_filename
= g_strconcat (path
, G_DIR_SEPARATOR_S
, file
, NULL
);
1577 old_filename
= g_strconcat (path
, G_DIR_SEPARATOR_S
, fs
->fileop_file
, NULL
);
1579 sys_new_filename
= g_filename_from_utf8 (new_filename
, -1, NULL
, NULL
, &error
);
1582 if (g_error_matches (error
, G_CONVERT_ERROR
, G_CONVERT_ERROR_ILLEGAL_SEQUENCE
))
1583 buf
= g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), new_filename
);
1585 buf
= g_strdup_printf (_("Error renaming file to \"%s\": %s\n%s"),
1586 new_filename
, error
->message
,
1587 _("You probably used symbols not allowed in filenames."));
1588 gtk_dir_selection_fileop_error (fs
, buf
);
1589 g_error_free (error
);
1593 sys_old_filename
= g_filename_from_utf8 (old_filename
, -1, NULL
, NULL
, &error
);
1596 if (g_error_matches (error
, G_CONVERT_ERROR
, G_CONVERT_ERROR_ILLEGAL_SEQUENCE
))
1597 buf
= g_strdup_printf (_("The file name \"%s\" contains symbols that are not allowed in filenames"), old_filename
);
1599 buf
= g_strdup_printf (_("Error renaming file \"%s\": %s\n%s"),
1600 old_filename
, error
->message
,
1601 _("It probably contains symbols not allowed in filenames."));
1602 gtk_dir_selection_fileop_error (fs
, buf
);
1603 g_error_free (error
);
1607 if (rename (sys_old_filename
, sys_new_filename
) < 0)
1609 buf
= g_strdup_printf (_("Error renaming file \"%s\" to \"%s\": %s"),
1610 sys_old_filename
, sys_new_filename
,
1611 g_strerror (errno
));
1612 gtk_dir_selection_fileop_error (fs
, buf
);
1616 gtk_dir_selection_populate (fs
, "", FALSE
, FALSE
);
1617 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), file
);
1620 g_free (sys_old_filename
);
1623 g_free (new_filename
);
1624 g_free (old_filename
);
1625 g_free (sys_new_filename
);
1627 gtk_widget_destroy (fs
->fileop_dialog
);
1631 gtk_dir_selection_rename_file (GtkWidget
*widget
,
1634 GtkDirSelection
*fs
= data
;
1641 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1643 if (fs
->fileop_dialog
)
1646 g_free (fs
->fileop_file
);
1647 fs
->fileop_file
= g_strdup (gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
)));
1648 if (strlen (fs
->fileop_file
) < 1)
1652 fs
->fileop_dialog
= dialog
= gtk_dialog_new ();
1653 g_signal_connect (dialog
, "destroy",
1654 G_CALLBACK (gtk_dir_selection_fileop_destroy
),
1656 gtk_window_set_title (GTK_WINDOW (dialog
), _("Rename File"));
1657 gtk_window_set_position (GTK_WINDOW (dialog
), GTK_WIN_POS_MOUSE
);
1658 gtk_window_set_transient_for (GTK_WINDOW (dialog
), GTK_WINDOW (fs
));
1660 /* If file dialog is grabbed, grab option dialog */
1661 /* When option dialog closed, file dialog will be grabbed again */
1662 if (GTK_WINDOW (fs
)->modal
)
1663 gtk_window_set_modal (GTK_WINDOW (dialog
), TRUE
);
1665 vbox
= gtk_vbox_new (FALSE
, 0);
1666 gtk_container_set_border_width (GTK_CONTAINER (vbox
), 8);
1667 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), vbox
,
1669 gtk_widget_show(vbox
);
1671 buf
= g_strdup_printf (_("Rename file \"%s\" to:"), fs
->fileop_file
);
1672 label
= gtk_label_new (buf
);
1673 gtk_misc_set_alignment (GTK_MISC (label
), 0.0, 0.0);
1674 gtk_box_pack_start (GTK_BOX (vbox
), label
, FALSE
, FALSE
, 5);
1675 gtk_widget_show (label
);
1678 /* New filename entry */
1679 fs
->fileop_entry
= gtk_entry_new ();
1680 gtk_box_pack_start (GTK_BOX (vbox
), fs
->fileop_entry
,
1682 GTK_WIDGET_SET_FLAGS (fs
->fileop_entry
, GTK_CAN_DEFAULT
);
1683 gtk_widget_show (fs
->fileop_entry
);
1685 gtk_entry_set_text (GTK_ENTRY (fs
->fileop_entry
), fs
->fileop_file
);
1686 gtk_editable_select_region (GTK_EDITABLE (fs
->fileop_entry
),
1687 0, strlen (fs
->fileop_file
));
1690 button
= gtk_button_new_from_stock (GTK_STOCK_CANCEL
);
1691 g_signal_connect_swapped (button
, "clicked",
1692 G_CALLBACK (gtk_widget_destroy
),
1694 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->action_area
),
1695 button
, TRUE
, TRUE
, 0);
1696 GTK_WIDGET_SET_FLAGS (button
, GTK_CAN_DEFAULT
);
1697 gtk_widget_grab_default (button
);
1698 gtk_widget_show (button
);
1700 gtk_widget_grab_focus (fs
->fileop_entry
);
1702 button
= gtk_button_new_with_mnemonic (_("_Rename"));
1703 g_signal_connect (button
, "clicked",
1704 G_CALLBACK (gtk_dir_selection_rename_dir_confirmed
),
1706 g_signal_connect (fs
->fileop_entry
, "changed",
1707 G_CALLBACK (gtk_dir_selection_fileop_entry_changed
),
1710 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->action_area
),
1711 button
, TRUE
, TRUE
, 0);
1712 GTK_WIDGET_SET_FLAGS (button
, GTK_CAN_DEFAULT
);
1713 gtk_widget_show (button
);
1715 gtk_widget_show (dialog
);
1719 gtk_dir_selection_insert_text (GtkWidget
*widget
,
1720 const gchar
*new_text
,
1721 gint new_text_length
,
1727 filename
= g_filename_from_utf8 (new_text
, new_text_length
, NULL
, NULL
, NULL
);
1731 gdk_display_beep (gtk_widget_get_display (widget
));
1732 g_signal_stop_emission_by_name (widget
, "insert_text");
1742 gtk_dir_selection_update_fileops (GtkDirSelection
*fs
)
1746 if (!fs
->selection_entry
)
1749 sensitive
= !entry_is_empty (GTK_ENTRY (fs
->selection_entry
));
1751 if (fs
->fileop_del_file
)
1752 gtk_widget_set_sensitive (fs
->fileop_del_file
, sensitive
);
1754 if (fs
->fileop_ren_file
)
1755 gtk_widget_set_sensitive (fs
->fileop_ren_file
, sensitive
);
1759 gtk_dir_selection_key_press (GtkWidget
*widget
,
1763 GtkDirSelection
*fs
;
1766 g_return_val_if_fail (widget
!= NULL
, FALSE
);
1767 g_return_val_if_fail (event
!= NULL
, FALSE
);
1769 if ((event
->keyval
== GDK_Tab
|| event
->keyval
== GDK_KP_Tab
) &&
1770 (event
->state
& gtk_accelerator_get_default_mod_mask ()) == 0)
1772 fs
= GTK_DIR_SELECTION (user_data
);
1773 #ifdef G_WITH_CYGWIN
1774 translate_win32_path (fs
);
1776 text
= g_strdup (gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
)));
1778 gtk_dir_selection_populate (fs
, text
, TRUE
, TRUE
);
1789 gtk_dir_selection_history_callback (GtkWidget
*widget
,
1792 GtkDirSelection
*fs
= data
;
1793 HistoryCallbackArg
*callback_arg
;
1796 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1798 list
= fs
->history_list
;
1801 callback_arg
= list
->data
;
1803 if (callback_arg
->menu_item
== widget
)
1805 gtk_dir_selection_populate (fs
, callback_arg
->directory
, FALSE
, FALSE
);
1814 gtk_dir_selection_update_history_menu (GtkDirSelection
*fs
,
1815 gchar
*current_directory
)
1817 HistoryCallbackArg
*callback_arg
;
1818 GtkWidget
*menu_item
;
1824 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
1825 g_return_if_fail (current_directory
!= NULL
);
1827 list
= fs
->history_list
;
1829 if (fs
->history_menu
)
1832 callback_arg
= list
->data
;
1833 g_free (callback_arg
->directory
);
1834 g_free (callback_arg
);
1837 g_list_free (fs
->history_list
);
1838 fs
->history_list
= NULL
;
1840 gtk_widget_destroy (fs
->history_menu
);
1843 fs
->history_menu
= gtk_menu_new ();
1845 current_dir
= g_strdup (current_directory
);
1847 dir_len
= strlen (current_dir
);
1849 for (i
= dir_len
; i
>= 0; i
--)
1851 /* the i == dir_len is to catch the full path for the first
1853 if ( (current_dir
[i
] == G_DIR_SEPARATOR
) || (i
== dir_len
))
1855 /* another small hack to catch the full path */
1857 current_dir
[i
+ 1] = '\0';
1858 #ifdef G_WITH_CYGWIN
1859 if (!strcmp (current_dir
, "//"))
1862 menu_item
= gtk_menu_item_new_with_label (current_dir
);
1864 callback_arg
= g_new (HistoryCallbackArg
, 1);
1865 callback_arg
->menu_item
= menu_item
;
1867 /* since the autocompletion gets confused if you don't
1868 * supply a trailing '/' on a dir entry, set the full
1869 * (current) path to "" which just refreshes the filesel */
1872 callback_arg
->directory
= g_strdup ("");
1876 callback_arg
->directory
= g_strdup (current_dir
);
1879 fs
->history_list
= g_list_append (fs
->history_list
, callback_arg
);
1881 g_signal_connect (menu_item
, "activate",
1882 G_CALLBACK (gtk_dir_selection_history_callback
),
1884 gtk_menu_shell_append (GTK_MENU_SHELL (fs
->history_menu
), menu_item
);
1885 gtk_widget_show (menu_item
);
1889 gtk_option_menu_set_menu (GTK_OPTION_MENU (fs
->history_pulldown
),
1891 g_free (current_dir
);
1895 get_real_filename (gchar
*filename
,
1898 #ifdef G_WITH_CYGWIN
1899 /* Check to see if the selection was a drive selector */
1900 if (isalpha (filename
[0]) && (filename
[1] == ':'))
1902 gchar temp_filename
[MAX_PATH
];
1905 cygwin_conv_to_posix_path (filename
, temp_filename
);
1907 /* we need trailing '/'. */
1908 len
= strlen (temp_filename
);
1909 if (len
> 0 && temp_filename
[len
-1] != '/')
1911 temp_filename
[len
] = '/';
1912 temp_filename
[len
+1] = '\0';
1918 return g_strdup (temp_filename
);
1920 #endif /* G_WITH_CYGWIN */
1925 gtk_dir_selection_file_activate (GtkTreeView
*tree_view
,
1927 GtkTreeViewColumn
*column
,
1930 GtkDirSelection
*fs
= GTK_DIR_SELECTION (user_data
);
1931 GtkTreeModel
*model
= gtk_tree_view_get_model (tree_view
);
1935 gtk_tree_model_get_iter (model
, &iter
, path
);
1936 gtk_tree_model_get (model
, &iter
, FILE_COLUMN
, &filename
, -1);
1937 filename
= get_real_filename (filename
, TRUE
);
1938 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), filename
);
1939 gtk_button_clicked (GTK_BUTTON (fs
->ok_button
));
1945 gtk_dir_selection_dir_activate (GtkTreeView
*tree_view
,
1947 GtkTreeViewColumn
*column
,
1950 GtkDirSelection
*fs
= GTK_DIR_SELECTION (user_data
);
1951 GtkTreeModel
*model
= gtk_tree_view_get_model (tree_view
);
1955 gtk_tree_model_get_iter (model
, &iter
, path
);
1956 gtk_tree_model_get (model
, &iter
, DIR_COLUMN
, &filename
, -1);
1957 filename
= get_real_filename (filename
, TRUE
);
1958 gtk_dir_selection_populate (fs
, filename
, FALSE
, FALSE
);
1962 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
1965 win32_gtk_add_drives_to_dir_list (GtkListStore
*model
)
1969 char formatBuffer
[128];
1972 /* Get the drives string */
1973 GetLogicalDriveStrings (sizeof (buffer
), buffer
);
1975 /* Add the drives as necessary */
1977 while (*textPtr
!= '\0')
1979 /* Ignore floppies (?) */
1980 if ((tolower (textPtr
[0]) != 'a') && (tolower (textPtr
[0]) != 'b'))
1982 /* Build the actual displayable string */
1983 g_snprintf (formatBuffer
, sizeof (formatBuffer
), "%c:\\", toupper (textPtr
[0]));
1985 /* Add to the list */
1986 gtk_list_store_append (model
, &iter
);
1987 gtk_list_store_set (model
, &iter
, DIR_COLUMN
, formatBuffer
, -1);
1989 textPtr
+= (strlen (textPtr
) + 1);
1995 escape_underscores (const gchar
*str
)
1997 GString
*result
= g_string_new (NULL
);
2001 g_string_append_c (result
, '_');
2003 g_string_append_c (result
, *str
);
2007 return g_string_free (result
, FALSE
);
2011 gtk_dir_selection_populate (GtkDirSelection
*fs
,
2013 gboolean try_complete
,
2014 gboolean reset_entry
)
2016 CompletionState
*cmpl_state
;
2017 PossibleCompletion
* poss
;
2019 GtkListStore
*dir_model
;
2020 GtkListStore
*file_model
;
2022 gchar
* rem_path
= rel_path
;
2024 gint did_recurse
= FALSE
;
2025 gint possible_count
= 0;
2026 gint selection_index
= -1;
2028 g_return_if_fail (GTK_IS_DIR_SELECTION (fs
));
2030 cmpl_state
= (CompletionState
*) fs
->cmpl_state
;
2031 poss
= cmpl_completion_matches (rel_path
, &rem_path
, cmpl_state
);
2033 if (!cmpl_state_okay (cmpl_state
))
2035 /* Something went wrong. */
2036 gtk_dir_selection_abort (fs
);
2040 g_assert (cmpl_state
->reference_dir
);
2042 dir_model
= GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs
->dir_list
)));
2043 file_model
= GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (fs
->file_list
)));
2045 gtk_list_store_clear (dir_model
);
2046 gtk_list_store_clear (file_model
);
2048 /* Set the dir list to include ./ and ../ */
2049 gtk_list_store_append (dir_model
, &iter
);
2050 gtk_list_store_set (dir_model
, &iter
, DIR_COLUMN
, "." G_DIR_SEPARATOR_S
, -1);
2051 gtk_list_store_append (dir_model
, &iter
);
2052 gtk_list_store_set (dir_model
, &iter
, DIR_COLUMN
, ".." G_DIR_SEPARATOR_S
, -1);
2056 if (cmpl_is_a_completion (poss
))
2058 possible_count
+= 1;
2060 filename
= cmpl_this_completion (poss
);
2062 if (cmpl_is_directory (poss
))
2064 if (strcmp (filename
, "." G_DIR_SEPARATOR_S
) != 0 &&
2065 strcmp (filename
, ".." G_DIR_SEPARATOR_S
) != 0)
2067 gtk_list_store_append (dir_model
, &iter
);
2068 gtk_list_store_set (dir_model
, &iter
, DIR_COLUMN
, filename
, -1);
2073 gtk_list_store_append (file_model
, &iter
);
2074 gtk_list_store_set (file_model
, &iter
, DIR_COLUMN
, filename
, -1);
2078 poss
= cmpl_next_completion (cmpl_state
);
2081 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
2082 /* For Windows, add drives as potential selections */
2083 win32_gtk_add_drives_to_dir_list (dir_model
);
2086 /* File lists are set. */
2088 g_assert (cmpl_state
->reference_dir
);
2093 /* User is trying to complete filenames, so advance the user's input
2094 * string to the updated_text, which is the common leading substring
2095 * of all possible completions, and if its a directory attempt
2096 * attempt completions in it. */
2098 if (cmpl_updated_text (cmpl_state
)[0])
2101 if (cmpl_updated_dir (cmpl_state
))
2103 gchar
* dir_name
= g_strdup (cmpl_updated_text (cmpl_state
));
2107 gtk_dir_selection_populate (fs
, dir_name
, TRUE
, TRUE
);
2113 if (fs
->selection_entry
)
2114 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
),
2115 cmpl_updated_text (cmpl_state
));
2120 selection_index
= cmpl_last_valid_char (cmpl_state
) -
2121 (strlen (rel_path
) - strlen (rem_path
));
2122 if (fs
->selection_entry
)
2123 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), rem_path
);
2126 else if (reset_entry
)
2128 if (fs
->selection_entry
)
2129 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), "");
2134 if (fs
->selection_entry
)
2135 gtk_editable_set_position (GTK_EDITABLE (fs
->selection_entry
),
2138 if (fs
->selection_entry
)
2140 char *escaped
= escape_underscores (cmpl_reference_position (cmpl_state
));
2141 sel_text
= g_strconcat (_("_Selection: "), escaped
, NULL
);
2144 gtk_label_set_text_with_mnemonic (GTK_LABEL (fs
->selection_text
), sel_text
);
2148 if (fs
->history_pulldown
)
2150 gtk_dir_selection_update_history_menu (fs
, cmpl_reference_position (cmpl_state
));
2157 gtk_dir_selection_abort (GtkDirSelection
*fs
)
2161 g_snprintf (err_buf
, sizeof (err_buf
), _("Folder unreadable: %s"), cmpl_strerror (cmpl_errno
));
2163 /* BEEP gdk_beep(); */
2165 if (fs
->selection_entry
)
2166 gtk_label_set_text (GTK_LABEL (fs
->selection_text
), err_buf
);
2170 * gtk_dir_selection_set_select_multiple:
2171 * @filesel: a #GtkDirSelection
2172 * @select_multiple: whether or not the user is allowed to select multiple
2173 * files in the file list.
2175 * Sets whether the user is allowed to select multiple files in the file list.
2176 * Use gtk_dir_selection_get_selections () to get the list of selected files.
2179 gtk_dir_selection_set_select_multiple (GtkDirSelection
*filesel
,
2180 gboolean select_multiple
)
2182 GtkTreeSelection
*sel
;
2183 GtkSelectionMode mode
;
2185 g_return_if_fail (GTK_IS_DIR_SELECTION (filesel
));
2187 sel
= gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel
->file_list
));
2189 mode
= select_multiple
? GTK_SELECTION_MULTIPLE
: GTK_SELECTION_SINGLE
;
2191 if (mode
!= gtk_tree_selection_get_mode (sel
))
2193 gtk_tree_selection_set_mode (sel
, mode
);
2195 g_object_notify (G_OBJECT (filesel
), "select-multiple");
2200 * gtk_dir_selection_get_select_multiple:
2201 * @filesel: a #GtkDirSelection
2203 * Determines whether or not the user is allowed to select multiple files in
2204 * the file list. See gtk_dir_selection_set_select_multiple().
2206 * Return value: %TRUE if the user is allowed to select multiple files in the
2210 gtk_dir_selection_get_select_multiple (GtkDirSelection
*filesel
)
2212 GtkTreeSelection
*sel
;
2214 g_return_val_if_fail (GTK_IS_DIR_SELECTION (filesel
), FALSE
);
2216 sel
= gtk_tree_view_get_selection (GTK_TREE_VIEW (filesel
->file_list
));
2217 return (gtk_tree_selection_get_mode (sel
) == GTK_SELECTION_MULTIPLE
);
2221 multiple_changed_foreach (GtkTreeModel
*model
,
2226 GPtrArray
*names
= data
;
2229 gtk_tree_model_get (model
, iter
, FILE_COLUMN
, &filename
, -1);
2231 g_ptr_array_add (names
, filename
);
2235 free_selected_names (GPtrArray
*names
)
2239 for (i
= 0; i
< names
->len
; i
++)
2240 g_free (g_ptr_array_index (names
, i
));
2242 g_ptr_array_free (names
, TRUE
);
2246 gtk_dir_selection_file_changed (GtkTreeSelection
*selection
,
2249 GtkDirSelection
*fs
= GTK_DIR_SELECTION (user_data
);
2250 GPtrArray
*new_names
;
2255 new_names
= g_ptr_array_sized_new (8);
2257 gtk_tree_selection_selected_foreach (selection
,
2258 multiple_changed_foreach
,
2261 /* nothing selected */
2262 if (new_names
->len
== 0)
2264 g_ptr_array_free (new_names
, TRUE
);
2266 if (fs
->selected_names
!= NULL
)
2268 free_selected_names (fs
->selected_names
);
2269 fs
->selected_names
= NULL
;
2272 goto maybe_clear_entry
;
2275 if (new_names
->len
!= 1)
2277 GPtrArray
*old_names
= fs
->selected_names
;
2279 if (old_names
!= NULL
)
2281 /* A common case is selecting a range of files from top to bottom,
2282 * so quickly check for that to avoid looping over the entire list
2284 if (compare_filenames (g_ptr_array_index (old_names
, old_names
->len
- 1),
2285 g_ptr_array_index (new_names
, new_names
->len
- 1)) != 0)
2286 index
= new_names
->len
- 1;
2289 gint i
= 0, j
= 0, cmp
;
2291 /* do a quick diff, stopping at the first file not in the
2294 while (i
< old_names
->len
&& j
< new_names
->len
)
2296 cmp
= compare_filenames (g_ptr_array_index (old_names
, i
),
2297 g_ptr_array_index (new_names
, j
));
2314 /* we ran off the end of the old list */
2315 if (index
== -1 && i
< new_names
->len
)
2321 /* A phantom anchor still exists at the point where the last item
2322 * was selected, which is used for subsequent range selections.
2323 * So search up from there.
2325 if (fs
->last_selected
&&
2326 compare_filenames (fs
->last_selected
,
2327 g_ptr_array_index (new_names
, 0)) == 0)
2328 index
= new_names
->len
- 1;
2336 if (fs
->selected_names
!= NULL
)
2337 free_selected_names (fs
->selected_names
);
2339 fs
->selected_names
= new_names
;
2343 if (fs
->last_selected
!= NULL
)
2344 g_free (fs
->last_selected
);
2346 fs
->last_selected
= g_strdup (g_ptr_array_index (new_names
, index
));
2347 filename
= get_real_filename (fs
->last_selected
, FALSE
);
2349 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), filename
);
2351 if (filename
!= fs
->last_selected
)
2359 entry
= gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
));
2360 if ((entry
!= NULL
) && (fs
->last_selected
!= NULL
) &&
2361 (compare_filenames (entry
, fs
->last_selected
) == 0))
2362 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), "");
2366 gtk_dir_selection_dir_changed (GtkTreeSelection
*selection
,
2369 GtkDirSelection
*fs
= GTK_DIR_SELECTION (user_data
);
2370 GPtrArray
*new_names
;
2375 new_names
= g_ptr_array_sized_new (8);
2377 gtk_tree_selection_selected_foreach (selection
,
2378 multiple_changed_foreach
,
2381 /* nothing selected */
2382 if (new_names
->len
== 0)
2384 g_ptr_array_free (new_names
, TRUE
);
2386 if (fs
->selected_names
!= NULL
)
2388 free_selected_names (fs
->selected_names
);
2389 fs
->selected_names
= NULL
;
2392 goto maybe_clear_entry
;
2395 if (new_names
->len
!= 1)
2397 GPtrArray
*old_names
= fs
->selected_names
;
2399 if (old_names
!= NULL
)
2401 /* A common case is selecting a range of files from top to bottom,
2402 * so quickly check for that to avoid looping over the entire list
2404 if (compare_filenames (g_ptr_array_index (old_names
, old_names
->len
- 1),
2405 g_ptr_array_index (new_names
, new_names
->len
- 1)) != 0)
2406 index
= new_names
->len
- 1;
2409 gint i
= 0, j
= 0, cmp
;
2411 /* do a quick diff, stopping at the first file not in the
2414 while (i
< old_names
->len
&& j
< new_names
->len
)
2416 cmp
= compare_filenames (g_ptr_array_index (old_names
, i
),
2417 g_ptr_array_index (new_names
, j
));
2434 /* we ran off the end of the old list */
2435 if (index
== -1 && i
< new_names
->len
)
2441 /* A phantom anchor still exists at the point where the last item
2442 * was selected, which is used for subsequent range selections.
2443 * So search up from there.
2445 if (fs
->last_selected
&&
2446 compare_filenames (fs
->last_selected
,
2447 g_ptr_array_index (new_names
, 0)) == 0)
2448 index
= new_names
->len
- 1;
2456 if (fs
->selected_names
!= NULL
)
2457 free_selected_names (fs
->selected_names
);
2459 fs
->selected_names
= new_names
;
2463 gchar
* err
, str
[256];
2464 err
= gtk_label_get_text (GTK_LABEL (fs
->selection_text
));
2465 err
+= 11; //pass over "Selection: "
2466 sprintf(str
,"%s\0",err
);
2469 if (fs
->last_selected
!= NULL
)
2470 g_free (fs
->last_selected
);
2472 fs
->last_selected
= g_strdup (g_ptr_array_index (new_names
, index
));
2473 filename
= get_real_filename (fs
->last_selected
, FALSE
);
2476 strcat(str
,filename
);
2477 str
[strlen(str
)-1] = '\0';
2479 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), str
);
2481 if (filename
!= fs
->last_selected
)
2489 entry
= gtk_entry_get_text (GTK_ENTRY (fs
->selection_entry
));
2490 if ((entry
!= NULL
) && (fs
->last_selected
!= NULL
) &&
2491 (compare_filenames (entry
, fs
->last_selected
) == 0))
2492 gtk_entry_set_text (GTK_ENTRY (fs
->selection_entry
), "");
2496 * gtk_dir_selection_get_selections:
2497 * @filesel: a #GtkDirSelection
2499 * Retrieves the list of file selections the user has made in the dialog box.
2500 * This function is intended for use when the user can select multiple files
2501 * in the file list. The first file in the list is equivalent to what
2502 * gtk_dir_selection_get_filename() would return.
2504 * The filenames are in the encoding of g_filename_from_utf8(), which may or
2505 * may not be the same as that used by GTK+ (UTF-8). To convert to UTF-8, call
2506 * g_filename_to_utf8() on each string.
2508 * Return value: a newly-allocated %NULL-terminated array of strings. Use
2509 * g_strfreev() to free it.
2512 gtk_dir_selection_get_selections (GtkDirSelection
*filesel
)
2516 gchar
*filename
, *dirname
;
2517 gchar
*current
, *buf
;
2519 gboolean unselected_entry
;
2521 g_return_val_if_fail (GTK_IS_DIR_SELECTION (filesel
), NULL
);
2523 filename
= g_strdup (gtk_dir_selection_get_filename (filesel
));
2525 if (strlen (filename
) == 0)
2531 names
= filesel
->selected_names
;
2534 selections
= g_new (gchar
*, names
->len
+ 2);
2536 selections
= g_new (gchar
*, 2);
2539 unselected_entry
= TRUE
;
2543 dirname
= g_path_get_dirname (filename
);
2545 for (i
= 0; i
< names
->len
; i
++)
2547 buf
= g_filename_from_utf8 (g_ptr_array_index (names
, i
), -1,
2549 current
= g_build_filename (dirname
, buf
, NULL
);
2552 selections
[count
++] = current
;
2554 if (unselected_entry
&& compare_filenames (current
, filename
) == 0)
2555 unselected_entry
= FALSE
;
2561 if (unselected_entry
)
2562 selections
[count
++] = filename
;
2566 selections
[count
] = NULL
;
2571 /**********************************************************************/
2572 /* External Interface */
2573 /**********************************************************************/
2575 /* The four completion state selectors
2578 cmpl_updated_text (CompletionState
*cmpl_state
)
2580 return cmpl_state
->updated_text
;
2584 cmpl_updated_dir (CompletionState
*cmpl_state
)
2586 return cmpl_state
->re_complete
;
2590 cmpl_reference_position (CompletionState
*cmpl_state
)
2592 return cmpl_state
->reference_dir
->fullname
;
2596 cmpl_last_valid_char (CompletionState
*cmpl_state
)
2598 return cmpl_state
->last_valid_char
;
2602 cmpl_completion_fullname (const gchar
*text
,
2603 CompletionState
*cmpl_state
)
2605 static const char nothing
[2] = "";
2607 if (!cmpl_state_okay (cmpl_state
))
2611 else if (g_path_is_absolute (text
))
2613 strcpy (cmpl_state
->updated_text
, text
);
2616 else if (text
[0] == '~')
2621 dir
= open_user_dir (text
, cmpl_state
);
2625 /* spencer says just return ~something, so
2626 * for now just do it. */
2627 strcpy (cmpl_state
->updated_text
, text
);
2632 strcpy (cmpl_state
->updated_text
, dir
->fullname
);
2634 slash
= strchr (text
, G_DIR_SEPARATOR
);
2637 strcat (cmpl_state
->updated_text
, slash
);
2643 strcpy (cmpl_state
->updated_text
, cmpl_state
->reference_dir
->fullname
);
2644 if (cmpl_state
->updated_text
[strlen (cmpl_state
->updated_text
) - 1] != G_DIR_SEPARATOR
)
2645 strcat (cmpl_state
->updated_text
, G_DIR_SEPARATOR_S
);
2646 strcat (cmpl_state
->updated_text
, text
);
2649 return cmpl_state
->updated_text
;
2652 /* The three completion selectors
2655 cmpl_this_completion (PossibleCompletion
* pc
)
2661 cmpl_is_directory (PossibleCompletion
* pc
)
2663 return pc
->is_directory
;
2667 cmpl_is_a_completion (PossibleCompletion
* pc
)
2669 return pc
->is_a_completion
;
2672 /**********************************************************************/
2673 /* Construction, deletion */
2674 /**********************************************************************/
2676 static CompletionState
*
2677 cmpl_init_state (void)
2679 gchar
*sys_getcwd_buf
;
2681 CompletionState
*new_state
;
2683 new_state
= g_new (CompletionState
, 1);
2685 /* g_get_current_dir() returns a string in the "system" charset */
2686 sys_getcwd_buf
= g_get_current_dir ();
2687 utf8_cwd
= g_filename_to_utf8 (sys_getcwd_buf
, -1, NULL
, NULL
, NULL
);
2688 g_free (sys_getcwd_buf
);
2692 new_state
->reference_dir
= NULL
;
2693 new_state
->completion_dir
= NULL
;
2694 new_state
->active_completion_dir
= NULL
;
2695 new_state
->directory_storage
= NULL
;
2696 new_state
->directory_sent_storage
= NULL
;
2697 new_state
->last_valid_char
= 0;
2698 new_state
->updated_text
= g_new (gchar
, MAXPATHLEN
);
2699 new_state
->updated_text_alloc
= MAXPATHLEN
;
2700 new_state
->the_completion
.text
= g_new (gchar
, MAXPATHLEN
);
2701 new_state
->the_completion
.text_alloc
= MAXPATHLEN
;
2702 new_state
->user_dir_name_buffer
= NULL
;
2703 new_state
->user_directories
= NULL
;
2705 new_state
->reference_dir
= open_dir (utf8_cwd
, new_state
);
2707 if (!new_state
->reference_dir
)
2709 /* Directories changing from underneath us, grumble */
2710 strcpy (utf8_cwd
, G_DIR_SEPARATOR_S
);
2719 cmpl_free_dir_list (GList
* dp0
)
2725 free_dir (dp
->data
);
2733 cmpl_free_dir_sent_list (GList
* dp0
)
2739 free_dir_sent (dp
->data
);
2747 cmpl_free_state (CompletionState
* cmpl_state
)
2749 g_return_if_fail (cmpl_state
!= NULL
);
2751 cmpl_free_dir_list (cmpl_state
->directory_storage
);
2752 cmpl_free_dir_sent_list (cmpl_state
->directory_sent_storage
);
2754 if (cmpl_state
->user_dir_name_buffer
)
2755 g_free (cmpl_state
->user_dir_name_buffer
);
2756 if (cmpl_state
->user_directories
)
2757 g_free (cmpl_state
->user_directories
);
2758 if (cmpl_state
->the_completion
.text
)
2759 g_free (cmpl_state
->the_completion
.text
);
2760 if (cmpl_state
->updated_text
)
2761 g_free (cmpl_state
->updated_text
);
2763 g_free (cmpl_state
);
2767 free_dir (CompletionDir
* dir
)
2769 g_free (dir
->cmpl_text
);
2770 g_free (dir
->fullname
);
2775 free_dir_sent (CompletionDirSent
* sent
)
2778 for (i
= 0; i
< sent
->entry_count
; i
++)
2780 g_free (sent
->entries
[i
].entry_name
);
2781 g_free (sent
->entries
[i
].sort_key
);
2783 g_free (sent
->entries
);
2788 prune_memory_usage (CompletionState
*cmpl_state
)
2790 GList
* cdsl
= cmpl_state
->directory_sent_storage
;
2791 GList
* cdl
= cmpl_state
->directory_storage
;
2795 for (; cdsl
&& len
< CMPL_DIRECTORY_CACHE_SIZE
; len
+= 1)
2800 cmpl_free_dir_sent_list (cdsl
->next
);
2804 cmpl_state
->directory_storage
= NULL
;
2807 if (cdl
->data
== cmpl_state
->reference_dir
)
2808 cmpl_state
->directory_storage
= g_list_prepend (NULL
, cdl
->data
);
2810 free_dir (cdl
->data
);
2817 /**********************************************************************/
2818 /* The main entrances. */
2819 /**********************************************************************/
2821 static PossibleCompletion
*
2822 cmpl_completion_matches (gchar
*text_to_complete
,
2823 gchar
**remaining_text
,
2824 CompletionState
*cmpl_state
)
2827 PossibleCompletion
*poss
;
2829 prune_memory_usage (cmpl_state
);
2831 g_assert (text_to_complete
!= NULL
);
2833 cmpl_state
->user_completion_index
= -1;
2834 cmpl_state
->last_completion_text
= text_to_complete
;
2835 cmpl_state
->the_completion
.text
[0] = 0;
2836 cmpl_state
->last_valid_char
= 0;
2837 cmpl_state
->updated_text_len
= -1;
2838 cmpl_state
->updated_text
[0] = 0;
2839 cmpl_state
->re_complete
= FALSE
;
2842 first_slash
= strchr (text_to_complete
, G_DIR_SEPARATOR
);
2844 if (text_to_complete
[0] == '~' && !first_slash
)
2846 /* Text starts with ~ and there is no slash, show all the
2847 * home directory completions.
2849 poss
= attempt_homedir_completion (text_to_complete
, cmpl_state
);
2851 update_cmpl (poss
, cmpl_state
);
2856 cmpl_state
->reference_dir
=
2857 open_ref_dir (text_to_complete
, remaining_text
, cmpl_state
);
2859 if (!cmpl_state
->reference_dir
)
2862 cmpl_state
->completion_dir
=
2863 find_completion_dir (*remaining_text
, remaining_text
, cmpl_state
);
2865 cmpl_state
->last_valid_char
= *remaining_text
- text_to_complete
;
2867 if (!cmpl_state
->completion_dir
)
2870 cmpl_state
->completion_dir
->cmpl_index
= -1;
2871 cmpl_state
->completion_dir
->cmpl_parent
= NULL
;
2872 cmpl_state
->completion_dir
->cmpl_text
= g_strdup (*remaining_text
);
2874 cmpl_state
->active_completion_dir
= cmpl_state
->completion_dir
;
2876 cmpl_state
->reference_dir
= cmpl_state
->completion_dir
;
2878 poss
= attempt_dir_completion (cmpl_state
);
2880 update_cmpl (poss
, cmpl_state
);
2885 static PossibleCompletion
*
2886 cmpl_next_completion (CompletionState
* cmpl_state
)
2888 PossibleCompletion
* poss
= NULL
;
2890 cmpl_state
->the_completion
.text
[0] = 0;
2893 if (cmpl_state
->user_completion_index
>= 0)
2894 poss
= attempt_homedir_completion (cmpl_state
->last_completion_text
, cmpl_state
);
2896 poss
= attempt_dir_completion (cmpl_state
);
2898 poss
= attempt_dir_completion (cmpl_state
);
2901 update_cmpl (poss
, cmpl_state
);
2906 /**********************************************************************/
2907 /* Directory Operations */
2908 /**********************************************************************/
2910 /* Open the directory where completion will begin from, if possible. */
2911 static CompletionDir
*
2912 open_ref_dir (gchar
*text_to_complete
,
2913 gchar
**remaining_text
,
2914 CompletionState
*cmpl_state
)
2917 CompletionDir
*new_dir
;
2919 first_slash
= strchr (text_to_complete
, G_DIR_SEPARATOR
);
2921 #ifdef G_WITH_CYGWIN
2922 if (text_to_complete
[0] == '/' && text_to_complete
[1] == '/')
2925 g_snprintf (root_dir
, sizeof (root_dir
), "//%c", text_to_complete
[2]);
2927 new_dir
= open_dir (root_dir
, cmpl_state
);
2930 *remaining_text
= text_to_complete
+ 4;
2938 else if (text_to_complete
[0] == '~')
2940 new_dir
= open_user_dir (text_to_complete
, cmpl_state
);
2945 *remaining_text
= first_slash
+ 1;
2947 *remaining_text
= text_to_complete
+ strlen (text_to_complete
);
2955 else if (g_path_is_absolute (text_to_complete
) || !cmpl_state
->reference_dir
)
2957 gchar
*tmp
= g_strdup (text_to_complete
);
2961 while (*p
&& *p
!= '*' && *p
!= '?')
2965 p
= strrchr (tmp
, G_DIR_SEPARATOR
);
2973 new_dir
= open_dir (tmp
, cmpl_state
);
2976 *remaining_text
= text_to_complete
+
2977 ((p
== tmp
+ 1) ? (p
- tmp
) : (p
+ 1 - tmp
));
2981 /* If no possible candidates, use the cwd */
2982 gchar
*sys_curdir
= g_get_current_dir ();
2983 gchar
*utf8_curdir
= g_filename_to_utf8 (sys_curdir
, -1, NULL
, NULL
, NULL
);
2985 g_free (sys_curdir
);
2987 new_dir
= open_dir (utf8_curdir
, cmpl_state
);
2990 *remaining_text
= text_to_complete
;
2992 g_free (utf8_curdir
);
2999 *remaining_text
= text_to_complete
;
3001 new_dir
= open_dir (cmpl_state
->reference_dir
->fullname
, cmpl_state
);
3006 new_dir
->cmpl_index
= -1;
3007 new_dir
->cmpl_parent
= NULL
;
3015 /* open a directory by user name */
3016 static CompletionDir
*
3017 open_user_dir (const gchar
*text_to_complete
,
3018 CompletionState
*cmpl_state
)
3020 CompletionDir
*result
;
3024 g_assert (text_to_complete
&& text_to_complete
[0] == '~');
3026 first_slash
= strchr (text_to_complete
, G_DIR_SEPARATOR
);
3029 cmp_len
= first_slash
- text_to_complete
- 1;
3031 cmp_len
= strlen (text_to_complete
+ 1);
3036 const gchar
*homedir
= g_get_home_dir ();
3037 gchar
*utf8_homedir
= g_filename_to_utf8 (homedir
, -1, NULL
, NULL
, NULL
);
3040 result
= open_dir (utf8_homedir
, cmpl_state
);
3044 g_free (utf8_homedir
);
3049 gchar
* copy
= g_new (char, cmp_len
+ 1);
3053 strncpy (copy
, text_to_complete
+ 1, cmp_len
);
3055 pwd
= getpwnam (copy
);
3062 utf8_dir
= g_filename_to_utf8 (pwd
->pw_dir
, -1, NULL
, NULL
, NULL
);
3063 result
= open_dir (utf8_dir
, cmpl_state
);
3071 /* open a directory relative the the current relative directory */
3072 static CompletionDir
*
3073 open_relative_dir (gchar
*dir_name
,
3075 CompletionState
*cmpl_state
)
3077 CompletionDir
*result
;
3080 path
= g_string_sized_new (dir
->fullname_len
+ strlen (dir_name
) + 10);
3081 g_string_assign (path
, dir
->fullname
);
3083 if (dir
->fullname_len
> 1
3084 && path
->str
[dir
->fullname_len
- 1] != G_DIR_SEPARATOR
)
3085 g_string_append_c (path
, G_DIR_SEPARATOR
);
3086 g_string_append (path
, dir_name
);
3088 result
= open_dir (path
->str
, cmpl_state
);
3090 g_string_free (path
, TRUE
);
3095 /* after the cache lookup fails, really open a new directory */
3096 static CompletionDirSent
*
3097 open_new_dir (gchar
*dir_name
,
3099 gboolean stat_subdirs
)
3101 CompletionDirSent
*sent
;
3104 GError
*error
= NULL
;
3105 gint entry_count
= 0;
3108 struct stat ent_sbuf
;
3110 gchar
*sys_dir_name
;
3112 sent
= g_new (CompletionDirSent
, 1);
3113 sent
->mtime
= sbuf
->st_mtime
;
3114 sent
->inode
= sbuf
->st_ino
;
3115 sent
->device
= sbuf
->st_dev
;
3117 path
= g_string_sized_new (2*MAXPATHLEN
+ 10);
3119 sys_dir_name
= g_filename_from_utf8 (dir_name
, -1, NULL
, NULL
, NULL
);
3122 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3126 directory
= g_dir_open (sys_dir_name
, 0, &error
);
3129 cmpl_errno
= error
->code
; /* ??? */
3130 g_free (sys_dir_name
);
3134 while ((dirent
= g_dir_read_name (directory
)) != NULL
)
3137 entry_count
+= 2; /* For ".",".." */
3139 sent
->entries
= g_new (CompletionDirEntry
, entry_count
);
3140 sent
->entry_count
= entry_count
;
3142 g_dir_rewind (directory
);
3144 for (i
= 0; i
< entry_count
; i
+= 1)
3146 GError
*error
= NULL
;
3154 dirent
= g_dir_read_name (directory
);
3155 if (!dirent
) /* Directory changed */
3159 sent
->entries
[n_entries
].entry_name
= g_filename_to_utf8 (dirent
, -1, NULL
, NULL
, &error
);
3160 if (sent
->entries
[n_entries
].entry_name
== NULL
3161 || !g_utf8_validate (sent
->entries
[n_entries
].entry_name
, -1, NULL
))
3163 gchar
*escaped_str
= g_strescape (dirent
, NULL
);
3164 g_message (_("The filename \"%s\" couldn't be converted to UTF-8 "
3165 "(try setting the environment variable G_BROKEN_FILENAMES): %s"),
3167 error
->message
? error
->message
: _("Invalid Utf-8"));
3168 g_free (escaped_str
);
3169 g_clear_error (&error
);
3172 g_clear_error (&error
);
3174 sent
->entries
[n_entries
].sort_key
= g_utf8_collate_key (sent
->entries
[n_entries
].entry_name
, -1);
3176 g_string_assign (path
, sys_dir_name
);
3177 if (path
->str
[path
->len
-1] != G_DIR_SEPARATOR
)
3179 g_string_append_c (path
, G_DIR_SEPARATOR
);
3181 g_string_append (path
, dirent
);
3185 /* Here we know path->str is a "system charset" string */
3186 if (stat (path
->str
, &ent_sbuf
) >= 0 && S_ISDIR (ent_sbuf
.st_mode
))
3187 sent
->entries
[n_entries
].is_dir
= TRUE
;
3189 /* stat may fail, and we don't mind, since it could be a
3190 * dangling symlink. */
3191 sent
->entries
[n_entries
].is_dir
= FALSE
;
3194 sent
->entries
[n_entries
].is_dir
= 1;
3198 sent
->entry_count
= n_entries
;
3200 g_free (sys_dir_name
);
3201 g_string_free (path
, TRUE
);
3202 qsort (sent
->entries
, sent
->entry_count
, sizeof (CompletionDirEntry
), compare_cmpl_dir
);
3204 g_dir_close (directory
);
3209 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3212 check_dir (gchar
*dir_name
,
3213 struct stat
*result
,
3214 gboolean
*stat_subdirs
)
3216 /* A list of directories that we know only contain other directories.
3217 * Trying to stat every file in these directories would be very
3224 struct stat statbuf
;
3225 } no_stat_dirs
[] = {
3226 { "/afs", FALSE
, { 0 } },
3227 { "/net", FALSE
, { 0 } }
3230 static const gint n_no_stat_dirs
= G_N_ELEMENTS (no_stat_dirs
);
3231 static gboolean initialized
= FALSE
;
3232 gchar
*sys_dir_name
;
3238 for (i
= 0; i
< n_no_stat_dirs
; i
++)
3240 if (stat (no_stat_dirs
[i
].name
, &no_stat_dirs
[i
].statbuf
) == 0)
3241 no_stat_dirs
[i
].present
= TRUE
;
3245 sys_dir_name
= g_filename_from_utf8 (dir_name
, -1, NULL
, NULL
, NULL
);
3248 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3252 if (stat (sys_dir_name
, result
) < 0)
3254 g_free (sys_dir_name
);
3258 g_free (sys_dir_name
);
3260 *stat_subdirs
= TRUE
;
3261 for (i
= 0; i
< n_no_stat_dirs
; i
++)
3263 if (no_stat_dirs
[i
].present
&&
3264 (no_stat_dirs
[i
].statbuf
.st_dev
== result
->st_dev
) &&
3265 (no_stat_dirs
[i
].statbuf
.st_ino
== result
->st_ino
))
3267 *stat_subdirs
= FALSE
;
3277 /* open a directory by absolute pathname */
3278 static CompletionDir
*
3279 open_dir (gchar
*dir_name
,
3280 CompletionState
*cmpl_state
)
3283 gboolean stat_subdirs
;
3284 CompletionDirSent
*sent
;
3287 #if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
3288 if (!check_dir (dir_name
, &sbuf
, &stat_subdirs
))
3291 cdsl
= cmpl_state
->directory_sent_storage
;
3297 if (sent
->inode
== sbuf
.st_ino
&&
3298 sent
->mtime
== sbuf
.st_mtime
&&
3299 sent
->device
== sbuf
.st_dev
)
3300 return attach_dir (sent
, dir_name
, cmpl_state
);
3305 stat_subdirs
= TRUE
;
3308 sent
= open_new_dir (dir_name
, &sbuf
, stat_subdirs
);
3312 cmpl_state
->directory_sent_storage
=
3313 g_list_prepend (cmpl_state
->directory_sent_storage
, sent
);
3315 return attach_dir (sent
, dir_name
, cmpl_state
);
3321 static CompletionDir
*
3322 attach_dir (CompletionDirSent
*sent
,
3324 CompletionState
*cmpl_state
)
3326 CompletionDir
* new_dir
;
3328 new_dir
= g_new (CompletionDir
, 1);
3330 cmpl_state
->directory_storage
=
3331 g_list_prepend (cmpl_state
->directory_storage
, new_dir
);
3333 new_dir
->sent
= sent
;
3334 new_dir
->fullname
= g_strdup (dir_name
);
3335 new_dir
->fullname_len
= strlen (dir_name
);
3336 new_dir
->cmpl_text
= NULL
;
3342 correct_dir_fullname (CompletionDir
* cmpl_dir
)
3344 gint length
= strlen (cmpl_dir
->fullname
);
3345 gchar
*first_slash
= strchr (cmpl_dir
->fullname
, G_DIR_SEPARATOR
);
3346 gchar
*sys_filename
;
3349 /* Does it end with /. (\.) ? */
3351 strcmp (cmpl_dir
->fullname
+ length
- 2, G_DIR_SEPARATOR_S
".") == 0)
3353 /* Is it just the root directory (on a drive) ? */
3354 if (cmpl_dir
->fullname
+ length
- 2 == first_slash
)
3356 cmpl_dir
->fullname
[length
- 1] = 0;
3357 cmpl_dir
->fullname_len
= length
- 1;
3362 cmpl_dir
->fullname
[length
- 2] = 0;
3366 /* Ends with /./ (\.\)? */
3367 else if (length
>= 3 &&
3368 strcmp (cmpl_dir
->fullname
+ length
- 3,
3369 G_DIR_SEPARATOR_S
"." G_DIR_SEPARATOR_S
) == 0)
3370 cmpl_dir
->fullname
[length
- 2] = 0;
3372 /* Ends with /.. (\..) ? */
3373 else if (length
>= 3 &&
3374 strcmp (cmpl_dir
->fullname
+ length
- 3,
3375 G_DIR_SEPARATOR_S
"..") == 0)
3377 /* Is it just /.. (X:\..)? */
3378 if (cmpl_dir
->fullname
+ length
- 3 == first_slash
)
3380 cmpl_dir
->fullname
[length
- 2] = 0;
3381 cmpl_dir
->fullname_len
= length
- 2;
3385 sys_filename
= g_filename_from_utf8 (cmpl_dir
->fullname
, -1, NULL
, NULL
, NULL
);
3388 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3392 if (stat (sys_filename
, &sbuf
) < 0)
3394 g_free (sys_filename
);
3398 g_free (sys_filename
);
3400 cmpl_dir
->fullname
[length
- 3] = 0;
3402 if (!correct_parent (cmpl_dir
, &sbuf
))
3406 /* Ends with /../ (\..\)? */
3407 else if (length
>= 4 &&
3408 strcmp (cmpl_dir
->fullname
+ length
- 4,
3409 G_DIR_SEPARATOR_S
".." G_DIR_SEPARATOR_S
) == 0)
3411 /* Is it just /../ (X:\..\)? */
3412 if (cmpl_dir
->fullname
+ length
- 4 == first_slash
)
3414 cmpl_dir
->fullname
[length
- 3] = 0;
3415 cmpl_dir
->fullname_len
= length
- 3;
3419 sys_filename
= g_filename_from_utf8 (cmpl_dir
->fullname
, -1, NULL
, NULL
, NULL
);
3422 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3426 if (stat (sys_filename
, &sbuf
) < 0)
3428 g_free (sys_filename
);
3432 g_free (sys_filename
);
3434 cmpl_dir
->fullname
[length
- 4] = 0;
3436 if (!correct_parent (cmpl_dir
, &sbuf
))
3440 cmpl_dir
->fullname_len
= strlen (cmpl_dir
->fullname
);
3446 correct_parent (CompletionDir
*cmpl_dir
,
3453 gchar
*sys_filename
;
3456 last_slash
= strrchr (cmpl_dir
->fullname
, G_DIR_SEPARATOR
);
3457 g_assert (last_slash
);
3458 first_slash
= strchr (cmpl_dir
->fullname
, G_DIR_SEPARATOR
);
3460 /* Clever (?) way to check for top-level directory that works also on
3461 * Win32, where there is a drive letter and colon prefixed...
3463 if (last_slash
!= first_slash
)
3473 sys_filename
= g_filename_from_utf8 (cmpl_dir
->fullname
, -1, NULL
, NULL
, NULL
);
3476 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3478 last_slash
[0] = G_DIR_SEPARATOR
;
3482 if (stat (sys_filename
, &parbuf
) < 0)
3484 g_free (sys_filename
);
3487 last_slash
[0] = G_DIR_SEPARATOR
;
3490 g_free (sys_filename
);
3492 #ifndef G_OS_WIN32 /* No inode numbers on Win32 */
3493 if (parbuf
.st_ino
== sbuf
->st_ino
&& parbuf
.st_dev
== sbuf
->st_dev
)
3494 /* it wasn't a link */
3500 last_slash
[0] = G_DIR_SEPARATOR
;
3502 /* it was a link, have to figure it out the hard way */
3504 new_name
= find_parent_dir_fullname (cmpl_dir
->fullname
);
3509 g_free (cmpl_dir
->fullname
);
3511 cmpl_dir
->fullname
= new_name
;
3520 find_parent_dir_fullname (gchar
* dirname
)
3522 gchar
*sys_orig_dir
;
3527 sys_orig_dir
= g_get_current_dir ();
3528 sys_dirname
= g_filename_from_utf8 (dirname
, -1, NULL
, NULL
, NULL
);
3531 g_free (sys_orig_dir
);
3532 cmpl_errno
= CMPL_ERRNO_DID_NOT_CONVERT
;
3536 if (chdir (sys_dirname
) != 0 || chdir ("..") != 0)
3539 chdir (sys_orig_dir
);
3540 g_free (sys_dirname
);
3541 g_free (sys_orig_dir
);
3544 g_free (sys_dirname
);
3546 sys_cwd
= g_get_current_dir ();
3547 result
= g_filename_to_utf8 (sys_cwd
, -1, NULL
, NULL
, NULL
);
3550 if (chdir (sys_orig_dir
) != 0)
3553 g_free (sys_orig_dir
);
3557 g_free (sys_orig_dir
);
3563 /**********************************************************************/
3564 /* Completion Operations */
3565 /**********************************************************************/
3569 static PossibleCompletion
*
3570 attempt_homedir_completion (gchar
*text_to_complete
,
3571 CompletionState
*cmpl_state
)
3575 if (!cmpl_state
->user_dir_name_buffer
&&
3576 !get_pwdb (cmpl_state
))
3578 length
= strlen (text_to_complete
) - 1;
3580 cmpl_state
->user_completion_index
+= 1;
3582 while (cmpl_state
->user_completion_index
< cmpl_state
->user_directories_len
)
3584 index
= first_diff_index (text_to_complete
+ 1,
3585 cmpl_state
->user_directories
3586 [cmpl_state
->user_completion_index
].login
);
3593 if (cmpl_state
->last_valid_char
< (index
+ 1))
3594 cmpl_state
->last_valid_char
= index
+ 1;
3595 cmpl_state
->user_completion_index
+= 1;
3599 cmpl_state
->the_completion
.is_a_completion
= 1;
3600 cmpl_state
->the_completion
.is_directory
= TRUE
;
3602 append_completion_text ("~", cmpl_state
);
3604 append_completion_text (cmpl_state
->
3605 user_directories
[cmpl_state
->user_completion_index
].login
,
3608 return append_completion_text (G_DIR_SEPARATOR_S
, cmpl_state
);
3611 if (text_to_complete
[1]
3612 || cmpl_state
->user_completion_index
> cmpl_state
->user_directories_len
)
3614 cmpl_state
->user_completion_index
= -1;
3619 cmpl_state
->user_completion_index
+= 1;
3620 cmpl_state
->the_completion
.is_a_completion
= 1;
3621 cmpl_state
->the_completion
.is_directory
= TRUE
;
3623 return append_completion_text ("~" G_DIR_SEPARATOR_S
, cmpl_state
);
3629 #if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
3630 #define FOLD(c) (tolower(c))
3635 /* returns the index (>= 0) of the first differing character,
3636 * PATTERN_MATCH if the completion matches */
3638 first_diff_index (gchar
*pat
,
3643 while (*pat
&& *text
&& FOLD (*text
) == FOLD (*pat
))
3653 return PATTERN_MATCH
;
3656 static PossibleCompletion
*
3657 append_completion_text (gchar
*text
,
3658 CompletionState
*cmpl_state
)
3662 if (!cmpl_state
->the_completion
.text
)
3665 len
= strlen (text
) + strlen (cmpl_state
->the_completion
.text
) + 1;
3667 if (cmpl_state
->the_completion
.text_alloc
> len
)
3669 strcat (cmpl_state
->the_completion
.text
, text
);
3670 return &cmpl_state
->the_completion
;
3676 cmpl_state
->the_completion
.text_alloc
= i
;
3678 cmpl_state
->the_completion
.text
= (gchar
*) g_realloc (cmpl_state
->the_completion
.text
, i
);
3680 if (!cmpl_state
->the_completion
.text
)
3684 strcat (cmpl_state
->the_completion
.text
, text
);
3685 return &cmpl_state
->the_completion
;
3689 static CompletionDir
*
3690 find_completion_dir (gchar
*text_to_complete
,
3691 gchar
**remaining_text
,
3692 CompletionState
*cmpl_state
)
3694 gchar
* first_slash
= strchr (text_to_complete
, G_DIR_SEPARATOR
);
3695 CompletionDir
* dir
= cmpl_state
->reference_dir
;
3696 CompletionDir
* next
;
3697 *remaining_text
= text_to_complete
;
3701 gint len
= first_slash
- *remaining_text
;
3703 gchar
*found_name
= NULL
; /* Quiet gcc */
3705 gchar
* pat_buf
= g_new (gchar
, len
+ 1);
3707 strncpy (pat_buf
, *remaining_text
, len
);
3710 for (i
= 0; i
< dir
->sent
->entry_count
; i
+= 1)
3712 if (dir
->sent
->entries
[i
].is_dir
&&
3713 _gtk_fnmatch (pat_buf
, dir
->sent
->entries
[i
].entry_name
))
3723 found_name
= dir
->sent
->entries
[i
].entry_name
;
3730 /* Perhaps we are trying to open an automount directory */
3731 found_name
= pat_buf
;
3734 next
= open_relative_dir (found_name
, dir
, cmpl_state
);
3742 next
->cmpl_parent
= dir
;
3746 if (!correct_dir_fullname (dir
))
3752 *remaining_text
= first_slash
+ 1;
3753 first_slash
= strchr (*remaining_text
, G_DIR_SEPARATOR
);
3762 update_cmpl (PossibleCompletion
*poss
,
3763 CompletionState
*cmpl_state
)
3767 if (!poss
|| !cmpl_is_a_completion (poss
))
3770 cmpl_len
= strlen (cmpl_this_completion (poss
));
3772 if (cmpl_state
->updated_text_alloc
< cmpl_len
+ 1)
3774 cmpl_state
->updated_text
=
3775 (gchar
*)g_realloc (cmpl_state
->updated_text
,
3776 cmpl_state
->updated_text_alloc
);
3777 cmpl_state
->updated_text_alloc
= 2*cmpl_len
;
3780 if (cmpl_state
->updated_text_len
< 0)
3782 strcpy (cmpl_state
->updated_text
, cmpl_this_completion (poss
));
3783 cmpl_state
->updated_text_len
= cmpl_len
;
3784 cmpl_state
->re_complete
= cmpl_is_directory (poss
);
3786 else if (cmpl_state
->updated_text_len
== 0)
3788 cmpl_state
->re_complete
= FALSE
;
3793 first_diff_index (cmpl_state
->updated_text
,
3794 cmpl_this_completion (poss
));
3796 cmpl_state
->re_complete
= FALSE
;
3798 if (first_diff
== PATTERN_MATCH
)
3801 if (first_diff
> cmpl_state
->updated_text_len
)
3802 strcpy (cmpl_state
->updated_text
, cmpl_this_completion (poss
));
3804 cmpl_state
->updated_text_len
= first_diff
;
3805 cmpl_state
->updated_text
[first_diff
] = 0;
3809 static PossibleCompletion
*
3810 attempt_dir_completion (CompletionState
*cmpl_state
)
3812 gchar
*pat_buf
, *first_slash
;
3813 CompletionDir
*dir
= cmpl_state
->active_completion_dir
;
3815 dir
->cmpl_index
+= 1;
3817 if (dir
->cmpl_index
== dir
->sent
->entry_count
)
3819 if (dir
->cmpl_parent
== NULL
)
3821 cmpl_state
->active_completion_dir
= NULL
;
3827 cmpl_state
->active_completion_dir
= dir
->cmpl_parent
;
3829 return attempt_dir_completion (cmpl_state
);
3833 g_assert (dir
->cmpl_text
);
3835 first_slash
= strchr (dir
->cmpl_text
, G_DIR_SEPARATOR
);
3839 gint len
= first_slash
- dir
->cmpl_text
;
3841 pat_buf
= g_new (gchar
, len
+ 1);
3842 strncpy (pat_buf
, dir
->cmpl_text
, len
);
3847 gint len
= strlen (dir
->cmpl_text
);
3849 pat_buf
= g_new (gchar
, len
+ 2);
3850 strcpy (pat_buf
, dir
->cmpl_text
);
3851 /* Don't append a * if the user entered one herself.
3852 * This way one can complete *.h and don't get matches
3853 * on any .help files, for instance.
3855 if (strchr (pat_buf
, '*') == NULL
)
3856 strcpy (pat_buf
+ len
, "*");
3861 if (dir
->sent
->entries
[dir
->cmpl_index
].is_dir
)
3863 if (_gtk_fnmatch (pat_buf
, dir
->sent
->entries
[dir
->cmpl_index
].entry_name
))
3865 CompletionDir
* new_dir
;
3867 new_dir
= open_relative_dir (dir
->sent
->entries
[dir
->cmpl_index
].entry_name
,
3876 new_dir
->cmpl_parent
= dir
;
3878 new_dir
->cmpl_index
= -1;
3879 new_dir
->cmpl_text
= g_strdup (first_slash
+ 1);
3881 cmpl_state
->active_completion_dir
= new_dir
;
3884 return attempt_dir_completion (cmpl_state
);
3889 return attempt_dir_completion (cmpl_state
);
3895 return attempt_dir_completion (cmpl_state
);
3900 if (dir
->cmpl_parent
!= NULL
)
3902 append_completion_text (dir
->fullname
+
3903 strlen (cmpl_state
->completion_dir
->fullname
) + 1,
3905 append_completion_text (G_DIR_SEPARATOR_S
, cmpl_state
);
3908 append_completion_text (dir
->sent
->entries
[dir
->cmpl_index
].entry_name
, cmpl_state
);
3910 cmpl_state
->the_completion
.is_a_completion
=
3911 _gtk_fnmatch (pat_buf
, dir
->sent
->entries
[dir
->cmpl_index
].entry_name
);
3913 cmpl_state
->the_completion
.is_directory
= dir
->sent
->entries
[dir
->cmpl_index
].is_dir
;
3914 if (dir
->sent
->entries
[dir
->cmpl_index
].is_dir
)
3915 append_completion_text (G_DIR_SEPARATOR_S
, cmpl_state
);
3918 return &cmpl_state
->the_completion
;
3925 get_pwdb (CompletionState
* cmpl_state
)
3927 struct passwd
*pwd_ptr
;
3930 gint len
= 0, i
, count
= 0;
3932 if (cmpl_state
->user_dir_name_buffer
)
3936 while ((pwd_ptr
= getpwent ()) != NULL
)
3938 utf8
= g_filename_to_utf8 (pwd_ptr
->pw_name
, -1, NULL
, NULL
, NULL
);
3939 len
+= strlen (utf8
);
3941 utf8
= g_filename_to_utf8 (pwd_ptr
->pw_dir
, -1, NULL
, NULL
, NULL
);
3942 len
+= strlen (utf8
);
3950 cmpl_state
->user_dir_name_buffer
= g_new (gchar
, len
);
3951 cmpl_state
->user_directories
= g_new (CompletionUserDir
, count
);
3952 cmpl_state
->user_directories_len
= count
;
3954 buf_ptr
= cmpl_state
->user_dir_name_buffer
;
3956 for (i
= 0; i
< count
; i
+= 1)
3958 pwd_ptr
= getpwent ();
3965 utf8
= g_filename_to_utf8 (pwd_ptr
->pw_name
, -1, NULL
, NULL
, NULL
);
3966 strcpy (buf_ptr
, utf8
);
3969 cmpl_state
->user_directories
[i
].login
= buf_ptr
;
3971 buf_ptr
+= strlen (buf_ptr
);
3974 utf8
= g_filename_to_utf8 (pwd_ptr
->pw_dir
, -1, NULL
, NULL
, NULL
);
3975 strcpy (buf_ptr
, utf8
);
3978 cmpl_state
->user_directories
[i
].homedir
= buf_ptr
;
3980 buf_ptr
+= strlen (buf_ptr
);
3984 qsort (cmpl_state
->user_directories
,
3985 cmpl_state
->user_directories_len
,
3986 sizeof (CompletionUserDir
),
3995 if (cmpl_state
->user_dir_name_buffer
)
3996 g_free (cmpl_state
->user_dir_name_buffer
);
3997 if (cmpl_state
->user_directories
)
3998 g_free (cmpl_state
->user_directories
);
4000 cmpl_state
->user_dir_name_buffer
= NULL
;
4001 cmpl_state
->user_directories
= NULL
;
4007 compare_user_dir (const void *a
,
4010 return strcmp ((((CompletionUserDir
*)a
))->login
,
4011 (((CompletionUserDir
*)b
))->login
);
4017 compare_cmpl_dir (const void *a
,
4021 return strcmp (((CompletionDirEntry
*)a
)->sort_key
,
4022 (((CompletionDirEntry
*)b
))->sort_key
);
4026 cmpl_state_okay (CompletionState
* cmpl_state
)
4028 return cmpl_state
&& cmpl_state
->reference_dir
;
4032 cmpl_strerror (gint err
)
4034 if (err
== CMPL_ERRNO_TOO_LONG
)
4035 return _("Name too long");
4036 else if (err
== CMPL_ERRNO_DID_NOT_CONVERT
)
4037 return _("Couldn't convert filename");
4039 return g_strerror (err
);
4042 gchar
* gtk_dir_selection_get_dir (GtkDirSelection
*filesel
)
4044 return gtk_entry_get_text (GTK_ENTRY (filesel
->selection_entry
));