1/* GtkPrinterOptionWidget
2 * Copyright (C) 2006 Alexander Larsson <alexl@redhat.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "config.h"
19#include <stdlib.h>
20#include <string.h>
21#include <stdio.h>
22#include <ctype.h>
23
24#include "gtkintl.h"
25#include "gtkcheckbutton.h"
26#include "gtkdropdown.h"
27#include "gtklistitem.h"
28#include "gtksignallistitemfactory.h"
29#include "gtkentry.h"
30#include "gtkfilechooserdialog.h"
31#include "gtkfilechooserprivate.h"
32#include "gtkimage.h"
33#include "gtklabel.h"
34#include "gtkliststore.h"
35#include "gtkcheckbutton.h"
36#include "gtkgrid.h"
37#include "gtktogglebutton.h"
38#include "gtkorientable.h"
39#include "gtkprivate.h"
40#include "gtkstringlist.h"
41
42#include "gtkprinteroptionwidget.h"
43
44/* This defines the max file length that the file chooser
45 * button should display. The total length will be
46 * FILENAME_LENGTH_MAX+3 because the truncated name is prefixed
47 * with “...”.
48 */
49#define FILENAME_LENGTH_MAX 27
50
51static void gtk_printer_option_widget_finalize (GObject *object);
52
53static void deconstruct_widgets (GtkPrinterOptionWidget *widget);
54static void construct_widgets (GtkPrinterOptionWidget *widget);
55static void update_widgets (GtkPrinterOptionWidget *widget);
56
57static char *trim_long_filename (const char *filename);
58
59struct GtkPrinterOptionWidgetPrivate
60{
61 GtkPrinterOption *source;
62 gulong source_changed_handler;
63
64 GtkWidget *check;
65 GtkWidget *combo;
66 GtkWidget *entry;
67 GtkWidget *image;
68 GtkWidget *label;
69 GtkWidget *info_label;
70 GtkWidget *box;
71 GtkWidget *button;
72
73 /* the last location for save to file, that the user selected */
74 GFile *last_location;
75};
76
77enum {
78 CHANGED,
79 LAST_SIGNAL
80};
81
82enum {
83 PROP_0,
84 PROP_SOURCE
85};
86
87static guint signals[LAST_SIGNAL] = { 0 };
88
89G_DEFINE_TYPE_WITH_PRIVATE (GtkPrinterOptionWidget, gtk_printer_option_widget, GTK_TYPE_BOX)
90
91static void gtk_printer_option_widget_set_property (GObject *object,
92 guint prop_id,
93 const GValue *value,
94 GParamSpec *pspec);
95static void gtk_printer_option_widget_get_property (GObject *object,
96 guint prop_id,
97 GValue *value,
98 GParamSpec *pspec);
99static gboolean gtk_printer_option_widget_mnemonic_activate (GtkWidget *widget,
100 gboolean group_cycling);
101
102static void
103gtk_printer_option_widget_class_init (GtkPrinterOptionWidgetClass *class)
104{
105 GObjectClass *object_class;
106 GtkWidgetClass *widget_class;
107
108 object_class = (GObjectClass *) class;
109 widget_class = (GtkWidgetClass *) class;
110
111 object_class->finalize = gtk_printer_option_widget_finalize;
112 object_class->set_property = gtk_printer_option_widget_set_property;
113 object_class->get_property = gtk_printer_option_widget_get_property;
114
115 widget_class->mnemonic_activate = gtk_printer_option_widget_mnemonic_activate;
116
117 signals[CHANGED] =
118 g_signal_new (I_("changed"),
119 G_TYPE_FROM_CLASS (class),
120 signal_flags: G_SIGNAL_RUN_LAST,
121 G_STRUCT_OFFSET (GtkPrinterOptionWidgetClass, changed),
122 NULL, NULL,
123 NULL,
124 G_TYPE_NONE, n_params: 0);
125
126 g_object_class_install_property (oclass: object_class,
127 property_id: PROP_SOURCE,
128 pspec: g_param_spec_object (name: "source",
129 P_("Source option"),
130 P_("The PrinterOption backing this widget"),
131 GTK_TYPE_PRINTER_OPTION,
132 GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
133
134}
135
136static void
137gtk_printer_option_widget_init (GtkPrinterOptionWidget *widget)
138{
139 widget->priv = gtk_printer_option_widget_get_instance_private (self: widget);
140
141 gtk_box_set_spacing (GTK_BOX (widget), spacing: 12);
142}
143
144static void
145gtk_printer_option_widget_finalize (GObject *object)
146{
147 GtkPrinterOptionWidget *widget = GTK_PRINTER_OPTION_WIDGET (object);
148 GtkPrinterOptionWidgetPrivate *priv = widget->priv;
149
150 if (priv->source)
151 {
152 g_signal_handler_disconnect (instance: priv->source,
153 handler_id: priv->source_changed_handler);
154 g_object_unref (object: priv->source);
155 priv->source = NULL;
156 }
157
158 G_OBJECT_CLASS (gtk_printer_option_widget_parent_class)->finalize (object);
159}
160
161static void
162gtk_printer_option_widget_set_property (GObject *object,
163 guint prop_id,
164 const GValue *value,
165 GParamSpec *pspec)
166{
167 GtkPrinterOptionWidget *widget;
168
169 widget = GTK_PRINTER_OPTION_WIDGET (object);
170
171 switch (prop_id)
172 {
173 case PROP_SOURCE:
174 gtk_printer_option_widget_set_source (setting: widget, source: g_value_get_object (value));
175 break;
176 default:
177 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
178 break;
179 }
180}
181
182static void
183gtk_printer_option_widget_get_property (GObject *object,
184 guint prop_id,
185 GValue *value,
186 GParamSpec *pspec)
187{
188 GtkPrinterOptionWidget *widget = GTK_PRINTER_OPTION_WIDGET (object);
189 GtkPrinterOptionWidgetPrivate *priv = widget->priv;
190
191 switch (prop_id)
192 {
193 case PROP_SOURCE:
194 g_value_set_object (value, v_object: priv->source);
195 break;
196 default:
197 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
198 break;
199 }
200}
201
202static gboolean
203gtk_printer_option_widget_mnemonic_activate (GtkWidget *widget,
204 gboolean group_cycling)
205{
206 GtkPrinterOptionWidget *powidget = GTK_PRINTER_OPTION_WIDGET (widget);
207 GtkPrinterOptionWidgetPrivate *priv = powidget->priv;
208
209 if (priv->check)
210 return gtk_widget_mnemonic_activate (widget: priv->check, group_cycling);
211 if (priv->combo)
212 return gtk_widget_mnemonic_activate (widget: priv->combo, group_cycling);
213 if (priv->entry)
214 return gtk_widget_mnemonic_activate (widget: priv->entry, group_cycling);
215 if (priv->button)
216 return gtk_widget_mnemonic_activate (widget: priv->button, group_cycling);
217
218 return FALSE;
219}
220
221static void
222emit_changed (GtkPrinterOptionWidget *widget)
223{
224 g_signal_emit (instance: widget, signal_id: signals[CHANGED], detail: 0);
225}
226
227GtkWidget *
228gtk_printer_option_widget_new (GtkPrinterOption *source)
229{
230 return g_object_new (GTK_TYPE_PRINTER_OPTION_WIDGET, first_property_name: "source", source, NULL);
231}
232
233static void
234source_changed_cb (GtkPrinterOption *source,
235 GtkPrinterOptionWidget *widget)
236{
237 update_widgets (widget);
238 emit_changed (widget);
239}
240
241void
242gtk_printer_option_widget_set_source (GtkPrinterOptionWidget *widget,
243 GtkPrinterOption *source)
244{
245 GtkPrinterOptionWidgetPrivate *priv = widget->priv;
246
247 if (source)
248 g_object_ref (source);
249
250 if (priv->source)
251 {
252 g_signal_handler_disconnect (instance: priv->source,
253 handler_id: priv->source_changed_handler);
254 g_object_unref (object: priv->source);
255 }
256
257 priv->source = source;
258
259 if (source)
260 priv->source_changed_handler =
261 g_signal_connect (source, "changed", G_CALLBACK (source_changed_cb), widget);
262
263 construct_widgets (widget);
264 update_widgets (widget);
265
266 g_object_notify (G_OBJECT (widget), property_name: "source");
267}
268
269#define GTK_TYPE_STRING_PAIR (gtk_string_pair_get_type ())
270G_DECLARE_FINAL_TYPE (GtkStringPair, gtk_string_pair, GTK, STRING_PAIR, GObject)
271
272struct _GtkStringPair {
273 GObject parent_instance;
274 char *id;
275 char *string;
276};
277
278enum {
279 PROP_ID = 1,
280 PROP_STRING,
281 PROP_NUM_PROPERTIES
282};
283
284G_DEFINE_TYPE (GtkStringPair, gtk_string_pair, G_TYPE_OBJECT);
285
286static void
287gtk_string_pair_init (GtkStringPair *pair)
288{
289}
290
291static void
292gtk_string_pair_finalize (GObject *object)
293{
294 GtkStringPair *pair = GTK_STRING_PAIR (ptr: object);
295
296 g_free (mem: pair->id);
297 g_free (mem: pair->string);
298
299 G_OBJECT_CLASS (gtk_string_pair_parent_class)->finalize (object);
300}
301
302static void
303gtk_string_pair_set_property (GObject *object,
304 guint property_id,
305 const GValue *value,
306 GParamSpec *pspec)
307{
308 GtkStringPair *pair = GTK_STRING_PAIR (ptr: object);
309
310 switch (property_id)
311 {
312 case PROP_STRING:
313 g_free (mem: pair->string);
314 pair->string = g_value_dup_string (value);
315 break;
316
317 case PROP_ID:
318 g_free (mem: pair->id);
319 pair->id = g_value_dup_string (value);
320 break;
321
322 default:
323 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
324 break;
325 }
326}
327
328static void
329gtk_string_pair_get_property (GObject *object,
330 guint property_id,
331 GValue *value,
332 GParamSpec *pspec)
333{
334 GtkStringPair *pair = GTK_STRING_PAIR (ptr: object);
335
336 switch (property_id)
337 {
338 case PROP_STRING:
339 g_value_set_string (value, v_string: pair->string);
340 break;
341
342 case PROP_ID:
343 g_value_set_string (value, v_string: pair->id);
344 break;
345
346 default:
347 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
348 break;
349 }
350}
351
352static void
353gtk_string_pair_class_init (GtkStringPairClass *class)
354{
355 GObjectClass *object_class = G_OBJECT_CLASS (class);
356 GParamSpec *pspec;
357
358 object_class->finalize = gtk_string_pair_finalize;
359 object_class->set_property = gtk_string_pair_set_property;
360 object_class->get_property = gtk_string_pair_get_property;
361
362 pspec = g_param_spec_string (name: "string", nick: "String", blurb: "String",
363 NULL,
364 flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
365
366 g_object_class_install_property (oclass: object_class, property_id: PROP_STRING, pspec);
367
368 pspec = g_param_spec_string (name: "id", nick: "ID", blurb: "ID",
369 NULL,
370 flags: G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
371
372 g_object_class_install_property (oclass: object_class, property_id: PROP_ID, pspec);
373}
374
375static GtkStringPair *
376gtk_string_pair_new (const char *id,
377 const char *string)
378{
379 return g_object_new (GTK_TYPE_STRING_PAIR,
380 first_property_name: "id", id,
381 "string", string,
382 NULL);
383}
384
385static const char *
386gtk_string_pair_get_string (GtkStringPair *pair)
387{
388 return pair->string;
389}
390
391static const char *
392gtk_string_pair_get_id (GtkStringPair *pair)
393{
394 return pair->id;
395}
396
397static void
398combo_box_set_model (GtkWidget *combo_box)
399{
400 GListStore *store;
401
402 store = g_list_store_new (GTK_TYPE_STRING_PAIR);
403 gtk_drop_down_set_model (self: GTK_DROP_DOWN (ptr: combo_box), model: G_LIST_MODEL (ptr: store));
404 g_object_unref (object: store);
405}
406
407static void
408setup_no_item (GtkSignalListItemFactory *factory,
409 GtkListItem *item)
410{
411}
412
413static void
414setup_list_item (GtkSignalListItemFactory *factory,
415 GtkListItem *item)
416{
417 GtkWidget *label;
418
419 label = gtk_label_new (str: "");
420 gtk_widget_set_halign (widget: label, align: GTK_ALIGN_START);
421 gtk_list_item_set_child (self: item, child: label);
422}
423
424static void
425bind_list_item (GtkSignalListItemFactory *factory,
426 GtkListItem *item)
427{
428 GtkStringPair *pair;
429 GtkWidget *label;
430
431 pair = gtk_list_item_get_item (self: item);
432 label = gtk_list_item_get_child (self: item);
433
434 gtk_label_set_text (GTK_LABEL (label), str: gtk_string_pair_get_string (pair));
435}
436
437static void
438combo_box_set_view (GtkWidget *combo_box)
439{
440 GtkListItemFactory *factory;
441
442 factory = gtk_signal_list_item_factory_new ();
443 g_signal_connect (factory, "setup", G_CALLBACK (setup_list_item), NULL);
444 g_signal_connect (factory, "bind", G_CALLBACK (bind_list_item), NULL);
445 gtk_drop_down_set_factory (self: GTK_DROP_DOWN (ptr: combo_box), factory);
446 g_object_unref (object: factory);
447}
448
449static void
450selected_changed (GtkDropDown *dropdown,
451 GParamSpec *pspec,
452 gpointer data)
453{
454 GListModel *model;
455 guint selected;
456 GtkStringPair *pair;
457 GtkWidget *entry = data;
458
459 model = gtk_drop_down_get_model (self: dropdown);
460 selected = gtk_drop_down_get_selected (self: dropdown);
461
462 pair = g_list_model_get_item (list: model, position: selected);
463 if (pair)
464 {
465 gtk_editable_set_text (GTK_EDITABLE (entry), text: gtk_string_pair_get_string (pair));
466 g_object_unref (object: pair);
467 }
468 else
469 gtk_editable_set_text (GTK_EDITABLE (entry), text: "");
470
471}
472
473static GtkWidget *
474combo_box_entry_new (void)
475{
476 GtkWidget *hbox, *entry, *button;
477 GtkListItemFactory *factory;
478
479 hbox = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 0);
480 gtk_widget_add_css_class (widget: hbox, css_class: "linked");
481
482 entry = gtk_entry_new ();
483 button = gtk_drop_down_new (NULL, NULL);
484 combo_box_set_model (combo_box: button);
485
486 factory = gtk_signal_list_item_factory_new ();
487 g_signal_connect (factory, "setup", G_CALLBACK (setup_no_item), NULL);
488 gtk_drop_down_set_factory (self: GTK_DROP_DOWN (ptr: button), factory);
489 g_object_unref (object: factory);
490
491 factory = gtk_signal_list_item_factory_new ();
492 g_signal_connect (factory, "setup", G_CALLBACK (setup_list_item), NULL);
493 g_signal_connect (factory, "bind", G_CALLBACK (bind_list_item), NULL);
494 gtk_drop_down_set_list_factory (self: GTK_DROP_DOWN (ptr: button), factory);
495 g_object_unref (object: factory);
496
497 g_signal_connect (button, "notify::selected", G_CALLBACK (selected_changed), entry);
498
499 gtk_box_append (GTK_BOX (hbox), child: entry);
500 gtk_box_append (GTK_BOX (hbox), child: button);
501
502 return hbox;
503}
504
505static GtkWidget *
506combo_box_new (void)
507{
508 GtkWidget *combo_box;
509
510 combo_box = gtk_drop_down_new (NULL, NULL);
511
512 combo_box_set_model (combo_box);
513 combo_box_set_view (combo_box);
514
515 return combo_box;
516}
517
518static void
519combo_box_append (GtkWidget *combo,
520 const char *display_text,
521 const char *value)
522{
523 GtkWidget *dropdown;
524 GListModel *model;
525 GtkStringPair *object;
526
527 if (GTK_IS_DROP_DOWN (ptr: combo))
528 dropdown = combo;
529 else
530 dropdown = gtk_widget_get_last_child (widget: combo);
531
532 model = gtk_drop_down_get_model (self: GTK_DROP_DOWN (ptr: dropdown));
533
534 object = gtk_string_pair_new (id: value, string: display_text);
535 g_list_store_append (store: G_LIST_STORE (ptr: model), item: object);
536 g_object_unref (object);
537}
538
539static void
540combo_box_set (GtkWidget *combo,
541 const char *value)
542{
543 GtkWidget *dropdown;
544 GListModel *model;
545 guint i;
546
547 if (GTK_IS_DROP_DOWN (ptr: combo))
548 dropdown = combo;
549 else
550 dropdown = gtk_widget_get_last_child (widget: combo);
551
552 model = gtk_drop_down_get_model (self: GTK_DROP_DOWN (ptr: dropdown));
553
554 for (i = 0; i < g_list_model_get_n_items (list: model); i++)
555 {
556 GtkStringPair *item = g_list_model_get_item (list: model, position: i);
557 if (strcmp (s1: value, s2: gtk_string_pair_get_id (pair: item)) == 0)
558 {
559 gtk_drop_down_set_selected (self: GTK_DROP_DOWN (ptr: dropdown), position: i);
560 g_object_unref (object: item);
561 break;
562 }
563 g_object_unref (object: item);
564 }
565}
566
567static char *
568combo_box_get (GtkWidget *combo, gboolean *custom)
569{
570 GtkWidget *dropdown;
571 GListModel *model;
572 guint selected;
573 gpointer item;
574 const char *id;
575 const char *string;
576
577 if (GTK_IS_DROP_DOWN (ptr: combo))
578 dropdown = combo;
579 else
580 dropdown = gtk_widget_get_last_child (widget: combo);
581
582 model = gtk_drop_down_get_model (self: GTK_DROP_DOWN (ptr: dropdown));
583 selected = gtk_drop_down_get_selected (self: GTK_DROP_DOWN (ptr: dropdown));
584 item = g_list_model_get_item (list: model, position: selected);
585 if (item)
586 {
587 id = gtk_string_pair_get_id (pair: item);
588 string = gtk_string_pair_get_string (pair: item);
589 g_object_unref (object: item);
590 }
591 else
592 {
593 id = "";
594 string = NULL;
595 }
596
597 if (dropdown == combo) // no entry
598 {
599 *custom = FALSE;
600 return g_strdup (str: id);
601 }
602 else
603 {
604 const char *text;
605
606 text = gtk_editable_get_text (GTK_EDITABLE (gtk_widget_get_first_child (combo)));
607 if (g_strcmp0 (str1: text, str2: string) == 0)
608 {
609 *custom = FALSE;
610 return g_strdup (str: id);
611 }
612 else
613 {
614 *custom = TRUE;
615 return g_strdup (str: text);
616 }
617 }
618}
619
620static void
621deconstruct_widgets (GtkPrinterOptionWidget *widget)
622{
623 GtkPrinterOptionWidgetPrivate *priv = widget->priv;
624
625 g_clear_pointer (&priv->check, gtk_widget_unparent);
626 g_clear_pointer (&priv->combo, gtk_widget_unparent);
627 g_clear_pointer (&priv->entry, gtk_widget_unparent);
628 g_clear_pointer (&priv->image, gtk_widget_unparent);
629 g_clear_pointer (&priv->label, gtk_widget_unparent);
630 g_clear_pointer (&priv->info_label, gtk_widget_unparent);
631}
632
633static void
634check_toggled_cb (GtkToggleButton *toggle_button,
635 GtkPrinterOptionWidget *widget)
636{
637 GtkPrinterOptionWidgetPrivate *priv = widget->priv;
638
639 g_signal_handler_block (instance: priv->source, handler_id: priv->source_changed_handler);
640 gtk_printer_option_set_boolean (option: priv->source,
641 value: gtk_toggle_button_get_active (toggle_button));
642 g_signal_handler_unblock (instance: priv->source, handler_id: priv->source_changed_handler);
643 emit_changed (widget);
644}
645
646static void
647dialog_response_callback (GtkDialog *dialog,
648 int response_id,
649 GtkPrinterOptionWidget *widget)
650{
651 GtkPrinterOptionWidgetPrivate *priv = widget->priv;
652 GFile *new_location = NULL;
653 char *uri = NULL;
654
655 if (response_id == GTK_RESPONSE_ACCEPT)
656 {
657 GFileInfo *info;
658
659 new_location = gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog));
660 info = g_file_query_info (file: new_location,
661 attributes: "standard::display-name",
662 flags: 0,
663 NULL,
664 NULL);
665 if (info != NULL)
666 {
667 const char *filename_utf8 = g_file_info_get_display_name (info);
668
669 char *filename_short = trim_long_filename (filename: filename_utf8);
670 gtk_button_set_label (GTK_BUTTON (priv->button), label: filename_short);
671
672 g_free (mem: filename_short);
673 g_object_unref (object: info);
674 }
675 else
676 {
677 const char *path = g_file_peek_path (file: new_location);
678 char *filename_utf8 = g_utf8_make_valid (str: path, len: -1);
679
680 char *filename_short = trim_long_filename (filename: filename_utf8);
681 gtk_button_set_label (GTK_BUTTON (priv->button), label: filename_short);
682
683 g_free (mem: filename_short);
684 g_free (mem: filename_utf8);
685 }
686 }
687
688 gtk_window_destroy (GTK_WINDOW (dialog));
689
690 if (new_location)
691 uri = g_file_get_uri (file: new_location);
692 else
693 uri = g_file_get_uri (file: priv->last_location);
694
695 if (uri != NULL)
696 {
697 gtk_printer_option_set (option: priv->source, value: uri);
698 emit_changed (widget);
699 g_free (mem: uri);
700 }
701
702 g_clear_object (&new_location);
703 g_clear_object (&priv->last_location);
704
705 /* unblock the handler which was blocked in the filesave_choose_cb function */
706 g_signal_handler_unblock (instance: priv->source, handler_id: priv->source_changed_handler);
707}
708
709static void
710filesave_choose_cb (GtkWidget *button,
711 GtkPrinterOptionWidget *widget)
712{
713 GtkPrinterOptionWidgetPrivate *priv = widget->priv;
714 GtkWidget *dialog;
715 GtkWindow *toplevel;
716
717 /* this will be unblocked in the dialog_response_callback function */
718 g_signal_handler_block (instance: priv->source, handler_id: priv->source_changed_handler);
719
720 toplevel = GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (widget)));
721 dialog = gtk_file_chooser_dialog_new (_("Select a filename"),
722 parent: toplevel,
723 action: GTK_FILE_CHOOSER_ACTION_SAVE,
724 _("_Cancel"), GTK_RESPONSE_CANCEL,
725 _("_Select"), GTK_RESPONSE_ACCEPT,
726 NULL);
727
728 /* select the current filename in the dialog */
729 if (priv->source != NULL && priv->source->value != NULL)
730 {
731 priv->last_location = g_file_new_for_uri (uri: priv->source->value);
732 if (priv->last_location)
733 gtk_file_chooser_set_file (GTK_FILE_CHOOSER (dialog), file: priv->last_location, NULL);
734 }
735
736 g_signal_connect (dialog, "response",
737 G_CALLBACK (dialog_response_callback),
738 widget);
739 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
740 gtk_window_present (GTK_WINDOW (dialog));
741}
742
743static char *
744filter_numeric (const char *val,
745 gboolean allow_neg,
746 gboolean allow_dec,
747 gboolean *changed_out)
748{
749 char *filtered_val;
750 int i, j;
751 int len = strlen (s: val);
752 gboolean dec_set = FALSE;
753
754 filtered_val = g_malloc (n_bytes: len + 1);
755
756 for (i = 0, j = 0; i < len; i++)
757 {
758 if (isdigit (val[i]))
759 {
760 filtered_val[j] = val[i];
761 j++;
762 }
763 else if (allow_dec && !dec_set &&
764 (val[i] == '.' || val[i] == ','))
765 {
766 /* allow one period or comma
767 * we should be checking locals
768 * but this is good enough for now
769 */
770 filtered_val[j] = val[i];
771 dec_set = TRUE;
772 j++;
773 }
774 else if (allow_neg && i == 0 && val[0] == '-')
775 {
776 filtered_val[0] = val[0];
777 j++;
778 }
779 }
780
781 filtered_val[j] = '\0';
782 *changed_out = !(i == j);
783
784 return filtered_val;
785}
786
787static void
788combo_changed_cb (GtkWidget *combo,
789 GParamSpec *pspec,
790 GtkPrinterOptionWidget *widget)
791{
792 GtkPrinterOptionWidgetPrivate *priv = widget->priv;
793 char *value;
794 char *filtered_val = NULL;
795 gboolean changed;
796 gboolean custom = TRUE;
797
798 g_signal_handler_block (instance: priv->source, handler_id: priv->source_changed_handler);
799
800 value = combo_box_get (combo: priv->combo, custom: &custom);
801
802 /* Handle constraints if the user entered a custom value. */
803 if (custom)
804 {
805 switch (priv->source->type)
806 {
807 case GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE:
808 filtered_val = filter_numeric (val: value, FALSE, FALSE, changed_out: &changed);
809 break;
810 case GTK_PRINTER_OPTION_TYPE_PICKONE_INT:
811 filtered_val = filter_numeric (val: value, TRUE, FALSE, changed_out: &changed);
812 break;
813 case GTK_PRINTER_OPTION_TYPE_PICKONE_REAL:
814 filtered_val = filter_numeric (val: value, TRUE, TRUE, changed_out: &changed);
815 break;
816 case GTK_PRINTER_OPTION_TYPE_BOOLEAN:
817 case GTK_PRINTER_OPTION_TYPE_PICKONE:
818 case GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD:
819 case GTK_PRINTER_OPTION_TYPE_PICKONE_STRING:
820 case GTK_PRINTER_OPTION_TYPE_ALTERNATIVE:
821 case GTK_PRINTER_OPTION_TYPE_STRING:
822 case GTK_PRINTER_OPTION_TYPE_FILESAVE:
823 case GTK_PRINTER_OPTION_TYPE_INFO:
824 default:
825 break;
826 }
827 }
828
829 if (filtered_val)
830 {
831 g_free (mem: value);
832
833 if (changed)
834 {
835 GtkWidget *entry = gtk_widget_get_first_child (widget: priv->combo);
836 gtk_editable_set_text (GTK_EDITABLE (entry), text: filtered_val);
837 }
838 value = filtered_val;
839 }
840
841 if (value)
842 gtk_printer_option_set (option: priv->source, value);
843 g_free (mem: value);
844 g_signal_handler_unblock (instance: priv->source, handler_id: priv->source_changed_handler);
845 emit_changed (widget);
846}
847
848static void
849entry_changed_cb (GtkWidget *entry,
850 GtkPrinterOptionWidget *widget)
851{
852 GtkPrinterOptionWidgetPrivate *priv = widget->priv;
853 const char *value;
854
855 g_signal_handler_block (instance: priv->source, handler_id: priv->source_changed_handler);
856 value = gtk_editable_get_text (GTK_EDITABLE (entry));
857 if (value)
858 gtk_printer_option_set (option: priv->source, value);
859 g_signal_handler_unblock (instance: priv->source, handler_id: priv->source_changed_handler);
860 emit_changed (widget);
861}
862
863
864static void
865radio_changed_cb (GtkWidget *button,
866 GtkPrinterOptionWidget *widget)
867{
868 GtkPrinterOptionWidgetPrivate *priv = widget->priv;
869 char *value;
870
871 g_signal_handler_block (instance: priv->source, handler_id: priv->source_changed_handler);
872 value = g_object_get_data (G_OBJECT (button), key: "value");
873 if (value)
874 gtk_printer_option_set (option: priv->source, value);
875 g_signal_handler_unblock (instance: priv->source, handler_id: priv->source_changed_handler);
876 emit_changed (widget);
877}
878
879static void
880alternative_set (GtkWidget *box,
881 const char *value)
882{
883 GtkWidget *child;
884
885 for (child = gtk_widget_get_first_child (widget: box);
886 child != NULL;
887 child = gtk_widget_get_next_sibling (widget: child))
888 {
889 char *v = g_object_get_data (G_OBJECT (child), key: "value");
890
891 if (strcmp (s1: value, s2: v) == 0)
892 {
893 gtk_check_button_set_active (GTK_CHECK_BUTTON (child), TRUE);
894 break;
895 }
896 }
897}
898
899static void
900alternative_append (GtkWidget *box,
901 const char *label,
902 const char *value,
903 GtkPrinterOptionWidget *widget,
904 GtkWidget **group)
905{
906 GtkWidget *button;
907
908 button = gtk_check_button_new_with_label (label);
909 if (*group)
910 gtk_check_button_set_group (GTK_CHECK_BUTTON (button), GTK_CHECK_BUTTON (*group));
911 else
912 *group = button;
913
914 gtk_widget_set_valign (widget: button, align: GTK_ALIGN_BASELINE);
915 gtk_box_append (GTK_BOX (box), child: button);
916
917 g_object_set_data (G_OBJECT (button), key: "value", data: (gpointer)value);
918 g_signal_connect (button, "toggled", G_CALLBACK (radio_changed_cb), widget);
919}
920
921static void
922construct_widgets (GtkPrinterOptionWidget *widget)
923{
924 GtkPrinterOptionWidgetPrivate *priv = widget->priv;
925 GtkPrinterOption *source;
926 char *text;
927 int i;
928 GtkWidget *group;
929
930 source = priv->source;
931
932 deconstruct_widgets (widget);
933
934 gtk_widget_set_sensitive (GTK_WIDGET (widget), TRUE);
935
936 if (source == NULL)
937 {
938 const char * strings[2];
939 strings[0] = _("Not available");
940 strings[1] = NULL;
941 priv->combo = gtk_drop_down_new_from_strings (strings);
942 gtk_drop_down_set_selected (self: GTK_DROP_DOWN (ptr: priv->combo), position: 0);
943 gtk_widget_set_sensitive (GTK_WIDGET (widget), FALSE);
944 gtk_widget_show (widget: priv->combo);
945 gtk_box_append (GTK_BOX (widget), child: priv->combo);
946 }
947 else switch (source->type)
948 {
949 case GTK_PRINTER_OPTION_TYPE_BOOLEAN:
950 priv->check = gtk_check_button_new_with_mnemonic (label: source->display_text);
951 g_signal_connect (priv->check, "toggled", G_CALLBACK (check_toggled_cb), widget);
952 gtk_widget_show (widget: priv->check);
953 gtk_box_append (GTK_BOX (widget), child: priv->check);
954 break;
955 case GTK_PRINTER_OPTION_TYPE_PICKONE:
956 case GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD:
957 case GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE:
958 case GTK_PRINTER_OPTION_TYPE_PICKONE_REAL:
959 case GTK_PRINTER_OPTION_TYPE_PICKONE_INT:
960 case GTK_PRINTER_OPTION_TYPE_PICKONE_STRING:
961 if (source->type == GTK_PRINTER_OPTION_TYPE_PICKONE)
962 {
963 priv->combo = combo_box_new ();
964 }
965 else
966 {
967 priv->combo = combo_box_entry_new ();
968
969 if (source->type == GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD ||
970 source->type == GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE)
971 {
972 GtkWidget *entry = gtk_widget_get_first_child (widget: priv->combo);
973 gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
974 }
975 }
976
977 for (i = 0; i < source->num_choices; i++)
978 combo_box_append (combo: priv->combo,
979 display_text: source->choices_display[i],
980 value: source->choices[i]);
981 gtk_widget_show (widget: priv->combo);
982 gtk_box_append (GTK_BOX (widget), child: priv->combo);
983 if (GTK_IS_DROP_DOWN (ptr: priv->combo))
984 g_signal_connect (priv->combo, "notify::selected", G_CALLBACK (combo_changed_cb),widget);
985 else
986 g_signal_connect (gtk_widget_get_last_child (priv->combo), "notify::selected",G_CALLBACK (combo_changed_cb), widget);
987
988 text = g_strdup_printf (format: "%s:", source->display_text);
989 priv->label = gtk_label_new_with_mnemonic (str: text);
990 g_free (mem: text);
991 gtk_widget_show (widget: priv->label);
992 break;
993
994 case GTK_PRINTER_OPTION_TYPE_ALTERNATIVE:
995 group = NULL;
996 priv->box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 12);
997 gtk_widget_set_valign (widget: priv->box, align: GTK_ALIGN_BASELINE);
998 gtk_widget_show (widget: priv->box);
999 gtk_box_append (GTK_BOX (widget), child: priv->box);
1000 for (i = 0; i < source->num_choices; i++)
1001 {
1002 alternative_append (box: priv->box,
1003 label: source->choices_display[i],
1004 value: source->choices[i],
1005 widget,
1006 group: &group);
1007 /* for mnemonic activation */
1008 if (i == 0)
1009 priv->button = group;
1010 }
1011
1012 if (source->display_text)
1013 {
1014 text = g_strdup_printf (format: "%s:", source->display_text);
1015 priv->label = gtk_label_new_with_mnemonic (str: text);
1016 gtk_widget_set_valign (widget: priv->label, align: GTK_ALIGN_BASELINE);
1017 g_free (mem: text);
1018 gtk_widget_show (widget: priv->label);
1019 }
1020 break;
1021
1022 case GTK_PRINTER_OPTION_TYPE_STRING:
1023 priv->entry = gtk_entry_new ();
1024 gtk_entry_set_activates_default (GTK_ENTRY (priv->entry),
1025 setting: gtk_printer_option_get_activates_default (option: source));
1026 gtk_widget_show (widget: priv->entry);
1027 gtk_box_append (GTK_BOX (widget), child: priv->entry);
1028 g_signal_connect (priv->entry, "changed", G_CALLBACK (entry_changed_cb), widget);
1029
1030 text = g_strdup_printf (format: "%s:", source->display_text);
1031 priv->label = gtk_label_new_with_mnemonic (str: text);
1032 g_free (mem: text);
1033 gtk_widget_show (widget: priv->label);
1034
1035 break;
1036
1037 case GTK_PRINTER_OPTION_TYPE_FILESAVE:
1038 priv->button = gtk_button_new ();
1039 gtk_widget_show (widget: priv->button);
1040 gtk_box_append (GTK_BOX (widget), child: priv->button);
1041 g_signal_connect (priv->button, "clicked", G_CALLBACK (filesave_choose_cb), widget);
1042
1043 text = g_strdup_printf (format: "%s:", source->display_text);
1044 priv->label = gtk_label_new_with_mnemonic (str: text);
1045 g_free (mem: text);
1046 gtk_widget_show (widget: priv->label);
1047
1048 break;
1049
1050 case GTK_PRINTER_OPTION_TYPE_INFO:
1051 priv->info_label = gtk_label_new (NULL);
1052 gtk_label_set_selectable (GTK_LABEL (priv->info_label), TRUE);
1053 gtk_box_append (GTK_BOX (widget), child: priv->info_label);
1054
1055 text = g_strdup_printf (format: "%s:", source->display_text);
1056 priv->label = gtk_label_new_with_mnemonic (str: text);
1057 g_free (mem: text);
1058
1059 break;
1060
1061 default:
1062 break;
1063 }
1064
1065 priv->image = gtk_image_new_from_icon_name (icon_name: "dialog-warning");
1066 gtk_box_append (GTK_BOX (widget), child: priv->image);
1067}
1068
1069/*
1070 * If the filename exceeds FILENAME_LENGTH_MAX, then trim it and replace
1071 * the first three letters with three dots.
1072 */
1073static char *
1074trim_long_filename (const char *filename)
1075{
1076 const char *home;
1077 int len, offset;
1078 char *result;
1079
1080 home = g_get_home_dir ();
1081 if (g_str_has_prefix (str: filename, prefix: home))
1082 {
1083 char *homeless_filename;
1084
1085 offset = g_utf8_strlen (p: home, max: -1);
1086 len = g_utf8_strlen (p: filename, max: -1);
1087 homeless_filename = g_utf8_substring (str: filename, start_pos: offset, end_pos: len);
1088 result = g_strconcat (string1: "~", homeless_filename, NULL);
1089 g_free (mem: homeless_filename);
1090 }
1091 else
1092 result = g_strdup (str: filename);
1093
1094 len = g_utf8_strlen (p: result, max: -1);
1095 if (len > FILENAME_LENGTH_MAX)
1096 {
1097 char *suffix;
1098
1099 suffix = g_utf8_substring (str: result, start_pos: len - FILENAME_LENGTH_MAX, end_pos: len);
1100 g_free (mem: result);
1101 result = g_strconcat (string1: "...", suffix, NULL);
1102 g_free (mem: suffix);
1103 }
1104
1105 return result;
1106}
1107
1108static void
1109update_widgets (GtkPrinterOptionWidget *widget)
1110{
1111 GtkPrinterOptionWidgetPrivate *priv = widget->priv;
1112 GtkPrinterOption *source;
1113
1114 source = priv->source;
1115
1116 if (source == NULL)
1117 {
1118 gtk_widget_hide (widget: priv->image);
1119 return;
1120 }
1121
1122 switch (source->type)
1123 {
1124 case GTK_PRINTER_OPTION_TYPE_BOOLEAN:
1125 if (g_ascii_strcasecmp (s1: source->value, s2: "True") == 0)
1126 gtk_check_button_set_active (GTK_CHECK_BUTTON (priv->check), TRUE);
1127 else
1128 gtk_check_button_set_active (GTK_CHECK_BUTTON (priv->check), FALSE);
1129 break;
1130 case GTK_PRINTER_OPTION_TYPE_PICKONE:
1131 combo_box_set (combo: priv->combo, value: source->value);
1132 break;
1133 case GTK_PRINTER_OPTION_TYPE_ALTERNATIVE:
1134 alternative_set (box: priv->box, value: source->value);
1135 break;
1136 case GTK_PRINTER_OPTION_TYPE_STRING:
1137 gtk_editable_set_text (GTK_EDITABLE (priv->entry), text: source->value);
1138 break;
1139 case GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD:
1140 case GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE:
1141 case GTK_PRINTER_OPTION_TYPE_PICKONE_REAL:
1142 case GTK_PRINTER_OPTION_TYPE_PICKONE_INT:
1143 case GTK_PRINTER_OPTION_TYPE_PICKONE_STRING:
1144 {
1145 GtkWidget *entry = gtk_widget_get_first_child (widget: priv->combo);
1146 if (gtk_printer_option_has_choice (option: source, choice: source->value))
1147 combo_box_set (combo: priv->combo, value: source->value);
1148 else
1149 gtk_editable_set_text (GTK_EDITABLE (entry), text: source->value);
1150
1151 break;
1152 }
1153 case GTK_PRINTER_OPTION_TYPE_FILESAVE:
1154 {
1155 char *text;
1156 char *filename;
1157
1158 filename = g_filename_from_uri (uri: source->value, NULL, NULL);
1159 if (filename != NULL)
1160 {
1161 text = g_filename_to_utf8 (opsysstring: filename, len: -1, NULL, NULL, NULL);
1162 if (text != NULL)
1163 {
1164 char *short_filename;
1165
1166 short_filename = trim_long_filename (filename: text);
1167 gtk_button_set_label (GTK_BUTTON (priv->button), label: short_filename);
1168 g_free (mem: short_filename);
1169 }
1170
1171 g_free (mem: text);
1172 g_free (mem: filename);
1173 }
1174 else
1175 gtk_button_set_label (GTK_BUTTON (priv->button), label: source->value);
1176 break;
1177 }
1178 case GTK_PRINTER_OPTION_TYPE_INFO:
1179 gtk_label_set_text (GTK_LABEL (priv->info_label), str: source->value);
1180 break;
1181 default:
1182 break;
1183 }
1184
1185 if (source->has_conflict)
1186 gtk_widget_show (widget: priv->image);
1187 else
1188 gtk_widget_hide (widget: priv->image);
1189}
1190
1191gboolean
1192gtk_printer_option_widget_has_external_label (GtkPrinterOptionWidget *widget)
1193{
1194 return widget->priv->label != NULL;
1195}
1196
1197GtkWidget *
1198gtk_printer_option_widget_get_external_label (GtkPrinterOptionWidget *widget)
1199{
1200 return widget->priv->label;
1201}
1202
1203const char *
1204gtk_printer_option_widget_get_value (GtkPrinterOptionWidget *widget)
1205{
1206 GtkPrinterOptionWidgetPrivate *priv = widget->priv;
1207
1208 if (priv->source)
1209 return priv->source->value;
1210
1211 return "";
1212}
1213

source code of gtk/gtk/gtkprinteroptionwidget.c