gtkdialog.c 58.6 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
Elliot Lee's avatar
Elliot Lee committed
2 3 4
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 *
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
Elliot Lee's avatar
Elliot Lee committed
6 7 8 9 10 11
 * 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
12
 * Lesser General Public License for more details.
Elliot Lee's avatar
Elliot Lee committed
13
 *
14
 * You should have received a copy of the GNU Lesser General Public
Javier Jardón's avatar
Javier Jardón committed
15
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
Elliot Lee's avatar
Elliot Lee committed
16
 */
17 18

/*
19
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20 21
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
22
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
23 24
 */

25 26
#include "config.h"

27 28
#include <stdlib.h>
#include <string.h>
29

Elliot Lee's avatar
Elliot Lee committed
30 31
#include "gtkbutton.h"
#include "gtkdialog.h"
32
#include "gtkdialogprivate.h"
33
#include "gtkheaderbar.h"
Matthias Clasen's avatar
Matthias Clasen committed
34
#include "gtkbbox.h"
35
#include "gtklabel.h"
36
#include "gtkmarshalers.h"
37
#include "gtkbox.h"
38
#include "gtkboxprivate.h"
39
#include "gtkcontainerprivate.h"
40
#include "gtkmain.h"
41
#include "gtkintl.h"
42
#include "gtkbindings.h"
43
#include "gtkprivate.h"
44
#include "gtkbuildable.h"
45
#include "gtksettings.h"
46
#include "gtktypebuiltins.h"
47
#include "deprecated/gtkstock.h"
48
#include "gtksizegroup.h"
Elliot Lee's avatar
Elliot Lee committed
49

50 51 52 53 54 55 56 57
/**
 * SECTION:gtkdialog
 * @Short_description: Create popup windows
 * @Title: GtkDialog
 * @See_also: #GtkVBox, #GtkWindow, #GtkButton
 *
 * Dialog boxes are a convenient way to prompt the user for a small amount
 * of input, e.g. to display a message, ask a question, or anything else
58
 * that does not require extensive effort on the user’s part.
59 60 61 62
 *
 * GTK+ treats a dialog as a window split vertically. The top section is a
 * #GtkVBox, and is where widgets such as a #GtkLabel or a #GtkEntry should
 * be packed. The bottom area is known as the
63
 * “action area”. This is generally used for
64
 * packing buttons into the dialog which may perform functions such as
65
 * cancel, ok, or apply.
66 67 68
 *
 * #GtkDialog boxes are created with a call to gtk_dialog_new() or
 * gtk_dialog_new_with_buttons(). gtk_dialog_new_with_buttons() is
69 70
 * recommended; it allows you to set the dialog title, some convenient
 * flags, and add simple buttons.
71
 *
72
 * If “dialog” is a newly created dialog, the two primary areas of the
73 74 75
 * window can be accessed through gtk_dialog_get_content_area() and
 * gtk_dialog_get_action_area(), as can be seen from the example below.
 *
76
 * A “modal” dialog (that is, one which freezes the rest of the application
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
 * from user input), can be created by calling gtk_window_set_modal() on the
 * dialog. Use the GTK_WINDOW() macro to cast the widget returned from
 * gtk_dialog_new() into a #GtkWindow. When using gtk_dialog_new_with_buttons()
 * you can also pass the #GTK_DIALOG_MODAL flag to make a dialog modal.
 *
 * If you add buttons to #GtkDialog using gtk_dialog_new_with_buttons(),
 * gtk_dialog_add_button(), gtk_dialog_add_buttons(), or
 * gtk_dialog_add_action_widget(), clicking the button will emit a signal
 * called #GtkDialog::response with a response ID that you specified. GTK+
 * will never assign a meaning to positive response IDs; these are entirely
 * user-defined. But for convenience, you can use the response IDs in the
 * #GtkResponseType enumeration (these all have values less than zero). If
 * a dialog receives a delete event, the #GtkDialog::response signal will
 * be emitted with a response ID of #GTK_RESPONSE_DELETE_EVENT.
 *
 * If you want to block waiting for a dialog to return before returning
 * control flow to your code, you can call gtk_dialog_run(). This function
 * enters a recursive main loop and waits for the user to respond to the
 * dialog, returning the response ID corresponding to the button the user
 * clicked.
 *
98 99
 * For the simple dialog in the following example, in reality you’d probably
 * use #GtkMessageDialog to save yourself some effort. But you’d need to
100 101
 * create the dialog contents manually if you had more than a simple message
 * in the dialog.
102 103
 *
 * An example for simple GtkDialog usage:
104
 * |[<!-- language="C" -->
105
 * // Function to open a dialog box with a message
106
 * void
107
 * quick_message (GtkWindow *parent, gchar *message)
108
 * {
109 110
 *  GtkWidget *dialog, *label, *content_area;
 *  GtkDialogFlags flags;
111
 *
112
 *  // Create the widgets
113 114 115 116 117 118 119 120 121
 *  flags = GTK_DIALOG_DESTROY_WITH_PARENT;
 *  dialog = gtk_dialog_new_with_buttons ("Message",
 *                                        parent,
 *                                        flags,
 *                                        _("_OK"),
 *                                        GTK_RESPONSE_NONE,
 *                                        NULL);
 *  content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
 *  label = gtk_label_new (message);
122
 *
123
 *  // Ensure that the dialog box is destroyed when the user responds
124
 *
125 126 127 128
 *  g_signal_connect_swapped (dialog,
 *                            "response",
 *                            G_CALLBACK (gtk_widget_destroy),
 *                            dialog);
129
 *
130
 *  // Add the label, and show everything we’ve added
131 132 133
 *
 *  gtk_container_add (GTK_CONTAINER (content_area), label);
 *  gtk_widget_show_all (dialog);
134
 * }
135
 * ]|
136
 *
137
 * # GtkDialog as GtkBuildable
138
 *
139
 * The GtkDialog implementation of the #GtkBuildable interface exposes the
140 141
 * @vbox and @action_area as internal children with the names “vbox” and
 * “action_area”.
142
 *
143 144 145
 * GtkDialog supports a custom <action-widgets> element, which can contain
 * multiple <action-widget> elements. The “response” attribute specifies a
 * numeric response, and the content of the element is the id of widget
146 147 148 149 150 151 152 153 154
 * (which should be a child of the dialogs @action_area). To mark a response
 * as default, set the “default“ attribute of the <action-widget> element
 * to true.
 *
 * GtkDialog supports adding action widgets by specifying “action“ as
 * the “type“ attribute of a <child> element. The widget will be added
 * either to the action area or the headerbar of the dialog, depending
 * on the “use-header-bar“ property. The response id has to be associated
 * with the action widget using the <action-widgets> element.
155 156
 *
 * An example of a #GtkDialog UI definition fragment:
157
 * |[
158
 * <object class="GtkDialog" id="dialog1">
159 160 161 162 163
 *   <child type="action">
 *     <object class="GtkButton" id="button_cancel"/>
 *   </child>
 *   <child type="action">
 *     <object class="GtkButton" id="button_ok"/>
164 165
 *   </child>
 *   <action-widgets>
166
 *     <action-widget response="cancel">button_cancel</action-widget>
167
 *     <action-widget response="ok" default="true">button_ok</action-widget>
168 169
 *   </action-widgets>
 * </object>
170
 * ]|
171
 */
