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-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>
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
{
73 NAUTILUS_DND_MC_DESKTOP_ICON
,
74 NAUTILUS_DND_URI_LIST
,
75 NAUTILUS_DND_TEXT_PLAIN
,
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
));
107 drag_data_received_callback (GtkWidget
*widget
,
108 GdkDragContext
*context
,
111 GtkSelectionData
*data
,
114 gpointer callback_data
)
117 NautilusApplication
*application
;
119 NautilusWindow
*new_window
;
120 NautilusNavigationWindow
*window
;
122 gboolean new_windows_for_extras
;
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
);
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
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?",
151 detail
= g_strdup_printf (ngettext("This will open %d separate window.",
152 "This will open %d separate windows.",
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
),
160 GTK_MESSAGE_QUESTION
,
163 GTK_STOCK_CANCEL
, GTK_STOCK_OK
,
164 NULL
) != 0 /* GNOME_OK */;
169 if (!new_windows_for_extras
) {
170 gtk_drag_finish (context
, FALSE
, FALSE
, time
);
175 nautilus_navigation_bar_set_location (NAUTILUS_NAVIGATION_BAR (widget
),
177 nautilus_navigation_bar_location_changed (NAUTILUS_NAVIGATION_BAR (widget
));
179 if (new_windows_for_extras
) {
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
);
195 gtk_drag_finish (context
, TRUE
, FALSE
, time
);
199 drag_data_get_callback (GtkWidget
*widget
,
200 GdkDragContext
*context
,
201 GtkSelectionData
*selection_data
,
204 gpointer callback_data
)
206 NautilusNavigationBar
*bar
;
209 g_assert (selection_data
!= NULL
);
210 bar
= NAUTILUS_NAVIGATION_BAR (callback_data
);
212 entry_text
= nautilus_navigation_bar_get_location (bar
);
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
));
223 g_assert_not_reached ();
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. */
235 style_set_handler (GtkWidget
*widget
, GtkStyle
*previous_style
)
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
);
259 label_button_pressed_callback (GtkWidget
*widget
,
260 GdkEventButton
*event
)
262 NautilusNavigationWindow
*window
;
266 if (event
->button
!= 3) {
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 */
275 strcmp (gtk_label_get_text (GTK_LABEL (label
)), LOCATION_LABEL
)) {
279 nautilus_view_pop_up_location_context_menu (view
, event
);
285 get_editable_number_of_chars (GtkEditable
*editable
)
290 text
= gtk_editable_get_chars (editable
, 0, -1);
291 length
= g_utf8_strlen (text
, -1);
297 set_position_and_selection_to_end (GtkEditable
*editable
)
301 end
= get_editable_number_of_chars (editable
);
302 gtk_editable_select_region (editable
, end
, end
);
303 gtk_editable_set_position (editable
, end
);
307 editable_event_after_callback (GtkEntry
*entry
,
311 nautilus_location_bar_update_label (NAUTILUS_LOCATION_BAR (user_data
));
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
);
329 real_cancel (NautilusNavigationBar
*navigation_bar
)
333 last_location
= NAUTILUS_LOCATION_BAR (navigation_bar
)->details
->last_location
;
334 nautilus_navigation_bar_set_location (navigation_bar
, last_location
);
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
));
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
));
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
;
390 nautilus_location_bar_init (NautilusLocationBar
*bar
)
394 GtkWidget
*event_box
;
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
),
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
,
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
);
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);
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
);
458 nautilus_location_bar_new (NautilusNavigationWindow
*window
)
461 NautilusLocationBar
*location_bar
;
463 bar
= gtk_widget_new (NAUTILUS_TYPE_LOCATION_BAR
, NULL
);
464 location_bar
= NAUTILUS_LOCATION_BAR (bar
);
467 nautilus_clipboard_set_up_editable
468 (GTK_EDITABLE (location_bar
->details
->entry
),
469 nautilus_window_get_ui_manager (NAUTILUS_WINDOW (window
)),
476 nautilus_location_bar_set_location (NautilusNavigationBar
*navigation_bar
,
477 const char *location
)
479 NautilusLocationBar
*bar
;
480 char *formatted_location
;
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
),
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
),
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.
526 nautilus_location_bar_get_location (NautilusNavigationBar
*navigation_bar
)
528 NautilusLocationBar
*bar
;
529 char *user_location
, *uri
;
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
);
543 * nautilus_location_bar_update_label
545 * if the text in the entry matches the uri, set the label to "location", otherwise use "goto"
549 nautilus_location_bar_update_label (NautilusLocationBar
*bar
)
551 const char *current_text
;
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
);
562 gtk_label_set_text (GTK_LABEL (bar
->details
->label
), GO_TO_LABEL
);
565 g_object_unref (location
);
566 g_object_unref (last_location
);