1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2 /* GTK - The GIMP Toolkit
3  * Copyright (C) 2000 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 /*
20  * Modified by the GTK+ Team and others 1997-2003.  See the AUTHORS
21  * file for a list of people on the GTK+ Team.  See the ChangeLog
22  * files for a list of changes.  These files are distributed with
23  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
24  */
25 
26 #include "config.h"
27 
28 #include <string.h>
29 
30 #include "gtkmessagedialog.h"
31 #include "gtkaccessible.h"
32 #include "gtkbuildable.h"
33 #include "gtklabel.h"
34 #include "gtkbox.h"
35 #include "gtkbbox.h"
36 #include "gtkimage.h"
37 #include "gtkintl.h"
38 #include "gtkprivate.h"
39 #include "gtktypebuiltins.h"
40 
41 /**
42  * SECTION:gtkmessagedialog
43  * @Short_description: A convenient message window
44  * @Title: GtkMessageDialog
45  * @See_also:#GtkDialog
46  *
47  * #GtkMessageDialog presents a dialog with some message text. It’s simply a
48  * convenience widget; you could construct the equivalent of #GtkMessageDialog
49  * from #GtkDialog without too much effort, but #GtkMessageDialog saves typing.
50  *
51  * One difference from #GtkDialog is that #GtkMessageDialog sets the
52  * #GtkWindow:skip-taskbar-hint property to %TRUE, so that the dialog is hidden
53  * from the taskbar by default.
54  *
55  * The easiest way to do a modal message dialog is to use gtk_dialog_run(), though
56  * you can also pass in the %GTK_DIALOG_MODAL flag, gtk_dialog_run() automatically
57  * makes the dialog modal and waits for the user to respond to it. gtk_dialog_run()
58  * returns when any dialog button is clicked.
59  *
60  * An example for using a modal dialog:
61  * |[<!-- language="C" -->
62  *  GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
63  *  dialog = gtk_message_dialog_new (parent_window,
64  *                                   flags,
65  *                                   GTK_MESSAGE_ERROR,
66  *                                   GTK_BUTTONS_CLOSE,
67  *                                   "Error reading “%s”: %s",
68  *                                   filename,
69  *                                   g_strerror (errno));
70  *  gtk_dialog_run (GTK_DIALOG (dialog));
71  *  gtk_widget_destroy (dialog);
72  * ]|
73  *
74  * You might do a non-modal #GtkMessageDialog as follows:
75  *
76  * An example for a non-modal dialog:
77  * |[<!-- language="C" -->
78  *  GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
79  *  dialog = gtk_message_dialog_new (parent_window,
80  *                                   flags,
81  *                                   GTK_MESSAGE_ERROR,
82  *                                   GTK_BUTTONS_CLOSE,
83  *                                   "Error reading “%s”: %s",
84  *                                   filename,
85  *                                   g_strerror (errno));
86  *
87  *  // Destroy the dialog when the user responds to it
88  *  // (e.g. clicks a button)
89  *
90  *  g_signal_connect_swapped (dialog, "response",
91  *                            G_CALLBACK (gtk_widget_destroy),
92  *                            dialog);
93  * ]|
94  *
95  * # GtkMessageDialog as GtkBuildable
96  *
97  * The GtkMessageDialog implementation of the GtkBuildable interface exposes
98  * the message area as an internal child with the name “message_area”.
99  */
100 
101 struct _GtkMessageDialogPrivate
102 {
103   GtkWidget     *image;
104   GtkWidget     *label;
105   GtkWidget     *message_area; /* vbox for the primary and secondary labels, and any extra content from the caller */
106   GtkWidget     *secondary_label;
107 
108   guint          has_primary_markup : 1;
109   guint          has_secondary_text : 1;
110   guint          message_type       : 3;
111 };
112 
113 static void gtk_message_dialog_style_updated (GtkWidget       *widget);
114 
115 static void gtk_message_dialog_constructed  (GObject          *object);
116 static void gtk_message_dialog_set_property (GObject          *object,
117 					     guint             prop_id,
118 					     const GValue     *value,
119 					     GParamSpec       *pspec);
120 static void gtk_message_dialog_get_property (GObject          *object,
121 					     guint             prop_id,
122 					     GValue           *value,
123 					     GParamSpec       *pspec);
124 static void gtk_message_dialog_add_buttons  (GtkMessageDialog *message_dialog,
125 					     GtkButtonsType    buttons);
126 static void      gtk_message_dialog_buildable_interface_init     (GtkBuildableIface *iface);
127 
128 enum {
129   PROP_0,
130   PROP_MESSAGE_TYPE,
131   PROP_BUTTONS,
132   PROP_TEXT,
133   PROP_USE_MARKUP,
134   PROP_SECONDARY_TEXT,
135   PROP_SECONDARY_USE_MARKUP,
136   PROP_IMAGE,
137   PROP_MESSAGE_AREA
138 };
139 
140 G_DEFINE_TYPE_WITH_CODE (GtkMessageDialog, gtk_message_dialog, GTK_TYPE_DIALOG,
141                          G_ADD_PRIVATE (GtkMessageDialog)
142                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
143                                                 gtk_message_dialog_buildable_interface_init))
144 
145 static GtkBuildableIface *parent_buildable_iface;
146 
147 static void
gtk_message_dialog_buildable_interface_init(GtkBuildableIface * iface)148 gtk_message_dialog_buildable_interface_init (GtkBuildableIface *iface)
149 {
150   parent_buildable_iface = g_type_interface_peek_parent (iface);
151   iface->custom_tag_start = parent_buildable_iface->custom_tag_start;
152   iface->custom_finished = parent_buildable_iface->custom_finished;
153 }
154 
155 static void
gtk_message_dialog_class_init(GtkMessageDialogClass * class)156 gtk_message_dialog_class_init (GtkMessageDialogClass *class)
157 {
158   GtkWidgetClass *widget_class;
159   GObjectClass *gobject_class;
160 
161   widget_class = GTK_WIDGET_CLASS (class);
162   gobject_class = G_OBJECT_CLASS (class);
163 
164   widget_class->style_updated = gtk_message_dialog_style_updated;
165 
166   gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_ALERT);
167 
168   gobject_class->constructed = gtk_message_dialog_constructed;
169   gobject_class->set_property = gtk_message_dialog_set_property;
170   gobject_class->get_property = gtk_message_dialog_get_property;
171 
172   gtk_widget_class_install_style_property (widget_class,
173 					   g_param_spec_int ("message-border",
174                                                              P_("label border"),
175                                                              P_("Width of border around the label in the message dialog"),
176                                                              0,
177                                                              G_MAXINT,
178                                                              12,
179                                                              GTK_PARAM_READABLE));
180 
181   /**
182    * GtkMessageDialog:message-type:
183    *
184    * The type of the message.
185    */
186   g_object_class_install_property (gobject_class,
187                                    PROP_MESSAGE_TYPE,
188                                    g_param_spec_enum ("message-type",
189 						      P_("Message Type"),
190 						      P_("The type of message"),
191 						      GTK_TYPE_MESSAGE_TYPE,
192                                                       GTK_MESSAGE_INFO,
193                                                       GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY));
194   g_object_class_install_property (gobject_class,
195                                    PROP_BUTTONS,
196                                    g_param_spec_enum ("buttons",
197 						      P_("Message Buttons"),
198 						      P_("The buttons shown in the message dialog"),
199 						      GTK_TYPE_BUTTONS_TYPE,
200                                                       GTK_BUTTONS_NONE,
201                                                       GTK_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
202 
203   /**
204    * GtkMessageDialog:text:
205    *
206    * The primary text of the message dialog. If the dialog has
207    * a secondary text, this will appear as the title.
208    *
209    * Since: 2.10
210    */
211   g_object_class_install_property (gobject_class,
212                                    PROP_TEXT,
213                                    g_param_spec_string ("text",
214                                                         P_("Text"),
215                                                         P_("The primary text of the message dialog"),
216                                                         "",
217                                                         GTK_PARAM_READWRITE));
218 
219   /**
220    * GtkMessageDialog:use-markup:
221    *
222    * %TRUE if the primary text of the dialog includes Pango markup.
223    * See pango_parse_markup().
224    *
225    * Since: 2.10
226    */
227   g_object_class_install_property (gobject_class,
228 				   PROP_USE_MARKUP,
229 				   g_param_spec_boolean ("use-markup",
230 							 P_("Use Markup"),
231 							 P_("The primary text of the title includes Pango markup."),
232 							 FALSE,
233 							 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
234 
235   /**
236    * GtkMessageDialog:secondary-text:
237    *
238    * The secondary text of the message dialog.
239    *
240    * Since: 2.10
241    */
242   g_object_class_install_property (gobject_class,
243                                    PROP_SECONDARY_TEXT,
244                                    g_param_spec_string ("secondary-text",
245                                                         P_("Secondary Text"),
246                                                         P_("The secondary text of the message dialog"),
247                                                         NULL,
248                                                         GTK_PARAM_READWRITE));
249 
250   /**
251    * GtkMessageDialog:secondary-use-markup:
252    *
253    * %TRUE if the secondary text of the dialog includes Pango markup.
254    * See pango_parse_markup().
255    *
256    * Since: 2.10
257    */
258   g_object_class_install_property (gobject_class,
259 				   PROP_SECONDARY_USE_MARKUP,
260 				   g_param_spec_boolean ("secondary-use-markup",
261 							 P_("Use Markup in secondary"),
262 							 P_("The secondary text includes Pango markup."),
263 							 FALSE,
264 							 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
265 
266   /**
267    * GtkMessageDialog:image:
268    *
269    * The image for this dialog.
270    *
271    * Since: 2.10
272    * Deprecated: 3.12: Use #GtkDialog to create dialogs with images
273    */
274   g_object_class_install_property (gobject_class,
275                                    PROP_IMAGE,
276                                    g_param_spec_object ("image",
277                                                         P_("Image"),
278                                                         P_("The image"),
279                                                         GTK_TYPE_WIDGET,
280                                                         GTK_PARAM_READWRITE|G_PARAM_DEPRECATED));
281 
282   /**
283    * GtkMessageDialog:message-area:
284    *
285    * The #GtkBox that corresponds to the message area of this dialog.  See
286    * gtk_message_dialog_get_message_area() for a detailed description of this
287    * area.
288    *
289    * Since: 2.22
290    */
291   g_object_class_install_property (gobject_class,
292 				   PROP_MESSAGE_AREA,
293 				   g_param_spec_object ("message-area",
294 							P_("Message area"),
295 							P_("GtkBox that holds the dialog's primary and secondary labels"),
296 							GTK_TYPE_WIDGET,
297 							GTK_PARAM_READABLE));
298 
299   /* Setup Composite data */
300   gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkmessagedialog.ui");
301   gtk_widget_class_bind_template_child_private (widget_class, GtkMessageDialog, label);
302   gtk_widget_class_bind_template_child_private (widget_class, GtkMessageDialog, secondary_label);
303   gtk_widget_class_bind_template_child_internal_private (widget_class, GtkMessageDialog, message_area);
304 
305   gtk_widget_class_set_css_name (widget_class, "messagedialog");
306 }
307 
308 static void
gtk_message_dialog_init(GtkMessageDialog * dialog)309 gtk_message_dialog_init (GtkMessageDialog *dialog)
310 {
311   GtkMessageDialogPrivate *priv;
312   GtkWidget *action_area;
313   GtkSettings *settings;
314   gboolean use_caret;
315 
316   dialog->priv = gtk_message_dialog_get_instance_private (dialog);
317   priv = dialog->priv;
318 
319   priv->has_primary_markup = FALSE;
320   priv->has_secondary_text = FALSE;
321   priv->has_primary_markup = FALSE;
322   priv->has_secondary_text = FALSE;
323   priv->message_type = GTK_MESSAGE_OTHER;
324 
325   gtk_widget_init_template (GTK_WIDGET (dialog));
326   gtk_message_dialog_style_updated (GTK_WIDGET (dialog));
327 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
328   action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
329 G_GNUC_END_IGNORE_DEPRECATIONS
330   gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_EXPAND);
331 
332   settings = gtk_widget_get_settings (GTK_WIDGET (dialog));
333   g_object_get (settings, "gtk-keynav-use-caret", &use_caret, NULL);
334   gtk_label_set_selectable (GTK_LABEL (priv->label), use_caret);
335   gtk_label_set_selectable (GTK_LABEL (priv->secondary_label), use_caret);
336 }
337 
338 static void
setup_primary_label_font(GtkMessageDialog * dialog)339 setup_primary_label_font (GtkMessageDialog *dialog)
340 {
341   GtkMessageDialogPrivate *priv = dialog->priv;
342 
343   if (!priv->has_primary_markup)
344     {
345       PangoAttrList *attributes;
346       PangoAttribute *attr;
347 
348       attributes = pango_attr_list_new ();
349 
350       attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
351       pango_attr_list_insert (attributes, attr);
352 
353       if (priv->has_secondary_text)
354         {
355           attr = pango_attr_scale_new (PANGO_SCALE_LARGE);
356           pango_attr_list_insert (attributes, attr);
357         }
358 
359       gtk_label_set_attributes (GTK_LABEL (priv->label), attributes);
360       pango_attr_list_unref (attributes);
361     }
362   else
363     {
364       /* unset the font settings */
365       gtk_label_set_attributes (GTK_LABEL (priv->label), NULL);
366     }
367 }
368 
369 static void
setup_type(GtkMessageDialog * dialog,GtkMessageType type)370 setup_type (GtkMessageDialog *dialog,
371 	    GtkMessageType    type)
372 {
373   GtkMessageDialogPrivate *priv = dialog->priv;
374   const gchar *name = NULL;
375   AtkObject *atk_obj;
376 
377   if (priv->message_type == type)
378     return;
379 
380   priv->message_type = type;
381 
382   switch (type)
383     {
384     case GTK_MESSAGE_INFO:
385       name = _("Information");
386       break;
387 
388     case GTK_MESSAGE_QUESTION:
389       name = _("Question");
390       break;
391 
392     case GTK_MESSAGE_WARNING:
393       name = _("Warning");
394       break;
395 
396     case GTK_MESSAGE_ERROR:
397       name = _("Error");
398       break;
399 
400     case GTK_MESSAGE_OTHER:
401       break;
402 
403     default:
404       g_warning ("Unknown GtkMessageType %u", type);
405       break;
406     }
407 
408   atk_obj = gtk_widget_get_accessible (GTK_WIDGET (dialog));
409   if (GTK_IS_ACCESSIBLE (atk_obj))
410     {
411       atk_object_set_role (atk_obj, ATK_ROLE_ALERT);
412       if (name)
413         atk_object_set_name (atk_obj, name);
414     }
415 
416   g_object_notify (G_OBJECT (dialog), "message-type");
417 }
418 
419 static void
update_title(GObject * dialog,GParamSpec * pspec,GtkWidget * label)420 update_title (GObject    *dialog,
421               GParamSpec *pspec,
422               GtkWidget  *label)
423 {
424   const gchar *title;
425 
426   title = gtk_window_get_title (GTK_WINDOW (dialog));
427   gtk_label_set_label (GTK_LABEL (label), title);
428   gtk_widget_set_visible (label, title && title[0]);
429 }
430 
431 static void
gtk_message_dialog_constructed(GObject * object)432 gtk_message_dialog_constructed (GObject *object)
433 {
434   GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG (object);
435   gboolean use_header;
436 
437   G_OBJECT_CLASS (gtk_message_dialog_parent_class)->constructed (object);
438 
439   g_object_get (gtk_widget_get_settings (GTK_WIDGET (dialog)),
440                 "gtk-dialogs-use-header", &use_header,
441                 NULL);
442 
443   if (use_header)
444     {
445       GtkWidget *box;
446       GtkWidget *label;
447 
448       box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
449       gtk_widget_show (box);
450       gtk_widget_set_size_request (box, -1, 16);
451       label = gtk_label_new ("");
452       gtk_widget_set_no_show_all (label, TRUE);
453       gtk_widget_set_margin_top (label, 6);
454       gtk_widget_set_margin_bottom (label, 6);
455       gtk_style_context_add_class (gtk_widget_get_style_context (label), "title");
456       gtk_box_set_center_widget (GTK_BOX (box), label);
457       g_signal_connect_object (dialog, "notify::title", G_CALLBACK (update_title), label, 0);
458 
459       gtk_window_set_titlebar (GTK_WINDOW (dialog), box);
460     }
461 }
462 
463 static void
gtk_message_dialog_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)464 gtk_message_dialog_set_property (GObject      *object,
465 				 guint         prop_id,
466 				 const GValue *value,
467 				 GParamSpec   *pspec)
468 {
469   GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG (object);
470   GtkMessageDialogPrivate *priv = dialog->priv;
471 
472   switch (prop_id)
473     {
474     case PROP_MESSAGE_TYPE:
475       setup_type (dialog, g_value_get_enum (value));
476       break;
477     case PROP_BUTTONS:
478       gtk_message_dialog_add_buttons (dialog, g_value_get_enum (value));
479       break;
480     case PROP_TEXT:
481       if (priv->has_primary_markup)
482 	gtk_label_set_markup (GTK_LABEL (priv->label),
483 			      g_value_get_string (value));
484       else
485 	gtk_label_set_text (GTK_LABEL (priv->label),
486 			    g_value_get_string (value));
487       break;
488     case PROP_USE_MARKUP:
489       if (priv->has_primary_markup != g_value_get_boolean (value))
490         {
491           priv->has_primary_markup = g_value_get_boolean (value);
492           gtk_label_set_use_markup (GTK_LABEL (priv->label), priv->has_primary_markup);
493           g_object_notify_by_pspec (object, pspec);
494         }
495         setup_primary_label_font (dialog);
496       break;
497     case PROP_SECONDARY_TEXT:
498       {
499 	const gchar *txt = g_value_get_string (value);
500 
501 	if (gtk_label_get_use_markup (GTK_LABEL (priv->secondary_label)))
502 	  gtk_label_set_markup (GTK_LABEL (priv->secondary_label), txt);
503 	else
504 	  gtk_label_set_text (GTK_LABEL (priv->secondary_label), txt);
505 
506 	if (txt)
507 	  {
508 	    priv->has_secondary_text = TRUE;
509 	    gtk_widget_show (priv->secondary_label);
510 	  }
511 	else
512 	  {
513 	    priv->has_secondary_text = FALSE;
514 	    gtk_widget_hide (priv->secondary_label);
515 	  }
516 	setup_primary_label_font (dialog);
517       }
518       break;
519     case PROP_SECONDARY_USE_MARKUP:
520       if (gtk_label_get_use_markup (GTK_LABEL (priv->secondary_label)) != g_value_get_boolean (value))
521         {
522           gtk_label_set_use_markup (GTK_LABEL (priv->secondary_label), g_value_get_boolean (value));
523           g_object_notify_by_pspec (object, pspec);
524         }
525       break;
526     case PROP_IMAGE:
527 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
528       gtk_message_dialog_set_image (dialog, g_value_get_object (value));
529 G_GNUC_END_IGNORE_DEPRECATIONS
530       break;
531 
532     default:
533       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
534       break;
535     }
536 }
537 
538 static void
gtk_message_dialog_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)539 gtk_message_dialog_get_property (GObject     *object,
540 				 guint        prop_id,
541 				 GValue      *value,
542 				 GParamSpec  *pspec)
543 {
544   GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG (object);
545   GtkMessageDialogPrivate *priv = dialog->priv;
546 
547   switch (prop_id)
548     {
549     case PROP_MESSAGE_TYPE:
550       g_value_set_enum (value, (GtkMessageType) priv->message_type);
551       break;
552     case PROP_TEXT:
553       g_value_set_string (value, gtk_label_get_label (GTK_LABEL (priv->label)));
554       break;
555     case PROP_USE_MARKUP:
556       g_value_set_boolean (value, priv->has_primary_markup);
557       break;
558     case PROP_SECONDARY_TEXT:
559       if (priv->has_secondary_text)
560       g_value_set_string (value,
561 			  gtk_label_get_label (GTK_LABEL (priv->secondary_label)));
562       else
563 	g_value_set_string (value, NULL);
564       break;
565     case PROP_SECONDARY_USE_MARKUP:
566       if (priv->has_secondary_text)
567 	g_value_set_boolean (value,
568 			     gtk_label_get_use_markup (GTK_LABEL (priv->secondary_label)));
569       else
570 	g_value_set_boolean (value, FALSE);
571       break;
572     case PROP_IMAGE:
573       g_value_set_object (value, priv->image);
574       break;
575     case PROP_MESSAGE_AREA:
576       g_value_set_object (value, priv->message_area);
577       break;
578     default:
579       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
580       break;
581     }
582 }
583 
584 /**
585  * gtk_message_dialog_new:
586  * @parent: (allow-none): transient parent, or %NULL for none
587  * @flags: flags
588  * @type: type of message
589  * @buttons: set of buttons to use
590  * @message_format: (allow-none): printf()-style format string, or %NULL
591  * @...: arguments for @message_format
592  *
593  * Creates a new message dialog, which is a simple dialog with some text
594  * the user may want to see. When the user clicks a button a “response”
595  * signal is emitted with response IDs from #GtkResponseType. See
596  * #GtkDialog for more details.
597  *
598  * Returns: (transfer none): a new #GtkMessageDialog
599  */
600 GtkWidget*
gtk_message_dialog_new(GtkWindow * parent,GtkDialogFlags flags,GtkMessageType type,GtkButtonsType buttons,const gchar * message_format,...)601 gtk_message_dialog_new (GtkWindow     *parent,
602                         GtkDialogFlags flags,
603                         GtkMessageType type,
604                         GtkButtonsType buttons,
605                         const gchar   *message_format,
606                         ...)
607 {
608   GtkWidget *widget;
609   GtkDialog *dialog;
610   gchar* msg = NULL;
611   va_list args;
612 
613   g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);
614 
615   widget = g_object_new (GTK_TYPE_MESSAGE_DIALOG,
616                          "use-header-bar", FALSE,
617 			 "message-type", type,
618 			 "buttons", buttons,
619 			 NULL);
620   dialog = GTK_DIALOG (widget);
621 
622   if (message_format)
623     {
624       va_start (args, message_format);
625       msg = g_strdup_vprintf (message_format, args);
626       va_end (args);
627 
628       gtk_label_set_text (GTK_LABEL (GTK_MESSAGE_DIALOG (widget)->priv->label), msg);
629 
630       g_free (msg);
631     }
632 
633   if (parent != NULL)
634     gtk_window_set_transient_for (GTK_WINDOW (widget), GTK_WINDOW (parent));
635 
636   if (flags & GTK_DIALOG_MODAL)
637     gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
638 
639   if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
640     gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
641 
642   return widget;
643 }
644 
645 /**
646  * gtk_message_dialog_new_with_markup:
647  * @parent: (allow-none): transient parent, or %NULL for none
648  * @flags: flags
649  * @type: type of message
650  * @buttons: set of buttons to use
651  * @message_format: (allow-none): printf()-style format string, or %NULL
652  * @...: arguments for @message_format
653  *
654  * Creates a new message dialog, which is a simple dialog with some text that
655  * is marked up with the [Pango text markup language][PangoMarkupFormat].
656  * When the user clicks a button a “response” signal is emitted with
657  * response IDs from #GtkResponseType. See #GtkDialog for more details.
658  *
659  * Special XML characters in the printf() arguments passed to this
660  * function will automatically be escaped as necessary.
661  * (See g_markup_printf_escaped() for how this is implemented.)
662  * Usually this is what you want, but if you have an existing
663  * Pango markup string that you want to use literally as the
664  * label, then you need to use gtk_message_dialog_set_markup()
665  * instead, since you can’t pass the markup string either
666  * as the format (it might contain “%” characters) or as a string
667  * argument.
668  * |[<!-- language="C" -->
669  *  GtkWidget *dialog;
670  *  GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
671  *  dialog = gtk_message_dialog_new (parent_window,
672  *                                   flags,
673  *                                   GTK_MESSAGE_ERROR,
674  *                                   GTK_BUTTONS_CLOSE,
675  *                                   NULL);
676  *  gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog),
677  *                                 markup);
678  * ]|
679  *
680  * Returns: a new #GtkMessageDialog
681  *
682  * Since: 2.4
683  **/
684 GtkWidget*
gtk_message_dialog_new_with_markup(GtkWindow * parent,GtkDialogFlags flags,GtkMessageType type,GtkButtonsType buttons,const gchar * message_format,...)685 gtk_message_dialog_new_with_markup (GtkWindow     *parent,
686                                     GtkDialogFlags flags,
687                                     GtkMessageType type,
688                                     GtkButtonsType buttons,
689                                     const gchar   *message_format,
690                                     ...)
691 {
692   GtkWidget *widget;
693   va_list args;
694   gchar *msg = NULL;
695 
696   g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);
697 
698   widget = gtk_message_dialog_new (parent, flags, type, buttons, NULL);
699 
700   if (message_format)
701     {
702       va_start (args, message_format);
703       msg = g_markup_vprintf_escaped (message_format, args);
704       va_end (args);
705 
706       gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (widget), msg);
707 
708       g_free (msg);
709     }
710 
711   return widget;
712 }
713 
714 /**
715  * gtk_message_dialog_set_image:
716  * @dialog: a #GtkMessageDialog
717  * @image: the image
718  *
719  * Sets the dialog’s image to @image.
720  *
721  * Since: 2.10
722  * Deprecated: 3.12: Use #GtkDialog to create dialogs with images
723  **/
724 void
gtk_message_dialog_set_image(GtkMessageDialog * dialog,GtkWidget * image)725 gtk_message_dialog_set_image (GtkMessageDialog *dialog,
726 			      GtkWidget        *image)
727 {
728   GtkMessageDialogPrivate *priv;
729   GtkWidget *parent;
730 
731   g_return_if_fail (GTK_IS_MESSAGE_DIALOG (dialog));
732   g_return_if_fail (image == NULL || GTK_IS_WIDGET (image));
733 
734   priv = dialog->priv;
735 
736   if (priv->image)
737     gtk_widget_destroy (priv->image);
738 
739   priv->image = image;
740 
741   if (priv->image)
742     {
743       gtk_widget_set_halign (priv->image, GTK_ALIGN_CENTER);
744       gtk_widget_set_valign (priv->image, GTK_ALIGN_START);
745       parent = gtk_widget_get_parent (priv->message_area);
746       gtk_container_add (GTK_CONTAINER (parent), priv->image);
747       gtk_box_reorder_child (GTK_BOX (parent), priv->image, 0);
748     }
749 
750   priv->message_type = GTK_MESSAGE_OTHER;
751 
752   g_object_notify (G_OBJECT (dialog), "image");
753   g_object_notify (G_OBJECT (dialog), "message-type");
754 }
755 
756 /**
757  * gtk_message_dialog_get_image:
758  * @dialog: a #GtkMessageDialog
759  *
760  * Gets the dialog’s image.
761  *
762  * Returns: (transfer none): the dialog’s image
763  *
764  * Since: 2.14
765  * Deprecated: 3.12: Use #GtkDialog for dialogs with images
766  **/
767 GtkWidget *
gtk_message_dialog_get_image(GtkMessageDialog * dialog)768 gtk_message_dialog_get_image (GtkMessageDialog *dialog)
769 {
770   g_return_val_if_fail (GTK_IS_MESSAGE_DIALOG (dialog), NULL);
771 
772   return dialog->priv->image;
773 }
774 
775 /**
776  * gtk_message_dialog_set_markup:
777  * @message_dialog: a #GtkMessageDialog
778  * @str: markup string (see [Pango markup format][PangoMarkupFormat])
779  *
780  * Sets the text of the message dialog to be @str, which is marked
781  * up with the [Pango text markup language][PangoMarkupFormat].
782  *
783  * Since: 2.4
784  **/
785 void
gtk_message_dialog_set_markup(GtkMessageDialog * message_dialog,const gchar * str)786 gtk_message_dialog_set_markup (GtkMessageDialog *message_dialog,
787                                const gchar      *str)
788 {
789   GtkMessageDialogPrivate *priv;
790 
791   g_return_if_fail (GTK_IS_MESSAGE_DIALOG (message_dialog));
792 
793   priv = message_dialog->priv;
794 
795   priv->has_primary_markup = TRUE;
796   gtk_label_set_markup (GTK_LABEL (priv->label), str);
797 }
798 
799 /**
800  * gtk_message_dialog_format_secondary_text:
801  * @message_dialog: a #GtkMessageDialog
802  * @message_format: (allow-none): printf()-style format string, or %NULL
803  * @...: arguments for @message_format
804  *
805  * Sets the secondary text of the message dialog to be @message_format
806  * (with printf()-style).
807  *
808  * Since: 2.6
809  */
810 void
gtk_message_dialog_format_secondary_text(GtkMessageDialog * message_dialog,const gchar * message_format,...)811 gtk_message_dialog_format_secondary_text (GtkMessageDialog *message_dialog,
812                                           const gchar      *message_format,
813                                           ...)
814 {
815   va_list args;
816   gchar *msg = NULL;
817   GtkMessageDialogPrivate *priv;
818 
819   g_return_if_fail (GTK_IS_MESSAGE_DIALOG (message_dialog));
820 
821   priv = message_dialog->priv;
822 
823   if (message_format)
824     {
825       priv->has_secondary_text = TRUE;
826 
827       va_start (args, message_format);
828       msg = g_strdup_vprintf (message_format, args);
829       va_end (args);
830 
831       gtk_widget_show (priv->secondary_label);
832       gtk_label_set_text (GTK_LABEL (priv->secondary_label), msg);
833 
834       g_free (msg);
835     }
836   else
837     {
838       priv->has_secondary_text = FALSE;
839       gtk_widget_hide (priv->secondary_label);
840     }
841 
842   setup_primary_label_font (message_dialog);
843 }
844 
845 /**
846  * gtk_message_dialog_format_secondary_markup:
847  * @message_dialog: a #GtkMessageDialog
848  * @message_format: printf()-style markup string (see
849      [Pango markup format][PangoMarkupFormat]), or %NULL
850  * @...: arguments for @message_format
851  *
852  * Sets the secondary text of the message dialog to be @message_format (with
853  * printf()-style), which is marked up with the
854  * [Pango text markup language][PangoMarkupFormat].
855  *
856  * Due to an oversight, this function does not escape special XML characters
857  * like gtk_message_dialog_new_with_markup() does. Thus, if the arguments
858  * may contain special XML characters, you should use g_markup_printf_escaped()
859  * to escape it.
860 
861  * |[<!-- language="C" -->
862  * gchar *msg;
863  *
864  * msg = g_markup_printf_escaped (message_format, ...);
865  * gtk_message_dialog_format_secondary_markup (message_dialog,
866  *                                             "%s", msg);
867  * g_free (msg);
868  * ]|
869  *
870  * Since: 2.6
871  */
872 void
gtk_message_dialog_format_secondary_markup(GtkMessageDialog * message_dialog,const gchar * message_format,...)873 gtk_message_dialog_format_secondary_markup (GtkMessageDialog *message_dialog,
874                                             const gchar      *message_format,
875                                             ...)
876 {
877   va_list args;
878   gchar *msg = NULL;
879   GtkMessageDialogPrivate *priv;
880 
881   g_return_if_fail (GTK_IS_MESSAGE_DIALOG (message_dialog));
882 
883   priv = message_dialog->priv;
884 
885   if (message_format)
886     {
887       priv->has_secondary_text = TRUE;
888 
889       va_start (args, message_format);
890       msg = g_strdup_vprintf (message_format, args);
891       va_end (args);
892 
893       gtk_widget_show (priv->secondary_label);
894       gtk_label_set_markup (GTK_LABEL (priv->secondary_label), msg);
895 
896       g_free (msg);
897     }
898   else
899     {
900       priv->has_secondary_text = FALSE;
901       gtk_widget_hide (priv->secondary_label);
902     }
903 
904   setup_primary_label_font (message_dialog);
905 }
906 
907 /**
908  * gtk_message_dialog_get_message_area:
909  * @message_dialog: a #GtkMessageDialog
910  *
911  * Returns the message area of the dialog. This is the box where the
912  * dialog’s primary and secondary labels are packed. You can add your
913  * own extra content to that box and it will appear below those labels.
914  * See gtk_dialog_get_content_area() for the corresponding
915  * function in the parent #GtkDialog.
916  *
917  * Returns: (transfer none): A #GtkBox corresponding to the
918  *     “message area” in the @message_dialog.
919  *
920  * Since: 2.22
921  **/
922 GtkWidget *
gtk_message_dialog_get_message_area(GtkMessageDialog * message_dialog)923 gtk_message_dialog_get_message_area (GtkMessageDialog *message_dialog)
924 {
925   g_return_val_if_fail (GTK_IS_MESSAGE_DIALOG (message_dialog), NULL);
926 
927   return message_dialog->priv->message_area;
928 }
929 
930 static void
gtk_message_dialog_add_buttons(GtkMessageDialog * message_dialog,GtkButtonsType buttons)931 gtk_message_dialog_add_buttons (GtkMessageDialog* message_dialog,
932 				GtkButtonsType buttons)
933 {
934   GtkDialog* dialog = GTK_DIALOG (message_dialog);
935 
936 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
937   switch (buttons)
938     {
939     case GTK_BUTTONS_NONE:
940       /* nothing */
941       break;
942 
943     case GTK_BUTTONS_OK:
944       gtk_dialog_add_button (dialog, _("_OK"), GTK_RESPONSE_OK);
945       break;
946 
947     case GTK_BUTTONS_CLOSE:
948       gtk_dialog_add_button (dialog, _("_Close"), GTK_RESPONSE_CLOSE);
949       break;
950 
951     case GTK_BUTTONS_CANCEL:
952       gtk_dialog_add_button (dialog, _("_Cancel"), GTK_RESPONSE_CANCEL);
953       break;
954 
955     case GTK_BUTTONS_YES_NO:
956       gtk_dialog_add_button (dialog, _("_No"), GTK_RESPONSE_NO);
957       gtk_dialog_add_button (dialog, _("_Yes"), GTK_RESPONSE_YES);
958       gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
959 					       GTK_RESPONSE_YES,
960 					       GTK_RESPONSE_NO,
961 					       -1);
962       break;
963 
964     case GTK_BUTTONS_OK_CANCEL:
965       gtk_dialog_add_button (dialog, _("_Cancel"), GTK_RESPONSE_CANCEL);
966       gtk_dialog_add_button (dialog, _("_OK"), GTK_RESPONSE_OK);
967       gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
968 					       GTK_RESPONSE_OK,
969 					       GTK_RESPONSE_CANCEL,
970 					       -1);
971       break;
972 
973     default:
974       g_warning ("Unknown GtkButtonsType");
975       break;
976     }
977 G_GNUC_END_IGNORE_DEPRECATIONS
978 
979   g_object_notify (G_OBJECT (message_dialog), "buttons");
980 }
981 
982 static void
gtk_message_dialog_style_updated(GtkWidget * widget)983 gtk_message_dialog_style_updated (GtkWidget *widget)
984 {
985   GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG (widget);
986   GtkWidget *parent;
987   gint border_width;
988 
989   parent = gtk_widget_get_parent (dialog->priv->message_area);
990 
991   if (parent)
992     {
993       gtk_widget_style_get (widget, "message-border",
994                             &border_width, NULL);
995 
996       gtk_container_set_border_width (GTK_CONTAINER (parent),
997                                       MAX (0, border_width - 7));
998     }
999 
1000   GTK_WIDGET_CLASS (gtk_message_dialog_parent_class)->style_updated (widget);
1001 }
1002