Federico Mena Quintero's avatar
Federico Mena Quintero committed
172

173
struct _GtkDialogPrivate
174 175
{
  GtkWidget *vbox;
176
  GtkWidget *headerbar;
177
  GtkWidget *action_area;
178
  GtkWidget *action_box;
179
  GtkSizeGroup *size_group;
180

181
  gint use_header_bar;
182
  gboolean constructed;
183
};
Federico Mena Quintero's avatar
Federico Mena Quintero committed
184

185 186 187 188 189 190 191
typedef struct _ResponseData ResponseData;

struct _ResponseData
{
  gint response_id;
};

192 193 194 195 196 197 198
static void      gtk_dialog_add_buttons_valist   (GtkDialog    *dialog,
                                                  const gchar  *first_button_text,
                                                  va_list       args);

static gboolean  gtk_dialog_delete_event_handler (GtkWidget    *widget,
                                                  GdkEventAny  *event,
                                                  gpointer      user_data);
199
static void      gtk_dialog_style_updated        (GtkWidget    *widget);
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
static void      gtk_dialog_map                  (GtkWidget    *widget);

static void      gtk_dialog_close                (GtkDialog    *dialog);

static ResponseData * get_response_data          (GtkWidget    *widget,
                                                  gboolean      create);

static void      gtk_dialog_buildable_interface_init     (GtkBuildableIface *iface);
static gboolean  gtk_dialog_buildable_custom_tag_start   (GtkBuildable  *buildable,
                                                          GtkBuilder    *builder,
                                                          GObject       *child,
                                                          const gchar   *tagname,
                                                          GMarkupParser *parser,
                                                          gpointer      *data);
static void      gtk_dialog_buildable_custom_finished    (GtkBuildable  *buildable,
                                                          GtkBuilder    *builder,
                                                          GObject       *child,
                                                          const gchar   *tagname,
                                                          gpointer       user_data);
219 220 221 222
static void      gtk_dialog_buildable_add_child          (GtkBuildable  *buildable,
                                                          GtkBuilder    *builder,
                                                          GObject       *child,
                                                          const gchar   *type);
223

224

225 226
enum {
  PROP_0,
227
  PROP_USE_HEADER_BAR
228
};
229 230 231

enum {
  RESPONSE,
232
  CLOSE,
233 234
  LAST_SIGNAL
};
Elliot Lee's avatar
Elliot Lee committed
235

236
static guint dialog_signals[LAST_SIGNAL];
Elliot Lee's avatar
Elliot Lee committed
237

238
G_DEFINE_TYPE_WITH_CODE (GtkDialog, gtk_dialog, GTK_TYPE_WINDOW,
239
                         G_ADD_PRIVATE (GtkDialog)
240 241
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
						gtk_dialog_buildable_interface_init))
Elliot Lee's avatar
Elliot Lee committed
242

243 244
static void
set_use_header_bar (GtkDialog *dialog,
245
                    gint       use_header_bar)
246 247 248
{
  GtkDialogPrivate *priv = dialog->priv;

249 250 251
  if (use_header_bar == -1)
    return;

252
  priv->use_header_bar = use_header_bar;
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
}

/* A convenience helper for built-in dialogs */
void
gtk_dialog_set_use_header_bar_from_setting (GtkDialog *dialog)
{
  GtkDialogPrivate *priv = dialog->priv;

  g_assert (!priv->constructed);

  g_object_get (gtk_widget_get_settings (GTK_WIDGET (dialog)),
                "gtk-dialogs-use-header", &priv->use_header_bar,
                NULL);
}

