source: trunk/third/nautilus/src/nautilus-location-bar.c @ 18663

Revision 18663, 23.3 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18662, which included commits to RCS files with non-trunk default branches.
Line 
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-bar.h"
35
36#include "nautilus-window-private.h"
37#include "nautilus-window.h"
38#include <eel/eel-accessibility.h>
39#include <eel/eel-glib-extensions.h>
40#include <eel/eel-gtk-macros.h>
41#include <eel/eel-stock-dialogs.h>
42#include <eel/eel-string.h>
43#include <eel/eel-vfs-extensions.h>
44#include <gtk/gtkdnd.h>
45#include <gtk/gtkeventbox.h>
46#include <gtk/gtksignal.h>
47#include <libgnome/gnome-i18n.h>
48#include <libgnomeui/gnome-stock-icons.h>
49#include <libgnomeui/gnome-uidefs.h>
50#include <libgnomevfs/gnome-vfs.h>
51#include <libnautilus-private/nautilus-entry.h>
52#include <libnautilus-private/nautilus-icon-dnd.h>
53#include <libnautilus/nautilus-clipboard.h>
54#include <stdio.h>
55#include <string.h>
56
57#define NAUTILUS_DND_URI_LIST_TYPE        "text/uri-list"
58#define NAUTILUS_DND_TEXT_PLAIN_TYPE      "text/plain"
59#define NAUTILUS_DND_URL_TYPE             "_NETSCAPE_URL"
60
61static const char untranslated_location_label[] = N_("Location:");
62static const char untranslated_go_to_label[] = N_("Go To:");
63#define LOCATION_LABEL _(untranslated_location_label)
64#define GO_TO_LABEL _(untranslated_go_to_label)
65
66struct NautilusLocationBarDetails {
67        GtkLabel *label;
68        NautilusEntry *entry;
69       
70        char *last_location;
71       
72        char *current_directory;
73        GList *file_info_list;
74       
75        guint idle_id;
76};
77
78enum {
79        NAUTILUS_DND_MC_DESKTOP_ICON,
80        NAUTILUS_DND_URI_LIST,
81        NAUTILUS_DND_TEXT_PLAIN,
82        NAUTILUS_DND_URL,
83        NAUTILUS_DND_NTARGETS
84};
85
86static GtkTargetEntry drag_types [] = {
87        { NAUTILUS_DND_URI_LIST_TYPE,   0, NAUTILUS_DND_URI_LIST },
88        { NAUTILUS_DND_TEXT_PLAIN_TYPE, 0, NAUTILUS_DND_TEXT_PLAIN },
89        { NAUTILUS_DND_URL_TYPE,        0, NAUTILUS_DND_URL }
90};
91
92static GtkTargetEntry drop_types [] = {
93        { NAUTILUS_DND_URI_LIST_TYPE,   0, NAUTILUS_DND_URI_LIST },
94        { NAUTILUS_DND_TEXT_PLAIN_TYPE, 0, NAUTILUS_DND_TEXT_PLAIN },
95        { NAUTILUS_DND_URL_TYPE,        0, NAUTILUS_DND_URL }
96};
97
98static char *nautilus_location_bar_get_location     (NautilusNavigationBar    *navigation_bar);
99static void  nautilus_location_bar_set_location     (NautilusNavigationBar    *navigation_bar,
100                                                     const char               *location);
101static void  nautilus_location_bar_class_init       (NautilusLocationBarClass *class);
102static void  nautilus_location_bar_init             (NautilusLocationBar      *bar);
103static void  nautilus_location_bar_update_label     (NautilusLocationBar      *bar);
104
105EEL_CLASS_BOILERPLATE (NautilusLocationBar,
106                       nautilus_location_bar,
107                       NAUTILUS_TYPE_NAVIGATION_BAR)
108
109static NautilusWindow *
110nautilus_location_bar_get_window (GtkWidget *bar)
111{
112        return NAUTILUS_WINDOW (gtk_widget_get_ancestor (bar, NAUTILUS_TYPE_WINDOW));
113}
114
115static void
116drag_data_received_callback (GtkWidget *widget,
117                             GdkDragContext *context,
118                             int x,
119                             int y,
120                             GtkSelectionData *data,
121                             guint info,
122                             guint32 time,
123                             gpointer callback_data)
124{
125        GList *names, *node;
126        NautilusApplication *application;
127        int name_count;
128        NautilusWindow *new_window;
129        NautilusWindow *window;
130        GdkScreen      *screen;
131        gboolean new_windows_for_extras;
132        char *prompt;
133
134        g_assert (NAUTILUS_IS_LOCATION_BAR (widget));
135        g_assert (data != NULL);
136        g_assert (callback_data == NULL);
137
138        names = nautilus_icon_dnd_uri_list_extract_uris (data->data);
139
140        if (names == NULL) {
141                g_warning ("No D&D URI's");
142                gtk_drag_finish (context, FALSE, FALSE, time);
143                return;
144        }
145
146        window = nautilus_location_bar_get_window (widget);
147        new_windows_for_extras = FALSE;
148        /* Ask user if they really want to open multiple windows
149         * for multiple dropped URIs. This is likely to have been
150         * a mistake.
151         */
152        name_count = g_list_length (names);
153        if (name_count > 1) {
154                prompt = g_strdup_printf (_("Do you want to view these %d locations "
155                                          "in separate windows?"),
156                                          name_count);
157                /* eel_run_simple_dialog should really take in pairs
158                 * like gtk_dialog_new_with_buttons() does. */
159                new_windows_for_extras = eel_run_simple_dialog
160                        (GTK_WIDGET (window),
161                         TRUE,
162                         prompt,
163                         _("View in Multiple Windows?"),
164                         GTK_STOCK_OK, GTK_STOCK_CANCEL,
165                         NULL) == 0 /* GNOME_OK */;
166
167                g_free (prompt);
168               
169                if (!new_windows_for_extras) {
170                        gtk_drag_finish (context, FALSE, FALSE, time);
171                        return;
172                }
173        }
174
175        nautilus_navigation_bar_set_location (NAUTILUS_NAVIGATION_BAR (widget),
176                                              names->data);     
177        nautilus_navigation_bar_location_changed (NAUTILUS_NAVIGATION_BAR (widget));
178
179        if (new_windows_for_extras) {
180                application = window->application;
181                screen = gtk_window_get_screen (GTK_WINDOW (window));
182
183                for (node = names->next; node != NULL; node = node->next) {
184                        new_window = nautilus_application_create_window (application, screen);
185                        nautilus_window_go_to (new_window, node->data);
186                }
187        }
188
189        nautilus_icon_dnd_uri_list_free_strings (names);
190
191        gtk_drag_finish (context, TRUE, FALSE, time);
192}
193
194static void
195drag_data_get_callback (GtkWidget *widget,
196                        GdkDragContext *context,
197                        GtkSelectionData *selection_data,
198                        guint info,
199                        guint32 time,
200                        gpointer callback_data)
201{
202        NautilusNavigationBar *bar;
203        char *entry_text;
204
205        g_assert (selection_data != NULL);
206        bar = NAUTILUS_NAVIGATION_BAR (callback_data);
207
208        entry_text = nautilus_navigation_bar_get_location (bar);
209       
210        switch (info) {
211        case NAUTILUS_DND_URI_LIST:
212        case NAUTILUS_DND_TEXT_PLAIN:
213        case NAUTILUS_DND_URL:
214                gtk_selection_data_set (selection_data,
215                                        selection_data->target,
216                                        8, (guchar *) entry_text,
217                                        eel_strlen (entry_text));
218                break;
219        default:
220                g_assert_not_reached ();
221        }
222        g_free (entry_text);
223}
224
225/* routine that determines the usize for the label widget as larger
226   then the size of the largest string and then sets it to that so
227   that we don't have localization problems. see
228   gtk_label_finalize_lines in gtklabel.c (line 618) for the code that
229   we are imitating here. */
230
231static void
232style_set_handler (GtkWidget *widget, GtkStyle *previous_style)
233{
234        PangoLayout *layout;
235        int width, width2;
236
237        layout = gtk_label_get_layout (GTK_LABEL(widget));
238
239        layout = pango_layout_copy (layout);
240
241        pango_layout_set_text (layout, LOCATION_LABEL, -1);
242        pango_layout_get_pixel_size (layout, &width, NULL);
243       
244        pango_layout_set_text (layout, GO_TO_LABEL, -1);
245        pango_layout_get_pixel_size (layout, &width2, NULL);
246        width = MAX (width, width2);
247
248        width += 2 * GTK_MISC (widget)->xpad;
249
250        gtk_widget_set_size_request (widget, width, -1);
251}
252
253static gboolean
254have_broken_filenames (void)
255{
256        static gboolean initialized = FALSE;
257        static gboolean broken;
258 
259        if (initialized) {
260                return broken;
261        }
262
263        broken = g_getenv ("G_BROKEN_FILENAMES") != NULL;
264 
265        initialized = TRUE;
266 
267        return broken;
268}
269
270
271/* utility routine to determine the string to expand to.  If we don't have anything yet, accept
272   the whole string, otherwise accept the largest part common to both */
273
274static char *
275accumulate_name_utf8 (char *full_name, char *candidate_name)
276{
277        char *result_name, *str1, *str2;
278
279        if (!g_utf8_validate (candidate_name, -1, NULL)) {
280                return full_name;
281        }
282       
283        if (full_name == NULL) {
284                result_name = g_strdup (candidate_name);
285        } else {
286                result_name = full_name;
287                if (!eel_str_has_prefix (full_name, candidate_name)) {
288                        str1 = full_name;
289                        str2 = candidate_name;
290
291                        while ((g_utf8_get_char (str1) == g_utf8_get_char (str2))) {
292                                str1 = g_utf8_next_char (str1);
293                                str2 = g_utf8_next_char (str2);
294                        }
295                        *str1 = '\0';
296                }
297        }
298
299        return result_name;
300}
301
302static char *
303accumulate_name_locale (char *full_name, char *candidate_name)
304{
305        char *result_name, *str1, *str2;
306
307        if (full_name == NULL)
308                result_name = g_strdup (candidate_name);
309        else {
310                result_name = full_name;
311                if (!eel_str_has_prefix (full_name, candidate_name)) {
312                        str1 = full_name;
313                        str2 = candidate_name;
314
315                        while (*str1 == *str2) {
316                                str1++;
317                                str2++;
318                        }
319                        *str1 = '\0';
320                }
321        }
322
323        return result_name;
324}
325
326/* utility routine to load the file info list for the current directory, if necessary */
327static void
328get_file_info_list (NautilusLocationBar *bar, const char* dir_name)
329{
330        GnomeVFSResult result;
331
332        if (eel_strcmp (bar->details->current_directory, dir_name) != 0) {
333                g_free (bar->details->current_directory);
334                if (bar->details->file_info_list) {
335                        gnome_vfs_file_info_list_free (bar->details->file_info_list);   
336                        bar->details->file_info_list = NULL;           
337                }
338
339                bar->details->current_directory = g_strdup (dir_name);
340                result = gnome_vfs_directory_list_load (&bar->details->file_info_list, dir_name,
341                                                        GNOME_VFS_FILE_INFO_DEFAULT);
342                if (result != GNOME_VFS_OK) {
343                        if (bar->details->file_info_list) {
344                                gnome_vfs_file_info_list_free (bar->details->file_info_list);   
345                                bar->details->file_info_list = NULL;                   
346                        }
347                }
348        }
349}
350
351/* routine that performs the tab expansion using gnome-vfs.  Extract the directory name and
352   incomplete basename, then iterate through the directory trying to complete it.  If we
353   find something, add it to the entry */
354 
355static gboolean
356try_to_expand_path (gpointer callback_data)
357{
358        NautilusLocationBar *bar;
359
360        GnomeVFSFileInfo *current_file_info;
361        GList *element;
362        GnomeVFSURI *uri;
363        GtkEditable *editable;
364
365        char *base_name_uri_escaped;
366        char *base_name;
367        char *base_name_utf8;
368        char *user_location;
369        char *current_path;
370        char *dir_name;
371        char *expand_text;
372        char *expand_text_utf8;
373        char *expand_name;
374        char *insert_text;
375
376        int base_name_length;
377        int user_location_length;
378        int expand_text_length;
379        int pos;
380
381        bar = NAUTILUS_LOCATION_BAR (callback_data);
382        editable = GTK_EDITABLE (bar->details->entry);
383        user_location = gtk_editable_get_chars (editable, 0, -1);
384        bar->details->idle_id = 0;
385
386        /* if it's just '~' don't expand because slash shouldn't be appended */
387        if (eel_strcmp (user_location, "~") == 0) {
388                g_free (user_location);
389                return FALSE;
390        }
391
392        /* Trailing whitespace is OK here since the cursor is known to
393           be at the end of the text and therefor after the whitespace. */
394        current_path = eel_make_uri_from_input_with_trailing_ws (user_location);
395        if (!eel_istr_has_prefix (current_path, "file://")) {
396                g_free (user_location);
397                g_free (current_path);
398                return FALSE;
399        }
400
401        /* We already completed if we have a trailing '/' */
402        if (current_path[strlen (current_path) - 1] == GNOME_VFS_URI_PATH_CHR) {
403                g_free (user_location);
404                g_free (current_path);
405                return FALSE;
406        }
407
408        user_location_length = g_utf8_strlen (user_location, -1);
409
410        g_free (user_location);
411
412        uri = gnome_vfs_uri_new (current_path);
413
414        base_name_uri_escaped = gnome_vfs_uri_extract_short_name (uri);
415        if (base_name_uri_escaped == NULL) {
416                base_name = NULL;
417        } else {
418                base_name = gnome_vfs_unescape_string (base_name_uri_escaped, NULL);
419        }
420        g_free (base_name_uri_escaped);
421
422        if (base_name == NULL) {
423                gnome_vfs_uri_unref (uri);
424                g_free (current_path);
425                return FALSE;
426        }
427
428        dir_name = gnome_vfs_uri_extract_dirname (uri);
429
430        gnome_vfs_uri_unref (uri);
431        uri = NULL;
432
433        /* get file info for the directory, if it hasn't changed since last time */
434        get_file_info_list (bar, dir_name);
435        if (bar->details->file_info_list == NULL) {
436                g_free (dir_name);
437                g_free (base_name);
438                g_free (current_path);
439                return FALSE;
440        }
441
442        /* iterate through the directory, keeping the intersection of all the names that
443           have the current basename as a prefix. */
444        expand_text = NULL;
445        for (element = bar->details->file_info_list; element != NULL; element = element->next) {
446                current_file_info = element->data;
447                if (eel_str_has_prefix (current_file_info->name, base_name)) {
448                        if (current_file_info->type == GNOME_VFS_FILE_TYPE_DIRECTORY) {
449                                expand_name = g_strconcat (current_file_info->name, "/", NULL);
450                        } else {
451                                expand_name = g_strdup (current_file_info->name);
452                        }
453                        if (have_broken_filenames()) {
454                                expand_text = accumulate_name_locale (expand_text, expand_name);
455                        } else {
456                                expand_text = accumulate_name_utf8 (expand_text, expand_name);
457                        }
458                        g_free (expand_name);
459                }
460        }
461
462        if (have_broken_filenames ()) {
463                if (expand_text) {
464                        expand_text_utf8 = g_locale_to_utf8 (expand_text, -1, NULL, NULL, NULL);
465                        g_free (expand_text);
466                        expand_text = expand_text_utf8;
467                }
468               
469                base_name_utf8 = g_locale_to_utf8 (base_name, -1, NULL, NULL, NULL);
470                g_free (base_name);
471                base_name = base_name_utf8;
472        }
473
474        /* if we've got something, add it to the entry */
475        if (expand_text != NULL && base_name != NULL) {
476                expand_text_length = g_utf8_strlen (expand_text, -1);
477                base_name_length = g_utf8_strlen (base_name, -1);
478               
479                if (!eel_str_has_suffix (base_name, expand_text)
480                    && base_name_length < expand_text_length) {
481                        insert_text = g_utf8_offset_to_pointer (expand_text, base_name_length);
482                        pos = user_location_length;
483                        gtk_editable_insert_text (editable,
484                                                  insert_text,
485                                                  g_utf8_strlen (insert_text, -1),
486                                                  &pos);
487
488                        pos = user_location_length;
489                        gtk_editable_select_region (editable, pos, -1);
490                }
491        }
492        g_free (expand_text);
493
494        g_free (dir_name);
495        g_free (base_name);
496        g_free (current_path);
497
498        return FALSE;
499}
500
501/* Until we have a more elegant solution, this is how we figure out if
502 * the GtkEntry inserted characters, assuming that the return value is
503 * TRUE indicating that the GtkEntry consumed the key event for some
504 * reason. This is a clone of code from GtkEntry.
505 */
506static gboolean
507entry_would_have_inserted_characters (const GdkEventKey *event)
508{
509        switch (event->keyval) {
510        case GDK_BackSpace:
511        case GDK_Clear:
512        case GDK_Insert:
513        case GDK_Delete:
514        case GDK_Home:
515        case GDK_End:
516        case GDK_Left:
517        case GDK_Right:
518        case GDK_Return:
519                return FALSE;
520        default:
521                if (event->keyval >= 0x20 && event->keyval <= 0xFF) {
522                        if ((event->state & GDK_CONTROL_MASK) != 0) {
523                                return FALSE;
524                        }
525                        if ((event->state & GDK_MOD1_MASK) != 0) {
526                                return FALSE;
527                        }
528                }
529                return event->length > 0;
530        }
531}
532
533static int
534get_editable_number_of_chars (GtkEditable *editable)
535{
536        char *text;
537        int length;
538
539        text = gtk_editable_get_chars (editable, 0, -1);
540        length = g_utf8_strlen (text, -1);
541        g_free (text);
542        return length;
543}
544
545static void
546set_position_and_selection_to_end (GtkEditable *editable)
547{
548        int end;
549
550        end = get_editable_number_of_chars (editable);
551        gtk_editable_select_region (editable, end, end);
552        gtk_editable_set_position (editable, end);
553}
554
555static gboolean
556position_and_selection_are_at_end (GtkEditable *editable)
557{
558        int end;
559        int start_sel, end_sel;
560       
561        end = get_editable_number_of_chars (editable);
562        if (gtk_editable_get_selection_bounds (editable, &start_sel, &end_sel)) {
563                if (start_sel != end || end_sel != end) {
564                        return FALSE;
565                }
566        }
567        return gtk_editable_get_position (editable) == end;
568}
569
570static void
571editable_event_after_callback (GtkEntry *entry,
572                               GdkEvent *event,
573                               gpointer user_data)
574{
575        GtkEditable *editable;
576        GdkEventKey *keyevent;
577        NautilusLocationBar *bar;
578
579        if (event->type != GDK_KEY_PRESS) {
580                return;
581        }
582
583        editable = GTK_EDITABLE (entry);
584        keyevent = (GdkEventKey *)event;
585        bar = NAUTILUS_LOCATION_BAR (user_data);
586
587        /* After typing the right arrow key we move the selection to
588         * the end, if we have a valid selection - since this is most
589         * likely an auto-completion. We ignore shift / control since
590         * they can validly be used to extend the selection.
591         */
592        if ((keyevent->keyval == GDK_Right || keyevent->keyval == GDK_End) &&
593            !(keyevent->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) &&
594            gtk_editable_get_selection_bounds (editable, NULL, NULL)) {
595                set_position_and_selection_to_end (editable);
596        }
597
598        /* Only do expanding when we are typing at the end of the
599         * text. Do the expand at idle time to avoid slowing down
600         * typing when the directory is large. Only trigger the expand
601         * when we type a key that would have inserted characters.
602         */
603        if (position_and_selection_are_at_end (editable)) {
604                if (entry_would_have_inserted_characters (keyevent)) {
605                        if (bar->details->idle_id == 0) {
606                                bar->details->idle_id = gtk_idle_add (try_to_expand_path, bar);
607                        }
608                }
609        } else {
610                /* FIXME: Also might be good to do this when you click
611                 * to change the position or selection.
612                 */
613                if (bar->details->idle_id != 0) {
614                        gtk_idle_remove (bar->details->idle_id);
615                        bar->details->idle_id = 0;
616                }
617        }
618
619        nautilus_location_bar_update_label (bar);
620}
621
622static void
623real_activate (NautilusNavigationBar *navigation_bar)
624{
625        NautilusLocationBar *bar;
626
627        bar = NAUTILUS_LOCATION_BAR (navigation_bar);
628
629        /* Put the keyboard focus in the text field when switching to this mode,
630         * and select all text for easy overtyping
631         */
632        gtk_widget_grab_focus (GTK_WIDGET (bar->details->entry));
633        nautilus_entry_select_all (bar->details->entry);
634}
635
636static void
637finalize (GObject *object)
638{
639        NautilusLocationBar *bar;
640
641        bar = NAUTILUS_LOCATION_BAR (object);
642
643        g_free (bar->details);
644
645        EEL_CALL_PARENT (G_OBJECT_CLASS, finalize, (object));
646}
647
648static void
649destroy (GtkObject *object)
650{
651        NautilusLocationBar *bar;
652
653        bar = NAUTILUS_LOCATION_BAR (object);
654       
655        /* cancel the pending idle call, if any */
656        if (bar->details->idle_id != 0) {
657                gtk_idle_remove (bar->details->idle_id);
658                bar->details->idle_id = 0;
659        }
660       
661        if (bar->details->file_info_list) {
662                gnome_vfs_file_info_list_free (bar->details->file_info_list);   
663                bar->details->file_info_list = NULL;
664        }
665       
666        g_free (bar->details->current_directory);
667        bar->details->current_directory = NULL;
668       
669        g_free (bar->details->last_location);
670        bar->details->last_location = NULL;
671       
672        EEL_CALL_PARENT (GTK_OBJECT_CLASS, destroy, (object));
673}
674
675static void
676nautilus_location_bar_class_init (NautilusLocationBarClass *class)
677{
678        GObjectClass *gobject_class;
679        GtkObjectClass *object_class;
680        NautilusNavigationBarClass *navigation_bar_class;
681
682        gobject_class = G_OBJECT_CLASS (class);
683        gobject_class->finalize = finalize;
684       
685        object_class = GTK_OBJECT_CLASS (class);
686        object_class->destroy = destroy;
687       
688        navigation_bar_class = NAUTILUS_NAVIGATION_BAR_CLASS (class);
689
690        navigation_bar_class->activate = real_activate;
691        navigation_bar_class->get_location = nautilus_location_bar_get_location;
692        navigation_bar_class->set_location = nautilus_location_bar_set_location;
693}
694
695static void
696nautilus_location_bar_init (NautilusLocationBar *bar)
697{
698        GtkWidget *label;
699        GtkWidget *entry;
700        GtkWidget *event_box;
701        GtkWidget *hbox;
702
703        bar->details = g_new0 (NautilusLocationBarDetails, 1);
704
705        hbox = gtk_hbox_new (0, FALSE);
706
707        event_box = gtk_event_box_new ();
708        gtk_container_set_border_width (GTK_CONTAINER (event_box),
709                                        GNOME_PAD_SMALL);
710        label = gtk_label_new (LOCATION_LABEL);
711        gtk_container_add   (GTK_CONTAINER (event_box), label);
712        gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_RIGHT);
713        gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);
714        g_signal_connect (label, "style_set",
715                          G_CALLBACK (style_set_handler), NULL);
716
717        gtk_box_pack_start (GTK_BOX (hbox), event_box, FALSE, TRUE,
718                            GNOME_PAD_SMALL);
719
720        entry = nautilus_entry_new ();
721
722        nautilus_entry_set_special_tab_handling (NAUTILUS_ENTRY (entry), TRUE);
723       
724        g_signal_connect_object (entry, "activate",
725                                 G_CALLBACK (nautilus_navigation_bar_location_changed),
726                                 bar, G_CONNECT_SWAPPED);
727        g_signal_connect_object (entry, "event_after",
728                                 G_CALLBACK (editable_event_after_callback), bar, 0);
729
730        gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
731
732        eel_accessibility_set_up_label_widget_relation (label, entry);
733
734        gtk_container_add (GTK_CONTAINER (bar), hbox);
735
736        /* Drag source */
737        gtk_drag_source_set (GTK_WIDGET (event_box),
738                             GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
739                             drag_types, G_N_ELEMENTS (drag_types),
740                             GDK_ACTION_LINK);
741        g_signal_connect_object (event_box, "drag_data_get",
742                                 G_CALLBACK (drag_data_get_callback), bar, 0);
743
744        /* Drag dest. */
745        gtk_drag_dest_set (GTK_WIDGET (bar),
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 (bar, "drag_data_received",
750                          G_CALLBACK (drag_data_received_callback), NULL);
751
752        gtk_widget_show_all (hbox);
753
754        bar->details->label = GTK_LABEL (label);
755        bar->details->entry = NAUTILUS_ENTRY (entry);   
756}
757
758GtkWidget *
759nautilus_location_bar_new (NautilusWindow *window)
760{
761        GtkWidget *bar;
762        NautilusLocationBar *location_bar;
763
764        bar = gtk_widget_new (NAUTILUS_TYPE_LOCATION_BAR, NULL);
765        location_bar = NAUTILUS_LOCATION_BAR (bar);
766
767        /* Clipboard */
768        nautilus_clipboard_set_up_editable
769                (GTK_EDITABLE (location_bar->details->entry),
770                 nautilus_window_get_ui_container (window),
771                 TRUE);
772
773        return bar;
774}
775
776static void
777nautilus_location_bar_set_location (NautilusNavigationBar *navigation_bar,
778                                    const char *location)
779{
780        NautilusLocationBar *bar;
781        char *formatted_location;
782
783        g_assert (location != NULL);
784       
785        bar = NAUTILUS_LOCATION_BAR (navigation_bar);
786
787        /* Note: This is called in reaction to external changes, and
788         * thus should not emit the LOCATION_CHANGED signal. */
789       
790        formatted_location = eel_format_uri_for_display (location);
791        nautilus_entry_set_text (NAUTILUS_ENTRY (bar->details->entry),
792                                 formatted_location);
793        set_position_and_selection_to_end (GTK_EDITABLE (bar->details->entry));
794        g_free (formatted_location);
795
796        /* free up the cached file info from the previous location */
797        g_free (bar->details->current_directory);
798        bar->details->current_directory = NULL;
799       
800        gnome_vfs_file_info_list_free (bar->details->file_info_list);   
801        bar->details->file_info_list = NULL;                   
802       
803        /* remember the original location for later comparison */
804       
805        g_free (bar->details->last_location);
806        bar->details->last_location = g_strdup (location);
807        nautilus_location_bar_update_label (bar);
808}
809
810/**
811 * nautilus_location_bar_get_location
812 *
813 * Get the "URI" represented by the text in the location bar.
814 *
815 * @bar: A NautilusLocationBar.
816 *
817 * returns a newly allocated "string" containing the mangled
818 * (by eel_make_uri_from_input) text that the user typed in...maybe a URI
819 * but not guaranteed.
820 *
821 **/
822static char *
823nautilus_location_bar_get_location (NautilusNavigationBar *navigation_bar)
824{
825        NautilusLocationBar *bar;
826        char *user_location, *best_uri;
827
828        bar = NAUTILUS_LOCATION_BAR (navigation_bar);
829       
830        user_location = gtk_editable_get_chars (GTK_EDITABLE (bar->details->entry), 0, -1);
831        best_uri = eel_make_uri_from_input (user_location);
832        g_free (user_location);
833        return best_uri;
834}
835               
836/**
837 * nautilus_location_bar_update_label
838 *
839 * if the text in the entry matches the uri, set the label to "location", otherwise use "goto"
840 *
841 **/
842static void
843nautilus_location_bar_update_label (NautilusLocationBar *bar)
844{
845        const char *current_text;
846        char *current_location;
847       
848        current_text = gtk_entry_get_text (GTK_ENTRY (bar->details->entry));
849        current_location = eel_make_uri_from_input (current_text);
850       
851        if (eel_uris_match (bar->details->last_location, current_location)) {
852                gtk_label_set_text (GTK_LABEL (bar->details->label), LOCATION_LABEL);
853        } else {                 
854                gtk_label_set_text (GTK_LABEL (bar->details->label), GO_TO_LABEL);
855        }
856
857        g_free (current_location);
858}
Note: See TracBrowser for help on using the repository browser.