1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2017 John Darrington
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.
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.
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/>.
19 #include "psppire-data-sheet.h"
22 #define _(msgid) gettext (msgid)
25 #include "value-variant.h"
27 #include "ui/gui/executor.h"
28 #include "psppire-data-window.h"
29 #include "ssw-axis-model.h"
32 do_sort (PsppireDataSheet *sheet, GtkSortType order)
34 SswRange *range = SSW_SHEET(sheet)->selection;
36 PsppireDataStore *data_store = NULL;
37 g_object_get (sheet, "data-model", &data_store, NULL);
42 PsppireDataWindow *pdw =
43 psppire_data_window_for_data_store (data_store);
45 GString *syntax = g_string_new ("SORT CASES BY");
46 for (i = range->start_x ; i <= range->end_x; ++i)
48 const struct variable *var = psppire_dict_get_variable (data_store->dict, i);
51 g_string_append_printf (syntax, " %s", var_get_name (var));
57 if (order == GTK_SORT_DESCENDING)
58 g_string_append (syntax, " (DOWN)");
59 g_string_append_c (syntax, '.');
60 execute_const_syntax_string (pdw, syntax->str);
62 g_string_free (syntax, TRUE);
67 sort_ascending (PsppireDataSheet *sheet)
69 do_sort (sheet, GTK_SORT_ASCENDING);
71 gtk_widget_queue_draw (GTK_WIDGET (sheet));
75 sort_descending (PsppireDataSheet *sheet)
77 do_sort (sheet, GTK_SORT_DESCENDING);
79 gtk_widget_queue_draw (GTK_WIDGET (sheet));
85 change_data_value (PsppireDataSheet *sheet, gint col, gint row, GValue *value)
87 PsppireDataStore *store = NULL;
88 g_object_get (sheet, "data-model", &store, NULL);
90 const struct variable *var = psppire_dict_get_variable (store->dict, col);
97 GVariant *vrnt = g_value_get_variant (value);
99 value_variant_get (&v, vrnt);
101 psppire_data_store_set_value (store, row, var, &v);
103 value_destroy_from_variant (&v, vrnt);
106 gboolean myreversefunc (GtkTreeModel *model, gint col, gint row, const gchar *in,
112 show_cases_row_popup (PsppireDataSheet *sheet, int row,
113 guint button, guint state, gpointer p)
115 GListModel *vmodel = NULL;
116 g_object_get (sheet, "vmodel", &vmodel, NULL);
120 guint n_items = g_list_model_get_n_items (vmodel);
128 g_object_set_data (G_OBJECT (sheet->data_sheet_cases_row_popup), "item",
129 GINT_TO_POINTER (row));
131 gtk_menu_popup_at_pointer (GTK_MENU (sheet->data_sheet_cases_row_popup), NULL);
136 insert_new_case (PsppireDataSheet *sheet)
138 PsppireDataStore *data_store = NULL;
139 g_object_get (sheet, "data-model", &data_store, NULL);
141 gint posn = GPOINTER_TO_INT (g_object_get_data
142 (G_OBJECT (sheet->data_sheet_cases_row_popup), "item"));
144 psppire_data_store_insert_new_case (data_store, posn);
146 gtk_widget_queue_draw (GTK_WIDGET (sheet));
150 delete_cases (PsppireDataSheet *sheet)
152 SswRange *range = SSW_SHEET(sheet)->selection;
154 PsppireDataStore *data_store = NULL;
155 g_object_get (sheet, "data-model", &data_store, NULL);
157 psppire_data_store_delete_cases (data_store, range->start_y,
158 range->end_y - range->start_y + 1);
160 gtk_widget_queue_draw (GTK_WIDGET (sheet));
164 create_data_row_header_popup_menu (PsppireDataSheet *sheet)
166 GtkWidget *menu = gtk_menu_new ();
169 gtk_menu_item_new_with_mnemonic (_("_Insert Case"));
171 g_signal_connect_swapped (item, "activate", G_CALLBACK (insert_new_case), sheet);
172 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
174 item = gtk_separator_menu_item_new ();
175 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
177 sheet->data_clear_cases_menu_item = gtk_menu_item_new_with_mnemonic (_("Cl_ear Cases"));
178 gtk_widget_set_sensitive (sheet->data_clear_cases_menu_item, FALSE);
179 gtk_menu_shell_append (GTK_MENU_SHELL (menu), sheet->data_clear_cases_menu_item);
180 g_signal_connect_swapped (sheet->data_clear_cases_menu_item, "activate",
181 G_CALLBACK (delete_cases), sheet);
183 gtk_widget_show_all (menu);
189 show_cases_column_popup (PsppireDataSheet *sheet, int column, guint button, guint state,
192 GListModel *hmodel = NULL;
193 g_object_get (sheet, "hmodel", &hmodel, NULL);
197 guint n_items = g_list_model_get_n_items (hmodel);
199 if (column >= n_items)
205 g_object_set_data (G_OBJECT (sheet->data_sheet_cases_column_popup), "item",
206 GINT_TO_POINTER (column));
208 gtk_menu_popup_at_pointer (GTK_MENU (sheet->data_sheet_cases_column_popup), NULL);
212 insert_new_variable (PsppireDataSheet *sheet)
214 PsppireDataStore *data_store = NULL;
215 g_object_get (sheet, "data-model", &data_store, NULL);
217 gint posn = GPOINTER_TO_INT (g_object_get_data
218 (G_OBJECT (sheet->data_sheet_cases_column_popup),
221 const struct variable *v = psppire_dict_insert_variable (data_store->dict,
224 psppire_data_store_insert_value (data_store, var_get_width(v),
225 var_get_case_index (v));
227 gtk_widget_queue_draw (GTK_WIDGET (sheet));
231 set_menu_items_sensitivity (PsppireDataSheet *sheet, gpointer selection, gpointer p)
233 SswRange *range = selection;
235 PsppireDataStore *data_store = NULL;
236 g_object_get (sheet, "data-model", &data_store, NULL);
239 gint width = gtk_tree_model_get_n_columns (GTK_TREE_MODEL (data_store));
240 gint length = psppire_data_store_get_case_count (data_store);
243 gboolean whole_row_selected = (range->start_x == 0 && range->end_x == width - 1);
244 gtk_widget_set_sensitive (sheet->data_clear_cases_menu_item, whole_row_selected);
246 gboolean whole_column_selected =
247 (range->start_y == 0 && range->end_y == length - 1);
248 gtk_widget_set_sensitive (sheet->data_clear_variables_menu_item,
249 whole_column_selected);
250 gtk_widget_set_sensitive (sheet->data_sort_ascending_menu_item,
251 whole_column_selected);
252 gtk_widget_set_sensitive (sheet->data_sort_descending_menu_item,
253 whole_column_selected);
257 delete_variables (PsppireDataSheet *sheet)
259 SswRange *range = SSW_SHEET(sheet)->selection;
261 PsppireDataStore *data_store = NULL;
262 g_object_get (sheet, "data-model", &data_store, NULL);
264 psppire_dict_delete_variables (data_store->dict, range->start_x,
265 (range->end_x - range->start_x + 1));
267 gtk_widget_queue_draw (GTK_WIDGET (sheet));
273 create_data_column_header_popup_menu (PsppireDataSheet *sheet)
275 GtkWidget *menu = gtk_menu_new ();
278 gtk_menu_item_new_with_mnemonic (_("_Insert Variable"));
279 g_signal_connect_swapped (item, "activate", G_CALLBACK (insert_new_variable),
281 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
283 item = gtk_separator_menu_item_new ();
284 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
286 sheet->data_clear_variables_menu_item =
287 gtk_menu_item_new_with_mnemonic (_("Cl_ear Variables"));
288 g_signal_connect_swapped (sheet->data_clear_variables_menu_item, "activate",
289 G_CALLBACK (delete_variables),
291 gtk_widget_set_sensitive (sheet->data_clear_variables_menu_item, FALSE);
292 gtk_menu_shell_append (GTK_MENU_SHELL (menu), sheet->data_clear_variables_menu_item);
294 item = gtk_separator_menu_item_new ();
295 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
298 sheet->data_sort_ascending_menu_item =
299 gtk_menu_item_new_with_mnemonic (_("Sort _Ascending"));
300 g_signal_connect_swapped (sheet->data_sort_ascending_menu_item, "activate",
301 G_CALLBACK (sort_ascending), sheet);
302 gtk_widget_set_sensitive (sheet->data_sort_ascending_menu_item, FALSE);
303 gtk_menu_shell_append (GTK_MENU_SHELL (menu), sheet->data_sort_ascending_menu_item);
305 sheet->data_sort_descending_menu_item =
306 gtk_menu_item_new_with_mnemonic (_("Sort _Descending"));
307 g_signal_connect_swapped (sheet->data_sort_descending_menu_item, "activate",
308 G_CALLBACK (sort_descending), sheet);
309 gtk_widget_set_sensitive (sheet->data_sort_descending_menu_item, FALSE);
310 gtk_menu_shell_append (GTK_MENU_SHELL (menu), sheet->data_sort_descending_menu_item);
312 gtk_widget_show_all (menu);
319 G_DEFINE_TYPE (PsppireDataSheet, psppire_data_sheet, SSW_TYPE_SHEET)
321 static GObjectClass * parent_class = NULL;
322 static gboolean dispose_has_run = FALSE;
325 psppire_data_sheet_dispose (GObject *obj)
327 // PsppireDataSheet *sheet = PSPPIRE_DATA_SHEET (obj);
332 dispose_has_run = TRUE;
334 /* Chain up to the parent class */
335 G_OBJECT_CLASS (parent_class)->dispose (obj);
339 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
341 GObjectClass *object_class = G_OBJECT_CLASS (class);
342 object_class->dispose = psppire_data_sheet_dispose;
344 parent_class = g_type_class_peek_parent (class);
348 psppire_data_sheet_new (void)
351 g_object_new (PSPPIRE_TYPE_DATA_SHEET,
352 "forward-conversion", psppire_data_store_value_to_string,
353 "reverse-conversion", myreversefunc,
355 "horizontal-draggable", TRUE,
358 return GTK_WIDGET (obj);
363 indicate_filtered_case (GtkWidget *widget, cairo_t *cr, PsppireDataStore *store)
365 guint row = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "row"));
367 if (!psppire_data_store_filtered (store, row))
370 /* Draw a diagonal line through the widget */
371 guint width = gtk_widget_get_allocated_width (widget);
372 guint height = gtk_widget_get_allocated_height (widget);
374 GtkStyleContext *sc = gtk_widget_get_style_context (widget);
375 gtk_render_line (sc, cr, 0, 0, width, height);
381 button_post_create (GtkWidget *button, guint i, gpointer user_data)
383 PsppireDataStore *data_store = PSPPIRE_DATA_STORE (user_data);
385 g_object_set_data (G_OBJECT (button), "row", GUINT_TO_POINTER (i));
386 g_signal_connect_after (button, "draw", G_CALLBACK (indicate_filtered_case), data_store);
390 set_dictionary (PsppireDataSheet *sheet)
392 GtkTreeModel *data_model = NULL;
393 g_object_get (sheet, "data-model", &data_model, NULL);
395 PsppireDataStore *store = PSPPIRE_DATA_STORE (data_model);
396 g_object_set (sheet, "hmodel", store->dict, NULL);
399 SswAxisModel *vmodel = NULL;
400 g_object_get (sheet, "vmodel", &vmodel, NULL);
401 g_assert (SSW_IS_AXIS_MODEL (vmodel));
403 g_object_set (vmodel,
404 "post-button-create-func", button_post_create,
405 "post-button-create-func-data", store,
410 move_variable (PsppireDataSheet *sheet, gint from, gint to, gpointer ud)
412 PsppireDataStore *data_store = NULL;
413 g_object_get (sheet, "data-model", &data_store, NULL);
415 if (data_store == NULL)
418 PsppireDict *dict = data_store->dict;
419 struct variable *var = psppire_dict_get_variable (dict, from);
424 /* The index refers to the final position, so if the source
425 is less than the destination, then we must subtract 1, to
426 account for the position vacated by the source */
429 dict_reorder_var (dict->dict, var, new_pos);
433 psppire_data_sheet_init (PsppireDataSheet *sheet)
435 sheet->data_sheet_cases_column_popup =
436 create_data_column_header_popup_menu (sheet);
438 sheet->data_sheet_cases_row_popup =
439 create_data_row_header_popup_menu (sheet);
441 g_signal_connect (sheet, "selection-changed",
442 G_CALLBACK (set_menu_items_sensitivity), sheet);
444 g_signal_connect (sheet, "column-header-pressed",
445 G_CALLBACK (show_cases_column_popup), sheet);
447 g_signal_connect (sheet, "row-header-pressed",
448 G_CALLBACK (show_cases_row_popup), sheet);
450 g_signal_connect (sheet, "value-changed",
451 G_CALLBACK (change_data_value), NULL);
453 g_signal_connect (sheet, "notify::data-model",
454 G_CALLBACK (set_dictionary), NULL);
456 g_signal_connect (sheet, "column-moved", G_CALLBACK (move_variable), NULL);