1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
6 * Copyright (C) 2000 Eazel, Inc.
8 * Nautilus is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
13 * Nautilus is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public
19 * License along with this program; see the file COPYING. If not,
20 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21 * Boston, MA 02111-1307, USA.
23 * Author: Maciej Stachowiak <mjs@eazel.com>
24 * Ettore Perazzoli <ettore@gnu.org>
25 * Michael Meeks <michael@nuclecu.unam.mx>
26 * Andy Hertzfeld <andy@eazel.com>
30 /* nautilus-location-bar.c - Location bar for Nautilus
34 #include "nautilus-location-entry.h"
36 #include "nautilus-window-private.h"
37 #include "nautilus-window.h"
38 #include <eel/eel-glib-extensions.h>
39 #include <eel/eel-gtk-macros.h>
40 #include <eel/eel-stock-dialogs.h>
41 #include <eel/eel-string.h>
42 #include <gtk/gtkdnd.h>
43 #include <gtk/gtksignal.h>
44 #include <glib/gi18n.h>
46 #include <libgnomeui/gnome-stock-icons.h>
47 #include <libgnomeui/gnome-uidefs.h>
48 #include <libnautilus-private/nautilus-file-utilities.h>
49 #include <libnautilus-private/nautilus-entry.h>
50 #include <libnautilus-private/nautilus-icon-dnd.h>
51 #include <libnautilus-private/nautilus-clipboard.h>
55 struct NautilusLocationEntryDetails
{
58 char *current_directory
;
59 GFilenameCompleter
*completer
;
63 gboolean has_special_text
;
64 gboolean setting_special_text
;
68 static void nautilus_location_entry_class_init (NautilusLocationEntryClass
*class);
69 static void nautilus_location_entry_init (NautilusLocationEntry
*entry
);
71 EEL_CLASS_BOILERPLATE (NautilusLocationEntry
,
72 nautilus_location_entry
,
75 /* routine that performs the tab expansion. Extract the directory name and
76 incomplete basename, then iterate through the directory trying to complete it. If we
77 find something, add it to the entry */
80 try_to_expand_path (gpointer callback_data
)
82 NautilusLocationEntry
*entry
;
83 GtkEditable
*editable
;
84 char *suffix
, *user_location
;
85 int user_location_length
, pos
;
87 entry
= NAUTILUS_LOCATION_ENTRY (callback_data
);
88 editable
= GTK_EDITABLE (entry
);
89 user_location
= gtk_editable_get_chars (editable
, 0, -1);
90 user_location_length
= g_utf8_strlen (user_location
, -1);
91 entry
->details
->idle_id
= 0;
93 suffix
= g_filename_completer_get_completion_suffix (entry
->details
->completer
,
95 g_free (user_location
);
97 /* if we've got something, add it to the entry */
99 pos
= user_location_length
;
100 gtk_editable_insert_text (editable
,
102 pos
= user_location_length
;
103 gtk_editable_select_region (editable
, pos
, -1);
111 /* Until we have a more elegant solution, this is how we figure out if
112 * the GtkEntry inserted characters, assuming that the return value is
113 * TRUE indicating that the GtkEntry consumed the key event for some
114 * reason. This is a clone of code from GtkEntry.
117 entry_would_have_inserted_characters (const GdkEventKey
*event
)
119 switch (event
->keyval
) {
135 if (event
->keyval
>= 0x20 && event
->keyval
<= 0xFF) {
136 if ((event
->state
& GDK_CONTROL_MASK
) != 0) {
139 if ((event
->state
& GDK_MOD1_MASK
) != 0) {
143 return event
->length
> 0;
148 get_editable_number_of_chars (GtkEditable
*editable
)
153 text
= gtk_editable_get_chars (editable
, 0, -1);
154 length
= g_utf8_strlen (text
, -1);
160 set_position_and_selection_to_end (GtkEditable
*editable
)
164 end
= get_editable_number_of_chars (editable
);
165 gtk_editable_select_region (editable
, end
, end
);
166 gtk_editable_set_position (editable
, end
);
170 position_and_selection_are_at_end (GtkEditable
*editable
)
173 int start_sel
, end_sel
;
175 end
= get_editable_number_of_chars (editable
);
176 if (gtk_editable_get_selection_bounds (editable
, &start_sel
, &end_sel
)) {
177 if (start_sel
!= end
|| end_sel
!= end
) {
181 return gtk_editable_get_position (editable
) == end
;
185 got_completion_data_callback (GFilenameCompleter
*completer
,
186 NautilusLocationEntry
*entry
)
188 if (entry
->details
->idle_id
) {
189 g_source_remove (entry
->details
->idle_id
);
190 entry
->details
->idle_id
= 0;
192 try_to_expand_path (entry
);
196 editable_event_after_callback (GtkEntry
*entry
,
198 NautilusLocationEntry
*location_entry
)
200 GtkEditable
*editable
;
201 GdkEventKey
*keyevent
;
203 if (event
->type
!= GDK_KEY_PRESS
) {
207 editable
= GTK_EDITABLE (entry
);
208 keyevent
= (GdkEventKey
*)event
;
210 /* After typing the right arrow key we move the selection to
211 * the end, if we have a valid selection - since this is most
212 * likely an auto-completion. We ignore shift / control since
213 * they can validly be used to extend the selection.
215 if ((keyevent
->keyval
== GDK_Right
|| keyevent
->keyval
== GDK_End
) &&
216 !(keyevent
->state
& (GDK_SHIFT_MASK
| GDK_CONTROL_MASK
)) &&
217 gtk_editable_get_selection_bounds (editable
, NULL
, NULL
)) {
218 set_position_and_selection_to_end (editable
);
221 /* Only do expanding when we are typing at the end of the
222 * text. Do the expand at idle time to avoid slowing down
223 * typing when the directory is large. Only trigger the expand
224 * when we type a key that would have inserted characters.
226 if (position_and_selection_are_at_end (editable
)) {
227 if (entry_would_have_inserted_characters (keyevent
)) {
228 if (location_entry
->details
->idle_id
== 0) {
229 location_entry
->details
->idle_id
= g_idle_add (try_to_expand_path
, location_entry
);
233 /* FIXME: Also might be good to do this when you click
234 * to change the position or selection.
236 if (location_entry
->details
->idle_id
!= 0) {
237 g_source_remove (location_entry
->details
->idle_id
);
238 location_entry
->details
->idle_id
= 0;
244 finalize (GObject
*object
)
246 NautilusLocationEntry
*entry
;
248 entry
= NAUTILUS_LOCATION_ENTRY (object
);
250 g_object_unref (entry
->details
->completer
);
251 g_free (entry
->details
->special_text
);
252 g_free (entry
->details
);
254 EEL_CALL_PARENT (G_OBJECT_CLASS
, finalize
, (object
));
258 destroy (GtkObject
*object
)
260 NautilusLocationEntry
*entry
;
262 entry
= NAUTILUS_LOCATION_ENTRY (object
);
264 /* cancel the pending idle call, if any */
265 if (entry
->details
->idle_id
!= 0) {
266 g_source_remove (entry
->details
->idle_id
);
267 entry
->details
->idle_id
= 0;
270 g_free (entry
->details
->current_directory
);
271 entry
->details
->current_directory
= NULL
;
273 EEL_CALL_PARENT (GTK_OBJECT_CLASS
, destroy
, (object
));
277 nautilus_location_entry_text_changed (NautilusLocationEntry
*entry
,
280 if (entry
->details
->setting_special_text
) {
284 entry
->details
->has_special_text
= FALSE
;
288 nautilus_location_entry_focus_in (GtkWidget
*widget
,
289 GdkEventFocus
*event
)
291 NautilusLocationEntry
*entry
= NAUTILUS_LOCATION_ENTRY (widget
);
293 if (entry
->details
->has_special_text
) {
294 entry
->details
->setting_special_text
= TRUE
;
295 gtk_entry_set_text (GTK_ENTRY (entry
), "");
296 entry
->details
->setting_special_text
= FALSE
;
299 return EEL_CALL_PARENT_WITH_RETURN_VALUE (GTK_WIDGET_CLASS
, focus_in_event
, (widget
, event
));
303 nautilus_location_entry_class_init (NautilusLocationEntryClass
*class)
305 GtkWidgetClass
*widget_class
;
306 GObjectClass
*gobject_class
;
307 GtkObjectClass
*object_class
;
309 widget_class
= GTK_WIDGET_CLASS (class);
311 widget_class
->focus_in_event
= nautilus_location_entry_focus_in
;
313 gobject_class
= G_OBJECT_CLASS (class);
314 gobject_class
->finalize
= finalize
;
316 object_class
= GTK_OBJECT_CLASS (class);
317 object_class
->destroy
= destroy
;
321 nautilus_location_entry_init (NautilusLocationEntry
*entry
)
323 entry
->details
= g_new0 (NautilusLocationEntryDetails
, 1);
325 entry
->details
->completer
= g_filename_completer_new ();
326 g_filename_completer_set_dirs_only (entry
->details
->completer
, TRUE
);
328 nautilus_entry_set_special_tab_handling (NAUTILUS_ENTRY (entry
), TRUE
);
330 g_signal_connect (entry
, "event_after",
331 G_CALLBACK (editable_event_after_callback
), entry
);
333 g_signal_connect (entry
, "notify::text",
334 G_CALLBACK (nautilus_location_entry_text_changed
), NULL
);
336 g_signal_connect (entry
->details
->completer
, "got_completion_data",
337 G_CALLBACK (got_completion_data_callback
), entry
);
341 nautilus_location_entry_new (void)
345 entry
= gtk_widget_new (NAUTILUS_TYPE_LOCATION_ENTRY
, NULL
);
351 nautilus_location_entry_set_special_text (NautilusLocationEntry
*entry
,
352 const char *special_text
)
354 entry
->details
->has_special_text
= TRUE
;
356 g_free (entry
->details
->special_text
);
357 entry
->details
->special_text
= g_strdup (special_text
);
359 entry
->details
->setting_special_text
= TRUE
;
360 gtk_entry_set_text (GTK_ENTRY (entry
), special_text
);
361 entry
->details
->setting_special_text
= FALSE
;