[gnome-control-center] printers: Allow users to change printer's PPD file
- From: Marek KaÅÃk <mkasik src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-control-center] printers: Allow users to change printer's PPD file
- Date: Mon, 16 Jul 2012 11:54:10 +0000 (UTC)
commit dd7479caeace07e44d72cd310753a87a44e3e578
Author: Marek Kasik <mkasik redhat com>
Date: Mon Jul 16 11:10:02 2012 +0200
printers: Allow users to change printer's PPD file
This commit adds popup window which when activated allows
user to select PPD from local database of installed PPDs,
select directly PPD from filesystem or select one from 3
recommended PPDs (#678637).
The popup is activated by clicking on model field (the panel
has to be unlocked). It starts to search for the best PPDs
available immediately after its popup.
All operations are asynchronous.
panels/printers/Makefile.am | 15 +-
panels/printers/cc-printers-panel.c | 641 +++++++++++-
panels/printers/pp-ppd-selection-dialog.c | 444 ++++++++
panels/printers/pp-ppd-selection-dialog.h | 46 +
panels/printers/pp-utils.c | 1635 +++++++++++++++++++++++++++++
panels/printers/pp-utils.h | 90 ++
panels/printers/ppd-selection-dialog.ui | 206 ++++
panels/printers/printers.ui | 75 ++-
8 files changed, 3119 insertions(+), 33 deletions(-)
---
diff --git a/panels/printers/Makefile.am b/panels/printers/Makefile.am
index 0dade8c..dab5ae5 100644
--- a/panels/printers/Makefile.am
+++ b/panels/printers/Makefile.am
@@ -3,6 +3,7 @@ cappletname = printers
uidir = $(pkgdatadir)/ui/printers
dist_ui_DATA = \
new-printer-dialog.ui \
+ ppd-selection-dialog.ui \
printers.ui
INCLUDES = \
@@ -18,12 +19,14 @@ ccpanelsdir = $(PANELS_DIR)
ccpanels_LTLIBRARIES = libprinters.la
libprinters_la_SOURCES = \
- printers-module.c \
- pp-utils.c \
- pp-utils.h \
- pp-new-printer-dialog.c \
- pp-new-printer-dialog.h \
- cc-printers-panel.c \
+ printers-module.c \
+ pp-utils.c \
+ pp-utils.h \
+ pp-new-printer-dialog.c \
+ pp-new-printer-dialog.h \
+ pp-ppd-selection-dialog.c \
+ pp-ppd-selection-dialog.h \
+ cc-printers-panel.c \
cc-printers-panel.h
libprinters_la_LIBADD = $(PRINTERS_PANEL_LIBS) $(PANEL_LIBS) $(CUPS_LIBS)
diff --git a/panels/printers/cc-printers-panel.c b/panels/printers/cc-printers-panel.c
index 0ea51c4..63fc803 100644
--- a/panels/printers/cc-printers-panel.c
+++ b/panels/printers/cc-printers-panel.c
@@ -33,6 +33,7 @@
#include "cc-editable-entry.h"
#include "pp-new-printer-dialog.h"
+#include "pp-ppd-selection-dialog.h"
#include "pp-utils.h"
G_DEFINE_DYNAMIC_TYPE (CcPrintersPanel, cc_printers_panel, CC_TYPE_PANEL)
@@ -83,6 +84,7 @@ struct _CcPrintersPanelPrivate
GSettings *lockdown_settings;
PpNewPrinterDialog *pp_new_printer_dialog;
+ PpPPDSelectionDialog *pp_ppd_selection_dialog;
GDBusProxy *cups_proxy;
GDBusConnection *cups_bus_connection;
@@ -91,13 +93,21 @@ struct _CcPrintersPanelPrivate
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;
+ gboolean getting_all_ppds;
+
gpointer dummy;
};
static void actualize_jobs_list (CcPrintersPanel *self);
static void actualize_printers_list (CcPrintersPanel *self);
static void actualize_allowed_users_list (CcPrintersPanel *self);
-static void actualize_sensitivity (gpointer user_data);
+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);
@@ -180,7 +190,22 @@ cc_printers_panel_dispose (GObject *object)
detach_from_cups_notifier (CC_PRINTERS_PANEL (object));
if (priv->cups_status_check_id > 0)
- g_source_remove (priv->cups_status_check_id);
+ {
+ 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;
+ }
G_OBJECT_CLASS (cc_printers_panel_parent_class)->dispose (object);
}
@@ -502,6 +527,8 @@ printer_selection_changed_cb (GtkTreeSelection *selection,
cups_ptype_t type = 0;
GtkTreeIter iter;
GtkWidget *widget;
+ GtkWidget *model_button;
+ GtkWidget *model_label;
gboolean sensitive;
GValue value = G_VALUE_INIT;
gchar *printer_make_and_model = NULL;
@@ -814,16 +841,23 @@ printer_selection_changed_cb (GtkTreeSelection *selection,
cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), EMPTY_TEXT);
- widget = (GtkWidget*)
+ 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)
{
- cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), 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
- cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), EMPTY_TEXT);
+ {
+ gtk_button_set_label (GTK_BUTTON (model_button), EMPTY_TEXT);
+ gtk_label_set_text (GTK_LABEL (model_label), EMPTY_TEXT);
+ }
widget = (GtkWidget*)
@@ -918,8 +952,12 @@ printer_selection_changed_cb (GtkTreeSelection *selection,
cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), "");
widget = (GtkWidget*)
+ gtk_builder_get_object (priv->builder, "printer-model-button");
+ gtk_button_set_label (GTK_BUTTON (widget), "");
+
+ widget = (GtkWidget*)
gtk_builder_get_object (priv->builder, "printer-model-label");
- cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), "");
+ gtk_label_set_text (GTK_LABEL (widget), "");
widget = (GtkWidget*)
gtk_builder_get_object (priv->builder, "printer-ip-address-label");
@@ -930,7 +968,7 @@ printer_selection_changed_cb (GtkTreeSelection *selection,
cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), "");
}
- actualize_sensitivity (self);
+ update_sensitivity (self);
}
static void
@@ -1157,7 +1195,7 @@ actualize_printers_list (CcPrintersPanel *self)
g_free (current_printer_instance);
g_object_unref (store);
- actualize_sensitivity (self);
+ update_sensitivity (self);
}
static void
@@ -2180,6 +2218,475 @@ printer_location_edit_cb (GtkWidget *entry,
}
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)
+ {
+ if (g_strcmp0 ((gchar *) iter->data, printer_name) == 0)
+ {
+ priv->driver_change_list = g_list_remove_link (priv->driver_change_list, iter);
+ g_list_free_full (iter, g_free);
+ 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,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+ GTK_STOCK_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)
+ {
+ priv->driver_change_list =
+ g_list_prepend (priv->driver_change_list, g_strdup (printer_name));
+ update_sensitivity (self);
+ printer_set_ppd_file_async (printer_name,
+ ppd_filename,
+ NULL,
+ 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)
+ {
+ priv->driver_change_list = g_list_prepend (priv->driver_change_list,
+ g_strdup (printer_name));
+ update_sensitivity (self);
+ printer_set_ppd_async (printer_name,
+ ppd_name,
+ NULL,
+ 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");
+ }
+ else
+ {
+ manufacturer =
+ get_ppd_attribute (priv->ppd_file_names[priv->current_dest],
+ "Manufacturer");
+ }
+ }
+
+ 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)
+ {
+ priv->driver_change_list = g_list_prepend (priv->driver_change_list,
+ g_strdup (printer_name));
+ update_sensitivity (self);
+ printer_set_ppd_async (printer_name,
+ ppd_name,
+ NULL,
+ 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
test_page_cb (GtkButton *button,
gpointer user_data)
{
@@ -2303,7 +2810,7 @@ test_page_cb (GtkButton *button,
}
static void
-actualize_sensitivity (gpointer user_data)
+update_sensitivity (gpointer user_data)
{
CcPrintersPanelPrivate *priv;
CcPrintersPanel *self = (CcPrintersPanel*) user_data;
@@ -2312,9 +2819,12 @@ actualize_sensitivity (gpointer user_data)
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;
+ GList *iter;
gint i;
priv = PRINTERS_PANEL_PRIVATE (self);
@@ -2330,15 +2840,26 @@ actualize_sensitivity (gpointer user_data)
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;
- break;
- }
- }
+ {
+ 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)
+ {
+ if (g_strcmp0 ((gchar *) iter->data, priv->dests[priv->current_dest].name) == 0)
+ {
+ is_changing_driver = TRUE;
+ }
+ }
+ }
cups_server = cupsServer ();
if (cups_server &&
@@ -2390,6 +2911,19 @@ actualize_sensitivity (gpointer user_data)
widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-location-label");
cc_editable_entry_set_editable (CC_EDITABLE_ENTRY (widget), local_server && !is_discovered && is_authorized);
+
+ 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 (local_server && !is_discovered && is_authorized && !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
@@ -2397,7 +2931,7 @@ on_permission_changed (GPermission *permission,
GParamSpec *pspec,
gpointer data)
{
- actualize_sensitivity (data);
+ update_sensitivity (data);
}
static void
@@ -2491,6 +3025,56 @@ cups_status_check (gpointer user_data)
}
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;
+
+ priv->getting_all_ppds = FALSE;
+
+ if (priv->pp_ppd_selection_dialog)
+ pp_ppd_selection_dialog_set_ppd_list (priv->pp_ppd_selection_dialog,
+ priv->all_ppds_list);
+}
+
+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
cc_printers_panel_init (CcPrintersPanel *self)
{
CcPrintersPanelPrivate *priv;
@@ -2532,6 +3116,13 @@ cc_printers_panel_init (CcPrintersPanel *self)
priv->permission = NULL;
priv->lockdown_settings = NULL;
+ priv->getting_ppd_names = FALSE;
+
+ priv->all_ppds_list = NULL;
+ priv->getting_all_ppds = FALSE;
+
+ priv->preferred_drivers = NULL;
+
builder_result = gtk_builder_add_objects_from_file (priv->builder,
DATADIR"/printers.ui",
objects, &error);
@@ -2628,6 +3219,11 @@ cc_printers_panel_init (CcPrintersPanel *self)
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*)
@@ -2666,10 +3262,6 @@ cc_printers_panel_init (CcPrintersPanel *self)
gtk_builder_get_object (priv->builder, "printer-ip-address-label");
cc_editable_entry_set_selectable (CC_EDITABLE_ENTRY (widget), TRUE);
- widget = (GtkWidget*)
- gtk_builder_get_object (priv->builder, "printer-model-label");
- cc_editable_entry_set_selectable (CC_EDITABLE_ENTRY (widget), TRUE);
-
/* Add unlock button */
priv->permission = (GPermission *)polkit_permission_new_sync (
@@ -2694,6 +3286,9 @@ Please check your installation");
populate_allowed_users_list (self);
attach_to_cups_notifier (self);
+ priv->getting_all_ppds = TRUE;
+ get_all_ppds_async (get_all_ppds_async_cb, self);
+
http = httpConnectEncrypt (cupsServer (), ippPort (), cupsEncryption ());
if (!http)
{
diff --git a/panels/printers/pp-ppd-selection-dialog.c b/panels/printers/pp-ppd-selection-dialog.c
new file mode 100644
index 0000000..2498319
--- /dev/null
+++ b/panels/printers/pp-ppd-selection-dialog.c
@@ -0,0 +1,444 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2012 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 3 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.
+ *
+ * Author: Marek Kasik <mkasik redhat com>
+ */
+
+#include "config.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+
+#include <cups/cups.h>
+#include <cups/ppd.h>
+
+#include "pp-ppd-selection-dialog.h"
+
+static void pp_ppd_selection_dialog_hide (PpPPDSelectionDialog *dialog);
+
+enum
+{
+ PPD_NAMES_COLUMN = 0,
+ PPD_DISPLAY_NAMES_COLUMN
+};
+
+enum
+{
+ PPD_MANUFACTURERS_NAMES_COLUMN = 0,
+ PPD_MANUFACTURERS_DISPLAY_NAMES_COLUMN
+};
+
+
+struct _PpPPDSelectionDialog {
+ GtkBuilder *builder;
+ GtkWidget *parent;
+ GtkWidget *dialog;
+
+ UserResponseCallback user_callback;
+ gpointer user_data;
+
+ gchar *ppd_name;
+ GtkResponseType response;
+ gchar *manufacturer;
+
+ PPDList *list;
+};
+
+static void
+manufacturer_selection_changed_cb (GtkTreeSelection *selection,
+ gpointer user_data)
+{
+ PpPPDSelectionDialog *dialog = (PpPPDSelectionDialog *) user_data;
+ GtkListStore *store;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeView *models_treeview;
+ gchar *manufacturer_name = NULL;
+ gint i, index;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_tree_model_get (model, &iter,
+ PPD_MANUFACTURERS_NAMES_COLUMN, &manufacturer_name,
+ -1);
+ }
+
+ if (manufacturer_name)
+ {
+ index = -1;
+ for (i = 0; i < dialog->list->num_of_manufacturers; i++)
+ {
+ if (g_strcmp0 (manufacturer_name,
+ dialog->list->manufacturers[i]->manufacturer_name) == 0)
+ {
+ index = i;
+ break;
+ }
+ }
+
+ if (index >= 0)
+ {
+ models_treeview = (GtkTreeView*)
+ gtk_builder_get_object (dialog->builder, "ppd-selection-models-treeview");
+
+ store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+
+ for (i = 0; i < dialog->list->manufacturers[index]->num_of_ppds; i++)
+ {
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ PPD_NAMES_COLUMN, dialog->list->manufacturers[index]->ppds[i]->ppd_name,
+ PPD_DISPLAY_NAMES_COLUMN, dialog->list->manufacturers[index]->ppds[i]->ppd_display_name,
+ -1);
+ }
+
+ gtk_tree_view_set_model (models_treeview, GTK_TREE_MODEL (store));
+ g_object_unref (store);
+ }
+
+ g_free (manufacturer_name);
+ }
+}
+
+static void
+model_selection_changed_cb (GtkTreeSelection *selection,
+ gpointer user_data)
+{
+ PpPPDSelectionDialog *dialog = (PpPPDSelectionDialog *) user_data;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkWidget *widget;
+ gchar *model_name = NULL;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_tree_model_get (model, &iter,
+ PPD_NAMES_COLUMN, &model_name,
+ -1);
+ }
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (dialog->builder, "ppd-selection-select-button");
+
+ if (model_name)
+ {
+ gtk_widget_set_sensitive (widget, TRUE);
+ g_free (model_name);
+ }
+ else
+ {
+ gtk_widget_set_sensitive (widget, FALSE);
+ }
+}
+
+static void
+fill_ppds_list (PpPPDSelectionDialog *dialog)
+{
+ GtkTreeSelection *selection;
+ GtkListStore *store;
+ GtkTreePath *path;
+ GtkTreeView *treeview;
+ GtkTreeIter iter;
+ GtkTreeIter *preselect_iter = NULL;
+ GtkWidget *widget;
+ gint i;
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (dialog->builder, "ppd-spinner");
+ gtk_widget_hide (widget);
+ gtk_spinner_stop (GTK_SPINNER (widget));
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (dialog->builder, "progress-label");
+ gtk_widget_hide (widget);
+
+ treeview = (GtkTreeView*)
+ gtk_builder_get_object (dialog->builder, "ppd-selection-manufacturers-treeview");
+
+ if (dialog->list)
+ {
+ store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+
+ for (i = 0; i < dialog->list->num_of_manufacturers; i++)
+ {
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ PPD_MANUFACTURERS_NAMES_COLUMN, dialog->list->manufacturers[i]->manufacturer_name,
+ PPD_MANUFACTURERS_DISPLAY_NAMES_COLUMN, dialog->list->manufacturers[i]->manufacturer_display_name,
+ -1);
+
+ if (g_strcmp0 (dialog->manufacturer,
+ dialog->list->manufacturers[i]->manufacturer_display_name) == 0)
+ {
+ preselect_iter = gtk_tree_iter_copy (&iter);
+ }
+ }
+
+ gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (store));
+
+ if (preselect_iter &&
+ (selection = gtk_tree_view_get_selection (treeview)) != NULL)
+ {
+ gtk_tree_selection_select_iter (selection, preselect_iter);
+ path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), preselect_iter);
+ gtk_tree_view_scroll_to_cell (treeview, path, NULL, TRUE, 0.5, 0.0);
+ gtk_tree_path_free (path);
+ gtk_tree_iter_free (preselect_iter);
+ }
+
+ g_object_unref (store);
+ }
+}
+
+static void
+populate_dialog (PpPPDSelectionDialog *dialog)
+{
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeView *manufacturers_treeview;
+ GtkTreeView *models_treeview;
+ GtkWidget *widget;
+
+ manufacturers_treeview = (GtkTreeView*)
+ gtk_builder_get_object (dialog->builder, "ppd-selection-manufacturers-treeview");
+
+ renderer = gtk_cell_renderer_text_new ();
+
+ /* Translators: Name of column showing printer manufacturers */
+ column = gtk_tree_view_column_new_with_attributes (_("Manufacturers"), renderer,
+ "text", PPD_MANUFACTURERS_DISPLAY_NAMES_COLUMN, NULL);
+ gtk_tree_view_column_set_expand (column, TRUE);
+ gtk_tree_view_append_column (manufacturers_treeview, column);
+
+
+ models_treeview = (GtkTreeView*)
+ gtk_builder_get_object (dialog->builder, "ppd-selection-models-treeview");
+
+ renderer = gtk_cell_renderer_text_new ();
+
+ /* Translators: Name of column showing printer drivers */
+ column = gtk_tree_view_column_new_with_attributes (_("Drivers"), renderer,
+ "text", PPD_DISPLAY_NAMES_COLUMN, NULL);
+ gtk_tree_view_column_set_expand (column, TRUE);
+ gtk_tree_view_append_column (models_treeview, column);
+
+
+ g_signal_connect (gtk_tree_view_get_selection (models_treeview),
+ "changed", G_CALLBACK (model_selection_changed_cb), dialog);
+
+ g_signal_connect (gtk_tree_view_get_selection (manufacturers_treeview),
+ "changed", G_CALLBACK (manufacturer_selection_changed_cb), dialog);
+
+ gtk_widget_show_all (dialog->dialog);
+
+ if (!dialog->list)
+ {
+ widget = (GtkWidget*)
+ gtk_builder_get_object (dialog->builder, "ppd-spinner");
+ gtk_widget_show (widget);
+ gtk_spinner_start (GTK_SPINNER (widget));
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (dialog->builder, "progress-label");
+ gtk_widget_show (widget);
+ }
+ else
+ {
+ fill_ppds_list (dialog);
+ }
+}
+
+static void
+ppd_selection_dialog_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ PpPPDSelectionDialog *ppd_selection_dialog = (PpPPDSelectionDialog*) user_data;
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeView *models_treeview;
+ GtkTreeIter iter;
+
+ pp_ppd_selection_dialog_hide (ppd_selection_dialog);
+
+ ppd_selection_dialog->response = response_id;
+ if (response_id == GTK_RESPONSE_OK)
+ {
+ models_treeview = (GtkTreeView*)
+ gtk_builder_get_object (ppd_selection_dialog->builder, "ppd-selection-models-treeview");
+
+ if (models_treeview)
+ {
+ selection = gtk_tree_view_get_selection (models_treeview);
+
+ if (selection)
+ {
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_tree_model_get (model, &iter,
+ PPD_NAMES_COLUMN, &ppd_selection_dialog->ppd_name,
+ -1);
+ }
+ }
+ }
+ }
+
+ ppd_selection_dialog->user_callback (GTK_DIALOG (ppd_selection_dialog->dialog),
+ response_id,
+ ppd_selection_dialog->user_data);
+}
+
+static void
+update_alignment_padding (GtkWidget *widget,
+ GtkAllocation *allocation,
+ gpointer user_data)
+{
+ PpPPDSelectionDialog *dialog = (PpPPDSelectionDialog*) user_data;
+ GtkAllocation allocation2;
+ GtkWidget *action_area;
+ gint offset_left, offset_right;
+ guint padding_left, padding_right,
+ padding_top, padding_bottom;
+
+ action_area = (GtkWidget*)
+ gtk_builder_get_object (dialog->builder, "dialog-action-area1");
+ gtk_widget_get_allocation (action_area, &allocation2);
+
+ offset_left = allocation2.x - allocation->x;
+ offset_right = (allocation->x + allocation->width) -
+ (allocation2.x + allocation2.width);
+
+ gtk_alignment_get_padding (GTK_ALIGNMENT (widget),
+ &padding_top, &padding_bottom,
+ &padding_left, &padding_right);
+ if (allocation->x >= 0 && allocation2.x >= 0)
+ {
+ if (offset_left > 0 && offset_left != padding_left)
+ gtk_alignment_set_padding (GTK_ALIGNMENT (widget),
+ padding_top, padding_bottom,
+ offset_left, padding_right);
+
+ gtk_alignment_get_padding (GTK_ALIGNMENT (widget),
+ &padding_top, &padding_bottom,
+ &padding_left, &padding_right);
+ if (offset_right > 0 && offset_right != padding_right)
+ gtk_alignment_set_padding (GTK_ALIGNMENT (widget),
+ padding_top, padding_bottom,
+ padding_left, offset_right);
+ }
+}
+
+PpPPDSelectionDialog *
+pp_ppd_selection_dialog_new (GtkWindow *parent,
+ PPDList *ppd_list,
+ gchar *manufacturer,
+ UserResponseCallback user_callback,
+ gpointer user_data)
+{
+ PpPPDSelectionDialog *dialog;
+ GtkWidget *widget;
+ GError *error = NULL;
+ gchar *objects[] = { "ppd-selection-dialog", NULL };
+ guint builder_result;
+
+ dialog = g_new0 (PpPPDSelectionDialog, 1);
+
+ dialog->builder = gtk_builder_new ();
+ dialog->parent = GTK_WIDGET (parent);
+
+ builder_result = gtk_builder_add_objects_from_file (dialog->builder,
+ DATADIR"/ppd-selection-dialog.ui",
+ objects, &error);
+
+ if (builder_result == 0)
+ {
+ g_warning ("Could not load ui: %s", error->message);
+ g_error_free (error);
+ return NULL;
+ }
+
+ dialog->dialog = (GtkWidget *) gtk_builder_get_object (dialog->builder, "ppd-selection-dialog");
+ dialog->user_callback = user_callback;
+ dialog->user_data = user_data;
+
+ dialog->response = GTK_RESPONSE_NONE;
+ dialog->list = ppd_list_copy (ppd_list);
+
+ dialog->manufacturer = get_standard_manufacturers_name (manufacturer);
+
+ /* connect signals */
+ g_signal_connect (dialog->dialog, "delete-event", G_CALLBACK (gtk_widget_hide_on_delete), NULL);
+ g_signal_connect (dialog->dialog, "response", G_CALLBACK (ppd_selection_dialog_response_cb), dialog);
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (dialog->builder, "content-alignment");
+ g_signal_connect (widget, "size-allocate", G_CALLBACK (update_alignment_padding), dialog);
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (dialog->builder, "ppd-spinner");
+ gtk_spinner_start (GTK_SPINNER (widget));
+
+ populate_dialog (dialog);
+
+ gtk_window_set_transient_for (GTK_WINDOW (dialog->dialog), GTK_WINDOW (parent));
+ gtk_window_present (GTK_WINDOW (dialog->dialog));
+ gtk_widget_show_all (GTK_WIDGET (dialog->dialog));
+
+ return dialog;
+}
+
+void
+pp_ppd_selection_dialog_free (PpPPDSelectionDialog *dialog)
+{
+ gtk_widget_destroy (GTK_WIDGET (dialog->dialog));
+
+ g_object_unref (dialog->builder);
+
+ g_free (dialog->ppd_name);
+
+ g_free (dialog->manufacturer);
+
+ g_free (dialog);
+}
+
+gchar *
+pp_ppd_selection_dialog_get_ppd_name (PpPPDSelectionDialog *dialog)
+{
+ return g_strdup (dialog->ppd_name);
+}
+
+void
+pp_ppd_selection_dialog_set_ppd_list (PpPPDSelectionDialog *dialog,
+ PPDList *list)
+{
+ dialog->list = list;
+ fill_ppds_list (dialog);
+}
+
+static void
+pp_ppd_selection_dialog_hide (PpPPDSelectionDialog *dialog)
+{
+ gtk_widget_hide (GTK_WIDGET (dialog->dialog));
+}
diff --git a/panels/printers/pp-ppd-selection-dialog.h b/panels/printers/pp-ppd-selection-dialog.h
new file mode 100644
index 0000000..2ea05d0
--- /dev/null
+++ b/panels/printers/pp-ppd-selection-dialog.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2012 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 3 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.
+ *
+ * Author: Marek Kasik <mkasik redhat com>
+ */
+
+#ifndef __PP_PPD_SELECTION_DIALOG_H__
+#define __PP_PPD_SELECTION_DIALOG_H__
+
+#include <gtk/gtk.h>
+#include "pp-utils.h"
+
+G_BEGIN_DECLS
+
+typedef struct _PpPPDSelectionDialog PpPPDSelectionDialog;
+
+typedef void (*UserResponseCallback) (GtkDialog *dialog, gint response_id, gpointer user_data);
+
+PpPPDSelectionDialog *pp_ppd_selection_dialog_new (GtkWindow *parent,
+ PPDList *ppd_list,
+ gchar *manufacturer,
+ UserResponseCallback user_callback,
+ gpointer user_data);
+gchar *pp_ppd_selection_dialog_get_ppd_name (PpPPDSelectionDialog *dialog);
+void pp_ppd_selection_dialog_set_ppd_list (PpPPDSelectionDialog *dialog,
+ PPDList *list);
+void pp_ppd_selection_dialog_free (PpPPDSelectionDialog *dialog);
+
+G_END_DECLS
+
+#endif
diff --git a/panels/printers/pp-utils.c b/panels/printers/pp-utils.c
index 5263af6..c78b70f 100644
--- a/panels/printers/pp-utils.c
+++ b/panels/printers/pp-utils.c
@@ -35,6 +35,8 @@
#define SCP_PATH "/org/fedoraproject/Config/Printing"
#define SCP_IFACE "org.fedoraproject.Config.Printing"
+#define DBUS_TIMEOUT 120000
+
gchar *
get_tag_value (const gchar *tag_string, const gchar *tag_name)
{
@@ -2560,3 +2562,1636 @@ printer_set_default_media_size (const gchar *printer_name)
g_error_free (error);
}
}
+
+
+typedef struct
+{
+ gchar *printer_name;
+ gchar **attributes_names;
+ GHashTable *result;
+ GIACallback callback;
+ gpointer user_data;
+ GMainContext *context;
+} GIAData;
+
+static gboolean
+get_ipp_attributes_idle_cb (gpointer user_data)
+{
+ GIAData *data = (GIAData *) user_data;
+
+ data->callback (data->result, data->user_data);
+
+ return FALSE;
+}
+
+static void
+get_ipp_attributes_data_free (gpointer user_data)
+{
+ GIAData *data = (GIAData *) user_data;
+
+ if (data->context)
+ g_main_context_unref (data->context);
+ g_free (data->printer_name);
+ if (data->attributes_names)
+ g_strfreev (data->attributes_names);
+ g_free (data);
+}
+
+static void
+get_ipp_attributes_cb (gpointer user_data)
+{
+ GIAData *data = (GIAData *) user_data;
+ GSource *idle_source;
+
+ idle_source = g_idle_source_new ();
+ g_source_set_callback (idle_source,
+ get_ipp_attributes_idle_cb,
+ data,
+ get_ipp_attributes_data_free);
+ g_source_attach (idle_source, data->context);
+ g_source_unref (idle_source);
+}
+
+static void
+ipp_attribute_free2 (gpointer attr)
+{
+ IPPAttribute *attribute = (IPPAttribute *) attr;
+ ipp_attribute_free (attribute);
+}
+
+static gpointer
+get_ipp_attributes_func (gpointer user_data)
+{
+ ipp_attribute_t *attr = NULL;
+ GIAData *data = (GIAData *) user_data;
+ ipp_t *request;
+ ipp_t *response = NULL;
+ gchar *printer_uri;
+ char **requested_attrs = NULL;
+ gint i, j, length = 0;
+
+ printer_uri = g_strdup_printf ("ipp://localhost/printers/%s", data->printer_name);
+
+ if (data->attributes_names)
+ {
+ length = g_strv_length (data->attributes_names);
+
+ requested_attrs = g_new0 (char *, length);
+ for (i = 0; data->attributes_names[i]; i++)
+ requested_attrs[i] = g_strdup (data->attributes_names[i]);
+
+ request = ippNewRequest (IPP_GET_PRINTER_ATTRIBUTES);
+ ippAddString (request, IPP_TAG_OPERATION, IPP_TAG_URI,
+ "printer-uri", NULL, printer_uri);
+ ippAddStrings (request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
+ "requested-attributes", length, NULL, (const char **) requested_attrs);
+ response = cupsDoRequest (CUPS_HTTP_DEFAULT, request, "/");
+ }
+
+ if (response)
+ {
+ if (response->request.status.status_code <= IPP_OK_CONFLICT)
+ {
+ for (j = 0; j < length; j++)
+ {
+ attr = ippFindAttribute (response, requested_attrs[j], IPP_TAG_ZERO);
+ if (attr && attr->num_values > 0 && attr->value_tag != IPP_TAG_NOVALUE)
+ {
+ IPPAttribute *attribute;
+
+ attribute = g_new0 (IPPAttribute, 1);
+ attribute->attribute_name = g_strdup (requested_attrs[j]);
+ attribute->attribute_values = g_new0 (IPPAttributeValue, attr->num_values);
+ attribute->num_of_values = attr->num_values;
+
+ if (attr->value_tag == IPP_TAG_INTEGER ||
+ attr->value_tag == IPP_TAG_ENUM)
+ {
+ attribute->attribute_type = IPP_ATTRIBUTE_TYPE_INTEGER;
+
+ for (i = 0; i < attr->num_values; i++)
+ attribute->attribute_values[i].integer_value = attr->values[i].integer;
+ }
+ else if (attr->value_tag == IPP_TAG_NAME ||
+ attr->value_tag == IPP_TAG_STRING ||
+ attr->value_tag == IPP_TAG_TEXT ||
+ attr->value_tag == IPP_TAG_URI ||
+ attr->value_tag == IPP_TAG_KEYWORD ||
+ attr->value_tag == IPP_TAG_URISCHEME)
+ {
+ attribute->attribute_type = IPP_ATTRIBUTE_TYPE_STRING;
+
+ for (i = 0; i < attr->num_values; i++)
+ attribute->attribute_values[i].string_value = g_strdup (attr->values[i].string.text);
+ }
+ else if (attr->value_tag == IPP_TAG_RANGE)
+ {
+ attribute->attribute_type = IPP_ATTRIBUTE_TYPE_RANGE;
+
+ for (i = 0; i < attr->num_values; i++)
+ {
+ attribute->attribute_values[i].lower_range = attr->values[i].range.lower;
+ attribute->attribute_values[i].upper_range = attr->values[i].range.upper;
+ }
+ }
+ else if (attr->value_tag == IPP_TAG_BOOLEAN)
+ {
+ attribute->attribute_type = IPP_ATTRIBUTE_TYPE_BOOLEAN;
+
+ for (i = 0; i < attr->num_values; i++)
+ attribute->attribute_values[i].boolean_value = attr->values[i].boolean;
+ }
+
+ if (!data->result)
+ data->result = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, ipp_attribute_free2);
+
+ g_hash_table_insert (data->result, g_strdup (requested_attrs[j]), attribute);
+ }
+ }
+ }
+
+ ippDelete (response);
+ }
+
+
+ for (i = 0; i < length; i++)
+ g_free (requested_attrs[i]);
+ g_free (requested_attrs);
+
+ g_free (printer_uri);
+
+ get_ipp_attributes_cb (data);
+
+ return NULL;
+}
+
+void
+get_ipp_attributes_async (const gchar *printer_name,
+ gchar **attributes_names,
+ GIACallback callback,
+ gpointer user_data)
+{
+ GIAData *data;
+ GThread *thread;
+ GError *error = NULL;
+
+ data = g_new0 (GIAData, 1);
+ data->printer_name = g_strdup (printer_name);
+ data->attributes_names = g_strdupv (attributes_names);
+ data->callback = callback;
+ data->user_data = user_data;
+ data->context = g_main_context_ref_thread_default ();
+
+ thread = g_thread_try_new ("get-ipp-attributes",
+ get_ipp_attributes_func,
+ data,
+ &error);
+
+ if (!thread)
+ {
+ g_warning ("%s", error->message);
+ callback (NULL, user_data);
+
+ g_error_free (error);
+ get_ipp_attributes_data_free (data);
+ }
+ else
+ {
+ g_thread_unref (thread);
+ }
+}
+
+IPPAttribute *
+ipp_attribute_copy (IPPAttribute *attr)
+{
+ IPPAttribute *result = NULL;
+ gint i;
+
+ if (attr)
+ {
+ result = g_new0 (IPPAttribute, 1);
+
+ *result = *attr;
+ result->attribute_name = g_strdup (attr->attribute_name);
+ result->attribute_values = g_new0 (IPPAttributeValue, attr->num_of_values);
+ for (i = 0; i < attr->num_of_values; i++)
+ {
+ result->attribute_values[i] = attr->attribute_values[i];
+ if (attr->attribute_values[i].string_value)
+ result->attribute_values[i].string_value = g_strdup (attr->attribute_values[i].string_value);
+ }
+ }
+
+ return result;
+}
+
+void
+ipp_attribute_free (IPPAttribute *attr)
+{
+ gint i;
+
+ if (attr)
+ {
+ for (i = 0; i < attr->num_of_values; i++)
+ g_free (attr->attribute_values[i].string_value);
+
+ g_free (attr->attribute_values);
+ g_free (attr->attribute_name);
+ g_free (attr);
+ }
+}
+
+
+
+typedef struct
+{
+ gchar *printer_name;
+ gchar *ppd_copy;
+ GCancellable *cancellable;
+ PSPCallback callback;
+ gpointer user_data;
+} PSPData;
+
+static void
+printer_set_ppd_async_dbus_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GVariant *output;
+ gboolean result = FALSE;
+ PSPData *data = (PSPData *) user_data;
+ GError *error = NULL;
+
+ output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+ res,
+ &error);
+ g_object_unref (source_object);
+
+ if (output)
+ {
+ const gchar *ret_error;
+
+ g_variant_get (output, "(&s)", &ret_error);
+ if (ret_error[0] != '\0')
+ g_warning ("%s", ret_error);
+ else
+ result = TRUE;
+
+ g_variant_unref (output);
+ }
+ else
+ {
+ if (error->code != G_IO_ERROR_CANCELLED)
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ data->callback (g_strdup (data->printer_name),
+ result,
+ data->user_data);
+
+ if (data->cancellable)
+ g_object_unref (data->cancellable);
+
+ if (data->ppd_copy)
+ {
+ g_unlink (data->ppd_copy);
+ g_free (data->ppd_copy);
+ }
+
+ g_free (data->printer_name);
+ g_free (data);
+}
+
+/*
+ * Set ppd for given printer.
+ * Don't use this for classes, just for printers.
+ */
+void
+printer_set_ppd_async (const gchar *printer_name,
+ const gchar *ppd_name,
+ GCancellable *cancellable,
+ PSPCallback callback,
+ gpointer user_data)
+{
+ GDBusConnection *bus;
+ PSPData *data;
+ GError *error = NULL;
+
+ data = g_new0 (PSPData, 1);
+ if (cancellable)
+ data->cancellable = g_object_ref (cancellable);
+ data->callback = callback;
+ data->user_data = user_data;
+ data->printer_name = g_strdup (printer_name);
+
+ if (printer_name == NULL ||
+ printer_name[0] == '\0')
+ {
+ goto out;
+ }
+
+ bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (!bus)
+ {
+ g_warning ("Failed to get system bus: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ g_dbus_connection_call (bus,
+ MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ "PrinterAdd",
+ g_variant_new ("(sssss)",
+ printer_name,
+ "",
+ ppd_name,
+ "",
+ ""),
+ G_VARIANT_TYPE ("(s)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ data->cancellable,
+ printer_set_ppd_async_dbus_cb,
+ data);
+
+ return;
+
+out:
+ callback (g_strdup (printer_name), FALSE, user_data);
+
+ if (data->cancellable)
+ g_object_unref (data->cancellable);
+ g_free (data->printer_name);
+ g_free (data);
+}
+
+static void
+printer_set_ppd_file_async_scb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GDBusConnection *bus;
+ gboolean success;
+ PSPData *data = (PSPData *) user_data;
+ GError *error = NULL;
+
+ success = g_file_copy_finish (G_FILE (source_object),
+ res,
+ &error);
+ g_object_unref (source_object);
+
+ if (!success)
+ {
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (!bus)
+ {
+ g_warning ("Failed to get system bus: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ g_dbus_connection_call (bus,
+ MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ "PrinterAddWithPpdFile",
+ g_variant_new ("(sssss)",
+ data->printer_name,
+ "",
+ data->ppd_copy,
+ "",
+ ""),
+ G_VARIANT_TYPE ("(s)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ data->cancellable,
+ printer_set_ppd_async_dbus_cb,
+ data);
+
+ return;
+
+out:
+ data->callback (g_strdup (data->printer_name), FALSE, data->user_data);
+
+ if (data->cancellable)
+ g_object_unref (data->cancellable);
+ g_free (data->printer_name);
+ g_free (data->ppd_copy);
+ g_free (data);
+}
+
+/*
+ * Set ppd for given printer.
+ * Don't use this for classes, just for printers.
+ */
+void
+printer_set_ppd_file_async (const gchar *printer_name,
+ const gchar *ppd_filename,
+ GCancellable *cancellable,
+ PSPCallback callback,
+ gpointer user_data)
+{
+ GFileIOStream *stream;
+ PSPData *data;
+ GFile *source_ppd_file;
+ GFile *destination_ppd_file;
+
+ data = g_new0 (PSPData, 1);
+ if (cancellable)
+ data->cancellable = g_object_ref (cancellable);
+ data->callback = callback;
+ data->user_data = user_data;
+ data->printer_name = g_strdup (printer_name);
+
+ if (printer_name == NULL ||
+ printer_name[0] == '\0')
+ {
+ goto out;
+ }
+
+ /*
+ * We need to copy the PPD to temp directory at first.
+ * This is needed because of SELinux.
+ */
+ source_ppd_file = g_file_new_for_path (ppd_filename);
+ destination_ppd_file = g_file_new_tmp ("g-c-c-XXXXXX.ppd", &stream, NULL);
+ g_object_unref (stream);
+ data->ppd_copy = g_strdup (g_file_get_path (destination_ppd_file));
+
+ g_file_copy_async (source_ppd_file,
+ destination_ppd_file,
+ G_FILE_COPY_OVERWRITE,
+ G_PRIORITY_DEFAULT,
+ cancellable,
+ NULL,
+ NULL,
+ printer_set_ppd_file_async_scb,
+ data);
+
+ g_object_unref (destination_ppd_file);
+
+ return;
+
+out:
+ callback (g_strdup (printer_name), FALSE, user_data);
+
+ if (data->cancellable)
+ g_object_unref (data->cancellable);
+ g_free (data->printer_name);
+ g_free (data);
+}
+
+
+
+typedef void (*GPACallback) (gchar **attribute_values,
+ gpointer user_data);
+
+typedef struct
+{
+ gchar *attribute_name;
+ gchar **ppds_names;
+ gchar **result;
+ GPACallback callback;
+ gpointer user_data;
+ GMainContext *context;
+} GPAData;
+
+static gboolean
+get_ppds_attribute_idle_cb (gpointer user_data)
+{
+ GPAData *data = (GPAData *) user_data;
+
+ data->callback (data->result, data->user_data);
+
+ return FALSE;
+}
+
+static void
+get_ppds_attribute_data_free (gpointer user_data)
+{
+ GPAData *data = (GPAData *) user_data;
+
+ if (data->context)
+ g_main_context_unref (data->context);
+ g_free (data->attribute_name);
+ g_strfreev (data->ppds_names);
+ g_free (data);
+}
+
+static void
+get_ppds_attribute_cb (gpointer user_data)
+{
+ GPAData *data = (GPAData *) user_data;
+ GSource *idle_source;
+
+ idle_source = g_idle_source_new ();
+ g_source_set_callback (idle_source,
+ get_ppds_attribute_idle_cb,
+ data,
+ get_ppds_attribute_data_free);
+ g_source_attach (idle_source, data->context);
+ g_source_unref (idle_source);
+}
+
+static gpointer
+get_ppds_attribute_func (gpointer user_data)
+{
+ ppd_file_t *ppd_file;
+ ppd_attr_t *ppd_attr;
+ GPAData *data = (GPAData *) user_data;
+ gchar *ppd_filename;
+ gint i;
+
+ data->result = g_new0 (gchar *, g_strv_length (data->ppds_names) + 1);
+ for (i = 0; data->ppds_names[i]; i++)
+ {
+ ppd_filename = g_strdup (cupsGetServerPPD (CUPS_HTTP_DEFAULT, data->ppds_names[i]));
+ if (ppd_filename)
+ {
+ ppd_file = ppdOpenFile (ppd_filename);
+ if (ppd_file)
+ {
+ ppd_attr = ppdFindAttr (ppd_file, data->attribute_name, NULL);
+ if (ppd_attr != NULL)
+ data->result[i] = g_strdup (ppd_attr->value);
+
+ ppdClose (ppd_file);
+ }
+
+ g_unlink (ppd_filename);
+ g_free (ppd_filename);
+ }
+ }
+
+ get_ppds_attribute_cb (data);
+
+ return NULL;
+}
+
+/*
+ * Get values of requested PPD attribute for given PPDs.
+ */
+static void
+get_ppds_attribute_async (gchar **ppds_names,
+ gchar *attribute_name,
+ GPACallback callback,
+ gpointer user_data)
+{
+ GPAData *data;
+ GThread *thread;
+ GError *error = NULL;
+
+ if (!ppds_names || !attribute_name)
+ callback (NULL, user_data);
+
+ data = g_new0 (GPAData, 1);
+ data->ppds_names = g_strdupv (ppds_names);
+ data->attribute_name = g_strdup (attribute_name);
+ data->callback = callback;
+ data->user_data = user_data;
+ data->context = g_main_context_ref_thread_default ();
+
+ thread = g_thread_try_new ("get-ppds-attribute",
+ get_ppds_attribute_func,
+ data,
+ &error);
+
+ if (!thread)
+ {
+ g_warning ("%s", error->message);
+ callback (NULL, user_data);
+
+ g_error_free (error);
+ get_ppds_attribute_data_free (data);
+ }
+ else
+ {
+ g_thread_unref (thread);
+ }
+}
+
+
+
+typedef void (*GDACallback) (gchar *device_id,
+ gchar *device_make_and_model,
+ gchar *device_uri,
+ gpointer user_data);
+
+typedef struct
+{
+ gchar *printer_name;
+ gchar *device_uri;
+ GCancellable *cancellable;
+ GList *backend_list;
+ GDACallback callback;
+ gpointer user_data;
+} GDAData;
+
+typedef struct
+{
+ gchar *printer_name;
+ gint count;
+ PPDName **result;
+ GCancellable *cancellable;
+ GPNCallback callback;
+ gpointer user_data;
+} GPNData;
+
+static void
+get_ppd_names_async_cb (gchar **attribute_values,
+ gpointer user_data)
+{
+ GPNData *data = (GPNData *) user_data;
+ gint i;
+
+ if (g_cancellable_is_cancelled (data->cancellable))
+ {
+ g_strfreev (attribute_values);
+
+ for (i = 0; data->result[i]; i++)
+ {
+ g_free (data->result[i]->ppd_name);
+ g_free (data->result[i]);
+ }
+
+ g_free (data->result);
+ data->result = NULL;
+
+ goto out;
+ }
+
+ if (attribute_values)
+ {
+ for (i = 0; attribute_values[i]; i++)
+ data->result[i]->ppd_display_name = attribute_values[i];
+
+ g_free (attribute_values);
+ }
+
+out:
+ data->callback (data->result,
+ data->printer_name,
+ g_cancellable_is_cancelled (data->cancellable),
+ data->user_data);
+
+ if (data->cancellable)
+ g_object_unref (data->cancellable);
+ g_free (data->printer_name);
+ g_free (data);
+}
+
+static void
+get_ppd_names_async_dbus_scb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GVariant *output;
+ PPDName *ppd_item;
+ PPDName **result = NULL;
+ GPNData *data = (GPNData *) user_data;
+ GError *error = NULL;
+ GList *driver_list = NULL;
+ GList *iter;
+ gint i, j, n = 0;
+ static const char * const match_levels[] = {
+ "exact-cmd",
+ "exact",
+ "close",
+ "generic",
+ "none"};
+
+ output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+ res,
+ &error);
+ g_object_unref (source_object);
+
+ if (output)
+ {
+ GVariant *array;
+
+ g_variant_get (output, "(@a(ss))",
+ &array);
+
+ if (array)
+ {
+ GVariantIter *iter;
+ GVariant *item;
+ gchar *driver;
+ gchar *match;
+
+ for (j = 0; j < G_N_ELEMENTS (match_levels) && n < data->count; j++)
+ {
+ g_variant_get (array,
+ "a(ss)",
+ &iter);
+
+ while ((item = g_variant_iter_next_value (iter)))
+ {
+ g_variant_get (item,
+ "(ss)",
+ &driver,
+ &match);
+
+ if (g_str_equal (match, match_levels[j]) && n < data->count)
+ {
+ ppd_item = g_new0 (PPDName, 1);
+ ppd_item->ppd_name = g_strdup (driver);
+
+ if (g_strcmp0 (match, "exact-cmd") == 0)
+ ppd_item->ppd_match_level = PPD_EXACT_CMD_MATCH;
+ else if (g_strcmp0 (match, "exact") == 0)
+ ppd_item->ppd_match_level = PPD_EXACT_MATCH;
+ else if (g_strcmp0 (match, "close") == 0)
+ ppd_item->ppd_match_level = PPD_CLOSE_MATCH;
+ else if (g_strcmp0 (match, "generic") == 0)
+ ppd_item->ppd_match_level = PPD_GENERIC_MATCH;
+ else if (g_strcmp0 (match, "none") == 0)
+ ppd_item->ppd_match_level = PPD_NO_MATCH;
+
+ driver_list = g_list_append (driver_list, ppd_item);
+
+ n++;
+ }
+
+ g_free (driver);
+ g_free (match);
+ g_variant_unref (item);
+ }
+ }
+
+ g_variant_unref (array);
+ }
+
+ g_variant_unref (output);
+ }
+ else
+ {
+ if (error->code != G_IO_ERROR_CANCELLED)
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ if (n > 0)
+ {
+ result = g_new0 (PPDName *, n + 1);
+ i = 0;
+ for (iter = driver_list; iter; iter = iter->next)
+ {
+ result[i] = iter->data;
+ i++;
+ }
+ }
+
+ if (result)
+ {
+ gchar **ppds_names;
+
+ data->result = result;
+
+ ppds_names = g_new0 (gchar *, n + 1);
+ for (i = 0; i < n; i++)
+ ppds_names[i] = g_strdup (result[i]->ppd_name);
+
+ get_ppds_attribute_async (ppds_names,
+ "NickName",
+ get_ppd_names_async_cb,
+ data);
+
+ g_strfreev (ppds_names);
+ }
+ else
+ {
+ data->callback (NULL,
+ data->printer_name,
+ g_cancellable_is_cancelled (data->cancellable),
+ data->user_data);
+
+ if (data->cancellable)
+ g_object_unref (data->cancellable);
+ g_free (data->printer_name);
+ g_free (data);
+ }
+}
+
+static void
+get_device_attributes_cb (gchar *device_id,
+ gchar *device_make_and_model,
+ gchar *device_uri,
+ gpointer user_data)
+{
+ GDBusConnection *bus;
+ GError *error = NULL;
+ GPNData *data = (GPNData *) user_data;
+
+ if (g_cancellable_is_cancelled (data->cancellable))
+ goto out;
+
+ if (!device_id || !device_make_and_model || !device_uri)
+ goto out;
+
+ bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ if (!bus)
+ {
+ g_warning ("Failed to get system bus: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ g_dbus_connection_call (bus,
+ SCP_BUS,
+ SCP_PATH,
+ SCP_IFACE,
+ "GetBestDrivers",
+ g_variant_new ("(sss)",
+ device_id,
+ device_make_and_model,
+ device_uri),
+ G_VARIANT_TYPE ("(a(ss))"),
+ G_DBUS_CALL_FLAGS_NONE,
+ DBUS_TIMEOUT,
+ data->cancellable,
+ get_ppd_names_async_dbus_scb,
+ data);
+
+ return;
+
+out:
+ data->callback (NULL,
+ data->printer_name,
+ g_cancellable_is_cancelled (data->cancellable),
+ data->user_data);
+
+ if (data->cancellable)
+ g_object_unref (data->cancellable);
+ g_free (data->printer_name);
+ g_free (data);
+}
+
+static void
+get_device_attributes_async_dbus_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+
+{
+ GVariant *output;
+ GDAData *data = (GDAData *) user_data;
+ GError *error = NULL;
+ GList *tmp;
+ gchar *device_id = NULL;
+ gchar *device_make_and_model = NULL;
+
+ output = g_dbus_connection_call_finish (G_DBUS_CONNECTION (source_object),
+ res,
+ &error);
+ g_object_unref (source_object);
+
+ if (output)
+ {
+ const gchar *ret_error;
+ GVariant *devices_variant = NULL;
+
+ g_variant_get (output, "(&s a{ss})",
+ &ret_error,
+ &devices_variant);
+
+ if (ret_error[0] != '\0')
+ {
+ g_warning ("%s", ret_error);
+ }
+
+ if (devices_variant)
+ {
+ GVariantIter *iter;
+ GVariant *item;
+ gint index = -1;
+
+ if (data->device_uri)
+ {
+ gchar *key;
+ gchar *value;
+ gchar *number;
+ gchar *endptr;
+ gchar *suffix;
+
+ g_variant_get (devices_variant,
+ "a{ss}",
+ &iter);
+
+ while ((item = g_variant_iter_next_value (iter)))
+ {
+ g_variant_get (item,
+ "{ss}",
+ &key,
+ &value);
+
+ if (g_str_equal (value, data->device_uri))
+ {
+ number = g_strrstr (key, ":");
+ if (number != NULL)
+ {
+ number++;
+ index = g_ascii_strtoll (number, &endptr, 10);
+ if (index == 0 && endptr == (number))
+ index = -1;
+ }
+ }
+
+ g_free (key);
+ g_free (value);
+ g_variant_unref (item);
+ }
+
+ suffix = g_strdup_printf (":%d", index);
+
+ g_variant_get (devices_variant,
+ "a{ss}",
+ &iter);
+
+ while ((item = g_variant_iter_next_value (iter)))
+ {
+ gchar *key;
+ gchar *value;
+
+ g_variant_get (item,
+ "{ss}",
+ &key,
+ &value);
+
+ if (g_str_has_suffix (key, suffix))
+ {
+ if (g_str_has_prefix (key, "device-id"))
+ {
+ device_id = g_strdup (value);
+ }
+
+ if (g_str_has_prefix (key, "device-make-and-model"))
+ {
+ device_make_and_model = g_strdup (value);
+ }
+ }
+
+ g_free (key);
+ g_free (value);
+ g_variant_unref (item);
+ }
+
+ g_free (suffix);
+ }
+
+ g_variant_unref (devices_variant);
+ }
+
+ g_variant_unref (output);
+ }
+ else
+ {
+ if (error->code != G_IO_ERROR_CANCELLED)
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ if (!device_id || !device_make_and_model)
+ {
+ GVariantBuilder include_scheme_builder;
+
+ g_free (device_id);
+ g_free (device_make_and_model);
+
+ device_id = NULL;
+ device_make_and_model = NULL;
+
+ if (data->backend_list && !g_cancellable_is_cancelled (data->cancellable))
+ {
+ g_variant_builder_init (&include_scheme_builder, G_VARIANT_TYPE ("as"));
+ g_variant_builder_add (&include_scheme_builder, "s", data->backend_list->data);
+
+ tmp = data->backend_list;
+ data->backend_list = g_list_remove_link (data->backend_list, tmp);
+ g_list_free_full (tmp, g_free);
+
+ g_dbus_connection_call (G_DBUS_CONNECTION (g_object_ref (source_object)),
+ MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ "DevicesGet",
+ g_variant_new ("(iiasas)",
+ 0,
+ 0,
+ &include_scheme_builder,
+ NULL),
+ G_VARIANT_TYPE ("(sa{ss})"),
+ G_DBUS_CALL_FLAGS_NONE,
+ DBUS_TIMEOUT,
+ data->cancellable,
+ get_device_attributes_async_dbus_cb,
+ user_data);
+ return;
+ }
+ }
+
+ g_object_unref (source_object);
+
+ if (data->backend_list)
+ {
+ g_list_free_full (data->backend_list, g_free);
+ data->backend_list = NULL;
+ }
+
+ data->callback (device_id,
+ device_make_and_model,
+ data->device_uri,
+ data->user_data);
+
+ if (data->cancellable)
+ g_object_unref (data->cancellable);
+ g_free (data->device_uri);
+ g_free (data->printer_name);
+ g_free (data);
+}
+
+static void
+get_device_attributes_async_scb (GHashTable *result,
+ gpointer user_data)
+{
+ GDBusConnection *bus;
+ GVariantBuilder include_scheme_builder;
+ IPPAttribute *attr;
+ GDAData *data = (GDAData *) user_data;
+ GError *error = NULL;
+ GList *tmp;
+ gint i;
+ const gchar *backends[] =
+ {"hpfax", "ncp", "beh", "bluetooth", "snmp",
+ "dnssd", "hp", "ipp", "lpd", "parallel",
+ "serial", "socket", "usb", NULL};
+
+ if (result)
+ {
+ attr = g_hash_table_lookup (result, "device-uri");
+ if (attr && attr->attribute_type == IPP_ATTRIBUTE_TYPE_STRING &&
+ attr->num_of_values > 0)
+ data->device_uri = g_strdup (attr->attribute_values[0].string_value);
+ g_hash_table_unref (result);
+ }
+
+ if (g_cancellable_is_cancelled (data->cancellable))
+ goto out;
+
+ if (!data->device_uri)
+ goto out;
+
+ bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (!bus)
+ {
+ g_warning ("Failed to get system bus: %s", error->message);
+ g_error_free (error);
+ goto out;
+ }
+
+ for (i = 0; backends[i]; i++)
+ data->backend_list = g_list_prepend (data->backend_list, g_strdup (backends[i]));
+
+ g_variant_builder_init (&include_scheme_builder, G_VARIANT_TYPE ("as"));
+ g_variant_builder_add (&include_scheme_builder, "s", data->backend_list->data);
+
+ tmp = data->backend_list;
+ data->backend_list = g_list_remove_link (data->backend_list, tmp);
+ g_list_free_full (tmp, g_free);
+
+ g_dbus_connection_call (g_object_ref (bus),
+ MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ "DevicesGet",
+ g_variant_new ("(iiasas)",
+ 0,
+ 0,
+ &include_scheme_builder,
+ NULL),
+ G_VARIANT_TYPE ("(sa{ss})"),
+ G_DBUS_CALL_FLAGS_NONE,
+ DBUS_TIMEOUT,
+ data->cancellable,
+ get_device_attributes_async_dbus_cb,
+ data);
+
+ return;
+
+out:
+ data->callback (NULL, NULL, NULL, data->user_data);
+
+ if (data->cancellable)
+ g_object_unref (data->cancellable);
+ g_free (data->device_uri);
+ g_free (data->printer_name);
+ g_free (data);
+}
+
+/*
+ * Get device-id, device-make-and-model and device-uri for given printer.
+ */
+static void
+get_device_attributes_async (const gchar *printer_name,
+ GCancellable *cancellable,
+ GDACallback callback,
+ gpointer user_data)
+{
+ GDAData *data;
+ gchar **attributes;
+
+ if (!printer_name)
+ {
+ callback (NULL, NULL, NULL, user_data);
+
+ return;
+ }
+
+ data = g_new0 (GDAData, 1);
+ data->printer_name = g_strdup (printer_name);
+ if (cancellable)
+ data->cancellable = g_object_ref (cancellable);
+ data->callback = callback;
+ data->user_data = user_data;
+
+ attributes = g_new0 (gchar *, 2);
+ attributes[0] = g_strdup ("device-uri");
+
+ get_ipp_attributes_async (printer_name,
+ attributes,
+ get_device_attributes_async_scb,
+ data);
+
+ g_strfreev (attributes);
+}
+
+/*
+ * Return "count" best matching driver names for given printer.
+ */
+void
+get_ppd_names_async (gchar *printer_name,
+ gint count,
+ GCancellable *cancellable,
+ GPNCallback callback,
+ gpointer user_data)
+{
+ GPNData *data;
+
+ if (!printer_name)
+ {
+ callback (NULL, NULL, TRUE, user_data);
+ return;
+ }
+
+ data = g_new0 (GPNData, 1);
+ data->printer_name = g_strdup (printer_name);
+ data->count = count;
+ if (cancellable)
+ data->cancellable = g_object_ref (cancellable);
+ data->callback = callback;
+ data->user_data = user_data;
+
+ /*
+ * We have to find out device-id for this printer at first.
+ */
+ get_device_attributes_async (printer_name,
+ cancellable,
+ get_device_attributes_cb,
+ data);
+}
+
+typedef struct
+{
+ PPDList *result;
+ GAPCallback callback;
+ gpointer user_data;
+ GMainContext *context;
+} GAPData;
+
+static gboolean
+get_all_ppds_idle_cb (gpointer user_data)
+{
+ GAPData *data = (GAPData *) user_data;
+
+ data->callback (data->result, data->user_data);
+
+ return FALSE;
+}
+
+static void
+get_all_ppds_data_free (gpointer user_data)
+{
+ GAPData *data = (GAPData *) user_data;
+
+ if (data->context)
+ g_main_context_unref (data->context);
+ g_free (data);
+}
+
+static void
+get_all_ppds_cb (gpointer user_data)
+{
+ GAPData *data = (GAPData *) user_data;
+ GSource *idle_source;
+
+ idle_source = g_idle_source_new ();
+ g_source_set_callback (idle_source,
+ get_all_ppds_idle_cb,
+ data,
+ get_all_ppds_data_free);
+ g_source_attach (idle_source, data->context);
+ g_source_unref (idle_source);
+}
+
+static const struct {
+ const char *normalized_name;
+ const char *display_name;
+} manufacturers_names[] = {
+ { "apollo", "Apollo" },
+ { "brother", "Brother" },
+ { "canon", "Canon" },
+ { "dell", "Dell" },
+ { "epson", "Epson" },
+ { "gelsprinter", "GelSprinter" },
+ { "generic", "Generic" },
+ { "gestetner", "Gestetner" },
+ { "hewlett packard", "Hewlett-Packard" },
+ { "hp", "Hewlett-Packard" },
+ { "ibm", "IBM" },
+ { "imagistics", "Imagistics" },
+ { "infoprint", "InfoPrint" },
+ { "infotec", "Infotec" },
+ { "konica minolta", "Minolta" },
+ { "kyocera", "Kyocera" },
+ { "kyocera mita", "Kyocera" },
+ { "lanier", "Lanier" },
+ { "lexmark international", "Lexmark" },
+ { "lexmark", "Lexmark" },
+ { "minolta", "Minolta" },
+ { "minolta qms", "Minolta" },
+ { "nec", "NEC" },
+ { "nrg", "NRG" },
+ { "oce", "Oce" },
+ { "oki", "Oki" },
+ { "oki data corp", "Oki" },
+ { "panasonic", "Panasonic" },
+ { "ricoh", "Ricoh" },
+ { "samsung", "Samsung" },
+ { "savin", "Savin" },
+ { "sharp", "Sharp" },
+ { "sony", "Sony" },
+ { "tektronix", "Tektronix" },
+ { "toshiba tec corp.", "Toshiba" },
+ { "xerox", "Xerox" },
+};
+
+static gpointer
+get_all_ppds_func (gpointer user_data)
+{
+ ipp_attribute_t *attr;
+ GHashTable *ppds_hash = NULL;
+ GHashTable *manufacturers_hash = NULL;
+ GAPData *data = (GAPData *) user_data;
+ PPDName *item;
+ ipp_t *request;
+ ipp_t *response;
+ GList *list;
+ gchar *ppd_make_and_model;
+ gchar *ppd_device_id;
+ gchar *ppd_name;
+ gchar *ppd_product;
+ gchar *mfg;
+ gchar *mfg_normalized;
+ gchar *mdl;
+ gchar *manufacturer_display_name;
+ gint i, j;
+
+ request = ippNewRequest (CUPS_GET_PPDS);
+ response = cupsDoRequest (CUPS_HTTP_DEFAULT, request, "/");
+
+ if (response &&
+ response->request.status.status_code <= IPP_OK_CONFLICT)
+ {
+ /*
+ * This hash contains names of manufacturers as keys and
+ * values are GLists of PPD names.
+ */
+ ppds_hash = g_hash_table_new (g_str_hash, g_str_equal);
+
+ /*
+ * This hash contains all possible names of manufacturers as keys
+ * and values are just first occurences of their equivalents.
+ * This is for mapping of e.g. "Hewlett Packard" and "HP" to the same name
+ * (the one which comes first).
+ */
+ manufacturers_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+ for (i = 0; i < G_N_ELEMENTS (manufacturers_names); i++)
+ {
+ g_hash_table_insert (manufacturers_hash,
+ g_strdup (manufacturers_names[i].normalized_name),
+ g_strdup (manufacturers_names[i].display_name));
+ }
+
+ for (attr = response->attrs; attr != NULL; attr = attr->next)
+ {
+ while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
+ attr = attr->next;
+
+ if (attr == NULL)
+ break;
+
+ ppd_device_id = NULL;
+ ppd_make_and_model = NULL;
+ ppd_name = NULL;
+ ppd_product = NULL;
+
+ while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
+ {
+ if (g_strcmp0 (attr->name, "ppd-device-id") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ ppd_device_id = attr->values[0].string.text;
+ else if (g_strcmp0 (attr->name, "ppd-make-and-model") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ ppd_make_and_model = attr->values[0].string.text;
+ else if (g_strcmp0 (attr->name, "ppd-name") == 0 &&
+ attr->value_tag == IPP_TAG_NAME)
+ ppd_name = attr->values[0].string.text;
+ else if (g_strcmp0 (attr->name, "ppd-product") == 0 &&
+ attr->value_tag == IPP_TAG_TEXT)
+ ppd_product = attr->values[0].string.text;
+
+ attr = attr->next;
+ }
+
+ if (ppd_make_and_model && ppd_name && ppd_product && ppd_device_id)
+ {
+ mfg = get_tag_value (ppd_device_id, "mfg");
+ if (!mfg)
+ mfg = get_tag_value (ppd_device_id, "manufacturer");
+ mfg_normalized = normalize (mfg);
+
+ if (mfg_normalized)
+ {
+ manufacturer_display_name = g_hash_table_lookup (manufacturers_hash, mfg_normalized);
+ if (!manufacturer_display_name)
+ {
+ g_hash_table_insert (manufacturers_hash, mfg_normalized, mfg);
+ }
+ else
+ {
+ g_free (mfg_normalized);
+ mfg_normalized = normalize (manufacturer_display_name);
+ }
+
+ item = g_new0 (PPDName, 1);
+ item->ppd_name = g_strdup (ppd_name);
+
+ mdl = get_tag_value (ppd_device_id, "mdl");
+ if (!mdl)
+ mdl = get_tag_value (ppd_device_id, "model");
+
+ if (!item->ppd_display_name &&
+ ppd_make_and_model &&
+ ppd_make_and_model[0] != '\0')
+ {
+ item->ppd_display_name = g_strdup (ppd_make_and_model);
+ }
+
+ if (!item->ppd_display_name &&
+ ppd_product &&
+ ppd_product[0] != '\0')
+ {
+ item->ppd_display_name = g_strdup (ppd_product);
+ }
+
+ if (!item->ppd_display_name &&
+ mdl && mdl[0] != '\0')
+ {
+ item->ppd_display_name = mdl;
+ }
+ else
+ {
+ g_free (mdl);
+ }
+
+ item->ppd_match_level = -1;
+
+ list = g_hash_table_lookup (ppds_hash, mfg_normalized);
+ if (list)
+ {
+ list = g_list_append (list, item);
+ }
+ else
+ {
+ list = g_list_append (list, item);
+ g_hash_table_insert (ppds_hash, mfg_normalized, list);
+ }
+ }
+ }
+
+ if (attr == NULL)
+ break;
+ }
+ }
+
+ if (response)
+ ippDelete(response);
+
+ if (ppds_hash &&
+ manufacturers_hash)
+ {
+ GHashTableIter iter;
+ gpointer key;
+ gpointer value;
+ GList *ppd_item;
+ GList *sort_list = NULL;
+ GList *list_iter;
+ gchar *name;
+
+ data->result = g_new0 (PPDList, 1);
+ data->result->num_of_manufacturers = g_hash_table_size (ppds_hash);
+ data->result->manufacturers = g_new0 (PPDManufacturerItem *, data->result->num_of_manufacturers);
+
+ g_hash_table_iter_init (&iter, ppds_hash);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ sort_list = g_list_append (sort_list, g_strdup (key));
+ }
+
+ /* Sort list of manufacturers */
+ sort_list = g_list_sort (sort_list, (GCompareFunc) g_strcmp0);
+
+ /*
+ * Fill resulting list of lists (list of manufacturers where
+ * each item contains list of PPD names)
+ */
+ i = 0;
+ for (list_iter = sort_list; list_iter; list_iter = list_iter->next)
+ {
+ name = (gchar *) list_iter->data;
+ value = g_hash_table_lookup (ppds_hash, name);
+
+ data->result->manufacturers[i] = g_new0 (PPDManufacturerItem, 1);
+ data->result->manufacturers[i]->manufacturer_name = g_strdup (name);
+ data->result->manufacturers[i]->manufacturer_display_name = g_strdup (g_hash_table_lookup (manufacturers_hash, name));
+ data->result->manufacturers[i]->num_of_ppds = g_list_length ((GList *) value);
+ data->result->manufacturers[i]->ppds = g_new0 (PPDName *, data->result->manufacturers[i]->num_of_ppds);
+
+ for (ppd_item = (GList *) value, j = 0; ppd_item; ppd_item = ppd_item->next, j++)
+ {
+ data->result->manufacturers[i]->ppds[j] = ppd_item->data;
+ }
+
+ g_list_free ((GList *) value);
+
+ i++;
+ }
+
+ g_list_free_full (sort_list, g_free);
+ g_hash_table_destroy (ppds_hash);
+ g_hash_table_destroy (manufacturers_hash);
+ }
+
+ get_all_ppds_cb (data);
+
+ return NULL;
+}
+
+/*
+ * Get names of all installed PPDs sorted by manufacturers names.
+ */
+void
+get_all_ppds_async (GAPCallback callback,
+ gpointer user_data)
+{
+ GAPData *data;
+ GThread *thread;
+ GError *error = NULL;
+
+ data = g_new0 (GAPData, 1);
+ data->callback = callback;
+ data->user_data = user_data;
+ data->context = g_main_context_ref_thread_default ();
+
+ thread = g_thread_try_new ("get-all-ppds",
+ get_all_ppds_func,
+ data,
+ &error);
+
+ if (!thread)
+ {
+ g_warning ("%s", error->message);
+ callback (NULL, user_data);
+
+ g_error_free (error);
+ get_all_ppds_data_free (data);
+ }
+ else
+ {
+ g_thread_unref (thread);
+ }
+}
+
+PPDList *
+ppd_list_copy (PPDList *list)
+{
+ PPDList *result = NULL;
+ gint i, j;
+
+ if (list)
+ {
+ result = g_new0 (PPDList, 1);
+ result->num_of_manufacturers = list->num_of_manufacturers;
+ result->manufacturers = g_new0 (PPDManufacturerItem *, list->num_of_manufacturers);
+
+ for (i = 0; i < result->num_of_manufacturers; i++)
+ {
+ result->manufacturers[i] = g_new0 (PPDManufacturerItem, 1);
+ result->manufacturers[i]->num_of_ppds = list->manufacturers[i]->num_of_ppds;
+ result->manufacturers[i]->ppds = g_new0 (PPDName *, result->manufacturers[i]->num_of_ppds);
+
+ result->manufacturers[i]->manufacturer_display_name =
+ g_strdup (list->manufacturers[i]->manufacturer_display_name);
+
+ result->manufacturers[i]->manufacturer_name =
+ g_strdup (list->manufacturers[i]->manufacturer_name);
+
+ for (j = 0; j < result->manufacturers[i]->num_of_ppds; j++)
+ {
+ result->manufacturers[i]->ppds[j] = g_new0 (PPDName, 1);
+
+ result->manufacturers[i]->ppds[j]->ppd_display_name =
+ g_strdup (list->manufacturers[i]->ppds[j]->ppd_display_name);
+
+ result->manufacturers[i]->ppds[j]->ppd_name =
+ g_strdup (list->manufacturers[i]->ppds[j]->ppd_name);
+
+ result->manufacturers[i]->ppds[j]->ppd_match_level =
+ list->manufacturers[i]->ppds[j]->ppd_match_level;
+ }
+ }
+ }
+
+ return result;
+}
+
+void
+ppd_list_free (PPDList *list)
+{
+ gint i, j;
+
+ if (list)
+ {
+ for (i = 0; i < list->num_of_manufacturers; i++)
+ {
+ for (j = 0; j < list->manufacturers[i]->num_of_ppds; j++)
+ {
+ g_free (list->manufacturers[i]->ppds[j]->ppd_name);
+ g_free (list->manufacturers[i]->ppds[j]->ppd_display_name);
+ g_free (list->manufacturers[i]->ppds[j]);
+ }
+
+ g_free (list->manufacturers[i]->manufacturer_name);
+ g_free (list->manufacturers[i]->manufacturer_display_name);
+ g_free (list->manufacturers[i]->ppds);
+ g_free (list->manufacturers[i]);
+ }
+
+ g_free (list->manufacturers);
+ g_free (list);
+ }
+}
+
+gchar *
+get_standard_manufacturers_name (gchar *name)
+{
+ gchar *normalized_name;
+ gchar *result = NULL;
+ gint i;
+
+ if (name)
+ {
+ normalized_name = normalize (name);
+
+ for (i = 0; i < G_N_ELEMENTS (manufacturers_names); i++)
+ {
+ if (g_strcmp0 (manufacturers_names[i].normalized_name, normalized_name) == 0)
+ {
+ result = g_strdup (manufacturers_names[i].display_name);
+ break;
+ }
+ }
+
+ g_free (normalized_name);
+ }
+
+ return result;
+}
diff --git a/panels/printers/pp-utils.h b/panels/printers/pp-utils.h
index f5ea27f..8eeaced 100644
--- a/panels/printers/pp-utils.h
+++ b/panels/printers/pp-utils.h
@@ -40,9 +40,24 @@ enum
typedef struct
{
gchar *ppd_name;
+ gchar *ppd_display_name;
gint ppd_match_level;
} PPDName;
+typedef struct
+{
+ gchar *manufacturer_name;
+ gchar *manufacturer_display_name;
+ PPDName **ppds;
+ gsize num_of_ppds;
+} PPDManufacturerItem;
+
+typedef struct
+{
+ PPDManufacturerItem **manufacturers;
+ gsize num_of_manufacturers;
+} PPDList;
+
gchar *get_tag_value (const gchar *tag_string,
const gchar *tag_name);
@@ -116,6 +131,81 @@ gchar *printer_get_hostname (cups_ptype_t printer_type,
void printer_set_default_media_size (const gchar *printer_name);
+typedef void (*PSPCallback) (gchar *printer_name,
+ gboolean success,
+ gpointer user_data);
+
+void printer_set_ppd_async (const gchar *printer_name,
+ const gchar *ppd_name,
+ GCancellable *cancellable,
+ PSPCallback callback,
+ gpointer user_data);
+
+void printer_set_ppd_file_async (const gchar *printer_name,
+ const gchar *ppd_filename,
+ GCancellable *cancellable,
+ PSPCallback callback,
+ gpointer user_data);
+
+typedef void (*GPNCallback) (PPDName **names,
+ const gchar *printer_name,
+ gboolean cancelled,
+ gpointer user_data);
+
+void get_ppd_names_async (gchar *printer_name,
+ gint count,
+ GCancellable *cancellable,
+ GPNCallback callback,
+ gpointer user_data);
+
+typedef void (*GAPCallback) (PPDList *ppds,
+ gpointer user_data);
+
+void get_all_ppds_async (GAPCallback callback,
+ gpointer user_data);
+
+PPDList *ppd_list_copy (PPDList *list);
+void ppd_list_free (PPDList *list);
+
+enum
+{
+ IPP_ATTRIBUTE_TYPE_INTEGER = 0,
+ IPP_ATTRIBUTE_TYPE_STRING,
+ IPP_ATTRIBUTE_TYPE_RANGE,
+ IPP_ATTRIBUTE_TYPE_BOOLEAN
+};
+
+typedef struct
+{
+ gboolean boolean_value;
+ gchar *string_value;
+ gint integer_value;
+ gint lower_range;
+ gint upper_range;
+} IPPAttributeValue;
+
+typedef struct
+{
+ gchar *attribute_name;
+ IPPAttributeValue *attribute_values;
+ gint num_of_values;
+ gint attribute_type;
+} IPPAttribute;
+
+typedef void (*GIACallback) (GHashTable *table,
+ gpointer user_data);
+
+void get_ipp_attributes_async (const gchar *printer_name,
+ gchar **attributes_names,
+ GIACallback callback,
+ gpointer user_data);
+
+IPPAttribute *ipp_attribute_copy (IPPAttribute *attr);
+
+void ipp_attribute_free (IPPAttribute *attr);
+
+gchar *get_standard_manufacturers_name (gchar *name);
+
G_END_DECLS
#endif /* __PP_UTILS_H */
diff --git a/panels/printers/ppd-selection-dialog.ui b/panels/printers/ppd-selection-dialog.ui
new file mode 100644
index 0000000..e0def83
--- /dev/null
+++ b/panels/printers/ppd-selection-dialog.ui
@@ -0,0 +1,206 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkDialog" id="ppd-selection-dialog">
+ <property name="width_request">500</property>
+ <property name="height_request">350</property>
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes"> </property>
+ <property name="modal">True</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="main-vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">10</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action-area1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ppd-selection-cancel-button">
+ <property name="label" translatable="yes">Cancel</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="ppd-selection-select-button">
+ <property name="label" translatable="yes">Select</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkAlignment" id="alignment1">
+ <property name="width_request">24</property>
+ <property name="height_request">24</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkSpinner" id="ppd-spinner">
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="progress-label">
+ <property name="can_focus">False</property>
+ <property name="no_show_all">True</property>
+ <property name="label" translatable="yes">Loading drivers database...</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="padding">10</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkAlignment" id="content-alignment">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox" id="box1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">10</property>
+ <child>
+ <object class="GtkBox" id="box3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
+ <property name="width_request">140</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="ppd-selection-manufacturers-treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow2">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="ppd-selection-models-treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection1"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="options-title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Select Printer Driver</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">ppd-selection-cancel-button</action-widget>
+ <action-widget response="-5">ppd-selection-select-button</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkSizeGroup" id="sizegroup1"/>
+</interface>
diff --git a/panels/printers/printers.ui b/panels/printers/printers.ui
index ff9157c..75b1483 100644
--- a/panels/printers/printers.ui
+++ b/panels/printers/printers.ui
@@ -315,17 +315,84 @@
</packing>
</child>
<child>
- <object class="CcEditableEntry" id="printer-model-label">
+ <object class="GtkNotebook" id="printer-model-notebook">
<property name="visible">True</property>
- <property name="xalign">0</property>
- <property name="text">---</property>
+ <property name="can_focus">True</property>
+ <property name="show_tabs">False</property>
+ <child>
+ <object class="GtkButton" id="printer-model-button">
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="relief">none</property>
+ <property name="xalign">0</property>
+ </object>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">page 1</property>
+ </object>
+ <packing>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="printer-model-label">
+ <property name="visible">True</property>
+ <property name="selectable">True</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">label</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label13">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">page 2</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label17">
+ <property name="visible">True</property>
+ <property name="sensitive">False</property>
+ <property name="can_focus">False</property>
+ <property name="xalign">0</property>
+ <property name="xpad">10</property>
+ <property name="label" translatable="yes">Setting new driver...</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child type="tab">
+ <object class="GtkLabel" id="label16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">page 3</property>
+ </object>
+ <packing>
+ <property name="position">2</property>
+ <property name="tab_fill">False</property>
+ </packing>
+ </child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
- <property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]