gtkmessagedialog.c 30.5 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 23 24 25
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
 */

26
#include "config.h"
27

28 29
#include <string.h>

30
#include "gtkmessagedialog.h"
Matthias Clasen's avatar
Matthias Clasen committed
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 43 44 45 46
/**
 * SECTION:gtkmessagedialog
 * @Short_description: A convenient message window
 * @Title: GtkMessageDialog
 * @See_also:#GtkDialog
 *
47
 * #GtkMessageDialog presents a dialog with some message text. It’s simply a
48 49 50
 * convenience widget; you could construct the equivalent of #GtkMessageDialog
 * from #GtkDialog without too much effort, but #GtkMessageDialog saves typing.
 *
51 52 53 54
 * One difference from #GtkDialog is that #GtkMessageDialog sets the
 * #GtkWindow:skip-taskbar-hint property to %TRUE, so that the dialog is hidden
 * from the taskbar by default.
 *
55 56 57 58
 * The easiest way to do a modal message dialog is to use gtk_dialog_run(), though
 * you can also pass in the %GTK_DIALOG_MODAL flag, gtk_dialog_run() automatically
 * makes the dialog modal and waits for the user to respond to it. gtk_dialog_run()
 * returns when any dialog button is clicked.
59 60
 *
 * An example for using a modal dialog:
61
 * |[<!-- language="C" -->
62 63 64
 *  GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
 *  dialog = gtk_message_dialog_new (parent_window,
 *                                   flags,
65 66
 *                                   GTK_MESSAGE_ERROR,
 *                                   GTK_BUTTONS_CLOSE,
67 68 69
 *                                   "Error reading “%s”: %s",
 *                                   filename,
 *                                   g_strerror (errno));
70 71
 *  gtk_dialog_run (GTK_DIALOG (dialog));
 *  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 79 80
 *  GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
 *  dialog = gtk_message_dialog_new (parent_window,
 *                                   flags,
81 82
 *                                   GTK_MESSAGE_ERROR,
 *                                   GTK_BUTTONS_CLOSE,
83 84 85 86
 *                                   "Error reading “%s”: %s",
 *                                   filename,
 *                                   g_strerror (errno));
 *
87 88
 *  // Destroy the dialog when the user responds to it
 *  // (e.g. clicks a button)
89 90 91 92
 *
 *  g_signal_connect_swapped (dialog, "response",
 *                            G_CALLBACK (gtk_widget_destroy),
 *                            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 104 105 106 107 108 109 110
  GtkWidget     *image;
  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;
111 112
};

113
static void gtk_message_dialog_style_updated (GtkWidget       *widget);
114

115
static void gtk_message_dialog_constructed  (GObject          *object);
116 117 118 119 120 121 122 123 124 125
static void gtk_message_dialog_set_property (GObject          *object,
					     guint             prop_id,
					     const GValue     *value,
					     GParamSpec       *pspec);
static void gtk_message_dialog_get_property (GObject          *object,
					     guint             prop_id,
					     GValue           *value,
					     GParamSpec       *pspec);
static void gtk_message_dialog_add_buttons  (GtkMessageDialog *message_dialog,
					     GtkButtonsType    buttons);
126
static void      gtk_message_dialog_buildable_interface_init     (GtkBuildableIface *iface);
127 128 129 130

enum {
  PROP_0,
  PROP_MESSAGE_TYPE,
131 132 133 134
  PROP_BUTTONS,
  PROP_TEXT,
  PROP_USE_MARKUP,
  PROP_SECONDARY_TEXT,
Matthias Clasen's avatar
Matthias Clasen committed
135
  PROP_SECONDARY_USE_MARKUP,
136 137
  PROP_IMAGE,
  PROP_MESSAGE_AREA
138 139
};

140
G_DEFINE_TYPE_WITH_CODE (GtkMessageDialog, gtk_message_dialog, GTK_TYPE_DIALOG,
141
                         G_ADD_PRIVATE (GtkMessageDialog)
142 143 144 145 146 147 148 149 150 151 152 153 154
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
                                                gtk_message_dialog_buildable_interface_init))

static GtkBuildableIface *parent_buildable_iface;

static void
gtk_message_dialog_buildable_interface_init (GtkBuildableIface *iface)
{
  parent_buildable_iface = g_type_interface_peek_parent (iface);
  iface->custom_tag_start = parent_buildable_iface->custom_tag_start;
  iface->custom_finished = parent_buildable_iface->custom_finished;
}

155 156 157
static void
gtk_message_dialog_class_init (GtkMessageDialogClass *class)
{
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 167
  gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_ALERT);

168
  gobject_class->constructed = gtk_message_dialog_constructed;
169 170
  gobject_class->set_property = gtk_message_dialog_set_property;
  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 175
                                                             P_("label border"),
                                                             P_("Width of border around the label in the message dialog"),
176 177
                                                             0,
                                                             G_MAXINT,
178
                                                             12,
179
                                                             GTK_PARAM_READABLE));
180

Matthias Clasen's avatar
Matthias Clasen committed
181 182 183
  /**
   * GtkMessageDialog:message-type:
   *
184
   * The type of the message.
Matthias Clasen's avatar
Matthias Clasen committed
185
   */
