/* -*- 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 2 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 */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include "pp-options-dialog.h" #include "pp-ppd-option-widget.h" #include "pp-ipp-option-widget.h" #include "pp-utils.h" struct _PpOptionsDialog { GtkBuilder *builder; GtkWidget *parent; GtkWidget *dialog; UserResponseCallback user_callback; gpointer user_data; gchar *printer_name; gchar *ppd_filename; gboolean ppd_filename_set; cups_dest_t *destination; gboolean destination_set; GHashTable *ipp_attributes; gboolean ipp_attributes_set; gboolean populating_dialog; GtkResponseType response; gboolean sensitive; }; static void pp_options_dialog_hide (PpOptionsDialog *dialog); enum { CATEGORY_IDS_COLUMN = 0, CATEGORY_NAMES_COLUMN }; /* These lists come from Gtk+ */ static const struct { const char *keyword; const char *translation; } ppd_option_translations[] = { { "Duplex", N_("Two Sided") }, { "MediaType", N_("Paper Type") }, { "InputSlot", N_("Paper Source") }, { "OutputBin", N_("Output Tray") }, { "Resolution", N_("Resolution") }, { "PreFilter", N_("GhostScript pre-filtering") }, }; /* keep sorted when changing */ static const char *page_setup_option_whitelist[] = { "InputSlot", "MediaType", "OutputBin", "PageSize", }; /* keep sorted when changing */ static const char *color_option_whitelist[] = { "BRColorEnhancement", "BRColorMatching", "BRColorMatching", "BRColorMode", "BRGammaValue", "BRImprovedGray", "BlackSubstitution", "ColorModel", "HPCMYKInks", "HPCSGraphics", "HPCSImages", "HPCSText", "HPColorSmart", "RPSBlackMode", "RPSBlackOverPrint", "Rcmyksimulation", }; /* keep sorted when changing */ static const char *color_group_whitelist[] = { "Color", "Color1", "Color2", "ColorBalance", "ColorPage", "ColorSettings1", "ColorSettings2", "ColorSettings3", "ColorSettings4", "EPColorSettings", "FPColorWise1", "FPColorWise2", "FPColorWise3", "FPColorWise4", "FPColorWise5", "HPCMYKInksPanel", "HPColorOptions", "HPColorOptionsPanel", "HPColorQualityOptionsPanel", "ManualColor", }; /* keep sorted when changing */ static const char *image_quality_option_whitelist[] = { "BRDocument", "BRHalfTonePattern", "BRNormalPrt", "BRPrintQuality", "BitsPerPixel", "Darkness", "Dithering", "EconoMode", "Economode", "HPEconoMode", "HPEdgeControl", "HPGraphicsHalftone", "HPHalftone", "HPImagingOptions", "HPLJDensity", "HPPhotoHalftone", "HPPrintQualityOptions", "HPResolutionOptions", "OutputMode", "REt", "RPSBitsPerPixel", "RPSDitherType", "Resolution", "ScreenLock", "Smoothing", "TonerSaveMode", "UCRGCRForImage", }; /* keep sorted when changing */ static const char *image_quality_group_whitelist[] = { "EPQualitySettings", "FPImageQuality1", "FPImageQuality2", "FPImageQuality3", "ImageQualityPage", "Quality", }; /* keep sorted when changing */ static const char * finishing_option_whitelist[] = { "BindColor", "BindEdge", "BindType", "BindWhen", "Booklet", "FoldType", "FoldWhen", "HPStaplerOptions", "Jog", "Slipsheet", "Sorter", "StapleLocation", "StapleOrientation", "StapleWhen", "StapleX", "StapleY", }; /* keep sorted when changing */ static const char *job_group_whitelist[] = { "JobHandling", "JobLog", }; /* keep sorted when changing */ static const char *finishing_group_whitelist[] = { "Booklet", "BookletCover", "BookletModeOptions", "FPFinishing1", "FPFinishing2", "FPFinishing3", "FPFinishing4", "Finishing", "FinishingOptions", "FinishingPage", "HPBookletPanel", "HPFinishing", "HPFinishingOptions", "HPFinishingPanel", }; /* keep sorted when changing */ static const char *installable_options_group_whitelist[] = { "InstallableOptions", }; /* keep sorted when changing */ static const char *page_setup_group_whitelist[] = { "HPMarginAndLayout", "OutputControl", "PaperHandling", "Paper", "Source", }; /* keep sorted when changing */ static const char *ppd_option_blacklist[] = { "Collate", "Copies", "Duplex", "HPManualDuplexOrientation", "HPManualDuplexSwitch", "OutputOrder", "PageRegion" }; static int strptr_cmp (const void *a, const void *b) { char **aa = (char **)a; char **bb = (char **)b; return strcmp (*aa, *bb); } static gboolean string_in_table (gchar *str, const gchar *table[], gint table_len) { return bsearch (&str, table, table_len, sizeof (char *), (void *)strptr_cmp) != NULL; } #define STRING_IN_TABLE(_str, _table) (string_in_table (_str, _table, G_N_ELEMENTS (_table))) static gchar * ppd_option_name_translate (ppd_option_t *option) { gint i; for (i = 0; i < G_N_ELEMENTS (ppd_option_translations); i++) { if (g_strcmp0 (ppd_option_translations[i].keyword, option->keyword) == 0) return g_strdup (_(ppd_option_translations[i].translation)); } return g_strdup (option->text); } static gint grid_get_height (GtkWidget *grid) { GList *children; GList *child; gint height = 0; gint top_attach = 0; gint max = 0; children = gtk_container_get_children (GTK_CONTAINER (grid)); for (child = children; child; child = g_list_next (child)) { gtk_container_child_get (GTK_CONTAINER (grid), child->data, "top-attach", &top_attach, "height", &height, NULL); if (height + top_attach > max) max = height + top_attach; } g_list_free (children); return max; } static gboolean grid_is_empty (GtkWidget *grid) { GList *children; children = gtk_container_get_children (GTK_CONTAINER (grid)); if (children) { g_list_free (children); return FALSE; } else { return TRUE; } } static GtkWidget * ipp_option_add (IPPAttribute *attr_supported, IPPAttribute *attr_default, const gchar *option_name, const gchar *option_display_name, const gchar *printer_name, GtkWidget *grid, gboolean sensitive) { GtkStyleContext *context; GtkWidget *widget; GtkWidget *label; gint position; widget = (GtkWidget *) pp_ipp_option_widget_new (attr_supported, attr_default, option_name, printer_name); if (widget) { gtk_widget_set_sensitive (widget, sensitive); position = grid_get_height (grid); label = gtk_label_new (option_display_name); context = gtk_widget_get_style_context (label); gtk_style_context_add_class (context, "dim-label-ucc"); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_widget_set_margin_left (label, 10); gtk_grid_attach (GTK_GRID (grid), label, 0, position, 1, 1); gtk_widget_set_margin_left (widget, 20); gtk_grid_attach (GTK_GRID (grid), widget, 1, position, 1, 1); } return widget; } static GtkWidget * ppd_option_add (ppd_option_t option, const gchar *printer_name, GtkWidget *grid, gboolean sensitive) { GtkStyleContext *context; GtkWidget *widget; GtkWidget *label; gint position; widget = (GtkWidget *) pp_ppd_option_widget_new (&option, printer_name); if (widget) { gtk_widget_set_sensitive (widget, sensitive); position = grid_get_height (grid); label = gtk_label_new (ppd_option_name_translate (&option)); context = gtk_widget_get_style_context (label); gtk_style_context_add_class (context, "dim-label-ucc"); gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); gtk_widget_set_margin_left (label, 10); gtk_grid_attach (GTK_GRID (grid), label, 0, position, 1, 1); gtk_widget_set_margin_left (widget, 20); gtk_grid_attach (GTK_GRID (grid), widget, 1, position, 1, 1); } return widget; } static GtkWidget * tab_grid_new () { GtkWidget *grid; grid = gtk_grid_new (); gtk_container_set_border_width (GTK_CONTAINER (grid), 20); gtk_grid_set_row_spacing (GTK_GRID (grid), 15); return grid; } static void tab_add (const gchar *tab_name, GtkWidget *options_notebook, GtkTreeView *treeview, GtkWidget *grid) { GtkListStore *store; GtkTreeIter iter; GtkWidget *scrolled_window; gboolean unref_store = FALSE; gint id; if (!grid_is_empty (grid)) { scrolled_window = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (scrolled_window), grid); id = gtk_notebook_append_page (GTK_NOTEBOOK (options_notebook), scrolled_window, NULL); if (id >= 0) { store = GTK_LIST_STORE (gtk_tree_view_get_model (treeview)); if (!store) { store = gtk_list_store_new (2, G_TYPE_INT, G_TYPE_STRING); unref_store = TRUE; } gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, CATEGORY_IDS_COLUMN, id, CATEGORY_NAMES_COLUMN, tab_name, -1); if (unref_store) { gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (store)); g_object_unref (store); } } } else { g_object_ref_sink (grid); g_object_unref (grid); } } static void category_selection_changed_cb (GtkTreeSelection *selection, gpointer user_data) { PpOptionsDialog *dialog = (PpOptionsDialog *) user_data; GtkTreeModel *model; GtkTreeIter iter; GtkWidget *options_notebook; gint id = -1; if (gtk_tree_selection_get_selected (selection, &model, &iter)) { gtk_tree_model_get (model, &iter, CATEGORY_IDS_COLUMN, &id, -1); } if (id >= 0) { options_notebook = (GtkWidget*) gtk_builder_get_object (dialog->builder, "options-notebook"); gtk_notebook_set_current_page (GTK_NOTEBOOK (options_notebook), id); } } static void populate_options_real (PpOptionsDialog *dialog) { GtkTreeSelection *selection; GtkTreeModel *model; GtkTreeView *treeview; GtkTreeIter iter; ppd_file_t *ppd_file; GtkWidget *notebook; GtkWidget *grid; GtkWidget *general_tab_grid = tab_grid_new (); GtkWidget *page_setup_tab_grid = tab_grid_new (); GtkWidget *installable_options_tab_grid = tab_grid_new (); GtkWidget *job_tab_grid = tab_grid_new (); GtkWidget *image_quality_tab_grid = tab_grid_new (); GtkWidget *color_tab_grid = tab_grid_new (); GtkWidget *finishing_tab_grid = tab_grid_new (); GtkWidget *advanced_tab_grid = tab_grid_new (); GtkWidget *widget; gint i, j; widget = (GtkWidget*) gtk_builder_get_object (dialog->builder, "options-spinner"); gtk_widget_hide (widget); gtk_spinner_stop (GTK_SPINNER (widget)); widget = (GtkWidget*) gtk_builder_get_object (dialog->builder, "progress-label"); gtk_widget_hide (widget); treeview = (GtkTreeView *) gtk_builder_get_object (dialog->builder, "options-categories-treeview"); notebook = (GtkWidget *) gtk_builder_get_object (dialog->builder, "options-notebook"); if (dialog->ipp_attributes) { /* Add number-up option to Page Setup tab */ ipp_option_add (g_hash_table_lookup (dialog->ipp_attributes, "number-up-supported"), g_hash_table_lookup (dialog->ipp_attributes, "number-up-default"), "number-up", /* Translators: This option sets number of pages printed on one sheet */ _("Pages per side"), dialog->printer_name, page_setup_tab_grid, dialog->sensitive); /* Add sides option to Page Setup tab */ ipp_option_add (g_hash_table_lookup (dialog->ipp_attributes, "sides-supported"), g_hash_table_lookup (dialog->ipp_attributes, "sides-default"), "sides", /* Translators: This option sets whether to print on both sides of paper */ _("Two-sided"), dialog->printer_name, page_setup_tab_grid, dialog->sensitive); /* Add orientation-requested option to Page Setup tab */ ipp_option_add (g_hash_table_lookup (dialog->ipp_attributes, "orientation-requested-supported"), g_hash_table_lookup (dialog->ipp_attributes, "orientation-requested-default"), "orientation-requested", /* Translators: This option sets orientation of print (portrait, landscape...) */ _("Orientation"), dialog->printer_name, page_setup_tab_grid, dialog->sensitive); } if (dialog->destination && dialog->ppd_filename) { ppd_file = ppdOpenFile (dialog->ppd_filename); ppdLocalize (ppd_file); if (ppd_file) { ppdMarkDefaults (ppd_file); cupsMarkOptions (ppd_file, dialog->destination->num_options, dialog->destination->options); for (i = 0; i < ppd_file->num_groups; i++) { for (j = 0; j < ppd_file->groups[i].num_options; j++) { grid = NULL; if (STRING_IN_TABLE (ppd_file->groups[i].name, color_group_whitelist)) grid = color_tab_grid; else if (STRING_IN_TABLE (ppd_file->groups[i].name, image_quality_group_whitelist)) grid = image_quality_tab_grid; else if (STRING_IN_TABLE (ppd_file->groups[i].name, job_group_whitelist)) grid = job_tab_grid; else if (STRING_IN_TABLE (ppd_file->groups[i].name, finishing_group_whitelist)) grid = finishing_tab_grid; else if (STRING_IN_TABLE (ppd_file->groups[i].name, installable_options_group_whitelist)) grid = installable_options_tab_grid; else if (STRING_IN_TABLE (ppd_file->groups[i].name, page_setup_group_whitelist)) grid = page_setup_tab_grid; if (!STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword, ppd_option_blacklist)) { if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword, color_option_whitelist)) grid = color_tab_grid; else if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword, image_quality_option_whitelist)) grid = image_quality_tab_grid; else if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword, finishing_option_whitelist)) grid = finishing_tab_grid; else if (!grid && STRING_IN_TABLE (ppd_file->groups[i].options[j].keyword, page_setup_option_whitelist)) grid = page_setup_tab_grid; if (!grid) grid = advanced_tab_grid; ppd_option_add (ppd_file->groups[i].options[j], dialog->printer_name, grid, dialog->sensitive); } } } ppdClose (ppd_file); } } dialog->ppd_filename_set = FALSE; if (dialog->ppd_filename) { g_unlink (dialog->ppd_filename); g_free (dialog->ppd_filename); dialog->ppd_filename = NULL; } dialog->destination_set = FALSE; if (dialog->destination) { cupsFreeDests (1, dialog->destination); dialog->destination = NULL; } dialog->ipp_attributes_set = FALSE; if (dialog->ipp_attributes) { g_hash_table_unref (dialog->ipp_attributes); dialog->ipp_attributes = NULL; } /* Translators: "General" tab contains general printer options */ tab_add (C_("Printer Option Group", "General"), notebook, treeview, general_tab_grid); /* Translators: "Page Setup" tab contains settings related to pages (page size, paper source, etc.) */ tab_add (C_("Printer Option Group", "Page Setup"), notebook, treeview, page_setup_tab_grid); /* Translators: "Installable Options" tab contains settings of presence of installed options (amount of RAM, duplex unit, etc.) */ tab_add (C_("Printer Option Group", "Installable Options"), notebook, treeview, installable_options_tab_grid); /* Translators: "Job" tab contains settings for jobs */ tab_add (C_("Printer Option Group", "Job"), notebook, treeview, job_tab_grid); /* Translators: "Image Quality" tab contains settings for quality of output print (e.g. resolution) */ tab_add (C_("Printer Option Group", "Image Quality"), notebook, treeview, image_quality_tab_grid); /* Translators: "Color" tab contains color settings (e.g. color printing) */ tab_add (C_("Printer Option Group", "Color"), notebook, treeview, color_tab_grid); /* Translators: "Finishing" tab contains finishing settings (e.g. booklet printing) */ tab_add (C_("Printer Option Group", "Finishing"), notebook, treeview, finishing_tab_grid); /* Translators: "Advanced" tab contains all others settings */ tab_add (C_("Printer Option Group", "Advanced"), notebook, treeview, advanced_tab_grid); gtk_widget_show_all (GTK_WIDGET (notebook)); /* Select the first option group */ if ((selection = gtk_tree_view_get_selection (treeview)) != NULL) { g_signal_connect (selection, "changed", G_CALLBACK (category_selection_changed_cb), dialog); if ((model = gtk_tree_view_get_model (treeview)) != NULL && gtk_tree_model_get_iter_first (model, &iter)) gtk_tree_selection_select_iter (selection, &iter); } dialog->populating_dialog = FALSE; if (dialog->response != GTK_RESPONSE_NONE) { dialog->user_callback (GTK_DIALOG (dialog->dialog), dialog->response, dialog->user_data); } } static void printer_get_ppd_cb (const gchar *ppd_filename, gpointer user_data) { PpOptionsDialog *dialog = (PpOptionsDialog *) user_data; if (dialog->ppd_filename) { g_unlink (dialog->ppd_filename); g_free (dialog->ppd_filename); } dialog->ppd_filename = g_strdup (ppd_filename); dialog->ppd_filename_set = TRUE; if (dialog->destination_set && dialog->ipp_attributes_set) { populate_options_real (dialog); } } static void get_named_dest_cb (cups_dest_t *dest, gpointer user_data) { PpOptionsDialog *dialog = (PpOptionsDialog *) user_data; if (dialog->destination) cupsFreeDests (1, dialog->destination); dialog->destination = dest; dialog->destination_set = TRUE; if (dialog->ppd_filename_set && dialog->ipp_attributes_set) { populate_options_real (dialog); } } static void get_ipp_attributes_cb (GHashTable *table, gpointer user_data) { PpOptionsDialog *dialog = (PpOptionsDialog *) user_data; if (dialog->ipp_attributes) g_hash_table_unref (dialog->ipp_attributes); dialog->ipp_attributes = table; dialog->ipp_attributes_set = TRUE; if (dialog->ppd_filename_set && dialog->destination_set) { populate_options_real (dialog); } } static void populate_options (PpOptionsDialog *dialog) { GtkTreeViewColumn *column; GtkCellRenderer *renderer; GtkTreeView *treeview; GtkWidget *widget; /* * Options which we need to obtain through an IPP request * to be able to fill the options dialog. * *-supported - possible values of the option * *-default - actual value of the option */ const gchar *attributes[] = { "number-up-supported", "number-up-default", "sides-supported", "sides-default", "orientation-requested-supported", "orientation-requested-default", NULL}; treeview = (GtkTreeView *) gtk_builder_get_object (dialog->builder, "options-categories-treeview"); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ("Categories", renderer, "text", CATEGORY_NAMES_COLUMN, NULL); gtk_tree_view_column_set_expand (column, TRUE); gtk_tree_view_append_column (treeview, column); widget = (GtkWidget*) gtk_builder_get_object (dialog->builder, "options-spinner"); gtk_widget_show (widget); gtk_spinner_start (GTK_SPINNER (widget)); widget = (GtkWidget*) gtk_builder_get_object (dialog->builder, "progress-label"); gtk_widget_show (widget); printer_get_ppd_async (dialog->printer_name, NULL, 0, printer_get_ppd_cb, dialog); get_named_dest_async (dialog->printer_name, get_named_dest_cb, dialog); get_ipp_attributes_async (dialog->printer_name, (gchar **) attributes, get_ipp_attributes_cb, dialog); } /* * Modify padding of the content area of the GtkDialog * so it is aligned with the action area. */ static void update_alignment_padding (GtkWidget *widget, GtkAllocation *allocation, gpointer user_data) { PpOptionsDialog *dialog = (PpOptionsDialog*) user_data; GtkAllocation allocation1, allocation2; GtkWidget *action_area; GtkWidget *content_area; gint offset_left, offset_right; guint padding_left, padding_right, padding_top, padding_bottom; action_area = (GtkWidget*) gtk_builder_get_object (dialog->builder, "dialog-action-area1"); gtk_widget_get_allocation (action_area, &allocation2); content_area = (GtkWidget*) gtk_builder_get_object (dialog->builder, "content-alignment"); gtk_widget_get_allocation (content_area, &allocation1); offset_left = allocation2.x - allocation1.x; offset_right = (allocation1.x + allocation1.width) - (allocation2.x + allocation2.width); gtk_alignment_get_padding (GTK_ALIGNMENT (content_area), &padding_top, &padding_bottom, &padding_left, &padding_right); if (allocation1.x >= 0 && allocation2.x >= 0) { if (offset_left > 0 && offset_left != padding_left) gtk_alignment_set_padding (GTK_ALIGNMENT (content_area), padding_top, padding_bottom, offset_left, padding_right); gtk_alignment_get_padding (GTK_ALIGNMENT (content_area), &padding_top, &padding_bottom, &padding_left, &padding_right); if (offset_right > 0 && offset_right != padding_right) gtk_alignment_set_padding (GTK_ALIGNMENT (content_area), padding_top, padding_bottom, padding_left, offset_right); } } static void options_dialog_response_cb (GtkDialog *_dialog, gint response_id, gpointer user_data) { PpOptionsDialog *dialog = (PpOptionsDialog*) user_data; pp_options_dialog_hide (dialog); dialog->response = response_id; if (!dialog->populating_dialog) dialog->user_callback (GTK_DIALOG (dialog->dialog), response_id, dialog->user_data); } PpOptionsDialog * pp_options_dialog_new (GtkWindow *parent, UserResponseCallback user_callback, gpointer user_data, gchar *printer_name, gboolean sensitive) { PpOptionsDialog *dialog; GtkWidget *widget; GError *error = NULL; gchar *objects[] = { "options-dialog", NULL }; guint builder_result; gchar *title; dialog = g_new0 (PpOptionsDialog, 1); dialog->builder = gtk_builder_new (); dialog->parent = GTK_WIDGET (parent); builder_result = gtk_builder_add_objects_from_file (dialog->builder, DATADIR"/options-dialog.ui", objects, &error); if (builder_result == 0) { g_warning ("Could not load ui: %s", error->message); g_error_free (error); return NULL; } dialog->dialog = (GtkWidget *) gtk_builder_get_object (dialog->builder, "options-dialog"); gtk_window_set_transient_for (GTK_WINDOW (dialog->dialog), GTK_WINDOW (parent)); dialog->user_callback = user_callback; dialog->user_data = user_data; dialog->printer_name = g_strdup (printer_name); dialog->ppd_filename = NULL; dialog->ppd_filename_set = FALSE; dialog->destination = NULL; dialog->destination_set = FALSE; dialog->ipp_attributes = NULL; dialog->ipp_attributes_set = FALSE; dialog->response = GTK_RESPONSE_NONE; dialog->sensitive = sensitive; /* connect signals */ g_signal_connect (dialog->dialog, "response", G_CALLBACK (options_dialog_response_cb), dialog); g_signal_connect (dialog->dialog, "size-allocate", G_CALLBACK (update_alignment_padding), dialog); widget = (GtkWidget*) gtk_builder_get_object (dialog->builder, "options-title"); /* Translators: Options of given printer (e.g. "MyPrinter Options") */ title = g_strdup_printf (_("%s Options"), printer_name); gtk_label_set_label (GTK_LABEL (widget), title); g_free (title); gtk_widget_show_all (GTK_WIDGET (dialog->dialog)); dialog->populating_dialog = TRUE; populate_options (dialog); return dialog; } void pp_options_dialog_free (PpOptionsDialog *dialog) { gtk_widget_destroy (GTK_WIDGET (dialog->dialog)); dialog->dialog = NULL; g_object_unref (dialog->builder); dialog->builder = NULL; g_free (dialog->printer_name); dialog->printer_name = NULL; if (dialog->ppd_filename) { g_unlink (dialog->ppd_filename); g_free (dialog->ppd_filename); dialog->ppd_filename = NULL; } if (dialog->destination) { cupsFreeDests (1, dialog->destination); dialog->destination = NULL; } if (dialog->ipp_attributes) { g_hash_table_unref (dialog->ipp_attributes); dialog->ipp_attributes = NULL; } g_free (dialog); } static void pp_options_dialog_hide (PpOptionsDialog *dialog) { gtk_widget_hide (GTK_WIDGET (dialog->dialog)); }