1 /* GTK - The GTK+ Toolkit
2 * gtklinkbutton.c - an hyperlink-enabled button
4 * Copyright (C) 2006 Emmanuele Bassi <ebassi@gmail.com>
7 * Based on gnome-href code by:
8 * James Henstridge <james@daa.com.au>
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA.
29 #include "gtkclipboard.h"
31 #include "gtkimagemenuitem.h"
35 #include "gtkmenuitem.h"
37 #include "gtktooltip.h"
39 #include "gtklinkbutton.h"
45 struct _GtkLinkButtonPrivate
51 GtkWidget *popup_menu;
61 #define GTK_LINK_BUTTON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_LINK_BUTTON, GtkLinkButtonPrivate))
63 static void gtk_link_button_finalize (GObject *object);
64 static void gtk_link_button_get_property (GObject *object,
68 static void gtk_link_button_set_property (GObject *object,
72 static void gtk_link_button_add (GtkContainer *container,
74 static gboolean gtk_link_button_button_press (GtkWidget *widget,
75 GdkEventButton *event);
76 static void gtk_link_button_clicked (GtkButton *button);
77 static gboolean gtk_link_button_popup_menu (GtkWidget *widget);
78 static void gtk_link_button_style_set (GtkWidget *widget,
80 static gboolean gtk_link_button_enter_cb (GtkWidget *widget,
81 GdkEventCrossing *event,
83 static gboolean gtk_link_button_leave_cb (GtkWidget *widget,
84 GdkEventCrossing *event,
86 static void gtk_link_button_drag_data_get_cb (GtkWidget *widget,
87 GdkDragContext *context,
88 GtkSelectionData *selection,
92 static gboolean gtk_link_button_query_tooltip_cb (GtkWidget *widget,
95 gboolean keyboard_tip,
100 static const GtkTargetEntry link_drop_types[] = {
101 { "text/uri-list", 0, 0 },
102 { "_NETSCAPE_URL", 0, 0 }
105 static const GdkColor default_link_color = { 0, 0, 0, 0xeeee };
106 static const GdkColor default_visited_link_color = { 0, 0x5555, 0x1a1a, 0x8b8b };
108 static GtkLinkButtonUriFunc uri_func = NULL;
109 static gpointer uri_func_data = NULL;
110 static GDestroyNotify uri_func_destroy = NULL;
112 G_DEFINE_TYPE (GtkLinkButton, gtk_link_button, GTK_TYPE_BUTTON)
115 gtk_link_button_class_init (GtkLinkButtonClass *klass)
117 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
118 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
119 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
120 GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass);
122 gobject_class->set_property = gtk_link_button_set_property;
123 gobject_class->get_property = gtk_link_button_get_property;
124 gobject_class->finalize = gtk_link_button_finalize;
126 widget_class->button_press_event = gtk_link_button_button_press;
127 widget_class->popup_menu = gtk_link_button_popup_menu;
128 widget_class->style_set = gtk_link_button_style_set;
130 container_class->add = gtk_link_button_add;
132 button_class->clicked = gtk_link_button_clicked;
137 * The URI bound to this button.
141 g_object_class_install_property (gobject_class,
143 g_param_spec_string ("uri",
145 _("The URI bound to this button"),
149 g_type_class_add_private (gobject_class, sizeof (GtkLinkButtonPrivate));
153 gtk_link_button_init (GtkLinkButton *link_button)
155 link_button->priv = GTK_LINK_BUTTON_GET_PRIVATE (link_button),
157 gtk_button_set_relief (GTK_BUTTON (link_button), GTK_RELIEF_NONE);
159 g_signal_connect (link_button, "enter_notify_event",
160 G_CALLBACK (gtk_link_button_enter_cb), NULL);
161 g_signal_connect (link_button, "leave_notify_event",
162 G_CALLBACK (gtk_link_button_leave_cb), NULL);
163 g_signal_connect (link_button, "drag_data_get",
164 G_CALLBACK (gtk_link_button_drag_data_get_cb), NULL);
165 g_object_set (link_button, "has-tooltip", TRUE, NULL);
166 g_signal_connect (link_button, "query-tooltip",
167 G_CALLBACK (gtk_link_button_query_tooltip_cb), NULL);
169 /* enable drag source */
170 gtk_drag_source_set (GTK_WIDGET (link_button),
172 link_drop_types, G_N_ELEMENTS (link_drop_types),
177 gtk_link_button_finalize (GObject *object)
179 GtkLinkButton *link_button = GTK_LINK_BUTTON (object);
181 g_free (link_button->priv->uri);
183 G_OBJECT_CLASS (gtk_link_button_parent_class)->finalize (object);
187 gtk_link_button_get_property (GObject *object,
192 GtkLinkButton *link_button = GTK_LINK_BUTTON (object);
197 g_value_set_string (value, link_button->priv->uri);
200 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
206 gtk_link_button_set_property (GObject *object,
211 GtkLinkButton *link_button = GTK_LINK_BUTTON (object);
216 gtk_link_button_set_uri (link_button, g_value_get_string (value));
219 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
225 set_link_color (GtkLinkButton *link_button)
227 GdkColor *link_color = NULL;
230 label = gtk_bin_get_child (GTK_BIN (link_button));
232 if (link_button->priv->visited)
234 gtk_widget_style_get (GTK_WIDGET (link_button),
235 "visited-link-color", &link_color, NULL);
237 link_color = (GdkColor *) &default_visited_link_color;
241 gtk_widget_style_get (GTK_WIDGET (link_button),
242 "link-color", &link_color, NULL);
244 link_color = (GdkColor *) &default_link_color;
247 gtk_widget_modify_fg (label, GTK_STATE_NORMAL, link_color);
248 gtk_widget_modify_fg (label, GTK_STATE_ACTIVE, link_color);
249 gtk_widget_modify_fg (label, GTK_STATE_PRELIGHT, link_color);
250 gtk_widget_modify_fg (label, GTK_STATE_SELECTED, link_color);
252 if (link_color != &default_link_color &&
253 link_color != &default_visited_link_color)
254 gdk_color_free (link_color);
258 set_link_underline (GtkLinkButton *link_button)
262 label = gtk_bin_get_child (GTK_BIN (link_button));
263 if (GTK_IS_LABEL (label))
265 PangoAttrList *attributes;
266 PangoAttribute *uline;
268 uline = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE);
269 uline->start_index = 0;
270 uline->end_index = G_MAXUINT;
271 attributes = pango_attr_list_new ();
272 pango_attr_list_insert (attributes, uline);
273 gtk_label_set_attributes (GTK_LABEL (label), attributes);
274 pango_attr_list_unref (attributes);
279 gtk_link_button_add (GtkContainer *container,
282 GTK_CONTAINER_CLASS (gtk_link_button_parent_class)->add (container, widget);
284 set_link_underline (GTK_LINK_BUTTON (container));
288 gtk_link_button_style_set (GtkWidget *widget,
291 GtkLinkButton *link_button = GTK_LINK_BUTTON (widget);
293 set_link_color (link_button);
297 set_hand_cursor (GtkWidget *widget,
303 display = gtk_widget_get_display (widget);
307 cursor = gdk_cursor_new_for_display (display, GDK_HAND2);
309 gdk_window_set_cursor (widget->window, cursor);
310 gdk_display_flush (display);
313 gdk_cursor_unref (cursor);
317 popup_menu_detach (GtkWidget *attach_widget,
320 GtkLinkButton *link_button = GTK_LINK_BUTTON (attach_widget);
322 link_button->priv->popup_menu = NULL;
326 popup_position_func (GtkMenu *menu,
332 GtkLinkButton *link_button = GTK_LINK_BUTTON (user_data);
333 GtkLinkButtonPrivate *priv = link_button->priv;
334 GtkWidget *widget = GTK_WIDGET (link_button);
335 GdkScreen *screen = gtk_widget_get_screen (widget);
338 GdkRectangle monitor;
340 g_return_if_fail (GTK_WIDGET_REALIZED (link_button));
342 gdk_window_get_origin (widget->window, x, y);
344 gtk_widget_size_request (priv->popup_menu, &req);
346 *x += widget->allocation.width / 2;
347 *y += widget->allocation.height;
349 monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y);
350 gtk_menu_set_monitor (menu, monitor_num);
351 gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
353 *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width));
354 *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height));
360 copy_activate_cb (GtkWidget *widget,
361 GtkLinkButton *link_button)
363 GtkLinkButtonPrivate *priv = link_button->priv;
365 gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (link_button),
366 GDK_SELECTION_CLIPBOARD),
371 gtk_link_button_do_popup (GtkLinkButton *link_button,
372 GdkEventButton *event)
374 GtkLinkButtonPrivate *priv = link_button->priv;
380 button = event->button;
386 time = gtk_get_current_event_time ();
389 if (GTK_WIDGET_REALIZED (link_button))
391 GtkWidget *menu_item;
393 if (priv->popup_menu)
394 gtk_widget_destroy (priv->popup_menu);
396 priv->popup_menu = gtk_menu_new ();
398 gtk_menu_attach_to_widget (GTK_MENU (priv->popup_menu),
399 GTK_WIDGET (link_button),
402 menu_item = gtk_image_menu_item_new_with_mnemonic (_("Copy URL"));
403 gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (menu_item),
404 gtk_image_new_from_stock (GTK_STOCK_COPY,
405 GTK_ICON_SIZE_MENU));
406 g_signal_connect (menu_item, "activate",
407 G_CALLBACK (copy_activate_cb), link_button);
408 gtk_widget_show (menu_item);
409 gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menu_item);
412 gtk_menu_popup (GTK_MENU (priv->popup_menu), NULL, NULL,
417 gtk_menu_popup (GTK_MENU (priv->popup_menu), NULL, NULL,
418 popup_position_func, link_button,
420 gtk_menu_shell_select_first (GTK_MENU_SHELL (priv->popup_menu), FALSE);
426 gtk_link_button_button_press (GtkWidget *widget,
427 GdkEventButton *event)
429 if (!GTK_WIDGET_HAS_FOCUS (widget))
430 gtk_widget_grab_focus (widget);
432 if ((event->button == 3) && (event->type == GDK_BUTTON_PRESS))
434 gtk_link_button_do_popup (GTK_LINK_BUTTON (widget), event);
439 if (GTK_WIDGET_CLASS (gtk_link_button_parent_class)->button_press_event)
440 return (* GTK_WIDGET_CLASS (gtk_link_button_parent_class)->button_press_event) (widget, event);
446 gtk_link_button_clicked (GtkButton *button)
448 GtkLinkButton *link_button = GTK_LINK_BUTTON (button);
451 (* uri_func) (link_button, link_button->priv->uri, uri_func_data);
453 link_button->priv->visited = TRUE;
455 set_link_color (link_button);
459 gtk_link_button_popup_menu (GtkWidget *widget)
461 gtk_link_button_do_popup (GTK_LINK_BUTTON (widget), NULL);
467 gtk_link_button_enter_cb (GtkWidget *widget,
468 GdkEventCrossing *crossing,
471 set_hand_cursor (widget, TRUE);
477 gtk_link_button_leave_cb (GtkWidget *widget,
478 GdkEventCrossing *crossing,
481 set_hand_cursor (widget, FALSE);
487 gtk_link_button_drag_data_get_cb (GtkWidget *widget,
488 GdkDragContext *context,
489 GtkSelectionData *selection,
494 GtkLinkButton *link_button = GTK_LINK_BUTTON (widget);
497 uri = g_strdup_printf ("%s\r\n", link_button->priv->uri);
498 gtk_selection_data_set (selection,
508 * gtk_link_button_new:
511 * Creates a new #GtkLinkButton with the URI as its text.
513 * Return value: a new link button widget.
518 gtk_link_button_new (const gchar *uri)
520 gchar *utf8_uri = NULL;
523 g_return_val_if_fail (uri != NULL, NULL);
525 if (g_utf8_validate (uri, -1, NULL))
527 utf8_uri = g_strdup (uri);
531 GError *conv_err = NULL;
533 utf8_uri = g_locale_to_utf8 (uri, -1, NULL, NULL, &conv_err);
536 g_warning ("Attempting to convert URI `%s' to UTF-8, but failed "
540 g_error_free (conv_err);
542 utf8_uri = g_strdup (_("Invalid URI"));
546 retval = g_object_new (GTK_TYPE_LINK_BUTTON,
557 * gtk_link_button_new_with_label:
559 * @label: the text of the button
561 * Creates a new #GtkLinkButton containing a label.
563 * Return value: a new link button widget.
568 gtk_link_button_new_with_label (const gchar *uri,
573 g_return_val_if_fail (uri != NULL, NULL);
576 return gtk_link_button_new (uri);
578 retval = g_object_new (GTK_TYPE_LINK_BUTTON,
587 gtk_link_button_query_tooltip_cb (GtkWidget *widget,
590 gboolean keyboard_tip,
594 GtkLinkButton *link_button = GTK_LINK_BUTTON (widget);
595 const gchar *label, *uri;
597 label = gtk_button_get_label (GTK_BUTTON (link_button));
598 uri = link_button->priv->uri;
600 if (label && *label != '\0' && uri && strcmp (label, uri) != 0)
602 gtk_tooltip_set_text (tooltip, uri);
611 * gtk_link_button_set_uri:
612 * @link_button: a #GtkLinkButton
615 * Sets @uri as the URI where the #GtkLinkButton points.
620 gtk_link_button_set_uri (GtkLinkButton *link_button,
623 GtkLinkButtonPrivate *priv;
625 g_return_if_fail (GTK_IS_LINK_BUTTON (link_button));
626 g_return_if_fail (uri != NULL);
628 priv = link_button->priv;
631 priv->uri = g_strdup (uri);
633 priv->visited = FALSE;
635 g_object_notify (G_OBJECT (link_button), "uri");
639 * gtk_link_button_get_uri:
640 * @link_button: a #GtkLinkButton
642 * Retrieves the URI set using gtk_link_button_set_uri().
644 * Return value: a valid URI. The returned string is owned by the link button
645 * and should not be modified or freed.
649 G_CONST_RETURN gchar *
650 gtk_link_button_get_uri (GtkLinkButton *link_button)
652 g_return_val_if_fail (GTK_IS_LINK_BUTTON (link_button), NULL);
654 return link_button->priv->uri;
658 * gtk_link_button_set_uri_hook:
659 * @func: a function called each time a #GtkLinkButton is clicked, or %NULL
660 * @data: user data to be passed to @func, or %NULL
661 * @destroy: a #GDestroyNotify that gets called when @data is no longer needed, or %NULL
663 * Sets @func as the function that should be invoked every time a user clicks
664 * a #GtkLinkButton. This function is called before every callback registered
665 * for the "clicked" signal.
667 * Return value: the previously set hook function.
672 gtk_link_button_set_uri_hook (GtkLinkButtonUriFunc func,
674 GDestroyNotify destroy)
676 GtkLinkButtonUriFunc old_uri_func;
678 if (uri_func_destroy)
679 (* uri_func_destroy) (uri_func_data);
681 old_uri_func = uri_func;
684 uri_func_data = data;
685 uri_func_destroy = destroy;
690 #define __GTK_LINK_BUTTON_C__
691 #include "gtkaliasdef.c"