2008-05-05 Paolo Borelli <pborelli@katamail.com>
[nautilus.git] / src / nautilus-location-entry.c
blob898d86b8554e21a30656367ee05d18479dc324a7
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
3 /*
4 * Nautilus
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
33 #include <config.h>
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>
45 #include <gio/gio.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>
52 #include <stdio.h>
53 #include <string.h>
55 struct NautilusLocationEntryDetails {
56 GtkLabel *label;
58 char *current_directory;
59 GFilenameCompleter *completer;
61 guint idle_id;
63 gboolean has_special_text;
64 gboolean setting_special_text;
65 gchar *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,
73 NAUTILUS_TYPE_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 */
79 static gboolean
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,
94 user_location);
95 g_free (user_location);
97 /* if we've got something, add it to the entry */
98 if (suffix != NULL) {
99 pos = user_location_length;
100 gtk_editable_insert_text (editable,
101 suffix, -1, &pos);
102 pos = user_location_length;
103 gtk_editable_select_region (editable, pos, -1);
105 g_free (suffix);
108 return FALSE;
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.
116 static gboolean
117 entry_would_have_inserted_characters (const GdkEventKey *event)
119 switch (event->keyval) {
120 case GDK_BackSpace:
121 case GDK_Clear:
122 case GDK_Insert:
123 case GDK_Delete:
124 case GDK_Home:
125 case GDK_End:
126 case GDK_KP_Home:
127 case GDK_KP_End:
128 case GDK_Left:
129 case GDK_Right:
130 case GDK_KP_Left:
131 case GDK_KP_Right:
132 case GDK_Return:
133 return FALSE;
134 default:
135 if (event->keyval >= 0x20 && event->keyval <= 0xFF) {
136 if ((event->state & GDK_CONTROL_MASK) != 0) {
137 return FALSE;
139 if ((event->state & GDK_MOD1_MASK) != 0) {
140 return FALSE;
143 return event->length > 0;
147 static int
148 get_editable_number_of_chars (GtkEditable *editable)
150 char *text;
151 int length;
153 text = gtk_editable_get_chars (editable, 0, -1);
154 length = g_utf8_strlen (text, -1);
155 g_free (text);
156 return length;
159 static void
160 set_position_and_selection_to_end (GtkEditable *editable)
162 int end;
164 end = get_editable_number_of_chars (editable);
165 gtk_editable_select_region (editable, end, end);
166 gtk_editable_set_position (editable, end);
169 static gboolean
170 position_and_selection_are_at_end (GtkEditable *editable)
172 int end;
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) {
178 return FALSE;
181 return gtk_editable_get_position (editable) == end;
184 static void
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);
195 static void
196 editable_event_after_callback (GtkEntry *entry,
197 GdkEvent *event,
198 NautilusLocationEntry *location_entry)
200 GtkEditable *editable;
201 GdkEventKey *keyevent;
203 if (event->type != GDK_KEY_PRESS) {
204 return;
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);
232 } else {
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;
243 static void
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));
257 static void
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));
276 static void
277 nautilus_location_entry_text_changed (NautilusLocationEntry *entry,
278 GParamSpec *pspec)
280 if (entry->details->setting_special_text) {
281 return;
284 entry->details->has_special_text = FALSE;
287 static gboolean
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));
302 static void
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;
320 static void
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);
340 GtkWidget *
341 nautilus_location_entry_new (void)
343 GtkWidget *entry;
345 entry = gtk_widget_new (NAUTILUS_TYPE_LOCATION_ENTRY, NULL);
347 return entry;
350 void
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;