1 /*
2   A widget to display and manipulate tabular data
3   Copyright (C) 2016, 2020  John Darrington
4 
5   This program is free software: you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation, either version 3 of the License, or
8   (at your option) any later version.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <config.h>
20 #include "ssw-sheet-single.h"
21 #include "ssw-sheet-axis.h"
22 #include "ssw-sheet-body.h"
23 #include "ssw-axis-model.h"
24 #include "ssw-cell.h"
25 
26 #define P_(X) (X)
27 
28 static AtkObject*
__ref_at(AtkTable * table,gint row,gint column)29 __ref_at (AtkTable      *table,
30           gint          row,
31           gint          column)
32 {
33   SswSheetBody *body = SSW_SHEET_BODY (SSW_SHEET_SINGLE (table)->body);
34   GString *output = g_string_new (NULL);
35 
36   ssw_sheet_body_value_to_string (body, column, row, output);
37 
38   AtkObject *o = g_object_new (SSW_TYPE_CELL,
39                                "content", output->str,
40                                NULL);
41 
42   g_string_free (output, FALSE);
43 
44   return o;
45 }
46 
47 
48 static   gint
__get_n_cols(AtkTable * table)49 __get_n_cols (AtkTable *table)
50 {
51   GtkTreeModel *model = SSW_SHEET_SINGLE (table)->data_model;
52   return gtk_tree_model_get_n_columns (model);
53 }
54 
55 static   gint
__get_n_rows(AtkTable * table)56 __get_n_rows (AtkTable *table)
57 {
58   GtkTreeModel *model = SSW_SHEET_SINGLE (table)->data_model;
59   return gtk_tree_model_iter_n_children (model, NULL);
60 }
61 
62 
63 static void
__atk_table_init(AtkTableIface * iface)64 __atk_table_init (AtkTableIface *iface)
65 {
66   iface->ref_at = __ref_at;
67   iface->get_n_columns = __get_n_cols;
68   iface->get_n_rows = __get_n_rows;
69 }
70 
71 
72 
73 G_DEFINE_TYPE_WITH_CODE (SswSheetSingle, ssw_sheet_single, GTK_TYPE_GRID,
74                          G_IMPLEMENT_INTERFACE (GTK_TYPE_SCROLLABLE, NULL)
75                          G_IMPLEMENT_INTERFACE (ATK_TYPE_TABLE, __atk_table_init)
76                          );
77 
78 
79 static void
__realize(GtkWidget * w)80 __realize (GtkWidget *w)
81 {
82   SswSheetSingle *single = SSW_SHEET_SINGLE (w);
83   SswSheet *psheet = SSW_SHEET (single->sheet);
84 
85   GTK_WIDGET_CLASS (ssw_sheet_single_parent_class)->realize (w);
86 
87   g_object_set (single->body,
88                 "sheet",     single->sheet,
89                 "expand",    TRUE,
90                 "can-focus", TRUE,
91                 NULL);
92 
93   GList focus_chain;
94   focus_chain.next = NULL;
95   focus_chain.prev = NULL;
96   focus_chain.data = single->body;
97   gtk_container_set_focus_chain (GTK_CONTAINER (single), &focus_chain);
98 
99 
100   /* The button determines the dimensions of the axes.
101      But the button is only shown on the primary sheet (sheet[0])
102      So we must force the axes on the other sheets to
103      the dimensions here. */
104 
105   GtkAllocation alloc;
106   gtk_widget_get_allocation (SSW_SHEET_SINGLE(psheet->sheet[0])->button,
107                              &alloc);
108 
109   gtk_widget_set_size_request (single->vertical_axis, alloc.width, -1);
110   gtk_widget_set_size_request (single->horizontal_axis, -1, alloc.height);
111 }
112 
113 
114 GtkWidget *
ssw_sheet_single_new(SswSheet * psheet,SswSheetAxis * haxis,SswSheetAxis * vaxis,SswRange * selection)115 ssw_sheet_single_new (SswSheet *psheet,
116                       SswSheetAxis *haxis, SswSheetAxis *vaxis, SswRange *selection)
117 {
118   GObject *sheet = g_object_new (SSW_TYPE_SHEET_SINGLE,
119                                  "sheet", psheet,
120                                  "horizontal-axis", haxis,
121                                  "vertical-axis", vaxis,
122                                  "selection", selection,
123                                  NULL);
124   return GTK_WIDGET (sheet);
125 }
126 
127 
128 enum
129   {
130    PROP_0,
131    PROP_VAXIS,
132    PROP_HAXIS,
133    PROP_VADJUSTMENT,
134    PROP_HADJUSTMENT,
135    PROP_VSCROLL_POLICY,
136    PROP_HSCROLL_POLICY,
137    PROP_DATA_MODEL,
138    PROP_SHEET,
139    PROP_SELECTION
140   };
141 
142 
143 static void
__arrange_properties(GObject * object)144 __arrange_properties (GObject *object)
145 {
146   SswSheetSingle *sheet = SSW_SHEET_SINGLE (object);
147 
148   if (sheet->horizontal_axis)
149     g_object_set (sheet->horizontal_axis, "adjustment", sheet->hadj, NULL);
150 
151   if (sheet->vertical_axis)
152     g_object_set (sheet->vertical_axis, "adjustment", sheet->vadj, NULL);
153 
154   /* Only show the button if both axes belong to "us".
155      Or to put it another way:  Don't show the button if either
156      axis is "borrowed" from another sheet. */
157   GtkWidget *vparent = sheet->vertical_axis ? gtk_widget_get_parent (sheet->vertical_axis) : NULL;
158   GtkWidget *hparent = sheet->horizontal_axis ? gtk_widget_get_parent (sheet->horizontal_axis) : NULL;
159 
160   g_object_set (sheet->button,
161                 "no-show-all", (vparent != GTK_WIDGET (sheet)) || (hparent != GTK_WIDGET (sheet)),
162                 NULL);
163 }
164 
165 static void
__set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)166 __set_property (GObject *object,
167                 guint prop_id, const GValue *value, GParamSpec *pspec)
168 {
169   SswSheetSingle *sheet = SSW_SHEET_SINGLE (object);
170   if (sheet->dispose_has_run)
171     return;
172 
173   switch (prop_id)
174     {
175     case PROP_VAXIS:
176       SSW_SHEET_SINGLE (object)->vertical_axis = g_value_get_object (value);
177       g_object_set (sheet->body, "vertical-axis", sheet->vertical_axis, NULL);
178       if (NULL == gtk_widget_get_parent (sheet->vertical_axis))
179         gtk_grid_attach (GTK_GRID (sheet), sheet->vertical_axis, 0, 1, 1, 1);
180       __arrange_properties (object);
181       break;
182     case PROP_HAXIS:
183       SSW_SHEET_SINGLE (object)->horizontal_axis = g_value_get_object (value);
184       g_object_set (sheet->body, "horizontal-axis", sheet->horizontal_axis, NULL);
185       if (NULL == gtk_widget_get_parent (sheet->horizontal_axis))
186         gtk_grid_attach (GTK_GRID (sheet), sheet->horizontal_axis, 1, 0, 1, 1);
187       __arrange_properties (object);
188       break;
189     case PROP_DATA_MODEL:
190       SSW_SHEET_SINGLE (object)->data_model = g_value_get_object (value);
191       g_object_set (SSW_SHEET_SINGLE (object)->body, "data-model",
192                     SSW_SHEET_SINGLE (object)->data_model, NULL);
193       break;
194     case PROP_HADJUSTMENT:
195       g_set_object (&SSW_SHEET_SINGLE (object)->hadj, g_value_get_object (value));
196       __arrange_properties (object);
197       break;
198     case PROP_VADJUSTMENT:
199       g_set_object (&SSW_SHEET_SINGLE (object)->vadj, g_value_get_object (value));
200       __arrange_properties (object);
201       break;
202     case PROP_VSCROLL_POLICY:
203     case PROP_HSCROLL_POLICY:
204       break;
205     case PROP_SHEET:
206       SSW_SHEET_SINGLE (object)->sheet = g_value_get_object (value);
207       break;
208     case PROP_SELECTION:
209       g_object_set (sheet->body, "selection", g_value_get_pointer (value), NULL);
210       break;
211     default:
212       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
213       break;
214     }
215   if (sheet->body && SSW_SHEET_SINGLE (object)->sheet)
216     g_object_set (sheet->body, "sheet", SSW_SHEET_SINGLE (object)->sheet, NULL);
217 }
218 
219 static void
__get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)220 __get_property (GObject * object,
221                 guint prop_id, GValue * value, GParamSpec * pspec)
222 {
223   switch (prop_id)
224     {
225     case PROP_VAXIS:
226       g_value_set_object (value, SSW_SHEET_SINGLE (object)->vertical_axis);
227       break;
228     case PROP_HAXIS:
229       g_value_set_object (value, SSW_SHEET_SINGLE (object)->horizontal_axis);
230       break;
231     case PROP_SHEET:
232       g_value_set_object (value, SSW_SHEET_SINGLE (object)->sheet);
233       break;
234     case PROP_VSCROLL_POLICY:
235     case PROP_HSCROLL_POLICY:
236       break;
237     default:
238       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
239       break;
240     }
241 }
242 
243 static void
__dispose(GObject * object)244 __dispose (GObject *object)
245 {
246   SswSheetSingle *sheet = SSW_SHEET_SINGLE (object);
247 
248   if (sheet->dispose_has_run)
249     return;
250 
251   sheet->dispose_has_run = TRUE;
252 
253   if (SSW_SHEET_SINGLE (object)->hadj)
254     g_object_unref (SSW_SHEET_SINGLE (object)->hadj);
255 
256   if (SSW_SHEET_SINGLE (object)->vadj)
257     g_object_unref (SSW_SHEET_SINGLE (object)->vadj);
258 
259   G_OBJECT_CLASS (ssw_sheet_single_parent_class)->dispose (object);
260 }
261 
262 static void
ssw_sheet_single_class_init(SswSheetSingleClass * class)263 ssw_sheet_single_class_init (SswSheetSingleClass * class)
264 {
265   GObjectClass *object_class = G_OBJECT_CLASS (class);
266   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
267 
268   GParamSpec *sheet_spec =
269     g_param_spec_object ("sheet",
270                          P_("Sheet"),
271                          P_("The Parent Sheet"),
272                          SSW_TYPE_SHEET,
273                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
274 
275 
276   GParamSpec *haxis_spec =
277     g_param_spec_object ("horizontal-axis",
278                          P_("Horizontal Axis"),
279                          P_("The Horizontal Axis"),
280                          SSW_TYPE_SHEET_AXIS,
281                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
282 
283   GParamSpec *vaxis_spec =
284     g_param_spec_object ("vertical-axis",
285                          P_("Vertical Axis"),
286                          P_("The Vertical Axis"),
287                          SSW_TYPE_SHEET_AXIS,
288                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
289 
290   GParamSpec *data_model_spec =
291     g_param_spec_object ("data-model",
292                          P_("Data Model"),
293                          P_("The model describing the contents of the data"),
294                          GTK_TYPE_TREE_MODEL,
295                          G_PARAM_READWRITE);
296 
297   GParamSpec *selection_spec =
298     g_param_spec_pointer ("selection",
299                           P_("The selection"),
300                           P_("A pointer to the current selection"),
301                           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
302 
303   object_class->dispose = __dispose;
304   widget_class->realize = __realize;
305 
306   object_class->set_property = __set_property;
307   object_class->get_property = __get_property;
308 
309   g_object_class_install_property (object_class,
310                                    PROP_SELECTION,
311                                    selection_spec);
312 
313   g_object_class_install_property (object_class,
314                                    PROP_VAXIS,
315                                    vaxis_spec);
316 
317   g_object_class_install_property (object_class,
318                                    PROP_HAXIS,
319                                    haxis_spec);
320 
321 
322   g_object_class_install_property (object_class,
323                                    PROP_DATA_MODEL,
324                                    data_model_spec);
325 
326   g_object_class_install_property (object_class,
327                                    PROP_SHEET,
328                                    sheet_spec);
329 
330   g_object_class_override_property (object_class,
331                                     PROP_VADJUSTMENT,
332                                     "vadjustment");
333 
334   g_object_class_override_property (object_class,
335                                     PROP_HADJUSTMENT,
336                                     "hadjustment");
337 
338 
339   g_object_class_override_property (object_class,
340                                     PROP_HSCROLL_POLICY,
341                                     "hscroll-policy");
342 
343   g_object_class_override_property (object_class,
344                                     PROP_VSCROLL_POLICY,
345                                     "vscroll-policy");
346 }
347 
348 static void
ssw_sheet_single_init(SswSheetSingle * sheet)349 ssw_sheet_single_init (SswSheetSingle *sheet)
350 {
351   gtk_widget_set_has_window (GTK_WIDGET (sheet), FALSE);
352 
353   sheet->vadj = NULL;
354   sheet->hadj = NULL;
355   sheet->horizontal_axis = NULL;
356   sheet->vertical_axis = NULL;
357   sheet->dispose_has_run = FALSE;
358 
359   sheet->button = gtk_button_new_with_label ("");
360   gtk_grid_attach (GTK_GRID (sheet), sheet->button, 0, 0, 1, 1);
361 
362   sheet->body = ssw_sheet_body_new (NULL);
363   gtk_grid_attach (GTK_GRID (sheet), sheet->body,
364                    1, 1, 1, 1);
365 }
366