1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
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 <gtk-contrib/gtkextra-sheet.h>
20 #include "psppire-data-editor.h"
21 #include "psppire-var-sheet.h"
24 #include "psppire-data-store.h"
25 #include <libpspp/i18n.h>
26 #include <ui/gui/sheet/psppire-axis.h>
29 #include <gtk-contrib/gtkxpaned.h>
31 #define _(msgid) gettext (msgid)
32 #define N_(msgid) msgid
35 static void psppire_data_editor_remove_split (PsppireDataEditor *de);
36 static void psppire_data_editor_set_split (PsppireDataEditor *de);
39 DATA_SELECTION_CHANGED,
40 DATA_AVAILABLE_CHANGED,
47 static guint data_editor_signals [n_SIGNALS] = { 0 };
50 static gboolean data_is_selected (PsppireDataEditor *de);
52 static void psppire_data_editor_class_init (PsppireDataEditorClass *klass);
53 static void psppire_data_editor_init (PsppireDataEditor *de);
56 psppire_data_editor_get_type (void)
58 static GType de_type = 0;
62 static const GTypeInfo de_info =
64 sizeof (PsppireDataEditorClass),
66 NULL, /* base_finalize */
67 (GClassInitFunc) psppire_data_editor_class_init,
68 NULL, /* class_finalize */
69 NULL, /* class_data */
70 sizeof (PsppireDataEditor),
72 (GInstanceInitFunc) psppire_data_editor_init,
75 de_type = g_type_register_static (GTK_TYPE_NOTEBOOK, "PsppireDataEditor",
82 static GObjectClass * parent_class = NULL;
85 psppire_data_editor_dispose (GObject *obj)
87 PsppireDataEditor *de = (PsppireDataEditor *) obj;
89 if (de->dispose_has_run)
92 g_object_unref (de->data_window);
93 g_object_unref (de->data_store);
94 g_object_unref (de->var_store);
96 /* Make sure dispose does not run twice. */
97 de->dispose_has_run = TRUE;
99 /* Chain up to the parent class */
100 G_OBJECT_CLASS (parent_class)->dispose (obj);
104 psppire_data_editor_finalize (GObject *obj)
106 /* Chain up to the parent class */
107 G_OBJECT_CLASS (parent_class)->finalize (obj);
112 static void popup_variable_column_menu (PsppireSheet *sheet, gint column,
113 GdkEventButton *event, gpointer data);
115 static void popup_variable_row_menu (PsppireSheet *sheet, gint row,
116 GdkEventButton *event, gpointer data);
119 static void popup_cases_menu (PsppireSheet *sheet, gint row,
120 GdkEventButton *event, gpointer data);
126 /* Callback which occurs when the data sheet's column title
129 on_data_column_clicked (PsppireDataEditor *de, gint col, gpointer data)
131 PsppireSheetRange visible_range;
132 gint current_row, current_column;
134 gtk_notebook_set_current_page (GTK_NOTEBOOK (de),
135 PSPPIRE_DATA_EDITOR_VARIABLE_VIEW);
137 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->var_sheet),
138 ¤t_row, ¤t_column);
140 psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->var_sheet), col, current_column);
143 psppire_sheet_get_visible_range (PSPPIRE_SHEET (de->var_sheet), &visible_range);
145 if ( col < visible_range.row0 || col > visible_range.rowi)
146 psppire_sheet_moveto (PSPPIRE_SHEET (de->var_sheet), col, current_column, 0.5, 0.5);
153 /* Callback which occurs when the var sheet's row title
154 button is double clicked */
156 on_var_row_clicked (PsppireDataEditor *de, gint row, gpointer data)
158 PsppireSheetRange visible_range;
160 gint current_row, current_column;
162 gtk_notebook_set_current_page (GTK_NOTEBOOK(de), PSPPIRE_DATA_EDITOR_DATA_VIEW);
164 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]),
165 ¤t_row, ¤t_column);
167 psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), current_row, row);
169 psppire_sheet_get_visible_range (PSPPIRE_SHEET (de->data_sheet[0]), &visible_range);
171 if ( row < visible_range.col0 || row > visible_range.coli)
172 psppire_sheet_moveto (PSPPIRE_SHEET (de->data_sheet[0]), -1, row, 0.5, 0.5);
178 /* Moves the focus to a new cell.
179 Returns TRUE iff the move should be disallowed */
181 traverse_cell_callback (PsppireSheet *sheet,
182 PsppireSheetCell *existing_cell,
183 PsppireSheetCell *new_cell,
186 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
187 const PsppireDict *dict = de->data_store->dict;
189 if ( new_cell->col >= psppire_dict_get_var_cnt (dict))
213 #define DEFAULT_ROW_HEIGHT 25
216 new_data_callback (PsppireDataStore *ds, gpointer data)
219 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
221 casenumber n_cases = psppire_data_store_get_case_count (ds);
223 for (i = 0; i < 2; ++i)
225 psppire_axis_clear (de->vaxis[i]);
226 psppire_axis_append_n (de->vaxis[i], n_cases, DEFAULT_ROW_HEIGHT);
229 /* All of the data (potentially) changed, so unselect any selected cell(s) in
230 the data sheets. If we don't do this, then the sheet remembers the value
231 that was in the selected cell and stores it back, wiping out whatever
232 value there is in the new data. Bug #30502. */
233 if (de->data_sheet[0] != NULL)
234 psppire_sheet_unselect_range (PSPPIRE_SHEET (de->data_sheet[0]));
238 case_inserted_callback (PsppireDataStore *ds, gint before, gpointer data)
241 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
243 for (i = 0; i < 2; ++i)
244 psppire_axis_insert (de->vaxis[i], before, DEFAULT_ROW_HEIGHT);
249 cases_deleted_callback (PsppireDataStore *ds, gint first, gint n_cases, gpointer data)
252 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
254 for (i = 0; i < 2; ++i)
255 psppire_axis_delete (de->vaxis[0], first, n_cases);
260 /* Return the width (in pixels) of an upper case M when rendered in the
264 width_of_m (GtkWidget *w)
267 PangoLayout *layout = gtk_widget_create_pango_layout (w, "M");
269 pango_layout_get_pixel_extents (layout, NULL, &rect);
271 g_object_unref (layout);
276 /* Callback for the axis' resize signal.
277 Changes the variable's display width */
279 rewidth_variable (GtkWidget *w, gint unit, glong size)
281 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (w);
283 const PsppireDict *dict = de->data_store->dict;
284 struct variable *var = psppire_dict_get_variable (dict, unit);
289 var_set_display_width (var, size / (float) width_of_m (w));
294 new_variables_callback (PsppireDict *dict, gpointer data)
297 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
298 gint m_width = width_of_m (GTK_WIDGET (de));
301 g_object_get (de->var_sheet, "vertical-axis", &vaxis, NULL);
303 psppire_axis_clear (vaxis);
304 psppire_axis_append_n (vaxis, 1 + psppire_dict_get_var_cnt (dict), DEFAULT_ROW_HEIGHT);
306 g_signal_connect_swapped (de->haxis, "resize-unit",
307 G_CALLBACK (rewidth_variable), de);
309 psppire_axis_clear (de->haxis);
311 for (v = 0 ; v < psppire_dict_get_var_cnt (dict); ++v)
313 const struct variable *var = psppire_dict_get_variable (dict, v);
315 psppire_axis_append (de->haxis, m_width * var_get_display_width (var));
320 insert_variable_callback (PsppireDict *dict, gint x, gpointer data)
322 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
324 gint m_width = width_of_m (GTK_WIDGET (de));
326 PsppireAxis *var_vaxis;
328 const struct variable *var = psppire_dict_get_variable (dict, x);
330 g_object_get (de->var_sheet, "vertical-axis", &var_vaxis, NULL);
332 psppire_axis_insert (var_vaxis, x, DEFAULT_ROW_HEIGHT);
335 psppire_axis_insert (de->haxis, x, m_width * var_get_display_width (var));
340 delete_variable_callback (PsppireDict *dict,
341 const struct variable *var UNUSED,
342 gint dict_idx, gint case_idx UNUSED, gpointer data)
344 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
346 PsppireAxis *var_vaxis;
347 g_object_get (de->var_sheet, "vertical-axis", &var_vaxis, NULL);
349 psppire_axis_delete (var_vaxis, dict_idx, 1);
351 psppire_axis_delete (de->haxis, dict_idx, 1);
356 rewidth_variable_callback (PsppireDict *dict, gint posn, gpointer data)
358 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
359 gint m_width = width_of_m (GTK_WIDGET (de));
361 const struct variable *var = psppire_dict_get_variable (dict, posn);
363 gint var_width = var_get_display_width (var);
365 /* Don't allow zero width */
369 psppire_axis_resize (de->haxis, posn, m_width * var_width);
374 psppire_data_editor_set_property (GObject *object,
380 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
384 case PROP_SPLIT_WINDOW:
385 psppire_data_editor_split_window (de, g_value_get_boolean (value));
387 case PROP_DATA_WINDOW:
389 g_object_unref (de->data_window);
390 de->data_window = g_value_get_pointer (value);
391 g_object_ref (de->data_window);
393 case PROP_DATA_STORE:
394 if ( de->data_store) g_object_unref (de->data_store);
395 de->data_store = g_value_get_pointer (value);
396 g_object_ref (de->data_store);
398 for (i = 0 ; i < 4 ; ++i )
400 g_object_set (de->data_sheet[i],
401 "model", de->data_store,
404 g_signal_connect_swapped (de->data_store->dict, "filter-changed",
405 G_CALLBACK (gtk_widget_queue_draw),
409 g_signal_connect (de->data_store->dict, "backend-changed",
410 G_CALLBACK (new_variables_callback), de);
412 g_signal_connect (de->data_store->dict, "variable-inserted",
413 G_CALLBACK (insert_variable_callback), de);
415 g_signal_connect (de->data_store->dict, "variable-deleted",
416 G_CALLBACK (delete_variable_callback), de);
418 g_signal_connect (de->data_store->dict, "variable-display-width-changed",
419 G_CALLBACK (rewidth_variable_callback), de);
421 g_signal_connect (de->data_store, "backend-changed",
422 G_CALLBACK (new_data_callback), de);
424 g_signal_connect (de->data_store, "case-inserted",
425 G_CALLBACK (case_inserted_callback), de);
427 g_signal_connect (de->data_store, "cases-deleted",
428 G_CALLBACK (cases_deleted_callback), de);
432 if ( de->var_store) g_object_unref (de->var_store);
433 de->var_store = g_value_get_pointer (value);
434 g_object_ref (de->var_store);
436 g_object_set (de->var_sheet,
437 "model", de->var_store,
440 case PROP_VS_ROW_MENU:
442 GObject *menu = g_value_get_object (value);
444 g_signal_connect (de->var_sheet, "button-event-row",
445 G_CALLBACK (popup_variable_row_menu), menu);
448 case PROP_DS_COLUMN_MENU:
450 GObject *menu = g_value_get_object (value);
452 g_signal_connect (de->data_sheet[0], "button-event-column",
453 G_CALLBACK (popup_variable_column_menu), menu);
456 case PROP_DS_ROW_MENU:
458 GObject *menu = g_value_get_object (value);
460 g_signal_connect (de->data_sheet[0], "button-event-row",
461 G_CALLBACK (popup_cases_menu), menu);
464 case PROP_CURRENT_VAR:
467 gint var = g_value_get_long (value);
468 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (object)))
470 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
471 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
472 psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), row, var);
473 psppire_sheet_moveto (PSPPIRE_SHEET (de->data_sheet[0]), -1, var, 0.5, 0.5);
475 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
476 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->var_sheet), &row, &col);
477 psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->var_sheet), var, col);
478 psppire_sheet_moveto (PSPPIRE_SHEET (de->var_sheet), var, -1, 0.5, 0.5);
481 g_assert_not_reached ();
486 case PROP_CURRENT_CASE:
489 gint case_num = g_value_get_long (value);
490 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
491 psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), case_num, col);
492 psppire_sheet_moveto (PSPPIRE_SHEET (de->data_sheet[0]), case_num, -1, 0.5, 0.5);
495 case PROP_VALUE_LABELS:
497 psppire_data_store_show_labels (de->data_store,
498 g_value_get_boolean (value));
502 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
508 psppire_data_editor_get_property (GObject *object,
513 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
517 case PROP_SPLIT_WINDOW:
518 g_value_set_boolean (value, de->split);
520 case PROP_DATA_WINDOW:
521 g_value_set_pointer (value, de->data_window);
523 case PROP_DATA_STORE:
524 g_value_set_pointer (value, de->data_store);
527 g_value_set_pointer (value, de->var_store);
529 case PROP_CURRENT_CASE:
532 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &column);
533 g_value_set_long (value, row);
536 case PROP_CURRENT_VAR:
539 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &column);
540 g_value_set_long (value, column);
543 case PROP_DATA_SELECTED:
544 g_value_set_boolean (value, data_is_selected (de));
547 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
554 psppire_data_editor_class_init (PsppireDataEditorClass *klass)
556 GParamSpec *data_window_spec ;
557 GParamSpec *data_store_spec ;
558 GParamSpec *var_store_spec ;
559 GParamSpec *column_menu_spec;
560 GParamSpec *ds_row_menu_spec;
561 GParamSpec *vs_row_menu_spec;
562 GParamSpec *value_labels_spec;
563 GParamSpec *current_case_spec;
564 GParamSpec *current_var_spec;
565 GParamSpec *data_selected_spec;
566 GParamSpec *split_window_spec;
567 GObjectClass *object_class = G_OBJECT_CLASS (klass);
569 parent_class = g_type_class_peek_parent (klass);
571 object_class->dispose = psppire_data_editor_dispose;
572 object_class->finalize = psppire_data_editor_finalize;
574 object_class->set_property = psppire_data_editor_set_property;
575 object_class->get_property = psppire_data_editor_get_property;
580 g_param_spec_pointer ("data-window",
582 "A pointer to the data window associated with this editor",
583 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
585 g_object_class_install_property (object_class,
590 g_param_spec_pointer ("data-store",
592 "A pointer to the data store associated with this editor",
593 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
595 g_object_class_install_property (object_class,
600 g_param_spec_pointer ("var-store",
602 "A pointer to the variable store associated with this editor",
603 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
605 g_object_class_install_property (object_class,
610 g_param_spec_object ("datasheet-column-menu",
611 "Data Sheet Column Menu",
612 "A menu to be displayed when button 3 is pressed in thedata sheet's column title buttons",
616 g_object_class_install_property (object_class,
622 g_param_spec_object ("datasheet-row-menu",
623 "Data Sheet Row Menu",
624 "A menu to be displayed when button 3 is pressed in the data sheet's row title buttons",
628 g_object_class_install_property (object_class,
634 g_param_spec_object ("varsheet-row-menu",
635 "Variable Sheet Row Menu",
636 "A menu to be displayed when button 3 is pressed in the variable sheet's row title buttons",
640 g_object_class_install_property (object_class,
646 g_param_spec_boolean ("value-labels",
648 "Whether or not the data sheet should display labels instead of values",
650 G_PARAM_WRITABLE | G_PARAM_READABLE);
652 g_object_class_install_property (object_class,
658 g_param_spec_long ("current-case",
660 "Zero based number of the selected case",
663 G_PARAM_WRITABLE | G_PARAM_READABLE);
665 g_object_class_install_property (object_class,
671 g_param_spec_long ("current-variable",
673 "Zero based number of the selected variable",
676 G_PARAM_WRITABLE | G_PARAM_READABLE);
678 g_object_class_install_property (object_class,
684 g_param_spec_boolean ("data-selected",
686 "True iff the data view is active and one or more cells of data have been selected.",
690 g_object_class_install_property (object_class,
697 g_param_spec_boolean ("split",
699 "True iff the data sheet is split",
701 G_PARAM_READABLE | G_PARAM_WRITABLE);
703 g_object_class_install_property (object_class,
707 data_editor_signals [DATA_SELECTION_CHANGED] =
708 g_signal_new ("data-selection-changed",
709 G_TYPE_FROM_CLASS (klass),
713 g_cclosure_marshal_VOID__BOOLEAN,
718 data_editor_signals [CASES_SELECTED] =
719 g_signal_new ("cases-selected",
720 G_TYPE_FROM_CLASS (klass),
724 g_cclosure_marshal_VOID__INT,
730 data_editor_signals [VARIABLES_SELECTED] =
731 g_signal_new ("variables-selected",
732 G_TYPE_FROM_CLASS (klass),
736 g_cclosure_marshal_VOID__INT,
742 data_editor_signals [DATA_AVAILABLE_CHANGED] =
743 g_signal_new ("data-available-changed",
744 G_TYPE_FROM_CLASS (klass),
748 g_cclosure_marshal_VOID__BOOLEAN,
754 /* Update the data_ref_entry with the reference of the active cell */
756 update_data_ref_entry (const PsppireSheet *sheet,
758 gint old_row, gint old_col,
761 PsppireDataEditor *de = data;
763 PsppireDataStore *data_store =
764 PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
768 const struct variable *var =
769 psppire_dict_get_variable (data_store->dict, col);
773 gchar *text = g_strdup_printf ("%d: %s", row + FIRST_CASE_NUMBER,
777 gtk_entry_set_text (GTK_ENTRY (de->cell_ref_entry), text);
787 psppire_data_store_get_string (data_store, row,
788 var_get_dict_index(var));
795 gtk_entry_set_text (GTK_ENTRY (de->datum_entry), text);
807 gtk_entry_set_text (GTK_ENTRY (de->datum_entry), "");
814 datum_entry_activate (GtkEntry *entry, gpointer data)
817 PsppireDataEditor *de = data;
819 const gchar *text = gtk_entry_get_text (entry);
821 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &column);
823 if ( row == -1 || column == -1)
826 psppire_data_store_set_string (de->data_store, text, row, column);
829 static void on_activate (PsppireDataEditor *de);
830 static gboolean on_switch_page (PsppireDataEditor *de, GtkNotebookPage *p, gint pagenum, gpointer data);
831 static void on_select_range (PsppireDataEditor *de);
833 static void on_select_row (PsppireSheet *, gint, PsppireDataEditor *);
834 static void on_select_variable (PsppireSheet *, gint, PsppireDataEditor *);
837 static void on_owner_change (GtkClipboard *,
838 GdkEventOwnerChange *, gpointer);
841 on_map (GtkWidget *w)
843 GtkClipboard *clip = gtk_widget_get_clipboard (w, GDK_SELECTION_CLIPBOARD);
845 g_signal_connect (clip, "owner-change", G_CALLBACK (on_owner_change), w);
850 init_sheet (PsppireDataEditor *de, int i,
851 GtkAdjustment *hadj, GtkAdjustment *vadj,
856 de->sheet_bin[i] = gtk_scrolled_window_new (hadj, vadj);
858 de->data_sheet[i] = psppire_sheet_new (NULL);
860 g_object_set (de->sheet_bin[i],
862 "shadow-type", GTK_SHADOW_ETCHED_IN,
865 g_object_set (haxis, "default-size", 75, NULL);
866 g_object_set (vaxis, "default-size", 25, NULL);
868 g_object_set (de->data_sheet[i],
869 "horizontal-axis", haxis,
870 "vertical-axis", vaxis,
873 gtk_container_add (GTK_CONTAINER (de->sheet_bin[i]), de->data_sheet[i]);
875 g_signal_connect (de->data_sheet[i], "traverse",
876 G_CALLBACK (traverse_cell_callback), de);
878 gtk_widget_show (de->sheet_bin[i]);
883 init_data_sheet (PsppireDataEditor *de)
885 GtkAdjustment *vadj0, *hadj0;
886 GtkAdjustment *vadj1, *hadj1;
889 de->vaxis[0] = psppire_axis_new ();
890 de->vaxis[1] = psppire_axis_new ();
892 /* There's only one horizontal axis, since the
893 column widths are parameters of the variables */
894 de->haxis = psppire_axis_new ();
897 de->paned = gtk_xpaned_new ();
899 init_sheet (de, 0, NULL, NULL, de->vaxis[0], de->haxis);
900 gtk_widget_show (de->sheet_bin[0]);
901 vadj0 = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[0]));
902 hadj0 = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[0]));
904 g_object_set (de->sheet_bin[0], "vscrollbar-policy", GTK_POLICY_NEVER, NULL);
905 g_object_set (de->sheet_bin[0], "hscrollbar-policy", GTK_POLICY_NEVER, NULL);
907 init_sheet (de, 1, NULL, vadj0, de->vaxis[0], de->haxis);
908 gtk_widget_show (de->sheet_bin[1]);
909 sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[1]));
910 psppire_sheet_hide_row_titles (PSPPIRE_SHEET (sheet));
911 hadj1 = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[1]));
912 g_object_set (de->sheet_bin[1], "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
913 g_object_set (de->sheet_bin[1], "hscrollbar-policy", GTK_POLICY_NEVER, NULL);
915 init_sheet (de, 2, hadj0, NULL, de->vaxis[1], de->haxis);
916 gtk_widget_show (de->sheet_bin[2]);
917 sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[2]));
918 psppire_sheet_hide_column_titles (PSPPIRE_SHEET (sheet));
919 g_object_set (de->sheet_bin[2], "vscrollbar-policy", GTK_POLICY_NEVER, NULL);
920 g_object_set (de->sheet_bin[2], "hscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
921 vadj1 = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[2]));
923 init_sheet (de, 3, hadj1, vadj1, de->vaxis[1], de->haxis);
924 gtk_widget_show (de->sheet_bin[3]);
925 sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[3]));
926 psppire_sheet_hide_column_titles (PSPPIRE_SHEET (sheet));
927 psppire_sheet_hide_row_titles (PSPPIRE_SHEET (sheet));
928 g_object_set (de->sheet_bin[3], "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
929 g_object_set (de->sheet_bin[3], "hscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
931 gtk_xpaned_pack_top_left (GTK_XPANED (de->paned), de->sheet_bin[0], TRUE, TRUE);
932 gtk_xpaned_pack_top_right (GTK_XPANED (de->paned), de->sheet_bin[1], TRUE, TRUE);
933 gtk_xpaned_pack_bottom_left (GTK_XPANED (de->paned), de->sheet_bin[2], TRUE, TRUE);
934 gtk_xpaned_pack_bottom_right (GTK_XPANED (de->paned), de->sheet_bin[3], TRUE, TRUE);
936 gtk_xpaned_set_position_y (GTK_XPANED (de->paned), 150);
937 gtk_xpaned_set_position_x (GTK_XPANED (de->paned), 350);
942 psppire_data_editor_init (PsppireDataEditor *de)
944 GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
945 GtkWidget *sw_vs = gtk_scrolled_window_new (NULL, NULL);
947 init_data_sheet (de);
949 de->data_vbox = gtk_vbox_new (FALSE, 0);
950 de->var_sheet = psppire_var_sheet_new ();
952 g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
954 de->datum_entry = gtk_entry_new ();
955 de->cell_ref_entry = gtk_entry_new ();
957 g_object_set (de->cell_ref_entry,
963 gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_entry, FALSE, FALSE, 0);
964 gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0);
967 gtk_container_add (GTK_CONTAINER (sw_vs), de->var_sheet);
968 gtk_widget_show_all (sw_vs);
971 gtk_box_pack_start (GTK_BOX (de->data_vbox), hbox, FALSE, FALSE, 0);
972 gtk_box_pack_start (GTK_BOX (de->data_vbox), de->paned, TRUE, TRUE, 0);
975 psppire_data_editor_remove_split (de);
977 gtk_widget_show_all (de->data_vbox);
979 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->data_vbox,
980 gtk_label_new_with_mnemonic (_("Data View")));
982 gtk_notebook_append_page (GTK_NOTEBOOK (de), sw_vs,
983 gtk_label_new_with_mnemonic (_("Variable View")));
985 g_signal_connect (de->data_sheet[0], "activate",
986 G_CALLBACK (update_data_ref_entry),
989 g_signal_connect (de->datum_entry, "activate",
990 G_CALLBACK (datum_entry_activate),
994 g_signal_connect_swapped (de->data_sheet[0],
995 "double-click-column",
996 G_CALLBACK (on_data_column_clicked),
999 g_signal_connect_swapped (de->var_sheet,
1001 G_CALLBACK (on_var_row_clicked),
1004 g_signal_connect_swapped (de->data_sheet[0], "activate",
1005 G_CALLBACK (on_activate),
1008 g_signal_connect_swapped (de->data_sheet[0], "select-range",
1009 G_CALLBACK (on_select_range),
1012 g_signal_connect (de->data_sheet[0], "select-row",
1013 G_CALLBACK (on_select_row), de);
1015 g_signal_connect (de->data_sheet[0], "select-column",
1016 G_CALLBACK (on_select_variable), de);
1019 g_signal_connect (de->var_sheet, "select-row",
1020 G_CALLBACK (on_select_variable), de);
1023 g_signal_connect_after (de, "switch-page",
1024 G_CALLBACK (on_switch_page),
1027 g_object_set (de, "can-focus", FALSE, NULL);
1029 g_signal_connect (de, "map", G_CALLBACK (on_map), NULL);
1032 // psppire_sheet_hide_column_titles (de->var_sheet);
1033 // psppire_sheet_hide_row_titles (de->data_sheet);
1036 de->dispose_has_run = FALSE;
1041 psppire_data_editor_new (PsppireDataWindow *data_window,
1042 PsppireVarStore *var_store,
1043 PsppireDataStore *data_store)
1045 return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
1046 "data-window", data_window,
1047 "var-store", var_store,
1048 "data-store", data_store,
1054 psppire_data_editor_remove_split (PsppireDataEditor *de)
1056 if ( !de->split ) return;
1059 g_object_ref (de->sheet_bin[0]);
1060 gtk_container_remove (GTK_CONTAINER (de->paned), de->sheet_bin[0]);
1062 g_object_ref (de->paned);
1063 gtk_container_remove (GTK_CONTAINER (de->data_vbox), de->paned);
1065 gtk_box_pack_start (GTK_BOX (de->data_vbox), de->sheet_bin[0],
1068 g_object_unref (de->sheet_bin[0]);
1070 g_object_set (de->sheet_bin[0], "vscrollbar-policy",
1071 GTK_POLICY_ALWAYS, NULL);
1073 g_object_set (de->sheet_bin[0], "hscrollbar-policy",
1074 GTK_POLICY_ALWAYS, NULL);
1079 psppire_data_editor_set_split (PsppireDataEditor *de)
1081 if ( de->split ) return;
1084 g_object_ref (de->sheet_bin[0]);
1085 gtk_container_remove (GTK_CONTAINER (de->data_vbox), de->sheet_bin[0]);
1087 gtk_xpaned_pack_top_left (GTK_XPANED (de->paned), de->sheet_bin [0],
1090 gtk_box_pack_start (GTK_BOX (de->data_vbox), de->paned,
1093 g_object_unref (de->paned);
1095 g_object_set (de->sheet_bin[0], "vscrollbar-policy",
1096 GTK_POLICY_NEVER, NULL);
1098 g_object_set (de->sheet_bin[0], "hscrollbar-policy",
1099 GTK_POLICY_NEVER, NULL);
1103 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
1106 psppire_data_editor_set_split (de);
1108 psppire_data_editor_remove_split (de);
1110 gtk_widget_show_all (de->data_vbox);
1113 static void data_sheet_set_clip (PsppireSheet *sheet);
1114 static void data_sheet_contents_received_callback (GtkClipboard *clipboard,
1115 GtkSelectionData *sd,
1120 psppire_data_editor_clip_copy (PsppireDataEditor *de)
1122 data_sheet_set_clip (PSPPIRE_SHEET (de->data_sheet[0]));
1126 psppire_data_editor_clip_paste (PsppireDataEditor *de)
1128 GdkDisplay *display = gtk_widget_get_display ( GTK_WIDGET (de));
1129 GtkClipboard *clipboard =
1130 gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
1132 gtk_clipboard_request_contents (clipboard,
1133 gdk_atom_intern ("UTF8_STRING", TRUE),
1134 data_sheet_contents_received_callback,
1141 psppire_data_editor_clip_cut (PsppireDataEditor *de)
1143 gint max_rows, max_columns;
1145 PsppireSheetRange range;
1146 PsppireDataStore *ds = de->data_store;
1148 data_sheet_set_clip (PSPPIRE_SHEET (de->data_sheet[0]));
1150 /* Now blank all the cells */
1151 psppire_sheet_get_selected_range (PSPPIRE_SHEET (de->data_sheet[0]), &range);
1153 /* If nothing selected, then use active cell */
1154 if ( range.row0 < 0 || range.col0 < 0 )
1157 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
1159 range.row0 = range.rowi = row;
1160 range.col0 = range.coli = col;
1163 /* The sheet range can include cells that do not include data.
1164 Exclude them from the range. */
1165 max_rows = psppire_data_store_get_case_count (ds);
1166 if (range.rowi >= max_rows)
1170 range.rowi = max_rows - 1;
1173 max_columns = dict_get_var_cnt (ds->dict->dict);
1174 if (range.coli >= max_columns)
1176 if (max_columns == 0)
1178 range.coli = max_columns - 1;
1181 g_return_if_fail (range.rowi >= range.row0);
1182 g_return_if_fail (range.row0 >= 0);
1183 g_return_if_fail (range.coli >= range.col0);
1184 g_return_if_fail (range.col0 >= 0);
1187 for (r = range.row0; r <= range.rowi ; ++r )
1191 for (c = range.col0 ; c <= range.coli; ++c)
1193 psppire_data_store_set_string (ds, "", r, c);
1197 /* and remove the selection */
1198 psppire_sheet_unselect_range (PSPPIRE_SHEET (de->data_sheet[0]));
1204 /* Popup menu related stuff */
1207 popup_variable_column_menu (PsppireSheet *sheet, gint column,
1208 GdkEventButton *event, gpointer data)
1210 GtkMenu *menu = GTK_MENU (data);
1212 PsppireDataStore *data_store =
1213 PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
1215 const struct variable *v =
1216 psppire_dict_get_variable (data_store->dict, column);
1218 if ( v && event->button == 3)
1220 psppire_sheet_select_column (sheet, column);
1222 gtk_menu_popup (menu,
1223 NULL, NULL, NULL, NULL,
1224 event->button, event->time);
1230 popup_variable_row_menu (PsppireSheet *sheet, gint row,
1231 GdkEventButton *event, gpointer data)
1233 GtkMenu *menu = GTK_MENU (data);
1235 PsppireVarStore *var_store =
1236 PSPPIRE_VAR_STORE (psppire_sheet_get_model (sheet));
1239 const struct variable *v ;
1241 g_object_get (var_store, "dictionary", &dict, NULL);
1243 v = psppire_dict_get_variable (dict, row);
1245 if ( v && event->button == 3)
1247 psppire_sheet_select_row (sheet, row);
1249 gtk_menu_popup (menu,
1250 NULL, NULL, NULL, NULL,
1251 event->button, event->time);
1257 popup_cases_menu (PsppireSheet *sheet, gint row,
1258 GdkEventButton *event, gpointer data)
1260 GtkMenu *menu = GTK_MENU (data);
1262 PsppireDataStore *data_store =
1263 PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
1265 if ( row <= psppire_data_store_get_case_count (data_store) &&
1268 psppire_sheet_select_row (sheet, row);
1270 gtk_menu_popup (menu,
1271 NULL, NULL, NULL, NULL,
1272 event->button, event->time);
1281 do_sort (PsppireDataEditor *de, int var, gboolean descend)
1283 const struct variable *v
1284 = psppire_dict_get_variable (de->data_store->dict, var);
1287 syntax = g_strdup_printf ("SORT CASES BY %s%s.",
1288 var_get_name (v), descend ? " (D)" : "");
1289 g_free (execute_syntax_string (de->data_window, syntax));
1293 /* Sort the data by the the variable which the editor has currently
1296 psppire_data_editor_sort_ascending (PsppireDataEditor *de)
1298 PsppireSheetRange range;
1299 psppire_sheet_get_selected_range (PSPPIRE_SHEET(de->data_sheet[0]), &range);
1301 do_sort (de, range.col0, FALSE);
1305 /* Sort the data by the the variable which the editor has currently
1308 psppire_data_editor_sort_descending (PsppireDataEditor *de)
1310 PsppireSheetRange range;
1311 psppire_sheet_get_selected_range (PSPPIRE_SHEET(de->data_sheet[0]), &range);
1313 do_sort (de, range.col0, TRUE);
1320 /* Insert a new variable before the currently selected position */
1322 psppire_data_editor_insert_variable (PsppireDataEditor *de)
1326 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
1328 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
1329 if ( PSPPIRE_SHEET (de->data_sheet[0])->select_status
1330 == PSPPIRE_SHEET_COLUMN_SELECTED )
1331 posn = PSPPIRE_SHEET (de->data_sheet[0])->range.col0;
1333 posn = PSPPIRE_SHEET (de->data_sheet[0])->active_cell.col;
1335 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
1336 if ( PSPPIRE_SHEET (de->var_sheet)->select_status
1337 == PSPPIRE_SHEET_ROW_SELECTED )
1338 posn = PSPPIRE_SHEET (de->var_sheet)->range.row0;
1340 posn = PSPPIRE_SHEET (de->var_sheet)->active_cell.row;
1343 g_assert_not_reached ();
1347 psppire_dict_insert_variable (de->data_store->dict, posn, NULL);
1350 /* Insert a new case before the currently selected position */
1352 psppire_data_editor_insert_case (PsppireDataEditor *de)
1356 if ( PSPPIRE_SHEET (de->data_sheet[0])->select_status == PSPPIRE_SHEET_ROW_SELECTED )
1358 posn = PSPPIRE_SHEET (de->data_sheet[0])->range.row0;
1362 posn = PSPPIRE_SHEET (de->data_sheet[0])->active_cell.row;
1365 if ( posn == -1 ) posn = 0;
1367 psppire_data_store_insert_new_case (de->data_store, posn);
1370 /* Delete the cases currently selected in the data sheet */
1372 psppire_data_editor_delete_cases (PsppireDataEditor *de)
1374 gint first = PSPPIRE_SHEET (de->data_sheet[0])->range.row0;
1375 gint n = PSPPIRE_SHEET (de->data_sheet[0])->range.rowi - first + 1;
1377 psppire_data_store_delete_cases (de->data_store, first, n);
1379 psppire_sheet_unselect_range (PSPPIRE_SHEET (de->data_sheet[0]));
1382 /* Delete the variables currently selected in the
1383 datasheet or variable sheet */
1385 psppire_data_editor_delete_variables (PsppireDataEditor *de)
1387 PsppireDict *dict = NULL;
1390 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
1392 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
1393 first = PSPPIRE_SHEET (de->data_sheet[0])->range.col0;
1394 n = PSPPIRE_SHEET (de->data_sheet[0])->range.coli - first + 1;
1396 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
1397 first = PSPPIRE_SHEET (de->var_sheet)->range.row0;
1398 n = PSPPIRE_SHEET (de->var_sheet)->range.rowi - first + 1;
1401 g_assert_not_reached ();
1405 g_object_get (de->var_store, "dictionary", &dict, NULL);
1407 psppire_dict_delete_variables (dict, first, n);
1409 psppire_sheet_unselect_range (PSPPIRE_SHEET (de->data_sheet[0]));
1410 psppire_sheet_unselect_range (PSPPIRE_SHEET (de->var_sheet));
1415 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
1417 psppire_sheet_show_grid (PSPPIRE_SHEET (de->var_sheet), grid_visible);
1418 psppire_sheet_show_grid (PSPPIRE_SHEET (de->data_sheet[0]), grid_visible);
1423 set_font (GtkWidget *w, gpointer data)
1425 PangoFontDescription *font_desc = data;
1426 GtkRcStyle *style = gtk_widget_get_modifier_style (w);
1428 pango_font_description_free (style->font_desc);
1429 style->font_desc = pango_font_description_copy (font_desc);
1431 gtk_widget_modify_style (w, style);
1433 if ( GTK_IS_CONTAINER (w))
1434 gtk_container_foreach (GTK_CONTAINER (w), set_font, font_desc);
1438 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
1440 set_font (GTK_WIDGET (de), font_desc);
1448 emit_selected_signal (PsppireDataEditor *de)
1450 gboolean data_selected = data_is_selected (de);
1452 g_signal_emit (de, data_editor_signals[DATA_SELECTION_CHANGED], 0, data_selected);
1457 on_activate (PsppireDataEditor *de)
1460 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
1463 if ( row < psppire_data_store_get_case_count (de->data_store)
1465 col < psppire_var_store_get_var_cnt (de->var_store))
1467 emit_selected_signal (de);
1471 emit_selected_signal (de);
1476 on_select_range (PsppireDataEditor *de)
1478 PsppireSheetRange range;
1480 psppire_sheet_get_selected_range (PSPPIRE_SHEET (de->data_sheet[0]), &range);
1482 if ( range.rowi < psppire_data_store_get_case_count (de->data_store)
1484 range.coli < psppire_var_store_get_var_cnt (de->var_store))
1486 emit_selected_signal (de);
1490 emit_selected_signal (de);
1495 on_switch_page (PsppireDataEditor *de, GtkNotebookPage *p,
1496 gint pagenum, gpointer data)
1500 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
1501 gtk_widget_grab_focus (de->data_vbox);
1502 on_select_range (de);
1504 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
1505 gtk_widget_grab_focus (de->var_sheet);
1506 emit_selected_signal (de);
1518 data_is_selected (PsppireDataEditor *de)
1520 PsppireSheetRange range;
1523 if ( gtk_notebook_get_current_page (GTK_NOTEBOOK (de)) != PSPPIRE_DATA_EDITOR_DATA_VIEW)
1526 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
1528 if ( row >= psppire_data_store_get_case_count (de->data_store)
1530 col >= psppire_var_store_get_var_cnt (de->var_store))
1535 psppire_sheet_get_selected_range (PSPPIRE_SHEET (de->data_sheet[0]), &range);
1537 if ( range.rowi >= psppire_data_store_get_case_count (de->data_store)
1539 range.coli >= psppire_var_store_get_var_cnt (de->var_store))
1549 on_select_row (PsppireSheet *sheet, gint row, PsppireDataEditor *de)
1551 g_signal_emit (de, data_editor_signals[CASES_SELECTED], 0, row);
1556 on_select_variable (PsppireSheet *sheet, gint var, PsppireDataEditor *de)
1558 g_signal_emit (de, data_editor_signals[VARIABLES_SELECTED], 0, var);
1564 /* Clipboard stuff */
1567 #include <data/casereader.h>
1568 #include <data/case-map.h>
1569 #include <data/casewriter.h>
1571 #include <data/data-out.h>
1574 /* A casereader and dictionary holding the data currently in the clip */
1575 static struct casereader *clip_datasheet = NULL;
1576 static struct dictionary *clip_dict = NULL;
1579 static void data_sheet_update_clipboard (PsppireSheet *);
1581 /* Set the clip according to the currently
1582 selected range in the data sheet */
1584 data_sheet_set_clip (PsppireSheet *sheet)
1587 struct casewriter *writer ;
1588 PsppireSheetRange range;
1589 PsppireDataStore *ds;
1590 struct case_map *map = NULL;
1591 casenumber max_rows;
1596 ds = PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
1598 psppire_sheet_get_selected_range (sheet, &range);
1600 col0 = MIN (range.col0, range.coli);
1601 coli = MAX (range.col0, range.coli);
1602 row0 = MIN (range.row0, range.rowi);
1603 rowi = MAX (range.row0, range.rowi);
1605 /* If nothing selected, then use active cell */
1606 if ( row0 < 0 || col0 < 0 )
1609 psppire_sheet_get_active_cell (sheet, &row, &col);
1615 /* The sheet range can include cells that do not include data.
1616 Exclude them from the range. */
1617 max_rows = psppire_data_store_get_case_count (ds);
1618 if (rowi >= max_rows)
1622 rowi = max_rows - 1;
1624 max_columns = dict_get_var_cnt (ds->dict->dict);
1625 if (coli >= max_columns)
1627 if (max_columns == 0)
1629 coli = max_columns - 1;
1632 /* Destroy any existing clip */
1633 if ( clip_datasheet )
1635 casereader_destroy (clip_datasheet);
1636 clip_datasheet = NULL;
1641 dict_destroy (clip_dict);
1645 /* Construct clip dictionary. */
1646 clip_dict = dict_create (dict_get_encoding (ds->dict->dict));
1647 for (i = col0; i <= coli; i++)
1648 dict_clone_var_assert (clip_dict, dict_get_var (ds->dict->dict, i));
1650 /* Construct clip data. */
1651 map = case_map_by_name (ds->dict->dict, clip_dict);
1652 writer = autopaging_writer_create (dict_get_proto (clip_dict));
1653 for (i = row0; i <= rowi ; ++i )
1655 struct ccase *old = psppire_data_store_get_case (ds, i);
1657 casewriter_write (writer, case_map_execute (map, old));
1659 casewriter_force_error (writer);
1661 case_map_destroy (map);
1663 clip_datasheet = casewriter_make_reader (writer);
1665 data_sheet_update_clipboard (sheet);
1675 /* Perform data_out for case CC, variable V, appending to STRING */
1677 data_out_g_string (GString *string, const struct variable *v,
1678 const struct ccase *cc)
1680 const struct fmt_spec *fs = var_get_print_format (v);
1681 const union value *val = case_data (cc, v);
1683 char *s = data_out (val, var_get_encoding (v), fs);
1685 g_string_append (string, s);
1696 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
1697 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
1698 const size_t var_cnt = dict_get_var_cnt (clip_dict);
1700 string = g_string_sized_new (10 * val_cnt * case_cnt);
1702 for (r = 0 ; r < case_cnt ; ++r )
1707 cc = casereader_peek (clip_datasheet, r);
1710 g_warning ("Clipboard seems to have inexplicably shrunk");
1714 for (c = 0 ; c < var_cnt ; ++c)
1716 const struct variable *v = dict_get_var (clip_dict, c);
1717 data_out_g_string (string, v, cc);
1718 if ( c < val_cnt - 1 )
1719 g_string_append (string, "\t");
1723 g_string_append (string, "\n");
1738 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
1739 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
1740 const size_t var_cnt = dict_get_var_cnt (clip_dict);
1742 /* Guestimate the size needed */
1743 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
1745 g_string_append (string,
1746 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
1748 g_string_append (string, "<table>\n");
1749 for (r = 0 ; r < case_cnt ; ++r )
1752 struct ccase *cc = casereader_peek (clip_datasheet, r);
1755 g_warning ("Clipboard seems to have inexplicably shrunk");
1758 g_string_append (string, "<tr>\n");
1760 for (c = 0 ; c < var_cnt ; ++c)
1762 const struct variable *v = dict_get_var (clip_dict, c);
1763 g_string_append (string, "<td>");
1764 data_out_g_string (string, v, cc);
1765 g_string_append (string, "</td>\n");
1768 g_string_append (string, "</tr>\n");
1772 g_string_append (string, "</table>\n");
1780 clipboard_get_cb (GtkClipboard *clipboard,
1781 GtkSelectionData *selection_data,
1785 GString *string = NULL;
1789 case SELECT_FMT_TEXT:
1790 string = clip_to_text ();
1792 case SELECT_FMT_HTML:
1793 string = clip_to_html ();
1796 g_assert_not_reached ();
1799 gtk_selection_data_set (selection_data, selection_data->target,
1801 (const guchar *) string->str, string->len);
1803 g_string_free (string, TRUE);
1807 clipboard_clear_cb (GtkClipboard *clipboard,
1810 dict_destroy (clip_dict);
1813 casereader_destroy (clip_datasheet);
1814 clip_datasheet = NULL;
1818 static const GtkTargetEntry targets[] = {
1819 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
1820 { "STRING", 0, SELECT_FMT_TEXT },
1821 { "TEXT", 0, SELECT_FMT_TEXT },
1822 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
1823 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
1824 { "text/plain", 0, SELECT_FMT_TEXT },
1825 { "text/html", 0, SELECT_FMT_HTML }
1831 data_sheet_update_clipboard (PsppireSheet *sheet)
1833 GtkClipboard *clipboard =
1834 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
1835 GDK_SELECTION_CLIPBOARD);
1837 if (!gtk_clipboard_set_with_owner (clipboard, targets,
1838 G_N_ELEMENTS (targets),
1839 clipboard_get_cb, clipboard_clear_cb,
1841 clipboard_clear_cb (clipboard, sheet);
1846 /* A callback for when the clipboard contents have been received */
1848 data_sheet_contents_received_callback (GtkClipboard *clipboard,
1849 GtkSelectionData *sd,
1854 gint next_row, next_column;
1857 PsppireDataEditor *data_editor = data;
1859 if ( sd->length < 0 )
1862 if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE))
1865 c = (char *) sd->data;
1867 /* Paste text to selected position */
1868 psppire_sheet_get_active_cell (PSPPIRE_SHEET (data_editor->data_sheet[0]),
1871 g_return_if_fail (row >= 0);
1872 g_return_if_fail (column >= 0);
1874 first_column = column;
1876 next_column = column;
1877 while (count < sd->length)
1882 column = next_column;
1883 while (*c != '\t' && *c != '\n' && count < sd->length)
1891 next_column = column + 1;
1893 else if ( *c == '\n')
1896 next_column = first_column;
1902 /* Append some new cases if pasting beyond the last row */
1903 if ( row >= psppire_data_store_get_case_count (data_editor->data_store))
1904 psppire_data_store_insert_new_case (data_editor->data_store, row);
1906 psppire_data_store_set_string (data_editor->data_store, s, row, column);
1912 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
1915 gboolean compatible_target = FALSE;
1916 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
1918 for (i = 0 ; i < sizeof (targets) / sizeof(targets[0]) ; ++i )
1920 GdkAtom atom = gdk_atom_intern (targets[i].target, TRUE);
1921 if ( gtk_clipboard_wait_is_target_available (clip, atom))
1923 compatible_target = TRUE;
1928 g_signal_emit (de, data_editor_signals[DATA_AVAILABLE_CHANGED], 0,