1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- 2 * 3 * Copyright 2012 Red Hat, Inc, 4 * 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. 9 * 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. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, see <http://www.gnu.org/licenses/>. 17 * 18 * Author: Marek Kasik <mkasik@redhat.com> 19 */ 20 21 #include "config.h" 22 23 #include <unistd.h> 24 #include <stdlib.h> 25 #include <sys/types.h> 26 #include <sys/wait.h> 27 28 #include <glib.h> 29 #include <glib/gi18n.h> 30 #include <glib/gstdio.h> 31 #include <gtk/gtk.h> 32 33 #include <cups/cups.h> 34 #include <cups/ppd.h> 35 36 #include "pp-options-dialog.h" 37 #include "pp-maintenance-command.h" 38 #include "pp-ppd-option-widget.h" 39 #include "pp-ipp-option-widget.h" 40 #include "pp-utils.h" 41 #include "pp-printer.h" 42 43 struct _PpOptionsDialog { 44 GtkDialog parent_instance; 45 46 GtkTreeSelection *categories_selection; 47 GtkTreeView *categories_treeview; 48 GtkBox *main_box; 49 GtkNotebook *notebook; 50 GtkSpinner *spinner; 51 GtkStack *stack; 52 53 gchar *printer_name; 54 55 gchar *ppd_filename; 56 gboolean ppd_filename_set; 57 58 cups_dest_t *destination; 59 gboolean destination_set; 60 61 GHashTable *ipp_attributes; 62 gboolean ipp_attributes_set; 63 64 gboolean sensitive; 65 }; 66 67 G_DEFINE_TYPE (PpOptionsDialog, pp_options_dialog, GTK_TYPE_DIALOG) 68 69 enum 70 { 71 CATEGORY_IDS_COLUMN = 0, 72 CATEGORY_NAMES_COLUMN 73 }; 74 75 /* These lists come from Gtk+ */ 76 /* TODO: Only "Resolution" currently has a context to disambiguate it from 77 * the display settings. All of these should have contexts, but it 78 * was late in the release cycle and this partial solution was 79 * preferable. See: 80 * https://gitlab.gnome.org/GNOME/gnome-control-center/merge_requests/414#note_446778 81 */ 82 static const struct { 83 const char *keyword; 84 const char *translation_context; 85 const char *translation; 86 } ppd_option_translations[] = { 87 { "Duplex", NULL, N_("Two Sided") }, 88 { "MediaType", NULL, N_("Paper Type") }, 89 { "InputSlot", NULL, N_("Paper Source") }, 90 { "OutputBin", NULL, N_("Output Tray") }, 91 { "Resolution", "printing option", NC_("printing option", "Resolution") }, 92 { "PreFilter", NULL, N_("GhostScript pre-filtering") }, 93 }; 94 95 /* keep sorted when changing */ 96 static const char *allowed_page_setup_options[] = { 97 "InputSlot", 98 "MediaType", 99 "OutputBin", 100 "PageSize", 101 }; 102 103 /* keep sorted when changing */ 104 static const char *allowed_color_options[] = { 105 "BRColorEnhancement", 106 "BRColorMatching", 107 "BRColorMatching", 108 "BRColorMode", 109 "BRGammaValue", 110 "BRImprovedGray", 111 "BlackSubstitution", 112 "ColorModel", 113 "HPCMYKInks", 114 "HPCSGraphics", 115 "HPCSImages", 116 "HPCSText", 117 "HPColorSmart", 118 "RPSBlackMode", 119 "RPSBlackOverPrint", 120 "Rcmyksimulation", 121 }; 122 123 /* keep sorted when changing */ 124 static const char *allowed_color_groups[] = { 125 "Color", 126 "Color1", 127 "Color2", 128 "ColorBalance", 129 "ColorPage", 130 "ColorSettings1", 131 "ColorSettings2", 132 "ColorSettings3", 133 "ColorSettings4", 134 "EPColorSettings", 135 "FPColorWise1", 136 "FPColorWise2", 137 "FPColorWise3", 138 "FPColorWise4", 139 "FPColorWise5", 140 "HPCMYKInksPanel", 141 "HPColorOptions", 142 "HPColorOptionsPanel", 143 "HPColorQualityOptionsPanel", 144 "ManualColor", 145 }; 146 147 /* keep sorted when changing */ 148 static const char *allowed_image_quality_options[] = { 149 "BRDocument", 150 "BRHalfTonePattern", 151 "BRNormalPrt", 152 "BRPrintQuality", 153 "BitsPerPixel", 154 "Darkness", 155 "Dithering", 156 "EconoMode", 157 "Economode", 158 "HPEconoMode", 159 "HPEdgeControl", 160 "HPGraphicsHalftone", 161 "HPHalftone", 162 "HPImagingOptions", 163 "HPLJDensity", 164 "HPPhotoHalftone", 165 "HPPrintQualityOptions", 166 "HPResolutionOptions", 167 "OutputMode", 168 "REt", 169 "RPSBitsPerPixel", 170 "RPSDitherType", 171 "Resolution", 172 "ScreenLock", 173 "Smoothing", 174 "TonerSaveMode", 175 "UCRGCRForImage", 176 }; 177 178 /* keep sorted when changing */ 179 static const char *allowed_image_quality_groups[] = { 180 "EPQualitySettings", 181 "FPImageQuality1", 182 "FPImageQuality2", 183 "FPImageQuality3", 184 "ImageQualityPage", 185 "Quality", 186 }; 187 188 /* keep sorted when changing */ 189 static const char * allowed_finishing_options[] = { 190 "BindColor", 191 "BindEdge", 192 "BindType", 193 "BindWhen", 194 "Booklet", 195 "FoldType", 196 "FoldWhen", 197 "HPStaplerOptions", 198 "Jog", 199 "Slipsheet", 200 "Sorter", 201 "StapleLocation", 202 "StapleOrientation", 203 "StapleWhen", 204 "StapleX", 205 "StapleY", 206 }; 207 208 /* keep sorted when changing */ 209 static const char *allowed_job_groups[] = { 210 "JobHandling", 211 "JobLog", 212 }; 213 214 /* keep sorted when changing */ 215 static const char *allowed_finishing_groups[] = { 216 "Booklet", 217 "BookletCover", 218 "BookletModeOptions", 219 "FPFinishing1", 220 "FPFinishing2", 221 "FPFinishing3", 222 "FPFinishing4", 223 "Finishing", 224 "FinishingOptions", 225 "FinishingPage", 226 "HPBookletPanel", 227 "HPFinishing", 228 "HPFinishingOptions", 229 "HPFinishingPanel", 230 }; 231 232 /* keep sorted when changing */ 233 static const char *allowed_installable_options_groups[] = { 234 "InstallableOptions", 235 }; 236 237 /* keep sorted when changing */ 238 static const char *allowed_page_setup_groups[] = { 239 "HPMarginAndLayout", 240 "OutputControl", 241 "PaperHandling", 242 "Paper", 243 "Source", 244 }; 245 246 /* keep sorted when changing */ 247 static const char *disallowed_ppd_options[] = { 248 "Collate", 249 "Copies", 250 "Duplex", 251 "HPManualDuplexOrientation", 252 "HPManualDuplexSwitch", 253 "OutputOrder", 254 "PageRegion" 255 }; 256 257 static int 258 strptr_cmp (const void *a, 259 const void *b) 260 { 261 char **aa = (char **)a; 262 char **bb = (char **)b; 263 return strcmp (*aa, *bb); 264 } 265 266 static gboolean 267 string_in_table (gchar *str, 268 const gchar *table[], 269 gint table_len) 270 { 271 return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL; 272 } 273 274 #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table))) 275 276 static const gchar * 277 ppd_option_name_translate (ppd_option_t *option) 278 { 279 gint i; 280 281 for (i = 0; i < G_N_ELEMENTS (ppd_option_translations); i++) 282 { 283 if (g_strcmp0 (ppd_option_translations[i].keyword, option->keyword) == 0) 284 { 285 if (ppd_option_translations[i].translation_context) 286 return g_dpgettext2(NULL, ppd_option_translations[i].translation_context, ppd_option_translations[i].translation); 287 else 288 return _(ppd_option_translations[i].translation); 289 } 290 } 291 292 return option->text; 293 } 294 295 static gint 296 grid_get_height (GtkWidget *grid) 297 { 298 GList *children; 299 GList *child; 300 gint height = 0; 301 gint top_attach = 0; 302 gint max = 0; 303 304 children = gtk_container_get_children (GTK_CONTAINER (grid)); 305 for (child = children; child; child = g_list_next (child)) 306 { 307 gtk_container_child_get (GTK_CONTAINER (grid), child->data, 308 "top-attach", &top_attach, 309 "height", &height, 310 NULL); 311 312 if (height + top_attach > max) 313 max = height + top_attach; 314 } 315 316 g_list_free (children); 317 318 return max; 319 } 320 321 static gboolean 322 grid_is_empty (GtkWidget *grid) 323 { 324 GList *children; 325 326 children = gtk_container_get_children (GTK_CONTAINER (grid)); 327 if (children) 328 { 329 g_list_free (children); 330 return FALSE; 331 } 332 else 333 { 334 return TRUE; 335 } 336 } 337 338 static GtkWidget * 339 ipp_option_add (IPPAttribute *attr_supported, 340 IPPAttribute *attr_default, 341 const gchar *option_name, 342 const gchar *option_display_name, 343 const gchar *printer_name, 344 GtkWidget *grid, 345 gboolean sensitive) 346 { 347 GtkStyleContext *context; 348 GtkWidget *widget; 349 GtkWidget *label; 350 gint position; 351 352 widget = (GtkWidget *) pp_ipp_option_widget_new (attr_supported, 353 attr_default, 354 option_name, 355 printer_name); 356 if (widget) 357 { 358 gtk_widget_show (widget); 359 gtk_widget_set_sensitive (widget, sensitive); 360 position = grid_get_height (grid); 361 362 label = gtk_label_new (option_display_name); 363 gtk_widget_show (GTK_WIDGET (label)); 364 gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget); 365 context = gtk_widget_get_style_context (label); 366 gtk_style_context_add_class (context, "dim-label"); 367 gtk_widget_set_halign (label, GTK_ALIGN_END); 368 gtk_widget_set_valign (label, GTK_ALIGN_CENTER); 369 gtk_widget_set_margin_start (label, 10); 370 gtk_grid_attach (GTK_GRID (grid), label, 0, position, 1, 1); 371 372 gtk_widget_set_margin_start (widget, 20); 373 gtk_grid_attach (GTK_GRID (grid), widget, 1, position, 1, 1); 374 } 375 376 return widget; 377 } 378 379 static GtkWidget * 380 ppd_option_add (ppd_option_t option, 381 const gchar *printer_name, 382 GtkWidget *grid, 383 gboolean sensitive) 384 { 385 GtkStyleContext *context; 386 GtkWidget *widget; 387 GtkWidget *label; 388 gint position; 389 390 widget = (GtkWidget *) pp_ppd_option_widget_new (&option, printer_name); 391 if (widget) 392 { 393 gtk_widget_show (widget); 394 gtk_widget_set_sensitive (widget, sensitive); 395 position = grid_get_height (grid); 396 397 label = gtk_label_new (ppd_option_name_translate (&option)); 398 gtk_widget_show (GTK_WIDGET (label)); 399 gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget); 400 context = gtk_widget_get_style_context (label); 401 gtk_style_context_add_class (context, "dim-label"); 402 gtk_widget_set_halign (label, GTK_ALIGN_END); 403 gtk_widget_set_valign (label, GTK_ALIGN_CENTER); 404 gtk_widget_set_margin_start (label, 10); 405 gtk_grid_attach (GTK_GRID (grid), label, 0, position, 1, 1); 406 407 gtk_widget_set_margin_start (widget, 20); 408 gtk_grid_attach (GTK_GRID (grid), widget, 1, position, 1, 1); 409 } 410 411 return widget; 412 } 413 414 static GtkWidget * 415 tab_grid_new () 416 { 417 GtkWidget *grid; 418 419 grid = gtk_grid_new (); 420 gtk_widget_show (GTK_WIDGET (grid)); 421 gtk_container_set_border_width (GTK_CONTAINER (grid), 20); 422 gtk_grid_set_row_spacing (GTK_GRID (grid), 15); 423 424 return grid; 425 } 426 427 static void 428 tab_add (PpOptionsDialog *self, 429 const gchar *tab_name, 430 GtkWidget *grid) 431 { 432 GtkListStore *store; 433 GtkTreeIter iter; 434 GtkWidget *scrolled_window; 435 gboolean unref_store = FALSE; 436 gint id; 437 438 if (!grid_is_empty (grid)) 439 { 440 scrolled_window = gtk_scrolled_window_new (NULL, NULL); 441 gtk_widget_show (GTK_WIDGET (scrolled_window)); 442 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), 443 GTK_POLICY_NEVER, 444 GTK_POLICY_AUTOMATIC); 445 gtk_container_add (GTK_CONTAINER (scrolled_window), grid); 446 447 id = gtk_notebook_append_page (self->notebook, 448 scrolled_window, 449 NULL); 450 451 if (id >= 0) 452 { 453 store = GTK_LIST_STORE (gtk_tree_view_get_model (self->categories_treeview)); 454 if (!store) 455 { 456 store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING); 457 unref_store = TRUE; 458 } 459 460 gtk_list_store_append (store, &iter); 461 gtk_list_store_set (store, &iter, 462 CATEGORY_IDS_COLUMN, id, 463 CATEGORY_NAMES_COLUMN, tab_name, 464 -1); 465 466 if (unref_store) 467 { 468 gtk_tree_view_set_model (self->categories_treeview, GTK_TREE_MODEL (store)); 469 g_object_unref (store); 470 } 471 } 472 } 473 else 474 { 475 g_object_ref_sink (grid); 476 g_object_unref (grid); 477 } 478 } 479 480 static void 481 category_selection_changed_cb (PpOptionsDialog *self) 482 { 483 GtkTreeModel *model; 484 GtkTreeIter iter; 485 gint id = -1; 486 487 if (gtk_tree_selection_get_selected (self->categories_selection, &model, &iter)) 488 { 489 gtk_tree_model_get (model, &iter, 490 CATEGORY_IDS_COLUMN, &id, 491 -1); 492 } 493 494 if (id >= 0) 495 { 496 gtk_notebook_set_current_page (self->notebook, id); 497 } 498 } 499 500 static void 501 populate_options_real (PpOptionsDialog *self) 502 { 503 GtkTreeModel *model; 504 GtkTreeIter iter; 505 ppd_file_t *ppd_file; 506 GtkWidget *grid; 507 GtkWidget *general_tab_grid = tab_grid_new (); 508 GtkWidget *page_setup_tab_grid = tab_grid_new (); 509 GtkWidget *installable_options_tab_grid = tab_grid_new (); 510 GtkWidget *job_tab_grid = tab_grid_new (); 511 GtkWidget *image_quality_tab_grid = tab_grid_new (); 512 GtkWidget *color_tab_grid = tab_grid_new (); 513 GtkWidget *finishing_tab_grid = tab_grid_new (); 514 GtkWidget *advanced_tab_grid = tab_grid_new (); 515 gint i, j; 516 517 gtk_spinner_stop (self->spinner); 518 519 gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->main_box)); 520 521 if (self->ipp_attributes) 522 { 523 /* Add number-up option to Page Setup tab */ 524 ipp_option_add (g_hash_table_lookup (self->ipp_attributes, 525 "number-up-supported"), 526 g_hash_table_lookup (self->ipp_attributes, 527 "number-up-default"), 528 "number-up", 529 /* Translators: This option sets number of pages printed on one sheet */ 530 _("Pages per side"), 531 self->printer_name, 532 page_setup_tab_grid, 533 self->sensitive); 534 535 /* Add sides option to Page Setup tab */ 536 ipp_option_add (g_hash_table_lookup (self->ipp_attributes, 537 "sides-supported"), 538 g_hash_table_lookup (self->ipp_attributes, 539 "sides-default"), 540 "sides", 541 /* Translators: This option sets whether to print on both sides of paper */ 542 _("Two-sided"), 543 self->printer_name, 544 page_setup_tab_grid, 545 self->sensitive); 546 547 /* Add orientation-requested option to Page Setup tab */ 548 ipp_option_add (g_hash_table_lookup (self->ipp_attributes, 549 "orientation-requested-supported"), 550 g_hash_table_lookup (self->ipp_attributes, 551 "orientation-requested-default"), 552 "orientation-requested", 553 /* Translators: This option sets orientation of print (portrait, landscape...) */ 554 _("Orientation"), 555 self->printer_name, 556 page_setup_tab_grid, 557 self->sensitive); 558 } 559 560 if (self->destination && self->ppd_filename) 561 { 562 ppd_file = ppdOpenFile (self->ppd_filename); 563 ppdLocalize (ppd_file); 564 565 if (ppd_file) 566 { 567 ppdMarkDefaults (ppd_file); 568 cupsMarkOptions (ppd_file, 569 self->destination->num_options, 570 self->destination->options); 571 572 for (i = 0; i < ppd_file->num_groups; i++) 573 { 574 for (j = 0; j < ppd_file->groups[i].num_options; j++) 575 { 576 grid = NULL; 577 578 if (STRING_IN_TABLE (ppd_file->groups[i].name, 579 allowed_color_groups)) 580 grid = color_tab_grid; 581 else if (STRING_IN_TABLE (ppd_file->groups[i].name, 582 allowed_image_quality_groups)) 583 grid = image_quality_tab_grid; 584 else if (STRING_IN_TABLE (ppd_file->groups[i].name, 585 allowed_job_groups)) 586 grid = job_tab_grid; 587 else if (STRING_IN_TABLE (ppd_file->groups[i].name, 588 allowed_finishing_groups)) 589 grid = finishing_tab_grid; 590 else if (STRING_IN_TABLE (ppd_file->groups[i].name, 591 allowed_installable_options_groups)) 592 grid = installable_options_tab_grid; 593 else if (STRING_IN_TABLE (ppd_file->groups[i].name, 594 allowed_page_setup_groups)) 595 grid = page_setup_tab_grid; 596 597 if (!STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword, 598 disallowed_ppd_options)) 599 { 600 if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword, 601 allowed_color_options)) 602 grid = color_tab_grid; 603 else if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword, 604 allowed_image_quality_options)) 605 grid = image_quality_tab_grid; 606 else if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword, 607 allowed_finishing_options)) 608 grid = finishing_tab_grid; 609 else if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword, 610 allowed_page_setup_options)) 611 grid = page_setup_tab_grid; 612 613 if (!grid) 614 grid = advanced_tab_grid; 615 616 ppd_option_add (ppd_file->groups[i].options[j], 617 self->printer_name, 618 grid, 619 self->sensitive); 620 } 621 } 622 } 623 624 ppdClose (ppd_file); 625 } 626 } 627 628 self->ppd_filename_set = FALSE; 629 if (self->ppd_filename) 630 { 631 g_unlink (self->ppd_filename); 632 g_free (self->ppd_filename); 633 self->ppd_filename = NULL; 634 } 635 636 self->destination_set = FALSE; 637 if (self->destination) 638 { 639 cupsFreeDests (1, self->destination); 640 self->destination = NULL; 641 } 642 643 self->ipp_attributes_set = FALSE; 644 if (self->ipp_attributes) 645 { 646 g_hash_table_unref (self->ipp_attributes); 647 self->ipp_attributes = NULL; 648 } 649 650 /* Translators: "General" tab contains general printer options */ 651 tab_add (self, C_("Printer Option Group", "General"), general_tab_grid); 652 653 /* Translators: "Page Setup" tab contains settings related to pages (page size, paper source, etc.) */ 654 tab_add (self, C_("Printer Option Group", "Page Setup"), page_setup_tab_grid); 655 656 /* Translators: "Installable Options" tab contains settings of presence of installed options (amount of RAM, duplex unit, etc.) */ 657 tab_add (self, C_("Printer Option Group", "Installable Options"), installable_options_tab_grid); 658 659 /* Translators: "Job" tab contains settings for jobs */ 660 tab_add (self, C_("Printer Option Group", "Job"), job_tab_grid); 661 662 /* Translators: "Image Quality" tab contains settings for quality of output print (e.g. resolution) */ 663 tab_add (self, C_("Printer Option Group", "Image Quality"), image_quality_tab_grid); 664 665 /* Translators: "Color" tab contains color settings (e.g. color printing) */ 666 tab_add (self, C_("Printer Option Group", "Color"), color_tab_grid); 667 668 /* Translators: "Finishing" tab contains finishing settings (e.g. booklet printing) */ 669 tab_add (self, C_("Printer Option Group", "Finishing"), finishing_tab_grid); 670 671 /* Translators: "Advanced" tab contains all others settings */ 672 tab_add (self, C_("Printer Option Group", "Advanced"), advanced_tab_grid); 673 674 /* Select the first option group */ 675 if ((model = gtk_tree_view_get_model (self->categories_treeview)) != NULL && 676 gtk_tree_model_get_iter_first (model, &iter)) 677 gtk_tree_selection_select_iter (self->categories_selection, &iter); 678 } 679 680 static void 681 printer_get_ppd_cb (const gchar *ppd_filename, 682 gpointer user_data) 683 { 684 PpOptionsDialog *self = (PpOptionsDialog *) user_data; 685 686 if (self->ppd_filename) 687 { 688 g_unlink (self->ppd_filename); 689 g_free (self->ppd_filename); 690 } 691 692 self->ppd_filename = g_strdup (ppd_filename); 693 self->ppd_filename_set = TRUE; 694 695 if (self->destination_set && 696 self->ipp_attributes_set) 697 { 698 populate_options_real (self); 699 } 700 } 701 702 static void 703 get_named_dest_cb (cups_dest_t *dest, 704 gpointer user_data) 705 { 706 PpOptionsDialog *self = (PpOptionsDialog *) user_data; 707 708 if (self->destination) 709 cupsFreeDests (1, self->destination); 710 711 self->destination = dest; 712 self->destination_set = TRUE; 713 714 if (self->ppd_filename_set && 715 self->ipp_attributes_set) 716 { 717 populate_options_real (self); 718 } 719 } 720 721 static void 722 get_ipp_attributes_cb (GHashTable *table, 723 gpointer user_data) 724 { 725 PpOptionsDialog *self = (PpOptionsDialog *) user_data; 726 727 if (self->ipp_attributes) 728 g_hash_table_unref (self->ipp_attributes); 729 730 self->ipp_attributes = g_hash_table_ref (table); 731 self->ipp_attributes_set = TRUE; 732 733 if (self->ppd_filename_set && 734 self->destination_set) 735 { 736 populate_options_real (self); 737 } 738 } 739 740 static void 741 populate_options (PpOptionsDialog *self) 742 { 743 GtkTreeViewColumn *column; 744 GtkCellRenderer *renderer; 745 /* 746 * Options which we need to obtain through an IPP request 747 * to be able to fill the options dialog. 748 * *-supported - possible values of the option 749 * *-default - actual value of the option 750 */ 751 const gchar *attributes[] = 752 { "number-up-supported", 753 "number-up-default", 754 "sides-supported", 755 "sides-default", 756 "orientation-requested-supported", 757 "orientation-requested-default", 758 NULL}; 759 760 gtk_stack_set_visible_child (self->stack, GTK_WIDGET (self->spinner)); 761 762 renderer = gtk_cell_renderer_text_new (); 763 764 column = gtk_tree_view_column_new_with_attributes ("Categories", renderer, 765 "text", CATEGORY_NAMES_COLUMN, NULL); 766 gtk_tree_view_column_set_expand (column, TRUE); 767 gtk_tree_view_append_column (self->categories_treeview, column); 768 769 gtk_spinner_start (self->spinner); 770 771 printer_get_ppd_async (self->printer_name, 772 NULL, 773 0, 774 printer_get_ppd_cb, 775 self); 776 777 get_named_dest_async (self->printer_name, 778 get_named_dest_cb, 779 self); 780 781 get_ipp_attributes_async (self->printer_name, 782 (gchar **) attributes, 783 get_ipp_attributes_cb, 784 self); 785 } 786 787 static void 788 pp_maintenance_command_execute_cb (GObject *source_object, 789 GAsyncResult *res, 790 gpointer user_data) 791 { 792 pp_maintenance_command_execute_finish (PP_MAINTENANCE_COMMAND(source_object), res, NULL); 793 } 794 795 static gchar * 796 get_testprint_filename (const gchar *datadir) 797 { 798 const gchar *testprint[] = { "/data/testprint", 799 "/data/testprint.ps", 800 NULL }; 801 gchar *filename = NULL; 802 gint i; 803 804 for (i = 0; testprint[i] != NULL; i++) 805 { 806 filename = g_strconcat (datadir, testprint[i], NULL); 807 if (g_access (filename, R_OK) == 0) 808 break; 809 810 g_clear_pointer (&filename, g_free); 811 } 812 813 return filename; 814 } 815 816 static void 817 print_test_page_cb (GObject *source_object, 818 GAsyncResult *result, 819 gpointer user_data) 820 { 821 pp_printer_print_file_finish (PP_PRINTER (source_object), 822 result, NULL); 823 } 824 825 static void 826 test_page_cb (PpOptionsDialog *self) 827 { 828 gint i; 829 830 if (self->printer_name) 831 { 832 const gchar *const dirs[] = { "/usr/share/cups", 833 "/usr/local/share/cups", 834 NULL }; 835 const gchar *datadir = NULL; 836 g_autofree gchar *filename = NULL; 837 838 datadir = getenv ("CUPS_DATADIR"); 839 if (datadir != NULL) 840 { 841 filename = get_testprint_filename (datadir); 842 } 843 else 844 { 845 for (i = 0; dirs[i] != NULL && filename == NULL; i++) 846 filename = get_testprint_filename (dirs[i]); 847 } 848 849 if (filename != NULL) 850 { 851 g_autoptr(PpPrinter) printer = NULL; 852 853 printer = pp_printer_new (self->printer_name); 854 pp_printer_print_file_async (printer, 855 filename, 856 /* Translators: Name of job which makes printer to print test page */ 857 _("Test Page"), 858 NULL, 859 print_test_page_cb, 860 NULL); 861 } 862 else 863 { 864 g_autoptr(PpMaintenanceCommand) command = NULL; 865 866 command = pp_maintenance_command_new (self->printer_name, 867 "PrintSelfTestPage", 868 NULL, 869 /* Translators: Name of job which makes printer to print test page */ 870 _("Test page")); 871 872 pp_maintenance_command_execute_async (command, NULL, pp_maintenance_command_execute_cb, NULL); 873 } 874 } 875 } 876 877 PpOptionsDialog * 878 pp_options_dialog_new (gchar *printer_name, 879 gboolean sensitive) 880 { 881 PpOptionsDialog *self; 882 883 self = g_object_new (pp_options_dialog_get_type (), 884 "use-header-bar", 1, 885 NULL); 886 887 self->printer_name = g_strdup (printer_name); 888 889 self->sensitive = sensitive; 890 891 gtk_window_set_title (GTK_WINDOW (self), printer_name); 892 893 populate_options (self); 894 895 return self; 896 } 897 898 static void 899 pp_options_dialog_dispose (GObject *object) 900 { 901 PpOptionsDialog *self = PP_OPTIONS_DIALOG (object); 902 903 g_free (self->printer_name); 904 self->printer_name = NULL; 905 906 if (self->ppd_filename) 907 { 908 g_unlink (self->ppd_filename); 909 g_free (self->ppd_filename); 910 self->ppd_filename = NULL; 911 } 912 913 if (self->destination) 914 { 915 cupsFreeDests (1, self->destination); 916 self->destination = NULL; 917 } 918 919 if (self->ipp_attributes) 920 { 921 g_hash_table_unref (self->ipp_attributes); 922 self->ipp_attributes = NULL; 923 } 924 925 G_OBJECT_CLASS (pp_options_dialog_parent_class)->dispose (object); 926 } 927 928 void 929 pp_options_dialog_class_init (PpOptionsDialogClass *klass) 930 { 931 GObjectClass *object_class = G_OBJECT_CLASS (klass); 932 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); 933 934 object_class->dispose = pp_options_dialog_dispose; 935 936 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/printers/pp-options-dialog.ui"); 937 938 gtk_widget_class_bind_template_child (widget_class, PpOptionsDialog, categories_selection); 939 gtk_widget_class_bind_template_child (widget_class, PpOptionsDialog, categories_treeview); 940 gtk_widget_class_bind_template_child (widget_class, PpOptionsDialog, main_box); 941 gtk_widget_class_bind_template_child (widget_class, PpOptionsDialog, notebook); 942 gtk_widget_class_bind_template_child (widget_class, PpOptionsDialog, spinner); 943 gtk_widget_class_bind_template_child (widget_class, PpOptionsDialog, stack); 944 945 gtk_widget_class_bind_template_callback (widget_class, category_selection_changed_cb); 946 gtk_widget_class_bind_template_callback (widget_class, test_page_cb); 947 } 948 949 void 950 pp_options_dialog_init (PpOptionsDialog *self) 951 { 952 gtk_widget_init_template (GTK_WIDGET (self)); 953 } 954