gnome-control-center/panels/printers/cc-printers-panel.c
Christopher Davis d25c0e345d general: Use AdwBanner in CcPermissionInfobar
AdwBanner is a new adaptive widget that replaces GtkInfoBar.
AdwBanner adapts better to mobile sizes and has an API
that fits with how we use infobars.

This commit changes CcPermissionInfobar to use an AdwBanner
internally instead of a GtkInfoBar. It also re-implements
part of GtkLockButton, as AdwBanner does not support adding
arbitrary widgets.
2023-04-11 12:23:16 +00:00

1338 lines
43 KiB
C

/*
* 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_sync (printer, NULL, 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);
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_window_present (GTK_WINDOW (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_set_visible (GTK_WIDGET (self->pp_new_printer_dialog), TRUE);
}
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);
gtk_widget_set_sensitive (widget, !empty_state);
widget = (GtkWidget*) gtk_builder_get_object (self->builder, "search-bar");
gtk_widget_set_visible (widget, !no_cups);
gtk_widget_set_sensitive (widget, !empty_state);
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);
}