1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2007 Free Software Foundation
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 <gtksheet/gtksheet.h>
20 #include "clipboard.h"
21 #include <data/case.h>
22 #include "psppire-data-store.h"
23 #include <data/casereader.h>
24 #include <data/case-map.h>
25 #include <data/casewriter.h>
26 #include <data/format.h>
27 #include <data/data-out.h>
30 #include "data-editor.h"
34 /* A casereader and dictionary holding the data currently in the clip */
35 static struct casereader *clip_datasheet = NULL;
36 static struct dictionary *clip_dict = NULL;
41 static void data_sheet_update_clipboard (GtkSheet *);
43 /* Set the clip according to the currently
44 selected range in the data sheet */
46 data_sheet_set_clip (GtkSheet *sheet)
49 struct casewriter *writer ;
52 struct case_map *map = NULL;
56 ds = PSPPIRE_DATA_STORE (gtk_sheet_get_model (sheet));
58 gtk_sheet_get_selected_range (sheet, &range);
60 /* If nothing selected, then use active cell */
61 if ( range.row0 < 0 || range.col0 < 0 )
64 gtk_sheet_get_active_cell (sheet, &row, &col);
66 range.row0 = range.rowi = row;
67 range.col0 = range.coli = col;
70 /* The sheet range can include cells that do not include data.
71 Exclude them from the range. */
72 max_rows = psppire_data_store_get_case_count (ds);
73 if (range.rowi >= max_rows)
77 range.rowi = max_rows - 1;
79 max_columns = dict_get_var_cnt (ds->dict->dict);
80 if (range.coli >= max_columns)
84 range.coli = max_columns - 1;
87 g_return_if_fail (range.rowi >= range.row0);
88 g_return_if_fail (range.row0 >= 0);
89 g_return_if_fail (range.coli >= range.col0);
90 g_return_if_fail (range.col0 >= 0);
92 /* Destroy any existing clip */
95 casereader_destroy (clip_datasheet);
96 clip_datasheet = NULL;
101 dict_destroy (clip_dict);
105 /* Construct clip dictionary. */
106 clip_dict = dict_create ();
107 for (i = range.col0; i <= range.coli; i++)
109 const struct variable *old = dict_get_var (ds->dict->dict, i);
110 dict_clone_var_assert (clip_dict, old, var_get_name (old));
113 /* Construct clip data. */
114 map = case_map_by_name (ds->dict->dict, clip_dict);
115 writer = autopaging_writer_create (dict_get_next_value_idx (clip_dict));
116 for (i = range.row0; i <= range.rowi ; ++i )
120 if (psppire_case_file_get_case (ds->case_file, i, &old))
124 case_map_execute (map, &old, &new);
126 casewriter_write (writer, &new);
129 casewriter_force_error (writer);
131 case_map_destroy (map);
133 clip_datasheet = casewriter_make_reader (writer);
135 data_sheet_update_clipboard (sheet);
145 /* Perform data_out for case CC, variable V, appending to STRING */
147 data_out_g_string (GString *string, const struct variable *v,
148 const struct ccase *cc)
152 const struct fmt_spec *fs = var_get_print_format (v);
153 const union value *val = case_data (cc, v);
154 buf = xzalloc (fs->w);
156 data_out (val, fs, buf);
158 g_string_append_len (string, buf, fs->w);
169 const size_t val_cnt = casereader_get_value_cnt (clip_datasheet);
170 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
171 const size_t var_cnt = dict_get_var_cnt (clip_dict);
173 string = g_string_sized_new (10 * val_cnt * case_cnt);
175 for (r = 0 ; r < case_cnt ; ++r )
179 if ( ! casereader_peek (clip_datasheet, r, &cc))
181 g_warning ("Clipboard seems to have inexplicably shrunk");
185 for (c = 0 ; c < var_cnt ; ++c)
187 const struct variable *v = dict_get_var (clip_dict, c);
188 data_out_g_string (string, v, &cc);
189 if ( c < val_cnt - 1 )
190 g_string_append (string, "\t");
194 g_string_append (string, "\n");
209 const size_t val_cnt = casereader_get_value_cnt (clip_datasheet);
210 const casenumber case_cnt = casereader_get_case_cnt (clip_datasheet);
211 const size_t var_cnt = dict_get_var_cnt (clip_dict);
214 /* Guestimate the size needed */
215 string = g_string_sized_new (20 * val_cnt * case_cnt);
217 g_string_append (string, "<table>\n");
218 for (r = 0 ; r < case_cnt ; ++r )
222 if ( ! casereader_peek (clip_datasheet, r, &cc))
224 g_warning ("Clipboard seems to have inexplicably shrunk");
227 g_string_append (string, "<tr>\n");
229 for (c = 0 ; c < var_cnt ; ++c)
231 const struct variable *v = dict_get_var (clip_dict, c);
232 g_string_append (string, "<td>");
233 data_out_g_string (string, v, &cc);
234 g_string_append (string, "</td>\n");
237 g_string_append (string, "</tr>\n");
241 g_string_append (string, "</table>\n");
249 clipboard_get_cb (GtkClipboard *clipboard,
250 GtkSelectionData *selection_data,
254 GString *string = NULL;
258 case SELECT_FMT_TEXT:
259 string = clip_to_text ();
261 case SELECT_FMT_HTML:
262 string = clip_to_html ();
265 g_assert_not_reached ();
268 gtk_selection_data_set (selection_data, selection_data->target,
270 (const guchar *) string->str, string->len);
272 g_string_free (string, TRUE);
276 clipboard_clear_cb (GtkClipboard *clipboard,
279 dict_destroy (clip_dict);
282 casereader_destroy (clip_datasheet);
283 clip_datasheet = NULL;
289 data_sheet_update_clipboard (GtkSheet *sheet)
291 static const GtkTargetEntry targets[] = {
292 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
293 { "STRING", 0, SELECT_FMT_TEXT },
294 { "TEXT", 0, SELECT_FMT_TEXT },
295 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
296 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
297 { "text/plain", 0, SELECT_FMT_TEXT },
298 { "text/html", 0, SELECT_FMT_HTML }
301 GtkClipboard *clipboard =
302 gtk_widget_get_clipboard (GTK_WIDGET (sheet),
303 GDK_SELECTION_CLIPBOARD);
305 if (!gtk_clipboard_set_with_owner (clipboard, targets,
306 G_N_ELEMENTS (targets),
307 clipboard_get_cb, clipboard_clear_cb,
309 clipboard_clear_cb (clipboard, sheet);
314 /* A callback for when clipboard contents have been received */
316 data_sheet_contents_received_callback (GtkClipboard *clipboard,
317 GtkSelectionData *sd,
320 struct data_editor *de = data;
324 gint next_row, next_column;
327 GtkSheet *data_sheet ;
328 PsppireDataStore *data_store;
330 if ( sd->length < 0 )
333 if ( sd->type != gdk_atom_intern ("UTF8_STRING", FALSE))
336 c = (char *) sd->data;
338 /* Paste text to selected position */
339 data_sheet = GTK_SHEET (get_widget_assert (de->xml, "data_sheet"));
340 data_store = PSPPIRE_DATA_STORE (gtk_sheet_get_model (data_sheet));
342 gtk_sheet_get_active_cell (data_sheet, &row, &column);
344 g_return_if_fail (row >= 0);
345 g_return_if_fail (column >= 0);
347 first_column = column;
349 next_column = column;
350 while (count < sd->length)
355 column = next_column;
356 while (*c != '\t' && *c != '\n' && count < sd->length)
364 next_column = column + 1;
366 else if ( *c == '\n')
369 next_column = first_column;
374 /* Append some new cases if pasting beyond the last row */
375 if ( row >= psppire_data_store_get_case_count (data_store))
376 psppire_data_store_insert_new_case (data_store, row);
378 gtk_sheet_set_cell_text (data_sheet, row, column, s);