1 /* gdict-source-dialog.c - source dialog
2 *
3 * This file is part of GNOME Dictionary
4 *
5 * Copyright (C) 2005 Emmanuele Bassi
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <time.h>
27 #include <sys/types.h>
28 #ifdef HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31
32 #include <glib/gi18n.h>
33
34 #include <gio/gio.h>
35
36 #include "gdict-source-dialog.h"
37 #include "gdict-common.h"
38
39 #define GDICT_SOURCE_UI "/org/gnome/Dictionary/gdict-source-dialog.ui"
40
41 /*********************
42 * GdictSourceDialog *
43 *********************/
44
45 struct _GdictSourceDialog
46 {
47 GtkDialog parent_instance;
48
49 GSettings *settings;
50
51 GdictSourceLoader *loader;
52 GdictSource *source;
53 gchar *source_name;
54 GdictContext *context;
55
56 GdictSourceDialogAction action;
57
58 GdictSourceTransport transport;
59
60 GtkWidget *hostname_label;
61 GtkWidget *hostname_entry;
62 GtkWidget *port_label;
63 GtkWidget *port_entry;
64
65 GtkWidget *description_label;
66 GtkWidget *description_entry;
67
68 GtkWidget *add_button;
69 GtkWidget *close_button;
70 GtkWidget *cancel_button;
71 GtkWidget *help_button;
72
73 GtkWidget *db_vbox;
74 GtkWidget *db_chooser;
75 GtkWidget *strat_vbox;
76 GtkWidget *strat_chooser;
77
78 GtkWidget *transport_combo;
79 };
80
81 struct _GdictSourceDialogClass
82 {
83 GtkDialogClass parent_class;
84 };
85
86 enum
87 {
88 PROP_0,
89
90 PROP_SOURCE_LOADER,
91 PROP_SOURCE_NAME,
92 PROP_ACTION
93 };
94
G_DEFINE_TYPE(GdictSourceDialog,gdict_source_dialog,GTK_TYPE_DIALOG)95 G_DEFINE_TYPE (GdictSourceDialog, gdict_source_dialog, GTK_TYPE_DIALOG)
96
97 static void
98 set_source_loader (GdictSourceDialog *dialog,
99 GdictSourceLoader *loader)
100 {
101 if (dialog->loader)
102 g_object_unref (dialog->loader);
103
104 dialog->loader = g_object_ref (loader);
105 }
106
107 static void
on_transport_changed(GtkWidget * widget,gpointer user_data)108 on_transport_changed (GtkWidget *widget,
109 gpointer user_data)
110 {
111 GdictSourceDialog *dialog = user_data;
112 GdictSourceTransport transport;
113
114 transport = gtk_combo_box_get_active (GTK_COMBO_BOX (widget));
115 if (transport == dialog->transport)
116 return;
117
118 /* Hide everything by default */
119 gtk_widget_hide (dialog->hostname_label);
120 gtk_widget_hide (dialog->hostname_entry);
121 gtk_widget_hide (dialog->port_label);
122 gtk_widget_hide (dialog->port_entry);
123
124 if (dialog->action == GDICT_SOURCE_DIALOG_CREATE)
125 {
126 gtk_widget_set_sensitive (dialog->add_button, FALSE);
127 dialog->transport = GDICT_SOURCE_TRANSPORT_INVALID;
128 }
129
130 /* Then show what's needed depending on the transport */
131 switch (transport)
132 {
133 case GDICT_SOURCE_TRANSPORT_DICTD:
134 gtk_widget_show (dialog->hostname_label);
135 gtk_widget_show (dialog->hostname_entry);
136 gtk_widget_show (dialog->port_label);
137 gtk_widget_show (dialog->port_entry);
138
139 if (dialog->action == GDICT_SOURCE_DIALOG_CREATE)
140 {
141 gtk_widget_set_sensitive (dialog->add_button, TRUE);
142 dialog->transport = GDICT_SOURCE_TRANSPORT_DICTD;
143 }
144 break;
145
146 default:
147 break;
148 }
149 }
150
151 static void
set_transport_settings(GdictSourceDialog * dialog)152 set_transport_settings (GdictSourceDialog *dialog)
153 {
154 switch (dialog->transport)
155 {
156 case GDICT_SOURCE_TRANSPORT_DICTD:
157 {
158 GdictClientContext *context;
159 const gchar *hostname;
160 gchar *port_str;
161 guint port;
162
163 context = GDICT_CLIENT_CONTEXT (dialog->context);
164 hostname = gdict_client_context_get_hostname (context);
165 port = gdict_client_context_get_port (context);
166 port_str = g_strdup_printf ("%d", port);
167
168 gtk_entry_set_text (GTK_ENTRY (dialog->hostname_entry), hostname);
169 gtk_entry_set_text (GTK_ENTRY (dialog->port_entry), port_str);
170
171 gtk_widget_show (dialog->hostname_label);
172 gtk_widget_show (dialog->hostname_entry);
173 gtk_widget_show (dialog->port_label);
174 gtk_widget_show (dialog->port_entry);
175
176 g_free (port_str);
177 }
178 break;
179
180 default:
181 break;
182 }
183 }
184
185 static void
update_dialog_ui(GdictSourceDialog * dialog)186 update_dialog_ui (GdictSourceDialog *dialog)
187 {
188 GdictSource *source;
189
190 /* TODO - add code to update the contents of the dialog depending
191 * on the action; if we are in _CREATE, no action is needed
192 */
193 switch (dialog->action)
194 {
195 case GDICT_SOURCE_DIALOG_VIEW:
196 case GDICT_SOURCE_DIALOG_EDIT:
197 if (!dialog->source_name)
198 {
199 g_warning ("Attempting to retrieve source, but no "
200 "source name has been defined. Aborting...");
201 return;
202 }
203
204 source = gdict_source_loader_get_source (dialog->loader, dialog->source_name);
205 if (!source)
206 {
207 g_warning ("Attempting to retrieve source, but no "
208 "source named `%s' was found. Aborting...",
209 dialog->source_name);
210 return;
211 }
212
213 g_object_ref (source);
214
215 dialog->source = g_object_ref (source);
216 gtk_entry_set_text (GTK_ENTRY (dialog->description_entry), gdict_source_get_description (source));
217 dialog->transport = gdict_source_get_transport (source);
218 gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->transport_combo), (gint) dialog->transport);
219
220 /* set the context for the database and strategy choosers */
221 dialog->context = gdict_source_get_context (source);
222 if (!dialog->context)
223 {
224 g_warning ("Attempting to retrieve the context, but "
225 "none was found for source `%s'.",
226 dialog->source_name);
227 return;
228 }
229
230 set_transport_settings (dialog);
231
232 gdict_database_chooser_set_context (GDICT_DATABASE_CHOOSER (dialog->db_chooser),
233 dialog->context);
234 gdict_database_chooser_refresh (GDICT_DATABASE_CHOOSER (dialog->db_chooser));
235 gdict_strategy_chooser_set_context (GDICT_STRATEGY_CHOOSER (dialog->strat_chooser),
236 dialog->context);
237 gdict_strategy_chooser_refresh (GDICT_STRATEGY_CHOOSER (dialog->strat_chooser));
238 break;
239
240 case GDICT_SOURCE_DIALOG_CREATE:
241 /* DICTD transport is default */
242 gtk_combo_box_set_active (GTK_COMBO_BOX (dialog->transport_combo), 0);
243 g_signal_emit_by_name (dialog->transport_combo, "changed");
244 break;
245
246 default:
247 g_assert_not_reached ();
248 break;
249 }
250 }
251
252 static void
build_new_source(GdictSourceDialog * dialog)253 build_new_source (GdictSourceDialog *dialog)
254 {
255 GdictSource *source;
256 gchar *name, *text;
257 GdictSourceTransport transport;
258 gchar *data;
259 gsize length;
260 GError *error;
261 gchar *filename;
262 gchar *config_dir;
263 GdictDatabaseChooser *db_chooser;
264 GdictStrategyChooser *strat_chooser;
265
266 source = gdict_source_new ();
267
268 /* use the timestamp and the pid to get a unique name */
269 name = g_strdup_printf ("source-%lu-%u",
270 (gulong) time (NULL),
271 (guint) getpid ());
272 gdict_source_set_name (source, name);
273 g_free (name);
274
275 gdict_source_set_description (source, gtk_entry_get_text (GTK_ENTRY (dialog->description_entry)));
276
277 db_chooser = GDICT_DATABASE_CHOOSER (dialog->db_chooser);
278 text = gdict_database_chooser_get_current_database (db_chooser);
279 gdict_source_set_database (source, text);
280 g_free (text);
281
282 strat_chooser = GDICT_STRATEGY_CHOOSER (dialog->strat_chooser);
283 text = gdict_strategy_chooser_get_current_strategy (strat_chooser);
284 gdict_source_set_strategy (source, text);
285 g_free (text);
286
287 /* get the selected transport id */
288 transport = dialog->transport;
289 switch (transport)
290 {
291 case GDICT_SOURCE_TRANSPORT_DICTD:
292 {
293 const char *host = gtk_entry_get_text (GTK_ENTRY (dialog->hostname_entry));
294 const char *port = gtk_entry_get_text (GTK_ENTRY (dialog->port_entry));
295
296 gdict_source_set_transport (source, GDICT_SOURCE_TRANSPORT_DICTD,
297 "hostname", host,
298 "port", atoi (port),
299 NULL);
300 }
301 break;
302
303 case GDICT_SOURCE_TRANSPORT_INVALID:
304 default:
305 g_warning ("Invalid transport");
306 return;
307 }
308
309 error = NULL;
310 data = gdict_source_to_data (source, &length, &error);
311 if (error != NULL)
312 {
313 gdict_show_gerror_dialog (GTK_WINDOW (dialog),
314 _("Unable to create a source file"),
315 error);
316
317 g_object_unref (source);
318 return;
319 }
320
321 config_dir = gdict_get_config_dir ();
322 name = g_strconcat (gdict_source_get_name (source), ".desktop", NULL);
323 filename = g_build_filename (config_dir, name, NULL);
324 g_free (config_dir);
325 g_free (name);
326
327 g_debug ("Saving new source '%s' (%s) at '%s'",
328 gdict_source_get_description (source),
329 gdict_source_get_name (source),
330 filename);
331
332 g_file_set_contents (filename, data, length, &error);
333
334 if (error != NULL)
335 gdict_show_gerror_dialog (GTK_WINDOW (dialog),
336 _("Unable to save source file"),
337 error);
338
339 g_free (filename);
340 g_free (data);
341 g_object_unref (source);
342 }
343
344 static void
save_source(GdictSourceDialog * dialog)345 save_source (GdictSourceDialog *dialog)
346 {
347 GdictSource *source;
348 GdictDatabaseChooser *db_chooser;
349 GdictStrategyChooser *strat_chooser;
350 gchar *name, *text;
351 GdictSourceTransport transport;
352 gchar *data;
353 gsize length;
354 GError *error;
355 gchar *filename;
356 gchar *config_dir;
357
358 source = gdict_source_loader_get_source (dialog->loader, dialog->source_name);
359 if (!source)
360 {
361 g_warning ("Attempting to save source `%s', but no "
362 "source for that name was found.",
363 dialog->source_name);
364
365 return;
366 }
367
368 gdict_source_set_description (source, gtk_entry_get_text (GTK_ENTRY (dialog->description_entry)));
369
370 db_chooser = GDICT_DATABASE_CHOOSER (dialog->db_chooser);
371 text = gdict_database_chooser_get_current_database (db_chooser);
372 gdict_source_set_database (source, text);
373 g_free (text);
374
375 strat_chooser = GDICT_STRATEGY_CHOOSER (dialog->strat_chooser);
376 text = gdict_strategy_chooser_get_current_strategy (strat_chooser);
377 gdict_source_set_strategy (source, text);
378 g_free (text);
379
380
381 /* get the selected transport id */
382 transport = dialog->transport;
383 switch (transport)
384 {
385 case GDICT_SOURCE_TRANSPORT_DICTD:
386 {
387 const char *host = gtk_entry_get_text (GTK_ENTRY (dialog->hostname_entry));
388 const char *port = gtk_entry_get_text (GTK_ENTRY (dialog->port_entry));
389
390 gdict_source_set_transport (source, GDICT_SOURCE_TRANSPORT_DICTD,
391 "hostname", host,
392 "port", atoi (port),
393 NULL);
394 }
395 break;
396
397 case GDICT_SOURCE_TRANSPORT_INVALID:
398 default:
399 g_warning ("Invalid transport");
400 return;
401 }
402
403 error = NULL;
404 data = gdict_source_to_data (source, &length, &error);
405 if (error)
406 {
407 gdict_show_gerror_dialog (GTK_WINDOW (dialog),
408 _("Unable to create a source file"),
409 error);
410
411 g_object_unref (source);
412 return;
413 }
414
415 g_object_get (source, "filename", &filename, NULL);
416 if (!filename)
417 {
418 config_dir = gdict_get_config_dir();
419 name = g_strconcat (gdict_source_get_name (source), ".desktop", NULL);
420 filename = g_build_filename (config_dir, name, NULL);
421 g_free (config_dir);
422 g_free (name);
423 }
424
425 g_file_set_contents (filename, data, length, &error);
426 if (error)
427 gdict_show_gerror_dialog (GTK_WINDOW (dialog),
428 _("Unable to save source file"),
429 error);
430
431 g_free (filename);
432 g_free (data);
433 g_object_unref (source);
434 }
435
436 static void
on_dialog_response(GtkDialog * dialog,gint response_id,gpointer user_data)437 on_dialog_response (GtkDialog *dialog,
438 gint response_id,
439 gpointer user_data)
440 {
441 GError *err = NULL;
442
443 switch (response_id)
444 {
445 case GTK_RESPONSE_ACCEPT:
446 build_new_source (GDICT_SOURCE_DIALOG (dialog));
447 break;
448
449 case GTK_RESPONSE_HELP:
450 #if GTK_CHECK_VERSION (3, 22, 0)
451 gtk_show_uri_on_window (GTK_WINDOW (dialog),
452 "help:gnome-dictionary/gnome-dictionary-add-source",
453 gtk_get_current_event_time (), &err);
454 #else
455 gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (dialog)),
456 "help:gnome-dictionary/gnome-dictionary-add-source",
457 gtk_get_current_event_time (), &err);
458 #endif
459
460 if (err)
461 {
462 gdict_show_gerror_dialog (GTK_WINDOW (dialog),
463 _("There was an error while displaying help"),
464 err);
465 g_error_free (err);
466 }
467
468 /* we don't want the dialog to close itself */
469 g_signal_stop_emission_by_name (dialog, "response");
470 break;
471
472 case GTK_RESPONSE_CLOSE:
473 save_source (GDICT_SOURCE_DIALOG (dialog));
474 break;
475
476 case GTK_RESPONSE_CANCEL:
477 break;
478
479 default:
480 break;
481 }
482 }
483
484 static void
gdict_source_dialog_finalize(GObject * object)485 gdict_source_dialog_finalize (GObject *object)
486 {
487 GdictSourceDialog *dialog = GDICT_SOURCE_DIALOG (object);
488
489 g_clear_object (&dialog->settings);
490 g_clear_object (&dialog->source);
491 g_clear_object (&dialog->loader);
492
493 g_free (dialog->source_name);
494
495 G_OBJECT_CLASS (gdict_source_dialog_parent_class)->finalize (object);
496 }
497
498 static void
gdict_source_dialog_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)499 gdict_source_dialog_set_property (GObject *object,
500 guint prop_id,
501 const GValue *value,
502 GParamSpec *pspec)
503 {
504 GdictSourceDialog *dialog = GDICT_SOURCE_DIALOG (object);
505
506 switch (prop_id)
507 {
508 case PROP_SOURCE_LOADER:
509 set_source_loader (dialog, g_value_get_object (value));
510 break;
511
512 case PROP_SOURCE_NAME:
513 g_free (dialog->source_name);
514 dialog->source_name = g_strdup (g_value_get_string (value));
515 break;
516
517 case PROP_ACTION:
518 dialog->action = (GdictSourceDialogAction) g_value_get_int (value);
519 break;
520
521 default:
522 break;
523 }
524 }
525
526 static void
gdict_source_dialog_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)527 gdict_source_dialog_get_property (GObject *object,
528 guint prop_id,
529 GValue *value,
530 GParamSpec *pspec)
531 {
532 GdictSourceDialog *dialog = GDICT_SOURCE_DIALOG (object);
533
534 switch (prop_id)
535 {
536 case PROP_SOURCE_LOADER:
537 g_value_set_object (value, dialog->loader);
538 break;
539
540 case PROP_SOURCE_NAME:
541 g_value_set_string (value, dialog->source_name);
542 break;
543
544 case PROP_ACTION:
545 g_value_set_int (value, dialog->action);
546 break;
547
548 default:
549 break;
550 }
551 }
552
553 static void
gdict_source_dialog_constructed(GObject * object)554 gdict_source_dialog_constructed (GObject *object)
555 {
556 GdictSourceDialog *dialog = GDICT_SOURCE_DIALOG (object);
557
558 /* the UI changes depending on the action that the source dialog
559 * should perform
560 */
561 switch (dialog->action)
562 {
563 case GDICT_SOURCE_DIALOG_VIEW:
564 /* disable every editable widget */
565 gtk_editable_set_editable (GTK_EDITABLE (dialog->description_entry), FALSE);
566 gtk_editable_set_editable (GTK_EDITABLE (dialog->hostname_entry), FALSE);
567 gtk_editable_set_editable (GTK_EDITABLE (dialog->port_entry), FALSE);
568 gtk_widget_set_sensitive (dialog->transport_combo, FALSE);
569
570 /* we just allow closing the dialog */
571 dialog->close_button = gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Close"), GTK_RESPONSE_CANCEL);
572 break;
573
574 case GDICT_SOURCE_DIALOG_CREATE:
575 dialog->cancel_button = gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Cancel"), GTK_RESPONSE_CANCEL);
576 dialog->add_button = gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Add"), GTK_RESPONSE_ACCEPT);
577 /* the "add" button sensitivity is controlled by the transport_combo
578 * since it's the only setting that makes a source usable.
579 */
580 gtk_widget_set_sensitive (dialog->add_button, FALSE);
581 break;
582
583 case GDICT_SOURCE_DIALOG_EDIT:
584 dialog->cancel_button = gtk_dialog_add_button (GTK_DIALOG (dialog), _("C_ancel"), GTK_RESPONSE_CANCEL);
585 dialog->close_button = gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Close"), GTK_RESPONSE_CLOSE);
586 break;
587
588 default:
589 g_assert_not_reached ();
590 break;
591 }
592
593 /* this will take care of updating the contents of the dialog
594 * based on the action
595 */
596 update_dialog_ui (dialog);
597 }
598
599 static void
gdict_source_dialog_class_init(GdictSourceDialogClass * klass)600 gdict_source_dialog_class_init (GdictSourceDialogClass *klass)
601 {
602 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
603 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
604
605 gobject_class->constructed = gdict_source_dialog_constructed;
606 gobject_class->set_property = gdict_source_dialog_set_property;
607 gobject_class->get_property = gdict_source_dialog_get_property;
608 gobject_class->finalize = gdict_source_dialog_finalize;
609
610 g_object_class_install_property (gobject_class,
611 PROP_SOURCE_LOADER,
612 g_param_spec_object ("source-loader",
613 "Source Loader",
614 "The GdictSourceLoader used by the application",
615 GDICT_TYPE_SOURCE_LOADER,
616 G_PARAM_READWRITE |
617 G_PARAM_CONSTRUCT_ONLY |
618 G_PARAM_STATIC_STRINGS));
619 g_object_class_install_property (gobject_class,
620 PROP_SOURCE_NAME,
621 g_param_spec_string ("source-name",
622 "Source Name",
623 "The source name",
624 NULL,
625 G_PARAM_READWRITE |
626 G_PARAM_CONSTRUCT_ONLY |
627 G_PARAM_STATIC_STRINGS));
628 g_object_class_install_property (gobject_class,
629 PROP_ACTION,
630 g_param_spec_int ("action",
631 "Action",
632 "The action the source dialog should perform",
633 -1,
634 GDICT_SOURCE_DIALOG_EDIT,
635 GDICT_SOURCE_DIALOG_VIEW,
636 G_PARAM_READWRITE |
637 G_PARAM_CONSTRUCT_ONLY |
638 G_PARAM_STATIC_STRINGS));
639
640 gtk_widget_class_set_template_from_resource (widget_class, GDICT_SOURCE_UI);
641
642 gtk_widget_class_bind_template_child (widget_class, GdictSourceDialog, transport_combo);
643 gtk_widget_class_bind_template_child (widget_class, GdictSourceDialog, hostname_label);
644 gtk_widget_class_bind_template_child (widget_class, GdictSourceDialog, hostname_entry);
645 gtk_widget_class_bind_template_child (widget_class, GdictSourceDialog, port_label);
646 gtk_widget_class_bind_template_child (widget_class, GdictSourceDialog, port_entry);
647 gtk_widget_class_bind_template_child (widget_class, GdictSourceDialog, description_label);
648 gtk_widget_class_bind_template_child (widget_class, GdictSourceDialog, description_entry);
649 gtk_widget_class_bind_template_child (widget_class, GdictSourceDialog, db_vbox);
650 gtk_widget_class_bind_template_child (widget_class, GdictSourceDialog, strat_vbox);
651
652 gtk_widget_class_bind_template_callback (widget_class, on_dialog_response);
653 gtk_widget_class_bind_template_callback (widget_class, on_transport_changed);
654 }
655
656 static void
gdict_source_dialog_init(GdictSourceDialog * dialog)657 gdict_source_dialog_init (GdictSourceDialog *dialog)
658 {
659 gtk_widget_init_template (GTK_WIDGET (dialog));
660
661 gtk_widget_set_size_request (GTK_WIDGET (dialog), 400, 300);
662
663 dialog->transport = GDICT_SOURCE_TRANSPORT_INVALID;
664
665 /* The help button is always visible */
666 dialog->help_button = gtk_dialog_add_button (GTK_DIALOG (dialog), _("_Help"), GTK_RESPONSE_HELP);
667
668 /* Add our custom widgets */
669 dialog->db_chooser = gdict_database_chooser_new ();
670 gtk_box_pack_start (GTK_BOX (dialog->db_vbox), dialog->db_chooser, TRUE, TRUE, 0);
671 gtk_widget_show (dialog->db_chooser);
672
673 dialog->strat_chooser = gdict_strategy_chooser_new ();
674 gtk_box_pack_start (GTK_BOX (dialog->strat_vbox), dialog->strat_chooser, TRUE, TRUE, 0);
675 gtk_widget_show (dialog->strat_chooser);
676 }
677
678 GtkWidget *
gdict_source_dialog_new(GtkWindow * parent,const gchar * title,GdictSourceDialogAction action,GdictSourceLoader * loader,const gchar * source_name)679 gdict_source_dialog_new (GtkWindow *parent,
680 const gchar *title,
681 GdictSourceDialogAction action,
682 GdictSourceLoader *loader,
683 const gchar *source_name)
684 {
685 GtkWidget *retval;
686
687 g_return_val_if_fail ((parent == NULL || GTK_IS_WINDOW (parent)), NULL);
688 g_return_val_if_fail (GDICT_IS_SOURCE_LOADER (loader), NULL);
689
690 retval = g_object_new (GDICT_TYPE_SOURCE_DIALOG,
691 "source-loader", loader,
692 "source-name", source_name,
693 "action", action,
694 "title", title,
695 NULL);
696
697 if (parent)
698 {
699 gtk_window_set_transient_for (GTK_WINDOW (retval), parent);
700 gtk_window_set_destroy_with_parent (GTK_WINDOW (retval), TRUE);
701 gtk_window_set_screen (GTK_WINDOW (retval),
702 gtk_widget_get_screen (GTK_WIDGET (parent)));
703 }
704
705 return retval;
706 }
707