186 187
  g_object_class_install_property (gobject_class,
                                   PROP_MESSAGE_TYPE,
188
                                   g_param_spec_enum ("message-type",
189 190
						      P_("Message Type"),
						      P_("The type of message"),
191 192
						      GTK_TYPE_MESSAGE_TYPE,
                                                      GTK_MESSAGE_INFO,
193
                                                      GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY));
194 195 196
  g_object_class_install_property (gobject_class,
                                   PROP_BUTTONS,
                                   g_param_spec_enum ("buttons",
197 198
						      P_("Message Buttons"),
						      P_("The buttons shown in the message dialog"),
199 200
						      GTK_TYPE_BUTTONS_TYPE,
                                                      GTK_BUTTONS_NONE,
201
                                                      GTK_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY));
202 203 204 205 206 207 208 209 210 211 212 213 214 215

  /**
   * GtkMessageDialog:text:
   * 
   * The primary text of the message dialog. If the dialog has 
   * a secondary text, this will appear as the title.
   *
   * Since: 2.10
   */
  g_object_class_install_property (gobject_class,
                                   PROP_TEXT,
                                   g_param_spec_string ("text",
                                                        P_("Text"),
                                                        P_("The primary text of the message dialog"),
216
                                                        "",
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
                                                        GTK_PARAM_READWRITE));

  /**
   * GtkMessageDialog:use-markup:
   * 
   * %TRUE if the primary text of the dialog includes Pango markup. 
   * See pango_parse_markup(). 
   *
   * Since: 2.10
   */
  g_object_class_install_property (gobject_class,
				   PROP_USE_MARKUP,
				   g_param_spec_boolean ("use-markup",
							 P_("Use Markup"),
							 P_("The primary text of the title includes Pango markup."),
							 FALSE,
233
							 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
  
  /**
   * GtkMessageDialog:secondary-text:
   * 
   * The secondary text of the message dialog. 
   *
   * Since: 2.10
   */
  g_object_class_install_property (gobject_class,
                                   PROP_SECONDARY_TEXT,
                                   g_param_spec_string ("secondary-text",
                                                        P_("Secondary Text"),
                                                        P_("The secondary text of the message dialog"),
                                                        NULL,
                                                        GTK_PARAM_READWRITE));

  /**
   * GtkMessageDialog:secondary-use-markup:
   * 
   * %TRUE if the secondary text of the dialog includes Pango markup. 
   * See pango_parse_markup(). 
   *
   * Since: 2.10
   */
  g_object_class_install_property (gobject_class,
				   PROP_SECONDARY_USE_MARKUP,
				   g_param_spec_boolean ("secondary-use-markup",
							 P_("Use Markup in secondary"),
							 P_("The secondary text includes Pango markup."),
							 FALSE,
264
							 GTK_PARAM_READWRITE|G_PARAM_EXPLICIT_NOTIFY));
265

Matthias Clasen's avatar
Matthias Clasen committed
266 267
  /**
   * GtkMessageDialog:image:
268
   *
Matthias Clasen's avatar
Matthias Clasen committed
269 270 271
   * The image for this dialog.
   *
   * Since: 2.10
272
   * Deprecated: 3.12: Use #GtkDialog to create dialogs with images
Matthias Clasen's avatar
Matthias Clasen committed
273 274 275 276 277 278 279
   */
  g_object_class_install_property (gobject_class,
                                   PROP_IMAGE,
                                   g_param_spec_object ("image",
                                                        P_("Image"),
                                                        P_("The image"),
                                                        GTK_TYPE_WIDGET,
280
                                                        GTK_PARAM_READWRITE|G_PARAM_DEPRECATED));
Matthias Clasen's avatar
Matthias Clasen committed
281

282
  /**
283
   * GtkMessageDialog:message-area:
284
   *
285
   * The #GtkBox that corresponds to the message area of this dialog.  See
286 287 288 289 290 291 292 293 294 295 296 297 298
   * gtk_message_dialog_get_message_area() for a detailed description of this
   * area.
   *
   * Since: 2.22
   */
  g_object_class_install_property (gobject_class,
				   PROP_MESSAGE_AREA,
				   g_param_spec_object ("message-area",
							P_("Message area"),
							P_("GtkVBox that holds the dialog's primary and secondary labels"),
							GTK_TYPE_WIDGET,
							GTK_PARAM_READABLE));

299
  /* Setup Composite data */
300
  gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkmessagedialog.ui");
301 302 303
  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);
304 305 306 307 308
}

