1 /*
2 * Copyright (c) 2003-2005 by the gtk2-perl team (see the file AUTHORS)
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301 USA.
18 *
19 * $Id$
20 */
21
22 #include "gtk2perl.h"
23 #include <gperl_marshal.h>
24
25 /*
26 * The next three functions are also used in GtkInfoBar.xs, thus they are not
27 * declared static.
28 */
29
30 /*
31 * GtkDialog interprets the response id as completely user-defined for
32 * positive values, and as special enums for negative values. so, we
33 * will handle the response_id as a plain SV so we can implement this
34 * special behavior.
35 */
36
37 gint
gtk2perl_dialog_response_id_from_sv(SV * sv)38 gtk2perl_dialog_response_id_from_sv (SV * sv)
39 {
40 int n;
41 if (looks_like_number (sv))
42 return SvIV (sv);
43 if (!gperl_try_convert_enum (GTK_TYPE_RESPONSE_TYPE, sv, &n))
44 croak ("response_id should be either a GtkResponseType or an integer");
45 return n;
46 }
47
48 SV *
gtk2perl_dialog_response_id_to_sv(gint response)49 gtk2perl_dialog_response_id_to_sv (gint response)
50 {
51 return gperl_convert_back_enum_pass_unknown (GTK_TYPE_RESPONSE_TYPE,
52 response);
53 }
54
55 /*
56 GtkDialog's response event is defined in Gtk as having a signal parameter
57 of type G_TYPE_INT, but GtkResponseType values are passed through it.
58
59 this custom marshaller allows us to catch and convert enum codes like those
60 returned by $dialog->run , instead of requiring the callback to deal with
61 the raw negative numeric values for the predefined constants.
62 */
63 void
gtk2perl_dialog_response_marshal(GClosure * closure,GValue * return_value,guint n_param_values,const GValue * param_values,gpointer invocation_hint,gpointer marshal_data)64 gtk2perl_dialog_response_marshal (GClosure * closure,
65 GValue * return_value,
66 guint n_param_values,
67 const GValue * param_values,
68 gpointer invocation_hint,
69 gpointer marshal_data)
70 {
71 dGPERL_CLOSURE_MARSHAL_ARGS;
72
73 GPERL_CLOSURE_MARSHAL_INIT (closure, marshal_data);
74
75 PERL_UNUSED_VAR (return_value);
76 PERL_UNUSED_VAR (n_param_values);
77 PERL_UNUSED_VAR (invocation_hint);
78
79 ENTER;
80 SAVETMPS;
81
82 PUSHMARK (SP);
83
84 GPERL_CLOSURE_MARSHAL_PUSH_INSTANCE (param_values);
85
86 /* the second parameter for this signal is defined as an int
87 * but is actually a response code, and can have GtkResponseType
88 * values. */
89 XPUSHs (sv_2mortal (gtk2perl_dialog_response_id_to_sv
90 (g_value_get_int (param_values + 1))));
91
92 GPERL_CLOSURE_MARSHAL_PUSH_DATA;
93
94 PUTBACK;
95
96 GPERL_CLOSURE_MARSHAL_CALL (G_DISCARD);
97
98 /*
99 * clean up
100 */
101
102 FREETMPS;
103 LEAVE;
104 }
105
106 MODULE = Gtk2::Dialog PACKAGE = Gtk2::Dialog PREFIX = gtk_dialog_
107
108 =for position SYNOPSIS
109
110 =head1 SYNOPSIS
111
112 # create a new dialog with some buttons - one stock, one not.
113 $dialog = Gtk2::Dialog->new ($title, $parent_window, $flags,
114 'gtk-cancel' => 'cancel',
115 'Do it' => 'ok');
116 # create window contents for yourself.
117 $dialog->get_content_area ()->add ($some_widget);
118
119 $dialog->set_default_response ('ok');
120
121 # show and interact modally -- blocks until the user
122 # activates a response.
123 $response = $dialog->run;
124 if ($response eq 'ok') {
125 do_the_stuff ();
126 }
127
128 # activating a response does not destroy the window,
129 # that's up to you.
130 $dialog->destroy;
131
132 =cut
133
134 =for position DESCRIPTION
135
136 =head1 DESCRIPTION
137
138 Dialog boxes are a convenient way to prompt the user for a small amount of
139 input, eg. to display a message, ask a question, or anything else that does not
140 require extensive effort on the user's part.
141
142 GTK+ treats a dialog as a window split vertically. The top section is a
143 Gtk2::VBox, and is where widgets such as a Gtk2::Label or a Gtk2::Entry should
144 be packed. The bottom area is known as the "action_area". This is generally
145 used for packing buttons into the dialog which may perform functions such as
146 cancel, ok, or apply. The two areas are separated by a Gtk2::HSeparator.
147
148 GtkDialog boxes are created with a call to C<< Gtk2::Dialog->new >>. The
149 multi-argument form (and its alias, C<new_with_buttons> is recommended; it
150 allows you to set the dialog title, some convenient flags, and add simple
151 buttons all in one go.
152
153 If I<$dialog> is a newly created dialog, the two primary areas of the window
154 can be accessed as C<< $dialog->get_content_area () >> and
155 C<< $dialog->get_action_area () >>, as can be seen from the example, below.
156
157 A 'modal' dialog (that is, one which freezes the rest of the application from
158 user input), can be created by calling the Gtk2::Window method C<set_modal> on
159 the dialog. You can also pass the 'modal' flag to C<new>.
160
161 If you add buttons to GtkDialog using C<new>, C<new_with_buttons>,
162 C<add_button>, C<add_buttons>, or C<add_action_widget>, clicking the button
163 will emit a signal called "response" with a response ID that you specified.
164 GTK+ will never assign a meaning to positive response IDs; these are entirely
165 user-defined. But for convenience, you can use the response IDs in the
166 Gtk2::ResponseType enumeration. If a dialog receives a delete event, the
167 "response" signal will be emitted with a response ID of 'delete-event'.
168
169 If you want to block waiting for a dialog to return before returning control
170 flow to your code, you can call C<< $dialog->run >>. This function enters a
171 recursive main loop and waits for the user to respond to the dialog, returning
172 the response ID corresponding to the button the user clicked.
173
174 For the simple dialog in the following example, in reality you'd probably use
175 Gtk2::MessageDialog to save yourself some effort. But you'd need to create the
176 dialog contents manually if you had more than a simple message in the dialog.
177
178 # Function to open a dialog box displaying the message provided.
179
180 sub quick_message {
181 my $message = shift;
182 my $dialog = Gtk2::Dialog->new ('Message', $main_app_window,
183 'destroy-with-parent',
184 'gtk-ok' => 'none');
185 my $label = Gtk2::Label->new (message);
186 $dialog->get_content_area ()->add ($label);
187
188 # Ensure that the dialog box is destroyed when the user responds.
189 $dialog->signal_connect (response => sub { $_[0]->destroy });
190
191 $dialog->show_all;
192 }
193
194 =head2 Delete, Close and Destroy
195
196 In the default keybindings the "Esc" key calls the C<close> action
197 signal. The default in that signal is to synthesise a C<delete-event>
198 like a window manager close would do.
199
200 A delete-event first runs the C<response> signal with ID
201 C<"delete-event">, but the handler there can't influence the default
202 destroy behaviour of the C<delete-event> signal. See L<Gtk2::Window>
203 for notes on destroy vs hide.
204
205 If you add your own "Close" button to the dialog, perhaps using the
206 builtin C<close> response ID, you must make your C<response> signal
207 handler do whatever's needed for closing. Often a good thing is just
208 to run the C<close> action signal the same as the Esc key.
209
210 sub my_response_handler {
211 my ($dialog, $response) = @_;
212 if ($response eq 'close') {
213 $self->signal_emit ('close');
214
215 } elsif ...
216 }
217
218 =cut
219
220 =for position post_signals
221
222 Note that currently in a Perl subclass of C<Gtk2::Dialog> a class
223 closure, ie. class default signal handler, for the C<response> signal
224 will be called with the response ID just as an integer, it's not
225 turned into an enum string like C<"ok"> the way a handler setup with
226 C<signal_connect> receives.
227
228 Hopefully this will change in the future, so don't count on it. In
229 the interim the easiest thing to do is install your default handler in
230 C<INIT_INSTANCE> with a C<signal_connect>. (The subtleties of what
231 order handlers are called in will differ, but often that doesn't
232 matter.)
233
234 =cut
235
236 =for enum GtkResponseType
237
238 The response type is somewhat abnormal as far as gtk2-perl enums go. In C,
239 this enum lists named, predefined integer values for a field that is other
240 composed of whatever integer values you like. In Perl, we allow this to
241 be either one of the string constants listed here or any positive integer
242 value. For example, 'ok', 'cancel', 4, and 42 are all valid response ids.
243 You cannot use arbitrary string values, they must be integers. Be careful,
244 because unknown string values tend to be mapped to 0.
245
246 =cut
247
248 =for enum GtkDialogFlags
249 =cut
250
251 BOOT:
252 gperl_signal_set_marshaller_for (GTK_TYPE_DIALOG, "response",
253 gtk2perl_dialog_response_marshal);
254
255 =for apidoc Gtk2::Dialog::vbox __hide__
256 =cut
257
258 =for apidoc Gtk2::Dialog::action_area __hide__
259 =cut
260
261 GtkWidget *
262 get_content_area (dialog)
263 GtkDialog * dialog
264 ALIAS:
265 Gtk2::Dialog::vbox = 1
266 Gtk2::Dialog::get_action_area = 2
267 Gtk2::Dialog::action_area = 3
268 CODE:
269 switch(ix) {
270 case 0:
271 case 1:
272 #if GTK_CHECK_VERSION (2, 14, 0)
273 RETVAL = gtk_dialog_get_content_area (dialog);
274 #else
275 RETVAL = dialog->vbox;
276 #endif
277 break;
278 case 2:
279 case 3:
280 #if GTK_CHECK_VERSION (2, 14, 0)
281 RETVAL = gtk_dialog_get_action_area (dialog);
282 #else
283 RETVAL = dialog->action_area;
284 #endif
285 break;
286 default:
287 RETVAL = NULL;
288 g_assert_not_reached ();
289 }
290 OUTPUT:
291 RETVAL
292
293 ##GtkWidget *
294 ##gtk_dialog_new (class)
295 ##
296 ##GtkWidget* gtk_dialog_new_with_buttons (const gchar *title,
297 ## GtkWindow *parent,
298 ## GtkDialogFlags flags,
299 ## const gchar *first_button_text,
300 ## ...);
301
302 =for apidoc Gtk2::Dialog::new_with_buttons
303 =for signature $widget = Gtk2::Dialog->new_with_buttons ($title, $parent, $flags, ...)
304 =for arg ... of button-text => response-id pairs.
305
306 Alias for the multi-argument version of C<< Gtk2::Dialog->new >>.
307
308 =cut
309
310 =for apidoc
311 =for signature $widget = Gtk2::Dialog->new;
312 =for signature $widget = Gtk2::Dialog->new ($title, $parent, $flags, ...)
313 =for arg title (string) window title
314 =for arg parent (GtkWindow_ornull) make the new dialog transient for this window
315 =for arg flags (GtkDialogFlags) interesting properties
316 =for arg ... of button-text => response-id pairs.
317
318 The multi-argument form takes the same list of text => response-id pairs as
319 C<< $dialog->add_buttons >>. Do not pack widgets directly into the window;
320 add them to C<< $dialog->get_content_area () >>.
321
322 Here's a simple example:
323
324 $dialog = Gtk2::Dialog->new ('A cool dialog',
325 $main_app_window,
326 [qw/modal destroy-with-parent/],
327 'gtk-ok' => 'accept',
328 'gtk-cancel' => 'reject');
329
330 =cut
331 GtkWidget *
332 gtk_dialog_new (class, ...)
333 ALIAS:
334 Gtk2::Dialog::new_with_buttons = 1
335 PREINIT:
336 int i;
337 gchar * title;
338 GtkWidget * dialog;
339 GtkWindow * parent;
340 int flags;
341 CODE:
342 PERL_UNUSED_VAR (ix);
343 if (items == 1) {
344 /* the easy way out... */
345 dialog = gtk_dialog_new ();
346
347 } else if ((items < 4) || (items % 2)) {
348 croak ("USAGE: Gtk2::Dialog->new ()\n"
349 " or Gtk2::Dialog->new (TITLE, PARENT, FLAGS, ...)\n"
350 " where ... is a series of button text and response id pairs");
351 } else {
352 title = SvGChar (ST (1));
353 parent = SvGtkWindow_ornull (ST (2));
354 flags = SvGtkDialogFlags (ST (3));
355
356 /* we can't really pass on a varargs call (at least, i don't
357 * know how to convert from perl stack to C va_list), so we
358 * have to duplicate a bit of the functionality of the C
359 * version. luckily it's nothing too intense. */
360
361 dialog = gtk_dialog_new ();
362 if (title)
363 gtk_window_set_title (GTK_WINDOW (dialog), title);
364 if (parent)
365 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
366 if (flags & GTK_DIALOG_MODAL)
367 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
368 if (flags & GTK_DIALOG_DESTROY_WITH_PARENT)
369 gtk_window_set_destroy_with_parent (GTK_WINDOW (dialog), TRUE);
370 if (flags & GTK_DIALOG_NO_SEPARATOR)
371 gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
372
373 /* skip the first 4 stack items --- we've already seen them! */
374 for (i = 4; i < items; i += 2) {
375 gchar * text = SvGChar (ST (i));
376 int response_id =
377 gtk2perl_dialog_response_id_from_sv (ST (i+1));
378 gtk_dialog_add_button (GTK_DIALOG (dialog), text,
379 response_id);
380 }
381 }
382 RETVAL = dialog;
383 OUTPUT:
384 RETVAL
385
386
387 =for apidoc
388 =for signature $responsetype = $dialog->run
389 Blocks in a recursive main loop until the dialog either emits the response
390 signal, or is destroyed. If the dialog is destroyed during the call to
391 C<< $dialog->run >>, the function returns 'GTK_RESPONSE_NONE' ('none').
392 Otherwise, it returns the response ID from the "response" signal emission.
393 Before entering the recursive main loop, C<< $dialog->run >> calls
394 C<< $widget->show >> on I<$dialog> for you. Note that you still need to show
395 any children of the dialog yourself.
396
397 During C<run>, the default behavior of "delete_event" is disabled; if the
398 dialog receives "delete_event", it will not be destroyed as windows usually
399 are, and C<run> will return 'delete-event'.
400 Also, during C<run> the dialog will be modal. You can force C<run> to return
401 at any time by calling C<< $dialog->response >> to emit the "response" signal.
402 Destroying the dialog during C<run> is a very bad idea, because your post-run
403 code won't know whether the dialog was destroyed or not.
404
405 After C<run> returns, you are responsible for hiding or destroying the dialog
406 if you wish to do so.
407
408 Typical usage of this function might be:
409
410 if ('accept' eq $dialog->run) {
411 do_application_specific_something ();
412 } else {
413 do_nothing_since_dialog_was_cancelled ();
414 }
415 $dialog->destroy;
416
417 =cut
418 SV *
419 gtk_dialog_run (dialog)
420 GtkDialog * dialog
421 CODE:
422 RETVAL = gtk2perl_dialog_response_id_to_sv (gtk_dialog_run (dialog));
423 OUTPUT:
424 RETVAL
425
426
427 =for apidoc
428 =for arg response_id (GtkResponseType)
429 Emit the response signal, as though the user had clicked on the button with
430 I<$response_id>.
431 =cut
432 void
433 gtk_dialog_response (dialog, response_id)
434 GtkDialog * dialog
435 SV * response_id
436 C_ARGS:
437 dialog, gtk2perl_dialog_response_id_from_sv (response_id)
438
439
440
441 =for apidoc
442 =for arg button_text (string) may be arbitrary text with mnenonics, or stock ids
443 =for arg response_id (GtkResponseType)
444 Returns the created button.
445 =cut
446 GtkWidget *
447 gtk_dialog_add_button (dialog, button_text, response_id)
448 GtkDialog * dialog
449 const gchar * button_text
450 SV * response_id
451 CODE:
452 RETVAL = gtk_dialog_add_button (dialog, button_text,
453 gtk2perl_dialog_response_id_from_sv (
454 response_id));
455 OUTPUT:
456 RETVAL
457
458 =for apidoc
459 =for arg ... of button-text => response-id pairs
460 Like calling C<< $dialog->add_button >> repeatedly, except you don't get the
461 created widgets back. The buttons go from left to right, so the first button
462 added will be the left-most one.
463 =cut
464 void
465 gtk_dialog_add_buttons (dialog, ...)
466 GtkDialog * dialog
467 PREINIT:
468 int i;
469 CODE:
470 if (!(items % 2))
471 croak("gtk_dialog_add_buttons: odd number of parameters");
472 /* we can't make var args, so we'll call add_button for each */
473 for (i = 1; i < items; i += 2)
474 gtk_dialog_add_button (dialog, SvGChar (ST (i)),
475 gtk2perl_dialog_response_id_from_sv (
476 ST (i+1)));
477
478 =for apidoc
479 =for arg response_id (GtkResponseType)
480 Enable or disable an action button by its I<$response_id>.
481 =cut
482 void
483 gtk_dialog_set_response_sensitive (dialog, response_id, setting)
484 GtkDialog * dialog
485 SV * response_id
486 gboolean setting
487 CODE:
488 gtk_dialog_set_response_sensitive (dialog,
489 gtk2perl_dialog_response_id_from_sv (
490 response_id),
491 setting);
492
493 =for apidoc
494 =for arg response_id (GtkResponseType)
495 =cut
496 void
497 gtk_dialog_add_action_widget (dialog, child, response_id)
498 GtkDialog * dialog
499 GtkWidget * child
500 SV * response_id
501 CODE:
502 gtk_dialog_add_action_widget (dialog, child,
503 gtk2perl_dialog_response_id_from_sv (
504 response_id));
505
506
507 =for apidoc
508 =for arg response_id (GtkResponseType)
509 =cut
510 void
511 gtk_dialog_set_default_response (dialog, response_id)
512 GtkDialog * dialog
513 SV * response_id
514 CODE:
515 gtk_dialog_set_default_response (dialog,
516 gtk2perl_dialog_response_id_from_sv (
517 response_id));
518
519 void
520 gtk_dialog_set_has_separator (dialog, setting)
521 GtkDialog * dialog
522 gboolean setting
523
524 gboolean
525 gtk_dialog_get_has_separator (dialog)
526 GtkDialog * dialog
527
528 #if GTK_CHECK_VERSION (2, 6, 0)
529
530 ## void gtk_dialog_set_alternative_button_order (GtkDialog *dialog, gint first_response_id, ...)
531 void
532 gtk_dialog_set_alternative_button_order (dialog, ...)
533 GtkDialog *dialog
534 PREINIT:
535 gint n_params, i;
536 gint *new_order;
537 CODE:
538 if ((n_params = (items - 1)) > 0) {
539 new_order = g_new0 (gint, n_params);
540 for (i = 1; i < items; i++)
541 new_order[i - 1] = gtk2perl_dialog_response_id_from_sv (
542 ST (i));
543
544 gtk_dialog_set_alternative_button_order_from_array (
545 dialog, n_params, new_order);
546
547 g_free (new_order);
548 }
549
550 #endif
551
552 #if GTK_CHECK_VERSION (2, 8, 0)
553
554 ## gint gtk_dialog_get_response_for_widget (GtkDialog *dialog, GtkWidget *widget);
555 SV *
556 gtk_dialog_get_response_for_widget (dialog, widget)
557 GtkDialog *dialog
558 GtkWidget *widget
559 PREINIT:
560 gint tmp;
561 CODE:
562 tmp = gtk_dialog_get_response_for_widget (dialog, widget);
563 RETVAL = gtk2perl_dialog_response_id_to_sv (tmp);
564 OUTPUT:
565 RETVAL
566
567 #endif
568
569 #if GTK_CHECK_VERSION (2, 20, 0)
570
571 ## GtkWidget *widget gtk_dialog_get_widget_for_response (GtkDialog *dialog, gint);
572 =for arg response_id (GtkResponseType)
573 =cut
574 GtkWidget *
575 gtk_dialog_get_widget_for_response (dialog, response_id)
576 GtkDialog *dialog
577 SV *response_id
578 C_ARGS:
579 dialog, gtk2perl_dialog_response_id_from_sv (response_id)
580
581 #endif
582
583 MODULE = Gtk2::Dialog PACKAGE = Gtk2 PREFIX = gtk_
584
585 #if GTK_CHECK_VERSION (2, 6, 0)
586
587 # don't override the pod from Gtk2.pm...
588 =for object Gtk2::main
589 =cut
590
591 ## gboolean gtk_alternative_dialog_button_order (GdkScreen *screen);
592 gboolean
593 gtk_alternative_dialog_button_order (class, screen=NULL)
594 GdkScreen_ornull *screen
595 C_ARGS:
596 screen
597
598 #endif
599