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 <gtk/gtk.h>
21 #include "ssw-virtual-model.h"
22
23 #define P_(X) (X)
24
25 enum {ITEMS_CHANGED,
26 n_SIGNALS};
27
28 static guint signals [n_SIGNALS];
29
30 static gint
__iter_n_children(GtkTreeModel * tree_model,GtkTreeIter * iter)31 __iter_n_children (GtkTreeModel *tree_model,
32 GtkTreeIter *iter)
33 {
34 SswVirtualModel *m = SSW_VIRTUAL_MODEL (tree_model);
35 return m->rows;
36 }
37
38 static gint
__get_n_columns(GtkTreeModel * tree_model)39 __get_n_columns (GtkTreeModel *tree_model)
40 {
41 SswVirtualModel *m = SSW_VIRTUAL_MODEL (tree_model);
42
43 return m->cols;
44 }
45
46
47 static gboolean
__iter_nth_child(GtkTreeModel * tree_model,GtkTreeIter * iter,GtkTreeIter * parent,gint n)48 __iter_nth_child (GtkTreeModel *tree_model,
49 GtkTreeIter *iter,
50 GtkTreeIter *parent,
51 gint n)
52 {
53 SswVirtualModel *m = SSW_VIRTUAL_MODEL (tree_model);
54
55 g_assert (parent == NULL);
56
57 iter->stamp = m->stamp;
58 iter->user_data = GINT_TO_POINTER (n);
59 return TRUE;
60 }
61
62 static void
__get_value(GtkTreeModel * tree_model,GtkTreeIter * iter,gint column,GValue * value)63 __get_value (GtkTreeModel *tree_model,
64 GtkTreeIter *iter,
65 gint column,
66 GValue *value)
67 {
68 SswVirtualModel *m = SSW_VIRTUAL_MODEL (tree_model);
69 g_return_if_fail (iter->stamp == m->stamp);
70
71 g_value_init (value, G_TYPE_STRING);
72
73 gint row = GPOINTER_TO_INT (iter->user_data);
74 g_value_take_string (value, g_strdup_printf ("r%dc%d", row, column));
75 }
76
77 static GType
__get_type(GtkTreeModel * tree_model,gint col)78 __get_type (GtkTreeModel *tree_model,
79 gint col)
80 {
81 SswVirtualModel *m = SSW_VIRTUAL_MODEL (tree_model);
82 return G_TYPE_STRING;
83 }
84
85
86 static GtkTreePath *
__get_path(GtkTreeModel * tree_model,GtkTreeIter * iter)87 __get_path (GtkTreeModel *tree_model,
88 GtkTreeIter *iter)
89 {
90 SswVirtualModel *m = SSW_VIRTUAL_MODEL (tree_model);
91 g_return_val_if_fail (iter->stamp == m->stamp, NULL);
92 return gtk_tree_path_new_from_indices (GPOINTER_TO_INT (iter->user_data), -1);
93 }
94
95 static GtkTreeModelFlags
__get_flags(GtkTreeModel * tm)96 __get_flags (GtkTreeModel *tm)
97 {
98 return GTK_TREE_MODEL_LIST_ONLY;
99 }
100
101
102 static void
__init_iface(GtkTreeModelIface * iface)103 __init_iface (GtkTreeModelIface *iface)
104 {
105 iface->iter_n_children = __iter_n_children;
106 iface->get_n_columns = __get_n_columns;
107 iface->iter_nth_child = __iter_nth_child;
108 iface->get_value = __get_value;
109 iface->get_column_type = __get_type;
110 iface->get_path = __get_path;
111 iface->get_flags = __get_flags;
112 }
113
114 G_DEFINE_TYPE_WITH_CODE (SswVirtualModel, ssw_virtual_model, G_TYPE_OBJECT,
115 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, __init_iface));
116
117
118 static void
__finalize(GObject * obj)119 __finalize (GObject *obj)
120 {
121 SswVirtualModel *model = SSW_VIRTUAL_MODEL (obj);
122 }
123
124
125 enum
126 {
127 PROP_0,
128 PROP_COLS,
129 PROP_ROWS
130 };
131
132
133 /* GObject vfuncs {{{ */
134 static void
__set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)135 __set_property (GObject *object,
136 guint prop_id, const GValue *value, GParamSpec * pspec)
137 {
138 SswVirtualModel *m = SSW_VIRTUAL_MODEL (object);
139 gint r;
140
141 switch (prop_id)
142 {
143 case PROP_COLS:
144 m->cols = g_value_get_uint (value);
145 break;
146 case PROP_ROWS:
147 {
148 gint old_rows = m->rows;
149 gint n = g_value_get_uint (value);
150 g_return_if_fail (n >= 0);
151 m->rows = n;
152 if (old_rows != -1)
153 {
154 /* Rows inserted */
155 for (r = old_rows; r < m->rows; ++r)
156 {
157 GtkTreeIter iter;
158 gtk_tree_model_iter_nth_child (GTK_TREE_MODEL (m), &iter, NULL, r);
159 GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL(m), &iter);
160 g_signal_emit_by_name (object, "row-inserted",
161 path, &iter);
162
163 gtk_tree_path_free (path);
164 }
165 /* Rows deleted */
166 for (r = m->rows; r < old_rows; ++r)
167 {
168 GtkTreePath *path =
169 gtk_tree_path_new_from_indices (r, -1);
170
171 g_signal_emit_by_name (object, "row-deleted", path);
172
173 gtk_tree_path_free (path);
174 }
175 gint diff = (m->rows - old_rows);
176 g_signal_emit_by_name (object, "items-changed", old_rows,
177 (diff > 0) ? 0 : -diff,
178 (diff > 0) ? diff : 0);
179 }
180 }
181 break;
182 default:
183 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
184 break;
185 }
186 }
187
188 static void
__get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)189 __get_property (GObject * object,
190 guint prop_id, GValue * value, GParamSpec * pspec)
191 {
192 SswVirtualModel *m = SSW_VIRTUAL_MODEL (object);
193 switch (prop_id)
194 {
195 case PROP_COLS:
196 g_value_set_uint (value, m->cols);
197 break;
198 case PROP_ROWS:
199 g_value_set_uint (value, m->rows);
200 break;
201 default:
202 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
203 break;
204 }
205 }
206
207
208
209 static void
ssw_virtual_model_class_init(SswVirtualModelClass * class)210 ssw_virtual_model_class_init (SswVirtualModelClass *class)
211 {
212 GObjectClass *object_class = G_OBJECT_CLASS (class);
213
214 object_class->set_property = __set_property;
215 object_class->get_property = __get_property;
216
217 GParamSpec *cols_spec =
218 g_param_spec_uint ("columns",
219 P_("Columns"),
220 P_("The number of columns in the model"),
221 0, UINT_MAX, 10000,
222 G_PARAM_READWRITE);
223
224 GParamSpec *rows_spec =
225 g_param_spec_uint ("rows",
226 P_("Rows"),
227 P_("The number of rows in the model"),
228 0, UINT_MAX, 10000,
229 G_PARAM_READWRITE);
230
231
232 g_object_class_install_property (object_class,
233 PROP_COLS,
234 cols_spec);
235
236 g_object_class_install_property (object_class,
237 PROP_ROWS,
238 rows_spec);
239
240 object_class->finalize = __finalize;
241
242 signals [ITEMS_CHANGED] =
243 g_signal_new ("items-changed",
244 G_TYPE_FROM_CLASS (class),
245 G_SIGNAL_RUN_FIRST,
246 0,
247 NULL, NULL,
248 g_cclosure_marshal_generic,
249 G_TYPE_NONE,
250 3,
251 G_TYPE_UINT,
252 G_TYPE_UINT,
253 G_TYPE_UINT);
254 }
255
256 static void
ssw_virtual_model_init(SswVirtualModel * obj)257 ssw_virtual_model_init (SswVirtualModel *obj)
258 {
259 SswVirtualModel *model = SSW_VIRTUAL_MODEL (obj);
260 model->rows = -1;
261 model->cols = -1;
262 model->stamp = g_random_int ();
263 }
264
265
266 GObject *
ssw_virtual_model_new(void)267 ssw_virtual_model_new (void)
268 {
269 return g_object_new (SSW_TYPE_VIRTUAL_MODEL, NULL);
270 }
271