1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3
* Copyright 2012 Red Hat, Inc,
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
* Author: Marek Kasik <mkasik@redhat.com>
26
#include <sys/types.h>
30
#include <glib/gi18n.h>
31
#include <glib/gstdio.h>
34
#include <cups/cups.h>
37
#include "pp-options-dialog.h"
38
#include "pp-ppd-option-widget.h"
39
#include "pp-ipp-option-widget.h"
42
struct _PpOptionsDialog {
48
UserResponseCallback user_callback;
54
gboolean ppd_filename_set;
56
cups_dest_t *destination;
57
gboolean destination_set;
59
GHashTable *ipp_attributes;
60
gboolean ipp_attributes_set;
62
gboolean populating_dialog;
64
GtkResponseType response;
69
static void pp_options_dialog_hide (PpOptionsDialog *dialog);
73
CATEGORY_IDS_COLUMN = 0,
77
/* These lists come from Gtk+ */
80
const char *translation;
81
} ppd_option_translations[] = {
82
{ "Duplex", N_("Two Sided") },
83
{ "MediaType", N_("Paper Type") },
84
{ "InputSlot", N_("Paper Source") },
85
{ "OutputBin", N_("Output Tray") },
86
{ "Resolution", N_("Resolution") },
87
{ "PreFilter", N_("GhostScript pre-filtering") },
90
/* keep sorted when changing */
91
static const char *page_setup_option_whitelist[] = {
98
/* keep sorted when changing */
99
static const char *color_option_whitelist[] = {
100
"BRColorEnhancement",
118
/* keep sorted when changing */
119
static const char *color_group_whitelist[] = {
137
"HPColorOptionsPanel",
138
"HPColorQualityOptionsPanel",
142
/* keep sorted when changing */
143
static const char *image_quality_option_whitelist[] = {
155
"HPGraphicsHalftone",
160
"HPPrintQualityOptions",
161
"HPResolutionOptions",
173
/* keep sorted when changing */
174
static const char *image_quality_group_whitelist[] = {
183
/* keep sorted when changing */
184
static const char * finishing_option_whitelist[] = {
203
/* keep sorted when changing */
204
static const char *job_group_whitelist[] = {
209
/* keep sorted when changing */
210
static const char *finishing_group_whitelist[] = {
213
"BookletModeOptions",
223
"HPFinishingOptions",
227
/* keep sorted when changing */
228
static const char *installable_options_group_whitelist[] = {
229
"InstallableOptions",
232
/* keep sorted when changing */
233
static const char *page_setup_group_whitelist[] = {
241
/* keep sorted when changing */
242
static const char *ppd_option_blacklist[] = {
246
"HPManualDuplexOrientation",
247
"HPManualDuplexSwitch",
253
strptr_cmp (const void *a,
256
char **aa = (char **)a;
257
char **bb = (char **)b;
258
return strcmp (*aa, *bb);
262
string_in_table (gchar *str,
263
const gchar *table[],
266
return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL;
269
#define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table)))
272
ppd_option_name_translate (ppd_option_t *option)
276
for (i = 0; i < G_N_ELEMENTS (ppd_option_translations); i++)
278
if (g_strcmp0 (ppd_option_translations[i].keyword, option->keyword) == 0)
279
return g_strdup (_(ppd_option_translations[i].translation));
282
return g_strdup (option->text);
286
grid_get_height (GtkWidget *grid)
294
children = gtk_container_get_children (GTK_CONTAINER (grid));
295
for (child = children; child; child = g_list_next (child))
297
gtk_container_child_get (GTK_CONTAINER (grid), child->data,
298
"top-attach", &top_attach,
302
if (height + top_attach > max)
303
max = height + top_attach;
306
g_list_free (children);
312
grid_is_empty (GtkWidget *grid)
316
children = gtk_container_get_children (GTK_CONTAINER (grid));
319
g_list_free (children);
329
ipp_option_add (IPPAttribute *attr_supported,
330
IPPAttribute *attr_default,
331
const gchar *option_name,
332
const gchar *option_display_name,
333
const gchar *printer_name,
337
GtkStyleContext *context;
342
widget = (GtkWidget *) pp_ipp_option_widget_new (attr_supported,
348
gtk_widget_set_sensitive (widget, sensitive);
349
position = grid_get_height (grid);
351
label = gtk_label_new (option_display_name);
352
context = gtk_widget_get_style_context (label);
353
gtk_style_context_add_class (context, "dim-label");
354
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
355
gtk_widget_set_margin_left (label, 10);
356
gtk_grid_attach (GTK_GRID (grid), label, 0, position, 1, 1);
358
gtk_widget_set_margin_left (widget, 20);
359
gtk_grid_attach (GTK_GRID (grid), widget, 1, position, 1, 1);
366
ppd_option_add (ppd_option_t option,
367
const gchar *printer_name,
371
GtkStyleContext *context;
376
widget = (GtkWidget *) pp_ppd_option_widget_new (&option, printer_name);
379
gtk_widget_set_sensitive (widget, sensitive);
380
position = grid_get_height (grid);
382
label = gtk_label_new (ppd_option_name_translate (&option));
383
context = gtk_widget_get_style_context (label);
384
gtk_style_context_add_class (context, "dim-label");
385
gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5);
386
gtk_widget_set_margin_left (label, 10);
387
gtk_grid_attach (GTK_GRID (grid), label, 0, position, 1, 1);
389
gtk_widget_set_margin_left (widget, 20);
390
gtk_grid_attach (GTK_GRID (grid), widget, 1, position, 1, 1);
401
grid = gtk_grid_new ();
402
gtk_container_set_border_width (GTK_CONTAINER (grid), 20);
403
gtk_grid_set_row_spacing (GTK_GRID (grid), 15);
409
tab_add (const gchar *tab_name,
410
GtkWidget *options_notebook,
411
GtkTreeView *treeview,
416
GtkWidget *scrolled_window;
417
gboolean unref_store = FALSE;
420
if (!grid_is_empty (grid))
422
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
423
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
425
GTK_POLICY_AUTOMATIC);
426
gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window),
429
id = gtk_notebook_append_page (GTK_NOTEBOOK (options_notebook),
435
store = GTK_LIST_STORE (gtk_tree_view_get_model (treeview));
438
store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING);
442
gtk_list_store_append (store, &iter);
443
gtk_list_store_set (store, &iter,
444
CATEGORY_IDS_COLUMN, id,
445
CATEGORY_NAMES_COLUMN, tab_name,
450
gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (store));
451
g_object_unref (store);
457
g_object_ref_sink (grid);
458
g_object_unref (grid);
463
category_selection_changed_cb (GtkTreeSelection *selection,
466
PpOptionsDialog *dialog = (PpOptionsDialog *) user_data;
469
GtkWidget *options_notebook;
472
if (gtk_tree_selection_get_selected (selection, &model, &iter))
474
gtk_tree_model_get (model, &iter,
475
CATEGORY_IDS_COLUMN, &id,
481
options_notebook = (GtkWidget*)
482
gtk_builder_get_object (dialog->builder, "options-notebook");
484
gtk_notebook_set_current_page (GTK_NOTEBOOK (options_notebook), id);
489
populate_options_real (PpOptionsDialog *dialog)
491
GtkTreeSelection *selection;
493
GtkTreeView *treeview;
495
ppd_file_t *ppd_file;
498
GtkWidget *general_tab_grid = tab_grid_new ();
499
GtkWidget *page_setup_tab_grid = tab_grid_new ();
500
GtkWidget *installable_options_tab_grid = tab_grid_new ();
501
GtkWidget *job_tab_grid = tab_grid_new ();
502
GtkWidget *image_quality_tab_grid = tab_grid_new ();
503
GtkWidget *color_tab_grid = tab_grid_new ();
504
GtkWidget *finishing_tab_grid = tab_grid_new ();
505
GtkWidget *advanced_tab_grid = tab_grid_new ();
509
widget = (GtkWidget*)
510
gtk_builder_get_object (dialog->builder, "options-spinner");
511
gtk_widget_hide (widget);
512
gtk_spinner_stop (GTK_SPINNER (widget));
514
widget = (GtkWidget*)
515
gtk_builder_get_object (dialog->builder, "progress-label");
516
gtk_widget_hide (widget);
518
treeview = (GtkTreeView *)
519
gtk_builder_get_object (dialog->builder, "options-categories-treeview");
521
notebook = (GtkWidget *)
522
gtk_builder_get_object (dialog->builder, "options-notebook");
524
if (dialog->ipp_attributes)
526
/* Add number-up option to Page Setup tab */
527
ipp_option_add (g_hash_table_lookup (dialog->ipp_attributes,
528
"number-up-supported"),
529
g_hash_table_lookup (dialog->ipp_attributes,
530
"number-up-default"),
532
/* Translators: This option sets number of pages printed on one sheet */
534
dialog->printer_name,
538
/* Add sides option to Page Setup tab */
539
ipp_option_add (g_hash_table_lookup (dialog->ipp_attributes,
541
g_hash_table_lookup (dialog->ipp_attributes,
544
/* Translators: This option sets whether to print on both sides of paper */
546
dialog->printer_name,
550
/* Add orientation-requested option to Page Setup tab */
551
ipp_option_add (g_hash_table_lookup (dialog->ipp_attributes,
552
"orientation-requested-supported"),
553
g_hash_table_lookup (dialog->ipp_attributes,
554
"orientation-requested-default"),
555
"orientation-requested",
556
/* Translators: This option sets orientation of print (portrait, landscape...) */
558
dialog->printer_name,
563
if (dialog->destination && dialog->ppd_filename)
565
ppd_file = ppdOpenFile (dialog->ppd_filename);
566
ppdLocalize (ppd_file);
570
ppdMarkDefaults (ppd_file);
571
cupsMarkOptions (ppd_file,
572
dialog->destination->num_options,
573
dialog->destination->options);
575
for (i = 0; i < ppd_file->num_groups; i++)
577
for (j = 0; j < ppd_file->groups[i].num_options; j++)
581
if (STRING_IN_TABLE (ppd_file->groups[i].name,
582
color_group_whitelist))
583
grid = color_tab_grid;
584
else if (STRING_IN_TABLE (ppd_file->groups[i].name,
585
image_quality_group_whitelist))
586
grid = image_quality_tab_grid;
587
else if (STRING_IN_TABLE (ppd_file->groups[i].name,
588
job_group_whitelist))
590
else if (STRING_IN_TABLE (ppd_file->groups[i].name,
591
finishing_group_whitelist))
592
grid = finishing_tab_grid;
593
else if (STRING_IN_TABLE (ppd_file->groups[i].name,
594
installable_options_group_whitelist))
595
grid = installable_options_tab_grid;
596
else if (STRING_IN_TABLE (ppd_file->groups[i].name,
597
page_setup_group_whitelist))
598
grid = page_setup_tab_grid;
600
if (!STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword,
601
ppd_option_blacklist))
603
if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword,
604
color_option_whitelist))
605
grid = color_tab_grid;
606
else if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword,
607
image_quality_option_whitelist))
608
grid = image_quality_tab_grid;
609
else if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword,
610
finishing_option_whitelist))
611
grid = finishing_tab_grid;
612
else if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword,
613
page_setup_option_whitelist))
614
grid = page_setup_tab_grid;
617
grid = advanced_tab_grid;
619
ppd_option_add (ppd_file->groups[i].options[j],
620
dialog->printer_name,
631
dialog->ppd_filename_set = FALSE;
632
if (dialog->ppd_filename)
634
g_unlink (dialog->ppd_filename);
635
g_free (dialog->ppd_filename);
636
dialog->ppd_filename = NULL;
639
dialog->destination_set = FALSE;
640
if (dialog->destination)
642
cupsFreeDests (1, dialog->destination);
643
dialog->destination = NULL;
646
dialog->ipp_attributes_set = FALSE;
647
if (dialog->ipp_attributes)
649
g_hash_table_unref (dialog->ipp_attributes);
650
dialog->ipp_attributes = NULL;
653
/* Translators: "General" tab contains general printer options */
654
tab_add (C_("Printer Option Group", "General"), notebook, treeview, general_tab_grid);
656
/* Translators: "Page Setup" tab contains settings related to pages (page size, paper source, etc.) */
657
tab_add (C_("Printer Option Group", "Page Setup"), notebook, treeview, page_setup_tab_grid);
659
/* Translators: "Installable Options" tab contains settings of presence of installed options (amount of RAM, duplex unit, etc.) */
660
tab_add (C_("Printer Option Group", "Installable Options"), notebook, treeview, installable_options_tab_grid);
662
/* Translators: "Job" tab contains settings for jobs */
663
tab_add (C_("Printer Option Group", "Job"), notebook, treeview, job_tab_grid);
665
/* Translators: "Image Quality" tab contains settings for quality of output print (e.g. resolution) */
666
tab_add (C_("Printer Option Group", "Image Quality"), notebook, treeview, image_quality_tab_grid);
668
/* Translators: "Color" tab contains color settings (e.g. color printing) */
669
tab_add (C_("Printer Option Group", "Color"), notebook, treeview, color_tab_grid);
671
/* Translators: "Finishing" tab contains finishing settings (e.g. booklet printing) */
672
tab_add (C_("Printer Option Group", "Finishing"), notebook, treeview, finishing_tab_grid);
674
/* Translators: "Advanced" tab contains all others settings */
675
tab_add (C_("Printer Option Group", "Advanced"), notebook, treeview, advanced_tab_grid);
677
gtk_widget_show_all (GTK_WIDGET (notebook));
679
/* Select the first option group */
680
if ((selection = gtk_tree_view_get_selection (treeview)) != NULL)
682
g_signal_connect (selection,
684
G_CALLBACK (category_selection_changed_cb), dialog);
686
if ((model = gtk_tree_view_get_model (treeview)) != NULL &&
687
gtk_tree_model_get_iter_first (model, &iter))
688
gtk_tree_selection_select_iter (selection, &iter);
691
dialog->populating_dialog = FALSE;
692
if (dialog->response != GTK_RESPONSE_NONE)
694
dialog->user_callback (GTK_DIALOG (dialog->dialog), dialog->response, dialog->user_data);
699
printer_get_ppd_cb (const gchar *ppd_filename,
702
PpOptionsDialog *dialog = (PpOptionsDialog *) user_data;
704
if (dialog->ppd_filename)
706
g_unlink (dialog->ppd_filename);
707
g_free (dialog->ppd_filename);
710
dialog->ppd_filename = g_strdup (ppd_filename);
711
dialog->ppd_filename_set = TRUE;
713
if (dialog->destination_set &&
714
dialog->ipp_attributes_set)
716
populate_options_real (dialog);
721
get_named_dest_cb (cups_dest_t *dest,
724
PpOptionsDialog *dialog = (PpOptionsDialog *) user_data;
726
if (dialog->destination)
727
cupsFreeDests (1, dialog->destination);
729
dialog->destination = dest;
730
dialog->destination_set = TRUE;
732
if (dialog->ppd_filename_set &&
733
dialog->ipp_attributes_set)
735
populate_options_real (dialog);
740
get_ipp_attributes_cb (GHashTable *table,
743
PpOptionsDialog *dialog = (PpOptionsDialog *) user_data;
745
if (dialog->ipp_attributes)
746
g_hash_table_unref (dialog->ipp_attributes);
748
dialog->ipp_attributes = table;
749
dialog->ipp_attributes_set = TRUE;
751
if (dialog->ppd_filename_set &&
752
dialog->destination_set)
754
populate_options_real (dialog);
759
populate_options (PpOptionsDialog *dialog)
761
GtkTreeViewColumn *column;
762
GtkCellRenderer *renderer;
763
GtkTreeView *treeview;
766
* Options which we need to obtain through an IPP request
767
* to be able to fill the options dialog.
768
* *-supported - possible values of the option
769
* *-default - actual value of the option
771
const gchar *attributes[] =
772
{ "number-up-supported",
776
"orientation-requested-supported",
777
"orientation-requested-default",
780
treeview = (GtkTreeView *)
781
gtk_builder_get_object (dialog->builder, "options-categories-treeview");
783
renderer = gtk_cell_renderer_text_new ();
785
column = gtk_tree_view_column_new_with_attributes ("Categories", renderer,
786
"text", CATEGORY_NAMES_COLUMN, NULL);
787
gtk_tree_view_column_set_expand (column, TRUE);
788
gtk_tree_view_append_column (treeview, column);
790
widget = (GtkWidget*)
791
gtk_builder_get_object (dialog->builder, "options-spinner");
792
gtk_widget_show (widget);
793
gtk_spinner_start (GTK_SPINNER (widget));
795
widget = (GtkWidget*)
796
gtk_builder_get_object (dialog->builder, "progress-label");
797
gtk_widget_show (widget);
799
printer_get_ppd_async (dialog->printer_name,
805
get_named_dest_async (dialog->printer_name,
809
get_ipp_attributes_async (dialog->printer_name,
810
(gchar **) attributes,
811
get_ipp_attributes_cb,
816
* Modify padding of the content area of the GtkDialog
817
* so it is aligned with the action area.
820
update_alignment_padding (GtkWidget *widget,
821
GtkAllocation *allocation,
824
PpOptionsDialog *dialog = (PpOptionsDialog*) user_data;
825
GtkAllocation allocation1, allocation2;
826
GtkWidget *action_area;
827
GtkWidget *content_area;
828
gint offset_left, offset_right;
829
guint padding_left, padding_right,
830
padding_top, padding_bottom;
832
action_area = (GtkWidget*)
833
gtk_builder_get_object (dialog->builder, "dialog-action-area1");
834
gtk_widget_get_allocation (action_area, &allocation2);
836
content_area = (GtkWidget*)
837
gtk_builder_get_object (dialog->builder, "content-alignment");
838
gtk_widget_get_allocation (content_area, &allocation1);
840
offset_left = allocation2.x - allocation1.x;
841
offset_right = (allocation1.x + allocation1.width) -
842
(allocation2.x + allocation2.width);
844
gtk_alignment_get_padding (GTK_ALIGNMENT (content_area),
845
&padding_top, &padding_bottom,
846
&padding_left, &padding_right);
847
if (allocation1.x >= 0 && allocation2.x >= 0)
849
if (offset_left > 0 && offset_left != padding_left)
850
gtk_alignment_set_padding (GTK_ALIGNMENT (content_area),
851
padding_top, padding_bottom,
852
offset_left, padding_right);
854
gtk_alignment_get_padding (GTK_ALIGNMENT (content_area),
855
&padding_top, &padding_bottom,
856
&padding_left, &padding_right);
857
if (offset_right > 0 && offset_right != padding_right)
858
gtk_alignment_set_padding (GTK_ALIGNMENT (content_area),
859
padding_top, padding_bottom,
860
padding_left, offset_right);
865
options_dialog_response_cb (GtkDialog *_dialog,
869
PpOptionsDialog *dialog = (PpOptionsDialog*) user_data;
871
pp_options_dialog_hide (dialog);
872
dialog->response = response_id;
874
if (!dialog->populating_dialog)
875
dialog->user_callback (GTK_DIALOG (dialog->dialog), response_id, dialog->user_data);
879
pp_options_dialog_new (GtkWindow *parent,
880
UserResponseCallback user_callback,
885
PpOptionsDialog *dialog;
887
GError *error = NULL;
888
gchar *objects[] = { "options-dialog", NULL };
889
guint builder_result;
892
dialog = g_new0 (PpOptionsDialog, 1);
894
dialog->builder = gtk_builder_new ();
895
dialog->parent = GTK_WIDGET (parent);
897
builder_result = gtk_builder_add_objects_from_file (dialog->builder,
898
DATADIR"/options-dialog.ui",
901
if (builder_result == 0)
903
g_warning ("Could not load ui: %s", error->message);
904
g_error_free (error);
908
dialog->dialog = (GtkWidget *) gtk_builder_get_object (dialog->builder, "options-dialog");
909
gtk_window_set_transient_for (GTK_WINDOW (dialog->dialog), GTK_WINDOW (parent));
911
dialog->user_callback = user_callback;
912
dialog->user_data = user_data;
914
dialog->printer_name = g_strdup (printer_name);
916
dialog->ppd_filename = NULL;
917
dialog->ppd_filename_set = FALSE;
919
dialog->destination = NULL;
920
dialog->destination_set = FALSE;
922
dialog->ipp_attributes = NULL;
923
dialog->ipp_attributes_set = FALSE;
925
dialog->response = GTK_RESPONSE_NONE;
927
dialog->sensitive = sensitive;
929
/* connect signals */
930
g_signal_connect (dialog->dialog, "response", G_CALLBACK (options_dialog_response_cb), dialog);
931
g_signal_connect (dialog->dialog, "size-allocate", G_CALLBACK (update_alignment_padding), dialog);
933
widget = (GtkWidget*)
934
gtk_builder_get_object (dialog->builder, "options-title");
935
/* Translators: Options of given printer (e.g. "MyPrinter Options") */
936
title = g_strdup_printf (_("%s Options"), printer_name);
937
gtk_label_set_label (GTK_LABEL (widget), title);
940
gtk_widget_show_all (GTK_WIDGET (dialog->dialog));
942
dialog->populating_dialog = TRUE;
943
populate_options (dialog);
949
pp_options_dialog_free (PpOptionsDialog *dialog)
951
gtk_widget_destroy (GTK_WIDGET (dialog->dialog));
952
dialog->dialog = NULL;
954
g_object_unref (dialog->builder);
955
dialog->builder = NULL;
957
g_free (dialog->printer_name);
958
dialog->printer_name = NULL;
960
if (dialog->ppd_filename)
962
g_unlink (dialog->ppd_filename);
963
g_free (dialog->ppd_filename);
964
dialog->ppd_filename = NULL;
967
if (dialog->destination)
969
cupsFreeDests (1, dialog->destination);
970
dialog->destination = NULL;
973
if (dialog->ipp_attributes)
975
g_hash_table_unref (dialog->ipp_attributes);
976
dialog->ipp_attributes = NULL;
983
pp_options_dialog_hide (PpOptionsDialog *dialog)
985
gtk_widget_hide (GTK_WIDGET (dialog->dialog));