static void
gtk_message_dialog_init (GtkMessageDialog *dialog)
{
309
  GtkMessageDialogPrivate *priv;
310
  GtkWidget *action_area;
311

312
  dialog->priv = gtk_message_dialog_get_instance_private (dialog);
313
  priv = dialog->priv;
314

315 316
  priv->has_primary_markup = FALSE;
  priv->has_secondary_text = FALSE;
317 318
  priv->has_primary_markup = FALSE;
  priv->has_secondary_text = FALSE;
319
  priv->message_type = GTK_MESSAGE_OTHER;
320

321
  gtk_widget_init_template (GTK_WIDGET (dialog));
322
  gtk_message_dialog_style_updated (GTK_WIDGET (dialog));
323 324 325 326
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
  action_area = gtk_dialog_get_action_area (GTK_DIALOG (dialog));
G_GNUC_END_IGNORE_DEPRECATIONS
  gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area), GTK_BUTTONBOX_EXPAND);
327 328
}

329 330 331
static void
setup_primary_label_font (GtkMessageDialog *dialog)
{
332
  GtkMessageDialogPrivate *priv = dialog->priv;
333

334
  if (!priv->has_primary_markup)
335
    {
336 337 338 339 340 341 342 343
      PangoAttrList *attributes;
      PangoAttribute *attr;

      attributes = pango_attr_list_new ();

      attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
      pango_attr_list_insert (attributes, attr);

344 345 346 347 348
      if (priv->has_secondary_text)
        {
          attr = pango_attr_scale_new (PANGO_SCALE_LARGE);
          pango_attr_list_insert (attributes, attr);
        }
349 350 351 352 353 354 355 356

      gtk_label_set_attributes (GTK_LABEL (priv->label), attributes);
      pango_attr_list_unref (attributes);
    }
  else
    {
      /* unset the font settings */
      gtk_label_set_attributes (GTK_LABEL (priv->label), NULL);
357 358 359
    }
}

