1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright © 2002 Ricardo Fernández Pascual
4 * Copyright © 2003, 2004 Marco Pesenti Gritti
5 * Copyright © 2003, 2004, 2005 Christian Persch
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2, or (at your option)
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include "ephy-icon-entry.h"
27 #include "ephy-tree-model-node.h"
28 #include "ephy-location-entry.h"
29 #include "ephy-marshal.h"
30 #include "ephy-signal-accumulator.h"
32 #include "egg-editable-toolbar.h"
33 #include "ephy-stock-icons.h"
34 #include "ephy-debug.h"
37 #include <glib/gi18n.h>
38 #include <gdk/gdkkeysyms.h>
39 #include <gtk/gtktoolbar.h>
40 #include <gtk/gtkentry.h>
41 #include <gtk/gtkwindow.h>
42 #include <gtk/gtkcellrenderertext.h>
43 #include <gtk/gtkcellrendererpixbuf.h>
44 #include <gtk/gtkcelllayout.h>
45 #include <gtk/gtktreemodelsort.h>
46 #include <gtk/gtkstock.h>
47 #include <gtk/gtkimage.h>
48 #include <gtk/gtkeventbox.h>
49 #include <gtk/gtkbox.h>
50 #include <gtk/gtkhbox.h>
51 #include <gtk/gtkvbox.h>
52 #include <gtk/gtkimagemenuitem.h>
53 #include <gtk/gtkseparatormenuitem.h>
54 #include <gtk/gtkalignment.h>
55 #include <gtk/gtkclipboard.h>
56 #include <gtk/gtkversion.h>
60 #define EPHY_LOCATION_ENTRY_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_LOCATION_ENTRY, EphyLocationEntryPrivate))
62 struct _EphyLocationEntryPrivate
65 EphyIconEntry *icon_entry;
71 GdkColor secure_bg_colour;
72 GdkColor secure_fg_colour;
74 char *before_completion;
86 guint user_changed : 1;
87 guint original_address : 1;
89 guint apply_colours : 1;
90 guint needs_reset : 1;
100 { "http://www.", 11 },
101 { "http://ftp.", 11 },
103 { "https://www.", 12 },
111 static const GtkTargetEntry url_drag_types [] =
113 { EPHY_DND_URL_TYPE, 0, 0 },
114 { EPHY_DND_URI_LIST_TYPE, 0, 1 },
115 { EPHY_DND_TEXT_TYPE, 0, 2 }
118 static const GdkColor fallback_bg_colour = { 0, 0xf7f7, 0xf7f7, 0xbebe }; /* yellow-ish */
119 static const GdkColor fallback_fg_colour = { 0, 0, 0, 0 }; /* black */
121 static void ephy_location_entry_class_init (EphyLocationEntryClass *klass);
122 static void ephy_location_entry_init (EphyLocationEntry *le);
123 static gboolean ephy_location_entry_reset_internal (EphyLocationEntry *, gboolean);
125 static GObjectClass *parent_class = NULL;
135 static gint signals[LAST_SIGNAL] = { 0 };
137 #define MAX_LOC_HISTORY_ITEMS 10
138 #define EPHY_LOC_HISTORY_XML_ROOT "ephy_location_history"
139 #define EPHY_LOC_HISTORY_XML_VERSION "0.1"
142 ephy_location_entry_get_type (void)
144 static GType type = 0;
146 if (G_UNLIKELY (type == 0))
148 const GTypeInfo our_info =
150 sizeof (EphyLocationEntryClass),
153 (GClassInitFunc) ephy_location_entry_class_init,
156 sizeof (EphyLocationEntry),
158 (GInstanceInitFunc) ephy_location_entry_init
161 type = g_type_register_static (GTK_TYPE_TOOL_ITEM,
170 ephy_location_entry_set_tooltip (GtkToolItem *tool_item,
171 GtkTooltips *tooltips,
172 const char *tip_text,
173 const char *tip_private)
175 EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (tool_item);
176 EphyLocationEntryPrivate *priv = entry->priv;
178 gtk_tooltips_set_tip (tooltips, priv->icon_entry->entry,
179 tip_text, tip_private);
185 ephy_location_entry_style_set (GtkWidget *widget,
186 GtkStyle *previous_style)
188 EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (widget);
189 EphyLocationEntryPrivate *priv = entry->priv;
190 GtkSettings *settings;
191 GdkColor *bg_colour = NULL, *fg_colour = NULL;
193 gboolean is_a11y_theme;
195 if (GTK_WIDGET_CLASS (parent_class)->style_set)
197 GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
200 settings = gtk_settings_get_for_screen (gtk_widget_get_screen (widget));
201 g_object_get (settings, "gtk-theme-name", &theme, NULL);
202 is_a11y_theme = strstr (theme, "HighContrast") != NULL ||
203 strstr (theme, "LowContrast") != NULL;
206 gtk_widget_style_get (widget,
207 "secure-fg-color", &fg_colour,
208 "secure-bg-color", &bg_colour,
211 /* We only use the fallback colours when we don't have an a11y theme */
212 priv->apply_colours = !is_a11y_theme || (fg_colour != NULL && bg_colour != NULL);
214 if (fg_colour != NULL)
216 priv->secure_fg_colour = *fg_colour;
217 gdk_color_free (fg_colour);
221 priv->secure_fg_colour = fallback_fg_colour;
224 if (bg_colour != NULL)
226 priv->secure_bg_colour = *bg_colour;
227 gdk_color_free (bg_colour);
231 priv->secure_bg_colour = fallback_bg_colour;
234 /* Apply the new style */
235 ephy_location_entry_set_secure (entry, priv->secure);
239 ephy_location_entry_finalize (GObject *object)
241 EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (object);
242 EphyLocationEntryPrivate *priv = entry->priv;
244 if (priv->favicon != NULL)
246 g_object_unref (priv->favicon);
249 g_object_unref (priv->tips);
251 parent_class->finalize (object);
255 ephy_location_entry_class_init (EphyLocationEntryClass *klass)
257 GObjectClass *object_class = G_OBJECT_CLASS (klass);
258 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
259 GtkToolItemClass *tool_item_class = GTK_TOOL_ITEM_CLASS (klass);
261 parent_class = g_type_class_peek_parent (klass);
263 object_class->finalize = ephy_location_entry_finalize;
265 widget_class->style_set = ephy_location_entry_style_set;
267 tool_item_class->set_tooltip = ephy_location_entry_set_tooltip;
269 signals[USER_CHANGED] = g_signal_new (
270 "user_changed", G_OBJECT_CLASS_TYPE (klass),
271 G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
272 G_STRUCT_OFFSET (EphyLocationEntryClass, user_changed),
274 g_cclosure_marshal_VOID__VOID,
279 signals[LOCK_CLICKED] = g_signal_new (
281 EPHY_TYPE_LOCATION_ENTRY,
282 G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
283 G_STRUCT_OFFSET (EphyLocationEntryClass, lock_clicked),
285 g_cclosure_marshal_VOID__VOID,
289 signals[GET_LOCATION] = g_signal_new (
290 "get-location", G_OBJECT_CLASS_TYPE (klass),
291 G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
292 G_STRUCT_OFFSET (EphyLocationEntryClass, get_location),
293 ephy_signal_accumulator_string, NULL,
294 ephy_marshal_STRING__VOID,
299 signals[GET_TITLE] = g_signal_new (
300 "get-title", G_OBJECT_CLASS_TYPE (klass),
301 G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST,
302 G_STRUCT_OFFSET (EphyLocationEntryClass, get_title),
303 ephy_signal_accumulator_string, NULL,
304 ephy_marshal_STRING__VOID,
309 gtk_widget_class_install_style_property (widget_class,
310 g_param_spec_boxed ("secure-bg-color",
311 "Secure background colour",
312 "Background colour to use for secure sites",
314 G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
316 gtk_widget_class_install_style_property (widget_class,
317 g_param_spec_boxed ("secure-fg-color",
318 "Secure foreground Colour",
319 "Foreground colour to use for secure sites",
321 G_PARAM_READABLE | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
323 g_type_class_add_private (object_class, sizeof (EphyLocationEntryPrivate));
327 update_address_state (EphyLocationEntry *entry)
329 EphyLocationEntryPrivate *priv = entry->priv;
332 text = gtk_entry_get_text (GTK_ENTRY (priv->icon_entry->entry));
333 priv->original_address = text != NULL &&
334 g_str_hash (text) == priv->hash;
338 update_favicon (EphyLocationEntry *entry)
340 EphyLocationEntryPrivate *priv = entry->priv;
341 GtkImage *image = GTK_IMAGE (priv->icon);
343 /* Only show the favicon if the entry's text is the
344 * address of the current page.
346 if (priv->favicon != NULL && priv->original_address)
348 gtk_image_set_from_pixbuf (image, priv->favicon);
352 /* Here we could consider using fallback favicon that matches
353 * the page MIME type, though text/html should be good enough
354 * most of the time. See #337140
356 gtk_image_set_from_icon_name (image,
363 editable_changed_cb (GtkEditable *editable,
364 EphyLocationEntry *entry)
366 EphyLocationEntryPrivate *priv = entry->priv;
368 update_address_state (entry);
370 if (priv->user_changed == FALSE) return;
372 update_favicon (entry);
374 g_signal_emit (entry, signals[USER_CHANGED], 0);
378 entry_button_press_cb (GtkWidget *entry, GdkEventButton *event, EphyLocationEntry *le)
380 if (event->button == 1 && event->type == GDK_2BUTTON_PRESS)
382 ephy_location_entry_activate (le);
390 entry_key_press_cb (GtkEntry *entry,
392 EphyLocationEntry *lentry)
394 guint state = event->state & gtk_accelerator_get_default_mod_mask ();
396 if (event->keyval == GDK_Escape && state == 0)
398 ephy_location_entry_reset_internal (lentry, TRUE);
399 /* don't return TRUE since we want to cancel the autocompletion popup too */
406 entry_key_press_after_cb (GtkEntry *entry,
408 EphyLocationEntry *lentry)
410 EphyLocationEntryPrivate *priv = lentry->priv;
412 guint state = event->state & gtk_accelerator_get_default_mod_mask ();
414 if ((event->keyval == GDK_Return ||
415 event->keyval == GDK_KP_Enter ||
416 event->keyval == GDK_ISO_Enter) &&
417 (state == GDK_CONTROL_MASK ||
418 state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK)))
420 gtk_im_context_reset (entry->im_context);
422 priv->needs_reset = TRUE;
423 g_signal_emit_by_name (entry, "activate");
432 entry_activate_after_cb (GtkEntry *entry,
433 EphyLocationEntry *lentry)
435 EphyLocationEntryPrivate *priv = lentry->priv;
437 if (priv->needs_reset)
439 ephy_location_entry_reset_internal (lentry, TRUE);
440 priv->needs_reset = FALSE;
445 keyword_match (const char *list,
452 keyword_len = strlen (keyword);
458 for (i = 0; i < keyword_len; i++)
460 if (p[i] != keyword[i])
470 while (*p && !g_ascii_ispunct(*p) && !g_ascii_isspace(*p)) p++;
471 while (*p && (g_ascii_ispunct(*p) || g_ascii_isspace(*p))) p++;
478 completion_func (GtkEntryCompletion *completion,
483 int i, len_key, len_prefix;
485 char *keywords = NULL;
486 gboolean ret = FALSE;
487 EphyLocationEntry *le = EPHY_LOCATION_ENTRY (data);
490 model = gtk_entry_completion_get_model (completion);
492 gtk_tree_model_get (model, iter,
493 le->priv->text_col, &item,
494 le->priv->keywords_col, &keywords,
497 len_key = strlen (key);
498 if (!strncasecmp (key, item, len_key))
502 else if (keyword_match (keywords, key))
508 for (i = 0; i < G_N_ELEMENTS (web_prefixes); i++)
510 len_prefix = web_prefixes[i].len;
511 if (!strncmp (web_prefixes[i].prefix, item, len_prefix) &&
512 !strncasecmp (key, item + len_prefix, len_key))
527 match_selected_cb (GtkEntryCompletion *completion,
530 EphyLocationEntry *entry)
532 EphyLocationEntryPrivate *priv = entry->priv;
536 gtk_tree_model_get (model, iter,
537 priv->action_col, &item, -1);
538 if (item == NULL) return FALSE;
540 ephy_gui_get_current_event (NULL, &state, NULL);
542 priv->needs_reset = (state == GDK_CONTROL_MASK ||
543 state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK));
545 ephy_location_entry_set_location (entry, item, NULL);
546 gtk_im_context_reset (GTK_ENTRY (entry->priv->icon_entry->entry)->im_context);
547 g_signal_emit_by_name (priv->icon_entry->entry, "activate");
555 action_activated_after_cb (GtkEntryCompletion *completion,
557 EphyLocationEntry *lentry)
561 ephy_gui_get_current_event (NULL, &state, &button);
562 if ((state == GDK_CONTROL_MASK ||
563 state == (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) ||
566 ephy_location_entry_reset_internal (lentry, TRUE);
571 toolbar_is_editable (GtkWidget *widget)
575 etoolbar = gtk_widget_get_ancestor (widget, EGG_TYPE_EDITABLE_TOOLBAR);
579 return egg_editable_toolbar_get_edit_mode
580 (EGG_EDITABLE_TOOLBAR (etoolbar));
587 entry_drag_motion_cb (GtkWidget *widget,
588 GdkDragContext *context,
593 if (toolbar_is_editable (widget))
595 g_signal_stop_emission_by_name (widget, "drag_motion");
602 entry_drag_drop_cb (GtkWidget *widget,
603 GdkDragContext *context,
608 if (toolbar_is_editable (widget))
610 g_signal_stop_emission_by_name (widget, "drag_drop");
617 entry_clear_activate_cb (GtkMenuItem *item,
618 EphyLocationEntry *entry)
620 EphyLocationEntryPrivate *priv = entry->priv;
622 priv->user_changed = FALSE;
623 gtk_entry_set_text (GTK_ENTRY (priv->icon_entry->entry), "");
624 priv->user_changed = TRUE;
628 entry_populate_popup_cb (GtkEntry *entry,
630 EphyLocationEntry *lentry)
632 EphyLocationEntryPrivate *priv = lentry->priv;
635 GList *children, *item;
636 int pos = 0, sep = 0;
637 gboolean is_editable;
639 /* Clear and Copy mnemonics conflict, make custom menuitem */
640 image = gtk_image_new_from_stock (GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
641 gtk_widget_show (image);
643 /* Translators: the mnemonic shouldn't conflict with any of the
644 * standard items in the GtkEntry context menu (Cut, Copy, Paste, Delete,
645 * Select All, Input Methods and Insert Unicode control character.)
647 menuitem = gtk_image_menu_item_new_with_mnemonic (_("Cl_ear"));
648 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM(menuitem), image);
649 g_signal_connect (menuitem , "activate",
650 G_CALLBACK (entry_clear_activate_cb), lentry);
651 is_editable = gtk_editable_get_editable (GTK_EDITABLE (priv->icon_entry->entry));
652 gtk_widget_set_sensitive (menuitem, is_editable);
653 gtk_widget_show (menuitem);
655 /* search for the 2nd separator (the one after Select All) in the context
656 * menu, and insert this menu item before it.
657 * It's a bit of a hack, but there seems to be no better way to do it :/
659 children = GTK_MENU_SHELL (menu)->children;
660 for (item = children; item != NULL && sep < 2; item = item->next, pos++)
662 if (GTK_IS_SEPARATOR_MENU_ITEM (item->data)) sep++;
665 gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem, pos - 1);
669 each_url_get_data_binder (EphyDragEachSelectedItemDataGet iteratee,
670 gpointer iterator_context,
671 gpointer return_data)
673 EphyLocationEntry *entry = EPHY_LOCATION_ENTRY (iterator_context);
674 char *title = NULL, *address = NULL;
676 g_signal_emit (entry, signals[GET_LOCATION], 0, &address);
677 g_signal_emit (entry, signals[GET_TITLE], 0, &title);
678 g_return_if_fail (address != NULL && title != NULL);
680 iteratee (address, title, return_data);
686 #define DRAG_ICON_LAYOUT_BORDER 2
687 #define DRAG_ICON_ICON_SPACING DRAG_ICON_LAYOUT_BORDER * 2
688 #define DRAG_ICON_MAX_WIDTH_CHARS 32
691 favicon_create_drag_pixmap (EphyLocationEntry *entry,
694 EphyLocationEntryPrivate *priv = entry->priv;
695 char *title = NULL, *address = NULL;
697 GdkDrawable *drawable;
698 PangoContext *context;
700 PangoFontMetrics *metrics;
701 int pixmap_height, pixmap_width;
702 int layout_width, layout_height;
703 int icon_width = 0, icon_height = 0, offset_x = 0;
706 g_signal_emit (entry, signals[GET_LOCATION], 0, &address);
707 g_signal_emit (entry, signals[GET_TITLE], 0, &title);
708 if (address == NULL || title == NULL) return NULL;
711 title = g_strstrip (title);
713 text = g_string_sized_new (strlen (address) + strlen (title) + 2);
714 if (title[0] != '\0')
716 g_string_append (text, title);
717 g_string_append (text, "\n");
720 if (address[0] != '\0')
722 g_string_append (text, address);
725 /* Now build the pixmap */
727 if (priv->favicon != NULL)
729 icon_width = gdk_pixbuf_get_width (priv->favicon);
730 icon_height = gdk_pixbuf_get_height (priv->favicon);
733 context = gtk_widget_get_pango_context (widget);
734 layout = pango_layout_new (context);
736 context = gtk_widget_get_pango_context (widget);
737 metrics = pango_context_get_metrics (context,
738 widget->style->font_desc,
739 pango_context_get_language (context));
741 char_width = pango_font_metrics_get_approximate_digit_width (metrics);
742 pango_font_metrics_unref (metrics);
744 pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
745 pango_layout_set_width (layout, char_width * DRAG_ICON_MAX_WIDTH_CHARS);
746 pango_layout_set_text (layout, text->str, text->len);
748 pango_layout_get_size (layout, &layout_width, &layout_height);
750 pixmap_width = layout_width / PANGO_SCALE + DRAG_ICON_LAYOUT_BORDER * 2;
752 if (priv->favicon != NULL)
754 offset_x = icon_width + 2 * DRAG_ICON_ICON_SPACING;
755 pixmap_width += offset_x;
756 pixmap_height = MAX (layout_height / PANGO_SCALE, icon_height) + DRAG_ICON_LAYOUT_BORDER * 2;
760 pixmap_height = layout_height / PANGO_SCALE + DRAG_ICON_LAYOUT_BORDER * 2;
763 drawable = gdk_pixmap_new (widget->window,
768 gdk_draw_rectangle (drawable,
769 widget->style->base_gc [GTK_WIDGET_STATE (widget)],
776 if (priv->favicon != NULL)
778 gdk_draw_pixbuf (drawable,
779 widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
782 1 + DRAG_ICON_LAYOUT_BORDER + DRAG_ICON_ICON_SPACING,
783 1 + DRAG_ICON_LAYOUT_BORDER + (pixmap_height - icon_height) / 2,
785 GDK_RGB_DITHER_NONE, 0, 0);
788 gdk_draw_layout (drawable,
789 widget->style->text_gc [GTK_WIDGET_STATE (widget)],
790 1 + DRAG_ICON_LAYOUT_BORDER + offset_x,
791 1 + DRAG_ICON_LAYOUT_BORDER,
794 gdk_draw_rectangle (drawable,
795 widget->style->black_gc,
801 g_object_unref (layout);
805 g_string_free (text,TRUE);
811 favicon_drag_begin_cb (GtkWidget *widget,
812 GdkDragContext *context,
813 EphyLocationEntry *entry)
817 pixmap = favicon_create_drag_pixmap (entry, widget);
821 gtk_drag_set_icon_pixmap (context,
822 gdk_drawable_get_colormap (pixmap),
823 pixmap, NULL, -2, -2);
824 g_object_unref (pixmap);
829 favicon_drag_data_get_cb (GtkWidget *widget,
830 GdkDragContext *context,
831 GtkSelectionData *selection_data,
834 EphyLocationEntry *entry)
836 g_assert (widget != NULL);
837 g_return_if_fail (context != NULL);
839 ephy_dnd_drag_data_get (widget, context, selection_data,
840 time, entry, each_url_get_data_binder);
844 icon_button_press_event_cb (GtkWidget *ebox,
845 GdkEventButton *event,
846 EphyLocationEntry *entry)
848 guint state = event->state & gtk_accelerator_get_default_mod_mask ();
850 if (event->type == GDK_BUTTON_PRESS &&
851 event->button == 1 &&
852 state == 0 /* left */)
855 EphyLocationEntryPrivate *priv = entry->priv;
857 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (entry));
858 gtk_window_set_focus (GTK_WINDOW(toplevel),
859 priv->icon_entry->entry);
861 gtk_editable_select_region (GTK_EDITABLE (priv->icon_entry->entry),
871 lock_button_press_event_cb (GtkWidget *ebox,
872 GdkEventButton *event,
873 EphyLocationEntry *entry)
875 if (event->type == GDK_BUTTON_PRESS && event->button == 1 /* left */)
877 g_signal_emit (entry, signals[LOCK_CLICKED], 0);
886 ephy_location_entry_construct_contents (EphyLocationEntry *entry)
888 EphyLocationEntryPrivate *priv = entry->priv;
889 GtkWidget *alignment;
891 LOG ("EphyLocationEntry constructing contents %p", entry);
893 alignment = gtk_alignment_new (0.0, 0.5, 1.0, 0.0);
894 gtk_alignment_set_padding (GTK_ALIGNMENT (alignment), 0, 0, 1, 1);
895 gtk_container_add (GTK_CONTAINER (entry), alignment);
897 priv->icon_entry = EPHY_ICON_ENTRY (ephy_icon_entry_new ());
898 gtk_container_add (GTK_CONTAINER (alignment),
899 GTK_WIDGET (priv->icon_entry));
901 priv->icon_ebox = gtk_event_box_new ();
902 gtk_container_set_border_width (GTK_CONTAINER (priv->icon_ebox), 2);
903 gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->icon_ebox), FALSE);
904 gtk_widget_add_events (priv->icon_ebox, GDK_BUTTON_PRESS_MASK |
905 GDK_BUTTON_RELEASE_MASK |
906 GDK_POINTER_MOTION_HINT_MASK);
907 gtk_drag_source_set (priv->icon_ebox, GDK_BUTTON1_MASK,
908 url_drag_types, G_N_ELEMENTS (url_drag_types),
909 GDK_ACTION_ASK | GDK_ACTION_COPY | GDK_ACTION_LINK);
910 g_signal_connect (priv->icon_ebox, "button-press-event",
911 G_CALLBACK (icon_button_press_event_cb), entry);
913 gtk_tooltips_set_tip (priv->tips, priv->icon_ebox,
914 _("Drag and drop this icon to create a link to this page"), NULL);
916 priv->icon = gtk_image_new ();
917 gtk_container_add (GTK_CONTAINER (priv->icon_ebox), priv->icon);
919 ephy_icon_entry_pack_widget (priv->icon_entry, priv->icon_ebox, TRUE);
921 priv->lock_ebox = gtk_event_box_new ();
922 gtk_container_set_border_width (GTK_CONTAINER (priv->lock_ebox), 2);
923 gtk_widget_add_events (priv->lock_ebox, GDK_BUTTON_PRESS_MASK);
924 gtk_event_box_set_visible_window (GTK_EVENT_BOX (priv->lock_ebox), FALSE);
926 priv->lock = gtk_image_new_from_stock (STOCK_LOCK_INSECURE,
928 gtk_container_add (GTK_CONTAINER (priv->lock_ebox), priv->lock);
930 ephy_icon_entry_pack_widget (priv->icon_entry, priv->lock_ebox, FALSE);
932 g_signal_connect (priv->icon_ebox, "drag-data-get",
933 G_CALLBACK (favicon_drag_data_get_cb), entry);
934 g_signal_connect (priv->icon_ebox, "drag-begin",
935 G_CALLBACK (favicon_drag_begin_cb), entry);
937 g_signal_connect (priv->lock_ebox, "button-press-event",
938 G_CALLBACK (lock_button_press_event_cb), entry);
940 g_signal_connect (priv->icon_entry->entry, "populate_popup",
941 G_CALLBACK (entry_populate_popup_cb), entry);
942 g_signal_connect (priv->icon_entry->entry, "key-press-event",
943 G_CALLBACK (entry_key_press_cb), entry);
944 g_signal_connect_after (priv->icon_entry->entry, "key-press-event",
945 G_CALLBACK (entry_key_press_after_cb), entry);
946 g_signal_connect_after (priv->icon_entry->entry, "activate",
947 G_CALLBACK (entry_activate_after_cb), entry);
948 g_signal_connect (priv->icon_entry->entry, "button-press-event",
949 G_CALLBACK (entry_button_press_cb), entry);
950 g_signal_connect (priv->icon_entry->entry, "changed",
951 G_CALLBACK (editable_changed_cb), entry);
952 g_signal_connect (priv->icon_entry->entry, "drag-motion",
953 G_CALLBACK (entry_drag_motion_cb), entry);
954 g_signal_connect (priv->icon_entry->entry, "drag-drop",
955 G_CALLBACK (entry_drag_drop_cb), entry);
957 gtk_widget_show_all (alignment);
961 ephy_location_entry_init (EphyLocationEntry *le)
963 EphyLocationEntryPrivate *p;
965 LOG ("EphyLocationEntry initialising %p", le);
967 p = EPHY_LOCATION_ENTRY_GET_PRIVATE (le);
970 p->user_changed = TRUE;
972 p->tips = gtk_tooltips_new ();
973 g_object_ref_sink (p->tips);
975 ephy_location_entry_construct_contents (le);
977 gtk_tool_item_set_expand (GTK_TOOL_ITEM (le), TRUE);
981 ephy_location_entry_new (void)
983 return GTK_WIDGET (g_object_new (EPHY_TYPE_LOCATION_ENTRY, NULL));
987 sort_func (GtkTreeModel *model,
993 EphyLocationEntry *le = EPHY_LOCATION_ENTRY (data);
995 gtk_tree_model_get (model, a,
996 le->priv->relevance_col, &valuea, -1);
997 gtk_tree_model_get (model, b,
998 le->priv->relevance_col, &valueb, -1);
1000 return valueb - valuea;
1003 #if GTK_CHECK_VERSION (2, 11, 0)
1005 cursor_on_match_cb (GtkEntryCompletion *completion,
1006 GtkTreeModel *model,
1008 EphyLocationEntry *le)
1013 gtk_tree_model_get (model, iter,
1017 entry = gtk_entry_completion_get_entry (completion);
1018 gtk_entry_set_text (GTK_ENTRY (entry), item);
1024 #endif /* GTK+ 2.11.0 */
1027 ephy_location_entry_set_completion (EphyLocationEntry *le,
1028 GtkTreeModel *model,
1032 guint relevance_col,
1037 EphyLocationEntryPrivate *priv = le->priv;
1038 GtkTreeModel *sort_model;
1039 GtkEntryCompletion *completion;
1040 GtkCellRenderer *cell, *extracell, *iconcell;
1042 le->priv->text_col = text_col;
1043 le->priv->action_col = action_col;
1044 le->priv->keywords_col = keywords_col;
1045 le->priv->relevance_col = relevance_col;
1046 le->priv->extra_col = extra_col;
1047 le->priv->favicon_col = favicon_col;
1048 le->priv->url_col = url_col;
1050 sort_model = gtk_tree_model_sort_new_with_model (model);
1051 g_object_unref (model);
1052 gtk_tree_sortable_set_default_sort_func
1053 (GTK_TREE_SORTABLE (sort_model),
1054 sort_func, le, NULL);
1056 completion = gtk_entry_completion_new ();
1057 gtk_entry_completion_set_model (completion, sort_model);
1058 g_object_unref (sort_model);
1059 gtk_entry_completion_set_match_func (completion, completion_func, le, NULL);
1060 g_signal_connect (completion, "match-selected",
1061 G_CALLBACK (match_selected_cb), le);
1062 g_signal_connect_after (completion, "action-activated",
1063 G_CALLBACK (action_activated_after_cb), le);
1065 iconcell = gtk_cell_renderer_pixbuf_new ();
1067 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion),
1069 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
1070 iconcell, "pixbuf", favicon_col);
1072 cell = gtk_cell_renderer_text_new ();
1074 "ellipsize", PANGO_ELLIPSIZE_END,
1075 "ellipsize-set", TRUE,
1077 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion),
1079 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
1080 cell, "text", text_col);
1082 extracell = gtk_cell_renderer_text_new ();
1083 g_object_set (extracell,
1084 "ellipsize", PANGO_ELLIPSIZE_END,
1085 "ellipsize-set", TRUE,
1086 "foreground", "Gray",
1087 "foreground-set", TRUE,
1088 "alignment", PANGO_ALIGN_RIGHT,
1091 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (completion),
1093 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (completion),
1094 extracell, "text", extra_col);
1096 #if GTK_CHECK_VERSION (2, 11, 0)
1097 g_object_set (completion, "inline-selection", TRUE, NULL);
1098 g_signal_connect (completion, "cursor-on-match",
1099 G_CALLBACK (cursor_on_match_cb), le);
1102 gtk_entry_set_completion (GTK_ENTRY (priv->icon_entry->entry), completion);
1103 g_object_unref (completion);
1107 ephy_location_entry_set_location (EphyLocationEntry *entry,
1108 const char *address,
1109 const char *typed_address)
1111 EphyLocationEntryPrivate *priv = entry->priv;
1112 GtkClipboard *clipboard;
1114 char* selection = NULL;
1117 g_return_if_fail (address != NULL);
1119 /* Setting a new text will clear the clipboard. This makes it impossible
1120 * to copy&paste from the location entry of one tab into another tab, see
1121 * bug #155824. So we save the selection iff the clipboard was owned by
1122 * the location entry.
1124 if (GTK_WIDGET_REALIZED (GTK_WIDGET (priv->icon_entry)))
1126 GtkWidget *gtkentry = priv->icon_entry->entry;
1128 clipboard = gtk_widget_get_clipboard (gtkentry,
1129 GDK_SELECTION_PRIMARY);
1130 g_return_if_fail (clipboard != NULL);
1132 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (gtkentry) &&
1133 gtk_editable_get_selection_bounds (GTK_EDITABLE (gtkentry),
1136 selection = gtk_editable_get_chars (GTK_EDITABLE (gtkentry),
1141 if (typed_address != NULL)
1143 text = typed_address;
1145 else if (address != NULL && strcmp (address, "about:blank") != 0)
1154 /* First record the new hash, then update the entry text */
1155 priv->hash = g_str_hash (address);
1157 priv->user_changed = FALSE;
1158 gtk_entry_set_text (GTK_ENTRY (priv->icon_entry->entry), text);
1159 priv->user_changed = TRUE;
1161 /* We need to call update_address_state() here, as the 'changed' signal
1162 * may not get called if the user has typed in the exact correct url */
1163 update_address_state (entry);
1164 update_favicon (entry);
1166 /* Now restore the selection.
1167 * Note that it's not owned by the entry anymore!
1169 if (selection != NULL)
1171 gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY),
1172 selection, strlen (selection));
1178 ephy_location_entry_get_location (EphyLocationEntry *entry)
1180 EphyLocationEntryPrivate *priv = entry->priv;
1182 return gtk_entry_get_text (GTK_ENTRY (priv->icon_entry->entry));
1186 ephy_location_entry_reset_internal (EphyLocationEntry *entry,
1189 EphyLocationEntryPrivate *priv = entry->priv;
1190 const char *text, *old_text;
1194 g_signal_emit (entry, signals[GET_LOCATION], 0, &url);
1195 text = url != NULL ? url : "";
1196 old_text = gtk_entry_get_text (GTK_ENTRY (priv->icon_entry->entry));
1197 old_text = old_text != NULL ? old_text : "";
1199 retval = g_str_hash (text) != g_str_hash (old_text);
1201 ephy_location_entry_set_location (entry, text, NULL);
1206 g_signal_emit (entry, signals[USER_CHANGED], 0);
1213 ephy_location_entry_reset (EphyLocationEntry *entry)
1215 return ephy_location_entry_reset_internal (entry, FALSE);
1219 ephy_location_entry_activate (EphyLocationEntry *entry)
1221 EphyLocationEntryPrivate *priv = entry->priv;
1222 GtkWidget *toplevel;
1224 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (entry));
1226 gtk_editable_select_region (GTK_EDITABLE(priv->icon_entry->entry),
1228 gtk_window_set_focus (GTK_WINDOW(toplevel),
1229 priv->icon_entry->entry);
1233 ephy_location_entry_get_entry (EphyLocationEntry *entry)
1235 EphyLocationEntryPrivate *priv = entry->priv;
1237 return priv->icon_entry->entry;
1241 ephy_location_entry_set_favicon (EphyLocationEntry *entry,
1244 EphyLocationEntryPrivate *priv = entry->priv;
1246 if (priv->favicon != NULL)
1248 g_object_unref (priv->favicon);
1251 priv->favicon = pixbuf ? g_object_ref (pixbuf) : NULL;
1253 update_favicon (entry);
1257 ephy_location_entry_set_secure (EphyLocationEntry *entry,
1260 EphyLocationEntryPrivate *priv = entry->priv;
1261 GtkWidget *widget = GTK_WIDGET (entry);
1262 GtkWidget *gentry = ephy_icon_entry_get_entry (entry->priv->icon_entry);
1264 priv->secure = secure;
1266 /* We have to set the colour of the GtkEntry in the EphyIconEntry */
1267 if (priv->secure && priv->apply_colours)
1269 gtk_widget_modify_text (gentry, GTK_STATE_NORMAL, &priv->secure_fg_colour);
1270 gtk_widget_modify_base (gentry, GTK_STATE_NORMAL, &priv->secure_bg_colour);
1274 gtk_widget_modify_text (gentry, GTK_STATE_NORMAL, NULL);
1275 gtk_widget_modify_base (gentry, GTK_STATE_NORMAL, NULL);
1278 gtk_widget_queue_draw (widget);
1282 ephy_location_entry_set_show_lock (EphyLocationEntry *entry,
1285 EphyLocationEntryPrivate *priv = entry->priv;
1287 g_object_set (priv->lock_ebox, "visible", show_lock, NULL);
1291 ephy_location_entry_set_lock_stock (EphyLocationEntry *entry,
1292 const char *stock_id)
1295 EphyLocationEntryPrivate *priv = entry->priv;
1297 gtk_image_set_from_stock (GTK_IMAGE (priv->lock), stock_id,
1298 GTK_ICON_SIZE_MENU);
1302 ephy_location_entry_set_lock_tooltip (EphyLocationEntry *entry,
1303 const char *tooltip)
1305 EphyLocationEntryPrivate *priv = entry->priv;
1307 gtk_tooltips_set_tip (priv->tips, priv->lock_ebox, tooltip, NULL);