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"
23 #define _(msgid) gettext (msgid)
26 #include "value-variant.h"
28 #include "ui/gui/executor.h"
29 #include "psppire-data-window.h"
30 #include "ssw-axis-model.h"
33 do_sort (PsppireDataSheet *sheet, GtkSortType order)
35 SswRange *range = SSW_SHEET(sheet)->selection;
37 PsppireDataStore *data_store = NULL;
38 g_object_get (sheet, "data-model", &data_store, NULL);
43 PsppireDataWindow *pdw =
44 psppire_data_window_for_data_store (data_store);
46 GString *syntax = g_string_new ("SORT CASES BY");
47 for (i = range->start_x ; i <= range->end_x; ++i)
49 const struct variable *var = psppire_dict_get_variable (data_store->dict, i);
52 g_string_append_printf (syntax, " %s", var_get_name (var));
58 if (order == GTK_SORT_DESCENDING)
59 g_string_append (syntax, " (DOWN)");
60 g_string_append_c (syntax, '.');
61 execute_const_syntax_string (pdw, syntax->str);
63 g_string_free (syntax, TRUE);
68 sort_ascending (PsppireDataSheet *sheet)
70 do_sort (sheet, GTK_SORT_ASCENDING);
72 gtk_widget_queue_draw (GTK_WIDGET (sheet));
76 sort_descending (PsppireDataSheet *sheet)
78 do_sort (sheet, GTK_SORT_DESCENDING);
80 gtk_widget_queue_draw (GTK_WIDGET (sheet));
86 change_data_value (PsppireDataSheet *sheet, gint col, gint row, GValue *value)
88 PsppireDataStore *store = NULL;
89 g_object_get (sheet, "data-model", &store, NULL);
91 const struct variable *var = psppire_dict_get_variable (store->dict, col);
98 GVariant *vrnt = g_value_get_variant (value);
100 value_variant_get (&v, vrnt);
102 psppire_data_store_set_value (store, row, var, &v);
104 value_destroy_from_variant (&v, vrnt);
110 show_cases_row_popup (PsppireDataSheet *sheet, int row,
111 guint button, guint state, gpointer p)
113 GListModel *vmodel = NULL;
114 g_object_get (sheet, "vmodel", &vmodel, NULL);
118 guint n_items = g_list_model_get_n_items (vmodel);
126 g_object_set_data (G_OBJECT (sheet->data_sheet_cases_row_popup), "item",
127 GINT_TO_POINTER (row));
129 gtk_menu_popup_at_pointer (GTK_MENU (sheet->data_sheet_cases_row_popup), NULL);
134 insert_new_case (PsppireDataSheet *sheet)
136 PsppireDataStore *data_store = NULL;
137 g_object_get (sheet, "data-model", &data_store, NULL);
139 gint posn = GPOINTER_TO_INT (g_object_get_data
140 (G_OBJECT (sheet->data_sheet_cases_row_popup), "item"));
142 psppire_data_store_insert_new_case (data_store, posn);
144 gtk_widget_queue_draw (GTK_WIDGET (sheet));
148 delete_cases (PsppireDataSheet *sheet)
150 SswRange *range = SSW_SHEET(sheet)->selection;
152 PsppireDataStore *data_store = NULL;
153 g_object_get (sheet, "data-model", &data_store, NULL);
155 psppire_data_store_delete_cases (data_store, range->start_y,
156 range->end_y - range->start_y + 1);
158 gtk_widget_queue_draw (GTK_WIDGET (sheet));
162 create_data_row_header_popup_menu (PsppireDataSheet *sheet)
164 GtkWidget *menu = gtk_menu_new ();
167 gtk_menu_item_new_with_mnemonic (_("_Insert Case"));
169 g_signal_connect_swapped (item, "activate", G_CALLBACK (insert_new_case), sheet);
170 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
172 item = gtk_separator_menu_item_new ();
173 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
175 sheet->data_clear_cases_menu_item = gtk_menu_item_new_with_mnemonic (_("Cl_ear Cases"));
176 gtk_widget_set_sensitive (sheet->data_clear_cases_menu_item, FALSE);
177 gtk_menu_shell_append (GTK_MENU_SHELL (menu), sheet->data_clear_cases_menu_item);
178 g_signal_connect_swapped (sheet->data_clear_cases_menu_item, "activate",
179 G_CALLBACK (delete_cases), sheet);
181 gtk_widget_show_all (menu);
187 show_cases_column_popup (PsppireDataSheet *sheet, int column, guint button, guint state,
190 GListModel *hmodel = NULL;
191 g_object_get (sheet, "hmodel", &hmodel, NULL);
195 guint n_items = g_list_model_get_n_items (hmodel);
197 if (column >= n_items)
203 g_object_set_data (G_OBJECT (sheet->data_sheet_cases_column_popup), "item",
204 GINT_TO_POINTER (column));
206 gtk_menu_popup_at_pointer (GTK_MENU (sheet->data_sheet_cases_column_popup), NULL);
210 insert_new_variable (PsppireDataSheet *sheet)
212 PsppireDataStore *data_store = NULL;
213 g_object_get (sheet, "data-model", &data_store, NULL);
215 gint posn = GPOINTER_TO_INT (g_object_get_data
216 (G_OBJECT (sheet->data_sheet_cases_column_popup),
219 const struct variable *v = psppire_dict_insert_variable (data_store->dict,
222 psppire_data_store_insert_value (data_store, var_get_width(v),
223 var_get_case_index (v));
225 gtk_widget_queue_draw (GTK_WIDGET (sheet));
229 set_menu_items_sensitivity (PsppireDataSheet *sheet, gpointer selection, gpointer p)
231 SswRange *range = selection;
233 PsppireDataStore *data_store = NULL;
234 g_object_get (sheet, "data-model", &data_store, NULL);
237 gint width = gtk_tree_model_get_n_columns (GTK_TREE_MODEL (data_store));
238 gint length = psppire_data_store_get_case_count (data_store);
241 gboolean whole_row_selected = (range->start_x == 0 && range->end_x == width - 1);
242 gtk_widget_set_sensitive (sheet->data_clear_cases_menu_item, whole_row_selected);
244 gboolean whole_column_selected =
245 (range->start_y == 0 && range->end_y == length - 1);
246 gtk_widget_set_sensitive (sheet->data_clear_variables_menu_item,
247 whole_column_selected);
248 gtk_widget_set_sensitive (sheet->data_sort_ascending_menu_item,
249 whole_column_selected);
250 gtk_widget_set_sensitive (sheet->data_sort_descending_menu_item,
251 whole_column_selected);
255 delete_variables (PsppireDataSheet *sheet)
257 SswRange *range = SSW_SHEET(sheet)->selection;
259 PsppireDataStore *data_store = NULL;
260 g_object_get (sheet, "data-model", &data_store, NULL);
262 psppire_dict_delete_variables (data_store->dict, range->start_x,
263 (range->end_x - range->start_x + 1));
265 gtk_widget_queue_draw (GTK_WIDGET (sheet));
271 create_data_column_header_popup_menu (PsppireDataSheet *sheet)
273 GtkWidget *menu = gtk_menu_new ();
276 gtk_menu_item_new_with_mnemonic (_("_Insert Variable"));
277 g_signal_connect_swapped (item, "activate", G_CALLBACK (insert_new_variable),
279 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
281 item = gtk_separator_menu_item_new ();
282 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
284 sheet->data_clear_variables_menu_item =
285 gtk_menu_item_new_with_mnemonic (_("Cl_ear Variables"));
286 g_signal_connect_swapped (sheet->data_clear_variables_menu_item, "activate",
287 G_CALLBACK (delete_variables),
289 gtk_widget_set_sensitive (sheet->data_clear_variables_menu_item, FALSE);
290 gtk_menu_shell_append (GTK_MENU_SHELL (menu), sheet->data_clear_variables_menu_item);
292 item = gtk_separator_menu_item_new ();
293 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
296 sheet->data_sort_ascending_menu_item =
297 gtk_menu_item_new_with_mnemonic (_("Sort _Ascending"));
298 g_signal_connect_swapped (sheet->data_sort_ascending_menu_item, "activate",
299 G_CALLBACK (sort_ascending), sheet);
300 gtk_widget_set_sensitive (sheet->data_sort_ascending_menu_item, FALSE);
301 gtk_menu_shell_append (GTK_MENU_SHELL (menu), sheet->data_sort_ascending_menu_item);
303 sheet->data_sort_descending_menu_item =
304 gtk_menu_item_new_with_mnemonic (_("Sort _Descending"));
305 g_signal_connect_swapped (sheet->data_sort_descending_menu_item, "activate",
306 G_CALLBACK (sort_descending), sheet);
307 gtk_widget_set_sensitive (sheet->data_sort_descending_menu_item, FALSE);
308 gtk_menu_shell_append (GTK_MENU_SHELL (menu), sheet->data_sort_descending_menu_item);
310 gtk_widget_show_all (menu);
317 G_DEFINE_TYPE (PsppireDataSheet, psppire_data_sheet, SSW_TYPE_SHEET)
319 static GObjectClass * parent_class = NULL;
320 static gboolean dispose_has_run = FALSE;
323 psppire_data_sheet_dispose (GObject *obj)
325 // PsppireDataSheet *sheet = PSPPIRE_DATA_SHEET (obj);
330 dispose_has_run = TRUE;
332 /* Chain up to the parent class */
333 G_OBJECT_CLASS (parent_class)->dispose (obj);
337 psppire_data_sheet_class_init (PsppireDataSheetClass *class)
339 GObjectClass *object_class = G_OBJECT_CLASS (class);
340 object_class->dispose = psppire_data_sheet_dispose;
342 parent_class = g_type_class_peek_parent (class);
346 psppire_data_sheet_new (void)
349 g_object_new (PSPPIRE_TYPE_DATA_SHEET,
350 "forward-conversion", psppire_data_store_value_to_string,
351 "reverse-conversion", psppire_data_store_string_to_value,
353 "horizontal-draggable", TRUE,
356 return GTK_WIDGET (obj);
361 indicate_filtered_case (GtkWidget *widget, cairo_t *cr, PsppireDataStore *store)
363 guint row = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (widget), "row"));
365 if (!psppire_data_store_filtered (store, row))
368 /* Draw a diagonal line through the widget */
369 guint width = gtk_widget_get_allocated_width (widget);
370 guint height = gtk_widget_get_allocated_height (widget);
372 GtkStyleContext *sc = gtk_widget_get_style_context (widget);
373 gtk_render_line (sc, cr, 0, 0, width, height);
379 button_post_create (GtkWidget *button, guint i, gpointer user_data)
381 PsppireDataStore *data_store = PSPPIRE_DATA_STORE (user_data);
383 g_object_set_data (G_OBJECT (button), "row", GUINT_TO_POINTER (i));
384 g_signal_connect_after (button, "draw", G_CALLBACK (indicate_filtered_case), data_store);
389 resize_display_width (PsppireDict *dict, gint pos, gint size, gpointer user_data)
394 PsppireDataSheet *sheet = PSPPIRE_DATA_SHEET (user_data);
395 PangoContext *context = gtk_widget_create_pango_context (GTK_WIDGET (sheet));
396 PangoLayout *layout = pango_layout_new (context);
399 pango_layout_set_text (layout, "M", 1);
400 pango_layout_get_extents (layout, NULL, &rect);
402 gdouble width_of_M = rect.width / (gdouble) PANGO_SCALE;
404 g_object_unref (G_OBJECT (layout));
405 g_object_unref (G_OBJECT (context));
407 gint Ms = round ((size / width_of_M) - 0.25);
408 struct variable *var = psppire_dict_get_variable (dict, pos);
409 g_return_val_if_fail (var, TRUE);
410 var_set_display_width (var, Ms);
415 set_dictionary (PsppireDataSheet *sheet)
417 GtkTreeModel *data_model = NULL;
418 g_object_get (sheet, "data-model", &data_model, NULL);
420 PsppireDataStore *store = PSPPIRE_DATA_STORE (data_model);
421 g_object_set (sheet, "hmodel", store->dict, NULL);
423 g_signal_connect (store->dict, "resize-item", G_CALLBACK (resize_display_width),
426 SswAxisModel *vmodel = NULL;
427 g_object_get (sheet, "vmodel", &vmodel, NULL);
428 g_assert (SSW_IS_AXIS_MODEL (vmodel));
430 g_object_set (vmodel,
431 "post-button-create-func", button_post_create,
432 "post-button-create-func-data", store,
437 move_variable (PsppireDataSheet *sheet, gint from, gint to, gpointer ud)
439 PsppireDataStore *data_store = NULL;
440 g_object_get (sheet, "data-model", &data_store, NULL);
442 if (data_store == NULL)
445 PsppireDict *dict = data_store->dict;
446 struct variable *var = psppire_dict_get_variable (dict, from);
451 /* The index refers to the final position, so if the source
452 is less than the destination, then we must subtract 1, to
453 account for the position vacated by the source */
456 dict_reorder_var (dict->dict, var, new_pos);
460 psppire_data_sheet_init (PsppireDataSheet *sheet)
462 sheet->data_sheet_cases_column_popup =
463 create_data_column_header_popup_menu (sheet);
465 sheet->data_sheet_cases_row_popup =
466 create_data_row_header_popup_menu (sheet);
468 g_signal_connect (sheet, "selection-changed",
469 G_CALLBACK (set_menu_items_sensitivity), sheet);
471 g_signal_connect (sheet, "column-header-pressed",
472 G_CALLBACK (show_cases_column_popup), sheet);
474 g_signal_connect (sheet, "row-header-pressed",
475 G_CALLBACK (show_cases_row_popup), sheet);
477 g_signal_connect (sheet, "value-changed",
478 G_CALLBACK (change_data_value), NULL);
480 g_signal_connect (sheet, "notify::data-model",
481 G_CALLBACK (set_dictionary), NULL);
483 g_signal_connect (sheet, "column-moved", G_CALLBACK (move_variable), NULL);