[gnome-control-center] printer: Add "Options" dialog
- From: Marek KaÅÃk <mkasik src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-control-center] printer: Add "Options" dialog
- Date: Mon, 16 Jul 2012 13:26:49 +0000 (UTC)
commit 7ae3027b472422cdae47c09dd0420b297dacf089
Author: Marek Kasik <mkasik redhat com>
Date: Mon Jul 16 15:25:10 2012 +0200
printer: Add "Options" dialog
Add Options dialog which allows users to set more options than current dialog.
The dialog reads printer's PPD file and add its options to the dialog together
with some preselected IPP options (#678637).
All operations in the dialog are asynchronous.
During implementation of this, the option for setting allowed users was removed
because this is not suitable for this panel (the option is intended for
administrators).
panels/printers/Makefile.am | 7 +
panels/printers/cc-printers-panel.c | 343 ++----------
panels/printers/options-dialog.ui | 182 ++++++
panels/printers/pp-ipp-option-widget.c | 605 ++++++++++++++++++++
panels/printers/pp-ipp-option-widget.h | 70 +++
panels/printers/pp-options-dialog.c | 984 ++++++++++++++++++++++++++++++++
panels/printers/pp-options-dialog.h | 42 ++
panels/printers/pp-ppd-option-widget.c | 624 ++++++++++++++++++++
panels/printers/pp-ppd-option-widget.h | 63 ++
panels/printers/pp-utils.c | 285 +++++++++
panels/printers/pp-utils.h | 25 +
11 files changed, 2937 insertions(+), 293 deletions(-)
---
diff --git a/panels/printers/Makefile.am b/panels/printers/Makefile.am
index dab5ae5..249ce69 100644
--- a/panels/printers/Makefile.am
+++ b/panels/printers/Makefile.am
@@ -4,6 +4,7 @@ uidir = $(pkgdatadir)/ui/printers
dist_ui_DATA = \
new-printer-dialog.ui \
ppd-selection-dialog.ui \
+ options-dialog.ui \
printers.ui
INCLUDES = \
@@ -22,10 +23,16 @@ libprinters_la_SOURCES = \
printers-module.c \
pp-utils.c \
pp-utils.h \
+ pp-ppd-option-widget.c \
+ pp-ppd-option-widget.h \
+ pp-ipp-option-widget.c \
+ pp-ipp-option-widget.h \
pp-new-printer-dialog.c \
pp-new-printer-dialog.h \
pp-ppd-selection-dialog.c \
pp-ppd-selection-dialog.h \
+ pp-options-dialog.c \
+ pp-options-dialog.h \
cc-printers-panel.c \
cc-printers-panel.h
diff --git a/panels/printers/cc-printers-panel.c b/panels/printers/cc-printers-panel.c
index 63fc803..72cbc9a 100644
--- a/panels/printers/cc-printers-panel.c
+++ b/panels/printers/cc-printers-panel.c
@@ -34,6 +34,7 @@
#include "cc-editable-entry.h"
#include "pp-new-printer-dialog.h"
#include "pp-ppd-selection-dialog.h"
+#include "pp-options-dialog.h"
#include "pp-utils.h"
G_DEFINE_DYNAMIC_TYPE (CcPrintersPanel, cc_printers_panel, CC_TYPE_PANEL)
@@ -73,10 +74,6 @@ struct _CcPrintersPanelPrivate
int num_jobs;
int current_job;
- gchar **allowed_users;
- int num_allowed_users;
- int current_allowed_user;
-
GdkRGBA background_color;
GPermission *permission;
@@ -85,6 +82,7 @@ struct _CcPrintersPanelPrivate
PpNewPrinterDialog *pp_new_printer_dialog;
PpPPDSelectionDialog *pp_ppd_selection_dialog;
+ PpOptionsDialog *pp_options_dialog;
GDBusProxy *cups_proxy;
GDBusConnection *cups_bus_connection;
@@ -106,7 +104,6 @@ struct _CcPrintersPanelPrivate
static void actualize_jobs_list (CcPrintersPanel *self);
static void actualize_printers_list (CcPrintersPanel *self);
-static void actualize_allowed_users_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);
@@ -143,7 +140,6 @@ static void
cc_printers_panel_dispose (GObject *object)
{
CcPrintersPanelPrivate *priv = CC_PRINTERS_PANEL (object)->priv;
- int i;
if (priv->pp_new_printer_dialog)
{
@@ -159,16 +155,6 @@ cc_printers_panel_dispose (GObject *object)
priv->num_jobs = 0;
priv->current_job = -1;
- if (priv->num_allowed_users > 0)
- {
- for (i = 0; i < priv->num_allowed_users; i++)
- g_free (priv->allowed_users[i]);
- g_free (priv->allowed_users);
- }
- priv->allowed_users = NULL;
- priv->num_allowed_users = 0;
- priv->current_allowed_user = -1;
-
if (priv->builder)
{
g_object_unref (priv->builder);
@@ -224,12 +210,6 @@ cc_printers_panel_get_permission (CcPanel *panel)
return priv->permission;
}
-static const char *
-cc_printers_panel_get_help_uri (CcPanel *panel)
-{
- return "help:gnome-help/printing";
-}
-
static void
cc_printers_panel_class_init (CcPrintersPanelClass *klass)
{
@@ -244,7 +224,6 @@ cc_printers_panel_class_init (CcPrintersPanelClass *klass)
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
@@ -646,8 +625,6 @@ printer_selection_changed_cb (GtkTreeSelection *selection,
actualize_jobs_list (self);
}
- actualize_allowed_users_list (self);
-
if (priv->current_dest >= 0 &&
priv->current_dest < priv->num_dests &&
priv->dests != NULL)
@@ -1499,108 +1476,6 @@ populate_jobs_list (CcPrintersPanel *self)
actualize_jobs_list (self);
}
-enum
-{
- ALLOWED_USERS_ID_COLUMN,
- ALLOWED_USERS_NAME_COLUMN,
- ALLOWED_USERS_N_COLUMNS
-};
-
-static void
-actualize_allowed_users_list (CcPrintersPanel *self)
-{
- CcPrintersPanelPrivate *priv;
- GtkListStore *store;
- GtkTreeView *treeview;
- GtkTreeIter iter;
- int i;
-
- priv = PRINTERS_PANEL_PRIVATE (self);
-
- treeview = (GtkTreeView*)
- gtk_builder_get_object (priv->builder, "allowed-users-treeview");
-
- if (priv->allowed_users)
- {
- for (i = 0; i < priv->num_allowed_users; i++)
- g_free (priv->allowed_users[i]);
- g_free (priv->allowed_users);
- priv->allowed_users = NULL;
- priv->num_allowed_users = 0;
- }
-
- priv->current_allowed_user = -1;
-
- if (priv->current_dest >= 0 &&
- priv->current_dest < priv->num_dests &&
- priv->dests != NULL)
- priv->num_allowed_users = ccGetAllowedUsers (&priv->allowed_users, priv->dests[priv->current_dest].name);
-
- store = gtk_list_store_new (ALLOWED_USERS_N_COLUMNS, G_TYPE_INT, G_TYPE_STRING);
-
- for (i = 0; i < priv->num_allowed_users; i++)
- {
- gtk_list_store_append (store, &iter);
- gtk_list_store_set (store, &iter,
- ALLOWED_USERS_ID_COLUMN, i,
- ALLOWED_USERS_NAME_COLUMN, priv->allowed_users[i],
- -1);
- }
-
- gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (store));
- g_object_unref (store);
-}
-
-static void
-allowed_users_selection_changed_cb (GtkTreeSelection *selection,
- gpointer user_data)
-{
- CcPrintersPanelPrivate *priv;
- CcPrintersPanel *self = (CcPrintersPanel*) user_data;
- GtkTreeModel *model;
- GtkTreeIter iter;
- int id = -1;
-
- priv = PRINTERS_PANEL_PRIVATE (self);
-
- if (gtk_tree_selection_get_selected (selection, &model, &iter))
- gtk_tree_model_get (model, &iter,
- ALLOWED_USERS_ID_COLUMN, &id,
- -1);
- else
- id = -1;
-
- priv->current_allowed_user = id;
-}
-
-static void
-populate_allowed_users_list (CcPrintersPanel *self)
-{
-
- CcPrintersPanelPrivate *priv;
- GtkTreeViewColumn *column;
- GtkCellRenderer *renderer;
- GtkTreeView *treeview;
-
- priv = PRINTERS_PANEL_PRIVATE (self);
-
- actualize_allowed_users_list (self);
-
- treeview = (GtkTreeView*)
- gtk_builder_get_object (priv->builder, "allowed-users-treeview");
-
- gtk_tree_view_set_headers_visible (treeview, FALSE);
-
- renderer = gtk_cell_renderer_text_new ();
-
- column = gtk_tree_view_column_new_with_attributes (NULL, renderer,
- "text", ALLOWED_USERS_NAME_COLUMN, NULL);
- gtk_tree_view_append_column (treeview, column);
-
- g_signal_connect (gtk_tree_view_get_selection (treeview),
- "changed", G_CALLBACK (allowed_users_selection_changed_cb), self);
-}
-
static void
job_process_cb (GtkButton *button,
gpointer user_data)
@@ -1933,134 +1808,6 @@ supply_levels_draw_cb (GtkWidget *widget,
}
static void
-allowed_user_remove_cb (GtkToolButton *button,
- gpointer user_data)
-{
- CcPrintersPanelPrivate *priv;
- CcPrintersPanel *self = (CcPrintersPanel*) user_data;
- char *printer_name = NULL;
- char **names = NULL;
- char *name = NULL;
- int i, j;
-
- priv = PRINTERS_PANEL_PRIVATE (self);
-
- if (priv->current_allowed_user >= 0 &&
- priv->current_allowed_user < priv->num_allowed_users &&
- priv->allowed_users != NULL)
- name = priv->allowed_users[priv->current_allowed_user];
-
- if (priv->current_dest >= 0 &&
- priv->current_dest < priv->num_dests &&
- priv->dests != NULL)
- printer_name = priv->dests[priv->current_dest].name;
-
- if (name && printer_name)
- {
- names = g_new0 (gchar*, priv->num_allowed_users);
- j = 0;
- for (i = 0; i < (priv->num_allowed_users); i++)
- {
- if (i != priv->current_allowed_user)
- {
- names[j] = priv->allowed_users[i];
- j++;
- }
- }
-
- printer_set_users (printer_name, names, TRUE);
- actualize_allowed_users_list (self);
-
- g_free (names);
- }
-}
-
-static void
-allowed_user_add_cb (GtkCellRendererText *renderer,
- gchar *path,
- gchar *new_text,
- gpointer user_data)
-{
- CcPrintersPanelPrivate *priv;
- CcPrintersPanel *self = (CcPrintersPanel*) user_data;
- char *printer_name = NULL;
- char **names = NULL;
- int i;
-
- priv = PRINTERS_PANEL_PRIVATE (self);
-
- g_signal_handlers_disconnect_by_func (G_OBJECT (renderer),
- allowed_user_add_cb,
- self);
- g_object_set (G_OBJECT (renderer), "editable", FALSE, NULL);
-
- if (priv->current_dest >= 0 &&
- priv->current_dest < priv->num_dests &&
- priv->dests != NULL)
- printer_name = priv->dests[priv->current_dest].name;
-
- if (new_text && new_text[0] != '\0' && printer_name)
- {
- names = g_new0 (char *, priv->num_allowed_users + 2);
- for (i = 0; i < (priv->num_allowed_users); i++)
- names[i] = priv->allowed_users[i];
- names[priv->num_allowed_users] = new_text;
-
- printer_set_users (printer_name, names, TRUE);
-
- g_free (names);
- }
-
- actualize_allowed_users_list (self);
-}
-
-static void
-allowed_user_add_button_cb (GtkToolButton *button,
- gpointer user_data)
-{
- CcPrintersPanelPrivate *priv;
- GtkTreeViewColumn *column;
- CcPrintersPanel *self = (CcPrintersPanel*) user_data;
- GtkListStore *liststore;
- GtkTreeView *treeview;
- GtkTreeIter iter;
- GtkTreePath *path;
- GList *renderers;
-
- priv = PRINTERS_PANEL_PRIVATE (self);
-
- treeview = (GtkTreeView*)
- gtk_builder_get_object (priv->builder, "allowed-users-treeview");
-
- liststore = (GtkListStore*)
- gtk_tree_view_get_model (treeview);
-
- gtk_list_store_prepend (liststore, &iter);
- column = gtk_tree_view_get_column (treeview, 0);
- path = gtk_tree_model_get_path (GTK_TREE_MODEL (liststore), &iter);
- renderers = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (column));
-
- if (column && renderers)
- {
- g_signal_connect (G_OBJECT (renderers->data),
- "edited",
- G_CALLBACK (allowed_user_add_cb),
- self);
-
- g_object_set (renderers->data, "editable", TRUE, NULL);
- gtk_widget_grab_focus (GTK_WIDGET (treeview));
- gtk_tree_view_set_cursor_on_cell (treeview,
- path,
- column,
- GTK_CELL_RENDERER (renderers->data),
- TRUE);
- }
-
- g_list_free (renderers);
- gtk_tree_path_free (path);
-}
-
-static void
printer_set_default_cb (GtkToggleButton *button,
gpointer user_data)
{
@@ -2888,17 +2635,12 @@ update_sensitivity (gpointer user_data)
widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-default-check-button");
gtk_widget_set_sensitive (widget, is_authorized);
- widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "allowed-user-add-button");
- gtk_widget_set_sensitive (widget, local_server && !is_discovered && is_authorized);
-
- widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "allowed-user-remove-button");
- gtk_widget_set_sensitive (widget, local_server && !is_discovered && is_authorized);
-
widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "print-test-page-button");
gtk_widget_set_sensitive (widget, printer_selected);
widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-options-button");
- gtk_widget_set_sensitive (widget, printer_selected);
+ gtk_widget_set_sensitive (widget, printer_selected && local_server && !is_discovered &&
+ !priv->pp_options_dialog);
widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-jobs-button");
gtk_widget_set_sensitive (widget, printer_selected);
@@ -2987,18 +2729,55 @@ switch_to_jobs_cb (GtkButton *button,
}
static void
-switch_to_options_cb (GtkButton *button,
- gpointer user_data)
+printer_options_response_cb (GtkDialog *dialog,
+ gint response_id,
+ gpointer user_data)
{
- CcPrintersPanelPrivate *priv;
- CcPrintersPanel *self = (CcPrintersPanel*) user_data;
- GtkWidget *widget;
+ 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, "notebook");
- gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), NOTEBOOK_OPTIONS_PAGE);
+ 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
@@ -3100,11 +2879,8 @@ cc_printers_panel_init (CcPrintersPanel *self)
priv->num_jobs = 0;
priv->current_job = -1;
- priv->allowed_users = NULL;
- priv->num_allowed_users = 0;
- priv->current_allowed_user = -1;
-
priv->pp_new_printer_dialog = NULL;
+ priv->pp_options_dialog = NULL;
priv->subscription_id = 0;
priv->cups_status_check_id = 0;
@@ -3169,14 +2945,6 @@ cc_printers_panel_init (CcPrintersPanel *self)
g_signal_connect (widget, "notify::active", G_CALLBACK (printer_disable_cb), self);
widget = (GtkWidget*)
- gtk_builder_get_object (priv->builder, "allowed-user-remove-button");
- g_signal_connect (widget, "clicked", G_CALLBACK (allowed_user_remove_cb), self);
-
- widget = (GtkWidget*)
- gtk_builder_get_object (priv->builder, "allowed-user-add-button");
- g_signal_connect (widget, "clicked", G_CALLBACK (allowed_user_add_button_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);
@@ -3202,7 +2970,7 @@ cc_printers_panel_init (CcPrintersPanel *self)
widget = (GtkWidget*)
gtk_builder_get_object (priv->builder, "printer-options-button");
- g_signal_connect (widget, "clicked", G_CALLBACK (switch_to_options_cb), self);
+ g_signal_connect (widget, "clicked", G_CALLBACK (printer_options_cb), self);
widget = (GtkWidget*)
gtk_builder_get_object (priv->builder, "printer-name-label");
@@ -3237,16 +3005,6 @@ cc_printers_panel_init (CcPrintersPanel *self)
gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
widget = (GtkWidget*)
- gtk_builder_get_object (priv->builder, "allowed-users-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, "allowed-users-toolbar");
- context = gtk_widget_get_style_context (widget);
- gtk_style_context_set_junction_sides (context, GTK_JUNCTION_TOP);
-
- widget = (GtkWidget*)
gtk_builder_get_object (priv->builder, "queue-scrolledwindow");
context = gtk_widget_get_style_context (widget);
gtk_style_context_set_junction_sides (context, GTK_JUNCTION_BOTTOM);
@@ -3283,7 +3041,6 @@ Please check your installation");
populate_printers_list (self);
populate_jobs_list (self);
- populate_allowed_users_list (self);
attach_to_cups_notifier (self);
priv->getting_all_ppds = TRUE;
diff --git a/panels/printers/options-dialog.ui b/panels/printers/options-dialog.ui
new file mode 100644
index 0000000..f100335
--- /dev/null
+++ b/panels/printers/options-dialog.ui
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 3.0 -->
+ <object class="GtkDialog" id="options-dialog">
+ <property name="width_request">500</property>
+ <property name="height_request">400</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>
+ <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="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">Options</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolled-window1">
+ <property name="width_request">120</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="options-categories-treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="headers_visible">False</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection1"/>
+ </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="GtkNotebook" id="options-notebook">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="tab_pos">left</property>
+ <property name="show_tabs">False</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</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">0</property>
+ </packing>
+ </child>
+ <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="GtkBox" id="box3">
+ <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="options-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 options...</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">0</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="options-close-button">
+ <property name="label" translatable="yes">Close</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">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="0">options-close-button</action-widget>
+ </action-widgets>
+ </object>
+ <object class="GtkSizeGroup" id="sizegroup1"/>
+</interface>
diff --git a/panels/printers/pp-ipp-option-widget.c b/panels/printers/pp-ipp-option-widget.c
new file mode 100644
index 0000000..8ee3ba3
--- /dev/null
+++ b/panels/printers/pp-ipp-option-widget.c
@@ -0,0 +1,605 @@
+/* -*- 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <glib/gi18n-lib.h>
+
+#include "pp-ipp-option-widget.h"
+#include "pp-utils.h"
+
+#define PP_IPP_OPTION_WIDGET_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), PP_TYPE_IPP_OPTION_WIDGET, PpIPPOptionWidgetPrivate))
+
+static void pp_ipp_option_widget_finalize (GObject *object);
+
+static gboolean construct_widget (PpIPPOptionWidget *widget);
+static void update_widget (PpIPPOptionWidget *widget);
+static void update_widget_real (PpIPPOptionWidget *widget);
+
+struct PpIPPOptionWidgetPrivate
+{
+ GtkWidget *switch_button;
+ GtkWidget *spin_button;
+ GtkWidget *combo;
+ GtkWidget *box;
+
+ IPPAttribute *option_supported;
+ IPPAttribute *option_default;
+
+ gchar *printer_name;
+ gchar *option_name;
+
+ GHashTable *ipp_attribute;
+};
+
+G_DEFINE_TYPE (PpIPPOptionWidget, pp_ipp_option_widget, GTK_TYPE_HBOX)
+
+static const struct {
+ const char *keyword;
+ const char *choice;
+ const char *translation;
+} ipp_choice_translations[] = {
+ /* Translators: this is an option of "Two Sided" */
+ { "sides", "one-sided", N_("One Sided") },
+ /* Translators: this is an option of "Two Sided" */
+ { "sides", "two-sided-long-edge", N_("Long Edge (Standard)") },
+ /* Translators: this is an option of "Two Sided" */
+ { "sides", "two-sided-short-edge", N_("Short Edge (Flip)") },
+ /* Translators: this is an option of "Orientation" */
+ { "orientation-requested", "3", N_("Portrait") },
+ /* Translators: this is an option of "Orientation" */
+ { "orientation-requested", "4", N_("Landscape") },
+ /* Translators: this is an option of "Orientation" */
+ { "orientation-requested", "5", N_("Reverse landscape") },
+ /* Translators: this is an option of "Orientation" */
+ { "orientation-requested", "6", N_("Reverse portrait") },
+};
+
+static const gchar *
+ipp_choice_translate (const gchar *option,
+ const gchar *choice)
+{
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (ipp_choice_translations); i++)
+ {
+ if (g_strcmp0 (ipp_choice_translations[i].keyword, option) == 0 &&
+ g_strcmp0 (ipp_choice_translations[i].choice, choice) == 0)
+ return _(ipp_choice_translations[i].translation);
+ }
+
+ return choice;
+}
+
+static void
+pp_ipp_option_widget_class_init (PpIPPOptionWidgetClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (class);
+
+ object_class->finalize = pp_ipp_option_widget_finalize;
+
+ g_type_class_add_private (class, sizeof (PpIPPOptionWidgetPrivate));
+}
+
+static void
+pp_ipp_option_widget_init (PpIPPOptionWidget *widget)
+{
+ PpIPPOptionWidgetPrivate *priv;
+
+ priv = widget->priv = PP_IPP_OPTION_WIDGET_GET_PRIVATE (widget);
+
+ priv->switch_button = NULL;
+ priv->spin_button = NULL;
+ priv->combo = NULL;
+ priv->box = NULL;
+
+ priv->printer_name = NULL;
+ priv->option_name = NULL;
+
+ priv->option_supported = NULL;
+ priv->option_default = NULL;
+
+ priv->ipp_attribute = NULL;
+}
+
+static void
+pp_ipp_option_widget_finalize (GObject *object)
+{
+ PpIPPOptionWidget *widget = PP_IPP_OPTION_WIDGET (object);
+ PpIPPOptionWidgetPrivate *priv = widget->priv;
+
+ if (priv)
+ {
+ if (priv->option_name)
+ {
+ g_free (priv->option_name);
+ priv->option_name = NULL;
+ }
+
+ if (priv->printer_name)
+ {
+ g_free (priv->printer_name);
+ priv->printer_name = NULL;
+ }
+
+ if (priv->option_supported)
+ {
+ ipp_attribute_free (priv->option_supported);
+ priv->option_supported = NULL;
+ }
+
+ if (priv->option_default)
+ {
+ ipp_attribute_free (priv->option_default);
+ priv->option_default = NULL;
+ }
+
+ if (priv->ipp_attribute)
+ {
+ g_hash_table_unref (priv->ipp_attribute);
+ priv->ipp_attribute = NULL;
+ }
+ }
+
+ G_OBJECT_CLASS (pp_ipp_option_widget_parent_class)->finalize (object);
+}
+
+GtkWidget *
+pp_ipp_option_widget_new (IPPAttribute *attr_supported,
+ IPPAttribute *attr_default,
+ const gchar *option_name,
+ const gchar *printer)
+{
+ PpIPPOptionWidgetPrivate *priv;
+ PpIPPOptionWidget *widget = NULL;
+
+ if (attr_supported && option_name && printer)
+ {
+ widget = g_object_new (PP_TYPE_IPP_OPTION_WIDGET, NULL);
+
+ priv = PP_IPP_OPTION_WIDGET_GET_PRIVATE (widget);
+
+ priv->printer_name = g_strdup (printer);
+ priv->option_name = g_strdup (option_name);
+ priv->option_supported = ipp_attribute_copy (attr_supported);
+ priv->option_default = ipp_attribute_copy (attr_default);
+
+ if (construct_widget (widget))
+ {
+ update_widget_real (widget);
+ }
+ else
+ {
+ g_object_ref_sink (widget);
+ g_object_unref (widget);
+ widget = NULL;
+ }
+ }
+
+ return (GtkWidget *) widget;
+}
+
+enum {
+ NAME_COLUMN,
+ VALUE_COLUMN,
+ N_COLUMNS
+};
+
+static GtkWidget *
+combo_box_new (void)
+{
+ GtkCellRenderer *cell;
+ GtkListStore *store;
+ GtkWidget *combo_box;
+
+ combo_box = gtk_combo_box_new ();
+
+ store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
+ gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (store));
+ g_object_unref (store);
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
+ "text", NAME_COLUMN,
+ NULL);
+
+ return combo_box;
+}
+
+static void
+combo_box_append (GtkWidget *combo,
+ const gchar *display_text,
+ const gchar *value)
+{
+ GtkTreeModel *model;
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+ store = GTK_LIST_STORE (model);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ NAME_COLUMN, display_text,
+ VALUE_COLUMN, value,
+ -1);
+}
+
+struct ComboSet {
+ GtkComboBox *combo;
+ const gchar *value;
+};
+
+static gboolean
+set_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ struct ComboSet *set_data = data;
+ gboolean found;
+ char *value;
+
+ gtk_tree_model_get (model, iter, VALUE_COLUMN, &value, -1);
+ found = (strcmp (value, set_data->value) == 0);
+ g_free (value);
+
+ if (found)
+ gtk_combo_box_set_active_iter (set_data->combo, iter);
+
+ return found;
+}
+
+static void
+combo_box_set (GtkWidget *combo,
+ const gchar *value)
+{
+ struct ComboSet set_data;
+ GtkTreeModel *model;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+
+ set_data.combo = GTK_COMBO_BOX (combo);
+ set_data.value = value;
+ gtk_tree_model_foreach (model, set_cb, &set_data);
+}
+
+static char *
+combo_box_get (GtkWidget *combo)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *value = NULL;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+
+ if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
+ gtk_tree_model_get (model, &iter, VALUE_COLUMN, &value, -1);
+
+ return value;
+}
+
+static void
+printer_add_option_async_cb (gboolean success,
+ gpointer user_data)
+{
+ update_widget (user_data);
+}
+
+static void
+switch_changed_cb (GtkWidget *switch_button,
+ GParamSpec *pspec,
+ PpIPPOptionWidget *widget)
+{
+ PpIPPOptionWidgetPrivate *priv = widget->priv;
+ gchar **values;
+
+ values = g_new0 (gchar *, 2);
+
+ if (gtk_switch_get_active (GTK_SWITCH (switch_button)))
+ values[0] = g_strdup ("True");
+ else
+ values[0] = g_strdup ("False");
+
+ printer_add_option_async (priv->printer_name,
+ priv->option_name,
+ values,
+ TRUE,
+ NULL,
+ printer_add_option_async_cb,
+ widget);
+
+ g_strfreev (values);
+}
+
+static void
+combo_changed_cb (GtkWidget *combo,
+ PpIPPOptionWidget *widget)
+{
+ PpIPPOptionWidgetPrivate *priv = widget->priv;
+ gchar **values;
+
+ values = g_new0 (gchar *, 2);
+ values[0] = combo_box_get (combo);
+
+ printer_add_option_async (priv->printer_name,
+ priv->option_name,
+ values,
+ TRUE,
+ NULL,
+ printer_add_option_async_cb,
+ widget);
+
+ g_strfreev (values);
+}
+
+static void
+spin_button_changed_cb (GtkWidget *spin_button,
+ PpIPPOptionWidget *widget)
+{
+ PpIPPOptionWidgetPrivate *priv = widget->priv;
+ gchar **values;
+
+ values = g_new0 (gchar *, 2);
+ values[0] = g_strdup_printf ("%d", gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spin_button)));
+
+ printer_add_option_async (priv->printer_name,
+ priv->option_name,
+ values,
+ TRUE,
+ NULL,
+ printer_add_option_async_cb,
+ widget);
+
+ g_strfreev (values);
+}
+
+static gboolean
+construct_widget (PpIPPOptionWidget *widget)
+{
+ PpIPPOptionWidgetPrivate *priv = widget->priv;
+ gboolean trivial_option = FALSE;
+ gboolean result = FALSE;
+ gchar *value;
+ gint i;
+
+ if (priv->option_supported)
+ {
+ switch (priv->option_supported->attribute_type)
+ {
+ case IPP_ATTRIBUTE_TYPE_INTEGER:
+ if (priv->option_supported->num_of_values <= 1)
+ trivial_option = TRUE;
+ break;
+
+ case IPP_ATTRIBUTE_TYPE_STRING:
+ if (priv->option_supported->num_of_values <= 1)
+ trivial_option = TRUE;
+ break;
+
+ case IPP_ATTRIBUTE_TYPE_RANGE:
+ if (priv->option_supported->attribute_values[0].lower_range ==
+ priv->option_supported->attribute_values[0].upper_range)
+ trivial_option = TRUE;
+ break;
+ }
+
+ if (!trivial_option)
+ {
+ switch (priv->option_supported->attribute_type)
+ {
+ case IPP_ATTRIBUTE_TYPE_BOOLEAN:
+ priv->switch_button = gtk_switch_new ();
+
+ gtk_box_pack_start (GTK_BOX (widget), priv->switch_button, FALSE, FALSE, 0);
+ g_signal_connect (priv->switch_button, "notify::active", G_CALLBACK (switch_changed_cb), widget);
+ break;
+
+ case IPP_ATTRIBUTE_TYPE_INTEGER:
+ priv->combo = combo_box_new ();
+
+ for (i = 0; i < priv->option_supported->num_of_values; i++)
+ {
+ value = g_strdup_printf ("%d", priv->option_supported->attribute_values[i].integer_value);
+ combo_box_append (priv->combo,
+ ipp_choice_translate (priv->option_name,
+ value),
+ value);
+ g_free (value);
+ }
+
+ gtk_box_pack_start (GTK_BOX (widget), priv->combo, FALSE, FALSE, 0);
+ g_signal_connect (priv->combo, "changed", G_CALLBACK (combo_changed_cb), widget);
+ break;
+
+ case IPP_ATTRIBUTE_TYPE_STRING:
+ priv->combo = combo_box_new ();
+
+ for (i = 0; i < priv->option_supported->num_of_values; i++)
+ combo_box_append (priv->combo,
+ ipp_choice_translate (priv->option_name,
+ priv->option_supported->attribute_values[i].string_value),
+ priv->option_supported->attribute_values[i].string_value);
+
+ gtk_box_pack_start (GTK_BOX (widget), priv->combo, FALSE, FALSE, 0);
+ g_signal_connect (priv->combo, "changed", G_CALLBACK (combo_changed_cb), widget);
+ break;
+
+ case IPP_ATTRIBUTE_TYPE_RANGE:
+ priv->spin_button = gtk_spin_button_new_with_range (
+ priv->option_supported->attribute_values[0].lower_range,
+ priv->option_supported->attribute_values[0].upper_range,
+ 1);
+
+ gtk_box_pack_start (GTK_BOX (widget), priv->spin_button, FALSE, FALSE, 0);
+ g_signal_connect (priv->spin_button, "value-changed", G_CALLBACK (spin_button_changed_cb), widget);
+ break;
+
+ default:
+ break;
+ }
+
+ result = TRUE;
+ }
+ }
+
+ return result;
+}
+
+static void
+update_widget_real (PpIPPOptionWidget *widget)
+{
+ PpIPPOptionWidgetPrivate *priv = widget->priv;
+ IPPAttribute *attr = NULL;
+ gchar *value;
+ gchar *attr_name;
+
+ if (priv->option_default)
+ {
+ attr = ipp_attribute_copy (priv->option_default);
+
+ ipp_attribute_free (priv->option_default);
+ priv->option_default = NULL;
+ }
+ else if (priv->ipp_attribute)
+ {
+ attr_name = g_strdup_printf ("%s-default", priv->option_name);
+ attr = ipp_attribute_copy (g_hash_table_lookup (priv->ipp_attribute, attr_name));
+
+ g_free (attr_name);
+ g_hash_table_unref (priv->ipp_attribute);
+ priv->ipp_attribute = NULL;
+ }
+
+ switch (priv->option_supported->attribute_type)
+ {
+ case IPP_ATTRIBUTE_TYPE_BOOLEAN:
+ g_signal_handlers_block_by_func (priv->switch_button, switch_changed_cb, widget);
+
+ if (attr && attr->num_of_values > 0 &&
+ attr->attribute_type == IPP_ATTRIBUTE_TYPE_BOOLEAN)
+ {
+ gtk_switch_set_active (GTK_SWITCH (priv->switch_button),
+ attr->attribute_values[0].boolean_value);
+ }
+
+ g_signal_handlers_unblock_by_func (priv->switch_button, switch_changed_cb, widget);
+ break;
+
+ case IPP_ATTRIBUTE_TYPE_INTEGER:
+ g_signal_handlers_block_by_func (priv->combo, combo_changed_cb, widget);
+
+ if (attr && attr->num_of_values > 0 &&
+ attr->attribute_type == IPP_ATTRIBUTE_TYPE_INTEGER)
+ {
+ value = g_strdup_printf ("%d", attr->attribute_values[0].integer_value);
+ combo_box_set (priv->combo, value);
+ g_free (value);
+ }
+ else
+ {
+ value = g_strdup_printf ("%d", priv->option_supported->attribute_values[0].integer_value);
+ combo_box_set (priv->combo, value);
+ g_free (value);
+ }
+
+ g_signal_handlers_unblock_by_func (priv->combo, combo_changed_cb, widget);
+ break;
+
+ case IPP_ATTRIBUTE_TYPE_STRING:
+ g_signal_handlers_block_by_func (priv->combo, combo_changed_cb, widget);
+
+ if (attr && attr->num_of_values > 0 &&
+ attr->attribute_type == IPP_ATTRIBUTE_TYPE_STRING)
+ {
+ combo_box_set (priv->combo, attr->attribute_values[0].string_value);
+ }
+ else
+ {
+ combo_box_set (priv->combo, priv->option_supported->attribute_values[0].string_value);
+ }
+
+ g_signal_handlers_unblock_by_func (priv->combo, combo_changed_cb, widget);
+ break;
+
+ case IPP_ATTRIBUTE_TYPE_RANGE:
+ g_signal_handlers_block_by_func (priv->spin_button, spin_button_changed_cb, widget);
+
+ if (attr && attr->num_of_values > 0 &&
+ attr->attribute_type == IPP_ATTRIBUTE_TYPE_INTEGER)
+ {
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->spin_button),
+ attr->attribute_values[0].integer_value);
+ }
+ else
+ {
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (priv->spin_button),
+ priv->option_supported->attribute_values[0].lower_range);
+ }
+
+ g_signal_handlers_unblock_by_func (priv->spin_button, spin_button_changed_cb, widget);
+ break;
+
+ default:
+ break;
+ }
+
+ ipp_attribute_free (attr);
+}
+
+static void
+get_ipp_attributes_cb (GHashTable *table,
+ gpointer user_data)
+{
+ PpIPPOptionWidget *widget = (PpIPPOptionWidget *) user_data;
+ PpIPPOptionWidgetPrivate *priv = widget->priv;
+
+ if (priv->ipp_attribute)
+ g_hash_table_unref (priv->ipp_attribute);
+
+ priv->ipp_attribute = table;
+
+ update_widget_real (widget);
+}
+
+static void
+update_widget (PpIPPOptionWidget *widget)
+{
+ PpIPPOptionWidgetPrivate *priv = widget->priv;
+ gchar **attributes_names;
+
+ attributes_names = g_new0 (gchar *, 2);
+ attributes_names[0] = g_strdup_printf ("%s-default", priv->option_name);
+
+ get_ipp_attributes_async (priv->printer_name,
+ attributes_names,
+ get_ipp_attributes_cb,
+ widget);
+
+ g_strfreev (attributes_names);
+}
diff --git a/panels/printers/pp-ipp-option-widget.h b/panels/printers/pp-ipp-option-widget.h
new file mode 100644
index 0000000..343ebc0
--- /dev/null
+++ b/panels/printers/pp-ipp-option-widget.h
@@ -0,0 +1,70 @@
+/* -*- 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_IPP_OPTION_WIDGET_H__
+#define __PP_IPP_OPTION_WIDGET_H__
+
+#include <gtk/gtk.h>
+#include <cups/cups.h>
+#include <cups/ppd.h>
+
+#include "pp-utils.h"
+
+G_BEGIN_DECLS
+
+#define PP_TYPE_IPP_OPTION_WIDGET (pp_ipp_option_widget_get_type ())
+#define PP_IPP_OPTION_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PP_TYPE_IPP_OPTION_WIDGET, PpIPPOptionWidget))
+#define PP_IPP_OPTION_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PP_TYPE_IPP_OPTION_WIDGET, PpIPPOptionWidgetClass))
+#define PP_IS_IPP_OPTION_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PP_TYPE_IPP_OPTION_WIDGET))
+#define PP_IS_IPP_OPTION_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PP_TYPE_IPP_OPTION_WIDGET))
+#define PP_IPP_OPTION_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PP_TYPE_IPP_OPTION_WIDGET, PpIPPOptionWidgetClass))
+
+typedef struct _PpIPPOptionWidget PpIPPOptionWidget;
+typedef struct _PpIPPOptionWidgetClass PpIPPOptionWidgetClass;
+typedef struct PpIPPOptionWidgetPrivate PpIPPOptionWidgetPrivate;
+
+struct _PpIPPOptionWidget
+{
+ GtkHBox parent_instance;
+
+ PpIPPOptionWidgetPrivate *priv;
+};
+
+struct _PpIPPOptionWidgetClass
+{
+ GtkHBoxClass parent_class;
+
+ void (*changed) (PpIPPOptionWidget *widget);
+};
+
+typedef void (*IPPOptionCallback) (GtkWidget *widget,
+ gpointer user_data);
+
+GType pp_ipp_option_widget_get_type (void) G_GNUC_CONST;
+
+GtkWidget *pp_ipp_option_widget_new (IPPAttribute *attr_supported,
+ IPPAttribute *attr_default,
+ const gchar *option_name,
+ const gchar *printer);
+
+G_END_DECLS
+
+#endif /* __PP_IPP_OPTION_WIDGET_H__ */
diff --git a/panels/printers/pp-options-dialog.c b/panels/printers/pp-options-dialog.c
new file mode 100644
index 0000000..ea9c8af
--- /dev/null
+++ b/panels/printers/pp-options-dialog.c
@@ -0,0 +1,984 @@
+/* -*- 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-options-dialog.h"
+#include "pp-ppd-option-widget.h"
+#include "pp-ipp-option-widget.h"
+#include "pp-utils.h"
+
+struct _PpOptionsDialog {
+ GtkBuilder *builder;
+ GtkWidget *parent;
+
+ GtkWidget *dialog;
+
+ UserResponseCallback user_callback;
+ gpointer user_data;
+
+ gchar *printer_name;
+
+ gchar *ppd_filename;
+ gboolean ppd_filename_set;
+
+ cups_dest_t *destination;
+ gboolean destination_set;
+
+ GHashTable *ipp_attributes;
+ gboolean ipp_attributes_set;
+
+ gboolean populating_dialog;
+
+ GtkResponseType response;
+
+ gboolean sensitive;
+};
+
+static void pp_options_dialog_hide (PpOptionsDialog *dialog);
+
+enum
+{
+ CATEGORY_IDS_COLUMN = 0,
+ CATEGORY_NAMES_COLUMN
+};
+
+/* These lists come from Gtk+ */
+static const struct {
+ const char *keyword;
+ const char *translation;
+} ppd_option_translations[] = {
+ { "Duplex", N_("Two Sided") },
+ { "MediaType", N_("Paper Type") },
+ { "InputSlot", N_("Paper Source") },
+ { "OutputBin", N_("Output Tray") },
+ { "Resolution", N_("Resolution") },
+ { "PreFilter", N_("GhostScript pre-filtering") },
+};
+
+/* keep sorted when changing */
+static const char *page_setup_option_whitelist[] = {
+ "InputSlot",
+ "MediaType",
+ "OutputBin",
+ "PageSize",
+};
+
+/* keep sorted when changing */
+static const char *color_option_whitelist[] = {
+ "BRColorEnhancement",
+ "BRColorMatching",
+ "BRColorMatching",
+ "BRColorMode",
+ "BRGammaValue",
+ "BRImprovedGray",
+ "BlackSubstitution",
+ "ColorModel",
+ "HPCMYKInks",
+ "HPCSGraphics",
+ "HPCSImages",
+ "HPCSText",
+ "HPColorSmart",
+ "RPSBlackMode",
+ "RPSBlackOverPrint",
+ "Rcmyksimulation",
+};
+
+/* keep sorted when changing */
+static const char *color_group_whitelist[] = {
+ "Color",
+ "Color1",
+ "Color2",
+ "ColorBalance",
+ "ColorPage",
+ "ColorSettings1",
+ "ColorSettings2",
+ "ColorSettings3",
+ "ColorSettings4",
+ "EPColorSettings",
+ "FPColorWise1",
+ "FPColorWise2",
+ "FPColorWise3",
+ "FPColorWise4",
+ "FPColorWise5",
+ "HPCMYKInksPanel",
+ "HPColorOptions",
+ "HPColorOptionsPanel",
+ "HPColorQualityOptionsPanel",
+ "ManualColor",
+};
+
+/* keep sorted when changing */
+static const char *image_quality_option_whitelist[] = {
+ "BRDocument",
+ "BRHalfTonePattern",
+ "BRNormalPrt",
+ "BRPrintQuality",
+ "BitsPerPixel",
+ "Darkness",
+ "Dithering",
+ "EconoMode",
+ "Economode",
+ "HPEconoMode",
+ "HPEdgeControl",
+ "HPGraphicsHalftone",
+ "HPHalftone",
+ "HPImagingOptions",
+ "HPLJDensity",
+ "HPPhotoHalftone",
+ "HPPrintQualityOptions",
+ "HPResolutionOptions",
+ "OutputMode",
+ "REt",
+ "RPSBitsPerPixel",
+ "RPSDitherType",
+ "Resolution",
+ "ScreenLock",
+ "Smoothing",
+ "TonerSaveMode",
+ "UCRGCRForImage",
+};
+
+/* keep sorted when changing */
+static const char *image_quality_group_whitelist[] = {
+ "EPQualitySettings",
+ "FPImageQuality1",
+ "FPImageQuality2",
+ "FPImageQuality3",
+ "ImageQualityPage",
+ "Quality",
+};
+
+/* keep sorted when changing */
+static const char * finishing_option_whitelist[] = {
+ "BindColor",
+ "BindEdge",
+ "BindType",
+ "BindWhen",
+ "Booklet",
+ "FoldType",
+ "FoldWhen",
+ "HPStaplerOptions",
+ "Jog",
+ "Slipsheet",
+ "Sorter",
+ "StapleLocation",
+ "StapleOrientation",
+ "StapleWhen",
+ "StapleX",
+ "StapleY",
+};
+
+/* keep sorted when changing */
+static const char *job_group_whitelist[] = {
+ "JobHandling",
+ "JobLog",
+};
+
+/* keep sorted when changing */
+static const char *finishing_group_whitelist[] = {
+ "Booklet",
+ "BookletCover",
+ "BookletModeOptions",
+ "FPFinishing1",
+ "FPFinishing2",
+ "FPFinishing3",
+ "FPFinishing4",
+ "Finishing",
+ "FinishingOptions",
+ "FinishingPage",
+ "HPBookletPanel",
+ "HPFinishing",
+ "HPFinishingOptions",
+ "HPFinishingPanel",
+};
+
+/* keep sorted when changing */
+static const char *installable_options_group_whitelist[] = {
+ "InstallableOptions",
+};
+
+/* keep sorted when changing */
+static const char *page_setup_group_whitelist[] = {
+ "HPMarginAndLayout",
+ "OutputControl",
+ "PaperHandling",
+ "Paper",
+ "Source",
+};
+
+/* keep sorted when changing */
+static const char *ppd_option_blacklist[] = {
+ "Collate",
+ "Copies",
+ "Duplex",
+ "HPManualDuplexOrientation",
+ "HPManualDuplexSwitch",
+ "OutputOrder",
+ "PageRegion"
+};
+
+static int
+strptr_cmp (const void *a,
+ const void *b)
+{
+ char **aa = (char **)a;
+ char **bb = (char **)b;
+ return strcmp (*aa, *bb);
+}
+
+static gboolean
+string_in_table (gchar *str,
+ const gchar *table[],
+ gint table_len)
+{
+ return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
+}
+
+#define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
+
+static gchar *
+ppd_option_name_translate (ppd_option_t *option)
+{
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (ppd_option_translations); i++)
+ {
+ if (g_strcmp0 (ppd_option_translations[i].keyword, option->keyword) == 0)
+ return g_strdup (_(ppd_option_translations[i].translation));
+ }
+
+ return g_strdup (option->text);
+}
+
+static gint
+grid_get_height (GtkWidget *grid)
+{
+ GList *children;
+ GList *child;
+ gint height = 0;
+ gint top_attach = 0;
+ gint max = 0;
+
+ children = gtk_container_get_children (GTK_CONTAINER (grid));
+ for (child = children; child; child = g_list_next (child))
+ {
+ gtk_container_child_get (GTK_CONTAINER (grid), child->data,
+ "top-attach", &top_attach,
+ "height", &height,
+ NULL);
+
+ if (height + top_attach > max)
+ max = height + top_attach;
+ }
+
+ g_list_free (children);
+
+ return max;
+}
+
+static gboolean
+grid_is_empty (GtkWidget *grid)
+{
+ GList *children;
+
+ children = gtk_container_get_children (GTK_CONTAINER (grid));
+ if (children)
+ {
+ g_list_free (children);
+ return FALSE;
+ }
+ else
+ {
+ return TRUE;
+ }
+}
+
+static GtkWidget *
+ipp_option_add (IPPAttribute *attr_supported,
+ IPPAttribute *attr_default,
+ const gchar *option_name,
+ const gchar *option_display_name,
+ const gchar *printer_name,
+ GtkWidget *grid,
+ gboolean sensitive)
+{
+ GtkStyleContext *context;
+ GtkWidget *widget;
+ GtkWidget *label;
+ gint position;
+
+ widget = (GtkWidget *) pp_ipp_option_widget_new (attr_supported,
+ attr_default,
+ option_name,
+ printer_name);
+ if (widget)
+ {
+ gtk_widget_set_sensitive (widget, sensitive);
+ position = grid_get_height (grid);
+
+ label = gtk_label_new (option_display_name);
+ context = gtk_widget_get_style_context (label);
+ gtk_style_context_add_class (context, "dim-label");
+ gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
+ gtk_widget_set_margin_left (label, 10);
+ gtk_grid_attach (GTK_GRID (grid), label, 0, position, 1, 1);
+
+ gtk_widget_set_margin_left (widget, 20);
+ gtk_grid_attach (GTK_GRID (grid), widget, 1, position, 1, 1);
+ }
+
+ return widget;
+}
+
+static GtkWidget *
+ppd_option_add (ppd_option_t option,
+ const gchar *printer_name,
+ GtkWidget *grid,
+ gboolean sensitive)
+{
+ GtkStyleContext *context;
+ GtkWidget *widget;
+ GtkWidget *label;
+ gint position;
+
+ widget = (GtkWidget *) pp_ppd_option_widget_new (&option, printer_name);
+ if (widget)
+ {
+ gtk_widget_set_sensitive (widget, sensitive);
+ position = grid_get_height (grid);
+
+ label = gtk_label_new (ppd_option_name_translate (&option));
+ context = gtk_widget_get_style_context (label);
+ gtk_style_context_add_class (context, "dim-label");
+ gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
+ gtk_widget_set_margin_left (label, 10);
+ gtk_grid_attach (GTK_GRID (grid), label, 0, position, 1, 1);
+
+ gtk_widget_set_margin_left (widget, 20);
+ gtk_grid_attach (GTK_GRID (grid), widget, 1, position, 1, 1);
+ }
+
+ return widget;
+}
+
+static GtkWidget *
+tab_grid_new ()
+{
+ GtkWidget *grid;
+
+ grid = gtk_grid_new ();
+ gtk_container_set_border_width (GTK_CONTAINER (grid), 20);
+ gtk_grid_set_row_spacing (GTK_GRID (grid), 15);
+
+ return grid;
+}
+
+static void
+tab_add (const gchar *tab_name,
+ GtkWidget *options_notebook,
+ GtkTreeView *treeview,
+ GtkWidget *grid)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ GtkWidget *scrolled_window;
+ gboolean unref_store = FALSE;
+ gint id;
+
+ if (!grid_is_empty (grid))
+ {
+ scrolled_window = gtk_scrolled_window_new (NULL, NULL);
+ gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
+ gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window),
+ grid);
+
+ id = gtk_notebook_append_page (GTK_NOTEBOOK (options_notebook),
+ scrolled_window,
+ NULL);
+
+ if (id >= 0)
+ {
+ store = GTK_LIST_STORE (gtk_tree_view_get_model (treeview));
+ if (!store)
+ {
+ store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
+ unref_store = TRUE;
+ }
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ CATEGORY_IDS_COLUMN, id,
+ CATEGORY_NAMES_COLUMN, tab_name,
+ -1);
+
+ if (unref_store)
+ {
+ gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (store));
+ g_object_unref (store);
+ }
+ }
+ }
+ else
+ {
+ g_object_ref_sink (grid);
+ g_object_unref (grid);
+ }
+}
+
+static void
+category_selection_changed_cb (GtkTreeSelection *selection,
+ gpointer user_data)
+{
+ PpOptionsDialog *dialog = (PpOptionsDialog *) user_data;
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkWidget *options_notebook;
+ gint id = -1;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_tree_model_get (model, &iter,
+ CATEGORY_IDS_COLUMN, &id,
+ -1);
+ }
+
+ if (id >= 0)
+ {
+ options_notebook = (GtkWidget*)
+ gtk_builder_get_object (dialog->builder, "options-notebook");
+
+ gtk_notebook_set_current_page (GTK_NOTEBOOK (options_notebook), id);
+ }
+}
+
+static void
+populate_options_real (PpOptionsDialog *dialog)
+{
+ GtkTreeSelection *selection;
+ GtkTreeModel *model;
+ GtkTreeView *treeview;
+ GtkTreeIter iter;
+ ppd_file_t *ppd_file;
+ GtkWidget *notebook;
+ GtkWidget *grid;
+ GtkWidget *general_tab_grid = tab_grid_new ();
+ GtkWidget *page_setup_tab_grid = tab_grid_new ();
+ GtkWidget *installable_options_tab_grid = tab_grid_new ();
+ GtkWidget *job_tab_grid = tab_grid_new ();
+ GtkWidget *image_quality_tab_grid = tab_grid_new ();
+ GtkWidget *color_tab_grid = tab_grid_new ();
+ GtkWidget *finishing_tab_grid = tab_grid_new ();
+ GtkWidget *advanced_tab_grid = tab_grid_new ();
+ GtkWidget *widget;
+ gint i, j;
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (dialog->builder, "options-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, "options-categories-treeview");
+
+ notebook = (GtkWidget *)
+ gtk_builder_get_object (dialog->builder, "options-notebook");
+
+ if (dialog->ipp_attributes)
+ {
+ /* Add number-up option to Page Setup tab */
+ ipp_option_add (g_hash_table_lookup (dialog->ipp_attributes,
+ "number-up-supported"),
+ g_hash_table_lookup (dialog->ipp_attributes,
+ "number-up-default"),
+ "number-up",
+ /* Translators: This option sets number of pages printed on one sheet */
+ _("Pages per side"),
+ dialog->printer_name,
+ page_setup_tab_grid,
+ dialog->sensitive);
+
+ /* Add sides option to Page Setup tab */
+ ipp_option_add (g_hash_table_lookup (dialog->ipp_attributes,
+ "sides-supported"),
+ g_hash_table_lookup (dialog->ipp_attributes,
+ "sides-default"),
+ "sides",
+ /* Translators: This option sets whether to print on both sides of paper */
+ _("Two-sided"),
+ dialog->printer_name,
+ page_setup_tab_grid,
+ dialog->sensitive);
+
+ /* Add orientation-requested option to Page Setup tab */
+ ipp_option_add (g_hash_table_lookup (dialog->ipp_attributes,
+ "orientation-requested-supported"),
+ g_hash_table_lookup (dialog->ipp_attributes,
+ "orientation-requested-default"),
+ "orientation-requested",
+ /* Translators: This option sets orientation of print (portrait, landscape...) */
+ _("Orientation"),
+ dialog->printer_name,
+ page_setup_tab_grid,
+ dialog->sensitive);
+ }
+
+ if (dialog->destination && dialog->ppd_filename)
+ {
+ ppd_file = ppdOpenFile (dialog->ppd_filename);
+ ppdLocalize (ppd_file);
+
+ if (ppd_file)
+ {
+ ppdMarkDefaults (ppd_file);
+ cupsMarkOptions (ppd_file,
+ dialog->destination->num_options,
+ dialog->destination->options);
+
+ for (i = 0; i < ppd_file->num_groups; i++)
+ {
+ for (j = 0; j < ppd_file->groups[i].num_options; j++)
+ {
+ grid = NULL;
+
+ if (STRING_IN_TABLE (ppd_file->groups[i].name,
+ color_group_whitelist))
+ grid = color_tab_grid;
+ else if (STRING_IN_TABLE (ppd_file->groups[i].name,
+ image_quality_group_whitelist))
+ grid = image_quality_tab_grid;
+ else if (STRING_IN_TABLE (ppd_file->groups[i].name,
+ job_group_whitelist))
+ grid = job_tab_grid;
+ else if (STRING_IN_TABLE (ppd_file->groups[i].name,
+ finishing_group_whitelist))
+ grid = finishing_tab_grid;
+ else if (STRING_IN_TABLE (ppd_file->groups[i].name,
+ installable_options_group_whitelist))
+ grid = installable_options_tab_grid;
+ else if (STRING_IN_TABLE (ppd_file->groups[i].name,
+ page_setup_group_whitelist))
+ grid = page_setup_tab_grid;
+
+ if (!STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword,
+ ppd_option_blacklist))
+ {
+ if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword,
+ color_option_whitelist))
+ grid = color_tab_grid;
+ else if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword,
+ image_quality_option_whitelist))
+ grid = image_quality_tab_grid;
+ else if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword,
+ finishing_option_whitelist))
+ grid = finishing_tab_grid;
+ else if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword,
+ page_setup_option_whitelist))
+ grid = page_setup_tab_grid;
+
+ if (!grid)
+ grid = advanced_tab_grid;
+
+ ppd_option_add (ppd_file->groups[i].options[j],
+ dialog->printer_name,
+ grid,
+ dialog->sensitive);
+ }
+ }
+ }
+
+ ppdClose (ppd_file);
+ }
+ }
+
+ dialog->ppd_filename_set = FALSE;
+ if (dialog->ppd_filename)
+ {
+ g_unlink (dialog->ppd_filename);
+ g_free (dialog->ppd_filename);
+ dialog->ppd_filename = NULL;
+ }
+
+ dialog->destination_set = FALSE;
+ if (dialog->destination)
+ {
+ cupsFreeDests (1, dialog->destination);
+ dialog->destination = NULL;
+ }
+
+ dialog->ipp_attributes_set = FALSE;
+ if (dialog->ipp_attributes)
+ {
+ g_hash_table_unref (dialog->ipp_attributes);
+ dialog->ipp_attributes = NULL;
+ }
+
+ /* Translators: "General" tab contains general printer options */
+ tab_add (C_("Printer Option Group", "General"), notebook, treeview, general_tab_grid);
+
+ /* Translators: "Page Setup" tab contains settings related to pages (page size, paper source, etc.) */
+ tab_add (C_("Printer Option Group", "Page Setup"), notebook, treeview, page_setup_tab_grid);
+
+ /* Translators: "Installable Options" tab contains settings of presence of installed options (amount of RAM, duplex unit, etc.) */
+ tab_add (C_("Printer Option Group", "Installable Options"), notebook, treeview, installable_options_tab_grid);
+
+ /* Translators: "Job" tab contains settings for jobs */
+ tab_add (C_("Printer Option Group", "Job"), notebook, treeview, job_tab_grid);
+
+ /* Translators: "Image Quality" tab contains settings for quality of output print (e.g. resolution) */
+ tab_add (C_("Printer Option Group", "Image Quality"), notebook, treeview, image_quality_tab_grid);
+
+ /* Translators: "Color" tab contains color settings (e.g. color printing) */
+ tab_add (C_("Printer Option Group", "Color"), notebook, treeview, color_tab_grid);
+
+ /* Translators: "Finishing" tab contains finishing settings (e.g. booklet printing) */
+ tab_add (C_("Printer Option Group", "Finishing"), notebook, treeview, finishing_tab_grid);
+
+ /* Translators: "Advanced" tab contains all others settings */
+ tab_add (C_("Printer Option Group", "Advanced"), notebook, treeview, advanced_tab_grid);
+
+ gtk_widget_show_all (GTK_WIDGET (notebook));
+
+ /* Select the first option group */
+ if ((selection = gtk_tree_view_get_selection (treeview)) != NULL)
+ {
+ g_signal_connect (selection,
+ "changed",
+ G_CALLBACK (category_selection_changed_cb), dialog);
+
+ if ((model = gtk_tree_view_get_model (treeview)) != NULL &&
+ gtk_tree_model_get_iter_first (model, &iter))
+ gtk_tree_selection_select_iter (selection, &iter);
+ }
+
+ dialog->populating_dialog = FALSE;
+ if (dialog->response != GTK_RESPONSE_NONE)
+ {
+ dialog->user_callback (GTK_DIALOG (dialog->dialog), dialog->response, dialog->user_data);
+ }
+}
+
+static void
+printer_get_ppd_cb (const gchar *ppd_filename,
+ gpointer user_data)
+{
+ PpOptionsDialog *dialog = (PpOptionsDialog *) user_data;
+
+ if (dialog->ppd_filename)
+ {
+ g_unlink (dialog->ppd_filename);
+ g_free (dialog->ppd_filename);
+ }
+
+ dialog->ppd_filename = g_strdup (ppd_filename);
+ dialog->ppd_filename_set = TRUE;
+
+ if (dialog->destination_set &&
+ dialog->ipp_attributes_set)
+ {
+ populate_options_real (dialog);
+ }
+}
+
+static void
+get_named_dest_cb (cups_dest_t *dest,
+ gpointer user_data)
+{
+ PpOptionsDialog *dialog = (PpOptionsDialog *) user_data;
+
+ if (dialog->destination)
+ cupsFreeDests (1, dialog->destination);
+
+ dialog->destination = dest;
+ dialog->destination_set = TRUE;
+
+ if (dialog->ppd_filename_set &&
+ dialog->ipp_attributes_set)
+ {
+ populate_options_real (dialog);
+ }
+}
+
+static void
+get_ipp_attributes_cb (GHashTable *table,
+ gpointer user_data)
+{
+ PpOptionsDialog *dialog = (PpOptionsDialog *) user_data;
+
+ if (dialog->ipp_attributes)
+ g_hash_table_unref (dialog->ipp_attributes);
+
+ dialog->ipp_attributes = table;
+ dialog->ipp_attributes_set = TRUE;
+
+ if (dialog->ppd_filename_set &&
+ dialog->destination_set)
+ {
+ populate_options_real (dialog);
+ }
+}
+
+static void
+populate_options (PpOptionsDialog *dialog)
+{
+ GtkTreeViewColumn *column;
+ GtkCellRenderer *renderer;
+ GtkTreeView *treeview;
+ GtkWidget *widget;
+ /*
+ * Options which we need to obtain through an IPP request
+ * to be able to fill the options dialog.
+ * *-supported - possible values of the option
+ * *-default - actual value of the option
+ */
+ const gchar *attributes[] =
+ { "number-up-supported",
+ "number-up-default",
+ "sides-supported",
+ "sides-default",
+ "orientation-requested-supported",
+ "orientation-requested-default",
+ NULL};
+
+ treeview = (GtkTreeView *)
+ gtk_builder_get_object (dialog->builder, "options-categories-treeview");
+
+ renderer = gtk_cell_renderer_text_new ();
+
+ column = gtk_tree_view_column_new_with_attributes ("Categories", renderer,
+ "text", CATEGORY_NAMES_COLUMN, NULL);
+ gtk_tree_view_column_set_expand (column, TRUE);
+ gtk_tree_view_append_column (treeview, column);
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (dialog->builder, "options-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);
+
+ printer_get_ppd_async (dialog->printer_name,
+ printer_get_ppd_cb,
+ dialog);
+
+ get_named_dest_async (dialog->printer_name,
+ get_named_dest_cb,
+ dialog);
+
+ get_ipp_attributes_async (dialog->printer_name,
+ (gchar **) attributes,
+ get_ipp_attributes_cb,
+ dialog);
+}
+
+/*
+ * Modify padding of the content area of the GtkDialog
+ * so it is aligned with the action area.
+ */
+static void
+update_alignment_padding (GtkWidget *widget,
+ GtkAllocation *allocation,
+ gpointer user_data)
+{
+ PpOptionsDialog *dialog = (PpOptionsDialog*) user_data;
+ GtkAllocation allocation1, allocation2;
+ GtkWidget *action_area;
+ GtkWidget *content_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);
+
+ content_area = (GtkWidget*)
+ gtk_builder_get_object (dialog->builder, "content-alignment");
+ gtk_widget_get_allocation (content_area, &allocation1);
+
+ offset_left = allocation2.x - allocation1.x;
+ offset_right = (allocation1.x + allocation1.width) -
+ (allocation2.x + allocation2.width);
+
+ gtk_alignment_get_padding (GTK_ALIGNMENT (content_area),
+ &padding_top, &padding_bottom,
+ &padding_left, &padding_right);
+ if (allocation1.x >= 0 && allocation2.x >= 0)
+ {
+ if (offset_left > 0 && offset_left != padding_left)
+ gtk_alignment_set_padding (GTK_ALIGNMENT (content_area),
+ padding_top, padding_bottom,
+ offset_left, padding_right);
+
+ gtk_alignment_get_padding (GTK_ALIGNMENT (content_area),
+ &padding_top, &padding_bottom,
+ &padding_left, &padding_right);
+ if (offset_right > 0 && offset_right != padding_right)
+ gtk_alignment_set_padding (GTK_ALIGNMENT (content_area),
+ padding_top, padding_bottom,
+ padding_left, offset_right);
+ }
+}
+
+static void
+options_dialog_response_cb (GtkDialog *_dialog,
+ gint response_id,
+ gpointer user_data)
+{
+ PpOptionsDialog *dialog = (PpOptionsDialog*) user_data;
+
+ pp_options_dialog_hide (dialog);
+ dialog->response = response_id;
+
+ if (!dialog->populating_dialog)
+ dialog->user_callback (GTK_DIALOG (dialog->dialog), response_id, dialog->user_data);
+}
+
+PpOptionsDialog *
+pp_options_dialog_new (GtkWindow *parent,
+ UserResponseCallback user_callback,
+ gpointer user_data,
+ gchar *printer_name,
+ gboolean sensitive)
+{
+ PpOptionsDialog *dialog;
+ GtkWidget *widget;
+ GError *error = NULL;
+ gchar *objects[] = { "options-dialog", NULL };
+ guint builder_result;
+ gchar *title;
+
+ dialog = g_new0 (PpOptionsDialog, 1);
+
+ dialog->builder = gtk_builder_new ();
+ dialog->parent = GTK_WIDGET (parent);
+
+ builder_result = gtk_builder_add_objects_from_file (dialog->builder,
+ DATADIR"/options-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, "options-dialog");
+ gtk_window_set_transient_for (GTK_WINDOW (dialog->dialog), GTK_WINDOW (parent));
+
+ dialog->user_callback = user_callback;
+ dialog->user_data = user_data;
+
+ dialog->printer_name = g_strdup (printer_name);
+
+ dialog->ppd_filename = NULL;
+ dialog->ppd_filename_set = FALSE;
+
+ dialog->destination = NULL;
+ dialog->destination_set = FALSE;
+
+ dialog->ipp_attributes = NULL;
+ dialog->ipp_attributes_set = FALSE;
+
+ dialog->response = GTK_RESPONSE_NONE;
+
+ dialog->sensitive = sensitive;
+
+ /* connect signals */
+ g_signal_connect (dialog->dialog, "response", G_CALLBACK (options_dialog_response_cb), dialog);
+ g_signal_connect (dialog->dialog, "size-allocate", G_CALLBACK (update_alignment_padding), dialog);
+
+ widget = (GtkWidget*)
+ gtk_builder_get_object (dialog->builder, "options-title");
+ /* Translators: Options of given printer (e.g. "MyPrinter Options") */
+ title = g_strdup_printf (_("%s Options"), printer_name);
+ gtk_label_set_label (GTK_LABEL (widget), title);
+ g_free (title);
+
+ gtk_widget_show_all (GTK_WIDGET (dialog->dialog));
+
+ dialog->populating_dialog = TRUE;
+ populate_options (dialog);
+
+ return dialog;
+}
+
+void
+pp_options_dialog_free (PpOptionsDialog *dialog)
+{
+ gtk_widget_destroy (GTK_WIDGET (dialog->dialog));
+ dialog->dialog = NULL;
+
+ g_object_unref (dialog->builder);
+ dialog->builder = NULL;
+
+ g_free (dialog->printer_name);
+ dialog->printer_name = NULL;
+
+ if (dialog->ppd_filename)
+ {
+ g_unlink (dialog->ppd_filename);
+ g_free (dialog->ppd_filename);
+ dialog->ppd_filename = NULL;
+ }
+
+ if (dialog->destination)
+ {
+ cupsFreeDests (1, dialog->destination);
+ dialog->destination = NULL;
+ }
+
+ if (dialog->ipp_attributes)
+ {
+ g_hash_table_unref (dialog->ipp_attributes);
+ dialog->ipp_attributes = NULL;
+ }
+
+ g_free (dialog);
+}
+
+static void
+pp_options_dialog_hide (PpOptionsDialog *dialog)
+{
+ gtk_widget_hide (GTK_WIDGET (dialog->dialog));
+}
diff --git a/panels/printers/pp-options-dialog.h b/panels/printers/pp-options-dialog.h
new file mode 100644
index 0000000..9ac3992
--- /dev/null
+++ b/panels/printers/pp-options-dialog.h
@@ -0,0 +1,42 @@
+/* -*- 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_OPTIONS_DIALOG_H__
+#define __PP_OPTIONS_DIALOG_H__
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef struct _PpOptionsDialog PpOptionsDialog;
+
+typedef void (*UserResponseCallback) (GtkDialog *dialog, gint response_id, gpointer user_data);
+
+PpOptionsDialog *pp_options_dialog_new (GtkWindow *parent,
+ UserResponseCallback user_callback,
+ gpointer user_data,
+ gchar *printer_name,
+ gboolean sensitive);
+void pp_options_dialog_free (PpOptionsDialog *dialog);
+
+G_END_DECLS
+
+#endif
diff --git a/panels/printers/pp-ppd-option-widget.c b/panels/printers/pp-ppd-option-widget.c
new file mode 100644
index 0000000..22cb301
--- /dev/null
+++ b/panels/printers/pp-ppd-option-widget.c
@@ -0,0 +1,624 @@
+/* -*- 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 <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <glib/gi18n-lib.h>
+#include <glib/gstdio.h>
+
+#include "pp-ppd-option-widget.h"
+#include "pp-utils.h"
+
+#define PP_PPD_OPTION_WIDGET_GET_PRIVATE(o) \
+ (G_TYPE_INSTANCE_GET_PRIVATE ((o), PP_TYPE_PPD_OPTION_WIDGET, PpPPDOptionWidgetPrivate))
+
+static void pp_ppd_option_widget_finalize (GObject *object);
+
+static gboolean construct_widget (PpPPDOptionWidget *widget);
+static void update_widget (PpPPDOptionWidget *widget);
+static void update_widget_real (PpPPDOptionWidget *widget);
+
+struct PpPPDOptionWidgetPrivate
+{
+ GtkWidget *switch_button;
+ GtkWidget *combo;
+ GtkWidget *image;
+ GtkWidget *box;
+
+ ppd_option_t *option;
+
+ gchar *printer_name;
+ gchar *option_name;
+
+ cups_dest_t *destination;
+ gboolean destination_set;
+
+ gchar *ppd_filename;
+ gboolean ppd_filename_set;
+};
+
+G_DEFINE_TYPE (PpPPDOptionWidget, pp_ppd_option_widget, GTK_TYPE_HBOX)
+
+/* This list comes from Gtk+ */
+static const struct {
+ const char *keyword;
+ const char *choice;
+ const char *translation;
+} ppd_choice_translations[] = {
+ { "Duplex", "None", N_("One Sided") },
+ /* Translators: this is an option of "Two Sided" */
+ { "Duplex", "DuplexNoTumble", N_("Long Edge (Standard)") },
+ /* Translators: this is an option of "Two Sided" */
+ { "Duplex", "DuplexTumble", N_("Short Edge (Flip)") },
+ /* Translators: this is an option of "Paper Source" */
+ { "InputSlot", "Auto", N_("Auto Select") },
+ /* Translators: this is an option of "Paper Source" */
+ { "InputSlot", "AutoSelect", N_("Auto Select") },
+ /* Translators: this is an option of "Paper Source" */
+ { "InputSlot", "Default", N_("Printer Default") },
+ /* Translators: this is an option of "Paper Source" */
+ { "InputSlot", "None", N_("Printer Default") },
+ /* Translators: this is an option of "Paper Source" */
+ { "InputSlot", "PrinterDefault", N_("Printer Default") },
+ /* Translators: this is an option of "Paper Source" */
+ { "InputSlot", "Unspecified", N_("Auto Select") },
+ /* Translators: this is an option of "Resolution" */
+ { "Resolution", "default", N_("Printer Default") },
+ /* Translators: this is an option of "GhostScript" */
+ { "PreFilter", "EmbedFonts", N_("Embed GhostScript fonts only") },
+ /* Translators: this is an option of "GhostScript" */
+ { "PreFilter", "Level1", N_("Convert to PS level 1") },
+ /* Translators: this is an option of "GhostScript" */
+ { "PreFilter", "Level2", N_("Convert to PS level 2") },
+ /* Translators: this is an option of "GhostScript" */
+ { "PreFilter", "No", N_("No pre-filtering") },
+};
+
+static ppd_option_t *
+cups_option_copy (ppd_option_t *option)
+{
+ ppd_option_t *result;
+ gint i;
+
+ result = g_new0 (ppd_option_t, 1);
+
+ *result = *option;
+
+ result->choices = g_new (ppd_choice_t, result->num_choices);
+ for (i = 0; i < result->num_choices; i++)
+ {
+ result->choices[i] = option->choices[i];
+ result->choices[i].code = g_strdup (option->choices[i].code);
+ result->choices[i].option = result;
+ }
+
+ return result;
+}
+
+static void
+cups_option_free (ppd_option_t *option)
+{
+ gint i;
+
+ if (option)
+ {
+ for (i = 0; i < option->num_choices; i++)
+ g_free (option->choices[i].code);
+
+ g_free (option->choices);
+ g_free (option);
+ }
+}
+
+static void
+pp_ppd_option_widget_class_init (PpPPDOptionWidgetClass *class)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (class);
+
+ object_class->finalize = pp_ppd_option_widget_finalize;
+
+ g_type_class_add_private (class, sizeof (PpPPDOptionWidgetPrivate));
+}
+
+static void
+pp_ppd_option_widget_init (PpPPDOptionWidget *widget)
+{
+ PpPPDOptionWidgetPrivate *priv;
+
+ priv = widget->priv = PP_PPD_OPTION_WIDGET_GET_PRIVATE (widget);
+
+ priv->switch_button = NULL;
+ priv->combo = NULL;
+ priv->image = NULL;
+ priv->box = NULL;
+
+ priv->printer_name = NULL;
+ priv->option_name = NULL;
+
+ priv->destination = NULL;
+ priv->destination_set = FALSE;
+
+ priv->ppd_filename = NULL;
+ priv->ppd_filename_set = FALSE;
+}
+
+static void
+pp_ppd_option_widget_finalize (GObject *object)
+{
+ PpPPDOptionWidget *widget = PP_PPD_OPTION_WIDGET (object);
+ PpPPDOptionWidgetPrivate *priv = widget->priv;
+
+ if (priv)
+ {
+ if (priv->option)
+ {
+ cups_option_free (priv->option);
+ priv->option = NULL;
+ }
+
+ if (priv->printer_name)
+ {
+ g_free (priv->printer_name);
+ priv->printer_name = NULL;
+ }
+
+ if (priv->option_name)
+ {
+ g_free (priv->printer_name);
+ priv->printer_name = NULL;
+ }
+
+ if (priv->destination)
+ {
+ cupsFreeDests (1, priv->destination);
+ priv->destination = NULL;
+ }
+
+ if (priv->ppd_filename)
+ {
+ g_unlink (priv->ppd_filename);
+ g_free (priv->ppd_filename);
+ priv->ppd_filename = NULL;
+ }
+ }
+
+ G_OBJECT_CLASS (pp_ppd_option_widget_parent_class)->finalize (object);
+}
+
+static const gchar *
+ppd_choice_translate (ppd_choice_t *choice)
+{
+ const gchar *keyword = choice->option->keyword;
+ gint i;
+
+ for (i = 0; i < G_N_ELEMENTS (ppd_choice_translations); i++)
+ {
+ if (g_strcmp0 (ppd_choice_translations[i].keyword, keyword) == 0 &&
+ g_strcmp0 (ppd_choice_translations[i].choice, choice->choice) == 0)
+ return _(ppd_choice_translations[i].translation);
+ }
+
+ return choice->text;
+}
+
+GtkWidget *
+pp_ppd_option_widget_new (ppd_option_t *option,
+ const gchar *printer_name)
+{
+ PpPPDOptionWidgetPrivate *priv;
+ PpPPDOptionWidget *widget = NULL;
+
+ if (option && printer_name)
+ {
+ widget = g_object_new (PP_TYPE_PPD_OPTION_WIDGET, NULL);
+
+ priv = PP_PPD_OPTION_WIDGET_GET_PRIVATE (widget);
+
+ priv->printer_name = g_strdup (printer_name);
+ priv->option = cups_option_copy (option);
+ priv->option_name = g_strdup (option->keyword);
+
+ if (construct_widget (widget))
+ {
+ update_widget_real (widget);
+ }
+ else
+ {
+ g_object_ref_sink (widget);
+ g_object_unref (widget);
+ widget = NULL;
+ }
+ }
+
+ return (GtkWidget *) widget;
+}
+
+enum {
+ NAME_COLUMN,
+ VALUE_COLUMN,
+ N_COLUMNS
+};
+
+static GtkWidget *
+combo_box_new (void)
+{
+ GtkCellRenderer *cell;
+ GtkListStore *store;
+ GtkWidget *combo_box;
+
+ combo_box = gtk_combo_box_new ();
+
+ store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
+ gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (store));
+ g_object_unref (store);
+
+ cell = gtk_cell_renderer_text_new ();
+ gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
+ gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
+ "text", NAME_COLUMN,
+ NULL);
+
+ return combo_box;
+}
+
+static void
+combo_box_append (GtkWidget *combo,
+ const gchar *display_text,
+ const gchar *value)
+{
+ GtkTreeModel *model;
+ GtkListStore *store;
+ GtkTreeIter iter;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+ store = GTK_LIST_STORE (model);
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter,
+ NAME_COLUMN, display_text,
+ VALUE_COLUMN, value,
+ -1);
+}
+
+struct ComboSet {
+ GtkComboBox *combo;
+ const gchar *value;
+};
+
+static gboolean
+set_cb (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ gpointer data)
+{
+ struct ComboSet *set_data = data;
+ gboolean found;
+ char *value;
+
+ gtk_tree_model_get (model, iter, VALUE_COLUMN, &value, -1);
+ found = (strcmp (value, set_data->value) == 0);
+ g_free (value);
+
+ if (found)
+ gtk_combo_box_set_active_iter (set_data->combo, iter);
+
+ return found;
+}
+
+static void
+combo_box_set (GtkWidget *combo,
+ const gchar *value)
+{
+ struct ComboSet set_data;
+ GtkTreeModel *model;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+
+ set_data.combo = GTK_COMBO_BOX (combo);
+ set_data.value = value;
+ gtk_tree_model_foreach (model, set_cb, &set_data);
+}
+
+static char *
+combo_box_get (GtkWidget *combo)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ gchar *value = NULL;
+
+ model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
+
+ if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
+ gtk_tree_model_get (model, &iter, VALUE_COLUMN, &value, -1);
+
+ return value;
+}
+
+static void
+printer_add_option_async_cb (gboolean success,
+ gpointer user_data)
+{
+ update_widget (user_data);
+}
+
+static void
+switch_changed_cb (GtkWidget *switch_button,
+ GParamSpec *pspec,
+ PpPPDOptionWidget *widget)
+{
+ PpPPDOptionWidgetPrivate *priv = widget->priv;
+ gchar **values;
+
+ values = g_new0 (gchar *, 2);
+
+ if (gtk_switch_get_active (GTK_SWITCH (switch_button)))
+ values[0] = g_strdup ("True");
+ else
+ values[0] = g_strdup ("False");
+
+ printer_add_option_async (priv->printer_name,
+ priv->option_name,
+ values,
+ FALSE,
+ NULL,
+ printer_add_option_async_cb,
+ widget);
+
+ g_strfreev (values);
+}
+
+static void
+combo_changed_cb (GtkWidget *combo,
+ PpPPDOptionWidget *widget)
+{
+ PpPPDOptionWidgetPrivate *priv = widget->priv;
+ gchar **values;
+
+ values = g_new0 (gchar *, 2);
+ values[0] = combo_box_get (combo);
+
+ printer_add_option_async (priv->printer_name,
+ priv->option_name,
+ values,
+ FALSE,
+ NULL,
+ printer_add_option_async_cb,
+ widget);
+
+ g_strfreev (values);
+}
+
+static gboolean
+construct_widget (PpPPDOptionWidget *widget)
+{
+ PpPPDOptionWidgetPrivate *priv = widget->priv;
+ gint i;
+
+ /* Don't show options which has only one choice */
+ if (priv->option && priv->option->num_choices > 1)
+ {
+ switch (priv->option->ui)
+ {
+ case PPD_UI_BOOLEAN:
+ priv->switch_button = gtk_switch_new ();
+ g_signal_connect (priv->switch_button, "notify::active", G_CALLBACK (switch_changed_cb), widget);
+ gtk_box_pack_start (GTK_BOX (widget), priv->switch_button, FALSE, FALSE, 0);
+ break;
+
+ case PPD_UI_PICKONE:
+ priv->combo = combo_box_new ();
+
+ for (i = 0; i < priv->option->num_choices; i++)
+ {
+ combo_box_append (priv->combo,
+ ppd_choice_translate (&priv->option->choices[i]),
+ priv->option->choices[i].choice);
+ }
+
+ gtk_box_pack_start (GTK_BOX (widget), priv->combo, FALSE, FALSE, 0);
+ g_signal_connect (priv->combo, "changed", G_CALLBACK (combo_changed_cb), widget);
+ break;
+
+ case PPD_UI_PICKMANY:
+ priv->combo = combo_box_new ();
+
+ for (i = 0; i < priv->option->num_choices; i++)
+ {
+ combo_box_append (priv->combo,
+ ppd_choice_translate (&priv->option->choices[i]),
+ priv->option->choices[i].choice);
+ }
+
+ gtk_box_pack_start (GTK_BOX (widget), priv->combo, TRUE, TRUE, 0);
+ g_signal_connect (priv->combo, "changed", G_CALLBACK (combo_changed_cb), widget);
+ break;
+
+ default:
+ break;
+ }
+
+ priv->image = gtk_image_new_from_icon_name ("dialog-warning-symbolic", GTK_ICON_SIZE_MENU);
+ if (!priv->image)
+ priv->image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_MENU);
+ gtk_box_pack_start (GTK_BOX (widget), priv->image, FALSE, FALSE, 0);
+ gtk_widget_set_no_show_all (GTK_WIDGET (priv->image), TRUE);
+
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+static void
+update_widget_real (PpPPDOptionWidget *widget)
+{
+ PpPPDOptionWidgetPrivate *priv = widget->priv;
+ ppd_option_t *option = NULL, *iter;
+ ppd_file_t *ppd_file;
+ gchar *value = NULL;
+ gint i;
+
+ if (priv->option)
+ {
+ option = cups_option_copy (priv->option);
+ cups_option_free (priv->option);
+ priv->option = NULL;
+ }
+ else if (priv->ppd_filename)
+ {
+ ppd_file = ppdOpenFile (priv->ppd_filename);
+ ppdLocalize (ppd_file);
+
+ if (ppd_file)
+ {
+ ppdMarkDefaults (ppd_file);
+ cupsMarkOptions (ppd_file,
+ priv->destination->num_options,
+ priv->destination->options);
+
+ for (iter = ppdFirstOption(ppd_file); iter; iter = ppdNextOption(ppd_file))
+ {
+ if (g_str_equal (iter->keyword, priv->option_name))
+ {
+ option = cups_option_copy (iter);
+ break;
+ }
+ }
+
+ ppdClose (ppd_file);
+ }
+
+ g_unlink (priv->ppd_filename);
+ g_free (priv->ppd_filename);
+ priv->ppd_filename = NULL;
+ }
+
+ if (option)
+ {
+ for (i = 0; i < option->num_choices; i++)
+ if (option->choices[i].marked)
+ value = g_strdup (option->choices[i].choice);
+
+ if (value == NULL)
+ value = g_strdup (option->defchoice);
+
+ if (value)
+ {
+ switch (option->ui)
+ {
+ case PPD_UI_BOOLEAN:
+ g_signal_handlers_block_by_func (priv->switch_button, switch_changed_cb, widget);
+ if (g_ascii_strcasecmp (value, "True") == 0)
+ gtk_switch_set_active (GTK_SWITCH (priv->switch_button), TRUE);
+ else
+ gtk_switch_set_active (GTK_SWITCH (priv->switch_button), FALSE);
+ g_signal_handlers_unblock_by_func (priv->switch_button, switch_changed_cb, widget);
+ break;
+
+ case PPD_UI_PICKONE:
+ g_signal_handlers_block_by_func (priv->combo, combo_changed_cb, widget);
+ combo_box_set (priv->combo, value);
+ g_signal_handlers_unblock_by_func (priv->combo, combo_changed_cb, widget);
+ break;
+
+ case PPD_UI_PICKMANY:
+ g_signal_handlers_block_by_func (priv->combo, combo_changed_cb, widget);
+ combo_box_set (priv->combo, value);
+ g_signal_handlers_unblock_by_func (priv->combo, combo_changed_cb, widget);
+ break;
+
+ default:
+ break;
+ }
+
+ g_free (value);
+ }
+
+ if (option->conflicted)
+ gtk_widget_show (priv->image);
+ else
+ gtk_widget_hide (priv->image);
+ }
+
+ cups_option_free (option);
+}
+
+static void
+get_named_dest_cb (cups_dest_t *dest,
+ gpointer user_data)
+{
+ PpPPDOptionWidget *widget = (PpPPDOptionWidget *) user_data;
+ PpPPDOptionWidgetPrivate *priv = widget->priv;
+
+ if (priv->destination)
+ cupsFreeDests (1, priv->destination);
+
+ priv->destination = dest;
+ priv->destination_set = TRUE;
+
+ if (priv->ppd_filename_set)
+ {
+ update_widget_real (widget);
+ }
+}
+
+static void
+printer_get_ppd_cb (const gchar *ppd_filename,
+ gpointer user_data)
+{
+ PpPPDOptionWidget *widget = (PpPPDOptionWidget *) user_data;
+ PpPPDOptionWidgetPrivate *priv = widget->priv;
+
+ if (priv->ppd_filename)
+ {
+ g_unlink (priv->ppd_filename);
+ g_free (priv->ppd_filename);
+ }
+
+ priv->ppd_filename = g_strdup (ppd_filename);
+ priv->ppd_filename_set = TRUE;
+
+ if (priv->destination_set)
+ {
+ update_widget_real (widget);
+ }
+}
+
+static void
+update_widget (PpPPDOptionWidget *widget)
+{
+ PpPPDOptionWidgetPrivate *priv = widget->priv;
+
+ get_named_dest_async (priv->printer_name,
+ get_named_dest_cb,
+ widget);
+
+ printer_get_ppd_async (priv->printer_name,
+ printer_get_ppd_cb,
+ widget);
+}
diff --git a/panels/printers/pp-ppd-option-widget.h b/panels/printers/pp-ppd-option-widget.h
new file mode 100644
index 0000000..82b57dd
--- /dev/null
+++ b/panels/printers/pp-ppd-option-widget.h
@@ -0,0 +1,63 @@
+/* -*- 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_OPTION_WIDGET_H__
+#define __PP_PPD_OPTION_WIDGET_H__
+
+#include <gtk/gtk.h>
+#include <cups/cups.h>
+#include <cups/ppd.h>
+
+G_BEGIN_DECLS
+
+#define PP_TYPE_PPD_OPTION_WIDGET (pp_ppd_option_widget_get_type ())
+#define PP_PPD_OPTION_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), PP_TYPE_PPD_OPTION_WIDGET, PpPPDOptionWidget))
+#define PP_PPD_OPTION_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), PP_TYPE_PPD_OPTION_WIDGET, PpPPDOptionWidgetClass))
+#define PP_IS_PPD_OPTION_WIDGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PP_TYPE_PPD_OPTION_WIDGET))
+#define PP_IS_PPD_OPTION_WIDGET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), PP_TYPE_PPD_OPTION_WIDGET))
+#define PP_PPD_OPTION_WIDGET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), PP_TYPE_PPD_OPTION_WIDGET, PpPPDOptionWidgetClass))
+
+typedef struct _PpPPDOptionWidget PpPPDOptionWidget;
+typedef struct _PpPPDOptionWidgetClass PpPPDOptionWidgetClass;
+typedef struct PpPPDOptionWidgetPrivate PpPPDOptionWidgetPrivate;
+
+struct _PpPPDOptionWidget
+{
+ GtkHBox parent_instance;
+
+ PpPPDOptionWidgetPrivate *priv;
+};
+
+struct _PpPPDOptionWidgetClass
+{
+ GtkHBoxClass parent_class;
+
+ void (*changed) (PpPPDOptionWidget *widget);
+};
+
+GType pp_ppd_option_widget_get_type (void) G_GNUC_CONST;
+
+GtkWidget *pp_ppd_option_widget_new (ppd_option_t *source,
+ const gchar *printer_name);
+
+G_END_DECLS
+
+#endif /* __PP_PPD_OPTION_WIDGET_H__ */
diff --git a/panels/printers/pp-utils.c b/panels/printers/pp-utils.c
index c78b70f..2750a34 100644
--- a/panels/printers/pp-utils.c
+++ b/panels/printers/pp-utils.c
@@ -4195,3 +4195,288 @@ get_standard_manufacturers_name (gchar *name)
return result;
}
+
+typedef struct
+{
+ gchar *printer_name;
+ gchar *result;
+ PGPCallback callback;
+ gpointer user_data;
+ GMainContext *context;
+} PGPData;
+
+static gboolean
+printer_get_ppd_idle_cb (gpointer user_data)
+{
+ PGPData *data = (PGPData *) user_data;
+
+ data->callback (data->result, data->user_data);
+
+ return FALSE;
+}
+
+static void
+printer_get_ppd_data_free (gpointer user_data)
+{
+ PGPData *data = (PGPData *) user_data;
+
+ if (data->context)
+ g_main_context_unref (data->context);
+ g_free (data->result);
+ g_free (data->printer_name);
+ g_free (data);
+}
+
+static void
+printer_get_ppd_cb (gpointer user_data)
+{
+ PGPData *data = (PGPData *) user_data;
+ GSource *idle_source;
+
+ idle_source = g_idle_source_new ();
+ g_source_set_callback (idle_source,
+ printer_get_ppd_idle_cb,
+ data,
+ printer_get_ppd_data_free);
+ g_source_attach (idle_source, data->context);
+ g_source_unref (idle_source);
+}
+
+static gpointer
+printer_get_ppd_func (gpointer user_data)
+{
+ PGPData *data = (PGPData *) user_data;
+
+ data->result = g_strdup (cupsGetPPD (data->printer_name));
+
+ printer_get_ppd_cb (data);
+
+ return NULL;
+}
+
+void
+printer_get_ppd_async (const gchar *printer_name,
+ PGPCallback callback,
+ gpointer user_data)
+{
+ PGPData *data;
+ GThread *thread;
+ GError *error = NULL;
+
+ data = g_new0 (PGPData, 1);
+ data->printer_name = g_strdup (printer_name);
+ data->callback = callback;
+ data->user_data = user_data;
+ data->context = g_main_context_ref_thread_default ();
+
+ thread = g_thread_try_new ("printer-get-ppd",
+ printer_get_ppd_func,
+ data,
+ &error);
+
+ if (!thread)
+ {
+ g_warning ("%s", error->message);
+ callback (NULL, user_data);
+
+ g_error_free (error);
+ printer_get_ppd_data_free (data);
+ }
+ else
+ {
+ g_thread_unref (thread);
+ }
+}
+
+typedef struct
+{
+ gchar *printer_name;
+ cups_dest_t *result;
+ GNDCallback callback;
+ gpointer user_data;
+ GMainContext *context;
+} GNDData;
+
+static gboolean
+get_named_dest_idle_cb (gpointer user_data)
+{
+ GNDData *data = (GNDData *) user_data;
+
+ data->callback (data->result, data->user_data);
+
+ return FALSE;
+}
+
+static void
+get_named_dest_data_free (gpointer user_data)
+{
+ GNDData *data = (GNDData *) user_data;
+
+ if (data->context)
+ g_main_context_unref (data->context);
+ g_free (data->printer_name);
+ g_free (data);
+}
+
+static void
+get_named_dest_cb (gpointer user_data)
+{
+ GNDData *data = (GNDData *) user_data;
+ GSource *idle_source;
+
+ idle_source = g_idle_source_new ();
+ g_source_set_callback (idle_source,
+ get_named_dest_idle_cb,
+ data,
+ get_named_dest_data_free);
+ g_source_attach (idle_source, data->context);
+ g_source_unref (idle_source);
+}
+
+static gpointer
+get_named_dest_func (gpointer user_data)
+{
+ GNDData *data = (GNDData *) user_data;
+
+ data->result = cupsGetNamedDest (CUPS_HTTP_DEFAULT, data->printer_name, NULL);
+
+ get_named_dest_cb (data);
+
+ return NULL;
+}
+
+void
+get_named_dest_async (const gchar *printer_name,
+ GNDCallback callback,
+ gpointer user_data)
+{
+ GNDData *data;
+ GThread *thread;
+ GError *error = NULL;
+
+ data = g_new0 (GNDData, 1);
+ data->printer_name = g_strdup (printer_name);
+ data->callback = callback;
+ data->user_data = user_data;
+ data->context = g_main_context_ref_thread_default ();
+
+ thread = g_thread_try_new ("get-named-dest",
+ get_named_dest_func,
+ data,
+ &error);
+
+ if (!thread)
+ {
+ g_warning ("%s", error->message);
+ callback (NULL, user_data);
+
+ g_error_free (error);
+ get_named_dest_data_free (data);
+ }
+ else
+ {
+ g_thread_unref (thread);
+ }
+}
+
+typedef struct
+{
+ GCancellable *cancellable;
+ PAOCallback callback;
+ gpointer user_data;
+} PAOData;
+
+static void
+printer_add_option_async_dbus_cb (GObject *source_object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GVariant *output;
+ gboolean success = FALSE;
+ PAOData *data = (PAOData *) 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
+ success = TRUE;
+
+ g_variant_unref (output);
+ }
+ else
+ {
+ if (error->code != G_IO_ERROR_CANCELLED)
+ g_warning ("%s", error->message);
+ g_error_free (error);
+ }
+
+ data->callback (success, data->user_data);
+
+ if (data->cancellable)
+ g_object_unref (data->cancellable);
+ g_free (data);
+}
+
+void
+printer_add_option_async (const gchar *printer_name,
+ const gchar *option_name,
+ gchar **values,
+ gboolean set_default,
+ GCancellable *cancellable,
+ PAOCallback callback,
+ gpointer user_data)
+{
+ GVariantBuilder array_builder;
+ GDBusConnection *bus;
+ PAOData *data;
+ GError *error = NULL;
+ gint i;
+
+ 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);
+ callback (FALSE, user_data);
+ return;
+ }
+
+ g_variant_builder_init (&array_builder, G_VARIANT_TYPE ("as"));
+ if (values)
+ {
+ for (i = 0; values[i]; i++)
+ g_variant_builder_add (&array_builder, "s", values[i]);
+ }
+
+ data = g_new0 (PAOData, 1);
+ data->cancellable = cancellable;
+ data->callback = callback;
+ data->user_data = user_data;
+
+ g_dbus_connection_call (bus,
+ MECHANISM_BUS,
+ "/",
+ MECHANISM_BUS,
+ set_default ? "PrinterAddOptionDefault" :
+ "PrinterAddOption",
+ g_variant_new ("(ssas)",
+ printer_name,
+ option_name,
+ &array_builder),
+ G_VARIANT_TYPE ("(s)"),
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+ cancellable,
+ printer_add_option_async_dbus_cb,
+ data);
+}
diff --git a/panels/printers/pp-utils.h b/panels/printers/pp-utils.h
index 8eeaced..5b4b71e 100644
--- a/panels/printers/pp-utils.h
+++ b/panels/printers/pp-utils.h
@@ -206,6 +206,31 @@ void ipp_attribute_free (IPPAttribute *attr);
gchar *get_standard_manufacturers_name (gchar *name);
+typedef void (*PGPCallback) (const gchar *ppd_filename,
+ gpointer user_data);
+
+void printer_get_ppd_async (const gchar *printer_name,
+ PGPCallback callback,
+ gpointer user_data);
+
+typedef void (*GNDCallback) (cups_dest_t *destination,
+ gpointer user_data);
+
+void get_named_dest_async (const gchar *printer_name,
+ GNDCallback callback,
+ gpointer user_data);
+
+typedef void (*PAOCallback) (gboolean success,
+ gpointer user_data);
+
+void printer_add_option_async (const gchar *printer_name,
+ const gchar *option_name,
+ gchar **values,
+ gboolean set_default,
+ GCancellable *cancellable,
+ PAOCallback callback,
+ gpointer user_data);
+
G_END_DECLS
#endif /* __PP_UTILS_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]