360 361 362 363
static void
setup_type (GtkMessageDialog *dialog,
	    GtkMessageType    type)
{
364
  GtkMessageDialogPrivate *priv = dialog->priv;
365
  const gchar *name = NULL;
Matthias Clasen's avatar
Matthias Clasen committed
366
  AtkObject *atk_obj;
367

368 369 370
  if (priv->message_type == type)
    return;

Matthias Clasen's avatar
Matthias Clasen committed
371 372
  priv->message_type = type;

373 374 375
  switch (type)
    {
    case GTK_MESSAGE_INFO:
376
      name = _("Information");
377 378 379
      break;

    case GTK_MESSAGE_QUESTION:
380
      name = _("Question");
381 382 383
      break;

    case GTK_MESSAGE_WARNING:
384
      name = _("Warning");
385
      break;
386

387
    case GTK_MESSAGE_ERROR:
388
      name = _("Error");
389
      break;
390

Matthias Clasen's avatar
Matthias Clasen committed
391 392 393
    case GTK_MESSAGE_OTHER:
      break;

394
    default:
395
      g_warning ("Unknown GtkMessageType %u", type);
396 397 398
      break;
    }

Matthias Clasen's avatar
Matthias Clasen committed
399 400 401 402
  atk_obj = gtk_widget_get_accessible (GTK_WIDGET (dialog));
  if (GTK_IS_ACCESSIBLE (atk_obj))
    {
      atk_object_set_role (atk_obj, ATK_ROLE_ALERT);
403 404
      if (name)
        atk_object_set_name (atk_obj, name);
Matthias Clasen's avatar
Matthias Clasen committed
405
    }
406 407

  g_object_notify (G_OBJECT (dialog), "message-type");
408 409
}

410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
static void
update_title (GObject    *dialog,
              GParamSpec *pspec,
              GtkWidget  *label)
{
  const gchar *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_set_margin_top (label, 6);
      gtk_widget_set_margin_bottom (label, 6);
      gtk_style_context_add_class (gtk_widget_get_style_context (label), "title");
      gtk_box_set_center_widget (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);
    }
}

453 454 455 456 457 458
static void 
gtk_message_dialog_set_property (GObject      *object,
				 guint         prop_id,
				 const GValue *value,
				 GParamSpec   *pspec)
{
459
  GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG (object);
460
  GtkMessageDialogPrivate *priv = dialog->priv;
461

462 463 464 465 466 467 468 469
  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;
470 471
    case PROP_TEXT:
      if (priv->has_primary_markup)
472
	gtk_label_set_markup (GTK_LABEL (priv->label),
473 474
			      g_value_get_string (value));
      else
475
	gtk_label_set_text (GTK_LABEL (priv->label),
476 477 478
			    g_value_get_string (value));
      break;
    case PROP_USE_MARKUP:
479 480 481 482 483 484 485
      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);
        }
        setup_primary_label_font (dialog);
486 487 488 489
      break;
    case PROP_SECONDARY_TEXT:
      {
	const gchar *txt = g_value_get_string (value);
490

491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
	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_show (priv->secondary_label);
	  }
	else
	  {
	    priv->has_secondary_text = FALSE;
	    gtk_widget_hide (priv->secondary_label);
	  }
	setup_primary_label_font (dialog);
      }
      break;
    case PROP_SECONDARY_USE_MARKUP:
510 511 512 513 514
      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);
        }
515
      break;
Matthias Clasen's avatar
Matthias Clasen committed
516
    case PROP_IMAGE:
517
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
518
      gtk_message_dialog_set_image (dialog, g_value_get_object (value));
519
G_GNUC_END_IGNORE_DEPRECATIONS
Matthias Clasen's avatar
Matthias Clasen committed
520 521
      break;

