summaryrefslogtreecommitdiffstats
path: root/panels/printers/cc-printers-panel.c
diff options
context:
space:
mode:
Diffstat (limited to 'panels/printers/cc-printers-panel.c')
-rw-r--r--panels/printers/cc-printers-panel.c1340
1 files changed, 1340 insertions, 0 deletions
diff --git a/panels/printers/cc-printers-panel.c b/panels/printers/cc-printers-panel.c
new file mode 100644
index 0000000..34774a7
--- /dev/null
+++ b/panels/printers/cc-printers-panel.c
@@ -0,0 +1,1340 @@
+/*
+ * Copyright (C) 2010 Red Hat, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <config.h>
+
+#include "shell/cc-object-storage.h"
+
+#include "cc-printers-panel.h"
+#include "cc-printers-resources.h"
+#include "pp-printer.h"
+
+#include <string.h>
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+#include <polkit/polkit.h>
+#include <gdesktop-enums.h>
+
+#include <cups/cups.h>
+#include <cups/ppd.h>
+
+#include <math.h>
+
+#include "pp-new-printer-dialog.h"
+#include "pp-utils.h"
+#include "pp-cups.h"
+#include "pp-printer-entry.h"
+#include "pp-job.h"
+#include "pp-new-printer.h"
+
+#include "cc-permission-infobar.h"
+#include "cc-util.h"
+
+#define JOB_DEFAULT_PRIORITY 50
+#define RENEW_INTERVAL 500
+#define SUBSCRIPTION_DURATION 600
+
+#define CUPS_DBUS_NAME "org.cups.cupsd.Notifier"
+#define CUPS_DBUS_PATH "/org/cups/cupsd/Notifier"
+#define CUPS_DBUS_INTERFACE "org.cups.cupsd.Notifier"
+
+#define CUPS_STATUS_CHECK_INTERVAL 5
+
+#if (CUPS_VERSION_MAJOR > 1) || (CUPS_VERSION_MINOR > 5)
+#define HAVE_CUPS_1_6 1
+#endif
+
+#ifndef HAVE_CUPS_1_6
+#define ippGetState(ipp) ipp->state
+#define ippGetStatusCode(ipp) ipp->request.status.status_code
+#define ippGetString(attr, element, language) attr->values[element].string.text
+#endif
+
+struct _CcPrintersPanel
+{
+ CcPanel parent_instance;
+
+ GtkBuilder *builder;
+
+ PpCups *cups;
+
+ cups_dest_t *dests;
+ int num_dests;
+
+ GPermission *permission;
+ gboolean is_authorized;
+
+ GSettings *lockdown_settings;
+ CcPermissionInfobar *permission_infobar;
+
+ PpNewPrinterDialog *pp_new_printer_dialog;
+
+ GDBusProxy *cups_proxy;
+ GDBusConnection *cups_bus_connection;
+ gint subscription_id;
+ guint subscription_renewal_id;
+ guint cups_status_check_id;
+ guint dbus_subscription_id;
+ guint remove_printer_timeout_id;
+
+ GtkRevealer *notification;
+ PPDList *all_ppds_list;
+
+ gchar *new_printer_name;
+
+ gchar *renamed_printer_name;
+ gchar *old_printer_name;
+ gchar *deleted_printer_name;
+ GList *deleted_printers;
+ GObject *reference;
+
+ GHashTable *printer_entries;
+ gboolean entries_filled;
+ GVariant *action;
+
+ GtkSizeGroup *size_group;
+};
+
+CC_PANEL_REGISTER (CcPrintersPanel, cc_printers_panel)
+
+typedef struct
+{
+ gchar *printer_name;
+ GCancellable *cancellable;
+} SetPPDItem;
+
+enum {
+ PROP_0,
+ PROP_PARAMETERS
+};
+
+static void actualize_printers_list (CcPrintersPanel *self);
+static void update_sensitivity (gpointer user_data);
+static void detach_from_cups_notifier (gpointer data);
+static void free_dests (CcPrintersPanel *self);
+static void set_current_page (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data);
+
+static void
+execute_action (CcPrintersPanel *self,
+ GVariant *action)
+{
+ PpPrinterEntry *printer_entry;
+ const gchar *action_name;
+ const gchar *printer_name;
+ gint count;
+
+ count = g_variant_n_children (action);
+ if (count == 2)
+ {
+ g_autoptr(GVariant) action_variant = NULL;
+
+ g_variant_get_child (action, 0, "v", &action_variant);
+ action_name = g_variant_get_string (action_variant, NULL);
+
+ /* authenticate-jobs printer-name */
+ if (g_strcmp0 (action_name, "authenticate-jobs") == 0)
+ {
+ g_autoptr(GVariant) variant = NULL;
+
+ g_variant_get_child (action, 1, "v", &variant);
+ printer_name = g_variant_get_string (variant, NULL);
+
+ printer_entry = PP_PRINTER_ENTRY (g_hash_table_lookup (self->printer_entries, printer_name));
+ if (printer_entry != NULL)
+ pp_printer_entry_authenticate_jobs (printer_entry);
+ else
+ g_warning ("Could not find printer \"%s\"!", printer_name);
+ }
+ /* show-jobs printer-name */
+ else if (g_strcmp0 (action_name, "show-jobs") == 0)
+ {
+ g_autoptr(GVariant) variant = NULL;
+
+ g_variant_get_child (action, 1, "v", &variant);
+ printer_name = g_variant_get_string (variant, NULL);
+
+ printer_entry = PP_PRINTER_ENTRY (g_hash_table_lookup (self->printer_entries, printer_name));
+ if (printer_entry != NULL)
+ pp_printer_entry_show_jobs_dialog (printer_entry);
+ else
+ g_warning ("Could not find printer \"%s\"!", printer_name);
+ }
+ }
+}
+
+static void
+cc_printers_panel_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id)
+ {
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+cc_printers_panel_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ CcPrintersPanel *self = CC_PRINTERS_PANEL (object);
+ GVariant *parameters;
+
+ switch (property_id)
+ {
+ case PROP_PARAMETERS:
+ parameters = g_value_get_variant (value);
+ if (parameters != NULL && g_variant_n_children (parameters) > 0)
+ {
+ if (self->entries_filled)
+ {
+ execute_action (CC_PRINTERS_PANEL (object), parameters);
+ }
+ else
+ {
+ if (self->action != NULL)
+ g_variant_unref (self->action);
+ self->action = g_variant_ref (parameters);
+ }
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+ }
+}
+
+static void
+cc_printers_panel_constructed (GObject *object)
+{
+ CcPrintersPanel *self = CC_PRINTERS_PANEL (object);
+ GtkWidget *widget;
+ CcShell *shell;
+
+ G_OBJECT_CLASS (cc_printers_panel_parent_class)->constructed (object);
+
+ shell = cc_panel_get_shell (CC_PANEL (self));
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (self->builder, "search-bar");
+ gtk_search_bar_set_key_capture_widget (GTK_SEARCH_BAR (widget),
+ GTK_WIDGET (shell));
+}
+
+static void
+printer_removed_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ PpPrinter *printer = PP_PRINTER (source_object);
+ g_autoptr(GError) error = NULL;
+
+ pp_printer_delete_finish (printer, result, &error);
+
+ if (user_data != NULL)
+ {
+ g_autoptr(GObject) reference = G_OBJECT (user_data);
+
+ if (g_object_get_data (reference, "self") != NULL)
+ {
+ CcPrintersPanel *self = CC_PRINTERS_PANEL (g_object_get_data (reference, "self"));
+ GList *iter;
+
+ for (iter = self->deleted_printers; iter != NULL; iter = iter->next)
+ {
+ if (g_strcmp0 (iter->data, pp_printer_get_name (printer)) == 0)
+ {
+ g_free (iter->data);
+ self->deleted_printers = g_list_delete_link (self->deleted_printers, iter);
+ break;
+ }
+ }
+ }
+ }
+
+ if (error != NULL)
+ g_warning ("Printer could not be deleted: %s", error->message);
+}
+
+static void
+cc_printers_panel_dispose (GObject *object)
+{
+ CcPrintersPanel *self = CC_PRINTERS_PANEL (object);
+
+ detach_from_cups_notifier (CC_PRINTERS_PANEL (object));
+
+ if (self->deleted_printer_name != NULL)
+ {
+ g_autoptr(PpPrinter) printer = pp_printer_new (self->deleted_printer_name);
+ pp_printer_delete_async (printer,
+ NULL,
+ printer_removed_cb,
+ NULL);
+ }
+
+ g_clear_object (&self->cups);
+ g_clear_pointer (&self->new_printer_name, g_free);
+ g_clear_pointer (&self->renamed_printer_name, g_free);
+ g_clear_pointer (&self->old_printer_name, g_free);
+ g_clear_object (&self->builder);
+ g_clear_object (&self->lockdown_settings);
+ g_clear_object (&self->permission);
+ g_clear_handle_id (&self->cups_status_check_id, g_source_remove);
+ g_clear_handle_id (&self->remove_printer_timeout_id, g_source_remove);
+ g_clear_pointer (&self->deleted_printer_name, g_free);
+ g_clear_pointer (&self->action, g_variant_unref);
+ g_clear_pointer (&self->printer_entries, g_hash_table_destroy);
+ g_clear_pointer (&self->all_ppds_list, ppd_list_free);
+ free_dests (self);
+ g_list_free_full (self->deleted_printers, g_free);
+ self->deleted_printers = NULL;
+ if (self->reference != NULL)
+ g_object_set_data (self->reference, "self", NULL);
+ g_clear_object (&self->reference);
+
+ G_OBJECT_CLASS (cc_printers_panel_parent_class)->dispose (object);
+}
+
+static const char *
+cc_printers_panel_get_help_uri (CcPanel *panel)
+{
+ return "help:gnome-help/printing";
+}
+
+static void
+cc_printers_panel_class_init (CcPrintersPanelClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ CcPanelClass *panel_class = CC_PANEL_CLASS (klass);
+
+ object_class->get_property = cc_printers_panel_get_property;
+ object_class->set_property = cc_printers_panel_set_property;
+ object_class->constructed = cc_printers_panel_constructed;
+ object_class->dispose = cc_printers_panel_dispose;
+
+ panel_class->get_help_uri = cc_printers_panel_get_help_uri;
+
+ g_object_class_override_property (object_class, PROP_PARAMETERS, "parameters");
+}
+
+static void
+on_get_job_attributes_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) user_data;
+ const gchar *job_originating_user_name;
+ const gchar *job_printer_uri;
+ g_autoptr(GVariant) attributes = NULL;
+ g_autoptr(GError) error = NULL;
+
+ attributes = pp_job_get_attributes_finish (PP_JOB (source_object), res, &error);
+
+ if (attributes != NULL)
+ {
+ g_autoptr(GVariant) username = NULL;
+
+ if ((username = g_variant_lookup_value (attributes, "job-originating-user-name", G_VARIANT_TYPE ("as"))) != NULL)
+ {
+ g_autoptr(GVariant) printer_uri = NULL;
+
+ if ((printer_uri = g_variant_lookup_value (attributes, "job-printer-uri", G_VARIANT_TYPE ("as"))) != NULL)
+ {
+ job_originating_user_name = g_variant_get_string (g_variant_get_child_value (username, 0), NULL);
+ job_printer_uri = g_variant_get_string (g_variant_get_child_value (printer_uri, 0), NULL);
+
+ if (job_originating_user_name != NULL && job_printer_uri != NULL &&
+ g_strcmp0 (job_originating_user_name, cupsUser ()) == 0 &&
+ g_strrstr (job_printer_uri, "/") != 0 &&
+ self->dests != NULL)
+ {
+ PpPrinterEntry *printer_entry;
+ gchar *printer_name;
+
+ printer_name = g_strrstr (job_printer_uri, "/") + 1;
+ printer_entry = PP_PRINTER_ENTRY (g_hash_table_lookup (self->printer_entries, printer_name));
+
+ pp_printer_entry_update_jobs_count (printer_entry);
+ }
+ }
+ }
+ }
+}
+
+static void
+on_cups_notification (GDBusConnection *connection,
+ const char *sender_name,
+ const char *object_path,
+ const char *interface_name,
+ const char *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) user_data;
+ gboolean printer_is_accepting_jobs;
+ gchar *printer_name = NULL;
+ gchar *text = NULL;
+ gchar *printer_uri = NULL;
+ gchar *printer_state_reasons = NULL;
+ gchar *job_state_reasons = NULL;
+ gchar *job_name = NULL;
+ guint job_id;
+ gint printer_state;
+ gint job_state;
+ gint job_impressions_completed;
+ static gchar *requested_attrs[] = {
+ "job-printer-uri",
+ "job-originating-user-name",
+ NULL };
+
+ if (g_strcmp0 (signal_name, "PrinterAdded") != 0 &&
+ g_strcmp0 (signal_name, "PrinterDeleted") != 0 &&
+ g_strcmp0 (signal_name, "PrinterStateChanged") != 0 &&
+ g_strcmp0 (signal_name, "PrinterStopped") != 0 &&
+ g_strcmp0 (signal_name, "JobCreated") != 0 &&
+ g_strcmp0 (signal_name, "JobCompleted") != 0)
+ return;
+
+ if (g_variant_n_children (parameters) == 1)
+ g_variant_get (parameters, "(&s)", &text);
+ else if (g_variant_n_children (parameters) == 6)
+ {
+ g_variant_get (parameters, "(&s&s&su&sb)",
+ &text,
+ &printer_uri,
+ &printer_name,
+ &printer_state,
+ &printer_state_reasons,
+ &printer_is_accepting_jobs);
+ }
+ else if (g_variant_n_children (parameters) == 11)
+ {
+ g_variant_get (parameters, "(&s&s&su&sbuu&s&su)",
+ &text,
+ &printer_uri,
+ &printer_name,
+ &printer_state,
+ &printer_state_reasons,
+ &printer_is_accepting_jobs,
+ &job_id,
+ &job_state,
+ &job_state_reasons,
+ &job_name,
+ &job_impressions_completed);
+ }
+
+ if (g_strcmp0 (signal_name, "PrinterAdded") == 0 ||
+ g_strcmp0 (signal_name, "PrinterDeleted") == 0 ||
+ g_strcmp0 (signal_name, "PrinterStateChanged") == 0 ||
+ g_strcmp0 (signal_name, "PrinterStopped") == 0)
+ actualize_printers_list (self);
+ else if (g_strcmp0 (signal_name, "JobCreated") == 0 ||
+ g_strcmp0 (signal_name, "JobCompleted") == 0)
+ {
+ g_autoptr(PpJob) job = NULL;
+
+ job = pp_job_new (job_id, NULL, 0, JOB_DEFAULT_PRIORITY, NULL);
+ pp_job_get_attributes_async (job,
+ requested_attrs,
+ cc_panel_get_cancellable (CC_PANEL (self)),
+ on_get_job_attributes_cb,
+ self);
+ }
+}
+
+static gchar *subscription_events[] = {
+ "printer-added",
+ "printer-deleted",
+ "printer-stopped",
+ "printer-state-changed",
+ "job-created",
+ "job-completed",
+ NULL};
+
+static void
+renew_subscription_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) user_data;
+ gint subscription_id;
+
+ subscription_id = pp_cups_renew_subscription_finish (PP_CUPS (source_object), result);
+
+ if (subscription_id > 0)
+ self->subscription_id = subscription_id;
+}
+
+static gboolean
+renew_subscription (gpointer data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) data;
+
+ pp_cups_renew_subscription_async (self->cups,
+ self->subscription_id,
+ subscription_events,
+ SUBSCRIPTION_DURATION,
+ cc_panel_get_cancellable (CC_PANEL (self)),
+ renew_subscription_cb,
+ data);
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+attach_to_cups_notifier_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) user_data;
+ g_autoptr(GError) error = NULL;
+ gint subscription_id;
+
+ subscription_id = pp_cups_renew_subscription_finish (PP_CUPS (source_object), result);
+
+ if (subscription_id > 0)
+ {
+ self->subscription_id = subscription_id;
+
+ self->subscription_renewal_id =
+ g_timeout_add_seconds (RENEW_INTERVAL, renew_subscription, self);
+
+ self->cups_proxy = cc_object_storage_create_dbus_proxy_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ CUPS_DBUS_NAME,
+ CUPS_DBUS_PATH,
+ CUPS_DBUS_INTERFACE,
+ NULL,
+ &error);
+
+ if (!self->cups_proxy)
+ {
+ g_warning ("%s", error->message);
+ return;
+ }
+
+ self->cups_bus_connection = g_dbus_proxy_get_connection (self->cups_proxy);
+
+ self->dbus_subscription_id =
+ g_dbus_connection_signal_subscribe (self->cups_bus_connection,
+ NULL,
+ CUPS_DBUS_INTERFACE,
+ NULL,
+ CUPS_DBUS_PATH,
+ NULL,
+ 0,
+ on_cups_notification,
+ self,
+ NULL);
+ }
+}
+
+static void
+attach_to_cups_notifier (gpointer data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) data;
+
+ pp_cups_renew_subscription_async (self->cups,
+ self->subscription_id,
+ subscription_events,
+ SUBSCRIPTION_DURATION,
+ cc_panel_get_cancellable (CC_PANEL (self)),
+ attach_to_cups_notifier_cb,
+ data);
+}
+
+static void
+subscription_cancel_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ pp_cups_cancel_subscription_finish (PP_CUPS (source_object), result);
+}
+
+static void
+detach_from_cups_notifier (gpointer data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) data;
+
+ if (self->dbus_subscription_id != 0) {
+ g_dbus_connection_signal_unsubscribe (self->cups_bus_connection,
+ self->dbus_subscription_id);
+ self->dbus_subscription_id = 0;
+ }
+
+ pp_cups_cancel_subscription_async (self->cups,
+ self->subscription_id,
+ subscription_cancel_cb,
+ NULL);
+
+ self->subscription_id = 0;
+
+ if (self->subscription_renewal_id != 0) {
+ g_source_remove (self->subscription_renewal_id);
+ self->subscription_renewal_id = 0;
+ }
+
+ g_clear_object (&self->cups_proxy);
+}
+
+static void
+free_dests (CcPrintersPanel *self)
+{
+ if (self->num_dests > 0)
+ {
+ cupsFreeDests (self->num_dests, self->dests);
+ }
+ self->dests = NULL;
+ self->num_dests = 0;
+}
+
+static void
+on_printer_deletion_undone (CcPrintersPanel *self)
+{
+ GtkWidget *widget;
+
+ gtk_revealer_set_reveal_child (self->notification, FALSE);
+
+ g_clear_pointer (&self->deleted_printer_name, g_free);
+
+ widget = (GtkWidget*) gtk_builder_get_object (self->builder, "content");
+ gtk_list_box_invalidate_filter (GTK_LIST_BOX (widget));
+
+ g_clear_handle_id (&self->remove_printer_timeout_id, g_source_remove);
+
+ if (self->num_dests > 0)
+ {
+ widget = (GtkWidget *) gtk_builder_get_object (self->builder, "main-vbox");
+ gtk_stack_set_visible_child_name (GTK_STACK (widget), "printers-list");
+ }
+}
+
+static void
+on_notification_dismissed (CcPrintersPanel *self)
+{
+ g_clear_handle_id (&self->remove_printer_timeout_id, g_source_remove);
+
+ if (self->deleted_printer_name != NULL)
+ {
+ g_autoptr(PpPrinter) printer = NULL;
+
+ printer = pp_printer_new (self->deleted_printer_name);
+ /* The reference tells to the callback whether
+ printers panel was already destroyed so
+ it knows whether it can access the list
+ of deleted printers in it (see below).
+ */
+ pp_printer_delete_async (printer,
+ NULL,
+ printer_removed_cb,
+ g_object_ref (self->reference));
+
+ /* List of printers which were recently deleted but are still available
+ in CUPS due to async nature of the method (e.g. quick deletion
+ of several printers).
+ */
+ self->deleted_printers = g_list_prepend (self->deleted_printers, self->deleted_printer_name);
+ self->deleted_printer_name = NULL;
+ }
+
+ gtk_revealer_set_reveal_child (self->notification, FALSE);
+}
+
+static gboolean
+on_remove_printer_timeout (CcPrintersPanel *self)
+{
+ self->remove_printer_timeout_id = 0;
+
+ on_notification_dismissed (self);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+on_printer_deleted (CcPrintersPanel *self,
+ PpPrinterEntry *printer_entry)
+{
+ GtkLabel *label;
+ g_autofree gchar *notification_message = NULL;
+ GtkWidget *widget;
+
+ on_notification_dismissed (self);
+
+ /* Translators: %s is the printer name */
+ notification_message = g_strdup_printf (_("Printer ā€œ%sā€ has been deleted"),
+ pp_printer_entry_get_name (printer_entry));
+ label = (GtkLabel*)
+ gtk_builder_get_object (self->builder, "notification-label");
+ gtk_label_set_label (label, notification_message);
+
+ self->deleted_printer_name = g_strdup (pp_printer_entry_get_name (printer_entry));
+
+ widget = (GtkWidget*) gtk_builder_get_object (self->builder, "content");
+ gtk_list_box_invalidate_filter (GTK_LIST_BOX (widget));
+
+ gtk_revealer_set_reveal_child (self->notification, TRUE);
+
+ self->remove_printer_timeout_id = g_timeout_add_seconds (10, G_SOURCE_FUNC (on_remove_printer_timeout), self);
+
+ if (self->num_dests == 1 + g_list_length (self->deleted_printers))
+ pp_cups_connection_test_async (self->cups, NULL, set_current_page, self);
+}
+
+static void
+on_printer_renamed (CcPrintersPanel *self,
+ gchar *new_name,
+ PpPrinterEntry *printer_entry)
+{
+ self->old_printer_name = g_strdup (pp_printer_entry_get_name (printer_entry));
+ self->renamed_printer_name = g_strdup (new_name);
+}
+
+static void
+on_printer_changed (CcPrintersPanel *self)
+{
+ actualize_printers_list (self);
+}
+
+static void
+add_printer_entry (CcPrintersPanel *self,
+ cups_dest_t printer)
+{
+ PpPrinterEntry *printer_entry;
+ GtkWidget *content;
+ GSList *widgets, *l;
+
+ content = (GtkWidget*) gtk_builder_get_object (self->builder, "content");
+
+ printer_entry = pp_printer_entry_new (printer, self->is_authorized);
+ gtk_widget_show (GTK_WIDGET (printer_entry));
+
+ widgets = pp_printer_entry_get_size_group_widgets (printer_entry);
+ for (l = widgets; l != NULL; l = l->next)
+ gtk_size_group_add_widget (self->size_group, GTK_WIDGET (l->data));
+ g_slist_free (widgets);
+
+ g_signal_connect_object (printer_entry,
+ "printer-changed",
+ G_CALLBACK (on_printer_changed),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (printer_entry,
+ "printer-delete",
+ G_CALLBACK (on_printer_deleted),
+ self,
+ G_CONNECT_SWAPPED);
+ g_signal_connect_object (printer_entry,
+ "printer-renamed",
+ G_CALLBACK (on_printer_renamed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ gtk_list_box_insert (GTK_LIST_BOX (content), GTK_WIDGET (printer_entry), -1);
+
+ g_hash_table_insert (self->printer_entries, g_strdup (printer.name), printer_entry);
+}
+
+static void
+set_current_page (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel *) user_data;
+ GtkWidget *widget;
+ gboolean success;
+
+ success = pp_cups_connection_test_finish (PP_CUPS (source_object), result, NULL);
+
+ widget = (GtkWidget*) gtk_builder_get_object (self->builder, "main-vbox");
+ if (success)
+ gtk_stack_set_visible_child_name (GTK_STACK (widget), "empty-state");
+ else
+ gtk_stack_set_visible_child_name (GTK_STACK (widget), "no-cups-page");
+
+ update_sensitivity (user_data);
+}
+
+static gboolean
+remove_nonexisting_entry (CcPrintersPanel *self,
+ PpPrinterEntry *entry)
+{
+ gboolean exists = FALSE;
+ gint i;
+
+ for (i = 0; i < self->num_dests; i++)
+ {
+ if (g_strcmp0 (self->dests[i].name, pp_printer_entry_get_name (entry)) == 0)
+ {
+ exists = TRUE;
+ break;
+ }
+ }
+
+ if (!exists)
+ g_hash_table_remove (self->printer_entries, pp_printer_entry_get_name (entry));
+
+ return !exists;
+}
+
+static void
+actualize_printers_list_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) user_data;
+ GtkWidget *widget;
+ PpCupsDests *cups_dests;
+ GtkWidget *child;
+ gboolean new_printer_available = FALSE;
+ g_autoptr(GError) error = NULL;
+ gpointer item;
+ int i;
+
+ cups_dests = pp_cups_get_dests_finish (PP_CUPS (source_object), result, &error);
+
+ if (cups_dests == NULL && error != NULL)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_warning ("Could not get dests: %s", error->message);
+ }
+
+ return;
+ }
+
+ free_dests (self);
+ self->dests = cups_dests->dests;
+ self->num_dests = cups_dests->num_of_dests;
+ g_free (cups_dests);
+
+ widget = (GtkWidget*) gtk_builder_get_object (self->builder, "main-vbox");
+ if ((self->num_dests == 0 && self->new_printer_name == NULL) ||
+ (self->num_dests == 1 + g_list_length (self->deleted_printers) &&
+ self->deleted_printer_name != NULL))
+ pp_cups_connection_test_async (PP_CUPS (source_object), NULL, set_current_page, self);
+ else
+ gtk_stack_set_visible_child_name (GTK_STACK (widget), "printers-list");
+
+ widget = (GtkWidget*) gtk_builder_get_object (self->builder, "content");
+ child = gtk_widget_get_first_child (widget);
+ while (child)
+ {
+ GtkWidget *next = gtk_widget_get_next_sibling (child);
+
+ if (remove_nonexisting_entry (self, PP_PRINTER_ENTRY (child)))
+ gtk_list_box_remove (GTK_LIST_BOX (widget), child);
+
+ child = next;
+ }
+
+ for (i = 0; i < self->num_dests; i++)
+ {
+ new_printer_available = g_strcmp0 (self->dests[i].name, self->renamed_printer_name) == 0;
+ if (new_printer_available)
+ break;
+ }
+
+ for (i = 0; i < self->num_dests; i++)
+ {
+ if (new_printer_available && g_strcmp0 (self->dests[i].name, self->old_printer_name) == 0)
+ continue;
+
+ item = g_hash_table_lookup (self->printer_entries, self->dests[i].name);
+ if (item != NULL)
+ pp_printer_entry_update (PP_PRINTER_ENTRY (item), self->dests[i], self->is_authorized);
+ else
+ add_printer_entry (self, self->dests[i]);
+ }
+
+ if (!self->entries_filled)
+ {
+ if (self->action != NULL)
+ {
+ execute_action (self, self->action);
+ g_variant_unref (self->action);
+ self->action = NULL;
+ }
+
+ self->entries_filled = TRUE;
+ }
+
+ update_sensitivity (user_data);
+
+ if (self->new_printer_name != NULL)
+ {
+ GtkScrolledWindow *scrolled_window;
+ GtkAllocation allocation;
+ GtkAdjustment *adjustment;
+ GtkWidget *printer_entry;
+
+ /* Scroll the view to show the newly added printer-entry. */
+ scrolled_window = GTK_SCROLLED_WINDOW (gtk_builder_get_object (self->builder,
+ "scrolled-window"));
+ adjustment = gtk_scrolled_window_get_vadjustment (scrolled_window);
+
+ printer_entry = GTK_WIDGET (g_hash_table_lookup (self->printer_entries,
+ self->new_printer_name));
+ if (printer_entry != NULL)
+ {
+ gtk_widget_get_allocation (printer_entry, &allocation);
+ g_clear_pointer (&self->new_printer_name, g_free);
+
+ gtk_adjustment_set_value (adjustment,
+ allocation.y - gtk_widget_get_margin_top (printer_entry));
+ }
+ }
+}
+
+static void
+actualize_printers_list (CcPrintersPanel *self)
+{
+ pp_cups_get_dests_async (self->cups,
+ cc_panel_get_cancellable (CC_PANEL (self)),
+ actualize_printers_list_cb,
+ self);
+}
+
+static void
+printer_add_async_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) user_data;
+ gboolean success;
+ g_autoptr(GError) error = NULL;
+
+ success = pp_new_printer_add_finish (PP_NEW_PRINTER (source_object), res, &error);
+
+ if (!success)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_warning ("%s", error->message);
+
+ GtkWidget *message_dialog;
+
+ message_dialog = gtk_message_dialog_new (NULL,
+ 0,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_CLOSE,
+ /* Translators: Addition of the new printer failed. */
+ _("Failed to add new printer."));
+ g_signal_connect (message_dialog,
+ "response",
+ G_CALLBACK (gtk_window_destroy),
+ NULL);
+ gtk_widget_show (message_dialog);
+ }
+ }
+
+ actualize_printers_list (self);
+}
+
+static void
+new_printer_dialog_response_cb (GtkDialog *_dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) user_data;
+ PpNewPrinterDialog *pp_new_printer_dialog = PP_NEW_PRINTER_DIALOG (_dialog);
+ g_autoptr(PpNewPrinter) new_printer = NULL;
+
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ new_printer = pp_new_printer_dialog_get_new_printer (pp_new_printer_dialog);
+ g_object_get(G_OBJECT (new_printer), "name", &self->new_printer_name, NULL);
+
+ actualize_printers_list (self);
+
+ pp_new_printer_add_async (new_printer,
+ cc_panel_get_cancellable (CC_PANEL (self)),
+ printer_add_async_cb,
+ self);
+ }
+
+ gtk_window_destroy (GTK_WINDOW (pp_new_printer_dialog));
+ self->pp_new_printer_dialog = NULL;
+}
+
+static void
+printer_add_cb (CcPrintersPanel *self)
+{
+ GtkNative *native;
+
+ native = gtk_widget_get_native (GTK_WIDGET (self));
+ self->pp_new_printer_dialog = pp_new_printer_dialog_new (self->all_ppds_list,
+ new_printer_dialog_response_cb,
+ self);
+
+ gtk_window_set_transient_for (GTK_WINDOW (self->pp_new_printer_dialog),
+ GTK_WINDOW (native));
+
+ gtk_widget_show (GTK_WIDGET (self->pp_new_printer_dialog));
+}
+
+static void
+update_sensitivity (gpointer user_data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) user_data;
+ const char *cups_server = NULL;
+ GtkWidget *widget;
+ gboolean local_server = TRUE;
+ gboolean no_cups = FALSE;
+ gboolean empty_state = FALSE;
+
+ self->is_authorized =
+ self->permission &&
+ g_permission_get_allowed (G_PERMISSION (self->permission)) &&
+ self->lockdown_settings &&
+ !g_settings_get_boolean (self->lockdown_settings, "disable-print-setup");
+
+ widget = (GtkWidget*) gtk_builder_get_object (self->builder, "main-vbox");
+ if (g_strcmp0 (gtk_stack_get_visible_child_name (GTK_STACK (widget)), "no-cups-page") == 0)
+ no_cups = TRUE;
+ else if (g_strcmp0 (gtk_stack_get_visible_child_name (GTK_STACK (widget)), "empty-state") == 0)
+ empty_state = TRUE;
+
+ cups_server = cupsServer ();
+ if (cups_server &&
+ g_ascii_strncasecmp (cups_server, "localhost", 9) != 0 &&
+ g_ascii_strncasecmp (cups_server, "127.0.0.1", 9) != 0 &&
+ g_ascii_strncasecmp (cups_server, "::1", 3) != 0 &&
+ cups_server[0] != '/')
+ local_server = FALSE;
+
+ widget = (GtkWidget*) gtk_builder_get_object (self->builder, "search-button");
+ gtk_widget_set_visible (widget, !no_cups);
+
+ widget = (GtkWidget*) gtk_builder_get_object (self->builder, "search-bar");
+ gtk_widget_set_visible (widget, !no_cups);
+
+ widget = (GtkWidget*) gtk_builder_get_object (self->builder, "printer-add-button");
+ gtk_widget_set_visible (widget, !empty_state);
+ gtk_widget_set_sensitive (widget, local_server && self->is_authorized && !no_cups && !self->new_printer_name);
+
+ widget = (GtkWidget*) gtk_builder_get_object (self->builder, "printer-add-button2");
+ gtk_widget_set_sensitive (widget, local_server && self->is_authorized && !no_cups && !self->new_printer_name);
+}
+
+static void
+on_permission_changed (CcPrintersPanel *self)
+{
+ actualize_printers_list (self);
+ update_sensitivity (self);
+}
+
+static void
+on_lockdown_settings_changed (CcPrintersPanel *self,
+ const char *key)
+{
+ if (g_str_equal (key, "disable-print-setup") == FALSE)
+ return;
+
+#if 0
+ /* FIXME */
+ gtk_widget_set_sensitive (self->lock_button,
+ !g_settings_get_boolean (self->lockdown_settings, "disable-print-setup"));
+#endif
+
+ on_permission_changed (self);
+}
+
+static void
+cups_status_check_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) user_data;
+ gboolean success;
+
+ success = pp_cups_connection_test_finish (PP_CUPS (source_object), result, NULL);
+ if (success)
+ {
+ actualize_printers_list (self);
+ attach_to_cups_notifier (self);
+
+ g_source_remove (self->cups_status_check_id);
+ self->cups_status_check_id = 0;
+ }
+}
+
+static gboolean
+cups_status_check (gpointer user_data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) user_data;
+
+ pp_cups_connection_test_async (self->cups, NULL, cups_status_check_cb, self);
+
+ return self->cups_status_check_id != 0;
+}
+
+static void
+connection_test_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CcPrintersPanel *self;
+ gboolean success;
+ g_autoptr(GError) error = NULL;
+
+ success = pp_cups_connection_test_finish (PP_CUPS (source_object), result, &error);
+
+ if (error != NULL)
+ {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ {
+ g_warning ("Could not test connection: %s", error->message);
+ }
+
+ return;
+ }
+
+ self = CC_PRINTERS_PANEL (user_data);
+
+ if (!success)
+ {
+ self->cups_status_check_id =
+ g_timeout_add_seconds (CUPS_STATUS_CHECK_INTERVAL, cups_status_check, self);
+ }
+}
+
+static void
+get_all_ppds_async_cb (PPDList *ppds,
+ gpointer user_data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) user_data;
+
+ self->all_ppds_list = ppd_list_copy (ppds);
+
+ if (self->pp_new_printer_dialog)
+ pp_new_printer_dialog_set_ppd_list (self->pp_new_printer_dialog,
+ self->all_ppds_list);
+}
+
+static gboolean
+filter_function (GtkListBoxRow *row,
+ gpointer user_data)
+{
+ CcPrintersPanel *self = (CcPrintersPanel*) user_data;
+ PpPrinterEntry *entry = PP_PRINTER_ENTRY (row);
+ GtkWidget *search_entry;
+ gboolean retval;
+ g_autofree gchar *search = NULL;
+ g_autofree gchar *name = NULL;
+ g_autofree gchar *location = NULL;
+ GList *iter;
+ const gchar *search_text;
+
+ search_entry = (GtkWidget*)
+ gtk_builder_get_object (self->builder, "search-entry");
+ search_text = gtk_editable_get_text (GTK_EDITABLE (search_entry));
+
+ if (g_utf8_strlen (search_text, -1) == 0)
+ {
+ retval = TRUE;
+ }
+ else
+ {
+ name = cc_util_normalize_casefold_and_unaccent (pp_printer_entry_get_name (entry));
+ location = cc_util_normalize_casefold_and_unaccent (pp_printer_entry_get_location (entry));
+
+ search = cc_util_normalize_casefold_and_unaccent (search_text);
+
+ retval = strstr (name, search) != NULL;
+ if (location != NULL)
+ retval = retval || (strstr (location, search) != NULL);
+ }
+
+ if (self->deleted_printer_name != NULL &&
+ g_strcmp0 (self->deleted_printer_name, pp_printer_entry_get_name (entry)) == 0)
+ {
+ retval = FALSE;
+ }
+
+ if (self->deleted_printers != NULL)
+ {
+ for (iter = self->deleted_printers; iter != NULL; iter = iter->next)
+ {
+ if (g_strcmp0 (iter->data, pp_printer_entry_get_name (entry)) == 0)
+ {
+ retval = FALSE;
+ break;
+ }
+ }
+ }
+
+ return retval;
+}
+
+static gint
+sort_function (GtkListBoxRow *row1,
+ GtkListBoxRow *row2,
+ gpointer user_data)
+{
+ PpPrinterEntry *entry1 = PP_PRINTER_ENTRY (row1);
+ PpPrinterEntry *entry2 = PP_PRINTER_ENTRY (row2);
+
+ if (pp_printer_entry_get_name (entry1) != NULL)
+ {
+ if (pp_printer_entry_get_name (entry2) != NULL)
+ return g_ascii_strcasecmp (pp_printer_entry_get_name (entry1), pp_printer_entry_get_name (entry2));
+ else
+ return 1;
+ }
+ else
+ {
+ if (pp_printer_entry_get_name (entry2) != NULL)
+ return -1;
+ else
+ return 0;
+ }
+}
+
+static void
+cc_printers_panel_init (CcPrintersPanel *self)
+{
+ GtkWidget *top_widget;
+ GtkWidget *widget;
+ g_autoptr(GError) error = NULL;
+ const gchar *objects[] = { "overlay", "permission-infobar", "top-right-buttons", "printer-add-button", "search-button", NULL };
+ guint builder_result;
+
+ g_resources_register (cc_printers_get_resource ());
+
+ /* initialize main data structure */
+ self->builder = gtk_builder_new ();
+ self->reference = g_object_new (G_TYPE_OBJECT, NULL);
+
+ self->cups = pp_cups_new ();
+
+ self->printer_entries = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+
+ g_type_ensure (CC_TYPE_PERMISSION_INFOBAR);
+
+ g_object_set_data_full (self->reference, "self", self, NULL);
+
+ builder_result = gtk_builder_add_objects_from_resource (self->builder,
+ "/org/gnome/control-center/printers/printers.ui",
+ objects, &error);
+
+ if (builder_result == 0)
+ {
+ /* Translators: The XML file containing user interface can not be loaded */
+ g_warning (_("Could not load ui: %s"), error->message);
+ return;
+ }
+
+ self->notification = (GtkRevealer*)
+ gtk_builder_get_object (self->builder, "notification");
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (self->builder, "notification-undo-button");
+ g_signal_connect_object (widget, "clicked", G_CALLBACK (on_printer_deletion_undone), self, G_CONNECT_SWAPPED);
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (self->builder, "notification-dismiss-button");
+ g_signal_connect_object (widget, "clicked", G_CALLBACK (on_notification_dismissed), self, G_CONNECT_SWAPPED);
+
+ self->permission_infobar = (CcPermissionInfobar*)
+ gtk_builder_get_object (self->builder, "permission-infobar");
+
+ /* add the top level widget */
+ top_widget = (GtkWidget*)
+ gtk_builder_get_object (self->builder, "overlay");
+
+ /* connect signals */
+ widget = (GtkWidget*)
+ gtk_builder_get_object (self->builder, "printer-add-button");
+ g_signal_connect_object (widget, "clicked", G_CALLBACK (printer_add_cb), self, G_CONNECT_SWAPPED);
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (self->builder, "printer-add-button2");
+ g_signal_connect_object (widget, "clicked", G_CALLBACK (printer_add_cb), self, G_CONNECT_SWAPPED);
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (self->builder, "content");
+ gtk_list_box_set_filter_func (GTK_LIST_BOX (widget),
+ filter_function,
+ self,
+ NULL);
+ g_signal_connect_swapped (gtk_builder_get_object (self->builder, "search-entry"),
+ "search-changed",
+ G_CALLBACK (gtk_list_box_invalidate_filter),
+ widget);
+ gtk_list_box_set_sort_func (GTK_LIST_BOX (widget),
+ sort_function,
+ NULL,
+ NULL);
+
+ self->lockdown_settings = g_settings_new ("org.gnome.desktop.lockdown");
+ if (self->lockdown_settings)
+ g_signal_connect_object (self->lockdown_settings,
+ "changed",
+ G_CALLBACK (on_lockdown_settings_changed),
+ self,
+ G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+
+ /* Add unlock button */
+ self->permission = (GPermission *)polkit_permission_new_sync (
+ "org.opensuse.cupspkhelper.mechanism.all-edit", NULL, NULL, NULL);
+ if (self->permission != NULL)
+ {
+ g_signal_connect_object (self->permission,
+ "notify",
+ G_CALLBACK (on_permission_changed),
+ self,
+ G_CONNECT_SWAPPED | G_CONNECT_AFTER);
+
+ cc_permission_infobar_set_permission (self->permission_infobar,
+ self->permission);
+ cc_permission_infobar_set_title (self->permission_infobar,
+ _("Unlock to Add Printers and Change Settings"));
+
+ on_permission_changed (self);
+ }
+ else
+ g_warning ("Your system does not have the cups-pk-helper's policy \
+\"org.opensuse.cupspkhelper.mechanism.all-edit\" installed. \
+Please check your installation");
+
+ self->size_group = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
+
+ actualize_printers_list (self);
+ attach_to_cups_notifier (self);
+
+ get_all_ppds_async (cc_panel_get_cancellable (CC_PANEL (self)),
+ get_all_ppds_async_cb,
+ self);
+
+ pp_cups_connection_test_async (self->cups, cc_panel_get_cancellable (CC_PANEL (self)), connection_test_cb, self);
+ cc_panel_set_content (CC_PANEL (self), top_widget);
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (self->builder, "top-right-buttons");
+ adw_header_bar_pack_end (ADW_HEADER_BAR (cc_panel_get_titlebar (CC_PANEL (self))),
+ widget);
+}