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