522 523 524 525 526 527 528 529 530 531 532 533
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void 
gtk_message_dialog_get_property (GObject     *object,
				 guint        prop_id,
				 GValue      *value,
				 GParamSpec  *pspec)
{
534
  GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG (object);
535
  GtkMessageDialogPrivate *priv = dialog->priv;
536

537 538 539
  switch (prop_id)
    {
    case PROP_MESSAGE_TYPE:
Matthias Clasen's avatar
Matthias Clasen committed
540
      g_value_set_enum (value, (GtkMessageType) priv->message_type);
541
      break;
542
    case PROP_TEXT:
543
      g_value_set_string (value, gtk_label_get_label (GTK_LABEL (priv->label)));
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561
      break;
    case PROP_USE_MARKUP:
      g_value_set_boolean (value, priv->has_primary_markup);
      break;
    case PROP_SECONDARY_TEXT:
      if (priv->has_secondary_text)
      g_value_set_string (value, 
			  gtk_label_get_label (GTK_LABEL (priv->secondary_label)));
      else
	g_value_set_string (value, NULL);
      break;
    case PROP_SECONDARY_USE_MARKUP:
      if (priv->has_secondary_text)
	g_value_set_boolean (value, 
			     gtk_label_get_use_markup (GTK_LABEL (priv->secondary_label)));
      else
	g_value_set_boolean (value, FALSE);
      break;
Matthias Clasen's avatar
Matthias Clasen committed
562
    case PROP_IMAGE:
563
      g_value_set_object (value, priv->image);
Matthias Clasen's avatar
Matthias Clasen committed
564
      break;
565 566 567
    case PROP_MESSAGE_AREA:
      g_value_set_object (value, priv->message_area);
      break;
568 569 570 571 572 573
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

574 575
/**
 * gtk_message_dialog_new:
576
 * @parent: (allow-none): transient parent, or %NULL for none
577 578 579
 * @flags: flags
 * @type: type of message
 * @buttons: set of buttons to use
580
 * @message_format: (allow-none): printf()-style format string, or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
581
 * @...: arguments for @message_format
582
 *
583 584
 * Creates a new message dialog, which is a simple dialog with some text
 * the user may want to see. When the user clicks a button a “response”
585 586
 * signal is emitted with response IDs from #GtkResponseType. See
 * #GtkDialog for more details.
587
 *
588
 * Returns: (transfer none): a new #GtkMessageDialog
Matthias Clasen's avatar
Matthias Clasen committed
589
 */
590 591 592 593 594 595 596 597 598 599
GtkWidget*
gtk_message_dialog_new (GtkWindow     *parent,
                        GtkDialogFlags flags,
                        GtkMessageType type,
                        GtkButtonsType buttons,
                        const gchar   *message_format,
                        ...)
{
  GtkWidget *widget;
  GtkDialog *dialog;
600
  gchar* msg = NULL;
601
  va_list args;
602 603 604

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

Manish Singh's avatar
Manish Singh committed
605
  widget = g_object_new (GTK_TYPE_MESSAGE_DIALOG,
606
                         "use-header-bar", FALSE,
607
			 "message-type", type,
Manish Singh's avatar
Manish Singh committed
608 609
			 "buttons", buttons,
			 NULL);
610 611 612 613 614
  dialog = GTK_DIALOG (widget);

  if (message_format)
    {
      va_start (args, message_format);
Manish Singh's avatar
Manish Singh committed
615
      msg = g_strdup_vprintf (message_format, args);
616
      va_end (args);
617

618
      gtk_label_set_text (GTK_LABEL (GTK_MESSAGE_DIALOG (widget)->priv->label), msg);
619

620 621 622 623
      g_free (msg);
    }

  if (parent != NULL)
624
    gtk_window_set_transient_for (GTK_WINDOW (widget), GTK_WINDOW (parent));
625 626 627 628 629 630
  
  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);
631

632 633 634
  return widget;
}

635 636
/**
 * gtk_message_dialog_new_with_markup:
Matthias Clasen's avatar
Matthias Clasen committed
637
 * @parent: (allow-none): transient parent, or %NULL for none
638 639 640
 * @flags: flags
 * @type: type of message
 * @buttons: set of buttons to use
641
 * @message_format: (allow-none): printf()-style format string, or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
642 643
 * @...: arguments for @message_format
 *
644
 * Creates a new message dialog, which is a simple dialog with some text that
645
 * is marked up with the [Pango text markup language][PangoMarkupFormat].
646
 * When the user clicks a button a “response” signal is emitted with
647 648
 * response IDs from #GtkResponseType. See #GtkDialog for more details.
 *
649 650 651 652 653 654
 * 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
 * label, then you need to use gtk_message_dialog_set_markup()
655
 * instead, since you can’t pass the markup string either
656
 * as the format (it might contain “%” characters) or as a string
657
 * argument.
658
 * |[<!-- language="C" -->
659
 *  GtkWidget *dialog;
660 661 662
 *  GtkDialogFlags flags = GTK_DIALOG_DESTROY_WITH_PARENT;
 *  dialog = gtk_message_dialog_new (parent_window,
 *                                   flags,
663
 *                                   GTK_MESSAGE_ERROR,
664
 *                                   GTK_BUTTONS_CLOSE,
665 666 667
 *                                   NULL);
 *  gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog),
 *                                 markup);
Matthias Clasen's avatar
Matthias Clasen committed
668
 * ]|
669
 * 
670
 * Returns: a new #GtkMessageDialog
671 672 673 674 675 676 677 678 679 680 681 682 683
 *
 * Since: 2.4
 **/
GtkWidget*
gtk_message_dialog_new_with_markup (GtkWindow     *parent,
                                    GtkDialogFlags flags,
                                    GtkMessageType type,
                                    GtkButtonsType buttons,
                                    const gchar   *message_format,
                                    ...)
{
  GtkWidget *widget;
  va_list args;
684
  gchar *msg = NULL;
685 686 687

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

688
  widget = gtk_message_dialog_new (parent, flags, type, buttons, NULL);
689 690 691 692

  if (message_format)
    {
      va_start (args, message_format);
693
      msg = g_markup_vprintf_escaped (message_format, args);
694 695
      va_end (args);

696
      gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (widget), msg);
697 698 699 700 701 702 703

      g_free (msg);
    }

  return widget;
}

