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 <errno.h> |
29 | #include <string.h> |
30 | |
31 | #include "gtkmountoperationprivate.h" |
32 | #include "gtkbox.h" |
33 | #include "gtkdbusgenerated.h" |
34 | #include "gtkentry.h" |
35 | #include "gtkbox.h" |
36 | #include "gtkintl.h" |
37 | #include "gtklabel.h" |
38 | #include "gtkmessagedialog.h" |
39 | #include "gtkmountoperation.h" |
40 | #include "gtkprivate.h" |
41 | #include "gtkcheckbutton.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 "gtkmain.h" |
51 | #include "gtksettings.h" |
52 | #include "gtkdialogprivate.h" |
53 | #include "gtkgestureclick.h" |
54 | #include "gtkmodelbuttonprivate.h" |
55 | #include "gtkpopover.h" |
56 | #include "gtksnapshot.h" |
57 | #include "gdktextureprivate.h" |
58 | #include "gtkshortcutcontroller.h" |
59 | #include "gtkshortcuttrigger.h" |
60 | #include "gtkshortcutaction.h" |
61 | #include "gtkshortcut.h" |
62 | #include "gtkliststore.h" |
63 | #include <glib/gprintf.h> |
64 | |
65 | /** |
66 | * GtkMountOperation: |
67 | * |
68 | * `GtkMountOperation` is an implementation of `GMountOperation`. |
69 | * |
70 | * The functions and objects described here make working with GTK and |
71 | * GIO more convenient. |
72 | * |
73 | * `GtkMountOperation` is needed when mounting volumes: |
74 | * It is an implementation of `GMountOperation` that can be used with |
75 | * GIO functions for mounting volumes such as |
76 | * g_file_mount_enclosing_volume(), g_file_mount_mountable(), |
77 | * g_volume_mount(), g_mount_unmount_with_operation() and others. |
78 | * |
79 | * When necessary, `GtkMountOperation` shows dialogs to let the user |
80 | * enter passwords, ask questions or show processes blocking unmount. |
81 | */ |
82 | |
83 | static void gtk_mount_operation_finalize (GObject *object); |
84 | static void gtk_mount_operation_set_property (GObject *object, |
85 | guint prop_id, |
86 | const GValue *value, |
87 | GParamSpec *pspec); |
88 | static void gtk_mount_operation_get_property (GObject *object, |
89 | guint prop_id, |
90 | GValue *value, |
91 | GParamSpec *pspec); |
92 | |
93 | static void gtk_mount_operation_ask_password (GMountOperation *op, |
94 | const char *message, |
95 | const char *default_user, |
96 | const char *default_domain, |
97 | GAskPasswordFlags flags); |
98 | |
99 | static void gtk_mount_operation_ask_question (GMountOperation *op, |
100 | const char *message, |
101 | const char *choices[]); |
102 | |
103 | static void gtk_mount_operation_show_processes (GMountOperation *op, |
104 | const char *message, |
105 | GArray *processes, |
106 | const char *choices[]); |
107 | |
108 | static void gtk_mount_operation_aborted (GMountOperation *op); |
109 | |
110 | struct _GtkMountOperationPrivate { |
111 | GtkWindow *parent_window; |
112 | GtkDialog *dialog; |
113 | GdkDisplay *display; |
114 | |
115 | /* bus proxy */ |
116 | _GtkMountOperationHandler *handler; |
117 | GCancellable *cancellable; |
118 | gboolean handler_showing; |
119 | |
120 | /* for the ask-password dialog */ |
121 | GtkWidget *grid; |
122 | GtkWidget *username_entry; |
123 | GtkWidget *domain_entry; |
124 | GtkWidget *password_entry; |
125 | GtkWidget *pim_entry; |
126 | GtkWidget *anonymous_toggle; |
127 | GtkWidget *tcrypt_hidden_toggle; |
128 | GtkWidget *tcrypt_system_toggle; |
129 | GList *user_widgets; |
130 | |
131 | GAskPasswordFlags ask_flags; |
132 | GPasswordSave password_save; |
133 | gboolean anonymous; |
134 | |
135 | /* for the show-processes dialog */ |
136 | GtkWidget *process_tree_view; |
137 | GtkListStore *process_list_store; |
138 | }; |
139 | |
140 | enum { |
141 | PROP_0, |
142 | PROP_PARENT, |
143 | PROP_IS_SHOWING, |
144 | PROP_DISPLAY |
145 | |
146 | }; |
147 | |
148 | G_DEFINE_TYPE_WITH_PRIVATE (GtkMountOperation, gtk_mount_operation, G_TYPE_MOUNT_OPERATION) |
149 | |
150 | static void |
151 | gtk_mount_operation_class_init (GtkMountOperationClass *klass) |
152 | { |
153 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
154 | GMountOperationClass *mount_op_class = G_MOUNT_OPERATION_CLASS (klass); |
155 | |
156 | object_class->finalize = gtk_mount_operation_finalize; |
157 | object_class->get_property = gtk_mount_operation_get_property; |
158 | object_class->set_property = gtk_mount_operation_set_property; |
159 | |
160 | mount_op_class->ask_password = gtk_mount_operation_ask_password; |
161 | mount_op_class->ask_question = gtk_mount_operation_ask_question; |
162 | mount_op_class->show_processes = gtk_mount_operation_show_processes; |
163 | mount_op_class->aborted = gtk_mount_operation_aborted; |
164 | |
165 | /** |
166 | * GtkMountOperation:parent: (attributes org.gtk.Property.get=gtk_mount_operation_get_parent org.gtk.Property.set=gtk_mount_operation_set_parent) |
167 | * |
168 | * The parent window. |
169 | */ |
170 | g_object_class_install_property (oclass: object_class, |
171 | property_id: PROP_PARENT, |
172 | pspec: g_param_spec_object (name: "parent" , |
173 | P_("Parent" ), |
174 | P_("The parent window" ), |
175 | GTK_TYPE_WINDOW, |
176 | GTK_PARAM_READWRITE)); |
177 | |
178 | /** |
179 | * GtkMountOperation:is-showing: (attributes org.gtk.Property.get=gtk_mount_operation_is_showing) |
180 | * |
181 | * Whether a dialog is currently shown. |
182 | */ |
183 | g_object_class_install_property (oclass: object_class, |
184 | property_id: PROP_IS_SHOWING, |
185 | pspec: g_param_spec_boolean (name: "is-showing" , |
186 | P_("Is Showing" ), |
187 | P_("Are we showing a dialog" ), |
188 | FALSE, |
189 | GTK_PARAM_READABLE)); |
190 | |
191 | /** |
192 | * GtkMountOperation:display: (attributes org.gtk.Property.get=gtk_mount_operation_get_display org.gtk.Property.set=gtk_mount_operation_set_display) |
193 | * |
194 | * The display where dialogs will be shown. |
195 | */ |
196 | g_object_class_install_property (oclass: object_class, |
197 | property_id: PROP_DISPLAY, |
198 | pspec: g_param_spec_object (name: "display" , |
199 | P_("Display" ), |
200 | P_("The display where this window will be displayed." ), |
201 | GDK_TYPE_DISPLAY, |
202 | GTK_PARAM_READWRITE)); |
203 | } |
204 | |
205 | static void |
206 | gtk_mount_operation_init (GtkMountOperation *operation) |
207 | { |
208 | char *name_owner; |
209 | |
210 | operation->priv = gtk_mount_operation_get_instance_private (self: operation); |
211 | |
212 | operation->priv->handler = |
213 | _gtk_mount_operation_handler_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, |
214 | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, |
215 | "org.gtk.MountOperationHandler" , |
216 | "/org/gtk/MountOperationHandler" , |
217 | NULL, NULL); |
218 | if (!operation->priv->handler) |
219 | return; |
220 | |
221 | name_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (operation->priv->handler)); |
222 | if (!name_owner) |
223 | g_clear_object (&operation->priv->handler); |
224 | g_free (mem: name_owner); |
225 | |
226 | if (operation->priv->handler) |
227 | g_dbus_proxy_set_default_timeout (G_DBUS_PROXY (operation->priv->handler), G_MAXINT); |
228 | } |
229 | |
230 | static void |
231 | parent_destroyed (GtkWidget *parent, |
232 | gpointer **pointer) |
233 | { |
234 | *pointer = NULL; |
235 | } |
236 | |
237 | static void |
238 | gtk_mount_operation_finalize (GObject *object) |
239 | { |
240 | GtkMountOperation *operation = GTK_MOUNT_OPERATION (object); |
241 | GtkMountOperationPrivate *priv = operation->priv; |
242 | |
243 | if (priv->user_widgets) |
244 | g_list_free (list: priv->user_widgets); |
245 | |
246 | if (priv->parent_window) |
247 | { |
248 | g_signal_handlers_disconnect_by_func (priv->parent_window, |
249 | parent_destroyed, |
250 | &priv->parent_window); |
251 | g_object_unref (object: priv->parent_window); |
252 | } |
253 | |
254 | if (priv->display) |
255 | g_object_unref (object: priv->display); |
256 | |
257 | if (priv->handler) |
258 | g_object_unref (object: priv->handler); |
259 | |
260 | G_OBJECT_CLASS (gtk_mount_operation_parent_class)->finalize (object); |
261 | } |
262 | |
263 | static void |
264 | gtk_mount_operation_set_property (GObject *object, |
265 | guint prop_id, |
266 | const GValue *value, |
267 | GParamSpec *pspec) |
268 | { |
269 | GtkMountOperation *operation = GTK_MOUNT_OPERATION (object); |
270 | |
271 | switch (prop_id) |
272 | { |
273 | case PROP_PARENT: |
274 | gtk_mount_operation_set_parent (op: operation, parent: g_value_get_object (value)); |
275 | break; |
276 | |
277 | case PROP_DISPLAY: |
278 | gtk_mount_operation_set_display (op: operation, display: g_value_get_object (value)); |
279 | break; |
280 | |
281 | case PROP_IS_SHOWING: |
282 | default: |
283 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
284 | break; |
285 | } |
286 | } |
287 | |
288 | static void |
289 | gtk_mount_operation_get_property (GObject *object, |
290 | guint prop_id, |
291 | GValue *value, |
292 | GParamSpec *pspec) |
293 | { |
294 | GtkMountOperation *operation = GTK_MOUNT_OPERATION (object); |
295 | GtkMountOperationPrivate *priv = operation->priv; |
296 | |
297 | switch (prop_id) |
298 | { |
299 | case PROP_PARENT: |
300 | g_value_set_object (value, v_object: priv->parent_window); |
301 | break; |
302 | |
303 | case PROP_IS_SHOWING: |
304 | g_value_set_boolean (value, v_boolean: priv->dialog != NULL || priv->handler_showing); |
305 | break; |
306 | |
307 | case PROP_DISPLAY: |
308 | g_value_set_object (value, v_object: priv->display); |
309 | break; |
310 | |
311 | default: |
312 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
313 | break; |
314 | } |
315 | } |
316 | |
317 | static void |
318 | gtk_mount_operation_proxy_finish (GtkMountOperation *op, |
319 | GMountOperationResult result) |
320 | { |
321 | _gtk_mount_operation_handler_call_close (op->priv->handler, NULL, NULL, NULL); |
322 | |
323 | op->priv->handler_showing = FALSE; |
324 | g_object_notify (G_OBJECT (op), property_name: "is-showing" ); |
325 | |
326 | g_mount_operation_reply (G_MOUNT_OPERATION (op), result); |
327 | |
328 | /* drop the reference acquired when calling the proxy method */ |
329 | g_object_unref (object: op); |
330 | } |
331 | |
332 | static void |
333 | remember_button_toggled (GtkCheckButton *button, |
334 | GtkMountOperation *operation) |
335 | { |
336 | GtkMountOperationPrivate *priv = operation->priv; |
337 | |
338 | if (gtk_check_button_get_active (self: button)) |
339 | { |
340 | gpointer data; |
341 | |
342 | data = g_object_get_data (G_OBJECT (button), key: "password-save" ); |
343 | priv->password_save = GPOINTER_TO_INT (data); |
344 | } |
345 | } |
346 | |
347 | static void |
348 | pw_dialog_got_response (GtkDialog *dialog, |
349 | int response_id, |
350 | GtkMountOperation *mount_op) |
351 | { |
352 | GtkMountOperationPrivate *priv = mount_op->priv; |
353 | GMountOperation *op = G_MOUNT_OPERATION (mount_op); |
354 | |
355 | if (response_id == GTK_RESPONSE_OK) |
356 | { |
357 | const char *text; |
358 | |
359 | if (priv->ask_flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED) |
360 | g_mount_operation_set_anonymous (op, anonymous: priv->anonymous); |
361 | |
362 | if (priv->username_entry) |
363 | { |
364 | text = gtk_editable_get_text (GTK_EDITABLE (priv->username_entry)); |
365 | g_mount_operation_set_username (op, username: text); |
366 | } |
367 | |
368 | if (priv->domain_entry) |
369 | { |
370 | text = gtk_editable_get_text (GTK_EDITABLE (priv->domain_entry)); |
371 | g_mount_operation_set_domain (op, domain: text); |
372 | } |
373 | |
374 | if (priv->password_entry) |
375 | { |
376 | text = gtk_editable_get_text (GTK_EDITABLE (priv->password_entry)); |
377 | g_mount_operation_set_password (op, password: text); |
378 | } |
379 | |
380 | if (priv->pim_entry) |
381 | { |
382 | text = gtk_editable_get_text (GTK_EDITABLE (priv->pim_entry)); |
383 | if (text && strlen (s: text) > 0) |
384 | { |
385 | guint64 pim; |
386 | char *end = NULL; |
387 | |
388 | errno = 0; |
389 | pim = g_ascii_strtoull (nptr: text, endptr: &end, base: 10); |
390 | if (errno == 0 && pim <= G_MAXUINT && end != text) |
391 | g_mount_operation_set_pim (op, pim: (guint) pim); |
392 | } |
393 | } |
394 | |
395 | if (priv->tcrypt_hidden_toggle && gtk_check_button_get_active (GTK_CHECK_BUTTON (priv->tcrypt_hidden_toggle))) |
396 | g_mount_operation_set_is_tcrypt_hidden_volume (op, TRUE); |
397 | |
398 | if (priv->tcrypt_system_toggle && gtk_check_button_get_active (GTK_CHECK_BUTTON (priv->tcrypt_system_toggle))) |
399 | g_mount_operation_set_is_tcrypt_system_volume (op, TRUE); |
400 | |
401 | if (priv->ask_flags & G_ASK_PASSWORD_SAVING_SUPPORTED) |
402 | g_mount_operation_set_password_save (op, save: priv->password_save); |
403 | |
404 | g_mount_operation_reply (op, result: G_MOUNT_OPERATION_HANDLED); |
405 | } |
406 | else |
407 | g_mount_operation_reply (op, result: G_MOUNT_OPERATION_ABORTED); |
408 | |
409 | priv->dialog = NULL; |
410 | g_object_notify (G_OBJECT (op), property_name: "is-showing" ); |
411 | gtk_window_destroy (GTK_WINDOW (dialog)); |
412 | g_object_unref (object: op); |
413 | } |
414 | |
415 | static gboolean |
416 | entry_has_input (GtkWidget *entry_widget) |
417 | { |
418 | const char *text; |
419 | |
420 | if (entry_widget == NULL) |
421 | return TRUE; |
422 | |
423 | text = gtk_editable_get_text (GTK_EDITABLE (entry_widget)); |
424 | |
425 | return text != NULL && text[0] != '\0'; |
426 | } |
427 | |
428 | static gboolean |
429 | pim_entry_is_valid (GtkWidget *entry_widget) |
430 | { |
431 | const char *text; |
432 | char *end = NULL; |
433 | guint64 pim; |
434 | |
435 | if (entry_widget == NULL) |
436 | return TRUE; |
437 | |
438 | text = gtk_editable_get_text (GTK_EDITABLE (entry_widget)); |
439 | /* An empty PIM entry is OK */ |
440 | if (text == NULL || text[0] == '\0') |
441 | return TRUE; |
442 | |
443 | errno = 0; |
444 | pim = g_ascii_strtoull (nptr: text, endptr: &end, base: 10); |
445 | if (errno || pim > G_MAXUINT || end == text) |
446 | return FALSE; |
447 | else |
448 | return TRUE; |
449 | } |
450 | |
451 | static gboolean |
452 | pw_dialog_input_is_valid (GtkMountOperation *operation) |
453 | { |
454 | GtkMountOperationPrivate *priv = operation->priv; |
455 | gboolean is_valid = TRUE; |
456 | |
457 | /* We don't require password to be non-empty here |
458 | * since there are situations where it is not needed, |
459 | * see bug 578365. |
460 | * We may add a way for the backend to specify that it |
461 | * definitively needs a password. |
462 | */ |
463 | is_valid = entry_has_input (entry_widget: priv->username_entry) && |
464 | entry_has_input (entry_widget: priv->domain_entry) && |
465 | pim_entry_is_valid (entry_widget: priv->pim_entry); |
466 | |
467 | return is_valid; |
468 | } |
469 | |
470 | static void |
471 | pw_dialog_verify_input (GtkEditable *editable, |
472 | GtkMountOperation *operation) |
473 | { |
474 | GtkMountOperationPrivate *priv = operation->priv; |
475 | gboolean is_valid; |
476 | |
477 | is_valid = pw_dialog_input_is_valid (operation); |
478 | gtk_dialog_set_response_sensitive (GTK_DIALOG (priv->dialog), |
479 | response_id: GTK_RESPONSE_OK, |
480 | setting: is_valid); |
481 | } |
482 | |
483 | static void |
484 | pw_dialog_anonymous_toggled (GtkWidget *widget, |
485 | GtkMountOperation *operation) |
486 | { |
487 | GtkMountOperationPrivate *priv = operation->priv; |
488 | gboolean is_valid; |
489 | GList *l; |
490 | |
491 | priv->anonymous = widget == priv->anonymous_toggle; |
492 | |
493 | if (priv->anonymous) |
494 | is_valid = TRUE; |
495 | else |
496 | is_valid = pw_dialog_input_is_valid (operation); |
497 | |
498 | for (l = priv->user_widgets; l != NULL; l = l->next) |
499 | { |
500 | gtk_widget_set_sensitive (GTK_WIDGET (l->data), sensitive: !priv->anonymous); |
501 | } |
502 | |
503 | gtk_dialog_set_response_sensitive (GTK_DIALOG (priv->dialog), |
504 | response_id: GTK_RESPONSE_OK, |
505 | setting: is_valid); |
506 | } |
507 | |
508 | |
509 | static void |
510 | pw_dialog_cycle_focus (GtkWidget *widget, |
511 | GtkMountOperation *operation) |
512 | { |
513 | GtkMountOperationPrivate *priv; |
514 | GtkWidget *next_widget = NULL; |
515 | |
516 | priv = operation->priv; |
517 | |
518 | if (widget == priv->username_entry) |
519 | { |
520 | if (priv->domain_entry != NULL) |
521 | next_widget = priv->domain_entry; |
522 | else if (priv->password_entry != NULL) |
523 | next_widget = priv->password_entry; |
524 | } |
525 | else if (widget == priv->domain_entry && priv->password_entry) |
526 | next_widget = priv->password_entry; |
527 | |
528 | if (next_widget) |
529 | gtk_widget_grab_focus (widget: next_widget); |
530 | else if (pw_dialog_input_is_valid (operation)) |
531 | gtk_widget_activate_default (widget); |
532 | } |
533 | |
534 | static GtkWidget * |
535 | table_add_entry (GtkMountOperation *operation, |
536 | int row, |
537 | const char *label_text, |
538 | const char *value, |
539 | gpointer user_data) |
540 | { |
541 | GtkWidget *entry; |
542 | GtkWidget *label; |
543 | |
544 | label = gtk_label_new_with_mnemonic (str: label_text); |
545 | gtk_widget_set_halign (widget: label, align: GTK_ALIGN_END); |
546 | gtk_widget_set_valign (widget: label, align: GTK_ALIGN_CENTER); |
547 | gtk_widget_set_hexpand (widget: label, FALSE); |
548 | operation->priv->user_widgets = g_list_prepend (list: operation->priv->user_widgets, data: label); |
549 | |
550 | entry = gtk_entry_new (); |
551 | gtk_widget_set_hexpand (widget: entry, TRUE); |
552 | |
553 | if (value) |
554 | gtk_editable_set_text (GTK_EDITABLE (entry), text: value); |
555 | |
556 | gtk_grid_attach (GTK_GRID (operation->priv->grid), child: label, column: 0, row, width: 1, height: 1); |
557 | gtk_grid_attach (GTK_GRID (operation->priv->grid), child: entry, column: 1, row, width: 1, height: 1); |
558 | gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget: entry); |
559 | operation->priv->user_widgets = g_list_prepend (list: operation->priv->user_widgets, data: entry); |
560 | |
561 | g_signal_connect (entry, "changed" , |
562 | G_CALLBACK (pw_dialog_verify_input), user_data); |
563 | |
564 | g_signal_connect (entry, "activate" , |
565 | G_CALLBACK (pw_dialog_cycle_focus), user_data); |
566 | |
567 | return entry; |
568 | } |
569 | |
570 | static void |
571 | gtk_mount_operation_ask_password_do_gtk (GtkMountOperation *operation, |
572 | const char *message, |
573 | const char *default_user, |
574 | const char *default_domain) |
575 | { |
576 | GtkMountOperationPrivate *priv; |
577 | GtkWidget *widget; |
578 | GtkDialog *dialog; |
579 | GtkWindow *window; |
580 | GtkWidget *hbox, *main_vbox, *icon; |
581 | GtkWidget *grid; |
582 | GtkWidget *label; |
583 | GtkWidget *content_area; |
584 | gboolean can_anonymous; |
585 | guint rows; |
586 | char *primary; |
587 | const char *secondary = NULL; |
588 | gboolean ; |
589 | |
590 | priv = operation->priv; |
591 | |
592 | g_object_get (object: gtk_settings_get_default (), |
593 | first_property_name: "gtk-dialogs-use-header" , &use_header, |
594 | NULL); |
595 | widget = g_object_new (GTK_TYPE_DIALOG, |
596 | first_property_name: "use-header-bar" , use_header, |
597 | NULL); |
598 | dialog = GTK_DIALOG (widget); |
599 | window = GTK_WINDOW (widget); |
600 | |
601 | priv->dialog = dialog; |
602 | |
603 | content_area = gtk_dialog_get_content_area (dialog); |
604 | |
605 | gtk_window_set_resizable (window, FALSE); |
606 | gtk_window_set_title (window, title: "" ); |
607 | gtk_window_set_icon_name (window, name: "dialog-password" ); |
608 | |
609 | gtk_dialog_add_buttons (dialog, |
610 | _("_Cancel" ), GTK_RESPONSE_CANCEL, |
611 | _("Co_nnect" ), GTK_RESPONSE_OK, |
612 | NULL); |
613 | gtk_dialog_set_default_response (dialog, response_id: GTK_RESPONSE_OK); |
614 | |
615 | |
616 | /* Build contents */ |
617 | hbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 12); |
618 | g_object_set (object: hbox, |
619 | first_property_name: "margin-start" , 12, |
620 | "margin-end" , 12, |
621 | "margin-top" , 12, |
622 | "margin-bottom" , 12, |
623 | NULL); |
624 | gtk_box_append (GTK_BOX (content_area), child: hbox); |
625 | |
626 | icon = gtk_image_new_from_icon_name (icon_name: "dialog-password" ); |
627 | gtk_image_set_icon_size (GTK_IMAGE (icon), icon_size: GTK_ICON_SIZE_LARGE); |
628 | |
629 | gtk_widget_set_halign (widget: icon, align: GTK_ALIGN_CENTER); |
630 | gtk_widget_set_valign (widget: icon, align: GTK_ALIGN_START); |
631 | gtk_box_append (GTK_BOX (hbox), child: icon); |
632 | |
633 | main_vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 18); |
634 | gtk_box_append (GTK_BOX (hbox), child: main_vbox); |
635 | |
636 | primary = strstr (haystack: message, needle: "\n" ); |
637 | if (primary) |
638 | { |
639 | secondary = primary + 1; |
640 | primary = g_strndup (str: message, n: primary - message); |
641 | } |
642 | |
643 | label = gtk_label_new (str: primary != NULL ? primary : message); |
644 | gtk_widget_set_halign (widget: label, align: GTK_ALIGN_START); |
645 | gtk_widget_set_valign (widget: label, align: GTK_ALIGN_CENTER); |
646 | gtk_label_set_wrap (GTK_LABEL (label), TRUE); |
647 | gtk_box_append (GTK_BOX (main_vbox), GTK_WIDGET (label)); |
648 | g_free (mem: primary); |
649 | gtk_widget_add_css_class (widget: label, css_class: "title-3" ); |
650 | |
651 | if (secondary != NULL) |
652 | { |
653 | label = gtk_label_new (str: secondary); |
654 | gtk_widget_set_halign (widget: label, align: GTK_ALIGN_START); |
655 | gtk_widget_set_valign (widget: label, align: GTK_ALIGN_CENTER); |
656 | gtk_label_set_wrap (GTK_LABEL (label), TRUE); |
657 | gtk_box_append (GTK_BOX (main_vbox), GTK_WIDGET (label)); |
658 | } |
659 | |
660 | grid = gtk_grid_new (); |
661 | operation->priv->grid = grid; |
662 | gtk_grid_set_row_spacing (GTK_GRID (grid), spacing: 12); |
663 | gtk_grid_set_column_spacing (GTK_GRID (grid), spacing: 12); |
664 | gtk_widget_set_margin_bottom (widget: grid, margin: 12); |
665 | gtk_box_append (GTK_BOX (main_vbox), child: grid); |
666 | |
667 | can_anonymous = priv->ask_flags & G_ASK_PASSWORD_ANONYMOUS_SUPPORTED; |
668 | |
669 | rows = 0; |
670 | |
671 | priv->anonymous_toggle = NULL; |
672 | if (can_anonymous) |
673 | { |
674 | GtkWidget *anon_box; |
675 | GtkWidget *choice; |
676 | |
677 | label = gtk_label_new (_("Connect As" )); |
678 | gtk_widget_set_halign (widget: label, align: GTK_ALIGN_END); |
679 | gtk_widget_set_valign (widget: label, align: GTK_ALIGN_START); |
680 | gtk_widget_set_hexpand (widget: label, FALSE); |
681 | gtk_grid_attach (GTK_GRID (grid), child: label, column: 0, row: rows, width: 1, height: 1); |
682 | |
683 | anon_box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0); |
684 | gtk_grid_attach (GTK_GRID (grid), child: anon_box, column: 1, row: rows++, width: 1, height: 1); |
685 | |
686 | choice = gtk_check_button_new_with_mnemonic (_("_Anonymous" )); |
687 | gtk_box_append (GTK_BOX (anon_box), child: choice); |
688 | g_signal_connect (choice, "toggled" , |
689 | G_CALLBACK (pw_dialog_anonymous_toggled), operation); |
690 | priv->anonymous_toggle = choice; |
691 | |
692 | choice = gtk_check_button_new_with_mnemonic (_("Registered U_ser" )); |
693 | gtk_check_button_set_group (GTK_CHECK_BUTTON (choice), GTK_CHECK_BUTTON (priv->anonymous_toggle)); |
694 | gtk_box_append (GTK_BOX (anon_box), child: choice); |
695 | g_signal_connect (choice, "toggled" , |
696 | G_CALLBACK (pw_dialog_anonymous_toggled), operation); |
697 | } |
698 | |
699 | priv->username_entry = NULL; |
700 | |
701 | if (priv->ask_flags & G_ASK_PASSWORD_NEED_USERNAME) |
702 | priv->username_entry = table_add_entry (operation, row: rows++, _("_Username" ), |
703 | value: default_user, user_data: operation); |
704 | |
705 | priv->domain_entry = NULL; |
706 | if (priv->ask_flags & G_ASK_PASSWORD_NEED_DOMAIN) |
707 | priv->domain_entry = table_add_entry (operation, row: rows++, _("_Domain" ), |
708 | value: default_domain, user_data: operation); |
709 | |
710 | priv->pim_entry = NULL; |
711 | if (priv->ask_flags & G_ASK_PASSWORD_TCRYPT) |
712 | { |
713 | GtkWidget *volume_type_label; |
714 | GtkWidget *volume_type_box; |
715 | |
716 | volume_type_label = gtk_label_new (_("Volume type" )); |
717 | gtk_widget_set_halign (widget: volume_type_label, align: GTK_ALIGN_END); |
718 | gtk_widget_set_hexpand (widget: volume_type_label, FALSE); |
719 | gtk_grid_attach (GTK_GRID (grid), child: volume_type_label, column: 0, row: rows, width: 1, height: 1); |
720 | priv->user_widgets = g_list_prepend (list: priv->user_widgets, data: volume_type_label); |
721 | |
722 | volume_type_box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 10); |
723 | gtk_grid_attach (GTK_GRID (grid), child: volume_type_box, column: 1, row: rows++, width: 1, height: 1); |
724 | priv->user_widgets = g_list_prepend (list: priv->user_widgets, data: volume_type_box); |
725 | |
726 | priv->tcrypt_hidden_toggle = gtk_check_button_new_with_mnemonic (_("_Hidden" )); |
727 | gtk_box_append (GTK_BOX (volume_type_box), child: priv->tcrypt_hidden_toggle); |
728 | |
729 | priv->tcrypt_system_toggle = gtk_check_button_new_with_mnemonic (_("_Windows system" )); |
730 | gtk_box_append (GTK_BOX (volume_type_box), child: priv->tcrypt_system_toggle); |
731 | |
732 | priv->pim_entry = table_add_entry (operation, row: rows++, _("_PIM" ), NULL, user_data: operation); |
733 | } |
734 | |
735 | priv->password_entry = NULL; |
736 | if (priv->ask_flags & G_ASK_PASSWORD_NEED_PASSWORD) |
737 | { |
738 | priv->password_entry = table_add_entry (operation, row: rows++, _("_Password" ), |
739 | NULL, user_data: operation); |
740 | gtk_entry_set_visibility (GTK_ENTRY (priv->password_entry), FALSE); |
741 | } |
742 | |
743 | if (priv->ask_flags & G_ASK_PASSWORD_SAVING_SUPPORTED) |
744 | { |
745 | GtkWidget *remember_box; |
746 | GtkWidget *choice; |
747 | GtkWidget *group; |
748 | GPasswordSave password_save; |
749 | |
750 | remember_box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 0); |
751 | gtk_grid_attach (GTK_GRID (grid), child: remember_box, column: 0, row: rows++, width: 2, height: 1); |
752 | priv->user_widgets = g_list_prepend (list: priv->user_widgets, data: remember_box); |
753 | |
754 | label = gtk_label_new (str: "" ); |
755 | gtk_box_append (GTK_BOX (remember_box), child: label); |
756 | |
757 | password_save = g_mount_operation_get_password_save (G_MOUNT_OPERATION (operation)); |
758 | priv->password_save = password_save; |
759 | |
760 | choice = gtk_check_button_new_with_mnemonic (_("Forget password _immediately" )); |
761 | gtk_check_button_set_active (GTK_CHECK_BUTTON (choice), |
762 | setting: password_save == G_PASSWORD_SAVE_NEVER); |
763 | g_object_set_data (G_OBJECT (choice), key: "password-save" , |
764 | GINT_TO_POINTER (G_PASSWORD_SAVE_NEVER)); |
765 | g_signal_connect (choice, "toggled" , |
766 | G_CALLBACK (remember_button_toggled), operation); |
767 | gtk_box_append (GTK_BOX (remember_box), child: choice); |
768 | group = choice; |
769 | |
770 | choice = gtk_check_button_new_with_mnemonic (_("Remember password until you _logout" )); |
771 | gtk_check_button_set_group (GTK_CHECK_BUTTON (choice), GTK_CHECK_BUTTON (group)); |
772 | gtk_check_button_set_active (GTK_CHECK_BUTTON (choice), |
773 | setting: password_save == G_PASSWORD_SAVE_FOR_SESSION); |
774 | g_object_set_data (G_OBJECT (choice), key: "password-save" , |
775 | GINT_TO_POINTER (G_PASSWORD_SAVE_FOR_SESSION)); |
776 | g_signal_connect (choice, "toggled" , |
777 | G_CALLBACK (remember_button_toggled), operation); |
778 | gtk_box_append (GTK_BOX (remember_box), child: choice); |
779 | group = choice; |
780 | |
781 | choice = gtk_check_button_new_with_mnemonic (_("Remember _forever" )); |
782 | gtk_check_button_set_group (GTK_CHECK_BUTTON (choice), GTK_CHECK_BUTTON (group)); |
783 | gtk_check_button_set_active (GTK_CHECK_BUTTON (choice), |
784 | setting: password_save == G_PASSWORD_SAVE_PERMANENTLY); |
785 | g_object_set_data (G_OBJECT (choice), key: "password-save" , |
786 | GINT_TO_POINTER (G_PASSWORD_SAVE_PERMANENTLY)); |
787 | g_signal_connect (choice, "toggled" , |
788 | G_CALLBACK (remember_button_toggled), operation); |
789 | gtk_box_append (GTK_BOX (remember_box), child: choice); |
790 | } |
791 | |
792 | g_signal_connect (G_OBJECT (dialog), "response" , |
793 | G_CALLBACK (pw_dialog_got_response), operation); |
794 | |
795 | if (can_anonymous) |
796 | { |
797 | /* The anonymous option will be active by default, |
798 | * ensure the toggled signal is emitted for it. |
799 | */ |
800 | g_signal_emit_by_name (instance: priv->anonymous_toggle, detailed_signal: "toggled" ); |
801 | } |
802 | else if (! pw_dialog_input_is_valid (operation)) |
803 | gtk_dialog_set_response_sensitive (dialog, response_id: GTK_RESPONSE_OK, FALSE); |
804 | |
805 | g_object_notify (G_OBJECT (operation), property_name: "is-showing" ); |
806 | |
807 | if (priv->parent_window) |
808 | { |
809 | gtk_window_set_transient_for (window, parent: priv->parent_window); |
810 | gtk_window_set_modal (window, TRUE); |
811 | } |
812 | else if (priv->display) |
813 | gtk_window_set_display (GTK_WINDOW (dialog), display: priv->display); |
814 | |
815 | gtk_widget_show (GTK_WIDGET (dialog)); |
816 | |
817 | g_object_ref (operation); |
818 | } |
819 | |
820 | static void |
821 | call_password_proxy_cb (GObject *source, |
822 | GAsyncResult *res, |
823 | gpointer user_data) |
824 | { |
825 | _GtkMountOperationHandler *proxy = _GTK_MOUNT_OPERATION_HANDLER (source); |
826 | GMountOperation *op = user_data; |
827 | GMountOperationResult result; |
828 | GVariant *result_details; |
829 | GVariantIter iter; |
830 | const char *key; |
831 | GVariant *value; |
832 | GError *error = NULL; |
833 | |
834 | if (!_gtk_mount_operation_handler_call_ask_password_finish (proxy, |
835 | &result, |
836 | &result_details, |
837 | res, |
838 | &error)) |
839 | { |
840 | result = G_MOUNT_OPERATION_ABORTED; |
841 | g_warning ("Shell mount operation error: %s" , error->message); |
842 | g_error_free (error); |
843 | goto out; |
844 | } |
845 | |
846 | g_variant_iter_init (iter: &iter, value: result_details); |
847 | while (g_variant_iter_loop (iter: &iter, format_string: "{&sv}" , &key, &value)) |
848 | { |
849 | if (strcmp (s1: key, s2: "password" ) == 0) |
850 | g_mount_operation_set_password (op, password: g_variant_get_string (value, NULL)); |
851 | else if (strcmp (s1: key, s2: "password_save" ) == 0) |
852 | g_mount_operation_set_password_save (op, save: g_variant_get_uint32 (value)); |
853 | else if (strcmp (s1: key, s2: "hidden_volume" ) == 0) |
854 | g_mount_operation_set_is_tcrypt_hidden_volume (op, hidden_volume: g_variant_get_boolean (value)); |
855 | else if (strcmp (s1: key, s2: "system_volume" ) == 0) |
856 | g_mount_operation_set_is_tcrypt_system_volume (op, system_volume: g_variant_get_boolean (value)); |
857 | else if (strcmp (s1: key, s2: "pim" ) == 0) |
858 | g_mount_operation_set_pim (op, pim: g_variant_get_uint32 (value)); |
859 | } |
860 | |
861 | out: |
862 | gtk_mount_operation_proxy_finish (GTK_MOUNT_OPERATION (op), result); |
863 | } |
864 | |
865 | static void |
866 | gtk_mount_operation_ask_password_do_proxy (GtkMountOperation *operation, |
867 | const char *message, |
868 | const char *default_user, |
869 | const char *default_domain) |
870 | { |
871 | char id[255]; |
872 | g_sprintf(string: id, format: "GtkMountOperation%p" , operation); |
873 | |
874 | operation->priv->handler_showing = TRUE; |
875 | g_object_notify (G_OBJECT (operation), property_name: "is-showing" ); |
876 | |
877 | /* keep a ref to the operation while the handler is showing */ |
878 | g_object_ref (operation); |
879 | |
880 | _gtk_mount_operation_handler_call_ask_password (operation->priv->handler, id, |
881 | message, "drive-harddisk" , |
882 | default_user, default_domain, |
883 | operation->priv->ask_flags, NULL, |
884 | call_password_proxy_cb, operation); |
885 | } |
886 | |
887 | static void |
888 | gtk_mount_operation_ask_password (GMountOperation *mount_op, |
889 | const char *message, |
890 | const char *default_user, |
891 | const char *default_domain, |
892 | GAskPasswordFlags flags) |
893 | { |
894 | GtkMountOperation *operation; |
895 | GtkMountOperationPrivate *priv; |
896 | gboolean use_gtk; |
897 | |
898 | operation = GTK_MOUNT_OPERATION (mount_op); |
899 | priv = operation->priv; |
900 | priv->ask_flags = flags; |
901 | |
902 | use_gtk = (operation->priv->handler == NULL) || |
903 | (priv->ask_flags & G_ASK_PASSWORD_NEED_DOMAIN) || |
904 | (priv->ask_flags & G_ASK_PASSWORD_NEED_USERNAME); |
905 | |
906 | if (use_gtk) |
907 | gtk_mount_operation_ask_password_do_gtk (operation, message, default_user, default_domain); |
908 | else |
909 | gtk_mount_operation_ask_password_do_proxy (operation, message, default_user, default_domain); |
910 | } |
911 | |
912 | static void |
913 | question_dialog_button_clicked (GtkDialog *dialog, |
914 | int button_number, |
915 | GMountOperation *op) |
916 | { |
917 | GtkMountOperationPrivate *priv; |
918 | GtkMountOperation *operation; |
919 | |
920 | operation = GTK_MOUNT_OPERATION (op); |
921 | priv = operation->priv; |
922 | |
923 | if (button_number >= 0) |
924 | { |
925 | g_mount_operation_set_choice (op, choice: button_number); |
926 | g_mount_operation_reply (op, result: G_MOUNT_OPERATION_HANDLED); |
927 | } |
928 | else |
929 | g_mount_operation_reply (op, result: G_MOUNT_OPERATION_ABORTED); |
930 | |
931 | priv->dialog = NULL; |
932 | g_object_notify (G_OBJECT (operation), property_name: "is-showing" ); |
933 | gtk_window_destroy (GTK_WINDOW (dialog)); |
934 | g_object_unref (object: op); |
935 | } |
936 | |
937 | static void |
938 | gtk_mount_operation_ask_question_do_gtk (GtkMountOperation *op, |
939 | const char *message, |
940 | const char *choices[]) |
941 | { |
942 | GtkMountOperationPrivate *priv; |
943 | GtkWidget *dialog; |
944 | const char *secondary = NULL; |
945 | char *primary; |
946 | int count, len = 0; |
947 | |
948 | g_return_if_fail (GTK_IS_MOUNT_OPERATION (op)); |
949 | g_return_if_fail (message != NULL); |
950 | g_return_if_fail (choices != NULL); |
951 | |
952 | priv = op->priv; |
953 | |
954 | primary = strstr (haystack: message, needle: "\n" ); |
955 | if (primary) |
956 | { |
957 | secondary = primary + 1; |
958 | primary = g_strndup (str: message, n: primary - message); |
959 | } |
960 | |
961 | dialog = gtk_message_dialog_new (parent: priv->parent_window, flags: 0, |
962 | type: GTK_MESSAGE_QUESTION, |
963 | buttons: GTK_BUTTONS_NONE, message_format: "%s" , |
964 | primary != NULL ? primary : message); |
965 | g_free (mem: primary); |
966 | |
967 | if (secondary) |
968 | gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), |
969 | message_format: "%s" , secondary); |
970 | |
971 | /* First count the items in the list then |
972 | * add the buttons in reverse order */ |
973 | |
974 | while (choices[len] != NULL) |
975 | len++; |
976 | |
977 | for (count = len - 1; count >= 0; count--) |
978 | gtk_dialog_add_button (GTK_DIALOG (dialog), button_text: choices[count], response_id: count); |
979 | |
980 | g_signal_connect (G_OBJECT (dialog), "response" , |
981 | G_CALLBACK (question_dialog_button_clicked), op); |
982 | |
983 | priv->dialog = GTK_DIALOG (dialog); |
984 | g_object_notify (G_OBJECT (op), property_name: "is-showing" ); |
985 | |
986 | if (priv->parent_window == NULL && priv->display) |
987 | gtk_window_set_display (GTK_WINDOW (dialog), display: priv->display); |
988 | |
989 | gtk_widget_show (widget: dialog); |
990 | g_object_ref (op); |
991 | } |
992 | |
993 | static void |
994 | call_question_proxy_cb (GObject *source, |
995 | GAsyncResult *res, |
996 | gpointer user_data) |
997 | { |
998 | _GtkMountOperationHandler *proxy = _GTK_MOUNT_OPERATION_HANDLER (source); |
999 | GMountOperation *op = user_data; |
1000 | GMountOperationResult result; |
1001 | GVariant *result_details; |
1002 | GVariantIter iter; |
1003 | const char *key; |
1004 | GVariant *value; |
1005 | GError *error = NULL; |
1006 | |
1007 | if (!_gtk_mount_operation_handler_call_ask_question_finish (proxy, |
1008 | &result, |
1009 | &result_details, |
1010 | res, |
1011 | &error)) |
1012 | { |
1013 | result = G_MOUNT_OPERATION_ABORTED; |
1014 | g_warning ("Shell mount operation error: %s" , error->message); |
1015 | g_error_free (error); |
1016 | goto out; |
1017 | } |
1018 | |
1019 | g_variant_iter_init (iter: &iter, value: result_details); |
1020 | while (g_variant_iter_loop (iter: &iter, format_string: "{&sv}" , &key, &value)) |
1021 | { |
1022 | if (strcmp (s1: key, s2: "choice" ) == 0) |
1023 | g_mount_operation_set_choice (op, choice: g_variant_get_int32 (value)); |
1024 | } |
1025 | |
1026 | out: |
1027 | gtk_mount_operation_proxy_finish (GTK_MOUNT_OPERATION (op), result); |
1028 | } |
1029 | |
1030 | static void |
1031 | gtk_mount_operation_ask_question_do_proxy (GtkMountOperation *operation, |
1032 | const char *message, |
1033 | const char *choices[]) |
1034 | { |
1035 | char id[255]; |
1036 | g_sprintf(string: id, format: "GtkMountOperation%p" , operation); |
1037 | |
1038 | operation->priv->handler_showing = TRUE; |
1039 | g_object_notify (G_OBJECT (operation), property_name: "is-showing" ); |
1040 | |
1041 | /* keep a ref to the operation while the handler is showing */ |
1042 | g_object_ref (operation); |
1043 | |
1044 | _gtk_mount_operation_handler_call_ask_question (operation->priv->handler, id, |
1045 | message, "drive-harddisk" , |
1046 | choices, NULL, |
1047 | call_question_proxy_cb, operation); |
1048 | } |
1049 | |
1050 | static void |
1051 | gtk_mount_operation_ask_question (GMountOperation *op, |
1052 | const char *message, |
1053 | const char *choices[]) |
1054 | { |
1055 | GtkMountOperation *operation; |
1056 | gboolean use_gtk; |
1057 | |
1058 | operation = GTK_MOUNT_OPERATION (op); |
1059 | use_gtk = (operation->priv->handler == NULL); |
1060 | |
1061 | if (use_gtk) |
1062 | gtk_mount_operation_ask_question_do_gtk (op: operation, message, choices); |
1063 | else |
1064 | gtk_mount_operation_ask_question_do_proxy (operation, message, choices); |
1065 | } |
1066 | |
1067 | static void |
1068 | show_processes_button_clicked (GtkDialog *dialog, |
1069 | int button_number, |
1070 | GMountOperation *op) |
1071 | { |
1072 | GtkMountOperationPrivate *priv; |
1073 | GtkMountOperation *operation; |
1074 | |
1075 | operation = GTK_MOUNT_OPERATION (op); |
1076 | priv = operation->priv; |
1077 | |
1078 | if (button_number >= 0) |
1079 | { |
1080 | g_mount_operation_set_choice (op, choice: button_number); |
1081 | g_mount_operation_reply (op, result: G_MOUNT_OPERATION_HANDLED); |
1082 | } |
1083 | else |
1084 | g_mount_operation_reply (op, result: G_MOUNT_OPERATION_ABORTED); |
1085 | |
1086 | priv->dialog = NULL; |
1087 | g_object_notify (G_OBJECT (operation), property_name: "is-showing" ); |
1088 | gtk_window_destroy (GTK_WINDOW (dialog)); |
1089 | g_object_unref (object: op); |
1090 | } |
1091 | |
1092 | static int |
1093 | pid_equal (gconstpointer a, |
1094 | gconstpointer b) |
1095 | { |
1096 | GPid pa, pb; |
1097 | |
1098 | pa = *((GPid *) a); |
1099 | pb = *((GPid *) b); |
1100 | |
1101 | return GPOINTER_TO_INT(pb) - GPOINTER_TO_INT(pa); |
1102 | } |
1103 | |
1104 | static void |
1105 | diff_sorted_arrays (GArray *array1, |
1106 | GArray *array2, |
1107 | GCompareFunc compare, |
1108 | GArray *added_indices, |
1109 | GArray *removed_indices) |
1110 | { |
1111 | int order; |
1112 | guint n1, n2; |
1113 | guint elem_size; |
1114 | |
1115 | n1 = n2 = 0; |
1116 | |
1117 | elem_size = g_array_get_element_size (array: array1); |
1118 | g_assert (elem_size == g_array_get_element_size (array2)); |
1119 | |
1120 | while (n1 < array1->len && n2 < array2->len) |
1121 | { |
1122 | order = (*compare) (((const char*) array1->data) + n1 * elem_size, |
1123 | ((const char*) array2->data) + n2 * elem_size); |
1124 | if (order < 0) |
1125 | { |
1126 | g_array_append_val (removed_indices, n1); |
1127 | n1++; |
1128 | } |
1129 | else if (order > 0) |
1130 | { |
1131 | g_array_append_val (added_indices, n2); |
1132 | n2++; |
1133 | } |
1134 | else |
1135 | { /* same item */ |
1136 | n1++; |
1137 | n2++; |
1138 | } |
1139 | } |
1140 | |
1141 | while (n1 < array1->len) |
1142 | { |
1143 | g_array_append_val (removed_indices, n1); |
1144 | n1++; |
1145 | } |
1146 | while (n2 < array2->len) |
1147 | { |
1148 | g_array_append_val (added_indices, n2); |
1149 | n2++; |
1150 | } |
1151 | } |
1152 | |
1153 | static GdkTexture * |
1154 | render_paintable_to_texture (GdkPaintable *paintable) |
1155 | { |
1156 | GtkSnapshot *snapshot; |
1157 | GskRenderNode *node; |
1158 | int width, height; |
1159 | cairo_surface_t *surface; |
1160 | cairo_t *cr; |
1161 | GdkTexture *texture; |
1162 | |
1163 | width = gdk_paintable_get_intrinsic_width (paintable); |
1164 | height = gdk_paintable_get_intrinsic_height (paintable); |
1165 | |
1166 | surface = cairo_image_surface_create (format: CAIRO_FORMAT_ARGB32, width, height); |
1167 | |
1168 | snapshot = gtk_snapshot_new (); |
1169 | gdk_paintable_snapshot (paintable, snapshot, width, height); |
1170 | node = gtk_snapshot_free_to_node (snapshot); |
1171 | |
1172 | cr = cairo_create (target: surface); |
1173 | gsk_render_node_draw (node, cr); |
1174 | cairo_destroy (cr); |
1175 | |
1176 | gsk_render_node_unref (node); |
1177 | |
1178 | texture = gdk_texture_new_for_surface (surface); |
1179 | cairo_surface_destroy (surface); |
1180 | |
1181 | return texture; |
1182 | } |
1183 | |
1184 | static void |
1185 | add_pid_to_process_list_store (GtkMountOperation *mount_operation, |
1186 | GtkMountOperationLookupContext *lookup_context, |
1187 | GtkListStore *list_store, |
1188 | GPid pid) |
1189 | { |
1190 | char *command_line; |
1191 | char *name; |
1192 | GdkTexture *texture; |
1193 | char *markup; |
1194 | GtkTreeIter iter; |
1195 | |
1196 | name = NULL; |
1197 | texture = NULL; |
1198 | command_line = NULL; |
1199 | _gtk_mount_operation_lookup_info (context: lookup_context, |
1200 | pid, |
1201 | size_pixels: 24, |
1202 | out_name: &name, |
1203 | out_command_line: &command_line, |
1204 | out_texture: &texture); |
1205 | |
1206 | if (name == NULL) |
1207 | name = g_strdup_printf (_("Unknown Application (PID %d)" ), (int) (gssize) pid); |
1208 | |
1209 | if (command_line == NULL) |
1210 | command_line = g_strdup (str: "" ); |
1211 | |
1212 | if (texture == NULL) |
1213 | { |
1214 | GtkIconTheme *theme; |
1215 | GtkIconPaintable *icon; |
1216 | |
1217 | theme = gtk_icon_theme_get_for_display (display: gtk_widget_get_display (GTK_WIDGET (mount_operation->priv->dialog))); |
1218 | icon = gtk_icon_theme_lookup_icon (self: theme, |
1219 | icon_name: "application-x-executable" , |
1220 | NULL, |
1221 | size: 24, scale: 1, |
1222 | direction: gtk_widget_get_direction (GTK_WIDGET (mount_operation->priv->dialog)), |
1223 | flags: 0); |
1224 | texture = render_paintable_to_texture (paintable: GDK_PAINTABLE (ptr: icon)); |
1225 | g_object_unref (object: icon); |
1226 | } |
1227 | |
1228 | markup = g_strdup_printf (format: "<b>%s</b>\n" |
1229 | "<small>%s</small>" , |
1230 | name, |
1231 | command_line); |
1232 | |
1233 | gtk_list_store_append (list_store, iter: &iter); |
1234 | gtk_list_store_set (list_store, iter: &iter, |
1235 | 0, texture, |
1236 | 1, markup, |
1237 | 2, pid, |
1238 | -1); |
1239 | |
1240 | if (texture != NULL) |
1241 | g_object_unref (object: texture); |
1242 | g_free (mem: markup); |
1243 | g_free (mem: name); |
1244 | g_free (mem: command_line); |
1245 | } |
1246 | |
1247 | static void |
1248 | remove_pid_from_process_list_store (GtkMountOperation *mount_operation, |
1249 | GtkListStore *list_store, |
1250 | GPid pid) |
1251 | { |
1252 | GtkTreeIter iter; |
1253 | GPid pid_of_item; |
1254 | |
1255 | if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), iter: &iter)) |
1256 | { |
1257 | do |
1258 | { |
1259 | gtk_tree_model_get (GTK_TREE_MODEL (list_store), |
1260 | iter: &iter, |
1261 | 2, &pid_of_item, |
1262 | -1); |
1263 | |
1264 | if (pid_of_item == pid) |
1265 | { |
1266 | gtk_list_store_remove (list_store, iter: &iter); |
1267 | break; |
1268 | } |
1269 | } |
1270 | while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store), iter: &iter)); |
1271 | } |
1272 | } |
1273 | |
1274 | |
1275 | static void |
1276 | update_process_list_store (GtkMountOperation *mount_operation, |
1277 | GtkListStore *list_store, |
1278 | GArray *processes) |
1279 | { |
1280 | guint n; |
1281 | GtkMountOperationLookupContext *lookup_context; |
1282 | GArray *current_pids; |
1283 | GArray *pid_indices_to_add; |
1284 | GArray *pid_indices_to_remove; |
1285 | GtkTreeIter iter; |
1286 | GPid pid; |
1287 | |
1288 | /* Just removing all items and adding new ones will screw up the |
1289 | * focus handling in the treeview - so compute the delta, and add/remove |
1290 | * items as appropriate |
1291 | */ |
1292 | current_pids = g_array_new (FALSE, FALSE, element_size: sizeof (GPid)); |
1293 | pid_indices_to_add = g_array_new (FALSE, FALSE, element_size: sizeof (int)); |
1294 | pid_indices_to_remove = g_array_new (FALSE, FALSE, element_size: sizeof (int)); |
1295 | |
1296 | if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), iter: &iter)) |
1297 | { |
1298 | do |
1299 | { |
1300 | gtk_tree_model_get (GTK_TREE_MODEL (list_store), |
1301 | iter: &iter, |
1302 | 2, &pid, |
1303 | -1); |
1304 | |
1305 | g_array_append_val (current_pids, pid); |
1306 | } |
1307 | while (gtk_tree_model_iter_next (GTK_TREE_MODEL (list_store), iter: &iter)); |
1308 | } |
1309 | |
1310 | g_array_sort (array: current_pids, compare_func: pid_equal); |
1311 | g_array_sort (array: processes, compare_func: pid_equal); |
1312 | |
1313 | diff_sorted_arrays (array1: current_pids, array2: processes, compare: pid_equal, added_indices: pid_indices_to_add, removed_indices: pid_indices_to_remove); |
1314 | |
1315 | for (n = 0; n < pid_indices_to_remove->len; n++) |
1316 | { |
1317 | pid = g_array_index (current_pids, GPid, n); |
1318 | remove_pid_from_process_list_store (mount_operation, list_store, pid); |
1319 | } |
1320 | |
1321 | if (pid_indices_to_add->len > 0) |
1322 | { |
1323 | lookup_context = _gtk_mount_operation_lookup_context_get (display: gtk_widget_get_display (widget: mount_operation->priv->process_tree_view)); |
1324 | for (n = 0; n < pid_indices_to_add->len; n++) |
1325 | { |
1326 | pid = g_array_index (processes, GPid, n); |
1327 | add_pid_to_process_list_store (mount_operation, lookup_context, list_store, pid); |
1328 | } |
1329 | _gtk_mount_operation_lookup_context_free (context: lookup_context); |
1330 | } |
1331 | |
1332 | /* select the first item, if we went from a zero to a non-zero amount of processes */ |
1333 | if (current_pids->len == 0 && pid_indices_to_add->len > 0) |
1334 | { |
1335 | if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), iter: &iter)) |
1336 | { |
1337 | GtkTreeSelection *tree_selection; |
1338 | tree_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (mount_operation->priv->process_tree_view)); |
1339 | gtk_tree_selection_select_iter (selection: tree_selection, iter: &iter); |
1340 | } |
1341 | } |
1342 | |
1343 | g_array_unref (array: current_pids); |
1344 | g_array_unref (array: pid_indices_to_add); |
1345 | g_array_unref (array: pid_indices_to_remove); |
1346 | } |
1347 | |
1348 | static void |
1349 | on_dialog_response (GtkDialog *dialog, |
1350 | int response) |
1351 | { |
1352 | /* GTK_RESPONSE_NONE means the dialog were programmatically destroy, e.g. that |
1353 | * GTK_DIALOG_DESTROY_WITH_PARENT kicked in - so it would trigger a warning to |
1354 | * destroy the dialog in that case |
1355 | */ |
1356 | if (response != GTK_RESPONSE_NONE) |
1357 | gtk_window_destroy (GTK_WINDOW (dialog)); |
1358 | } |
1359 | |
1360 | static void |
1361 | on_end_process_activated (GtkModelButton *button, |
1362 | gpointer user_data) |
1363 | { |
1364 | GtkMountOperation *op = GTK_MOUNT_OPERATION (user_data); |
1365 | GtkTreeSelection *selection; |
1366 | GtkTreeIter iter; |
1367 | GPid pid_to_kill; |
1368 | GError *error; |
1369 | |
1370 | selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (op->priv->process_tree_view)); |
1371 | |
1372 | if (!gtk_tree_selection_get_selected (selection, |
1373 | NULL, |
1374 | iter: &iter)) |
1375 | goto out; |
1376 | |
1377 | gtk_tree_model_get (GTK_TREE_MODEL (op->priv->process_list_store), |
1378 | iter: &iter, |
1379 | 2, &pid_to_kill, |
1380 | -1); |
1381 | |
1382 | /* TODO: We might want to either |
1383 | * |
1384 | * - Be smart about things and send SIGKILL rather than SIGTERM if |
1385 | * this is the second time the user requests killing a process |
1386 | * |
1387 | * - Or, easier (but worse user experience), offer both "End Process" |
1388 | * and "Terminate Process" options |
1389 | * |
1390 | * But that's not how things work right now.... |
1391 | */ |
1392 | error = NULL; |
1393 | if (!_gtk_mount_operation_kill_process (pid: pid_to_kill, error: &error)) |
1394 | { |
1395 | GtkWidget *dialog; |
1396 | |
1397 | /* Use GTK_DIALOG_DESTROY_WITH_PARENT here since the parent dialog can be |
1398 | * indeed be destroyed via the GMountOperation::abort signal... for example, |
1399 | * this is triggered if the user yanks the device while we are showing |
1400 | * the dialog... |
1401 | */ |
1402 | dialog = gtk_message_dialog_new (GTK_WINDOW (op->priv->dialog), |
1403 | flags: GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, |
1404 | type: GTK_MESSAGE_ERROR, |
1405 | buttons: GTK_BUTTONS_CLOSE, |
1406 | _("Unable to end process" )); |
1407 | gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), |
1408 | message_format: "%s" , |
1409 | error->message); |
1410 | |
1411 | gtk_widget_show (widget: dialog); |
1412 | |
1413 | g_signal_connect (dialog, "response" , G_CALLBACK (on_dialog_response), NULL); |
1414 | |
1415 | g_error_free (error); |
1416 | } |
1417 | |
1418 | out: |
1419 | ; |
1420 | } |
1421 | |
1422 | static gboolean |
1423 | (GtkWidget *widget, |
1424 | GdkEvent *event, |
1425 | GtkMountOperation *op) |
1426 | { |
1427 | GtkWidget *; |
1428 | GtkWidget *item; |
1429 | double x, y; |
1430 | |
1431 | menu = gtk_popover_new (); |
1432 | gtk_widget_set_parent (widget: menu, parent: widget); |
1433 | gtk_widget_add_css_class (widget: menu, css_class: "context-menu" ); |
1434 | |
1435 | item = gtk_model_button_new (); |
1436 | g_object_set (object: item, first_property_name: "text" , _("_End Process" ), NULL); |
1437 | g_signal_connect (item, "clicked" , |
1438 | G_CALLBACK (on_end_process_activated), |
1439 | op); |
1440 | gtk_box_append (GTK_BOX (menu), child: item); |
1441 | |
1442 | if (event && gdk_event_triggers_context_menu (event)) |
1443 | { |
1444 | GtkTreePath *path; |
1445 | GtkTreeSelection *selection; |
1446 | |
1447 | gdk_event_get_position (event, x: &x, y: &y); |
1448 | if (gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (op->priv->process_tree_view), |
1449 | x: (int) x, |
1450 | y: (int) y, |
1451 | path: &path, |
1452 | NULL, |
1453 | NULL, |
1454 | NULL)) |
1455 | { |
1456 | selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (op->priv->process_tree_view)); |
1457 | gtk_tree_selection_select_path (selection, path); |
1458 | gtk_tree_path_free (path); |
1459 | } |
1460 | else |
1461 | { |
1462 | /* don't popup a menu if the user right-clicked in an area with no rows */ |
1463 | return FALSE; |
1464 | } |
1465 | |
1466 | gtk_popover_set_pointing_to (GTK_POPOVER (menu), rect: &(GdkRectangle){ x, y, 1, 1}); |
1467 | } |
1468 | |
1469 | gtk_popover_popup (GTK_POPOVER (menu)); |
1470 | return TRUE; |
1471 | } |
1472 | |
1473 | static gboolean |
1474 | (GtkWidget *widget, |
1475 | GVariant *args, |
1476 | gpointer user_data) |
1477 | { |
1478 | GtkMountOperation *op = GTK_MOUNT_OPERATION (user_data); |
1479 | return do_popup_menu_for_process_tree_view (widget, NULL, op); |
1480 | } |
1481 | |
1482 | static void |
1483 | click_cb (GtkGesture *gesture, |
1484 | int n_press, |
1485 | double x, |
1486 | double y, |
1487 | gpointer user_data) |
1488 | { |
1489 | GtkMountOperation *op = GTK_MOUNT_OPERATION (user_data); |
1490 | GdkEvent *event; |
1491 | GdkEventSequence *sequence; |
1492 | GtkWidget *widget; |
1493 | |
1494 | sequence = gtk_gesture_get_last_updated_sequence (gesture); |
1495 | event = gtk_gesture_get_last_event (gesture, sequence); |
1496 | widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture)); |
1497 | do_popup_menu_for_process_tree_view (widget, event, op); |
1498 | } |
1499 | |
1500 | static GtkWidget * |
1501 | create_show_processes_dialog (GtkMountOperation *op, |
1502 | const char *message, |
1503 | const char *choices[]) |
1504 | { |
1505 | GtkMountOperationPrivate *priv; |
1506 | GtkWidget *dialog; |
1507 | const char *secondary = NULL; |
1508 | char *primary; |
1509 | int count, len = 0; |
1510 | GtkWidget *label; |
1511 | GtkWidget *tree_view; |
1512 | GtkWidget *scrolled_window; |
1513 | GtkWidget *vbox; |
1514 | GtkWidget *content_area; |
1515 | GtkTreeViewColumn *column; |
1516 | GtkCellRenderer *renderer; |
1517 | GtkListStore *list_store; |
1518 | char *s; |
1519 | gboolean ; |
1520 | GtkGesture *gesture; |
1521 | GtkEventController *controller; |
1522 | GtkShortcutTrigger *trigger; |
1523 | GtkShortcutAction *action; |
1524 | GtkShortcut *shortcut; |
1525 | |
1526 | priv = op->priv; |
1527 | |
1528 | primary = strstr (haystack: message, needle: "\n" ); |
1529 | if (primary) |
1530 | { |
1531 | secondary = primary + 1; |
1532 | primary = g_strndup (str: message, n: primary - message); |
1533 | } |
1534 | |
1535 | g_object_get (object: gtk_settings_get_default (), |
1536 | first_property_name: "gtk-dialogs-use-header" , &use_header, |
1537 | NULL); |
1538 | dialog = g_object_new (GTK_TYPE_DIALOG, |
1539 | first_property_name: "use-header-bar" , use_header, |
1540 | NULL); |
1541 | |
1542 | if (priv->parent_window != NULL) |
1543 | gtk_window_set_transient_for (GTK_WINDOW (dialog), parent: priv->parent_window); |
1544 | gtk_window_set_title (GTK_WINDOW (dialog), title: "" ); |
1545 | |
1546 | content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); |
1547 | vbox = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 12); |
1548 | gtk_box_append (GTK_BOX (content_area), child: vbox); |
1549 | |
1550 | if (secondary != NULL) |
1551 | s = g_strdup_printf (format: "<big><b>%s</b></big>\n\n%s" , primary, secondary); |
1552 | else |
1553 | s = g_strdup_printf (format: "%s" , primary); |
1554 | |
1555 | g_free (mem: primary); |
1556 | label = gtk_label_new (NULL); |
1557 | gtk_label_set_markup (GTK_LABEL (label), str: s); |
1558 | g_free (mem: s); |
1559 | gtk_box_append (GTK_BOX (vbox), child: label); |
1560 | |
1561 | /* First count the items in the list then |
1562 | * add the buttons in reverse order |
1563 | */ |
1564 | |
1565 | while (choices[len] != NULL) |
1566 | len++; |
1567 | |
1568 | for (count = len - 1; count >= 0; count--) |
1569 | gtk_dialog_add_button (GTK_DIALOG (dialog), button_text: choices[count], response_id: count); |
1570 | |
1571 | g_signal_connect (G_OBJECT (dialog), "response" , |
1572 | G_CALLBACK (show_processes_button_clicked), op); |
1573 | |
1574 | priv->dialog = GTK_DIALOG (dialog); |
1575 | g_object_notify (G_OBJECT (op), property_name: "is-showing" ); |
1576 | |
1577 | if (priv->parent_window == NULL && priv->display) |
1578 | gtk_window_set_display (GTK_WINDOW (dialog), display: priv->display); |
1579 | |
1580 | tree_view = gtk_tree_view_new (); |
1581 | gtk_widget_set_size_request (widget: tree_view, width: 300, height: 120); |
1582 | |
1583 | column = gtk_tree_view_column_new (); |
1584 | renderer = gtk_cell_renderer_pixbuf_new (); |
1585 | gtk_tree_view_column_pack_start (tree_column: column, cell: renderer, FALSE); |
1586 | gtk_tree_view_column_set_attributes (tree_column: column, cell_renderer: renderer, |
1587 | "texture" , 0, |
1588 | NULL); |
1589 | renderer = gtk_cell_renderer_text_new (); |
1590 | g_object_set (object: renderer, |
1591 | first_property_name: "ellipsize" , PANGO_ELLIPSIZE_MIDDLE, |
1592 | "ellipsize-set" , TRUE, |
1593 | NULL); |
1594 | gtk_tree_view_column_pack_start (tree_column: column, cell: renderer, TRUE); |
1595 | gtk_tree_view_column_set_attributes (tree_column: column, cell_renderer: renderer, |
1596 | "markup" , 1, |
1597 | NULL); |
1598 | gtk_tree_view_append_column (GTK_TREE_VIEW (tree_view), column); |
1599 | gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (tree_view), FALSE); |
1600 | |
1601 | |
1602 | scrolled_window = gtk_scrolled_window_new (); |
1603 | gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), |
1604 | hscrollbar_policy: GTK_POLICY_NEVER, |
1605 | vscrollbar_policy: GTK_POLICY_AUTOMATIC); |
1606 | gtk_scrolled_window_set_has_frame (GTK_SCROLLED_WINDOW (scrolled_window), TRUE); |
1607 | |
1608 | gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled_window), child: tree_view); |
1609 | gtk_box_append (GTK_BOX (vbox), child: scrolled_window); |
1610 | |
1611 | controller = gtk_shortcut_controller_new (); |
1612 | trigger = gtk_alternative_trigger_new (first: gtk_keyval_trigger_new (GDK_KEY_F10, modifiers: GDK_SHIFT_MASK), |
1613 | second: gtk_keyval_trigger_new (GDK_KEY_Menu, modifiers: 0)); |
1614 | action = gtk_callback_action_new (callback: on_popup_menu_for_process_tree_view, |
1615 | data: op, |
1616 | NULL); |
1617 | shortcut = gtk_shortcut_new_with_arguments (trigger, action, format_string: "s" , "sv" ); |
1618 | gtk_shortcut_controller_add_shortcut (GTK_SHORTCUT_CONTROLLER (controller), shortcut); |
1619 | gtk_widget_add_controller (GTK_WIDGET (tree_view), controller); |
1620 | |
1621 | gesture = gtk_gesture_click_new (); |
1622 | gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), GDK_BUTTON_SECONDARY); |
1623 | g_signal_connect (gesture, "pressed" , |
1624 | G_CALLBACK (click_cb), op); |
1625 | gtk_widget_add_controller (widget: tree_view, GTK_EVENT_CONTROLLER (gesture)); |
1626 | |
1627 | list_store = gtk_list_store_new (n_columns: 3, |
1628 | GDK_TYPE_TEXTURE, |
1629 | G_TYPE_STRING, |
1630 | G_TYPE_INT); |
1631 | |
1632 | gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (list_store)); |
1633 | |
1634 | priv->process_list_store = list_store; |
1635 | priv->process_tree_view = tree_view; |
1636 | /* set pointers to NULL when dialog goes away */ |
1637 | g_object_add_weak_pointer (G_OBJECT (priv->process_list_store), weak_pointer_location: (gpointer *) &priv->process_list_store); |
1638 | g_object_add_weak_pointer (G_OBJECT (priv->process_tree_view), weak_pointer_location: (gpointer *) &priv->process_tree_view); |
1639 | |
1640 | g_object_unref (object: list_store); |
1641 | g_object_ref (op); |
1642 | |
1643 | return dialog; |
1644 | } |
1645 | |
1646 | static void |
1647 | call_processes_proxy_cb (GObject *source, |
1648 | GAsyncResult *res, |
1649 | gpointer user_data) |
1650 | { |
1651 | _GtkMountOperationHandler *proxy = _GTK_MOUNT_OPERATION_HANDLER (source); |
1652 | GMountOperation *op = user_data; |
1653 | GMountOperationResult result; |
1654 | GVariant *result_details; |
1655 | GVariantIter iter; |
1656 | const char *key; |
1657 | GVariant *value; |
1658 | GError *error = NULL; |
1659 | |
1660 | if (!_gtk_mount_operation_handler_call_show_processes_finish (proxy, |
1661 | &result, |
1662 | &result_details, |
1663 | res, |
1664 | &error)) |
1665 | { |
1666 | result = G_MOUNT_OPERATION_ABORTED; |
1667 | g_warning ("Shell mount operation error: %s" , error->message); |
1668 | g_error_free (error); |
1669 | goto out; |
1670 | } |
1671 | |
1672 | /* If the request was unhandled it means we called the method again; |
1673 | * in this case, just return and wait for the next response. |
1674 | */ |
1675 | if (result == G_MOUNT_OPERATION_UNHANDLED) |
1676 | return; |
1677 | |
1678 | g_variant_iter_init (iter: &iter, value: result_details); |
1679 | while (g_variant_iter_loop (iter: &iter, format_string: "{&sv}" , &key, &value)) |
1680 | { |
1681 | if (strcmp (s1: key, s2: "choice" ) == 0) |
1682 | g_mount_operation_set_choice (op, choice: g_variant_get_int32 (value)); |
1683 | } |
1684 | |
1685 | out: |
1686 | gtk_mount_operation_proxy_finish (GTK_MOUNT_OPERATION (op), result); |
1687 | } |
1688 | |
1689 | static void |
1690 | gtk_mount_operation_show_processes_do_proxy (GtkMountOperation *operation, |
1691 | const char *message, |
1692 | GArray *processes, |
1693 | const char *choices[]) |
1694 | { |
1695 | char id[255]; |
1696 | g_sprintf(string: id, format: "GtkMountOperation%p" , operation); |
1697 | |
1698 | operation->priv->handler_showing = TRUE; |
1699 | g_object_notify (G_OBJECT (operation), property_name: "is-showing" ); |
1700 | |
1701 | /* keep a ref to the operation while the handler is showing */ |
1702 | g_object_ref (operation); |
1703 | |
1704 | _gtk_mount_operation_handler_call_show_processes (operation->priv->handler, id, |
1705 | message, "drive-harddisk" , |
1706 | g_variant_new_fixed_array (G_VARIANT_TYPE_INT32, |
1707 | elements: processes->data, n_elements: processes->len, |
1708 | element_size: sizeof (GPid)), |
1709 | choices, NULL, |
1710 | call_processes_proxy_cb, operation); |
1711 | } |
1712 | |
1713 | static void |
1714 | gtk_mount_operation_show_processes_do_gtk (GtkMountOperation *op, |
1715 | const char *message, |
1716 | GArray *processes, |
1717 | const char *choices[]) |
1718 | { |
1719 | GtkMountOperationPrivate *priv; |
1720 | GtkWidget *dialog = NULL; |
1721 | |
1722 | g_return_if_fail (GTK_IS_MOUNT_OPERATION (op)); |
1723 | g_return_if_fail (message != NULL); |
1724 | g_return_if_fail (processes != NULL); |
1725 | g_return_if_fail (choices != NULL); |
1726 | |
1727 | priv = op->priv; |
1728 | |
1729 | if (priv->process_list_store == NULL) |
1730 | { |
1731 | /* need to create the dialog */ |
1732 | dialog = create_show_processes_dialog (op, message, choices); |
1733 | } |
1734 | |
1735 | /* otherwise, we're showing the dialog, assume messages+choices hasn't changed */ |
1736 | |
1737 | update_process_list_store (mount_operation: op, |
1738 | list_store: priv->process_list_store, |
1739 | processes); |
1740 | |
1741 | if (dialog != NULL) |
1742 | { |
1743 | gtk_widget_show (widget: dialog); |
1744 | } |
1745 | } |
1746 | |
1747 | |
1748 | static void |
1749 | gtk_mount_operation_show_processes (GMountOperation *op, |
1750 | const char *message, |
1751 | GArray *processes, |
1752 | const char *choices[]) |
1753 | { |
1754 | |
1755 | GtkMountOperation *operation; |
1756 | gboolean use_gtk; |
1757 | |
1758 | operation = GTK_MOUNT_OPERATION (op); |
1759 | use_gtk = (operation->priv->handler == NULL); |
1760 | |
1761 | if (use_gtk) |
1762 | gtk_mount_operation_show_processes_do_gtk (op: operation, message, processes, choices); |
1763 | else |
1764 | gtk_mount_operation_show_processes_do_proxy (operation, message, processes, choices); |
1765 | } |
1766 | |
1767 | static void |
1768 | gtk_mount_operation_aborted (GMountOperation *op) |
1769 | { |
1770 | GtkMountOperationPrivate *priv; |
1771 | |
1772 | priv = GTK_MOUNT_OPERATION (op)->priv; |
1773 | |
1774 | if (priv->dialog != NULL) |
1775 | { |
1776 | gtk_window_destroy (GTK_WINDOW (priv->dialog)); |
1777 | priv->dialog = NULL; |
1778 | g_object_notify (G_OBJECT (op), property_name: "is-showing" ); |
1779 | g_object_unref (object: op); |
1780 | } |
1781 | |
1782 | if (priv->handler != NULL) |
1783 | { |
1784 | _gtk_mount_operation_handler_call_close (priv->handler, NULL, NULL, NULL); |
1785 | |
1786 | priv->handler_showing = FALSE; |
1787 | g_object_notify (G_OBJECT (op), property_name: "is-showing" ); |
1788 | } |
1789 | } |
1790 | |
1791 | /** |
1792 | * gtk_mount_operation_new: |
1793 | * @parent: (nullable): transient parent of the window |
1794 | * |
1795 | * Creates a new `GtkMountOperation`. |
1796 | * |
1797 | * Returns: a new `GtkMountOperation` |
1798 | */ |
1799 | GMountOperation * |
1800 | gtk_mount_operation_new (GtkWindow *parent) |
1801 | { |
1802 | GMountOperation *mount_operation; |
1803 | |
1804 | mount_operation = g_object_new (GTK_TYPE_MOUNT_OPERATION, |
1805 | first_property_name: "parent" , parent, NULL); |
1806 | |
1807 | return mount_operation; |
1808 | } |
1809 | |
1810 | /** |
1811 | * gtk_mount_operation_is_showing: (attributes org.gtk.Method.get_property=is-showing) |
1812 | * @op: a `GtkMountOperation` |
1813 | * |
1814 | * Returns whether the `GtkMountOperation` is currently displaying |
1815 | * a window. |
1816 | * |
1817 | * Returns: %TRUE if @op is currently displaying a window |
1818 | */ |
1819 | gboolean |
1820 | gtk_mount_operation_is_showing (GtkMountOperation *op) |
1821 | { |
1822 | g_return_val_if_fail (GTK_IS_MOUNT_OPERATION (op), FALSE); |
1823 | |
1824 | return op->priv->dialog != NULL; |
1825 | } |
1826 | |
1827 | /** |
1828 | * gtk_mount_operation_set_parent: (attributes org.gtk.Method.set_property=parent) |
1829 | * @op: a `GtkMountOperation` |
1830 | * @parent: (nullable): transient parent of the window |
1831 | * |
1832 | * Sets the transient parent for windows shown by the |
1833 | * `GtkMountOperation`. |
1834 | */ |
1835 | void |
1836 | gtk_mount_operation_set_parent (GtkMountOperation *op, |
1837 | GtkWindow *parent) |
1838 | { |
1839 | GtkMountOperationPrivate *priv; |
1840 | |
1841 | g_return_if_fail (GTK_IS_MOUNT_OPERATION (op)); |
1842 | g_return_if_fail (parent == NULL || GTK_IS_WINDOW (parent)); |
1843 | |
1844 | priv = op->priv; |
1845 | |
1846 | if (priv->parent_window == parent) |
1847 | return; |
1848 | |
1849 | if (priv->parent_window) |
1850 | { |
1851 | g_signal_handlers_disconnect_by_func (priv->parent_window, |
1852 | parent_destroyed, |
1853 | &priv->parent_window); |
1854 | g_object_unref (object: priv->parent_window); |
1855 | } |
1856 | priv->parent_window = parent; |
1857 | if (priv->parent_window) |
1858 | { |
1859 | g_object_ref (priv->parent_window); |
1860 | g_signal_connect (priv->parent_window, "destroy" , |
1861 | G_CALLBACK (parent_destroyed), &priv->parent_window); |
1862 | } |
1863 | |
1864 | if (priv->dialog) |
1865 | gtk_window_set_transient_for (GTK_WINDOW (priv->dialog), parent: priv->parent_window); |
1866 | |
1867 | g_object_notify (G_OBJECT (op), property_name: "parent" ); |
1868 | } |
1869 | |
1870 | /** |
1871 | * gtk_mount_operation_get_parent: (attributes org.gtk.Method.get_property=parent) |
1872 | * @op: a `GtkMountOperation` |
1873 | * |
1874 | * Gets the transient parent used by the `GtkMountOperation`. |
1875 | * |
1876 | * Returns: (transfer none) (nullable): the transient parent for windows shown by @op |
1877 | */ |
1878 | GtkWindow * |
1879 | gtk_mount_operation_get_parent (GtkMountOperation *op) |
1880 | { |
1881 | g_return_val_if_fail (GTK_IS_MOUNT_OPERATION (op), NULL); |
1882 | |
1883 | return op->priv->parent_window; |
1884 | } |
1885 | |
1886 | /** |
1887 | * gtk_mount_operation_set_display: (attributes org.gtk.Method.set_property=display) |
1888 | * @op: a `GtkMountOperation` |
1889 | * @display: a `GdkDisplay` |
1890 | * |
1891 | * Sets the display to show windows of the `GtkMountOperation` on. |
1892 | */ |
1893 | void |
1894 | gtk_mount_operation_set_display (GtkMountOperation *op, |
1895 | GdkDisplay *display) |
1896 | { |
1897 | GtkMountOperationPrivate *priv; |
1898 | |
1899 | g_return_if_fail (GTK_IS_MOUNT_OPERATION (op)); |
1900 | g_return_if_fail (GDK_IS_DISPLAY (display)); |
1901 | |
1902 | priv = op->priv; |
1903 | |
1904 | if (priv->display == display) |
1905 | return; |
1906 | |
1907 | if (priv->display) |
1908 | g_object_unref (object: priv->display); |
1909 | |
1910 | priv->display = g_object_ref (display); |
1911 | |
1912 | if (priv->dialog) |
1913 | gtk_window_set_display (GTK_WINDOW (priv->dialog), display); |
1914 | |
1915 | g_object_notify (G_OBJECT (op), property_name: "display" ); |
1916 | } |
1917 | |
1918 | /** |
1919 | * gtk_mount_operation_get_display: (attributes org.gtk.Method.get_property=display) |
1920 | * @op: a `GtkMountOperation` |
1921 | * |
1922 | * Gets the display on which windows of the `GtkMountOperation` |
1923 | * will be shown. |
1924 | * |
1925 | * Returns: (transfer none): the display on which windows of @op are shown |
1926 | */ |
1927 | GdkDisplay * |
1928 | gtk_mount_operation_get_display (GtkMountOperation *op) |
1929 | { |
1930 | GtkMountOperationPrivate *priv; |
1931 | |
1932 | g_return_val_if_fail (GTK_IS_MOUNT_OPERATION (op), NULL); |
1933 | |
1934 | priv = op->priv; |
1935 | |
1936 | if (priv->dialog) |
1937 | return gtk_widget_get_display (GTK_WIDGET (priv->dialog)); |
1938 | else if (priv->parent_window) |
1939 | return gtk_widget_get_display (GTK_WIDGET (priv->parent_window)); |
1940 | else if (priv->display) |
1941 | return priv->display; |
1942 | else |
1943 | return gdk_display_get_default (); |
1944 | } |
1945 | |