1 | /* GTK - The GIMP Toolkit |
2 | * gtklinkbutton.c - a hyperlink-enabled button |
3 | * |
4 | * Copyright (C) 2006 Emmanuele Bassi <ebassi@gmail.com> |
5 | * All rights reserved. |
6 | * |
7 | * Based on gnome-href code by: |
8 | * James Henstridge <james@daa.com.au> |
9 | * |
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. |
14 | * |
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. |
19 | * |
20 | * You should have received a copy of the GNU Library General Public |
21 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
22 | */ |
23 | |
24 | /** |
25 | * GtkLinkButton: |
26 | * |
27 | * A `GtkLinkButton` is a button with a hyperlink. |
28 | * |
29 | * ![An example GtkLinkButton](link-button.png) |
30 | * |
31 | * It is useful to show quick links to resources. |
32 | * |
33 | * A link button is created by calling either [ctor@Gtk.LinkButton.new] or |
34 | * [ctor@Gtk.LinkButton.new_with_label]. If using the former, the URI you |
35 | * pass to the constructor is used as a label for the widget. |
36 | * |
37 | * The URI bound to a `GtkLinkButton` can be set specifically using |
38 | * [method@Gtk.LinkButton.set_uri]. |
39 | * |
40 | * By default, `GtkLinkButton` calls [func@Gtk.show_uri] when the button |
41 | * is clicked. This behaviour can be overridden by connecting to the |
42 | * [signal@Gtk.LinkButton::activate-link] signal and returning %TRUE from |
43 | * the signal handler. |
44 | * |
45 | * # CSS nodes |
46 | * |
47 | * `GtkLinkButton` has a single CSS node with name button. To differentiate |
48 | * it from a plain `GtkButton`, it gets the .link style class. |
49 | * |
50 | * # Accessibility |
51 | * |
52 | * `GtkLinkButton` uses the %GTK_ACCESSIBLE_ROLE_LINK role. |
53 | */ |
54 | |
55 | #include "config.h" |
56 | |
57 | #include "gtklinkbutton.h" |
58 | |
59 | #include "gtkdragsource.h" |
60 | #include "gtkgestureclick.h" |
61 | #include "gtkgesturesingle.h" |
62 | #include "gtkintl.h" |
63 | #include "gtklabel.h" |
64 | #include "gtkmain.h" |
65 | #include "gtkmarshalers.h" |
66 | #include "gtkpopovermenu.h" |
67 | #include "gtkprivate.h" |
68 | #include "gtkshow.h" |
69 | #include "gtksizerequest.h" |
70 | #include "gtktooltip.h" |
71 | #include "gtkwidgetprivate.h" |
72 | |
73 | #include <string.h> |
74 | |
75 | typedef struct _GtkLinkButtonClass GtkLinkButtonClass; |
76 | |
77 | struct _GtkLinkButton |
78 | { |
79 | /*< private >*/ |
80 | GtkButton parent_instance; |
81 | |
82 | char *uri; |
83 | gboolean visited; |
84 | GtkWidget *; |
85 | }; |
86 | |
87 | struct _GtkLinkButtonClass |
88 | { |
89 | /*< private >*/ |
90 | GtkButtonClass parent_class; |
91 | |
92 | /*< public >*/ |
93 | gboolean (* activate_link) (GtkLinkButton *button); |
94 | }; |
95 | |
96 | enum |
97 | { |
98 | PROP_0, |
99 | PROP_URI, |
100 | PROP_VISITED |
101 | }; |
102 | |
103 | enum |
104 | { |
105 | ACTIVATE_LINK, |
106 | |
107 | LAST_SIGNAL |
108 | }; |
109 | |
110 | static void gtk_link_button_finalize (GObject *object); |
111 | static void gtk_link_button_get_property (GObject *object, |
112 | guint prop_id, |
113 | GValue *value, |
114 | GParamSpec *pspec); |
115 | static void gtk_link_button_set_property (GObject *object, |
116 | guint prop_id, |
117 | const GValue *value, |
118 | GParamSpec *pspec); |
119 | static void gtk_link_button_clicked (GtkButton *button); |
120 | static void gtk_link_button_popup_menu (GtkWidget *widget, |
121 | const char *action_name, |
122 | GVariant *parameters); |
123 | static gboolean gtk_link_button_query_tooltip_cb (GtkWidget *widget, |
124 | int x, |
125 | int y, |
126 | gboolean keyboard_tip, |
127 | GtkTooltip *tooltip, |
128 | gpointer data); |
129 | static void gtk_link_button_pressed_cb (GtkGestureClick *gesture, |
130 | int n_press, |
131 | double x, |
132 | double y, |
133 | gpointer user_data); |
134 | |
135 | static gboolean gtk_link_button_activate_link (GtkLinkButton *link_button); |
136 | |
137 | static const char *link_drop_types[] = { |
138 | "text/uri-list" , |
139 | "_NETSCAPE_URL" |
140 | }; |
141 | |
142 | static guint link_signals[LAST_SIGNAL] = { 0, }; |
143 | |
144 | G_DEFINE_TYPE (GtkLinkButton, gtk_link_button, GTK_TYPE_BUTTON) |
145 | |
146 | static void |
147 | gtk_link_button_activate_clipboard_copy (GtkWidget *widget, |
148 | const char *name, |
149 | GVariant *parameter) |
150 | { |
151 | GtkLinkButton *link_button = GTK_LINK_BUTTON (widget); |
152 | |
153 | gdk_clipboard_set_text (clipboard: gtk_widget_get_clipboard (widget), text: link_button->uri); |
154 | } |
155 | |
156 | static void |
157 | gtk_link_button_class_init (GtkLinkButtonClass *klass) |
158 | { |
159 | GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
160 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); |
161 | GtkButtonClass *button_class = GTK_BUTTON_CLASS (klass); |
162 | |
163 | gobject_class->set_property = gtk_link_button_set_property; |
164 | gobject_class->get_property = gtk_link_button_get_property; |
165 | gobject_class->finalize = gtk_link_button_finalize; |
166 | |
167 | button_class->clicked = gtk_link_button_clicked; |
168 | |
169 | klass->activate_link = gtk_link_button_activate_link; |
170 | |
171 | /** |
172 | * GtkLinkButton:uri: (attributes org.gtk.Property.get=gtk_link_button_get_uri org.gtk.Property.set=gtk_link_button_set_uri) |
173 | * |
174 | * The URI bound to this button. |
175 | */ |
176 | g_object_class_install_property (oclass: gobject_class, |
177 | property_id: PROP_URI, |
178 | pspec: g_param_spec_string (name: "uri" , |
179 | P_("URI" ), |
180 | P_("The URI bound to this button" ), |
181 | NULL, |
182 | GTK_PARAM_READWRITE)); |
183 | |
184 | /** |
185 | * GtkLinkButton:visited: (attributes org.gtk.Property.get=gtk_link_button_get_visited org.gtk.Property.set=gtk_link_button_set_visited) |
186 | * |
187 | * The 'visited' state of this button. |
188 | * |
189 | * A visited link is drawn in a different color. |
190 | */ |
191 | g_object_class_install_property (oclass: gobject_class, |
192 | property_id: PROP_VISITED, |
193 | pspec: g_param_spec_boolean (name: "visited" , |
194 | P_("Visited" ), |
195 | P_("Whether this link has been visited." ), |
196 | FALSE, |
197 | GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY)); |
198 | |
199 | /** |
200 | * GtkLinkButton::activate-link: |
201 | * @button: the `GtkLinkButton` that emitted the signal |
202 | * |
203 | * Emitted each time the `GtkLinkButton` is clicked. |
204 | * |
205 | * The default handler will call [func@Gtk.show_uri] with the URI |
206 | * stored inside the [property@Gtk.LinkButton:uri] property. |
207 | * |
208 | * To override the default behavior, you can connect to the |
209 | * ::activate-link signal and stop the propagation of the signal |
210 | * by returning %TRUE from your handler. |
211 | * |
212 | * Returns: %TRUE if the signal has been handled |
213 | */ |
214 | link_signals[ACTIVATE_LINK] = |
215 | g_signal_new (I_("activate-link" ), |
216 | G_TYPE_FROM_CLASS (klass), |
217 | signal_flags: G_SIGNAL_RUN_LAST, |
218 | G_STRUCT_OFFSET (GtkLinkButtonClass, activate_link), |
219 | accumulator: _gtk_boolean_handled_accumulator, NULL, |
220 | c_marshaller: _gtk_marshal_BOOLEAN__VOID, |
221 | G_TYPE_BOOLEAN, n_params: 0); |
222 | |
223 | gtk_widget_class_set_css_name (widget_class, I_("button" )); |
224 | gtk_widget_class_set_accessible_role (widget_class, accessible_role: GTK_ACCESSIBLE_ROLE_LINK); |
225 | |
226 | /** |
227 | * GtkLinkButton|clipboard.copy: |
228 | * |
229 | * Copies the url to the clipboard. |
230 | */ |
231 | gtk_widget_class_install_action (widget_class, action_name: "clipboard.copy" , NULL, |
232 | activate: gtk_link_button_activate_clipboard_copy); |
233 | |
234 | /** |
235 | * GtkLinkButton|menu.popup: |
236 | * |
237 | * Opens the context menu. |
238 | */ |
239 | gtk_widget_class_install_action (widget_class, action_name: "menu.popup" , NULL, activate: gtk_link_button_popup_menu); |
240 | |
241 | gtk_widget_class_add_binding_action (widget_class, |
242 | GDK_KEY_F10, mods: GDK_SHIFT_MASK, |
243 | action_name: "menu.popup" , |
244 | NULL); |
245 | gtk_widget_class_add_binding_action (widget_class, |
246 | GDK_KEY_Menu, mods: 0, |
247 | action_name: "menu.popup" , |
248 | NULL); |
249 | } |
250 | |
251 | static GMenuModel * |
252 | (void) |
253 | { |
254 | GMenu *, *section; |
255 | |
256 | menu = g_menu_new (); |
257 | |
258 | section = g_menu_new (); |
259 | g_menu_append (menu: section, _("_Copy URL" ), detailed_action: "clipboard.copy" ); |
260 | g_menu_append_section (menu, NULL, G_MENU_MODEL (section)); |
261 | g_object_unref (object: section); |
262 | |
263 | return G_MENU_MODEL (menu); |
264 | } |
265 | |
266 | #define GTK_TYPE_LINK_CONTENT (gtk_link_content_get_type ()) |
267 | #define GTK_LINK_CONTENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_LINK_CONTENT, GtkLinkContent)) |
268 | |
269 | typedef struct _GtkLinkContent GtkLinkContent; |
270 | typedef struct _GtkLinkContentClass GtkLinkContentClass; |
271 | |
272 | struct _GtkLinkContent |
273 | { |
274 | GdkContentProvider parent; |
275 | GtkLinkButton *link; |
276 | }; |
277 | |
278 | struct _GtkLinkContentClass |
279 | { |
280 | GdkContentProviderClass parent_class; |
281 | }; |
282 | |
283 | GType gtk_link_content_get_type (void) G_GNUC_CONST; |
284 | |
285 | G_DEFINE_TYPE (GtkLinkContent, gtk_link_content, GDK_TYPE_CONTENT_PROVIDER) |
286 | |
287 | static GdkContentFormats * |
288 | gtk_link_content_ref_formats (GdkContentProvider *provider) |
289 | { |
290 | GtkLinkContent *content = GTK_LINK_CONTENT (provider); |
291 | |
292 | if (content->link) |
293 | return gdk_content_formats_union (first: gdk_content_formats_new_for_gtype (G_TYPE_STRING), |
294 | second: gdk_content_formats_new (mime_types: link_drop_types, G_N_ELEMENTS (link_drop_types))); |
295 | else |
296 | return gdk_content_formats_new (NULL, n_mime_types: 0); |
297 | } |
298 | |
299 | static gboolean |
300 | gtk_link_content_get_value (GdkContentProvider *provider, |
301 | GValue *value, |
302 | GError **error) |
303 | { |
304 | GtkLinkContent *content = GTK_LINK_CONTENT (provider); |
305 | |
306 | if (G_VALUE_HOLDS (value, G_TYPE_STRING) && |
307 | content->link != NULL) |
308 | { |
309 | char *uri; |
310 | |
311 | uri = g_strdup_printf (format: "%s\r\n" , content->link->uri); |
312 | g_value_set_string (value, v_string: uri); |
313 | g_free (mem: uri); |
314 | |
315 | return TRUE; |
316 | } |
317 | |
318 | return GDK_CONTENT_PROVIDER_CLASS (gtk_link_content_parent_class)->get_value (provider, value, error); |
319 | } |
320 | |
321 | static void |
322 | gtk_link_content_class_init (GtkLinkContentClass *class) |
323 | { |
324 | GdkContentProviderClass *provider_class = GDK_CONTENT_PROVIDER_CLASS (class); |
325 | |
326 | provider_class->ref_formats = gtk_link_content_ref_formats; |
327 | provider_class->get_value = gtk_link_content_get_value; |
328 | } |
329 | |
330 | static void |
331 | gtk_link_content_init (GtkLinkContent *content) |
332 | { |
333 | } |
334 | |
335 | static void |
336 | gtk_link_button_init (GtkLinkButton *link_button) |
337 | { |
338 | GtkGesture *gesture; |
339 | GdkContentProvider *content; |
340 | GtkDragSource *source; |
341 | |
342 | gtk_button_set_has_frame (GTK_BUTTON (link_button), FALSE); |
343 | gtk_widget_set_state_flags (GTK_WIDGET (link_button), flags: GTK_STATE_FLAG_LINK, FALSE); |
344 | gtk_widget_set_has_tooltip (GTK_WIDGET (link_button), TRUE); |
345 | |
346 | g_signal_connect (link_button, "query-tooltip" , |
347 | G_CALLBACK (gtk_link_button_query_tooltip_cb), NULL); |
348 | |
349 | source = gtk_drag_source_new (); |
350 | content = g_object_new (GTK_TYPE_LINK_CONTENT, NULL); |
351 | GTK_LINK_CONTENT (content)->link = link_button; |
352 | gtk_drag_source_set_content (source, content); |
353 | g_object_unref (object: content); |
354 | gtk_widget_add_controller (GTK_WIDGET (link_button), GTK_EVENT_CONTROLLER (source)); |
355 | |
356 | gesture = gtk_gesture_click_new (); |
357 | gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture), FALSE); |
358 | gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), button: 0); |
359 | gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture), phase: GTK_PHASE_BUBBLE); |
360 | g_signal_connect (gesture, "pressed" , G_CALLBACK (gtk_link_button_pressed_cb), |
361 | link_button); |
362 | gtk_widget_add_controller (GTK_WIDGET (link_button), GTK_EVENT_CONTROLLER (gesture)); |
363 | |
364 | gtk_widget_add_css_class (GTK_WIDGET (link_button), css_class: "link" ); |
365 | |
366 | gtk_widget_set_cursor_from_name (GTK_WIDGET (link_button), name: "pointer" ); |
367 | } |
368 | |
369 | static void |
370 | gtk_link_button_finalize (GObject *object) |
371 | { |
372 | GtkLinkButton *link_button = GTK_LINK_BUTTON (object); |
373 | |
374 | g_free (mem: link_button->uri); |
375 | |
376 | g_clear_pointer (&link_button->popup_menu, gtk_widget_unparent); |
377 | |
378 | G_OBJECT_CLASS (gtk_link_button_parent_class)->finalize (object); |
379 | } |
380 | |
381 | static void |
382 | gtk_link_button_get_property (GObject *object, |
383 | guint prop_id, |
384 | GValue *value, |
385 | GParamSpec *pspec) |
386 | { |
387 | GtkLinkButton *link_button = GTK_LINK_BUTTON (object); |
388 | |
389 | switch (prop_id) |
390 | { |
391 | case PROP_URI: |
392 | g_value_set_string (value, v_string: link_button->uri); |
393 | break; |
394 | case PROP_VISITED: |
395 | g_value_set_boolean (value, v_boolean: link_button->visited); |
396 | break; |
397 | default: |
398 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
399 | break; |
400 | } |
401 | } |
402 | |
403 | static void |
404 | gtk_link_button_set_property (GObject *object, |
405 | guint prop_id, |
406 | const GValue *value, |
407 | GParamSpec *pspec) |
408 | { |
409 | GtkLinkButton *link_button = GTK_LINK_BUTTON (object); |
410 | |
411 | switch (prop_id) |
412 | { |
413 | case PROP_URI: |
414 | gtk_link_button_set_uri (link_button, uri: g_value_get_string (value)); |
415 | break; |
416 | case PROP_VISITED: |
417 | gtk_link_button_set_visited (link_button, visited: g_value_get_boolean (value)); |
418 | break; |
419 | default: |
420 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
421 | break; |
422 | } |
423 | } |
424 | |
425 | static void |
426 | (GtkLinkButton *link_button, |
427 | double x, |
428 | double y) |
429 | { |
430 | if (!link_button->popup_menu) |
431 | { |
432 | GMenuModel *model; |
433 | |
434 | model = gtk_link_button_get_menu_model (); |
435 | link_button->popup_menu = gtk_popover_menu_new_from_model (model); |
436 | gtk_widget_set_parent (widget: link_button->popup_menu, GTK_WIDGET (link_button)); |
437 | gtk_popover_set_position (GTK_POPOVER (link_button->popup_menu), position: GTK_POS_BOTTOM); |
438 | |
439 | gtk_popover_set_has_arrow (GTK_POPOVER (link_button->popup_menu), FALSE); |
440 | gtk_widget_set_halign (widget: link_button->popup_menu, align: GTK_ALIGN_START); |
441 | |
442 | g_object_unref (object: model); |
443 | } |
444 | |
445 | if (x != -1 && y != -1) |
446 | { |
447 | GdkRectangle rect = { x, y, 1, 1 }; |
448 | gtk_popover_set_pointing_to (GTK_POPOVER (link_button->popup_menu), rect: &rect); |
449 | } |
450 | else |
451 | gtk_popover_set_pointing_to (GTK_POPOVER (link_button->popup_menu), NULL); |
452 | |
453 | gtk_popover_popup (GTK_POPOVER (link_button->popup_menu)); |
454 | } |
455 | |
456 | static void |
457 | gtk_link_button_pressed_cb (GtkGestureClick *gesture, |
458 | int n_press, |
459 | double x, |
460 | double y, |
461 | gpointer user_data) |
462 | { |
463 | GtkLinkButton *link_button = user_data; |
464 | GdkEventSequence *sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture)); |
465 | GdkEvent *event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence); |
466 | |
467 | if (!gtk_widget_has_focus (GTK_WIDGET (link_button))) |
468 | gtk_widget_grab_focus (GTK_WIDGET (link_button)); |
469 | |
470 | if (gdk_event_triggers_context_menu (event) && |
471 | link_button->uri != NULL) |
472 | { |
473 | gtk_link_button_do_popup (link_button, x, y); |
474 | gtk_gesture_set_state (GTK_GESTURE (gesture), state: GTK_EVENT_SEQUENCE_CLAIMED); |
475 | } |
476 | else |
477 | { |
478 | gtk_gesture_set_state (GTK_GESTURE (gesture), state: GTK_EVENT_SEQUENCE_DENIED); |
479 | } |
480 | } |
481 | |
482 | static gboolean |
483 | gtk_link_button_activate_link (GtkLinkButton *link_button) |
484 | { |
485 | GtkWidget *toplevel; |
486 | |
487 | toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (link_button))); |
488 | |
489 | gtk_show_uri (GTK_WINDOW (toplevel), uri: link_button->uri, GDK_CURRENT_TIME); |
490 | gtk_link_button_set_visited (link_button, TRUE); |
491 | |
492 | return TRUE; |
493 | } |
494 | |
495 | static void |
496 | gtk_link_button_clicked (GtkButton *button) |
497 | { |
498 | gboolean retval = FALSE; |
499 | |
500 | g_signal_emit (instance: button, signal_id: link_signals[ACTIVATE_LINK], detail: 0, &retval); |
501 | } |
502 | |
503 | static void |
504 | (GtkWidget *widget, |
505 | const char *action_name, |
506 | GVariant *parameters) |
507 | { |
508 | gtk_link_button_do_popup (GTK_LINK_BUTTON (widget), x: -1, y: -1); |
509 | } |
510 | |
511 | /** |
512 | * gtk_link_button_new: |
513 | * @uri: a valid URI |
514 | * |
515 | * Creates a new `GtkLinkButton` with the URI as its text. |
516 | * |
517 | * Returns: a new link button widget. |
518 | */ |
519 | GtkWidget * |
520 | gtk_link_button_new (const char *uri) |
521 | { |
522 | char *utf8_uri = NULL; |
523 | GtkWidget *retval; |
524 | |
525 | g_return_val_if_fail (uri != NULL, NULL); |
526 | |
527 | if (g_utf8_validate (str: uri, max_len: -1, NULL)) |
528 | { |
529 | utf8_uri = g_strdup (str: uri); |
530 | } |
531 | else |
532 | { |
533 | GError *conv_err = NULL; |
534 | |
535 | utf8_uri = g_locale_to_utf8 (opsysstring: uri, len: -1, NULL, NULL, error: &conv_err); |
536 | if (conv_err) |
537 | { |
538 | g_warning ("Attempting to convert URI '%s' to UTF-8, but failed " |
539 | "with error: %s" , |
540 | uri, |
541 | conv_err->message); |
542 | g_error_free (error: conv_err); |
543 | |
544 | utf8_uri = g_strdup (_("Invalid URI" )); |
545 | } |
546 | } |
547 | |
548 | retval = g_object_new (GTK_TYPE_LINK_BUTTON, |
549 | first_property_name: "label" , utf8_uri, |
550 | "uri" , uri, |
551 | NULL); |
552 | |
553 | g_free (mem: utf8_uri); |
554 | |
555 | return retval; |
556 | } |
557 | |
558 | /** |
559 | * gtk_link_button_new_with_label: |
560 | * @uri: a valid URI |
561 | * @label: (nullable): the text of the button |
562 | * |
563 | * Creates a new `GtkLinkButton` containing a label. |
564 | * |
565 | * Returns: (transfer none): a new link button widget. |
566 | */ |
567 | GtkWidget * |
568 | gtk_link_button_new_with_label (const char *uri, |
569 | const char *label) |
570 | { |
571 | GtkWidget *retval; |
572 | |
573 | g_return_val_if_fail (uri != NULL, NULL); |
574 | |
575 | if (!label) |
576 | return gtk_link_button_new (uri); |
577 | |
578 | retval = g_object_new (GTK_TYPE_LINK_BUTTON, |
579 | first_property_name: "label" , label, |
580 | "uri" , uri, |
581 | NULL); |
582 | |
583 | return retval; |
584 | } |
585 | |
586 | static gboolean |
587 | gtk_link_button_query_tooltip_cb (GtkWidget *widget, |
588 | int x, |
589 | int y, |
590 | gboolean keyboard_tip, |
591 | GtkTooltip *tooltip, |
592 | gpointer data) |
593 | { |
594 | GtkLinkButton *link_button = GTK_LINK_BUTTON (widget); |
595 | const char *label, *uri; |
596 | const char *text, *markup; |
597 | |
598 | label = gtk_button_get_label (GTK_BUTTON (link_button)); |
599 | uri = link_button->uri; |
600 | text = gtk_widget_get_tooltip_text (widget); |
601 | markup = gtk_widget_get_tooltip_markup (widget); |
602 | |
603 | if (text == NULL && |
604 | markup == NULL && |
605 | label && *label != '\0' && uri && strcmp (s1: label, s2: uri) != 0) |
606 | { |
607 | gtk_tooltip_set_text (tooltip, text: uri); |
608 | return TRUE; |
609 | } |
610 | |
611 | return FALSE; |
612 | } |
613 | |
614 | /** |
615 | * gtk_link_button_set_uri: (attributes org.gtk.Method.set_property=uri) |
616 | * @link_button: a `GtkLinkButton` |
617 | * @uri: a valid URI |
618 | * |
619 | * Sets @uri as the URI where the `GtkLinkButton` points. |
620 | * |
621 | * As a side-effect this unsets the “visited” state of the button. |
622 | */ |
623 | void |
624 | gtk_link_button_set_uri (GtkLinkButton *link_button, |
625 | const char *uri) |
626 | { |
627 | g_return_if_fail (GTK_IS_LINK_BUTTON (link_button)); |
628 | g_return_if_fail (uri != NULL); |
629 | |
630 | g_free (mem: link_button->uri); |
631 | link_button->uri = g_strdup (str: uri); |
632 | |
633 | g_object_notify (G_OBJECT (link_button), property_name: "uri" ); |
634 | |
635 | gtk_link_button_set_visited (link_button, FALSE); |
636 | } |
637 | |
638 | /** |
639 | * gtk_link_button_get_uri: (attributes org.gtk.Method.get_property=uri) |
640 | * @link_button: a `GtkLinkButton` |
641 | * |
642 | * Retrieves the URI of the `GtkLinkButton`. |
643 | * |
644 | * Returns: a valid URI. The returned string is owned by the link button |
645 | * and should not be modified or freed. |
646 | */ |
647 | const char * |
648 | gtk_link_button_get_uri (GtkLinkButton *link_button) |
649 | { |
650 | g_return_val_if_fail (GTK_IS_LINK_BUTTON (link_button), NULL); |
651 | |
652 | return link_button->uri; |
653 | } |
654 | |
655 | /** |
656 | * gtk_link_button_set_visited: (attributes org.gtk.Method.set_property=visited) |
657 | * @link_button: a `GtkLinkButton` |
658 | * @visited: the new “visited” state |
659 | * |
660 | * Sets the “visited” state of the `GtkLinkButton`. |
661 | * |
662 | * See [method@Gtk.LinkButton.get_visited] for more details. |
663 | */ |
664 | void |
665 | gtk_link_button_set_visited (GtkLinkButton *link_button, |
666 | gboolean visited) |
667 | { |
668 | g_return_if_fail (GTK_IS_LINK_BUTTON (link_button)); |
669 | |
670 | visited = visited != FALSE; |
671 | |
672 | if (link_button->visited != visited) |
673 | { |
674 | link_button->visited = visited; |
675 | |
676 | if (visited) |
677 | { |
678 | gtk_widget_unset_state_flags (GTK_WIDGET (link_button), flags: GTK_STATE_FLAG_LINK); |
679 | gtk_widget_set_state_flags (GTK_WIDGET (link_button), flags: GTK_STATE_FLAG_VISITED, FALSE); |
680 | } |
681 | else |
682 | { |
683 | gtk_widget_unset_state_flags (GTK_WIDGET (link_button), flags: GTK_STATE_FLAG_VISITED); |
684 | gtk_widget_set_state_flags (GTK_WIDGET (link_button), flags: GTK_STATE_FLAG_LINK, FALSE); |
685 | } |
686 | |
687 | g_object_notify (G_OBJECT (link_button), property_name: "visited" ); |
688 | } |
689 | } |
690 | |
691 | /** |
692 | * gtk_link_button_get_visited: (attributes org.gtk.Method.get_property=visited) |
693 | * @link_button: a `GtkLinkButton` |
694 | * |
695 | * Retrieves the “visited” state of the `GtkLinkButton`. |
696 | * |
697 | * The button becomes visited when it is clicked. If the URI |
698 | * is changed on the button, the “visited” state is unset again. |
699 | * |
700 | * The state may also be changed using [method@Gtk.LinkButton.set_visited]. |
701 | * |
702 | * Returns: %TRUE if the link has been visited, %FALSE otherwise |
703 | */ |
704 | gboolean |
705 | gtk_link_button_get_visited (GtkLinkButton *link_button) |
706 | { |
707 | g_return_val_if_fail (GTK_IS_LINK_BUTTON (link_button), FALSE); |
708 | |
709 | return link_button->visited; |
710 | } |
711 | |