Matthias Clasen's avatar
Matthias Clasen committed
704 705 706 707 708
/**
 * gtk_message_dialog_set_image:
 * @dialog: a #GtkMessageDialog
 * @image: the image
 * 
709
 * Sets the dialog’s image to @image.
Matthias Clasen's avatar
Matthias Clasen committed
710 711
 *
 * Since: 2.10
712
 * Deprecated: 3.12: Use #GtkDialog to create dialogs with images
Matthias Clasen's avatar
Matthias Clasen committed
713 714 715 716 717
 **/
void
gtk_message_dialog_set_image (GtkMessageDialog *dialog,
			      GtkWidget        *image)
{
718
  GtkMessageDialogPrivate *priv;
Matthias Clasen's avatar
Matthias Clasen committed
719 720 721
  GtkWidget *parent;

  g_return_if_fail (GTK_IS_MESSAGE_DIALOG (dialog));
722 723
  g_return_if_fail (image == NULL || GTK_IS_WIDGET (image));

724
  priv = dialog->priv;
725 726 727
  
  if (priv->image)
    gtk_widget_destroy (priv->image);
728

729 730 731 732 733 734 735 736 737
  priv->image = image;
 
  if (priv->image)
    { 
      gtk_widget_set_halign (priv->image, GTK_ALIGN_CENTER);
      gtk_widget_set_valign (priv->image, GTK_ALIGN_START);
      parent = gtk_widget_get_parent (priv->message_area);
      gtk_container_add (GTK_CONTAINER (parent), priv->image);
      gtk_box_reorder_child (GTK_BOX (parent), priv->image, 0);
738
    }
Matthias Clasen's avatar
Matthias Clasen committed
739 740

  priv->message_type = GTK_MESSAGE_OTHER;
741

Matthias Clasen's avatar
Matthias Clasen committed
742
  g_object_notify (G_OBJECT (dialog), "image");
743
  g_object_notify (G_OBJECT (dialog), "message-type");
Matthias Clasen's avatar
Matthias Clasen committed
744 745
}

746 747 748 749
/**
 * gtk_message_dialog_get_image:
 * @dialog: a #GtkMessageDialog
 *
750
 * Gets the dialog’s image.
Matthias Clasen's avatar
Matthias Clasen committed
751
 *
752
 * Returns: (transfer none): the dialog’s image
753 754
 *
 * Since: 2.14
755
 * Deprecated: 3.12: Use #GtkDialog for dialogs with images
756 757 758 759
 **/
