2 Copyright (C) 2006, 2008, 2009 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/>.
18 This file is derived from the gtksheet.c and extensively modified for the
19 requirements of PSPPIRE. The changes are copyright by the
20 Free Software Foundation. The copyright notice for the original work is
24 /* GtkSheet widget for Gtk+.
25 * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
27 * Based on GtkClist widget by Jay Painter, but major changes.
28 * Memory allocation routines inspired on SC (Spreadsheet Calculator)
30 * This library is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Lesser General Public
32 * License as published by the Free Software Foundation; either
33 * version 2.1 of the License, or (at your option) any later version.
35 * This library is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38 * Lesser General Public License for more details.
40 * You should have received a copy of the GNU Lesser General Public
41 * License along with this library; if not, write to the Free Software
42 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
46 * SECTION:psppiresheet
47 * @short_description: spreadsheet widget for gtk2
49 * PsppireSheet is a matrix widget for GTK+. It consists of an scrollable grid of
50 * cells where you can allocate text. Cell contents can be edited interactively
51 * through a specially designed entry, GtkItemEntry.
61 #include <gdk/gdkkeysyms.h>
62 #include <gtk/gtksignal.h>
63 #include <gtk/gtkbutton.h>
64 #include <gtk/gtkadjustment.h>
65 #include <gtk/gtktypeutils.h>
66 #include <gtk/gtkentry.h>
67 #include <gtk/gtkcontainer.h>
68 #include <pango/pango.h>
69 #include "psppire-sheet.h"
70 #include <ui/gui/psppire-marshal.h>
71 #include <ui/gui/sheet/psppire-sheetmodel.h>
72 #include <ui/gui/sheet/psppire-axis.h>
73 #include <libpspp/misc.h>
80 PSPPIRE_SHEET_IN_XDRAG = 1 << 1,
81 PSPPIRE_SHEET_IN_YDRAG = 1 << 2,
82 PSPPIRE_SHEET_IN_DRAG = 1 << 3,
84 /* This flag is set when the user is actually in the process
85 of making a selection - ie while the mouse button is
88 PSPPIRE_SHEET_IN_SELECTION = 1 << 4,
90 PSPPIRE_SHEET_IN_RESIZE = 1 << 5
93 #define PSPPIRE_SHEET_FLAGS(sheet) (PSPPIRE_SHEET (sheet)->flags)
94 #define PSPPIRE_SHEET_SET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) |= (flag))
95 #define PSPPIRE_SHEET_UNSET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) &= ~ (flag))
97 #define PSPPIRE_SHEET_IN_XDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_XDRAG)
98 #define PSPPIRE_SHEET_IN_YDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_YDRAG)
99 #define PSPPIRE_SHEET_IN_DRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_DRAG)
100 #define PSPPIRE_SHEET_IN_SELECTION(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_SELECTION)
101 #define PSPPIRE_SHEET_IN_RESIZE(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_RESIZE)
103 #define CELL_SPACING 1
105 #define TIMEOUT_HOVER 300
106 #define COLUMN_MIN_WIDTH 10
107 #define COLUMN_TITLES_HEIGHT 4
108 #define DEFAULT_COLUMN_WIDTH 80
109 #define DEFAULT_ROW_HEIGHT 25
111 static void set_entry_widget_font (PsppireSheet *sheet);
113 static void psppire_sheet_update_primary_selection (PsppireSheet *sheet);
114 static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
115 static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
116 static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range);
119 static void set_row_height (PsppireSheet *sheet,
123 static void destroy_hover_window (PsppireSheetHoverTitle *);
124 static PsppireSheetHoverTitle *create_hover_window (void);
126 static GtkStateType psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col);
130 dispose_string (const PsppireSheet *sheet, gchar *text)
132 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
137 if (psppire_sheet_model_free_strings (model))
142 /* FIXME: Why bother with these two ? */
144 /* returns the column index from a pixel location */
146 column_from_xpixel (const PsppireSheet *sheet, gint pixel)
148 return psppire_axis_unit_at_pixel (sheet->haxis, pixel);
152 row_from_ypixel (const PsppireSheet *sheet, gint pixel)
154 return psppire_axis_unit_at_pixel (sheet->vaxis, pixel);
158 /* Return the lowest row number which is wholly or partially on
159 the visible range of the sheet */
161 min_visible_row (const PsppireSheet *sheet)
163 return row_from_ypixel (sheet, sheet->vadjustment->value);
167 min_fully_visible_row (const PsppireSheet *sheet)
169 glong row = min_visible_row (sheet);
171 if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value)
178 max_visible_row (const PsppireSheet *sheet)
180 return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size);
185 max_fully_visible_row (const PsppireSheet *sheet)
187 glong row = max_visible_row (sheet);
189 if ( psppire_axis_start_pixel (sheet->vaxis, row)
191 psppire_axis_unit_size (sheet->vaxis, row)
192 > sheet->vadjustment->value)
199 /* Returns the lowest column number which is wholly or partially
202 min_visible_column (const PsppireSheet *sheet)
204 return column_from_xpixel (sheet, sheet->hadjustment->value);
208 min_fully_visible_column (const PsppireSheet *sheet)
210 glong col = min_visible_column (sheet);
212 if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value)
219 /* Returns the highest column number which is wholly or partially
222 max_visible_column (const PsppireSheet *sheet)
224 return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size);
228 max_fully_visible_column (const PsppireSheet *sheet)
230 glong col = max_visible_column (sheet);
232 if ( psppire_axis_start_pixel (sheet->haxis, col)
234 psppire_axis_unit_size (sheet->haxis, col)
235 > sheet->hadjustment->value)
243 /* The size of the region (in pixels) around the row/column boundaries
244 where the height/width may be grabbed to change size */
248 on_column_boundary (const PsppireSheet *sheet, gint x, gint *column)
253 x += sheet->hadjustment->value;
258 col = column_from_xpixel (sheet, x);
260 pixel = x - DRAG_WIDTH / 2;
264 if ( column_from_xpixel (sheet, pixel) < col )
270 if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col )
280 on_row_boundary (const PsppireSheet *sheet, gint y, gint *row)
285 y += sheet->vadjustment->value;
290 r = row_from_ypixel (sheet, y);
292 pixel = y - DRAG_WIDTH / 2;
296 if ( row_from_ypixel (sheet, pixel) < r )
302 if ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r )
312 static inline gboolean
313 POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y,
314 gint *drag_row, gint *drag_column)
318 /* Can't drag if nothing is selected */
319 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
320 sheet->range.col0 < 0 || sheet->range.coli < 0 )
323 *drag_column = column_from_xpixel (sheet, x);
324 *drag_row = row_from_ypixel (sheet, y);
326 if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 &&
327 x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
328 psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2)
330 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0);
331 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
333 *drag_row = sheet->range.row0;
336 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
337 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
338 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
340 *drag_row = sheet->range.rowi;
345 if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 &&
346 y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
347 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2)
349 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0);
350 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
352 *drag_column = sheet->range.col0;
355 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
356 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
357 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
359 *drag_column = sheet->range.coli;
367 static inline gboolean
368 POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y,
369 gint *drag_row, gint *drag_column)
373 /* Can't drag if nothing is selected */
374 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
375 sheet->range.col0 < 0 || sheet->range.coli < 0 )
378 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+
379 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
381 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
382 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
384 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED)
385 ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet));
387 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED)
388 xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet));
390 *drag_column = column_from_xpixel (sheet, x);
391 *drag_row = row_from_ypixel (sheet, y);
393 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
394 y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
401 rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *range,
404 g_return_val_if_fail (range, FALSE);
406 r->x = psppire_axis_start_pixel (sheet->haxis, range->col0);
407 r->x -= round (sheet->hadjustment->value);
409 r->y = psppire_axis_start_pixel (sheet->vaxis, range->row0);
410 r->y -= round (sheet->vadjustment->value);
412 r->width = psppire_axis_start_pixel (sheet->haxis, range->coli) -
413 psppire_axis_start_pixel (sheet->haxis, range->col0) +
414 psppire_axis_unit_size (sheet->haxis, range->coli);
416 r->height = psppire_axis_start_pixel (sheet->vaxis, range->rowi) -
417 psppire_axis_start_pixel (sheet->vaxis, range->row0) +
418 psppire_axis_unit_size (sheet->vaxis, range->rowi);
420 if ( sheet->column_titles_visible)
422 r->y += sheet->column_title_area.height;
425 if ( sheet->row_titles_visible)
427 r->x += sheet->row_title_area.width;
434 rectangle_from_cell (PsppireSheet *sheet, gint row, gint col,
437 PsppireSheetRange range;
438 g_return_val_if_fail (row >= 0, FALSE);
439 g_return_val_if_fail (col >= 0, FALSE);
441 range.row0 = range.rowi = row;
442 range.col0 = range.coli = col;
444 return rectangle_from_range (sheet, &range, r);
448 static void psppire_sheet_class_init (PsppireSheetClass *klass);
449 static void psppire_sheet_init (PsppireSheet *sheet);
450 static void psppire_sheet_dispose (GObject *object);
451 static void psppire_sheet_finalize (GObject *object);
452 static void psppire_sheet_style_set (GtkWidget *widget,
453 GtkStyle *previous_style);
454 static void psppire_sheet_realize (GtkWidget *widget);
455 static void psppire_sheet_unrealize (GtkWidget *widget);
456 static void psppire_sheet_map (GtkWidget *widget);
457 static void psppire_sheet_unmap (GtkWidget *widget);
458 static gint psppire_sheet_expose (GtkWidget *widget,
459 GdkEventExpose *event);
461 static void psppire_sheet_forall (GtkContainer *container,
462 gboolean include_internals,
463 GtkCallback callback,
464 gpointer callback_data);
466 static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
467 GtkAdjustment *hadjustment,
468 GtkAdjustment *vadjustment);
470 static gint psppire_sheet_button_press (GtkWidget *widget,
471 GdkEventButton *event);
472 static gint psppire_sheet_button_release (GtkWidget *widget,
473 GdkEventButton *event);
474 static gint psppire_sheet_motion (GtkWidget *widget,
475 GdkEventMotion *event);
476 static gboolean psppire_sheet_crossing_notify (GtkWidget *widget,
477 GdkEventCrossing *event);
478 static gint psppire_sheet_entry_key_press (GtkWidget *widget,
480 static gboolean psppire_sheet_key_press (GtkWidget *widget,
482 static void psppire_sheet_size_request (GtkWidget *widget,
483 GtkRequisition *requisition);
484 static void psppire_sheet_size_allocate (GtkWidget *widget,
485 GtkAllocation *allocation);
487 static gboolean psppire_sheet_focus_in (GtkWidget *widget,
488 GdkEventFocus *event);
492 static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
493 const PsppireSheetRange *range);
494 static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet,
495 gint row, gint column);
496 /* Drawing Routines */
499 static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column);
502 /* draw visible part of range. */
503 static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region);
508 static void psppire_sheet_real_select_range (PsppireSheet *sheet,
509 const PsppireSheetRange *range);
510 static void psppire_sheet_real_unselect_range (PsppireSheet *sheet,
511 const PsppireSheetRange *range);
512 static void psppire_sheet_draw_border (PsppireSheet *sheet,
513 PsppireSheetRange range);
515 /* Active Cell handling */
517 static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet);
518 static void change_active_cell (PsppireSheet *sheet, gint row, gint col);
519 static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet);
520 static void psppire_sheet_show_entry_widget (PsppireSheet *sheet);
521 static gboolean psppire_sheet_click_cell (PsppireSheet *sheet,
528 static void adjust_scrollbars (PsppireSheet *sheet);
529 static void vadjustment_value_changed (GtkAdjustment *adjustment,
531 static void hadjustment_value_changed (GtkAdjustment *adjustment,
535 static void draw_xor_vline (PsppireSheet *sheet);
536 static void draw_xor_hline (PsppireSheet *sheet);
537 static void draw_xor_rectangle (PsppireSheet *sheet,
538 PsppireSheetRange range);
542 static void create_global_button (PsppireSheet *sheet);
543 static void global_button_clicked (GtkWidget *widget,
547 static void create_sheet_entry (PsppireSheet *sheet);
548 static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet);
550 /* Sheet button gadgets */
552 static void draw_column_title_buttons (PsppireSheet *sheet);
553 static void draw_row_title_buttons (PsppireSheet *sheet);
556 static void size_allocate_global_button (PsppireSheet *sheet);
557 static void psppire_sheet_button_size_request (PsppireSheet *sheet,
558 const PsppireSheetButton *button,
559 GtkRequisition *requisition);
561 static void psppire_sheet_real_cell_clear (PsppireSheet *sheet,
583 static GtkContainerClass *parent_class = NULL;
584 static guint sheet_signals[LAST_SIGNAL] = { 0 };
588 psppire_sheet_get_type ()
590 static GType sheet_type = 0;
594 static const GTypeInfo sheet_info =
596 sizeof (PsppireSheetClass),
599 (GClassInitFunc) psppire_sheet_class_init,
602 sizeof (PsppireSheet),
604 (GInstanceInitFunc) psppire_sheet_init,
609 g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
617 static PsppireSheetRange*
618 psppire_sheet_range_copy (const PsppireSheetRange *range)
620 PsppireSheetRange *new_range;
622 g_return_val_if_fail (range != NULL, NULL);
624 new_range = g_new (PsppireSheetRange, 1);
632 psppire_sheet_range_free (PsppireSheetRange *range)
634 g_return_if_fail (range != NULL);
640 psppire_sheet_range_get_type (void)
642 static GType sheet_range_type = 0;
644 if (!sheet_range_type)
647 g_boxed_type_register_static ("PsppireSheetRange",
648 (GBoxedCopyFunc) psppire_sheet_range_copy,
649 (GBoxedFreeFunc) psppire_sheet_range_free);
652 return sheet_range_type;
655 static PsppireSheetCell*
656 psppire_sheet_cell_copy (const PsppireSheetCell *cell)
658 PsppireSheetCell *new_cell;
660 g_return_val_if_fail (cell != NULL, NULL);
662 new_cell = g_new (PsppireSheetCell, 1);
670 psppire_sheet_cell_free (PsppireSheetCell *cell)
672 g_return_if_fail (cell != NULL);
678 psppire_sheet_cell_get_type (void)
680 static GType sheet_cell_type = 0;
682 if (!sheet_cell_type)
685 g_boxed_type_register_static ("PsppireSheetCell",
686 (GBoxedCopyFunc) psppire_sheet_cell_copy,
687 (GBoxedFreeFunc) psppire_sheet_cell_free);
690 return sheet_cell_type;
705 resize_column (PsppireSheet *sheet, gint unit, glong size)
707 PsppireSheetRange range;
709 range.coli = max_visible_column (sheet);
710 range.row0 = min_visible_row (sheet);
711 range.rowi = max_visible_row (sheet);
713 redraw_range (sheet, &range);
715 draw_column_title_buttons_range (sheet, range.col0, range.coli);
720 psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
723 g_object_unref (sheet->haxis);
726 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
729 g_object_ref (sheet->haxis);
733 resize_row (PsppireSheet *sheet, gint unit, glong size)
735 PsppireSheetRange range;
736 range.col0 = min_visible_column (sheet);
737 range.coli = max_visible_column (sheet);
739 range.rowi = max_visible_row (sheet);
741 redraw_range (sheet, &range);
743 draw_row_title_buttons_range (sheet, range.row0, range.rowi);
747 psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
750 g_object_unref (sheet->vaxis);
754 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
757 g_object_ref (sheet->vaxis);
760 static const GtkBorder default_cell_padding = {3, 3, 3, 3};
763 psppire_sheet_set_property (GObject *object,
769 PsppireSheet *sheet = PSPPIRE_SHEET (object);
773 case PROP_CELL_PADDING:
774 if ( sheet->cell_padding)
775 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
777 sheet->cell_padding = g_value_dup_boxed (value);
779 if (NULL == sheet->cell_padding)
780 sheet->cell_padding = g_boxed_copy (GTK_TYPE_BORDER,
781 &default_cell_padding);
784 g_object_set (sheet->vaxis, "padding",
785 sheet->cell_padding->top + sheet->cell_padding->bottom,
789 g_object_set (sheet->haxis, "padding",
790 sheet->cell_padding->left + sheet->cell_padding->right,
794 psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
795 g_object_set (sheet->vaxis, "padding",
796 sheet->cell_padding->top + sheet->cell_padding->bottom,
800 psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
801 g_object_set (sheet->haxis, "padding",
802 sheet->cell_padding->left + sheet->cell_padding->right,
806 psppire_sheet_set_model (sheet, g_value_get_pointer (value));
809 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
815 psppire_sheet_get_property (GObject *object,
820 PsppireSheet *sheet = PSPPIRE_SHEET (object);
824 case PROP_CELL_PADDING:
825 g_value_set_boxed (value, sheet->cell_padding);
828 g_value_set_pointer (value, sheet->vaxis);
831 g_value_set_pointer (value, sheet->haxis);
834 g_value_set_pointer (value, sheet->model);
837 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
844 psppire_sheet_class_init (PsppireSheetClass *klass)
846 GObjectClass *object_class = G_OBJECT_CLASS (klass);
848 GParamSpec *haxis_spec ;
849 GParamSpec *vaxis_spec ;
850 GParamSpec *model_spec ;
851 GParamSpec *cell_padding_spec ;
853 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
854 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
856 parent_class = g_type_class_peek_parent (klass);
859 * PsppireSheet::select-row
860 * @sheet: the sheet widget that emitted the signal
861 * @row: the newly selected row index
863 * A row has been selected.
865 sheet_signals[SELECT_ROW] =
866 g_signal_new ("select-row",
867 G_TYPE_FROM_CLASS (object_class),
869 offsetof (PsppireSheetClass, select_row),
871 g_cclosure_marshal_VOID__INT,
878 * PsppireSheet::select - column
879 * @sheet: the sheet widget that emitted the signal
880 * @column: the newly selected column index
882 * A column has been selected.
884 sheet_signals[SELECT_COLUMN] =
885 g_signal_new ("select-column",
886 G_TYPE_FROM_CLASS (object_class),
888 offsetof (PsppireSheetClass, select_column),
890 g_cclosure_marshal_VOID__INT,
897 * PsppireSheet::double-click-row
898 * @sheet: the sheet widget that emitted the signal
899 * @row: the row that was double clicked.
901 * A row's title button has been double clicked
903 sheet_signals[DOUBLE_CLICK_ROW] =
904 g_signal_new ("double-click-row",
905 G_TYPE_FROM_CLASS (object_class),
909 g_cclosure_marshal_VOID__INT,
916 * PsppireSheet::double-click-column
917 * @sheet: the sheet widget that emitted the signal
918 * @column: the column that was double clicked.
920 * A column's title button has been double clicked
922 sheet_signals[DOUBLE_CLICK_COLUMN] =
923 g_signal_new ("double-click-column",
924 G_TYPE_FROM_CLASS (object_class),
928 g_cclosure_marshal_VOID__INT,
935 * PsppireSheet::button-event-column
936 * @sheet: the sheet widget that emitted the signal
937 * @column: the column on which the event occured.
939 * A button event occured on a column title button
941 sheet_signals[BUTTON_EVENT_COLUMN] =
942 g_signal_new ("button-event-column",
943 G_TYPE_FROM_CLASS (object_class),
947 psppire_marshal_VOID__INT_POINTER,
956 * PsppireSheet::button-event-row
957 * @sheet: the sheet widget that emitted the signal
958 * @column: the column on which the event occured.
960 * A button event occured on a row title button
962 sheet_signals[BUTTON_EVENT_ROW] =
963 g_signal_new ("button-event-row",
964 G_TYPE_FROM_CLASS (object_class),
968 psppire_marshal_VOID__INT_POINTER,
976 sheet_signals[SELECT_RANGE] =
977 g_signal_new ("select-range",
978 G_TYPE_FROM_CLASS (object_class),
980 offsetof (PsppireSheetClass, select_range),
982 g_cclosure_marshal_VOID__BOXED,
985 PSPPIRE_TYPE_SHEET_RANGE);
988 sheet_signals[RESIZE_RANGE] =
989 g_signal_new ("resize-range",
990 G_TYPE_FROM_CLASS (object_class),
992 offsetof (PsppireSheetClass, resize_range),
994 psppire_marshal_VOID__BOXED_BOXED,
997 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
1000 sheet_signals[MOVE_RANGE] =
1001 g_signal_new ("move-range",
1002 G_TYPE_FROM_CLASS (object_class),
1004 offsetof (PsppireSheetClass, move_range),
1006 psppire_marshal_VOID__BOXED_BOXED,
1009 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
1012 sheet_signals[TRAVERSE] =
1013 g_signal_new ("traverse",
1014 G_TYPE_FROM_CLASS (object_class),
1016 offsetof (PsppireSheetClass, traverse),
1018 psppire_marshal_BOOLEAN__BOXED_POINTER,
1020 PSPPIRE_TYPE_SHEET_CELL,
1024 sheet_signals[ACTIVATE] =
1025 g_signal_new ("activate",
1026 G_TYPE_FROM_CLASS (object_class),
1028 offsetof (PsppireSheetClass, activate),
1030 psppire_marshal_VOID__INT_INT_INT_INT,
1032 G_TYPE_INT, G_TYPE_INT,
1033 G_TYPE_INT, G_TYPE_INT);
1035 widget_class->set_scroll_adjustments_signal =
1036 g_signal_new ("set-scroll-adjustments",
1037 G_TYPE_FROM_CLASS (object_class),
1039 offsetof (PsppireSheetClass, set_scroll_adjustments),
1041 psppire_marshal_VOID__OBJECT_OBJECT,
1042 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1045 container_class->add = NULL;
1046 container_class->remove = NULL;
1047 container_class->forall = psppire_sheet_forall;
1048 container_class->set_focus_child = NULL;
1050 object_class->dispose = psppire_sheet_dispose;
1051 object_class->finalize = psppire_sheet_finalize;
1054 g_param_spec_boxed ("cell-padding",
1056 "The space between a cell's contents and its border",
1058 G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
1061 g_param_spec_pointer ("vertical-axis",
1063 "A pointer to the PsppireAxis object for the rows",
1064 G_PARAM_READABLE | G_PARAM_WRITABLE );
1067 g_param_spec_pointer ("horizontal-axis",
1069 "A pointer to the PsppireAxis object for the columns",
1070 G_PARAM_READABLE | G_PARAM_WRITABLE );
1073 g_param_spec_pointer ("model",
1075 "A pointer to the data model",
1076 G_PARAM_READABLE | G_PARAM_WRITABLE );
1079 object_class->set_property = psppire_sheet_set_property;
1080 object_class->get_property = psppire_sheet_get_property;
1082 g_object_class_install_property (object_class,
1086 g_object_class_install_property (object_class,
1090 g_object_class_install_property (object_class,
1094 g_object_class_install_property (object_class,
1099 widget_class->realize = psppire_sheet_realize;
1100 widget_class->unrealize = psppire_sheet_unrealize;
1101 widget_class->map = psppire_sheet_map;
1102 widget_class->unmap = psppire_sheet_unmap;
1103 widget_class->style_set = psppire_sheet_style_set;
1104 widget_class->button_press_event = psppire_sheet_button_press;
1105 widget_class->button_release_event = psppire_sheet_button_release;
1106 widget_class->motion_notify_event = psppire_sheet_motion;
1107 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1108 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1109 widget_class->key_press_event = psppire_sheet_key_press;
1110 widget_class->expose_event = psppire_sheet_expose;
1111 widget_class->size_request = psppire_sheet_size_request;
1112 widget_class->size_allocate = psppire_sheet_size_allocate;
1113 widget_class->focus_in_event = psppire_sheet_focus_in;
1114 widget_class->focus_out_event = NULL;
1116 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1117 klass->select_row = NULL;
1118 klass->select_column = NULL;
1119 klass->select_range = NULL;
1120 klass->resize_range = NULL;
1121 klass->move_range = NULL;
1122 klass->traverse = NULL;
1123 klass->activate = NULL;
1124 klass->changed = NULL;
1128 psppire_sheet_init (PsppireSheet *sheet)
1130 sheet->model = NULL;
1131 sheet->haxis = NULL;
1132 sheet->vaxis = NULL;
1135 sheet->selection_mode = GTK_SELECTION_NONE;
1136 sheet->select_status = PSPPIRE_SHEET_NORMAL;
1138 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1139 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1141 sheet->column_title_window = NULL;
1142 sheet->column_title_area.x = 0;
1143 sheet->column_title_area.y = 0;
1144 sheet->column_title_area.width = 0;
1145 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1147 sheet->row_title_window = NULL;
1148 sheet->row_title_area.x = 0;
1149 sheet->row_title_area.y = 0;
1150 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1151 sheet->row_title_area.height = 0;
1154 sheet->active_cell.row = 0;
1155 sheet->active_cell.col = 0;
1157 sheet->range.row0 = 0;
1158 sheet->range.rowi = 0;
1159 sheet->range.col0 = 0;
1160 sheet->range.coli = 0;
1162 sheet->sheet_window = NULL;
1163 sheet->entry_widget = NULL;
1164 sheet->button = NULL;
1166 sheet->hadjustment = NULL;
1167 sheet->vadjustment = NULL;
1169 sheet->cursor_drag = NULL;
1171 sheet->xor_gc = NULL;
1172 sheet->fg_gc = NULL;
1173 sheet->bg_gc = NULL;
1176 sheet->show_grid = TRUE;
1178 sheet->motion_timer = 0;
1180 sheet->row_titles_visible = TRUE;
1181 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1183 sheet->column_titles_visible = TRUE;
1186 /* create sheet entry */
1187 sheet->entry_type = GTK_TYPE_ENTRY;
1188 create_sheet_entry (sheet);
1190 /* create global selection button */
1191 create_global_button (sheet);
1195 /* Cause RANGE to be redrawn. If RANGE is null, then the
1196 entire visible range will be redrawn.
1199 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1203 if ( ! GTK_WIDGET_REALIZED (sheet))
1206 if ( NULL != range )
1207 rectangle_from_range (sheet, range, &rect);
1210 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1211 gdk_region_get_clipbox (r, &rect);
1213 if ( sheet->column_titles_visible)
1215 rect.y += sheet->column_title_area.height;
1216 rect.height -= sheet->column_title_area.height;
1219 if ( sheet->row_titles_visible)
1221 rect.x += sheet->row_title_area.width;
1222 rect.width -= sheet->row_title_area.width;
1226 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1230 /* Callback which occurs whenever columns are inserted / deleted in the model */
1232 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1236 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1238 PsppireSheetRange range;
1239 gint model_columns = psppire_sheet_model_get_column_count (model);
1242 /* Need to update all the columns starting from the first column and onwards.
1243 * Previous column are unchanged, so don't need to be updated.
1245 range.col0 = first_column;
1247 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1248 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1250 adjust_scrollbars (sheet);
1252 if (sheet->active_cell.col >= model_columns)
1253 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1255 draw_column_title_buttons_range (sheet,
1256 first_column, max_visible_column (sheet));
1259 redraw_range (sheet, &range);
1265 /* Callback which occurs whenever rows are inserted / deleted in the model */
1267 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1268 gint n_rows, gpointer data)
1270 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1272 PsppireSheetRange range;
1274 gint model_rows = psppire_sheet_model_get_row_count (model);
1276 /* Need to update all the rows starting from the first row and onwards.
1277 * Previous rows are unchanged, so don't need to be updated.
1279 range.row0 = first_row;
1281 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1282 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1284 adjust_scrollbars (sheet);
1286 if (sheet->active_cell.row >= model_rows)
1287 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1289 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1291 redraw_range (sheet, &range);
1295 If row0 or rowi are negative, then all rows will be updated.
1296 If col0 or coli are negative, then all columns will be updated.
1299 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1300 gint rowi, gint coli, gpointer data)
1302 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1304 PsppireSheetRange range;
1311 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1314 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1316 redraw_range (sheet, NULL);
1317 adjust_scrollbars (sheet);
1319 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1320 max_visible_row (sheet));
1322 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1323 max_visible_column (sheet));
1327 else if ( row0 < 0 || rowi < 0 )
1329 range.row0 = min_visible_row (sheet);
1330 range.rowi = max_visible_row (sheet);
1332 else if ( col0 < 0 || coli < 0 )
1334 range.col0 = min_visible_column (sheet);
1335 range.coli = max_visible_column (sheet);
1338 redraw_range (sheet, &range);
1343 * psppire_sheet_new:
1344 * @rows: initial number of rows
1345 * @columns: initial number of columns
1346 * @title: sheet title
1347 * @model: the model to use for the sheet data
1349 * Creates a new sheet widget with the given number of rows and columns.
1351 * Returns: the new sheet widget
1354 psppire_sheet_new (PsppireSheetModel *model)
1356 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1364 * psppire_sheet_set_model
1365 * @sheet: the sheet to set the model for
1366 * @model: the model to use for the sheet data
1368 * Sets the model for a PsppireSheet
1372 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1374 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1376 if (sheet->model ) g_object_unref (sheet->model);
1378 sheet->model = model;
1382 g_object_ref (model);
1384 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1385 G_CALLBACK (range_update_callback),
1388 g_signal_connect (model, "rows_inserted",
1389 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1391 g_signal_connect (model, "rows_deleted",
1392 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1394 g_signal_connect (model, "columns_inserted",
1395 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1397 g_signal_connect (model, "columns_deleted",
1398 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1404 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1408 g_return_if_fail (sheet != NULL);
1409 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1411 state = sheet->select_status;
1413 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1414 psppire_sheet_hide_entry_widget (sheet);
1416 sheet->entry_type = entry_type;
1418 create_sheet_entry (sheet);
1420 if (state == PSPPIRE_SHEET_NORMAL)
1422 psppire_sheet_show_entry_widget (sheet);
1428 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1430 g_return_if_fail (sheet != NULL);
1431 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1433 if (show == sheet->show_grid) return;
1435 sheet->show_grid = show;
1437 redraw_range (sheet, NULL);
1441 psppire_sheet_grid_visible (PsppireSheet *sheet)
1443 g_return_val_if_fail (sheet != NULL, 0);
1444 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1446 return sheet->show_grid;
1450 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1452 g_return_val_if_fail (sheet != NULL, 0);
1453 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1455 return psppire_axis_unit_count (sheet->haxis);
1458 static void set_column_width (PsppireSheet *sheet,
1464 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1466 if (sheet->column_titles_visible) return;
1468 sheet->column_titles_visible = TRUE;
1470 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1473 gdk_window_show (sheet->column_title_window);
1474 gdk_window_move_resize (sheet->column_title_window,
1475 sheet->column_title_area.x,
1476 sheet->column_title_area.y,
1477 sheet->column_title_area.width,
1478 sheet->column_title_area.height);
1480 adjust_scrollbars (sheet);
1482 if (sheet->vadjustment)
1483 g_signal_emit_by_name (sheet->vadjustment,
1486 size_allocate_global_button (sheet);
1488 if ( sheet->row_titles_visible)
1489 gtk_widget_show (sheet->button);
1494 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1496 if (sheet->row_titles_visible) return;
1498 sheet->row_titles_visible = TRUE;
1501 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1503 gdk_window_show (sheet->row_title_window);
1504 gdk_window_move_resize (sheet->row_title_window,
1505 sheet->row_title_area.x,
1506 sheet->row_title_area.y,
1507 sheet->row_title_area.width,
1508 sheet->row_title_area.height);
1510 adjust_scrollbars (sheet);
1513 if (sheet->hadjustment)
1514 g_signal_emit_by_name (sheet->hadjustment,
1517 size_allocate_global_button (sheet);
1519 if ( sheet->column_titles_visible)
1520 gtk_widget_show (sheet->button);
1524 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1526 if (!sheet->column_titles_visible) return;
1528 sheet->column_titles_visible = FALSE;
1530 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1532 if (sheet->column_title_window)
1533 gdk_window_hide (sheet->column_title_window);
1535 gtk_widget_hide (sheet->button);
1537 adjust_scrollbars (sheet);
1540 if (sheet->vadjustment)
1541 g_signal_emit_by_name (sheet->vadjustment,
1546 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1548 if (!sheet->row_titles_visible) return;
1550 sheet->row_titles_visible = FALSE;
1552 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1554 if (sheet->row_title_window)
1555 gdk_window_hide (sheet->row_title_window);
1557 gtk_widget_hide (sheet->button);
1559 adjust_scrollbars (sheet);
1562 if (sheet->hadjustment)
1563 g_signal_emit_by_name (sheet->hadjustment,
1568 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1569 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1570 at the {top,left} of the sheet. If it's 1, then it'll
1571 be placed at the {bottom,right}.
1572 ROW or COL may be -1, in which case scrolling in that dimension
1576 psppire_sheet_moveto (PsppireSheet *sheet,
1584 g_return_if_fail (row_align >= 0);
1585 g_return_if_fail (col_align >= 0);
1587 g_return_if_fail (row_align <= 1);
1588 g_return_if_fail (col_align <= 1);
1590 g_return_if_fail (col <
1591 psppire_axis_unit_count (sheet->haxis));
1592 g_return_if_fail (row <
1593 psppire_axis_unit_count (sheet->vaxis));
1595 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1600 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1602 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1608 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1610 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1616 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
1618 g_return_if_fail (sheet != NULL);
1619 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1621 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
1624 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
1625 psppire_sheet_real_unselect_range (sheet, NULL);
1627 sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
1628 sheet->range.row0 = row;
1629 sheet->range.col0 = 0;
1630 sheet->range.rowi = row;
1631 sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1633 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
1634 psppire_sheet_real_select_range (sheet, NULL);
1639 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
1641 g_return_if_fail (sheet != NULL);
1642 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1644 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
1647 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
1648 psppire_sheet_real_unselect_range (sheet, NULL);
1650 sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
1651 sheet->range.row0 = 0;
1652 sheet->range.col0 = column;
1653 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1654 sheet->range.coli = column;
1656 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
1657 psppire_sheet_real_select_range (sheet, NULL);
1664 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1665 const PsppireSheetRange *range)
1667 g_return_val_if_fail (sheet != NULL, FALSE);
1669 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1672 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1675 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1678 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1681 if (range->rowi < min_visible_row (sheet))
1684 if (range->row0 > max_visible_row (sheet))
1687 if (range->coli < min_visible_column (sheet))
1690 if (range->col0 > max_visible_column (sheet))
1697 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1698 gint row, gint column)
1700 PsppireSheetRange range;
1703 range.col0 = column;
1705 range.coli = column;
1707 return psppire_sheet_range_isvisible (sheet, &range);
1711 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1713 g_return_if_fail (sheet != NULL);
1714 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1715 g_return_if_fail (range != NULL);
1717 range->row0 = min_visible_row (sheet);
1718 range->col0 = min_visible_column (sheet);
1719 range->rowi = max_visible_row (sheet);
1720 range->coli = max_visible_column (sheet);
1725 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1726 GtkAdjustment *hadjustment,
1727 GtkAdjustment *vadjustment)
1729 if ( sheet->vadjustment != vadjustment )
1731 if (sheet->vadjustment)
1732 g_object_unref (sheet->vadjustment);
1733 sheet->vadjustment = vadjustment;
1737 g_object_ref (vadjustment);
1739 g_signal_connect (sheet->vadjustment, "value_changed",
1740 G_CALLBACK (vadjustment_value_changed),
1745 if ( sheet->hadjustment != hadjustment )
1747 if (sheet->hadjustment)
1748 g_object_unref (sheet->hadjustment);
1750 sheet->hadjustment = hadjustment;
1754 g_object_ref (hadjustment);
1756 g_signal_connect (sheet->hadjustment, "value_changed",
1757 G_CALLBACK (hadjustment_value_changed),
1765 psppire_sheet_finalize (GObject *object)
1767 PsppireSheet *sheet;
1769 g_return_if_fail (object != NULL);
1770 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1772 sheet = PSPPIRE_SHEET (object);
1774 if (G_OBJECT_CLASS (parent_class)->finalize)
1775 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1779 psppire_sheet_dispose (GObject *object)
1781 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1783 g_return_if_fail (object != NULL);
1784 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1786 if ( sheet->dispose_has_run )
1789 sheet->dispose_has_run = TRUE;
1791 if ( sheet->cell_padding)
1792 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1794 if (sheet->model) g_object_unref (sheet->model);
1795 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1796 if (sheet->haxis) g_object_unref (sheet->haxis);
1798 g_object_unref (sheet->button);
1799 sheet->button = NULL;
1801 /* unref adjustments */
1802 if (sheet->hadjustment)
1804 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1805 G_SIGNAL_MATCH_DATA,
1809 g_object_unref (sheet->hadjustment);
1810 sheet->hadjustment = NULL;
1813 if (sheet->vadjustment)
1815 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1816 G_SIGNAL_MATCH_DATA,
1820 g_object_unref (sheet->vadjustment);
1822 sheet->vadjustment = NULL;
1825 if (G_OBJECT_CLASS (parent_class)->dispose)
1826 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1830 psppire_sheet_style_set (GtkWidget *widget,
1831 GtkStyle *previous_style)
1833 PsppireSheet *sheet;
1835 g_return_if_fail (widget != NULL);
1836 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1838 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1839 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1841 sheet = PSPPIRE_SHEET (widget);
1843 if (GTK_WIDGET_REALIZED (widget))
1845 gtk_style_set_background (widget->style, widget->window, widget->state);
1848 set_entry_widget_font (sheet);
1853 psppire_sheet_realize (GtkWidget *widget)
1855 PsppireSheet *sheet;
1856 GdkWindowAttr attributes;
1857 const gint attributes_mask =
1858 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1861 GdkColormap *colormap;
1862 GdkDisplay *display;
1864 g_return_if_fail (widget != NULL);
1865 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1867 sheet = PSPPIRE_SHEET (widget);
1869 colormap = gtk_widget_get_colormap (widget);
1870 display = gtk_widget_get_display (widget);
1872 attributes.window_type = GDK_WINDOW_CHILD;
1873 attributes.x = widget->allocation.x;
1874 attributes.y = widget->allocation.y;
1875 attributes.width = widget->allocation.width;
1876 attributes.height = widget->allocation.height;
1877 attributes.wclass = GDK_INPUT_OUTPUT;
1879 attributes.visual = gtk_widget_get_visual (widget);
1880 attributes.colormap = colormap;
1882 attributes.event_mask = gtk_widget_get_events (widget);
1883 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1884 GDK_BUTTON_PRESS_MASK |
1885 GDK_BUTTON_RELEASE_MASK |
1886 GDK_KEY_PRESS_MASK |
1887 GDK_ENTER_NOTIFY_MASK |
1888 GDK_LEAVE_NOTIFY_MASK |
1889 GDK_POINTER_MOTION_MASK |
1890 GDK_POINTER_MOTION_HINT_MASK);
1892 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1895 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1897 gdk_window_set_user_data (widget->window, sheet);
1899 widget->style = gtk_style_attach (widget->style, widget->window);
1901 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1903 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1904 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1906 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1907 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1912 attributes.width = sheet->column_title_area.width;
1913 attributes.height = sheet->column_title_area.height;
1916 /* column - title window */
1917 sheet->column_title_window =
1918 gdk_window_new (widget->window, &attributes, attributes_mask);
1919 gdk_window_set_user_data (sheet->column_title_window, sheet);
1920 gtk_style_set_background (widget->style, sheet->column_title_window,
1926 attributes.width = sheet->row_title_area.width;
1927 attributes.height = sheet->row_title_area.height;
1929 /* row - title window */
1930 sheet->row_title_window = gdk_window_new (widget->window,
1931 &attributes, attributes_mask);
1932 gdk_window_set_user_data (sheet->row_title_window, sheet);
1933 gtk_style_set_background (widget->style, sheet->row_title_window,
1936 /* sheet - window */
1937 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1942 sheet->sheet_window = gdk_window_new (widget->window,
1943 &attributes, attributes_mask);
1944 gdk_window_set_user_data (sheet->sheet_window, sheet);
1946 gdk_cursor_unref (attributes.cursor);
1948 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1949 gdk_window_show (sheet->sheet_window);
1952 sheet->fg_gc = gdk_gc_new (widget->window);
1953 sheet->bg_gc = gdk_gc_new (widget->window);
1955 values.foreground = widget->style->white;
1956 values.function = GDK_INVERT;
1957 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1958 values.line_width = MAX (sheet->cell_padding->left,
1959 MAX (sheet->cell_padding->right,
1960 MAX (sheet->cell_padding->top,
1961 sheet->cell_padding->bottom)));
1963 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1971 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1972 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1974 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1975 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1977 sheet->button->style = gtk_style_attach (sheet->button->style,
1978 sheet->sheet_window);
1981 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1983 if (sheet->column_titles_visible)
1984 gdk_window_show (sheet->column_title_window);
1985 if (sheet->row_titles_visible)
1986 gdk_window_show (sheet->row_title_window);
1988 sheet->hover_window = create_hover_window ();
1990 draw_row_title_buttons (sheet);
1991 draw_column_title_buttons (sheet);
1993 psppire_sheet_update_primary_selection (sheet);
1996 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
2000 create_global_button (PsppireSheet *sheet)
2002 sheet->button = gtk_button_new_with_label (" ");
2004 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
2006 g_object_ref_sink (sheet->button);
2008 g_signal_connect (sheet->button,
2010 G_CALLBACK (global_button_clicked),
2015 size_allocate_global_button (PsppireSheet *sheet)
2017 GtkAllocation allocation;
2019 if (!sheet->column_titles_visible) return;
2020 if (!sheet->row_titles_visible) return;
2022 gtk_widget_size_request (sheet->button, NULL);
2026 allocation.width = sheet->row_title_area.width;
2027 allocation.height = sheet->column_title_area.height;
2029 gtk_widget_size_allocate (sheet->button, &allocation);
2033 global_button_clicked (GtkWidget *widget, gpointer data)
2035 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
2040 psppire_sheet_unrealize (GtkWidget *widget)
2042 PsppireSheet *sheet;
2044 g_return_if_fail (widget != NULL);
2045 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2047 sheet = PSPPIRE_SHEET (widget);
2049 gdk_cursor_unref (sheet->cursor_drag);
2050 sheet->cursor_drag = NULL;
2052 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2053 sheet->color, n_COLORS);
2055 g_object_unref (sheet->xor_gc);
2056 g_object_unref (sheet->fg_gc);
2057 g_object_unref (sheet->bg_gc);
2059 destroy_hover_window (sheet->hover_window);
2061 gdk_window_destroy (sheet->sheet_window);
2062 gdk_window_destroy (sheet->column_title_window);
2063 gdk_window_destroy (sheet->row_title_window);
2065 gtk_widget_unparent (sheet->entry_widget);
2066 if (sheet->button != NULL)
2067 gtk_widget_unparent (sheet->button);
2069 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2070 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2074 psppire_sheet_map (GtkWidget *widget)
2076 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2078 g_return_if_fail (widget != NULL);
2079 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2081 if (!GTK_WIDGET_MAPPED (widget))
2083 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2085 gdk_window_show (widget->window);
2086 gdk_window_show (sheet->sheet_window);
2088 if (sheet->column_titles_visible)
2090 draw_column_title_buttons (sheet);
2091 gdk_window_show (sheet->column_title_window);
2093 if (sheet->row_titles_visible)
2095 draw_row_title_buttons (sheet);
2096 gdk_window_show (sheet->row_title_window);
2099 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2100 && sheet->active_cell.row >= 0
2101 && sheet->active_cell.col >= 0 )
2103 gtk_widget_show (sheet->entry_widget);
2104 gtk_widget_map (sheet->entry_widget);
2107 if (!GTK_WIDGET_MAPPED (sheet->button))
2109 gtk_widget_show (sheet->button);
2110 gtk_widget_map (sheet->button);
2113 redraw_range (sheet, NULL);
2114 change_active_cell (sheet,
2115 sheet->active_cell.row,
2116 sheet->active_cell.col);
2121 psppire_sheet_unmap (GtkWidget *widget)
2123 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2125 if (!GTK_WIDGET_MAPPED (widget))
2128 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2130 gdk_window_hide (sheet->sheet_window);
2131 if (sheet->column_titles_visible)
2132 gdk_window_hide (sheet->column_title_window);
2133 if (sheet->row_titles_visible)
2134 gdk_window_hide (sheet->row_title_window);
2135 gdk_window_hide (widget->window);
2137 gtk_widget_unmap (sheet->entry_widget);
2138 gtk_widget_unmap (sheet->button);
2139 gtk_widget_unmap (sheet->hover_window->window);
2142 /* get cell attributes of the given cell */
2143 /* TRUE means that the cell is currently allocated */
2144 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2146 PsppireSheetCellAttr *attributes);
2151 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2153 PangoLayout *layout;
2154 PangoRectangle text;
2155 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2160 PsppireSheetCellAttr attributes;
2163 g_return_if_fail (sheet != NULL);
2165 /* bail now if we aren't yet drawable */
2166 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2169 row >= psppire_axis_unit_count (sheet->vaxis))
2173 col >= psppire_axis_unit_count (sheet->haxis))
2176 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2178 /* select GC for background rectangle */
2179 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2180 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2182 rectangle_from_cell (sheet, row, col, &area);
2184 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2186 if (sheet->show_grid)
2188 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2190 gdk_draw_rectangle (sheet->sheet_window,
2194 area.width, area.height);
2198 label = psppire_sheet_cell_get_text (sheet, row, col);
2203 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2204 dispose_string (sheet, label);
2207 pango_layout_set_font_description (layout, font_desc);
2209 pango_layout_get_pixel_extents (layout, NULL, &text);
2211 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2213 font_height = pango_font_description_get_size (font_desc);
2214 if ( !pango_font_description_get_size_is_absolute (font_desc))
2215 font_height /= PANGO_SCALE;
2218 if ( sheet->cell_padding )
2220 area.x += sheet->cell_padding->left;
2221 area.width -= sheet->cell_padding->right
2222 + sheet->cell_padding->left;
2224 area.y += sheet->cell_padding->top;
2225 area.height -= sheet->cell_padding->bottom
2227 sheet->cell_padding->top;
2230 /* Centre the text vertically */
2231 area.y += (area.height - font_height) / 2.0;
2233 switch (attributes.justification)
2235 case GTK_JUSTIFY_RIGHT:
2236 area.x += area.width - text.width;
2238 case GTK_JUSTIFY_CENTER:
2239 area.x += (area.width - text.width) / 2.0;
2241 case GTK_JUSTIFY_LEFT:
2245 g_critical ("Unhandled justification %d in column %d\n",
2246 attributes.justification, col);
2250 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2255 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2256 g_object_unref (layout);
2261 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2263 PsppireSheetRange range;
2268 PsppireSheetRange drawing_range;
2270 gdk_region_get_clipbox (region, &area);
2272 y = area.y + sheet->vadjustment->value;
2273 x = area.x + sheet->hadjustment->value;
2275 if ( sheet->column_titles_visible)
2276 y -= sheet->column_title_area.height;
2278 if ( sheet->row_titles_visible)
2279 x -= sheet->row_title_area.width;
2281 maximize_int (&x, 0);
2282 maximize_int (&y, 0);
2284 range.row0 = row_from_ypixel (sheet, y);
2285 range.rowi = row_from_ypixel (sheet, y + area.height);
2287 range.col0 = column_from_xpixel (sheet, x);
2288 range.coli = column_from_xpixel (sheet, x + area.width);
2290 g_return_if_fail (sheet != NULL);
2291 g_return_if_fail (PSPPIRE_SHEET (sheet));
2293 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2294 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2295 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2298 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2299 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2300 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2301 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2303 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2304 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2306 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2308 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2309 psppire_sheet_cell_draw (sheet, i, j);
2312 if (sheet->select_status == GTK_STATE_NORMAL &&
2313 sheet->active_cell.row >= drawing_range.row0 &&
2314 sheet->active_cell.row <= drawing_range.rowi &&
2315 sheet->active_cell.col >= drawing_range.col0 &&
2316 sheet->active_cell.col <= drawing_range.coli)
2317 psppire_sheet_show_entry_widget (sheet);
2323 safe_strcmp (const gchar *s1, const gchar *s2)
2325 if ( !s1 && !s2) return 0;
2326 if ( !s1) return -1;
2327 if ( !s2) return +1;
2328 return strcmp (s1, s2);
2332 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2333 GtkJustification justification,
2336 PsppireSheetModel *model ;
2339 g_return_if_fail (sheet != NULL);
2340 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2342 if (col >= psppire_axis_unit_count (sheet->haxis)
2343 || row >= psppire_axis_unit_count (sheet->vaxis))
2346 if (col < 0 || row < 0) return;
2348 model = psppire_sheet_get_model (sheet);
2350 old_text = psppire_sheet_model_get_string (model, row, col);
2352 if (0 != safe_strcmp (old_text, text))
2354 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2355 psppire_sheet_model_set_string (model, text, row, col);
2356 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2359 if ( psppire_sheet_model_free_strings (model))
2365 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2367 PsppireSheetRange range;
2369 g_return_if_fail (sheet != NULL);
2370 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2371 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2372 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2374 if (column < 0 || row < 0) return;
2378 range.col0 = min_visible_column (sheet);
2379 range.coli = max_visible_column (sheet);
2381 psppire_sheet_real_cell_clear (sheet, row, column);
2383 redraw_range (sheet, &range);
2387 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2389 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2391 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2393 if (old_text && strlen (old_text) > 0 )
2395 psppire_sheet_model_datum_clear (model, row, column);
2398 dispose_string (sheet, old_text);
2402 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2404 PsppireSheetModel *model;
2405 g_return_val_if_fail (sheet != NULL, NULL);
2406 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2408 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2410 if (col < 0 || row < 0) return NULL;
2412 model = psppire_sheet_get_model (sheet);
2417 return psppire_sheet_model_get_string (model, row, col);
2422 psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
2425 PsppireSheetRange *range;
2427 g_return_val_if_fail (sheet != NULL, 0);
2428 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2429 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
2430 if (col < 0 || row < 0) return 0;
2432 state = sheet->select_status;
2433 range = &sheet->range;
2437 case PSPPIRE_SHEET_NORMAL:
2438 return GTK_STATE_NORMAL;
2440 case PSPPIRE_SHEET_ROW_SELECTED:
2441 if (row >= range->row0 && row <= range->rowi)
2442 return GTK_STATE_SELECTED;
2444 case PSPPIRE_SHEET_COLUMN_SELECTED:
2445 if (col >= range->col0 && col <= range->coli)
2446 return GTK_STATE_SELECTED;
2448 case PSPPIRE_SHEET_RANGE_SELECTED:
2449 if (row >= range->row0 && row <= range->rowi && \
2450 col >= range->col0 && col <= range->coli)
2451 return GTK_STATE_SELECTED;
2454 return GTK_STATE_NORMAL;
2457 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2458 If the function returns FALSE, then the results will be unreliable.
2461 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2469 *column = -G_MAXINT;
2471 g_return_val_if_fail (sheet != NULL, 0);
2472 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2474 /* bounds checking, return false if the user clicked
2482 if ( sheet->column_titles_visible)
2483 y -= sheet->column_title_area.height;
2485 y += sheet->vadjustment->value;
2487 if ( y < 0 && sheet->column_titles_visible)
2493 trow = row_from_ypixel (sheet, y);
2494 if (trow > psppire_axis_unit_count (sheet->vaxis))
2500 if ( sheet->row_titles_visible)
2501 x -= sheet->row_title_area.width;
2503 x += sheet->hadjustment->value;
2505 if ( x < 0 && sheet->row_titles_visible)
2511 tcol = column_from_xpixel (sheet, x);
2512 if (tcol > psppire_axis_unit_count (sheet->haxis))
2522 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2527 g_return_val_if_fail (sheet != NULL, 0);
2528 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2530 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2533 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2534 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2536 area->width= (column == -1) ? sheet->row_title_area.width
2537 : psppire_axis_unit_size (sheet->haxis, column);
2539 area->height= (row == -1) ? sheet->column_title_area.height
2540 : psppire_axis_unit_size (sheet->vaxis, row);
2546 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2548 g_return_if_fail (sheet != NULL);
2549 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2551 if (row < -1 || col < -1)
2554 if (row >= psppire_axis_unit_count (sheet->vaxis)
2556 col >= psppire_axis_unit_count (sheet->haxis))
2559 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2562 if ( row == -1 || col == -1)
2564 psppire_sheet_hide_entry_widget (sheet);
2568 change_active_cell (sheet, row, col);
2572 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2574 g_return_if_fail (sheet != NULL);
2575 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2577 if ( row ) *row = sheet->active_cell.row;
2578 if (column) *column = sheet->active_cell.col;
2582 entry_load_text (PsppireSheet *sheet)
2586 GtkJustification justification;
2587 PsppireSheetCellAttr attributes;
2589 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2590 if (sheet->select_status != GTK_STATE_NORMAL) return;
2592 row = sheet->active_cell.row;
2593 col = sheet->active_cell.col;
2595 if (row < 0 || col < 0) return;
2597 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2599 if (text && strlen (text) > 0)
2601 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2602 justification = attributes.justification;
2603 psppire_sheet_set_cell (sheet, row, col, justification, text);
2609 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2611 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2614 if (sheet->active_cell.row < 0 ||
2615 sheet->active_cell.col < 0) return;
2617 gtk_widget_hide (sheet->entry_widget);
2618 gtk_widget_unmap (sheet->entry_widget);
2620 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2624 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2626 gint old_row, old_col;
2628 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2630 if (row < 0 || col < 0)
2633 if ( row > psppire_axis_unit_count (sheet->vaxis)
2634 || col > psppire_axis_unit_count (sheet->haxis))
2637 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2639 sheet->select_status = PSPPIRE_SHEET_NORMAL;
2640 psppire_sheet_real_unselect_range (sheet, NULL);
2643 old_row = sheet->active_cell.row;
2644 old_col = sheet->active_cell.col;
2646 entry_load_text (sheet);
2648 /* Erase the old cell border */
2649 psppire_sheet_draw_active_cell (sheet);
2651 sheet->active_cell.row = row;
2652 sheet->active_cell.col = col;
2654 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2656 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2658 psppire_sheet_draw_active_cell (sheet);
2659 psppire_sheet_show_entry_widget (sheet);
2661 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2663 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2664 row, col, old_row, old_col);
2669 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2671 GtkEntry *sheet_entry;
2672 PsppireSheetCellAttr attributes;
2676 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2678 row = sheet->active_cell.row;
2679 col = sheet->active_cell.col;
2681 /* Don't show the active cell, if there is no active cell: */
2682 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2685 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2686 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2687 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2689 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2691 sheet_entry = psppire_sheet_get_entry (sheet);
2693 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2695 if (GTK_IS_ENTRY (sheet_entry))
2697 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2698 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2701 text = g_strdup ("");
2703 if (strcmp (old_text, text) != 0)
2704 gtk_entry_set_text (sheet_entry, text);
2706 dispose_string (sheet, text);
2709 switch (attributes.justification)
2711 case GTK_JUSTIFY_RIGHT:
2712 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2714 case GTK_JUSTIFY_CENTER:
2715 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2717 case GTK_JUSTIFY_LEFT:
2719 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2725 psppire_sheet_size_allocate_entry (sheet);
2727 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2728 psppire_sheet_model_is_editable (sheet->model,
2730 gtk_widget_map (sheet->entry_widget);
2734 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2737 PsppireSheetRange range;
2739 row = sheet->active_cell.row;
2740 col = sheet->active_cell.col;
2742 if (row < 0 || col < 0) return FALSE;
2744 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2747 range.col0 = range.coli = col;
2748 range.row0 = range.rowi = row;
2750 psppire_sheet_draw_border (sheet, range);
2758 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
2762 rectangle_from_range (sheet, &new_range, &area);
2767 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
2769 area.x += sheet->cell_padding->left / 2;
2770 area.y += sheet->cell_padding->top / 2;
2771 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
2772 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
2774 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
2781 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
2786 psppire_sheet_real_select_range (PsppireSheet *sheet,
2787 const PsppireSheetRange *range)
2791 g_return_if_fail (sheet != NULL);
2793 if (range == NULL) range = &sheet->range;
2795 memcpy (&sheet->range, range, sizeof (*range));
2797 if (range->row0 < 0 || range->rowi < 0) return;
2798 if (range->col0 < 0 || range->coli < 0) return;
2800 state = sheet->select_status;
2802 psppire_sheet_update_primary_selection (sheet);
2804 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
2809 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
2811 g_return_if_fail (sheet != NULL);
2812 *range = sheet->range;
2817 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
2819 g_return_if_fail (sheet != NULL);
2821 if (range == NULL) range=&sheet->range;
2823 if (range->row0 < 0 || range->rowi < 0) return;
2824 if (range->col0 < 0 || range->coli < 0) return;
2827 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2828 psppire_sheet_real_unselect_range (sheet, NULL);
2830 sheet->range.row0 = range->row0;
2831 sheet->range.rowi = range->rowi;
2832 sheet->range.col0 = range->col0;
2833 sheet->range.coli = range->coli;
2835 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
2836 psppire_sheet_real_select_range (sheet, NULL);
2840 psppire_sheet_unselect_range (PsppireSheet *sheet)
2842 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2845 psppire_sheet_real_unselect_range (sheet, NULL);
2846 sheet->select_status = GTK_STATE_NORMAL;
2851 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
2852 const PsppireSheetRange *range)
2854 g_return_if_fail (sheet != NULL);
2855 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
2858 range = &sheet->range;
2860 if (range->row0 < 0 || range->rowi < 0) return;
2861 if (range->col0 < 0 || range->coli < 0) return;
2863 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
2864 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
2866 sheet->range.row0 = -1;
2867 sheet->range.rowi = -1;
2868 sheet->range.col0 = -1;
2869 sheet->range.coli = -1;
2874 psppire_sheet_expose (GtkWidget *widget, GdkEventExpose *event)
2876 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2878 g_return_val_if_fail (event != NULL, FALSE);
2880 if (!GTK_WIDGET_DRAWABLE (widget))
2883 /* exposure events on the sheet */
2884 if (event->window == sheet->row_title_window &&
2885 sheet->row_titles_visible)
2887 draw_row_title_buttons_range (sheet,
2888 min_visible_row (sheet),
2889 max_visible_row (sheet));
2892 if (event->window == sheet->column_title_window &&
2893 sheet->column_titles_visible)
2895 draw_column_title_buttons_range (sheet,
2896 min_visible_column (sheet),
2897 max_visible_column (sheet));
2900 if (event->window == sheet->sheet_window)
2902 draw_sheet_region (sheet, event->region);
2905 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2908 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2909 psppire_sheet_range_draw (sheet, &sheet->range);
2911 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2912 psppire_sheet_range_draw (sheet, &sheet->drag_range);
2915 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2919 rectangle_from_range (sheet, &sheet->range, &area);
2921 gdk_draw_rectangle (sheet->sheet_window,
2924 area.x + 1, area.y + 1,
2925 area.width, area.height);
2930 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2931 draw_xor_rectangle (sheet, sheet->drag_range);
2936 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
2939 PsppireSheetRange range;
2940 range.row0 = range.rowi = sheet->active_cell.row;
2941 range.col0 = range.coli = sheet->active_cell.col;
2943 rectangle_from_range (sheet, &range, &rect);
2945 if (GDK_OVERLAP_RECTANGLE_OUT !=
2946 gdk_region_rect_in (event->region, &rect))
2948 psppire_sheet_draw_active_cell (sheet);
2954 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2961 psppire_sheet_button_press (GtkWidget *widget,
2962 GdkEventButton *event)
2964 PsppireSheet *sheet;
2965 GdkModifierType mods;
2970 g_return_val_if_fail (widget != NULL, FALSE);
2971 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
2972 g_return_val_if_fail (event != NULL, FALSE);
2974 sheet = PSPPIRE_SHEET (widget);
2976 /* Cancel any pending tooltips */
2977 if (sheet->motion_timer)
2979 g_source_remove (sheet->motion_timer);
2980 sheet->motion_timer = 0;
2983 gtk_widget_get_pointer (widget, &x, &y);
2984 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2987 if (event->window == sheet->column_title_window)
2989 sheet->x_drag = event->x;
2990 g_signal_emit (sheet,
2991 sheet_signals[BUTTON_EVENT_COLUMN], 0,
2994 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2996 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2997 g_signal_emit (sheet,
2998 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3001 else if (event->window == sheet->row_title_window)
3003 g_signal_emit (sheet,
3004 sheet_signals[BUTTON_EVENT_ROW], 0,
3007 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3009 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3010 g_signal_emit (sheet,
3011 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3015 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3017 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3020 /* press on resize windows */
3021 if (event->window == sheet->column_title_window)
3023 sheet->x_drag = event->x;
3025 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3027 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3028 gdk_pointer_grab (sheet->column_title_window, FALSE,
3029 GDK_POINTER_MOTION_HINT_MASK |
3030 GDK_BUTTON1_MOTION_MASK |
3031 GDK_BUTTON_RELEASE_MASK,
3032 NULL, NULL, event->time);
3034 draw_xor_vline (sheet);
3039 if (event->window == sheet->row_title_window)
3041 sheet->y_drag = event->y;
3043 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3045 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3046 gdk_pointer_grab (sheet->row_title_window, FALSE,
3047 GDK_POINTER_MOTION_HINT_MASK |
3048 GDK_BUTTON1_MOTION_MASK |
3049 GDK_BUTTON_RELEASE_MASK,
3050 NULL, NULL, event->time);
3052 draw_xor_hline (sheet);
3057 /* the sheet itself does not handle other than single click events */
3058 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3060 /* selections on the sheet */
3061 if (event->window == sheet->sheet_window)
3063 gtk_widget_get_pointer (widget, &x, &y);
3064 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3065 gdk_pointer_grab (sheet->sheet_window, FALSE,
3066 GDK_POINTER_MOTION_HINT_MASK |
3067 GDK_BUTTON1_MOTION_MASK |
3068 GDK_BUTTON_RELEASE_MASK,
3069 NULL, NULL, event->time);
3070 gtk_grab_add (GTK_WIDGET (sheet));
3072 if (psppire_sheet_click_cell (sheet, row, column))
3073 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3076 if (event->window == sheet->column_title_window)
3078 gtk_widget_get_pointer (widget, &x, &y);
3079 if ( sheet->row_titles_visible)
3080 x -= sheet->row_title_area.width;
3082 x += sheet->hadjustment->value;
3084 column = column_from_xpixel (sheet, x);
3086 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3088 gtk_grab_add (GTK_WIDGET (sheet));
3089 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3093 if (event->window == sheet->row_title_window)
3095 gtk_widget_get_pointer (widget, &x, &y);
3096 if ( sheet->column_titles_visible)
3097 y -= sheet->column_title_area.height;
3099 y += sheet->vadjustment->value;
3101 row = row_from_ypixel (sheet, y);
3102 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3104 gtk_grab_add (GTK_WIDGET (sheet));
3105 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3113 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3115 PsppireSheetCell cell;
3116 gboolean forbid_move;
3121 if (row >= psppire_axis_unit_count (sheet->vaxis)
3122 || column >= psppire_axis_unit_count (sheet->haxis))
3127 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3128 &sheet->active_cell,
3134 if (sheet->select_status == GTK_STATE_NORMAL)
3137 row = sheet->active_cell.row;
3138 column = sheet->active_cell.col;
3140 change_active_cell (sheet, row, column);
3144 if (row == -1 && column >= 0)
3146 psppire_sheet_select_column (sheet, column);
3150 if (column == -1 && row >= 0)
3152 psppire_sheet_select_row (sheet, row);
3156 if (row == -1 && column == -1)
3158 sheet->range.row0 = 0;
3159 sheet->range.col0 = 0;
3160 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3162 psppire_axis_unit_count (sheet->haxis) - 1;
3163 psppire_sheet_select_range (sheet, NULL);
3167 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
3169 sheet->select_status = PSPPIRE_SHEET_NORMAL;
3170 psppire_sheet_real_unselect_range (sheet, NULL);
3174 change_active_cell (sheet, row, column);
3177 sheet->range.row0 = row;
3178 sheet->range.col0 = column;
3179 sheet->range.rowi = row;
3180 sheet->range.coli = column;
3181 sheet->select_status = PSPPIRE_SHEET_NORMAL;
3182 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3184 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3190 psppire_sheet_button_release (GtkWidget *widget,
3191 GdkEventButton *event)
3193 GdkDisplay *display = gtk_widget_get_display (widget);
3195 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3197 /* release on resize windows */
3198 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3201 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3202 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3204 gdk_display_pointer_ungrab (display, event->time);
3205 draw_xor_vline (sheet);
3208 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3209 + sheet->hadjustment->value;
3211 set_column_width (sheet, sheet->drag_cell.col, width);
3216 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3219 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3220 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3222 gdk_display_pointer_ungrab (display, event->time);
3223 draw_xor_hline (sheet);
3226 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3227 sheet->vadjustment->value;
3229 set_row_height (sheet, sheet->drag_cell.row, height);
3234 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3236 PsppireSheetRange old_range;
3237 draw_xor_rectangle (sheet, sheet->drag_range);
3238 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3239 gdk_display_pointer_ungrab (display, event->time);
3241 psppire_sheet_real_unselect_range (sheet, NULL);
3243 old_range = sheet->range;
3244 sheet->range = sheet->drag_range;
3245 sheet->drag_range = old_range;
3246 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3247 &sheet->drag_range, &sheet->range);
3248 psppire_sheet_select_range (sheet, &sheet->range);
3251 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3253 PsppireSheetRange old_range;
3254 draw_xor_rectangle (sheet, sheet->drag_range);
3255 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3256 gdk_display_pointer_ungrab (display, event->time);
3258 psppire_sheet_real_unselect_range (sheet, NULL);
3260 old_range = sheet->range;
3261 sheet->range = sheet->drag_range;
3262 sheet->drag_range = old_range;
3264 if (sheet->select_status == GTK_STATE_NORMAL)
3265 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3267 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3268 &sheet->drag_range, &sheet->range);
3269 psppire_sheet_select_range (sheet, &sheet->range);
3272 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet))
3274 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3275 gdk_display_pointer_ungrab (display, event->time);
3276 change_active_cell (sheet, sheet->active_cell.row,
3277 sheet->active_cell.col);
3280 if (PSPPIRE_SHEET_IN_SELECTION)
3281 gdk_display_pointer_ungrab (display, event->time);
3282 gtk_grab_remove (GTK_WIDGET (sheet));
3284 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3293 /* Shamelessly lifted from gtktooltips */
3295 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3299 gtk_widget_size_request (tip_window, &req);
3300 gtk_paint_flat_box (tip_window->style, tip_window->window,
3301 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3302 NULL, GTK_WIDGET(tip_window), "tooltip",
3303 0, 0, req.width, req.height);
3309 destroy_hover_window (PsppireSheetHoverTitle *h)
3311 gtk_widget_destroy (h->window);
3315 static PsppireSheetHoverTitle *
3316 create_hover_window (void)
3318 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3320 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3322 #if GTK_CHECK_VERSION (2, 9, 0)
3323 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3324 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3327 gtk_widget_set_app_paintable (hw->window, TRUE);
3328 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3329 gtk_widget_set_name (hw->window, "gtk-tooltips");
3330 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3332 g_signal_connect (hw->window,
3334 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3337 hw->label = gtk_label_new (NULL);
3340 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3341 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3343 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3345 gtk_widget_show (hw->label);
3347 g_signal_connect (hw->window,
3349 G_CALLBACK (gtk_widget_destroyed),
3355 #define HOVER_WINDOW_Y_OFFSET 2
3358 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3359 const gchar *subtitle)
3368 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3372 sheet->hover_window->row = row;
3373 sheet->hover_window->column = column;
3375 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3377 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3379 gtk_widget_show (sheet->hover_window->window);
3381 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3387 y += sheet->column_title_area.y;
3388 y += sheet->column_title_area.height;
3389 y += HOVER_WINDOW_Y_OFFSET;
3395 x += sheet->row_title_area.x;
3396 x += sheet->row_title_area.width * 2 / 3.0;
3399 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3404 motion_timeout_callback (gpointer data)
3406 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3410 gdk_threads_enter ();
3411 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3413 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3415 if (sheet->row_title_under && row >= 0)
3417 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3419 show_subtitle (sheet, row, -1, text);
3423 if (sheet->column_title_under && column >= 0)
3425 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3428 show_subtitle (sheet, -1, column, text);
3434 gdk_threads_leave ();
3439 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3441 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3442 GdkModifierType mods;
3443 GdkCursorType new_cursor;
3446 GdkDisplay *display;
3448 g_return_val_if_fail (event != NULL, FALSE);
3450 display = gtk_widget_get_display (widget);
3452 /* selections on the sheet */
3456 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3458 if ( sheet->motion_timer > 0 )
3459 g_source_remove (sheet->motion_timer);
3460 sheet->motion_timer =
3461 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3467 gtk_widget_get_pointer (widget, &wx, &wy);
3469 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3471 if ( row != sheet->hover_window->row ||
3472 column != sheet->hover_window->column)
3474 gtk_widget_hide (sheet->hover_window->window);
3479 if (event->window == sheet->column_title_window)
3481 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3482 on_column_boundary (sheet, x, &column))
3484 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3485 if (new_cursor != sheet->cursor_drag->type)
3487 gdk_cursor_unref (sheet->cursor_drag);
3488 sheet->cursor_drag =
3489 gdk_cursor_new_for_display (display, new_cursor);
3491 gdk_window_set_cursor (sheet->column_title_window,
3492 sheet->cursor_drag);
3497 new_cursor = GDK_TOP_LEFT_ARROW;
3498 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3499 new_cursor != sheet->cursor_drag->type)
3501 gdk_cursor_unref (sheet->cursor_drag);
3502 sheet->cursor_drag =
3503 gdk_cursor_new_for_display (display, new_cursor);
3504 gdk_window_set_cursor (sheet->column_title_window,
3505 sheet->cursor_drag);
3509 else if (event->window == sheet->row_title_window)
3511 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3512 on_row_boundary (sheet, y, &row))
3514 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3515 if (new_cursor != sheet->cursor_drag->type)
3517 gdk_cursor_unref (sheet->cursor_drag);
3518 sheet->cursor_drag =
3519 gdk_cursor_new_for_display (display, new_cursor);
3520 gdk_window_set_cursor (sheet->row_title_window,
3521 sheet->cursor_drag);
3526 new_cursor = GDK_TOP_LEFT_ARROW;
3527 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3528 new_cursor != sheet->cursor_drag->type)
3530 gdk_cursor_unref (sheet->cursor_drag);
3531 sheet->cursor_drag =
3532 gdk_cursor_new_for_display (display, new_cursor);
3533 gdk_window_set_cursor (sheet->row_title_window,
3534 sheet->cursor_drag);
3539 new_cursor = GDK_PLUS;
3540 if ( event->window == sheet->sheet_window &&
3541 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3542 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3543 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3544 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3545 new_cursor != sheet->cursor_drag->type)
3547 gdk_cursor_unref (sheet->cursor_drag);
3548 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3549 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3552 new_cursor = GDK_TOP_LEFT_ARROW;
3553 if ( event->window == sheet->sheet_window &&
3554 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3555 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3556 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3557 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3558 new_cursor != sheet->cursor_drag->type)
3560 gdk_cursor_unref (sheet->cursor_drag);
3561 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3562 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3565 new_cursor = GDK_SIZING;
3566 if ( event->window == sheet->sheet_window &&
3567 sheet->selection_mode != GTK_SELECTION_NONE &&
3568 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3569 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3570 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3571 new_cursor != sheet->cursor_drag->type)
3573 gdk_cursor_unref (sheet->cursor_drag);
3574 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3575 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3579 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3580 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3582 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3584 if (event->x != sheet->x_drag)
3586 draw_xor_vline (sheet);
3587 sheet->x_drag = event->x;
3588 draw_xor_vline (sheet);
3594 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3596 if (event->y != sheet->y_drag)
3598 draw_xor_hline (sheet);
3599 sheet->y_drag = event->y;
3600 draw_xor_hline (sheet);
3606 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3608 PsppireSheetRange aux;
3609 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3610 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3611 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3612 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3616 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3617 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3619 aux = sheet->drag_range;
3620 sheet->drag_range.row0 = sheet->range.row0 + row;
3621 sheet->drag_range.col0 = sheet->range.col0 + column;
3622 sheet->drag_range.rowi = sheet->range.rowi + row;
3623 sheet->drag_range.coli = sheet->range.coli + column;
3624 if (aux.row0 != sheet->drag_range.row0 ||
3625 aux.col0 != sheet->drag_range.col0)
3627 draw_xor_rectangle (sheet, aux);
3628 draw_xor_rectangle (sheet, sheet->drag_range);
3634 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3636 PsppireSheetRange aux;
3637 gint v_h, current_col, current_row, col_threshold, row_threshold;
3639 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
3640 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
3642 current_col = column_from_xpixel (sheet, x);
3643 current_row = row_from_ypixel (sheet, y);
3644 column = current_col - sheet->drag_cell.col;
3645 row = current_row - sheet->drag_cell.row;
3647 /*use half of column width resp. row height as threshold to
3649 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
3650 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
3653 if (x < col_threshold)
3656 else if (column < 0)
3658 if (x > col_threshold)
3661 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
3662 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
3665 if (y < row_threshold)
3670 if (y > row_threshold)
3674 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3675 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3685 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3686 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3688 aux = sheet->drag_range;
3689 sheet->drag_range = sheet->range;
3691 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
3692 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
3693 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
3694 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
3696 if (aux.row0 != sheet->drag_range.row0 ||
3697 aux.rowi != sheet->drag_range.rowi ||
3698 aux.col0 != sheet->drag_range.col0 ||
3699 aux.coli != sheet->drag_range.coli)
3701 draw_xor_rectangle (sheet, aux);
3702 draw_xor_rectangle (sheet, sheet->drag_range);
3708 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3710 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
3711 column == sheet->active_cell.col) return TRUE;
3713 if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
3716 sheet->range.rowi = row;
3717 sheet->range.coli = column;
3718 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3720 rectangle_from_range (sheet, &sheet->range, &area);
3721 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
3728 psppire_sheet_crossing_notify (GtkWidget *widget,
3729 GdkEventCrossing *event)
3731 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3733 if (event->window == sheet->column_title_window)
3734 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
3735 else if (event->window == sheet->row_title_window)
3736 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
3738 if (event->type == GDK_LEAVE_NOTIFY)
3739 gtk_widget_hide (sheet->hover_window->window);
3746 psppire_sheet_focus_in (GtkWidget *w,
3747 GdkEventFocus *event)
3749 PsppireSheet *sheet = PSPPIRE_SHEET (w);
3751 gtk_widget_grab_focus (sheet->entry_widget);
3759 psppire_sheet_entry_key_press (GtkWidget *widget,
3763 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
3768 /* Number of rows in a step-increment */
3769 #define ROWS_PER_STEP 1
3773 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
3775 gint old_row = sheet->active_cell.row ;
3776 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
3780 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
3781 min_visible_row (sheet));
3785 case GTK_SCROLL_PAGE_DOWN:
3786 gtk_adjustment_set_value (sheet->vadjustment,
3787 sheet->vadjustment->value +
3788 sheet->vadjustment->page_increment);
3790 case GTK_SCROLL_PAGE_UP:
3791 gtk_adjustment_set_value (sheet->vadjustment,
3792 sheet->vadjustment->value -
3793 sheet->vadjustment->page_increment);
3797 g_assert_not_reached ();
3802 vpixel += psppire_axis_start_pixel (sheet->vaxis,
3803 min_visible_row (sheet));
3805 new_row = row_from_ypixel (sheet, vpixel);
3807 change_active_cell (sheet, new_row,
3808 sheet->active_cell.col);
3813 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
3815 gint current_row = sheet->active_cell.row;
3816 gint current_col = sheet->active_cell.col;
3817 PsppireSheetCell new_cell ;
3818 gboolean forbidden = FALSE;
3820 new_cell.row = current_row;
3821 new_cell.col = current_col;
3825 case GTK_SCROLL_STEP_DOWN:
3828 case GTK_SCROLL_STEP_UP:
3831 case GTK_SCROLL_STEP_RIGHT:
3834 case GTK_SCROLL_STEP_LEFT:
3837 case GTK_SCROLL_STEP_FORWARD:
3840 psppire_sheet_model_get_column_count (sheet->model))
3846 case GTK_SCROLL_STEP_BACKWARD:
3848 if (new_cell.col < 0)
3851 psppire_sheet_model_get_column_count (sheet->model) - 1;
3856 g_assert_not_reached ();
3860 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3861 &sheet->active_cell,
3869 maximize_int (&new_cell.row, 0);
3870 maximize_int (&new_cell.col, 0);
3872 minimize_int (&new_cell.row,
3873 psppire_axis_unit_count (sheet->vaxis) - 1);
3875 minimize_int (&new_cell.col,
3876 psppire_axis_unit_count (sheet->haxis) - 1);
3878 change_active_cell (sheet, new_cell.row, new_cell.col);
3881 if ( new_cell.col > max_fully_visible_column (sheet))
3884 psppire_axis_start_pixel (sheet->haxis,
3886 hpos -= sheet->hadjustment->page_size;
3888 gtk_adjustment_set_value (sheet->hadjustment,
3891 else if ( new_cell.col < min_fully_visible_column (sheet))
3894 psppire_axis_start_pixel (sheet->haxis,
3897 gtk_adjustment_set_value (sheet->hadjustment,
3902 if ( new_cell.row > max_fully_visible_row (sheet))
3905 psppire_axis_start_pixel (sheet->vaxis,
3907 vpos -= sheet->vadjustment->page_size;
3909 gtk_adjustment_set_value (sheet->vadjustment,
3912 else if ( new_cell.row < min_fully_visible_row (sheet))
3915 psppire_axis_start_pixel (sheet->vaxis,
3918 gtk_adjustment_set_value (sheet->vadjustment,
3922 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3927 psppire_sheet_key_press (GtkWidget *widget,
3930 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3932 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3934 switch (key->keyval)
3937 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
3940 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
3942 case GDK_ISO_Left_Tab:
3943 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
3946 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
3950 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3953 step_sheet (sheet, GTK_SCROLL_STEP_UP);
3957 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
3960 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
3964 gtk_adjustment_set_value (sheet->vadjustment,
3965 sheet->vadjustment->lower);
3967 change_active_cell (sheet, 0,
3968 sheet->active_cell.col);
3973 gtk_adjustment_set_value (sheet->vadjustment,
3974 sheet->vadjustment->upper -
3975 sheet->vadjustment->page_size -
3976 sheet->vadjustment->page_increment);
3979 change_active_cellx (sheet,
3980 psppire_axis_unit_count (sheet->vaxis) - 1,
3981 sheet->active_cell.col);
3985 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
3996 psppire_sheet_size_request (GtkWidget *widget,
3997 GtkRequisition *requisition)
3999 PsppireSheet *sheet;
4001 g_return_if_fail (widget != NULL);
4002 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4003 g_return_if_fail (requisition != NULL);
4005 sheet = PSPPIRE_SHEET (widget);
4007 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4008 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4010 /* compute the size of the column title area */
4011 if (sheet->column_titles_visible)
4012 requisition->height += sheet->column_title_area.height;
4014 /* compute the size of the row title area */
4015 if (sheet->row_titles_visible)
4016 requisition->width += sheet->row_title_area.width;
4021 psppire_sheet_size_allocate (GtkWidget *widget,
4022 GtkAllocation *allocation)
4024 PsppireSheet *sheet;
4025 GtkAllocation sheet_allocation;
4028 g_return_if_fail (widget != NULL);
4029 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4030 g_return_if_fail (allocation != NULL);
4032 sheet = PSPPIRE_SHEET (widget);
4033 widget->allocation = *allocation;
4034 border_width = GTK_CONTAINER (widget)->border_width;
4036 if (GTK_WIDGET_REALIZED (widget))
4037 gdk_window_move_resize (widget->window,
4038 allocation->x + border_width,
4039 allocation->y + border_width,
4040 allocation->width - 2 * border_width,
4041 allocation->height - 2 * border_width);
4043 sheet_allocation.x = 0;
4044 sheet_allocation.y = 0;
4045 sheet_allocation.width = allocation->width - 2 * border_width;
4046 sheet_allocation.height = allocation->height - 2 * border_width;
4048 if (GTK_WIDGET_REALIZED (widget))
4049 gdk_window_move_resize (sheet->sheet_window,
4052 sheet_allocation.width,
4053 sheet_allocation.height);
4055 /* position the window which holds the column title buttons */
4056 sheet->column_title_area.x = 0;
4057 sheet->column_title_area.y = 0;
4058 sheet->column_title_area.width = sheet_allocation.width ;
4061 /* position the window which holds the row title buttons */
4062 sheet->row_title_area.x = 0;
4063 sheet->row_title_area.y = 0;
4064 sheet->row_title_area.height = sheet_allocation.height;
4066 if (sheet->row_titles_visible)
4067 sheet->column_title_area.x += sheet->row_title_area.width;
4069 if (sheet->column_titles_visible)
4070 sheet->row_title_area.y += sheet->column_title_area.height;
4072 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4073 gdk_window_move_resize (sheet->column_title_window,
4074 sheet->column_title_area.x,
4075 sheet->column_title_area.y,
4076 sheet->column_title_area.width,
4077 sheet->column_title_area.height);
4080 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4081 gdk_window_move_resize (sheet->row_title_window,
4082 sheet->row_title_area.x,
4083 sheet->row_title_area.y,
4084 sheet->row_title_area.width,
4085 sheet->row_title_area.height);
4087 size_allocate_global_button (sheet);
4091 gint width = sheet->column_title_area.width;
4093 if ( sheet->row_titles_visible)
4094 width -= sheet->row_title_area.width;
4096 g_object_set (sheet->haxis,
4097 "minimum-extent", width,
4104 gint height = sheet->row_title_area.height;
4106 if ( sheet->column_titles_visible)
4107 height -= sheet->column_title_area.height;
4109 g_object_set (sheet->vaxis,
4110 "minimum-extent", height,
4115 /* set the scrollbars adjustments */
4116 adjust_scrollbars (sheet);
4120 draw_column_title_buttons (PsppireSheet *sheet)
4124 if (!sheet->column_titles_visible) return;
4125 if (!GTK_WIDGET_REALIZED (sheet))
4128 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4131 if (sheet->row_titles_visible)
4133 x = sheet->row_title_area.width;
4136 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4138 sheet->column_title_area.width = width;
4139 sheet->column_title_area.x = x;
4140 gdk_window_move_resize (sheet->column_title_window,
4141 sheet->column_title_area.x,
4142 sheet->column_title_area.y,
4143 sheet->column_title_area.width,
4144 sheet->column_title_area.height);
4147 if (max_visible_column (sheet) ==
4148 psppire_axis_unit_count (sheet->haxis) - 1)
4149 gdk_window_clear_area (sheet->column_title_window,
4151 sheet->column_title_area.width,
4152 sheet->column_title_area.height);
4154 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4156 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4157 max_visible_column (sheet));
4161 draw_row_title_buttons (PsppireSheet *sheet)
4166 if (!sheet->row_titles_visible) return;
4167 if (!GTK_WIDGET_REALIZED (sheet))
4170 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4172 if (sheet->column_titles_visible)
4174 y = sheet->column_title_area.height;
4177 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4179 sheet->row_title_area.y = y;
4180 sheet->row_title_area.height = height;
4181 gdk_window_move_resize (sheet->row_title_window,
4182 sheet->row_title_area.x,
4183 sheet->row_title_area.y,
4184 sheet->row_title_area.width,
4185 sheet->row_title_area.height);
4188 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4189 gdk_window_clear_area (sheet->row_title_window,
4191 sheet->row_title_area.width,
4192 sheet->row_title_area.height);
4194 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4196 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4197 max_visible_row (sheet));
4202 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4204 GtkAllocation entry_alloc;
4205 PsppireSheetCellAttr attributes = { 0 };
4206 GtkEntry *sheet_entry;
4208 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4209 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4211 sheet_entry = psppire_sheet_get_entry (sheet);
4213 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4214 sheet->active_cell.col,
4218 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4220 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4222 style->bg[GTK_STATE_NORMAL] = attributes.background;
4223 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4224 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4225 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4226 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4227 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4230 rectangle_from_cell (sheet, sheet->active_cell.row,
4231 sheet->active_cell.col, &entry_alloc);
4233 entry_alloc.x += sheet->cell_padding->left;
4234 entry_alloc.y += sheet->cell_padding->right;
4235 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4236 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4239 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4240 entry_alloc.height);
4241 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4245 /* Copy the sheet's font to the entry widget */
4247 set_entry_widget_font (PsppireSheet *sheet)
4249 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4251 pango_font_description_free (style->font_desc);
4252 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4254 gtk_widget_modify_style (sheet->entry_widget, style);
4258 create_sheet_entry (PsppireSheet *sheet)
4260 if (sheet->entry_widget)
4262 gtk_widget_unparent (sheet->entry_widget);
4265 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4266 g_object_ref_sink (sheet->entry_widget);
4268 gtk_widget_size_request (sheet->entry_widget, NULL);
4270 if ( GTK_IS_ENTRY (sheet->entry_widget))
4272 g_object_set (sheet->entry_widget,
4277 if (GTK_WIDGET_REALIZED (sheet))
4279 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4280 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4281 gtk_widget_realize (sheet->entry_widget);
4284 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4285 G_CALLBACK (psppire_sheet_entry_key_press),
4288 set_entry_widget_font (sheet);
4290 gtk_widget_show (sheet->entry_widget);
4294 /* Finds the last child widget that happens to be of type GtkEntry */
4296 find_entry (GtkWidget *w, gpointer user_data)
4298 GtkWidget **entry = user_data;
4299 if ( GTK_IS_ENTRY (w))
4307 psppire_sheet_get_entry (PsppireSheet *sheet)
4309 GtkWidget *w = sheet->entry_widget;
4311 g_return_val_if_fail (sheet != NULL, NULL);
4312 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4313 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4315 while (! GTK_IS_ENTRY (w))
4317 GtkWidget *entry = NULL;
4319 if (GTK_IS_CONTAINER (w))
4321 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4330 return GTK_ENTRY (w);
4335 draw_button (PsppireSheet *sheet, GdkWindow *window,
4336 PsppireSheetButton *button, gboolean is_sensitive,
4337 GdkRectangle allocation)
4339 GtkShadowType shadow_type;
4340 gint text_width = 0, text_height = 0;
4341 PangoAlignment align = PANGO_ALIGN_LEFT;
4347 g_return_if_fail (sheet != NULL);
4348 g_return_if_fail (button != NULL);
4351 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4353 gdk_window_clear_area (window,
4354 allocation.x, allocation.y,
4355 allocation.width, allocation.height);
4357 gtk_widget_ensure_style (sheet->button);
4359 gtk_paint_box (sheet->button->style, window,
4360 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4362 GTK_WIDGET (sheet->button),
4364 allocation.x, allocation.y,
4365 allocation.width, allocation.height);
4367 state = button->state;
4368 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4370 if (state == GTK_STATE_ACTIVE)
4371 shadow_type = GTK_SHADOW_IN;
4373 shadow_type = GTK_SHADOW_OUT;
4375 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4376 gtk_paint_box (sheet->button->style, window,
4377 button->state, shadow_type,
4378 &allocation, GTK_WIDGET (sheet->button),
4380 allocation.x, allocation.y,
4381 allocation.width, allocation.height);
4383 if ( button->overstruck)
4385 GdkPoint points[2] = {
4386 {allocation.x, allocation.y},
4387 {allocation.x + allocation.width,
4388 allocation.y + allocation.height}
4391 gtk_paint_polygon (sheet->button->style,
4403 if (button->label_visible)
4405 text_height = DEFAULT_ROW_HEIGHT -
4406 2 * COLUMN_TITLES_HEIGHT;
4408 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4410 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4413 allocation.y += 2 * sheet->button->style->ythickness;
4415 if (button->label && strlen (button->label) > 0)
4417 PangoRectangle rect;
4418 gchar *line = button->label;
4420 PangoLayout *layout = NULL;
4421 gint real_x = allocation.x;
4422 gint real_y = allocation.y;
4424 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4425 pango_layout_get_extents (layout, NULL, &rect);
4427 text_width = PANGO_PIXELS (rect.width);
4428 switch (button->justification)
4430 case GTK_JUSTIFY_LEFT:
4431 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4432 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4434 case GTK_JUSTIFY_RIGHT:
4435 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4436 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4438 case GTK_JUSTIFY_CENTER:
4440 real_x = allocation.x + (allocation.width - text_width)/2;
4441 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4442 pango_layout_set_justify (layout, TRUE);
4444 pango_layout_set_alignment (layout, align);
4445 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4454 g_object_unref (layout);
4457 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4459 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4463 psppire_sheet_button_free (button);
4467 /* Draw the column title buttons FIRST through to LAST */
4469 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4473 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4475 if (!sheet->column_titles_visible) return;
4477 g_return_if_fail (first >= min_visible_column (sheet));
4478 g_return_if_fail (last <= max_visible_column (sheet));
4481 rect.height = sheet->column_title_area.height;
4482 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4483 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4484 + psppire_axis_unit_size (sheet->haxis, last);
4486 rect.x -= sheet->hadjustment->value;
4488 minimize_int (&rect.width, sheet->column_title_area.width);
4489 maximize_int (&rect.x, 0);
4491 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4493 for (col = first ; col <= last ; ++col)
4495 GdkRectangle allocation;
4496 gboolean is_sensitive = FALSE;
4498 PsppireSheetButton *
4499 button = psppire_sheet_model_get_column_button (sheet->model, col);
4501 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4503 allocation.x -= sheet->hadjustment->value;
4505 allocation.height = sheet->column_title_area.height;
4506 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4507 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4509 draw_button (sheet, sheet->column_title_window,
4510 button, is_sensitive, allocation);
4513 gdk_window_end_paint (sheet->column_title_window);
4518 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4522 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4524 if (!sheet->row_titles_visible) return;
4526 g_return_if_fail (first >= min_visible_row (sheet));
4527 g_return_if_fail (last <= max_visible_row (sheet));
4530 rect.width = sheet->row_title_area.width;
4531 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4532 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4533 + psppire_axis_unit_size (sheet->vaxis, last);
4535 rect.y -= sheet->vadjustment->value;
4537 minimize_int (&rect.height, sheet->row_title_area.height);
4538 maximize_int (&rect.y, 0);
4540 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4541 for (row = first; row <= last; ++row)
4543 GdkRectangle allocation;
4545 gboolean is_sensitive = FALSE;
4547 PsppireSheetButton *button =
4548 psppire_sheet_model_get_row_button (sheet->model, row);
4550 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4552 allocation.y -= sheet->vadjustment->value;
4554 allocation.width = sheet->row_title_area.width;
4555 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4556 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4558 draw_button (sheet, sheet->row_title_window,
4559 button, is_sensitive, allocation);
4562 gdk_window_end_paint (sheet->row_title_window);
4569 * vadjustment_value_changed
4570 * hadjustment_value_changed */
4574 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4577 (adj->value + adj->page_size)
4579 (adj->upper - adj->lower);
4581 const glong last_item = psppire_axis_unit_count (axis) - 1;
4583 if (isnan (position) || position < 0)
4587 psppire_axis_start_pixel (axis, last_item)
4589 psppire_axis_unit_size (axis, last_item)
4593 adj->page_size = page_size;
4596 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4598 if ( adj->value < adj->lower)
4599 adj->value = adj->lower;
4602 gtk_adjustment_changed (adj);
4607 adjust_scrollbars (PsppireSheet *sheet)
4611 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4614 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4616 if ( sheet->row_titles_visible)
4617 width -= sheet->row_title_area.width;
4619 if (sheet->column_titles_visible)
4620 height -= sheet->column_title_area.height;
4622 if (sheet->vadjustment)
4624 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4626 sheet->vadjustment->step_increment =
4628 psppire_axis_unit_size (sheet->vaxis, last_row);
4630 sheet->vadjustment->page_increment =
4632 sheet->column_title_area.height -
4633 psppire_axis_unit_size (sheet->vaxis, last_row);
4635 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4638 if (sheet->hadjustment)
4640 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4641 sheet->hadjustment->step_increment = 1;
4643 sheet->hadjustment->page_increment = width;
4645 sheet->hadjustment->upper =
4646 psppire_axis_start_pixel (sheet->haxis, last_col)
4648 psppire_axis_unit_size (sheet->haxis, last_col)
4651 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4655 /* Subtracts the region of WIDGET from REGION */
4657 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4660 GdkRectangle intersect;
4663 gdk_region_get_clipbox (region, &rect);
4664 gtk_widget_intersect (widget,
4668 region2 = gdk_region_rectangle (&intersect);
4669 gdk_region_subtract (region, region2);
4670 gdk_region_destroy (region2);
4674 vadjustment_value_changed (GtkAdjustment *adjustment,
4678 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4680 g_return_if_fail (adjustment != NULL);
4682 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4684 gtk_widget_hide (sheet->entry_widget);
4687 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4689 subtract_widget_region (region, sheet->button);
4690 gdk_window_begin_paint_region (sheet->sheet_window, region);
4692 draw_sheet_region (sheet, region);
4694 draw_row_title_buttons (sheet);
4695 psppire_sheet_draw_active_cell (sheet);
4697 gdk_window_end_paint (sheet->sheet_window);
4698 gdk_region_destroy (region);
4703 hadjustment_value_changed (GtkAdjustment *adjustment,
4707 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4709 g_return_if_fail (adjustment != NULL);
4711 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4713 gtk_widget_hide (sheet->entry_widget);
4717 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4719 subtract_widget_region (region, sheet->button);
4720 gdk_window_begin_paint_region (sheet->sheet_window, region);
4722 draw_sheet_region (sheet, region);
4724 draw_column_title_buttons (sheet);
4726 psppire_sheet_draw_active_cell (sheet);
4728 gdk_window_end_paint (sheet->sheet_window);
4730 gdk_region_destroy (region);
4734 /* COLUMN RESIZING */
4736 draw_xor_vline (PsppireSheet *sheet)
4739 gint xpos = sheet->x_drag;
4740 gdk_drawable_get_size (sheet->sheet_window,
4743 if (sheet->row_titles_visible)
4744 xpos += sheet->row_title_area.width;
4746 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4748 sheet->column_title_area.height,
4750 height + CELL_SPACING);
4755 draw_xor_hline (PsppireSheet *sheet)
4759 gint ypos = sheet->y_drag;
4761 gdk_drawable_get_size (sheet->sheet_window,
4765 if (sheet->column_titles_visible)
4766 ypos += sheet->column_title_area.height;
4768 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4769 sheet->row_title_area.width,
4771 width + CELL_SPACING,
4775 /* SELECTED RANGE */
4777 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
4780 GdkRectangle clip_area, area;
4783 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
4784 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
4785 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
4786 psppire_axis_unit_size (sheet->haxis, range.coli);
4787 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
4788 psppire_axis_unit_size (sheet->vaxis, range.rowi);
4790 clip_area.x = sheet->row_title_area.width;
4791 clip_area.y = sheet->column_title_area.height;
4793 gdk_drawable_get_size (sheet->sheet_window,
4794 &clip_area.width, &clip_area.height);
4796 if (!sheet->row_titles_visible) clip_area.x = 0;
4797 if (!sheet->column_titles_visible) clip_area.y = 0;
4801 area.width = area.width + area.x;
4804 if (area.width > clip_area.width) area.width = clip_area.width + 10;
4807 area.height = area.height + area.y;
4810 if (area.height > clip_area.height) area.height = clip_area.height + 10;
4814 clip_area.width += 3;
4815 clip_area.height += 3;
4817 gdk_gc_get_values (sheet->xor_gc, &values);
4819 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
4821 gdk_draw_rectangle (sheet->sheet_window,
4824 area.x + i, area.y + i,
4825 area.width - 2 * i, area.height - 2 * i);
4828 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4830 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
4835 set_column_width (PsppireSheet *sheet,
4839 g_return_if_fail (sheet != NULL);
4840 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4842 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
4848 psppire_axis_resize (sheet->haxis, column,
4849 width - sheet->cell_padding->left -
4850 sheet->cell_padding->right);
4852 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4854 draw_column_title_buttons (sheet);
4855 adjust_scrollbars (sheet);
4856 psppire_sheet_size_allocate_entry (sheet);
4857 redraw_range (sheet, NULL);
4862 set_row_height (PsppireSheet *sheet,
4866 g_return_if_fail (sheet != NULL);
4867 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4869 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
4875 psppire_axis_resize (sheet->vaxis, row,
4876 height - sheet->cell_padding->top -
4877 sheet->cell_padding->bottom);
4879 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
4881 draw_row_title_buttons (sheet);
4882 adjust_scrollbars (sheet);
4883 psppire_sheet_size_allocate_entry (sheet);
4884 redraw_range (sheet, NULL);
4889 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
4890 PsppireSheetCellAttr *attr)
4893 const GtkJustification *j ;
4894 GdkColormap *colormap;
4896 g_return_val_if_fail (sheet != NULL, FALSE);
4897 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
4899 if (row < 0 || col < 0) return FALSE;
4901 attr->foreground = GTK_WIDGET (sheet)->style->black;
4902 attr->background = sheet->color[BG_COLOR];
4904 attr->border.width = 0;
4905 attr->border.line_style = GDK_LINE_SOLID;
4906 attr->border.cap_style = GDK_CAP_NOT_LAST;
4907 attr->border.join_style = GDK_JOIN_MITER;
4908 attr->border.mask = 0;
4909 attr->border.color = GTK_WIDGET (sheet)->style->black;
4911 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
4912 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
4915 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
4916 attr->foreground = *fg;
4919 bg = psppire_sheet_model_get_background (sheet->model, row, col);
4922 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
4923 attr->background = *bg;
4926 attr->justification =
4927 psppire_sheet_model_get_column_justification (sheet->model, col);
4929 j = psppire_sheet_model_get_justification (sheet->model, row, col);
4931 attr->justification = *j;
4937 psppire_sheet_button_size_request (PsppireSheet *sheet,
4938 const PsppireSheetButton *button,
4939 GtkRequisition *button_requisition)
4941 GtkRequisition requisition;
4942 GtkRequisition label_requisition;
4944 label_requisition.height = DEFAULT_ROW_HEIGHT;
4945 label_requisition.width = COLUMN_MIN_WIDTH;
4947 requisition.height = DEFAULT_ROW_HEIGHT;
4948 requisition.width = COLUMN_MIN_WIDTH;
4951 *button_requisition = requisition;
4952 button_requisition->width = MAX (requisition.width, label_requisition.width);
4953 button_requisition->height = MAX (requisition.height, label_requisition.height);
4958 psppire_sheet_forall (GtkContainer *container,
4959 gboolean include_internals,
4960 GtkCallback callback,
4961 gpointer callback_data)
4963 PsppireSheet *sheet = PSPPIRE_SHEET (container);
4965 g_return_if_fail (callback != NULL);
4967 if (sheet->button && sheet->button->parent)
4968 (* callback) (sheet->button, callback_data);
4970 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
4971 (* callback) (sheet->entry_widget, callback_data);
4976 psppire_sheet_get_model (const PsppireSheet *sheet)
4978 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4980 return sheet->model;
4984 PsppireSheetButton *
4985 psppire_sheet_button_new (void)
4987 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
4989 button->state = GTK_STATE_NORMAL;
4990 button->label = NULL;
4991 button->label_visible = TRUE;
4992 button->justification = GTK_JUSTIFY_FILL;
4993 button->overstruck = FALSE;
5000 psppire_sheet_button_free (PsppireSheetButton *button)
5002 if (!button) return ;
5004 g_free (button->label);
5009 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5011 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5013 if ( NULL == celltext)
5016 g_string_append (string, celltext);
5022 range_to_text (const PsppireSheet *sheet)
5027 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5030 string = g_string_sized_new (80);
5032 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5034 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5036 append_cell_text (string, sheet, r, c);
5037 g_string_append (string, "\t");
5039 append_cell_text (string, sheet, r, c);
5040 if ( r < sheet->range.rowi)
5041 g_string_append (string, "\n");
5048 range_to_html (const PsppireSheet *sheet)
5053 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5056 string = g_string_sized_new (480);
5058 g_string_append (string, "<html>\n");
5059 g_string_append (string, "<body>\n");
5060 g_string_append (string, "<table>\n");
5061 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5063 g_string_append (string, "<tr>\n");
5064 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5066 g_string_append (string, "<td>");
5067 append_cell_text (string, sheet, r, c);
5068 g_string_append (string, "</td>\n");
5070 g_string_append (string, "</tr>\n");
5072 g_string_append (string, "</table>\n");
5073 g_string_append (string, "</body>\n");
5074 g_string_append (string, "</html>\n");
5086 primary_get_cb (GtkClipboard *clipboard,
5087 GtkSelectionData *selection_data,
5091 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5092 GString *string = NULL;
5096 case SELECT_FMT_TEXT:
5097 string = range_to_text (sheet);
5099 case SELECT_FMT_HTML:
5100 string = range_to_html (sheet);
5103 g_assert_not_reached ();
5106 gtk_selection_data_set (selection_data, selection_data->target,
5108 (const guchar *) string->str, string->len);
5109 g_string_free (string, TRUE);
5113 primary_clear_cb (GtkClipboard *clipboard,
5116 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5117 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5120 psppire_sheet_real_unselect_range (sheet, NULL);
5124 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5126 static const GtkTargetEntry targets[] = {
5127 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5128 { "STRING", 0, SELECT_FMT_TEXT },
5129 { "TEXT", 0, SELECT_FMT_TEXT },
5130 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5131 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5132 { "text/plain", 0, SELECT_FMT_TEXT },
5133 { "text/html", 0, SELECT_FMT_HTML }
5136 GtkClipboard *clipboard;
5138 if (!GTK_WIDGET_REALIZED (sheet))
5141 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5142 GDK_SELECTION_PRIMARY);
5144 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5146 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5147 G_N_ELEMENTS (targets),
5148 primary_get_cb, primary_clear_cb,
5150 primary_clear_cb (clipboard, sheet);
5154 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5155 gtk_clipboard_clear (clipboard);