]> Pileus Git - ~andy/gtk/blob - gtk/gtkprinteroptionwidget.c
Allow absolute filenames in filename entry. Patch from Yevgen Muntyan
[~andy/gtk] / gtk / gtkprinteroptionwidget.c
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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #include "config.h"
21 #include <stdlib.h>
22 #include <string.h>
23 #include <stdio.h>
24
25 #include "gtkintl.h"
26 #include "gtkalignment.h"
27 #include "gtkcheckbutton.h"
28 #include "gtkcelllayout.h"
29 #include "gtkcellrenderertext.h"
30 #include "gtkcombobox.h"
31 #include "gtkfilechooserbutton.h"
32 #include "gtkimage.h"
33 #include "gtklabel.h"
34 #include "gtkliststore.h"
35 #include "gtkstock.h"
36 #include "gtktable.h"
37 #include "gtktogglebutton.h"
38 #include "gtkprivate.h"
39
40 #include "gtkprinteroptionwidget.h"
41 #include "gtkalias.h"
42
43 #define GTK_PRINTER_OPTION_WIDGET_GET_PRIVATE(o)  \
44    (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_PRINTER_OPTION_WIDGET, GtkPrinterOptionWidgetPrivate))
45
46 static void gtk_printer_option_widget_finalize (GObject *object);
47
48 static void deconstruct_widgets (GtkPrinterOptionWidget *widget);
49 static void construct_widgets (GtkPrinterOptionWidget *widget);
50 static void update_widgets (GtkPrinterOptionWidget *widget);
51
52 struct GtkPrinterOptionWidgetPrivate
53 {
54   GtkPrinterOption *source;
55   gulong source_changed_handler;
56   
57   GtkWidget *check;
58   GtkWidget *combo;
59   GtkWidget *entry;
60   GtkWidget *image;
61   GtkWidget *label;
62   GtkWidget *filechooser;
63 };
64
65 enum {
66   CHANGED,
67   LAST_SIGNAL
68 };
69
70 enum {
71   PROP_0,
72   PROP_SOURCE,
73 };
74
75 static guint signals[LAST_SIGNAL] = { 0 };
76
77 G_DEFINE_TYPE (GtkPrinterOptionWidget, gtk_printer_option_widget, GTK_TYPE_HBOX)
78
79 static void gtk_printer_option_widget_set_property (GObject      *object,
80                                                     guint         prop_id,
81                                                     const GValue *value,
82                                                     GParamSpec   *pspec);
83 static void gtk_printer_option_widget_get_property (GObject      *object,
84                                                     guint         prop_id,
85                                                     GValue       *value,
86                                                     GParamSpec   *pspec);
87 static gboolean gtk_printer_option_widget_mnemonic_activate (GtkWidget *widget,
88                                                               gboolean   group_cycling);
89
90 static void
91 gtk_printer_option_widget_class_init (GtkPrinterOptionWidgetClass *class)
92 {
93   GObjectClass *object_class;
94   GtkWidgetClass *widget_class;
95
96   object_class = (GObjectClass *) class;
97   widget_class = (GtkWidgetClass *) class;
98
99   object_class->finalize = gtk_printer_option_widget_finalize;
100   object_class->set_property = gtk_printer_option_widget_set_property;
101   object_class->get_property = gtk_printer_option_widget_get_property;
102
103   widget_class->mnemonic_activate = gtk_printer_option_widget_mnemonic_activate;
104
105   g_type_class_add_private (class, sizeof (GtkPrinterOptionWidgetPrivate));  
106
107   signals[CHANGED] =
108     g_signal_new ("changed",
109                   G_TYPE_FROM_CLASS (class),
110                   G_SIGNAL_RUN_LAST,
111                   G_STRUCT_OFFSET (GtkPrinterOptionWidgetClass, changed),
112                   NULL, NULL,
113                   g_cclosure_marshal_VOID__VOID,
114                   G_TYPE_NONE, 0);
115
116   g_object_class_install_property (object_class,
117                                    PROP_SOURCE,
118                                    g_param_spec_object ("source",
119                                                         P_("Source option"),
120                                                         P_("The PrinterOption backing this widget"),
121                                                         GTK_TYPE_PRINTER_OPTION,
122                                                         GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT));
123
124 }
125
126 static void
127 gtk_printer_option_widget_init (GtkPrinterOptionWidget *widget)
128 {
129   widget->priv = GTK_PRINTER_OPTION_WIDGET_GET_PRIVATE (widget); 
130
131   gtk_box_set_spacing (GTK_BOX (widget), 12);
132 }
133
134 static void
135 gtk_printer_option_widget_finalize (GObject *object)
136 {
137   GtkPrinterOptionWidget *widget = GTK_PRINTER_OPTION_WIDGET (object);
138   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
139   
140   if (priv->source)
141     {
142       g_object_unref (priv->source);
143       priv->source = NULL;
144     }
145   
146   G_OBJECT_CLASS (gtk_printer_option_widget_parent_class)->finalize (object);
147 }
148
149 static void
150 gtk_printer_option_widget_set_property (GObject         *object,
151                                         guint            prop_id,
152                                         const GValue    *value,
153                                         GParamSpec      *pspec)
154 {
155   GtkPrinterOptionWidget *widget;
156   
157   widget = GTK_PRINTER_OPTION_WIDGET (object);
158
159   switch (prop_id)
160     {
161     case PROP_SOURCE:
162       gtk_printer_option_widget_set_source (widget, g_value_get_object (value));
163       break;
164     default:
165       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
166       break;
167     }
168 }
169
170 static void
171 gtk_printer_option_widget_get_property (GObject    *object,
172                                         guint       prop_id,
173                                         GValue     *value,
174                                         GParamSpec *pspec)
175 {
176   GtkPrinterOptionWidget *widget = GTK_PRINTER_OPTION_WIDGET (object);
177   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
178
179   switch (prop_id)
180     {
181     case PROP_SOURCE:
182       g_value_set_object (value, priv->source);
183       break;
184     default:
185       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
186       break;
187     }
188 }
189
190 static gboolean
191 gtk_printer_option_widget_mnemonic_activate (GtkWidget *widget,
192                                              gboolean   group_cycling)
193 {
194   GtkPrinterOptionWidget *powidget = GTK_PRINTER_OPTION_WIDGET (widget);
195   GtkPrinterOptionWidgetPrivate *priv = powidget->priv;
196
197   if (priv->check)
198     return gtk_widget_mnemonic_activate (priv->check, group_cycling);
199   if (priv->combo)
200     return gtk_widget_mnemonic_activate (priv->combo, group_cycling);
201   if (priv->entry)
202     return gtk_widget_mnemonic_activate (priv->entry, group_cycling);
203
204   return FALSE;
205 }
206
207 static void
208 emit_changed (GtkPrinterOptionWidget *widget)
209 {
210   g_signal_emit (widget, signals[CHANGED], 0);
211 }
212
213 GtkWidget *
214 gtk_printer_option_widget_new (GtkPrinterOption *source)
215 {
216   return g_object_new (GTK_TYPE_PRINTER_OPTION_WIDGET, "source", source, NULL);
217 }
218
219 static void
220 source_changed_cb (GtkPrinterOption *source,
221                    GtkPrinterOptionWidget  *widget)
222 {
223   update_widgets (widget);
224   emit_changed (widget);
225 }
226
227 void
228 gtk_printer_option_widget_set_source (GtkPrinterOptionWidget  *widget,
229                                       GtkPrinterOption *source)
230 {
231   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
232
233   if (source)
234     g_object_ref (source);
235   
236   if (priv->source)
237     {
238       g_signal_handler_disconnect (priv->source,
239                                    priv->source_changed_handler);
240       g_object_unref (priv->source);
241     }
242
243   priv->source = source;
244
245   if (source)
246     priv->source_changed_handler =
247       g_signal_connect (source, "changed", G_CALLBACK (source_changed_cb), widget);
248
249   construct_widgets (widget);
250   update_widgets (widget);
251
252   g_object_notify (G_OBJECT (widget), "source");
253 }
254
255 static GtkWidget *
256 combo_box_new (void)
257 {
258   GtkWidget *combo_box;
259   GtkCellRenderer *cell;
260   GtkListStore *store;
261
262   store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
263   combo_box = gtk_combo_box_new_with_model (GTK_TREE_MODEL (store));
264   g_object_unref (store);
265
266   cell = gtk_cell_renderer_text_new ();
267   gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo_box), cell, TRUE);
268   gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
269                                   "text", 0,
270                                   NULL);
271
272   return combo_box;
273 }
274   
275 static void
276 combo_box_append (GtkWidget *combo,
277                   const char *display_text,
278                   const char *value)
279 {
280   GtkTreeModel *model;
281   GtkListStore *store;
282   GtkTreeIter iter;
283   
284   model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
285   store = GTK_LIST_STORE (model);
286
287   gtk_list_store_append (store, &iter);
288   gtk_list_store_set (store, &iter,
289                       0, display_text,
290                       1, value,
291                       -1);
292 }
293
294 struct ComboSet {
295   GtkComboBox *combo;
296   const char *value;
297 };
298
299 static gboolean
300 set_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
301 {
302   struct ComboSet *set_data = data;
303   gboolean found;
304   char *value;
305   
306   gtk_tree_model_get (model, iter, 1, &value, -1);
307   found = (strcmp (value, set_data->value) == 0);
308   g_free (value);
309   
310   if (found)
311     gtk_combo_box_set_active_iter (set_data->combo, iter);
312
313   return found;
314 }
315
316 static void
317 combo_box_set (GtkWidget *combo,
318                const char *value)
319 {
320   GtkTreeModel *model;
321   GtkListStore *store;
322   struct ComboSet set_data;
323   
324   model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
325   store = GTK_LIST_STORE (model);
326
327   set_data.combo = GTK_COMBO_BOX (combo);
328   set_data.value = value;
329   gtk_tree_model_foreach (model, set_cb, &set_data);
330 }
331
332 static char *
333 combo_box_get (GtkWidget *combo)
334 {
335   GtkTreeModel *model;
336   char *val;
337   GtkTreeIter iter;
338   
339   model = gtk_combo_box_get_model (GTK_COMBO_BOX (combo));
340
341   val = NULL;
342   if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combo), &iter))
343     gtk_tree_model_get (model, &iter,
344                         1, &val,
345                         -1);
346   return val;
347 }
348
349
350 static void
351 deconstruct_widgets (GtkPrinterOptionWidget *widget)
352 {
353   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
354
355   if (priv->check)
356     {
357       gtk_widget_destroy (priv->check);
358       priv->check = NULL;
359     }
360   
361   if (priv->combo)
362     {
363       gtk_widget_destroy (priv->combo);
364       priv->combo = NULL;
365     }
366   
367   if (priv->entry)
368     {
369       gtk_widget_destroy (priv->entry);
370       priv->entry = NULL;
371     }
372
373   /* make sure entry and combo are destroyed first */
374   /* as we use the two of them to create the filechooser */
375   if (priv->filechooser)
376     {
377       gtk_widget_destroy (priv->filechooser);
378       priv->filechooser = NULL;
379     }
380
381   if (priv->image)
382     {
383       gtk_widget_destroy (priv->image);
384       priv->image = NULL;
385     }
386
387   if (priv->label)
388     {
389       gtk_widget_destroy (priv->label);
390       priv->label = NULL;
391     }
392 }
393
394 static void
395 check_toggled_cb (GtkToggleButton *toggle_button,
396                   GtkPrinterOptionWidget *widget)
397 {
398   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
399
400   g_signal_handler_block (priv->source, priv->source_changed_handler);
401   gtk_printer_option_set_boolean (priv->source,
402                                   gtk_toggle_button_get_active (toggle_button));
403   g_signal_handler_unblock (priv->source, priv->source_changed_handler);
404   emit_changed (widget);
405 }
406
407 static void
408 filesave_changed_cb (GtkWidget *w,
409                      GtkPrinterOptionWidget *widget)
410 {
411   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
412   char *value;
413   char *directory;
414   const char *file;
415
416   /* combine the value of the chooser with the value of the entry */
417   g_signal_handler_block (priv->source, priv->source_changed_handler);  
418   
419   /* TODO: how do we support nonlocal file systems? */
420   directory = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (priv->combo));
421   file =  gtk_entry_get_text (GTK_ENTRY (priv->entry));
422
423   if (g_path_is_absolute (file))
424     value = g_strdup (file);
425 #ifdef G_OS_UNIX
426   else if (file[0] == '~' && file[1] == '/')
427     value = g_build_filename (g_get_home_dir (), file + 2, NULL);
428 #endif
429   else
430     value = g_build_filename (directory, file, NULL);
431
432   if (value)
433     gtk_printer_option_set (priv->source, value);
434
435   g_free (directory);
436   g_free (value);
437
438   g_signal_handler_unblock (priv->source, priv->source_changed_handler);
439   emit_changed (widget);
440 }
441
442 static void
443 combo_changed_cb (GtkWidget *combo,
444                   GtkPrinterOptionWidget *widget)
445 {
446   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
447   char *value;
448   
449   g_signal_handler_block (priv->source, priv->source_changed_handler);
450   value = combo_box_get (combo);
451   if (value)
452     gtk_printer_option_set (priv->source, value);
453   g_free (value);
454   g_signal_handler_unblock (priv->source, priv->source_changed_handler);
455   emit_changed (widget);
456 }
457
458 static void
459 entry_changed_cb (GtkWidget *entry,
460                   GtkPrinterOptionWidget *widget)
461 {
462   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
463   const char *value;
464   
465   g_signal_handler_block (priv->source, priv->source_changed_handler);
466   value = gtk_entry_get_text (GTK_ENTRY (entry));
467   if (value)
468     gtk_printer_option_set (priv->source, value);
469   g_signal_handler_unblock (priv->source, priv->source_changed_handler);
470   emit_changed (widget);
471 }
472
473
474 static void
475 construct_widgets (GtkPrinterOptionWidget *widget)
476 {
477   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
478   GtkPrinterOption *source;
479   char *text;
480   int i;
481
482   source = priv->source;
483   
484   deconstruct_widgets (widget);
485   
486   if (source == NULL)
487     {
488       priv->combo = combo_box_new ();
489       combo_box_append (priv->combo,_("Not available"), "None");
490       gtk_combo_box_set_active (GTK_COMBO_BOX (priv->combo), 0);
491       gtk_widget_set_sensitive (priv->combo, FALSE);
492       gtk_widget_show (priv->combo);
493       gtk_box_pack_start (GTK_BOX (widget), priv->combo, TRUE, TRUE, 0);
494     }
495   else switch (source->type)
496     {
497     case GTK_PRINTER_OPTION_TYPE_BOOLEAN:
498       priv->check = gtk_check_button_new_with_mnemonic (source->display_text);
499       g_signal_connect (priv->check, "toggled", G_CALLBACK (check_toggled_cb), widget);
500       gtk_widget_show (priv->check);
501       gtk_box_pack_start (GTK_BOX (widget), priv->check, TRUE, TRUE, 0);
502       break;
503     case GTK_PRINTER_OPTION_TYPE_PICKONE:
504       priv->combo = combo_box_new ();
505       for (i = 0; i < source->num_choices; i++)
506           combo_box_append (priv->combo,
507                             source->choices_display[i],
508                             source->choices[i]);
509       gtk_widget_show (priv->combo);
510       gtk_box_pack_start (GTK_BOX (widget), priv->combo, TRUE, TRUE, 0);
511       g_signal_connect (priv->combo, "changed", G_CALLBACK (combo_changed_cb), widget);
512
513       text = g_strdup_printf ("%s:", source->display_text);
514       priv->label = gtk_label_new_with_mnemonic (text);
515       g_free (text);
516       gtk_widget_show (priv->label);
517       break;
518     case GTK_PRINTER_OPTION_TYPE_STRING:
519       priv->entry = gtk_entry_new ();
520       gtk_widget_show (priv->entry);
521       gtk_box_pack_start (GTK_BOX (widget), priv->entry, TRUE, TRUE, 0);
522       g_signal_connect (priv->entry, "changed", G_CALLBACK (entry_changed_cb), widget);
523
524       text = g_strdup_printf ("%s:", source->display_text);
525       priv->label = gtk_label_new_with_mnemonic (text);
526       g_free (text);
527       gtk_widget_show (priv->label);
528
529       break;
530
531     case GTK_PRINTER_OPTION_TYPE_FILESAVE:
532       {
533         GtkWidget *label;
534         
535         priv->filechooser = gtk_table_new (2, 2, FALSE);
536         gtk_table_set_row_spacings (GTK_TABLE (priv->filechooser), 6);
537         gtk_table_set_col_spacings (GTK_TABLE (priv->filechooser), 12);
538
539         /* TODO: make this a gtkfilechooserentry once we move to GTK */
540         priv->entry = gtk_entry_new ();
541         priv->combo = gtk_file_chooser_button_new (_("Print to PDF"),
542                                                            GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
543
544         label = gtk_label_new_with_mnemonic (_("_Name:"));
545         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
546         gtk_label_set_mnemonic_widget (GTK_LABEL (label), priv->entry);
547
548         gtk_table_attach (GTK_TABLE (priv->filechooser), label,
549                           0, 1, 0, 1, GTK_FILL, 0,
550                           0, 0);
551
552         gtk_table_attach (GTK_TABLE (priv->filechooser), priv->entry,
553                           1, 2, 0, 1, GTK_FILL, 0,
554                           0, 0);
555
556         label = gtk_label_new_with_mnemonic (_("_Save in folder:"));
557         gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
558         gtk_label_set_mnemonic_widget (GTK_LABEL (label), priv->combo);
559
560         gtk_table_attach (GTK_TABLE (priv->filechooser), label,
561                           0, 1, 1, 2, GTK_FILL, 0,
562                           0, 0);
563
564         gtk_table_attach (GTK_TABLE (priv->filechooser), priv->combo,
565                           1, 2, 1, 2, GTK_FILL, 0,
566                           0, 0);
567
568         gtk_widget_show_all (priv->filechooser);
569         gtk_box_pack_start (GTK_BOX (widget), priv->filechooser, TRUE, TRUE, 0);
570
571         g_signal_connect (priv->entry, "changed", G_CALLBACK (filesave_changed_cb), widget);
572
573         g_signal_connect (priv->combo, "current-folder-changed", G_CALLBACK (filesave_changed_cb), widget);
574       }
575       break;
576     default:
577       break;
578     }
579
580   priv->image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_MENU);
581   gtk_box_pack_start (GTK_BOX (widget), priv->image, FALSE, FALSE, 0);
582 }
583
584 static void
585 update_widgets (GtkPrinterOptionWidget *widget)
586 {
587   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
588   GtkPrinterOption *source;
589
590   source = priv->source;
591   
592   if (source == NULL)
593     {
594       gtk_widget_hide (priv->image);
595       return;
596     }
597
598   switch (source->type)
599     {
600     case GTK_PRINTER_OPTION_TYPE_BOOLEAN:
601       if (strcmp (source->value, "True") == 0)
602         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->check), TRUE);
603       else
604         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->check), FALSE);
605       break;
606     case GTK_PRINTER_OPTION_TYPE_PICKONE:
607       combo_box_set (priv->combo, source->value);
608       break;
609     case GTK_PRINTER_OPTION_TYPE_STRING:
610       gtk_entry_set_text (GTK_ENTRY (priv->entry), source->value);
611       break;
612     case GTK_PRINTER_OPTION_TYPE_FILESAVE:
613       {
614         char *basename = g_path_get_basename (source->value);
615         char *dirname = g_path_get_dirname (source->value);
616         gtk_entry_set_text (GTK_ENTRY (priv->entry), basename);
617         if (g_path_is_absolute (dirname))
618           gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (priv->combo),
619                                                dirname);
620         g_free (basename);
621         g_free (dirname);
622         break;
623       }
624     default:
625       break;
626     }
627
628   if (source->has_conflict)
629     gtk_widget_show (priv->image);
630   else
631     gtk_widget_hide (priv->image);
632 }
633
634 gboolean
635 gtk_printer_option_widget_has_external_label (GtkPrinterOptionWidget  *widget)
636 {
637   return widget->priv->label != NULL;
638 }
639
640 GtkWidget *
641 gtk_printer_option_widget_get_external_label (GtkPrinterOptionWidget  *widget)
642 {
643   return widget->priv->label;
644 }
645
646 const char *
647 gtk_printer_option_widget_get_value (GtkPrinterOptionWidget  *widget)
648 {
649   GtkPrinterOptionWidgetPrivate *priv = widget->priv;
650
651   if (priv->source)
652     return priv->source->value;
653   
654   return "";
655 }
656
657 #define __GTK_PRINTER_OPTION_WIDGET_C__
658 #include "gtkaliasdef.c"