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 "gtkcelllayout.h"
27 #include "gtkcellrenderertext.h"
28 #include "gtkcombobox.h"
29 #include "gtkfilechooserdialog.h"
30 #include "gtkimage.h"
31 #include "gtklabel.h"
32 #include "gtkliststore.h"
33 #include "gtkradiobutton.h"
34 #include "gtkgrid.h"
35 #include "gtktogglebutton.h"
36 #include "gtkorientable.h"
37 #include "gtkprivate.h"
38 
39 #include "gtkprinteroptionwidget.h"
40 
41 /* This defines the max file length that the file chooser
42  * button should display. The total length will be
43  * FILENAME_LENGTH_MAX+3 because the truncated name is prefixed
44  * with “...”.
45  */
46 #define FILENAME_LENGTH_MAX 27
47 
48 static void gtk_printer_option_widget_finalize (GObject *object);
49 
50 static void deconstruct_widgets (GtkPrinterOptionWidget *widget);
51 static void construct_widgets   (GtkPrinterOptionWidget *widget);
52 static void update_widgets      (GtkPrinterOptionWidget *widget);
53 
54 static gchar *trim_long_filename (const gchar *filename);
55 
56 struct GtkPrinterOptionWidgetPrivate
57 {
58   GtkPrinterOption *source;
59   gulong source_changed_handler;
60 
61   GtkWidget *check;
62   GtkWidget *combo;
63   GtkWidget *entry;
64   GtkWidget *image;
65   GtkWidget *label;
66   GtkWidget *info_label;
67   GtkWidget *box;
68   GtkWidget *button;
69 
70   /* the last location for save to file, that the user selected */
71   gchar *last_location;
72 };
73 
74 enum {
75   CHANGED,
76   LAST_SIGNAL
77 };
78 
79 enum {
80   PROP_0,
81   PROP_SOURCE
82 };
83 
84 static guint signals[LAST_SIGNAL] = { 0 };
85 
86 G_DEFINE_TYPE_WITH_PRIVATE (GtkPrinterOptionWidget, gtk_printer_option_widget, GTK_TYPE_BOX)
87 
88 static void gtk_printer_option_widget_set_property (GObject      *object,
89 						    guint         prop_id,
90 						    const GValue *value,
91 						    GParamSpec   *pspec);
92 static void gtk_printer_option_widget_get_property (GObject      *object,
93 						    guint         prop_id,
94 						    GValue       *value,
95 						    GParamSpec   *pspec);
96 static gboolean gtk_printer_option_widget_mnemonic_activate (GtkWidget *widget,
97 							     gboolean   group_cycling);
98 
99 static void
gtk_printer_option_widget_class_init(GtkPrinterOptionWidgetClass * class)100 gtk_printer_option_widget_class_init (GtkPrinterOptionWidgetClass *class)
101 {
102   GObjectClass *object_class;
103   GtkWidgetClass *widget_class;
104 
105   object_class = (GObjectClass *) class;
106   widget_class = (GtkWidgetClass *) class;
107 
108   object_class->finalize = gtk_printer_option_widget_finalize;
109   object_class->set_property = gtk_printer_option_widget_set_property;
110   object_class->get_property = gtk_printer_option_widget_get_property;
111 
112   widget_class->mnemonic_activate = gtk_printer_option_widget_mnemonic_activate;
113 
114   signals[CHANGED] =
115     g_signal_new (I_("changed"),
116 		  G_TYPE_FROM_CLASS (class),
117 		  G_SIGNAL_RUN_LAST,
118 		  G_STRUCT_OFFSET (GtkPrinterOptionWidgetClass, changed),
119 		  NULL, NULL,
120 		  NULL,
121 		  G_TYPE_NONE, 0);
122 
123   g_object_class_install_property (object_class,
124                                    PROP_SOURCE,
125                                    g_param_spec_object ("source",
126 							P_("Source option"),
127 							P_("The PrinterOption backing this widget"),
128 							GTK_TYPE_PRINTER_OPTION,
129 							GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
130 
131 }
132 
133 static void
gtk_printer_option_widget_init(GtkPrinterOptionWidget * widget)134 gtk_printer_option_widget_init (GtkPrinterOptionWidget *widget)
135 {
136   widget->priv = gtk_printer_option_widget_get_instance_private (widget);
137 
138   gtk_box_set_spacing (GTK_BOX (widget), 12);
139 }
140 
141 static void
gtk_printer_option_widget_finalize(GObject * object)142 gtk_printer_option_widget_finalize (GObject *object)
143 {
144   GtkPrinterOptionWidget *widget = GTK_PRINTER_OPTION_WIDGET (object);
145   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
146 
147   if (priv->source)
148     {
149       g_signal_handler_disconnect (priv->source,
150 				   priv->source_changed_handler);
151       g_object_unref (priv->source);
152       priv->source = NULL;
153     }
154 
155   G_OBJECT_CLASS (gtk_printer_option_widget_parent_class)->finalize (object);
156 }
157 
158 static void
gtk_printer_option_widget_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)159 gtk_printer_option_widget_set_property (GObject         *object,
160 					guint            prop_id,
161 					const GValue    *value,
162 					GParamSpec      *pspec)
163 {
164   GtkPrinterOptionWidget *widget;
165 
166   widget = GTK_PRINTER_OPTION_WIDGET (object);
167 
168   switch (prop_id)
169     {
170     case PROP_SOURCE:
171       gtk_printer_option_widget_set_source (widget, g_value_get_object (value));
172       break;
173     default:
174       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
175       break;
176     }
177 }
178 
179 static void
gtk_printer_option_widget_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)180 gtk_printer_option_widget_get_property (GObject    *object,
181 					guint       prop_id,
182 					GValue     *value,
183 					GParamSpec *pspec)
184 {
185   GtkPrinterOptionWidget *widget = GTK_PRINTER_OPTION_WIDGET (object);
186   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
187 
188   switch (prop_id)
189     {
190     case PROP_SOURCE:
191       g_value_set_object (value, priv->source);
192       break;
193     default:
194       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
195       break;
196     }
197 }
198 
199 static gboolean
gtk_printer_option_widget_mnemonic_activate(GtkWidget * widget,gboolean group_cycling)200 gtk_printer_option_widget_mnemonic_activate (GtkWidget *widget,
201 					     gboolean   group_cycling)
202 {
203   GtkPrinterOptionWidget *powidget = GTK_PRINTER_OPTION_WIDGET (widget);
204   GtkPrinterOptionWidgetPrivate *priv = powidget->priv;
205 
206   if (priv->check)
207     return gtk_widget_mnemonic_activate (priv->check, group_cycling);
208   if (priv->combo)
209     return gtk_widget_mnemonic_activate (priv->combo, group_cycling);
210   if (priv->entry)
211     return gtk_widget_mnemonic_activate (priv->entry, group_cycling);
212   if (priv->button)
213     return gtk_widget_mnemonic_activate (priv->button, group_cycling);
214 
215   return FALSE;
216 }
217 
218 static void
emit_changed(GtkPrinterOptionWidget * widget)219 emit_changed (GtkPrinterOptionWidget *widget)
220 {
221   g_signal_emit (widget, signals[CHANGED], 0);
222 }
223 
224 GtkWidget *
gtk_printer_option_widget_new(GtkPrinterOption * source)225 gtk_printer_option_widget_new (GtkPrinterOption *source)
226 {
227   return g_object_new (GTK_TYPE_PRINTER_OPTION_WIDGET, "source", source, NULL);
228 }
229 
230 static void
source_changed_cb(GtkPrinterOption * source,GtkPrinterOptionWidget * widget)231 source_changed_cb (GtkPrinterOption *source,
232 		   GtkPrinterOptionWidget  *widget)
233 {
234   update_widgets (widget);
235   emit_changed (widget);
236 }
237 
238 void
gtk_printer_option_widget_set_source(GtkPrinterOptionWidget * widget,GtkPrinterOption * source)239 gtk_printer_option_widget_set_source (GtkPrinterOptionWidget *widget,
240 				      GtkPrinterOption       *source)
241 {
242   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
243 
244   if (source)
245     g_object_ref (source);
246 
247   if (priv->source)
248     {
249       g_signal_handler_disconnect (priv->source,
250 				   priv->source_changed_handler);
251       g_object_unref (priv->source);
252     }
253 
254   priv->source = source;
255 
256   if (source)
257     priv->source_changed_handler =
258       g_signal_connect (source, "changed", G_CALLBACK (source_changed_cb), widget);
259 
260   construct_widgets (widget);
261   update_widgets (widget);
262 
263   g_object_notify (G_OBJECT (widget), "source");
264 }
265 
266 enum {
267   NAME_COLUMN,
268   VALUE_COLUMN,
269   N_COLUMNS
270 };
271 
272 static void
combo_box_set_model(GtkWidget * combo_box)273 combo_box_set_model (GtkWidget *combo_box)
274 {
275   GtkListStore *store;
276 
277   store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING);
278   gtk_combo_box_set_model (GTK_COMBO_BOX (combo_box), GTK_TREE_MODEL (store));
279   g_object_unref (store);
280 }
281 
282 static void
combo_box_set_view(GtkWidget * combo_box)283 combo_box_set_view (GtkWidget *combo_box)
284 {
285   GtkCellRenderer *cell;
286 
287   cell = gtk_cell_renderer_text_new ();
288   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
289   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
290                                   "text", NAME_COLUMN,
291                                    NULL);
292 }
293 
294 static GtkWidget *
combo_box_entry_new(void)295 combo_box_entry_new (void)
296 {
297   GtkWidget *combo_box;
298   combo_box = g_object_new (GTK_TYPE_COMBO_BOX, "has-entry", TRUE, NULL);
299 
300   combo_box_set_model (combo_box);
301 
302   gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (combo_box), NAME_COLUMN);
303 
304   return combo_box;
305 }
306 
307 static GtkWidget *
combo_box_new(void)308 combo_box_new (void)
309 {
310   GtkWidget *combo_box;
311   combo_box = gtk_combo_box_new ();
312 
313   combo_box_set_model (combo_box);
314   combo_box_set_view (combo_box);
315 
316   return combo_box;
317 }
318 
319 static void
combo_box_append(GtkWidget * combo,const gchar * display_text,const gchar * value)320 combo_box_append (GtkWidget   *combo,
321 		  const gchar *display_text,
322 		  const gchar *value)
323 {
324   GtkTreeModel *model;
325   GtkListStore *store;
326   GtkTreeIter iter;
327 
328   model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
329   store = GTK_LIST_STORE (model);
330 
331   gtk_list_store_append (store, &iter);
332   gtk_list_store_set (store, &iter,
333 		      NAME_COLUMN, display_text,
334 		      VALUE_COLUMN, value,
335 		      -1);
336 }
337 
338 struct ComboSet {
339   GtkComboBox *combo;
340   const gchar *value;
341 };
342 
343 static gboolean
set_cb(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer data)344 set_cb (GtkTreeModel *model,
345 	GtkTreePath  *path,
346 	GtkTreeIter  *iter,
347 	gpointer      data)
348 {
349   struct ComboSet *set_data = data;
350   gboolean found;
351   char *value;
352 
353   gtk_tree_model_get (model, iter, VALUE_COLUMN, &value, -1);
354   found = (strcmp (value, set_data->value) == 0);
355   g_free (value);
356 
357   if (found)
358     gtk_combo_box_set_active_iter (set_data->combo, iter);
359 
360   return found;
361 }
362 
363 static void
combo_box_set(GtkWidget * combo,const gchar * value)364 combo_box_set (GtkWidget   *combo,
365 	       const gchar *value)
366 {
367   GtkTreeModel *model;
368   struct ComboSet set_data;
369 
370   model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
371 
372   set_data.combo = GTK_COMBO_BOX (combo);
373   set_data.value = value;
374   gtk_tree_model_foreach (model, set_cb, &set_data);
375 }
376 
377 static gchar *
combo_box_get(GtkWidget * combo,gboolean * custom)378 combo_box_get (GtkWidget *combo, gboolean *custom)
379 {
380   GtkTreeModel *model;
381   gchar *value;
382   GtkTreeIter iter;
383 
384   model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
385 
386   value = NULL;
387   if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
388     {
389       gtk_tree_model_get (model, &iter, VALUE_COLUMN, &value, -1);
390       *custom = FALSE;
391     }
392   else
393     {
394       if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (combo)))
395         {
396           value = g_strdup (gtk_entry_get_text (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo)))));
397           *custom = TRUE;
398         }
399 
400       if (!value || !gtk_tree_model_get_iter_first (model, &iter))
401         return value;
402 
403       /* If the user entered an item from the dropdown list manually, return
404        * the non-custom option instead. */
405       do
406         {
407           gchar *val, *name;
408           gtk_tree_model_get (model, &iter, VALUE_COLUMN, &val,
409                                             NAME_COLUMN, &name, -1);
410           if (g_str_equal (value, name))
411             {
412               *custom = FALSE;
413               g_free (name);
414               g_free (value);
415               return val;
416             }
417 
418           g_free (val);
419           g_free (name);
420         }
421       while (gtk_tree_model_iter_next (model, &iter));
422     }
423 
424   return value;
425 }
426 
427 
428 static void
deconstruct_widgets(GtkPrinterOptionWidget * widget)429 deconstruct_widgets (GtkPrinterOptionWidget *widget)
430 {
431   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
432 
433   if (priv->check)
434     {
435       gtk_widget_destroy (priv->check);
436       priv->check = NULL;
437     }
438 
439   if (priv->combo)
440     {
441       gtk_widget_destroy (priv->combo);
442       priv->combo = NULL;
443     }
444 
445   if (priv->entry)
446     {
447       gtk_widget_destroy (priv->entry);
448       priv->entry = NULL;
449     }
450 
451   if (priv->image)
452     {
453       gtk_widget_destroy (priv->image);
454       priv->image = NULL;
455     }
456 
457   if (priv->label)
458     {
459       gtk_widget_destroy (priv->label);
460       priv->label = NULL;
461     }
462   if (priv->info_label)
463     {
464       gtk_widget_destroy (priv->info_label);
465       priv->info_label = NULL;
466     }
467 }
468 
469 static void
check_toggled_cb(GtkToggleButton * toggle_button,GtkPrinterOptionWidget * widget)470 check_toggled_cb (GtkToggleButton        *toggle_button,
471 		  GtkPrinterOptionWidget *widget)
472 {
473   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
474 
475   g_signal_handler_block (priv->source, priv->source_changed_handler);
476   gtk_printer_option_set_boolean (priv->source,
477 				  gtk_toggle_button_get_active (toggle_button));
478   g_signal_handler_unblock (priv->source, priv->source_changed_handler);
479   emit_changed (widget);
480 }
481 
482 static void
dialog_response_callback(GtkDialog * dialog,gint response_id,GtkPrinterOptionWidget * widget)483 dialog_response_callback (GtkDialog              *dialog,
484                           gint                    response_id,
485                           GtkPrinterOptionWidget *widget)
486 {
487   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
488   gchar *uri = NULL;
489   gchar *new_location = NULL;
490 
491   if (response_id == GTK_RESPONSE_ACCEPT)
492     {
493       gchar *filename;
494       gchar *filename_utf8;
495       gchar *filename_short;
496 
497       new_location = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (dialog));
498 
499       filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
500       filename_utf8 = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
501       filename_short = trim_long_filename (filename_utf8);
502       gtk_button_set_label (GTK_BUTTON (priv->button), filename_short);
503       g_free (filename_short);
504       g_free (filename_utf8);
505       g_free (filename);
506     }
507 
508   gtk_widget_destroy (GTK_WIDGET (dialog));
509 
510   if (new_location)
511     uri = new_location;
512   else
513     uri = priv->last_location;
514 
515   if (uri)
516     {
517       gtk_printer_option_set (priv->source, uri);
518       emit_changed (widget);
519     }
520 
521   g_free (new_location);
522   g_free (priv->last_location);
523   priv->last_location = NULL;
524 
525   /* unblock the handler which was blocked in the filesave_choose_cb function */
526   g_signal_handler_unblock (priv->source, priv->source_changed_handler);
527 }
528 
529 static void
filesave_choose_cb(GtkWidget * button,GtkPrinterOptionWidget * widget)530 filesave_choose_cb (GtkWidget              *button,
531                     GtkPrinterOptionWidget *widget)
532 {
533   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
534   gchar *last_location = NULL;
535   GtkWidget *dialog;
536   GtkWindow *toplevel;
537 
538   /* this will be unblocked in the dialog_response_callback function */
539   g_signal_handler_block (priv->source, priv->source_changed_handler);
540 
541   toplevel = GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (widget)));
542   dialog = gtk_file_chooser_dialog_new (_("Select a filename"),
543                                         toplevel,
544                                         GTK_FILE_CHOOSER_ACTION_SAVE,
545                                         _("_Cancel"), GTK_RESPONSE_CANCEL,
546                                         _("_Select"), GTK_RESPONSE_ACCEPT,
547                                         NULL);
548 
549   /* The confirmation dialog will appear, when the user clicks print */
550   gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), FALSE);
551 
552   /* select the current filename in the dialog */
553   if (priv->source != NULL)
554     {
555       priv->last_location = last_location = g_strdup (priv->source->value);
556       if (last_location)
557         {
558           GFile *file;
559           gchar *basename;
560           gchar *basename_utf8;
561 
562           gtk_file_chooser_select_uri (GTK_FILE_CHOOSER (dialog), last_location);
563           file = g_file_new_for_uri (last_location);
564           basename = g_file_get_basename (file);
565           basename_utf8 = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
566           gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), basename_utf8);
567           g_free (basename_utf8);
568           g_free (basename);
569           g_object_unref (file);
570         }
571     }
572 
573   g_signal_connect (dialog, "response",
574                     G_CALLBACK (dialog_response_callback), widget);
575   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
576   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
577   gtk_window_present (GTK_WINDOW (dialog));
578   G_GNUC_END_IGNORE_DEPRECATIONS
579 }
580 
581 static gchar *
filter_numeric(const gchar * val,gboolean allow_neg,gboolean allow_dec,gboolean * changed_out)582 filter_numeric (const gchar *val,
583                 gboolean     allow_neg,
584 		gboolean     allow_dec,
585                 gboolean    *changed_out)
586 {
587   gchar *filtered_val;
588   int i, j;
589   int len = strlen (val);
590   gboolean dec_set = FALSE;
591 
592   filtered_val = g_malloc (len + 1);
593 
594   for (i = 0, j = 0; i < len; i++)
595     {
596       if (isdigit (val[i]))
597         {
598           filtered_val[j] = val[i];
599 	  j++;
600 	}
601       else if (allow_dec && !dec_set &&
602                (val[i] == '.' || val[i] == ','))
603         {
604 	  /* allow one period or comma
605 	   * we should be checking locals
606 	   * but this is good enough for now
607 	   */
608           filtered_val[j] = val[i];
609 	  dec_set = TRUE;
610 	  j++;
611 	}
612       else if (allow_neg && i == 0 && val[0] == '-')
613         {
614           filtered_val[0] = val[0];
615 	  j++;
616 	}
617     }
618 
619   filtered_val[j] = '\0';
620   *changed_out = !(i == j);
621 
622   return filtered_val;
623 }
624 
625 static void
combo_changed_cb(GtkWidget * combo,GtkPrinterOptionWidget * widget)626 combo_changed_cb (GtkWidget              *combo,
627 		  GtkPrinterOptionWidget *widget)
628 {
629   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
630   gchar *value;
631   gchar *filtered_val = NULL;
632   gboolean changed;
633   gboolean custom = TRUE;
634 
635   g_signal_handler_block (priv->source, priv->source_changed_handler);
636 
637   value = combo_box_get (combo, &custom);
638 
639   /* Handle constraints if the user entered a custom value. */
640   if (custom)
641     {
642       switch (priv->source->type)
643         {
644         case GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE:
645           filtered_val = filter_numeric (value, FALSE, FALSE, &changed);
646           break;
647         case GTK_PRINTER_OPTION_TYPE_PICKONE_INT:
648           filtered_val = filter_numeric (value, TRUE, FALSE, &changed);
649           break;
650         case GTK_PRINTER_OPTION_TYPE_PICKONE_REAL:
651           filtered_val = filter_numeric (value, TRUE, TRUE, &changed);
652           break;
653         default:
654           break;
655         }
656     }
657 
658   if (filtered_val)
659     {
660       g_free (value);
661 
662       if (changed)
663         {
664           GtkEntry *entry;
665 
666 	  entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (combo)));
667 
668           gtk_entry_set_text (entry, filtered_val);
669 	}
670       value = filtered_val;
671     }
672 
673   if (value)
674     gtk_printer_option_set (priv->source, value);
675   g_free (value);
676   g_signal_handler_unblock (priv->source, priv->source_changed_handler);
677   emit_changed (widget);
678 }
679 
680 static void
entry_changed_cb(GtkWidget * entry,GtkPrinterOptionWidget * widget)681 entry_changed_cb (GtkWidget              *entry,
682 		  GtkPrinterOptionWidget *widget)
683 {
684   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
685   const gchar *value;
686 
687   g_signal_handler_block (priv->source, priv->source_changed_handler);
688   value = gtk_entry_get_text (GTK_ENTRY (entry));
689   if (value)
690     gtk_printer_option_set (priv->source, value);
691   g_signal_handler_unblock (priv->source, priv->source_changed_handler);
692   emit_changed (widget);
693 }
694 
695 
696 static void
radio_changed_cb(GtkWidget * button,GtkPrinterOptionWidget * widget)697 radio_changed_cb (GtkWidget              *button,
698 		  GtkPrinterOptionWidget *widget)
699 {
700   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
701   gchar *value;
702 
703   g_signal_handler_block (priv->source, priv->source_changed_handler);
704   value = g_object_get_data (G_OBJECT (button), "value");
705   if (value)
706     gtk_printer_option_set (priv->source, value);
707   g_signal_handler_unblock (priv->source, priv->source_changed_handler);
708   emit_changed (widget);
709 }
710 
711 static void
select_maybe(GtkWidget * widget,const gchar * value)712 select_maybe (GtkWidget   *widget,
713 	      const gchar *value)
714 {
715   gchar *v = g_object_get_data (G_OBJECT (widget), "value");
716 
717   if (strcmp (value, v) == 0)
718     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
719 }
720 
721 static void
alternative_set(GtkWidget * box,const gchar * value)722 alternative_set (GtkWidget   *box,
723 		 const gchar *value)
724 {
725   gtk_container_foreach (GTK_CONTAINER (box),
726 			 (GtkCallback) select_maybe,
727 			 (gpointer) value);
728 }
729 
730 static GSList *
alternative_append(GtkWidget * box,const gchar * label,const gchar * value,GtkPrinterOptionWidget * widget,GSList * group)731 alternative_append (GtkWidget              *box,
732 		    const gchar            *label,
733                     const gchar            *value,
734 		    GtkPrinterOptionWidget *widget,
735 		    GSList                 *group)
736 {
737   GtkWidget *button;
738 
739   button = gtk_radio_button_new_with_label (group, label);
740   gtk_widget_show (button);
741   gtk_widget_set_valign (button, GTK_ALIGN_BASELINE);
742   gtk_box_pack_start (GTK_BOX (box), button, FALSE, FALSE, 0);
743 
744   g_object_set_data (G_OBJECT (button), "value", (gpointer)value);
745   g_signal_connect (button, "toggled",
746 		    G_CALLBACK (radio_changed_cb), widget);
747 
748   return gtk_radio_button_get_group (GTK_RADIO_BUTTON (button));
749 }
750 
751 static void
construct_widgets(GtkPrinterOptionWidget * widget)752 construct_widgets (GtkPrinterOptionWidget *widget)
753 {
754   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
755   GtkPrinterOption *source;
756   char *text;
757   int i;
758   GSList *group;
759 
760   source = priv->source;
761 
762   deconstruct_widgets (widget);
763 
764   gtk_widget_set_sensitive (GTK_WIDGET (widget), TRUE);
765 
766   if (source == NULL)
767     {
768       priv->combo = combo_box_new ();
769       combo_box_append (priv->combo,_("Not available"), "None");
770       gtk_combo_box_set_active (GTK_COMBO_BOX (priv->combo), 0);
771       gtk_widget_set_sensitive (GTK_WIDGET (widget), FALSE);
772       gtk_widget_show (priv->combo);
773       gtk_box_pack_start (GTK_BOX (widget), priv->combo, TRUE, TRUE, 0);
774     }
775   else switch (source->type)
776     {
777     case GTK_PRINTER_OPTION_TYPE_BOOLEAN:
778       priv->check = gtk_check_button_new_with_mnemonic (source->display_text);
779       g_signal_connect (priv->check, "toggled", G_CALLBACK (check_toggled_cb), widget);
780       gtk_widget_show (priv->check);
781       gtk_box_pack_start (GTK_BOX (widget), priv->check, TRUE, TRUE, 0);
782       break;
783     case GTK_PRINTER_OPTION_TYPE_PICKONE:
784     case GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD:
785     case GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE:
786     case GTK_PRINTER_OPTION_TYPE_PICKONE_REAL:
787     case GTK_PRINTER_OPTION_TYPE_PICKONE_INT:
788     case GTK_PRINTER_OPTION_TYPE_PICKONE_STRING:
789       if (source->type == GTK_PRINTER_OPTION_TYPE_PICKONE)
790         {
791           priv->combo = combo_box_new ();
792 	}
793       else
794         {
795           priv->combo = combo_box_entry_new ();
796 
797           if (source->type == GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD ||
798 	      source->type == GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE)
799 	    {
800               GtkEntry *entry;
801 
802 	      entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->combo)));
803 
804               gtk_entry_set_visibility (entry, FALSE);
805 	    }
806         }
807 
808       for (i = 0; i < source->num_choices; i++)
809         combo_box_append (priv->combo,
810                           source->choices_display[i],
811                           source->choices[i]);
812       gtk_widget_show (priv->combo);
813       gtk_box_pack_start (GTK_BOX (widget), priv->combo, TRUE, TRUE, 0);
814       g_signal_connect (priv->combo, "changed", G_CALLBACK (combo_changed_cb), widget);
815 
816       text = g_strdup_printf ("%s:", source->display_text);
817       priv->label = gtk_label_new_with_mnemonic (text);
818       g_free (text);
819       gtk_widget_show (priv->label);
820       break;
821 
822     case GTK_PRINTER_OPTION_TYPE_ALTERNATIVE:
823       group = NULL;
824       priv->box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 12);
825       gtk_widget_set_valign (priv->box, GTK_ALIGN_BASELINE);
826       gtk_widget_show (priv->box);
827       gtk_box_pack_start (GTK_BOX (widget), priv->box, TRUE, TRUE, 0);
828       for (i = 0; i < source->num_choices; i++)
829         {
830 	  group = alternative_append (priv->box,
831                                       source->choices_display[i],
832                                       source->choices[i],
833                                       widget,
834                                       group);
835           /* for mnemonic activation */
836           if (i == 0)
837             priv->button = group->data;
838         }
839 
840       if (source->display_text)
841 	{
842 	  text = g_strdup_printf ("%s:", source->display_text);
843 	  priv->label = gtk_label_new_with_mnemonic (text);
844           gtk_widget_set_valign (priv->label, GTK_ALIGN_BASELINE);
845 	  g_free (text);
846 	  gtk_widget_show (priv->label);
847 	}
848       break;
849 
850     case GTK_PRINTER_OPTION_TYPE_STRING:
851       priv->entry = gtk_entry_new ();
852       gtk_entry_set_activates_default (GTK_ENTRY (priv->entry),
853                                        gtk_printer_option_get_activates_default (source));
854       gtk_widget_show (priv->entry);
855       gtk_box_pack_start (GTK_BOX (widget), priv->entry, TRUE, TRUE, 0);
856       g_signal_connect (priv->entry, "changed", G_CALLBACK (entry_changed_cb), widget);
857 
858       text = g_strdup_printf ("%s:", source->display_text);
859       priv->label = gtk_label_new_with_mnemonic (text);
860       g_free (text);
861       gtk_widget_show (priv->label);
862 
863       break;
864 
865     case GTK_PRINTER_OPTION_TYPE_FILESAVE:
866       priv->button = gtk_button_new ();
867       gtk_widget_show (priv->button);
868       gtk_box_pack_start (GTK_BOX (widget), priv->button, TRUE, TRUE, 0);
869       g_signal_connect (priv->button, "clicked", G_CALLBACK (filesave_choose_cb), widget);
870 
871       text = g_strdup_printf ("%s:", source->display_text);
872       priv->label = gtk_label_new_with_mnemonic (text);
873       g_free (text);
874       gtk_widget_show (priv->label);
875 
876       break;
877 
878     case GTK_PRINTER_OPTION_TYPE_INFO:
879       priv->info_label = gtk_label_new (NULL);
880       gtk_label_set_selectable (GTK_LABEL (priv->info_label), TRUE);
881       gtk_widget_show (priv->info_label);
882       gtk_box_pack_start (GTK_BOX (widget), priv->info_label, FALSE, TRUE, 0);
883 
884       text = g_strdup_printf ("%s:", source->display_text);
885       priv->label = gtk_label_new_with_mnemonic (text);
886       g_free (text);
887       gtk_widget_show (priv->label);
888 
889       break;
890 
891     default:
892       break;
893     }
894 
895   priv->image = gtk_image_new_from_icon_name ("dialog-warning", GTK_ICON_SIZE_MENU);
896   gtk_box_pack_start (GTK_BOX (widget), priv->image, FALSE, FALSE, 0);
897 }
898 
899 /*
900  * If the filename exceeds FILENAME_LENGTH_MAX, then trim it and replace
901  * the first three letters with three dots.
902  */
903 static gchar *
trim_long_filename(const gchar * filename)904 trim_long_filename (const gchar *filename)
905 {
906   const gchar *home;
907   gint len, offset;
908   gchar *result;
909 
910   home = g_get_home_dir ();
911   if (g_str_has_prefix (filename, home))
912     {
913       gchar *homeless_filename;
914 
915       offset = g_utf8_strlen (home, -1);
916       len = g_utf8_strlen (filename, -1);
917       homeless_filename = g_utf8_substring (filename, offset, len);
918       result = g_strconcat ("~", homeless_filename, NULL);
919       g_free (homeless_filename);
920     }
921   else
922     result = g_strdup (filename);
923 
924   len = g_utf8_strlen (result, -1);
925   if (len > FILENAME_LENGTH_MAX)
926     {
927       gchar *suffix;
928 
929       suffix = g_utf8_substring (result, len - FILENAME_LENGTH_MAX, len);
930       g_free (result);
931       result = g_strconcat ("...", suffix, NULL);
932       g_free (suffix);
933     }
934 
935   return result;
936 }
937 
938 static void
update_widgets(GtkPrinterOptionWidget * widget)939 update_widgets (GtkPrinterOptionWidget *widget)
940 {
941   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
942   GtkPrinterOption *source;
943 
944   source = priv->source;
945 
946   if (source == NULL)
947     {
948       gtk_widget_hide (priv->image);
949       return;
950     }
951 
952   switch (source->type)
953     {
954     case GTK_PRINTER_OPTION_TYPE_BOOLEAN:
955       if (g_ascii_strcasecmp (source->value, "True") == 0)
956 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->check), TRUE);
957       else
958 	gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->check), FALSE);
959       break;
960     case GTK_PRINTER_OPTION_TYPE_PICKONE:
961       combo_box_set (priv->combo, source->value);
962       break;
963     case GTK_PRINTER_OPTION_TYPE_ALTERNATIVE:
964       alternative_set (priv->box, source->value);
965       break;
966     case GTK_PRINTER_OPTION_TYPE_STRING:
967       gtk_entry_set_text (GTK_ENTRY (priv->entry), source->value);
968       break;
969     case GTK_PRINTER_OPTION_TYPE_PICKONE_PASSWORD:
970     case GTK_PRINTER_OPTION_TYPE_PICKONE_PASSCODE:
971     case GTK_PRINTER_OPTION_TYPE_PICKONE_REAL:
972     case GTK_PRINTER_OPTION_TYPE_PICKONE_INT:
973     case GTK_PRINTER_OPTION_TYPE_PICKONE_STRING:
974       {
975         GtkEntry *entry;
976 
977         entry = GTK_ENTRY (gtk_bin_get_child (GTK_BIN (priv->combo)));
978         if (gtk_printer_option_has_choice (source, source->value))
979           combo_box_set (priv->combo, source->value);
980         else
981           gtk_entry_set_text (entry, source->value);
982 
983         break;
984       }
985     case GTK_PRINTER_OPTION_TYPE_FILESAVE:
986       {
987         gchar *text;
988         gchar *filename;
989 
990         filename = g_filename_from_uri (source->value, NULL, NULL);
991         if (filename != NULL)
992           {
993             text = g_filename_to_utf8 (filename, -1, NULL, NULL, NULL);
994             if (text != NULL)
995               {
996                 gchar *short_filename;
997 
998                 short_filename = trim_long_filename (text);
999                 gtk_button_set_label (GTK_BUTTON (priv->button), short_filename);
1000                 g_free (short_filename);
1001               }
1002 
1003             g_free (text);
1004             g_free (filename);
1005           }
1006         else
1007           gtk_button_set_label (GTK_BUTTON (priv->button), source->value);
1008         break;
1009       }
1010     case GTK_PRINTER_OPTION_TYPE_INFO:
1011       gtk_label_set_text (GTK_LABEL (priv->info_label), source->value);
1012       break;
1013     default:
1014       break;
1015     }
1016 
1017   if (source->has_conflict)
1018     gtk_widget_show (priv->image);
1019   else
1020     gtk_widget_hide (priv->image);
1021 }
1022 
1023 gboolean
gtk_printer_option_widget_has_external_label(GtkPrinterOptionWidget * widget)1024 gtk_printer_option_widget_has_external_label (GtkPrinterOptionWidget *widget)
1025 {
1026   return widget->priv->label != NULL;
1027 }
1028 
1029 GtkWidget *
gtk_printer_option_widget_get_external_label(GtkPrinterOptionWidget * widget)1030 gtk_printer_option_widget_get_external_label (GtkPrinterOptionWidget  *widget)
1031 {
1032   return widget->priv->label;
1033 }
1034 
1035 const gchar *
gtk_printer_option_widget_get_value(GtkPrinterOptionWidget * widget)1036 gtk_printer_option_widget_get_value (GtkPrinterOptionWidget *widget)
1037 {
1038   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
1039 
1040   if (priv->source)
1041     return priv->source->value;
1042 
1043   return "";
1044 }
1045