X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtklinkbutton.c;h=b5981c741f5ea55192dd40ddbeb9d8fb5e7b5f94;hb=HEAD;hp=c0e2ea038cc11e938ceece9c13c14de352000069;hpb=40aa02249b9f527994e8d7f624bca28ef3aad93b;p=~andy%2Fgtk diff --git a/gtk/gtklinkbutton.c b/gtk/gtklinkbutton.c index c0e2ea038..b5981c741 100644 --- a/gtk/gtklinkbutton.c +++ b/gtk/gtklinkbutton.c @@ -1,12 +1,12 @@ /* GTK - The GIMP Toolkit * gtklinkbutton.c - an hyperlink-enabled button - * + * * Copyright (C) 2006 Emmanuele Bassi * All rights reserved. * * Based on gnome-href code by: - * James Henstridge - * + * James Henstridge + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either @@ -18,32 +18,54 @@ * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Cambridge, MA 02139, USA. + * License along with this library. If not, see . + */ + +/** + * SECTION:gtklinkbutton + * @Title: GtkLinkButton + * @Short_description: Create buttons bound to a URL + * @See_also: #GtkButton + * + * A GtkLinkButton is a #GtkButton with a hyperlink, similar to the one + * used by web browsers, which triggers an action when clicked. It is useful + * to show quick links to resources. + * + * A link button is created by calling either gtk_link_button_new() or + * gtk_link_button_new_with_label(). If using the former, the URI you pass + * to the constructor is used as a label for the widget. + * + * The URI bound to a GtkLinkButton can be set specifically using + * gtk_link_button_set_uri(), and retrieved using gtk_link_button_get_uri(). + * + * By default, GtkLinkButton calls gtk_show_uri() when the button is + * clicked. This behaviour can be overridden by connecting to the + * #GtkLinkButton::activate-link signal and returning %TRUE from the + * signal handler. */ #include "config.h" -#include +#include "gtklinkbutton.h" -#include -#include -#include +#include #include "gtkclipboard.h" #include "gtkdnd.h" #include "gtkimagemenuitem.h" #include "gtklabel.h" #include "gtkmain.h" +#include "gtkmarshalers.h" #include "gtkmenu.h" #include "gtkmenuitem.h" +#include "gtksizerequest.h" #include "gtkstock.h" - -#include "gtklinkbutton.h" - +#include "gtkshow.h" +#include "gtktooltip.h" +#include "gtkprivate.h" #include "gtkintl.h" -#include "gtkalias.h" +#include "a11y/gtklinkbuttonaccessible.h" struct _GtkLinkButtonPrivate { @@ -57,11 +79,16 @@ struct _GtkLinkButtonPrivate enum { PROP_0, - PROP_URI, + PROP_VISITED }; -#define GTK_LINK_BUTTON_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_LINK_BUTTON, GtkLinkButtonPrivate)) +enum +{ + ACTIVATE_LINK, + + LAST_SIGNAL +}; static void gtk_link_button_finalize (GObject *object); static void gtk_link_button_get_property (GObject *object, @@ -78,8 +105,8 @@ static gboolean gtk_link_button_button_press (GtkWidget *widget, GdkEventButton *event); static void gtk_link_button_clicked (GtkButton *button); static gboolean gtk_link_button_popup_menu (GtkWidget *widget); -static void gtk_link_button_style_set (GtkWidget *widget, - GtkStyle *old_style); +static void gtk_link_button_style_updated (GtkWidget *widget); +static void gtk_link_button_unrealize (GtkWidget *widget); static gboolean gtk_link_button_enter_cb (GtkWidget *widget, GdkEventCrossing *event, gpointer user_data); @@ -92,28 +119,32 @@ static void gtk_link_button_drag_data_get_cb (GtkWidget *widget, guint _info, guint _time, gpointer user_data); - +static gboolean gtk_link_button_query_tooltip_cb (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_tip, + GtkTooltip *tooltip, + gpointer data); +static gboolean gtk_link_button_activate_link (GtkLinkButton *link_button); static const GtkTargetEntry link_drop_types[] = { { "text/uri-list", 0, 0 }, { "_NETSCAPE_URL", 0, 0 } }; -static GdkColor default_link_color = { 0, 0, 0, 0xeeee }; -static GdkColor default_visited_link_color = { 0, 0x5555, 0x1a1a, 0x8b8b }; +static const GdkColor default_link_color = { 0, 0, 0, 0xeeee }; +static const GdkColor default_visited_link_color = { 0, 0x5555, 0x1a1a, 0x8b8b }; -static GtkLinkButtonUriFunc uri_func = NULL; -static gpointer uri_func_data = NULL; -static GDestroyNotify uri_func_destroy = NULL; +static guint link_signals[LAST_SIGNAL] = { 0, }; -G_DEFINE_TYPE (GtkLinkButton, gtk_link_button, GTK_TYPE_BUTTON); +G_DEFINE_TYPE (GtkLinkButton, gtk_link_button, GTK_TYPE_BUTTON) static void gtk_link_button_class_init (GtkLinkButtonClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); - GtkContainerClass *container_class = GTK_WIDGET_CLASS (klass); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass); gobject_class->set_property = gtk_link_button_set_property; @@ -122,15 +153,18 @@ gtk_link_button_class_init (GtkLinkButtonClass *klass) widget_class->button_press_event = gtk_link_button_button_press; widget_class->popup_menu = gtk_link_button_popup_menu; - widget_class->style_set = gtk_link_button_style_set; + widget_class->style_updated = gtk_link_button_style_updated; + widget_class->unrealize = gtk_link_button_unrealize; container_class->add = gtk_link_button_add; button_class->clicked = gtk_link_button_clicked; + klass->activate_link = gtk_link_button_activate_link; + /** - * GtkLinkButton:uri - * + * GtkLinkButton:uri: + * * The URI bound to this button. * * Since: 2.10 @@ -138,19 +172,61 @@ gtk_link_button_class_init (GtkLinkButtonClass *klass) g_object_class_install_property (gobject_class, PROP_URI, g_param_spec_string ("uri", - _("URI"), - _("The URI bound to this button"), - "http://www.gtk.org", + P_("URI"), + P_("The URI bound to this button"), + NULL, G_PARAM_READWRITE)); + /** + * GtkLinkButton:visited: + * + * The 'visited' state of this button. A visited link is drawn in a + * different color. + * + * Since: 2.14 + */ + g_object_class_install_property (gobject_class, + PROP_VISITED, + g_param_spec_boolean ("visited", + P_("Visited"), + P_("Whether this link has been visited."), + FALSE, + G_PARAM_READWRITE)); g_type_class_add_private (gobject_class, sizeof (GtkLinkButtonPrivate)); + + /** + * GtkLinkButton::activate-link: + * @button: the #GtkLinkButton that emitted the signal + * + * The ::activate-link signal is emitted each time the #GtkLinkButton + * has been clicked. + * + * The default handler will call gtk_show_uri() with the URI stored inside + * the #GtkLinkButton:uri property. + * + * To override the default behavior, you can connect to the ::activate-link + * signal and stop the propagation of the signal by returning %TRUE from + * your handler. + */ + link_signals[ACTIVATE_LINK] = + g_signal_new (I_("activate-link"), + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GtkLinkButtonClass, activate_link), + _gtk_boolean_handled_accumulator, NULL, + _gtk_marshal_BOOLEAN__VOID, + G_TYPE_BOOLEAN, 0); + + gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_LINK_BUTTON_ACCESSIBLE); } static void gtk_link_button_init (GtkLinkButton *link_button) { - link_button->priv = GTK_LINK_BUTTON_GET_PRIVATE (link_button), - + link_button->priv = G_TYPE_INSTANCE_GET_PRIVATE (link_button, + GTK_TYPE_LINK_BUTTON, + GtkLinkButtonPrivate); + gtk_button_set_relief (GTK_BUTTON (link_button), GTK_RELIEF_NONE); g_signal_connect (link_button, "enter-notify-event", @@ -159,6 +235,10 @@ gtk_link_button_init (GtkLinkButton *link_button) G_CALLBACK (gtk_link_button_leave_cb), NULL); g_signal_connect (link_button, "drag-data-get", G_CALLBACK (gtk_link_button_drag_data_get_cb), NULL); + + g_object_set (link_button, "has-tooltip", TRUE, NULL); + g_signal_connect (link_button, "query-tooltip", + G_CALLBACK (gtk_link_button_query_tooltip_cb), NULL); /* enable drag source */ gtk_drag_source_set (GTK_WIDGET (link_button), @@ -190,6 +270,9 @@ gtk_link_button_get_property (GObject *object, case PROP_URI: g_value_set_string (value, link_button->priv->uri); break; + case PROP_VISITED: + g_value_set_boolean (value, link_button->priv->visited); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -209,6 +292,9 @@ gtk_link_button_set_property (GObject *object, case PROP_URI: gtk_link_button_set_uri (link_button, g_value_get_string (value)); break; + case PROP_VISITED: + gtk_link_button_set_visited (link_button, g_value_get_boolean (value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -220,28 +306,35 @@ set_link_color (GtkLinkButton *link_button) { GdkColor *link_color = NULL; GtkWidget *label; + GdkRGBA rgba; label = gtk_bin_get_child (GTK_BIN (link_button)); + if (!GTK_IS_LABEL (label)) + return; if (link_button->priv->visited) { - gtk_widget_style_get (GTK_WIDGET (link_button), + gtk_widget_style_get (GTK_WIDGET (link_button), "visited-link-color", &link_color, NULL); if (!link_color) - link_color = &default_visited_link_color; + link_color = (GdkColor *) &default_visited_link_color; } else { - gtk_widget_style_get (GTK_WIDGET (link_button), + gtk_widget_style_get (GTK_WIDGET (link_button), "link-color", &link_color, NULL); if (!link_color) - link_color = &default_link_color; + link_color = (GdkColor *) &default_link_color; } - - gtk_widget_modify_fg (label, GTK_STATE_NORMAL, link_color); - gtk_widget_modify_fg (label, GTK_STATE_ACTIVE, link_color); - gtk_widget_modify_fg (label, GTK_STATE_PRELIGHT, link_color); - gtk_widget_modify_fg (label, GTK_STATE_SELECTED, link_color); + + rgba.red = link_color->red / 65535.; + rgba.green = link_color->green / 65535.; + rgba.blue = link_color->blue / 65535.; + rgba.alpha = 1; + gtk_widget_override_color (label, GTK_STATE_FLAG_NORMAL, &rgba); + gtk_widget_override_color (label, GTK_STATE_FLAG_ACTIVE, &rgba); + gtk_widget_override_color (label, GTK_STATE_FLAG_PRELIGHT, &rgba); + gtk_widget_override_color (label, GTK_STATE_FLAG_SELECTED, &rgba); if (link_color != &default_link_color && link_color != &default_visited_link_color) @@ -265,6 +358,7 @@ set_link_underline (GtkLinkButton *link_button) attributes = pango_attr_list_new (); pango_attr_list_insert (attributes, uline); gtk_label_set_attributes (GTK_LABEL (label), attributes); + pango_attr_list_unref (attributes); } } @@ -274,16 +368,16 @@ gtk_link_button_add (GtkContainer *container, { GTK_CONTAINER_CLASS (gtk_link_button_parent_class)->add (container, widget); + set_link_color (GTK_LINK_BUTTON (container)); set_link_underline (GTK_LINK_BUTTON (container)); } static void -gtk_link_button_style_set (GtkWidget *widget, - GtkStyle *old_style) +gtk_link_button_style_updated (GtkWidget *widget) { - GtkLinkButton *link_button = GTK_LINK_BUTTON (widget); + GTK_WIDGET_CLASS (gtk_link_button_parent_class)->style_updated (widget); - set_link_color (link_button); + set_link_color (GTK_LINK_BUTTON (widget)); } static void @@ -299,11 +393,19 @@ set_hand_cursor (GtkWidget *widget, if (show_hand) cursor = gdk_cursor_new_for_display (display, GDK_HAND2); - gdk_window_set_cursor (widget->window, cursor); + gdk_window_set_cursor (gtk_widget_get_window (widget), cursor); gdk_display_flush (display); if (cursor) - gdk_cursor_unref (cursor); + g_object_unref (cursor); +} + +static void +gtk_link_button_unrealize (GtkWidget *widget) +{ + set_hand_cursor (widget, FALSE); + + GTK_WIDGET_CLASS (gtk_link_button_parent_class)->unrealize (widget); } static void @@ -324,24 +426,26 @@ popup_position_func (GtkMenu *menu, { GtkLinkButton *link_button = GTK_LINK_BUTTON (user_data); GtkLinkButtonPrivate *priv = link_button->priv; + GtkAllocation allocation; GtkWidget *widget = GTK_WIDGET (link_button); GdkScreen *screen = gtk_widget_get_screen (widget); GtkRequisition req; gint monitor_num; GdkRectangle monitor; - g_return_if_fail (GTK_WIDGET_REALIZED (link_button)); + g_return_if_fail (gtk_widget_get_realized (widget)); - gdk_window_get_origin (widget->window, x, y); + gdk_window_get_origin (gtk_widget_get_window (widget), x, y); - gtk_widget_size_request (priv->popup_menu, &req); + gtk_widget_get_preferred_size (priv->popup_menu, &req, NULL); - *x += widget->allocation.width / 2; - *y += widget->allocation.height; + gtk_widget_get_allocation (widget, &allocation); + *x += allocation.width / 2; + *y += allocation.height; monitor_num = gdk_screen_get_monitor_at_point (screen, *x, *y); gtk_menu_set_monitor (menu, monitor_num); - gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); + gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor); *x = CLAMP (*x, monitor.x, monitor.x + MAX (0, monitor.width - req.width)); *y = CLAMP (*y, monitor.y, monitor.y + MAX (0, monitor.height - req.height)); @@ -379,7 +483,7 @@ gtk_link_button_do_popup (GtkLinkButton *link_button, time = gtk_get_current_event_time (); } - if (GTK_WIDGET_REALIZED (link_button)) + if (gtk_widget_get_realized (GTK_WIDGET (link_button))) { GtkWidget *menu_item; @@ -419,33 +523,59 @@ static gboolean gtk_link_button_button_press (GtkWidget *widget, GdkEventButton *event) { - if (!GTK_WIDGET_HAS_FOCUS (widget)) + if (!gtk_widget_has_focus (widget)) gtk_widget_grab_focus (widget); - if ((event->button == 3) && (event->type == GDK_BUTTON_PRESS)) + /* Don't popup the menu if there's no URI set, + * otherwise the menu item will trigger a warning */ + if (gdk_event_triggers_context_menu ((GdkEvent *) event) && + GTK_LINK_BUTTON (widget)->priv->uri != NULL) { gtk_link_button_do_popup (GTK_LINK_BUTTON (widget), event); - + return TRUE; } if (GTK_WIDGET_CLASS (gtk_link_button_parent_class)->button_press_event) - return (* GTK_WIDGET_CLASS (gtk_link_button_parent_class)->button_press_event) (widget, event); + return GTK_WIDGET_CLASS (gtk_link_button_parent_class)->button_press_event (widget, event); return FALSE; } -static void -gtk_link_button_clicked (GtkButton *button) +static gboolean +gtk_link_button_activate_link (GtkLinkButton *link_button) { - GtkLinkButton *link_button = GTK_LINK_BUTTON (button); + GdkScreen *screen; + GError *error; + + if (gtk_widget_has_screen (GTK_WIDGET (link_button))) + screen = gtk_widget_get_screen (GTK_WIDGET (link_button)); + else + screen = NULL; - if (uri_func) - (* uri_func) (button, link_button->priv->uri, uri_func_data); + error = NULL; + gtk_show_uri (screen, link_button->priv->uri, GDK_CURRENT_TIME, &error); + if (error) + { + g_warning ("Unable to show '%s': %s", + link_button->priv->uri, + error->message); + g_error_free (error); + + return FALSE; + } - link_button->priv->visited = TRUE; + gtk_link_button_set_visited (link_button, TRUE); + + return TRUE; +} + +static void +gtk_link_button_clicked (GtkButton *button) +{ + gboolean retval = FALSE; - set_link_color (link_button); + g_signal_emit (button, link_signals[ACTIVATE_LINK], 0, &retval); } static gboolean @@ -489,7 +619,7 @@ gtk_link_button_drag_data_get_cb (GtkWidget *widget, uri = g_strdup_printf ("%s\r\n", link_button->priv->uri); gtk_selection_data_set (selection, - selection->target, + gtk_selection_data_get_target (selection), 8, (guchar *) uri, strlen (uri)); @@ -537,8 +667,8 @@ gtk_link_button_new (const gchar *uri) } retval = g_object_new (GTK_TYPE_LINK_BUTTON, - "uri", uri, "label", utf8_uri, + "uri", uri, NULL); g_free (utf8_uri); @@ -549,11 +679,11 @@ gtk_link_button_new (const gchar *uri) /** * gtk_link_button_new_with_label: * @uri: a valid URI - * @label: the text of the button + * @label: (allow-none): the text of the button * * Creates a new #GtkLinkButton containing a label. * - * Return value: a new link button widget. + * Return value: (transfer none): a new link button widget. * * Since: 2.10 */ @@ -563,7 +693,7 @@ gtk_link_button_new_with_label (const gchar *uri, { GtkWidget *retval; - g_return_val_if_fail (uri != NULL, FALSE); + g_return_val_if_fail (uri != NULL, NULL); if (!label) return gtk_link_button_new (uri); @@ -576,12 +706,39 @@ gtk_link_button_new_with_label (const gchar *uri, return retval; } +static gboolean +gtk_link_button_query_tooltip_cb (GtkWidget *widget, + gint x, + gint y, + gboolean keyboard_tip, + GtkTooltip *tooltip, + gpointer data) +{ + GtkLinkButton *link_button = GTK_LINK_BUTTON (widget); + const gchar *label, *uri; + + label = gtk_button_get_label (GTK_BUTTON (link_button)); + uri = link_button->priv->uri; + + if (!gtk_widget_get_tooltip_text (widget) + && !gtk_widget_get_tooltip_markup (widget) + && label && *label != '\0' && uri && strcmp (label, uri) != 0) + { + gtk_tooltip_set_text (tooltip, uri); + return TRUE; + } + + return FALSE; +} + + /** * gtk_link_button_set_uri: * @link_button: a #GtkLinkButton * @uri: a valid URI * - * Sets @uri as the URI where the #GtkLinkButton points. + * Sets @uri as the URI where the #GtkLinkButton points. As a side-effect + * this unsets the 'visited' state of the button. * * Since: 2.10 */ @@ -589,18 +746,19 @@ void gtk_link_button_set_uri (GtkLinkButton *link_button, const gchar *uri) { - gchar *tmp; + GtkLinkButtonPrivate *priv; g_return_if_fail (GTK_IS_LINK_BUTTON (link_button)); g_return_if_fail (uri != NULL); - - tmp = link_button->priv->uri; - link_button->priv->uri = g_strdup (uri); - g_free (tmp); - link_button->priv->visited = FALSE; - + priv = link_button->priv; + + g_free (priv->uri); + priv->uri = g_strdup (uri); + g_object_notify (G_OBJECT (link_button), "uri"); + + gtk_link_button_set_visited (link_button, FALSE); } /** @@ -614,7 +772,7 @@ gtk_link_button_set_uri (GtkLinkButton *link_button, * * Since: 2.10 */ -G_CONST_RETURN gchar * +const gchar * gtk_link_button_get_uri (GtkLinkButton *link_button) { g_return_val_if_fail (GTK_IS_LINK_BUTTON (link_button), NULL); @@ -623,37 +781,51 @@ gtk_link_button_get_uri (GtkLinkButton *link_button) } /** - * gtk_link_button_set_uri_hook: - * @func: a function called each time a #GtkLinkButton is clicked, or %NULL - * @data: user data to be passed to @func, or %NULL - * @destroy: a #GDestroyNotify that gets called when @data is no longer needed, or %NULL - * - * Sets @func as the function that should be invoked every time a user clicks - * a #GtkLinkButton. This function is called before every callback registered - * for the "clicked" signal. + * gtk_link_button_set_visited: + * @link_button: a #GtkLinkButton + * @visited: the new 'visited' state * - * Return value: the previously set hook function. + * Sets the 'visited' state of the URI where the #GtkLinkButton + * points. See gtk_link_button_get_visited() for more details. * - * Since: 2.10 + * Since: 2.14 */ -GtkLinkButtonUriFunc -gtk_link_button_set_uri_hook (GtkLinkButtonUriFunc func, - gpointer data, - GDestroyNotify destroy) +void +gtk_link_button_set_visited (GtkLinkButton *link_button, + gboolean visited) { - GtkLinkButtonUriFunc old_uri_func; + g_return_if_fail (GTK_IS_LINK_BUTTON (link_button)); - if (uri_func_destroy) - (* uri_func_destroy) (uri_func_data); + visited = visited != FALSE; - old_uri_func = uri_func; + if (link_button->priv->visited != visited) + { + link_button->priv->visited = visited; - uri_func = func; - uri_func_data = data; - uri_func_destroy = destroy; + set_link_color (link_button); - return old_uri_func; + g_object_notify (G_OBJECT (link_button), "visited"); + } } -#define __GTK_LINK_BUTTON_C__ -#include "gtkaliasdef.c" +/** + * gtk_link_button_get_visited: + * @link_button: a #GtkLinkButton + * + * Retrieves the 'visited' state of the URI where the #GtkLinkButton + * points. The button becomes visited when it is clicked. If the URI + * is changed on the button, the 'visited' state is unset again. + * + * The state may also be changed using gtk_link_button_set_visited(). + * + * Return value: %TRUE if the link has been visited, %FALSE otherwise + * + * Since: 2.14 + */ +gboolean +gtk_link_button_get_visited (GtkLinkButton *link_button) +{ + g_return_val_if_fail (GTK_IS_LINK_BUTTON (link_button), FALSE); + + return link_button->priv->visited; +}