268 269 270 271 272 273 274 275 276 277 278
static void
gtk_dialog_set_property (GObject      *object,
                         guint         prop_id,
                         const GValue *value,
                         GParamSpec   *pspec)
{
  GtkDialog *dialog = GTK_DIALOG (object);

  switch (prop_id)
    {
    case PROP_USE_HEADER_BAR:
279
      set_use_header_bar (dialog, g_value_get_int (value));
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
gtk_dialog_get_property (GObject      *object,
                         guint         prop_id,
                         GValue       *value,
                         GParamSpec   *pspec)
{
  GtkDialog *dialog = GTK_DIALOG (object);
  GtkDialogPrivate *priv = dialog->priv;

  switch (prop_id)
    {
    case PROP_USE_HEADER_BAR:
300
      g_value_set_int (value, priv->use_header_bar);
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
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
    }
}

static void
action_widget_activated (GtkWidget *widget, GtkDialog *dialog)
{
  gint response_id;

  response_id = gtk_dialog_get_response_for_widget (dialog, widget);

  gtk_dialog_response (dialog, response_id);
}

typedef struct {
  GtkWidget *child;
  gint       response_id;
} ActionWidgetData;

static void
add_response_data (GtkDialog *dialog,
                   GtkWidget *child,
                   gint       response_id)
{
  ResponseData *ad;
  guint signal_id;

  ad = get_response_data (child, TRUE);
  ad->response_id = response_id;

  if (GTK_IS_BUTTON (child))
    signal_id = g_signal_lookup ("clicked", GTK_TYPE_BUTTON);
  else
    signal_id = GTK_WIDGET_GET_CLASS (child)->activate_signal;

  if (signal_id)
    {
      GClosure *closure;

      closure = g_cclosure_new_object (G_CALLBACK (action_widget_activated),
                                       G_OBJECT (dialog));
      g_signal_connect_closure_by_id (child, signal_id, 0, closure, FALSE);
    }
  else
    g_warning ("Only 'activatable' widgets can be packed into the action area of a GtkDialog");
}

352 353 354 355 356 357 358 359
static void
apply_response_for_header_bar (GtkDialog *dialog,
                               GtkWidget *child,
                               gint       response_id)
{
  GtkDialogPrivate *priv = dialog->priv;
  GtkPackType pack;

360 361
  g_assert (gtk_widget_get_parent (child) == priv->headerbar);

362 363 364 365 366 367 368 369 370 371 372 373 374
  if (response_id == GTK_RESPONSE_CANCEL || response_id == GTK_RESPONSE_HELP)
    pack = GTK_PACK_START;
  else
    pack = GTK_PACK_END;

  gtk_container_child_set (GTK_CONTAINER (priv->headerbar), child,
                           "pack-type", pack,
                           NULL);

  if (response_id == GTK_RESPONSE_CANCEL || response_id == GTK_RESPONSE_CLOSE)
    gtk_header_bar_set_show_close_button (GTK_HEADER_BAR (priv->headerbar), FALSE);
}

375 376 377 378 379 380 381 382
static void
add_to_header_bar (GtkDialog *dialog,
                   GtkWidget *child,
                   gint       response_id)
{
  GtkDialogPrivate *priv = dialog->priv;

  gtk_widget_set_valign (child, GTK_ALIGN_CENTER);
383 384 385
  gtk_container_add (GTK_CONTAINER (priv->headerbar), child);
  gtk_size_group_add_widget (priv->size_group, child);
  apply_response_for_header_bar (dialog, child, response_id);
386

387
}
388

389 390 391 392 393 394
static void
apply_response_for_action_area (GtkDialog *dialog,
                                GtkWidget *child,
                                gint       response_id)
{
  GtkDialogPrivate *priv = dialog->priv;
395

396 397
  g_assert (gtk_widget_get_parent (child) == priv->action_area);

398 399
  if (response_id == GTK_RESPONSE_HELP)
    gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (priv->action_area), child, TRUE);
400 401 402 403 404 405 406 407 408 409 410
}

static void
add_to_action_area (GtkDialog *dialog,
                    GtkWidget *child,
                    gint       response_id)
{
  GtkDialogPrivate *priv = dialog->priv;

  gtk_widget_set_valign (child, GTK_ALIGN_BASELINE);
  gtk_container_add (GTK_CONTAINER (priv->action_area), child);
411
  apply_response_for_action_area (dialog, child, response_id);
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
static void
update_suggested_action (GtkDialog *dialog)
{
  GtkDialogPrivate *priv = dialog->priv;

  if (priv->use_header_bar)
    {
      GList *children, *l;

      children = gtk_container_get_children (GTK_CONTAINER (priv->headerbar));
      for (l = children; l != NULL; l = l->next)
        {
          GtkWidget *child = l->data;
	  GtkStyleContext *context = gtk_widget_get_style_context (child);

          if (gtk_style_context_has_class (context, GTK_STYLE_CLASS_DEFAULT))
            gtk_style_context_add_class (context, GTK_STYLE_CLASS_SUGGESTED_ACTION);
          else
            gtk_style_context_remove_class (context, GTK_STYLE_CLASS_SUGGESTED_ACTION);
        }
      g_list_free (children);
    }
}

438
static void
439 440 441
add_cb (GtkContainer *container,
        GtkWidget    *widget,
        GtkDialog    *dialog)
442 443
{
  GtkDialogPrivate *priv = dialog->priv;
444 445 446 447

  if (priv->use_header_bar)
    g_warning ("Content added to the action area of a dialog using header bars");

448 449
  gtk_widget_set_visible (priv->action_box, TRUE);
  gtk_widget_set_no_show_all (priv->action_box, FALSE);
450 451 452 453 454 455 456 457 458 459 460 461 462
}

static void
gtk_dialog_constructed (GObject *object)
{
  GtkDialog *dialog = GTK_DIALOG (object);
  GtkDialogPrivate *priv = dialog->priv;

  G_OBJECT_CLASS (gtk_dialog_parent_class)->constructed (object);

  priv->constructed = TRUE;
  if (priv->use_header_bar == -1)
    priv->use_header_bar = FALSE;
463 464 465

  if (priv->use_header_bar)
    {
466 467
      GList *children, *l;

468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
      children = gtk_container_get_children (GTK_CONTAINER (priv->action_area));
      for (l = children; l != NULL; l = l->next)
        {
          GtkWidget *child = l->data;
          gboolean has_default;
          ResponseData *rd;
          gint response_id;

          has_default = gtk_widget_has_default (child);
          rd = get_response_data (child, FALSE);
          response_id = rd ? rd->response_id : GTK_RESPONSE_NONE;

          g_object_ref (child);
          gtk_container_remove (GTK_CONTAINER (priv->action_area), child);
          add_to_header_bar (dialog, child, response_id);
          g_object_unref (child);

          if (has_default)
486
            gtk_widget_grab_default (child);
487 488
        }
      g_list_free (children);
489 490

      update_suggested_action (dialog);
491

492 493
      g_signal_connect (priv->action_area, "add", G_CALLBACK (add_cb), dialog);
    }
494 495 496 497 498
  else
    {
      gtk_window_set_titlebar (GTK_WINDOW (dialog), NULL);
      priv->headerbar = NULL;
    }
499

500
  gtk_widget_set_visible (priv->action_box, !priv->use_header_bar);
501
  gtk_widget_set_no_show_all (priv->action_box, priv->use_header_bar);
502 503
}

504 505 506 507 508 509 510 511 512 513
static void
gtk_dialog_finalize (GObject *obj)
{
  GtkDialog *dialog = GTK_DIALOG (obj);

  g_object_unref (dialog->priv->size_group);

  G_OBJECT_CLASS (gtk_dialog_parent_class)->finalize (obj);
}

Elliot Lee's avatar
Elliot Lee committed
514 515 516
static void
gtk_dialog_class_init (GtkDialogClass *class)
{
517
  GObjectClass *gobject_class;
518
  GtkWidgetClass *widget_class;
519
  GtkBindingSet *binding_set;
520

521
  gobject_class = G_OBJECT_CLASS (class);
522
  widget_class = GTK_WIDGET_CLASS (class);
523

524
  gobject_class->constructed  = gtk_dialog_constructed;
525 526
  gobject_class->set_property = gtk_dialog_set_property;
  gobject_class->get_property = gtk_dialog_get_property;
527
  gobject_class->finalize = gtk_dialog_finalize;
528

529
  widget_class->map = gtk_dialog_map;
530
  widget_class->style_updated = gtk_dialog_style_updated;
531

532 533
  gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_DIALOG);

534
  class->close = gtk_dialog_close;
Federico Mena Quintero's avatar
Federico Mena Quintero committed
535

536 537 538 539
  /**
   * GtkDialog::response:
   * @dialog: the object on which the signal is emitted
   * @response_id: the response ID
540 541 542 543
   *
   * Emitted when an action widget is clicked, the dialog receives a
   * delete event, or the application programmer calls gtk_dialog_response().
   * On a delete event, the response ID is #GTK_RESPONSE_DELETE_EVENT.
544 545
   * Otherwise, it depends on which action widget was clicked.
   */
546
  dialog_signals[RESPONSE] =
547
    g_signal_new (I_("response"),
Manish Singh's avatar
Manish Singh committed
548 549 550 551
		  G_OBJECT_CLASS_TYPE (class),
		  G_SIGNAL_RUN_LAST,
		  G_STRUCT_OFFSET (GtkDialogClass, response),
		  NULL, NULL,
552
		  _gtk_marshal_VOID__INT,
Manish Singh's avatar
Manish Singh committed
553 554
		  G_TYPE_NONE, 1,
		  G_TYPE_INT);
555

556 557 558
  /**
   * GtkDialog::close:
   *
559
   * The ::close signal is a
560
   * [keybinding signal][GtkBindingSignal]
561
   * which gets emitted when the user uses a keybinding to close
562 563 564
   * the dialog.
   *
   * The default binding for this signal is the Escape key.
565
   */
566
  dialog_signals[CLOSE] =
567
    g_signal_new (I_("close"),
Manish Singh's avatar
Manish Singh committed
568 569 570 571
		  G_OBJECT_CLASS_TYPE (class),
		  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GtkDialogClass, close),
		  NULL, NULL,
572
		  _gtk_marshal_VOID__VOID,
Manish Singh's avatar
Manish Singh committed
573
		  G_TYPE_NONE, 0);
574

575 576 577 578 579 580 581 582
  /**
   * GtkDialog:content-area-border:
   *
   * The default border width used around the
   * content area of the dialog, as returned by
   * gtk_dialog_get_content_area(), unless gtk_container_set_border_width()
   * was called on that widget directly.
   */
583
  gtk_widget_class_install_style_property (widget_class,
584
					   g_param_spec_int ("content-area-border",
585 586
                                                             P_("Content area border"),
                                                             P_("Width of border around the main dialog area"),
587 588 589
                                                             0,
                                                             G_MAXINT,
                                                             2,
590
                                                             GTK_PARAM_READABLE));
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
  /**
   * GtkDialog:content-area-spacing:
   *
   * The default spacing used between elements of the
   * content area of the dialog, as returned by
   * gtk_dialog_get_content_area(), unless gtk_box_set_spacing()
   * was called on that widget directly.
   *
   * Since: 2.16
   */
  gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_int ("content-area-spacing",
                                                             P_("Content area spacing"),
                                                             P_("Spacing between elements of the main dialog area"),
                                                             0,
                                                             G_MAXINT,
                                                             0,
                                                             GTK_PARAM_READABLE));
