GCC Code Coverage Report


Directory: ./
File: panels/printers/cc-printers-panel.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 520 0.0%
Functions: 0 44 0.0%
Branches: 0 284 0.0%

Line Branch Exec Source
1 /*
2 * Copyright (C) 2010 Red Hat, Inc
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19 #include <config.h>
20
21 #include "shell/cc-object-storage.h"
22
23 #include "cc-printers-panel.h"
24 #include "cc-printers-resources.h"
25 #include "pp-printer.h"
26
27 #include <string.h>
28 #include <glib/gi18n-lib.h>
29 #include <glib/gstdio.h>
30 #include <polkit/polkit.h>
31 #include <gdesktop-enums.h>
32
33 #include <cups/cups.h>
34 #include <cups/ppd.h>
35
36 #include <math.h>
37
38 #include "pp-new-printer-dialog.h"
39 #include "pp-utils.h"
40 #include "pp-cups.h"
41 #include "pp-printer-entry.h"
42 #include "pp-job.h"
43 #include "pp-new-printer.h"
44
45 #include "cc-permission-infobar.h"
46 #include "cc-util.h"
47
48 #define JOB_DEFAULT_PRIORITY 50
49 #define RENEW_INTERVAL 500
50 #define SUBSCRIPTION_DURATION 600
51
52 #define CUPS_DBUS_NAME "org.cups.cupsd.Notifier"
53 #define CUPS_DBUS_PATH "/org/cups/cupsd/Notifier"
54 #define CUPS_DBUS_INTERFACE "org.cups.cupsd.Notifier"
55
56 #define CUPS_STATUS_CHECK_INTERVAL 5
57
58 #if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5)
59 #define HAVE_CUPS_1_6 1
60 #endif
61
62 #ifndef HAVE_CUPS_1_6
63 #define ippGetState(ipp) ipp->state
64 #define ippGetStatusCode(ipp) ipp->request.status.status_code
65 #define ippGetString(attr, element, language) attr->values[element].string.text
66 #endif
67
68 struct _CcPrintersPanel
69 {
70 CcPanel parent_instance;
71
72 GtkListBox *content;
73 GtkStack *main_stack;
74 GtkRevealer *notification;
75 GtkLabel *notification_label;
76 CcPermissionInfobar *permission_infobar;
77 GtkWidget *printer_add_button;
78 GtkWidget *printer_add_button_empty;
79 GtkScrolledWindow *scrolled_window;
80 GtkSearchBar *search_bar;
81 GtkWidget *search_button;
82 GtkEditable *search_entry;
83
84 PpCups *cups;
85
86 cups_dest_t *dests;
87 int num_dests;
88
89 GPermission *permission;
90 gboolean is_authorized;
91
92 GSettings *lockdown_settings;
93
94 PpNewPrinterDialog *pp_new_printer_dialog;
95
96 GDBusProxy *cups_proxy;
97 GDBusConnection *cups_bus_connection;
98 gint subscription_id;
99 guint subscription_renewal_id;
100 guint cups_status_check_id;
101 guint dbus_subscription_id;
102 guint remove_printer_timeout_id;
103
104 PPDList *all_ppds_list;
105
106 gchar *new_printer_name;
107
108 gchar *renamed_printer_name;
109 gchar *old_printer_name;
110 gchar *deleted_printer_name;
111 GList *deleted_printers;
112 GObject *reference;
113
114 GHashTable *printer_entries;
115 gboolean entries_filled;
116 GVariant *action;
117
118 GtkSizeGroup *size_group;
119 gboolean compact;
120 };
121
122 CC_PANEL_REGISTER (CcPrintersPanel, cc_printers_panel)
123
124 typedef struct
125 {
126 gchar *printer_name;
127 GCancellable *cancellable;
128 } SetPPDItem;
129
130 enum {
131 PROP_0,
132 PROP_PARAMETERS,
133 PROP_COMPACT,
134 };
135
136 static void actualize_printers_list (CcPrintersPanel *self);
137 static void update_sensitivity (gpointer user_data);
138 static void detach_from_cups_notifier (gpointer data);
139 static void free_dests (CcPrintersPanel *self);
140 static void set_current_page (GObject *source_object,
141 GAsyncResult *result,
142 gpointer user_data);
143
144 static void
145 execute_action (CcPrintersPanel *self,
146 GVariant *action)
147 {
148 PpPrinterEntry *printer_entry;
149 const gchar *action_name;
150 const gchar *printer_name;
151 gint count;
152
153 count = g_variant_n_children (action);
154 if (count == 2)
155 {
156 g_autoptr(GVariant) action_variant = NULL;
157
158 g_variant_get_child (action, 0, "v", &action_variant);
159 action_name = g_variant_get_string (action_variant, NULL);
160
161 /* authenticate-jobs printer-name */
162 if (g_strcmp0 (action_name, "authenticate-jobs") == 0)
163 {
164 g_autoptr(GVariant) variant = NULL;
165
166 g_variant_get_child (action, 1, "v", &variant);
167 printer_name = g_variant_get_string (variant, NULL);
168
169 printer_entry = PP_PRINTER_ENTRY (g_hash_table_lookup (self->printer_entries, printer_name));
170 if (printer_entry != NULL)
171 pp_printer_entry_authenticate_jobs (printer_entry);
172 else
173 g_warning ("Could not find printer \"%s\"!", printer_name);
174 }
175 /* show-jobs printer-name */
176 else if (g_strcmp0 (action_name, "show-jobs") == 0)
177 {
178 g_autoptr(GVariant) variant = NULL;
179
180 g_variant_get_child (action, 1, "v", &variant);
181 printer_name = g_variant_get_string (variant, NULL);
182
183 printer_entry = PP_PRINTER_ENTRY (g_hash_table_lookup (self->printer_entries, printer_name));
184 if (printer_entry != NULL)
185 pp_printer_entry_show_jobs_dialog (printer_entry);
186 else
187 g_warning ("Could not find printer \"%s\"!", printer_name);
188 }
189 }
190 }
191
192 static void
193 cc_printers_panel_get_property (GObject *object,
194 guint property_id,
195 GValue *value,
196 GParamSpec *pspec)
197 {
198 CcPrintersPanel *self = CC_PRINTERS_PANEL (object);
199
200 switch (property_id)
201 {
202 case PROP_COMPACT:
203 g_value_set_boolean (value, self->compact);
204 break;
205
206 default:
207 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
208 }
209 }
210
211 static void
212 cc_printers_panel_set_property (GObject *object,
213 guint property_id,
214 const GValue *value,
215 GParamSpec *pspec)
216 {
217 CcPrintersPanel *self = CC_PRINTERS_PANEL (object);
218 GVariant *parameters;
219
220 switch (property_id)
221 {
222 case PROP_PARAMETERS:
223 parameters = g_value_get_variant (value);
224 if (parameters != NULL && g_variant_n_children (parameters) > 0)
225 {
226 if (self->entries_filled)
227 {
228 execute_action (CC_PRINTERS_PANEL (object), parameters);
229 }
230 else
231 {
232 if (self->action != NULL)
233 g_variant_unref (self->action);
234 self->action = g_variant_ref (parameters);
235 }
236 }
237 break;
238
239 case PROP_COMPACT:
240 self->compact = g_value_get_boolean (value);
241 break;
242
243 default:
244 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
245 }
246 }
247
248 static void
249 cc_printers_panel_constructed (GObject *object)
250 {
251 CcPrintersPanel *self = CC_PRINTERS_PANEL (object);
252 CcShell *shell;
253
254 G_OBJECT_CLASS (cc_printers_panel_parent_class)->constructed (object);
255
256 shell = cc_panel_get_shell (CC_PANEL (self));
257
258 gtk_search_bar_connect_entry (self->search_bar, self->search_entry);
259 gtk_search_bar_set_key_capture_widget (self->search_bar,
260 GTK_WIDGET (shell));
261 }
262
263 static void
264 printer_removed_cb (GObject *source_object,
265 GAsyncResult *result,
266 gpointer user_data)
267 {
268 PpPrinter *printer = PP_PRINTER (source_object);
269 g_autoptr(GError) error = NULL;
270
271 pp_printer_delete_finish (printer, result, &error);
272
273 if (user_data != NULL)
274 {
275 g_autoptr(GObject) reference = G_OBJECT (user_data);
276
277 if (g_object_get_data (reference, "self") != NULL)
278 {
279 CcPrintersPanel *self = CC_PRINTERS_PANEL (g_object_get_data (reference, "self"));
280 GList *iter;
281
282 for (iter = self->deleted_printers; iter != NULL; iter = iter->next)
283 {
284 if (g_strcmp0 (iter->data, pp_printer_get_name (printer)) == 0)
285 {
286 g_free (iter->data);
287 self->deleted_printers = g_list_delete_link (self->deleted_printers, iter);
288 break;
289 }
290 }
291 }
292 }
293
294 if (error != NULL)
295 g_warning ("Printer could not be deleted: %s", error->message);
296 }
297
298 static void
299 cc_printers_panel_dispose (GObject *object)
300 {
301 CcPrintersPanel *self = CC_PRINTERS_PANEL (object);
302
303 detach_from_cups_notifier (CC_PRINTERS_PANEL (object));
304
305 if (self->deleted_printer_name != NULL)
306 {
307 g_autoptr(PpPrinter) printer = pp_printer_new (self->deleted_printer_name);
308 pp_printer_delete_sync (printer, NULL, NULL);
309 }
310
311 g_clear_object (&self->cups);
312 g_clear_pointer (&self->new_printer_name, g_free);
313 g_clear_pointer (&self->renamed_printer_name, g_free);
314 g_clear_pointer (&self->old_printer_name, g_free);
315 g_clear_object (&self->lockdown_settings);
316 g_clear_object (&self->permission);
317 g_clear_handle_id (&self->cups_status_check_id, g_source_remove);
318 g_clear_handle_id (&self->remove_printer_timeout_id, g_source_remove);
319 g_clear_pointer (&self->deleted_printer_name, g_free);
320 g_clear_pointer (&self->action, g_variant_unref);
321 g_clear_pointer (&self->printer_entries, g_hash_table_destroy);
322 g_clear_pointer (&self->all_ppds_list, ppd_list_free);
323 free_dests (self);
324 g_list_free_full (self->deleted_printers, g_free);
325 self->deleted_printers = NULL;
326 if (self->reference != NULL)
327 g_object_set_data (self->reference, "self", NULL);
328 g_clear_object (&self->reference);
329
330 G_OBJECT_CLASS (cc_printers_panel_parent_class)->dispose (object);
331 }
332
333 static const char *
334 cc_printers_panel_get_help_uri (CcPanel *panel)
335 {
336 return "help:gnome-help/printing";
337 }
338
339 static void
340 on_get_job_attributes_cb (GObject *source_object,
341 GAsyncResult *res,
342 gpointer user_data)
343 {
344 CcPrintersPanel *self = (CcPrintersPanel*) user_data;
345 const gchar *job_originating_user_name;
346 const gchar *job_printer_uri;
347 g_autoptr(GVariant) attributes = NULL;
348 g_autoptr(GError) error = NULL;
349
350 attributes = pp_job_get_attributes_finish (PP_JOB (source_object), res, &error);
351
352 if (attributes != NULL)
353 {
354 g_autoptr(GVariant) username = NULL;
355
356 if ((username = g_variant_lookup_value (attributes, "job-originating-user-name", G_VARIANT_TYPE ("as"))) != NULL)
357 {
358 g_autoptr(GVariant) printer_uri = NULL;
359
360 if ((printer_uri = g_variant_lookup_value (attributes, "job-printer-uri", G_VARIANT_TYPE ("as"))) != NULL)
361 {
362 job_originating_user_name = g_variant_get_string (g_variant_get_child_value (username, 0), NULL);
363 job_printer_uri = g_variant_get_string (g_variant_get_child_value (printer_uri, 0), NULL);
364
365 if (job_originating_user_name != NULL && job_printer_uri != NULL &&
366 g_strcmp0 (job_originating_user_name, cupsUser ()) == 0 &&
367 g_strrstr (job_printer_uri, "/") != 0 &&
368 self->dests != NULL)
369 {
370 PpPrinterEntry *printer_entry;
371 gchar *printer_name;
372
373 printer_name = g_strrstr (job_printer_uri, "/") + 1;
374 printer_entry = PP_PRINTER_ENTRY (g_hash_table_lookup (self->printer_entries, printer_name));
375
376 pp_printer_entry_update_jobs_count (printer_entry);
377 }
378 }
379 }
380 }
381 }
382
383 static void
384 on_cups_notification (GDBusConnection *connection,
385 const char *sender_name,
386 const char *object_path,
387 const char *interface_name,
388 const char *signal_name,
389 GVariant *parameters,
390 gpointer user_data)
391 {
392 CcPrintersPanel *self = (CcPrintersPanel*) user_data;
393 gboolean printer_is_accepting_jobs;
394 gchar *printer_name = NULL;
395 gchar *text = NULL;
396 gchar *printer_uri = NULL;
397 gchar *printer_state_reasons = NULL;
398 gchar *job_state_reasons = NULL;
399 gchar *job_name = NULL;
400 guint job_id;
401 gint printer_state;
402 gint job_state;
403 gint job_impressions_completed;
404 static gchar *requested_attrs[] = {
405 "job-printer-uri",
406 "job-originating-user-name",
407 NULL };
408
409 if (g_strcmp0 (signal_name, "PrinterAdded") != 0 &&
410 g_strcmp0 (signal_name, "PrinterDeleted") != 0 &&
411 g_strcmp0 (signal_name, "PrinterStateChanged") != 0 &&
412 g_strcmp0 (signal_name, "PrinterStopped") != 0 &&
413 g_strcmp0 (signal_name, "JobCreated") != 0 &&
414 g_strcmp0 (signal_name, "JobCompleted") != 0)
415 return;
416
417 if (g_variant_n_children (parameters) == 1)
418 g_variant_get (parameters, "(&s)", &text);
419 else if (g_variant_n_children (parameters) == 6)
420 {
421 g_variant_get (parameters, "(&s&s&su&sb)",
422 &text,
423 &printer_uri,
424 &printer_name,
425 &printer_state,
426 &printer_state_reasons,
427 &printer_is_accepting_jobs);
428 }
429 else if (g_variant_n_children (parameters) == 11)
430 {
431 g_variant_get (parameters, "(&s&s&su&sbuu&s&su)",
432 &text,
433 &printer_uri,
434 &printer_name,
435 &printer_state,
436 &printer_state_reasons,
437 &printer_is_accepting_jobs,
438 &job_id,
439 &job_state,
440 &job_state_reasons,
441 &job_name,
442 &job_impressions_completed);
443 }
444
445 if (g_strcmp0 (signal_name, "PrinterAdded") == 0 ||
446 g_strcmp0 (signal_name, "PrinterDeleted") == 0 ||
447 g_strcmp0 (signal_name, "PrinterStateChanged") == 0 ||
448 g_strcmp0 (signal_name, "PrinterStopped") == 0)
449 actualize_printers_list (self);
450 else if (g_strcmp0 (signal_name, "JobCreated") == 0 ||
451 g_strcmp0 (signal_name, "JobCompleted") == 0)
452 {
453 g_autoptr(PpJob) job = NULL;
454
455 job = pp_job_new (job_id, NULL, 0, JOB_DEFAULT_PRIORITY, NULL);
456 pp_job_get_attributes_async (job,
457 requested_attrs,
458 cc_panel_get_cancellable (CC_PANEL (self)),
459 on_get_job_attributes_cb,
460 self);
461 }
462 }
463
464 static gchar *subscription_events[] = {
465 "printer-added",
466 "printer-deleted",
467 "printer-stopped",
468 "printer-state-changed",
469 "job-created",
470 "job-completed",
471 NULL};
472
473 static void
474 renew_subscription_cb (GObject *source_object,
475 GAsyncResult *result,
476 gpointer user_data)
477 {
478 CcPrintersPanel *self = (CcPrintersPanel*) user_data;
479 gint subscription_id;
480
481 subscription_id = pp_cups_renew_subscription_finish (PP_CUPS (source_object), result);
482
483 if (subscription_id > 0)
484 self->subscription_id = subscription_id;
485 }
486
487 static gboolean
488 renew_subscription (gpointer data)
489 {
490 CcPrintersPanel *self = (CcPrintersPanel*) data;
491
492 pp_cups_renew_subscription_async (self->cups,
493 self->subscription_id,
494 subscription_events,
495 SUBSCRIPTION_DURATION,
496 cc_panel_get_cancellable (CC_PANEL (self)),
497 renew_subscription_cb,
498 data);
499
500 return G_SOURCE_CONTINUE;
501 }
502
503 static void
504 attach_to_cups_notifier_cb (GObject *source_object,
505 GAsyncResult *result,
506 gpointer user_data)
507 {
508 CcPrintersPanel *self = (CcPrintersPanel*) user_data;
509 g_autoptr(GError) error = NULL;
510 gint subscription_id;
511
512 subscription_id = pp_cups_renew_subscription_finish (PP_CUPS (source_object), result);
513
514 if (subscription_id > 0)
515 {
516 self->subscription_id = subscription_id;
517
518 self->subscription_renewal_id =
519 g_timeout_add_seconds (RENEW_INTERVAL, renew_subscription, self);
520
521 self->cups_proxy = cc_object_storage_create_dbus_proxy_sync (G_BUS_TYPE_SYSTEM,
522 G_DBUS_PROXY_FLAGS_NONE,
523 CUPS_DBUS_NAME,
524 CUPS_DBUS_PATH,
525 CUPS_DBUS_INTERFACE,
526 NULL,
527 &error);
528
529 if (!self->cups_proxy)
530 {
531 g_warning ("%s", error->message);
532 return;
533 }
534
535 self->cups_bus_connection = g_dbus_proxy_get_connection (self->cups_proxy);
536
537 self->dbus_subscription_id =
538 g_dbus_connection_signal_subscribe (self->cups_bus_connection,
539 NULL,
540 CUPS_DBUS_INTERFACE,
541 NULL,
542 CUPS_DBUS_PATH,
543 NULL,
544 0,
545 on_cups_notification,
546 self,
547 NULL);
548 }
549 }
550
551 static void
552 attach_to_cups_notifier (gpointer data)
553 {
554 CcPrintersPanel *self = (CcPrintersPanel*) data;
555
556 pp_cups_renew_subscription_async (self->cups,
557 self->subscription_id,
558 subscription_events,
559 SUBSCRIPTION_DURATION,
560 cc_panel_get_cancellable (CC_PANEL (self)),
561 attach_to_cups_notifier_cb,
562 data);
563 }
564
565 static void
566 subscription_cancel_cb (GObject *source_object,
567 GAsyncResult *result,
568 gpointer user_data)
569 {
570 pp_cups_cancel_subscription_finish (PP_CUPS (source_object), result);
571 }
572
573 static void
574 detach_from_cups_notifier (gpointer data)
575 {
576 CcPrintersPanel *self = (CcPrintersPanel*) data;
577
578 if (self->dbus_subscription_id != 0) {
579 g_dbus_connection_signal_unsubscribe (self->cups_bus_connection,
580 self->dbus_subscription_id);
581 self->dbus_subscription_id = 0;
582 }
583
584 pp_cups_cancel_subscription_async (self->cups,
585 self->subscription_id,
586 subscription_cancel_cb,
587 NULL);
588
589 self->subscription_id = 0;
590
591 g_clear_handle_id (&self->subscription_renewal_id, g_source_remove);
592
593 g_clear_object (&self->cups_proxy);
594 }
595
596 static void
597 free_dests (CcPrintersPanel *self)
598 {
599 if (self->num_dests > 0)
600 {
601 cupsFreeDests (self->num_dests, self->dests);
602 }
603 self->dests = NULL;
604 self->num_dests = 0;
605 }
606
607 static void
608 on_printer_deletion_undone (CcPrintersPanel *self)
609 {
610 gtk_revealer_set_reveal_child (self->notification, FALSE);
611
612 g_clear_pointer (&self->deleted_printer_name, g_free);
613
614 gtk_list_box_invalidate_filter (self->content);
615
616 g_clear_handle_id (&self->remove_printer_timeout_id, g_source_remove);
617
618 if (self->num_dests > 0)
619 gtk_stack_set_visible_child_name (self->main_stack, "printers-list");
620 }
621
622 static void
623 on_notification_dismissed (CcPrintersPanel *self)
624 {
625 g_clear_handle_id (&self->remove_printer_timeout_id, g_source_remove);
626
627 if (self->deleted_printer_name != NULL)
628 {
629 g_autoptr(PpPrinter) printer = NULL;
630
631 printer = pp_printer_new (self->deleted_printer_name);
632 /* The reference tells to the callback whether
633 printers panel was already destroyed so
634 it knows whether it can access the list
635 of deleted printers in it (see below).
636 */
637 pp_printer_delete_async (printer,
638 NULL,
639 printer_removed_cb,
640 g_object_ref (self->reference));
641
642 /* List of printers which were recently deleted but are still available
643 in CUPS due to async nature of the method (e.g. quick deletion
644 of several printers).
645 */
646 self->deleted_printers = g_list_prepend (self->deleted_printers, self->deleted_printer_name);
647 self->deleted_printer_name = NULL;
648 }
649
650 gtk_revealer_set_reveal_child (self->notification, FALSE);
651 }
652
653 static gboolean
654 on_remove_printer_timeout (CcPrintersPanel *self)
655 {
656 self->remove_printer_timeout_id = 0;
657
658 on_notification_dismissed (self);
659
660 return G_SOURCE_REMOVE;
661 }
662
663 static void
664 on_printer_deleted (CcPrintersPanel *self,
665 PpPrinterEntry *printer_entry)
666 {
667 g_autofree gchar *notification_message = NULL;
668
669 on_notification_dismissed (self);
670
671 /* Translators: %s is the printer name */
672 notification_message = g_strdup_printf (_("Printer “%s” has been deleted"),
673 pp_printer_entry_get_name (printer_entry));
674 gtk_label_set_label (self->notification_label, notification_message);
675
676 self->deleted_printer_name = g_strdup (pp_printer_entry_get_name (printer_entry));
677
678 gtk_list_box_invalidate_filter (self->content);
679
680 gtk_revealer_set_reveal_child (self->notification, TRUE);
681
682 self->remove_printer_timeout_id = g_timeout_add_seconds (10, G_SOURCE_FUNC (on_remove_printer_timeout), self);
683
684 if (self->num_dests == 1 + g_list_length (self->deleted_printers))
685 pp_cups_connection_test_async (self->cups, NULL, set_current_page, self);
686 }
687
688 static void
689 on_printer_renamed (CcPrintersPanel *self,
690 gchar *new_name,
691 PpPrinterEntry *printer_entry)
692 {
693 self->old_printer_name = g_strdup (pp_printer_entry_get_name (printer_entry));
694 self->renamed_printer_name = g_strdup (new_name);
695 }
696
697 static void
698 on_printer_changed (CcPrintersPanel *self)
699 {
700 actualize_printers_list (self);
701 }
702
703 static void
704 add_printer_entry (CcPrintersPanel *self,
705 cups_dest_t printer)
706 {
707 PpPrinterEntry *printer_entry;
708 GSList *widgets, *l;
709
710 printer_entry = pp_printer_entry_new (printer, self->is_authorized);
711
712 widgets = pp_printer_entry_get_size_group_widgets (printer_entry);
713 for (l = widgets; l != NULL; l = l->next)
714 gtk_size_group_add_widget (self->size_group, GTK_WIDGET (l->data));
715 g_slist_free (widgets);
716
717 g_signal_connect_object (printer_entry,
718 "printer-changed",
719 G_CALLBACK (on_printer_changed),
720 self,
721 G_CONNECT_SWAPPED);
722 g_signal_connect_object (printer_entry,
723 "printer-delete",
724 G_CALLBACK (on_printer_deleted),
725 self,
726 G_CONNECT_SWAPPED);
727 g_signal_connect_object (printer_entry,
728 "printer-renamed",
729 G_CALLBACK (on_printer_renamed),
730 self,
731 G_CONNECT_SWAPPED);
732
733 g_object_bind_property (self, "compact",
734 printer_entry, "compact",
735 G_BINDING_SYNC_CREATE);
736
737 gtk_list_box_insert (self->content, GTK_WIDGET (printer_entry), -1);
738
739 g_hash_table_insert (self->printer_entries, g_strdup (printer.name), printer_entry);
740 }
741
742 static void
743 set_current_page (GObject *source_object,
744 GAsyncResult *result,
745 gpointer user_data)
746 {
747 CcPrintersPanel *self = (CcPrintersPanel *) user_data;
748 gboolean success;
749
750 success = pp_cups_connection_test_finish (PP_CUPS (source_object), result, NULL);
751
752 if (success)
753 gtk_stack_set_visible_child_name (self->main_stack, "empty-state");
754 else
755 gtk_stack_set_visible_child_name (self->main_stack, "no-cups-page");
756
757 update_sensitivity (user_data);
758 }
759
760 static gboolean
761 remove_nonexisting_entry (CcPrintersPanel *self,
762 PpPrinterEntry *entry)
763 {
764 gboolean exists = FALSE;
765 gint i;
766
767 for (i = 0; i < self->num_dests; i++)
768 {
769 if (g_strcmp0 (self->dests[i].name, pp_printer_entry_get_name (entry)) == 0)
770 {
771 exists = TRUE;
772 break;
773 }
774 }
775
776 if (!exists)
777 g_hash_table_remove (self->printer_entries, pp_printer_entry_get_name (entry));
778
779 return !exists;
780 }
781
782 static void
783 actualize_printers_list_cb (GObject *source_object,
784 GAsyncResult *result,
785 gpointer user_data)
786 {
787 CcPrintersPanel *self = (CcPrintersPanel*) user_data;
788 PpCupsDests *cups_dests;
789 GtkWidget *child;
790 gboolean new_printer_available = FALSE;
791 g_autoptr(GError) error = NULL;
792 gpointer item;
793 int i;
794
795 cups_dests = pp_cups_get_dests_finish (PP_CUPS (source_object), result, &error);
796
797 if (cups_dests == NULL && error != NULL)
798 {
799 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
800 {
801 g_warning ("Could not get dests: %s", error->message);
802 }
803
804 return;
805 }
806
807 free_dests (self);
808 self->dests = cups_dests->dests;
809 self->num_dests = cups_dests->num_of_dests;
810 g_free (cups_dests);
811
812 if ((self->num_dests == 0 && self->new_printer_name == NULL) ||
813 (self->num_dests == 1 + g_list_length (self->deleted_printers) &&
814 self->deleted_printer_name != NULL))
815 pp_cups_connection_test_async (PP_CUPS (source_object), NULL, set_current_page, self);
816 else
817 gtk_stack_set_visible_child_name (self->main_stack, "printers-list");
818
819 child = gtk_widget_get_first_child (GTK_WIDGET (self->content));
820 while (child)
821 {
822 GtkWidget *next = gtk_widget_get_next_sibling (child);
823
824 if (remove_nonexisting_entry (self, PP_PRINTER_ENTRY (child)))
825 gtk_list_box_remove (self->content, child);
826
827 child = next;
828 }
829
830 for (i = 0; i < self->num_dests; i++)
831 {
832 new_printer_available = g_strcmp0 (self->dests[i].name, self->renamed_printer_name) == 0;
833 if (new_printer_available)
834 break;
835 }
836
837 for (i = 0; i < self->num_dests; i++)
838 {
839 if (new_printer_available && g_strcmp0 (self->dests[i].name, self->old_printer_name) == 0)
840 continue;
841
842 item = g_hash_table_lookup (self->printer_entries, self->dests[i].name);
843 if (item != NULL)
844 pp_printer_entry_update (PP_PRINTER_ENTRY (item), self->dests[i], self->is_authorized);
845 else
846 add_printer_entry (self, self->dests[i]);
847 }
848
849 if (!self->entries_filled)
850 {
851 if (self->action != NULL)
852 {
853 execute_action (self, self->action);
854 g_variant_unref (self->action);
855 self->action = NULL;
856 }
857
858 self->entries_filled = TRUE;
859 }
860
861 update_sensitivity (user_data);
862
863 if (self->new_printer_name != NULL)
864 {
865 GtkAllocation allocation;
866 GtkAdjustment *adjustment;
867 GtkWidget *printer_entry;
868
869 /* Scroll the view to show the newly added printer-entry. */
870 adjustment = gtk_scrolled_window_get_vadjustment (self->scrolled_window);
871
872 printer_entry = GTK_WIDGET (g_hash_table_lookup (self->printer_entries,
873 self->new_printer_name));
874 if (printer_entry != NULL)
875 {
876 gtk_widget_get_allocation (printer_entry, &allocation);
877 g_clear_pointer (&self->new_printer_name, g_free);
878
879 gtk_adjustment_set_value (adjustment,
880 allocation.y - gtk_widget_get_margin_top (printer_entry));
881 }
882 }
883 }
884
885 static void
886 actualize_printers_list (CcPrintersPanel *self)
887 {
888 pp_cups_get_dests_async (self->cups,
889 cc_panel_get_cancellable (CC_PANEL (self)),
890 actualize_printers_list_cb,
891 self);
892 }
893
894 static void
895 printer_add_async_cb (GObject *source_object,
896 GAsyncResult *res,
897 gpointer user_data)
898 {
899 CcPrintersPanel *self = (CcPrintersPanel*) user_data;
900 gboolean success;
901 g_autoptr(GError) error = NULL;
902
903 success = pp_new_printer_add_finish (PP_NEW_PRINTER (source_object), res, &error);
904
905 if (!success)
906 {
907 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
908 {
909 g_warning ("%s", error->message);
910
911 GtkWidget *message_dialog;
912
913 message_dialog = gtk_message_dialog_new (NULL,
914 0,
915 GTK_MESSAGE_ERROR,
916 GTK_BUTTONS_CLOSE,
917 /* Translators: Addition of the new printer failed. */
918 _("Failed to add new printer."));
919 g_signal_connect (message_dialog,
920 "response",
921 G_CALLBACK (gtk_window_destroy),
922 NULL);
923 gtk_window_present (GTK_WINDOW (message_dialog));
924 }
925 }
926
927 actualize_printers_list (self);
928 }
929
930 static void
931 new_printer_dialog_response_cb (GtkWindow *_dialog,
932 gint response_id,
933 gpointer user_data)
934 {
935 CcPrintersPanel *self = (CcPrintersPanel*) user_data;
936 PpNewPrinterDialog *pp_new_printer_dialog = PP_NEW_PRINTER_DIALOG (_dialog);
937 g_autoptr(PpNewPrinter) new_printer = NULL;
938
939 if (response_id == GTK_RESPONSE_OK)
940 {
941 new_printer = pp_new_printer_dialog_get_new_printer (pp_new_printer_dialog);
942 g_object_get(G_OBJECT (new_printer), "name", &self->new_printer_name, NULL);
943
944 actualize_printers_list (self);
945
946 pp_new_printer_add_async (new_printer,
947 cc_panel_get_cancellable (CC_PANEL (self)),
948 printer_add_async_cb,
949 self);
950 }
951
952 gtk_window_destroy (GTK_WINDOW (pp_new_printer_dialog));
953 self->pp_new_printer_dialog = NULL;
954 }
955
956 static void
957 printer_add_cb (CcPrintersPanel *self)
958 {
959 GtkNative *native;
960
961 native = gtk_widget_get_native (GTK_WIDGET (self));
962 self->pp_new_printer_dialog = pp_new_printer_dialog_new (self->all_ppds_list,
963 new_printer_dialog_response_cb,
964 self);
965
966 gtk_window_set_transient_for (GTK_WINDOW (self->pp_new_printer_dialog),
967 GTK_WINDOW (native));
968
969 gtk_widget_set_visible (GTK_WIDGET (self->pp_new_printer_dialog), TRUE);
970 }
971
972 static void
973 update_sensitivity (gpointer user_data)
974 {
975 CcPrintersPanel *self = (CcPrintersPanel*) user_data;
976 const char *cups_server = NULL;
977 gboolean local_server = TRUE;
978 gboolean no_cups = FALSE;
979 gboolean empty_state = FALSE;
980
981 self->is_authorized =
982 self->permission &&
983 g_permission_get_allowed (G_PERMISSION (self->permission)) &&
984 self->lockdown_settings &&
985 !g_settings_get_boolean (self->lockdown_settings, "disable-print-setup");
986
987 if (g_strcmp0 (gtk_stack_get_visible_child_name (self->main_stack), "no-cups-page") == 0)
988 no_cups = TRUE;
989 else if (g_strcmp0 (gtk_stack_get_visible_child_name (self->main_stack), "empty-state") == 0)
990 empty_state = TRUE;
991
992 cups_server = cupsServer ();
993 if (cups_server &&
994 g_ascii_strncasecmp (cups_server, "localhost", 9) != 0 &&
995 g_ascii_strncasecmp (cups_server, "127.0.0.1", 9) != 0 &&
996 g_ascii_strncasecmp (cups_server, "::1", 3) != 0 &&
997 cups_server[0] != '/')
998 local_server = FALSE;
999
1000 gtk_widget_set_visible (self->search_button, !no_cups);
1001 gtk_widget_set_sensitive (self->search_button, !empty_state);
1002
1003 gtk_widget_set_visible (GTK_WIDGET (self->search_bar), !no_cups);
1004 gtk_widget_set_sensitive (GTK_WIDGET (self->search_bar), !empty_state);
1005
1006 gtk_widget_set_visible (self->printer_add_button, !empty_state);
1007 gtk_widget_set_sensitive (self->printer_add_button, local_server && self->is_authorized && !no_cups && !self->new_printer_name);
1008
1009 gtk_widget_set_sensitive (self->printer_add_button_empty, local_server && self->is_authorized && !no_cups && !self->new_printer_name);
1010 }
1011
1012 static void
1013 on_permission_changed (CcPrintersPanel *self)
1014 {
1015 actualize_printers_list (self);
1016 update_sensitivity (self);
1017 }
1018
1019 static void
1020 on_lockdown_settings_changed (CcPrintersPanel *self,
1021 const char *key)
1022 {
1023 if (g_str_equal (key, "disable-print-setup") == FALSE)
1024 return;
1025
1026 #if 0
1027 /* FIXME */
1028 gtk_widget_set_sensitive (self->lock_button,
1029 !g_settings_get_boolean (self->lockdown_settings, "disable-print-setup"));
1030 #endif
1031
1032 on_permission_changed (self);
1033 }
1034
1035 static void
1036 cups_status_check_cb (GObject *source_object,
1037 GAsyncResult *result,
1038 gpointer user_data)
1039 {
1040 CcPrintersPanel *self = (CcPrintersPanel*) user_data;
1041 gboolean success;
1042
1043 success = pp_cups_connection_test_finish (PP_CUPS (source_object), result, NULL);
1044 if (success)
1045 {
1046 actualize_printers_list (self);
1047 attach_to_cups_notifier (self);
1048
1049 g_clear_handle_id (&self->cups_status_check_id, g_source_remove);
1050 }
1051 }
1052
1053 static gboolean
1054 cups_status_check (gpointer user_data)
1055 {
1056 CcPrintersPanel *self = (CcPrintersPanel*) user_data;
1057
1058 pp_cups_connection_test_async (self->cups, NULL, cups_status_check_cb, self);
1059
1060 return self->cups_status_check_id != 0;
1061 }
1062
1063 static void
1064 connection_test_cb (GObject *source_object,
1065 GAsyncResult *result,
1066 gpointer user_data)
1067 {
1068 CcPrintersPanel *self;
1069 gboolean success;
1070 g_autoptr(GError) error = NULL;
1071
1072 success = pp_cups_connection_test_finish (PP_CUPS (source_object), result, &error);
1073
1074 if (error != NULL)
1075 {
1076 if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
1077 {
1078 g_warning ("Could not test connection: %s", error->message);
1079 }
1080
1081 return;
1082 }
1083
1084 self = CC_PRINTERS_PANEL (user_data);
1085
1086 if (!success)
1087 {
1088 self->cups_status_check_id =
1089 g_timeout_add_seconds (CUPS_STATUS_CHECK_INTERVAL, cups_status_check, self);
1090 }
1091 }
1092
1093 static void
1094 get_all_ppds_async_cb (PPDList *ppds,
1095 gpointer user_data)
1096 {
1097 CcPrintersPanel *self = (CcPrintersPanel*) user_data;
1098
1099 self->all_ppds_list = ppd_list_copy (ppds);
1100
1101 if (self->pp_new_printer_dialog)
1102 pp_new_printer_dialog_set_ppd_list (self->pp_new_printer_dialog,
1103 self->all_ppds_list);
1104 }
1105
1106 static gboolean
1107 filter_function (GtkListBoxRow *row,
1108 gpointer user_data)
1109 {
1110 CcPrintersPanel *self = (CcPrintersPanel*) user_data;
1111 PpPrinterEntry *entry = PP_PRINTER_ENTRY (row);
1112 gboolean retval;
1113 g_autofree gchar *search = NULL;
1114 g_autofree gchar *name = NULL;
1115 g_autofree gchar *location = NULL;
1116 GList *iter;
1117 const gchar *search_text;
1118
1119 search_text = gtk_editable_get_text (GTK_EDITABLE (self->search_entry));
1120
1121 if (g_utf8_strlen (search_text, -1) == 0)
1122 {
1123 retval = TRUE;
1124 }
1125 else
1126 {
1127 name = cc_util_normalize_casefold_and_unaccent (pp_printer_entry_get_name (entry));
1128 location = cc_util_normalize_casefold_and_unaccent (pp_printer_entry_get_location (entry));
1129
1130 search = cc_util_normalize_casefold_and_unaccent (search_text);
1131
1132 retval = strstr (name, search) != NULL;
1133 if (location != NULL)
1134 retval = retval || (strstr (location, search) != NULL);
1135 }
1136
1137 if (self->deleted_printer_name != NULL &&
1138 g_strcmp0 (self->deleted_printer_name, pp_printer_entry_get_name (entry)) == 0)
1139 {
1140 retval = FALSE;
1141 }
1142
1143 if (self->deleted_printers != NULL)
1144 {
1145 for (iter = self->deleted_printers; iter != NULL; iter = iter->next)
1146 {
1147 if (g_strcmp0 (iter->data, pp_printer_entry_get_name (entry)) == 0)
1148 {
1149 retval = FALSE;
1150 break;
1151 }
1152 }
1153 }
1154
1155 gtk_widget_set_visible (GTK_WIDGET (row), retval);
1156
1157 return retval;
1158 }
1159
1160 static gint
1161 sort_function (GtkListBoxRow *row1,
1162 GtkListBoxRow *row2,
1163 gpointer user_data)
1164 {
1165 PpPrinterEntry *entry1 = PP_PRINTER_ENTRY (row1);
1166 PpPrinterEntry *entry2 = PP_PRINTER_ENTRY (row2);
1167
1168 if (pp_printer_entry_get_name (entry1) != NULL)
1169 {
1170 if (pp_printer_entry_get_name (entry2) != NULL)
1171 return g_ascii_strcasecmp (pp_printer_entry_get_name (entry1), pp_printer_entry_get_name (entry2));
1172 else
1173 return 1;
1174 }
1175 else
1176 {
1177 if (pp_printer_entry_get_name (entry2) != NULL)
1178 return -1;
1179 else
1180 return 0;
1181 }
1182 }
1183
1184 static void
1185 cc_printers_panel_class_init (CcPrintersPanelClass *klass)
1186 {
1187 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1188 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1189 CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
1190
1191 object_class->get_property = cc_printers_panel_get_property;
1192 object_class->set_property = cc_printers_panel_set_property;
1193 object_class->constructed = cc_printers_panel_constructed;
1194 object_class->dispose = cc_printers_panel_dispose;
1195
1196 panel_class->get_help_uri = cc_printers_panel_get_help_uri;
1197
1198 g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
1199
1200 g_object_class_install_property (object_class,
1201 PROP_COMPACT,
1202 g_param_spec_boolean ("compact",
1203 "compact",
1204 "compact",
1205 FALSE,
1206 G_PARAM_READWRITE));
1207
1208 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/printers/cc-printers-panel.ui");
1209
1210 gtk_widget_class_bind_template_child (widget_class, CcPrintersPanel, content);
1211 gtk_widget_class_bind_template_child (widget_class, CcPrintersPanel, main_stack);
1212 gtk_widget_class_bind_template_child (widget_class, CcPrintersPanel, notification);
1213 gtk_widget_class_bind_template_child (widget_class, CcPrintersPanel, notification_label);
1214 gtk_widget_class_bind_template_child (widget_class, CcPrintersPanel, permission_infobar);
1215 gtk_widget_class_bind_template_child (widget_class, CcPrintersPanel, printer_add_button);
1216 gtk_widget_class_bind_template_child (widget_class, CcPrintersPanel, printer_add_button_empty);
1217 gtk_widget_class_bind_template_child (widget_class, CcPrintersPanel, scrolled_window);
1218 gtk_widget_class_bind_template_child (widget_class, CcPrintersPanel, search_bar);
1219 gtk_widget_class_bind_template_child (widget_class, CcPrintersPanel, search_button);
1220 gtk_widget_class_bind_template_child (widget_class, CcPrintersPanel, search_entry);
1221
1222 gtk_widget_class_bind_template_callback (widget_class, printer_add_cb);
1223 gtk_widget_class_bind_template_callback (widget_class, on_printer_deletion_undone);
1224 gtk_widget_class_bind_template_callback (widget_class, on_notification_dismissed);
1225 }
1226
1227 static void
1228 cc_printers_panel_init (CcPrintersPanel *self)
1229 {
1230 g_autoptr(GtkCssProvider) provider = NULL;
1231
1232 gtk_widget_init_template (GTK_WIDGET (self));
1233
1234 g_resources_register (cc_printers_get_resource ());
1235
1236 provider = gtk_css_provider_new ();
1237 gtk_css_provider_load_from_resource (provider,
1238 "/org/gnome/control-center/printers/printers.css");
1239 gtk_style_context_add_provider_for_display (gdk_display_get_default (),
1240 GTK_STYLE_PROVIDER (provider),
1241 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
1242
1243 /* initialize main data structure */
1244 self->reference = g_object_new (G_TYPE_OBJECT, NULL);
1245
1246 self->cups = pp_cups_new ();
1247
1248 self->printer_entries = g_hash_table_new_full (g_str_hash,
1249 g_str_equal,
1250 g_free,
1251 NULL);
1252
1253 g_type_ensure (CC_TYPE_PERMISSION_INFOBAR);
1254
1255 g_object_set_data_full (self->reference, "self", self, NULL);
1256
1257 /* connect signals */
1258 gtk_list_box_set_filter_func (self->content,
1259 filter_function,
1260 self,
1261 NULL);
1262 g_signal_connect_swapped (self->search_entry,
1263 "search-changed",
1264 G_CALLBACK (gtk_list_box_invalidate_filter),
1265 self->content);
1266 gtk_list_box_set_sort_func (self->content,
1267 sort_function,
1268 NULL,
1269 NULL);
1270
1271 self->lockdown_settings = g_settings_new ("org.gnome.desktop.lockdown");
1272 if (self->lockdown_settings)
1273 g_signal_connect_object (self->lockdown_settings,
1274 "changed",
1275 G_CALLBACK (on_lockdown_settings_changed),
1276 self,
1277 G_CONNECT_SWAPPED | G_CONNECT_AFTER);
1278
1279 /* Add unlock button */
1280 self->permission = (GPermission *)polkit_permission_new_sync (
1281 "org.opensuse.cupspkhelper.mechanism.all-edit", NULL, NULL, NULL);
1282 if (self->permission != NULL)
1283 {
1284 g_signal_connect_object (self->permission,
1285 "notify",
1286 G_CALLBACK (on_permission_changed),
1287 self,
1288 G_CONNECT_SWAPPED | G_CONNECT_AFTER);
1289
1290 cc_permission_infobar_set_permission (self->permission_infobar,
1291 self->permission);
1292 cc_permission_infobar_set_title (self->permission_infobar,
1293 _("Unlock to add printers and change settings"));
1294
1295 on_permission_changed (self);
1296 }
1297 else
1298 g_warning ("Your system does not have the cups-pk-helper's policy \
1299 \"org.opensuse.cupspkhelper.mechanism.all-edit\" installed. \
1300 Please check your installation");
1301
1302 self->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
1303
1304 actualize_printers_list (self);
1305 attach_to_cups_notifier (self);
1306
1307 get_all_ppds_async (cc_panel_get_cancellable (CC_PANEL (self)),
1308 get_all_ppds_async_cb,
1309 self);
1310
1311 pp_cups_connection_test_async (self->cups, cc_panel_get_cancellable (CC_PANEL (self)), connection_test_cb, self);
1312 }
1313