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;
227
G_OBJECT_CLASS (parent_class)->finalize (object);
231
vino_url_set_property (GObject *object,
236
VinoURL *url = VINO_URL (object);
241
vino_url_set_address (url, g_value_get_string (value));
244
vino_url_set_tooltip (url, g_value_get_string (value));
247
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
253
vino_url_get_property (GObject *object,
258
VinoURL *url = VINO_URL (object);
263
g_value_set_string (value, vino_url_get_address (url));
266
g_value_set_string (value, vino_url_get_tooltip (url));
269
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
275
vino_url_realize (GtkWidget *widget)
277
VinoURL *url = VINO_URL (widget);
278
GdkWindowAttr attributes;
279
GdkCursor *cursor = NULL;
280
gint attributes_mask;
282
GTK_WIDGET_CLASS (parent_class)->realize (widget);
284
attributes.window_type = GDK_WINDOW_CHILD;
285
attributes.x = widget->allocation.x;
286
attributes.y = widget->allocation.y;
287
attributes.width = widget->allocation.width;
288
attributes.height = widget->allocation.height;
289
attributes.wclass = GDK_INPUT_ONLY;
290
attributes.event_mask = gtk_widget_get_events (widget) |
291
GDK_BUTTON_PRESS_MASK |
292
GDK_BUTTON_RELEASE_MASK |
293
GDK_ENTER_NOTIFY_MASK |
294
GDK_LEAVE_NOTIFY_MASK;
296
attributes_mask = GDK_WA_X | GDK_WA_Y;
298
if (GTK_WIDGET_IS_SENSITIVE (widget))
300
attributes.cursor = cursor =
301
gdk_cursor_new_for_display (gtk_widget_get_display (widget),
303
attributes_mask |= GDK_WA_CURSOR;
306
url->priv->event_window = gdk_window_new (gtk_widget_get_parent_window (widget),
307
&attributes, attributes_mask);
308
gdk_window_set_user_data (url->priv->event_window, widget);
311
gdk_cursor_unref (cursor);
315
vino_url_unrealize (GtkWidget *widget)
317
VinoURL *url = VINO_URL (widget);
319
if (url->priv->event_window)
321
gdk_window_set_user_data (url->priv->event_window, NULL);
322
gdk_window_destroy (url->priv->event_window);
323
url->priv->event_window = NULL;
326
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
330
vino_url_size_request (GtkWidget *widget,
331
GtkRequisition *requisition)
336
gtk_widget_style_get (widget,
337
"focus-line-width", &focus_width,
338
"focus-padding", &focus_pad,
341
GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
343
requisition->width += 2 * (focus_width + focus_pad);
344
requisition->height += 2 * (focus_width + focus_pad);
348
vino_url_size_allocate (GtkWidget *widget,
349
GtkAllocation *allocation)
351
VinoURL *url = VINO_URL (widget);
353
GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, allocation);
355
if (GTK_WIDGET_REALIZED (widget))
357
gdk_window_move_resize (url->priv->event_window,
366
vino_url_map (GtkWidget *widget)
368
VinoURL *url = VINO_URL (widget);
370
GTK_WIDGET_CLASS (parent_class)->map (widget);
372
if (url->priv->event_window)
373
gdk_window_show (url->priv->event_window);
377
vino_url_unmap (GtkWidget *widget)
379
VinoURL *url = VINO_URL (widget);
381
if (url->priv->event_window)
382
gdk_window_hide (url->priv->event_window);
384
GTK_WIDGET_CLASS (parent_class)->unmap (widget);
388
vino_url_expose (GtkWidget *widget,
389
GdkEventExpose *event)
391
GtkAllocation real_allocation;
397
gtk_widget_style_get (widget,
398
"focus-line-width", &focus_width,
399
"focus-padding", &focus_pad,
402
/* We need to fool GtkLabel into drawing the label
403
* in the right place.
405
real_allocation = widget->allocation;
407
widget->allocation.x += focus_width + focus_pad;
408
widget->allocation.y += focus_width + focus_pad;
409
widget->allocation.width -= 2 * (focus_width + focus_pad);
410
widget->allocation.height -= 2 * (focus_width + focus_pad);
412
GTK_WIDGET_CLASS (parent_class)->expose_event (widget, event);
414
layout = gtk_label_get_layout (GTK_LABEL (widget));
417
pango_layout_get_pixel_size (layout, &width, &height);
419
width += 2 * (focus_pad + focus_width);
420
height += 2 * (focus_pad + focus_width);
422
widget->allocation = real_allocation;
424
if (GTK_WIDGET_HAS_FOCUS (widget))
425
gtk_paint_focus (widget->style,
427
GTK_WIDGET_STATE (widget),
431
widget->allocation.x,
432
widget->allocation.y,
440
vino_url_button_press (GtkWidget *widget,
441
GdkEventButton *event)
443
VinoURL *url = VINO_URL (widget);
445
if (event->button == 1 && event->window == url->priv->event_window)
447
url->priv->button_down = TRUE;
455
vino_url_button_release (GtkWidget *widget,
456
GdkEventButton *event)
458
VinoURL *url = VINO_URL (widget);
460
if (event->button == 1 && url->priv->button_down)
462
gtk_widget_activate (widget);
463
url->priv->button_down = FALSE;
471
vino_url_focus (GtkWidget *widget,
472
GtkDirectionType direction)
474
if (!gtk_widget_is_focus (widget))
476
gtk_widget_grab_focus (widget);
484
vino_url_state_changed (GtkWidget *widget,
485
GtkStateType previous_state)
487
if (GTK_WIDGET_REALIZED (widget))
489
if (GTK_WIDGET_IS_SENSITIVE (widget))
493
cursor = gdk_cursor_new_for_display (gtk_widget_get_display (widget),
495
gdk_window_set_cursor (VINO_URL (widget)->priv->event_window, cursor);
496
gdk_cursor_unref (cursor);
500
gdk_window_set_cursor (VINO_URL (widget)->priv->event_window, NULL);
504
vino_url_set_use_url_color (VINO_URL (widget), GTK_WIDGET_IS_SENSITIVE (widget));
508
vino_url_activate (VinoURL *url)
510
GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (url));
511
GError *error = NULL;
513
if (!url->priv->address)
516
if (!gnome_url_show_on_screen (url->priv->address, screen, &error))
518
/* FIXME better error handling!
519
* What best to do? For the specific case
520
* in this preferences dialog we want to be
521
* able to pop up a dialog with the error
522
* but also the vino URL as a selectable
525
* Maybe chain this up to the caller?
528
g_warning ("Failed to show URL '%s': %s\n",
529
url->priv->address, error->message);
530
g_error_free (error);
535
vino_url_new (const char *address,
539
g_return_val_if_fail (address != NULL, NULL);
541
return g_object_new (VINO_TYPE_URL,
549
vino_url_set_use_underline (VinoURL *url,
550
gboolean use_underline)
552
if (!url->priv->underline_modified)
556
vino_url_change_attribute_internal (url,
557
pango_attr_underline_new (PANGO_UNDERLINE_SINGLE),
562
vino_url_unset_attribute_type (url, PANGO_ATTR_UNDERLINE);
568
vino_url_set_use_url_color (VinoURL *url,
569
gboolean use_url_color)
571
if (!url->priv->foreground_modified)
575
GdkColor blue = { 0, 0x0000, 0x0000, 0xffff };
577
PangoAttribute *foreground;
579
gtk_widget_style_get (GTK_WIDGET (url),
580
"url-color", &url_color,
585
foreground = pango_attr_foreground_new (url_color->red,
589
vino_url_change_attribute_internal (url, foreground, TRUE);
591
if (url_color != &blue)
592
gdk_color_free (url_color);
596
vino_url_unset_attribute_type (url, PANGO_ATTR_FOREGROUND);
602
vino_url_set_address (VinoURL *url,
605
g_return_if_fail (VINO_IS_URL (url));
607
g_free (url->priv->address);
608
url->priv->address = g_strdup (address);
610
g_object_notify (G_OBJECT (url), "address");
613
G_CONST_RETURN char *
614
vino_url_get_address (VinoURL *url)
616
g_return_val_if_fail (VINO_IS_URL (url), NULL);
618
return url->priv->address;
622
vino_url_set_tooltip (VinoURL *url,
625
g_return_if_fail (VINO_IS_URL (url));
627
g_free (url->priv->tooltip);
628
url->priv->tooltip = g_strdup (tooltip);
630
gtk_tooltips_set_tip (url->priv->tooltips,
635
g_object_notify (G_OBJECT (url), "tooltip");
638
G_CONST_RETURN char *
639
vino_url_get_tooltip (VinoURL *url)
641
g_return_val_if_fail (VINO_IS_URL (url), NULL);
643
return url->priv->tooltip;
646
/* Debugging; There should probably be a nicer API
647
* for fiddling with the attributes on a label;
648
* Either that or I'm being dumb again;
650
#define SANITY_CHECK_ATTRIBUTES
653
sanity_check_attributes_notify (VinoURL *url)
655
PangoAttrList *attrs;
657
attrs = gtk_label_get_attributes (GTK_LABEL (url));
658
if (attrs != url->priv->attributes)
660
g_warning ("Label attributes changed, resetting");
661
gtk_label_set_attributes (GTK_LABEL (url),
662
url->priv->attributes);
667
sanity_check_attributes (VinoURL *url)
669
#ifdef SANITY_CHECK_ATTRIBUTES
670
g_signal_connect (url, "notify::attributes",
671
G_CALLBACK (sanity_check_attributes_notify), NULL);
672
#endif /* SANITY_CHECK_ATTRIBUTES */
676
vino_url_change_attribute_internal (VinoURL *url,
677
PangoAttribute *attribute,
680
if (!url->priv->attributes)
682
url->priv->attributes = pango_attr_list_new ();
683
gtk_label_set_attributes (GTK_LABEL (url), url->priv->attributes);
684
sanity_check_attributes (url);
687
attribute->start_index = 0;
688
attribute->end_index = G_MAXINT;
692
if (attribute->klass->type == PANGO_ATTR_FOREGROUND)
693
url->priv->foreground_modified = TRUE;
695
if (attribute->klass->type == PANGO_ATTR_UNDERLINE)
696
url->priv->underline_modified = TRUE;
699
pango_attr_list_change (url->priv->attributes, attribute);
703
vino_url_change_attribute (VinoURL *url,
704
PangoAttribute *attribute)
706
g_return_if_fail (VINO_IS_URL (url));
708
vino_url_change_attribute_internal (url, attribute, FALSE);
712
filter_out_attr_type (PangoAttribute *attribute,
715
PangoAttrType attr_type = GPOINTER_TO_INT (data);
717
return attribute->klass->type == attr_type;
721
vino_url_unset_attribute_type (VinoURL *url,
722
PangoAttrType attr_type)
724
g_return_if_fail (VINO_IS_URL (url));
726
if (url->priv->attributes)
728
PangoAttrList *filtered;
731
pango_attr_list_filter (url->priv->attributes,
732
(PangoAttrFilterFunc) filter_out_attr_type,
733
GINT_TO_POINTER (attr_type));
735
pango_attr_list_unref (filtered);