2
* Copyright (C) 2003 Sun Microsystems, Inc.
4
* This library is free software; you can redistribute it and/or
5
* modify it under the terms of the GNU Library General Public
6
* License as published by the Free Software Foundation; either
7
* version 2 of the License, or (at your option) any later version.
9
* This library is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
* Library General Public License for more details.
14
* You should have received a copy of the GNU Library General Public
15
* License along with this library; if not, write to the
16
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17
* Boston, MA 02111-1307, USA.
20
* Mark McLoughlin <mark@skynet.ie>
28
#include <gtk/gtktooltips.h>
29
#include <libgnomeui/gnome-url.h>
31
#define _(x) dgettext (GETTEXT_PACKAGE, x)
40
struct _VinoURLPrivate
45
PangoAttrList *attributes;
46
GtkTooltips *tooltips;
47
GdkWindow *event_window;
49
guint button_down : 1;
50
guint foreground_modified : 1;
51
guint underline_modified : 1;
54
static void vino_url_class_init (VinoURLClass *klass);
55
static void vino_url_instance_init (VinoURL *url);
57
static void vino_url_finalize (GObject *object);
58
static void vino_url_set_property (GObject *object,
62
static void vino_url_get_property (GObject *object,
67
static void vino_url_realize (GtkWidget *widget);
68
static void vino_url_unrealize (GtkWidget *widget);
69
static void vino_url_size_allocate (GtkWidget *widget,
70
GtkAllocation *allocation);
71
static void vino_url_size_request (GtkWidget *widget,
72
GtkRequisition *requisition);
73
static void vino_url_map (GtkWidget *widget);
74
static void vino_url_unmap (GtkWidget *widget);
75
static gboolean vino_url_expose (GtkWidget *widget,
76
GdkEventExpose *event);
77
static gboolean vino_url_button_press (GtkWidget *widget,
78
GdkEventButton *event);
79
static gboolean vino_url_button_release (GtkWidget *widget,
80
GdkEventButton *event);
81
static gboolean vino_url_focus (GtkWidget *widget,
82
GtkDirectionType direction);
83
static void vino_url_state_changed (GtkWidget *widget,
84
GtkStateType previous_state);
86
static void vino_url_activate (VinoURL *url);
88
static void vino_url_change_attribute_internal (VinoURL *url,
89
PangoAttribute *attribute,
91
static void vino_url_set_use_underline (VinoURL *url,
92
gboolean use_underline);
93
static void vino_url_set_use_url_color (VinoURL *url,
94
gboolean use_underline);
96
static GtkLabelClass *parent_class = NULL;
99
vino_url_get_type (void)
101
static GType url_type = 0;
105
static const GTypeInfo url_info =
107
sizeof (VinoURLClass),
108
NULL, /* base_init */
109
NULL, /* base_finalize */
110
(GClassInitFunc) vino_url_class_init,
111
NULL, /* class_finalize */
112
NULL, /* class_data */
115
(GInstanceInitFunc) vino_url_instance_init,
118
url_type = g_type_register_static (GTK_TYPE_LABEL,
127
vino_url_class_init (VinoURLClass *klass)
129
GObjectClass *gobject_class;
130
GtkWidgetClass *widget_class;
132
parent_class = g_type_class_peek_parent (klass);
134
gobject_class = (GObjectClass *) klass;
135
widget_class = (GtkWidgetClass *) klass;
137
gobject_class->finalize = vino_url_finalize;
138
gobject_class->set_property = vino_url_set_property;
139
gobject_class->get_property = vino_url_get_property;
141
widget_class->realize = vino_url_realize;
142
widget_class->unrealize = vino_url_unrealize;
143
widget_class->size_request = vino_url_size_request;
144
widget_class->size_allocate = vino_url_size_allocate;
145
widget_class->map = vino_url_map;
146
widget_class->unmap = vino_url_unmap;
147
widget_class->expose_event = vino_url_expose;
148
widget_class->button_press_event = vino_url_button_press;
149
widget_class->button_release_event = vino_url_button_release;
150
widget_class->focus = vino_url_focus;
151
widget_class->state_changed = vino_url_state_changed;
153
klass->activate = vino_url_activate;
155
g_object_class_install_property (gobject_class,
157
g_param_spec_string ("address",
159
_("The address pointed to by the widget"),
161
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
163
g_object_class_install_property (gobject_class,
165
g_param_spec_string ("tooltip",
167
_("A tooltip for this URL"),
169
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
171
gtk_widget_class_install_style_property (widget_class,
172
g_param_spec_boxed ("url-color",
174
_("The color of the URL's label"),
178
widget_class->activate_signal =
179
g_signal_new ("activate",
180
G_TYPE_FROM_CLASS (gobject_class),
181
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
182
G_STRUCT_OFFSET (VinoURLClass, activate),
184
g_cclosure_marshal_VOID__VOID,
189
vino_url_instance_init (VinoURL *url)
191
url->priv = g_new0 (VinoURLPrivate, 1);
193
vino_url_set_use_url_color (url, TRUE);
194
vino_url_set_use_underline (url, TRUE);
196
url->priv->tooltips = gtk_tooltips_new ();
197
g_object_ref (url->priv->tooltips);
198
gtk_object_sink (GTK_OBJECT (url->priv->tooltips));
200
/* Chain up to the label's focus handling code
201
* which is meant for selection, even though
202
* we're not selectable.
204
GTK_WIDGET_SET_FLAGS (url, GTK_CAN_FOCUS);
208
vino_url_finalize (GObject *object)
210
VinoURL *url = VINO_URL (object);
212
if (url->priv->address)
213
g_free (url->priv->address);
214
url->priv->address = NULL;
216
if (url->priv->attributes)
217
pango_attr_list_unref (url->priv->attributes);
218
url->priv->attributes = NULL;
220
if (url->priv->tooltips)
221
g_object_unref (url->priv->tooltips);
222
url->priv->tooltips = NULL;
224
if (url->priv->tooltip)
225
g_free (url->priv->tooltip);
226
url->priv->tooltip = NULL;
231
G_OBJECT_CLASS (parent_class)->finalize (object);
235
vino_url_set_property (GObject *object,
240
VinoURL *url = VINO_URL (object);
245
vino_url_set_address (url, g_value_get_string (value));
248
vino_url_set_tooltip (url, g_value_get_string (value));
251
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
257
vino_url_get_property (GObject *object,
262
VinoURL *url = VINO_URL (object);
267
g_value_set_string (value, vino_url_get_address (url));
270
g_value_set_string (value, vino_url_get_tooltip (url));
273
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
279
vino_url_realize (GtkWidget *widget)
281
VinoURL *url = VINO_URL (widget);
282
GdkWindowAttr attributes;
283
GdkCursor *cursor = NULL;
284
gint attributes_mask;
286
GTK_WIDGET_CLASS (parent_class)->realize (widget);
288
attributes.window_type = GDK_WINDOW_CHILD;
289
attributes.x = widget->allocation.x;
290
attributes.y = widget->allocation.y;
291
attributes.width = widget->allocation.width;
292
attributes.height = widget->allocation.height;
293
attributes.wclass = GDK_INPUT_ONLY;
294
attributes.event_mask = gtk_widget_get_events (widget) |
295
GDK_BUTTON_PRESS_MASK |
296
GDK_BUTTON_RELEASE_MASK |
297
GDK_ENTER_NOTIFY_MASK |
298
GDK_LEAVE_NOTIFY_MASK;
300
attributes_mask = GDK_WA_X | GDK_WA_Y;
302
if (GTK_WIDGET_IS_SENSITIVE (widget))
304
attributes.cursor = cursor =
305
gdk_cursor_new_for_display (gtk_widget_get_display (widget),
307
attributes_mask |= GDK_WA_CURSOR;
310
url->priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
311
&attributes, attributes_mask);
312
gdk_window_set_user_data (url->priv->event_window, widget);
315
gdk_cursor_unref (cursor);
319
vino_url_unrealize (GtkWidget *widget)
321
VinoURL *url = VINO_URL (widget);
323
if (url->priv->event_window)
325
gdk_window_set_user_data (url->priv->event_window, NULL);
326
gdk_window_destroy (url->priv->event_window);
327
url->priv->event_window = NULL;
330
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
334
vino_url_size_request (GtkWidget *widget,
335
GtkRequisition *requisition)
340
gtk_widget_style_get (widget,
341
"focus-line-width", &focus_width,
342
"focus-padding", &focus_pad,
345
GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
347
requisition->width += 2 * (focus_width + focus_pad);
348
requisition->height += 2 * (focus_width + focus_pad);
352
vino_url_size_allocate (GtkWidget *widget,
353
GtkAllocation *allocation)
355
VinoURL *url = VINO_URL (widget);
357
GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
359
if (GTK_WIDGET_REALIZED (widget))
361
gdk_window_move_resize (url->priv->event_window,
370
vino_url_map (GtkWidget *widget)
372
VinoURL *url = VINO_URL (widget);
374
GTK_WIDGET_CLASS (parent_class)->map (widget);
376
if (url->priv->event_window)
377
gdk_window_show (url->priv->event_window);
381
vino_url_unmap (GtkWidget *widget)
383
VinoURL *url = VINO_URL (widget);
385
if (url->priv->event_window)
386
gdk_window_hide (url->priv->event_window);
388
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
392
vino_url_expose (GtkWidget *widget,
393
GdkEventExpose *event)
395
GtkAllocation real_allocation;
401
gtk_widget_style_get (widget,
402
"focus-line-width", &focus_width,
403
"focus-padding", &focus_pad,
406
/* We need to fool GtkLabel into drawing the label
407
* in the right place.
409
real_allocation = widget->allocation;
411
widget->allocation.x += focus_width + focus_pad;
412
widget->allocation.y += focus_width + focus_pad;
413
widget->allocation.width -= 2 * (focus_width + focus_pad);
414
widget->allocation.height -= 2 * (focus_width + focus_pad);
416
GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
418
layout = gtk_label_get_layout (GTK_LABEL (widget));
421
pango_layout_get_pixel_size (layout, &width, &height);
423
width += 2 * (focus_pad + focus_width);
424
height += 2 * (focus_pad + focus_width);
426
widget->allocation = real_allocation;
428
if (GTK_WIDGET_HAS_FOCUS (widget))
429
gtk_paint_focus (widget->style,
431
GTK_WIDGET_STATE (widget),
435
widget->allocation.x,
436
widget->allocation.y,
444
vino_url_button_press (GtkWidget *widget,
445
GdkEventButton *event)
447
VinoURL *url = VINO_URL (widget);
449
if (event->button == 1 && event->window == url->priv->event_window)
451
url->priv->button_down = TRUE;
459
vino_url_button_release (GtkWidget *widget,
460
GdkEventButton *event)
462
VinoURL *url = VINO_URL (widget);
464
if (event->button == 1 && url->priv->button_down)
466
gtk_widget_activate (widget);
467
url->priv->button_down = FALSE;
475
vino_url_focus (GtkWidget *widget,
476
GtkDirectionType direction)
478
if (!gtk_widget_is_focus (widget))
480
gtk_widget_grab_focus (widget);
488
vino_url_state_changed (GtkWidget *widget,
489
GtkStateType previous_state)
491
if (GTK_WIDGET_REALIZED (widget))
493
if (GTK_WIDGET_IS_SENSITIVE (widget))
497
cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
499
gdk_window_set_cursor (VINO_URL (widget)->priv->event_window, cursor);
500
gdk_cursor_unref (cursor);
504
gdk_window_set_cursor (VINO_URL (widget)->priv->event_window, NULL);
508
vino_url_set_use_url_color (VINO_URL (widget), GTK_WIDGET_IS_SENSITIVE (widget));
512
vino_url_activate (VinoURL *url)
514
GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (url));
515
GError *error = NULL;
517
if (!url->priv->address)
520
if (!gnome_url_show_on_screen (url->priv->address, screen, &error))
522
/* FIXME better error handling!
523
* What best to do? For the specific case
524
* in this preferences dialog we want to be
525
* able to pop up a dialog with the error
526
* but also the vino URL as a selectable
529
* Maybe chain this up to the caller?
532
g_warning ("Failed to show URL '%s': %s\n",
533
url->priv->address, error->message);
534
g_error_free (error);
539
vino_url_new (const char *address,
543
g_return_val_if_fail (address != NULL, NULL);
545
return g_object_new (VINO_TYPE_URL,
553
vino_url_set_use_underline (VinoURL *url,
554
gboolean use_underline)
556
if (!url->priv->underline_modified)
560
vino_url_change_attribute_internal (url,
561
pango_attr_underline_new (PANGO_UNDERLINE_SINGLE),
566
vino_url_unset_attribute_type (url, PANGO_ATTR_UNDERLINE);
572
vino_url_set_use_url_color (VinoURL *url,
573
gboolean use_url_color)
575
if (!url->priv->foreground_modified)
579
GdkColor blue = { 0, 0x0000, 0x0000, 0xffff };
581
PangoAttribute *foreground;
583
gtk_widget_style_get (GTK_WIDGET (url),
584
"url-color", &url_color,
589
foreground = pango_attr_foreground_new (url_color->red,
593
vino_url_change_attribute_internal (url, foreground, TRUE);
595
if (url_color != &blue)
596
gdk_color_free (url_color);
600
vino_url_unset_attribute_type (url, PANGO_ATTR_FOREGROUND);
606
vino_url_set_address (VinoURL *url,
609
g_return_if_fail (VINO_IS_URL (url));
611
g_free (url->priv->address);
612
url->priv->address = g_strdup (address);
614
g_object_notify (G_OBJECT (url), "address");
617
G_CONST_RETURN char *
618
vino_url_get_address (VinoURL *url)
620
g_return_val_if_fail (VINO_IS_URL (url), NULL);
622
return url->priv->address;
626
vino_url_set_tooltip (VinoURL *url,
629
g_return_if_fail (VINO_IS_URL (url));
631
g_free (url->priv->tooltip);
632
url->priv->tooltip = g_strdup (tooltip);
634
gtk_tooltips_set_tip (url->priv->tooltips,
639
g_object_notify (G_OBJECT (url), "tooltip");
642
G_CONST_RETURN char *
643
vino_url_get_tooltip (VinoURL *url)
645
g_return_val_if_fail (VINO_IS_URL (url), NULL);
647
return url->priv->tooltip;
650
/* Debugging; There should probably be a nicer API
651
* for fiddling with the attributes on a label;
652
* Either that or I'm being dumb again;
654
#define SANITY_CHECK_ATTRIBUTES
657
sanity_check_attributes_notify (VinoURL *url)
659
PangoAttrList *attrs;
661
attrs = gtk_label_get_attributes (GTK_LABEL (url));
662
if (attrs != url->priv->attributes)
664
g_warning ("Label attributes changed, resetting");
665
gtk_label_set_attributes (GTK_LABEL (url),
666
url->priv->attributes);
671
sanity_check_attributes (VinoURL *url)
673
#ifdef SANITY_CHECK_ATTRIBUTES
674
g_signal_connect (url, "notify::attributes",
675
G_CALLBACK (sanity_check_attributes_notify), NULL);
676
#endif /* SANITY_CHECK_ATTRIBUTES */
680
vino_url_change_attribute_internal (VinoURL *url,
681
PangoAttribute *attribute,
684
if (!url->priv->attributes)
686
url->priv->attributes = pango_attr_list_new ();
687
gtk_label_set_attributes (GTK_LABEL (url), url->priv->attributes);
688
sanity_check_attributes (url);
691
attribute->start_index = 0;
692
attribute->end_index = G_MAXINT;
696
if (attribute->klass->type == PANGO_ATTR_FOREGROUND)
697
url->priv->foreground_modified = TRUE;
699
if (attribute->klass->type == PANGO_ATTR_UNDERLINE)
700
url->priv->underline_modified = TRUE;
703
pango_attr_list_change (url->priv->attributes, attribute);
707
vino_url_change_attribute (VinoURL *url,
708
PangoAttribute *attribute)
710
g_return_if_fail (VINO_IS_URL (url));
712
vino_url_change_attribute_internal (url, attribute, FALSE);
716
filter_out_attr_type (PangoAttribute *attribute,
719
PangoAttrType attr_type = GPOINTER_TO_INT (data);
721
return attribute->klass->type == attr_type;
725
vino_url_unset_attribute_type (VinoURL *url,
726
PangoAttrType attr_type)
728
g_return_if_fail (VINO_IS_URL (url));
730
if (url->priv->attributes)
732
PangoAttrList *filtered;
735
pango_attr_list_filter (url->priv->attributes,
736
(PangoAttrFilterFunc) filter_out_attr_type,
737
GINT_TO_POINTER (attr_type));
739
pango_attr_list_unref (filtered);