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