printers: Allow users to change printer's PPD file

This commit adds popup window which when activated allows
user to select PPD from local database of installed PPDs,
select directly PPD from filesystem or select one from 3
recommended PPDs (#678637).
The popup is activated by clicking on model field (the panel
has to be unlocked). It starts to search for the best PPDs
available immediately after its popup.
All operations are asynchronous.
This commit is contained in:
Marek Kasik 2012-07-16 11:10:02 +02:00
parent 1b3e877ef2
commit dd7479caea
8 changed files with 3119 additions and 33 deletions

View file

@ -3,6 +3,7 @@ cappletname = printers
uidir = $(pkgdatadir)/ui/printers
dist_ui_DATA = \
new-printer-dialog.ui \
ppd-selection-dialog.ui \
printers.ui
INCLUDES = \
@ -18,12 +19,14 @@ ccpanelsdir = $(PANELS_DIR)
ccpanels_LTLIBRARIES = libprinters.la
libprinters_la_SOURCES = \
printers-module.c \
pp-utils.c \
pp-utils.h \
pp-new-printer-dialog.c \
pp-new-printer-dialog.h \
cc-printers-panel.c \
printers-module.c \
pp-utils.c \
pp-utils.h \
pp-new-printer-dialog.c \
pp-new-printer-dialog.h \
pp-ppd-selection-dialog.c \
pp-ppd-selection-dialog.h \
cc-printers-panel.c \
cc-printers-panel.h
libprinters_la_LIBADD = $(PRINTERS_PANEL_LIBS) $(PANEL_LIBS) $(CUPS_LIBS)

View file

@ -33,6 +33,7 @@
#include "cc-editable-entry.h"
#include "pp-new-printer-dialog.h"
#include "pp-ppd-selection-dialog.h"
#include "pp-utils.h"
G_DEFINE_DYNAMIC_TYPE (CcPrintersPanel, cc_printers_panel, CC_TYPE_PANEL)
@ -83,6 +84,7 @@ struct _CcPrintersPanelPrivate
GSettings *lockdown_settings;
PpNewPrinterDialog *pp_new_printer_dialog;
PpPPDSelectionDialog *pp_ppd_selection_dialog;
GDBusProxy *cups_proxy;
GDBusConnection *cups_bus_connection;
@ -91,13 +93,21 @@ struct _CcPrintersPanelPrivate
guint cups_status_check_id;
guint dbus_subscription_id;
GtkWidget *popup_menu;
GList *driver_change_list;
GCancellable *get_ppd_name_cancellable;
gboolean getting_ppd_names;
PPDList *all_ppds_list;
GHashTable *preferred_drivers;
gboolean getting_all_ppds;
gpointer dummy;
};
static void actualize_jobs_list (CcPrintersPanel *self);
static void actualize_printers_list (CcPrintersPanel *self);
static void actualize_allowed_users_list (CcPrintersPanel *self);
static void actualize_sensitivity (gpointer user_data);
static void update_sensitivity (gpointer user_data);
static void printer_disable_cb (GObject *gobject, GParamSpec *pspec, gpointer user_data);
static void printer_set_default_cb (GtkToggleButton *button, gpointer user_data);
static void detach_from_cups_notifier (gpointer data);
@ -180,7 +190,22 @@ cc_printers_panel_dispose (GObject *object)
detach_from_cups_notifier (CC_PRINTERS_PANEL (object));
if (priv->cups_status_check_id > 0)
g_source_remove (priv->cups_status_check_id);
{
g_source_remove (priv->cups_status_check_id);
priv->cups_status_check_id = 0;
}
if (priv->all_ppds_list)
{
ppd_list_free (priv->all_ppds_list);
priv->all_ppds_list = NULL;
}
if (priv->preferred_drivers)
{
g_hash_table_unref (priv->preferred_drivers);
priv->preferred_drivers = NULL;
}
G_OBJECT_CLASS (cc_printers_panel_parent_class)->dispose (object);
}
@ -502,6 +527,8 @@ printer_selection_changed_cb (GtkTreeSelection *selection,
cups_ptype_t type = 0;
GtkTreeIter iter;
GtkWidget *widget;
GtkWidget *model_button;
GtkWidget *model_label;
gboolean sensitive;
GValue value = G_VALUE_INIT;
gchar *printer_make_and_model = NULL;
@ -814,16 +841,23 @@ printer_selection_changed_cb (GtkTreeSelection *selection,
cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), EMPTY_TEXT);
widget = (GtkWidget*)
model_button = (GtkWidget*)
gtk_builder_get_object (priv->builder, "printer-model-button");
model_label = (GtkWidget*)
gtk_builder_get_object (priv->builder, "printer-model-label");
if (printer_model)
{
cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), printer_model);
gtk_button_set_label (GTK_BUTTON (model_button), printer_model);
gtk_label_set_text (GTK_LABEL (model_label), printer_model);
g_free (printer_model);
}
else
cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), EMPTY_TEXT);
{
gtk_button_set_label (GTK_BUTTON (model_button), EMPTY_TEXT);
gtk_label_set_text (GTK_LABEL (model_label), EMPTY_TEXT);
}
widget = (GtkWidget*)
@ -917,9 +951,13 @@ printer_selection_changed_cb (GtkTreeSelection *selection,
gtk_builder_get_object (priv->builder, "printer-location-label");
cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), "");
widget = (GtkWidget*)
gtk_builder_get_object (priv->builder, "printer-model-button");
gtk_button_set_label (GTK_BUTTON (widget), "");
widget = (GtkWidget*)
gtk_builder_get_object (priv->builder, "printer-model-label");
cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), "");
gtk_label_set_text (GTK_LABEL (widget), "");
widget = (GtkWidget*)
gtk_builder_get_object (priv->builder, "printer-ip-address-label");
@ -930,7 +968,7 @@ printer_selection_changed_cb (GtkTreeSelection *selection,
cc_editable_entry_set_text (CC_EDITABLE_ENTRY (widget), "");
}
actualize_sensitivity (self);
update_sensitivity (self);
}
static void
@ -1157,7 +1195,7 @@ actualize_printers_list (CcPrintersPanel *self)
g_free (current_printer_instance);
g_object_unref (store);
actualize_sensitivity (self);
update_sensitivity (self);
}
static void
@ -2179,6 +2217,475 @@ printer_location_edit_cb (GtkWidget *entry,
actualize_printers_list (self);
}
static void
set_ppd_cb (gchar *printer_name,
gboolean success,
gpointer user_data)
{
CcPrintersPanelPrivate *priv;
CcPrintersPanel *self = (CcPrintersPanel*) user_data;
GList *iter;
priv = PRINTERS_PANEL_PRIVATE (self);
for (iter = priv->driver_change_list; iter; iter = iter->next)
{
if (g_strcmp0 ((gchar *) iter->data, printer_name) == 0)
{
priv->driver_change_list = g_list_remove_link (priv->driver_change_list, iter);
g_list_free_full (iter, g_free);
break;
}
}
update_sensitivity (self);
if (success)
{
actualize_printers_list (self);
}
g_free (printer_name);
}
static void
select_ppd_manually (GtkMenuItem *menuitem,
gpointer user_data)
{
CcPrintersPanelPrivate *priv;
CcPrintersPanel *self = (CcPrintersPanel*) user_data;
GtkFileFilter *filter;
GtkWidget *dialog;
gchar *printer_name = NULL;
priv = PRINTERS_PANEL_PRIVATE (self);
gtk_menu_shell_cancel (GTK_MENU_SHELL (priv->popup_menu));
dialog = gtk_file_chooser_dialog_new (_("Select PPD File"),
NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
NULL);
filter = gtk_file_filter_new ();
gtk_file_filter_set_name (filter,
_("PostScript Printer Description files (*.ppd, *.PPD, *.ppd.gz, *.PPD.gz, *.PPD.GZ)"));
gtk_file_filter_add_pattern (filter, "*.ppd");
gtk_file_filter_add_pattern (filter, "*.PPD");
gtk_file_filter_add_pattern (filter, "*.ppd.gz");
gtk_file_filter_add_pattern (filter, "*.PPD.gz");
gtk_file_filter_add_pattern (filter, "*.PPD.GZ");
gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (dialog), filter);
if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
{
gchar *ppd_filename;
ppd_filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
if (priv->current_dest >= 0 &&
priv->current_dest < priv->num_dests &&
priv->dests != NULL)
printer_name = priv->dests[priv->current_dest].name;
if (printer_name && ppd_filename)
{
priv->driver_change_list =
g_list_prepend (priv->driver_change_list, g_strdup (printer_name));
update_sensitivity (self);
printer_set_ppd_file_async (printer_name,
ppd_filename,
NULL,
set_ppd_cb,
user_data);
}
g_free (ppd_filename);
}
gtk_widget_destroy (dialog);
}
static void
ppd_selection_dialog_response_cb (GtkDialog *dialog,
gint response_id,
gpointer user_data)
{
CcPrintersPanelPrivate *priv;
CcPrintersPanel *self = (CcPrintersPanel*) user_data;
gchar *printer_name = NULL;
priv = PRINTERS_PANEL_PRIVATE (self);
if (response_id == GTK_RESPONSE_OK)
{
gchar *ppd_name;
ppd_name = pp_ppd_selection_dialog_get_ppd_name (priv->pp_ppd_selection_dialog);
if (priv->current_dest >= 0 &&
priv->current_dest < priv->num_dests &&
priv->dests != NULL)
printer_name = priv->dests[priv->current_dest].name;
if (printer_name && ppd_name)
{
priv->driver_change_list = g_list_prepend (priv->driver_change_list,
g_strdup (printer_name));
update_sensitivity (self);
printer_set_ppd_async (printer_name,
ppd_name,
NULL,
set_ppd_cb,
user_data);
}
g_free (ppd_name);
}
pp_ppd_selection_dialog_free (priv->pp_ppd_selection_dialog);
priv->pp_ppd_selection_dialog = NULL;
}
static void
select_ppd_in_dialog (GtkMenuItem *menuitem,
gpointer user_data)
{
CcPrintersPanelPrivate *priv;
CcPrintersPanel *self = (CcPrintersPanel*) user_data;
GtkWidget *widget;
gchar *device_id = NULL;
gchar *manufacturer = NULL;
priv = PRINTERS_PANEL_PRIVATE (self);
widget = (GtkWidget*)
gtk_builder_get_object (priv->builder, "main-vbox");
if (!priv->pp_ppd_selection_dialog)
{
if (priv->current_dest >= 0 &&
priv->current_dest < priv->num_dests)
{
device_id =
get_ppd_attribute (priv->ppd_file_names[priv->current_dest],
"1284DeviceID");
if (device_id)
{
manufacturer = get_tag_value (device_id, "mfg");
if (!manufacturer)
manufacturer = get_tag_value (device_id, "manufacturer");
}
else
{
manufacturer =
get_ppd_attribute (priv->ppd_file_names[priv->current_dest],
"Manufacturer");
}
}
priv->pp_ppd_selection_dialog = pp_ppd_selection_dialog_new (
GTK_WINDOW (gtk_widget_get_toplevel (widget)),
priv->all_ppds_list,
manufacturer,
ppd_selection_dialog_response_cb,
self);
g_free (manufacturer);
g_free (device_id);
}
}
static void
set_ppd_from_list (GtkMenuItem *menuitem,
gpointer user_data)
{
CcPrintersPanelPrivate *priv;
CcPrintersPanel *self = (CcPrintersPanel*) user_data;
gchar *printer_name = NULL;
gchar *ppd_name;
priv = PRINTERS_PANEL_PRIVATE (self);
ppd_name = (gchar *) g_object_get_data (G_OBJECT (menuitem), "ppd-name");
if (priv->current_dest >= 0 &&
priv->current_dest < priv->num_dests &&
priv->dests != NULL)
printer_name = priv->dests[priv->current_dest].name;
if (printer_name && ppd_name)
{
priv->driver_change_list = g_list_prepend (priv->driver_change_list,
g_strdup (printer_name));
update_sensitivity (self);
printer_set_ppd_async (printer_name,
ppd_name,
NULL,
set_ppd_cb,
user_data);
}
}
static void
ppd_names_free (gpointer user_data)
{
PPDName **names = (PPDName **) user_data;
gint i;
if (names)
{
for (i = 0; names[i]; i++)
{
g_free (names[i]->ppd_name);
g_free (names[i]->ppd_display_name);
g_free (names[i]);
}
g_free (names);
}
}
static void
get_ppd_names_cb (PPDName **names,
const gchar *printer_name,
gboolean cancelled,
gpointer user_data)
{
CcPrintersPanelPrivate *priv;
CcPrintersPanel *self = (CcPrintersPanel*) user_data;
GtkWidget *informal = NULL;
GtkWidget *placeholders[3];
GtkWidget *spinner;
gpointer value = NULL;
gboolean found = FALSE;
PPDName **hash_names = NULL;
GList *children, *iter;
gint i;
priv = PRINTERS_PANEL_PRIVATE (self);
priv->getting_ppd_names = FALSE;
for (i = 0; i < 3; i++)
placeholders[i] = NULL;
children = gtk_container_get_children (GTK_CONTAINER (priv->popup_menu));
if (children)
{
for (iter = children; iter; iter = iter->next)
{
if (g_strcmp0 ((gchar *) g_object_get_data (G_OBJECT (iter->data), "purpose"),
"informal") == 0)
informal = GTK_WIDGET (iter->data);
else if (g_strcmp0 ((gchar *) g_object_get_data (G_OBJECT (iter->data), "purpose"),
"placeholder1") == 0)
placeholders[0] = GTK_WIDGET (iter->data);
else if (g_strcmp0 ((gchar *) g_object_get_data (G_OBJECT (iter->data), "purpose"),
"placeholder2") == 0)
placeholders[1] = GTK_WIDGET (iter->data);
else if (g_strcmp0 ((gchar *) g_object_get_data (G_OBJECT (iter->data), "purpose"),
"placeholder3") == 0)
placeholders[2] = GTK_WIDGET (iter->data);
}
g_list_free (children);
}
if (!priv->preferred_drivers)
{
priv->preferred_drivers = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, ppd_names_free);
}
if (!cancelled &&
!g_hash_table_lookup_extended (priv->preferred_drivers,
printer_name, NULL, NULL))
g_hash_table_insert (priv->preferred_drivers, g_strdup (printer_name), names);
if (priv->preferred_drivers &&
g_hash_table_lookup_extended (priv->preferred_drivers,
printer_name, NULL, &value))
{
hash_names = (PPDName **) value;
if (hash_names)
{
for (i = 0; hash_names[i]; i++)
{
if (placeholders[i])
{
gtk_menu_item_set_label (GTK_MENU_ITEM (placeholders[i]),
hash_names[i]->ppd_display_name);
g_object_set_data_full (G_OBJECT (placeholders[i]),
"ppd-name",
g_strdup (hash_names[i]->ppd_name),
g_free);
g_signal_connect (placeholders[i],
"activate",
G_CALLBACK (set_ppd_from_list),
self);
gtk_widget_set_sensitive (GTK_WIDGET (placeholders[i]), TRUE);
gtk_widget_show (placeholders[i]);
}
}
found = TRUE;
}
else
{
found = FALSE;
}
}
if (informal)
{
gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (informal), FALSE);
spinner = gtk_image_menu_item_get_image (GTK_IMAGE_MENU_ITEM (informal));
if (spinner)
gtk_spinner_stop (GTK_SPINNER (spinner));
if (found)
gtk_widget_hide (informal);
else
gtk_menu_item_set_label (GTK_MENU_ITEM (informal), _("No suitable driver found"));
}
gtk_widget_show_all (priv->popup_menu);
update_sensitivity (self);
}
static void
popup_menu_done (GtkMenuShell *menushell,
gpointer user_data)
{
CcPrintersPanelPrivate *priv;
CcPrintersPanel *self = (CcPrintersPanel*) user_data;
priv = PRINTERS_PANEL_PRIVATE (self);
if (priv->get_ppd_name_cancellable)
{
g_cancellable_cancel (priv->get_ppd_name_cancellable);
g_object_unref (priv->get_ppd_name_cancellable);
priv->get_ppd_name_cancellable = NULL;
}
}
static void
popup_model_menu_cb (GtkButton *button,
gpointer user_data)
{
CcPrintersPanelPrivate *priv;
CcPrintersPanel *self = (CcPrintersPanel*) user_data;
GtkWidget *spinner;
GtkWidget *item;
priv = PRINTERS_PANEL_PRIVATE (self);
priv->popup_menu = gtk_menu_new ();
g_signal_connect (priv->popup_menu,
"selection-done",
G_CALLBACK (popup_menu_done),
user_data);
/*
* These placeholders are a workaround for a situation
* when we want to actually append new menu item in a callback.
* But unfortunately it is not possible to connect to "activate"
* signal of such menu item (appended after gtk_menu_popup()).
*/
item = gtk_image_menu_item_new_with_label ("");
g_object_set_data_full (G_OBJECT (item), "purpose",
g_strdup ("placeholder1"), g_free);
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
gtk_widget_set_no_show_all (item, TRUE);
gtk_widget_hide (item);
item = gtk_image_menu_item_new_with_label ("");
g_object_set_data_full (G_OBJECT (item), "purpose",
g_strdup ("placeholder2"), g_free);
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
gtk_widget_set_no_show_all (item, TRUE);
gtk_widget_hide (item);
item = gtk_image_menu_item_new_with_label ("");
g_object_set_data_full (G_OBJECT (item), "purpose",
g_strdup ("placeholder3"), g_free);
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
gtk_widget_set_no_show_all (item, TRUE);
gtk_widget_hide (item);
item = gtk_image_menu_item_new_with_label (_("Searching for preferred drivers..."));
spinner = gtk_spinner_new ();
gtk_spinner_start (GTK_SPINNER (spinner));
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), spinner);
gtk_image_menu_item_set_always_show_image (GTK_IMAGE_MENU_ITEM (item), TRUE);
g_object_set_data_full (G_OBJECT (item), "purpose",
g_strdup ("informal"), g_free);
gtk_widget_set_sensitive (item, FALSE);
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
gtk_widget_set_no_show_all (item, TRUE);
gtk_widget_show (item);
item = gtk_separator_menu_item_new ();
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
item = gtk_menu_item_new_with_label (_("Select from database..."));
g_object_set_data_full (G_OBJECT (item), "purpose",
g_strdup ("ppd-select"), g_free);
g_signal_connect (item, "activate", G_CALLBACK (select_ppd_in_dialog), self);
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
item = gtk_separator_menu_item_new ();
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
item = gtk_menu_item_new_with_label (_("Provide PPD File..."));
g_object_set_data_full (G_OBJECT (item), "purpose",
g_strdup ("ppdfile-select"), g_free);
g_signal_connect (item, "activate", G_CALLBACK (select_ppd_manually), self);
gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), item);
gtk_widget_show_all (priv->popup_menu);
gtk_menu_popup (GTK_MENU (priv->popup_menu),
NULL, NULL, NULL, NULL, 0,
gtk_get_current_event_time());
if (priv->current_dest >= 0 &&
priv->current_dest < priv->num_dests &&
priv->dests != NULL)
{
if (priv->preferred_drivers &&
g_hash_table_lookup_extended (priv->preferred_drivers,
priv->dests[priv->current_dest].name,
NULL, NULL))
{
get_ppd_names_cb (NULL,
priv->dests[priv->current_dest].name,
FALSE,
user_data);
}
else
{
priv->get_ppd_name_cancellable = g_cancellable_new ();
priv->getting_ppd_names = TRUE;
get_ppd_names_async (priv->dests[priv->current_dest].name,
3,
priv->get_ppd_name_cancellable,
get_ppd_names_cb,
user_data);
update_sensitivity (self);
}
}
}
static void
test_page_cb (GtkButton *button,
gpointer user_data)
@ -2303,7 +2810,7 @@ test_page_cb (GtkButton *button,
}
static void
actualize_sensitivity (gpointer user_data)
update_sensitivity (gpointer user_data)
{
CcPrintersPanelPrivate *priv;
CcPrintersPanel *self = (CcPrintersPanel*) user_data;
@ -2312,9 +2819,12 @@ actualize_sensitivity (gpointer user_data)
GtkWidget *widget;
gboolean is_authorized;
gboolean is_discovered = FALSE;
gboolean is_class = FALSE;
gboolean is_changing_driver = FALSE;
gboolean printer_selected;
gboolean local_server = TRUE;
gboolean no_cups = FALSE;
GList *iter;
gint i;
priv = PRINTERS_PANEL_PRIVATE (self);
@ -2330,15 +2840,26 @@ actualize_sensitivity (gpointer user_data)
priv->dests != NULL;
if (printer_selected)
for (i = 0; i < priv->dests[priv->current_dest].num_options; i++)
{
if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "printer-type") == 0)
{
type = atoi (priv->dests[priv->current_dest].options[i].value);
is_discovered = type & CUPS_PRINTER_DISCOVERED;
break;
}
}
{
for (i = 0; i < priv->dests[priv->current_dest].num_options; i++)
{
if (g_strcmp0 (priv->dests[priv->current_dest].options[i].name, "printer-type") == 0)
{
type = atoi (priv->dests[priv->current_dest].options[i].value);
is_discovered = type & CUPS_PRINTER_DISCOVERED;
is_class = type & CUPS_PRINTER_CLASS;
break;
}
}
for (iter = priv->driver_change_list; iter; iter = iter->next)
{
if (g_strcmp0 ((gchar *) iter->data, priv->dests[priv->current_dest].name) == 0)
{
is_changing_driver = TRUE;
}
}
}
cups_server = cupsServer ();
if (cups_server &&
@ -2390,6 +2911,19 @@ actualize_sensitivity (gpointer user_data)
widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-location-label");
cc_editable_entry_set_editable (CC_EDITABLE_ENTRY (widget), local_server && !is_discovered && is_authorized);
widget = (GtkWidget*) gtk_builder_get_object (priv->builder, "printer-model-notebook");
if (is_changing_driver)
{
gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 2);
}
else
{
if (local_server && !is_discovered && is_authorized && !is_class && !priv->getting_ppd_names)
gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 0);
else
gtk_notebook_set_current_page (GTK_NOTEBOOK (widget), 1);
}
}
static void
@ -2397,7 +2931,7 @@ on_permission_changed (GPermission *permission,
GParamSpec *pspec,
gpointer data)
{
actualize_sensitivity (data);
update_sensitivity (data);
}
static void
@ -2490,6 +3024,56 @@ cups_status_check (gpointer user_data)
return result;
}
static void
get_all_ppds_async_cb (PPDList *ppds,
gpointer user_data)
{
CcPrintersPanelPrivate *priv;
CcPrintersPanel *self = (CcPrintersPanel*) user_data;
priv = self->priv = PRINTERS_PANEL_PRIVATE (self);
priv->all_ppds_list = ppds;
priv->getting_all_ppds = FALSE;
if (priv->pp_ppd_selection_dialog)
pp_ppd_selection_dialog_set_ppd_list (priv->pp_ppd_selection_dialog,
priv->all_ppds_list);
}
static void
update_label_padding (GtkWidget *widget,
GtkAllocation *allocation,
gpointer user_data)
{
CcPrintersPanelPrivate *priv;
CcPrintersPanel *self = (CcPrintersPanel*) user_data;
GtkAllocation allocation1, allocation2;
GtkWidget *label;
GtkWidget *sublabel;
gint offset;
gint pad;
priv = PRINTERS_PANEL_PRIVATE (self);
sublabel = gtk_bin_get_child (GTK_BIN (widget));
if (sublabel)
{
gtk_widget_get_allocation (widget, &allocation1);
gtk_widget_get_allocation (sublabel, &allocation2);
offset = allocation2.x - allocation1.x;
label = (GtkWidget*)
gtk_builder_get_object (priv->builder, "printer-model-label");
gtk_misc_get_padding (GTK_MISC (label), &pad, NULL);
if (offset != pad)
gtk_misc_set_padding (GTK_MISC (label), offset, 0);
}
}
static void
cc_printers_panel_init (CcPrintersPanel *self)
{
@ -2532,6 +3116,13 @@ cc_printers_panel_init (CcPrintersPanel *self)
priv->permission = NULL;
priv->lockdown_settings = NULL;
priv->getting_ppd_names = FALSE;
priv->all_ppds_list = NULL;
priv->getting_all_ppds = FALSE;
priv->preferred_drivers = NULL;
builder_result = gtk_builder_add_objects_from_file (priv->builder,
DATADIR"/printers.ui",
objects, &error);
@ -2628,6 +3219,11 @@ cc_printers_panel_init (CcPrintersPanel *self)
G_CALLBACK (on_lockdown_settings_changed),
self);
widget = (GtkWidget*)
gtk_builder_get_object (priv->builder, "printer-model-button");
g_signal_connect (widget, "clicked", G_CALLBACK (popup_model_menu_cb), self);
g_signal_connect (widget, "size-allocate", G_CALLBACK (update_label_padding), self);
/* Set junctions */
widget = (GtkWidget*)
@ -2666,10 +3262,6 @@ cc_printers_panel_init (CcPrintersPanel *self)
gtk_builder_get_object (priv->builder, "printer-ip-address-label");
cc_editable_entry_set_selectable (CC_EDITABLE_ENTRY (widget), TRUE);
widget = (GtkWidget*)
gtk_builder_get_object (priv->builder, "printer-model-label");
cc_editable_entry_set_selectable (CC_EDITABLE_ENTRY (widget), TRUE);
/* Add unlock button */
priv->permission = (GPermission *)polkit_permission_new_sync (
@ -2694,6 +3286,9 @@ Please check your installation");
populate_allowed_users_list (self);
attach_to_cups_notifier (self);
priv->getting_all_ppds = TRUE;
get_all_ppds_async (get_all_ppds_async_cb, self);
http = httpConnectEncrypt (cupsServer (), ippPort (), cupsEncryption ());
if (!http)
{

View file

@ -0,0 +1,444 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright 2012 Red Hat, Inc,
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Author: Marek Kasik <mkasik@redhat.com>
*/
#include "config.h"
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <gtk/gtk.h>
#include <cups/cups.h>
#include <cups/ppd.h>
#include "pp-ppd-selection-dialog.h"
static void pp_ppd_selection_dialog_hide (PpPPDSelectionDialog *dialog);
enum
{
PPD_NAMES_COLUMN = 0,
PPD_DISPLAY_NAMES_COLUMN
};
enum
{
PPD_MANUFACTURERS_NAMES_COLUMN = 0,
PPD_MANUFACTURERS_DISPLAY_NAMES_COLUMN
};
struct _PpPPDSelectionDialog {
GtkBuilder *builder;
GtkWidget *parent;
GtkWidget *dialog;
UserResponseCallback user_callback;
gpointer user_data;
gchar *ppd_name;
GtkResponseType response;
gchar *manufacturer;
PPDList *list;
};
static void
manufacturer_selection_changed_cb (GtkTreeSelection *selection,
gpointer user_data)
{
PpPPDSelectionDialog *dialog = (PpPPDSelectionDialog *) user_data;
GtkListStore *store;
GtkTreeModel *model;
GtkTreeIter iter;
GtkTreeView *models_treeview;
gchar *manufacturer_name = NULL;
gint i, index;
if (gtk_tree_selection_get_selected (selection, &model, &iter))
{
gtk_tree_model_get (model, &iter,
PPD_MANUFACTURERS_NAMES_COLUMN, &manufacturer_name,
-1);
}
if (manufacturer_name)
{
index = -1;
for (i = 0; i < dialog->list->num_of_manufacturers; i++)
{
if (g_strcmp0 (manufacturer_name,
dialog->list->manufacturers[i]->manufacturer_name) == 0)
{
index = i;
break;
}
}
if (index >= 0)
{
models_treeview = (GtkTreeView*)
gtk_builder_get_object (dialog->builder, "ppd-selection-models-treeview");
store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
for (i = 0; i < dialog->list->manufacturers[index]->num_of_ppds; i++)
{
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
PPD_NAMES_COLUMN, dialog->list->manufacturers[index]->ppds[i]->ppd_name,
PPD_DISPLAY_NAMES_COLUMN, dialog->list->manufacturers[index]->ppds[i]->ppd_display_name,
-1);
}
gtk_tree_view_set_model (models_treeview, GTK_TREE_MODEL (store));
g_object_unref (store);
}
g_free (manufacturer_name);
}
}
static void
model_selection_changed_cb (GtkTreeSelection *selection,
gpointer user_data)
{
PpPPDSelectionDialog *dialog = (PpPPDSelectionDialog *) user_data;
GtkTreeModel *model;
GtkTreeIter iter;
GtkWidget *widget;
gchar *model_name = NULL;
if (gtk_tree_selection_get_selected (selection, &model, &iter))
{
gtk_tree_model_get (model, &iter,
PPD_NAMES_COLUMN, &model_name,
-1);
}
widget = (GtkWidget*)
gtk_builder_get_object (dialog->builder, "ppd-selection-select-button");
if (model_name)
{
gtk_widget_set_sensitive (widget, TRUE);
g_free (model_name);
}
else
{
gtk_widget_set_sensitive (widget, FALSE);
}
}
static void
fill_ppds_list (PpPPDSelectionDialog *dialog)
{
GtkTreeSelection *selection;
GtkListStore *store;
GtkTreePath *path;
GtkTreeView *treeview;
GtkTreeIter iter;
GtkTreeIter *preselect_iter = NULL;
GtkWidget *widget;
gint i;
widget = (GtkWidget*)
gtk_builder_get_object (dialog->builder, "ppd-spinner");
gtk_widget_hide (widget);
gtk_spinner_stop (GTK_SPINNER (widget));
widget = (GtkWidget*)
gtk_builder_get_object (dialog->builder, "progress-label");
gtk_widget_hide (widget);
treeview = (GtkTreeView*)
gtk_builder_get_object (dialog->builder, "ppd-selection-manufacturers-treeview");
if (dialog->list)
{
store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
for (i = 0; i < dialog->list->num_of_manufacturers; i++)
{
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
PPD_MANUFACTURERS_NAMES_COLUMN, dialog->list->manufacturers[i]->manufacturer_name,
PPD_MANUFACTURERS_DISPLAY_NAMES_COLUMN, dialog->list->manufacturers[i]->manufacturer_display_name,
-1);
if (g_strcmp0 (dialog->manufacturer,
dialog->list->manufacturers[i]->manufacturer_display_name) == 0)
{
preselect_iter = gtk_tree_iter_copy (&iter);
}
}
gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (store));
if (preselect_iter &&
(selection = gtk_tree_view_get_selection (treeview)) != NULL)
{
gtk_tree_selection_select_iter (selection, preselect_iter);
path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), preselect_iter);
gtk_tree_view_scroll_to_cell (treeview, path, NULL, TRUE, 0.5, 0.0);
gtk_tree_path_free (path);
gtk_tree_iter_free (preselect_iter);
}
g_object_unref (store);
}
}
static void
populate_dialog (PpPPDSelectionDialog *dialog)
{
GtkTreeViewColumn *column;
GtkCellRenderer *renderer;
GtkTreeView *manufacturers_treeview;
GtkTreeView *models_treeview;
GtkWidget *widget;
manufacturers_treeview = (GtkTreeView*)
gtk_builder_get_object (dialog->builder, "ppd-selection-manufacturers-treeview");
renderer = gtk_cell_renderer_text_new ();
/* Translators: Name of column showing printer manufacturers */
column = gtk_tree_view_column_new_with_attributes (_("Manufacturers"), renderer,
"text", PPD_MANUFACTURERS_DISPLAY_NAMES_COLUMN, NULL);
gtk_tree_view_column_set_expand (column, TRUE);
gtk_tree_view_append_column (manufacturers_treeview, column);
models_treeview = (GtkTreeView*)
gtk_builder_get_object (dialog->builder, "ppd-selection-models-treeview");
renderer = gtk_cell_renderer_text_new ();
/* Translators: Name of column showing printer drivers */
column = gtk_tree_view_column_new_with_attributes (_("Drivers"), renderer,
"text", PPD_DISPLAY_NAMES_COLUMN, NULL);
gtk_tree_view_column_set_expand (column, TRUE);
gtk_tree_view_append_column (models_treeview, column);
g_signal_connect (gtk_tree_view_get_selection (models_treeview),
"changed", G_CALLBACK (model_selection_changed_cb), dialog);
g_signal_connect (gtk_tree_view_get_selection (manufacturers_treeview),
"changed", G_CALLBACK (manufacturer_selection_changed_cb), dialog);
gtk_widget_show_all (dialog->dialog);
if (!dialog->list)
{
widget = (GtkWidget*)
gtk_builder_get_object (dialog->builder, "ppd-spinner");
gtk_widget_show (widget);
gtk_spinner_start (GTK_SPINNER (widget));
widget = (GtkWidget*)
gtk_builder_get_object (dialog->builder, "progress-label");
gtk_widget_show (widget);
}
else
{
fill_ppds_list (dialog);
}
}
static void
ppd_selection_dialog_response_cb (GtkDialog *dialog,
gint response_id,
gpointer user_data)
{
PpPPDSelectionDialog *ppd_selection_dialog = (PpPPDSelectionDialog*) user_data;
GtkTreeSelection *selection;
GtkTreeModel *model;
GtkTreeView *models_treeview;
GtkTreeIter iter;
pp_ppd_selection_dialog_hide (ppd_selection_dialog);
ppd_selection_dialog->response = response_id;
if (response_id == GTK_RESPONSE_OK)
{
models_treeview = (GtkTreeView*)
gtk_builder_get_object (ppd_selection_dialog->builder, "ppd-selection-models-treeview");
if (models_treeview)
{
selection = gtk_tree_view_get_selection (models_treeview);
if (selection)
{
if (gtk_tree_selection_get_selected (selection, &model, &iter))
{
gtk_tree_model_get (model, &iter,
PPD_NAMES_COLUMN, &ppd_selection_dialog->ppd_name,
-1);
}
}
}
}
ppd_selection_dialog->user_callback (GTK_DIALOG (ppd_selection_dialog->dialog),
response_id,
ppd_selection_dialog->user_data);
}
static void
update_alignment_padding (GtkWidget *widget,
GtkAllocation *allocation,
gpointer user_data)
{
PpPPDSelectionDialog *dialog = (PpPPDSelectionDialog*) user_data;
GtkAllocation allocation2;
GtkWidget *action_area;
gint offset_left, offset_right;
guint padding_left, padding_right,
padding_top, padding_bottom;
action_area = (GtkWidget*)
gtk_builder_get_object (dialog->builder, "dialog-action-area1");
gtk_widget_get_allocation (action_area, &allocation2);
offset_left = allocation2.x - allocation->x;
offset_right = (allocation->x + allocation->width) -
(allocation2.x + allocation2.width);
gtk_alignment_get_padding (GTK_ALIGNMENT (widget),
&padding_top, &padding_bottom,
&padding_left, &padding_right);
if (allocation->x >= 0 && allocation2.x >= 0)
{
if (offset_left > 0 && offset_left != padding_left)
gtk_alignment_set_padding (GTK_ALIGNMENT (widget),
padding_top, padding_bottom,
offset_left, padding_right);
gtk_alignment_get_padding (GTK_ALIGNMENT (widget),
&padding_top, &padding_bottom,
&padding_left, &padding_right);
if (offset_right > 0 && offset_right != padding_right)
gtk_alignment_set_padding (GTK_ALIGNMENT (widget),
padding_top, padding_bottom,
padding_left, offset_right);
}
}
PpPPDSelectionDialog *
pp_ppd_selection_dialog_new (GtkWindow *parent,
PPDList *ppd_list,
gchar *manufacturer,
UserResponseCallback user_callback,
gpointer user_data)
{
PpPPDSelectionDialog *dialog;
GtkWidget *widget;
GError *error = NULL;
gchar *objects[] = { "ppd-selection-dialog", NULL };
guint builder_result;
dialog = g_new0 (PpPPDSelectionDialog, 1);
dialog->builder = gtk_builder_new ();
dialog->parent = GTK_WIDGET (parent);
builder_result = gtk_builder_add_objects_from_file (dialog->builder,
DATADIR"/ppd-selection-dialog.ui",
objects, &error);
if (builder_result == 0)
{
g_warning ("Could not load ui: %s", error->message);
g_error_free (error);
return NULL;
}
dialog->dialog = (GtkWidget *) gtk_builder_get_object (dialog->builder, "ppd-selection-dialog");
dialog->user_callback = user_callback;
dialog->user_data = user_data;
dialog->response = GTK_RESPONSE_NONE;
dialog->list = ppd_list_copy (ppd_list);
dialog->manufacturer = get_standard_manufacturers_name (manufacturer);
/* connect signals */
g_signal_connect (dialog->dialog, "delete-event", G_CALLBACK (gtk_widget_hide_on_delete), NULL);
g_signal_connect (dialog->dialog, "response", G_CALLBACK (ppd_selection_dialog_response_cb), dialog);
widget = (GtkWidget*)
gtk_builder_get_object (dialog->builder, "content-alignment");
g_signal_connect (widget, "size-allocate", G_CALLBACK (update_alignment_padding), dialog);
widget = (GtkWidget*)
gtk_builder_get_object (dialog->builder, "ppd-spinner");
gtk_spinner_start (GTK_SPINNER (widget));
populate_dialog (dialog);
gtk_window_set_transient_for (GTK_WINDOW (dialog->dialog), GTK_WINDOW (parent));
gtk_window_present (GTK_WINDOW (dialog->dialog));
gtk_widget_show_all (GTK_WIDGET (dialog->dialog));
return dialog;
}
void
pp_ppd_selection_dialog_free (PpPPDSelectionDialog *dialog)
{
gtk_widget_destroy (GTK_WIDGET (dialog->dialog));
g_object_unref (dialog->builder);
g_free (dialog->ppd_name);
g_free (dialog->manufacturer);
g_free (dialog);
}
gchar *
pp_ppd_selection_dialog_get_ppd_name (PpPPDSelectionDialog *dialog)
{
return g_strdup (dialog->ppd_name);
}
void
pp_ppd_selection_dialog_set_ppd_list (PpPPDSelectionDialog *dialog,
PPDList *list)
{
dialog->list = list;
fill_ppds_list (dialog);
}
static void
pp_ppd_selection_dialog_hide (PpPPDSelectionDialog *dialog)
{
gtk_widget_hide (GTK_WIDGET (dialog->dialog));
}