GtkWidget *
gtk_message_dialog_get_image (GtkMessageDialog *dialog)
{
760
  g_return_val_if_fail (GTK_IS_MESSAGE_DIALOG (dialog), NULL);
761

762
  return dialog->priv->image;
763 764
}

765 766 767
/**
 * gtk_message_dialog_set_markup:
 * @message_dialog: a #GtkMessageDialog
768
 * @str: markup string (see [Pango markup format][PangoMarkupFormat])
769 770
 * 
 * Sets the text of the message dialog to be @str, which is marked
771
 * up with the [Pango text markup language][PangoMarkupFormat].
772 773 774 775 776
 *
 * Since: 2.4
 **/
void
gtk_message_dialog_set_markup (GtkMessageDialog *message_dialog,
777
                               const gchar      *str)
778
{
779
  GtkMessageDialogPrivate *priv;
780

781
  g_return_if_fail (GTK_IS_MESSAGE_DIALOG (message_dialog));
782

783 784
  priv = message_dialog->priv;

785
  priv->has_primary_markup = TRUE;
786
  gtk_label_set_markup (GTK_LABEL (priv->label), str);
787 788
}

789 790 791
/**
 * gtk_message_dialog_format_secondary_text:
 * @message_dialog: a #GtkMessageDialog
792
 * @message_format: (allow-none): printf()-style format string, or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
793 794 795
 * @...: arguments for @message_format
 *
 * Sets the secondary text of the message dialog to be @message_format
796 797 798
 * (with printf()-style).
 *
 * Since: 2.6
Matthias Clasen's avatar
Matthias Clasen committed
799
 */
800 801 802 803 804 805 806
void
gtk_message_dialog_format_secondary_text (GtkMessageDialog *message_dialog,
                                          const gchar      *message_format,
                                          ...)
{
  va_list args;
  gchar *msg = NULL;
807
  GtkMessageDialogPrivate *priv;
808 809 810

  g_return_if_fail (GTK_IS_MESSAGE_DIALOG (message_dialog));

811
  priv = message_dialog->priv;
812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837

  if (message_format)
    {
      priv->has_secondary_text = TRUE;

      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;
      gtk_widget_hide (priv->secondary_label);
    }

  setup_primary_label_font (message_dialog);
}

/**
 * gtk_message_dialog_format_secondary_markup:
 * @message_dialog: a #GtkMessageDialog
Matthias Clasen's avatar
Matthias Clasen committed
838
 * @message_format: printf()-style markup string (see
839
     [Pango markup format][PangoMarkupFormat]), or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
840 841 842 843
 * @...: arguments for @message_format
 *
 * Sets the secondary text of the message dialog to be @message_format (with
 * printf()-style), which is marked up with the
844
 * [Pango text markup language][PangoMarkupFormat].
845
 *
846
 * Due to an oversight, this function does not escape special XML characters
Matthias Clasen's avatar
Matthias Clasen committed
847
 * like gtk_message_dialog_new_with_markup() does. Thus, if the arguments
848 849
 * may contain special XML characters, you should use g_markup_printf_escaped()
 * to escape it.
Matthias Clasen's avatar
Matthias Clasen committed
850

851
 * |[<!-- language="C" -->
852
 * gchar *msg;
Matthias Clasen's avatar
Matthias Clasen committed
853
 *
854
 * msg = g_markup_printf_escaped (message_format, ...);
855 856
 * gtk_message_dialog_format_secondary_markup (message_dialog,
 *                                             "%s", msg);
857
 * g_free (msg);
858
 * ]|
859
 *
860
 * Since: 2.6
Matthias Clasen's avatar
Matthias Clasen committed
861
 */