609
  gtk_widget_class_install_style_property (widget_class,
610
                                           g_param_spec_int ("button-spacing",
611 612
                                                             P_("Button spacing"),
                                                             P_("Spacing between buttons"),
613 614
                                                             0,
                                                             G_MAXINT,
615
                                                             6,
616
                                                             GTK_PARAM_READABLE));
617

618 619 620 621 622 623 624 625
  /**
   * GtkDialog:action-area-border:
   *
   * The default border width used around the
   * action area of the dialog, as returned by
   * gtk_dialog_get_action_area(), unless gtk_container_set_border_width()
   * was called on that widget directly.
   */
626
  gtk_widget_class_install_style_property (widget_class,
627
                                           g_param_spec_int ("action-area-border",
628 629
                                                             P_("Action area border"),
                                                             P_("Width of border around the button area at the bottom of the dialog"),
630 631
                                                             0,
                                                             G_MAXINT,
632
                                                             5,
633
                                                             GTK_PARAM_READABLE));
634

635 636 637 638 639 640
  /**
   * GtkDialog:use-header-bar:
   *
   * %TRUE if the dialog uses a #GtkHeaderBar for action buttons
   * instead of the action-area.
   *
641 642 643
   * For technical reasons, this property is declared as an integer
   * property, but you should only set it to %TRUE or %FALSE.
   *
644 645 646 647
   * Since: 3.12
   */
  g_object_class_install_property (gobject_class,
                                   PROP_USE_HEADER_BAR,
648 649 650 651 652
                                   g_param_spec_int ("use-header-bar",
                                                     P_("Use Header Bar"),
                                                     P_("Use Header Bar for actions."),
                                                     -1, 1, -1,
                                                     GTK_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
653

654
  binding_set = gtk_binding_set_by_class (class);
655
  gtk_binding_entry_add_signal (binding_set, GDK_KEY_Escape, 0, "close", 0);
656 657 658

  /* Bind class to template
   */
659
  gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/ui/gtkdialog.ui");
660
  gtk_widget_class_bind_template_child_internal_private (widget_class, GtkDialog, vbox);
661
  gtk_widget_class_bind_template_child_internal_private (widget_class, GtkDialog, headerbar);
662
  gtk_widget_class_bind_template_child_internal_private (widget_class, GtkDialog, action_area);
663
  gtk_widget_class_bind_template_child_internal_private (widget_class, GtkDialog, action_box);
664
  gtk_widget_class_bind_template_callback (widget_class, gtk_dialog_delete_event_handler);
665 666 667 668 669
}

static void
update_spacings (GtkDialog *dialog)
{
670
  GtkDialogPrivate *priv = dialog->priv;
671
  gint content_area_border;
672
  gint content_area_spacing;
673 674 675
  gint button_spacing;
  gint action_area_border;

676
  gtk_widget_style_get (GTK_WIDGET (dialog),
677
                        "content-area-border", &content_area_border,
678
                        "content-area-spacing", &content_area_spacing,
679 680
                        "button-spacing", &button_spacing,
                        "action-area-border", &action_area_border,
681
                        NULL);
682 683

  if (!_gtk_container_get_border_width_set (GTK_CONTAINER (priv->vbox)))
684 685 686 687 688
    {
      gtk_container_set_border_width (GTK_CONTAINER (priv->vbox),
                                      content_area_border);
      _gtk_container_set_border_width_set (GTK_CONTAINER (priv->vbox), FALSE);
    }
689

690
  if (!_gtk_box_get_spacing_set (GTK_BOX (priv->vbox)))
691
    {
692 693
      gtk_box_set_spacing (GTK_BOX (priv->vbox), content_area_spacing);
      _gtk_box_set_spacing_set (GTK_BOX (priv->vbox), FALSE);
694
    }
695

696
  gtk_box_set_spacing (GTK_BOX (priv->action_area),
697
                       button_spacing);
698 699

  if (!_gtk_container_get_border_width_set (GTK_CONTAINER (priv->action_area)))
700 701 702 703 704
    {
      gtk_container_set_border_width (GTK_CONTAINER (priv->action_area),
                                      action_area_border);
      _gtk_container_set_border_width_set (GTK_CONTAINER (priv->action_area), FALSE);
    }
Elliot Lee's avatar
Elliot Lee committed
705 706 707 708 709
}

static void
gtk_dialog_init (GtkDialog *dialog)
{
710
  dialog->priv = gtk_dialog_get_instance_private (dialog);
Elliot Lee's avatar
Elliot Lee committed
711

712
  dialog->priv->use_header_bar = -1;
713
  dialog->priv->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
714

715
  gtk_widget_init_template (GTK_WIDGET (dialog));
716 717

  update_spacings (dialog);
Elliot Lee's avatar
Elliot Lee committed
718 719
}

720 721
static GtkBuildableIface *parent_buildable_iface;

722 723 724
static void
gtk_dialog_buildable_interface_init (GtkBuildableIface *iface)
{
725
  parent_buildable_iface = g_type_interface_peek_parent (iface);
726 727
  iface->custom_tag_start = gtk_dialog_buildable_custom_tag_start;
  iface->custom_finished = gtk_dialog_buildable_custom_finished;
728
  iface->add_child = gtk_dialog_buildable_add_child;
729 730
}

731
static gboolean
732 733 734 735 736
gtk_dialog_delete_event_handler (GtkWidget   *widget,
                                 GdkEventAny *event,
                                 gpointer     user_data)
{
  /* emit response signal */
737
  gtk_dialog_response (GTK_DIALOG (widget), GTK_RESPONSE_DELETE_EVENT);
738 739 740 741 742

  /* Do the destroy by default */
  return FALSE;
}

743 744 745 746 747 748 749 750 751 752 753 754 755 756
static GList *
get_action_children (GtkDialog *dialog)
{
  GtkDialogPrivate *priv = dialog->priv;
  GList *children;

  if (priv->constructed && priv->use_header_bar)
    children = gtk_container_get_children (GTK_CONTAINER (priv->headerbar));
  else
    children = gtk_container_get_children (GTK_CONTAINER (priv->action_area));

  return children;
}

757 758 759 760 761
/* A far too tricky heuristic for getting the right initial
 * focus widget if none was set. What we do is we focus the first
 * widget in the tab chain, but if this results in the focus
 * ending up on one of the response widgets _other_ than the
 * default response, we focus the default response instead.
762 763 764
 *
 * Additionally, skip selectable labels when looking for the
 * right initial focus widget.
765 766 767 768
 */
static void
gtk_dialog_map (GtkWidget *widget)
{
769
  GtkWidget *default_widget, *focus;
770 771
  GtkWindow *window = GTK_WINDOW (widget);
  GtkDialog *dialog = GTK_DIALOG (widget);
772

773
  if (gtk_window_get_transient_for (window) == NULL)
774
    g_message ("GtkDialog mapped without a transient parent. This is discouraged.");
775

Matthias Clasen's avatar
Matthias Clasen committed
776
  GTK_WIDGET_CLASS (gtk_dialog_parent_class)->map (widget);
777

778 779
  focus = gtk_window_get_focus (window);
  if (!focus)
780 781
    {
      GList *children, *tmp_list;
782
      GtkWidget *first_focus = NULL;
783 784 785 786

      do
        {
          g_signal_emit_by_name (window, "move_focus", GTK_DIR_TAB_FORWARD);
787

788
          focus = gtk_window_get_focus (window);
789 790 791 792
          if (GTK_IS_LABEL (focus) &&
              !gtk_label_get_current_uri (GTK_LABEL (focus)))
            gtk_label_select_region (GTK_LABEL (focus), 0, 0);

793
          if (first_focus == NULL)
794
            first_focus = focus;
795
          else if (first_focus == focus)
796 797
            break;

798
          if (!GTK_IS_LABEL (focus))
799
            break;
800
        }
801
      while (TRUE);
802

803
      tmp_list = children = get_action_children (dialog);
804

805 806 807
      while (tmp_list)
	{
	  GtkWidget *child = tmp_list->data;
808 809 810 811 812

          default_widget = gtk_window_get_default_widget (window);
	  if ((focus == NULL || child == focus) &&
	      child != default_widget &&
	      default_widget)
813
	    {
814
	      gtk_widget_grab_focus (default_widget);
815 816
	      break;
	    }
817

818 819
	  tmp_list = tmp_list->next;
	}
820

821 822 823 824
      g_list_free (children);
    }
}

825
static void
826
gtk_dialog_style_updated (GtkWidget *widget)
827
{
828 829
  GTK_WIDGET_CLASS (gtk_dialog_parent_class)->style_updated (widget);

830 831 832
  update_spacings (GTK_DIALOG (widget));
}

833 834
static GtkWidget *
dialog_find_button (GtkDialog *dialog,
835
                   gint       response_id)
836
{
837
  GtkWidget *child = NULL;
838
  GList *children, *tmp_list;
839

840
  children = get_action_children (dialog);
841 842 843

  for (tmp_list = children; tmp_list; tmp_list = tmp_list->next)
    {
844
      ResponseData *rd = get_response_data (tmp_list->data, FALSE);
845

846
      if (rd && rd->response_id == response_id)
847 848 849 850
       {
         child = tmp_list->data;
         break;
       }
851 852 853 854
    }

  g_list_free (children);

855
  return child;
856 857
}

858 859
static void
gtk_dialog_close (GtkDialog *dialog)
860
{
861
  gtk_window_close (GTK_WINDOW (dialog));
862 863
}

864 865 866 867 868 869 870 871 872 873
/**
 * gtk_dialog_new:
 *
 * Creates a new dialog box.
 *
 * Widgets should not be packed into this #GtkWindow
 * directly, but into the @vbox and @action_area, as described above.
 *
 * Returns: the new dialog as a #GtkWidget
 */
Elliot Lee's avatar
Elliot Lee committed
874
GtkWidget*
875
gtk_dialog_new (void)
Elliot Lee's avatar
Elliot Lee committed
876
{
Manish Singh's avatar
Manish Singh committed
877
  return g_object_new (GTK_TYPE_DIALOG, NULL);
Elliot Lee's avatar
Elliot Lee committed
878
}
879 880 881 882 883 884 885 886

static GtkWidget*
gtk_dialog_new_empty (const gchar     *title,
                      GtkWindow       *parent,
                      GtkDialogFlags   flags)
{
  GtkDialog *dialog;

887 888 889
  dialog = g_object_new (GTK_TYPE_DIALOG,
                         "use-header-bar", (flags & GTK_DIALOG_USE_HEADER_BAR) != 0,
                         NULL);
890 891 892 893 894 895 896 897 898

  if (title)
    gtk_window_set_title (GTK_WINDOW (dialog), title);

  if (parent)
    gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);

  if (flags & GTK_DIALOG_MODAL)
    gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
899

900 901 902 903 904 905 906 907
  if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
    gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);

  return GTK_WIDGET (dialog);
}

