1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2009, 2010, 2011 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, gint posn,
341 gint x UNUSED, gint y UNUSED, gpointer data)
343 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
345 PsppireAxis *var_vaxis;
346 g_object_get (de->var_sheet, "vertical-axis", &var_vaxis, NULL);
348 psppire_axis_delete (var_vaxis, posn, 1);
350 psppire_axis_delete (de->haxis, posn, 1);
355 rewidth_variable_callback (PsppireDict *dict, gint posn, gpointer data)
357 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (data);
358 gint m_width = width_of_m (GTK_WIDGET (de));
360 const struct variable *var = psppire_dict_get_variable (dict, posn);
362 gint var_width = var_get_display_width (var);
364 /* Don't allow zero width */
368 psppire_axis_resize (de->haxis, posn, m_width * var_width);
373 psppire_data_editor_set_property (GObject *object,
379 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
383 case PROP_SPLIT_WINDOW:
384 psppire_data_editor_split_window (de, g_value_get_boolean (value));
386 case PROP_DATA_WINDOW:
387 de->data_window = g_value_get_pointer (value);
388 g_object_ref (de->data_window);
390 case PROP_DATA_STORE:
391 if ( de->data_store) g_object_unref (de->data_store);
392 de->data_store = g_value_get_pointer (value);
393 g_object_ref (de->data_store);
395 for (i = 0 ; i < 4 ; ++i )
397 g_object_set (de->data_sheet[i],
398 "model", de->data_store,
401 g_signal_connect_swapped (de->data_store->dict, "filter-changed",
402 G_CALLBACK (gtk_widget_queue_draw),
406 g_signal_connect (de->data_store->dict, "backend-changed",
407 G_CALLBACK (new_variables_callback), de);
409 g_signal_connect (de->data_store->dict, "variable-inserted",
410 G_CALLBACK (insert_variable_callback), de);
412 g_signal_connect (de->data_store->dict, "variable-deleted",
413 G_CALLBACK (delete_variable_callback), de);
415 g_signal_connect (de->data_store->dict, "variable-display-width-changed",
416 G_CALLBACK (rewidth_variable_callback), de);
418 g_signal_connect (de->data_store, "backend-changed",
419 G_CALLBACK (new_data_callback), de);
421 g_signal_connect (de->data_store, "case-inserted",
422 G_CALLBACK (case_inserted_callback), de);
424 g_signal_connect (de->data_store, "cases-deleted",
425 G_CALLBACK (cases_deleted_callback), de);
429 if ( de->var_store) g_object_unref (de->var_store);
430 de->var_store = g_value_get_pointer (value);
431 g_object_ref (de->var_store);
433 g_object_set (de->var_sheet,
434 "model", de->var_store,
437 case PROP_VS_ROW_MENU:
439 GObject *menu = g_value_get_object (value);
441 g_signal_connect (de->var_sheet, "button-event-row",
442 G_CALLBACK (popup_variable_row_menu), menu);
445 case PROP_DS_COLUMN_MENU:
447 GObject *menu = g_value_get_object (value);
449 g_signal_connect (de->data_sheet[0], "button-event-column",
450 G_CALLBACK (popup_variable_column_menu), menu);
453 case PROP_DS_ROW_MENU:
455 GObject *menu = g_value_get_object (value);
457 g_signal_connect (de->data_sheet[0], "button-event-row",
458 G_CALLBACK (popup_cases_menu), menu);
461 case PROP_CURRENT_VAR:
464 gint var = g_value_get_long (value);
465 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (object)))
467 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
468 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
469 psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), row, var);
470 psppire_sheet_moveto (PSPPIRE_SHEET (de->data_sheet[0]), -1, var, 0.5, 0.5);
472 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
473 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->var_sheet), &row, &col);
474 psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->var_sheet), var, col);
475 psppire_sheet_moveto (PSPPIRE_SHEET (de->var_sheet), var, -1, 0.5, 0.5);
478 g_assert_not_reached ();
483 case PROP_CURRENT_CASE:
486 gint case_num = g_value_get_long (value);
487 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
488 psppire_sheet_set_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), case_num, col);
489 psppire_sheet_moveto (PSPPIRE_SHEET (de->data_sheet[0]), case_num, -1, 0.5, 0.5);
492 case PROP_VALUE_LABELS:
494 psppire_data_store_show_labels (de->data_store,
495 g_value_get_boolean (value));
499 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
505 psppire_data_editor_get_property (GObject *object,
510 PsppireDataEditor *de = PSPPIRE_DATA_EDITOR (object);
514 case PROP_SPLIT_WINDOW:
515 g_value_set_boolean (value, de->split);
517 case PROP_DATA_WINDOW:
518 g_value_set_pointer (value, de->data_window);
520 case PROP_DATA_STORE:
521 g_value_set_pointer (value, de->data_store);
524 g_value_set_pointer (value, de->var_store);
526 case PROP_CURRENT_CASE:
529 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &column);
530 g_value_set_long (value, row);
533 case PROP_CURRENT_VAR:
536 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &column);
537 g_value_set_long (value, column);
540 case PROP_DATA_SELECTED:
541 g_value_set_boolean (value, data_is_selected (de));
544 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
551 psppire_data_editor_class_init (PsppireDataEditorClass *klass)
553 GParamSpec *data_window_spec ;
554 GParamSpec *data_store_spec ;
555 GParamSpec *var_store_spec ;
556 GParamSpec *column_menu_spec;
557 GParamSpec *ds_row_menu_spec;
558 GParamSpec *vs_row_menu_spec;
559 GParamSpec *value_labels_spec;
560 GParamSpec *current_case_spec;
561 GParamSpec *current_var_spec;
562 GParamSpec *data_selected_spec;
563 GParamSpec *split_window_spec;
564 GObjectClass *object_class = G_OBJECT_CLASS (klass);
566 parent_class = g_type_class_peek_parent (klass);
568 object_class->dispose = psppire_data_editor_dispose;
569 object_class->finalize = psppire_data_editor_finalize;
571 object_class->set_property = psppire_data_editor_set_property;
572 object_class->get_property = psppire_data_editor_get_property;
577 g_param_spec_pointer ("data-window",
579 "A pointer to the data window associated with this editor",
580 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
582 g_object_class_install_property (object_class,
587 g_param_spec_pointer ("data-store",
589 "A pointer to the data store associated with this editor",
590 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
592 g_object_class_install_property (object_class,
597 g_param_spec_pointer ("var-store",
599 "A pointer to the variable store associated with this editor",
600 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_READABLE );
602 g_object_class_install_property (object_class,
607 g_param_spec_object ("datasheet-column-menu",
608 "Data Sheet Column Menu",
609 "A menu to be displayed when button 3 is pressed in thedata sheet's column title buttons",
613 g_object_class_install_property (object_class,
619 g_param_spec_object ("datasheet-row-menu",
620 "Data Sheet Row Menu",
621 "A menu to be displayed when button 3 is pressed in the data sheet's row title buttons",
625 g_object_class_install_property (object_class,
631 g_param_spec_object ("varsheet-row-menu",
632 "Variable Sheet Row Menu",
633 "A menu to be displayed when button 3 is pressed in the variable sheet's row title buttons",
637 g_object_class_install_property (object_class,
643 g_param_spec_boolean ("value-labels",
645 "Whether or not the data sheet should display labels instead of values",
647 G_PARAM_WRITABLE | G_PARAM_READABLE);
649 g_object_class_install_property (object_class,
655 g_param_spec_long ("current-case",
657 "Zero based number of the selected case",
660 G_PARAM_WRITABLE | G_PARAM_READABLE);
662 g_object_class_install_property (object_class,
668 g_param_spec_long ("current-variable",
670 "Zero based number of the selected variable",
673 G_PARAM_WRITABLE | G_PARAM_READABLE);
675 g_object_class_install_property (object_class,
681 g_param_spec_boolean ("data-selected",
683 "True iff the data view is active and one or more cells of data have been selected.",
687 g_object_class_install_property (object_class,
694 g_param_spec_boolean ("split",
696 "True iff the data sheet is split",
698 G_PARAM_READABLE | G_PARAM_WRITABLE);
700 g_object_class_install_property (object_class,
704 data_editor_signals [DATA_SELECTION_CHANGED] =
705 g_signal_new ("data-selection-changed",
706 G_TYPE_FROM_CLASS (klass),
710 g_cclosure_marshal_VOID__BOOLEAN,
715 data_editor_signals [CASES_SELECTED] =
716 g_signal_new ("cases-selected",
717 G_TYPE_FROM_CLASS (klass),
721 g_cclosure_marshal_VOID__INT,
727 data_editor_signals [VARIABLES_SELECTED] =
728 g_signal_new ("variables-selected",
729 G_TYPE_FROM_CLASS (klass),
733 g_cclosure_marshal_VOID__INT,
739 data_editor_signals [DATA_AVAILABLE_CHANGED] =
740 g_signal_new ("data-available-changed",
741 G_TYPE_FROM_CLASS (klass),
745 g_cclosure_marshal_VOID__BOOLEAN,
751 /* Update the data_ref_entry with the reference of the active cell */
753 update_data_ref_entry (const PsppireSheet *sheet,
755 gint old_row, gint old_col,
758 PsppireDataEditor *de = data;
760 PsppireDataStore *data_store =
761 PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
765 const struct variable *var =
766 psppire_dict_get_variable (data_store->dict, col);
770 gchar *text = g_strdup_printf ("%d: %s", row + FIRST_CASE_NUMBER,
774 gtk_entry_set_text (GTK_ENTRY (de->cell_ref_entry), text);
784 psppire_data_store_get_string (data_store, row,
785 var_get_dict_index(var));
792 gtk_entry_set_text (GTK_ENTRY (de->datum_entry), text);
804 gtk_entry_set_text (GTK_ENTRY (de->datum_entry), "");
811 datum_entry_activate (GtkEntry *entry, gpointer data)
814 PsppireDataEditor *de = data;
816 const gchar *text = gtk_entry_get_text (entry);
818 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &column);
820 if ( row == -1 || column == -1)
823 psppire_data_store_set_string (de->data_store, text, row, column);
826 static void on_activate (PsppireDataEditor *de);
827 static gboolean on_switch_page (PsppireDataEditor *de, GtkNotebookPage *p, gint pagenum, gpointer data);
828 static void on_select_range (PsppireDataEditor *de);
830 static void on_select_row (PsppireSheet *, gint, PsppireDataEditor *);
831 static void on_select_variable (PsppireSheet *, gint, PsppireDataEditor *);
834 static void on_owner_change (GtkClipboard *,
835 GdkEventOwnerChange *, gpointer);
838 on_map (GtkWidget *w)
840 GtkClipboard *clip = gtk_widget_get_clipboard (w, GDK_SELECTION_CLIPBOARD);
842 g_signal_connect (clip, "owner-change", G_CALLBACK (on_owner_change), w);
847 init_sheet (PsppireDataEditor *de, int i,
848 GtkAdjustment *hadj, GtkAdjustment *vadj,
853 de->sheet_bin[i] = gtk_scrolled_window_new (hadj, vadj);
855 de->data_sheet[i] = psppire_sheet_new (NULL);
857 g_object_set (de->sheet_bin[i],
859 "shadow-type", GTK_SHADOW_ETCHED_IN,
862 g_object_set (haxis, "default-size", 75, NULL);
863 g_object_set (vaxis, "default-size", 25, NULL);
865 g_object_set (de->data_sheet[i],
866 "horizontal-axis", haxis,
867 "vertical-axis", vaxis,
870 gtk_container_add (GTK_CONTAINER (de->sheet_bin[i]), de->data_sheet[i]);
872 g_signal_connect (de->data_sheet[i], "traverse",
873 G_CALLBACK (traverse_cell_callback), de);
875 gtk_widget_show (de->sheet_bin[i]);
880 init_data_sheet (PsppireDataEditor *de)
882 GtkAdjustment *vadj0, *hadj0;
883 GtkAdjustment *vadj1, *hadj1;
886 de->vaxis[0] = psppire_axis_new ();
887 de->vaxis[1] = psppire_axis_new ();
889 /* There's only one horizontal axis, since the
890 column widths are parameters of the variables */
891 de->haxis = psppire_axis_new ();
894 de->paned = gtk_xpaned_new ();
896 init_sheet (de, 0, NULL, NULL, de->vaxis[0], de->haxis);
897 gtk_widget_show (de->sheet_bin[0]);
898 vadj0 = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[0]));
899 hadj0 = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[0]));
901 g_object_set (de->sheet_bin[0], "vscrollbar-policy", GTK_POLICY_NEVER, NULL);
902 g_object_set (de->sheet_bin[0], "hscrollbar-policy", GTK_POLICY_NEVER, NULL);
904 init_sheet (de, 1, NULL, vadj0, de->vaxis[0], de->haxis);
905 gtk_widget_show (de->sheet_bin[1]);
906 sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[1]));
907 psppire_sheet_hide_row_titles (PSPPIRE_SHEET (sheet));
908 hadj1 = gtk_scrolled_window_get_hadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[1]));
909 g_object_set (de->sheet_bin[1], "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
910 g_object_set (de->sheet_bin[1], "hscrollbar-policy", GTK_POLICY_NEVER, NULL);
912 init_sheet (de, 2, hadj0, NULL, de->vaxis[1], de->haxis);
913 gtk_widget_show (de->sheet_bin[2]);
914 sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[2]));
915 psppire_sheet_hide_column_titles (PSPPIRE_SHEET (sheet));
916 g_object_set (de->sheet_bin[2], "vscrollbar-policy", GTK_POLICY_NEVER, NULL);
917 g_object_set (de->sheet_bin[2], "hscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
918 vadj1 = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (de->sheet_bin[2]));
920 init_sheet (de, 3, hadj1, vadj1, de->vaxis[1], de->haxis);
921 gtk_widget_show (de->sheet_bin[3]);
922 sheet = gtk_bin_get_child (GTK_BIN (de->sheet_bin[3]));
923 psppire_sheet_hide_column_titles (PSPPIRE_SHEET (sheet));
924 psppire_sheet_hide_row_titles (PSPPIRE_SHEET (sheet));
925 g_object_set (de->sheet_bin[3], "vscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
926 g_object_set (de->sheet_bin[3], "hscrollbar-policy", GTK_POLICY_ALWAYS, NULL);
928 gtk_xpaned_pack_top_left (GTK_XPANED (de->paned), de->sheet_bin[0], TRUE, TRUE);
929 gtk_xpaned_pack_top_right (GTK_XPANED (de->paned), de->sheet_bin[1], TRUE, TRUE);
930 gtk_xpaned_pack_bottom_left (GTK_XPANED (de->paned), de->sheet_bin[2], TRUE, TRUE);
931 gtk_xpaned_pack_bottom_right (GTK_XPANED (de->paned), de->sheet_bin[3], TRUE, TRUE);
933 gtk_xpaned_set_position_y (GTK_XPANED (de->paned), 150);
934 gtk_xpaned_set_position_x (GTK_XPANED (de->paned), 350);
939 psppire_data_editor_init (PsppireDataEditor *de)
941 GtkWidget *hbox = gtk_hbox_new (FALSE, 0);
942 GtkWidget *sw_vs = gtk_scrolled_window_new (NULL, NULL);
944 init_data_sheet (de);
946 de->data_vbox = gtk_vbox_new (FALSE, 0);
947 de->var_sheet = psppire_var_sheet_new ();
949 g_object_set (de, "tab-pos", GTK_POS_BOTTOM, NULL);
951 de->datum_entry = gtk_entry_new ();
952 de->cell_ref_entry = gtk_entry_new ();
954 g_object_set (de->cell_ref_entry,
960 gtk_box_pack_start (GTK_BOX (hbox), de->cell_ref_entry, FALSE, FALSE, 0);
961 gtk_box_pack_start (GTK_BOX (hbox), de->datum_entry, TRUE, TRUE, 0);
964 gtk_container_add (GTK_CONTAINER (sw_vs), de->var_sheet);
965 gtk_widget_show_all (sw_vs);
968 gtk_box_pack_start (GTK_BOX (de->data_vbox), hbox, FALSE, FALSE, 0);
969 gtk_box_pack_start (GTK_BOX (de->data_vbox), de->paned, TRUE, TRUE, 0);
972 psppire_data_editor_remove_split (de);
974 gtk_widget_show_all (de->data_vbox);
976 gtk_notebook_append_page (GTK_NOTEBOOK (de), de->data_vbox,
977 gtk_label_new_with_mnemonic (_("Data View")));
979 gtk_notebook_append_page (GTK_NOTEBOOK (de), sw_vs,
980 gtk_label_new_with_mnemonic (_("Variable View")));
982 g_signal_connect (de->data_sheet[0], "activate",
983 G_CALLBACK (update_data_ref_entry),
986 g_signal_connect (de->datum_entry, "activate",
987 G_CALLBACK (datum_entry_activate),
991 g_signal_connect_swapped (de->data_sheet[0],
992 "double-click-column",
993 G_CALLBACK (on_data_column_clicked),
996 g_signal_connect_swapped (de->var_sheet,
998 G_CALLBACK (on_var_row_clicked),
1001 g_signal_connect_swapped (de->data_sheet[0], "activate",
1002 G_CALLBACK (on_activate),
1005 g_signal_connect_swapped (de->data_sheet[0], "select-range",
1006 G_CALLBACK (on_select_range),
1009 g_signal_connect (de->data_sheet[0], "select-row",
1010 G_CALLBACK (on_select_row), de);
1012 g_signal_connect (de->data_sheet[0], "select-column",
1013 G_CALLBACK (on_select_variable), de);
1016 g_signal_connect (de->var_sheet, "select-row",
1017 G_CALLBACK (on_select_variable), de);
1020 g_signal_connect_after (de, "switch-page",
1021 G_CALLBACK (on_switch_page),
1024 g_object_set (de, "can-focus", FALSE, NULL);
1026 g_signal_connect (de, "map", G_CALLBACK (on_map), NULL);
1029 // psppire_sheet_hide_column_titles (de->var_sheet);
1030 // psppire_sheet_hide_row_titles (de->data_sheet);
1033 de->dispose_has_run = FALSE;
1038 psppire_data_editor_new (PsppireDataWindow *data_window,
1039 PsppireVarStore *var_store,
1040 PsppireDataStore *data_store)
1042 return g_object_new (PSPPIRE_DATA_EDITOR_TYPE,
1043 "data-window", data_window,
1044 "var-store", var_store,
1045 "data-store", data_store,
1051 psppire_data_editor_remove_split (PsppireDataEditor *de)
1053 if ( !de->split ) return;
1056 g_object_ref (de->sheet_bin[0]);
1057 gtk_container_remove (GTK_CONTAINER (de->paned), de->sheet_bin[0]);
1059 g_object_ref (de->paned);
1060 gtk_container_remove (GTK_CONTAINER (de->data_vbox), de->paned);
1062 gtk_box_pack_start (GTK_BOX (de->data_vbox), de->sheet_bin[0],
1065 g_object_unref (de->sheet_bin[0]);
1067 g_object_set (de->sheet_bin[0], "vscrollbar-policy",
1068 GTK_POLICY_ALWAYS, NULL);
1070 g_object_set (de->sheet_bin[0], "hscrollbar-policy",
1071 GTK_POLICY_ALWAYS, NULL);
1076 psppire_data_editor_set_split (PsppireDataEditor *de)
1078 if ( de->split ) return;
1081 g_object_ref (de->sheet_bin[0]);
1082 gtk_container_remove (GTK_CONTAINER (de->data_vbox), de->sheet_bin[0]);
1084 gtk_xpaned_pack_top_left (GTK_XPANED (de->paned), de->sheet_bin [0],
1087 gtk_box_pack_start (GTK_BOX (de->data_vbox), de->paned,
1090 g_object_unref (de->paned);
1092 g_object_set (de->sheet_bin[0], "vscrollbar-policy",
1093 GTK_POLICY_NEVER, NULL);
1095 g_object_set (de->sheet_bin[0], "hscrollbar-policy",
1096 GTK_POLICY_NEVER, NULL);
1100 psppire_data_editor_split_window (PsppireDataEditor *de, gboolean split)
1103 psppire_data_editor_set_split (de);
1105 psppire_data_editor_remove_split (de);
1107 gtk_widget_show_all (de->data_vbox);
1110 static void data_sheet_set_clip (PsppireSheet *sheet);
1111 static void data_sheet_contents_received_callback (GtkClipboard *clipboard,
1112 GtkSelectionData *sd,
1117 psppire_data_editor_clip_copy (PsppireDataEditor *de)
1119 data_sheet_set_clip (PSPPIRE_SHEET (de->data_sheet[0]));
1123 psppire_data_editor_clip_paste (PsppireDataEditor *de)
1125 GdkDisplay *display = gtk_widget_get_display ( GTK_WIDGET (de));
1126 GtkClipboard *clipboard =
1127 gtk_clipboard_get_for_display (display, GDK_SELECTION_CLIPBOARD);
1129 gtk_clipboard_request_contents (clipboard,
1130 gdk_atom_intern ("UTF8_STRING", TRUE),
1131 data_sheet_contents_received_callback,
1138 psppire_data_editor_clip_cut (PsppireDataEditor *de)
1140 gint max_rows, max_columns;
1142 PsppireSheetRange range;
1143 PsppireDataStore *ds = de->data_store;
1145 data_sheet_set_clip (PSPPIRE_SHEET (de->data_sheet[0]));
1147 /* Now blank all the cells */
1148 psppire_sheet_get_selected_range (PSPPIRE_SHEET (de->data_sheet[0]), &range);
1150 /* If nothing selected, then use active cell */
1151 if ( range.row0 < 0 || range.col0 < 0 )
1154 psppire_sheet_get_active_cell (PSPPIRE_SHEET (de->data_sheet[0]), &row, &col);
1156 range.row0 = range.rowi = row;
1157 range.col0 = range.coli = col;
1160 /* The sheet range can include cells that do not include data.
1161 Exclude them from the range. */
1162 max_rows = psppire_data_store_get_case_count (ds);
1163 if (range.rowi >= max_rows)
1167 range.rowi = max_rows - 1;
1170 max_columns = dict_get_var_cnt (ds->dict->dict);
1171 if (range.coli >= max_columns)
1173 if (max_columns == 0)
1175 range.coli = max_columns - 1;
1178 g_return_if_fail (range.rowi >= range.row0);
1179 g_return_if_fail (range.row0 >= 0);
1180 g_return_if_fail (range.coli >= range.col0);
1181 g_return_if_fail (range.col0 >= 0);
1184 for (r = range.row0; r <= range.rowi ; ++r )
1188 for (c = range.col0 ; c <= range.coli; ++c)
1190 psppire_data_store_set_string (ds, "", r, c);
1194 /* and remove the selection */
1195 psppire_sheet_unselect_range (PSPPIRE_SHEET (de->data_sheet[0]));
1201 /* Popup menu related stuff */
1204 popup_variable_column_menu (PsppireSheet *sheet, gint column,
1205 GdkEventButton *event, gpointer data)
1207 GtkMenu *menu = GTK_MENU (data);
1209 PsppireDataStore *data_store =
1210 PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
1212 const struct variable *v =
1213 psppire_dict_get_variable (data_store->dict, column);
1215 if ( v && event->button == 3)
1217 psppire_sheet_select_column (sheet, column);
1219 gtk_menu_popup (menu,
1220 NULL, NULL, NULL, NULL,
1221 event->button, event->time);
1227 popup_variable_row_menu (PsppireSheet *sheet, gint row,
1228 GdkEventButton *event, gpointer data)
1230 GtkMenu *menu = GTK_MENU (data);
1232 PsppireVarStore *var_store =
1233 PSPPIRE_VAR_STORE (psppire_sheet_get_model (sheet));
1236 const struct variable *v ;
1238 g_object_get (var_store, "dictionary", &dict, NULL);
1240 v = psppire_dict_get_variable (dict, row);
1242 if ( v && event->button == 3)
1244 psppire_sheet_select_row (sheet, row);
1246 gtk_menu_popup (menu,
1247 NULL, NULL, NULL, NULL,
1248 event->button, event->time);
1254 popup_cases_menu (PsppireSheet *sheet, gint row,
1255 GdkEventButton *event, gpointer data)
1257 GtkMenu *menu = GTK_MENU (data);
1259 PsppireDataStore *data_store =
1260 PSPPIRE_DATA_STORE (psppire_sheet_get_model (sheet));
1262 if ( row <= psppire_data_store_get_case_count (data_store) &&
1265 psppire_sheet_select_row (sheet, row);
1267 gtk_menu_popup (menu,
1268 NULL, NULL, NULL, NULL,
1269 event->button, event->time);
1278 do_sort (PsppireDataEditor *de, int var, gboolean descend)
1280 const struct variable *v
1281 = psppire_dict_get_variable (de->data_store->dict, var);
1284 syntax = g_strdup_printf ("SORT CASES BY %s%s.",
1285 var_get_name (v), descend ? " (D)" : "");
1286 g_free (execute_syntax_string (de->data_window, syntax));
1290 /* Sort the data by the the variable which the editor has currently
1293 psppire_data_editor_sort_ascending (PsppireDataEditor *de)
1295 PsppireSheetRange range;
1296 psppire_sheet_get_selected_range (PSPPIRE_SHEET(de->data_sheet[0]), &range);
1298 do_sort (de, range.col0, FALSE);
1302 /* Sort the data by the the variable which the editor has currently
1305 psppire_data_editor_sort_descending (PsppireDataEditor *de)
1307 PsppireSheetRange range;
1308 psppire_sheet_get_selected_range (PSPPIRE_SHEET(de->data_sheet[0]), &range);
1310 do_sort (de, range.col0, TRUE);
1317 /* Insert a new variable before the currently selected position */
1319 psppire_data_editor_insert_variable (PsppireDataEditor *de)
1323 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
1325 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
1326 if ( PSPPIRE_SHEET (de->data_sheet[0])->select_status
1327 == PSPPIRE_SHEET_COLUMN_SELECTED )
1328 posn = PSPPIRE_SHEET (de->data_sheet[0])->range.col0;
1330 posn = PSPPIRE_SHEET (de->data_sheet[0])->active_cell.col;
1332 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
1333 if ( PSPPIRE_SHEET (de->var_sheet)->select_status
1334 == PSPPIRE_SHEET_ROW_SELECTED )
1335 posn = PSPPIRE_SHEET (de->var_sheet)->range.row0;
1337 posn = PSPPIRE_SHEET (de->var_sheet)->active_cell.row;
1340 g_assert_not_reached ();
1344 psppire_dict_insert_variable (de->data_store->dict, posn, NULL);
1347 /* Insert a new case before the currently selected position */
1349 psppire_data_editor_insert_case (PsppireDataEditor *de)
1353 if ( PSPPIRE_SHEET (de->data_sheet[0])->select_status == PSPPIRE_SHEET_ROW_SELECTED )
1355 posn = PSPPIRE_SHEET (de->data_sheet[0])->range.row0;
1359 posn = PSPPIRE_SHEET (de->data_sheet[0])->active_cell.row;
1362 if ( posn == -1 ) posn = 0;
1364 psppire_data_store_insert_new_case (de->data_store, posn);
1367 /* Delete the cases currently selected in the data sheet */
1369 psppire_data_editor_delete_cases (PsppireDataEditor *de)
1371 gint first = PSPPIRE_SHEET (de->data_sheet[0])->range.row0;
1372 gint n = PSPPIRE_SHEET (de->data_sheet[0])->range.rowi - first + 1;
1374 psppire_data_store_delete_cases (de->data_store, first, n);
1376 psppire_sheet_unselect_range (PSPPIRE_SHEET (de->data_sheet[0]));
1379 /* Delete the variables currently selected in the
1380 datasheet or variable sheet */
1382 psppire_data_editor_delete_variables (PsppireDataEditor *de)
1384 PsppireDict *dict = NULL;
1387 switch (gtk_notebook_get_current_page (GTK_NOTEBOOK (de)))
1389 case PSPPIRE_DATA_EDITOR_DATA_VIEW:
1390 first = PSPPIRE_SHEET (de->data_sheet[0])->range.col0;
1391 n = PSPPIRE_SHEET (de->data_sheet[0])->range.coli - first + 1;
1393 case PSPPIRE_DATA_EDITOR_VARIABLE_VIEW:
1394 first = PSPPIRE_SHEET (de->var_sheet)->range.row0;
1395 n = PSPPIRE_SHEET (de->var_sheet)->range.rowi - first + 1;
1398 g_assert_not_reached ();
1402 g_object_get (de->var_store, "dictionary", &dict, NULL);
1404 psppire_dict_delete_variables (dict, first, n);
1406 psppire_sheet_unselect_range (PSPPIRE_SHEET (de->data_sheet[0]));
1407 psppire_sheet_unselect_range (PSPPIRE_SHEET (de->var_sheet));
1412 psppire_data_editor_show_grid (PsppireDataEditor *de, gboolean grid_visible)
1414 psppire_sheet_show_grid (PSPPIRE_SHEET (de->var_sheet), grid_visible);
1415 psppire_sheet_show_grid (PSPPIRE_SHEET (de->data_sheet[0]), grid_visible);
1420 set_font (GtkWidget *w, gpointer data)
1422 PangoFontDescription *font_desc = data;
1423 GtkRcStyle *style = gtk_widget_get_modifier_style (w);
1425 pango_font_description_free (style->font_desc);
1426 style->font_desc = pango_font_description_copy (font_desc);
1428 gtk_widget_modify_style (w, style);
1430 if ( GTK_IS_CONTAINER (w))
1431 gtk_container_foreach (GTK_CONTAINER (w), set_font, font_desc);
1435 psppire_data_editor_set_font (PsppireDataEditor *de, PangoFontDescription *font_desc)
1437 set_font (GTK_WIDGET (de), font_desc);
1438 gtk_container_foreach (GTK_CONTAINER (de), set_font, 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,