1 /*
2  * Copyright (C) 2018 Purism SPC
3  *
4  * SPDX-License-Identifier: LGPL-2.1+
5  */
6 
7 #include "config.h"
8 #include "hdy-action-row.h"
9 
10 #include <glib/gi18n-lib.h>
11 
12 /**
13  * SECTION:hdy-action-row
14  * @short_description: A #GtkListBox row used to present actions.
15  * @Title: HdyActionRow
16  *
17  * The #HdyActionRow widget can have a title, a subtitle and an icon. The row
18  * can receive action widgets at its end, prefix widgets at its start or widgets
19  * below it.
20  *
21  * Note that action widgets are packed starting from the end.
22  *
23  * It is convenient to present a list of preferences and their related actions.
24  *
25  * # HdyActionRow as GtkBuildable
26  *
27  * The GtkWindow implementation of the GtkBuildable interface supports setting a
28  * child as an action widget by specifying “action” as the “type” attribute of a
29  * &lt;child&gt; element.
30  *
31  * It also supports setting a child as a prefix widget by specifying “prefix” as
32  * the “type” attribute of a &lt;child&gt; element.
33  *
34  * Since: 0.0.6
35  */
36 
37 typedef struct
38 {
39   GtkBox *box;
40   GtkBox *header;
41   GtkImage *image;
42   GtkBox *prefixes;
43   GtkLabel *subtitle;
44   GtkLabel *title;
45   GtkBox *title_box;
46 
47   GtkWidget *previous_parent;
48 
49   gboolean use_underline;
50   GtkWidget *activatable_widget;
51 } HdyActionRowPrivate;
52 
53 static void hdy_action_row_buildable_init (GtkBuildableIface *iface);
54 
55 G_DEFINE_TYPE_WITH_CODE (HdyActionRow, hdy_action_row, HDY_TYPE_PREFERENCES_ROW,
56                          G_ADD_PRIVATE (HdyActionRow)
57                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
58                          hdy_action_row_buildable_init))
59 
60 static GtkBuildableIface *parent_buildable_iface;
61 
62 enum {
63   PROP_0,
64   PROP_ICON_NAME,
65   PROP_ACTIVATABLE_WIDGET,
66   PROP_SUBTITLE,
67   PROP_TITLE,
68   PROP_USE_UNDERLINE,
69   LAST_PROP,
70 };
71 
72 static GParamSpec *props[LAST_PROP];
73 
74 static void
row_activated_cb(HdyActionRow * self,GtkListBoxRow * row)75 row_activated_cb (HdyActionRow  *self,
76                   GtkListBoxRow *row)
77 {
78   /* No need to use GTK_LIST_BOX_ROW() for a pointer comparison. */
79   if ((GtkListBoxRow *) self == row)
80     hdy_action_row_activate (self);
81 }
82 
83 static void
parent_cb(HdyActionRow * self)84 parent_cb (HdyActionRow *self)
85 {
86   HdyActionRowPrivate *priv = hdy_action_row_get_instance_private (self);
87   GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (self));
88 
89   if (priv->previous_parent != NULL) {
90     g_signal_handlers_disconnect_by_func (priv->previous_parent, G_CALLBACK (row_activated_cb), self);
91     priv->previous_parent = NULL;
92   }
93 
94   if (parent == NULL || !GTK_IS_LIST_BOX (parent))
95     return;
96 
97   priv->previous_parent = parent;
98   g_signal_connect_swapped (parent, "row-activated", G_CALLBACK (row_activated_cb), self);
99 }
100 
101 static void
update_subtitle_visibility(HdyActionRow * self)102 update_subtitle_visibility (HdyActionRow *self)
103 {
104   HdyActionRowPrivate *priv = hdy_action_row_get_instance_private (self);
105 
106   gtk_widget_set_visible (GTK_WIDGET (priv->subtitle),
107                           gtk_label_get_text (priv->subtitle) != NULL &&
108                           g_strcmp0 (gtk_label_get_text (priv->subtitle), "") != 0);
109 }
110 
111 static void
hdy_action_row_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)112 hdy_action_row_get_property (GObject    *object,
113                              guint       prop_id,
114                              GValue     *value,
115                              GParamSpec *pspec)
116 {
117   HdyActionRow *self = HDY_ACTION_ROW (object);
118 
119   switch (prop_id) {
120   case PROP_ICON_NAME:
121     g_value_set_string (value, hdy_action_row_get_icon_name (self));
122     break;
123   case PROP_ACTIVATABLE_WIDGET:
124     g_value_set_object (value, (GObject *) hdy_action_row_get_activatable_widget (self));
125     break;
126   case PROP_SUBTITLE:
127     g_value_set_string (value, hdy_action_row_get_subtitle (self));
128     break;
129   case PROP_TITLE:
130     g_value_set_string (value, hdy_action_row_get_title (self));
131     break;
132   case PROP_USE_UNDERLINE:
133     g_value_set_boolean (value, hdy_action_row_get_use_underline (self));
134     break;
135   default:
136     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
137   }
138 }
139 
140 static void
hdy_action_row_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)141 hdy_action_row_set_property (GObject      *object,
142                              guint         prop_id,
143                              const GValue *value,
144                              GParamSpec   *pspec)
145 {
146   HdyActionRow *self = HDY_ACTION_ROW (object);
147 
148   switch (prop_id) {
149   case PROP_ICON_NAME:
150     hdy_action_row_set_icon_name (self, g_value_get_string (value));
151     break;
152   case PROP_ACTIVATABLE_WIDGET:
153     hdy_action_row_set_activatable_widget (self, (GtkWidget*) g_value_get_object (value));
154     break;
155   case PROP_SUBTITLE:
156     hdy_action_row_set_subtitle (self, g_value_get_string (value));
157     break;
158   case PROP_TITLE:
159     hdy_action_row_set_title (self, g_value_get_string (value));
160     break;
161   case PROP_USE_UNDERLINE:
162     hdy_action_row_set_use_underline (self, g_value_get_boolean (value));
163     break;
164   default:
165     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
166   }
167 }
168 
169 static void
hdy_action_row_dispose(GObject * object)170 hdy_action_row_dispose (GObject *object)
171 {
172   HdyActionRow *self = HDY_ACTION_ROW (object);
173   HdyActionRowPrivate *priv = hdy_action_row_get_instance_private (self);
174 
175   if (priv->previous_parent != NULL) {
176     g_signal_handlers_disconnect_by_func (priv->previous_parent, G_CALLBACK (row_activated_cb), self);
177     priv->previous_parent = NULL;
178   }
179 
180   G_OBJECT_CLASS (hdy_action_row_parent_class)->dispose (object);
181 }
182 
183 static void
hdy_action_row_show_all(GtkWidget * widget)184 hdy_action_row_show_all (GtkWidget *widget)
185 {
186   HdyActionRow *self = HDY_ACTION_ROW (widget);
187   HdyActionRowPrivate *priv;
188 
189   g_return_if_fail (HDY_IS_ACTION_ROW (self));
190 
191   priv = hdy_action_row_get_instance_private (self);
192 
193   gtk_container_foreach (GTK_CONTAINER (priv->prefixes),
194                          (GtkCallback) gtk_widget_show_all,
195                          NULL);
196   GTK_WIDGET_CLASS (hdy_action_row_parent_class)->show_all (widget);
197 }
198 
199 static void
hdy_action_row_destroy(GtkWidget * widget)200 hdy_action_row_destroy (GtkWidget *widget)
201 {
202   HdyActionRow *self = HDY_ACTION_ROW (widget);
203   HdyActionRowPrivate *priv = hdy_action_row_get_instance_private (self);
204 
205   if (priv->box) {
206     gtk_widget_destroy (GTK_WIDGET (priv->box));
207     priv->box = NULL;
208   }
209 
210   hdy_action_row_set_activatable_widget (self, NULL);
211 
212   priv->prefixes = NULL;
213   priv->header = NULL;
214 
215   GTK_WIDGET_CLASS (hdy_action_row_parent_class)->destroy (widget);
216 }
217 
218 static void
hdy_action_row_add(GtkContainer * container,GtkWidget * child)219 hdy_action_row_add (GtkContainer *container,
220                     GtkWidget    *child)
221 {
222   HdyActionRow *self = HDY_ACTION_ROW (container);
223   HdyActionRowPrivate *priv = hdy_action_row_get_instance_private (self);
224 
225   /* When constructing the widget, we want the box to be added as the child of
226    * the GtkListBoxRow, as an implementation detail.
227    */
228   if (priv->box == NULL)
229     GTK_CONTAINER_CLASS (hdy_action_row_parent_class)->add (container, child);
230   else
231     gtk_container_add (GTK_CONTAINER (priv->box), child);
232 }
233 
234 typedef struct {
235   HdyActionRow *row;
236   GtkCallback callback;
237   gpointer callback_data;
238 } ForallData;
239 
240 static void
for_non_internal_child(GtkWidget * widget,gpointer callback_data)241 for_non_internal_child (GtkWidget *widget,
242                         gpointer   callback_data)
243 {
244   ForallData *data = callback_data;
245   HdyActionRowPrivate *priv = hdy_action_row_get_instance_private (data->row);
246 
247   if (widget != (GtkWidget *) priv->box &&
248       widget != (GtkWidget *) priv->image &&
249       widget != (GtkWidget *) priv->prefixes &&
250       widget != (GtkWidget *) priv->title_box)
251     data->callback (widget, data->callback_data);
252 }
253 
254 static void
hdy_action_row_forall(GtkContainer * container,gboolean include_internals,GtkCallback callback,gpointer callback_data)255 hdy_action_row_forall (GtkContainer *container,
256                        gboolean      include_internals,
257                        GtkCallback   callback,
258                        gpointer      callback_data)
259 {
260   HdyActionRow *self = HDY_ACTION_ROW (container);
261   HdyActionRowPrivate *priv = hdy_action_row_get_instance_private (self);
262   ForallData data;
263 
264   if (include_internals) {
265     GTK_CONTAINER_CLASS (hdy_action_row_parent_class)->forall (GTK_CONTAINER (self), include_internals, callback, callback_data);
266 
267     return;
268   }
269 
270   data.row = self;
271   data.callback = callback;
272   data.callback_data = callback_data;
273 
274   if (priv->prefixes)
275     GTK_CONTAINER_GET_CLASS (priv->prefixes)->forall (GTK_CONTAINER (priv->prefixes), include_internals, for_non_internal_child, &data);
276   if (priv->header)
277     GTK_CONTAINER_GET_CLASS (priv->header)->forall (GTK_CONTAINER (priv->header), include_internals, for_non_internal_child, &data);
278   if (priv->box)
279     GTK_CONTAINER_GET_CLASS (priv->box)->forall (GTK_CONTAINER (priv->box), include_internals, for_non_internal_child, &data);
280 }
281 
282 static void
hdy_action_row_activate_real(HdyActionRow * self)283 hdy_action_row_activate_real (HdyActionRow *self)
284 {
285   HdyActionRowPrivate *priv = hdy_action_row_get_instance_private (self);
286 
287   if (priv->activatable_widget)
288     gtk_widget_mnemonic_activate (priv->activatable_widget, FALSE);
289 }
290 
291 static void
hdy_action_row_class_init(HdyActionRowClass * klass)292 hdy_action_row_class_init (HdyActionRowClass *klass)
293 {
294   GObjectClass *object_class = G_OBJECT_CLASS (klass);
295   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
296   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
297 
298   object_class->get_property = hdy_action_row_get_property;
299   object_class->set_property = hdy_action_row_set_property;
300   object_class->dispose = hdy_action_row_dispose;
301 
302   widget_class->destroy = hdy_action_row_destroy;
303   widget_class->show_all = hdy_action_row_show_all;
304 
305   container_class->add = hdy_action_row_add;
306   container_class->forall = hdy_action_row_forall;
307 
308   klass->activate = hdy_action_row_activate_real;
309 
310   /**
311    * HdyActionRow:icon-name:
312    *
313    * The icon name for this row.
314    *
315    * Since: 0.0.6
316    */
317   props[PROP_ICON_NAME] =
318     g_param_spec_string ("icon-name",
319                          _("Icon name"),
320                          _("Icon name"),
321                          "",
322                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
323 
324   /**
325    * HdyActionRow:activatable-widget:
326    *
327    * The activatable widget for this row.
328    *
329    * Since: 0.0.7
330    */
331   props[PROP_ACTIVATABLE_WIDGET] =
332       g_param_spec_object ("activatable-widget",
333                            _("Activatable widget"),
334                            _("The widget to be activated when the row is activated"),
335                            GTK_TYPE_WIDGET,
336                            G_PARAM_READWRITE);
337 
338   /**
339    * HdyActionRow:subtitle:
340    *
341    * The subtitle for this row.
342    *
343    * Since: 0.0.6
344    */
345   props[PROP_SUBTITLE] =
346     g_param_spec_string ("subtitle",
347                          _("Subtitle"),
348                          _("Subtitle"),
349                          "",
350                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
351 
352   /**
353    * HdyActionRow:title:
354    *
355    * The title for this row.
356    *
357    * Since: 0.0.6
358    */
359   props[PROP_TITLE] =
360     g_param_spec_string ("title",
361                          _("Title"),
362                          _("Title"),
363                          "",
364                          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);
365 
366   /**
367    * HdyActionRow:use-underline:
368    *
369    * Whether an embedded underline in the text of the title and subtitle labels
370    * indicates a mnemonic.
371    *
372    * Since: 0.0.6
373    */
374   props[PROP_USE_UNDERLINE] =
375     g_param_spec_boolean ("use-underline",
376                           _("Use underline"),
377                           _("If set, an underline in the text indicates the next character should be used for the mnemonic accelerator key"),
378                           FALSE,
379                           G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
380 
381   g_object_class_install_properties (object_class, LAST_PROP, props);
382 
383   gtk_widget_class_set_template_from_resource (widget_class,
384                                                "/sm/puri/handy/ui/hdy-action-row.ui");
385   gtk_widget_class_bind_template_child_private (widget_class, HdyActionRow, box);
386   gtk_widget_class_bind_template_child_private (widget_class, HdyActionRow, header);
387   gtk_widget_class_bind_template_child_private (widget_class, HdyActionRow, image);
388   gtk_widget_class_bind_template_child_private (widget_class, HdyActionRow, prefixes);
389   gtk_widget_class_bind_template_child_private (widget_class, HdyActionRow, subtitle);
390   gtk_widget_class_bind_template_child_private (widget_class, HdyActionRow, title);
391   gtk_widget_class_bind_template_child_private (widget_class, HdyActionRow, title_box);
392 }
393 
394 static void
hdy_action_row_init(HdyActionRow * self)395 hdy_action_row_init (HdyActionRow *self)
396 {
397   gtk_widget_init_template (GTK_WIDGET (self));
398 
399   update_subtitle_visibility (self);
400 
401   g_signal_connect (self, "notify::parent", G_CALLBACK (parent_cb), NULL);
402 
403 }
404 
405 static void
hdy_action_row_buildable_add_child(GtkBuildable * buildable,GtkBuilder * builder,GObject * child,const gchar * type)406 hdy_action_row_buildable_add_child (GtkBuildable *buildable,
407                                     GtkBuilder   *builder,
408                                     GObject      *child,
409                                     const gchar  *type)
410 {
411   if (type && strcmp (type, "action") == 0)
412     hdy_action_row_add_action (HDY_ACTION_ROW (buildable), GTK_WIDGET (child));
413   else if (type && strcmp (type, "prefix") == 0)
414     hdy_action_row_add_prefix (HDY_ACTION_ROW (buildable), GTK_WIDGET (child));
415   else
416     parent_buildable_iface->add_child (buildable, builder, child, type);
417 }
418 
419 static void
hdy_action_row_buildable_init(GtkBuildableIface * iface)420 hdy_action_row_buildable_init (GtkBuildableIface *iface)
421 {
422   parent_buildable_iface = g_type_interface_peek_parent (iface);
423   iface->add_child = hdy_action_row_buildable_add_child;
424 }
425 
426 /**
427  * hdy_action_row_new:
428  *
429  * Creates a new #HdyActionRow.
430  *
431  * Returns: a new #HdyActionRow
432  *
433  * Since: 0.0.6
434  */
435 HdyActionRow *
hdy_action_row_new(void)436 hdy_action_row_new (void)
437 {
438   return g_object_new (HDY_TYPE_ACTION_ROW, NULL);
439 }
440 
441 /**
442  * hdy_action_row_get_title:
443  * @self: a #HdyActionRow
444  *
445  * Gets the title for @self.
446  *
447  * Returns: the title for @self.
448  *
449  * Since: 0.0.6
450  */
451 const gchar *
hdy_action_row_get_title(HdyActionRow * self)452 hdy_action_row_get_title (HdyActionRow *self)
453 {
454   HdyActionRowPrivate *priv;
455 
456   g_return_val_if_fail (HDY_IS_ACTION_ROW (self), NULL);
457 
458   priv = hdy_action_row_get_instance_private (self);
459 
460   return gtk_label_get_text (priv->title);
461 }
462 
463 /**
464  * hdy_action_row_set_title:
465  * @self: a #HdyActionRow
466  * @title: the title
467  *
468  * Sets the title for @self.
469  *
470  * Since: 0.0.6
471  */
472 void
hdy_action_row_set_title(HdyActionRow * self,const gchar * title)473 hdy_action_row_set_title (HdyActionRow *self,
474                           const gchar  *title)
475 {
476   HdyActionRowPrivate *priv;
477 
478   g_return_if_fail (HDY_IS_ACTION_ROW (self));
479 
480   priv = hdy_action_row_get_instance_private (self);
481   hdy_preferences_row_set_title (HDY_PREFERENCES_ROW (self), title);
482 
483   if (g_strcmp0 (gtk_label_get_text (priv->title), title) == 0)
484     return;
485 
486   gtk_label_set_text (priv->title, title);
487   gtk_widget_set_visible (GTK_WIDGET (priv->title),
488                           title != NULL && g_strcmp0 (title, "") != 0);
489 
490   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_TITLE]);
491 }
492 
493 /**
494  * hdy_action_row_get_subtitle:
495  * @self: a #HdyActionRow
496  *
497  * Gets the subtitle for @self.
498  *
499  * Returns: the subtitle for @self.
500  *
501  * Since: 0.0.6
502  */
503 const gchar *
hdy_action_row_get_subtitle(HdyActionRow * self)504 hdy_action_row_get_subtitle (HdyActionRow *self)
505 {
506   HdyActionRowPrivate *priv;
507 
508   g_return_val_if_fail (HDY_IS_ACTION_ROW (self), NULL);
509 
510   priv = hdy_action_row_get_instance_private (self);
511 
512   return gtk_label_get_text (priv->subtitle);
513 }
514 
515 /**
516  * hdy_action_row_set_subtitle:
517  * @self: a #HdyActionRow
518  * @subtitle: the subtitle
519  *
520  * Sets the subtitle for @self.
521  *
522  * Since: 0.0.6
523  */
524 void
hdy_action_row_set_subtitle(HdyActionRow * self,const gchar * subtitle)525 hdy_action_row_set_subtitle (HdyActionRow *self,
526                              const gchar  *subtitle)
527 {
528   HdyActionRowPrivate *priv;
529 
530   g_return_if_fail (HDY_IS_ACTION_ROW (self));
531 
532   priv = hdy_action_row_get_instance_private (self);
533 
534   if (g_strcmp0 (gtk_label_get_text (priv->subtitle), subtitle) == 0)
535     return;
536 
537   gtk_label_set_text (priv->subtitle, subtitle);
538   gtk_widget_set_visible (GTK_WIDGET (priv->subtitle),
539                           subtitle != NULL && g_strcmp0 (subtitle, "") != 0);
540 
541   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_SUBTITLE]);
542 }
543 
544 /**
545  * hdy_action_row_get_icon_name:
546  * @self: a #HdyActionRow
547  *
548  * Gets the icon name for @self.
549  *
550  * Returns: the icon name for @self.
551  *
552  * Since: 0.0.6
553  */
554 const gchar *
hdy_action_row_get_icon_name(HdyActionRow * self)555 hdy_action_row_get_icon_name (HdyActionRow *self)
556 {
557   HdyActionRowPrivate *priv;
558   const gchar *icon_name;
559 
560   g_return_val_if_fail (HDY_IS_ACTION_ROW (self), NULL);
561 
562   priv = hdy_action_row_get_instance_private (self);
563 
564   gtk_image_get_icon_name (priv->image, &icon_name, NULL);
565 
566   return icon_name;
567 }
568 
569 /**
570  * hdy_action_row_set_icon_name:
571  * @self: a #HdyActionRow
572  * @icon_name: the icon name
573  *
574  * Sets the icon name for @self.
575  *
576  * Since: 0.0.6
577  */
578 void
hdy_action_row_set_icon_name(HdyActionRow * self,const gchar * icon_name)579 hdy_action_row_set_icon_name (HdyActionRow *self,
580                               const gchar  *icon_name)
581 {
582   HdyActionRowPrivate *priv;
583   const gchar *old_icon_name;
584 
585   g_return_if_fail (HDY_IS_ACTION_ROW (self));
586 
587   priv = hdy_action_row_get_instance_private (self);
588 
589   gtk_image_get_icon_name (priv->image, &old_icon_name, NULL);
590   if (g_strcmp0 (old_icon_name, icon_name) == 0)
591     return;
592 
593   gtk_image_set_from_icon_name (priv->image, icon_name, GTK_ICON_SIZE_INVALID);
594   gtk_widget_set_visible (GTK_WIDGET (priv->image),
595                           icon_name != NULL && g_strcmp0 (icon_name, "") != 0);
596 
597   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ICON_NAME]);
598 }
599 
600 /**
601  * hdy_action_row_get_activatable_widget:
602  * @self: a #HdyActionRow
603  *
604  * Gets the widget activated when @self is activated.
605  *
606  * Returns: (nullable) (transfer none): the widget activated when @self is
607  *          activated, or %NULL if none has been set.
608  *
609  * Since: 0.0.7
610  */
611 GtkWidget *
hdy_action_row_get_activatable_widget(HdyActionRow * self)612 hdy_action_row_get_activatable_widget (HdyActionRow *self)
613 {
614   HdyActionRowPrivate *priv;
615 
616   g_return_val_if_fail (HDY_IS_ACTION_ROW (self), NULL);
617 
618   priv = hdy_action_row_get_instance_private (self);
619 
620   return priv->activatable_widget;
621 }
622 
623 static void
activatable_widget_weak_notify(gpointer data,GObject * where_the_object_was)624 activatable_widget_weak_notify (gpointer  data,
625                                 GObject  *where_the_object_was)
626 {
627   HdyActionRow *self = HDY_ACTION_ROW (data);
628   HdyActionRowPrivate *priv = hdy_action_row_get_instance_private (self);
629 
630   priv->activatable_widget = NULL;
631 
632   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ACTIVATABLE_WIDGET]);
633 }
634 
635 /**
636  * hdy_action_row_set_activatable_widget:
637  * @self: a #HdyActionRow
638  * @widget: (nullable): the target #GtkWidget, or %NULL to unset
639  *
640  * Sets the widget to activate when @self is activated, either by clicking
641  * on it, by calling hdy_action_row_activate(), or via mnemonics in the title or
642  * the subtitle. See the “use_underline” property to enable mnemonics.
643  *
644  * The target widget will be activated by emitting the
645  * GtkWidget::mnemonic-activate signal on it.
646  *
647  * Since: 0.0.7
648  */
649 void
hdy_action_row_set_activatable_widget(HdyActionRow * self,GtkWidget * widget)650 hdy_action_row_set_activatable_widget (HdyActionRow *self,
651                                        GtkWidget    *widget)
652 {
653   HdyActionRowPrivate *priv;
654 
655   g_return_if_fail (HDY_IS_ACTION_ROW (self));
656 
657   priv = hdy_action_row_get_instance_private (self);
658 
659   if (priv->activatable_widget == widget)
660     return;
661 
662   if (widget != NULL)
663     g_return_if_fail (GTK_IS_WIDGET (widget));
664 
665   if (priv->activatable_widget)
666     g_object_weak_unref (G_OBJECT (priv->activatable_widget),
667                          activatable_widget_weak_notify,
668                          self);
669 
670   priv->activatable_widget = widget;
671 
672   if (priv->activatable_widget != NULL)
673     g_object_weak_ref (G_OBJECT (priv->activatable_widget),
674                        activatable_widget_weak_notify,
675                        self);
676 
677   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_ACTIVATABLE_WIDGET]);
678 }
679 
680 /**
681  * hdy_action_row_get_use_underline:
682  * @self: a #HdyActionRow
683  *
684  * Gets whether an embedded underline in the text of the title and subtitle
685  * labels indicates a mnemonic. See hdy_action_row_set_use_underline().
686  *
687  * Returns: %TRUE if an embedded underline in the title and subtitle labels
688  *          indicates the mnemonic accelerator keys.
689  *
690  * Since: 0.0.6
691  */
692 gboolean
hdy_action_row_get_use_underline(HdyActionRow * self)693 hdy_action_row_get_use_underline (HdyActionRow *self)
694 {
695   HdyActionRowPrivate *priv;
696 
697   g_return_val_if_fail (HDY_IS_ACTION_ROW (self), FALSE);
698 
699   priv = hdy_action_row_get_instance_private (self);
700 
701   return priv->use_underline;
702 }
703 
704 /**
705  * hdy_action_row_set_use_underline:
706  * @self: a #HdyActionRow
707  * @use_underline: %TRUE if underlines in the text indicate mnemonics
708  *
709  * If true, an underline in the text of the title and subtitle labels indicates
710  * the next character should be used for the mnemonic accelerator key.
711  *
712  * Since: 0.0.6
713  */
714 void
hdy_action_row_set_use_underline(HdyActionRow * self,gboolean use_underline)715 hdy_action_row_set_use_underline (HdyActionRow *self,
716                                   gboolean      use_underline)
717 {
718   HdyActionRowPrivate *priv;
719 
720   g_return_if_fail (HDY_IS_ACTION_ROW (self));
721 
722   priv = hdy_action_row_get_instance_private (self);
723 
724   if (priv->use_underline == !!use_underline)
725     return;
726 
727   priv->use_underline = !!use_underline;
728   hdy_preferences_row_set_use_underline (HDY_PREFERENCES_ROW (self), priv->use_underline);
729   gtk_label_set_use_underline (priv->title, priv->use_underline);
730   gtk_label_set_use_underline (priv->subtitle, priv->use_underline);
731   gtk_label_set_mnemonic_widget (priv->title, GTK_WIDGET (self));
732   gtk_label_set_mnemonic_widget (priv->subtitle, GTK_WIDGET (self));
733 
734   g_object_notify_by_pspec (G_OBJECT (self), props[PROP_USE_UNDERLINE]);
735 }
736 
737 /**
738  * hdy_action_row_add_action:
739  * @self: a #HdyActionRow
740  * @widget: (allow-none): the action widget
741  *
742  * Adds an action widget to @self.
743  *
744  * Since: 0.0.6
745  */
746 void
hdy_action_row_add_action(HdyActionRow * self,GtkWidget * widget)747 hdy_action_row_add_action (HdyActionRow *self,
748                            GtkWidget    *widget)
749 {
750   HdyActionRowPrivate *priv;
751 
752   g_return_if_fail (HDY_IS_ACTION_ROW (self));
753 
754   priv = hdy_action_row_get_instance_private (self);
755 
756   gtk_box_pack_end (priv->header, widget, FALSE, TRUE, 0);
757 }
758 
759 /**
760  * hdy_action_row_add_prefix:
761  * @self: a #HdyActionRow
762  * @widget: (allow-none): the prefix widget
763  *
764  * Adds a prefix widget to @self.
765  *
766  * Since: 0.0.6
767  */
768 void
hdy_action_row_add_prefix(HdyActionRow * self,GtkWidget * widget)769 hdy_action_row_add_prefix (HdyActionRow *self,
770                            GtkWidget    *widget)
771 {
772   HdyActionRowPrivate *priv;
773 
774   g_return_if_fail (HDY_IS_ACTION_ROW (self));
775 
776   priv = hdy_action_row_get_instance_private (self);
777 
778   gtk_box_pack_start (priv->prefixes, widget, FALSE, TRUE, 0);
779   gtk_widget_show (GTK_WIDGET (priv->prefixes));
780 }
781 
782 void
hdy_action_row_activate(HdyActionRow * self)783 hdy_action_row_activate (HdyActionRow *self)
784 {
785   g_return_if_fail (HDY_IS_ACTION_ROW (self));
786 
787   HDY_ACTION_ROW_GET_CLASS (self)->activate (self);
788 }
789