/**
 * gtk_dialog_new_with_buttons:
908 909
 * @title: (allow-none): Title of the dialog, or %NULL
 * @parent: (allow-none): Transient parent of the dialog, or %NULL
910
 * @flags: from #GtkDialogFlags
911
 * @first_button_text: (allow-none): text to go in first button, or %NULL
Matthias Clasen's avatar
Matthias Clasen committed
912
 * @...: response ID for first button, then additional buttons, ending with %NULL
913
 *
914
 * Creates a new #GtkDialog with title @title (or %NULL for the default
915
 * title; see gtk_window_set_title()) and transient parent @parent (or
916 917
 * %NULL for none; see gtk_window_set_transient_for()). The @flags
 * argument can be used to make the dialog modal (#GTK_DIALOG_MODAL)
918
 * and/or to have it destroyed along with its transient parent
919 920
 * (#GTK_DIALOG_DESTROY_WITH_PARENT). After @flags, button
 * text/response ID pairs should be listed, with a %NULL pointer ending
921
 * the list. Button text can be arbitrary text. A response ID can be
922 923
 * any positive number, or one of the values in the #GtkResponseType
 * enumeration. If the user clicks one of these dialog buttons,
924
 * #GtkDialog will emit the #GtkDialog::response signal with the corresponding
925
 * response ID. If a #GtkDialog receives the #GtkWidget::delete-event signal,
926 927
 * it will emit ::response with a response ID of #GTK_RESPONSE_DELETE_EVENT.
 * However, destroying a dialog does not emit the ::response signal;
928
 * so be careful relying on ::response when using the
929
 * #GTK_DIALOG_DESTROY_WITH_PARENT flag. Buttons are from left to right,
930 931
 * so the first button in the list will be the leftmost button in the dialog.
 *
932
 * Here’s a simple example:
933
 * |[<!-- language="C" -->
934 935 936 937 938 939 940 941 942 943
 *  GtkWidget *dialog;
 *  GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
 *  dialog = gtk_dialog_new_with_buttons ("My dialog",
 *                                        main_app_window,
 *                                        flags,
 *                                        _("_OK"),
 *                                        GTK_RESPONSE_ACCEPT,
 *                                        _("_Cancel"),
 *                                        GTK_RESPONSE_REJECT,
 *                                        NULL);
Matthias Clasen's avatar
Matthias Clasen committed
944
 * ]|
945
 *
946
 * Returns: a new #GtkDialog
Matthias Clasen's avatar
Matthias Clasen committed
947
 */
