/* * 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include "cc-printers-panel.h" #include #include #include #include #include #include #include #include "cc-editable-entry.h" #include "pp-new-printer-dialog.h" #include "pp-ppd-selection-dialog.h" #include "pp-options-dialog.h" #include "pp-jobs-dialog.h" #include "pp-utils.h" #include "pp-maintenance-command.h" CC_PANEL_REGISTER (CcPrintersPanel, cc_printers_panel) #define PRINTERS_PANEL_PRIVATE(o) \ (G_TYPE_INSTANCE_GET_PRIVATE ((o), CC_TYPE_PRINTERS_PANEL, CcPrintersPanelPrivate)) #define SUPPLY_BAR_HEIGHT 20 #define EMPTY_TEXT "\xe2\x80\x94" #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 _CcPrintersPanelPrivate { GtkBuilder *builder; cups_dest_t *dests; gchar **dest_model_names; gchar **ppd_file_names; int num_dests; int current_dest; int num_jobs; GdkRGBA background_color; GPermission *permission; GSettings *lockdown_settings; PpNewPrinterDialog *pp_new_printer_dialog; PpPPDSelectionDialog *pp_ppd_selection_dialog; PpOptionsDialog *pp_options_dialog; PpJobsDialog *pp_jobs_dialog; GDBusProxy *cups_proxy; GDBusConnection *cups_bus_connection; gint subscription_id; guint subscription_renewal_id; guint cups_status_check_id; guint dbus_subscription_id; GtkWidget *popup_menu; GList *driver_change_list; GCancellable *get_ppd_name_cancellable; gboolean getting_ppd_names; PPDList *all_ppds_list; GHashTable *preferred_drivers; GCancellable *get_all_ppds_cancellable; gchar *new_printer_name; gchar *new_printer_location; gchar *new_printer_make_and_model; gboolean new_printer_on_network; gboolean select_new_printer; gpointer dummy; }; typedef struct { gchar *printer_name; GCancellable *cancellable; } SetPPDItem; static void update_jobs_count (CcPrintersPanel *self); static void actualize_printers_list (CcPrintersPanel *self); static void update_sensitivity (gpointer user_data); static void printer_disable_cb (GObject *gobject, GParamSpec *pspec, gpointer user_data); static void printer_set_default_cb (GtkToggleButton *button, gpointer user_data); static void detach_from_cups_notifier (gpointer data); static void free_dests (CcPrintersPanel *self); 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) { switch (property_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void cc_printers_panel_dispose (GObject *object) { CcPrintersPanelPrivate *priv = CC_PRINTERS_PANEL (object)->priv; if (priv->pp_new_printer_dialog) g_clear_object (&priv->pp_new_printer_dialog); free_dests (CC_PRINTERS_PANEL (object)); g_clear_pointer (&priv->new_printer_name, g_free); g_clear_pointer (&priv->new_printer_location, g_free); g_clear_pointer (&priv->new_printer_make_and_model, g_free); if (priv->builder) { g_object_unref (priv->builder); priv->builder = NULL; } if (priv->lockdown_settings) { g_object_unref (priv->lockdown_settings); priv->lockdown_settings = NULL; } if (priv->permission) { g_object_unref (priv->permission); priv->permission = NULL; } detach_from_cups_notifier (CC_PRINTERS_PANEL (object)); if (priv->cups_status_check_id > 0) { g_source_remove (priv->cups_status_check_id); priv->cups_status_check_id = 0; } if (priv->all_ppds_list) { ppd_list_free (priv->all_ppds_list); priv->all_ppds_list = NULL; } if (priv->preferred_drivers) { g_hash_table_unref (priv->preferred_drivers); priv->preferred_drivers = NULL; } if (priv->get_all_ppds_cancellable) { g_cancellable_cancel (priv->get_all_ppds_cancellable); g_object_unref (priv->get_all_ppds_cancellable); priv->get_all_ppds_cancellable = NULL; } if (priv->driver_change_list) { GList *iter; for (iter = priv->driver_change_list; iter; iter = iter->next) { SetPPDItem *item = (SetPPDItem *) iter->data; g_cancellable_cancel (item->cancellable); g_object_unref (item->cancellable); g_free (item->printer_name); g_free (item); } g_list_free (priv->driver_change_list); priv->driver_change_list = NULL; } G_OBJECT_CLASS (cc_printers_panel_parent_class)->dispose (object); } static void cc_printers_panel_finalize (GObject *object) { G_OBJECT_CLASS (cc_printers_panel_parent_class)->finalize (object); } static GPermission * cc_printers_panel_get_permission (CcPanel *panel) { CcPrintersPanelPrivate *priv = CC_PRINTERS_PANEL (panel)->priv; return priv->permission; } static const char * cc_printers_panel_get_help_uri (CcPanel *panel) { return "help:ubuntu-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); g_type_class_add_private (klass, sizeof (CcPrintersPanelPrivate)); object_class->get_property = cc_printers_panel_get_property; object_class->set_property = cc_printers_panel_set_property; object_class->dispose = cc_printers_panel_dispose; object_class->finalize = cc_printers_panel_finalize; panel_class->get_permission = cc_printers_panel_get_permission; panel_class->get_help_uri = cc_printers_panel_get_help_uri; } 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; CcPrintersPanelPrivate *priv; 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 const char * const requested_attrs[] = { "job-printer-uri", "job-originating-user-name"}; priv = PRINTERS_PANEL_PRIVATE (self); 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) { http_t *http; gchar *job_uri; ipp_t *request, *response; job_uri = g_strdup_printf ("ipp://localhost/jobs/%d", job_id); if ((http = httpConnectEncrypt (cupsServer (), ippPort (), cupsEncryption ())) != NULL) { request = ippNewRequest (IPP_GET_JOB_ATTRIBUTES); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri", NULL, job_uri); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser ()); ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", G_N_ELEMENTS (requested_attrs), NULL, requested_attrs); response = cupsDoRequest (http, request, "/"); if (response) { if (ippGetStatusCode (response) <= IPP_OK_CONFLICT) { ipp_attribute_t *attr_username = NULL; ipp_attribute_t *attr_printer_uri = NULL; attr_username = ippFindAttribute(response, "job-originating-user-name", IPP_TAG_NAME); attr_printer_uri = ippFindAttribute(response, "job-printer-uri", IPP_TAG_URI); if (attr_username && attr_printer_uri && g_strcmp0 (ippGetString (attr_username, 0, NULL), cupsUser ()) == 0 && g_strrstr (ippGetString (attr_printer_uri, 0, NULL), "/") != 0 && priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL && g_strcmp0 (g_strrstr (ippGetString (attr_printer_uri, 0, NULL), "/") + 1, priv->dests[priv->current_dest].name) == 0) update_jobs_count (self); } ippDelete(response); } httpClose (http); } g_free (job_uri); } } static gboolean renew_subscription (gpointer data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) data; static const char * const events[] = { "printer-added", "printer-deleted", "printer-stopped", "printer-state-changed", "job-created", "job-completed"}; priv = PRINTERS_PANEL_PRIVATE (self); priv->subscription_id = renew_cups_subscription (priv->subscription_id, events, G_N_ELEMENTS (events), SUBSCRIPTION_DURATION); if (priv->subscription_id > 0) return TRUE; else return FALSE; } static void attach_to_cups_notifier (gpointer data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) data; GError *error = NULL; priv = PRINTERS_PANEL_PRIVATE (self); if (renew_subscription (self)) { priv->subscription_renewal_id = g_timeout_add_seconds (RENEW_INTERVAL, renew_subscription, self); priv->cups_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, 0, NULL, CUPS_DBUS_NAME, CUPS_DBUS_PATH, CUPS_DBUS_INTERFACE, NULL, &error); if (!priv->cups_proxy) { g_warning ("%s", error->message); g_error_free (error); return; } priv->cups_bus_connection = g_dbus_proxy_get_connection (priv->cups_proxy); priv->dbus_subscription_id = g_dbus_connection_signal_subscribe (priv->cups_bus_connection, NULL, CUPS_DBUS_INTERFACE, NULL, CUPS_DBUS_PATH, NULL, 0, on_cups_notification, self, NULL); } } static void detach_from_cups_notifier (gpointer data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) data; priv = PRINTERS_PANEL_PRIVATE (self); if (priv->dbus_subscription_id != 0) { g_dbus_connection_signal_unsubscribe (priv->cups_bus_connection, priv->dbus_subscription_id); priv->dbus_subscription_id = 0; } cancel_cups_subscription (priv->subscription_id); priv->subscription_id = 0; if (priv->subscription_renewal_id != 0) { g_source_remove (priv->subscription_renewal_id); priv->subscription_renewal_id = 0; } if (priv->cups_proxy != NULL) { g_object_unref (priv->cups_proxy); priv->cups_proxy = NULL; } } static void free_dests (CcPrintersPanel *self) { CcPrintersPanelPrivate *priv; gint i; priv = PRINTERS_PANEL_PRIVATE (self); if (priv->num_dests > 0) { for (i = 0; i < priv->num_dests; i++) { g_free (priv->dest_model_names[i]); if (priv->ppd_file_names[i]) { g_unlink (priv->ppd_file_names[i]); g_free (priv->ppd_file_names[i]); } } g_free (priv->dest_model_names); g_free (priv->ppd_file_names); cupsFreeDests (priv->num_dests, priv->dests); } priv->dests = NULL; priv->num_dests = 0; priv->current_dest = -1; priv->dest_model_names = NULL; priv->ppd_file_names = NULL; } enum { NOTEBOOK_INFO_PAGE = 0, NOTEBOOK_NO_PRINTERS_PAGE, NOTEBOOK_NO_CUPS_PAGE, NOTEBOOK_N_PAGES }; enum { PRINTER_ID_COLUMN, PRINTER_NAME_COLUMN, PRINTER_PAUSED_COLUMN, PRINTER_DEFAULT_ICON_COLUMN, PRINTER_ICON_COLUMN, PRINTER_N_COLUMNS }; static void printer_selection_changed_cb (GtkTreeSelection *selection, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; GtkTreeModel *model; cups_ptype_t type = 0; GtkTreeIter iter; GtkWidget *widget; GtkWidget *model_button; GtkWidget *model_label; GValue value = G_VALUE_INIT; gchar *printer_make_and_model = NULL; gchar *printer_model = NULL; gchar *reason = NULL; gchar **printer_reasons = NULL; gchar *marker_types = NULL; gchar *printer_name = NULL; gchar *printer_icon = NULL; gchar *printer_type = NULL; gchar *supply_type = NULL; gchar *printer_uri = NULL; gchar *location = NULL; gchar *status = NULL; gchar *device_uri = NULL; gchar *printer_hostname = NULL; int printer_state = 3; int id = -1; int i, j; static const char * const reasons[] = { "toner-low", "toner-empty", "developer-low", "developer-empty", "marker-supply-low", "marker-supply-empty", "cover-open", "door-open", "media-low", "media-empty", "offline", "paused", "marker-waste-almost-full", "marker-waste-full", "opc-near-eol", "opc-life-over" }; static const char * statuses[] = { /* Translators: The printer is low on toner */ N_("Low on toner"), /* Translators: The printer has no toner left */ N_("Out of toner"), /* Translators: "Developer" is a chemical for photo development, * http://en.wikipedia.org/wiki/Photographic_developer */ N_("Low on developer"), /* Translators: "Developer" is a chemical for photo development, * http://en.wikipedia.org/wiki/Photographic_developer */ N_("Out of developer"), /* Translators: "marker" is one color bin of the printer */ N_("Low on a marker supply"), /* Translators: "marker" is one color bin of the printer */ N_("Out of a marker supply"), /* Translators: One or more covers on the printer are open */ N_("Open cover"), /* Translators: One or more doors on the printer are open */ N_("Open door"), /* Translators: At least one input tray is low on media */ N_("Low on paper"), /* Translators: At least one input tray is empty */ N_("Out of paper"), /* Translators: The printer is offline */ NC_("printer state", "Offline"), /* Translators: Someone has paused the Printer */ NC_("printer state", "Paused"), /* Translators: The printer marker supply waste receptacle is almost full */ N_("Waste receptacle almost full"), /* Translators: The printer marker supply waste receptacle is full */ N_("Waste receptacle full"), /* Translators: Optical photo conductors are used in laser printers */ N_("The optical photo conductor is near end of life"), /* Translators: Optical photo conductors are used in laser printers */ N_("The optical photo conductor is no longer functioning") }; priv = PRINTERS_PANEL_PRIVATE (self); if (gtk_tree_selection_get_selected (selection, &model, &iter)) { gtk_tree_model_get (model, &iter, PRINTER_ID_COLUMN, &id, PRINTER_NAME_COLUMN, &printer_name, PRINTER_ICON_COLUMN, &printer_icon, -1); } else id = -1; priv->current_dest = id; update_jobs_count (self); if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) { widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "notebook"); if (gtk_notebook_get_current_page (GTK_NOTEBOOK (widget)) >= NOTEBOOK_NO_PRINTERS_PAGE) gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), NOTEBOOK_INFO_PAGE); for (i = 0; i < priv->dests[id].num_options; i++) { if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "printer-location") == 0) location = g_strdup (priv->dests[priv->current_dest].options[i].value); else if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "printer-state") == 0) printer_state = atoi (priv->dests[priv->current_dest].options[i].value); else if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "printer-state-reasons") == 0) reason = priv->dests[priv->current_dest].options[i].value; else if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "marker-types") == 0) marker_types = priv->dests[priv->current_dest].options[i].value; else if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "printer-make-and-model") == 0) printer_make_and_model = priv->dests[priv->current_dest].options[i].value; else if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "printer-uri-supported") == 0) printer_uri = priv->dests[priv->current_dest].options[i].value; else if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "printer-type") == 0) printer_type = priv->dests[priv->current_dest].options[i].value; else if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "device-uri") == 0) device_uri = priv->dests[priv->current_dest].options[i].value; } if (priv->ppd_file_names[priv->current_dest] == NULL) priv->ppd_file_names[priv->current_dest] = g_strdup (cupsGetPPD (priv->dests[priv->current_dest].name)); if (priv->dest_model_names[priv->current_dest] == NULL) priv->dest_model_names[priv->current_dest] = get_ppd_attribute (priv->ppd_file_names[priv->current_dest], "ModelName"); printer_model = g_strdup (priv->dest_model_names[priv->current_dest]); if (printer_model == NULL && printer_make_and_model) { gchar *breakpoint = NULL, *tmp = NULL, *tmp2 = NULL; gchar backup; size_t length = 0; gchar *forbiden[] = { "foomatic", ",", "hpijs", "hpcups", "(recommended)", "postscript (recommended)", NULL }; tmp = g_ascii_strdown (printer_make_and_model, -1); for (i = 0; i < g_strv_length (forbiden); i++) { tmp2 = g_strrstr (tmp, forbiden[i]); if (breakpoint == NULL || (tmp2 != NULL && tmp2 < breakpoint)) breakpoint = tmp2; } if (breakpoint) { backup = *breakpoint; *breakpoint = '\0'; length = strlen (tmp); *breakpoint = backup; g_free (tmp); if (length > 0) printer_model = g_strndup (printer_make_and_model, length); } else printer_model = g_strdup (printer_make_and_model); } if (priv->new_printer_name && g_strcmp0 (priv->new_printer_name, printer_name) == 0) { /* Translators: Printer's state (printer is being configured right now) */ status = g_strdup ( C_("printer state", "Configuring")); } /* Find the first of the most severe reasons * and show it in the status field */ if (!status && reason && !g_str_equal (reason, "none")) { int errors = 0, warnings = 0, reports = 0; int error_index = -1, warning_index = -1, report_index = -1; printer_reasons = g_strsplit (reason, ",", -1); for (i = 0; i < g_strv_length (printer_reasons); i++) { for (j = 0; j < G_N_ELEMENTS (reasons); j++) if (strncmp (printer_reasons[i], reasons[j], strlen (reasons[j])) == 0) { if (g_str_has_suffix (printer_reasons[i], "-report")) { if (reports == 0) report_index = j; reports++; } else if (g_str_has_suffix (printer_reasons[i], "-warning")) { if (warnings == 0) warning_index = j; warnings++; } else { if (errors == 0) error_index = j; errors++; } } } g_strfreev (printer_reasons); if (error_index >= 0) status = g_strdup (_(statuses[error_index])); else if (warning_index >= 0) status = g_strdup (_(statuses[warning_index])); else if (report_index >= 0) status = g_strdup (_(statuses[report_index])); } if (status == NULL) { switch (printer_state) { case 3: /* Translators: Printer's state (can start new job without waiting) */ status = g_strdup ( C_("printer state", "Ready")); break; case 4: /* Translators: Printer's state (jobs are processing) */ status = g_strdup ( C_("printer state", "Processing")); break; case 5: /* Translators: Printer's state (no jobs can be processed) */ status = g_strdup ( C_("printer state", "Stopped")); break; } } widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-icon"); g_value_init (&value, G_TYPE_INT); g_object_get_property ((GObject *) widget, "icon-size", &value); if (printer_icon) { gtk_image_set_from_icon_name ((GtkImage *) widget, printer_icon, g_value_get_int (&value)); g_free (printer_icon); } else gtk_image_set_from_icon_name ((GtkImage *) widget, "printer", g_value_get_int (&value)); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-name-label"); if (printer_name) { cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), printer_name); g_free (printer_name); } else cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), EMPTY_TEXT); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-status-label"); if (status) { cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), status); g_free (status); } else cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), EMPTY_TEXT); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-location-label"); if (location) { cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), location); g_free (location); } else cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), EMPTY_TEXT); model_button = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-model-button"); model_label = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-model-label"); if (printer_model) { gtk_button_set_label (GTK_BUTTON (model_button), printer_model); gtk_label_set_text (GTK_LABEL (model_label), printer_model); g_free (printer_model); } else { gtk_button_set_label (GTK_BUTTON (model_button), EMPTY_TEXT); gtk_label_set_text (GTK_LABEL (model_label), EMPTY_TEXT); } widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-ip-address-label"); if (printer_type) type = atoi (printer_type); printer_hostname = printer_get_hostname (type, device_uri, printer_uri); if (printer_hostname) { cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), printer_hostname); g_free (printer_hostname); } else cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), EMPTY_TEXT); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-disable-switch"); g_signal_handlers_block_by_func (G_OBJECT (widget), printer_disable_cb, self); gtk_switch_set_active (GTK_SWITCH (widget), printer_state != 5); g_signal_handlers_unblock_by_func (G_OBJECT (widget), printer_disable_cb, self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-default-check-button"); g_signal_handlers_block_by_func (G_OBJECT (widget), printer_set_default_cb, self); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), priv->dests[id].is_default); g_signal_handlers_unblock_by_func (G_OBJECT (widget), printer_set_default_cb, self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "supply-drawing-area"); gtk_widget_set_size_request (widget, -1, SUPPLY_BAR_HEIGHT); gtk_widget_queue_draw (widget); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "supply-label"); if (marker_types && g_strrstr (marker_types, "toner") != NULL) /* Translators: Toner supply */ supply_type = g_strdup ( _("Toner Level")); else if (marker_types && g_strrstr (marker_types, "ink") != NULL) /* Translators: Ink supply */ supply_type = g_strdup ( _("Ink Level")); else /* Translators: By supply we mean ink, toner, staples, water, ... */ supply_type = g_strdup ( _("Supply Level")); if (supply_type) { gtk_label_set_text (GTK_LABEL (widget), supply_type); g_free (supply_type); } else gtk_label_set_text (GTK_LABEL (widget), EMPTY_TEXT); } else { if (id == -1) { if (priv->new_printer_name && g_strcmp0 (priv->new_printer_name, printer_name) == 0) { /* Translators: Printer's state (printer is being installed right now) */ status = g_strdup ( C_("printer state", "Installing")); location = g_strdup (priv->new_printer_location); printer_model = g_strdup (priv->new_printer_make_and_model); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "notebook"); if (gtk_notebook_get_current_page (GTK_NOTEBOOK (widget)) >= NOTEBOOK_NO_PRINTERS_PAGE) gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), NOTEBOOK_INFO_PAGE); } } widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-icon"); g_value_init (&value, G_TYPE_INT); g_object_get_property ((GObject *) widget, "icon-size", &value); if (printer_icon) { gtk_image_set_from_icon_name ((GtkImage *) widget, printer_icon, g_value_get_int (&value)); g_free (printer_icon); } else gtk_image_set_from_icon_name ((GtkImage *) widget, "printer", g_value_get_int (&value)); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-name-label"); if (printer_name) { cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), printer_name); g_free (printer_name); } else cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), EMPTY_TEXT); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-status-label"); if (status) { cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), status); g_free (status); } else cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), EMPTY_TEXT); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-location-label"); if (location) { cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), location); g_free (location); } else cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), EMPTY_TEXT); model_button = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-model-button"); model_label = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-model-label"); if (printer_model) { gtk_button_set_label (GTK_BUTTON (model_button), printer_model); gtk_label_set_text (GTK_LABEL (model_label), printer_model); g_free (printer_model); } else { gtk_button_set_label (GTK_BUTTON (model_button), EMPTY_TEXT); gtk_label_set_text (GTK_LABEL (model_label), EMPTY_TEXT); } widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-ip-address-label"); cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), EMPTY_TEXT); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-jobs-label"); cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), EMPTY_TEXT); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-disable-switch"); g_signal_handlers_block_by_func (G_OBJECT (widget), printer_disable_cb, self); gtk_switch_set_active (GTK_SWITCH (widget), FALSE); g_signal_handlers_unblock_by_func (G_OBJECT (widget), printer_disable_cb, self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-default-check-button"); g_signal_handlers_block_by_func (G_OBJECT (widget), printer_set_default_cb, self); gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), FALSE); g_signal_handlers_unblock_by_func (G_OBJECT (widget), printer_set_default_cb, self); } update_sensitivity (self); } static void actualize_printers_list (CcPrintersPanel *self) { CcPrintersPanelPrivate *priv; GtkTreeSelection *selection; GtkListStore *store; cups_ptype_t printer_type = 0; GtkTreeModel *model; GtkTreeIter selected_iter; GtkTreeView *treeview; GtkTreeIter iter; cups_job_t *jobs = NULL; GtkWidget *widget; gboolean paused = FALSE; gboolean selected_iter_set = FALSE; gboolean valid = FALSE; http_t *http; gchar *current_printer_name = NULL; gchar *printer_icon_name = NULL; gchar *default_icon_name = NULL; gchar *device_uri = NULL; gint new_printer_position = 0; int current_dest = -1; int i, j; int num_jobs = 0; priv = PRINTERS_PANEL_PRIVATE (self); treeview = (GtkTreeView*) gtk_builder_get_object (priv->builder, "printers-treeview"); if ((selection = gtk_tree_view_get_selection (treeview)) != NULL && gtk_tree_selection_get_selected (selection, &model, &iter)) { gtk_tree_model_get (model, &iter, PRINTER_NAME_COLUMN, ¤t_printer_name, -1); } if (priv->new_printer_name && priv->select_new_printer) { g_free (current_printer_name); current_printer_name = g_strdup (priv->new_printer_name); priv->select_new_printer = FALSE; } free_dests (self); priv->num_dests = cupsGetDests (&priv->dests); priv->dest_model_names = g_new0 (gchar *, priv->num_dests); priv->ppd_file_names = g_new0 (gchar *, priv->num_dests); store = gtk_list_store_new (PRINTER_N_COLUMNS, G_TYPE_INT, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING); if (priv->num_dests == 0 && !priv->new_printer_name) { widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "notebook"); http = httpConnectEncrypt (cupsServer (), ippPort (), cupsEncryption ()); if (http) { httpClose (http); gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), NOTEBOOK_NO_PRINTERS_PAGE); } else gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), NOTEBOOK_NO_CUPS_PAGE); gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, PRINTER_ID_COLUMN, 0, /* Translators: There are no printers available (none is configured or CUPS is not running) */ PRINTER_NAME_COLUMN, _("No printers available"), PRINTER_PAUSED_COLUMN, TRUE, PRINTER_DEFAULT_ICON_COLUMN, NULL, PRINTER_ICON_COLUMN, NULL, -1); gtk_widget_set_sensitive (GTK_WIDGET (treeview), FALSE); } else gtk_widget_set_sensitive (GTK_WIDGET (treeview), TRUE); for (i = 0; i < priv->num_dests; i++) { gchar *instance; if (priv->new_printer_name && new_printer_position >= 0) { gint comparison_result = g_ascii_strcasecmp (priv->dests[i].name, priv->new_printer_name); if (comparison_result < 0) new_printer_position = i + 1; else if (comparison_result == 0) new_printer_position = -1; } gtk_list_store_append (store, &iter); if (priv->dests[i].instance) { instance = g_strdup_printf ("%s / %s", priv->dests[i].name, priv->dests[i].instance); } else { instance = g_strdup (priv->dests[i].name); } for (j = 0; j < priv->dests[i].num_options; j++) { if (g_strcmp0 (priv->dests[i].options[j].name, "printer-state") == 0) paused = (g_strcmp0 (priv->dests[i].options[j].value, "5") == 0); else if (g_strcmp0 (priv->dests[i].options[j].name, "device-uri") == 0) device_uri = priv->dests[i].options[j].value; else if (g_strcmp0 (priv->dests[i].options[j].name, "printer-type") == 0) printer_type = atoi (priv->dests[i].options[j].value); } if (priv->dests[i].is_default) default_icon_name = g_strdup ("emblem-default-symbolic"); else default_icon_name = NULL; if (printer_is_local (printer_type, device_uri)) printer_icon_name = g_strdup ("printer"); else printer_icon_name = g_strdup ("printer-network"); gtk_list_store_set (store, &iter, PRINTER_ID_COLUMN, i, PRINTER_NAME_COLUMN, instance, PRINTER_PAUSED_COLUMN, paused, PRINTER_DEFAULT_ICON_COLUMN, default_icon_name, PRINTER_ICON_COLUMN, printer_icon_name, -1); if (g_strcmp0 (current_printer_name, instance) == 0) { current_dest = i; selected_iter = iter; selected_iter_set = TRUE; } g_free (instance); g_free (printer_icon_name); g_free (default_icon_name); } if (priv->new_printer_name && new_printer_position >= 0) { gtk_list_store_insert (store, &iter, new_printer_position); gtk_list_store_set (store, &iter, PRINTER_ID_COLUMN, -1, PRINTER_NAME_COLUMN, priv->new_printer_name, PRINTER_PAUSED_COLUMN, TRUE, PRINTER_DEFAULT_ICON_COLUMN, NULL, PRINTER_ICON_COLUMN, priv->new_printer_on_network ? "printer-network" : "printer", -1); if (g_strcmp0 (current_printer_name, priv->new_printer_name) == 0) { selected_iter = iter; selected_iter_set = TRUE; } } g_signal_handlers_block_by_func ( G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview))), printer_selection_changed_cb, self); gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (store)); g_signal_handlers_unblock_by_func ( G_OBJECT (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview))), printer_selection_changed_cb, self); if (selected_iter_set) { priv->current_dest = current_dest; gtk_tree_selection_select_iter ( gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), &selected_iter); } else { num_jobs = cupsGetJobs (&jobs, NULL, 1, CUPS_WHICHJOBS_ALL); /* Select last used printer */ if (num_jobs > 0) { for (i = 0; i < priv->num_dests; i++) if (g_strcmp0 (priv->dests[i].name, jobs[num_jobs - 1].dest) == 0) { priv->current_dest = i; break; } cupsFreeJobs (num_jobs, jobs); } /* Select default printer */ if (priv->current_dest < 0) { for (i = 0; i < priv->num_dests; i++) if (priv->dests[i].is_default) { priv->current_dest = i; break; } } if (priv->current_dest >= 0) { gint id; valid = gtk_tree_model_get_iter_first ((GtkTreeModel *) store, &selected_iter); while (valid) { gtk_tree_model_get ((GtkTreeModel *) store, &selected_iter, PRINTER_ID_COLUMN, &id, -1); if (id == priv->current_dest) break; valid = gtk_tree_model_iter_next ((GtkTreeModel *) store, &selected_iter); } gtk_tree_selection_select_iter ( gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), &selected_iter); } else if (priv->num_dests > 0) { /* Select first printer */ gtk_tree_model_get_iter_first ((GtkTreeModel *) store, &selected_iter); gtk_tree_selection_select_iter ( gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), &selected_iter); } } g_free (current_printer_name); g_object_unref (store); update_sensitivity (self); } static void set_cell_sensitivity_func (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer func_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) func_data; gboolean paused = FALSE; priv = PRINTERS_PANEL_PRIVATE (self); gtk_tree_model_get (tree_model, iter, PRINTER_PAUSED_COLUMN, &paused, -1); if (priv->num_dests == 0) g_object_set (G_OBJECT (cell), "ellipsize", PANGO_ELLIPSIZE_NONE, "width-chars", -1, NULL); else g_object_set (G_OBJECT (cell), "ellipsize", PANGO_ELLIPSIZE_END, "width-chars", 18, NULL); g_object_set (cell, "sensitive", !paused, NULL); } static void set_pixbuf_cell_sensitivity_func (GtkTreeViewColumn *tree_column, GtkCellRenderer *cell, GtkTreeModel *tree_model, GtkTreeIter *iter, gpointer func_data) { gboolean paused = FALSE; gtk_tree_model_get (tree_model, iter, PRINTER_PAUSED_COLUMN, &paused, -1); g_object_set (cell, "sensitive", !paused, NULL); } static void populate_printers_list (CcPrintersPanel *self) { CcPrintersPanelPrivate *priv; GtkTreeViewColumn *column; GtkCellRenderer *icon_renderer; GtkCellRenderer *icon_renderer2; GtkCellRenderer *renderer; GtkWidget *treeview; priv = PRINTERS_PANEL_PRIVATE (self); treeview = (GtkWidget*) gtk_builder_get_object (priv->builder, "printers-treeview"); g_signal_connect (gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview)), "changed", G_CALLBACK (printer_selection_changed_cb), self); actualize_printers_list (self); icon_renderer = gtk_cell_renderer_pixbuf_new (); g_object_set (icon_renderer, "stock-size", gtk_icon_size_from_name ("cc-sidebar-list"), NULL); gtk_cell_renderer_set_padding (icon_renderer, 4, 4); column = gtk_tree_view_column_new_with_attributes ("Icon", icon_renderer, "icon-name", PRINTER_ICON_COLUMN, NULL); gtk_tree_view_column_set_cell_data_func (column, icon_renderer, set_pixbuf_cell_sensitivity_func, self, NULL); gtk_tree_view_column_set_expand (column, FALSE); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); renderer = gtk_cell_renderer_text_new (); g_object_set (G_OBJECT (renderer), "ellipsize", PANGO_ELLIPSIZE_END, "width-chars", 18, NULL); column = gtk_tree_view_column_new_with_attributes ("Printer", renderer, "text", PRINTER_NAME_COLUMN, NULL); gtk_tree_view_column_set_cell_data_func (column, renderer, set_cell_sensitivity_func, self, NULL); gtk_tree_view_column_set_expand (column, FALSE); gtk_tree_view_column_set_min_width (column, 120); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); icon_renderer2 = gtk_cell_renderer_pixbuf_new (); column = gtk_tree_view_column_new_with_attributes ("Default", icon_renderer2, "icon-name", PRINTER_DEFAULT_ICON_COLUMN, NULL); gtk_tree_view_column_set_expand (column, FALSE); gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); } enum { JOB_ID_COLUMN, JOB_TITLE_COLUMN, JOB_STATE_COLUMN, JOB_CREATION_TIME_COLUMN, JOB_N_COLUMNS }; static void update_jobs_count (CcPrintersPanel *self) { CcPrintersPanelPrivate *priv; cups_job_t *jobs; GtkWidget *widget; gchar *active_jobs = NULL; gint num_jobs; priv = PRINTERS_PANEL_PRIVATE (self); priv->num_jobs = -1; if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) { priv->num_jobs = cupsGetJobs (&jobs, priv->dests[priv->current_dest].name, 1, CUPS_WHICHJOBS_ACTIVE); if (priv->num_jobs > 0) cupsFreeJobs (priv->num_jobs, jobs); num_jobs = priv->num_jobs < 0 ? 0 : (guint) priv->num_jobs; /* Translators: there is n active print jobs on this printer */ active_jobs = g_strdup_printf (ngettext ("%u active", "%u active", num_jobs), num_jobs); } widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-jobs-label"); if (active_jobs) { cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), active_jobs); g_free (active_jobs); } else cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), EMPTY_TEXT); if (priv->pp_jobs_dialog) { pp_jobs_dialog_update (priv->pp_jobs_dialog); } } static void printer_disable_cb (GObject *gobject, GParamSpec *pspec, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; gboolean paused = FALSE; char *name = NULL; int i; priv = PRINTERS_PANEL_PRIVATE (self); if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) { name = priv->dests[priv->current_dest].name; for (i = 0; i < priv->dests[priv->current_dest].num_options; i++) { if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "printer-state") == 0) paused = (g_strcmp0 (priv->dests[priv->current_dest].options[i].value, "5") == 0); } } if (name && printer_set_enabled (name, paused)) actualize_printers_list (self); } typedef struct { gchar *color; gchar *type; gchar *name; gint level; } MarkerItem; static gint markers_cmp (gconstpointer a, gconstpointer b) { MarkerItem *x = (MarkerItem*) a; MarkerItem *y = (MarkerItem*) b; if (x->level < y->level) return 1; else if (x->level == y->level) return 0; else return -1; } static void rounded_rectangle (cairo_t *cr, double x, double y, double w, double h, double r) { cairo_new_sub_path (cr); cairo_arc (cr, x + r, y + r, r, M_PI, 3 * M_PI / 2); cairo_arc (cr, x + w - r, y + r, r, 3 *M_PI / 2, 2 * M_PI); cairo_arc (cr, x + w - r, y + h - r, r, 0, M_PI / 2); cairo_arc (cr, x + r, y + h - r, r, M_PI / 2, M_PI); cairo_close_path (cr); } static gboolean supply_levels_draw_cb (GtkWidget *widget, cairo_t *cr, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; GtkStyleContext *context; gchar *marker_levels = NULL; gchar *marker_colors = NULL; gchar *marker_names = NULL; gchar *marker_types = NULL; gchar *tooltip_text = NULL; gint width; gint height; int i; priv = PRINTERS_PANEL_PRIVATE (self); width = gtk_widget_get_allocated_width (widget); height = gtk_widget_get_allocated_height (widget); cairo_rectangle (cr, 0.0, 0.0, width, height); gdk_cairo_set_source_rgba (cr, &priv->background_color); cairo_fill (cr); if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) { for (i = 0; i < priv->dests[priv->current_dest].num_options; i++) { if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "marker-names") == 0) marker_names = g_strcompress (priv->dests[priv->current_dest].options[i].value); else if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "marker-levels") == 0) marker_levels = priv->dests[priv->current_dest].options[i].value; else if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "marker-colors") == 0) marker_colors = priv->dests[priv->current_dest].options[i].value; else if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "marker-types") == 0) marker_types = priv->dests[priv->current_dest].options[i].value; } if (marker_levels && marker_colors && marker_names && marker_types) { GdkRGBA border_color = {0.0, 0.0, 0.0, 1.0}; GSList *markers = NULL; GSList *tmp_list = NULL; GValue int_val = G_VALUE_INIT; gchar **marker_levelsv = NULL; gchar **marker_colorsv = NULL; gchar **marker_namesv = NULL; gchar **marker_typesv = NULL; gchar *tmp = NULL; gint border_radius = 0; context = gtk_widget_get_style_context ((GtkWidget *) gtk_builder_get_object (priv->builder, "printer-options-button")); gtk_style_context_get_border_color (context, 0, &border_color); gtk_style_context_get_property ( context, GTK_STYLE_PROPERTY_BORDER_RADIUS, 0, &int_val); if (G_VALUE_HOLDS_INT (&int_val)) border_radius = g_value_get_int (&int_val); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "supply-drawing-area"); marker_levelsv = g_strsplit (marker_levels, ",", -1); marker_colorsv = g_strsplit (marker_colors, ",", -1); marker_namesv = g_strsplit (marker_names, ",", -1); marker_typesv = g_strsplit (marker_types, ",", -1); if (g_strv_length (marker_levelsv) == g_strv_length (marker_colorsv) && g_strv_length (marker_colorsv) == g_strv_length (marker_namesv) && g_strv_length (marker_namesv) == g_strv_length (marker_typesv)) { for (i = 0; i < g_strv_length (marker_levelsv); i++) { MarkerItem *marker; if (g_strcmp0 (marker_typesv[i], "ink") == 0 || g_strcmp0 (marker_typesv[i], "toner") == 0) { marker = g_new0 (MarkerItem, 1); marker->type = g_strdup (marker_typesv[i]); marker->name = g_strdup (marker_namesv[i]); marker->color = g_strdup (marker_colorsv[i]); marker->level = atoi (marker_levelsv[i]); markers = g_slist_prepend (markers, marker); } } markers = g_slist_sort (markers, markers_cmp); for (tmp_list = markers; tmp_list; tmp_list = tmp_list->next) { GdkRGBA color = {0.0, 0.0, 0.0, 1.0}; double display_value; int value; value = ((MarkerItem*) tmp_list->data)->level; gdk_rgba_parse (&color, ((MarkerItem*) tmp_list->data)->color); if (value > 0) { display_value = value / 100.0 * (width - 3.0); gdk_cairo_set_source_rgba (cr, &color); rounded_rectangle (cr, 1.5, 1.5, display_value, SUPPLY_BAR_HEIGHT - 3.0, border_radius); cairo_fill (cr); } if (tooltip_text) { tmp = g_strdup_printf ("%s\n%s", tooltip_text, ((MarkerItem*) tmp_list->data)->name); g_free (tooltip_text); tooltip_text = tmp; tmp = NULL; } else tooltip_text = g_strdup_printf ("%s", ((MarkerItem*) tmp_list->data)->name); } cairo_set_line_width (cr, 1.0); gdk_cairo_set_source_rgba (cr, &border_color); rounded_rectangle (cr, 1.5, 1.5, width - 3.0, SUPPLY_BAR_HEIGHT - 3.0, border_radius); cairo_stroke (cr); for (tmp_list = markers; tmp_list; tmp_list = tmp_list->next) { g_free (((MarkerItem*) tmp_list->data)->name); g_free (((MarkerItem*) tmp_list->data)->type); g_free (((MarkerItem*) tmp_list->data)->color); } g_slist_free_full (markers, g_free); } g_strfreev (marker_levelsv); g_strfreev (marker_colorsv); g_strfreev (marker_namesv); g_strfreev (marker_typesv); } g_free (marker_names); if (tooltip_text) { gtk_widget_set_tooltip_text (widget, tooltip_text); g_free (tooltip_text); } else { gtk_widget_set_tooltip_text (widget, NULL); gtk_widget_set_has_tooltip (widget, FALSE); } } return TRUE; } static void printer_set_default_cb (GtkToggleButton *button, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; char *name = NULL; priv = PRINTERS_PANEL_PRIVATE (self); if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) name = priv->dests[priv->current_dest].name; if (name) { printer_set_default (name); actualize_printers_list (self); g_signal_handlers_block_by_func (G_OBJECT (button), printer_set_default_cb, self); gtk_toggle_button_set_active (button, priv->dests[priv->current_dest].is_default); g_signal_handlers_unblock_by_func (G_OBJECT (button), printer_set_default_cb, self); } } static void new_printer_dialog_pre_response_cb (PpNewPrinterDialog *dialog, const gchar *device_name, const gchar *device_location, const gchar *device_make_and_model, gboolean is_network_device, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; priv = PRINTERS_PANEL_PRIVATE (self); priv->new_printer_name = g_strdup (device_name); priv->new_printer_location = g_strdup (device_location); priv->new_printer_make_and_model = g_strdup (device_make_and_model); priv->new_printer_on_network = is_network_device; priv->select_new_printer = TRUE; actualize_printers_list (self); } static void new_printer_dialog_response_cb (PpNewPrinterDialog *dialog, gint response_id, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; priv = PRINTERS_PANEL_PRIVATE (self); if (priv->pp_new_printer_dialog) g_clear_object (&priv->pp_new_printer_dialog); g_clear_pointer (&priv->new_printer_name, g_free); g_clear_pointer (&priv->new_printer_location, g_free); g_clear_pointer (&priv->new_printer_make_and_model, g_free); if (response_id == GTK_RESPONSE_REJECT) { 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_widget_destroy), NULL); gtk_widget_show (message_dialog); } actualize_printers_list (self); } static void printer_add_cb (GtkToolButton *toolbutton, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; GtkWidget *toplevel; priv = PRINTERS_PANEL_PRIVATE (self); toplevel = gtk_widget_get_toplevel (GTK_WIDGET (self)); priv->pp_new_printer_dialog = PP_NEW_PRINTER_DIALOG (pp_new_printer_dialog_new (GTK_WINDOW (toplevel))); g_signal_connect (priv->pp_new_printer_dialog, "pre-response", G_CALLBACK (new_printer_dialog_pre_response_cb), self); g_signal_connect (priv->pp_new_printer_dialog, "response", G_CALLBACK (new_printer_dialog_response_cb), self); } static void printer_remove_cb (GtkToolButton *toolbutton, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; char *printer_name = NULL; priv = PRINTERS_PANEL_PRIVATE (self); if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) printer_name = priv->dests[priv->current_dest].name; if (printer_name && printer_delete (printer_name)) actualize_printers_list (self); } static void printer_name_edit_cb (GtkWidget *entry, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; const gchar *new_name; gchar *old_name = NULL; gint i; priv = PRINTERS_PANEL_PRIVATE (self); new_name = cc_editable_entry_get_text (CC_EDITABLE_ENTRY (entry)); if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) old_name = priv->dests[priv->current_dest].name; if (printer_rename (old_name, new_name)) { free_dests (self); priv->num_dests = cupsGetDests (&priv->dests); priv->dest_model_names = g_new0 (gchar *, priv->num_dests); priv->ppd_file_names = g_new0 (gchar *, priv->num_dests); for (i = 0; i < priv->num_dests; i++) if (g_strcmp0 (priv->dests[i].name, new_name) == 0) { priv->current_dest = i; break; } } actualize_printers_list (self); } static void printer_location_edit_cb (GtkWidget *entry, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; const gchar *location; gchar *printer_name = NULL; priv = PRINTERS_PANEL_PRIVATE (self); location = cc_editable_entry_get_text (CC_EDITABLE_ENTRY (entry)); if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) printer_name = priv->dests[priv->current_dest].name; if (printer_name && location && printer_set_location (printer_name, location)) actualize_printers_list (self); } static void set_ppd_cb (gchar *printer_name, gboolean success, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; GList *iter; priv = PRINTERS_PANEL_PRIVATE (self); for (iter = priv->driver_change_list; iter; iter = iter->next) { SetPPDItem *item = (SetPPDItem *) iter->data; if (g_strcmp0 (item->printer_name, printer_name) == 0) { priv->driver_change_list = g_list_remove_link (priv->driver_change_list, iter); g_object_unref (item->cancellable); g_free (item->printer_name); g_free (item); g_list_free (iter); break; } } update_sensitivity (self); if (success) { actualize_printers_list (self); } g_free (printer_name); } static void select_ppd_manually (GtkMenuItem *menuitem, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; GtkFileFilter *filter; GtkWidget *dialog; gchar *printer_name = NULL; priv = PRINTERS_PANEL_PRIVATE (self); gtk_menu_shell_cancel (GTK_MENU_SHELL (priv->popup_menu)); dialog = gtk_file_chooser_dialog_new (_("Select PPD File"), NULL, GTK_FILE_CHOOSER_ACTION_OPEN, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Open"), GTK_RESPONSE_ACCEPT, NULL); filter = gtk_file_filter_new (); gtk_file_filter_set_name (filter, _("PostScript Printer Description files (*.ppd, *.PPD, *.ppd.gz, *.PPD.gz, *.PPD.GZ)")); gtk_file_filter_add_pattern (filter, "*.ppd"); gtk_file_filter_add_pattern (filter, "*.PPD"); gtk_file_filter_add_pattern (filter, "*.ppd.gz"); gtk_file_filter_add_pattern (filter, "*.PPD.gz"); gtk_file_filter_add_pattern (filter, "*.PPD.GZ"); gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT) { gchar *ppd_filename; ppd_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog)); if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) printer_name = priv->dests[priv->current_dest].name; if (printer_name && ppd_filename) { SetPPDItem *item; item = g_new0 (SetPPDItem, 1); item->printer_name = g_strdup (printer_name); item->cancellable = g_cancellable_new (); priv->driver_change_list = g_list_prepend (priv->driver_change_list, item); update_sensitivity (self); printer_set_ppd_file_async (printer_name, ppd_filename, item->cancellable, set_ppd_cb, user_data); } g_free (ppd_filename); } gtk_widget_destroy (dialog); } static void ppd_selection_dialog_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; gchar *printer_name = NULL; priv = PRINTERS_PANEL_PRIVATE (self); if (response_id == GTK_RESPONSE_OK) { gchar *ppd_name; ppd_name = pp_ppd_selection_dialog_get_ppd_name (priv->pp_ppd_selection_dialog); if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) printer_name = priv->dests[priv->current_dest].name; if (printer_name && ppd_name) { SetPPDItem *item; item = g_new0 (SetPPDItem, 1); item->printer_name = g_strdup (printer_name); item->cancellable = g_cancellable_new (); priv->driver_change_list = g_list_prepend (priv->driver_change_list, item); update_sensitivity (self); printer_set_ppd_async (printer_name, ppd_name, item->cancellable, set_ppd_cb, user_data); } g_free (ppd_name); } pp_ppd_selection_dialog_free (priv->pp_ppd_selection_dialog); priv->pp_ppd_selection_dialog = NULL; } static void select_ppd_in_dialog (GtkMenuItem *menuitem, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; GtkWidget *widget; gchar *device_id = NULL; gchar *manufacturer = NULL; priv = PRINTERS_PANEL_PRIVATE (self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "main-vbox"); if (!priv->pp_ppd_selection_dialog) { if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests) { device_id = get_ppd_attribute (priv->ppd_file_names[priv->current_dest], "1284DeviceID"); if (device_id) { manufacturer = get_tag_value (device_id, "mfg"); if (!manufacturer) manufacturer = get_tag_value (device_id, "manufacturer"); } if (manufacturer == NULL) { manufacturer = get_ppd_attribute (priv->ppd_file_names[priv->current_dest], "Manufacturer"); } if (manufacturer == NULL) { manufacturer = g_strdup ("Raw"); } } priv->pp_ppd_selection_dialog = pp_ppd_selection_dialog_new ( GTK_WINDOW (gtk_widget_get_toplevel (widget)), priv->all_ppds_list, manufacturer, ppd_selection_dialog_response_cb, self); g_free (manufacturer); g_free (device_id); } } static void set_ppd_from_list (GtkMenuItem *menuitem, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; gchar *printer_name = NULL; gchar *ppd_name; priv = PRINTERS_PANEL_PRIVATE (self); ppd_name = (gchar *) g_object_get_data (G_OBJECT (menuitem), "ppd-name"); if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) printer_name = priv->dests[priv->current_dest].name; if (printer_name && ppd_name) { SetPPDItem *item; item = g_new0 (SetPPDItem, 1); item->printer_name = g_strdup (printer_name); item->cancellable = g_cancellable_new (); priv->driver_change_list = g_list_prepend (priv->driver_change_list, item); update_sensitivity (self); printer_set_ppd_async (printer_name, ppd_name, item->cancellable, set_ppd_cb, user_data); } } static void ppd_names_free (gpointer user_data) { PPDName **names = (PPDName **) user_data; gint i; if (names) { for (i = 0; names[i]; i++) { g_free (names[i]->ppd_name); g_free (names[i]->ppd_display_name); g_free (names[i]); } g_free (names); } } static void get_ppd_names_cb (PPDName **names, const gchar *printer_name, gboolean cancelled, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; GtkWidget *informal = NULL; GtkWidget *placeholders[3]; GtkWidget *spinner; gpointer value = NULL; gboolean found = FALSE; PPDName **hash_names = NULL; GList *children, *iter; gint i; priv = PRINTERS_PANEL_PRIVATE (self); priv->getting_ppd_names = FALSE; for (i = 0; i < 3; i++) placeholders[i] = NULL; children = gtk_container_get_children (GTK_CONTAINER (priv->popup_menu)); if (children) { for (iter = children; iter; iter = iter->next) { if (g_strcmp0 ((gchar *) g_object_get_data (G_OBJECT (iter->data), "purpose"), "informal") == 0) informal = GTK_WIDGET (iter->data); else if (g_strcmp0 ((gchar *) g_object_get_data (G_OBJECT (iter->data), "purpose"), "placeholder1") == 0) placeholders[0] = GTK_WIDGET (iter->data); else if (g_strcmp0 ((gchar *) g_object_get_data (G_OBJECT (iter->data), "purpose"), "placeholder2") == 0) placeholders[1] = GTK_WIDGET (iter->data); else if (g_strcmp0 ((gchar *) g_object_get_data (G_OBJECT (iter->data), "purpose"), "placeholder3") == 0) placeholders[2] = GTK_WIDGET (iter->data); } g_list_free (children); } if (!priv->preferred_drivers) { priv->preferred_drivers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, ppd_names_free); } if (!cancelled && !g_hash_table_lookup_extended (priv->preferred_drivers, printer_name, NULL, NULL)) g_hash_table_insert (priv->preferred_drivers, g_strdup (printer_name), names); if (priv->preferred_drivers && g_hash_table_lookup_extended (priv->preferred_drivers, printer_name, NULL, &value)) { hash_names = (PPDName **) value; if (hash_names) { for (i = 0; hash_names[i]; i++) { if (placeholders[i]) { gtk_menu_item_set_label (GTK_MENU_ITEM (placeholders[i]), hash_names[i]->ppd_display_name); g_object_set_data_full (G_OBJECT (placeholders[i]), "ppd-name", g_strdup (hash_names[i]->ppd_name), g_free); g_signal_connect (placeholders[i], "activate", G_CALLBACK (set_ppd_from_list), self); gtk_widget_set_sensitive (GTK_WIDGET (placeholders[i]), TRUE); gtk_widget_show (placeholders[i]); } } found = TRUE; } else { found = FALSE; } } if (informal) { gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (informal), FALSE); spinner = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (informal)); if (spinner) gtk_spinner_stop (GTK_SPINNER (spinner)); if (found) gtk_widget_hide (informal); else gtk_menu_item_set_label (GTK_MENU_ITEM (informal), _("No suitable driver found")); } gtk_widget_show_all (priv->popup_menu); update_sensitivity (self); } static void popup_menu_done (GtkMenuShell *menushell, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; priv = PRINTERS_PANEL_PRIVATE (self); if (priv->get_ppd_name_cancellable) { g_cancellable_cancel (priv->get_ppd_name_cancellable); g_object_unref (priv->get_ppd_name_cancellable); priv->get_ppd_name_cancellable = NULL; } } static void popup_model_menu_cb (GtkButton *button, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; GtkWidget *spinner; GtkWidget *item; priv = PRINTERS_PANEL_PRIVATE (self); priv->popup_menu = gtk_menu_new (); g_signal_connect (priv->popup_menu, "selection-done", G_CALLBACK (popup_menu_done), user_data); /* * These placeholders are a workaround for a situation * when we want to actually append new menu item in a callback. * But unfortunately it is not possible to connect to "activate" * signal of such menu item (appended after gtk_menu_popup()). */ item = gtk_image_menu_item_new_with_label (""); g_object_set_data_full (G_OBJECT (item), "purpose", g_strdup ("placeholder1"), g_free); gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); gtk_widget_set_no_show_all (item, TRUE); gtk_widget_hide (item); item = gtk_image_menu_item_new_with_label (""); g_object_set_data_full (G_OBJECT (item), "purpose", g_strdup ("placeholder2"), g_free); gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); gtk_widget_set_no_show_all (item, TRUE); gtk_widget_hide (item); item = gtk_image_menu_item_new_with_label (""); g_object_set_data_full (G_OBJECT (item), "purpose", g_strdup ("placeholder3"), g_free); gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); gtk_widget_set_no_show_all (item, TRUE); gtk_widget_hide (item); item = gtk_image_menu_item_new_with_label (_("Searching for preferred drivers...")); spinner = gtk_spinner_new (); gtk_spinner_start (GTK_SPINNER (spinner)); gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), spinner); gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (item), TRUE); g_object_set_data_full (G_OBJECT (item), "purpose", g_strdup ("informal"), g_free); gtk_widget_set_sensitive (item, FALSE); gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); gtk_widget_set_no_show_all (item, TRUE); gtk_widget_show (item); item = gtk_separator_menu_item_new (); gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); item = gtk_menu_item_new_with_label (_("Select from database...")); g_object_set_data_full (G_OBJECT (item), "purpose", g_strdup ("ppd-select"), g_free); g_signal_connect (item, "activate", G_CALLBACK (select_ppd_in_dialog), self); gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); item = gtk_separator_menu_item_new (); gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); item = gtk_menu_item_new_with_label (_("Provide PPD File...")); g_object_set_data_full (G_OBJECT (item), "purpose", g_strdup ("ppdfile-select"), g_free); g_signal_connect (item, "activate", G_CALLBACK (select_ppd_manually), self); gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item); gtk_widget_show_all (priv->popup_menu); gtk_menu_popup (GTK_MENU (priv->popup_menu), NULL, NULL, NULL, NULL, 0, gtk_get_current_event_time()); if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) { if (priv->preferred_drivers && g_hash_table_lookup_extended (priv->preferred_drivers, priv->dests[priv->current_dest].name, NULL, NULL)) { get_ppd_names_cb (NULL, priv->dests[priv->current_dest].name, FALSE, user_data); } else { priv->get_ppd_name_cancellable = g_cancellable_new (); priv->getting_ppd_names = TRUE; get_ppd_names_async (priv->dests[priv->current_dest].name, 3, priv->get_ppd_name_cancellable, get_ppd_names_cb, user_data); update_sensitivity (self); } } } static void pp_maintenance_command_execute_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { PpMaintenanceCommand *command = (PpMaintenanceCommand *) source_object; GError *error = NULL; pp_maintenance_command_execute_finish (command, res, &error); g_object_unref (command); } static void test_page_cb (GtkButton *button, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; cups_ptype_t type = 0; const gchar *printer_type = NULL; gchar *printer_name = NULL; gint i; priv = PRINTERS_PANEL_PRIVATE (self); if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) { printer_name = priv->dests[priv->current_dest].name; printer_type = cupsGetOption ("printer-type", priv->dests[priv->current_dest].num_options, priv->dests[priv->current_dest].options); if (printer_type) type = atoi (printer_type); } if (printer_name) { const gchar *const dirs[] = { "/usr/share/cups", "/usr/local/share/cups", NULL }; const gchar *testprint[] = { "%s/data/testprint", "%s/data/testprint.ps", NULL }; const gchar **pattern; const gchar *datadir = NULL; http_t *http = NULL; gchar *printer_uri = NULL; gchar *filename = NULL; gchar *resource = NULL; ipp_t *response = NULL; ipp_t *request; if ((datadir = getenv ("CUPS_DATADIR")) != NULL) { for (pattern = testprint; *pattern != NULL; pattern++) { filename = g_strdup_printf (*pattern, datadir); if (g_access (filename, R_OK) == 0) break; else { g_free (filename); filename = NULL; } } } else { for (i = 0; (datadir = dirs[i]) != NULL && filename == NULL; i++) { for (pattern = testprint; *pattern != NULL; pattern++) { filename = g_strdup_printf (*pattern, datadir); if (g_access (filename, R_OK) == 0) break; else { g_free (filename); filename = NULL; } } } } if (filename) { if (type & CUPS_PRINTER_CLASS) { printer_uri = g_strdup_printf ("ipp://localhost/classes/%s", printer_name); resource = g_strdup_printf ("/classes/%s", printer_name); } else { printer_uri = g_strdup_printf ("ipp://localhost/printers/%s", printer_name); resource = g_strdup_printf ("/printers/%s", printer_name); } http = httpConnectEncrypt (cupsServer (), ippPort (), cupsEncryption ()); if (http) { request = ippNewRequest (IPP_PRINT_JOB); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri", NULL, printer_uri); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name", NULL, cupsUser ()); ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_NAME, /* Translators: Name of job which makes printer to print test page */ "job-name", NULL, _("Test page")); response = cupsDoFileRequest (http, request, resource, filename); httpClose (http); } if (response) { if (ippGetState (response) == IPP_ERROR) g_warning ("An error has occured during printing of test page."); ippDelete (response); } g_free (filename); g_free (printer_uri); g_free (resource); } else { PpMaintenanceCommand *command; command = pp_maintenance_command_new (printer_name, "PrintSelfTestPage", /* Translators: Name of job which makes printer to print test page */ _("Test page")); pp_maintenance_command_execute_async (command, NULL, pp_maintenance_command_execute_cb, self); } } } static void update_sensitivity (gpointer user_data) { CcPrintersPanelPrivate *priv; GtkTreeSelection *selection; CcPrintersPanel *self = (CcPrintersPanel*) user_data; cups_ptype_t type = 0; GtkTreeModel *model; GtkTreeView *treeview; GtkTreeIter tree_iter; const char *cups_server = NULL; GtkWidget *widget; gboolean is_authorized; gboolean is_discovered = FALSE; gboolean is_class = FALSE; gboolean is_changing_driver = FALSE; gboolean printer_selected; gboolean local_server = TRUE; gboolean no_cups = FALSE; gboolean is_new = FALSE; gboolean already_present_local; GList *iter; gchar *current_printer_name = NULL; gint i; priv = PRINTERS_PANEL_PRIVATE (self); is_authorized = priv->permission && g_permission_get_allowed (G_PERMISSION (priv->permission)) && priv->lockdown_settings && !g_settings_get_boolean (priv->lockdown_settings, "disable-print-setup"); printer_selected = priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL; if (printer_selected) { for (i = 0; i < priv->dests[priv->current_dest].num_options; i++) { if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "printer-type") == 0) { type = atoi (priv->dests[priv->current_dest].options[i].value); is_discovered = type & CUPS_PRINTER_DISCOVERED; is_class = type & CUPS_PRINTER_CLASS; break; } } for (iter = priv->driver_change_list; iter; iter = iter->next) { SetPPDItem *item = (SetPPDItem *) iter->data; if (g_strcmp0 (item->printer_name, priv->dests[priv->current_dest].name) == 0) { is_changing_driver = TRUE; } } } treeview = (GtkTreeView*) gtk_builder_get_object (priv->builder, "printers-treeview"); selection = gtk_tree_view_get_selection (treeview); if (selection && gtk_tree_selection_get_selected (selection, &model, &tree_iter)) { gtk_tree_model_get (model, &tree_iter, PRINTER_NAME_COLUMN, ¤t_printer_name, -1); } if (priv->new_printer_name && g_strcmp0 (priv->new_printer_name, current_printer_name) == 0) { printer_selected = TRUE; is_discovered = FALSE; is_class = FALSE; is_new = TRUE; } g_free (current_printer_name); 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 (priv->builder, "notebook"); if (gtk_notebook_get_current_page (GTK_NOTEBOOK (widget)) == NOTEBOOK_NO_CUPS_PAGE) no_cups = TRUE; already_present_local = local_server && !is_discovered && is_authorized && !is_new; widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-add-button"); gtk_widget_set_sensitive (widget, local_server && is_authorized && !no_cups && !priv->new_printer_name); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-add-button2"); gtk_widget_set_sensitive (widget, local_server && is_authorized && !no_cups && !priv->new_printer_name); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-remove-button"); gtk_widget_set_sensitive (widget, already_present_local && printer_selected && !no_cups); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-disable-switch"); gtk_widget_set_sensitive (widget, already_present_local); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-default-check-button"); gtk_widget_set_sensitive (widget, is_authorized && !is_new); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "print-test-page-button"); gtk_widget_set_sensitive (widget, printer_selected && !is_new); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-options-button"); gtk_widget_set_sensitive (widget, printer_selected && local_server && !is_discovered && !priv->pp_options_dialog && !is_new); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-jobs-button"); gtk_widget_set_sensitive (widget, printer_selected && !is_new); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-icon"); gtk_widget_set_sensitive (widget, printer_selected); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-name-label"); cc_editable_entry_set_editable (CC_EDITABLE_ENTRY (widget), already_present_local); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-location-label"); cc_editable_entry_set_editable (CC_EDITABLE_ENTRY (widget), already_present_local); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-model-notebook"); if (is_changing_driver) { gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 2); } else { if (already_present_local && !is_class && !priv->getting_ppd_names) gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 0); else gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 1); } } static void on_permission_changed (GPermission *permission, GParamSpec *pspec, gpointer data) { update_sensitivity (data); } static void on_lockdown_settings_changed (GSettings *settings, const char *key, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; if (g_str_equal (key, "disable-print-setup") == FALSE) return; priv = PRINTERS_PANEL_PRIVATE (self); #if 0 /* FIXME */ gtk_widget_set_sensitive (priv->lock_button, !g_settings_get_boolean (priv->lockdown_settings, "disable-print-setup")); #endif on_permission_changed (priv->permission, NULL, user_data); } static void printer_options_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; priv = PRINTERS_PANEL_PRIVATE (self); pp_options_dialog_free (priv->pp_options_dialog); priv->pp_options_dialog = NULL; update_sensitivity (self); if (response_id == GTK_RESPONSE_OK) actualize_printers_list (self); } static void printer_options_cb (GtkToolButton *toolbutton, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; GtkWidget *widget; gboolean is_authorized; priv = PRINTERS_PANEL_PRIVATE (self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "main-vbox"); is_authorized = priv->permission && g_permission_get_allowed (G_PERMISSION (priv->permission)) && priv->lockdown_settings && !g_settings_get_boolean (priv->lockdown_settings, "disable-print-setup"); if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) { priv->pp_options_dialog = pp_options_dialog_new ( GTK_WINDOW (gtk_widget_get_toplevel (widget)), printer_options_response_cb, self, priv->dests[priv->current_dest].name, is_authorized); update_sensitivity (self); } } static gboolean cups_status_check (gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; gboolean result = TRUE; http_t *http; priv = self->priv = PRINTERS_PANEL_PRIVATE (self); http = httpConnectEncrypt (cupsServer (), ippPort (), cupsEncryption ()); if (http) { httpClose (http); actualize_printers_list (self); attach_to_cups_notifier (self); priv->cups_status_check_id = 0; result = FALSE; } return result; } static void get_all_ppds_async_cb (PPDList *ppds, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; priv = self->priv = PRINTERS_PANEL_PRIVATE (self); priv->all_ppds_list = ppds; if (priv->pp_ppd_selection_dialog) pp_ppd_selection_dialog_set_ppd_list (priv->pp_ppd_selection_dialog, priv->all_ppds_list); g_object_unref (priv->get_all_ppds_cancellable); priv->get_all_ppds_cancellable = NULL; } static void update_label_padding (GtkWidget *widget, GtkAllocation *allocation, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; GtkAllocation allocation1, allocation2; GtkWidget *label; GtkWidget *sublabel; gint offset; gint pad; priv = PRINTERS_PANEL_PRIVATE (self); sublabel = gtk_bin_get_child (GTK_BIN (widget)); if (sublabel) { gtk_widget_get_allocation (widget, &allocation1); gtk_widget_get_allocation (sublabel, &allocation2); offset = allocation2.x - allocation1.x; label = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-model-label"); gtk_misc_get_padding (GTK_MISC (label), &pad, NULL); if (offset != pad) gtk_misc_set_padding (GTK_MISC (label), offset, 0); } } static void jobs_dialog_response_cb (GtkDialog *dialog, gint response_id, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; priv = PRINTERS_PANEL_PRIVATE (self); pp_jobs_dialog_free (priv->pp_jobs_dialog); priv->pp_jobs_dialog = NULL; } static void printer_jobs_cb (GtkToolButton *toolbutton, gpointer user_data) { CcPrintersPanelPrivate *priv; CcPrintersPanel *self = (CcPrintersPanel*) user_data; GtkWidget *widget; priv = PRINTERS_PANEL_PRIVATE (self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "main-vbox"); if (priv->current_dest >= 0 && priv->current_dest < priv->num_dests && priv->dests != NULL) priv->pp_jobs_dialog = pp_jobs_dialog_new ( GTK_WINDOW (gtk_widget_get_toplevel (widget)), jobs_dialog_response_cb, self, priv->dests[priv->current_dest].name); } static void cc_printers_panel_init (CcPrintersPanel *self) { CcPrintersPanelPrivate *priv; GtkWidget *top_widget; GtkWidget *widget; GError *error = NULL; http_t *http; gchar *objects[] = { "main-vbox", NULL }; GtkStyleContext *context; guint builder_result; priv = self->priv = PRINTERS_PANEL_PRIVATE (self); /* initialize main data structure */ priv->builder = gtk_builder_new (); priv->dests = NULL; priv->dest_model_names = NULL; priv->ppd_file_names = NULL; priv->num_dests = 0; priv->current_dest = -1; priv->num_jobs = 0; priv->pp_new_printer_dialog = NULL; priv->pp_options_dialog = NULL; priv->subscription_id = 0; priv->cups_status_check_id = 0; priv->subscription_renewal_id = 0; priv->cups_proxy = NULL; priv->cups_bus_connection = NULL; priv->dbus_subscription_id = 0; priv->new_printer_name = NULL; priv->new_printer_location = NULL; priv->new_printer_make_and_model = NULL; priv->new_printer_on_network = FALSE; priv->select_new_printer = FALSE; priv->permission = NULL; priv->lockdown_settings = NULL; priv->getting_ppd_names = FALSE; priv->all_ppds_list = NULL; priv->get_all_ppds_cancellable = NULL; priv->preferred_drivers = NULL; builder_result = gtk_builder_add_objects_from_file (priv->builder, DATADIR"/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); g_error_free (error); return; } /* add the top level widget */ top_widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "main-vbox"); /* connect signals */ widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-add-button"); g_signal_connect (widget, "clicked", G_CALLBACK (printer_add_cb), self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-add-button2"); g_signal_connect (widget, "clicked", G_CALLBACK (printer_add_cb), self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-remove-button"); g_signal_connect (widget, "clicked", G_CALLBACK (printer_remove_cb), self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-disable-switch"); g_signal_connect (widget, "notify::active", G_CALLBACK (printer_disable_cb), self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "supply-drawing-area"); g_signal_connect (widget, "draw", G_CALLBACK (supply_levels_draw_cb), self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-default-check-button"); g_signal_connect (widget, "toggled", G_CALLBACK (printer_set_default_cb), self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "print-test-page-button"); g_signal_connect (widget, "clicked", G_CALLBACK (test_page_cb), self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-jobs-button"); g_signal_connect (widget, "clicked", G_CALLBACK (printer_jobs_cb), self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-options-button"); g_signal_connect (widget, "clicked", G_CALLBACK (printer_options_cb), self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-name-label"); g_signal_connect (widget, "editing-done", G_CALLBACK (printer_name_edit_cb), self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-location-label"); g_signal_connect (widget, "editing-done", G_CALLBACK (printer_location_edit_cb), self); priv->lockdown_settings = g_settings_new ("org.gnome.desktop.lockdown"); if (priv->lockdown_settings) g_signal_connect (priv->lockdown_settings, "changed", G_CALLBACK (on_lockdown_settings_changed), self); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-model-button"); g_signal_connect (widget, "clicked", G_CALLBACK (popup_model_menu_cb), self); g_signal_connect (widget, "size-allocate", G_CALLBACK (update_label_padding), self); /* Set junctions */ widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printers-scrolledwindow"); context = gtk_widget_get_style_context (widget); gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM); widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printers-toolbar"); context = gtk_widget_get_style_context (widget); gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP); /* Make model label and ip-address label selectable */ widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-ip-address-label"); cc_editable_entry_set_selectable (CC_EDITABLE_ENTRY (widget), TRUE); /* Add unlock button */ priv->permission = (GPermission *)polkit_permission_new_sync ( "org.opensuse.cupspkhelper.mechanism.all-edit", NULL, NULL, NULL); if (priv->permission != NULL) { g_signal_connect (priv->permission, "notify", G_CALLBACK (on_permission_changed), self); on_permission_changed (priv->permission, NULL, 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"); gtk_style_context_get_background_color (gtk_widget_get_style_context (top_widget), GTK_STATE_FLAG_NORMAL, &priv->background_color); populate_printers_list (self); attach_to_cups_notifier (self); priv->get_all_ppds_cancellable = g_cancellable_new (); get_all_ppds_async (priv->get_all_ppds_cancellable, get_all_ppds_async_cb, self); http = httpConnectEncrypt (cupsServer (), ippPort (), cupsEncryption ()); if (!http) { priv->cups_status_check_id = g_timeout_add_seconds (CUPS_STATUS_CHECK_INTERVAL, cups_status_check, self); } else httpClose (http); gtk_container_add (GTK_CONTAINER (self), top_widget); gtk_widget_show_all (GTK_WIDGET (self)); } void cc_printers_panel_register (GIOModule *module) { cc_printers_panel_register_type (G_TYPE_MODULE (module)); g_io_extension_point_implement (CC_SHELL_PANEL_EXTENSION_POINT, CC_TYPE_PRINTERS_PANEL, "printers", 0); }