gtkmessagedialog.c 24.2 KB
Newer Older
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 2 -*- */
Cody Russell's avatar
Cody Russell committed
2
/* GTK - The GIMP Toolkit
3
4
5
6
7
8
9
10
11
12
13
14
15
 * Copyright (C) 2000 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
Javier Jardón's avatar
Javier Jardón committed
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
22
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * 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 "gtkmessagedialog.h"
29

Matthias Clasen's avatar
Matthias Clasen committed
30
#include "gtkbox.h"
31
32
#include "gtkbuildable.h"
#include "gtkdialogprivate.h"
33
#include "gtkintl.h"
34
#include "gtklabel.h"
35
#include "gtkprivate.h"
36
#include "gtktypebuiltins.h"
37

38
39
#include <string.h>

40
/**
Matthias Clasen's avatar
Matthias Clasen committed
41
 * GtkMessageDialog:
42
 *
Matthias Clasen's avatar
Matthias Clasen committed
43
44
45
46
47
48
49
 * `GtkMessageDialog` presents a dialog with some message text.
 *
 * ![An example GtkMessageDialog](messagedialog.png)
 *
 * It’s simply a convenience widget; you could construct the equivalent of
 * `GtkMessageDialog` from `GtkDialog` without too much effort, but
 * `GtkMessageDialog` saves typing.
50
 *
51
 * The easiest way to do a modal message dialog is to use the %GTK_DIALOG_MODAL
Matthias Clasen's avatar
Matthias Clasen committed
52
 * flag, which will call [method@Gtk.Window.set_modal] internally. The dialog will
53
 * prevent interaction with the parent window until it's hidden or destroyed.
Matthias Clasen's avatar
Matthias Clasen committed
54
55
 * You can use the [signal@Gtk.Dialog::response] signal to know when the user
 * dismissed the dialog.
Matthias Clasen's avatar
Matthias Clasen committed
56
57
 *
 * An example for using a modal dialog:
Matthias Clasen's avatar
Matthias Clasen committed
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
 * ```c
 * GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL;
 * dialog = gtk_message_dialog_new (parent_window,
 *                                  flags,
 *                                  GTK_MESSAGE_ERROR,
 *                                  GTK_BUTTONS_CLOSE,
 *                                  "Error reading “%s”: %s",
 *                                  filename,
 *                                  g_strerror (errno));
 * // Destroy the dialog when the user responds to it
 * // (e.g. clicks a button)
 *
 * g_signal_connect (dialog, "response",
 *                   G_CALLBACK (gtk_window_destroy),
 *                   NULL);
 * ```
 *
 * You might do a non-modal `GtkMessageDialog` simply by omitting the
76
 * %GTK_DIALOG_MODAL flag:
Matthias Clasen's avatar
Matthias Clasen committed
77
 *
Matthias Clasen's avatar
Matthias Clasen committed
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
 * ```c
 * GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
 * dialog = gtk_message_dialog_new (parent_window,
 *                                  flags,
 *                                  GTK_MESSAGE_ERROR,
 *                                  GTK_BUTTONS_CLOSE,
 *                                  "Error reading “%s”: %s",
 *                                  filename,
 *                                  g_strerror (errno));
 *
 * // Destroy the dialog when the user responds to it
 * // (e.g. clicks a button)
 * g_signal_connect (dialog, "response",
 *                   G_CALLBACK (gtk_window_destroy),
 *                   NULL);
 * ```
94
 *
95
 * # GtkMessageDialog as GtkBuildable
Matthias Clasen's avatar
Matthias Clasen committed
96
 *
Matthias Clasen's avatar
Matthias Clasen committed
97
 * The `GtkMessageDialog` implementation of the `GtkBuildable` interface exposes
William Jon McCann's avatar
William Jon McCann committed
98
 * the message area as an internal child with the name “message_area”.
99
100
 */

101
typedef struct
102
{
103
104
105
106
107
108
109
  GtkWidget     *label;
  GtkWidget     *message_area; /* vbox for the primary and secondary labels, and any extra content from the caller */
  GtkWidget     *secondary_label;

  guint          has_primary_markup : 1;
  guint          has_secondary_text : 1;
  guint          message_type       : 3;
110
} GtkMessageDialogPrivate;
111
112
113
114
115

struct _GtkMessageDialogClass
{
  GtkDialogClass parent_class;
};
116

