GCC Code Coverage Report


Directory: ./
File: panels/printers/pp-ppd-option-widget.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 211 0.0%
Functions: 0 22 0.0%
Branches: 0 103 0.0%

Line Branch Exec Source
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 #include <stdlib.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <glib/gi18n-lib.h>
27 #include <glib/gstdio.h>
28
29 #include "pp-ppd-option-widget.h"
30 #include "pp-utils.h"
31
32 static void pp_ppd_option_widget_finalize (GObject *object);
33
34 static gboolean construct_widget (PpPPDOptionWidget *self);
35 static void update_widget (PpPPDOptionWidget *self);
36 static void update_widget_real (PpPPDOptionWidget *self);
37
38 struct _PpPPDOptionWidget
39 {
40 GtkBox parent_instance;
41
42 GtkWidget *switch_button;
43 GtkWidget *dropdown;
44 GtkWidget *image;
45 GtkWidget *box;
46
47 ppd_option_t *option;
48
49 gchar *printer_name;
50 gchar *option_name;
51
52 cups_dest_t *destination;
53 gboolean destination_set;
54
55 gchar *ppd_filename;
56 gboolean ppd_filename_set;
57
58 GCancellable *cancellable;
59 };
60
61 G_DEFINE_TYPE (PpPPDOptionWidget, pp_ppd_option_widget, GTK_TYPE_BOX)
62
63 /* This list comes from Gtk+ */
64 static const struct {
65 const char *keyword;
66 const char *choice;
67 const char *translation;
68 } ppd_choice_translations[] = {
69 { "Duplex", "None", N_("One Sided") },
70 /* Translators: this is an option of "Two Sided" */
71 { "Duplex", "DuplexNoTumble", N_("Long Edge (Standard)") },
72 /* Translators: this is an option of "Two Sided" */
73 { "Duplex", "DuplexTumble", N_("Short Edge (Flip)") },
74 /* Translators: this is an option of "Paper Source" */
75 { "InputSlot", "Auto", N_("Auto Select") },
76 /* Translators: this is an option of "Paper Source" */
77 { "InputSlot", "AutoSelect", N_("Auto Select") },
78 /* Translators: this is an option of "Paper Source" */
79 { "InputSlot", "Default", N_("Printer Default") },
80 /* Translators: this is an option of "Paper Source" */
81 { "InputSlot", "None", N_("Printer Default") },
82 /* Translators: this is an option of "Paper Source" */
83 { "InputSlot", "PrinterDefault", N_("Printer Default") },
84 /* Translators: this is an option of "Paper Source" */
85 { "InputSlot", "Unspecified", N_("Auto Select") },
86 /* Translators: this is an option of "Resolution" */
87 { "Resolution", "default", N_("Printer Default") },
88 /* Translators: this is an option of "GhostScript" */
89 { "PreFilter", "EmbedFonts", N_("Embed GhostScript fonts only") },
90 /* Translators: this is an option of "GhostScript" */
91 { "PreFilter", "Level1", N_("Convert to PS level 1") },
92 /* Translators: this is an option of "GhostScript" */
93 { "PreFilter", "Level2", N_("Convert to PS level 2") },
94 /* Translators: this is an option of "GhostScript" */
95 { "PreFilter", "No", N_("No pre-filtering") },
96 };
97
98 static ppd_option_t *
99 cups_option_copy (ppd_option_t *option)
100 {
101 ppd_option_t *result;
102 gint i;
103
104 result = g_new0 (ppd_option_t, 1);
105
106 *result = *option;
107
108 result->choices = g_new (ppd_choice_t, result->num_choices);
109 for (i = 0; i < result->num_choices; i++)
110 {
111 result->choices[i] = option->choices[i];
112 result->choices[i].code = g_strdup (option->choices[i].code);
113 result->choices[i].option = result;
114 }
115
116 return result;
117 }
118
119 static void
120 cups_option_free (ppd_option_t *option)
121 {
122 gint i;
123
124 if (option)
125 {
126 for (i = 0; i < option->num_choices; i++)
127 g_free (option->choices[i].code);
128
129 g_free (option->choices);
130 g_free (option);
131 }
132 }
133
134 static void
135 pp_ppd_option_widget_class_init (PpPPDOptionWidgetClass *class)
136 {
137 GObjectClass *object_class;
138
139 object_class = G_OBJECT_CLASS (class);
140
141 object_class->finalize = pp_ppd_option_widget_finalize;
142 }
143
144 static void
145 pp_ppd_option_widget_init (PpPPDOptionWidget *self)
146 {
147 gtk_orientable_set_orientation (GTK_ORIENTABLE (self),
148 GTK_ORIENTATION_HORIZONTAL);
149 }
150
151 static void
152 pp_ppd_option_widget_finalize (GObject *object)
153 {
154 PpPPDOptionWidget *self = PP_PPD_OPTION_WIDGET (object);
155
156 g_cancellable_cancel (self->cancellable);
157 if (self->ppd_filename)
158 g_unlink (self->ppd_filename);
159
160 g_clear_pointer (&self->option, cups_option_free);
161 g_clear_pointer (&self->printer_name, g_free);
162 g_clear_pointer (&self->option_name, g_free);
163 if (self->destination)
164 {
165 cupsFreeDests (1, self->destination);
166 self->destination = NULL;
167 }
168 g_clear_pointer (&self->ppd_filename, g_free);
169 g_clear_object (&self->cancellable);
170
171 G_OBJECT_CLASS (pp_ppd_option_widget_parent_class)->finalize (object);
172 }
173
174 static const gchar *
175 ppd_choice_translate (ppd_choice_t *choice)
176 {
177 const gchar *keyword = choice->option->keyword;
178 gint i;
179
180 for (i = 0; i < G_N_ELEMENTS (ppd_choice_translations); i++)
181 {
182 if (g_strcmp0 (ppd_choice_translations[i].keyword, keyword) == 0 &&
183 g_strcmp0 (ppd_choice_translations[i].choice, choice->choice) == 0)
184 {
185 return _(ppd_choice_translations[i].translation);
186 }
187 }
188
189 return choice->text;
190 }
191
192 GtkWidget *
193 pp_ppd_option_widget_new (ppd_option_t *option,
194 const gchar *printer_name)
195 {
196 PpPPDOptionWidget *self = NULL;
197
198 if (option && printer_name)
199 {
200 self = g_object_new (PP_TYPE_PPD_OPTION_WIDGET, NULL);
201
202 self->printer_name = g_strdup (printer_name);
203 self->option = cups_option_copy (option);
204 self->option_name = g_strdup (option->keyword);
205
206 if (construct_widget (self))
207 {
208 update_widget_real (self);
209 }
210 else
211 {
212 g_object_ref_sink (self);
213 g_object_unref (self);
214 self = NULL;
215 }
216 }
217
218 return (GtkWidget *) self;
219 }
220
221 static GtkWidget *
222 dropdown_new (void)
223 {
224 GtkStringList *store = NULL;
225 GtkWidget *dropdown;
226
227 store = gtk_string_list_new (NULL);
228
229 dropdown = gtk_drop_down_new (G_LIST_MODEL (store), NULL);
230
231 return dropdown;
232 }
233
234 static void
235 dropdown_append (GtkWidget *dropdown,
236 const gchar *display_text)
237 {
238 GtkStringList *store;
239
240 store = GTK_STRING_LIST (gtk_drop_down_get_model (GTK_DROP_DOWN (dropdown)));
241
242 gtk_string_list_append (store, display_text);
243 }
244
245 static void
246 dropdown_set (GtkWidget *dropdown,
247 ppd_option_t *option,
248 const gchar *value)
249 {
250 for (guint i = 0; i < option->num_choices; i++)
251 {
252 if (g_strcmp0 (option->choices[i].choice, value) == 0)
253 {
254 gtk_drop_down_set_selected (GTK_DROP_DOWN (dropdown), i);
255 break;
256 }
257 }
258 }
259
260 static char *
261 dropdown_get (GtkWidget *dropdown,
262 ppd_option_t *option)
263 {
264 guint selected_item;
265 gchar *value = NULL;
266
267 selected_item = gtk_drop_down_get_selected (GTK_DROP_DOWN (dropdown));
268
269 if (selected_item != GTK_INVALID_LIST_POSITION)
270 {
271 value = option->choices[selected_item].choice;
272 }
273
274 return value;
275 }
276
277 static void
278 printer_add_option_async_cb (gboolean success,
279 gpointer user_data)
280 {
281 PpPPDOptionWidget *self = user_data;
282
283 update_widget (user_data);
284 g_clear_object (&self->cancellable);
285 }
286
287 static void
288 switch_changed_cb (PpPPDOptionWidget *self)
289 {
290 gchar **values;
291
292 values = g_new0 (gchar *, 2);
293
294 if (gtk_switch_get_active (GTK_SWITCH (self->switch_button)))
295 values[0] = g_strdup ("True");
296 else
297 values[0] = g_strdup ("False");
298
299 g_cancellable_cancel (self->cancellable);
300 g_clear_object (&self->cancellable);
301
302 self->cancellable = g_cancellable_new ();
303 printer_add_option_async (self->printer_name,
304 self->option_name,
305 values,
306 FALSE,
307 self->cancellable,
308 printer_add_option_async_cb,
309 self);
310
311 g_strfreev (values);
312 }
313
314 static void
315 dropdown_changed_cb (PpPPDOptionWidget *self)
316 {
317 gchar **values;
318
319 values = g_new0 (gchar *, 2);
320 values[0] = g_strdup (dropdown_get (self->dropdown, self->option));
321
322 g_cancellable_cancel (self->cancellable);
323 g_clear_object (&self->cancellable);
324
325 self->cancellable = g_cancellable_new ();
326 printer_add_option_async (self->printer_name,
327 self->option_name,
328 values,
329 FALSE,
330 self->cancellable,
331 printer_add_option_async_cb,
332 self);
333
334 g_strfreev (values);
335 }
336
337 static gboolean
338 construct_widget (PpPPDOptionWidget *self)
339 {
340 gint i;
341
342 /* Don't show options which has only one choice */
343 if (self->option && self->option->num_choices > 1)
344 {
345 switch (self->option->ui)
346 {
347 case PPD_UI_BOOLEAN:
348 self->switch_button = gtk_switch_new ();
349
350 g_signal_connect_object (self->switch_button, "notify::active", G_CALLBACK (switch_changed_cb), self, G_CONNECT_SWAPPED);
351 gtk_box_append (GTK_BOX (self), self->switch_button);
352 break;
353
354 case PPD_UI_PICKONE:
355 self->dropdown = dropdown_new ();
356
357 for (i = 0; i < self->option->num_choices; i++)
358 {
359 dropdown_append (self->dropdown,
360 ppd_choice_translate (&self->option->choices[i]));
361 }
362
363 gtk_box_append (GTK_BOX (self), self->dropdown);
364 g_signal_connect_object (self->dropdown, "notify::selected", G_CALLBACK (dropdown_changed_cb), self, G_CONNECT_SWAPPED);
365 break;
366
367 case PPD_UI_PICKMANY:
368 self->dropdown = dropdown_new ();
369
370 for (i = 0; i < self->option->num_choices; i++)
371 {
372 dropdown_append (self->dropdown,
373 ppd_choice_translate (&self->option->choices[i]));
374 }
375
376 gtk_box_append (GTK_BOX (self), self->dropdown);
377 g_signal_connect_object (self->dropdown, "notify::selected", G_CALLBACK (dropdown_changed_cb), self, G_CONNECT_SWAPPED);
378 break;
379
380 default:
381 break;
382 }
383
384 self->image = gtk_image_new_from_icon_name ("dialog-warning-symbolic");
385 if (!self->image)
386 self->image = gtk_image_new_from_icon_name ("dialog-warning");
387 gtk_box_append (GTK_BOX (self), self->image);
388
389 return TRUE;
390 }
391 else
392 {
393 return FALSE;
394 }
395 }
396
397 static void
398 update_widget_real (PpPPDOptionWidget *self)
399 {
400 ppd_option_t *option = NULL, *iter;
401 ppd_file_t *ppd_file;
402 gint i;
403
404 if (self->ppd_filename_set && self->ppd_filename)
405 {
406 ppd_file = ppdOpenFile (self->ppd_filename);
407 ppdLocalize (ppd_file);
408
409 if (ppd_file)
410 {
411 ppdMarkDefaults (ppd_file);
412
413 for (iter = ppdFirstOption(ppd_file); iter; iter = ppdNextOption(ppd_file))
414 {
415 if (g_str_equal (iter->keyword, self->option_name))
416 {
417 g_clear_pointer (&self->option, cups_option_free);
418 self->option = cups_option_copy (iter);
419 break;
420 }
421 }
422
423 ppdClose (ppd_file);
424 }
425
426 g_unlink (self->ppd_filename);
427 g_free (self->ppd_filename);
428 self->ppd_filename = NULL;
429 }
430
431 option = self->option;
432
433 if (option)
434 {
435 g_autofree gchar *value = NULL;
436
437 for (i = 0; i < option->num_choices; i++)
438 if (option->choices[i].marked)
439 value = g_strdup (option->choices[i].choice);
440
441 if (value == NULL)
442 value = g_strdup (option->defchoice);
443
444 if (value)
445 {
446 switch (option->ui)
447 {
448 case PPD_UI_BOOLEAN:
449 g_signal_handlers_block_by_func (self->switch_button, switch_changed_cb, self);
450 if (g_ascii_strcasecmp (value, "True") == 0)
451 gtk_switch_set_active (GTK_SWITCH (self->switch_button), TRUE);
452 else
453 gtk_switch_set_active (GTK_SWITCH (self->switch_button), FALSE);
454 g_signal_handlers_unblock_by_func (self->switch_button, switch_changed_cb, self);
455 break;
456
457 case PPD_UI_PICKONE:
458 g_signal_handlers_block_by_func (self->dropdown, dropdown_changed_cb, self);
459 dropdown_set (self->dropdown, option, value);
460 g_signal_handlers_unblock_by_func (self->dropdown, dropdown_changed_cb, self);
461 break;
462
463 case PPD_UI_PICKMANY:
464 g_signal_handlers_block_by_func (self->dropdown, dropdown_changed_cb, self);
465 dropdown_set (self->dropdown, option, value);
466 g_signal_handlers_unblock_by_func (self->dropdown, dropdown_changed_cb, self);
467 break;
468
469 default:
470 break;
471 }
472 }
473
474 gtk_widget_set_visible (self->image, option->conflicted);
475 }
476 }
477
478 static void
479 get_named_dest_cb (cups_dest_t *dest,
480 gpointer user_data)
481 {
482 PpPPDOptionWidget *self = user_data;
483
484 if (self->destination)
485 cupsFreeDests (1, self->destination);
486
487 self->destination = dest;
488 self->destination_set = TRUE;
489
490 if (self->ppd_filename_set)
491 {
492 update_widget_real (self);
493 }
494 }
495
496 static void
497 printer_get_ppd_cb (const gchar *ppd_filename,
498 gpointer user_data)
499 {
500 PpPPDOptionWidget *self = user_data;
501
502 if (self->ppd_filename)
503 {
504 g_unlink (self->ppd_filename);
505 g_free (self->ppd_filename);
506 }
507
508 self->ppd_filename = g_strdup (ppd_filename);
509 self->ppd_filename_set = TRUE;
510
511 if (self->destination_set)
512 {
513 update_widget_real (self);
514 }
515 }
516
517 static void
518 update_widget (PpPPDOptionWidget *self)
519 {
520 self->ppd_filename_set = FALSE;
521 self->destination_set = FALSE;
522
523 get_named_dest_async (self->printer_name,
524 get_named_dest_cb,
525 self);
526
527 printer_get_ppd_async (self->printer_name,
528 NULL,
529 0,
530 printer_get_ppd_cb,
531 self);
532 }
533