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