948 949 950 951 952 953 954 955 956
GtkWidget*
gtk_dialog_new_with_buttons (const gchar    *title,
                             GtkWindow      *parent,
                             GtkDialogFlags  flags,
                             const gchar    *first_button_text,
                             ...)
{
  GtkDialog *dialog;
  va_list args;
957

958 959 960 961 962 963 964
  dialog = GTK_DIALOG (gtk_dialog_new_empty (title, parent, flags));

  va_start (args, first_button_text);

  gtk_dialog_add_buttons_valist (dialog,
                                 first_button_text,
                                 args);
965

966 967 968 969 970
  va_end (args);

  return GTK_WIDGET (dialog);
}

971
static void
972 973 974 975 976
response_data_free (gpointer data)
{
  g_slice_free (ResponseData, data);
}

977
static ResponseData *
978 979
get_response_data (GtkWidget *widget,
		   gboolean   create)
980
{
Manish Singh's avatar
Manish Singh committed
981 982
  ResponseData *ad = g_object_get_data (G_OBJECT (widget),
                                        "gtk-dialog-response-data");
983

984
  if (ad == NULL && create)
985
    {
986
      ad = g_slice_new (ResponseData);
987

Manish Singh's avatar
Manish Singh committed
988
      g_object_set_data_full (G_OBJECT (widget),
989
                              I_("gtk-dialog-response-data"),
Manish Singh's avatar
Manish Singh committed
990
                              ad,
991
			      response_data_free);
992 993 994 995 996 997 998 999 1000 1001
    }

  return ad;
}

/**
 * gtk_dialog_add_action_widget:
 * @dialog: a #GtkDialog
 * @child: an activatable widget
 * @response_id: response ID for @child
1002
 *
1003
 * Adds an activatable widget to the action area of a #GtkDialog,
1004 1005
 * connecting a signal handler that will emit the #GtkDialog::response
 * signal on the dialog when the widget is activated. The widget is
1006
 * appended to the end of the dialog’s action area. If you want to add a
1007
 * non-activatable widget, simply pack it into the @action_area field
1008
 * of the #GtkDialog struct.
1009 1010
 **/