117
118
119
enum {
  PROP_0,
  PROP_MESSAGE_TYPE,
120
121
122
123
  PROP_BUTTONS,
  PROP_TEXT,
  PROP_USE_MARKUP,
  PROP_SECONDARY_TEXT,
Matthias Clasen's avatar
Matthias Clasen committed
124
  PROP_SECONDARY_USE_MARKUP,
125
126
  PROP_IMAGE,
  PROP_MESSAGE_AREA
127
128
};

129
G_DEFINE_TYPE_WITH_PRIVATE (GtkMessageDialog, gtk_message_dialog, GTK_TYPE_DIALOG)
130

131
132
static void
setup_type (GtkMessageDialog *dialog,
133
            GtkMessageType    type)
134
{
135
  GtkMessageDialogPrivate *priv = gtk_message_dialog_get_instance_private (dialog);
136

137
138
139
  if (priv->message_type == type)
    return;

Matthias Clasen's avatar
Matthias Clasen committed
140
141
  priv->message_type = type;

142
  g_object_notify (G_OBJECT (dialog), "message-type");
143
144
}

145
static void
146
147
gtk_message_dialog_add_buttons (GtkMessageDialog *message_dialog,
                                GtkButtonsType    buttons)
148
{
149
  GtkDialog* dialog = GTK_DIALOG (message_dialog);
150

151
152
153
154
155
  switch (buttons)
    {
    case GTK_BUTTONS_NONE:
      /* nothing */
      break;
156

157
158
159
    case GTK_BUTTONS_OK:
      gtk_dialog_add_button (dialog, _("_OK"), GTK_RESPONSE_OK);
      break;
160

161
162
163
    case GTK_BUTTONS_CLOSE:
      gtk_dialog_add_button (dialog, _("_Close"), GTK_RESPONSE_CLOSE);
      break;
164

165
166
167
    case GTK_BUTTONS_CANCEL:
      gtk_dialog_add_button (dialog, _("_Cancel"), GTK_RESPONSE_CANCEL);
      break;
168

169
170
171
172
    case GTK_BUTTONS_YES_NO:
      gtk_dialog_add_button (dialog, _("_No"), GTK_RESPONSE_NO);
      gtk_dialog_add_button (dialog, _("_Yes"), GTK_RESPONSE_YES);
      break;
173

174
175
176
177
    case GTK_BUTTONS_OK_CANCEL:
      gtk_dialog_add_button (dialog, _("_Cancel"), GTK_RESPONSE_CANCEL);
      gtk_dialog_add_button (dialog, _("_OK"), GTK_RESPONSE_OK);
      break;
178

179
180
181
    default:
      g_warning ("Unknown GtkButtonsType");
      break;
182
    }
183
184

  g_object_notify (G_OBJECT (message_dialog), "buttons");
185
186
}

187
static void
188
gtk_message_dialog_set_property (GObject      *object,
189
190
191
                                 guint         prop_id,
                                 const GValue *value,
                                 GParamSpec   *pspec)
