1 /* GtkPageSetupUnixDialog
2  * Copyright (C) 2006 Alexander Larsson <alexl@redhat.com>
3  * Copyright © 2006, 2007, 2008 Christian Persch
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 
20 #include "config.h"
21 #include <string.h>
22 #include <locale.h>
23 
24 #include "gtkintl.h"
25 #include "gtkprivate.h"
26 
27 #include "gtkbutton.h"
28 #include "gtkscrolledwindow.h"
29 #include "gtkcheckbutton.h"
30 #include "gtklabel.h"
31 #include "gtkgrid.h"
32 #include "gtkcelllayout.h"
33 #include "gtkcellrenderertext.h"
34 
35 #include "gtkpagesetupunixdialog.h"
36 #include "gtkcustompaperunixdialog.h"
37 #include "gtkprintbackendprivate.h"
38 #include "gtkpapersize.h"
39 #include "gtkprintutils.h"
40 #include "gtkdialogprivate.h"
41 
42 /**
43  * GtkPageSetupUnixDialog:
44  *
45  * `GtkPageSetupUnixDialog` implements a page setup dialog for platforms
46  * which don’t provide a native page setup dialog, like Unix.
47  *
48  * ![An example GtkPageSetupUnixDialog](pagesetupdialog.png)
49  *
50  * It can be used very much like any other GTK dialog, at the
51  * cost of the portability offered by the high-level printing
52  * API in [class@Gtk.PrintOperation].
53  */
54 
55 typedef struct _GtkPageSetupUnixDialogClass    GtkPageSetupUnixDialogClass;
56 
57 struct _GtkPageSetupUnixDialog
58 {
59   GtkDialog parent_instance;
60 
61   GListModel *printer_list;
62   GListStore *page_setup_list;
63   GListStore *custom_paper_list;
64   GListStore *manage_papers_list;
65   GListStore *paper_size_list;
66 
67   GList *print_backends;
68 
69   GtkWidget *printer_combo;
70   GtkWidget *paper_size_combo;
71   GtkWidget *paper_size_label;
72 
73   GtkWidget *portrait_radio;
74   GtkWidget *reverse_portrait_radio;
75   GtkWidget *landscape_radio;
76   GtkWidget *reverse_landscape_radio;
77 
78   gulong request_details_tag;
79   GtkPrinter *request_details_printer;
80 
81   GtkPrintSettings *print_settings;
82 
83   gboolean internal_change;
84 
85   /* Save last setup so we can re-set it after selecting manage custom sizes */
86   GtkPageSetup *last_setup;
87 };
88 
89 struct _GtkPageSetupUnixDialogClass
90 {
91   GtkDialogClass parent_class;
92 };
93 
94 
95 /* Keep these in line with GtkListStores defined in gtkpagesetupunixprintdialog.ui */
96 enum {
97   PRINTER_LIST_COL_NAME,
98   PRINTER_LIST_COL_PRINTER,
99   PRINTER_LIST_N_COLS
100 };
101 
102 enum {
103   PAGE_SETUP_LIST_COL_PAGE_SETUP,
104   PAGE_SETUP_LIST_COL_IS_SEPARATOR,
105   PAGE_SETUP_LIST_N_COLS
106 };
107 
108 G_DEFINE_TYPE (GtkPageSetupUnixDialog, gtk_page_setup_unix_dialog, GTK_TYPE_DIALOG)
109 
110 static void gtk_page_setup_unix_dialog_finalize  (GObject                *object);
111 static void fill_paper_sizes_from_printer        (GtkPageSetupUnixDialog *dialog,
112                                                   GtkPrinter             *printer);
113 static void printer_changed_callback             (GtkDropDown            *combo_box,
114                                                   GParamSpec             *pspec,
115                                                   GtkPageSetupUnixDialog *dialog);
116 static void paper_size_changed                   (GtkDropDown            *combo_box,
117                                                   GParamSpec             *pspec,
118                                                   GtkPageSetupUnixDialog *dialog);
119 static void load_print_backends                  (GtkPageSetupUnixDialog *dialog);
120 
121 
122 static const char common_paper_sizes[][16] = {
123   "na_letter",
124   "na_legal",
125   "iso_a4",
126   "iso_a5",
127   "roc_16k",
128   "iso_b5",
129   "jis_b5",
130   "na_number-10",
131   "iso_dl",
132   "jpn_chou3",
133   "na_ledger",
134   "iso_a3"
135 };
136 
137 
138 static void
gtk_page_setup_unix_dialog_class_init(GtkPageSetupUnixDialogClass * class)139 gtk_page_setup_unix_dialog_class_init (GtkPageSetupUnixDialogClass *class)
140 {
141   GObjectClass *object_class;
142   GtkWidgetClass *widget_class;
143 
144   object_class = G_OBJECT_CLASS (class);
145   widget_class = GTK_WIDGET_CLASS (class);
146 
147   object_class->finalize = gtk_page_setup_unix_dialog_finalize;
148 
149   /* Bind class to template
150    */
151   gtk_widget_class_set_template_from_resource (widget_class,
152                                                "/org/gtk/libgtk/ui/gtkpagesetupunixdialog.ui");
153 
154   gtk_widget_class_bind_template_child (widget_class, GtkPageSetupUnixDialog, printer_combo);
155   gtk_widget_class_bind_template_child (widget_class, GtkPageSetupUnixDialog, paper_size_combo);
156   gtk_widget_class_bind_template_child (widget_class, GtkPageSetupUnixDialog, paper_size_label);
157   gtk_widget_class_bind_template_child (widget_class, GtkPageSetupUnixDialog, portrait_radio);
158   gtk_widget_class_bind_template_child (widget_class, GtkPageSetupUnixDialog, reverse_portrait_radio);
159   gtk_widget_class_bind_template_child (widget_class, GtkPageSetupUnixDialog, landscape_radio);
160   gtk_widget_class_bind_template_child (widget_class, GtkPageSetupUnixDialog, reverse_landscape_radio);
161 
162   gtk_widget_class_bind_template_callback (widget_class, printer_changed_callback);
163   gtk_widget_class_bind_template_callback (widget_class, paper_size_changed);
164 }
165 
166 static void
setup_paper_size_item(GtkSignalListItemFactory * factory,GtkListItem * item)167 setup_paper_size_item (GtkSignalListItemFactory *factory,
168                        GtkListItem              *item)
169 {
170   GtkWidget *label;
171 
172   label = gtk_label_new ("");
173   gtk_widget_set_halign (label, GTK_ALIGN_START);
174   gtk_list_item_set_child (item, label);
175 }
176 
177 static void
bind_paper_size_list_item(GtkSignalListItemFactory * factory,GtkListItem * item,GtkPageSetupUnixDialog * self)178 bind_paper_size_list_item (GtkSignalListItemFactory *factory,
179                            GtkListItem              *item,
180                            GtkPageSetupUnixDialog   *self)
181 {
182   GtkPageSetup *page_setup;
183   GtkWidget *label;
184   guint pos;
185   GListModel *papers;
186   GListModel *model;
187   gpointer first;
188 
189   page_setup = gtk_list_item_get_item (item);
190   label = gtk_list_item_get_child (item);
191 
192   pos = gtk_list_item_get_position (item);
193   papers = gtk_drop_down_get_model (GTK_DROP_DOWN (self->paper_size_combo));
194   model = gtk_flatten_list_model_get_model_for_item (GTK_FLATTEN_LIST_MODEL (papers), pos);
195   if (model != G_LIST_MODEL (self->manage_papers_list))
196     {
197       GtkPaperSize *paper_size = gtk_page_setup_get_paper_size (page_setup);
198       gtk_label_set_text (GTK_LABEL (label), gtk_paper_size_get_display_name (paper_size));
199     }
200   else
201     gtk_label_set_text (GTK_LABEL (label), _("Manage Custom Sizes…"));
202 
203   first = g_list_model_get_item (model, 0);
204   g_object_unref (first);
205   if (pos != 0 &&
206       page_setup == GTK_PAGE_SETUP (first))
207     gtk_widget_add_css_class (gtk_widget_get_parent (label), "separator");
208   else
209     gtk_widget_remove_css_class (gtk_widget_get_parent (label), "separator");
210 }
211 
212 static void
bind_paper_size_item(GtkSignalListItemFactory * factory,GtkListItem * item,GtkPageSetupUnixDialog * self)213 bind_paper_size_item (GtkSignalListItemFactory *factory,
214                       GtkListItem              *item,
215                       GtkPageSetupUnixDialog   *self)
216 {
217   GtkWidget *label;
218 
219   bind_paper_size_list_item (factory, item, self);
220 
221   label = gtk_list_item_get_child (item);
222   gtk_widget_remove_css_class (label, "separator-before");
223 }
224 
225 static gboolean
match_func(gpointer item,gpointer user_data)226 match_func (gpointer item, gpointer user_data)
227 {
228   return !gtk_printer_is_virtual (GTK_PRINTER (item));
229 }
230 
231 static void
setup_printer_item(GtkSignalListItemFactory * factory,GtkListItem * item)232 setup_printer_item (GtkSignalListItemFactory *factory,
233                     GtkListItem              *item)
234 {
235   GtkWidget *label;
236 
237   label = gtk_label_new ("");
238   gtk_widget_set_halign (label, GTK_ALIGN_START);
239   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
240   gtk_list_item_set_child (item, label);
241 }
242 
243 static void
bind_printer_item(GtkSignalListItemFactory * factory,GtkListItem * item,GtkPageSetupUnixDialog * self)244 bind_printer_item (GtkSignalListItemFactory *factory,
245                    GtkListItem              *item,
246                    GtkPageSetupUnixDialog   *self)
247 {
248   GtkPrinter *printer;
249   GtkWidget *label;
250   const char *location;
251   const char *name;
252   char *str;
253 
254   printer = gtk_list_item_get_item (item);
255   label = gtk_list_item_get_child (item);
256 
257   name = gtk_printer_get_name (printer);
258   location = gtk_printer_get_location (printer);
259   str = g_strdup_printf ("<b>%s</b>\n%s", name, location ? location : "");
260   gtk_label_set_markup (GTK_LABEL (label), str);
261   g_free (str);
262 }
263 
264 static void
gtk_page_setup_unix_dialog_init(GtkPageSetupUnixDialog * dialog)265 gtk_page_setup_unix_dialog_init (GtkPageSetupUnixDialog *dialog)
266 {
267   GtkListItemFactory *factory;
268   GListStore *store;
269   GListModel *paper_size_list;
270   GtkPrinter *printer;
271   GListStore *printer_list;
272   GListStore *printer_list_list;
273   GListModel *full_list;
274   GtkFilter *filter;
275   GtkPageSetup *page_setup;
276 
277   dialog->internal_change = TRUE;
278   dialog->print_backends = NULL;
279 
280   gtk_widget_init_template (GTK_WIDGET (dialog));
281   gtk_dialog_set_use_header_bar_from_setting (GTK_DIALOG (dialog));
282   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
283                           _("_Cancel"), GTK_RESPONSE_CANCEL,
284                           _("_Apply"), GTK_RESPONSE_OK,
285                           NULL);
286   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
287 
288   dialog->page_setup_list = g_list_store_new (GTK_TYPE_PAGE_SETUP);
289   dialog->custom_paper_list = g_list_store_new (GTK_TYPE_PAGE_SETUP);
290   dialog->manage_papers_list = g_list_store_new (GTK_TYPE_PAGE_SETUP);
291   page_setup = gtk_page_setup_new ();
292   g_list_store_append (dialog->manage_papers_list, page_setup);
293   g_object_unref (page_setup);
294 
295   factory = gtk_signal_list_item_factory_new ();
296   g_signal_connect (factory, "setup", G_CALLBACK (setup_paper_size_item), dialog);
297   g_signal_connect (factory, "bind", G_CALLBACK (bind_paper_size_item), dialog);
298   gtk_drop_down_set_factory (GTK_DROP_DOWN (dialog->paper_size_combo), factory);
299   g_object_unref (factory);
300 
301   factory = gtk_signal_list_item_factory_new ();
302   g_signal_connect (factory, "setup", G_CALLBACK (setup_paper_size_item), dialog);
303   g_signal_connect (factory, "bind", G_CALLBACK (bind_paper_size_list_item), dialog);
304   gtk_drop_down_set_list_factory (GTK_DROP_DOWN (dialog->paper_size_combo), factory);
305   g_object_unref (factory);
306 
307   store = g_list_store_new (G_TYPE_LIST_MODEL);
308   g_list_store_append (store, dialog->page_setup_list);
309   g_list_store_append (store, dialog->custom_paper_list);
310   g_list_store_append (store, dialog->manage_papers_list);
311   paper_size_list = G_LIST_MODEL (gtk_flatten_list_model_new (G_LIST_MODEL (store)));
312   gtk_drop_down_set_model (GTK_DROP_DOWN (dialog->paper_size_combo), paper_size_list);
313   g_object_unref (paper_size_list);
314 
315   /* Do this in code, we want the translatable strings without the markup */
316   printer_list_list = g_list_store_new (G_TYPE_LIST_MODEL);
317   printer_list = g_list_store_new (GTK_TYPE_PRINTER);
318   printer = gtk_printer_new (_("Any Printer"), NULL, FALSE);
319   gtk_printer_set_location (printer, _("For portable documents"));
320   g_list_store_append (printer_list, printer);
321   g_object_unref (printer);
322   g_list_store_append (printer_list_list, printer_list);
323   g_object_unref (printer_list);
324 
325   full_list = G_LIST_MODEL (gtk_flatten_list_model_new (G_LIST_MODEL (printer_list_list)));
326 
327   filter = GTK_FILTER (gtk_custom_filter_new (match_func, NULL, NULL));
328   dialog->printer_list = G_LIST_MODEL (gtk_filter_list_model_new (full_list, filter));
329 
330   factory = gtk_signal_list_item_factory_new ();
331   g_signal_connect (factory, "setup", G_CALLBACK (setup_printer_item), dialog);
332   g_signal_connect (factory, "bind", G_CALLBACK (bind_printer_item), dialog);
333   gtk_drop_down_set_factory (GTK_DROP_DOWN (dialog->printer_combo), factory);
334   g_object_unref (factory);
335 
336   gtk_drop_down_set_model (GTK_DROP_DOWN (dialog->printer_combo), dialog->printer_list);
337   printer_changed_callback (GTK_DROP_DOWN (dialog->printer_combo), NULL, dialog);
338 
339   /* Load data */
340   gtk_print_load_custom_papers (dialog->custom_paper_list);
341   load_print_backends (dialog);
342   dialog->internal_change = FALSE;
343 }
344 
345 static void
gtk_page_setup_unix_dialog_finalize(GObject * object)346 gtk_page_setup_unix_dialog_finalize (GObject *object)
347 {
348   GtkPageSetupUnixDialog *dialog = GTK_PAGE_SETUP_UNIX_DIALOG (object);
349   GList *node;
350 
351   if (dialog->request_details_tag)
352     {
353       g_signal_handler_disconnect (dialog->request_details_printer,
354                                    dialog->request_details_tag);
355       g_object_unref (dialog->request_details_printer);
356       dialog->request_details_printer = NULL;
357       dialog->request_details_tag = 0;
358     }
359 
360   g_clear_object (&dialog->printer_list);
361   g_clear_object (&dialog->page_setup_list);
362   g_clear_object (&dialog->custom_paper_list);
363   g_clear_object (&dialog->manage_papers_list);
364 
365   if (dialog->print_settings)
366     {
367       g_object_unref (dialog->print_settings);
368       dialog->print_settings = NULL;
369     }
370 
371   for (node = dialog->print_backends; node != NULL; node = node->next)
372     gtk_print_backend_destroy (GTK_PRINT_BACKEND (node->data));
373   g_list_free_full (dialog->print_backends, g_object_unref);
374   dialog->print_backends = NULL;
375 
376   G_OBJECT_CLASS (gtk_page_setup_unix_dialog_parent_class)->finalize (object);
377 }
378 
379 static void
load_print_backends(GtkPageSetupUnixDialog * dialog)380 load_print_backends (GtkPageSetupUnixDialog *dialog)
381 {
382   GListModel *full_list;
383   GListStore *printer_list_list;
384   GList *node;
385 
386   full_list = gtk_filter_list_model_get_model (GTK_FILTER_LIST_MODEL (dialog->printer_list));
387   printer_list_list = G_LIST_STORE (gtk_flatten_list_model_get_model (GTK_FLATTEN_LIST_MODEL (full_list)));
388 
389   if (g_module_supported ())
390     dialog->print_backends = gtk_print_backend_load_modules ();
391 
392   for (node = dialog->print_backends; node != NULL; node = node->next)
393     {
394       GtkPrintBackend *backend = node->data;
395       g_list_store_append (printer_list_list, gtk_print_backend_get_printers (backend));
396     }
397 }
398 
399 static GtkPageSetup *
get_current_page_setup(GtkPageSetupUnixDialog * dialog)400 get_current_page_setup (GtkPageSetupUnixDialog *dialog)
401 {
402   guint selected;
403   GListModel *model;
404 
405   selected = gtk_drop_down_get_selected (GTK_DROP_DOWN (dialog->paper_size_combo));
406   model = gtk_drop_down_get_model (GTK_DROP_DOWN (dialog->paper_size_combo));
407   if (selected != GTK_INVALID_LIST_POSITION)
408     return g_list_model_get_item (model, selected);
409 
410   return gtk_page_setup_new ();
411 }
412 
413 static gboolean
page_setup_is_equal(GtkPageSetup * a,GtkPageSetup * b)414 page_setup_is_equal (GtkPageSetup *a,
415                      GtkPageSetup *b)
416 {
417   return
418     gtk_paper_size_is_equal (gtk_page_setup_get_paper_size (a),
419                              gtk_page_setup_get_paper_size (b)) &&
420     gtk_page_setup_get_top_margin (a, GTK_UNIT_MM) == gtk_page_setup_get_top_margin (b, GTK_UNIT_MM) &&
421     gtk_page_setup_get_bottom_margin (a, GTK_UNIT_MM) == gtk_page_setup_get_bottom_margin (b, GTK_UNIT_MM) &&
422     gtk_page_setup_get_left_margin (a, GTK_UNIT_MM) == gtk_page_setup_get_left_margin (b, GTK_UNIT_MM) &&
423     gtk_page_setup_get_right_margin (a, GTK_UNIT_MM) == gtk_page_setup_get_right_margin (b, GTK_UNIT_MM);
424 }
425 
426 static gboolean
page_setup_is_same_size(GtkPageSetup * a,GtkPageSetup * b)427 page_setup_is_same_size (GtkPageSetup *a,
428                          GtkPageSetup *b)
429 {
430   return gtk_paper_size_is_equal (gtk_page_setup_get_paper_size (a),
431                                   gtk_page_setup_get_paper_size (b));
432 }
433 
434 static gboolean
set_paper_size(GtkPageSetupUnixDialog * dialog,GtkPageSetup * page_setup,gboolean size_only,gboolean add_item)435 set_paper_size (GtkPageSetupUnixDialog *dialog,
436                 GtkPageSetup           *page_setup,
437                 gboolean                size_only,
438                 gboolean                add_item)
439 {
440   GListModel *model;
441   GtkPageSetup *list_page_setup;
442   guint i;
443 
444   if (page_setup == NULL)
445     return FALSE;
446 
447   model = gtk_drop_down_get_model (GTK_DROP_DOWN (dialog->paper_size_combo));
448   for (i = 0; i < g_list_model_get_n_items (model); i++)
449     {
450       list_page_setup = g_list_model_get_item (model, i);
451       if (list_page_setup == NULL)
452         continue;
453 
454       if ((size_only && page_setup_is_same_size (page_setup, list_page_setup)) ||
455           (!size_only && page_setup_is_equal (page_setup, list_page_setup)))
456         {
457           gtk_drop_down_set_selected (GTK_DROP_DOWN (dialog->paper_size_combo), i);
458           g_object_unref (list_page_setup);
459           return TRUE;
460         }
461 
462       g_object_unref (list_page_setup);
463     }
464 
465   if (add_item)
466     {
467       i = g_list_model_get_n_items (model);
468       g_list_store_append (dialog->page_setup_list, page_setup);
469       gtk_drop_down_set_selected (GTK_DROP_DOWN (dialog->paper_size_combo), i);
470       return TRUE;
471     }
472 
473   return FALSE;
474 }
475 
476 static void
fill_paper_sizes_from_printer(GtkPageSetupUnixDialog * dialog,GtkPrinter * printer)477 fill_paper_sizes_from_printer (GtkPageSetupUnixDialog *dialog,
478                                GtkPrinter             *printer)
479 {
480   GList *list, *l;
481   GtkPageSetup *current_page_setup, *page_setup;
482   GtkPaperSize *paper_size;
483   int i;
484 
485   g_list_store_remove_all (dialog->page_setup_list);
486 
487   if (printer == NULL)
488     {
489       for (i = 0; i < G_N_ELEMENTS (common_paper_sizes); i++)
490         {
491           page_setup = gtk_page_setup_new ();
492           paper_size = gtk_paper_size_new (common_paper_sizes[i]);
493           gtk_page_setup_set_paper_size_and_default_margins (page_setup, paper_size);
494           gtk_paper_size_free (paper_size);
495 
496           g_list_store_append (dialog->page_setup_list, page_setup);
497           g_object_unref (page_setup);
498         }
499     }
500   else
501     {
502       list = gtk_printer_list_papers (printer);
503       /* TODO: We should really sort this list so interesting size
504          are at the top */
505       for (l = list; l != NULL; l = l->next)
506         {
507           page_setup = l->data;
508           g_list_store_append (dialog->page_setup_list, page_setup);
509           g_object_unref (page_setup);
510         }
511       g_list_free (list);
512     }
513 
514   current_page_setup = NULL;
515 
516   /* When selecting a different printer, select its default paper size */
517   if (printer != NULL)
518     current_page_setup = gtk_printer_get_default_page_size (printer);
519 
520   if (current_page_setup == NULL)
521     current_page_setup = get_current_page_setup (dialog);
522 
523   if (!set_paper_size (dialog, current_page_setup, FALSE, FALSE))
524     set_paper_size (dialog, current_page_setup, TRUE, TRUE);
525 
526   if (current_page_setup)
527     g_object_unref (current_page_setup);
528 }
529 
530 static void
printer_changed_finished_callback(GtkPrinter * printer,gboolean success,GtkPageSetupUnixDialog * dialog)531 printer_changed_finished_callback (GtkPrinter             *printer,
532                                    gboolean                success,
533                                    GtkPageSetupUnixDialog *dialog)
534 {
535   g_signal_handler_disconnect (dialog->request_details_printer,
536                                dialog->request_details_tag);
537   g_object_unref (dialog->request_details_printer);
538   dialog->request_details_tag = 0;
539   dialog->request_details_printer = NULL;
540 
541   if (success)
542     fill_paper_sizes_from_printer (dialog, printer);
543 
544 }
545 
546 static void
printer_changed_callback(GtkDropDown * combo_box,GParamSpec * pspec,GtkPageSetupUnixDialog * dialog)547 printer_changed_callback (GtkDropDown            *combo_box,
548                           GParamSpec             *pspec,
549                           GtkPageSetupUnixDialog *dialog)
550 {
551   GtkPrinter *printer;
552   guint selected;
553 
554   if (dialog->request_details_tag)
555     {
556       g_signal_handler_disconnect (dialog->request_details_printer,
557                                    dialog->request_details_tag);
558       g_object_unref (dialog->request_details_printer);
559       dialog->request_details_printer = NULL;
560       dialog->request_details_tag = 0;
561     }
562 
563   selected = gtk_drop_down_get_selected (GTK_DROP_DOWN (dialog->printer_combo));
564   if (selected != GTK_INVALID_LIST_POSITION)
565     {
566       GListModel *model;
567 
568       model = gtk_drop_down_get_model (GTK_DROP_DOWN (dialog->printer_combo));
569       printer = g_list_model_get_item (model, selected);
570       if (strcmp (gtk_printer_get_name (printer), _("Any Printer")) == 0)
571         g_clear_object (&printer);
572 
573       if (printer == NULL ||
574           gtk_printer_has_details (printer))
575         fill_paper_sizes_from_printer (dialog, printer);
576       else
577         {
578           dialog->request_details_printer = g_object_ref (printer);
579           dialog->request_details_tag =
580             g_signal_connect (printer, "details-acquired",
581                               G_CALLBACK (printer_changed_finished_callback), dialog);
582           gtk_printer_request_details (printer);
583         }
584 
585       if (printer)
586         g_object_unref (printer);
587 
588       if (dialog->print_settings)
589         {
590           const char *name = NULL;
591 
592           if (printer)
593             name = gtk_printer_get_name (printer);
594 
595           gtk_print_settings_set (dialog->print_settings,
596                                   "format-for-printer", name);
597         }
598     }
599 }
600 
601 /* We do this munging because we don't want to show zero digits
602    after the decimal point, and not to many such digits if they
603    are nonzero. I wish printf let you specify max precision for %f... */
604 static char *
double_to_string(double d,GtkUnit unit)605 double_to_string (double d,
606                   GtkUnit unit)
607 {
608   char *val, *p;
609   struct lconv *locale_data;
610   const char *decimal_point;
611   int decimal_point_len;
612 
613   locale_data = localeconv ();
614   decimal_point = locale_data->decimal_point;
615   decimal_point_len = strlen (decimal_point);
616 
617   /* Max two decimal digits for inch, max one for mm */
618   if (unit == GTK_UNIT_INCH)
619     val = g_strdup_printf ("%.2f", d);
620   else
621     val = g_strdup_printf ("%.1f", d);
622 
623   if (strstr (val, decimal_point))
624     {
625       p = val + strlen (val) - 1;
626       while (*p == '0')
627         p--;
628       if (p - val + 1 >= decimal_point_len &&
629           strncmp (p - (decimal_point_len - 1), decimal_point, decimal_point_len) == 0)
630         p -= decimal_point_len;
631       p[1] = '\0';
632     }
633 
634   return val;
635 }
636 
637 
638 static void
custom_paper_dialog_response_cb(GtkDialog * custom_paper_dialog,int response_id,gpointer user_data)639 custom_paper_dialog_response_cb (GtkDialog *custom_paper_dialog,
640                                  int        response_id,
641                                  gpointer   user_data)
642 {
643   GtkPageSetupUnixDialog *dialog = GTK_PAGE_SETUP_UNIX_DIALOG (user_data);
644   GtkPageSetup *last_page_setup;
645 
646   dialog->internal_change = TRUE;
647   gtk_print_load_custom_papers (dialog->custom_paper_list);
648   printer_changed_callback (GTK_DROP_DOWN (dialog->printer_combo), NULL, dialog);
649   dialog->internal_change = FALSE;
650 
651   if (dialog->last_setup)
652     last_page_setup = g_object_ref (dialog->last_setup);
653   else
654     last_page_setup = gtk_page_setup_new (); /* "good" default */
655   set_paper_size (dialog, last_page_setup, FALSE, TRUE);
656   g_object_unref (last_page_setup);
657 
658   gtk_window_destroy (GTK_WINDOW (custom_paper_dialog));
659 }
660 
661 static void
paper_size_changed(GtkDropDown * combo_box,GParamSpec * pspec,GtkPageSetupUnixDialog * dialog)662 paper_size_changed (GtkDropDown            *combo_box,
663                     GParamSpec             *pspec,
664                     GtkPageSetupUnixDialog *dialog)
665 {
666   GtkPageSetup *page_setup, *last_page_setup;
667   guint selected;
668   GtkUnit unit;
669   char *str, *w, *h;
670   char *top, *bottom, *left, *right;
671   GtkLabel *label;
672   const char *unit_str;
673 
674   if (dialog->internal_change)
675     return;
676 
677   label = GTK_LABEL (dialog->paper_size_label);
678 
679   selected = gtk_drop_down_get_selected (GTK_DROP_DOWN (combo_box));
680   if (selected != GTK_INVALID_LIST_POSITION)
681     {
682       GListModel *papers, *model;
683 
684       papers = gtk_drop_down_get_model (GTK_DROP_DOWN (dialog->paper_size_combo));
685       page_setup = g_list_model_get_item (papers, selected);
686       model = gtk_flatten_list_model_get_model_for_item (GTK_FLATTEN_LIST_MODEL (papers), selected);
687 
688       if (model == G_LIST_MODEL (dialog->manage_papers_list))
689         {
690           GtkWidget *custom_paper_dialog;
691 
692           /* Change from "manage" menu item to last value */
693           if (dialog->last_setup)
694             last_page_setup = g_object_ref (dialog->last_setup);
695           else
696             last_page_setup = gtk_page_setup_new (); /* "good" default */
697           set_paper_size (dialog, last_page_setup, FALSE, TRUE);
698           g_object_unref (last_page_setup);
699 
700           /* And show the custom paper dialog */
701           custom_paper_dialog = _gtk_custom_paper_unix_dialog_new (GTK_WINDOW (dialog), NULL);
702           g_signal_connect (custom_paper_dialog, "response", G_CALLBACK (custom_paper_dialog_response_cb), dialog);
703           gtk_window_present (GTK_WINDOW (custom_paper_dialog));
704 
705           g_object_unref (page_setup);
706 
707           return;
708         }
709 
710       if (dialog->last_setup)
711         g_object_unref (dialog->last_setup);
712 
713       dialog->last_setup = g_object_ref (page_setup);
714 
715       unit = _gtk_print_get_default_user_units ();
716 
717       if (unit == GTK_UNIT_MM)
718         unit_str = _("mm");
719       else
720         unit_str = _("inch");
721 
722       w = double_to_string (gtk_page_setup_get_paper_width (page_setup, unit),
723                             unit);
724       h = double_to_string (gtk_page_setup_get_paper_height (page_setup, unit),
725                             unit);
726       str = g_strdup_printf ("%s × %s %s", w, h, unit_str);
727       g_free (w);
728       g_free (h);
729 
730       gtk_label_set_text (label, str);
731       g_free (str);
732 
733       top = double_to_string (gtk_page_setup_get_top_margin (page_setup, unit), unit);
734       bottom = double_to_string (gtk_page_setup_get_bottom_margin (page_setup, unit), unit);
735       left = double_to_string (gtk_page_setup_get_left_margin (page_setup, unit), unit);
736       right = double_to_string (gtk_page_setup_get_right_margin (page_setup, unit), unit);
737 
738       str = g_strdup_printf (_("Margins:\n"
739                                " Left: %s %s\n"
740                                " Right: %s %s\n"
741                                " Top: %s %s\n"
742                                " Bottom: %s %s"
743                                ),
744                              left, unit_str,
745                              right, unit_str,
746                              top, unit_str,
747                              bottom, unit_str);
748       g_free (top);
749       g_free (bottom);
750       g_free (left);
751       g_free (right);
752 
753       gtk_widget_set_tooltip_text (dialog->paper_size_label, str);
754       g_free (str);
755 
756       g_object_unref (page_setup);
757     }
758   else
759     {
760       gtk_label_set_text (label, "");
761       gtk_widget_set_tooltip_text (dialog->paper_size_label, NULL);
762       if (dialog->last_setup)
763         g_object_unref (dialog->last_setup);
764       dialog->last_setup = NULL;
765     }
766 }
767 
768 /**
769  * gtk_page_setup_unix_dialog_new:
770  * @title: (nullable): the title of the dialog
771  * @parent: (nullable): transient parent of the dialog
772  *
773  * Creates a new page setup dialog.
774  *
775  * Returns: the new `GtkPageSetupUnixDialog`
776  */
777 GtkWidget *
gtk_page_setup_unix_dialog_new(const char * title,GtkWindow * parent)778 gtk_page_setup_unix_dialog_new (const char *title,
779                                 GtkWindow   *parent)
780 {
781   GtkWidget *result;
782 
783   if (title == NULL)
784     title = _("Page Setup");
785 
786   result = g_object_new (GTK_TYPE_PAGE_SETUP_UNIX_DIALOG,
787                          "title", title,
788                          NULL);
789 
790   if (parent)
791     gtk_window_set_transient_for (GTK_WINDOW (result), parent);
792 
793   return result;
794 }
795 
796 static GtkPageOrientation
get_orientation(GtkPageSetupUnixDialog * dialog)797 get_orientation (GtkPageSetupUnixDialog *dialog)
798 {
799   if (gtk_check_button_get_active (GTK_CHECK_BUTTON (dialog->portrait_radio)))
800     return GTK_PAGE_ORIENTATION_PORTRAIT;
801   if (gtk_check_button_get_active (GTK_CHECK_BUTTON (dialog->landscape_radio)))
802     return GTK_PAGE_ORIENTATION_LANDSCAPE;
803   if (gtk_check_button_get_active (GTK_CHECK_BUTTON (dialog->reverse_landscape_radio)))
804     return GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE;
805   return GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT;
806 }
807 
808 static void
set_orientation(GtkPageSetupUnixDialog * dialog,GtkPageOrientation orientation)809 set_orientation (GtkPageSetupUnixDialog *dialog,
810                  GtkPageOrientation      orientation)
811 {
812   switch (orientation)
813     {
814     case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
815       gtk_check_button_set_active (GTK_CHECK_BUTTON (dialog->reverse_portrait_radio), TRUE);
816       break;
817     case GTK_PAGE_ORIENTATION_PORTRAIT:
818       gtk_check_button_set_active (GTK_CHECK_BUTTON (dialog->portrait_radio), TRUE);
819       break;
820     case GTK_PAGE_ORIENTATION_LANDSCAPE:
821       gtk_check_button_set_active (GTK_CHECK_BUTTON (dialog->landscape_radio), TRUE);
822       break;
823     case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
824       gtk_check_button_set_active (GTK_CHECK_BUTTON (dialog->reverse_landscape_radio), TRUE);
825       break;
826     default:
827       break;
828     }
829 }
830 
831 /**
832  * gtk_page_setup_unix_dialog_set_page_setup:
833  * @dialog: a `GtkPageSetupUnixDialog`
834  * @page_setup: a `GtkPageSetup`
835  *
836  * Sets the `GtkPageSetup` from which the page setup
837  * dialog takes its values.
838  */
839 void
gtk_page_setup_unix_dialog_set_page_setup(GtkPageSetupUnixDialog * dialog,GtkPageSetup * page_setup)840 gtk_page_setup_unix_dialog_set_page_setup (GtkPageSetupUnixDialog *dialog,
841                                            GtkPageSetup           *page_setup)
842 {
843   if (page_setup)
844     {
845       set_paper_size (dialog, page_setup, FALSE, TRUE);
846       set_orientation (dialog, gtk_page_setup_get_orientation (page_setup));
847     }
848 }
849 
850 /**
851  * gtk_page_setup_unix_dialog_get_page_setup:
852  * @dialog: a `GtkPageSetupUnixDialog`
853  *
854  * Gets the currently selected page setup from the dialog.
855  *
856  * Returns: (transfer none): the current page setup
857  */
858 GtkPageSetup *
gtk_page_setup_unix_dialog_get_page_setup(GtkPageSetupUnixDialog * dialog)859 gtk_page_setup_unix_dialog_get_page_setup (GtkPageSetupUnixDialog *dialog)
860 {
861   GtkPageSetup *page_setup;
862 
863   page_setup = get_current_page_setup (dialog);
864 
865   gtk_page_setup_set_orientation (page_setup, get_orientation (dialog));
866 
867   return page_setup;
868 }
869 
870 static gboolean
set_active_printer(GtkPageSetupUnixDialog * dialog,const char * printer_name)871 set_active_printer (GtkPageSetupUnixDialog *dialog,
872                     const char             *printer_name)
873 {
874   guint i, n;
875   GtkPrinter *printer;
876 
877   if (!printer_name)
878     return FALSE;
879 
880   for (i = 0, n = g_list_model_get_n_items (dialog->printer_list); i < n; i++)
881     {
882       printer = g_list_model_get_item (dialog->printer_list, i);
883 
884       if (strcmp (gtk_printer_get_name (printer), printer_name) == 0)
885         {
886           gtk_drop_down_set_selected (GTK_DROP_DOWN (dialog->printer_combo), i);
887           g_object_unref (printer);
888 
889           return TRUE;
890         }
891 
892       g_object_unref (printer);
893     }
894 
895   return FALSE;
896 }
897 
898 /**
899  * gtk_page_setup_unix_dialog_set_print_settings:
900  * @dialog: a `GtkPageSetupUnixDialog`
901  * @print_settings: a `GtkPrintSettings`
902  *
903  * Sets the `GtkPrintSettings` from which the page setup dialog
904  * takes its values.
905  */
906 void
gtk_page_setup_unix_dialog_set_print_settings(GtkPageSetupUnixDialog * dialog,GtkPrintSettings * print_settings)907 gtk_page_setup_unix_dialog_set_print_settings (GtkPageSetupUnixDialog *dialog,
908                                                GtkPrintSettings       *print_settings)
909 {
910   const char *format_for_printer;
911 
912   if (dialog->print_settings == print_settings) return;
913 
914   if (dialog->print_settings)
915     g_object_unref (dialog->print_settings);
916 
917   dialog->print_settings = print_settings;
918 
919   if (print_settings)
920     {
921       g_object_ref (print_settings);
922 
923       format_for_printer = gtk_print_settings_get (print_settings, "format-for-printer");
924 
925       /* Set printer if in list, otherwise set when
926        * that printer is added
927        */
928       set_active_printer (dialog, format_for_printer);
929     }
930 }
931 
932 /**
933  * gtk_page_setup_unix_dialog_get_print_settings:
934  * @dialog: a `GtkPageSetupUnixDialog`
935  *
936  * Gets the current print settings from the dialog.
937  *
938  * Returns: (transfer none): the current print settings
939  **/
940 GtkPrintSettings *
gtk_page_setup_unix_dialog_get_print_settings(GtkPageSetupUnixDialog * dialog)941 gtk_page_setup_unix_dialog_get_print_settings (GtkPageSetupUnixDialog *dialog)
942 {
943   return dialog->print_settings;
944 }
945