work on docs
[pspp] / src / ui / gui / psppire-spreadsheet-data-model.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2020  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 #include <config.h>
18 #include <glib.h>
19
20 #include <stdint.h>
21
22 #include <ui/gui/psppire-marshal.h>
23
24 #include "psppire-spreadsheet-data-model.h"
25 #include "data/spreadsheet-reader.h"
26
27
28 static void psppire_spreadsheet_data_model_init (PsppireSpreadsheetDataModel *
29                                             spreadsheetModel);
30 static void psppire_spreadsheet_data_model_class_init (PsppireSpreadsheetDataModelClass
31                                                   * class);
32
33 static void psppire_spreadsheet_data_model_finalize (GObject * object);
34 static void psppire_spreadsheet_data_model_dispose (GObject * object);
35
36 static GObjectClass *parent_class = NULL;
37
38
39 static void spreadsheet_tree_model_init (GtkTreeModelIface * iface);
40
41 enum
42   {
43     ITEMS_CHANGED,
44     n_SIGNALS
45   };
46
47 static guint signals [n_SIGNALS];
48
49 G_DEFINE_TYPE_WITH_CODE (PsppireSpreadsheetDataModel,\
50                          psppire_spreadsheet_data_model,\
51                          G_TYPE_OBJECT,
52                          G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
53                                                 spreadsheet_tree_model_init))
54
55 /* Properties */
56 enum
57 {
58   PROP_0,
59   PROP_SPREADSHEET,
60   PROP_SHEET_NUMBER
61 };
62
63 static void
64 psppire_spreadsheet_data_model_get_property (GObject         *object,
65                                              guint            prop_id,
66                                              GValue          *value,
67                                              GParamSpec      *pspec)
68 {
69   PsppireSpreadsheetDataModel *sp = PSPPIRE_SPREADSHEET_DATA_MODEL (object);
70
71   switch (prop_id)
72     {
73     case PROP_SPREADSHEET:
74       g_value_set_pointer (value, sp->spreadsheet);
75       break;
76     case PROP_SHEET_NUMBER:
77       g_value_set_int (value, sp->sheet_number);
78       break;
79     default:
80       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
81       break;
82     };
83 }
84
85
86 static void
87 psppire_spreadsheet_data_model_set_property (GObject * object,
88                                              guint prop_id,
89                                              const GValue * value,
90                                              GParamSpec * pspec)
91 {
92   PsppireSpreadsheetDataModel *sp = PSPPIRE_SPREADSHEET_DATA_MODEL (object);
93
94   switch (prop_id)
95     {
96     case PROP_SPREADSHEET:
97       {
98       struct spreadsheet *old = sp->spreadsheet;
99       sp->spreadsheet = spreadsheet_ref (g_value_get_pointer (value));
100       if (old)
101         spreadsheet_unref (old);
102       g_signal_emit (sp, signals[ITEMS_CHANGED], 0, 0, 0, 0);
103       }
104       break;
105     case PROP_SHEET_NUMBER:
106       sp->sheet_number = g_value_get_int (value);
107       g_signal_emit (sp, signals[ITEMS_CHANGED], 0, 0, 0, 0);
108       break;
109     default:
110       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
111       break;
112     };
113 }
114
115
116 static void
117 psppire_spreadsheet_data_model_dispose (GObject * object)
118 {
119   PsppireSpreadsheetDataModel *spreadsheetModel = PSPPIRE_SPREADSHEET_DATA_MODEL (object);
120
121   if (spreadsheetModel->dispose_has_run)
122     return;
123
124   spreadsheetModel->dispose_has_run = TRUE;
125
126   spreadsheet_unref (spreadsheetModel->spreadsheet);
127   /* Chain up to the parent class */
128   G_OBJECT_CLASS (parent_class)->dispose (object);
129 }
130
131 static void
132 psppire_spreadsheet_data_model_finalize (GObject * object)
133 {
134   //  PsppireSpreadsheetDataModel *spreadsheetModel = PSPPIRE_SPREADSHEET_DATA_MODEL (object);
135 }
136
137 static void
138 psppire_spreadsheet_data_model_class_init (PsppireSpreadsheetDataModelClass * 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
149   GParamSpec *sheet_number_spec = g_param_spec_int ("sheet-number",
150                                                     "Sheet Number",
151                                                     "The number of the sheet",
152                                                     0, G_MAXINT,
153                                                     0,
154                                                     G_PARAM_READABLE | G_PARAM_WRITABLE);
155
156   parent_class = g_type_class_peek_parent (class);
157   object_class = (GObjectClass *) class;
158
159   signals [ITEMS_CHANGED] =
160     g_signal_new ("items-changed",
161                   G_TYPE_FROM_CLASS (class),
162                   G_SIGNAL_RUN_FIRST,
163                   0,
164                   NULL, NULL,
165                   psppire_marshal_VOID__UINT_UINT_UINT,
166                   G_TYPE_NONE,
167                   3,
168                   G_TYPE_UINT,  /* Index of the start of the change */
169                   G_TYPE_UINT,  /* The number of items deleted */
170                   G_TYPE_UINT); /* The number of items inserted */
171
172
173
174   object_class->set_property = psppire_spreadsheet_data_model_set_property;
175   object_class->get_property = psppire_spreadsheet_data_model_get_property;
176
177   g_object_class_install_property (object_class,
178                                    PROP_SPREADSHEET, spreadsheet_spec);
179
180   g_object_class_install_property (object_class,
181                                    PROP_SHEET_NUMBER, sheet_number_spec);
182
183   object_class->finalize = psppire_spreadsheet_data_model_finalize;
184   object_class->dispose = psppire_spreadsheet_data_model_dispose;
185 }
186
187
188 static void
189 psppire_spreadsheet_data_model_init (PsppireSpreadsheetDataModel * spreadsheetModel)
190 {
191   spreadsheetModel->dispose_has_run = FALSE;
192   spreadsheetModel->stamp = g_random_int ();
193 }
194
195
196 GtkTreeModel *
197 psppire_spreadsheet_data_model_new (struct spreadsheet *sp, gint sheet_number)
198 {
199   return g_object_new (psppire_spreadsheet_data_model_get_type (),
200                        "spreadsheet", sp,
201                        "sheet-number", sheet_number,
202                        NULL);
203 }
204
205 \f
206
207 static gint
208 tree_model_n_columns (GtkTreeModel *model)
209 {
210   PsppireSpreadsheetDataModel *sp = PSPPIRE_SPREADSHEET_DATA_MODEL (model);
211
212   return spreadsheet_get_sheet_n_columns (sp->spreadsheet, sp->sheet_number);
213 }
214
215 static GtkTreeModelFlags
216 tree_model_get_flags (GtkTreeModel * model)
217 {
218   g_return_val_if_fail (PSPPIRE_IS_SPREADSHEET_DATA_MODEL (model),
219                         (GtkTreeModelFlags) 0);
220
221   return GTK_TREE_MODEL_LIST_ONLY;
222 }
223
224 static GType
225 tree_model_column_type (GtkTreeModel * model, gint index)
226 {
227   g_print ("%s:%d %p\n", __FILE__, __LINE__, model);
228   g_return_val_if_fail (PSPPIRE_IS_SPREADSHEET_DATA_MODEL (model), (GType) 0);
229
230   return G_TYPE_STRING;
231 }
232
233
234 static gboolean
235 tree_model_get_iter (GtkTreeModel * model, GtkTreeIter * iter,
236                      GtkTreePath * path)
237 {
238   g_print ("%s:%d %p\n", __FILE__, __LINE__, model);
239   PsppireSpreadsheetDataModel *spreadsheetModel =
240     PSPPIRE_SPREADSHEET_DATA_MODEL (model);
241   gint *indices, depth;
242   gint n;
243
244   g_return_val_if_fail (path, FALSE);
245
246   depth = gtk_tree_path_get_depth (path);
247
248   g_return_val_if_fail (depth == 1, FALSE);
249
250   indices = gtk_tree_path_get_indices (path);
251
252   n = indices[0];
253
254   iter->stamp = spreadsheetModel->stamp;
255   iter->user_data = (gpointer) (intptr_t) n;
256
257   return TRUE;
258 }
259
260 static gboolean
261 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter)
262 {
263   g_print ("%s:%d %p\n", __FILE__, __LINE__, model);
264   PsppireSpreadsheetDataModel *spreadsheetModel = PSPPIRE_SPREADSHEET_DATA_MODEL (model);
265   g_assert (iter);
266   g_return_val_if_fail (iter->stamp == spreadsheetModel->stamp, FALSE);
267
268
269   iter->user_data = GINT_TO_POINTER (GPOINTER_TO_INT (iter->user_data) + 1);
270
271   return TRUE;
272 }
273
274 static void
275 tree_model_get_value (GtkTreeModel *model, GtkTreeIter *iter,
276                       gint column, GValue *value)
277 {
278   PsppireSpreadsheetDataModel *sp = PSPPIRE_SPREADSHEET_DATA_MODEL (model);
279   g_return_if_fail (column >= 0);
280   g_return_if_fail (iter->stamp == sp->stamp);
281
282   gint row = GPOINTER_TO_INT (iter->user_data);
283
284   g_value_init (value, G_TYPE_STRING);
285
286   char *x = spreadsheet_get_cell (sp->spreadsheet, sp->sheet_number, row, column);
287
288   g_value_take_string (value, x);
289 }
290
291 static gboolean
292 tree_model_nth_child (GtkTreeModel *model, GtkTreeIter *iter,
293                       GtkTreeIter *parent, gint n)
294 {
295   PsppireSpreadsheetDataModel *spreadsheetModel =
296     PSPPIRE_SPREADSHEET_DATA_MODEL (model);
297
298   if (parent)
299     return FALSE;
300
301   iter->stamp = spreadsheetModel->stamp;
302   iter->user_data = GINT_TO_POINTER (n);
303
304   return TRUE;
305 }
306
307 static gint
308 tree_model_n_children (GtkTreeModel *model, GtkTreeIter *iter)
309 {
310   PsppireSpreadsheetDataModel *sp = PSPPIRE_SPREADSHEET_DATA_MODEL (model);
311
312   if (iter == NULL)
313     {
314       return spreadsheet_get_sheet_n_rows (sp->spreadsheet, sp->sheet_number);
315     }
316
317   return 0;
318 }
319
320 static gboolean
321 tree_model_iter_has_child (GtkTreeModel *model, GtkTreeIter *iter)
322 {
323   g_print ("%s:%d %p\n", __FILE__, __LINE__, model);
324   return FALSE;
325 }
326
327 static GtkTreePath *
328 tree_model_get_path (GtkTreeModel * model, GtkTreeIter * iter)
329 {
330   g_print ("%s:%d %p\n", __FILE__, __LINE__, model);
331   PsppireSpreadsheetDataModel *spreadsheetModel =
332     PSPPIRE_SPREADSHEET_DATA_MODEL (model);
333   GtkTreePath *path;
334   gint index = GPOINTER_TO_INT (iter->user_data);
335
336   g_return_val_if_fail (iter->stamp == spreadsheetModel->stamp, NULL);
337
338   path = gtk_tree_path_new ();
339
340   gtk_tree_path_append_index (path, index);
341
342   return path;
343 }
344
345
346 static gboolean
347 tree_model_children (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent)
348 {
349   g_print ("%s:%d %p\n", __FILE__, __LINE__, model);
350   PsppireSpreadsheetDataModel *spreadsheetModel = PSPPIRE_SPREADSHEET_DATA_MODEL (model);
351
352   if (parent != NULL)
353     return FALSE;
354
355   iter->stamp = spreadsheetModel->stamp;
356   iter->user_data = 0;
357
358   return TRUE;
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 }