pspp: Make a signal that indicates a bug re-raise that signal to exit.
[pspp] / src / ui / gui / sheet / psppire-sheetmodel.c
1 /* PsppireSheetModel --- an abstract model for the PsppireSheet widget.
2    Copyright (C) 2006, 2008 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
19 #include <glib.h>
20 #include "psppire-sheetmodel.h"
21 #include <ui/gui/psppire-marshal.h>
22
23 enum {
24   RANGE_CHANGED,
25   ROWS_INSERTED,
26   ROWS_DELETED,
27   COLUMNS_INSERTED,
28   COLUMNS_DELETED,
29   LAST_SIGNAL
30 };
31
32 static guint sheet_model_signals[LAST_SIGNAL] = { 0 };
33
34
35 static void      psppire_sheet_model_base_init   (gpointer           g_class);
36
37
38 GType
39 psppire_sheet_model_get_type (void)
40 {
41   static GType sheet_model_type = 0;
42
43   if (! sheet_model_type)
44     {
45       static const GTypeInfo sheet_model_info =
46       {
47         sizeof (PsppireSheetModelIface), /* class_size */
48         psppire_sheet_model_base_init,   /* base_init */
49         NULL,           /* base_finalize */
50         NULL,
51         NULL,           /* class_finalize */
52         NULL,           /* class_data */
53         0,
54         0,              /* n_preallocs */
55         NULL
56       };
57
58       sheet_model_type =
59         g_type_register_static (G_TYPE_INTERFACE, "PsppireSheetModel",
60                                 &sheet_model_info, 0);
61
62       g_type_interface_add_prerequisite (sheet_model_type, G_TYPE_OBJECT);
63     }
64
65   return sheet_model_type;
66 }
67
68 static void
69 psppire_sheet_model_base_init (gpointer g_class)
70 {
71   static gboolean initialized = FALSE;
72
73   if (! initialized)
74     {
75       sheet_model_signals[RANGE_CHANGED] =
76         g_signal_new ("range_changed",
77                       PSPPIRE_TYPE_SHEET_MODEL,
78                       G_SIGNAL_RUN_LAST,
79                       G_STRUCT_OFFSET (PsppireSheetModelIface, range_changed),
80                       NULL, NULL,
81                       psppire_marshal_VOID__INT_INT_INT_INT,
82                       G_TYPE_NONE, 4,
83                       G_TYPE_INT,
84                       G_TYPE_INT,
85                       G_TYPE_INT,
86                       G_TYPE_INT);
87
88
89
90       sheet_model_signals[ROWS_INSERTED] =
91         g_signal_new ("rows_inserted",
92                       PSPPIRE_TYPE_SHEET_MODEL,
93                       G_SIGNAL_RUN_LAST,
94                       G_STRUCT_OFFSET (PsppireSheetModelIface, rows_inserted),
95                       NULL, NULL,
96                       psppire_marshal_VOID__INT_INT,
97                       G_TYPE_NONE, 2,
98                       G_TYPE_INT,
99                       G_TYPE_INT);
100
101
102       sheet_model_signals[ROWS_DELETED] =
103         g_signal_new ("rows_deleted",
104                       PSPPIRE_TYPE_SHEET_MODEL,
105                       G_SIGNAL_RUN_LAST,
106                       G_STRUCT_OFFSET (PsppireSheetModelIface, rows_deleted),
107                       NULL, NULL,
108                       psppire_marshal_VOID__INT_INT,
109                       G_TYPE_NONE, 2,
110                       G_TYPE_INT,
111                       G_TYPE_INT);
112
113       sheet_model_signals[COLUMNS_INSERTED] =
114         g_signal_new ("columns_inserted",
115                       PSPPIRE_TYPE_SHEET_MODEL,
116                       G_SIGNAL_RUN_LAST,
117                       G_STRUCT_OFFSET (PsppireSheetModelIface, columns_inserted),
118                       NULL, NULL,
119                       psppire_marshal_VOID__INT_INT,
120                       G_TYPE_NONE, 2,
121                       G_TYPE_INT,
122                       G_TYPE_INT);
123
124
125       sheet_model_signals[COLUMNS_DELETED] =
126         g_signal_new ("columns_deleted",
127                       PSPPIRE_TYPE_SHEET_MODEL,
128                       G_SIGNAL_RUN_LAST,
129                       G_STRUCT_OFFSET (PsppireSheetModelIface, columns_deleted),
130                       NULL, NULL,
131                       psppire_marshal_VOID__INT_INT,
132                       G_TYPE_NONE, 2,
133                       G_TYPE_INT,
134                       G_TYPE_INT);
135
136
137       initialized = TRUE;
138     }
139 }
140
141
142 /**
143  * psppire_sheet_model_free_strings
144  * @sheet_model: A #PsppireSheetModel
145  *
146  * Returns: True if strings obtained with get_string should be freed by the
147  * sheet when no longer required.
148  **/
149 gboolean
150 psppire_sheet_model_free_strings (const PsppireSheetModel *sheet_model)
151 {
152   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model), FALSE);
153
154   return PSPPIRE_SHEET_MODEL_GET_IFACE (sheet_model)->free_strings;
155 }
156
157
158 /**
159  * psppire_sheet_model_get_string:
160  * @sheet_model: A #PsppireSheetModel
161  * @row: The row of the cell to be retrieved.
162  * @column: The column of the cell to be retrieved.
163  *
164  * Retrieves the datum at location ROW, COLUMN in the form of a string.
165  * Returns: The string representation of the datum, or NULL on error.
166  **/
167 gchar *
168 psppire_sheet_model_get_string (const PsppireSheetModel *sheet_model,
169                           glong row, glong column)
170 {
171   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model), 0);
172
173   g_assert (PSPPIRE_SHEET_MODEL_GET_IFACE (sheet_model)->get_string);
174
175   return (PSPPIRE_SHEET_MODEL_GET_IFACE (sheet_model)->get_string) (sheet_model, row, column);
176 }
177
178 /**
179  * psppire_sheet_model_set_string
180  * @sheet_model: A #PsppireSheetModel
181  * @text: The text describing the datum to be set.
182  * @row: The row of the cell to be cleared.
183  * @column: The column of the cell to be cleared.
184  *
185  * Sets the datum at a location from a string.
186  * Returns: TRUE if the datum was changed, FALSE otherwise.
187  **/
188 gboolean
189 psppire_sheet_model_set_string      (PsppireSheetModel *sheet_model,
190                                  const gchar *text,
191                                  glong row, glong column)
192 {
193   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model), FALSE);
194
195   g_assert (PSPPIRE_SHEET_MODEL_GET_IFACE (sheet_model)->set_string);
196
197   return PSPPIRE_SHEET_MODEL_GET_IFACE (sheet_model)->set_string (sheet_model,
198                                                             text, row, column);
199 }
200
201
202
203 /**
204  * psppire_sheet_model_datum_clear:
205  * @sheet_model: A #PsppireSheetModel
206  * @row: The row of the cell to be cleared.
207  * @column: The column of the cell to be cleared.
208  *
209  * Called when the datum at a location is to be cleared.
210  * Returns: TRUE if the datum was cleared, FALSE otherwise.
211  **/
212 gboolean
213 psppire_sheet_model_datum_clear    (PsppireSheetModel *sheet_model,
214                                 glong row, glong column)
215 {
216   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model), FALSE);
217
218   g_assert (PSPPIRE_SHEET_MODEL_GET_IFACE (sheet_model)->clear_datum);
219
220   return PSPPIRE_SHEET_MODEL_GET_IFACE (sheet_model)->clear_datum (sheet_model,
221                                                                 row, column);
222 }
223
224
225 /**
226  * psppire_sheet_model_range_changed:
227  * @sheet_model: A #PsppireSheetModel
228  * @range: The #PsppireSheetRange range of cells which have changed.
229  *
230  * Emits the "range_changed" signal on @sheet_model.
231  **/
232 void
233 psppire_sheet_model_range_changed (PsppireSheetModel *sheet_model,
234                                glong row0, glong col0,
235                                glong rowi, glong coli)
236 {
237   g_return_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model));
238
239   g_signal_emit (sheet_model, sheet_model_signals[RANGE_CHANGED], 0,
240                  row0, col0, rowi, coli);
241 }
242
243
244
245
246 /**
247  * psppire_sheet_model_rows_inserted:
248  * @sheet_model: A #PsppireSheetModel
249  * @row: The row before which the new rows should be inserted.
250  * @n_rows: The number of rows to insert.
251  *
252  * Emits the "rows_inserted" signal on @sheet_model.
253  **/
254 void
255 psppire_sheet_model_rows_inserted (PsppireSheetModel *sheet_model,
256                                glong row, glong n_rows)
257 {
258   g_return_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model));
259
260   g_signal_emit (sheet_model, sheet_model_signals[ROWS_INSERTED], 0,
261                  row, n_rows);
262 }
263
264
265 /**
266  * psppire_sheet_model_columns_inserted:
267  * @sheet_model: A #PsppireSheetModel
268  * @column: The column before which the new columns should be inserted.
269  * @n_columns: The number of columns to insert.
270  *
271  * Emits the "columns_inserted" signal on @sheet_model.
272  **/
273 void
274 psppire_sheet_model_columns_inserted (PsppireSheetModel *sheet_model,
275                                glong column, glong n_columns)
276 {
277   g_return_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model));
278
279   g_signal_emit (sheet_model, sheet_model_signals[COLUMNS_INSERTED], 0,
280                  column, n_columns);
281 }
282
283
284
285
286 /**
287  * psppire_sheet_model_rows_deleted:
288  * @sheet_model: A #PsppireSheetModel
289  * @row: The first row to be deleted.
290  * @n_rows: The number of rows to delete.
291  *
292  * Emits the "rows_deleted" signal on @sheet_model.
293  **/
294 void
295 psppire_sheet_model_rows_deleted (PsppireSheetModel *sheet_model,
296                                glong row, glong n_rows)
297 {
298   g_return_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model));
299
300   g_signal_emit (sheet_model, sheet_model_signals[ROWS_DELETED], 0,
301                  row, n_rows);
302 }
303
304
305
306 /**
307  * psppire_sheet_model_columns_deleted:
308  * @sheet_model: A #PsppireSheetModel
309  * @column: The first column to be deleted.
310  * @n_columns: The number of columns to delete.
311  *
312  * Emits the "columns_deleted" signal on @sheet_model.
313  **/
314 void
315 psppire_sheet_model_columns_deleted (PsppireSheetModel *sheet_model,
316                                glong column, glong n_columns)
317 {
318   g_return_if_fail (PSPPIRE_IS_SHEET_MODEL (sheet_model));
319
320   g_signal_emit (sheet_model, sheet_model_signals[COLUMNS_DELETED], 0,
321                  column, n_columns);
322 }
323
324
325
326
327
328 /**
329  * psppire_sheet_model_is_editable:
330  * @sheet_model: A #PsppireSheetModel
331  * @row: The row
332  * @column: The column
333  *
334  * Returns: TRUE if the cell is editable, FALSE otherwise
335  **/
336 gboolean
337 psppire_sheet_model_is_editable (const PsppireSheetModel *model,
338                              glong row, glong column)
339 {
340   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), TRUE);
341
342   if ( ! PSPPIRE_SHEET_MODEL_GET_IFACE (model)->is_editable )
343     return TRUE;
344
345   return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->is_editable (model,
346                                                           row, column);
347 }
348
349
350 /**
351  * psppire_sheet_model_get_foreground:
352  * @sheet_model: A #PsppireSheetModel
353  * @row: The row
354  * @column: The column
355  *
356  * Returns the foreground colour of the cell at @row, @column
357  * The color is unallocated.  It will be allocated by the viewing object.
358  **/
359 GdkColor *
360 psppire_sheet_model_get_foreground (const PsppireSheetModel *model,
361                                 glong row, glong column)
362 {
363   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), NULL);
364
365   if ( ! PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_foreground )
366     return NULL;
367
368   return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_foreground (model,
369                                                             row, column);
370 }
371
372 /**
373  * psppire_sheet_model_get_background:
374  * @sheet_model: A #PsppireSheetModel
375  * @row: The row
376  * @column: The column
377  *
378  * Returns the background colour of the cell at @row, @column
379  * The color is unallocated.  It will be allocated by the viewing object.
380  **/
381 GdkColor *
382 psppire_sheet_model_get_background (const PsppireSheetModel *model,
383                                 glong row, glong column)
384 {
385   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), NULL);
386
387   if ( ! PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_background )
388     return NULL;
389
390   return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_background (model,
391                                                             row, column);
392 }
393
394 /**
395  * psppire_sheet_model_get_justification:
396  * @sheet_model: A #PsppireSheetModel
397  * @row: The row
398  * @column: The column
399  *
400  * Returns the justification of the cell at @row, @column
401  * Returns: the justification, or NULL on error.
402  **/
403 const GtkJustification *
404 psppire_sheet_model_get_justification (const PsppireSheetModel *model,
405                                    glong row, glong column)
406 {
407   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), NULL);
408
409   if ( ! PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_justification)
410     return NULL;
411
412   return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_justification (model,
413                                                                row, column);
414 }
415
416
417 /**
418  * psppire_sheet_model_get_column_count:
419  * @model: A #PsppireSheetModel
420  *
421  * Returns the total number of columns represented by the model
422  **/
423 glong
424 psppire_sheet_model_get_column_count (const PsppireSheetModel *model)
425 {
426   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), -1);
427
428   return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_count (model);
429 }
430
431 /**
432  * psppire_sheet_model_get_row_count:
433  * @model: A #PsppireSheetModel
434  *
435  * Returns the total number of rows represented by the model
436  **/
437 gint
438 psppire_sheet_model_get_row_count(const PsppireSheetModel *model)
439 {
440   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), -1);
441
442   return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_count (model);
443 }
444
445 \f
446
447 /* Column related functions  */
448 gboolean
449 psppire_sheet_model_get_column_sensitivity (const PsppireSheetModel *model, gint col)
450 {
451   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), FALSE);
452
453   if ( NULL == PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_sensitivity)
454     return TRUE;
455
456   return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_sensitivity (model, col);
457 }
458
459
460 gchar *
461 psppire_sheet_model_get_column_subtitle (const PsppireSheetModel *model,
462                                    gint col)
463 {
464   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), NULL);
465   g_return_val_if_fail (col >= 0, NULL);
466
467   if ( NULL == PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_subtitle)
468     return NULL;
469
470   return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_subtitle (model, col);
471 }
472
473
474 PsppireSheetButton *
475 psppire_sheet_model_get_column_button (const PsppireSheetModel *model,
476                                  gint col)
477 {
478   PsppireSheetButton *button = psppire_sheet_button_new ();
479
480   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), NULL);
481
482   if ( PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_title)
483     button->label = PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_title (model, col);
484
485   button->overstruck = FALSE;
486
487   return button;
488 }
489
490 GtkJustification
491 psppire_sheet_model_get_column_justification (const PsppireSheetModel *model,
492                                         gint col)
493 {
494   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), GTK_JUSTIFY_LEFT);
495
496   if ( PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_justification)
497     return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_column_justification (model, col);
498
499   return GTK_JUSTIFY_LEFT;
500 }
501
502 \f
503
504 gboolean
505 psppire_sheet_model_get_row_sensitivity (const PsppireSheetModel *model, gint row)
506 {
507   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), FALSE);
508
509   if ( NULL == PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_sensitivity)
510     return TRUE;
511
512   return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_sensitivity (model, row);
513 }
514
515
516
517 gchar *
518 psppire_sheet_model_get_row_subtitle (const PsppireSheetModel *model,
519                                 gint row)
520 {
521   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), NULL);
522
523   if ( NULL == PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_subtitle)
524     return NULL;
525
526   return PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_subtitle (model, row);
527 }
528
529
530 PsppireSheetButton *
531 psppire_sheet_model_get_row_button (const PsppireSheetModel *model,
532                                  gint row)
533 {
534   PsppireSheetButton *button = psppire_sheet_button_new ();
535
536   g_return_val_if_fail (PSPPIRE_IS_SHEET_MODEL (model), NULL);
537
538   if ( PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_title)
539     button->label =
540       PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_title (model, row);
541
542   if ( PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_overstrike)
543     button->overstruck =
544       PSPPIRE_SHEET_MODEL_GET_IFACE (model)->get_row_overstrike (model, row);
545
546   return button;
547 }
548