1 /* PSPPIRE - a graphical user interface for PSPP. 2 Copyright (C) 2013, 2014 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 /* This file implements a GtkTreeModel. It allows GtkComboBox and 18 GtkTreeView to display the names and non-empty cell ranges of the 19 sheets aka "Tables" of spreadsheet files. 20 It doesn't take any notice of the spreadsheet data itself. 21 */ 22 23 #include <config.h> 24 #include <glib.h> 25 26 #include <stdint.h> 27 28 #include <gettext.h> 29 #define _(msgid) gettext (msgid) 30 #define N_(msgid) msgid 31 32 33 #include "psppire-spreadsheet-model.h" 34 #include "data/spreadsheet-reader.h" 35 36 static void psppire_spreadsheet_model_finalize (GObject * object); 37 static void psppire_spreadsheet_model_dispose (GObject * object); 38 39 static GObjectClass *parent_class = NULL; 40 41 static void spreadsheet_tree_model_init (GtkTreeModelIface * iface); 42 43 G_DEFINE_TYPE_WITH_CODE (PsppireSpreadsheetModel,\ 44 psppire_spreadsheet_model,\ 45 G_TYPE_OBJECT, 46 G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, 47 spreadsheet_tree_model_init)) 48 49 /* Properties */ 50 enum 51 { 52 PROP_0, 53 PROP_SPREADSHEET 54 }; 55 56 57 static void 58 psppire_spreadsheet_model_set_property (GObject * object, 59 guint prop_id, 60 const GValue * value, 61 GParamSpec * pspec) 62 { 63 PsppireSpreadsheetModel *spreadsheetModel = 64 PSPPIRE_SPREADSHEET_MODEL (object); 65 66 switch (prop_id) 67 { 68 case PROP_SPREADSHEET: 69 spreadsheetModel->spreadsheet = g_value_get_pointer (value); 70 spreadsheet_ref (spreadsheetModel->spreadsheet); 71 break; 72 default: 73 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); 74 break; 75 }; 76 } 77 78 79 static void 80 psppire_spreadsheet_model_dispose (GObject * object) 81 { 82 PsppireSpreadsheetModel *spreadsheetModel = PSPPIRE_SPREADSHEET_MODEL (object); 83 84 if (!spreadsheetModel->dispose_has_run) 85 { 86 spreadsheet_unref (spreadsheetModel->spreadsheet); 87 88 spreadsheetModel->dispose_has_run = TRUE; 89 } 90 } 91 92 static void 93 psppire_spreadsheet_model_finalize (GObject * object) 94 { 95 // PsppireSpreadsheetModel *spreadsheetModel = PSPPIRE_SPREADSHEET_MODEL (object); 96 } 97 98 static void 99 psppire_spreadsheet_model_class_init (PsppireSpreadsheetModelClass * class) 100 { 101 GObjectClass *object_class = G_OBJECT_CLASS (class); 102 103 GParamSpec *spreadsheet_spec = g_param_spec_pointer ("spreadsheet", 104 "Spreadsheet", 105 "The spreadsheet that this model represents", 106 G_PARAM_CONSTRUCT_ONLY 107 | G_PARAM_WRITABLE); 108 109 parent_class = g_type_class_peek_parent (class); 110 111 112 object_class->set_property = psppire_spreadsheet_model_set_property; 113 114 g_object_class_install_property (object_class, 115 PROP_SPREADSHEET, spreadsheet_spec); 116 117 object_class->finalize = psppire_spreadsheet_model_finalize; 118 object_class->dispose = psppire_spreadsheet_model_dispose; 119 } 120 121 122 static void 123 psppire_spreadsheet_model_init (PsppireSpreadsheetModel * spreadsheetModel) 124 { 125 spreadsheetModel->dispose_has_run = FALSE; 126 spreadsheetModel->stamp = g_random_int (); 127 } 128 129 130 GtkTreeModel * 131 psppire_spreadsheet_model_new (struct spreadsheet *sp) 132 { 133 return g_object_new (psppire_spreadsheet_model_get_type (), 134 "spreadsheet", sp, NULL); 135 } 136 137 138 139 140 static gint 141 tree_model_n_columns (GtkTreeModel * model) 142 { 143 return PSPPIRE_SPREADSHEET_MODEL_N_COLS; 144 } 145 146 static GtkTreeModelFlags 147 tree_model_get_flags (GtkTreeModel * model) 148 { 149 g_return_val_if_fail (PSPPIRE_IS_SPREADSHEET_MODEL (model), 150 (GtkTreeModelFlags) 0); 151 152 return GTK_TREE_MODEL_LIST_ONLY; 153 } 154 155 static GType 156 tree_model_column_type (GtkTreeModel * model, gint index) 157 { 158 g_return_val_if_fail (PSPPIRE_IS_SPREADSHEET_MODEL (model), (GType) 0); 159 g_return_val_if_fail (index < PSPPIRE_SPREADSHEET_MODEL_N_COLS, (GType) 0); 160 161 return G_TYPE_STRING; 162 } 163 164 165 static gboolean 166 tree_model_get_iter (GtkTreeModel * model, GtkTreeIter * iter, 167 GtkTreePath * path) 168 { 169 PsppireSpreadsheetModel *spreadsheetModel = 170 PSPPIRE_SPREADSHEET_MODEL (model); 171 gint *indices, depth; 172 gint n; 173 174 g_return_val_if_fail (path, FALSE); 175 176 depth = gtk_tree_path_get_depth (path); 177 178 g_return_val_if_fail (depth == 1, FALSE); 179 180 indices = gtk_tree_path_get_indices (path); 181 182 n = indices[0]; 183 184 iter->stamp = spreadsheetModel->stamp; 185 iter->user_data = (gpointer) (intptr_t) n; 186 187 return TRUE; 188 } 189 190 static gboolean 191 tree_model_iter_next (GtkTreeModel *model, GtkTreeIter *iter) 192 { 193 PsppireSpreadsheetModel *spreadsheetModel = PSPPIRE_SPREADSHEET_MODEL (model); 194 g_assert (iter); 195 g_return_val_if_fail (iter->stamp == spreadsheetModel->stamp, FALSE); 196 197 if ((intptr_t) iter->user_data >= spreadsheetModel->spreadsheet->n_sheets - 1) 198 { 199 iter->user_data = NULL; 200 iter->stamp = 0; 201 return FALSE; 202 } 203 204 iter->user_data = GINT_TO_POINTER (GPOINTER_TO_INT (iter->user_data) + 1); 205 206 return TRUE; 207 } 208 209 210 static void 211 tree_model_get_value (GtkTreeModel * model, GtkTreeIter * iter, 212 gint column, GValue * value) 213 { 214 PsppireSpreadsheetModel *spreadsheetModel = 215 PSPPIRE_SPREADSHEET_MODEL (model); 216 g_return_if_fail (column < PSPPIRE_SPREADSHEET_MODEL_N_COLS); 217 g_return_if_fail (iter->stamp == spreadsheetModel->stamp); 218 219 g_value_init (value, G_TYPE_STRING); 220 switch (column) 221 { 222 case PSPPIRE_SPREADSHEET_MODEL_COL_NAME: 223 { 224 const char *x = 225 spreadsheet_get_sheet_name (spreadsheetModel->spreadsheet, 226 (intptr_t) iter->user_data); 227 228 g_value_set_string (value, x); 229 } 230 break; 231 case PSPPIRE_SPREADSHEET_MODEL_COL_RANGE: 232 { 233 char *x = 234 spreadsheet_get_sheet_range (spreadsheetModel->spreadsheet, 235 (intptr_t) iter->user_data); 236 237 g_value_set_string (value, x ? x : _("(empty)")); 238 g_free (x); 239 } 240 break; 241 default: 242 g_error ("%s:%d Invalid column in spreadsheet model", 243 __FILE__, __LINE__); 244 break; 245 } 246 } 247 248 static gboolean 249 tree_model_nth_child (GtkTreeModel * model, GtkTreeIter * iter, 250 GtkTreeIter * parent, gint n) 251 { 252 PsppireSpreadsheetModel *spreadsheetModel = 253 PSPPIRE_SPREADSHEET_MODEL (model); 254 255 if (parent) 256 return FALSE; 257 258 if (n >= spreadsheetModel->spreadsheet->n_sheets) 259 return FALSE; 260 261 iter->stamp = spreadsheetModel->stamp; 262 iter->user_data = (gpointer) (intptr_t) n; 263 264 return TRUE; 265 } 266 267 static gint 268 tree_model_n_children (GtkTreeModel * model, GtkTreeIter * iter) 269 { 270 PsppireSpreadsheetModel *spreadsheetModel = 271 PSPPIRE_SPREADSHEET_MODEL (model); 272 273 if (iter == NULL) 274 return spreadsheetModel->spreadsheet->n_sheets; 275 276 return 0; 277 } 278 279 static gboolean 280 tree_model_iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter) 281 { 282 return FALSE; 283 } 284 285 static GtkTreePath * 286 tree_model_get_path (GtkTreeModel * model, GtkTreeIter * iter) 287 { 288 PsppireSpreadsheetModel *spreadsheetModel = 289 PSPPIRE_SPREADSHEET_MODEL (model); 290 GtkTreePath *path; 291 gint index = (intptr_t) iter->user_data; 292 293 g_return_val_if_fail (iter->stamp == spreadsheetModel->stamp, NULL); 294 295 path = gtk_tree_path_new (); 296 297 gtk_tree_path_append_index (path, index); 298 299 return path; 300 } 301 302 303 static gboolean 304 tree_model_children (GtkTreeModel *model, GtkTreeIter *iter, GtkTreeIter *parent) 305 { 306 PsppireSpreadsheetModel *spreadsheetModel = PSPPIRE_SPREADSHEET_MODEL (model); 307 308 if (parent != NULL) 309 return FALSE; 310 311 iter->stamp = spreadsheetModel->stamp; 312 iter->user_data = 0; 313 314 return TRUE; 315 } 316 317 318 319 static void 320 spreadsheet_tree_model_init (GtkTreeModelIface * iface) 321 { 322 iface->get_flags = tree_model_get_flags; 323 iface->get_n_columns = tree_model_n_columns; 324 iface->get_column_type = tree_model_column_type; 325 iface->get_iter = tree_model_get_iter; 326 iface->iter_next = tree_model_iter_next; 327 iface->get_value = tree_model_get_value; 328 329 iface->iter_children = tree_model_children; 330 iface->iter_parent = NULL; 331 332 iface->get_path = tree_model_get_path; 333 iface->iter_has_child = tree_model_iter_has_child; 334 iface->iter_n_children = tree_model_n_children; 335 iface->iter_nth_child = tree_model_nth_child; 336 } 337