void
Manish Singh's avatar
Manish Singh committed
1011 1012 1013
gtk_dialog_add_action_widget (GtkDialog *dialog,
                              GtkWidget *child,
                              gint       response_id)
1014
{
1015
  GtkDialogPrivate *priv = dialog->priv;
1016

1017 1018 1019
  g_return_if_fail (GTK_IS_DIALOG (dialog));
  g_return_if_fail (GTK_IS_WIDGET (child));

1020
  add_response_data (dialog, child, response_id);
1021

1022
  if (priv->constructed && priv->use_header_bar)
1023 1024 1025 1026 1027 1028 1029 1030 1031
    {
      add_to_header_bar (dialog, child, response_id);

      if (gtk_widget_has_default (child))
        {
          gtk_widget_grab_default (child);
          update_suggested_action (dialog);
        }
    }
1032
  else
1033
    add_to_action_area (dialog, child, response_id);
1034 1035 1036 1037 1038
}

/**
 * gtk_dialog_add_button:
 * @dialog: a #GtkDialog
1039
 * @button_text: text of button
1040
 * @response_id: response ID for the button
1041
 *
1042 1043 1044
 * Adds a button with the given text and sets things up so that
 * clicking the button will emit the #GtkDialog::response signal with
 * the given @response_id. The button is appended to the end of the
1045
 * dialog’s action area. The button widget is returned, but usually
1046
 * you don’t need it.
1047
 *
1048
 * Returns: (transfer none): the #GtkButton widget that was added
1049
 **/
1050
GtkWidget*
1051 1052 1053 1054 1055
gtk_dialog_add_button (GtkDialog   *dialog,
                       const gchar *button_text,
                       gint         response_id)
{
  GtkWidget *button;
1056

1057 1058
  g_return_val_if_fail (GTK_IS_DIALOG (dialog), NULL);
  g_return_val_if_fail (button_text != NULL, NULL);
1059

1060 1061 1062
  button = gtk_button_new_with_label (button_text);
  gtk_button_set_use_underline (GTK_BUTTON (button), TRUE);

1063 1064
  G_GNUC_BEGIN_IGNORE_DEPRECATIONS;

1065 1066 1067 1068 1069 1070
  if (button_text)
    {
      GtkStockItem item;
      if (gtk_stock_lookup (button_text, &item))
        g_object_set (button, "use-stock", TRUE, NULL);
    }
1071

1072 1073
  G_GNUC_END_IGNORE_DEPRECATIONS;

1074
  gtk_style_context_add_class (gtk_widget_get_style_context (button), "text-button");
1075
  gtk_widget_set_can_default (button, TRUE);
1076

1077
  gtk_widget_show (button);
1078

1079
  gtk_dialog_add_action_widget (dialog, button, response_id);
1080 1081

  return button;
1082 1083 1084
}

static void
Chema Celorio's avatar
Chema Celorio committed
1085 1086 1087
gtk_dialog_add_buttons_valist (GtkDialog      *dialog,
                               const gchar    *first_button_text,
                               va_list         args)
1088 1089 1090 1091
{
  const gchar* text;
  gint response_id;

1092
  g_return_if_fail (GTK_IS_DIALOG (dialog));
1093

1094 1095
  if (first_button_text == NULL)
    return;
1096

1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113
  text = first_button_text;
  response_id = va_arg (args, gint);

  while (text != NULL)
    {
      gtk_dialog_add_button (dialog, text, response_id);

      text = va_arg (args, gchar*);
      if (text == NULL)
        break;
      response_id = va_arg (args, int);
    }
}

/**
 * gtk_dialog_add_buttons:
 * @dialog: a #GtkDialog
1114
 * @first_button_text: button text
Matthias Clasen's avatar
Matthias Clasen committed
1115
 * @...: response ID for first button, then more text-response_id pairs
1116
 *
1117
 * Adds more buttons, same as calling gtk_dialog_add_button()
1118
 * repeatedly.  The variable argument list should be %NULL-terminated
1119 1120
 * as with gtk_dialog_new_with_buttons(). Each button must have both
 * text and response ID.
Matthias Clasen's avatar
Matthias Clasen committed
1121
 */
1122 1123 1124 1125
void
gtk_dialog_add_buttons (GtkDialog   *dialog,
                        const gchar *first_button_text,
                        ...)
1126
{
1127 1128 1129 1130 1131 1132 1133
  va_list args;

  va_start (args, first_button_text);

  gtk_dialog_add_buttons_valist (dialog,
                                 first_button_text,
                                 args);
1134

1135 1136 1137
  va_end (args);
}

1138 1139 1140 1141 1142 1143
/**
 * gtk_dialog_set_response_sensitive:
 * @dialog: a #GtkDialog
 * @response_id: a response ID
 * @setting: %TRUE for sensitive
 *
1144
 * Calls `gtk_widget_set_sensitive (widget, @setting)`
1145
 * for each widget in the dialog’s action area with the given @response_id.
1146 1147 1148 1149 1150 1151 1152 1153 1154 1155
 * A convenient way to sensitize/desensitize dialog buttons.
 **/
void
gtk_dialog_set_response_sensitive (GtkDialog *dialog,
                                   gint       response_id,
                                   gboolean   setting)
{
  GList *children;
  GList *tmp_list;

Darin Adler's avatar
Darin Adler committed
1156 1157
  g_return_if_fail (GTK_IS_DIALOG (dialog));

1158
  children = get_action_children (dialog);
1159 1160 1161 1162 1163

  tmp_list = children;
  while (tmp_list != NULL)
    {
      GtkWidget *widget = tmp_list->data;
1164
      ResponseData *rd = get_response_data (widget, FALSE);
1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178

      if (rd && rd->response_id == response_id)
        gtk_widget_set_sensitive (widget, setting);

      tmp_list = g_list_next (tmp_list);
    }

  g_list_free (children);
}

/**
 * gtk_dialog_set_default_response:
 * @dialog: a #GtkDialog
 * @response_id: a response ID
1179
 *
1180
 * Sets the last widget in the dialog’s action area with the given @response_id
1181
 * as the default widget for the dialog. Pressing “Enter” normally activates
1182 1183 1184 1185 1186 1187 1188 1189 1190
 * the default widget.
 **/