192
{
193
  GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG (object);
194
  GtkMessageDialogPrivate *priv = gtk_message_dialog_get_instance_private (dialog);
195

196
197
198
199
200
201
202
203
  switch (prop_id)
    {
    case PROP_MESSAGE_TYPE:
      setup_type (dialog, g_value_get_enum (value));
      break;
    case PROP_BUTTONS:
      gtk_message_dialog_add_buttons (dialog, g_value_get_enum (value));
      break;
204
205
    case PROP_TEXT:
      if (priv->has_primary_markup)
206
        gtk_label_set_markup (GTK_LABEL (priv->label), g_value_get_string (value));
207
      else
208
        gtk_label_set_text (GTK_LABEL (priv->label), g_value_get_string (value));
209
210
      break;
    case PROP_USE_MARKUP:
211
212
213
214
215
216
      if (priv->has_primary_markup != g_value_get_boolean (value))
        {
          priv->has_primary_markup = g_value_get_boolean (value);
          gtk_label_set_use_markup (GTK_LABEL (priv->label), priv->has_primary_markup);
          g_object_notify_by_pspec (object, pspec);
        }
217
218
219
      break;
    case PROP_SECONDARY_TEXT:
      {
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
        const char *txt = g_value_get_string (value);

        if (gtk_label_get_use_markup (GTK_LABEL (priv->secondary_label)))
          gtk_label_set_markup (GTK_LABEL (priv->secondary_label), txt);
        else
          gtk_label_set_text (GTK_LABEL (priv->secondary_label), txt);

        if (txt)
          {
            priv->has_secondary_text = TRUE;
                  gtk_widget_add_css_class (priv->label, "title");
            gtk_widget_show (priv->secondary_label);
          }
        else
          {
            priv->has_secondary_text = FALSE;
                  gtk_widget_remove_css_class (priv->label, "title");
            gtk_widget_hide (priv->secondary_label);
          }
239
240
241
      }
      break;
    case PROP_SECONDARY_USE_MARKUP:
242
243
244
245
246
      if (gtk_label_get_use_markup (GTK_LABEL (priv->secondary_label)) != g_value_get_boolean (value))
        {
          gtk_label_set_use_markup (GTK_LABEL (priv->secondary_label), g_value_get_boolean (value));
          g_object_notify_by_pspec (object, pspec);
        }
247
      break;
Matthias Clasen's avatar
Matthias Clasen committed
248

249
250
251
252
253
254
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

255
static void
256
gtk_message_dialog_get_property (GObject     *object,
257
258
259
                                 guint        prop_id,
                                 GValue      *value,
                                 GParamSpec  *pspec)
260
{
261
  GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG (object);
262
  GtkMessageDialogPrivate *priv = gtk_message_dialog_get_instance_private (dialog);
263

264
265
266
  switch (prop_id)
    {
    case PROP_MESSAGE_TYPE:
Matthias Clasen's avatar
Matthias Clasen committed
267
      g_value_set_enum (value, (GtkMessageType) priv->message_type);
268
      break;
269
    case PROP_TEXT:
270
      g_value_set_string (value, gtk_label_get_label (GTK_LABEL (priv->label)));
271
272
273
274
275
276
      break;
    case PROP_USE_MARKUP:
      g_value_set_boolean (value, priv->has_primary_markup);
      break;
    case PROP_SECONDARY_TEXT:
      if (priv->has_secondary_text)
277
278
      g_value_set_string (value,
                          gtk_label_get_label (GTK_LABEL (priv->secondary_label)));
279
      else
280
        g_value_set_string (value, NULL);
281
282
283
      break;
    case PROP_SECONDARY_USE_MARKUP:
      if (priv->has_secondary_text)
284
285
        g_value_set_boolean (value,
                             gtk_label_get_use_markup (GTK_LABEL (priv->secondary_label)));
286
      else
287
        g_value_set_boolean (value, FALSE);
288
      break;
289
290
291
    case PROP_MESSAGE_AREA:
      g_value_set_object (value, priv->message_area);
      break;
292
293
294
295
296
297
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
static void
update_title (GObject    *dialog,
              GParamSpec *pspec,
              GtkWidget  *label)
{
  const char *title;

  title = gtk_window_get_title (GTK_WINDOW (dialog));
  gtk_label_set_label (GTK_LABEL (label), title);
  gtk_widget_set_visible (label, title && title[0]);
}

static void
gtk_message_dialog_constructed (GObject *object)
{
  GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG (object);
  gboolean use_header;

  G_OBJECT_CLASS (gtk_message_dialog_parent_class)->constructed (object);

  g_object_get (gtk_widget_get_settings (GTK_WIDGET (dialog)),
                "gtk-dialogs-use-header", &use_header,
                NULL);

  if (use_header)
    {
      GtkWidget *box;
      GtkWidget *label;

      box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
      gtk_widget_show (box);
      gtk_widget_set_size_request (box, -1, 16);
      label = gtk_label_new ("");
      gtk_widget_hide (label);
      gtk_widget_set_margin_top (label, 6);
      gtk_widget_set_margin_bottom (label, 6);
      gtk_widget_set_halign (label, GTK_ALIGN_CENTER);
      gtk_widget_set_hexpand (label, TRUE);
      gtk_widget_add_css_class (label, "title");
      gtk_box_append (GTK_BOX (box), label);
      g_signal_connect_object (dialog, "notify::title", G_CALLBACK (update_title), label, 0);

      gtk_window_set_titlebar (GTK_WINDOW (dialog), box);
    }
}

static void
gtk_message_dialog_class_init (GtkMessageDialogClass *class)
{
  GtkWidgetClass *widget_class;
  GObjectClass *gobject_class;

  widget_class = GTK_WIDGET_CLASS (class);
  gobject_class = G_OBJECT_CLASS (class);

  gobject_class->constructed = gtk_message_dialog_constructed;
  gobject_class->set_property = gtk_message_dialog_set_property;
  gobject_class->get_property = gtk_message_dialog_get_property;

  /**
   * GtkMessageDialog:message-type:
   *
   * The type of the message.
   */
  g_object_class_install_property (gobject_class,
                                   PROP_MESSAGE_TYPE,
364
                                   g_param_spec_enum ("message-type", NULL, NULL,
365
366
367
368
369
                                                      GTK_TYPE_MESSAGE_TYPE,
                                                      GTK_MESSAGE_INFO,
                                                      GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY));
  g_object_class_install_property (gobject_class,
                                   PROP_BUTTONS,
370
                                   g_param_spec_enum ("buttons", NULL, NULL,
371
372
373
374
375
376
                                                      GTK_TYPE_BUTTONS_TYPE,
                                                      GTK_BUTTONS_NONE,
                                                      GTK_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
  /**
   * GtkMessageDialog:text:
   *
Matthias Clasen's avatar
Matthias Clasen committed
377
378
379
   * The primary text of the message dialog.
   *
   * If the dialog has a secondary text, this will appear as the title.
380
381
382
   */
  g_object_class_install_property (gobject_class,
                                   PROP_TEXT,
383
                                   g_param_spec_string ("text", NULL, NULL,
384
385
386
387
388
389
                                                        "",
                                                        GTK_PARAM_READWRITE));
  /**
   * GtkMessageDialog:use-markup:
   *
   * %TRUE if the primary text of the dialog includes Pango markup.
Matthias Clasen's avatar
Matthias Clasen committed
390
391
   *
   * See [func@Pango.parse_markup].
392
393
394
   */
  g_object_class_install_property (gobject_class,
                                   PROP_USE_MARKUP,
395
                                   g_param_spec_boolean ("use-markup", NULL, NULL,
396
397
398
399
400
401
402
403
404
                                                         FALSE,
                                                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
  /**
   * GtkMessageDialog:secondary-text:
   *
   * The secondary text of the message dialog.
   */
  g_object_class_install_property (gobject_class,
                                   PROP_SECONDARY_TEXT,
405
                                   g_param_spec_string ("secondary-text", NULL, NULL,
406
407
408
409
410
411
                                                        NULL,
                                                        GTK_PARAM_READWRITE));
  /**
   * GtkMessageDialog:secondary-use-markup:
   *
   * %TRUE if the secondary text of the dialog includes Pango markup.
Matthias Clasen's avatar
Matthias Clasen committed
412
413
   *
   * See [func@Pango.parse_markup].
414
415
416
   */
  g_object_class_install_property (gobject_class,
                                   PROP_SECONDARY_USE_MARKUP,
417
                                   g_param_spec_boolean ("secondary-use-markup", NULL, NULL,
418
419
420
421
422
                                                         FALSE,
                                                         GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
  /**
   * GtkMessageDialog:message-area:
   *
Matthias Clasen's avatar
Matthias Clasen committed
423
424
425
426
   * The `GtkBox` that corresponds to the message area of this dialog.
   *
   * See [method@Gtk.MessageDialog.get_message_area] for a detailed
   * description of this area.
427
428
429
   */
  g_object_class_install_property (gobject_class,
                                   PROP_MESSAGE_AREA,
430
                                   g_param_spec_object ("message-area", NULL, NULL,
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
                                                        GTK_TYPE_WIDGET,
                                                        GTK_PARAM_READABLE));

  gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkmessagedialog.ui");
  gtk_widget_class_bind_template_child_private (widget_class, GtkMessageDialog, label);
  gtk_widget_class_bind_template_child_private (widget_class, GtkMessageDialog, secondary_label);
  gtk_widget_class_bind_template_child_internal_private (widget_class, GtkMessageDialog, message_area);
}

static void
gtk_message_dialog_init (GtkMessageDialog *dialog)
{
  GtkMessageDialogPrivate *priv = gtk_message_dialog_get_instance_private (dialog);
  GtkWidget *action_area;
  GtkSettings *settings;
  gboolean use_caret;

  priv->has_primary_markup = FALSE;
  priv->has_secondary_text = FALSE;
  priv->message_type = GTK_MESSAGE_OTHER;

  gtk_widget_add_css_class (GTK_WIDGET (dialog), "message");

  gtk_widget_init_template (GTK_WIDGET (dialog));
  action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
  gtk_widget_set_halign (action_area, GTK_ALIGN_FILL);
  gtk_box_set_homogeneous (GTK_BOX (action_area), TRUE);

  settings = gtk_widget_get_settings (GTK_WIDGET (dialog));
  g_object_get (settings, "gtk-keynav-use-caret", &use_caret, NULL);
  gtk_label_set_selectable (GTK_LABEL (priv->label), use_caret);
  gtk_label_set_selectable (GTK_LABEL (priv->secondary_label), use_caret);
}

465
466
/**
 * gtk_message_dialog_new:
Matthias Clasen's avatar
Matthias Clasen committed
467
 * @parent: (nullable): transient parent
468
469
470
 * @flags: flags
 * @type: type of message
 * @buttons: set of buttons to use
Matthias Clasen's avatar
Matthias Clasen committed
471
 * @message_format: (nullable): printf()-style format string
Matthias Clasen's avatar
Matthias Clasen committed
472
 * @...: arguments for @message_format
473
 *
Matthias Clasen's avatar
Matthias Clasen committed
474
 * Creates a new message dialog.
475
 *
Matthias Clasen's avatar
Matthias Clasen committed
476
477
478
479
480
481
 * This is a simple dialog with some text the user may want to see.
 * When the user clicks a button a “response” signal is emitted with
 * response IDs from [enum@Gtk.ResponseType]. See [class@Gtk.Dialog]
 * for more details.
 *
 * Returns: (transfer none): a new `GtkMessageDialog`
Matthias Clasen's avatar
Matthias Clasen committed
482
 */
483
484
485
486
487
GtkWidget*
gtk_message_dialog_new (GtkWindow     *parent,
                        GtkDialogFlags flags,
                        GtkMessageType type,
                        GtkButtonsType buttons,
Benjamin Otte's avatar
Benjamin Otte committed
488
                        const char    *message_format,
489
490
491
492
                        ...)
{
  GtkWidget *widget;
  GtkDialog *dialog;
Benjamin Otte's avatar
Benjamin Otte committed
493
  char * msg = NULL;
494
  va_list args;
495
496
497

  g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);

Manish Singh's avatar
Manish Singh committed
498
  widget = g_object_new (GTK_TYPE_MESSAGE_DIALOG,
499
                         "use-header-bar", FALSE,
500
501
502
                         "message-type", type,
                         "buttons", buttons,
                         NULL);
503
504
505
506
  dialog = GTK_DIALOG (widget);

  if (message_format)
    {
507
      GtkMessageDialogPrivate *priv = gtk_message_dialog_get_instance_private ((GtkMessageDialog*)dialog);
508
      va_start (args, message_format);
Manish Singh's avatar
Manish Singh committed
509
      msg = g_strdup_vprintf (message_format, args);
510
      va_end (args);
511

512
      gtk_label_set_text (GTK_LABEL (priv->label), msg);
513

514
515
516
517
      g_free (msg);
    }

  if (parent != NULL)
518
    gtk_window_set_transient_for (GTK_WINDOW (widget), GTK_WINDOW (parent));
519

520
521
522
523
524
  if (flags & GTK_DIALOG_MODAL)
    gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);

  if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
    gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
Havoc Pennington's avatar
Havoc Pennington committed
525

526
527
528
  return widget;
}

529
530
/**
 * gtk_message_dialog_new_with_markup:
Matthias Clasen's avatar
Matthias Clasen committed
531
 * @parent: (nullable): transient parent
532
533
534
 * @flags: flags
 * @type: type of message
 * @buttons: set of buttons to use
Matthias Clasen's avatar
Matthias Clasen committed
535
 * @message_format: (nullable): printf()-style format string
Matthias Clasen's avatar
Matthias Clasen committed
536
537
 * @...: arguments for @message_format
 *
Matthias Clasen's avatar
Matthias Clasen committed
538
539
540
541
542
543
 * Creates a new message dialog.
 *
 * This is a simple dialog with some text that is marked up with
 * Pango markup. When the user clicks a button a “response” signal
 * is emitted with response IDs from [enum@Gtk.ResponseType]. See
 * [class@Gtk.Dialog] for more details.
544
 *
545
546
547
548
549
 * Special XML characters in the printf() arguments passed to this
 * function will automatically be escaped as necessary.
 * (See g_markup_printf_escaped() for how this is implemented.)
 * Usually this is what you want, but if you have an existing
 * Pango markup string that you want to use literally as the
Matthias Clasen's avatar
Matthias Clasen committed
550
 * label, then you need to use [method@Gtk.MessageDialog.set_markup]
551
 * instead, since you can’t pass the markup string either
William Jon McCann's avatar
William Jon McCann committed
552
 * as the format (it might contain “%” characters) or as a string
553
 * argument.
Matthias Clasen's avatar
Matthias Clasen committed
554
555
556
557
558
559
560
561
562
563
564
565
566
567
 *
 * ```c
 * GtkWidget *dialog;
 * GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
 * dialog = gtk_message_dialog_new (parent_window,
 *                                  flags,
 *                                  GTK_MESSAGE_ERROR,
 *                                  GTK_BUTTONS_CLOSE,
 *                                  NULL);
 * gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog),
 *                                markup);
 * ```
 *
 * Returns: a new `GtkMessageDialog`
568
569
570
571
572
573
 **/
GtkWidget*
gtk_message_dialog_new_with_markup (GtkWindow     *parent,
                                    GtkDialogFlags flags,
                                    GtkMessageType type,
                                    GtkButtonsType buttons,
Benjamin Otte's avatar
Benjamin Otte committed
574
                                    const char    *message_format,
575
576
577
578
                                    ...)
{
  GtkWidget *widget;
  va_list args;
Benjamin Otte's avatar
Benjamin Otte committed
579
  char *msg = NULL;
580
581
582

  g_return_val_if_fail (parent == NULL || GTK_IS_WINDOW (parent), NULL);

583
  widget = gtk_message_dialog_new (parent, flags, type, buttons, NULL);
584
585
586
587

  if (message_format)
    {
      va_start (args, message_format);
588
      msg = g_markup_vprintf_escaped (message_format, args);
589
590
      va_end (args);

591
      gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (widget), msg);
592
593
594
595
596
597
598

      g_free (msg);
    }

  return widget;
}

599
600
/**
 * gtk_message_dialog_set_markup:
Matthias Clasen's avatar
Matthias Clasen committed
601
602
 * @message_dialog: a `GtkMessageDialog`
 * @str: string with Pango markup
603
 *
Matthias Clasen's avatar
Matthias Clasen committed
604
605
 * Sets the text of the message dialog.
 */
606
607
void
gtk_message_dialog_set_markup (GtkMessageDialog *message_dialog,
Benjamin Otte's avatar
Benjamin Otte committed
608
                               const char       *str)
609
{
610
  GtkMessageDialogPrivate *priv = gtk_message_dialog_get_instance_private (message_dialog);
611

612
  g_return_if_fail (GTK_IS_MESSAGE_DIALOG (message_dialog));
613
614

  priv->has_primary_markup = TRUE;
615
  gtk_label_set_markup (GTK_LABEL (priv->label), str);
616
617
}

618
619
/**
 * gtk_message_dialog_format_secondary_text:
Matthias Clasen's avatar
Matthias Clasen committed
620
 * @message_dialog: a `GtkMessageDialog`
Matthias Clasen's avatar
Matthias Clasen committed
621
 * @message_format: (nullable): printf()-style format string
Matthias Clasen's avatar
Matthias Clasen committed
622
623
 * @...: arguments for @message_format
 *
Matthias Clasen's avatar
Matthias Clasen committed
624
 * Sets the secondary text of the message dialog.
Matthias Clasen's avatar
Matthias Clasen committed
625
 */
626
627
void
gtk_message_dialog_format_secondary_text (GtkMessageDialog *message_dialog,
Benjamin Otte's avatar
Benjamin Otte committed
628
                                          const char       *message_format,
629
630
                                          ...)
{
631
  GtkMessageDialogPrivate *priv = gtk_message_dialog_get_instance_private (message_dialog);
632
  va_list args;
Benjamin Otte's avatar
Benjamin Otte committed
633
  char *msg = NULL;
634
635
636
637
638
639

  g_return_if_fail (GTK_IS_MESSAGE_DIALOG (message_dialog));

  if (message_format)
    {
      priv->has_secondary_text = TRUE;
640
      gtk_widget_add_css_class (priv->label, "title");
641
642
643
644
645
646
647
648
649
650
651
652
653

      va_start (args, message_format);
      msg = g_strdup_vprintf (message_format, args);
      va_end (args);

      gtk_widget_show (priv->secondary_label);
      gtk_label_set_text (GTK_LABEL (priv->secondary_label), msg);

      g_free (msg);
    }
  else
    {
      priv->has_secondary_text = FALSE;
654
      gtk_widget_remove_css_class (priv->label, "title");
655
656
657
658
659
660
      gtk_widget_hide (priv->secondary_label);
    }
}

/**
 * gtk_message_dialog_format_secondary_markup:
Matthias Clasen's avatar
Matthias Clasen committed
661
662
 * @message_dialog: a `GtkMessageDialog`
 * @message_format: printf()-style string with Pango markup
Matthias Clasen's avatar
Matthias Clasen committed
663
664
 * @...: arguments for @message_format
 *
Matthias Clasen's avatar
Matthias Clasen committed
665
 * Sets the secondary text of the message dialog.
666
 *
Matthias Clasen's avatar
Matthias Clasen committed
667
668
669
670
671
672
673
674
 * The @message_format is assumed to contain Pango markup.
 *
 * Due to an oversight, this function does not escape special
 * XML characters like [ctor@Gtk.MessageDialog.new_with_markup]
 * does. Thus, if the arguments may contain special XML characters,
 * you should use g_markup_printf_escaped() to escape it.
 *
 * ```c
Benjamin Otte's avatar
Benjamin Otte committed
675
 * char *msg;
Matthias Clasen's avatar
Matthias Clasen committed
676
 *
677
 * msg = g_markup_printf_escaped (message_format, ...);
678
679
 * gtk_message_dialog_format_secondary_markup (message_dialog,
 *                                             "%s", msg);
680
 * g_free (msg);
Matthias Clasen's avatar
Matthias Clasen committed
681
 * ```
Matthias Clasen's avatar
Matthias Clasen committed
682
 */
683
684
void
gtk_message_dialog_format_secondary_markup (GtkMessageDialog *message_dialog,
Benjamin Otte's avatar
Benjamin Otte committed
685
                                            const char       *message_format,
686
687
                                            ...)
{
688
  GtkMessageDialogPrivate *priv = gtk_message_dialog_get_instance_private (message_dialog);
689
  va_list args;
Benjamin Otte's avatar
Benjamin Otte committed
690
  char *msg = NULL;
691
692
693
694
695
696

  g_return_if_fail (GTK_IS_MESSAGE_DIALOG (message_dialog));

  if (message_format)
    {
      priv->has_secondary_text = TRUE;
697
      gtk_widget_add_css_class (priv->label, "title");
698
699
700
701
702
703
704
705
706
707
708
709
710

      va_start (args, message_format);
      msg = g_strdup_vprintf (message_format, args);
      va_end (args);

      gtk_widget_show (priv->secondary_label);
      gtk_label_set_markup (GTK_LABEL (priv->secondary_label), msg);

      g_free (msg);
    }
  else
    {
      priv->has_secondary_text = FALSE;
711
      gtk_widget_remove_css_class (priv->label, "title");
712
713
714
715
      gtk_widget_hide (priv->secondary_label);
    }
}

716
717
/**
 * gtk_message_dialog_get_message_area:
Matthias Clasen's avatar
Matthias Clasen committed
718
719
720
 * @message_dialog: a `GtkMessageDialog`
 *
 * Returns the message area of the dialog.
721
 *
Matthias Clasen's avatar
Matthias Clasen committed
722
723
724
725
 * This is the box where the dialog’s primary and secondary labels
 * are packed. You can add your own extra content to that box and it
 * will appear below those labels. See [method@Gtk.Dialog.get_content_area]
 * for the corresponding function in the parent [class@Gtk.Dialog].
Matthias Clasen's avatar
Matthias Clasen committed
726
 *
Matthias Clasen's avatar
Matthias Clasen committed
727
 * Returns: (transfer none): A `GtkBox` corresponding to the
Matthias Clasen's avatar
Matthias Clasen committed
728
 *   “message area” in the @message_dialog
Matthias Clasen's avatar
Matthias Clasen committed
729
 */
730
731
732
GtkWidget *
gtk_message_dialog_get_message_area (GtkMessageDialog *message_dialog)
{
733
734
  GtkMessageDialogPrivate *priv = gtk_message_dialog_get_instance_private (message_dialog);

735
736
  g_return_val_if_fail (GTK_IS_MESSAGE_DIALOG (message_dialog), NULL);

737
  return priv->message_area;
738
}