]> Pileus Git - ~andy/gtk/blob - gtk/gtkmountoperation.c
Clean up unneeded includes
[~andy/gtk] / gtk / gtkmountoperation.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* GTK - The GIMP Toolkit
3  * Copyright (C) Christian Kellner <gicmo@gnome.org>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 /*
22  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
23  * file for a list of people on the GTK+ Team.  See the ChangeLog
24  * files for a list of changes.  These files are distributed with
25  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
26  */
27
28 #include "config.h"
29
30 #include <string.h>
31
32 #include "gtkmountoperationprivate.h"
33 #include "gtkbox.h"
34 #include "gtkentry.h"
35 #include "gtkhbox.h"
36 #include "gtkintl.h"
37 #include "gtklabel.h"
38 #include "gtkbox.h"
39 #include "gtkmessagedialog.h"
40 #include "gtkmountoperation.h"
41 #include "gtkprivate.h"
42 #include "gtkradiobutton.h"
43 #include "gtkstock.h"
44 #include "gtkgrid.h"
45 #include "gtkwindow.h"
46 #include "gtktreeview.h"
47 #include "gtktreeselection.h"
48 #include "gtkcellrenderertext.h"
49 #include "gtkcellrendererpixbuf.h"
50 #include "gtkscrolledwindow.h"
51 #include "gtkicontheme.h"
52 #include "gtkimagemenuitem.h"
53 #include "gtkmain.h"
54
55 /**
56  * SECTION:filesystem
57  * @short_description: Functions for working with GIO
58  * @Title: Filesystem utilities
59  *
60  * The functions and objects described here make working with GTK+ and
61  * GIO more convenient.
62  *
63  * #GtkMountOperation is needed when mounting volumes:
64  * It is an implementation of #GMountOperation that can be used with
65  * GIO functions for mounting volumes such as
66  * g_file_mount_enclosing_volume(), g_file_mount_mountable(),
67  * g_volume_mount(), g_mount_unmount_with_operation() and others.
68  *
69  * When necessary, #GtkMountOperation shows dialogs to ask for
70  * passwords, questions or show processes blocking unmount.
71  *
72  * gtk_show_uri() is a convenient way to launch applications for URIs.
73  *
74  * Another object that is worth mentioning in this context is
75  * #GdkAppLaunchContext, which provides visual feedback when lauching
76  * applications.
77  */
78
79 static void   gtk_mount_operation_finalize     (GObject          *object);
80 static void   gtk_mount_operation_set_property (GObject          *object,
81                                                 guint             prop_id,
82                                                 const GValue     *value,
83                                                 GParamSpec       *pspec);
84 static void   gtk_mount_operation_get_property (GObject          *object,
85                                                 guint             prop_id,
86                                                 GValue           *value,
87                                                 GParamSpec       *pspec);
88
89 static void   gtk_mount_operation_ask_password (GMountOperation *op,
90                                                 const char      *message,
91                                                 const char      *default_user,
92                                                 const char      *default_domain,
93                                                 GAskPasswordFlags flags);
94
95 static void   gtk_mount_operation_ask_question (GMountOperation *op,
96                                                 const char      *message,
97                                                 const char      *choices[]);
98
99 static void   gtk_mount_operation_show_processes (GMountOperation *op,
100                                                   const char      *message,
101                                                   GArray          *processes,
102                                                   const char      *choices[]);
103
104 static void   gtk_mount_operation_aborted      (GMountOperation *op);
105
106 G_DEFINE_TYPE (GtkMountOperation, gtk_mount_operation, G_TYPE_MOUNT_OPERATION);
107
108 enum {
109   PROP_0,
110   PROP_PARENT,
111   PROP_IS_SHOWING,
112   PROP_SCREEN
113
114 };
115
116 struct _GtkMountOperationPrivate {
117   GtkWindow *parent_window;
118   GtkDialog *dialog;
119   GdkScreen *screen;
120
121   /* for the ask-password dialog */
122   GtkWidget *entry_container;
123   GtkWidget *username_entry;
124   GtkWidget *domain_entry;
125   GtkWidget *password_entry;
126   GtkWidget *anonymous_toggle;
127
128   GAskPasswordFlags ask_flags;
129   GPasswordSave     password_save;
130   gboolean          anonymous;
131
132   /* for the show-processes dialog */
133   GtkWidget *process_tree_view;
134   GtkListStore *process_list_store;
135 };
136
137 static void
138 gtk_mount_operation_class_init (GtkMountOperationClass *klass)
139 {
140   GObjectClass         *object_class = G_OBJECT_CLASS (klass);
141   GMountOperationClass *mount_op_class = G_MOUNT_OPERATION_CLASS (klass);
142
143   g_type_class_add_private (klass, sizeof (GtkMountOperationPrivate));
144
145   object_class->finalize     = gtk_mount_operation_finalize;
146   object_class->get_property = gtk_mount_operation_get_property;
147   object_class->set_property = gtk_mount_operation_set_property;
148
149   mount_op_class->ask_password = gtk_mount_operation_ask_password;
150   mount_op_class->ask_question = gtk_mount_operation_ask_question;
151   mount_op_class->show_processes = gtk_mount_operation_show_processes;
152   mount_op_class->aborted = gtk_mount_operation_aborted;
153
154   g_object_class_install_property (object_class,
155                                    PROP_PARENT,
156                                    g_param_spec_object ("parent",
157                                                         P_("Parent"),
158                                                         P_("The parent window"),
159                                                         GTK_TYPE_WINDOW,
160                                                         GTK_PARAM_READWRITE));
161
162   g_object_class_install_property (object_class,
163                                    PROP_IS_SHOWING,
164                                    g_param_spec_boolean ("is-showing",
165                                                          P_("Is Showing"),
166                                                          P_("Are we showing a dialog"),
167                                                          FALSE,
168                                                          GTK_PARAM_READABLE));
169
170   g_object_class_install_property (object_class,
171                                    PROP_SCREEN,
172                                    g_param_spec_object ("screen",
173                                                         P_("Screen"),
174                                                         P_("The screen where this window will be displayed."),
175                                                         GDK_TYPE_SCREEN,
176                                                         GTK_PARAM_READWRITE));
177 }
178
179 static void
180 gtk_mount_operation_init (GtkMountOperation *operation)
181 {
182   operation->priv = G_TYPE_INSTANCE_GET_PRIVATE (operation,
183                                                  GTK_TYPE_MOUNT_OPERATION,
184                                                  GtkMountOperationPrivate);
185 }
186
187 static void
188 gtk_mount_operation_finalize (GObject *object)
189 {
190   GtkMountOperation *operation = GTK_MOUNT_OPERATION (object);
191   GtkMountOperationPrivate *priv = operation->priv;
192
193   if (priv->parent_window)
194     g_object_unref (priv->parent_window);
195
196   if (priv->screen)
197     g_object_unref (priv->screen);
198
199   G_OBJECT_CLASS (gtk_mount_operation_parent_class)->finalize (object);
200 }
201
202 static void
203 gtk_mount_operation_set_property (GObject      *object,
204                                   guint         prop_id,
205                                   const GValue *value,
206                                   GParamSpec   *pspec)
207 {
208   GtkMountOperation *operation = GTK_MOUNT_OPERATION (object);
209
210   switch (prop_id)
211     {
212     case PROP_PARENT:
213       gtk_mount_operation_set_parent (operation, g_value_get_object (value));
214       break;
215
216     case PROP_SCREEN:
217       gtk_mount_operation_set_screen (operation, g_value_get_object (value));
218       break;
219
220     case PROP_IS_SHOWING:
221     default:
222       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
223       break;
224     }
225 }
226
227 static void
228 gtk_mount_operation_get_property (GObject    *object,
229                                   guint       prop_id,
230                                   GValue     *value,
231                                   GParamSpec *pspec)
232 {
233   GtkMountOperation *operation = GTK_MOUNT_OPERATION (object);
234   GtkMountOperationPrivate *priv = operation->priv;
235
236   switch (prop_id)
237     {
238     case PROP_PARENT:
239       g_value_set_object (value, priv->parent_window);
240       break;
241
242     case PROP_IS_SHOWING:
243       g_value_set_boolean (value, priv->dialog != NULL);
244       break;
245
246     case PROP_SCREEN:
247       g_value_set_object (value, priv->screen);
248       break;
249
250     default:
251       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
252       break;
253     }
254 }
255
256 static void
257 remember_button_toggled (GtkToggleButton   *button,
258                          GtkMountOperation *operation)
259 {
260   GtkMountOperationPrivate *priv = operation->priv;
261
262   if (gtk_toggle_button_get_active (button))
263     {
264       gpointer data;
265
266       data = g_object_get_data (G_OBJECT (button), "password-save");
267       priv->password_save = GPOINTER_TO_INT (data);
268     }
269 }
270
271 static void
272 pw_dialog_got_response (GtkDialog         *dialog,
273                         gint               response_id,
274                         GtkMountOperation *mount_op)
275 {
276   GtkMountOperationPrivate *priv = mount_op->priv;
277   GMountOperation *op = G_MOUNT_OPERATION (mount_op);
278
279   if (response_id == GTK_RESPONSE_OK)
280     {
281       const char *text;
282
283       if (priv->ask_flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED)
284         g_mount_operation_set_anonymous (op, priv->anonymous);
285
286       if (priv->username_entry)
287         {
288           text = gtk_entry_get_text (GTK_ENTRY (priv->username_entry));
289           g_mount_operation_set_username (op, text);
290         }
291
292       if (priv->domain_entry)
293         {
294           text = gtk_entry_get_text (GTK_ENTRY (priv->domain_entry));
295           g_mount_operation_set_domain (op, text);
296         }
297
298       if (priv->password_entry)
299         {
300           text = gtk_entry_get_text (GTK_ENTRY (priv->password_entry));
301           g_mount_operation_set_password (op, text);
302         }
303
304       if (priv->ask_flags & G_ASK_PASSWORD_SAVING_SUPPORTED)
305         g_mount_operation_set_password_save (op, priv->password_save);
306
307       g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED);
308     }
309   else
310     g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED);
311
312   priv->dialog = NULL;
313   g_object_notify (G_OBJECT (op), "is-showing");
314   gtk_widget_destroy (GTK_WIDGET (dialog));
315   g_object_unref (op);
316 }
317
318 static gboolean
319 entry_has_input (GtkWidget *entry_widget)
320 {
321   const char *text;
322
323   if (entry_widget == NULL)
324     return TRUE;
325
326   text = gtk_entry_get_text (GTK_ENTRY (entry_widget));
327
328   return text != NULL && text[0] != '\0';
329 }
330
331 static gboolean
332 pw_dialog_input_is_valid (GtkMountOperation *operation)
333 {
334   GtkMountOperationPrivate *priv = operation->priv;
335   gboolean is_valid = TRUE;
336
337   /* We don't require password to be non-empty here
338    * since there are situations where it is not needed,
339    * see bug 578365.
340    * We may add a way for the backend to specify that it
341    * definitively needs a password.
342    */
343   is_valid = entry_has_input (priv->username_entry) &&
344              entry_has_input (priv->domain_entry);
345
346   return is_valid;
347 }
348
349 static void
350 pw_dialog_verify_input (GtkEditable       *editable,
351                         GtkMountOperation *operation)
352 {
353   GtkMountOperationPrivate *priv = operation->priv;
354   gboolean is_valid;
355
356   is_valid = pw_dialog_input_is_valid (operation);
357   gtk_dialog_set_response_sensitive (GTK_DIALOG (priv->dialog),
358                                      GTK_RESPONSE_OK,
359                                      is_valid);
360 }
361
362 static void
363 pw_dialog_anonymous_toggled (GtkWidget         *widget,
364                              GtkMountOperation *operation)
365 {
366   GtkMountOperationPrivate *priv = operation->priv;
367   gboolean is_valid;
368
369   priv->anonymous = widget == priv->anonymous_toggle;
370
371   if (priv->anonymous)
372     is_valid = TRUE;
373   else
374     is_valid = pw_dialog_input_is_valid (operation);
375
376   gtk_widget_set_sensitive (priv->entry_container, priv->anonymous == FALSE);
377   gtk_dialog_set_response_sensitive (GTK_DIALOG (priv->dialog),
378                                      GTK_RESPONSE_OK,
379                                      is_valid);
380 }
381
382
383 static void
384 pw_dialog_cycle_focus (GtkWidget         *widget,
385                        GtkMountOperation *operation)
386 {
387   GtkMountOperationPrivate *priv;
388   GtkWidget *next_widget = NULL;
389
390   priv = operation->priv;
391
392   if (widget == priv->username_entry)
393     {
394       if (priv->domain_entry != NULL)
395         next_widget = priv->domain_entry;
396       else if (priv->password_entry != NULL)
397         next_widget = priv->password_entry;
398     }
399   else if (widget == priv->domain_entry && priv->password_entry)
400     next_widget = priv->password_entry;
401
402   if (next_widget)
403     gtk_widget_grab_focus (next_widget);
404   else if (pw_dialog_input_is_valid (operation))
405     gtk_window_activate_default (GTK_WINDOW (priv->dialog));
406 }
407
408 static GtkWidget *
409 table_add_entry (GtkWidget  *table,
410                  int         row,
411                  const char *label_text,
412                  const char *value,
413                  gpointer    user_data)
414 {
415   GtkWidget *entry;
416   GtkWidget *label;
417
418   label = gtk_label_new_with_mnemonic (label_text);
419   gtk_widget_set_halign (label, GTK_ALIGN_START);
420   gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
421   gtk_widget_set_hexpand (label, TRUE);
422
423   entry = gtk_entry_new ();
424
425   if (value)
426     gtk_entry_set_text (GTK_ENTRY (entry), value);
427
428   gtk_grid_attach (GTK_GRID (table), label, 0, row, 1, 1);
429   gtk_grid_attach (GTK_GRID (table), entry, 1, row, 1, 1);
430   gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
431
432   g_signal_connect (entry, "changed",
433                     G_CALLBACK (pw_dialog_verify_input), user_data);
434
435   g_signal_connect (entry, "activate",
436                     G_CALLBACK (pw_dialog_cycle_focus), user_data);
437
438   return entry;
439 }
440
441 static void
442 gtk_mount_operation_ask_password (GMountOperation   *mount_op,
443                                   const char        *message,
444                                   const char        *default_user,
445                                   const char        *default_domain,
446                                   GAskPasswordFlags  flags)
447 {
448   GtkMountOperation *operation;
449   GtkMountOperationPrivate *priv;
450   GtkWidget *widget;
451   GtkDialog *dialog;
452   GtkWindow *window;
453   GtkWidget *hbox, *main_vbox, *vbox, *icon;
454   GtkWidget *table;
455   GtkWidget *message_label;
456   GtkWidget *content_area, *action_area;
457   gboolean   can_anonymous;
458   guint      rows;
459   const gchar *secondary;
460
461   operation = GTK_MOUNT_OPERATION (mount_op);
462   priv = operation->priv;
463
464   priv->ask_flags = flags;
465
466   widget = gtk_dialog_new ();
467   dialog = GTK_DIALOG (widget);
468   window = GTK_WINDOW (widget);
469
470   priv->dialog = dialog;
471
472   content_area = gtk_dialog_get_content_area (dialog);
473   action_area = gtk_dialog_get_action_area (dialog);
474
475   /* Set the dialog up with HIG properties */
476   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
477   gtk_box_set_spacing (GTK_BOX (content_area), 2); /* 2 * 5 + 2 = 12 */
478   gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
479   gtk_box_set_spacing (GTK_BOX (action_area), 6);
480
481   gtk_window_set_resizable (window, FALSE);
482   gtk_window_set_title (window, "");
483   gtk_window_set_icon_name (window, GTK_STOCK_DIALOG_AUTHENTICATION);
484
485   gtk_dialog_add_buttons (dialog,
486                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
487                           _("Co_nnect"), GTK_RESPONSE_OK,
488                           NULL);
489   gtk_dialog_set_default_response (dialog, GTK_RESPONSE_OK);
490
491   gtk_dialog_set_alternative_button_order (dialog,
492                                            GTK_RESPONSE_OK,
493                                            GTK_RESPONSE_CANCEL,
494                                            -1);
495
496   /* Build contents */
497   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
498   gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
499   gtk_box_pack_start (GTK_BOX (content_area), hbox, TRUE, TRUE, 0);
500
501   icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
502                                    GTK_ICON_SIZE_DIALOG);
503
504   gtk_widget_set_halign (icon, GTK_ALIGN_CENTER);
505   gtk_widget_set_valign (icon, GTK_ALIGN_START);
506   gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
507
508   main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 18);
509   gtk_box_pack_start (GTK_BOX (hbox), main_vbox, TRUE, TRUE, 0);
510
511   secondary = strstr (message, "\n");
512   if (secondary != NULL)
513     {
514       gchar *s;
515       gchar *primary;
516
517       primary = g_strndup (message, secondary - message + 1);
518       s = g_strdup_printf ("<big><b>%s</b></big>%s", primary, secondary);
519
520       message_label = gtk_label_new (NULL);
521       gtk_label_set_markup (GTK_LABEL (message_label), s);
522       gtk_widget_set_halign (message_label, GTK_ALIGN_START);
523       gtk_widget_set_valign (message_label, GTK_ALIGN_CENTER);
524       gtk_label_set_line_wrap (GTK_LABEL (message_label), TRUE);
525       gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (message_label),
526                           FALSE, TRUE, 0);
527
528       g_free (s);
529       g_free (primary);
530     }
531   else
532     {
533       message_label = gtk_label_new (message);
534       gtk_widget_set_halign (message_label, GTK_ALIGN_START);
535       gtk_widget_set_valign (message_label, GTK_ALIGN_CENTER);
536       gtk_label_set_line_wrap (GTK_LABEL (message_label), TRUE);
537       gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (message_label),
538                           FALSE, FALSE, 0);
539     }
540
541   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
542   gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
543
544   can_anonymous = flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED;
545
546   priv->anonymous_toggle = NULL;
547   if (can_anonymous)
548     {
549       GtkWidget *anon_box;
550       GtkWidget *choice;
551       GSList    *group;
552
553       anon_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
554       gtk_box_pack_start (GTK_BOX (vbox), anon_box,
555                           FALSE, FALSE, 0);
556
557       choice = gtk_radio_button_new_with_mnemonic (NULL, _("Connect _anonymously"));
558       gtk_box_pack_start (GTK_BOX (anon_box),
559                           choice,
560                           FALSE, FALSE, 0);
561       g_signal_connect (choice, "toggled",
562                         G_CALLBACK (pw_dialog_anonymous_toggled), operation);
563       priv->anonymous_toggle = choice;
564
565       group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (choice));
566       choice = gtk_radio_button_new_with_mnemonic (group, _("Connect as u_ser:"));
567       gtk_box_pack_start (GTK_BOX (anon_box),
568                           choice,
569                           FALSE, FALSE, 0);
570       g_signal_connect (choice, "toggled",
571                         G_CALLBACK (pw_dialog_anonymous_toggled), operation);
572     }
573
574   rows = 0;
575
576   if (flags & G_ASK_PASSWORD_NEED_PASSWORD)
577     rows++;
578
579   if (flags & G_ASK_PASSWORD_NEED_USERNAME)
580     rows++;
581
582   if (flags &G_ASK_PASSWORD_NEED_DOMAIN)
583     rows++;
584
585   /* The table that holds the entries */
586   table = gtk_grid_new ();
587   gtk_grid_set_row_spacing (GTK_GRID (table), 6);
588   gtk_grid_set_column_spacing (GTK_GRID (table), 6);
589
590   if (can_anonymous)
591     gtk_widget_set_margin_left (table, 12);
592
593   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
594   priv->entry_container = table;
595
596   rows = 0;
597
598   priv->username_entry = NULL;
599   if (flags & G_ASK_PASSWORD_NEED_USERNAME)
600     priv->username_entry = table_add_entry (table, rows++, _("_Username:"),
601                                             default_user, operation);
602
603   priv->domain_entry = NULL;
604   if (flags & G_ASK_PASSWORD_NEED_DOMAIN)
605     priv->domain_entry = table_add_entry (table, rows++, _("_Domain:"),
606                                           default_domain, operation);
607
608   priv->password_entry = NULL;
609   if (flags & G_ASK_PASSWORD_NEED_PASSWORD)
610     {
611       priv->password_entry = table_add_entry (table, rows++, _("_Password:"),
612                                               NULL, operation);
613       gtk_entry_set_visibility (GTK_ENTRY (priv->password_entry), FALSE);
614     }
615
616    if (flags & G_ASK_PASSWORD_SAVING_SUPPORTED)
617     {
618       GtkWidget    *choice;
619       GtkWidget    *remember_box;
620       GSList       *group;
621       GPasswordSave password_save;
622
623       remember_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
624       gtk_box_pack_start (GTK_BOX (vbox), remember_box,
625                           FALSE, FALSE, 0);
626
627       password_save = g_mount_operation_get_password_save (mount_op);
628       
629       choice = gtk_radio_button_new_with_mnemonic (NULL, _("Forget password _immediately"));
630       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (choice),
631                                     password_save == G_PASSWORD_SAVE_NEVER);
632       g_object_set_data (G_OBJECT (choice), "password-save",
633                          GINT_TO_POINTER (G_PASSWORD_SAVE_NEVER));
634       g_signal_connect (choice, "toggled",
635                         G_CALLBACK (remember_button_toggled), operation);
636       gtk_box_pack_start (GTK_BOX (remember_box), choice, FALSE, FALSE, 0);
637
638       group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (choice));
639       choice = gtk_radio_button_new_with_mnemonic (group, _("Remember password until you _logout"));
640       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (choice),
641                                     password_save == G_PASSWORD_SAVE_FOR_SESSION);
642       g_object_set_data (G_OBJECT (choice), "password-save",
643                          GINT_TO_POINTER (G_PASSWORD_SAVE_FOR_SESSION));
644       g_signal_connect (choice, "toggled",
645                         G_CALLBACK (remember_button_toggled), operation);
646       gtk_box_pack_start (GTK_BOX (remember_box), choice, FALSE, FALSE, 0);
647
648       group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (choice));
649       choice = gtk_radio_button_new_with_mnemonic (group, _("Remember _forever"));
650       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (choice),
651                                     password_save == G_PASSWORD_SAVE_PERMANENTLY);
652       g_object_set_data (G_OBJECT (choice), "password-save",
653                          GINT_TO_POINTER (G_PASSWORD_SAVE_PERMANENTLY));
654       g_signal_connect (choice, "toggled",
655                         G_CALLBACK (remember_button_toggled), operation);
656       gtk_box_pack_start (GTK_BOX (remember_box), choice, FALSE, FALSE, 0);
657     }
658
659   g_signal_connect (G_OBJECT (dialog), "response",
660                     G_CALLBACK (pw_dialog_got_response), operation);
661
662   if (can_anonymous)
663     {
664       /* The anonymous option will be active by default,
665        * ensure the toggled signal is emitted for it.
666        */
667       gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (priv->anonymous_toggle));
668     }
669   else if (! pw_dialog_input_is_valid (operation))
670     gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_OK, FALSE);
671
672   g_object_notify (G_OBJECT (operation), "is-showing");
673
674   if (priv->parent_window)
675     {
676       gtk_window_set_transient_for (window, priv->parent_window);
677       gtk_window_set_modal (window, TRUE);
678     }
679   else if (priv->screen)
680     gtk_window_set_screen (GTK_WINDOW (dialog), priv->screen);
681
682   gtk_widget_show_all (GTK_WIDGET (dialog));
683
684   g_object_ref (operation);
685 }
686
687 static void
688 question_dialog_button_clicked (GtkDialog       *dialog,
689                                 gint             button_number,
690                                 GMountOperation *op)
691 {
692   GtkMountOperationPrivate *priv;
693   GtkMountOperation *operation;
694
695   operation = GTK_MOUNT_OPERATION (op);
696   priv = operation->priv;
697
698   if (button_number >= 0)
699     {
700       g_mount_operation_set_choice (op, button_number);
701       g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED);
702     }
703   else
704     g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED);
705
706   priv->dialog = NULL;
707   g_object_notify (G_OBJECT (operation), "is-showing");
708   gtk_widget_destroy (GTK_WIDGET (dialog));
709   g_object_unref (op);
710 }
711
712 static void
713 gtk_mount_operation_ask_question (GMountOperation *op,
714                                   const char      *message,
715                                   const char      *choices[])
716 {
717   GtkMountOperationPrivate *priv;
718   GtkWidget  *dialog;
719   const char *secondary = NULL;
720   char       *primary;
721   int        count, len = 0;
722
723   g_return_if_fail (GTK_IS_MOUNT_OPERATION (op));
724   g_return_if_fail (message != NULL);
725   g_return_if_fail (choices != NULL);
726
727   priv = GTK_MOUNT_OPERATION (op)->priv;
728
729   primary = strstr (message, "\n");
730   if (primary)
731     {
732       secondary = primary + 1;
733       primary = g_strndup (message, primary - message);
734     }
735
736   dialog = gtk_message_dialog_new (priv->parent_window, 0,
737                                    GTK_MESSAGE_QUESTION,
738                                    GTK_BUTTONS_NONE, "%s",
739                                    primary != NULL ? primary : message);
740   g_free (primary);
741
742   if (secondary)
743     gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
744                                               "%s", secondary);
745
746   /* First count the items in the list then
747    * add the buttons in reverse order */
748
749   while (choices[len] != NULL)
750     len++;
751
752   for (count = len - 1; count >= 0; count--)
753     gtk_dialog_add_button (GTK_DIALOG (dialog), choices[count], count);
754
755   g_signal_connect (G_OBJECT (dialog), "response",
756                     G_CALLBACK (question_dialog_button_clicked), op);
757
758   priv->dialog = GTK_DIALOG (dialog);
759   g_object_notify (G_OBJECT (op), "is-showing");
760
761   if (priv->parent_window == NULL && priv->screen)
762     gtk_window_set_screen (GTK_WINDOW (dialog), priv->screen);
763
764   gtk_widget_show (dialog);
765   g_object_ref (op);
766 }
767
768 static void
769 show_processes_button_clicked (GtkDialog       *dialog,
770                                gint             button_number,
771                                GMountOperation *op)
772 {
773   GtkMountOperationPrivate *priv;
774   GtkMountOperation *operation;
775
776   operation = GTK_MOUNT_OPERATION (op);
777   priv = operation->priv;
778
779   if (button_number >= 0)
780     {
781       g_mount_operation_set_choice (op, button_number);
782       g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED);
783     }
784   else
785     g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED);
786
787   priv->dialog = NULL;
788   g_object_notify (G_OBJECT (operation), "is-showing");
789   gtk_widget_destroy (GTK_WIDGET (dialog));
790   g_object_unref (op);
791 }
792
793 static gint
794 pid_equal (gconstpointer a,
795            gconstpointer b)
796 {
797   GPid pa, pb;
798
799   pa = *((GPid *) a);
800   pb = *((GPid *) b);
801
802   return GPOINTER_TO_INT(pb) - GPOINTER_TO_INT(pa);
803 }
804
805 static void
806 diff_sorted_arrays (GArray         *array1,
807                     GArray         *array2,
808                     GCompareFunc   compare,
809                     GArray         *added_indices,
810                     GArray         *removed_indices)
811 {
812   gint order;
813   guint n1, n2;
814   guint elem_size;
815
816   n1 = n2 = 0;
817
818   elem_size = g_array_get_element_size (array1);
819   g_assert (elem_size == g_array_get_element_size (array2));
820
821   while (n1 < array1->len && n2 < array2->len)
822     {
823       order = (*compare) (((const char*) array1->data) + n1 * elem_size,
824                           ((const char*) array2->data) + n2 * elem_size);
825       if (order < 0)
826         {
827           g_array_append_val (removed_indices, n1);
828           n1++;
829         }
830       else if (order > 0)
831         {
832           g_array_append_val (added_indices, n2);
833           n2++;
834         }
835       else
836         { /* same item */
837           n1++;
838           n2++;
839         }
840     }
841
842   while (n1 < array1->len)
843     {
844       g_array_append_val (removed_indices, n1);
845       n1++;
846     }
847   while (n2 < array2->len)
848     {
849       g_array_append_val (added_indices, n2);
850       n2++;
851     }
852 }
853
854
855 static void
856 add_pid_to_process_list_store (GtkMountOperation              *mount_operation,
857                                GtkMountOperationLookupContext *lookup_context,
858                                GtkListStore                   *list_store,
859                                GPid                            pid)
860 {
861   gchar *command_line;
862   gchar *name;
863   GdkPixbuf *pixbuf;
864   gchar *markup;
865   GtkTreeIter iter;
866
867   name = NULL;
868   pixbuf = NULL;
869   command_line = NULL;
870   _gtk_mount_operation_lookup_info (lookup_context,
871                                     pid,
872                                     24,
873                                     &name,
874                                     &command_line,
875                                     &pixbuf);
876
877   if (name == NULL)
878     name = g_strdup_printf (_("Unknown Application (PID %d)"), pid);
879
880   if (command_line == NULL)
881     command_line = g_strdup ("");
882
883   if (pixbuf == NULL)
884     {
885       GtkIconTheme *theme;
886       theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (mount_operation->priv->dialog)));
887       pixbuf = gtk_icon_theme_load_icon (theme,
888                                          "application-x-executable",
889                                          24,
890                                          0,
891                                          NULL);
892     }
893
894   markup = g_strdup_printf ("<b>%s</b>\n"
895                             "<small>%s</small>",
896                             name,
897                             command_line);
898
899   gtk_list_store_append (list_store, &iter);
900   gtk_list_store_set (list_store, &iter,
901                       0, pixbuf,
902                       1, markup,
903                       2, pid,
904                       -1);
905
906   if (pixbuf != NULL)
907     g_object_unref (pixbuf);
908   g_free (markup);
909   g_free (name);
910   g_free (command_line);
911 }
912
913 static void
914 remove_pid_from_process_list_store (GtkMountOperation *mount_operation,
915                                     GtkListStore      *list_store,
916                                     GPid               pid)
917 {
918   GtkTreeIter iter;
919   GPid pid_of_item;
920
921   if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
922     {
923       do
924         {
925           gtk_tree_model_get (GTK_TREE_MODEL (list_store),
926                               &iter,
927                               2, &pid_of_item,
928                               -1);
929
930           if (pid_of_item == pid)
931             {
932               gtk_list_store_remove (list_store, &iter);
933               break;
934             }
935         }
936       while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store), &iter));
937     }
938 }
939
940
941 static void
942 update_process_list_store (GtkMountOperation *mount_operation,
943                            GtkListStore      *list_store,
944                            GArray            *processes)
945 {
946   guint n;
947   GtkMountOperationLookupContext *lookup_context;
948   GArray *current_pids;
949   GArray *pid_indices_to_add;
950   GArray *pid_indices_to_remove;
951   GtkTreeIter iter;
952   GPid pid;
953
954   /* Just removing all items and adding new ones will screw up the
955    * focus handling in the treeview - so compute the delta, and add/remove
956    * items as appropriate
957    */
958   current_pids = g_array_new (FALSE, FALSE, sizeof (GPid));
959   pid_indices_to_add = g_array_new (FALSE, FALSE, sizeof (gint));
960   pid_indices_to_remove = g_array_new (FALSE, FALSE, sizeof (gint));
961
962   if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
963     {
964       do
965         {
966           gtk_tree_model_get (GTK_TREE_MODEL (list_store),
967                               &iter,
968                               2, &pid,
969                               -1);
970
971           g_array_append_val (current_pids, pid);
972         }
973       while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store), &iter));
974     }
975
976   g_array_sort (current_pids, pid_equal);
977   g_array_sort (processes, pid_equal);
978
979   diff_sorted_arrays (current_pids, processes, pid_equal, pid_indices_to_add, pid_indices_to_remove);
980
981   for (n = 0; n < pid_indices_to_remove->len; n++)
982     {
983       pid = g_array_index (current_pids, GPid, n);
984       remove_pid_from_process_list_store (mount_operation, list_store, pid);
985     }
986
987   if (pid_indices_to_add->len > 0)
988     {
989       lookup_context = _gtk_mount_operation_lookup_context_get (gtk_widget_get_display (mount_operation->priv->process_tree_view));
990       for (n = 0; n < pid_indices_to_add->len; n++)
991         {
992           pid = g_array_index (processes, GPid, n);
993           add_pid_to_process_list_store (mount_operation, lookup_context, list_store, pid);
994         }
995       _gtk_mount_operation_lookup_context_free (lookup_context);
996     }
997
998   /* select the first item, if we went from a zero to a non-zero amount of processes */
999   if (current_pids->len == 0 && pid_indices_to_add->len > 0)
1000     {
1001       if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
1002         {
1003           GtkTreeSelection *tree_selection;
1004           tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (mount_operation->priv->process_tree_view));
1005           gtk_tree_selection_select_iter (tree_selection, &iter);
1006         }
1007     }
1008
1009   g_array_unref (current_pids);
1010   g_array_unref (pid_indices_to_add);
1011   g_array_unref (pid_indices_to_remove);
1012 }
1013
1014 static void
1015 on_end_process_activated (GtkMenuItem *item,
1016                           gpointer user_data)
1017 {
1018   GtkMountOperation *op = GTK_MOUNT_OPERATION (user_data);
1019   GtkTreeSelection *selection;
1020   GtkTreeIter iter;
1021   GPid pid_to_kill;
1022   GError *error;
1023
1024   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (op->priv->process_tree_view));
1025
1026   if (!gtk_tree_selection_get_selected (selection,
1027                                         NULL,
1028                                         &iter))
1029     goto out;
1030
1031   gtk_tree_model_get (GTK_TREE_MODEL (op->priv->process_list_store),
1032                       &iter,
1033                       2, &pid_to_kill,
1034                       -1);
1035
1036   /* TODO: We might want to either
1037    *
1038    *       - Be smart about things and send SIGKILL rather than SIGTERM if
1039    *         this is the second time the user requests killing a process
1040    *
1041    *       - Or, easier (but worse user experience), offer both "End Process"
1042    *         and "Terminate Process" options
1043    *
1044    *      But that's not how things work right now....
1045    */
1046   error = NULL;
1047   if (!_gtk_mount_operation_kill_process (pid_to_kill, &error))
1048     {
1049       GtkWidget *dialog;
1050       gint response;
1051
1052       /* Use GTK_DIALOG_DESTROY_WITH_PARENT here since the parent dialog can be
1053        * indeed be destroyed via the GMountOperation::abort signal... for example,
1054        * this is triggered if the user yanks the device while we are showing
1055        * the dialog...
1056        */
1057       dialog = gtk_message_dialog_new (GTK_WINDOW (op->priv->dialog),
1058                                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1059                                        GTK_MESSAGE_ERROR,
1060                                        GTK_BUTTONS_CLOSE,
1061                                        _("Unable to end process"));
1062       gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
1063                                                 "%s",
1064                                                 error->message);
1065
1066       gtk_widget_show_all (dialog);
1067       response = gtk_dialog_run (GTK_DIALOG (dialog));
1068
1069       /* GTK_RESPONSE_NONE means the dialog were programmatically destroy, e.g. that
1070        * GTK_DIALOG_DESTROY_WITH_PARENT kicked in - so it would trigger a warning to
1071        * destroy the dialog in that case
1072        */
1073       if (response != GTK_RESPONSE_NONE)
1074         gtk_widget_destroy (dialog);
1075
1076       g_error_free (error);
1077     }
1078
1079  out:
1080   ;
1081 }
1082
1083 static gboolean
1084 do_popup_menu_for_process_tree_view (GtkWidget         *widget,
1085                                      GdkEventButton    *event,
1086                                      GtkMountOperation *op)
1087 {
1088   GtkWidget *menu;
1089   GtkWidget *item;
1090   gint button;
1091   gint event_time;
1092   gboolean popped_up_menu;
1093
1094   popped_up_menu = FALSE;
1095
1096   menu = gtk_menu_new ();
1097
1098   item = gtk_image_menu_item_new_with_mnemonic (_("_End Process"));
1099   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
1100                                  gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU));
1101   g_signal_connect (item, "activate",
1102                     G_CALLBACK (on_end_process_activated),
1103                     op);
1104   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1105   gtk_widget_show_all (menu);
1106
1107   if (event != NULL)
1108     {
1109       GtkTreePath *path;
1110       GtkTreeSelection *selection;
1111
1112       if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (op->priv->process_tree_view),
1113                                          (gint) event->x,
1114                                          (gint) event->y,
1115                                          &path,
1116                                          NULL,
1117                                          NULL,
1118                                          NULL))
1119         {
1120           selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (op->priv->process_tree_view));
1121           gtk_tree_selection_select_path (selection, path);
1122           gtk_tree_path_free (path);
1123         }
1124       else
1125         {
1126           /* don't popup a menu if the user right-clicked in an area with no rows */
1127           goto out;
1128         }
1129
1130       button = event->button;
1131       event_time = event->time;
1132     }
1133   else
1134     {
1135       button = 0;
1136       event_time = gtk_get_current_event_time ();
1137     }
1138
1139   gtk_menu_popup (GTK_MENU (menu),
1140                   NULL,
1141                   widget,
1142                   NULL,
1143                   NULL,
1144                   button,
1145                   event_time);
1146
1147   popped_up_menu = TRUE;
1148
1149  out:
1150   return popped_up_menu;
1151 }
1152
1153 static gboolean
1154 on_popup_menu_for_process_tree_view (GtkWidget *widget,
1155                                      gpointer   user_data)
1156 {
1157   GtkMountOperation *op = GTK_MOUNT_OPERATION (user_data);
1158   return do_popup_menu_for_process_tree_view (widget, NULL, op);
1159 }
1160
1161 static gboolean
1162 on_button_press_event_for_process_tree_view (GtkWidget      *widget,
1163                                              GdkEventButton *event,
1164                                              gpointer        user_data)
1165 {
1166   GtkMountOperation *op = GTK_MOUNT_OPERATION (user_data);
1167   gboolean ret;
1168
1169   ret = FALSE;
1170
1171   /* Ignore double-clicks and triple-clicks */
1172   if (event->button == 3 && event->type == GDK_BUTTON_PRESS)
1173     {
1174       ret = do_popup_menu_for_process_tree_view (widget, event, op);
1175     }
1176
1177   return ret;
1178 }
1179
1180 static GtkWidget *
1181 create_show_processes_dialog (GMountOperation *op,
1182                               const char      *message,
1183                               const char      *choices[])
1184 {
1185   GtkMountOperationPrivate *priv;
1186   GtkWidget  *dialog;
1187   const char *secondary = NULL;
1188   char       *primary;
1189   int        count, len = 0;
1190   GtkWidget *label;
1191   GtkWidget *tree_view;
1192   GtkWidget *scrolled_window;
1193   GtkWidget *vbox;
1194   GtkWidget *content_area;
1195   GtkTreeViewColumn *column;
1196   GtkCellRenderer *renderer;
1197   GtkListStore *list_store;
1198   gchar *s;
1199
1200   priv = GTK_MOUNT_OPERATION (op)->priv;
1201
1202   primary = strstr (message, "\n");
1203   if (primary)
1204     {
1205       secondary = primary + 1;
1206       primary = g_strndup (message, primary - message);
1207     }
1208
1209   dialog = gtk_dialog_new ();
1210
1211   if (priv->parent_window != NULL)
1212     gtk_window_set_transient_for (GTK_WINDOW (dialog), priv->parent_window);
1213   gtk_window_set_title (GTK_WINDOW (dialog), "");
1214
1215   content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
1216   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
1217   gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
1218   gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0);
1219
1220   if (secondary != NULL)
1221     {
1222       s = g_strdup_printf ("<big><b>%s</b></big>\n\n%s", primary, secondary);
1223     }
1224   else
1225     {
1226       s = g_strdup_printf ("%s", primary);
1227     }
1228   g_free (primary);
1229   label = gtk_label_new (NULL);
1230   gtk_label_set_markup (GTK_LABEL (label), s);
1231   g_free (s);
1232   gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1233
1234   /* First count the items in the list then
1235    * add the buttons in reverse order */
1236
1237   while (choices[len] != NULL)
1238     len++;
1239
1240   for (count = len - 1; count >= 0; count--)
1241     gtk_dialog_add_button (GTK_DIALOG (dialog), choices[count], count);
1242
1243   g_signal_connect (G_OBJECT (dialog), "response",
1244                     G_CALLBACK (show_processes_button_clicked), op);
1245
1246   priv->dialog = GTK_DIALOG (dialog);
1247   g_object_notify (G_OBJECT (op), "is-showing");
1248
1249   if (priv->parent_window == NULL && priv->screen)
1250     gtk_window_set_screen (GTK_WINDOW (dialog), priv->screen);
1251
1252   tree_view = gtk_tree_view_new ();
1253   /* TODO: should use EM's when gtk+ RI patches land */
1254   gtk_widget_set_size_request (tree_view,
1255                                300,
1256                                120);
1257
1258   column = gtk_tree_view_column_new ();
1259   renderer = gtk_cell_renderer_pixbuf_new ();
1260   gtk_tree_view_column_pack_start (column, renderer, FALSE);
1261   gtk_tree_view_column_set_attributes (column, renderer,
1262                                        "pixbuf", 0,
1263                                        NULL);
1264   renderer = gtk_cell_renderer_text_new ();
1265   g_object_set (renderer,
1266                 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1267                 "ellipsize-set", TRUE,
1268                 NULL);
1269   gtk_tree_view_column_pack_start (column, renderer, TRUE);
1270   gtk_tree_view_column_set_attributes (column, renderer,
1271                                        "markup", 1,
1272                                        NULL);
1273   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
1274   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
1275
1276
1277   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1278   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
1279                                   GTK_POLICY_NEVER,
1280                                   GTK_POLICY_AUTOMATIC);
1281   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
1282
1283   gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
1284   gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
1285
1286   g_signal_connect (tree_view, "popup-menu",
1287                     G_CALLBACK (on_popup_menu_for_process_tree_view),
1288                     op);
1289   g_signal_connect (tree_view, "button-press-event",
1290                     G_CALLBACK (on_button_press_event_for_process_tree_view),
1291                     op);
1292
1293   list_store = gtk_list_store_new (3,
1294                                    GDK_TYPE_PIXBUF,
1295                                    G_TYPE_STRING,
1296                                    G_TYPE_INT);
1297
1298   gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (list_store));
1299
1300   priv->process_list_store = list_store;
1301   priv->process_tree_view = tree_view;
1302   /* set pointers to NULL when dialog goes away */
1303   g_object_add_weak_pointer (G_OBJECT (list_store), (gpointer *) &priv->process_list_store);
1304   g_object_add_weak_pointer (G_OBJECT (tree_view), (gpointer *) &priv->process_tree_view);
1305
1306   g_object_unref (list_store);
1307   g_object_ref (op);
1308
1309   return dialog;
1310 }
1311
1312 static void
1313 gtk_mount_operation_show_processes (GMountOperation *op,
1314                                     const char      *message,
1315                                     GArray          *processes,
1316                                     const char      *choices[])
1317 {
1318   GtkMountOperationPrivate *priv;
1319   GtkWidget *dialog = NULL;
1320
1321   g_return_if_fail (GTK_IS_MOUNT_OPERATION (op));
1322   g_return_if_fail (message != NULL);
1323   g_return_if_fail (processes != NULL);
1324   g_return_if_fail (choices != NULL);
1325
1326   priv = GTK_MOUNT_OPERATION (op)->priv;
1327
1328   if (priv->process_list_store == NULL)
1329     {
1330       /* need to create the dialog */
1331       dialog = create_show_processes_dialog (op, message, choices);
1332     }
1333
1334   /* otherwise, we're showing the dialog, assume messages+choices hasn't changed */
1335
1336   update_process_list_store (GTK_MOUNT_OPERATION (op),
1337                              priv->process_list_store,
1338                              processes);
1339
1340   if (dialog != NULL)
1341     {
1342       gtk_widget_show_all (dialog);
1343     }
1344 }
1345
1346 static void
1347 gtk_mount_operation_aborted (GMountOperation *op)
1348 {
1349   GtkMountOperationPrivate *priv;
1350
1351   priv = GTK_MOUNT_OPERATION (op)->priv;
1352
1353   if (priv->dialog != NULL)
1354     {
1355       gtk_widget_destroy (GTK_WIDGET (priv->dialog));
1356       priv->dialog = NULL;
1357       g_object_notify (G_OBJECT (op), "is-showing");
1358       g_object_unref (op);
1359     }
1360 }
1361
1362 /**
1363  * gtk_mount_operation_new:
1364  * @parent: (allow-none): transient parent of the window, or %NULL
1365  *
1366  * Creates a new #GtkMountOperation
1367  *
1368  * Returns: a new #GtkMountOperation
1369  *
1370  * Since: 2.14
1371  */
1372 GMountOperation *
1373 gtk_mount_operation_new (GtkWindow *parent)
1374 {
1375   GMountOperation *mount_operation;
1376
1377   mount_operation = g_object_new (GTK_TYPE_MOUNT_OPERATION,
1378                                   "parent", parent, NULL);
1379
1380   return mount_operation;
1381 }
1382
1383 /**
1384  * gtk_mount_operation_is_showing:
1385  * @op: a #GtkMountOperation
1386  *
1387  * Returns whether the #GtkMountOperation is currently displaying
1388  * a window.
1389  *
1390  * Returns: %TRUE if @op is currently displaying a window
1391  *
1392  * Since: 2.14
1393  */
1394 gboolean
1395 gtk_mount_operation_is_showing (GtkMountOperation *op)
1396 {
1397   g_return_val_if_fail (GTK_IS_MOUNT_OPERATION (op), FALSE);
1398
1399   return op->priv->dialog != NULL;
1400 }
1401
1402 /**
1403  * gtk_mount_operation_set_parent:
1404  * @op: a #GtkMountOperation
1405  * @parent: (allow-none): transient parent of the window, or %NULL
1406  *
1407  * Sets the transient parent for windows shown by the
1408  * #GtkMountOperation.
1409  *
1410  * Since: 2.14
1411  */
1412 void
1413 gtk_mount_operation_set_parent (GtkMountOperation *op,
1414                                 GtkWindow         *parent)
1415 {
1416   GtkMountOperationPrivate *priv;
1417
1418   g_return_if_fail (GTK_IS_MOUNT_OPERATION (op));
1419   g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent));
1420
1421   priv = op->priv;
1422
1423   if (priv->parent_window == parent)
1424     return;
1425
1426   if (priv->parent_window)
1427     {
1428       g_signal_handlers_disconnect_by_func (priv->parent_window,
1429                                             gtk_widget_destroyed,
1430                                             &priv->parent_window);
1431       priv->parent_window = NULL;
1432     }
1433
1434   if (parent)
1435     {
1436       priv->parent_window = g_object_ref (parent);
1437
1438       g_signal_connect (parent, "destroy",
1439                         G_CALLBACK (gtk_widget_destroyed),
1440                         &priv->parent_window);
1441
1442       if (priv->dialog)
1443         gtk_window_set_transient_for (GTK_WINDOW (priv->dialog), parent);
1444     }
1445
1446   g_object_notify (G_OBJECT (op), "parent");
1447 }
1448
1449 /**
1450  * gtk_mount_operation_get_parent:
1451  * @op: a #GtkMountOperation
1452  *
1453  * Gets the transient parent used by the #GtkMountOperation
1454  *
1455  * Returns: (transfer none): the transient parent for windows shown by @op
1456  *
1457  * Since: 2.14
1458  */
1459 GtkWindow *
1460 gtk_mount_operation_get_parent (GtkMountOperation *op)
1461 {
1462   g_return_val_if_fail (GTK_IS_MOUNT_OPERATION (op), NULL);
1463
1464   return op->priv->parent_window;
1465 }
1466
1467 /**
1468  * gtk_mount_operation_set_screen:
1469  * @op: a #GtkMountOperation
1470  * @screen: a #GdkScreen
1471  *
1472  * Sets the screen to show windows of the #GtkMountOperation on.
1473  *
1474  * Since: 2.14
1475  */
1476 void
1477 gtk_mount_operation_set_screen (GtkMountOperation *op,
1478                                 GdkScreen         *screen)
1479 {
1480   GtkMountOperationPrivate *priv;
1481
1482   g_return_if_fail (GTK_IS_MOUNT_OPERATION (op));
1483   g_return_if_fail (GDK_IS_SCREEN (screen));
1484
1485   priv = op->priv;
1486
1487   if (priv->screen == screen)
1488     return;
1489
1490   if (priv->screen)
1491     g_object_unref (priv->screen);
1492
1493   priv->screen = g_object_ref (screen);
1494
1495   if (priv->dialog)
1496     gtk_window_set_screen (GTK_WINDOW (priv->dialog), screen);
1497
1498   g_object_notify (G_OBJECT (op), "screen");
1499 }
1500
1501 /**
1502  * gtk_mount_operation_get_screen:
1503  * @op: a #GtkMountOperation
1504  *
1505  * Gets the screen on which windows of the #GtkMountOperation
1506  * will be shown.
1507  *
1508  * Returns: (transfer none): the screen on which windows of @op are shown
1509  *
1510  * Since: 2.14
1511  */
1512 GdkScreen *
1513 gtk_mount_operation_get_screen (GtkMountOperation *op)
1514 {
1515   GtkMountOperationPrivate *priv;
1516
1517   g_return_val_if_fail (GTK_IS_MOUNT_OPERATION (op), NULL);
1518
1519   priv = op->priv;
1520
1521   if (priv->dialog)
1522     return gtk_window_get_screen (GTK_WINDOW (priv->dialog));
1523   else if (priv->parent_window)
1524     return gtk_window_get_screen (GTK_WINDOW (priv->parent_window));
1525   else if (priv->screen)
1526     return priv->screen;
1527   else
1528     return gdk_screen_get_default ();
1529 }