nautilus-3.6.3/src/nautilus-location-entry.c

No issues found

  1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
  2 
  3 /*
  4  * Nautilus
  5  *
  6  * Copyright (C) 2000 Eazel, Inc.
  7  *
  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.
 12  *
 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.
 17  *
 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.
 22  *
 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>
 27  *
 28  */
 29 
 30 /* nautilus-location-bar.c - Location bar for Nautilus
 31  */
 32 
 33 #include <config.h>
 34 #include "nautilus-location-entry.h"
 35 
 36 #include "nautilus-application.h"
 37 #include "nautilus-window-private.h"
 38 #include "nautilus-window.h"
 39 #include <gtk/gtk.h>
 40 #include <gdk/gdkkeysyms.h>
 41 #include <glib/gi18n.h>
 42 #include <gio/gio.h>
 43 #include <libnautilus-private/nautilus-file-utilities.h>
 44 #include <libnautilus-private/nautilus-entry.h>
 45 #include <libnautilus-private/nautilus-clipboard.h>
 46 #include <eel/eel-stock-dialogs.h>
 47 #include <eel/eel-string.h>
 48 #include <eel/eel-vfs-extensions.h>
 49 #include <stdio.h>
 50 #include <string.h>
 51 
 52 #define NAUTILUS_DND_URI_LIST_TYPE 	  "text/uri-list"
 53 #define NAUTILUS_DND_TEXT_PLAIN_TYPE 	  "text/plain"
 54 
 55 enum {
 56 	NAUTILUS_DND_URI_LIST,
 57 	NAUTILUS_DND_TEXT_PLAIN,
 58 	NAUTILUS_DND_NTARGETS
 59 };
 60 
 61 static const GtkTargetEntry drag_types [] = {
 62 	{ NAUTILUS_DND_URI_LIST_TYPE,   0, NAUTILUS_DND_URI_LIST },
 63 	{ NAUTILUS_DND_TEXT_PLAIN_TYPE, 0, NAUTILUS_DND_TEXT_PLAIN },
 64 };
 65 
 66 static const GtkTargetEntry drop_types [] = {
 67 	{ NAUTILUS_DND_URI_LIST_TYPE,   0, NAUTILUS_DND_URI_LIST },
 68 	{ NAUTILUS_DND_TEXT_PLAIN_TYPE, 0, NAUTILUS_DND_TEXT_PLAIN },
 69 };
 70 
 71 struct NautilusLocationEntryDetails {
 72 	char *current_directory;
 73 	GFilenameCompleter *completer;
 74 
 75 	guint idle_id;
 76 
 77 	char *last_uri;
 78 
 79 	gboolean has_special_text;
 80 	gboolean setting_special_text;
 81 	gchar *special_text;
 82 	NautilusLocationEntryAction secondary_action;
 83 };
 84 
 85 enum {
 86 	CANCEL,
 87 	LOCATION_CHANGED,
 88 	LAST_SIGNAL
 89 };
 90 
 91 static guint signals[LAST_SIGNAL];
 92 
 93 G_DEFINE_TYPE (NautilusLocationEntry, nautilus_location_entry, NAUTILUS_TYPE_ENTRY);
 94 
 95 void
 96 nautilus_location_entry_focus (NautilusLocationEntry *entry)
 97 {
 98 	/* Put the keyboard focus in the text field when switching to this mode,
 99 	 * and select all text for easy overtyping
100 	 */
101 	gtk_widget_grab_focus (GTK_WIDGET (entry));
102 	nautilus_entry_select_all (NAUTILUS_ENTRY (entry));
103 }
104 
105 static GFile *
106 nautilus_location_entry_get_location (NautilusLocationEntry *entry)
107 {
108 	char *user_location;
109 	GFile *location;
110 
111 	user_location = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
112 	location = g_file_parse_name (user_location);
113 	g_free (user_location);
114 
115 	return location;
116 }
117 
118 static void
119 emit_location_changed (NautilusLocationEntry *entry)
120 {
121 	GFile *location;
122 
123 	location = nautilus_location_entry_get_location (entry);
124 	g_signal_emit (entry, signals[LOCATION_CHANGED], 0, location);
125 	g_object_unref (location);
126 }
127 
128 static void
129 nautilus_location_entry_update_action (NautilusLocationEntry *entry)
130 {
131 	const char *current_text;
132 	GFile *location;
133 	GFile *last_location;
134 
135 	if (entry->details->last_uri == NULL){
136 		nautilus_location_entry_set_secondary_action (entry,
137 							      NAUTILUS_LOCATION_ENTRY_ACTION_GOTO);
138 		return;
139 	}
140 
141 	current_text = gtk_entry_get_text (GTK_ENTRY (entry));
142 	location = g_file_parse_name (current_text);
143 	last_location = g_file_parse_name (entry->details->last_uri);
144 
145 	if (g_file_equal (last_location, location)) {
146 		nautilus_location_entry_set_secondary_action (entry,
147 							      NAUTILUS_LOCATION_ENTRY_ACTION_CLEAR);
148 	} else {
149 		nautilus_location_entry_set_secondary_action (entry,
150 							      NAUTILUS_LOCATION_ENTRY_ACTION_GOTO);
151 	}
152 
153 	g_object_unref (location);
154 	g_object_unref (last_location);
155 }
156 
157 static int
158 get_editable_number_of_chars (GtkEditable *editable)
159 {
160 	char *text;
161 	int length;
162 
163 	text = gtk_editable_get_chars (editable, 0, -1);
164 	length = g_utf8_strlen (text, -1);
165 	g_free (text);
166 	return length;
167 }
168 
169 static void
170 set_position_and_selection_to_end (GtkEditable *editable)
171 {
172 	int end;
173 
174 	end = get_editable_number_of_chars (editable);
175 	gtk_editable_select_region (editable, end, end);
176 	gtk_editable_set_position (editable, end);
177 }
178 
179 static void
180 nautilus_location_entry_update_current_uri (NautilusLocationEntry *entry,
181 					    const char *uri)
182 {
183 	g_free (entry->details->current_directory);
184 	entry->details->current_directory = g_strdup (uri);
185 
186 	nautilus_entry_set_text (NAUTILUS_ENTRY (entry), uri);
187 	set_position_and_selection_to_end (GTK_EDITABLE (entry));
188 }
189 
190 void
191 nautilus_location_entry_set_uri (NautilusLocationEntry *entry,
192 				 const char            *uri)
193 {
194 	char *formatted_uri;
195 	GFile *file;
196 
197 	g_assert (uri != NULL);
198 
199 	/* Note: This is called in reaction to external changes, and
200 	 * thus should not emit the LOCATION_CHANGED signal. */
201 
202 	if (eel_uri_is_search (uri)) {
203 		nautilus_location_entry_set_special_text (entry,
204 							  "");
205 	} else {
206 		file = g_file_new_for_uri (uri);
207 		formatted_uri = g_file_get_parse_name (file);
208 		g_object_unref (file);
209 		nautilus_location_entry_update_current_uri (entry,
210 								 formatted_uri);
211 		g_free (formatted_uri);
212 	}
213 
214 	/* remember the original uri for later comparison */
215 
216 	if (entry->details->last_uri != uri) {
217 		g_free (entry->details->last_uri);
218 		entry->details->last_uri = g_strdup (uri);
219 	}
220 
221 	nautilus_location_entry_update_action (entry);
222 }
223 
224 static void
225 drag_data_received_callback (GtkWidget *widget,
226 			     GdkDragContext *context,
227 			     int x,
228 			     int y,
229 			     GtkSelectionData *data,
230 			     guint info,
231 			     guint32 time,
232 			     gpointer callback_data)
233 {
234 	char **names;
235 	NautilusApplication *application;
236 	int name_count;
237 	NautilusWindow *new_window;
238 	GtkWidget *window;
239 	GdkScreen      *screen;
240 	gboolean new_windows_for_extras;
241 	char *prompt;
242 	char *detail;
243 	GFile *location;
244 	NautilusLocationEntry *self = NAUTILUS_LOCATION_ENTRY (widget);
245 
246 	g_assert (data != NULL);
247 	g_assert (callback_data == NULL);
248 
249 	names = g_uri_list_extract_uris ((const gchar *) gtk_selection_data_get_data (data));
250 
251 	if (names == NULL || *names == NULL) {
252 		g_warning ("No D&D URI's");
253 		gtk_drag_finish (context, FALSE, FALSE, time);
254 		return;
255 	}
256 
257 	window = gtk_widget_get_toplevel (widget);
258 	new_windows_for_extras = FALSE;
259 	/* Ask user if they really want to open multiple windows
260 	 * for multiple dropped URIs. This is likely to have been
261 	 * a mistake.
262 	 */
263 	name_count = g_strv_length (names);
264 	if (name_count > 1) {
265 		prompt = g_strdup_printf (ngettext("Do you want to view %d location?",
266 						   "Do you want to view %d locations?",
267 						   name_count),
268 					  name_count);
269 		detail = g_strdup_printf (ngettext("This will open %d separate window.",
270 						   "This will open %d separate windows.",
271 						   name_count),
272 					  name_count);
273 		/* eel_run_simple_dialog should really take in pairs
274 		 * like gtk_dialog_new_with_buttons() does. */
275 		new_windows_for_extras = eel_run_simple_dialog (GTK_WIDGET (window),
276 								TRUE,
277 								GTK_MESSAGE_QUESTION,
278 								prompt,
279 								detail,
280 								GTK_STOCK_CANCEL, GTK_STOCK_OK,
281 								NULL) != 0 /* GNOME_OK */;
282 
283 		g_free (prompt);
284 		g_free (detail);
285 
286 		if (!new_windows_for_extras) {
287 			gtk_drag_finish (context, FALSE, FALSE, time);
288 			return;
289 		}
290 	}
291 
292 	nautilus_location_entry_set_uri (self, names[0]);
293 	emit_location_changed (self);
294 
295 	if (new_windows_for_extras) {
296 		int i;
297 
298 		application = NAUTILUS_APPLICATION (g_application_get_default ());
299 		screen = gtk_window_get_screen (GTK_WINDOW (window));
300 
301 		for (i = 1; names[i] != NULL; ++i) {
302 			new_window = nautilus_application_create_window (application, screen);
303 			location = g_file_new_for_uri (names[i]);
304 			nautilus_window_go_to (new_window, location);
305 			g_object_unref (location);
306 		}
307 	}
308 
309 	g_strfreev (names);
310 
311 	gtk_drag_finish (context, TRUE, FALSE, time);
312 }
313 
314 static void
315 drag_data_get_callback (GtkWidget *widget,
316 			GdkDragContext *context,
317 			GtkSelectionData *selection_data,
318 			guint info,
319 			guint32 time,
320 			gpointer callback_data)
321 {
322 	NautilusLocationEntry *self;
323 	GFile *location;
324 	gchar *uri;
325 
326 	g_assert (selection_data != NULL);
327 	self = callback_data;
328 
329 	location = nautilus_location_entry_get_location (self);
330 	uri = g_file_get_uri (location);
331 
332 	switch (info) {
333 	case NAUTILUS_DND_URI_LIST:
334 	case NAUTILUS_DND_TEXT_PLAIN:
335 		gtk_selection_data_set (selection_data,
336 					gtk_selection_data_get_target (selection_data),
337 					8, (guchar *) uri,
338 					strlen (uri));
339 		break;
340 	default:
341 		g_assert_not_reached ();
342 	}
343 	g_free (uri);
344 	g_object_unref (location);
345 }
346 
347 /* routine that performs the tab expansion.  Extract the directory name and
348    incomplete basename, then iterate through the directory trying to complete it.  If we
349    find something, add it to the entry */
350   
351 static gboolean
352 try_to_expand_path (gpointer callback_data)
353 {
354 	NautilusLocationEntry *entry;
355 	GtkEditable *editable;
356 	char *suffix, *user_location, *absolute_location, *uri_scheme;
357 	int user_location_length, pos;
358 
359 	entry = NAUTILUS_LOCATION_ENTRY (callback_data);
360 	editable = GTK_EDITABLE (entry);
361 	user_location = gtk_editable_get_chars (editable, 0, -1);
362 	user_location_length = g_utf8_strlen (user_location, -1);
363 	entry->details->idle_id = 0;
364 
365 	uri_scheme = g_uri_parse_scheme (user_location);
366 
367 	if (!g_path_is_absolute (user_location) && uri_scheme == NULL && user_location[0] != '~') {
368 		absolute_location = g_build_filename (entry->details->current_directory, user_location, NULL);
369 		suffix = g_filename_completer_get_completion_suffix (entry->details->completer,
370 							     absolute_location);
371 		g_free (absolute_location);
372 	} else {
373 		suffix = g_filename_completer_get_completion_suffix (entry->details->completer,
374 							     user_location);
375 	}
376 
377 	g_free (user_location);
378 	g_free (uri_scheme);
379 
380 	/* if we've got something, add it to the entry */
381 	if (suffix != NULL) {
382 		pos = user_location_length;
383 		gtk_editable_insert_text (editable,
384 					  suffix, -1,  &pos);
385 		pos = user_location_length;
386 		gtk_editable_select_region (editable, pos, -1);
387 		
388 		g_free (suffix);
389 	}
390 
391 	return FALSE;
392 }
393 
394 /* Until we have a more elegant solution, this is how we figure out if
395  * the GtkEntry inserted characters, assuming that the return value is
396  * TRUE indicating that the GtkEntry consumed the key event for some
397  * reason. This is a clone of code from GtkEntry.
398  */
399 static gboolean
400 entry_would_have_inserted_characters (const GdkEventKey *event)
401 {
402 	switch (event->keyval) {
403 	case GDK_KEY_BackSpace:
404 	case GDK_KEY_Clear:
405 	case GDK_KEY_Insert:
406 	case GDK_KEY_Delete:
407 	case GDK_KEY_Home:
408 	case GDK_KEY_End:
409 	case GDK_KEY_KP_Home:
410 	case GDK_KEY_KP_End:
411 	case GDK_KEY_Left:
412 	case GDK_KEY_Right:
413 	case GDK_KEY_KP_Left:
414 	case GDK_KEY_KP_Right:
415 	case GDK_KEY_Return:
416 		return FALSE;
417 	default:
418 		if (event->keyval >= 0x20 && event->keyval <= 0xFF) {
419 			if ((event->state & GDK_CONTROL_MASK) != 0) {
420 				return FALSE;
421 			}
422 			if ((event->state & GDK_MOD1_MASK) != 0) {
423 				return FALSE;
424 			}
425 		}
426 		return event->length > 0;
427 	}
428 }
429 
430 static gboolean
431 position_and_selection_are_at_end (GtkEditable *editable)
432 {
433 	int end;
434 	int start_sel, end_sel;
435 	
436 	end = get_editable_number_of_chars (editable);
437 	if (gtk_editable_get_selection_bounds (editable, &start_sel, &end_sel)) {
438 		if (start_sel != end || end_sel != end) {
439 			return FALSE;
440 		}
441 	}
442 	return gtk_editable_get_position (editable) == end;
443 }
444 
445 static void
446 got_completion_data_callback (GFilenameCompleter *completer,
447 			      NautilusLocationEntry *entry)
448 {
449 	if (entry->details->idle_id) {
450 		g_source_remove (entry->details->idle_id);
451 		entry->details->idle_id = 0;
452 	}
453 	try_to_expand_path (entry);
454 }
455 
456 static void
457 editable_event_after_callback (GtkEntry *entry,
458 			       GdkEvent *event,
459 			       NautilusLocationEntry *location_entry)
460 {
461 	GtkEditable *editable;
462 	GdkEventKey *keyevent;
463 
464 	if (event->type != GDK_KEY_PRESS) {
465 		return;
466 	}
467 
468 	editable = GTK_EDITABLE (entry);
469 	keyevent = (GdkEventKey *)event;
470 
471 	/* After typing the right arrow key we move the selection to
472 	 * the end, if we have a valid selection - since this is most
473 	 * likely an auto-completion. We ignore shift / control since
474 	 * they can validly be used to extend the selection.
475 	 */
476 	if ((keyevent->keyval == GDK_KEY_Right || keyevent->keyval == GDK_KEY_End) &&
477 	    !(keyevent->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) && 
478 	    gtk_editable_get_selection_bounds (editable, NULL, NULL)) {
479 		set_position_and_selection_to_end (editable);
480 	}
481 
482 	/* Only do expanding when we are typing at the end of the
483 	 * text. Do the expand at idle time to avoid slowing down
484 	 * typing when the directory is large. Only trigger the expand
485 	 * when we type a key that would have inserted characters.
486 	 */
487 	if (position_and_selection_are_at_end (editable)) {
488 		if (entry_would_have_inserted_characters (keyevent)) {
489 			if (location_entry->details->idle_id == 0) {
490 				location_entry->details->idle_id = g_idle_add (try_to_expand_path, location_entry);
491 			}
492 		}
493 	} else {
494 		/* FIXME: Also might be good to do this when you click
495 		 * to change the position or selection.
496 		 */
497 		if (location_entry->details->idle_id != 0) {
498 			g_source_remove (location_entry->details->idle_id);
499 			location_entry->details->idle_id = 0;
500 		}
501 	}
502 }
503 
504 static void
505 finalize (GObject *object)
506 {
507 	NautilusLocationEntry *entry;
508 
509 	entry = NAUTILUS_LOCATION_ENTRY (object);
510 
511 	g_object_unref (entry->details->completer);
512 	g_free (entry->details->special_text);
513 
514 	g_free (entry->details->last_uri);
515 	entry->details->last_uri = NULL;
516 
517 	G_OBJECT_CLASS (nautilus_location_entry_parent_class)->finalize (object);
518 }
519 
520 static void
521 destroy (GtkWidget *object)
522 {
523 	NautilusLocationEntry *entry;
524 
525 	entry = NAUTILUS_LOCATION_ENTRY (object);
526 	
527 	/* cancel the pending idle call, if any */
528 	if (entry->details->idle_id != 0) {
529 		g_source_remove (entry->details->idle_id);
530 		entry->details->idle_id = 0;
531 	}
532 	
533 	g_free (entry->details->current_directory);
534 	entry->details->current_directory = NULL;
535 	
536 	GTK_WIDGET_CLASS (nautilus_location_entry_parent_class)->destroy (object);
537 }
538 
539 static void
540 nautilus_location_entry_text_changed (NautilusLocationEntry *entry,
541 				      GParamSpec            *pspec)
542 {
543 	if (entry->details->setting_special_text) {
544 		return;
545 	}
546 
547 	entry->details->has_special_text = FALSE;
548 }
549 
550 static void
551 nautilus_location_entry_icon_release (GtkEntry *gentry,
552 				      GtkEntryIconPosition position,
553 				      GdkEvent *event,
554 				      gpointer unused)
555 {
556 	switch (NAUTILUS_LOCATION_ENTRY (gentry)->details->secondary_action) {
557 	case NAUTILUS_LOCATION_ENTRY_ACTION_GOTO:
558 		g_signal_emit_by_name (gentry, "activate", gentry);
559 		break;
560 	case NAUTILUS_LOCATION_ENTRY_ACTION_CLEAR:
561 		gtk_entry_set_text (gentry, "");
562 		break;
563 	default:
564 		g_assert_not_reached ();
565 	}
566 }
567 
568 static gboolean
569 nautilus_location_entry_focus_in (GtkWidget     *widget,
570 				  GdkEventFocus *event)
571 {
572 	NautilusLocationEntry *entry = NAUTILUS_LOCATION_ENTRY (widget);
573 
574 	if (entry->details->has_special_text) {
575 		entry->details->setting_special_text = TRUE;
576 		gtk_entry_set_text (GTK_ENTRY (entry), "");
577 		entry->details->setting_special_text = FALSE;
578 	}
579 
580 	return GTK_WIDGET_CLASS (nautilus_location_entry_parent_class)->focus_in_event (widget, event);
581 }
582 
583 static void
584 nautilus_location_entry_activate (GtkEntry *entry)
585 {
586 	NautilusLocationEntry *loc_entry;
587 	const gchar *entry_text;
588 	gchar *full_path, *uri_scheme = NULL;
589 
590 	loc_entry = NAUTILUS_LOCATION_ENTRY (entry);
591 	entry_text = gtk_entry_get_text (entry);
592 
593 	if (entry_text != NULL && *entry_text != '\0') {
594 		uri_scheme = g_uri_parse_scheme (entry_text);
595 
596 		if (!g_path_is_absolute (entry_text) && uri_scheme == NULL && entry_text[0] != '~') {
597 			/* Fix non absolute paths */
598 			full_path = g_build_filename (loc_entry->details->current_directory, entry_text, NULL);
599 			gtk_entry_set_text (entry, full_path);
600 			g_free (full_path);
601 		}
602 
603 		g_free (uri_scheme);
604 	}
605 
606 	GTK_ENTRY_CLASS (nautilus_location_entry_parent_class)->activate (entry);
607 }
608 
609 static void
610 nautilus_location_entry_cancel (NautilusLocationEntry *entry)
611 {
612 	char *last_uri;
613 
614 	last_uri = entry->details->last_uri;
615 	nautilus_location_entry_set_uri (entry, last_uri);
616 }
617 
618 static void
619 nautilus_location_entry_class_init (NautilusLocationEntryClass *class)
620 {
621 	GtkWidgetClass *widget_class;
622 	GObjectClass *gobject_class;
623 	GtkEntryClass *entry_class;
624 	GtkBindingSet *binding_set;
625 
626 	widget_class = GTK_WIDGET_CLASS (class);
627 	widget_class->focus_in_event = nautilus_location_entry_focus_in;
628 	widget_class->destroy = destroy;
629 
630 	gobject_class = G_OBJECT_CLASS (class);
631 	gobject_class->finalize = finalize;
632 
633 	entry_class = GTK_ENTRY_CLASS (class);
634 	entry_class->activate = nautilus_location_entry_activate;
635 
636 	class->cancel = nautilus_location_entry_cancel;
637 
638 	signals[CANCEL] = g_signal_new
639 		("cancel",
640 		 G_TYPE_FROM_CLASS (class),
641 		 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
642 		 G_STRUCT_OFFSET (NautilusLocationEntryClass,
643 				  cancel),
644 		 NULL, NULL,
645 		 g_cclosure_marshal_VOID__VOID,
646 		 G_TYPE_NONE, 0);
647 
648 	signals[LOCATION_CHANGED] = g_signal_new
649 		("location-changed",
650 		 G_TYPE_FROM_CLASS (class),
651 		 G_SIGNAL_RUN_LAST, 0,
652 		 NULL, NULL,
653 		 g_cclosure_marshal_generic,
654 		 G_TYPE_NONE, 1, G_TYPE_OBJECT);
655 
656 	binding_set = gtk_binding_set_by_class (class);
657 	gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "cancel", 0);
658 
659 	g_type_class_add_private (class, sizeof (NautilusLocationEntryDetails));
660 }
661 
662 void
663 nautilus_location_entry_set_secondary_action (NautilusLocationEntry *entry,
664 					      NautilusLocationEntryAction secondary_action)
665 {
666 	if (entry->details->secondary_action == secondary_action) {
667 		return;
668 	}
669 	switch (secondary_action) {
670 	case NAUTILUS_LOCATION_ENTRY_ACTION_CLEAR:
671 		gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry), 
672 						   GTK_ENTRY_ICON_SECONDARY,
673 						   "edit-clear-symbolic");
674 		break;
675 	case NAUTILUS_LOCATION_ENTRY_ACTION_GOTO:
676 		gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry),
677 						   GTK_ENTRY_ICON_SECONDARY,
678 						   "go-next-symbolic");
679 		break;
680 	default:
681 		g_assert_not_reached ();
682 	}
683 	entry->details->secondary_action = secondary_action;
684 }
685 
686 static void
687 editable_activate_callback (GtkEntry *entry,
688 			    gpointer user_data)
689 {
690 	NautilusLocationEntry *self = user_data;
691 	const char *entry_text;
692 
693 	entry_text = gtk_entry_get_text (entry);
694 	if (entry_text != NULL && *entry_text != '\0') {
695 		emit_location_changed (self);
696 	}
697 }
698 
699 static void
700 editable_changed_callback (GtkEntry *entry,
701 			   gpointer user_data)
702 {
703 	nautilus_location_entry_update_action (NAUTILUS_LOCATION_ENTRY (entry));
704 }
705 
706 static void
707 nautilus_location_entry_init (NautilusLocationEntry *entry)
708 {
709 	GtkTargetList *targetlist;
710 
711 	entry->details = G_TYPE_INSTANCE_GET_PRIVATE (entry, NAUTILUS_TYPE_LOCATION_ENTRY,
712 						      NautilusLocationEntryDetails);
713 
714 	entry->details->completer = g_filename_completer_new ();
715 	g_filename_completer_set_dirs_only (entry->details->completer, TRUE);
716 
717 	gtk_entry_set_icon_from_icon_name (GTK_ENTRY (entry), GTK_ENTRY_ICON_PRIMARY, "folder-symbolic");
718 	gtk_entry_set_icon_activatable (GTK_ENTRY (entry), GTK_ENTRY_ICON_PRIMARY, FALSE);
719 	targetlist = gtk_target_list_new (drag_types, G_N_ELEMENTS (drag_types));
720 	gtk_entry_set_icon_drag_source (GTK_ENTRY (entry), GTK_ENTRY_ICON_PRIMARY, targetlist, GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
721 	gtk_target_list_unref (targetlist);
722 
723 	nautilus_location_entry_set_secondary_action (entry,
724 						      NAUTILUS_LOCATION_ENTRY_ACTION_CLEAR);
725 
726 	nautilus_entry_set_special_tab_handling (NAUTILUS_ENTRY (entry), TRUE);
727 
728 	g_signal_connect (entry, "event_after",
729 		          G_CALLBACK (editable_event_after_callback), entry);
730 
731 	g_signal_connect (entry, "notify::text",
732 			  G_CALLBACK (nautilus_location_entry_text_changed), NULL);
733 
734 	g_signal_connect (entry, "icon-release",
735 			  G_CALLBACK (nautilus_location_entry_icon_release), NULL);
736 
737 	g_signal_connect (entry->details->completer, "got_completion_data",
738 		          G_CALLBACK (got_completion_data_callback), entry);
739 
740 	/* Drag source */
741 	g_signal_connect_object (entry, "drag_data_get",
742 				 G_CALLBACK (drag_data_get_callback), entry, 0);
743 
744 	/* Drag dest. */
745 	gtk_drag_dest_set (GTK_WIDGET (entry),
746 			   GTK_DEST_DEFAULT_ALL,
747 			   drop_types, G_N_ELEMENTS (drop_types),
748 			   GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
749 	g_signal_connect (entry, "drag_data_received",
750 			  G_CALLBACK (drag_data_received_callback), NULL);
751 
752 	g_signal_connect_object (entry, "activate",
753 				 G_CALLBACK (editable_activate_callback), entry, G_CONNECT_AFTER);
754 	g_signal_connect_object (entry, "changed",
755 				 G_CALLBACK (editable_changed_callback), entry, 0);
756 }
757 
758 GtkWidget *
759 nautilus_location_entry_new (void)
760 {
761 	GtkWidget *entry;
762 
763 	entry = gtk_widget_new (NAUTILUS_TYPE_LOCATION_ENTRY, NULL);
764 
765 	return entry;
766 }
767 
768 void
769 nautilus_location_entry_set_special_text (NautilusLocationEntry *entry,
770 					  const char            *special_text)
771 {
772 	entry->details->has_special_text = TRUE;
773 	
774 	g_free (entry->details->special_text);
775 	entry->details->special_text = g_strdup (special_text);
776 
777 	entry->details->setting_special_text = TRUE;
778 	gtk_entry_set_text (GTK_ENTRY (entry), special_text);
779 	entry->details->setting_special_text = FALSE;
780 }