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