Allow adding new variables in the var sheet
[pspp] / src / ui / gui / psppire-spreadsheet-model.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2013, 2014  Free Software Foundation
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 /* This file implements a GtkTreeModel.  It allows GtkComboBox and 
18    GtkTreeView to display the names and non-empty cell ranges of the
19    sheets aka "Tables" of spreadsheet files.
20    It doesn't take any notice of the spreadsheet data itself.
21 */
22
23 #include <config.h>
24 #include <glib.h>
25
26 #include <stdint.h>
27
28 #include <gettext.h>
29 #define _(msgid) gettext (msgid)
30 #define N_(msgid) msgid
31
32
33 #include "psppire-spreadsheet-model.h"
34 #include "data/spreadsheet-reader.h"
35
36
37 static void psppire_spreadsheet_model_init (PsppireSpreadsheetModel *
38                                             spreadsheetModel);
39 static void psppire_spreadsheet_model_class_init (PsppireSpreadsheetModelClass
40                                                   * class);
41
42 static void psppire_spreadsheet_model_finalize (GObject * object);
43 static void psppire_spreadsheet_model_dispose (GObject * object);
44
45 static GObjectClass *parent_class = NULL;
46
47
48 static void spreadsheet_tree_model_init (GtkTreeModelIface * iface);
49
50
51 GType
52 psppire_spreadsheet_model_get_type (void)
53 {
54   static GType object_type = 0;
55
56   if (!object_type)
57     {
58       static const GTypeInfo spreadsheet_model_info = {
59         sizeof (PsppireSpreadsheetModelClass),
60         NULL,                   /* base_init */
61         NULL,                   /* base_finalize */
62         (GClassInitFunc) psppire_spreadsheet_model_class_init,
63         NULL,                   /* class_finalize */
64         NULL,                   /* class_data */
65         sizeof (PsppireSpreadsheetModel),
66         0,
67         (GInstanceInitFunc) psppire_spreadsheet_model_init,
68       };
69
70       static const GInterfaceInfo tree_model_info = {
71         (GInterfaceInitFunc) spreadsheet_tree_model_init,
72         NULL,
73         NULL
74       };
75
76       object_type = g_type_register_static (G_TYPE_OBJECT,
77                                             "PsppireSpreadsheetModel",
78                                             &spreadsheet_model_info, 0);
79
80       g_type_add_interface_static (object_type, GTK_TYPE_TREE_MODEL,
81                                    &tree_model_info);
82     }
83
84   return object_type;
85 }
86
87
88 /* Properties */
89 enum
90 {
91   PROP_0,
92   PROP_SPREADSHEET
93 };
94
95
96 static void
97 psppire_spreadsheet_model_set_property (GObject * object,
98                                         guint prop_id,
99                                         const GValue * value,
100                                         GParamSpec * pspec)
101 {
102   PsppireSpreadsheetModel *spreadsheetModel = 
103     PSPPIRE_SPREADSHEET_MODEL (object);
104
105   switch (prop_id)
106     {
107     case PROP_SPREADSHEET:
108       spreadsheetModel->spreadsheet = g_value_get_pointer (value);
109       spreadsheet_ref (spreadsheetModel->spreadsheet);
110       break;
111     default:
112       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
113       break;
114     };
115 }
116
117
118 static void
119 psppire_spreadsheet_model_dispose (GObject * object)
120 {
121   PsppireSpreadsheetModel *spreadsheetModel = PSPPIRE_SPREADSHEET_MODEL (object);
122
123   if (!spreadsheetModel->dispose_has_run)
124     {
125       spreadsheet_unref (spreadsheetModel->spreadsheet);
126
127       spreadsheetModel->dispose_has_run = TRUE;
128     }
129 }
130
131 static void
132 psppire_spreadsheet_model_finalize (GObject * object)
133 {
134   //  PsppireSpreadsheetModel *spreadsheetModel = PSPPIRE_SPREADSHEET_MODEL (object);
135 }
136
137 static void
138 psppire_spreadsheet_model_class_init (PsppireSpreadsheetModelClass * class)
139 {
140   GObjectClass *object_class;
141
142   GParamSpec *spreadsheet_spec = g_param_spec_pointer ("spreadsheet",
143                                                        "Spreadsheet",
144                                                        "The spreadsheet that this model represents",
145                                                        G_PARAM_CONSTRUCT_ONLY
146                                                        | G_PARAM_WRITABLE);
147
148   parent_class = g_type_class_peek_parent (class);
149   object_class = (GObjectClass *) class;
150
151   object_class->set_property = psppire_spreadsheet_model_set_property;
152
153   g_object_class_install_property (object_class,
154                                    PROP_SPREADSHEET, spreadsheet_spec);
155
156   object_class->finalize = psppire_spreadsheet_model_finalize;
157   object_class->dispose = psppire_spreadsheet_model_dispose;
158 }
159
160
161 static void
162 psppire_spreadsheet_model_init (PsppireSpreadsheetModel * spreadsheetModel)
163 {
164   spreadsheetModel->dispose_has_run = FALSE;
165   spreadsheetModel->stamp = g_random_int ();
166 }
167
168
169 GtkTreeModel *
170 psppire_spreadsheet_model_new (struct spreadsheet *sp)
171 {
172   return g_object_new (psppire_spreadsheet_model_get_type (),
173                        "spreadsheet", sp, NULL);
174 }
175
176
177 \f
178
179 static gint
180 tree_model_n_columns (GtkTreeModel * model)
181 {
182   return PSPPIRE_SPREADSHEET_MODEL_N_COLS;
183 }
184
185 static GtkTreeModelFlags
186 tree_model_get_flags (GtkTreeModel * model)
187 {
188   g_return_val_if_fail (PSPPIRE_IS_SPREADSHEET_MODEL (model),
189                         (GtkTreeModelFlags) 0);
190
191   return GTK_TREE_MODEL_LIST_ONLY;
192 }
193
194 static GType
195 tree_model_column_type (GtkTreeModel * model, gint index)
196 {
197   g_return_val_if_fail (PSPPIRE_IS_SPREADSHEET_MODEL (model), (GType) 0);
198   g_return_val_if_fail (index < PSPPIRE_SPREADSHEET_MODEL_N_COLS, (GType) 0);
199
200   return G_TYPE_STRING;
201 }
202
203
204 static gboolean
205 tree_model_get_iter (GtkTreeModel * model, GtkTreeIter * iter,
206                      GtkTreePath * path)
207 {
208   PsppireSpreadsheetModel *spreadsheetModel =
209     PSPPIRE_SPREADSHEET_MODEL (model);
210   gint *indices, depth;
211   gint n;
212
213   g_return_val_if_fail (path, FALSE);
214
215   depth = gtk_tree_path_get_depth (path);
216
217   g_return_val_if_fail (depth == 1, FALSE);
218
219   indices = gtk_tree_path_get_indices (path);
220
221   n = indices[0];
222
223   iter->stamp = spreadsheetModel->stamp;
224   iter->user_data = (gpointer) (intptr_t) n;
225
226   return TRUE;
227 }
228
229 static gboolean
230 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
231 {
232   PsppireSpreadsheetModel *spreadsheetModel =
233     PSPPIRE_SPREADSHEET_MODEL (model);
234   g_return_val_if_fail (iter->stamp == spreadsheetModel->stamp, FALSE);
235
236   if (iter == NULL)
237     return FALSE;
238
239   if ((intptr_t) iter->user_data >= spreadsheetModel->spreadsheet->n_sheets - 1)
240     {
241       iter->user_data = NULL;
242       iter->stamp = 0;
243       return FALSE;
244     }
245
246   iter->user_data++;
247
248   return TRUE;
249 }
250
251
252 static void
253 tree_model_get_value (GtkTreeModel * model, GtkTreeIter * iter,
254                       gint column, GValue * value)
255 {
256   PsppireSpreadsheetModel *spreadsheetModel =
257     PSPPIRE_SPREADSHEET_MODEL (model);
258   g_return_if_fail (column < PSPPIRE_SPREADSHEET_MODEL_N_COLS);
259   g_return_if_fail (iter->stamp == spreadsheetModel->stamp);
260
261   g_value_init (value, G_TYPE_STRING);
262   switch (column)
263     {
264     case PSPPIRE_SPREADSHEET_MODEL_COL_NAME:
265       {
266         const char *x =
267           spreadsheet_get_sheet_name (spreadsheetModel->spreadsheet,
268                                       (intptr_t) iter->user_data);
269         
270         g_value_set_string (value, x);
271       }
272       break;
273     case PSPPIRE_SPREADSHEET_MODEL_COL_RANGE:
274       {
275         char *x =
276           spreadsheet_get_sheet_range (spreadsheetModel->spreadsheet,
277                                        (intptr_t) iter->user_data);
278
279         g_value_set_string (value, x ? x : _("(empty)"));
280         g_free (x);
281       }
282       break;
283     default:
284       g_error ("%s:%d Invalid column in spreadsheet model",
285                __FILE__, __LINE__);
286       break;
287     }
288 }
289
290 static gboolean
291 tree_model_nth_child (GtkTreeModel * model, GtkTreeIter * iter,
292                       GtkTreeIter * parent, gint n)
293 {
294   PsppireSpreadsheetModel *spreadsheetModel =
295     PSPPIRE_SPREADSHEET_MODEL (model);
296
297   if (parent)
298     return FALSE;
299
300   if (n >= spreadsheetModel->spreadsheet->n_sheets)
301     return FALSE;
302
303   iter->stamp = spreadsheetModel->stamp;
304   iter->user_data = (gpointer) (intptr_t) n;
305
306   return TRUE;
307 }
308
309 static gint
310 tree_model_n_children (GtkTreeModel * model, GtkTreeIter * iter)
311 {
312   PsppireSpreadsheetModel *spreadsheetModel =
313     PSPPIRE_SPREADSHEET_MODEL (model);
314
315   if (iter == NULL)
316     return spreadsheetModel->spreadsheet->n_sheets;
317
318   return 0;
319 }
320
321 static gboolean
322 tree_model_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter)
323 {
324   return FALSE;
325 }
326
327 static GtkTreePath *
328 tree_model_get_path (GtkTreeModel * model, GtkTreeIter * iter)
329 {
330   PsppireSpreadsheetModel *spreadsheetModel =
331     PSPPIRE_SPREADSHEET_MODEL (model);
332   GtkTreePath *path;
333   gint index = (intptr_t) iter->user_data;
334
335   g_return_val_if_fail (iter->stamp == spreadsheetModel->stamp, NULL);
336
337   path = gtk_tree_path_new ();
338
339   gtk_tree_path_append_index (path, index);
340
341   return path;
342 }
343
344
345 static gboolean
346 tree_model_children (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent)
347 {
348   PsppireSpreadsheetModel *spreadsheetModel = PSPPIRE_SPREADSHEET_MODEL (model);
349
350   if (parent != NULL)
351     return FALSE;
352
353   iter->stamp = spreadsheetModel->stamp;
354   iter->user_data = 0;
355     
356   return TRUE;
357 }
358
359
360
361 static void
362 spreadsheet_tree_model_init (GtkTreeModelIface * iface)
363 {
364   iface->get_flags = tree_model_get_flags;
365   iface->get_n_columns = tree_model_n_columns;
366   iface->get_column_type = tree_model_column_type;
367   iface->get_iter = tree_model_get_iter;
368   iface->iter_next = tree_model_iter_next;
369   iface->get_value = tree_model_get_value;
370
371   iface->iter_children = tree_model_children;
372   iface->iter_parent = NULL;
373
374   iface->get_path = tree_model_get_path;
375   iface->iter_has_child = tree_model_iter_has_child;
376   iface->iter_n_children = tree_model_n_children;
377   iface->iter_nth_child = tree_model_nth_child;
378 }