View file

@ -0,0 +1,46 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
*
* Copyright 2012 Red Hat, Inc,
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Author: Marek Kasik <mkasik@redhat.com>
*/
#ifndef __PP_PPD_SELECTION_DIALOG_H__
#define __PP_PPD_SELECTION_DIALOG_H__
#include <gtk/gtk.h>
#include "pp-utils.h"
G_BEGIN_DECLS
typedef struct _PpPPDSelectionDialog PpPPDSelectionDialog;
typedef void (*UserResponseCallback) (GtkDialog *dialog, gint response_id, gpointer user_data);
PpPPDSelectionDialog *pp_ppd_selection_dialog_new (GtkWindow *parent,
PPDList *ppd_list,
gchar *manufacturer,
UserResponseCallback user_callback,
gpointer user_data);
gchar *pp_ppd_selection_dialog_get_ppd_name (PpPPDSelectionDialog *dialog);
void pp_ppd_selection_dialog_set_ppd_list (PpPPDSelectionDialog *dialog,
PPDList *list);
void pp_ppd_selection_dialog_free (PpPPDSelectionDialog *dialog);
G_END_DECLS
#endif

File diff suppressed because it is too large Load diff

View file

@ -40,9 +40,24 @@ enum
typedef struct
{
gchar *ppd_name;
gchar *ppd_display_name;
gint ppd_match_level;
} PPDName;
typedef struct
{
gchar *manufacturer_name;
gchar *manufacturer_display_name;
PPDName **ppds;
gsize num_of_ppds;
} PPDManufacturerItem;
typedef struct
{
PPDManufacturerItem **manufacturers;
gsize num_of_manufacturers;
} PPDList;
gchar *get_tag_value (const gchar *tag_string,
const gchar *tag_name);
@ -116,6 +131,81 @@ gchar *printer_get_hostname (cups_ptype_t printer_type,
void printer_set_default_media_size (const gchar *printer_name);
typedef void (*PSPCallback) (gchar *printer_name,
gboolean success,
gpointer user_data);
void printer_set_ppd_async (const gchar *printer_name,
const gchar *ppd_name,
GCancellable *cancellable,
PSPCallback callback,
gpointer user_data);
void printer_set_ppd_file_async (const gchar *printer_name,
const gchar *ppd_filename,
GCancellable *cancellable,
PSPCallback callback,
gpointer user_data);
typedef void (*GPNCallback) (PPDName **names,
const gchar *printer_name,
gboolean cancelled,
gpointer user_data);
void get_ppd_names_async (gchar *printer_name,
gint count,
GCancellable *cancellable,
GPNCallback callback,
gpointer user_data);
typedef void (*GAPCallback) (PPDList *ppds,
gpointer user_data);
void get_all_ppds_async (GAPCallback callback,
gpointer user_data);
PPDList *ppd_list_copy (PPDList *list);
void ppd_list_free (PPDList *list);
enum
{
IPP_ATTRIBUTE_TYPE_INTEGER = 0,
IPP_ATTRIBUTE_TYPE_STRING,
IPP_ATTRIBUTE_TYPE_RANGE,
IPP_ATTRIBUTE_TYPE_BOOLEAN
};
typedef struct
{
gboolean boolean_value;
gchar *string_value;
gint integer_value;
gint lower_range;
gint upper_range;
} IPPAttributeValue;
typedef struct
{
gchar *attribute_name;
IPPAttributeValue *attribute_values;
gint num_of_values;
gint attribute_type;
} IPPAttribute;
typedef void (*GIACallback) (GHashTable *table,
gpointer user_data);
void get_ipp_attributes_async (const gchar *printer_name,
gchar **attributes_names,
GIACallback callback,
gpointer user_data);
IPPAttribute *ipp_attribute_copy (IPPAttribute *attr);
void ipp_attribute_free (IPPAttribute *attr);
gchar *get_standard_manufacturers_name (gchar *name);
G_END_DECLS
#endif /* __PP_UTILS_H */

View file

@ -0,0 +1,206 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<!-- interface-requires gtk+ 3.0 -->
<object class="GtkDialog" id="ppd-selection-dialog">
<property name="width_request">500</property>
<property name="height_request">350</property>
<property name="can_focus">False</property>
<property name="border_width">5</property>
<property name="title" translatable="yes"> </property>
<property name="modal">True</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">dialog</property>
<child internal-child="vbox">
<object class="GtkBox" id="main-vbox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<child internal-child="action_area">
<object class="GtkButtonBox" id="dialog-action-area1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="layout_style">end</property>
<child>
<object class="GtkButton" id="ppd-selection-cancel-button">
<property name="label" translatable="yes">Cancel</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="ppd-selection-select-button">
<property name="label" translatable="yes">Select</property>
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="box2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkAlignment" id="alignment1">
<property name="width_request">24</property>
<property name="height_request">24</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkSpinner" id="ppd-spinner">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="progress-label">
<property name="can_focus">False</property>
<property name="no_show_all">True</property>
<property name="label" translatable="yes">Loading drivers database...</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">10</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
<property name="secondary">True</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkAlignment" id="content-alignment">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkBox" id="box1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<property name="spacing">10</property>
<child>
<object class="GtkBox" id="box3">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="width_request">140</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">never</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="ppd-selection-manufacturers-treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection"/>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<child>
<object class="GtkTreeView" id="ppd-selection-models-treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<child internal-child="selection">
<object class="GtkTreeSelection" id="treeview-selection1"/>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="options-title">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Select Printer Driver</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
</object>
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<action-widgets>
<action-widget response="-6">ppd-selection-cancel-button</action-widget>
<action-widget response="-5">ppd-selection-select-button</action-widget>
</action-widgets>
</object>
<object class="GtkSizeGroup" id="sizegroup1"/>
</interface>

View file

@ -315,17 +315,84 @@
</packing>
</child>
<child>
<object class="CcEditableEntry" id="printer-model-label">
<object class="GtkNotebook" id="printer-model-notebook">
<property name="visible">True</property>
<property name="xalign">0</property>
<property name="text">---</property>
<property name="can_focus">True</property>
<property name="show_tabs">False</property>
<child>
<object class="GtkButton" id="printer-model-button">
<property name="use_action_appearance">False</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<property name="relief">none</property>
<property name="xalign">0</property>
</object>
</child>
<child type="tab">
<object class="GtkLabel" id="label7">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">page 1</property>
</object>
<packing>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="printer-model-label">
<property name="visible">True</property>
<property name="selectable">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">label</property>
</object>
<packing>
<property name="position">1</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label13">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">page 2</property>
</object>
<packing>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label17">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="xpad">10</property>
<property name="label" translatable="yes">Setting new driver...</property>
</object>
<packing>
<property name="position">2</property>
</packing>
</child>
<child type="tab">
<object class="GtkLabel" id="label16">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">page 3</property>
</object>
<packing>
<property name="position">2</property>
<property name="tab_fill">False</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="right_attach">3</property>
<property name="top_attach">3</property>
<property name="bottom_attach">4</property>
<property name="y_options">GTK_FILL</property>
</packing>
</child>
<child>