]> Pileus Git - ~andy/gtk/blob - gtk/gtkmountoperation.c
Don't export private GtkMountOperationHandler api
[~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, see <http://www.gnu.org/licenses/>.
17  */
18
19 /*
20  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
21  * file for a list of people on the GTK+ Team.  See the ChangeLog
22  * files for a list of changes.  These files are distributed with
23  * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
24  */
25
26 #include "config.h"
27
28 #include <string.h>
29
30 #include "gtkmountoperationprivate.h"
31 #include "gtkbox.h"
32 #include "gtkdbusgenerated.h"
33 #include "gtkentry.h"
34 #include "gtkbox.h"
35 #include "gtkintl.h"
36 #include "gtklabel.h"
37 #include "gtkmessagedialog.h"
38 #include "gtkmountoperation.h"
39 #include "gtkprivate.h"
40 #include "gtkradiobutton.h"
41 #include "gtkstock.h"
42 #include "gtkgrid.h"
43 #include "gtkwindow.h"
44 #include "gtktreeview.h"
45 #include "gtktreeselection.h"
46 #include "gtkcellrenderertext.h"
47 #include "gtkcellrendererpixbuf.h"
48 #include "gtkscrolledwindow.h"
49 #include "gtkicontheme.h"
50 #include "gtkimagemenuitem.h"
51 #include "gtkmain.h"
52
53 #include <glib/gprintf.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   /* bus proxy */
122   _GtkMountOperationHandler *handler;
123   GCancellable *cancellable;
124   gboolean handler_showing;
125
126   /* for the ask-password dialog */
127   GtkWidget *entry_container;
128   GtkWidget *username_entry;
129   GtkWidget *domain_entry;
130   GtkWidget *password_entry;
131   GtkWidget *anonymous_toggle;
132
133   GAskPasswordFlags ask_flags;
134   GPasswordSave     password_save;
135   gboolean          anonymous;
136
137   /* for the show-processes dialog */
138   GtkWidget *process_tree_view;
139   GtkListStore *process_list_store;
140 };
141
142 static void
143 gtk_mount_operation_class_init (GtkMountOperationClass *klass)
144 {
145   GObjectClass         *object_class = G_OBJECT_CLASS (klass);
146   GMountOperationClass *mount_op_class = G_MOUNT_OPERATION_CLASS (klass);
147
148   g_type_class_add_private (klass, sizeof (GtkMountOperationPrivate));
149
150   object_class->finalize     = gtk_mount_operation_finalize;
151   object_class->get_property = gtk_mount_operation_get_property;
152   object_class->set_property = gtk_mount_operation_set_property;
153
154   mount_op_class->ask_password = gtk_mount_operation_ask_password;
155   mount_op_class->ask_question = gtk_mount_operation_ask_question;
156   mount_op_class->show_processes = gtk_mount_operation_show_processes;
157   mount_op_class->aborted = gtk_mount_operation_aborted;
158
159   g_object_class_install_property (object_class,
160                                    PROP_PARENT,
161                                    g_param_spec_object ("parent",
162                                                         P_("Parent"),
163                                                         P_("The parent window"),
164                                                         GTK_TYPE_WINDOW,
165                                                         GTK_PARAM_READWRITE));
166
167   g_object_class_install_property (object_class,
168                                    PROP_IS_SHOWING,
169                                    g_param_spec_boolean ("is-showing",
170                                                          P_("Is Showing"),
171                                                          P_("Are we showing a dialog"),
172                                                          FALSE,
173                                                          GTK_PARAM_READABLE));
174
175   g_object_class_install_property (object_class,
176                                    PROP_SCREEN,
177                                    g_param_spec_object ("screen",
178                                                         P_("Screen"),
179                                                         P_("The screen where this window will be displayed."),
180                                                         GDK_TYPE_SCREEN,
181                                                         GTK_PARAM_READWRITE));
182 }
183
184 static void
185 gtk_mount_operation_init (GtkMountOperation *operation)
186 {
187   gchar *name_owner;
188
189   operation->priv = G_TYPE_INSTANCE_GET_PRIVATE (operation,
190                                                  GTK_TYPE_MOUNT_OPERATION,
191                                                  GtkMountOperationPrivate);
192
193   operation->priv->handler =
194     _gtk_mount_operation_handler_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION,
195                                                          G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
196                                                          "org.gtk.MountOperationHandler",
197                                                          "/org/gtk/MountOperationHandler",
198                                                          NULL, NULL);
199   name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (operation->priv->handler));
200   if (!name_owner)
201     g_clear_object (&operation->priv->handler);
202   g_free (name_owner);
203 }
204
205 static void
206 gtk_mount_operation_finalize (GObject *object)
207 {
208   GtkMountOperation *operation = GTK_MOUNT_OPERATION (object);
209   GtkMountOperationPrivate *priv = operation->priv;
210
211   if (priv->parent_window)
212     g_object_unref (priv->parent_window);
213
214   if (priv->screen)
215     g_object_unref (priv->screen);
216
217   if (priv->handler)
218     g_object_unref (priv->handler);
219
220   G_OBJECT_CLASS (gtk_mount_operation_parent_class)->finalize (object);
221 }
222
223 static void
224 gtk_mount_operation_set_property (GObject      *object,
225                                   guint         prop_id,
226                                   const GValue *value,
227                                   GParamSpec   *pspec)
228 {
229   GtkMountOperation *operation = GTK_MOUNT_OPERATION (object);
230
231   switch (prop_id)
232     {
233     case PROP_PARENT:
234       gtk_mount_operation_set_parent (operation, g_value_get_object (value));
235       break;
236
237     case PROP_SCREEN:
238       gtk_mount_operation_set_screen (operation, g_value_get_object (value));
239       break;
240
241     case PROP_IS_SHOWING:
242     default:
243       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
244       break;
245     }
246 }
247
248 static void
249 gtk_mount_operation_get_property (GObject    *object,
250                                   guint       prop_id,
251                                   GValue     *value,
252                                   GParamSpec *pspec)
253 {
254   GtkMountOperation *operation = GTK_MOUNT_OPERATION (object);
255   GtkMountOperationPrivate *priv = operation->priv;
256
257   switch (prop_id)
258     {
259     case PROP_PARENT:
260       g_value_set_object (value, priv->parent_window);
261       break;
262
263     case PROP_IS_SHOWING:
264       g_value_set_boolean (value, priv->dialog != NULL || priv->handler_showing);
265       break;
266
267     case PROP_SCREEN:
268       g_value_set_object (value, priv->screen);
269       break;
270
271     default:
272       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
273       break;
274     }
275 }
276
277 static void
278 gtk_mount_operation_proxy_finish (GtkMountOperation     *op,
279                                   GMountOperationResult  result)
280 {
281   _gtk_mount_operation_handler_call_close (op->priv->handler, NULL, NULL, NULL);
282
283   op->priv->handler_showing = FALSE;
284   g_object_notify (G_OBJECT (op), "is-showing");
285
286   g_mount_operation_reply (G_MOUNT_OPERATION (op), result);
287
288   /* drop the reference acquired when calling the proxy method */
289   g_object_unref (op);
290 }
291
292 static void
293 remember_button_toggled (GtkToggleButton   *button,
294                          GtkMountOperation *operation)
295 {
296   GtkMountOperationPrivate *priv = operation->priv;
297
298   if (gtk_toggle_button_get_active (button))
299     {
300       gpointer data;
301
302       data = g_object_get_data (G_OBJECT (button), "password-save");
303       priv->password_save = GPOINTER_TO_INT (data);
304     }
305 }
306
307 static void
308 pw_dialog_got_response (GtkDialog         *dialog,
309                         gint               response_id,
310                         GtkMountOperation *mount_op)
311 {
312   GtkMountOperationPrivate *priv = mount_op->priv;
313   GMountOperation *op = G_MOUNT_OPERATION (mount_op);
314
315   if (response_id == GTK_RESPONSE_OK)
316     {
317       const char *text;
318
319       if (priv->ask_flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED)
320         g_mount_operation_set_anonymous (op, priv->anonymous);
321
322       if (priv->username_entry)
323         {
324           text = gtk_entry_get_text (GTK_ENTRY (priv->username_entry));
325           g_mount_operation_set_username (op, text);
326         }
327
328       if (priv->domain_entry)
329         {
330           text = gtk_entry_get_text (GTK_ENTRY (priv->domain_entry));
331           g_mount_operation_set_domain (op, text);
332         }
333
334       if (priv->password_entry)
335         {
336           text = gtk_entry_get_text (GTK_ENTRY (priv->password_entry));
337           g_mount_operation_set_password (op, text);
338         }
339
340       if (priv->ask_flags & G_ASK_PASSWORD_SAVING_SUPPORTED)
341         g_mount_operation_set_password_save (op, priv->password_save);
342
343       g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED);
344     }
345   else
346     g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED);
347
348   priv->dialog = NULL;
349   g_object_notify (G_OBJECT (op), "is-showing");
350   gtk_widget_destroy (GTK_WIDGET (dialog));
351   g_object_unref (op);
352 }
353
354 static gboolean
355 entry_has_input (GtkWidget *entry_widget)
356 {
357   const char *text;
358
359   if (entry_widget == NULL)
360     return TRUE;
361
362   text = gtk_entry_get_text (GTK_ENTRY (entry_widget));
363
364   return text != NULL && text[0] != '\0';
365 }
366
367 static gboolean
368 pw_dialog_input_is_valid (GtkMountOperation *operation)
369 {
370   GtkMountOperationPrivate *priv = operation->priv;
371   gboolean is_valid = TRUE;
372
373   /* We don't require password to be non-empty here
374    * since there are situations where it is not needed,
375    * see bug 578365.
376    * We may add a way for the backend to specify that it
377    * definitively needs a password.
378    */
379   is_valid = entry_has_input (priv->username_entry) &&
380              entry_has_input (priv->domain_entry);
381
382   return is_valid;
383 }
384
385 static void
386 pw_dialog_verify_input (GtkEditable       *editable,
387                         GtkMountOperation *operation)
388 {
389   GtkMountOperationPrivate *priv = operation->priv;
390   gboolean is_valid;
391
392   is_valid = pw_dialog_input_is_valid (operation);
393   gtk_dialog_set_response_sensitive (GTK_DIALOG (priv->dialog),
394                                      GTK_RESPONSE_OK,
395                                      is_valid);
396 }
397
398 static void
399 pw_dialog_anonymous_toggled (GtkWidget         *widget,
400                              GtkMountOperation *operation)
401 {
402   GtkMountOperationPrivate *priv = operation->priv;
403   gboolean is_valid;
404
405   priv->anonymous = widget == priv->anonymous_toggle;
406
407   if (priv->anonymous)
408     is_valid = TRUE;
409   else
410     is_valid = pw_dialog_input_is_valid (operation);
411
412   gtk_widget_set_sensitive (priv->entry_container, priv->anonymous == FALSE);
413   gtk_dialog_set_response_sensitive (GTK_DIALOG (priv->dialog),
414                                      GTK_RESPONSE_OK,
415                                      is_valid);
416 }
417
418
419 static void
420 pw_dialog_cycle_focus (GtkWidget         *widget,
421                        GtkMountOperation *operation)
422 {
423   GtkMountOperationPrivate *priv;
424   GtkWidget *next_widget = NULL;
425
426   priv = operation->priv;
427
428   if (widget == priv->username_entry)
429     {
430       if (priv->domain_entry != NULL)
431         next_widget = priv->domain_entry;
432       else if (priv->password_entry != NULL)
433         next_widget = priv->password_entry;
434     }
435   else if (widget == priv->domain_entry && priv->password_entry)
436     next_widget = priv->password_entry;
437
438   if (next_widget)
439     gtk_widget_grab_focus (next_widget);
440   else if (pw_dialog_input_is_valid (operation))
441     gtk_window_activate_default (GTK_WINDOW (priv->dialog));
442 }
443
444 static GtkWidget *
445 table_add_entry (GtkWidget  *table,
446                  int         row,
447                  const char *label_text,
448                  const char *value,
449                  gpointer    user_data)
450 {
451   GtkWidget *entry;
452   GtkWidget *label;
453
454   label = gtk_label_new_with_mnemonic (label_text);
455   gtk_widget_set_halign (label, GTK_ALIGN_START);
456   gtk_widget_set_valign (label, GTK_ALIGN_CENTER);
457   gtk_widget_set_hexpand (label, TRUE);
458
459   entry = gtk_entry_new ();
460
461   if (value)
462     gtk_entry_set_text (GTK_ENTRY (entry), value);
463
464   gtk_grid_attach (GTK_GRID (table), label, 0, row, 1, 1);
465   gtk_grid_attach (GTK_GRID (table), entry, 1, row, 1, 1);
466   gtk_label_set_mnemonic_widget (GTK_LABEL (label), entry);
467
468   g_signal_connect (entry, "changed",
469                     G_CALLBACK (pw_dialog_verify_input), user_data);
470
471   g_signal_connect (entry, "activate",
472                     G_CALLBACK (pw_dialog_cycle_focus), user_data);
473
474   return entry;
475 }
476
477 static void
478 gtk_mount_operation_ask_password_do_gtk (GtkMountOperation *operation,
479                                          const gchar       *message,
480                                          const gchar       *default_user,
481                                          const gchar       *default_domain)
482 {
483   GtkMountOperationPrivate *priv;
484   GtkWidget *widget;
485   GtkDialog *dialog;
486   GtkWindow *window;
487   GtkWidget *hbox, *main_vbox, *vbox, *icon;
488   GtkWidget *table;
489   GtkWidget *message_label;
490   GtkWidget *content_area, *action_area;
491   gboolean   can_anonymous;
492   guint      rows;
493   const gchar *secondary;
494
495   priv = operation->priv;
496
497   widget = gtk_dialog_new ();
498   dialog = GTK_DIALOG (widget);
499   window = GTK_WINDOW (widget);
500
501   priv->dialog = dialog;
502
503   content_area = gtk_dialog_get_content_area (dialog);
504   action_area = gtk_dialog_get_action_area (dialog);
505
506   /* Set the dialog up with HIG properties */
507   gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
508   gtk_box_set_spacing (GTK_BOX (content_area), 2); /* 2 * 5 + 2 = 12 */
509   gtk_container_set_border_width (GTK_CONTAINER (action_area), 5);
510   gtk_box_set_spacing (GTK_BOX (action_area), 6);
511
512   gtk_window_set_resizable (window, FALSE);
513   gtk_window_set_title (window, "");
514   gtk_window_set_icon_name (window, GTK_STOCK_DIALOG_AUTHENTICATION);
515
516   gtk_dialog_add_buttons (dialog,
517                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
518                           _("Co_nnect"), GTK_RESPONSE_OK,
519                           NULL);
520   gtk_dialog_set_default_response (dialog, GTK_RESPONSE_OK);
521
522   gtk_dialog_set_alternative_button_order (dialog,
523                                            GTK_RESPONSE_OK,
524                                            GTK_RESPONSE_CANCEL,
525                                            -1);
526
527   /* Build contents */
528   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
529   gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
530   gtk_box_pack_start (GTK_BOX (content_area), hbox, TRUE, TRUE, 0);
531
532   icon = gtk_image_new_from_stock (GTK_STOCK_DIALOG_AUTHENTICATION,
533                                    GTK_ICON_SIZE_DIALOG);
534
535   gtk_widget_set_halign (icon, GTK_ALIGN_CENTER);
536   gtk_widget_set_valign (icon, GTK_ALIGN_START);
537   gtk_box_pack_start (GTK_BOX (hbox), icon, FALSE, FALSE, 0);
538
539   main_vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 18);
540   gtk_box_pack_start (GTK_BOX (hbox), main_vbox, TRUE, TRUE, 0);
541
542   secondary = strstr (message, "\n");
543   if (secondary != NULL)
544     {
545       gchar *s;
546       gchar *primary;
547
548       primary = g_strndup (message, secondary - message + 1);
549       s = g_strdup_printf ("<big><b>%s</b></big>%s", primary, secondary);
550
551       message_label = gtk_label_new (NULL);
552       gtk_label_set_markup (GTK_LABEL (message_label), s);
553       gtk_widget_set_halign (message_label, GTK_ALIGN_START);
554       gtk_widget_set_valign (message_label, GTK_ALIGN_CENTER);
555       gtk_label_set_line_wrap (GTK_LABEL (message_label), TRUE);
556       gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (message_label),
557                           FALSE, TRUE, 0);
558
559       g_free (s);
560       g_free (primary);
561     }
562   else
563     {
564       message_label = gtk_label_new (message);
565       gtk_widget_set_halign (message_label, GTK_ALIGN_START);
566       gtk_widget_set_valign (message_label, GTK_ALIGN_CENTER);
567       gtk_label_set_line_wrap (GTK_LABEL (message_label), TRUE);
568       gtk_box_pack_start (GTK_BOX (main_vbox), GTK_WIDGET (message_label),
569                           FALSE, FALSE, 0);
570     }
571
572   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
573   gtk_box_pack_start (GTK_BOX (main_vbox), vbox, FALSE, FALSE, 0);
574
575   can_anonymous = priv->ask_flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED;
576
577   priv->anonymous_toggle = NULL;
578   if (can_anonymous)
579     {
580       GtkWidget *anon_box;
581       GtkWidget *choice;
582       GSList    *group;
583
584       anon_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
585       gtk_box_pack_start (GTK_BOX (vbox), anon_box,
586                           FALSE, FALSE, 0);
587
588       choice = gtk_radio_button_new_with_mnemonic (NULL, _("Connect _anonymously"));
589       gtk_box_pack_start (GTK_BOX (anon_box),
590                           choice,
591                           FALSE, FALSE, 0);
592       g_signal_connect (choice, "toggled",
593                         G_CALLBACK (pw_dialog_anonymous_toggled), operation);
594       priv->anonymous_toggle = choice;
595
596       group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (choice));
597       choice = gtk_radio_button_new_with_mnemonic (group, _("Connect as u_ser:"));
598       gtk_box_pack_start (GTK_BOX (anon_box),
599                           choice,
600                           FALSE, FALSE, 0);
601       g_signal_connect (choice, "toggled",
602                         G_CALLBACK (pw_dialog_anonymous_toggled), operation);
603     }
604
605   rows = 0;
606
607   if (priv->ask_flags & G_ASK_PASSWORD_NEED_PASSWORD)
608     rows++;
609
610   if (priv->ask_flags & G_ASK_PASSWORD_NEED_USERNAME)
611     rows++;
612
613   if (priv->ask_flags &G_ASK_PASSWORD_NEED_DOMAIN)
614     rows++;
615
616   /* The table that holds the entries */
617   table = gtk_grid_new ();
618   gtk_grid_set_row_spacing (GTK_GRID (table), 6);
619   gtk_grid_set_column_spacing (GTK_GRID (table), 6);
620
621   if (can_anonymous)
622     gtk_widget_set_margin_left (table, 12);
623
624   gtk_box_pack_start (GTK_BOX (vbox), table, FALSE, FALSE, 0);
625   priv->entry_container = table;
626
627   rows = 0;
628
629   priv->username_entry = NULL;
630
631   if (priv->ask_flags & G_ASK_PASSWORD_NEED_USERNAME)
632     priv->username_entry = table_add_entry (table, rows++, _("_Username:"),
633                                             default_user, operation);
634
635   priv->domain_entry = NULL;
636   if (priv->ask_flags & G_ASK_PASSWORD_NEED_DOMAIN)
637     priv->domain_entry = table_add_entry (table, rows++, _("_Domain:"),
638                                           default_domain, operation);
639
640   priv->password_entry = NULL;
641   if (priv->ask_flags & G_ASK_PASSWORD_NEED_PASSWORD)
642     {
643       priv->password_entry = table_add_entry (table, rows++, _("_Password:"),
644                                               NULL, operation);
645       gtk_entry_set_visibility (GTK_ENTRY (priv->password_entry), FALSE);
646     }
647
648    if (priv->ask_flags & G_ASK_PASSWORD_SAVING_SUPPORTED)
649     {
650       GtkWidget    *choice;
651       GtkWidget    *remember_box;
652       GSList       *group;
653       GPasswordSave password_save;
654
655       remember_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
656       gtk_box_pack_start (GTK_BOX (vbox), remember_box,
657                           FALSE, FALSE, 0);
658
659       password_save = g_mount_operation_get_password_save (G_MOUNT_OPERATION (operation));
660       
661       choice = gtk_radio_button_new_with_mnemonic (NULL, _("Forget password _immediately"));
662       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (choice),
663                                     password_save == G_PASSWORD_SAVE_NEVER);
664       g_object_set_data (G_OBJECT (choice), "password-save",
665                          GINT_TO_POINTER (G_PASSWORD_SAVE_NEVER));
666       g_signal_connect (choice, "toggled",
667                         G_CALLBACK (remember_button_toggled), operation);
668       gtk_box_pack_start (GTK_BOX (remember_box), choice, FALSE, FALSE, 0);
669
670       group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (choice));
671       choice = gtk_radio_button_new_with_mnemonic (group, _("Remember password until you _logout"));
672       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (choice),
673                                     password_save == G_PASSWORD_SAVE_FOR_SESSION);
674       g_object_set_data (G_OBJECT (choice), "password-save",
675                          GINT_TO_POINTER (G_PASSWORD_SAVE_FOR_SESSION));
676       g_signal_connect (choice, "toggled",
677                         G_CALLBACK (remember_button_toggled), operation);
678       gtk_box_pack_start (GTK_BOX (remember_box), choice, FALSE, FALSE, 0);
679
680       group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (choice));
681       choice = gtk_radio_button_new_with_mnemonic (group, _("Remember _forever"));
682       gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (choice),
683                                     password_save == G_PASSWORD_SAVE_PERMANENTLY);
684       g_object_set_data (G_OBJECT (choice), "password-save",
685                          GINT_TO_POINTER (G_PASSWORD_SAVE_PERMANENTLY));
686       g_signal_connect (choice, "toggled",
687                         G_CALLBACK (remember_button_toggled), operation);
688       gtk_box_pack_start (GTK_BOX (remember_box), choice, FALSE, FALSE, 0);
689     }
690
691   g_signal_connect (G_OBJECT (dialog), "response",
692                     G_CALLBACK (pw_dialog_got_response), operation);
693
694   if (can_anonymous)
695     {
696       /* The anonymous option will be active by default,
697        * ensure the toggled signal is emitted for it.
698        */
699       gtk_toggle_button_toggled (GTK_TOGGLE_BUTTON (priv->anonymous_toggle));
700     }
701   else if (! pw_dialog_input_is_valid (operation))
702     gtk_dialog_set_response_sensitive (dialog, GTK_RESPONSE_OK, FALSE);
703
704   g_object_notify (G_OBJECT (operation), "is-showing");
705
706   if (priv->parent_window)
707     {
708       gtk_window_set_transient_for (window, priv->parent_window);
709       gtk_window_set_modal (window, TRUE);
710     }
711   else if (priv->screen)
712     gtk_window_set_screen (GTK_WINDOW (dialog), priv->screen);
713
714   gtk_widget_show_all (GTK_WIDGET (dialog));
715
716   g_object_ref (operation);
717 }
718
719 static void
720 call_password_proxy_cb (GObject      *source,
721                         GAsyncResult *res,
722                         gpointer      user_data)
723 {
724   _GtkMountOperationHandler *proxy = _GTK_MOUNT_OPERATION_HANDLER (source);
725   GMountOperation *op = user_data;
726   GMountOperationResult result;
727   GVariant *result_details;
728   GVariantIter iter;
729   const gchar *key;
730   GVariant *value;
731   GError *error = NULL;
732
733   if (!_gtk_mount_operation_handler_call_ask_password_finish (proxy,
734                                                               &result,
735                                                               &result_details,
736                                                               res,
737                                                               &error))
738     {
739       result = G_MOUNT_OPERATION_ABORTED;
740       g_warning ("Shell mount operation error: %s\n", error->message);
741       g_error_free (error);
742       goto out;
743     }
744
745   g_variant_iter_init (&iter, result_details);
746   while (g_variant_iter_loop (&iter, "{&sv}", &key, &value))
747     {
748       if (strcmp (key, "password") == 0)
749         g_mount_operation_set_password (op, g_variant_get_string (value, NULL));
750       else if (strcmp (key, "password_save") == 0)
751         g_mount_operation_set_password_save (op, g_variant_get_uint32 (value));
752     }
753
754  out:
755   gtk_mount_operation_proxy_finish (GTK_MOUNT_OPERATION (op), result);
756 }
757
758 static void
759 gtk_mount_operation_ask_password_do_proxy (GtkMountOperation *operation,
760                                            const char        *message,
761                                            const char        *default_user,
762                                            const char        *default_domain)
763 {
764   gchar id[255];
765   g_sprintf(id, "GtkMountOperation%p", operation);
766
767   operation->priv->handler_showing = TRUE;
768   g_object_notify (G_OBJECT (operation), "is-showing");
769
770   /* keep a ref to the operation while the handler is showing */
771   g_object_ref (operation);
772
773   _gtk_mount_operation_handler_call_ask_password (operation->priv->handler, id,
774                                                   message, "drive-harddisk",
775                                                   default_user, default_domain,
776                                                   operation->priv->ask_flags, NULL,
777                                                   call_password_proxy_cb, operation);
778 }
779
780 static void
781 gtk_mount_operation_ask_password (GMountOperation   *mount_op,
782                                   const char        *message,
783                                   const char        *default_user,
784                                   const char        *default_domain,
785                                   GAskPasswordFlags  flags)
786 {
787   GtkMountOperation *operation;
788   GtkMountOperationPrivate *priv;
789   gboolean use_gtk;
790
791   operation = GTK_MOUNT_OPERATION (mount_op);
792   priv = operation->priv;
793   priv->ask_flags = flags;
794
795   use_gtk = (operation->priv->handler == NULL) ||
796     (priv->ask_flags & G_ASK_PASSWORD_NEED_DOMAIN) ||
797     (priv->ask_flags & G_ASK_PASSWORD_NEED_USERNAME);
798
799   if (use_gtk)
800     gtk_mount_operation_ask_password_do_gtk (operation, message, default_user, default_domain);
801   else
802     gtk_mount_operation_ask_password_do_proxy (operation, message, default_user, default_domain);
803 }
804
805 static void
806 question_dialog_button_clicked (GtkDialog       *dialog,
807                                 gint             button_number,
808                                 GMountOperation *op)
809 {
810   GtkMountOperationPrivate *priv;
811   GtkMountOperation *operation;
812
813   operation = GTK_MOUNT_OPERATION (op);
814   priv = operation->priv;
815
816   if (button_number >= 0)
817     {
818       g_mount_operation_set_choice (op, button_number);
819       g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED);
820     }
821   else
822     g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED);
823
824   priv->dialog = NULL;
825   g_object_notify (G_OBJECT (operation), "is-showing");
826   gtk_widget_destroy (GTK_WIDGET (dialog));
827   g_object_unref (op);
828 }
829
830 static void
831 gtk_mount_operation_ask_question_do_gtk (GtkMountOperation *op,
832                                          const char        *message,
833                                          const char        *choices[])
834 {
835   GtkMountOperationPrivate *priv;
836   GtkWidget  *dialog;
837   const char *secondary = NULL;
838   char       *primary;
839   int        count, len = 0;
840
841   g_return_if_fail (GTK_IS_MOUNT_OPERATION (op));
842   g_return_if_fail (message != NULL);
843   g_return_if_fail (choices != NULL);
844
845   priv = op->priv;
846
847   primary = strstr (message, "\n");
848   if (primary)
849     {
850       secondary = primary + 1;
851       primary = g_strndup (message, primary - message);
852     }
853
854   dialog = gtk_message_dialog_new (priv->parent_window, 0,
855                                    GTK_MESSAGE_QUESTION,
856                                    GTK_BUTTONS_NONE, "%s",
857                                    primary != NULL ? primary : message);
858   g_free (primary);
859
860   if (secondary)
861     gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
862                                               "%s", secondary);
863
864   /* First count the items in the list then
865    * add the buttons in reverse order */
866
867   while (choices[len] != NULL)
868     len++;
869
870   for (count = len - 1; count >= 0; count--)
871     gtk_dialog_add_button (GTK_DIALOG (dialog), choices[count], count);
872
873   g_signal_connect (G_OBJECT (dialog), "response",
874                     G_CALLBACK (question_dialog_button_clicked), op);
875
876   priv->dialog = GTK_DIALOG (dialog);
877   g_object_notify (G_OBJECT (op), "is-showing");
878
879   if (priv->parent_window == NULL && priv->screen)
880     gtk_window_set_screen (GTK_WINDOW (dialog), priv->screen);
881
882   gtk_widget_show (dialog);
883   g_object_ref (op);
884 }
885
886 static void
887 call_question_proxy_cb (GObject      *source,
888                         GAsyncResult *res,
889                         gpointer      user_data)
890 {
891   _GtkMountOperationHandler *proxy = _GTK_MOUNT_OPERATION_HANDLER (source);
892   GMountOperation *op = user_data;
893   GMountOperationResult result;
894   GVariant *result_details;
895   GVariantIter iter;
896   const gchar *key;
897   GVariant *value;
898   GError *error = NULL;
899
900   if (!_gtk_mount_operation_handler_call_ask_question_finish (proxy,
901                                                               &result,
902                                                               &result_details,
903                                                               res,
904                                                               &error))
905     {
906       result = G_MOUNT_OPERATION_ABORTED;
907       g_warning ("Shell mount operation error: %s\n", error->message);
908       g_error_free (error);
909       goto out;
910     }
911
912   g_variant_iter_init (&iter, result_details);
913   while (g_variant_iter_loop (&iter, "{&sv}", &key, &value))
914     {
915       if (strcmp (key, "choice") == 0)
916         g_mount_operation_set_choice (op, g_variant_get_int32 (value));
917     }
918  
919  out:
920   gtk_mount_operation_proxy_finish (GTK_MOUNT_OPERATION (op), result);
921 }
922
923 static void
924 gtk_mount_operation_ask_question_do_proxy (GtkMountOperation *operation,
925                                            const char        *message,
926                                            const char        *choices[])
927 {
928   gchar id[255];
929   g_sprintf(id, "GtkMountOperation%p", operation);
930
931   operation->priv->handler_showing = TRUE;
932   g_object_notify (G_OBJECT (operation), "is-showing");
933
934   /* keep a ref to the operation while the handler is showing */
935   g_object_ref (operation);
936
937   _gtk_mount_operation_handler_call_ask_question (operation->priv->handler, id,
938                                                   message, "drive-harddisk",
939                                                   choices, NULL,
940                                                   call_question_proxy_cb, operation);
941 }
942
943 static void
944 gtk_mount_operation_ask_question (GMountOperation *op,
945                                   const char      *message,
946                                   const char      *choices[])
947 {
948   GtkMountOperation *operation;
949   gboolean use_gtk;
950
951   operation = GTK_MOUNT_OPERATION (op);
952   use_gtk = (operation->priv->handler == NULL);
953
954   if (use_gtk)
955     gtk_mount_operation_ask_question_do_gtk (operation, message, choices);
956   else
957     gtk_mount_operation_ask_question_do_proxy (operation, message, choices);
958 }
959
960 static void
961 show_processes_button_clicked (GtkDialog       *dialog,
962                                gint             button_number,
963                                GMountOperation *op)
964 {
965   GtkMountOperationPrivate *priv;
966   GtkMountOperation *operation;
967
968   operation = GTK_MOUNT_OPERATION (op);
969   priv = operation->priv;
970
971   if (button_number >= 0)
972     {
973       g_mount_operation_set_choice (op, button_number);
974       g_mount_operation_reply (op, G_MOUNT_OPERATION_HANDLED);
975     }
976   else
977     g_mount_operation_reply (op, G_MOUNT_OPERATION_ABORTED);
978
979   priv->dialog = NULL;
980   g_object_notify (G_OBJECT (operation), "is-showing");
981   gtk_widget_destroy (GTK_WIDGET (dialog));
982   g_object_unref (op);
983 }
984
985 static gint
986 pid_equal (gconstpointer a,
987            gconstpointer b)
988 {
989   GPid pa, pb;
990
991   pa = *((GPid *) a);
992   pb = *((GPid *) b);
993
994   return GPOINTER_TO_INT(pb) - GPOINTER_TO_INT(pa);
995 }
996
997 static void
998 diff_sorted_arrays (GArray         *array1,
999                     GArray         *array2,
1000                     GCompareFunc   compare,
1001                     GArray         *added_indices,
1002                     GArray         *removed_indices)
1003 {
1004   gint order;
1005   guint n1, n2;
1006   guint elem_size;
1007
1008   n1 = n2 = 0;
1009
1010   elem_size = g_array_get_element_size (array1);
1011   g_assert (elem_size == g_array_get_element_size (array2));
1012
1013   while (n1 < array1->len && n2 < array2->len)
1014     {
1015       order = (*compare) (((const char*) array1->data) + n1 * elem_size,
1016                           ((const char*) array2->data) + n2 * elem_size);
1017       if (order < 0)
1018         {
1019           g_array_append_val (removed_indices, n1);
1020           n1++;
1021         }
1022       else if (order > 0)
1023         {
1024           g_array_append_val (added_indices, n2);
1025           n2++;
1026         }
1027       else
1028         { /* same item */
1029           n1++;
1030           n2++;
1031         }
1032     }
1033
1034   while (n1 < array1->len)
1035     {
1036       g_array_append_val (removed_indices, n1);
1037       n1++;
1038     }
1039   while (n2 < array2->len)
1040     {
1041       g_array_append_val (added_indices, n2);
1042       n2++;
1043     }
1044 }
1045
1046
1047 static void
1048 add_pid_to_process_list_store (GtkMountOperation              *mount_operation,
1049                                GtkMountOperationLookupContext *lookup_context,
1050                                GtkListStore                   *list_store,
1051                                GPid                            pid)
1052 {
1053   gchar *command_line;
1054   gchar *name;
1055   GdkPixbuf *pixbuf;
1056   gchar *markup;
1057   GtkTreeIter iter;
1058
1059   name = NULL;
1060   pixbuf = NULL;
1061   command_line = NULL;
1062   _gtk_mount_operation_lookup_info (lookup_context,
1063                                     pid,
1064                                     24,
1065                                     &name,
1066                                     &command_line,
1067                                     &pixbuf);
1068
1069   if (name == NULL)
1070     name = g_strdup_printf (_("Unknown Application (PID %d)"), pid);
1071
1072   if (command_line == NULL)
1073     command_line = g_strdup ("");
1074
1075   if (pixbuf == NULL)
1076     {
1077       GtkIconTheme *theme;
1078       theme = gtk_icon_theme_get_for_screen (gtk_widget_get_screen (GTK_WIDGET (mount_operation->priv->dialog)));
1079       pixbuf = gtk_icon_theme_load_icon (theme,
1080                                          "application-x-executable",
1081                                          24,
1082                                          0,
1083                                          NULL);
1084     }
1085
1086   markup = g_strdup_printf ("<b>%s</b>\n"
1087                             "<small>%s</small>",
1088                             name,
1089                             command_line);
1090
1091   gtk_list_store_append (list_store, &iter);
1092   gtk_list_store_set (list_store, &iter,
1093                       0, pixbuf,
1094                       1, markup,
1095                       2, pid,
1096                       -1);
1097
1098   if (pixbuf != NULL)
1099     g_object_unref (pixbuf);
1100   g_free (markup);
1101   g_free (name);
1102   g_free (command_line);
1103 }
1104
1105 static void
1106 remove_pid_from_process_list_store (GtkMountOperation *mount_operation,
1107                                     GtkListStore      *list_store,
1108                                     GPid               pid)
1109 {
1110   GtkTreeIter iter;
1111   GPid pid_of_item;
1112
1113   if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
1114     {
1115       do
1116         {
1117           gtk_tree_model_get (GTK_TREE_MODEL (list_store),
1118                               &iter,
1119                               2, &pid_of_item,
1120                               -1);
1121
1122           if (pid_of_item == pid)
1123             {
1124               gtk_list_store_remove (list_store, &iter);
1125               break;
1126             }
1127         }
1128       while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store), &iter));
1129     }
1130 }
1131
1132
1133 static void
1134 update_process_list_store (GtkMountOperation *mount_operation,
1135                            GtkListStore      *list_store,
1136                            GArray            *processes)
1137 {
1138   guint n;
1139   GtkMountOperationLookupContext *lookup_context;
1140   GArray *current_pids;
1141   GArray *pid_indices_to_add;
1142   GArray *pid_indices_to_remove;
1143   GtkTreeIter iter;
1144   GPid pid;
1145
1146   /* Just removing all items and adding new ones will screw up the
1147    * focus handling in the treeview - so compute the delta, and add/remove
1148    * items as appropriate
1149    */
1150   current_pids = g_array_new (FALSE, FALSE, sizeof (GPid));
1151   pid_indices_to_add = g_array_new (FALSE, FALSE, sizeof (gint));
1152   pid_indices_to_remove = g_array_new (FALSE, FALSE, sizeof (gint));
1153
1154   if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
1155     {
1156       do
1157         {
1158           gtk_tree_model_get (GTK_TREE_MODEL (list_store),
1159                               &iter,
1160                               2, &pid,
1161                               -1);
1162
1163           g_array_append_val (current_pids, pid);
1164         }
1165       while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store), &iter));
1166     }
1167
1168   g_array_sort (current_pids, pid_equal);
1169   g_array_sort (processes, pid_equal);
1170
1171   diff_sorted_arrays (current_pids, processes, pid_equal, pid_indices_to_add, pid_indices_to_remove);
1172
1173   for (n = 0; n < pid_indices_to_remove->len; n++)
1174     {
1175       pid = g_array_index (current_pids, GPid, n);
1176       remove_pid_from_process_list_store (mount_operation, list_store, pid);
1177     }
1178
1179   if (pid_indices_to_add->len > 0)
1180     {
1181       lookup_context = _gtk_mount_operation_lookup_context_get (gtk_widget_get_display (mount_operation->priv->process_tree_view));
1182       for (n = 0; n < pid_indices_to_add->len; n++)
1183         {
1184           pid = g_array_index (processes, GPid, n);
1185           add_pid_to_process_list_store (mount_operation, lookup_context, list_store, pid);
1186         }
1187       _gtk_mount_operation_lookup_context_free (lookup_context);
1188     }
1189
1190   /* select the first item, if we went from a zero to a non-zero amount of processes */
1191   if (current_pids->len == 0 && pid_indices_to_add->len > 0)
1192     {
1193       if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter))
1194         {
1195           GtkTreeSelection *tree_selection;
1196           tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (mount_operation->priv->process_tree_view));
1197           gtk_tree_selection_select_iter (tree_selection, &iter);
1198         }
1199     }
1200
1201   g_array_unref (current_pids);
1202   g_array_unref (pid_indices_to_add);
1203   g_array_unref (pid_indices_to_remove);
1204 }
1205
1206 static void
1207 on_end_process_activated (GtkMenuItem *item,
1208                           gpointer user_data)
1209 {
1210   GtkMountOperation *op = GTK_MOUNT_OPERATION (user_data);
1211   GtkTreeSelection *selection;
1212   GtkTreeIter iter;
1213   GPid pid_to_kill;
1214   GError *error;
1215
1216   selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (op->priv->process_tree_view));
1217
1218   if (!gtk_tree_selection_get_selected (selection,
1219                                         NULL,
1220                                         &iter))
1221     goto out;
1222
1223   gtk_tree_model_get (GTK_TREE_MODEL (op->priv->process_list_store),
1224                       &iter,
1225                       2, &pid_to_kill,
1226                       -1);
1227
1228   /* TODO: We might want to either
1229    *
1230    *       - Be smart about things and send SIGKILL rather than SIGTERM if
1231    *         this is the second time the user requests killing a process
1232    *
1233    *       - Or, easier (but worse user experience), offer both "End Process"
1234    *         and "Terminate Process" options
1235    *
1236    *      But that's not how things work right now....
1237    */
1238   error = NULL;
1239   if (!_gtk_mount_operation_kill_process (pid_to_kill, &error))
1240     {
1241       GtkWidget *dialog;
1242       gint response;
1243
1244       /* Use GTK_DIALOG_DESTROY_WITH_PARENT here since the parent dialog can be
1245        * indeed be destroyed via the GMountOperation::abort signal... for example,
1246        * this is triggered if the user yanks the device while we are showing
1247        * the dialog...
1248        */
1249       dialog = gtk_message_dialog_new (GTK_WINDOW (op->priv->dialog),
1250                                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
1251                                        GTK_MESSAGE_ERROR,
1252                                        GTK_BUTTONS_CLOSE,
1253                                        _("Unable to end process"));
1254       gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
1255                                                 "%s",
1256                                                 error->message);
1257
1258       gtk_widget_show_all (dialog);
1259       response = gtk_dialog_run (GTK_DIALOG (dialog));
1260
1261       /* GTK_RESPONSE_NONE means the dialog were programmatically destroy, e.g. that
1262        * GTK_DIALOG_DESTROY_WITH_PARENT kicked in - so it would trigger a warning to
1263        * destroy the dialog in that case
1264        */
1265       if (response != GTK_RESPONSE_NONE)
1266         gtk_widget_destroy (dialog);
1267
1268       g_error_free (error);
1269     }
1270
1271  out:
1272   ;
1273 }
1274
1275 static gboolean
1276 do_popup_menu_for_process_tree_view (GtkWidget         *widget,
1277                                      GdkEventButton    *event,
1278                                      GtkMountOperation *op)
1279 {
1280   GtkWidget *menu;
1281   GtkWidget *item;
1282   gint button;
1283   gint event_time;
1284   gboolean popped_up_menu;
1285
1286   popped_up_menu = FALSE;
1287
1288   menu = gtk_menu_new ();
1289
1290   item = gtk_image_menu_item_new_with_mnemonic (_("_End Process"));
1291   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item),
1292                                  gtk_image_new_from_stock (GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU));
1293   g_signal_connect (item, "activate",
1294                     G_CALLBACK (on_end_process_activated),
1295                     op);
1296   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1297   gtk_widget_show_all (menu);
1298
1299   if (event != NULL)
1300     {
1301       GtkTreePath *path;
1302       GtkTreeSelection *selection;
1303
1304       if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (op->priv->process_tree_view),
1305                                          (gint) event->x,
1306                                          (gint) event->y,
1307                                          &path,
1308                                          NULL,
1309                                          NULL,
1310                                          NULL))
1311         {
1312           selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (op->priv->process_tree_view));
1313           gtk_tree_selection_select_path (selection, path);
1314           gtk_tree_path_free (path);
1315         }
1316       else
1317         {
1318           /* don't popup a menu if the user right-clicked in an area with no rows */
1319           goto out;
1320         }
1321
1322       button = event->button;
1323       event_time = event->time;
1324     }
1325   else
1326     {
1327       button = 0;
1328       event_time = gtk_get_current_event_time ();
1329     }
1330
1331   gtk_menu_popup (GTK_MENU (menu),
1332                   NULL,
1333                   widget,
1334                   NULL,
1335                   NULL,
1336                   button,
1337                   event_time);
1338
1339   popped_up_menu = TRUE;
1340
1341  out:
1342   return popped_up_menu;
1343 }
1344
1345 static gboolean
1346 on_popup_menu_for_process_tree_view (GtkWidget *widget,
1347                                      gpointer   user_data)
1348 {
1349   GtkMountOperation *op = GTK_MOUNT_OPERATION (user_data);
1350   return do_popup_menu_for_process_tree_view (widget, NULL, op);
1351 }
1352
1353 static gboolean
1354 on_button_press_event_for_process_tree_view (GtkWidget      *widget,
1355                                              GdkEventButton *event,
1356                                              gpointer        user_data)
1357 {
1358   GtkMountOperation *op = GTK_MOUNT_OPERATION (user_data);
1359   gboolean ret;
1360
1361   ret = FALSE;
1362
1363   if (gdk_event_triggers_context_menu ((GdkEvent *) event))
1364     {
1365       ret = do_popup_menu_for_process_tree_view (widget, event, op);
1366     }
1367
1368   return ret;
1369 }
1370
1371 static GtkWidget *
1372 create_show_processes_dialog (GtkMountOperation *op,
1373                               const char      *message,
1374                               const char      *choices[])
1375 {
1376   GtkMountOperationPrivate *priv;
1377   GtkWidget  *dialog;
1378   const char *secondary = NULL;
1379   char       *primary;
1380   int        count, len = 0;
1381   GtkWidget *label;
1382   GtkWidget *tree_view;
1383   GtkWidget *scrolled_window;
1384   GtkWidget *vbox;
1385   GtkWidget *content_area;
1386   GtkTreeViewColumn *column;
1387   GtkCellRenderer *renderer;
1388   GtkListStore *list_store;
1389   gchar *s;
1390
1391   priv = op->priv;
1392
1393   primary = strstr (message, "\n");
1394   if (primary)
1395     {
1396       secondary = primary + 1;
1397       primary = g_strndup (message, primary - message);
1398     }
1399
1400   dialog = gtk_dialog_new ();
1401
1402   if (priv->parent_window != NULL)
1403     gtk_window_set_transient_for (GTK_WINDOW (dialog), priv->parent_window);
1404   gtk_window_set_title (GTK_WINDOW (dialog), "");
1405
1406   content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
1407   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
1408   gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
1409   gtk_box_pack_start (GTK_BOX (content_area), vbox, TRUE, TRUE, 0);
1410
1411   if (secondary != NULL)
1412     {
1413       s = g_strdup_printf ("<big><b>%s</b></big>\n\n%s", primary, secondary);
1414     }
1415   else
1416     {
1417       s = g_strdup_printf ("%s", primary);
1418     }
1419   g_free (primary);
1420   label = gtk_label_new (NULL);
1421   gtk_label_set_markup (GTK_LABEL (label), s);
1422   g_free (s);
1423   gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
1424
1425   /* First count the items in the list then
1426    * add the buttons in reverse order */
1427
1428   while (choices[len] != NULL)
1429     len++;
1430
1431   for (count = len - 1; count >= 0; count--)
1432     gtk_dialog_add_button (GTK_DIALOG (dialog), choices[count], count);
1433
1434   g_signal_connect (G_OBJECT (dialog), "response",
1435                     G_CALLBACK (show_processes_button_clicked), op);
1436
1437   priv->dialog = GTK_DIALOG (dialog);
1438   g_object_notify (G_OBJECT (op), "is-showing");
1439
1440   if (priv->parent_window == NULL && priv->screen)
1441     gtk_window_set_screen (GTK_WINDOW (dialog), priv->screen);
1442
1443   tree_view = gtk_tree_view_new ();
1444   /* TODO: should use EM's when gtk+ RI patches land */
1445   gtk_widget_set_size_request (tree_view,
1446                                300,
1447                                120);
1448
1449   column = gtk_tree_view_column_new ();
1450   renderer = gtk_cell_renderer_pixbuf_new ();
1451   gtk_tree_view_column_pack_start (column, renderer, FALSE);
1452   gtk_tree_view_column_set_attributes (column, renderer,
1453                                        "pixbuf", 0,
1454                                        NULL);
1455   renderer = gtk_cell_renderer_text_new ();
1456   g_object_set (renderer,
1457                 "ellipsize", PANGO_ELLIPSIZE_MIDDLE,
1458                 "ellipsize-set", TRUE,
1459                 NULL);
1460   gtk_tree_view_column_pack_start (column, renderer, TRUE);
1461   gtk_tree_view_column_set_attributes (column, renderer,
1462                                        "markup", 1,
1463                                        NULL);
1464   gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column);
1465   gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE);
1466
1467
1468   scrolled_window = gtk_scrolled_window_new (NULL, NULL);
1469   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
1470                                   GTK_POLICY_NEVER,
1471                                   GTK_POLICY_AUTOMATIC);
1472   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
1473
1474   gtk_container_add (GTK_CONTAINER (scrolled_window), tree_view);
1475   gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
1476
1477   g_signal_connect (tree_view, "popup-menu",
1478                     G_CALLBACK (on_popup_menu_for_process_tree_view),
1479                     op);
1480   g_signal_connect (tree_view, "button-press-event",
1481                     G_CALLBACK (on_button_press_event_for_process_tree_view),
1482                     op);
1483
1484   list_store = gtk_list_store_new (3,
1485                                    GDK_TYPE_PIXBUF,
1486                                    G_TYPE_STRING,
1487                                    G_TYPE_INT);
1488
1489   gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (list_store));
1490
1491   priv->process_list_store = list_store;
1492   priv->process_tree_view = tree_view;
1493   /* set pointers to NULL when dialog goes away */
1494   g_object_add_weak_pointer (G_OBJECT (list_store), (gpointer *) &priv->process_list_store);
1495   g_object_add_weak_pointer (G_OBJECT (tree_view), (gpointer *) &priv->process_tree_view);
1496
1497   g_object_unref (list_store);
1498   g_object_ref (op);
1499
1500   return dialog;
1501 }
1502
1503 static void
1504 call_processes_proxy_cb (GObject     *source,
1505                         GAsyncResult *res,
1506                         gpointer      user_data)
1507 {
1508   _GtkMountOperationHandler *proxy = _GTK_MOUNT_OPERATION_HANDLER (source);
1509   GMountOperation *op = user_data;
1510   GMountOperationResult result;
1511   GVariant *result_details;
1512   GVariantIter iter;
1513   const gchar *key;
1514   GVariant *value;
1515   GError *error = NULL;
1516
1517   if (!_gtk_mount_operation_handler_call_show_processes_finish (proxy,
1518                                                                 &result,
1519                                                                 &result_details,
1520                                                                 res,
1521                                                                 &error))
1522     {
1523       result = G_MOUNT_OPERATION_ABORTED;
1524       g_warning ("Shell mount operation error: %s\n", error->message);
1525       g_error_free (error);
1526       goto out;
1527     }
1528
1529   /* If the request was unhandled it means we called the method again;
1530    * in this case, just return and wait for the next response.
1531    */
1532   if (result == G_MOUNT_OPERATION_UNHANDLED)
1533     return;
1534
1535   g_variant_iter_init (&iter, result_details);
1536   while (g_variant_iter_loop (&iter, "{&sv}", &key, &value))
1537     {
1538       if (strcmp (key, "choice") == 0)
1539         g_mount_operation_set_choice (op, g_variant_get_int32 (value));
1540     }
1541
1542  out:
1543   gtk_mount_operation_proxy_finish (GTK_MOUNT_OPERATION (op), result);
1544 }
1545
1546 static void
1547 gtk_mount_operation_show_processes_do_proxy (GtkMountOperation *operation,
1548                                              const char        *message,
1549                                              GArray            *processes,
1550                                              const char        *choices[])
1551 {
1552   gchar id[255];
1553   g_sprintf(id, "GtkMountOperation%p", operation);
1554
1555   operation->priv->handler_showing = TRUE;
1556   g_object_notify (G_OBJECT (operation), "is-showing");
1557
1558   /* keep a ref to the operation while the handler is showing */
1559   g_object_ref (operation);
1560
1561   _gtk_mount_operation_handler_call_show_processes (operation->priv->handler, id,
1562                                                     message, "drive-harddisk",
1563                                                     g_variant_new_fixed_array (G_VARIANT_TYPE_INT32,
1564                                                                                processes->data, processes->len,
1565                                                                                sizeof (GPid)),
1566                                                     choices, NULL,
1567                                                     call_processes_proxy_cb, operation);
1568 }
1569
1570 static void
1571 gtk_mount_operation_show_processes_do_gtk (GtkMountOperation *op,
1572                                            const char        *message,
1573                                            GArray            *processes,
1574                                            const char        *choices[])
1575 {
1576   GtkMountOperationPrivate *priv;
1577   GtkWidget *dialog = NULL;
1578
1579   g_return_if_fail (GTK_IS_MOUNT_OPERATION (op));
1580   g_return_if_fail (message != NULL);
1581   g_return_if_fail (processes != NULL);
1582   g_return_if_fail (choices != NULL);
1583
1584   priv = op->priv;
1585
1586   if (priv->process_list_store == NULL)
1587     {
1588       /* need to create the dialog */
1589       dialog = create_show_processes_dialog (op, message, choices);
1590     }
1591
1592   /* otherwise, we're showing the dialog, assume messages+choices hasn't changed */
1593
1594   update_process_list_store (op,
1595                              priv->process_list_store,
1596                              processes);
1597
1598   if (dialog != NULL)
1599     {
1600       gtk_widget_show_all (dialog);
1601     }
1602 }
1603
1604
1605 static void
1606 gtk_mount_operation_show_processes (GMountOperation *op,
1607                                     const char      *message,
1608                                     GArray          *processes,
1609                                     const char      *choices[])
1610 {
1611
1612   GtkMountOperation *operation;
1613   gboolean use_gtk;
1614
1615   operation = GTK_MOUNT_OPERATION (op);
1616   use_gtk = (operation->priv->handler == NULL);
1617
1618   if (use_gtk)
1619     gtk_mount_operation_show_processes_do_gtk (operation, message, processes, choices);
1620   else
1621     gtk_mount_operation_show_processes_do_proxy (operation, message, processes, choices);
1622 }
1623
1624 static void
1625 gtk_mount_operation_aborted (GMountOperation *op)
1626 {
1627   GtkMountOperationPrivate *priv;
1628
1629   priv = GTK_MOUNT_OPERATION (op)->priv;
1630
1631   if (priv->dialog != NULL)
1632     {
1633       gtk_widget_destroy (GTK_WIDGET (priv->dialog));
1634       priv->dialog = NULL;
1635       g_object_notify (G_OBJECT (op), "is-showing");
1636       g_object_unref (op);
1637     }
1638
1639   if (priv->handler != NULL)
1640     {
1641       _gtk_mount_operation_handler_call_close (priv->handler, NULL, NULL, NULL);
1642
1643       priv->handler_showing = FALSE;
1644       g_object_notify (G_OBJECT (op), "is-showing");
1645     }
1646 }
1647
1648 /**
1649  * gtk_mount_operation_new:
1650  * @parent: (allow-none): transient parent of the window, or %NULL
1651  *
1652  * Creates a new #GtkMountOperation
1653  *
1654  * Returns: a new #GtkMountOperation
1655  *
1656  * Since: 2.14
1657  */
1658 GMountOperation *
1659 gtk_mount_operation_new (GtkWindow *parent)
1660 {
1661   GMountOperation *mount_operation;
1662
1663   mount_operation = g_object_new (GTK_TYPE_MOUNT_OPERATION,
1664                                   "parent", parent, NULL);
1665
1666   return mount_operation;
1667 }
1668
1669 /**
1670  * gtk_mount_operation_is_showing:
1671  * @op: a #GtkMountOperation
1672  *
1673  * Returns whether the #GtkMountOperation is currently displaying
1674  * a window.
1675  *
1676  * Returns: %TRUE if @op is currently displaying a window
1677  *
1678  * Since: 2.14
1679  */
1680 gboolean
1681 gtk_mount_operation_is_showing (GtkMountOperation *op)
1682 {
1683   g_return_val_if_fail (GTK_IS_MOUNT_OPERATION (op), FALSE);
1684
1685   return op->priv->dialog != NULL;
1686 }
1687
1688 /**
1689  * gtk_mount_operation_set_parent:
1690  * @op: a #GtkMountOperation
1691  * @parent: (allow-none): transient parent of the window, or %NULL
1692  *
1693  * Sets the transient parent for windows shown by the
1694  * #GtkMountOperation.
1695  *
1696  * Since: 2.14
1697  */
1698 void
1699 gtk_mount_operation_set_parent (GtkMountOperation *op,
1700                                 GtkWindow         *parent)
1701 {
1702   GtkMountOperationPrivate *priv;
1703
1704   g_return_if_fail (GTK_IS_MOUNT_OPERATION (op));
1705   g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent));
1706
1707   priv = op->priv;
1708
1709   if (priv->parent_window == parent)
1710     return;
1711
1712   if (priv->parent_window)
1713     {
1714       g_signal_handlers_disconnect_by_func (priv->parent_window,
1715                                             gtk_widget_destroyed,
1716                                             &priv->parent_window);
1717       priv->parent_window = NULL;
1718     }
1719
1720   if (parent)
1721     {
1722       priv->parent_window = g_object_ref (parent);
1723
1724       g_signal_connect (parent, "destroy",
1725                         G_CALLBACK (gtk_widget_destroyed),
1726                         &priv->parent_window);
1727
1728       if (priv->dialog)
1729         gtk_window_set_transient_for (GTK_WINDOW (priv->dialog), parent);
1730     }
1731
1732   g_object_notify (G_OBJECT (op), "parent");
1733 }
1734
1735 /**
1736  * gtk_mount_operation_get_parent:
1737  * @op: a #GtkMountOperation
1738  *
1739  * Gets the transient parent used by the #GtkMountOperation
1740  *
1741  * Returns: (transfer none): the transient parent for windows shown by @op
1742  *
1743  * Since: 2.14
1744  */
1745 GtkWindow *
1746 gtk_mount_operation_get_parent (GtkMountOperation *op)
1747 {
1748   g_return_val_if_fail (GTK_IS_MOUNT_OPERATION (op), NULL);
1749
1750   return op->priv->parent_window;
1751 }
1752
1753 /**
1754  * gtk_mount_operation_set_screen:
1755  * @op: a #GtkMountOperation
1756  * @screen: a #GdkScreen
1757  *
1758  * Sets the screen to show windows of the #GtkMountOperation on.
1759  *
1760  * Since: 2.14
1761  */
1762 void
1763 gtk_mount_operation_set_screen (GtkMountOperation *op,
1764                                 GdkScreen         *screen)
1765 {
1766   GtkMountOperationPrivate *priv;
1767
1768   g_return_if_fail (GTK_IS_MOUNT_OPERATION (op));
1769   g_return_if_fail (GDK_IS_SCREEN (screen));
1770
1771   priv = op->priv;
1772
1773   if (priv->screen == screen)
1774     return;
1775
1776   if (priv->screen)
1777     g_object_unref (priv->screen);
1778
1779   priv->screen = g_object_ref (screen);
1780
1781   if (priv->dialog)
1782     gtk_window_set_screen (GTK_WINDOW (priv->dialog), screen);
1783
1784   g_object_notify (G_OBJECT (op), "screen");
1785 }
1786
1787 /**
1788  * gtk_mount_operation_get_screen:
1789  * @op: a #GtkMountOperation
1790  *
1791  * Gets the screen on which windows of the #GtkMountOperation
1792  * will be shown.
1793  *
1794  * Returns: (transfer none): the screen on which windows of @op are shown
1795  *
1796  * Since: 2.14
1797  */
1798 GdkScreen *
1799 gtk_mount_operation_get_screen (GtkMountOperation *op)
1800 {
1801   GtkMountOperationPrivate *priv;
1802
1803   g_return_val_if_fail (GTK_IS_MOUNT_OPERATION (op), NULL);
1804
1805   priv = op->priv;
1806
1807   if (priv->dialog)
1808     return gtk_window_get_screen (GTK_WINDOW (priv->dialog));
1809   else if (priv->parent_window)
1810     return gtk_window_get_screen (GTK_WINDOW (priv->parent_window));
1811   else if (priv->screen)
1812     return priv->screen;
1813   else
1814     return gdk_screen_get_default ();
1815 }