1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2007, 2012 Free Software Foundation, Inc.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program 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
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 
18 /*
19   This widget is a GtkBox which looks roughly like:
20 
21   +-----------------------------+
22   |+------------+  +----------+	|
23   ||   Add      |  |	      |	|
24   |+------------+  |	      |	|
25   |                |	      |	|
26   |+------------+  |	      |	|
27   ||   Edit     |  |	      |	|
28   |+------------+  |	      |	|
29   |      	   |	      |	|
30   |+------------+  |	      |	|
31   ||  Remove    |  |	      |	|
32   |+------------+  +----------+	|
33   +-----------------------------+
34 
35 */
36 
37 #include <config.h>
38 #include <gtk/gtk.h>
39 
40 #include "psppire-acr.h"
41 #include "helper.h"
42 
43 #include "gettext.h"
44 #define _(msgid) gettext (msgid)
45 #define N_(msgid) msgid
46 
47 G_DEFINE_TYPE (PsppireAcr, psppire_acr, GTK_TYPE_BOX);
48 
49 static void
psppire_acr_dispose(GObject * obj)50 psppire_acr_dispose (GObject *obj)
51 {
52   PsppireAcr *acr = PSPPIRE_ACR (obj);
53 
54   if (acr->dispose_has_run)
55     return;
56   acr->dispose_has_run = TRUE;
57 
58   psppire_acr_set_model (acr, NULL);
59 
60   G_OBJECT_CLASS (psppire_acr_parent_class)->dispose (obj);
61 }
62 
63 static void
psppire_acr_class_init(PsppireAcrClass * class)64 psppire_acr_class_init (PsppireAcrClass *class)
65 {
66   G_OBJECT_CLASS (class)->dispose = psppire_acr_dispose;
67 }
68 
69 static gboolean row_is_selected (const PsppireAcr *acr);
70 
71 
72 static gboolean
value_from_entry(gint col,GValue * val,gpointer data)73 value_from_entry (gint col, GValue *val, gpointer data)
74 {
75   GtkEntry *entry = data;
76   const gchar *text = gtk_entry_get_text (entry);
77   gdouble x = g_strtod (text, 0);
78 
79   g_value_init (val, G_TYPE_DOUBLE);
80   g_value_set_double (val, x);
81 
82   return TRUE;
83 }
84 
85 
86 /* Returns true, if there's text in the entry */
87 static gboolean
entry_not_empty(gpointer data)88 entry_not_empty (gpointer data)
89 {
90   GtkEntry *entry = data;
91 
92   const char *text = gtk_entry_get_text (entry);
93 
94   return !g_str_equal (text, "");
95 }
96 
97 
98 static void
clear_entry(gpointer data)99 clear_entry (gpointer data)
100 {
101   GtkEntry *entry = data;
102   gtk_entry_set_text (entry, "");
103 }
104 
105 
106 static void
on_entry_change(GtkEntry * entry,PsppireAcr * acr)107 on_entry_change (GtkEntry *entry, PsppireAcr *acr)
108 {
109   gtk_widget_set_sensitive (acr->add_button, acr->enabled (entry));
110 
111   gtk_widget_set_sensitive (acr->change_button, acr->enabled (entry)
112 			    && row_is_selected (acr));
113 }
114 
115 void
psppire_acr_set_entry(PsppireAcr * acr,GtkEntry * entry)116 psppire_acr_set_entry  (PsppireAcr *acr, GtkEntry *entry)
117 {
118   acr->get_value = value_from_entry;
119   acr->get_value_data = entry;
120   acr->enabled = entry_not_empty;
121   acr->enabled_data = entry;
122   acr->update = clear_entry;
123   acr->update_data = entry;
124 
125   g_signal_connect (entry, "changed", G_CALLBACK (on_entry_change), acr);
126 }
127 
128 
129 /* Callback for when the Add button is clicked.
130    It appends an item to the list. */
131 static void
on_add_button_clicked(PsppireAcr * acr)132 on_add_button_clicked (PsppireAcr *acr)
133 {
134   gint i;
135   GtkTreeIter iter;
136   gtk_list_store_append (acr->list_store, &iter);
137 
138   for (i = 0 ;
139        i < gtk_tree_model_get_n_columns (GTK_TREE_MODEL (acr->list_store));
140        ++i)
141     {
142       static GValue value;
143       if (! acr->get_value (i, &value, acr->get_value_data))
144 	continue;
145 
146       gtk_list_store_set_value (acr->list_store, &iter,
147 				i, &value);
148       g_value_unset (&value);
149     }
150 
151   if (acr->update) acr->update (acr->update_data);
152 }
153 
154 
155 /* Callback for when the Changed button is clicked.
156    It replaces the currently selected entry. */
157 static void
on_change_button_clicked(PsppireAcr * acr)158 on_change_button_clicked (PsppireAcr *acr)
159 {
160   gint i;
161   GtkTreeModel *model = GTK_TREE_MODEL (acr->list_store);
162 
163   GList *l=
164     gtk_tree_selection_get_selected_rows (acr->selection,
165 					  &model);
166 
167   GtkTreePath *path = l->data;
168 
169   GtkTreeIter iter;
170 
171   gtk_tree_model_get_iter (model, &iter, path);
172 
173   for (i = 0 ;
174        i < gtk_tree_model_get_n_columns (GTK_TREE_MODEL (acr->list_store));
175        ++i)
176     {
177       static GValue value;
178       if (! acr->get_value (i, &value, acr->get_value_data))
179 	continue;
180 
181       gtk_list_store_set_value (acr->list_store, &iter,
182 				i, &value);
183       g_value_unset (&value);
184     }
185 
186   g_list_foreach (l, GFUNC_COMPAT_CAST (gtk_tree_path_free), NULL);
187   g_list_free (l);
188 
189   if (acr->update) acr->update (acr->update_data);
190 }
191 
192 
193 /* Callback for when the remove button is clicked.
194    It deletes the currently selected entry. */
195 static void
on_remove_button_clicked(PsppireAcr * acr)196 on_remove_button_clicked (PsppireAcr *acr)
197 {
198   GtkTreeModel *model = GTK_TREE_MODEL (acr->list_store);
199 
200   GList *l=
201     gtk_tree_selection_get_selected_rows (acr->selection,
202 					  &model);
203 
204   GtkTreePath *path = l->data;
205 
206   GtkTreeIter iter;
207 
208   gtk_tree_model_get_iter (model, &iter, path);
209 
210   gtk_list_store_remove (acr->list_store, &iter);
211 
212   g_list_foreach (l, GFUNC_COMPAT_CAST (gtk_tree_path_free), NULL);
213   g_list_free (l);
214 }
215 
216 /* Returns true if there is a row currently selected.
217    False otherwise. */
218 static gboolean
row_is_selected(const PsppireAcr * acr)219 row_is_selected (const PsppireAcr *acr)
220 {
221   gboolean result;
222   GtkTreeModel *model = GTK_TREE_MODEL (acr->list_store);
223   GList *l = gtk_tree_selection_get_selected_rows (acr->selection,
224 						   &model);
225 
226   result = (l != NULL);
227 
228   g_list_foreach (l, GFUNC_COMPAT_CAST (gtk_tree_path_free), NULL);
229   g_list_free (l);
230 
231   return result;
232 }
233 
234 
235 /* Callback which occurs when an item in the treeview
236    is selected */
237 static void
on_select(GtkTreeSelection * selection,gpointer data)238 on_select (GtkTreeSelection *selection, gpointer data)
239 {
240   PsppireAcr *acr = data;
241 
242   gtk_widget_set_sensitive (acr->remove_button, row_is_selected (acr));
243 
244   gtk_widget_set_sensitive (acr->change_button,
245 			    row_is_selected (acr)
246 			);
247 }
248 
249 
250 void
psppire_acr_set_enabled(PsppireAcr * acr,gboolean status)251 psppire_acr_set_enabled (PsppireAcr *acr, gboolean status)
252 {
253 
254   gtk_widget_set_sensitive (acr->add_button, status);
255 
256   gtk_widget_set_sensitive (acr->change_button, status
257 			    && row_is_selected (acr));
258 }
259 
260 static void
psppire_acr_init(PsppireAcr * acr)261 psppire_acr_init (PsppireAcr *acr)
262 {
263   GtkWidget *bb  = gtk_button_box_new (GTK_ORIENTATION_VERTICAL);
264 
265   GtkWidget *sw = gtk_scrolled_window_new (NULL, NULL);
266 
267   acr->dispose_has_run = FALSE;
268 
269   gtk_orientable_set_orientation (GTK_ORIENTABLE (acr), GTK_ORIENTATION_HORIZONTAL);
270 
271   acr->tv = GTK_TREE_VIEW (gtk_tree_view_new ());
272 
273   acr->add_button = gtk_button_new_with_label (_("Add"));
274   acr->change_button = gtk_button_new_with_label (_("Edit"));
275   acr->remove_button = gtk_button_new_with_label (_("Remove"));
276 
277   acr->get_value = NULL;
278   acr->get_value_data = NULL;
279   acr->enabled = NULL;
280   acr->update = NULL;
281 
282   gtk_widget_set_sensitive (acr->change_button, FALSE);
283   gtk_widget_set_sensitive (acr->remove_button, FALSE);
284   gtk_widget_set_sensitive (acr->add_button, FALSE);
285 
286   psppire_box_pack_start_defaults (GTK_BOX (bb), acr->add_button);
287   psppire_box_pack_start_defaults (GTK_BOX (bb), acr->change_button);
288   psppire_box_pack_start_defaults (GTK_BOX (bb), acr->remove_button);
289 
290   gtk_box_pack_start (GTK_BOX (acr), bb, FALSE, TRUE, 5);
291 
292   g_object_set (sw,
293 		"hscrollbar-policy", GTK_POLICY_NEVER,
294 		"vscrollbar-policy", GTK_POLICY_AUTOMATIC,
295 		"shadow-type", GTK_SHADOW_ETCHED_IN,
296 		NULL);
297 
298   gtk_container_add (GTK_CONTAINER (sw), GTK_WIDGET (acr->tv));
299 
300   gtk_box_pack_start (GTK_BOX (acr), sw, TRUE, TRUE, 5);
301 
302 
303   g_signal_connect_swapped (acr->add_button, "clicked",
304 			    G_CALLBACK (on_add_button_clicked), acr);
305   g_signal_connect_swapped (acr->change_button, "clicked",
306 			    G_CALLBACK (on_change_button_clicked), acr);
307   g_signal_connect_swapped (acr->remove_button, "clicked",
308 			    G_CALLBACK (on_remove_button_clicked), acr);
309 
310   gtk_widget_show_all (bb);
311 
312 
313   g_object_set (acr->tv, "headers-visible", FALSE, NULL);
314 
315   acr->list_store = NULL;
316 
317   psppire_acr_set_model (acr, acr->list_store);
318 
319   acr->selection = gtk_tree_view_get_selection (acr->tv);
320 
321   g_signal_connect (acr->selection, "changed", G_CALLBACK (on_select), acr);
322 
323   gtk_widget_set_sensitive (GTK_WIDGET (acr), FALSE);
324 
325   gtk_widget_show_all (sw);
326 
327   {
328     GtkCellRenderer *renderer = gtk_cell_renderer_text_new ();
329     GtkTreeViewColumn *column =
330       gtk_tree_view_column_new_with_attributes ("value",
331 						renderer,
332 						"text", 0,
333 						NULL);
334 
335     gtk_tree_view_append_column (acr->tv, column);
336   }
337 
338 }
339 
340 
341 GtkWidget*
psppire_acr_new(void)342 psppire_acr_new (void)
343 {
344   return GTK_WIDGET (g_object_new (psppire_acr_get_type (), NULL));
345 }
346 
347 
348 
349 /* Set the widget's treemodel to LISTSTORE.  LISTSTORE ownership is not
350    transferred. */
351 void
psppire_acr_set_model(PsppireAcr * acr,GtkListStore * liststore)352 psppire_acr_set_model (PsppireAcr *acr, GtkListStore *liststore)
353 {
354   if (acr->list_store)
355     g_object_unref (acr->list_store);
356   if (liststore)
357     g_object_ref (liststore);
358 
359   acr->list_store = liststore;
360 
361   gtk_tree_view_set_model (GTK_TREE_VIEW (acr->tv),
362 			   GTK_TREE_MODEL (liststore));
363 
364   gtk_widget_set_sensitive (GTK_WIDGET (acr), liststore != NULL);
365 }
366 
367 
368 void
psppire_acr_set_enable_func(PsppireAcr * acr,EnabledFunc func,gpointer p)369 psppire_acr_set_enable_func (PsppireAcr *acr, EnabledFunc func, gpointer p)
370 {
371   acr->enabled = func;
372   acr->enabled_data = p;
373 }
374 
375 void
psppire_acr_set_get_value_func(PsppireAcr * acr,GetValueFunc getvalue,gpointer data)376 psppire_acr_set_get_value_func (PsppireAcr *acr,
377 				GetValueFunc getvalue, gpointer data)
378 {
379   acr->get_value_data = data;
380   acr->get_value = getvalue;
381 }
382