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:
388 de->data_window = g_value_get_pointer (value);
389 g_object_ref (de->data_window);
391 case PROP_DATA_STORE:
392 if ( de->data_store) g_object_unref (de->data_store);
393 de->data_store = g_value_get_pointer (value);
394 g_object_ref (de->data_store);
396 for (i = 0 ; i < 4 ; ++i )
398 g_object_set (de->data_sheet[i],
399 "model", de->data_store,
402 g_signal_connect_swapped (de->data_store->dict, "filter-changed",
403 G_CALLBACK (gtk_widget_queue_draw),
407 g_signal_connect (de->data_store->dict, "backend-changed",
408 G_CALLBACK (new_variables_callback), de);
410 g_signal_connect (de->data_store->dict, "variable-inserted",
411 G_CALLBACK (insert_variable_callback), de);
413 g_signal_connect (de->data_store->dict, "variable-deleted",
414 G_CALLBACK (delete_variable_callback), de);
416 g_signal_connect (de->data_store->dict, "variable-display-width-changed",
417 G_CALLBACK (rewidth_variable_callback), de);
419 g_signal_connect (de->data_store, "backend-changed",
420 G_CALLBACK (new_data_callback), de);
422 g_signal_connect (de->data_store, "case-inserted",
423 G_CALLBACK (case_inserted_callback), de);
425 g_signal_connect (de->data_store, "cases-deleted",
426 G_CALLBACK (cases_deleted_callback), de);
430 if ( de->var_store) g_object_unref (de->var_store);
431 de->var_store = g_value_get_pointer (value);
432 g_object_ref (de->var_store);
434 g_object_set (de->var_sheet,
435 "model", de->var_store,
438 case PROP_VS_ROW_MENU:
440 GObject *menu = g_value_get_object (value);
442 g_signal_connect (de->var_sheet, "button-event-row",
443 G_CALLBACK (popup_variable_row_menu), menu);
446 case PROP_DS_COLUMN_MENU:
448 GObject *menu = g_value_get_object (value);
450 g_signal_connect (de->data_sheet[0], "button-event-column",
451 G_CALLBACK (popup_variable_column_menu), menu);
454 case PROP_DS_ROW_MENU:
456 GObject *menu = g_value_get_object (value);
458 g_signal_connect (de->data_sheet[0], "button-event-row",
459 G_CALLBACK (popup_cases_menu), menu);
462 case PROP_CURRENT_VAR:
465 gint var = g_value_get_long (value);
466 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (object)))
468 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
469 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
470 psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), row, var);
471 psppire_sheet_moveto (PSPPIRE_SHEET (de->data_sheet[0]), -1, var, 0.5, 0.5);
473 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
474 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->var_sheet), &row, &col);
475 psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->var_sheet), var, col);
476 psppire_sheet_moveto (PSPPIRE_SHEET (de->var_sheet), var, -1, 0.5, 0.5);
479 g_assert_not_reached ();
484 case PROP_CURRENT_CASE:
487 gint case_num = g_value_get_long (value);
488 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
489 psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), case_num, col);
490 psppire_sheet_moveto (PSPPIRE_SHEET (de->data_sheet[0]), case_num, -1, 0.5, 0.5);
493 case PROP_VALUE_LABELS:
495 psppire_data_store_show_labels (de->data_store,
496 g_value_get_boolean (value));
500 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
506 psppire_data_editor_get_property (GObject *object,
511 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
515 case PROP_SPLIT_WINDOW:
516 g_value_set_boolean (value, de->split);
518 case PROP_DATA_WINDOW:
519 g_value_set_pointer (value, de->data_window);
521 case PROP_DATA_STORE:
522 g_value_set_pointer (value, de->data_store);
525 g_value_set_pointer (value, de->var_store);
527 case PROP_CURRENT_CASE:
530 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &column);
531 g_value_set_long (value, row);
534 case PROP_CURRENT_VAR:
537 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &column);
538 g_value_set_long (value, column);
541 case PROP_DATA_SELECTED:
542 g_value_set_boolean (value, data_is_selected (de));
545 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
552 psppire_data_editor_class_init (PsppireDataEditorClass *klass)
554 GParamSpec *data_window_spec ;
555 GParamSpec *data_store_spec ;
556 GParamSpec *var_store_spec ;
557 GParamSpec *column_menu_spec;
558 GParamSpec *ds_row_menu_spec;
559 GParamSpec *vs_row_menu_spec;
560 GParamSpec *value_labels_spec;
561 GParamSpec *current_case_spec;
562 GParamSpec *current_var_spec;
563 GParamSpec *data_selected_spec;
564 GParamSpec *split_window_spec;
565 GObjectClass *object_class = G_OBJECT_CLASS (klass);
567 parent_class = g_type_class_peek_parent (klass);
569 object_class->dispose = psppire_data_editor_dispose;
570 object_class->finalize = psppire_data_editor_finalize;
572 object_class->set_property = psppire_data_editor_set_property;
573 object_class->get_property = psppire_data_editor_get_property;
578 g_param_spec_pointer ("data-window",
580 "A pointer to the data window associated with this editor",
581 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
583 g_object_class_install_property (object_class,
588 g_param_spec_pointer ("data-store",
590 "A pointer to the data store associated with this editor",
591 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
593 g_object_class_install_property (object_class,
598 g_param_spec_pointer ("var-store",
600 "A pointer to the variable store associated with this editor",
601 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
603 g_object_class_install_property (object_class,
608 g_param_spec_object ("datasheet-column-menu",
609 "Data Sheet Column Menu",
610 "A menu to be displayed when button 3 is pressed in thedata sheet's column title buttons",
614 g_object_class_install_property (object_class,
620 g_param_spec_object ("datasheet-row-menu",
621 "Data Sheet Row Menu",
622 "A menu to be displayed when button 3 is pressed in the data sheet's row title buttons",
626 g_object_class_install_property (object_class,
632 g_param_spec_object ("varsheet-row-menu",
633 "Variable Sheet Row Menu",
634 "A menu to be displayed when button 3 is pressed in the variable sheet's row title buttons",
638 g_object_class_install_property (object_class,
644 g_param_spec_boolean ("value-labels",
646 "Whether or not the data sheet should display labels instead of values",
648 G_PARAM_WRITABLE | G_PARAM_READABLE);
650 g_object_class_install_property (object_class,
656 g_param_spec_long ("current-case",
658 "Zero based number of the selected case",
661 G_PARAM_WRITABLE | G_PARAM_READABLE);
663 g_object_class_install_property (object_class,
669 g_param_spec_long ("current-variable",
671 "Zero based number of the selected variable",
674 G_PARAM_WRITABLE | G_PARAM_READABLE);
676 g_object_class_install_property (object_class,
682 g_param_spec_boolean ("data-selected",
684 "True iff the data view is active and one or more cells of data have been selected.",
688 g_object_class_install_property (object_class,
695 g_param_spec_boolean ("split",
697 "True iff the data sheet is split",
699 G_PARAM_READABLE | G_PARAM_WRITABLE);
701 g_object_class_install_property (object_class,
705 data_editor_signals [DATA_SELECTION_CHANGED] =
706 g_signal_new ("data-selection-changed",
707 G_TYPE_FROM_CLASS (klass),
711 g_cclosure_marshal_VOID__BOOLEAN,
716 data_editor_signals [CASES_SELECTED] =
717 g_signal_new ("cases-selected",
718 G_TYPE_FROM_CLASS (klass),
722 g_cclosure_marshal_VOID__INT,
728 data_editor_signals [VARIABLES_SELECTED] =
729 g_signal_new ("variables-selected",
730 G_TYPE_FROM_CLASS (klass),
734 g_cclosure_marshal_VOID__INT,
740 data_editor_signals [DATA_AVAILABLE_CHANGED] =
741 g_signal_new ("data-available-changed",
742 G_TYPE_FROM_CLASS (klass),
746 g_cclosure_marshal_VOID__BOOLEAN,
752 /* Update the data_ref_entry with the reference of the active cell */
754 update_data_ref_entry (const PsppireSheet *sheet,
756 gint old_row, gint old_col,
759 PsppireDataEditor *de = data;
761 PsppireDataStore *data_store =
762 PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
766 const struct variable *var =
767 psppire_dict_get_variable (data_store->dict, col);
771 gchar *text = g_strdup_printf ("%d: %s", row + FIRST_CASE_NUMBER,
775 gtk_entry_set_text (GTK_ENTRY (de->cell_ref_entry), text);
785 psppire_data_store_get_string (data_store, row,
786 var_get_dict_index(var));
793 gtk_entry_set_text (GTK_ENTRY (de->datum_entry), text);
805 gtk_entry_set_text (GTK_ENTRY (de->datum_entry), "");
812 datum_entry_activate (GtkEntry *entry, gpointer data)
815 PsppireDataEditor *de = data;
817 const gchar *text = gtk_entry_get_text (entry);
819 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &column);
821 if ( row == -1 || column == -1)
824 psppire_data_store_set_string (de->data_store, text, row, column);
827 static void on_activate (PsppireDataEditor *de);
828 static gboolean on_switch_page (PsppireDataEditor *de, GtkNotebookPage *p, gint pagenum, gpointer data);
829 static void on_select_range (PsppireDataEditor *de);
831 static void on_select_row (PsppireSheet *, gint, PsppireDataEditor *);
832 static void on_select_variable (PsppireSheet *, gint, PsppireDataEditor *);
835 static void on_owner_change (GtkClipboard *,
836 GdkEventOwnerChange *, gpointer);
839 on_map (GtkWidget *w)
841 GtkClipboard *clip = gtk_widget_get_clipboard (w, GDK_SELECTION_CLIPBOARD);
843 g_signal_connect (clip, "owner-change", G_CALLBACK (on_owner_change), w);
848 init_sheet (PsppireDataEditor *de, int i,
849 GtkAdjustment *hadj, GtkAdjustment *vadj,
854 de->sheet_bin[i] = gtk_scrolled_window_new (hadj, vadj);
856 de->data_sheet[i] = psppire_sheet_new (NULL);
858 g_object_set (de->sheet_bin[i],
860 "shadow-type", GTK_SHADOW_ETCHED_IN,
863 g_object_set (haxis, "default-size", 75, NULL);
864 g_object_set (vaxis, "default-size", 25, NULL);
866 g_object_set (de->data_sheet[i],
867 "horizontal-axis", haxis,
868 "vertical-axis", vaxis,
871 gtk_container_add (GTK_CONTAINER (de->sheet_bin[i]), de->data_sheet[i]);
873 g_signal_connect (de->data_sheet[i], "traverse",
874 G_CALLBACK (traverse_cell_callback), de);
876 gtk_widget_show (de->sheet_bin[i]);
881 init_data_sheet (PsppireDataEditor *de)
883 GtkAdjustment *vadj0, *hadj0;
884 GtkAdjustment *vadj1, *hadj1;
887 de->vaxis[0] = psppire_axis_new ();
888 de->vaxis[1] = psppire_axis_new ();
890 /* There's only one horizontal axis, since the
891 column widths are parameters of the variables */
892 de->haxis = psppire_axis_new ();
895 de->paned = gtk_xpaned_new ();
897 init_sheet (de, 0, NULL, NULL, de->vaxis[0], de->haxis);
898 gtk_widget_show (de->sheet_bin[0]);
899 vadj0 = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[0]));
900 hadj0 = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[0]));
902 g_object_set (de->sheet_bin[0], "vscrollbar-policy", GTK_POLICY_NEVER, NULL);
903 g_object_set (de->sheet_bin[0], "hscrollbar-policy", GTK_POLICY_NEVER, NULL);
905 init_sheet (de, 1, NULL, vadj0, de->vaxis[0], de->haxis);
906 gtk_widget_show (de->sheet_bin[1]);
907 sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[1]));
908 psppire_sheet_hide_row_titles (PSPPIRE_SHEET (sheet));
909 hadj1 = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[1]));
910 g_object_set (de->sheet_bin[1], "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
911 g_object_set (de->sheet_bin[1], "hscrollbar-policy", GTK_POLICY_NEVER, NULL);
913 init_sheet (de, 2, hadj0, NULL, de->vaxis[1], de->haxis);
914 gtk_widget_show (de->sheet_bin[2]);
915 sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[2]));
916 psppire_sheet_hide_column_titles (PSPPIRE_SHEET (sheet));
917 g_object_set (de->sheet_bin[2], "vscrollbar-policy", GTK_POLICY_NEVER, NULL);
918 g_object_set (de->sheet_bin[2], "hscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
919 vadj1 = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[2]));
921 init_sheet (de, 3, hadj1, vadj1, de->vaxis[1], de->haxis);
922 gtk_widget_show (de->sheet_bin[3]);
923 sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[3]));
924 psppire_sheet_hide_column_titles (PSPPIRE_SHEET (sheet));
925 psppire_sheet_hide_row_titles (PSPPIRE_SHEET (sheet));
926 g_object_set (de->sheet_bin[3], "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
927 g_object_set (de->sheet_bin[3], "hscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
929 gtk_xpaned_pack_top_left (GTK_XPANED (de->paned), de->sheet_bin[0], TRUE, TRUE);
930 gtk_xpaned_pack_top_right (GTK_XPANED (de->paned), de->sheet_bin[1], TRUE, TRUE);
931 gtk_xpaned_pack_bottom_left (GTK_XPANED (de->paned), de->sheet_bin[2], TRUE, TRUE);
932 gtk_xpaned_pack_bottom_right (GTK_XPANED (de->paned), de->sheet_bin[3], TRUE, TRUE);
934 gtk_xpaned_set_position_y (GTK_XPANED (de->paned), 150);
935 gtk_xpaned_set_position_x (GTK_XPANED (de->paned), 350);
940 psppire_data_editor_init (PsppireDataEditor *de)
942 GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
943 GtkWidget *sw_vs = gtk_scrolled_window_new (NULL, NULL);
945 init_data_sheet (de);
947 de->data_vbox = gtk_vbox_new (FALSE, 0);
948 de->var_sheet = psppire_var_sheet_new ();
950 g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
952 de->datum_entry = gtk_entry_new ();
953 de->cell_ref_entry = gtk_entry_new ();
955 g_object_set (de->cell_ref_entry,
961 gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_entry, FALSE, FALSE, 0);
962 gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0);
965 gtk_container_add (GTK_CONTAINER (sw_vs), de->var_sheet);
966 gtk_widget_show_all (sw_vs);
969 gtk_box_pack_start (GTK_BOX (de->data_vbox), hbox, FALSE, FALSE, 0);
970 gtk_box_pack_start (GTK_BOX (de->data_vbox), de->paned, TRUE, TRUE, 0);
973 psppire_data_editor_remove_split (de);
975 gtk_widget_show_all (de->data_vbox);
977 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->data_vbox,
978 gtk_label_new_with_mnemonic (_("Data View")));
980 gtk_notebook_append_page (GTK_NOTEBOOK (de), sw_vs,
981 gtk_label_new_with_mnemonic (_("Variable View")));
983 g_signal_connect (de->data_sheet[0], "activate",
984 G_CALLBACK (update_data_ref_entry),
987 g_signal_connect (de->datum_entry, "activate",
988 G_CALLBACK (datum_entry_activate),
992 g_signal_connect_swapped (de->data_sheet[0],
993 "double-click-column",
994 G_CALLBACK (on_data_column_clicked),
997 g_signal_connect_swapped (de->var_sheet,
999 G_CALLBACK (on_var_row_clicked),
1002 g_signal_connect_swapped (de->data_sheet[0], "activate",
1003 G_CALLBACK (on_activate),
1006 g_signal_connect_swapped (de->data_sheet[0], "select-range",
1007 G_CALLBACK (on_select_range),
1010 g_signal_connect (de->data_sheet[0], "select-row",
1011 G_CALLBACK (on_select_row), de);
1013 g_signal_connect (de->data_sheet[0], "select-column",
1014 G_CALLBACK (on_select_variable), de);
1017 g_signal_connect (de->var_sheet, "select-row",
1018 G_CALLBACK (on_select_variable), de);
1021 g_signal_connect_after (de, "switch-page",
1022 G_CALLBACK (on_switch_page),
1025 g_object_set (de, "can-focus", FALSE, NULL);
1027 g_signal_connect (de, "map", G_CALLBACK (on_map), NULL);
1030 // psppire_sheet_hide_column_titles (de->var_sheet);
1031 // psppire_sheet_hide_row_titles (de->data_sheet);
1034 de->dispose_has_run = FALSE;
1039 psppire_data_editor_new (PsppireDataWindow *data_window,
1040 PsppireVarStore *var_store,
1041 PsppireDataStore *data_store)
1043 return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
1044 "data-window", data_window,
1045 "var-store", var_store,
1046 "data-store", data_store,
1052 psppire_data_editor_remove_split (PsppireDataEditor *de)
1054 if ( !de->split ) return;
1057 g_object_ref (de->sheet_bin[0]);
1058 gtk_container_remove (GTK_CONTAINER (de->paned), de->sheet_bin[0]);
1060 g_object_ref (de->paned);
1061 gtk_container_remove (GTK_CONTAINER (de->data_vbox), de->paned);
1063 gtk_box_pack_start (GTK_BOX (de->data_vbox), de->sheet_bin[0],
1066 g_object_unref (de->sheet_bin[0]);
1068 g_object_set (de->sheet_bin[0], "vscrollbar-policy",
1069 GTK_POLICY_ALWAYS, NULL);
1071 g_object_set (de->sheet_bin[0], "hscrollbar-policy",
1072 GTK_POLICY_ALWAYS, NULL);
1077 psppire_data_editor_set_split (PsppireDataEditor *de)
1079 if ( de->split ) return;
1082 g_object_ref (de->sheet_bin[0]);
1083 gtk_container_remove (GTK_CONTAINER (de->data_vbox), de->sheet_bin[0]);
1085 gtk_xpaned_pack_top_left (GTK_XPANED (de->paned), de->sheet_bin [0],
1088 gtk_box_pack_start (GTK_BOX (de->data_vbox), de->paned,
1091 g_object_unref (de->paned);
1093 g_object_set (de->sheet_bin[0], "vscrollbar-policy",
1094 GTK_POLICY_NEVER, NULL);
1096 g_object_set (de->sheet_bin[0], "hscrollbar-policy",
1097 GTK_POLICY_NEVER, NULL);
1101 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
1104 psppire_data_editor_set_split (de);
1106 psppire_data_editor_remove_split (de);
1108 gtk_widget_show_all (de->data_vbox);
1111 static void data_sheet_set_clip (PsppireSheet *sheet);
1112 static void data_sheet_contents_received_callback (GtkClipboard *clipboard,
1113 GtkSelectionData *sd,
1118 psppire_data_editor_clip_copy (PsppireDataEditor *de)
1120 data_sheet_set_clip (PSPPIRE_SHEET (de->data_sheet[0]));
1124 psppire_data_editor_clip_paste (PsppireDataEditor *de)
1126 GdkDisplay *display = gtk_widget_get_display ( GTK_WIDGET (de));
1127 GtkClipboard *clipboard =
1128 gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
1130 gtk_clipboard_request_contents (clipboard,
1131 gdk_atom_intern ("UTF8_STRING", TRUE),
1132 data_sheet_contents_received_callback,
1139 psppire_data_editor_clip_cut (PsppireDataEditor *de)
1141 gint max_rows, max_columns;
1143 PsppireSheetRange range;
1144 PsppireDataStore *ds = de->data_store;
1146 data_sheet_set_clip (PSPPIRE_SHEET (de->data_sheet[0]));
1148 /* Now blank all the cells */
1149 psppire_sheet_get_selected_range (PSPPIRE_SHEET (de->data_sheet[0]), &range);
1151 /* If nothing selected, then use active cell */
1152 if ( range.row0 < 0 || range.col0 < 0 )
1155 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
1157 range.row0 = range.rowi = row;
1158 range.col0 = range.coli = col;
1161 /* The sheet range can include cells that do not include data.
1162 Exclude them from the range. */
1163 max_rows = psppire_data_store_get_case_count (ds);
1164 if (range.rowi >= max_rows)
1168 range.rowi = max_rows - 1;
1171 max_columns = dict_get_var_cnt (ds->dict->dict);
1172 if (range.coli >= max_columns)
1174 if (max_columns == 0)
1176 range.coli = max_columns - 1;
1179 g_return_if_fail (range.rowi >= range.row0);
1180 g_return_if_fail (range.row0 >= 0);
1181 g_return_if_fail (range.coli >= range.col0);
1182 g_return_if_fail (range.col0 >= 0);
1185 for (r = range.row0; r <= range.rowi ; ++r )
1189 for (c = range.col0 ; c <= range.coli; ++c)
1191 psppire_data_store_set_string (ds, "", r, c);
1195 /* and remove the selection */
1196 psppire_sheet_unselect_range (PSPPIRE_SHEET (de->data_sheet[0]));
1202 /* Popup menu related stuff */
1205 popup_variable_column_menu (PsppireSheet *sheet, gint column,
1206 GdkEventButton *event, gpointer data)
1208 GtkMenu *menu = GTK_MENU (data);
1210 PsppireDataStore *data_store =
1211 PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
1213 const struct variable *v =
1214 psppire_dict_get_variable (data_store->dict, column);
1216 if ( v && event->button == 3)
1218 psppire_sheet_select_column (sheet, column);
1220 gtk_menu_popup (menu,
1221 NULL, NULL, NULL, NULL,
1222 event->button, event->time);
1228 popup_variable_row_menu (PsppireSheet *sheet, gint row,
1229 GdkEventButton *event, gpointer data)
1231 GtkMenu *menu = GTK_MENU (data);
1233 PsppireVarStore *var_store =
1234 PSPPIRE_VAR_STORE (psppire_sheet_get_model (sheet));
1237 const struct variable *v ;
1239 g_object_get (var_store, "dictionary", &dict, NULL);
1241 v = psppire_dict_get_variable (dict, row);
1243 if ( v && event->button == 3)
1245 psppire_sheet_select_row (sheet, row);
1247 gtk_menu_popup (menu,
1248 NULL, NULL, NULL, NULL,
1249 event->button, event->time);
1255 popup_cases_menu (PsppireSheet *sheet, gint row,
1256 GdkEventButton *event, gpointer data)
1258 GtkMenu *menu = GTK_MENU (data);
1260 PsppireDataStore *data_store =
1261 PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
1263 if ( row <= psppire_data_store_get_case_count (data_store) &&
1266 psppire_sheet_select_row (sheet, row);
1268 gtk_menu_popup (menu,
1269 NULL, NULL, NULL, NULL,
1270 event->button, event->time);
1279 do_sort (PsppireDataEditor *de, int var, gboolean descend)
1281 const struct variable *v
1282 = psppire_dict_get_variable (de->data_store->dict, var);
1285 syntax = g_strdup_printf ("SORT CASES BY %s%s.",
1286 var_get_name (v), descend ? " (D)" : "");
1287 g_free (execute_syntax_string (de->data_window, syntax));
1291 /* Sort the data by the the variable which the editor has currently
1294 psppire_data_editor_sort_ascending (PsppireDataEditor *de)
1296 PsppireSheetRange range;
1297 psppire_sheet_get_selected_range (PSPPIRE_SHEET(de->data_sheet[0]), &range);
1299 do_sort (de, range.col0, FALSE);
1303 /* Sort the data by the the variable which the editor has currently
1306 psppire_data_editor_sort_descending (PsppireDataEditor *de)
1308 PsppireSheetRange range;
1309 psppire_sheet_get_selected_range (PSPPIRE_SHEET(de->data_sheet[0]), &range);
1311 do_sort (de, range.col0, TRUE);
1318 /* Insert a new variable before the currently selected position */
1320 psppire_data_editor_insert_variable (PsppireDataEditor *de)
1324 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
1326 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
1327 if ( PSPPIRE_SHEET (de->data_sheet[0])->select_status
1328 == PSPPIRE_SHEET_COLUMN_SELECTED )
1329 posn = PSPPIRE_SHEET (de->data_sheet[0])->range.col0;
1331 posn = PSPPIRE_SHEET (de->data_sheet[0])->active_cell.col;
1333 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
1334 if ( PSPPIRE_SHEET (de->var_sheet)->select_status
1335 == PSPPIRE_SHEET_ROW_SELECTED )
1336 posn = PSPPIRE_SHEET (de->var_sheet)->range.row0;
1338 posn = PSPPIRE_SHEET (de->var_sheet)->active_cell.row;
1341 g_assert_not_reached ();
1345 psppire_dict_insert_variable (de->data_store->dict, posn, NULL);
1348 /* Insert a new case before the currently selected position */
1350 psppire_data_editor_insert_case (PsppireDataEditor *de)
1354 if ( PSPPIRE_SHEET (de->data_sheet[0])->select_status == PSPPIRE_SHEET_ROW_SELECTED )
1356 posn = PSPPIRE_SHEET (de->data_sheet[0])->range.row0;
1360 posn = PSPPIRE_SHEET (de->data_sheet[0])->active_cell.row;
1363 if ( posn == -1 ) posn = 0;
1365 psppire_data_store_insert_new_case (de->data_store, posn);
1368 /* Delete the cases currently selected in the data sheet */
1370 psppire_data_editor_delete_cases (PsppireDataEditor *de)
1372 gint first = PSPPIRE_SHEET (de->data_sheet[0])->range.row0;
1373 gint n = PSPPIRE_SHEET (de->data_sheet[0])->range.rowi - first + 1;
1375 psppire_data_store_delete_cases (de->data_store, first, n);
1377 psppire_sheet_unselect_range (PSPPIRE_SHEET (de->data_sheet[0]));
1380 /* Delete the variables currently selected in the
1381 datasheet or variable sheet */
1383 psppire_data_editor_delete_variables (PsppireDataEditor *de)
1385 PsppireDict *dict = NULL;
1388 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
1390 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
1391 first = PSPPIRE_SHEET (de->data_sheet[0])->range.col0;
1392 n = PSPPIRE_SHEET (de->data_sheet[0])->range.coli - first + 1;
1394 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
1395 first = PSPPIRE_SHEET (de->var_sheet)->range.row0;
1396 n = PSPPIRE_SHEET (de->var_sheet)->range.rowi - first + 1;
1399 g_assert_not_reached ();
1403 g_object_get (de->var_store, "dictionary", &dict, NULL);
1405 psppire_dict_delete_variables (dict, first, n);
1407 psppire_sheet_unselect_range (PSPPIRE_SHEET (de->data_sheet[0]));
1408 psppire_sheet_unselect_range (PSPPIRE_SHEET (de->var_sheet));
1413 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
1415 psppire_sheet_show_grid (PSPPIRE_SHEET (de->var_sheet), grid_visible);
1416 psppire_sheet_show_grid (PSPPIRE_SHEET (de->data_sheet[0]), grid_visible);
1421 set_font (GtkWidget *w, gpointer data)
1423 PangoFontDescription *font_desc = data;
1424 GtkRcStyle *style = gtk_widget_get_modifier_style (w);
1426 pango_font_description_free (style->font_desc);
1427 style->font_desc = pango_font_description_copy (font_desc);
1429 gtk_widget_modify_style (w, style);
1431 if ( GTK_IS_CONTAINER (w))
1432 gtk_container_foreach (GTK_CONTAINER (w), set_font, font_desc);
1436 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
1438 set_font (GTK_WIDGET (de), font_desc);
1446 emit_selected_signal (PsppireDataEditor *de)
1448 gboolean data_selected = data_is_selected (de);
1450 g_signal_emit (de, data_editor_signals[DATA_SELECTION_CHANGED], 0, data_selected);
1455 on_activate (PsppireDataEditor *de)
1458 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
1461 if ( row < psppire_data_store_get_case_count (de->data_store)
1463 col < psppire_var_store_get_var_cnt (de->var_store))
1465 emit_selected_signal (de);
1469 emit_selected_signal (de);
1474 on_select_range (PsppireDataEditor *de)
1476 PsppireSheetRange range;
1478 psppire_sheet_get_selected_range (PSPPIRE_SHEET (de->data_sheet[0]), &range);
1480 if ( range.rowi < psppire_data_store_get_case_count (de->data_store)
1482 range.coli < psppire_var_store_get_var_cnt (de->var_store))
1484 emit_selected_signal (de);
1488 emit_selected_signal (de);
1493 on_switch_page (PsppireDataEditor *de, GtkNotebookPage *p,
1494 gint pagenum, gpointer data)
1498 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
1499 gtk_widget_grab_focus (de->data_vbox);
1500 on_select_range (de);
1502 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
1503 gtk_widget_grab_focus (de->var_sheet);
1504 emit_selected_signal (de);
1516 data_is_selected (PsppireDataEditor *de)
1518 PsppireSheetRange range;
1521 if ( gtk_notebook_get_current_page (GTK_NOTEBOOK (de)) != PSPPIRE_DATA_EDITOR_DATA_VIEW)
1524 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
1526 if ( row >= psppire_data_store_get_case_count (de->data_store)
1528 col >= psppire_var_store_get_var_cnt (de->var_store))
1533 psppire_sheet_get_selected_range (PSPPIRE_SHEET (de->data_sheet[0]), &range);
1535 if ( range.rowi >= psppire_data_store_get_case_count (de->data_store)
1537 range.coli >= psppire_var_store_get_var_cnt (de->var_store))
1547 on_select_row (PsppireSheet *sheet, gint row, PsppireDataEditor *de)
1549 g_signal_emit (de, data_editor_signals[CASES_SELECTED], 0, row);
1554 on_select_variable (PsppireSheet *sheet, gint var, PsppireDataEditor *de)
1556 g_signal_emit (de, data_editor_signals[VARIABLES_SELECTED], 0, var);
1562 /* Clipboard stuff */
1565 #include <data/casereader.h>
1566 #include <data/case-map.h>
1567 #include <data/casewriter.h>
1569 #include <data/data-out.h>
1572 /* A casereader and dictionary holding the data currently in the clip */
1573 static struct casereader *clip_datasheet = NULL;
1574 static struct dictionary *clip_dict = NULL;
1577 static void data_sheet_update_clipboard (PsppireSheet *);
1579 /* Set the clip according to the currently
1580 selected range in the data sheet */
1582 data_sheet_set_clip (PsppireSheet *sheet)
1585 struct casewriter *writer ;
1586 PsppireSheetRange range;
1587 PsppireDataStore *ds;
1588 struct case_map *map = NULL;
1589 casenumber max_rows;
1594 ds = PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
1596 psppire_sheet_get_selected_range (sheet, &range);
1598 col0 = MIN (range.col0, range.coli);
1599 coli = MAX (range.col0, range.coli);
1600 row0 = MIN (range.row0, range.rowi);
1601 rowi = MAX (range.row0, range.rowi);
1603 /* If nothing selected, then use active cell */
1604 if ( row0 < 0 || col0 < 0 )
1607 psppire_sheet_get_active_cell (sheet, &row, &col);
1613 /* The sheet range can include cells that do not include data.
1614 Exclude them from the range. */
1615 max_rows = psppire_data_store_get_case_count (ds);
1616 if (rowi >= max_rows)
1620 rowi = max_rows - 1;
1622 max_columns = dict_get_var_cnt (ds->dict->dict);
1623 if (coli >= max_columns)
1625 if (max_columns == 0)
1627 coli = max_columns - 1;
1630 /* Destroy any existing clip */
1631 if ( clip_datasheet )
1633 casereader_destroy (clip_datasheet);
1634 clip_datasheet = NULL;
1639 dict_destroy (clip_dict);
1643 /* Construct clip dictionary. */
1644 clip_dict = dict_create (dict_get_encoding (ds->dict->dict));
1645 for (i = col0; i <= coli; i++)
1646 dict_clone_var_assert (clip_dict, dict_get_var (ds->dict->dict, i));
1648 /* Construct clip data. */
1649 map = case_map_by_name (ds->dict->dict, clip_dict);
1650 writer = autopaging_writer_create (dict_get_proto (clip_dict));
1651 for (i = row0; i <= rowi ; ++i )
1653 struct ccase *old = psppire_data_store_get_case (ds, i);
1655 casewriter_write (writer, case_map_execute (map, old));
1657 casewriter_force_error (writer);
1659 case_map_destroy (map);
1661 clip_datasheet = casewriter_make_reader (writer);
1663 data_sheet_update_clipboard (sheet);
1673 /* Perform data_out for case CC, variable V, appending to STRING */
1675 data_out_g_string (GString *string, const struct variable *v,
1676 const struct ccase *cc)
1678 const struct fmt_spec *fs = var_get_print_format (v);
1679 const union value *val = case_data (cc, v);
1681 char *s = data_out (val, var_get_encoding (v), fs);
1683 g_string_append (string, s);
1694 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
1695 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
1696 const size_t var_cnt = dict_get_var_cnt (clip_dict);
1698 string = g_string_sized_new (10 * val_cnt * case_cnt);
1700 for (r = 0 ; r < case_cnt ; ++r )
1705 cc = casereader_peek (clip_datasheet, r);
1708 g_warning ("Clipboard seems to have inexplicably shrunk");
1712 for (c = 0 ; c < var_cnt ; ++c)
1714 const struct variable *v = dict_get_var (clip_dict, c);
1715 data_out_g_string (string, v, cc);
1716 if ( c < val_cnt - 1 )
1717 g_string_append (string, "\t");
1721 g_string_append (string, "\n");
1736 const size_t val_cnt = caseproto_get_n_widths (casereader_get_proto (clip_datasheet));
1737 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
1738 const size_t var_cnt = dict_get_var_cnt (clip_dict);
1740 /* Guestimate the size needed */
1741 string = g_string_sized_new (80 + 20 * val_cnt * case_cnt);
1743 g_string_append (string,
1744 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\n");
1746 g_string_append (string, "<table>\n");
1747 for (r = 0 ; r < case_cnt ; ++r )
1750 struct ccase *cc = casereader_peek (clip_datasheet, r);
1753 g_warning ("Clipboard seems to have inexplicably shrunk");
1756 g_string_append (string, "<tr>\n");
1758 for (c = 0 ; c < var_cnt ; ++c)
1760 const struct variable *v = dict_get_var (clip_dict, c);
1761 g_string_append (string, "<td>");
1762 data_out_g_string (string, v, cc);
1763 g_string_append (string, "</td>\n");
1766 g_string_append (string, "</tr>\n");
1770 g_string_append (string, "</table>\n");
1778 clipboard_get_cb (GtkClipboard *clipboard,
1779 GtkSelectionData *selection_data,
1783 GString *string = NULL;
1787 case SELECT_FMT_TEXT:
1788 string = clip_to_text ();
1790 case SELECT_FMT_HTML:
1791 string = clip_to_html ();
1794 g_assert_not_reached ();
1797 gtk_selection_data_set (selection_data, selection_data->target,
1799 (const guchar *) string->str, string->len);
1801 g_string_free (string, TRUE);
1805 clipboard_clear_cb (GtkClipboard *clipboard,
1808 dict_destroy (clip_dict);
1811 casereader_destroy (clip_datasheet);
1812 clip_datasheet = NULL;
1816 static const GtkTargetEntry targets[] = {
1817 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
1818 { "STRING", 0, SELECT_FMT_TEXT },
1819 { "TEXT", 0, SELECT_FMT_TEXT },
1820 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
1821 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
1822 { "text/plain", 0, SELECT_FMT_TEXT },
1823 { "text/html", 0, SELECT_FMT_HTML }
1829 data_sheet_update_clipboard (PsppireSheet *sheet)
1831 GtkClipboard *clipboard =
1832 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
1833 GDK_SELECTION_CLIPBOARD);
1835 if (!gtk_clipboard_set_with_owner (clipboard, targets,
1836 G_N_ELEMENTS (targets),
1837 clipboard_get_cb, clipboard_clear_cb,
1839 clipboard_clear_cb (clipboard, sheet);
1844 /* A callback for when the clipboard contents have been received */
1846 data_sheet_contents_received_callback (GtkClipboard *clipboard,
1847 GtkSelectionData *sd,
1852 gint next_row, next_column;
1855 PsppireDataEditor *data_editor = data;
1857 if ( sd->length < 0 )
1860 if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE))
1863 c = (char *) sd->data;
1865 /* Paste text to selected position */
1866 psppire_sheet_get_active_cell (PSPPIRE_SHEET (data_editor->data_sheet[0]),
1869 g_return_if_fail (row >= 0);
1870 g_return_if_fail (column >= 0);
1872 first_column = column;
1874 next_column = column;
1875 while (count < sd->length)
1880 column = next_column;
1881 while (*c != '\t' && *c != '\n' && count < sd->length)
1889 next_column = column + 1;
1891 else if ( *c == '\n')
1894 next_column = first_column;
1900 /* Append some new cases if pasting beyond the last row */
1901 if ( row >= psppire_data_store_get_case_count (data_editor->data_store))
1902 psppire_data_store_insert_new_case (data_editor->data_store, row);
1904 psppire_data_store_set_string (data_editor->data_store, s, row, column);
1910 on_owner_change (GtkClipboard *clip, GdkEventOwnerChange *event, gpointer data)
1913 gboolean compatible_target = FALSE;
1914 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
1916 for (i = 0 ; i < sizeof (targets) / sizeof(targets[0]) ; ++i )
1918 GdkAtom atom = gdk_atom_intern (targets[i].target, TRUE);
1919 if ( gtk_clipboard_wait_is_target_available (clip, atom))
1921 compatible_target = TRUE;
1926 g_signal_emit (de, data_editor_signals[DATA_AVAILABLE_CHANGED], 0,