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