862 863 864 865 866 867 868
void
gtk_message_dialog_format_secondary_markup (GtkMessageDialog *message_dialog,
                                            const gchar      *message_format,
                                            ...)
{
  va_list args;
  gchar *msg = NULL;
869
  GtkMessageDialogPrivate *priv;
870 871 872

  g_return_if_fail (GTK_IS_MESSAGE_DIALOG (message_dialog));

873
  priv = message_dialog->priv;
874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896

  if (message_format)
    {
      priv->has_secondary_text = TRUE;

      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;
      gtk_widget_hide (priv->secondary_label);
    }

  setup_primary_label_font (message_dialog);
}

897 898 899 900
/**
 * gtk_message_dialog_get_message_area:
 * @message_dialog: a #GtkMessageDialog
 *
901
 * Returns the message area of the dialog. This is the box where the
902
 * dialog’s primary and secondary labels are packed. You can add your
903 904
 * own extra content to that box and it will appear below those labels.
 * See gtk_dialog_get_content_area() for the corresponding
905 906
 * function in the parent #GtkDialog.
 *
907
 * Returns: (transfer none): A #GtkVBox corresponding to the
908
 *     “message area” in the @message_dialog.
909 910 911 912 913 914 915 916
 *
 * Since: 2.22
 **/
GtkWidget *
gtk_message_dialog_get_message_area (GtkMessageDialog *message_dialog)
{
  g_return_val_if_fail (GTK_IS_MESSAGE_DIALOG (message_dialog), NULL);

917
  return message_dialog->priv->message_area;
918 919
}

920 921 922 923 924 925
static void
gtk_message_dialog_add_buttons (GtkMessageDialog* message_dialog,
				GtkButtonsType buttons)
{
  GtkDialog* dialog = GTK_DIALOG (message_dialog);

926
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
927 928 929 930 931 932 933
  switch (buttons)
    {
    case GTK_BUTTONS_NONE:
      /* nothing */
      break;

    case GTK_BUTTONS_OK:
934
      gtk_dialog_add_button (dialog, _("_OK"), GTK_RESPONSE_OK);
935 936 937
      break;

    case GTK_BUTTONS_CLOSE:
938
      gtk_dialog_add_button (dialog, _("_Close"), GTK_RESPONSE_CLOSE);
939 940 941
      break;

    case GTK_BUTTONS_CANCEL:
942
      gtk_dialog_add_button (dialog, _("_Cancel"), GTK_RESPONSE_CANCEL);
943 944 945
      break;

    case GTK_BUTTONS_YES_NO:
946 947
      gtk_dialog_add_button (dialog, _("_No"), GTK_RESPONSE_NO);
      gtk_dialog_add_button (dialog, _("_Yes"), GTK_RESPONSE_YES);
948 949 950 951
      gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
					       GTK_RESPONSE_YES,
					       GTK_RESPONSE_NO,
					       -1);
952 953 954
      break;

    case GTK_BUTTONS_OK_CANCEL:
955 956
      gtk_dialog_add_button (dialog, _("_Cancel"), GTK_RESPONSE_CANCEL);
      gtk_dialog_add_button (dialog, _("_OK"), GTK_RESPONSE_OK);
957 958 959 960
      gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
					       GTK_RESPONSE_OK,
					       GTK_RESPONSE_CANCEL,
					       -1);
961 962 963 964 965
      break;
      
    default:
      g_warning ("Unknown GtkButtonsType");
      break;
966
    } 
967
G_GNUC_END_IGNORE_DEPRECATIONS
968 969

  g_object_notify (G_OBJECT (message_dialog), "buttons");
970
}
971 972

static void
973
gtk_message_dialog_style_updated (GtkWidget *widget)
974
{
975 976 977
  GtkMessageDialog *dialog = GTK_MESSAGE_DIALOG (widget);
  GtkWidget *parent;
  gint border_width;
978

979
  parent = gtk_widget_get_parent (dialog->priv->message_area);
980 981 982

  if (parent)
    {
983
      gtk_widget_style_get (widget, "message-border",
984
                            &border_width, NULL);
985

986
      gtk_container_set_border_width (GTK_CONTAINER (parent),
987
                                      MAX (0, border_width - 7));
988 989
    }

990
  GTK_WIDGET_CLASS (gtk_message_dialog_parent_class)->style_updated (widget);
991
}