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 | |
51 | static void gtk_printer_option_widget_finalize (GObject *object); |
52 | |
53 | static void deconstruct_widgets (GtkPrinterOptionWidget *widget); |
54 | static void construct_widgets (GtkPrinterOptionWidget *widget); |
55 | static void update_widgets (GtkPrinterOptionWidget *widget); |
56 | |
57 | static char *trim_long_filename (const char *filename); |
58 | |
59 | struct 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 | |
77 | enum { |
78 | CHANGED, |
79 | LAST_SIGNAL |
80 | }; |
81 | |
82 | enum { |
83 | PROP_0, |
84 | PROP_SOURCE |
85 | }; |
86 | |
87 | static guint signals[LAST_SIGNAL] = { 0 }; |
88 | |
89 | G_DEFINE_TYPE_WITH_PRIVATE (GtkPrinterOptionWidget, gtk_printer_option_widget, GTK_TYPE_BOX) |
90 | |
91 | static void gtk_printer_option_widget_set_property (GObject *object, |
92 | guint prop_id, |
93 | const GValue *value, |
94 | GParamSpec *pspec); |
95 | static void gtk_printer_option_widget_get_property (GObject *object, |
96 | guint prop_id, |
97 | GValue *value, |
98 | GParamSpec *pspec); |
99 | static gboolean gtk_printer_option_widget_mnemonic_activate (GtkWidget *widget, |
100 | gboolean group_cycling); |
101 | |
102 | static void |
103 | gtk_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 | |
136 | static void |
137 | gtk_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 | |
144 | static void |
145 | gtk_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 | |
161 | static void |
162 | gtk_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 | |
182 | static void |
183 | gtk_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 | |
202 | static gboolean |
203 | gtk_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 | |
221 | static void |
222 | emit_changed (GtkPrinterOptionWidget *widget) |
223 | { |
224 | g_signal_emit (instance: widget, signal_id: signals[CHANGED], detail: 0); |
225 | } |
226 | |
227 | GtkWidget * |
228 | gtk_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 | |
233 | static void |
234 | source_changed_cb (GtkPrinterOption *source, |
235 | GtkPrinterOptionWidget *widget) |
236 | { |
237 | update_widgets (widget); |
238 | emit_changed (widget); |
239 | } |
240 | |
241 | void |
242 | gtk_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 ()) |
270 | G_DECLARE_FINAL_TYPE (GtkStringPair, gtk_string_pair, GTK, STRING_PAIR, GObject) |
271 | |
272 | struct _GtkStringPair { |
273 | GObject parent_instance; |
274 | char *id; |
275 | char *string; |
276 | }; |
277 | |
278 | enum { |
279 | PROP_ID = 1, |
280 | PROP_STRING, |
281 | PROP_NUM_PROPERTIES |
282 | }; |
283 | |
284 | G_DEFINE_TYPE (GtkStringPair, gtk_string_pair, G_TYPE_OBJECT); |
285 | |
286 | static void |
287 | gtk_string_pair_init (GtkStringPair *pair) |
288 | { |
289 | } |
290 | |
291 | static void |
292 | gtk_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 | |
302 | static void |
303 | gtk_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 | |
328 | static void |
329 | gtk_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 | |
352 | static void |
353 | gtk_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 | |
375 | static GtkStringPair * |
376 | gtk_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 | |
385 | static const char * |
386 | gtk_string_pair_get_string (GtkStringPair *pair) |
387 | { |
388 | return pair->string; |
389 | } |
390 | |
391 | static const char * |
392 | gtk_string_pair_get_id (GtkStringPair *pair) |
393 | { |
394 | return pair->id; |
395 | } |
396 | |
397 | static void |
398 | combo_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 | |
407 | static void |
408 | setup_no_item (GtkSignalListItemFactory *factory, |
409 | GtkListItem *item) |
410 | { |
411 | } |
412 | |
413 | static void |
414 | setup_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 | |
424 | static void |
425 | bind_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 | |
437 | static void |
438 | combo_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 | |
449 | static void |
450 | selected_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 | |
473 | static GtkWidget * |
474 | combo_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 | |
505 | static GtkWidget * |
506 | combo_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 | |
518 | static void |
519 | combo_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 | |
539 | static void |
540 | combo_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 | |
567 | static char * |
568 | combo_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 | |
620 | static void |
621 | deconstruct_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 | |
633 | static void |
634 | check_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 | |
646 | static void |
647 | dialog_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 | |
709 | static void |
710 | filesave_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 | |
743 | static char * |
744 | filter_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 | |
787 | static void |
788 | combo_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 | |
848 | static void |
849 | entry_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 | |
864 | static void |
865 | radio_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 | |
879 | static void |
880 | alternative_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 | |
899 | static void |
900 | alternative_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 | |
921 | static void |
922 | construct_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 | */ |
1073 | static char * |
1074 | trim_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 | |
1108 | static void |
1109 | update_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 | |
1191 | gboolean |
1192 | gtk_printer_option_widget_has_external_label (GtkPrinterOptionWidget *widget) |
1193 | { |
1194 | return widget->priv->label != NULL; |
1195 | } |
1196 | |
1197 | GtkWidget * |
1198 | gtk_printer_option_widget_get_external_label (GtkPrinterOptionWidget *widget) |
1199 | { |
1200 | return widget->priv->label; |
1201 | } |
1202 | |
1203 | const char * |
1204 | gtk_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 | |