2008-04-30 Cosimo Cecchi <cosimoc@gnome.org>
[nautilus.git] / src / nautilus-location-bar.c
blobcee33bd81ba491a41211ec1b20920a6280ffead4
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-bar.h"
36 #include "nautilus-location-entry.h"
37 #include "nautilus-window-private.h"
38 #include "nautilus-window.h"
39 #include <eel/eel-accessibility.h>
40 #include <eel/eel-glib-extensions.h>
41 #include <eel/eel-gtk-macros.h>
42 #include <eel/eel-stock-dialogs.h>
43 #include <eel/eel-string.h>
44 #include <eel/eel-vfs-extensions.h>
45 #include <gtk/gtkdnd.h>
46 #include <gtk/gtksignal.h>
47 #include <glib/gi18n.h>
48 #include <libgnomeui/gnome-stock-icons.h>
49 #include <libgnomeui/gnome-uidefs.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 #define NAUTILUS_DND_URI_LIST_TYPE "text/uri-list"
56 #define NAUTILUS_DND_TEXT_PLAIN_TYPE "text/plain"
58 static const char untranslated_location_label[] = N_("Location:");
59 static const char untranslated_go_to_label[] = N_("Go To:");
60 #define LOCATION_LABEL _(untranslated_location_label)
61 #define GO_TO_LABEL _(untranslated_go_to_label)
63 struct NautilusLocationBarDetails {
64 GtkLabel *label;
65 NautilusEntry *entry;
67 char *last_location;
69 guint idle_id;
72 enum {
73 NAUTILUS_DND_MC_DESKTOP_ICON,
74 NAUTILUS_DND_URI_LIST,
75 NAUTILUS_DND_TEXT_PLAIN,
76 NAUTILUS_DND_NTARGETS
79 static const GtkTargetEntry drag_types [] = {
80 { NAUTILUS_DND_URI_LIST_TYPE, 0, NAUTILUS_DND_URI_LIST },
81 { NAUTILUS_DND_TEXT_PLAIN_TYPE, 0, NAUTILUS_DND_TEXT_PLAIN },
84 static const GtkTargetEntry drop_types [] = {
85 { NAUTILUS_DND_URI_LIST_TYPE, 0, NAUTILUS_DND_URI_LIST },
86 { NAUTILUS_DND_TEXT_PLAIN_TYPE, 0, NAUTILUS_DND_TEXT_PLAIN },
89 static char *nautilus_location_bar_get_location (NautilusNavigationBar *navigation_bar);
90 static void nautilus_location_bar_set_location (NautilusNavigationBar *navigation_bar,
91 const char *location);
92 static void nautilus_location_bar_class_init (NautilusLocationBarClass *class);
93 static void nautilus_location_bar_init (NautilusLocationBar *bar);
94 static void nautilus_location_bar_update_label (NautilusLocationBar *bar);
96 EEL_CLASS_BOILERPLATE (NautilusLocationBar,
97 nautilus_location_bar,
98 NAUTILUS_TYPE_NAVIGATION_BAR)
100 static NautilusNavigationWindow *
101 nautilus_location_bar_get_window (GtkWidget *bar)
103 return NAUTILUS_NAVIGATION_WINDOW (gtk_widget_get_ancestor (bar, NAUTILUS_TYPE_WINDOW));
106 static void
107 drag_data_received_callback (GtkWidget *widget,
108 GdkDragContext *context,
109 int x,
110 int y,
111 GtkSelectionData *data,
112 guint info,
113 guint32 time,
114 gpointer callback_data)
116 char **names;
117 NautilusApplication *application;
118 int name_count;
119 NautilusWindow *new_window;
120 NautilusNavigationWindow *window;
121 GdkScreen *screen;
122 gboolean new_windows_for_extras;
123 char *prompt;
124 char *detail;
125 GFile *location;
127 g_assert (NAUTILUS_IS_LOCATION_BAR (widget));
128 g_assert (data != NULL);
129 g_assert (callback_data == NULL);
131 names = g_uri_list_extract_uris (data->data);
133 if (names == NULL || *names == NULL) {
134 g_warning ("No D&D URI's");
135 gtk_drag_finish (context, FALSE, FALSE, time);
136 return;
139 window = nautilus_location_bar_get_window (widget);
140 new_windows_for_extras = FALSE;
141 /* Ask user if they really want to open multiple windows
142 * for multiple dropped URIs. This is likely to have been
143 * a mistake.
145 name_count = g_strv_length (names);
146 if (name_count > 1) {
147 prompt = g_strdup_printf (ngettext("Do you want to view %d location?",
148 "Do you want to view %d locations?",
149 name_count),
150 name_count);
151 detail = g_strdup_printf (ngettext("This will open %d separate window.",
152 "This will open %d separate windows.",
153 name_count),
154 name_count);
155 /* eel_run_simple_dialog should really take in pairs
156 * like gtk_dialog_new_with_buttons() does. */
157 new_windows_for_extras = eel_run_simple_dialog
158 (GTK_WIDGET (window),
159 TRUE,
160 GTK_MESSAGE_QUESTION,
161 prompt,
162 detail,
163 GTK_STOCK_CANCEL, GTK_STOCK_OK,
164 NULL) != 0 /* GNOME_OK */;
166 g_free (prompt);
167 g_free (detail);
169 if (!new_windows_for_extras) {
170 gtk_drag_finish (context, FALSE, FALSE, time);
171 return;
175 nautilus_navigation_bar_set_location (NAUTILUS_NAVIGATION_BAR (widget),
176 names[0]);
177 nautilus_navigation_bar_location_changed (NAUTILUS_NAVIGATION_BAR (widget));
179 if (new_windows_for_extras) {
180 int i;
182 application = NAUTILUS_WINDOW (window)->application;
183 screen = gtk_window_get_screen (GTK_WINDOW (window));
185 for (i = 1; names[i] != NULL; ++i) {
186 new_window = nautilus_application_create_navigation_window (application, NULL, screen);
187 location = g_file_new_for_uri (names[i]);
188 nautilus_window_go_to (new_window, location);
189 g_object_unref (location);
193 g_strfreev (names);
195 gtk_drag_finish (context, TRUE, FALSE, time);
198 static void
199 drag_data_get_callback (GtkWidget *widget,
200 GdkDragContext *context,
201 GtkSelectionData *selection_data,
202 guint info,
203 guint32 time,
204 gpointer callback_data)
206 NautilusNavigationBar *bar;
207 char *entry_text;
209 g_assert (selection_data != NULL);
210 bar = NAUTILUS_NAVIGATION_BAR (callback_data);
212 entry_text = nautilus_navigation_bar_get_location (bar);
214 switch (info) {
215 case NAUTILUS_DND_URI_LIST:
216 case NAUTILUS_DND_TEXT_PLAIN:
217 gtk_selection_data_set (selection_data,
218 selection_data->target,
219 8, (guchar *) entry_text,
220 eel_strlen (entry_text));
221 break;
222 default:
223 g_assert_not_reached ();
225 g_free (entry_text);
228 /* routine that determines the usize for the label widget as larger
229 then the size of the largest string and then sets it to that so
230 that we don't have localization problems. see
231 gtk_label_finalize_lines in gtklabel.c (line 618) for the code that
232 we are imitating here. */
234 static void
235 style_set_handler (GtkWidget *widget, GtkStyle *previous_style)
237 PangoLayout *layout;
238 int width, width2;
240 layout = gtk_label_get_layout (GTK_LABEL(widget));
242 layout = pango_layout_copy (layout);
244 pango_layout_set_text (layout, LOCATION_LABEL, -1);
245 pango_layout_get_pixel_size (layout, &width, NULL);
247 pango_layout_set_text (layout, GO_TO_LABEL, -1);
248 pango_layout_get_pixel_size (layout, &width2, NULL);
249 width = MAX (width, width2);
251 width += 2 * GTK_MISC (widget)->xpad;
253 gtk_widget_set_size_request (widget, width, -1);
255 g_object_unref (layout);
258 static gboolean
259 label_button_pressed_callback (GtkWidget *widget,
260 GdkEventButton *event)
262 NautilusNavigationWindow *window;
263 NautilusView *view;
264 GtkWidget *label;
266 if (event->button != 3) {
267 return FALSE;
270 window = nautilus_location_bar_get_window (widget->parent);
271 view = NAUTILUS_WINDOW (window)->content_view;
272 label = GTK_BIN (widget)->child;
273 /* only pop-up if the URI in the entry matches the displayed location */
274 if (view == NULL ||
275 strcmp (gtk_label_get_text (GTK_LABEL (label)), LOCATION_LABEL)) {
276 return FALSE;
279 nautilus_view_pop_up_location_context_menu (view, event);
281 return FALSE;
284 static int
285 get_editable_number_of_chars (GtkEditable *editable)
287 char *text;
288 int length;
290 text = gtk_editable_get_chars (editable, 0, -1);
291 length = g_utf8_strlen (text, -1);
292 g_free (text);
293 return length;
296 static void
297 set_position_and_selection_to_end (GtkEditable *editable)
299 int end;
301 end = get_editable_number_of_chars (editable);
302 gtk_editable_select_region (editable, end, end);
303 gtk_editable_set_position (editable, end);
306 static void
307 editable_event_after_callback (GtkEntry *entry,
308 GdkEvent *event,
309 gpointer user_data)
311 nautilus_location_bar_update_label (NAUTILUS_LOCATION_BAR (user_data));
314 static void
315 real_activate (NautilusNavigationBar *navigation_bar)
317 NautilusLocationBar *bar;
319 bar = NAUTILUS_LOCATION_BAR (navigation_bar);
321 /* Put the keyboard focus in the text field when switching to this mode,
322 * and select all text for easy overtyping
324 gtk_widget_grab_focus (GTK_WIDGET (bar->details->entry));
325 nautilus_entry_select_all (bar->details->entry);
328 static void
329 real_cancel (NautilusNavigationBar *navigation_bar)
331 char *last_location;
333 last_location = NAUTILUS_LOCATION_BAR (navigation_bar)->details->last_location;
334 nautilus_navigation_bar_set_location (navigation_bar, last_location);
337 static void
338 finalize (GObject *object)
340 NautilusLocationBar *bar;
342 bar = NAUTILUS_LOCATION_BAR (object);
344 g_free (bar->details);
346 EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
349 static void
350 destroy (GtkObject *object)
352 NautilusLocationBar *bar;
354 bar = NAUTILUS_LOCATION_BAR (object);
356 /* cancel the pending idle call, if any */
357 if (bar->details->idle_id != 0) {
358 g_source_remove (bar->details->idle_id);
359 bar->details->idle_id = 0;
362 g_free (bar->details->last_location);
363 bar->details->last_location = NULL;
365 EEL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
368 static void
369 nautilus_location_bar_class_init (NautilusLocationBarClass *class)
371 GObjectClass *gobject_class;
372 GtkObjectClass *object_class;
373 NautilusNavigationBarClass *navigation_bar_class;
375 gobject_class = G_OBJECT_CLASS (class);
376 gobject_class->finalize = finalize;
378 object_class = GTK_OBJECT_CLASS (class);
379 object_class->destroy = destroy;
381 navigation_bar_class = NAUTILUS_NAVIGATION_BAR_CLASS (class);
383 navigation_bar_class->activate = real_activate;
384 navigation_bar_class->cancel = real_cancel;
385 navigation_bar_class->get_location = nautilus_location_bar_get_location;
386 navigation_bar_class->set_location = nautilus_location_bar_set_location;
389 static void
390 nautilus_location_bar_init (NautilusLocationBar *bar)
392 GtkWidget *label;
393 GtkWidget *entry;
394 GtkWidget *event_box;
395 GtkWidget *hbox;
397 bar->details = g_new0 (NautilusLocationBarDetails, 1);
399 hbox = gtk_hbox_new (0, FALSE);
401 event_box = gtk_event_box_new ();
402 gtk_event_box_set_visible_window (GTK_EVENT_BOX (event_box), FALSE);
404 gtk_container_set_border_width (GTK_CONTAINER (event_box),
405 GNOME_PAD_SMALL);
406 label = gtk_label_new (LOCATION_LABEL);
407 gtk_container_add (GTK_CONTAINER (event_box), label);
408 gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_RIGHT);
409 gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
410 g_signal_connect (label, "style_set",
411 G_CALLBACK (style_set_handler), NULL);
413 gtk_box_pack_start (GTK_BOX (hbox), event_box, FALSE, TRUE,
414 GNOME_PAD_SMALL);
416 entry = nautilus_location_entry_new ();
418 g_signal_connect_object (entry, "activate",
419 G_CALLBACK (nautilus_navigation_bar_location_changed),
420 bar, G_CONNECT_SWAPPED);
421 g_signal_connect_object (entry, "event_after",
422 G_CALLBACK (editable_event_after_callback), bar, G_CONNECT_AFTER);
424 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
426 eel_accessibility_set_up_label_widget_relation (label, entry);
428 gtk_container_add (GTK_CONTAINER (bar), hbox);
431 /* Label context menu */
432 g_signal_connect (event_box, "button-press-event",
433 G_CALLBACK (label_button_pressed_callback), NULL);
435 /* Drag source */
436 gtk_drag_source_set (GTK_WIDGET (event_box),
437 GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
438 drag_types, G_N_ELEMENTS (drag_types),
439 GDK_ACTION_COPY | GDK_ACTION_LINK);
440 g_signal_connect_object (event_box, "drag_data_get",
441 G_CALLBACK (drag_data_get_callback), bar, 0);
443 /* Drag dest. */
444 gtk_drag_dest_set (GTK_WIDGET (bar),
445 GTK_DEST_DEFAULT_ALL,
446 drop_types, G_N_ELEMENTS (drop_types),
447 GDK_ACTION_COPY | GDK_ACTION_MOVE | GDK_ACTION_LINK);
448 g_signal_connect (bar, "drag_data_received",
449 G_CALLBACK (drag_data_received_callback), NULL);
451 gtk_widget_show_all (hbox);
453 bar->details->label = GTK_LABEL (label);
454 bar->details->entry = NAUTILUS_ENTRY (entry);
457 GtkWidget *
458 nautilus_location_bar_new (NautilusNavigationWindow *window)
460 GtkWidget *bar;
461 NautilusLocationBar *location_bar;
463 bar = gtk_widget_new (NAUTILUS_TYPE_LOCATION_BAR, NULL);
464 location_bar = NAUTILUS_LOCATION_BAR (bar);
466 /* Clipboard */
467 nautilus_clipboard_set_up_editable
468 (GTK_EDITABLE (location_bar->details->entry),
469 nautilus_window_get_ui_manager (NAUTILUS_WINDOW (window)),
470 TRUE);
472 return bar;
475 static void
476 nautilus_location_bar_set_location (NautilusNavigationBar *navigation_bar,
477 const char *location)
479 NautilusLocationBar *bar;
480 char *formatted_location;
481 GFile *file;
483 g_assert (location != NULL);
485 bar = NAUTILUS_LOCATION_BAR (navigation_bar);
487 /* Note: This is called in reaction to external changes, and
488 * thus should not emit the LOCATION_CHANGED signal. */
490 if (eel_uri_is_search (location)) {
491 nautilus_location_entry_set_special_text (NAUTILUS_LOCATION_ENTRY (bar->details->entry),
492 "");
493 } else {
494 file = g_file_new_for_uri (location);
495 formatted_location = g_file_get_parse_name (file);
496 g_object_unref (file);
497 nautilus_entry_set_text (NAUTILUS_ENTRY (bar->details->entry),
498 formatted_location);
499 set_position_and_selection_to_end (GTK_EDITABLE (bar->details->entry));
500 g_free (formatted_location);
503 /* remember the original location for later comparison */
505 if (bar->details->last_location != location) {
506 g_free (bar->details->last_location);
507 bar->details->last_location = g_strdup (location);
510 nautilus_location_bar_update_label (bar);
514 * nautilus_location_bar_get_location
516 * Get the "URI" represented by the text in the location bar.
518 * @bar: A NautilusLocationBar.
520 * returns a newly allocated "string" containing the mangled
521 * (by g_file_parse_name) text that the user typed in...maybe a URI
522 * but not guaranteed.
525 static char *
526 nautilus_location_bar_get_location (NautilusNavigationBar *navigation_bar)
528 NautilusLocationBar *bar;
529 char *user_location, *uri;
530 GFile *location;
532 bar = NAUTILUS_LOCATION_BAR (navigation_bar);
534 user_location = gtk_editable_get_chars (GTK_EDITABLE (bar->details->entry), 0, -1);
535 location = g_file_parse_name (user_location);
536 g_free (user_location);
537 uri = g_file_get_uri (location);
538 g_object_unref (location);
539 return uri;
543 * nautilus_location_bar_update_label
545 * if the text in the entry matches the uri, set the label to "location", otherwise use "goto"
548 static void
549 nautilus_location_bar_update_label (NautilusLocationBar *bar)
551 const char *current_text;
552 GFile *location;
553 GFile *last_location;
555 current_text = gtk_entry_get_text (GTK_ENTRY (bar->details->entry));
556 location = g_file_parse_name (current_text);
557 last_location = g_file_parse_name (bar->details->last_location);
559 if (g_file_equal (last_location, location)) {
560 gtk_label_set_text (GTK_LABEL (bar->details->label), LOCATION_LABEL);
561 } else {
562 gtk_label_set_text (GTK_LABEL (bar->details->label), GO_TO_LABEL);
565 g_object_unref (location);
566 g_object_unref (last_location);