1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2  *
3  * Copyright 2012  Red Hat, Inc,
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  *
18  * Author: Marek Kasik <mkasik@redhat.com>
19  */
20 
21 #include "config.h"
22 
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <sys/types.h>
26 #include <sys/wait.h>
27 
28 #include <glib.h>
29 #include <glib/gi18n.h>
30 #include <glib/gstdio.h>
31 #include <gtk/gtk.h>
32 
33 #include "pp-ppd-selection-dialog.h"
34 
35 enum
36 {
37   PPD_NAMES_COLUMN = 0,
38   PPD_DISPLAY_NAMES_COLUMN
39 };
40 
41 enum
42 {
43   PPD_MANUFACTURERS_NAMES_COLUMN = 0,
44   PPD_MANUFACTURERS_DISPLAY_NAMES_COLUMN
45 };
46 
47 
48 struct _PpPPDSelectionDialog {
49   GtkDialog parent_instance;
50 
51   GtkButton   *ppd_selection_select_button;
52   GtkSpinner  *ppd_spinner;
53   GtkLabel    *progress_label;
54   GtkTreeView *ppd_selection_manufacturers_treeview;
55   GtkTreeView *ppd_selection_models_treeview;
56 
57   UserResponseCallback user_callback;
58   gpointer             user_data;
59 
60   gchar           *ppd_name;
61   gchar           *ppd_display_name;
62   gchar           *manufacturer;
63 
64   PPDList *list;
65 };
66 
G_DEFINE_TYPE(PpPPDSelectionDialog,pp_ppd_selection_dialog,GTK_TYPE_DIALOG)67 G_DEFINE_TYPE (PpPPDSelectionDialog, pp_ppd_selection_dialog, GTK_TYPE_DIALOG)
68 
69 static void
70 manufacturer_selection_changed_cb (PpPPDSelectionDialog *self)
71 {
72   GtkTreeView  *treeview;
73   g_autoptr(GtkListStore) store = NULL;
74   GtkTreeModel *model;
75   GtkTreeIter   iter;
76   GtkTreeView  *models_treeview;
77   gchar        *manufacturer_name = NULL;
78   gint          i, index;
79 
80   treeview = self->ppd_selection_manufacturers_treeview;
81   if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (treeview), &model, &iter))
82     {
83       gtk_tree_model_get (model, &iter,
84                           PPD_MANUFACTURERS_NAMES_COLUMN, &manufacturer_name,
85                           -1);
86     }
87 
88   if (manufacturer_name)
89     {
90       index = -1;
91       for (i = 0; i < self->list->num_of_manufacturers; i++)
92         {
93           if (g_strcmp0 (manufacturer_name,
94                          self->list->manufacturers[i]->manufacturer_name) == 0)
95             {
96               index = i;
97               break;
98             }
99         }
100 
101       if (index >= 0)
102         {
103           models_treeview = self->ppd_selection_models_treeview;
104 
105           store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
106 
107           for (i = 0; i < self->list->manufacturers[index]->num_of_ppds; i++)
108             {
109               gtk_list_store_append (store, &iter);
110               gtk_list_store_set (store, &iter,
111                                   PPD_NAMES_COLUMN, self->list->manufacturers[index]->ppds[i]->ppd_name,
112                                   PPD_DISPLAY_NAMES_COLUMN, self->list->manufacturers[index]->ppds[i]->ppd_display_name,
113                                   -1);
114             }
115 
116           gtk_tree_view_set_model (models_treeview, GTK_TREE_MODEL (store));
117           gtk_tree_view_columns_autosize (models_treeview);
118         }
119 
120       g_free (manufacturer_name);
121     }
122 }
123 
124 static void
model_selection_changed_cb(PpPPDSelectionDialog * self)125 model_selection_changed_cb (PpPPDSelectionDialog *self)
126 {
127   GtkTreeView  *treeview;
128   GtkTreeModel *model;
129   GtkTreeIter   iter;
130   GtkButton    *ppd_select_button;
131   gchar        *model_name = NULL;
132 
133   treeview = self->ppd_selection_models_treeview;
134   if (gtk_tree_selection_get_selected (gtk_tree_view_get_selection (treeview), &model, &iter))
135     {
136       gtk_tree_model_get (model, &iter,
137                           PPD_NAMES_COLUMN, &model_name,
138                           -1);
139     }
140 
141   ppd_select_button = self->ppd_selection_select_button;
142 
143   if (model_name)
144     {
145       gtk_widget_set_sensitive (GTK_WIDGET (ppd_select_button), TRUE);
146       g_free (model_name);
147     }
148   else
149     {
150       gtk_widget_set_sensitive (GTK_WIDGET (ppd_select_button), FALSE);
151     }
152 }
153 
154 static void
fill_ppds_list(PpPPDSelectionDialog * self)155 fill_ppds_list (PpPPDSelectionDialog *self)
156 {
157   GtkTreeSelection *selection;
158   g_autoptr(GtkListStore) store = NULL;
159   GtkTreePath      *path;
160   GtkTreeView      *treeview;
161   GtkTreeIter       iter;
162   GtkTreeIter      *preselect_iter = NULL;
163   gint              i;
164 
165   gtk_widget_hide (GTK_WIDGET (self->ppd_spinner));
166   gtk_spinner_stop (self->ppd_spinner);
167 
168   gtk_widget_hide (GTK_WIDGET (self->progress_label));
169 
170   treeview = self->ppd_selection_manufacturers_treeview;
171 
172   if (self->list)
173     {
174       store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
175 
176       for (i = 0; i < self->list->num_of_manufacturers; i++)
177         {
178           gtk_list_store_append (store, &iter);
179           gtk_list_store_set (store, &iter,
180                               PPD_MANUFACTURERS_NAMES_COLUMN, self->list->manufacturers[i]->manufacturer_name,
181                               PPD_MANUFACTURERS_DISPLAY_NAMES_COLUMN, self->list->manufacturers[i]->manufacturer_display_name,
182                               -1);
183 
184           if (g_strcmp0 (self->manufacturer,
185                          self->list->manufacturers[i]->manufacturer_display_name) == 0)
186             {
187               preselect_iter = gtk_tree_iter_copy (&iter);
188             }
189         }
190 
191       gtk_tree_view_set_model (treeview, GTK_TREE_MODEL (store));
192 
193       if (preselect_iter &&
194           (selection = gtk_tree_view_get_selection (treeview)) != NULL)
195         {
196           gtk_tree_selection_select_iter (selection, preselect_iter);
197           path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), preselect_iter);
198           gtk_tree_view_scroll_to_cell (treeview, path, NULL, TRUE, 0.5, 0.0);
199           gtk_tree_path_free (path);
200           gtk_tree_iter_free (preselect_iter);
201         }
202     }
203 }
204 
205 static void
populate_dialog(PpPPDSelectionDialog * self)206 populate_dialog (PpPPDSelectionDialog *self)
207 {
208   GtkTreeViewColumn *column;
209   GtkCellRenderer   *renderer;
210   GtkTreeView       *manufacturers_treeview;
211   GtkTreeView       *models_treeview;
212   GtkWidget         *header;
213 
214   manufacturers_treeview = self->ppd_selection_manufacturers_treeview;
215 
216   renderer = gtk_cell_renderer_text_new ();
217   gtk_cell_renderer_set_padding (renderer, 10, 0);
218 
219   /* Translators: Name of column showing printer manufacturers */
220   column = gtk_tree_view_column_new_with_attributes (_("Manufacturer"), renderer,
221                                                      "text", PPD_MANUFACTURERS_DISPLAY_NAMES_COLUMN, NULL);
222   gtk_tree_view_column_set_expand (column, TRUE);
223   header = gtk_label_new (gtk_tree_view_column_get_title (column));
224   gtk_widget_set_margin_start (header, 10);
225   gtk_tree_view_column_set_widget (column, header);
226   gtk_widget_show (header);
227   gtk_tree_view_append_column (manufacturers_treeview, column);
228 
229 
230   models_treeview = self->ppd_selection_models_treeview;
231 
232   renderer = gtk_cell_renderer_text_new ();
233   gtk_cell_renderer_set_padding (renderer, 10, 0);
234 
235   /* Translators: Name of column showing printer drivers */
236   column = gtk_tree_view_column_new_with_attributes (_("Driver"), renderer,
237                                                      "text", PPD_DISPLAY_NAMES_COLUMN,
238                                                      NULL);
239   gtk_tree_view_column_set_expand (column, TRUE);
240   header = gtk_label_new (gtk_tree_view_column_get_title (column));
241   gtk_widget_set_margin_start (header, 10);
242   gtk_tree_view_column_set_widget (column, header);
243   gtk_widget_show (header);
244   gtk_tree_view_append_column (models_treeview, column);
245 
246 
247   g_signal_connect_object (gtk_tree_view_get_selection (models_treeview),
248                            "changed", G_CALLBACK (model_selection_changed_cb), self, G_CONNECT_SWAPPED);
249 
250   g_signal_connect_object (gtk_tree_view_get_selection (manufacturers_treeview),
251                            "changed", G_CALLBACK (manufacturer_selection_changed_cb), self, G_CONNECT_SWAPPED);
252 
253   if (!self->list)
254     {
255       gtk_widget_show (GTK_WIDGET (self->ppd_spinner));
256       gtk_spinner_start (self->ppd_spinner);
257 
258       gtk_widget_show (GTK_WIDGET (self->progress_label));
259     }
260   else
261     {
262       fill_ppds_list (self);
263     }
264 }
265 
266 static void
ppd_selection_dialog_response_cb(PpPPDSelectionDialog * self,gint response_id)267 ppd_selection_dialog_response_cb (PpPPDSelectionDialog *self,
268                                   gint       response_id)
269 {
270   GtkTreeSelection *selection;
271   GtkTreeModel     *model;
272   GtkTreeView      *models_treeview;
273   GtkTreeIter       iter;
274 
275   if (response_id == GTK_RESPONSE_OK)
276     {
277       models_treeview = self->ppd_selection_models_treeview;
278 
279       if (models_treeview)
280         {
281           selection = gtk_tree_view_get_selection (models_treeview);
282 
283           if (selection)
284             {
285               if (gtk_tree_selection_get_selected (selection, &model, &iter))
286                 {
287                   gtk_tree_model_get (model, &iter,
288                                       PPD_NAMES_COLUMN, &self->ppd_name,
289                                       PPD_DISPLAY_NAMES_COLUMN, &self->ppd_display_name,
290                                       -1);
291                 }
292             }
293         }
294     }
295 
296   self->user_callback (GTK_DIALOG (self), response_id, self->user_data);
297 }
298 
299 PpPPDSelectionDialog *
pp_ppd_selection_dialog_new(PPDList * ppd_list,const gchar * manufacturer,UserResponseCallback user_callback,gpointer user_data)300 pp_ppd_selection_dialog_new (PPDList              *ppd_list,
301                              const gchar          *manufacturer,
302                              UserResponseCallback  user_callback,
303                              gpointer              user_data)
304 {
305   PpPPDSelectionDialog *self;
306 
307   self = g_object_new (pp_ppd_selection_dialog_get_type (), NULL);
308 
309   self->user_callback = user_callback;
310   self->user_data = user_data;
311 
312   self->list = ppd_list_copy (ppd_list);
313 
314   self->manufacturer = get_standard_manufacturers_name (manufacturer);
315 
316   /* connect signal */
317   g_signal_connect_object (self, "response", G_CALLBACK (ppd_selection_dialog_response_cb), self, G_CONNECT_SWAPPED);
318 
319   gtk_spinner_start (self->ppd_spinner);
320 
321   populate_dialog (self);
322 
323   return self;
324 }
325 
326 static void
pp_ppd_selection_dialog_dispose(GObject * object)327 pp_ppd_selection_dialog_dispose (GObject *object)
328 {
329   PpPPDSelectionDialog *self = PP_PPD_SELECTION_DIALOG (object);
330 
331   g_clear_pointer (&self->ppd_name, g_free);
332   g_clear_pointer (&self->ppd_display_name, g_free);
333   g_clear_pointer (&self->manufacturer, g_free);
334 
335   G_OBJECT_CLASS (pp_ppd_selection_dialog_parent_class)->dispose (object);
336 }
337 
338 void
pp_ppd_selection_dialog_class_init(PpPPDSelectionDialogClass * klass)339 pp_ppd_selection_dialog_class_init (PpPPDSelectionDialogClass *klass)
340 {
341   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
342   GObjectClass   *object_class = G_OBJECT_CLASS (klass);
343 
344   gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/printers/ppd-selection-dialog.ui");
345   gtk_widget_class_bind_template_child (widget_class, PpPPDSelectionDialog, ppd_selection_select_button);
346   gtk_widget_class_bind_template_child (widget_class, PpPPDSelectionDialog, ppd_spinner);
347   gtk_widget_class_bind_template_child (widget_class, PpPPDSelectionDialog, progress_label);
348   gtk_widget_class_bind_template_child (widget_class, PpPPDSelectionDialog, ppd_selection_manufacturers_treeview);
349   gtk_widget_class_bind_template_child (widget_class, PpPPDSelectionDialog, ppd_selection_models_treeview);
350 
351   object_class->dispose = pp_ppd_selection_dialog_dispose;
352 }
353 
354 void
pp_ppd_selection_dialog_init(PpPPDSelectionDialog * self)355 pp_ppd_selection_dialog_init (PpPPDSelectionDialog *self)
356 {
357   gtk_widget_init_template (GTK_WIDGET (self));
358 }
359 
360 gchar *
pp_ppd_selection_dialog_get_ppd_name(PpPPDSelectionDialog * self)361 pp_ppd_selection_dialog_get_ppd_name (PpPPDSelectionDialog *self)
362 {
363   return g_strdup (self->ppd_name);
364 }
365 
366 gchar *
pp_ppd_selection_dialog_get_ppd_display_name(PpPPDSelectionDialog * self)367 pp_ppd_selection_dialog_get_ppd_display_name (PpPPDSelectionDialog *self)
368 {
369   return g_strdup (self->ppd_display_name);
370 }
371 
372 void
pp_ppd_selection_dialog_set_ppd_list(PpPPDSelectionDialog * self,PPDList * list)373 pp_ppd_selection_dialog_set_ppd_list (PpPPDSelectionDialog *self,
374                                       PPDList              *list)
375 {
376   self->list = list;
377   fill_ppds_list (self);
378 }
379