e0173c6adde38884cfd954820c51dac710025218
[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 static void psppire_spreadsheet_model_finalize (GObject * object);
37 static void psppire_spreadsheet_model_dispose (GObject * object);
38
39 static GObjectClass *parent_class = NULL;
40
41 static void spreadsheet_tree_model_init (GtkTreeModelIface * iface);
42
43 G_DEFINE_TYPE_WITH_CODE (PsppireSpreadsheetModel,\
44                          psppire_spreadsheet_model,\
45                          G_TYPE_OBJECT,
46                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
47                                                 spreadsheet_tree_model_init))
48
49 /* Properties */
50 enum
51 {
52   PROP_0,
53   PROP_SPREADSHEET
54 };
55
56
57 static void
58 psppire_spreadsheet_model_set_property (GObject * object,
59                                         guint prop_id,
60                                         const GValue * value,
61                                         GParamSpec * pspec)
62 {
63   PsppireSpreadsheetModel *spreadsheetModel =
64     PSPPIRE_SPREADSHEET_MODEL (object);
65
66   switch (prop_id)
67     {
68     case PROP_SPREADSHEET:
69       spreadsheetModel->spreadsheet = g_value_get_pointer (value);
70       spreadsheet_ref (spreadsheetModel->spreadsheet);
71       break;
72     default:
73       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
74       break;
75     };
76 }
77
78
79 static void
80 psppire_spreadsheet_model_dispose (GObject * object)
81 {
82   PsppireSpreadsheetModel *spreadsheetModel = PSPPIRE_SPREADSHEET_MODEL (object);
83
84   if (!spreadsheetModel->dispose_has_run)
85     {
86       spreadsheet_unref (spreadsheetModel->spreadsheet);
87
88       spreadsheetModel->dispose_has_run = TRUE;
89     }
90 }
91
92 static void
93 psppire_spreadsheet_model_finalize (GObject * object)
94 {
95   //  PsppireSpreadsheetModel *spreadsheetModel = PSPPIRE_SPREADSHEET_MODEL (object);
96 }
97
98 static void
99 psppire_spreadsheet_model_class_init (PsppireSpreadsheetModelClass * class)
100 {
101   GObjectClass *object_class = G_OBJECT_CLASS (class);
102
103   GParamSpec *spreadsheet_spec = g_param_spec_pointer ("spreadsheet",
104                                                        "Spreadsheet",
105                                                        "The spreadsheet that this model represents",
106                                                        G_PARAM_CONSTRUCT_ONLY
107                                                        | G_PARAM_WRITABLE);
108
109   parent_class = g_type_class_peek_parent (class);
110
111
112   object_class->set_property = psppire_spreadsheet_model_set_property;
113
114   g_object_class_install_property (object_class,
115                                    PROP_SPREADSHEET, spreadsheet_spec);
116
117   object_class->finalize = psppire_spreadsheet_model_finalize;
118   object_class->dispose = psppire_spreadsheet_model_dispose;
119 }
120
121
122 static void
123 psppire_spreadsheet_model_init (PsppireSpreadsheetModel * spreadsheetModel)
124 {
125   spreadsheetModel->dispose_has_run = FALSE;
126   spreadsheetModel->stamp = g_random_int ();
127 }
128
129
130 GtkTreeModel *
131 psppire_spreadsheet_model_new (struct spreadsheet *sp)
132 {
133   return g_object_new (psppire_spreadsheet_model_get_type (),
134                        "spreadsheet", sp, NULL);
135 }
136
137
138 \f
139
140 static gint
141 tree_model_n_columns (GtkTreeModel * model)
142 {
143   return PSPPIRE_SPREADSHEET_MODEL_N_COLS;
144 }
145
146 static GtkTreeModelFlags
147 tree_model_get_flags (GtkTreeModel * model)
148 {
149   g_return_val_if_fail (PSPPIRE_IS_SPREADSHEET_MODEL (model),
150                         (GtkTreeModelFlags) 0);
151
152   return GTK_TREE_MODEL_LIST_ONLY;
153 }
154
155 static GType
156 tree_model_column_type (GtkTreeModel * model, gint index)
157 {
158   g_return_val_if_fail (PSPPIRE_IS_SPREADSHEET_MODEL (model), (GType) 0);
159   g_return_val_if_fail (index < PSPPIRE_SPREADSHEET_MODEL_N_COLS, (GType) 0);
160
161   return G_TYPE_STRING;
162 }
163
164
165 static gboolean
166 tree_model_get_iter (GtkTreeModel * model, GtkTreeIter * iter,
167                      GtkTreePath * path)
168 {
169   PsppireSpreadsheetModel *spreadsheetModel =
170     PSPPIRE_SPREADSHEET_MODEL (model);
171   gint *indices, depth;
172   gint n;
173
174   g_return_val_if_fail (path, FALSE);
175
176   depth = gtk_tree_path_get_depth (path);
177
178   g_return_val_if_fail (depth == 1, FALSE);
179
180   indices = gtk_tree_path_get_indices (path);
181
182   n = indices[0];
183
184   iter->stamp = spreadsheetModel->stamp;
185   iter->user_data = (gpointer) (intptr_t) n;
186
187   return TRUE;
188 }
189
190 static gboolean
191 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
192 {
193   PsppireSpreadsheetModel *spreadsheetModel = PSPPIRE_SPREADSHEET_MODEL (model);
194   g_assert (iter);
195   g_return_val_if_fail (iter->stamp == spreadsheetModel->stamp, FALSE);
196
197   if ((intptr_t) iter->user_data >= spreadsheetModel->spreadsheet->n_sheets - 1)
198     {
199       iter->user_data = NULL;
200       iter->stamp = 0;
201       return FALSE;
202     }
203
204   iter->user_data = (void *) ((intptr_t)(iter->user_data) + 1);
205
206   return TRUE;
207 }
208
209
210 static void
211 tree_model_get_value (GtkTreeModel * model, GtkTreeIter * iter,
212                       gint column, GValue * value)
213 {
214   PsppireSpreadsheetModel *spreadsheetModel =
215     PSPPIRE_SPREADSHEET_MODEL (model);
216   g_return_if_fail (column < PSPPIRE_SPREADSHEET_MODEL_N_COLS);
217   g_return_if_fail (iter->stamp == spreadsheetModel->stamp);
218
219   g_value_init (value, G_TYPE_STRING);
220   switch (column)
221     {
222     case PSPPIRE_SPREADSHEET_MODEL_COL_NAME:
223       {
224         const char *x =
225           spreadsheet_get_sheet_name (spreadsheetModel->spreadsheet,
226                                       (intptr_t) iter->user_data);
227
228         g_value_set_string (value, x);
229       }
230       break;
231     case PSPPIRE_SPREADSHEET_MODEL_COL_RANGE:
232       {
233         char *x =
234           spreadsheet_get_sheet_range (spreadsheetModel->spreadsheet,
235                                        (intptr_t) iter->user_data);
236
237         g_value_set_string (value, x ? x : _("(empty)"));
238         g_free (x);
239       }
240       break;
241     default:
242       g_error ("%s:%d Invalid column in spreadsheet model",
243                __FILE__, __LINE__);
244       break;
245     }
246 }
247
248 static gboolean
249 tree_model_nth_child (GtkTreeModel * model, GtkTreeIter * iter,
250                       GtkTreeIter * parent, gint n)
251 {
252   PsppireSpreadsheetModel *spreadsheetModel =
253     PSPPIRE_SPREADSHEET_MODEL (model);
254
255   if (parent)
256     return FALSE;
257
258   if (n >= spreadsheetModel->spreadsheet->n_sheets)
259     return FALSE;
260
261   iter->stamp = spreadsheetModel->stamp;
262   iter->user_data = (gpointer) (intptr_t) n;
263
264   return TRUE;
265 }
266
267 static gint
268 tree_model_n_children (GtkTreeModel * model, GtkTreeIter * iter)
269 {
270   PsppireSpreadsheetModel *spreadsheetModel =
271     PSPPIRE_SPREADSHEET_MODEL (model);
272
273   if (iter == NULL)
274     return spreadsheetModel->spreadsheet->n_sheets;
275
276   return 0;
277 }
278
279 static gboolean
280 tree_model_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter)
281 {
282   return FALSE;
283 }
284
285 static GtkTreePath *
286 tree_model_get_path (GtkTreeModel * model, GtkTreeIter * iter)
287 {
288   PsppireSpreadsheetModel *spreadsheetModel =
289     PSPPIRE_SPREADSHEET_MODEL (model);
290   GtkTreePath *path;
291   gint index = (intptr_t) iter->user_data;
292
293   g_return_val_if_fail (iter->stamp == spreadsheetModel->stamp, NULL);
294
295   path = gtk_tree_path_new ();
296
297   gtk_tree_path_append_index (path, index);
298
299   return path;
300 }
301
302
303 static gboolean
304 tree_model_children (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent)
305 {
306   PsppireSpreadsheetModel *spreadsheetModel = PSPPIRE_SPREADSHEET_MODEL (model);
307
308   if (parent != NULL)
309     return FALSE;
310
311   iter->stamp = spreadsheetModel->stamp;
312   iter->user_data = 0;
313
314   return TRUE;
315 }
316
317
318
319 static void
320 spreadsheet_tree_model_init (GtkTreeModelIface * iface)
321 {
322   iface->get_flags = tree_model_get_flags;
323   iface->get_n_columns = tree_model_n_columns;
324   iface->get_column_type = tree_model_column_type;
325   iface->get_iter = tree_model_get_iter;
326   iface->iter_next = tree_model_iter_next;
327   iface->get_value = tree_model_get_value;
328
329   iface->iter_children = tree_model_children;
330   iface->iter_parent = NULL;
331
332   iface->get_path = tree_model_get_path;
333   iface->iter_has_child = tree_model_iter_has_child;
334   iface->iter_n_children = tree_model_n_children;
335   iface->iter_nth_child = tree_model_nth_child;
336 }