void
gtk_dialog_set_default_response (GtkDialog *dialog,
                                 gint       response_id)
{
  GList *children;
  GList *tmp_list;

Darin Adler's avatar
Darin Adler committed
1191 1192
  g_return_if_fail (GTK_IS_DIALOG (dialog));

1193
  children = get_action_children (dialog);
1194 1195 1196 1197 1198

  tmp_list = children;
  while (tmp_list != NULL)
    {
      GtkWidget *widget = tmp_list->data;
1199
      ResponseData *rd = get_response_data (widget, FALSE);
1200 1201

      if (rd && rd->response_id == response_id)
1202
	gtk_widget_grab_default (widget);
1203

1204 1205 1206 1207
      tmp_list = g_list_next (tmp_list);
    }

  g_list_free (children);
1208 1209 1210

  if (dialog->priv->use_header_bar)
    update_suggested_action (dialog);
1211 1212
}

1213 1214 1215
/**
 * gtk_dialog_response:
 * @dialog: a #GtkDialog
1216 1217 1218
 * @response_id: response ID
 *
 * Emits the #GtkDialog::response signal with the given response ID.
1219
 * Used to indicate that the user has responded to the dialog in some way;
1220
 * typically either you or gtk_dialog_run() will be monitoring the
1221
 * ::response signal and take appropriate action.
1222 1223 1224 1225 1226 1227 1228
 **/
void
gtk_dialog_response (GtkDialog *dialog,
                     gint       response_id)
{
  g_return_if_fail (GTK_IS_DIALOG (dialog));

Manish Singh's avatar
Manish Singh committed
1229 1230 1231 1232
  g_signal_emit (dialog,
		 dialog_signals[RESPONSE],
		 0,
		 response_id);
1233 1234 1235 1236 1237 1238 1239
}

typedef struct
{
  GtkDialog *dialog;
  gint response_id;
  GMainLoop *loop;
1240
  gboolean destroyed;
1241 1242 1243 1244 1245
} RunInfo;

static void
shutdown_loop (RunInfo *ri)
{
1246 1247
  if (g_main_loop_is_running (ri->loop))
    g_main_loop_quit (ri->loop);
1248 1249 1250
}

static void
1251
run_unmap_handler (GtkDialog *dialog, gpointer data)
1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277
{
  RunInfo *ri = data;

  shutdown_loop (ri);
}

static void
run_response_handler (GtkDialog *dialog,
                      gint response_id,
                      gpointer data)
{
  RunInfo *ri;

  ri = data;

  ri->response_id = response_id;

  shutdown_loop (ri);
}

static gint
run_delete_handler (GtkDialog *dialog,
                    GdkEventAny *event,
                    gpointer data)
{
  RunInfo *ri = data;
1278

1279
  shutdown_loop (ri);
1280

1281 1282 1283
  return TRUE; /* Do not destroy */
}

1284 1285 1286 1287 1288 1289
static void
run_destroy_handler (GtkDialog *dialog, gpointer data)
{
  RunInfo *ri = data;

  /* shutdown_loop will be called by run_unmap_handler */
1290

1291 1292 1293
  ri->destroyed = TRUE;
}

1294 1295 1296
/**
 * gtk_dialog_run:
 * @dialog: a #GtkDialog
1297
 *
1298
 * Blocks in a recursive main loop until the @dialog either emits the
1299 1300 1301
 * #GtkDialog::response signal, or is destroyed. If the dialog is
 * destroyed during the call to gtk_dialog_run(), gtk_dialog_run() returns
 * #GTK_RESPONSE_NONE. Otherwise, it returns the response ID from the
1302 1303
 * ::response signal emission.
 *
Soeren Sandmann's avatar
Soeren Sandmann committed
1304
 * Before entering the recursive main loop, gtk_dialog_run() calls
1305 1306 1307
 * gtk_widget_show() on the dialog for you. Note that you still
 * need to show any children of the dialog yourself.
 *
1308
 * During gtk_dialog_run(), the default behavior of #GtkWidget::delete-event
1309
 * is disabled; if the dialog receives ::delete_event, it will not be
1310
 * destroyed as windows usually are, and gtk_dialog_run() will return
1311
 * #GTK_RESPONSE_DELETE_EVENT. Also, during gtk_dialog_run() the dialog
1312
 * will be modal. You can force gtk_dialog_run() to return at any time by
1313 1314
 * calling gtk_dialog_response() to emit the ::response signal. Destroying
 * the dialog during gtk_dialog_run() is a very bad idea, because your
1315
 * post-run code won’t know whether the dialog was destroyed or not.
1316 1317 1318 1319 1320
 *
 * After gtk_dialog_run() returns, you are responsible for hiding or
 * destroying the dialog if you wish to do so.
 *
 * Typical usage of this function might be:
1321
 * |[<!-- language="C" -->
1322 1323 1324 1325
 *   gint result = gtk_dialog_run (GTK_DIALOG (dialog));
 *   switch (result)
 *     {
 *       case GTK_RESPONSE_ACCEPT:
Matthias Clasen's avatar
Matthias Clasen committed
1326
 *          do_application_specific_something ();
1327 1328
 *          break;
 *       default:
Matthias Clasen's avatar
Matthias Clasen committed
1329
 *          do_nothing_since_dialog_was_cancelled ();
1330 1331 1332
 *          break;
 *     }
 *   gtk_widget_destroy (dialog);
Matthias Clasen's avatar
Matthias Clasen committed
1333
 * ]|
1334
 *
1335
 * Note that even though the recursive main loop gives the effect of a
1336 1337
 * modal dialog (it prevents the user from interacting with other
 * windows in the same window group while the dialog is run), callbacks
1338
 * such as timeouts, IO channel watches, DND drops, etc, will
1339
 * be triggered during a gtk_dialog_run() call.
1340
 *
1341
 * Returns: response ID
1342 1343 1344 1345
 **/
gint
gtk_dialog_run (GtkDialog *dialog)
{
1346
  RunInfo ri = { NULL, GTK_RESPONSE_NONE, NULL, FALSE };
1347
  gboolean was_modal;
1348 1349 1350 1351
  gulong response_handler;
  gulong unmap_handler;
  gulong destroy_handler;
  gulong delete_handler;
1352

1353 1354
  g_return_val_if_fail (GTK_IS_DIALOG (dialog), -1);

Manish Singh's avatar
Manish Singh committed
1355
  g_object_ref (dialog);
1356

1357
  was_modal = gtk_window_get_modal (GTK_WINDOW (dialog));