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 old_row = sheet->active_cell.row;
2638 old_col = sheet->active_cell.col;
2640 entry_load_text (sheet);
2642 /* Erase the old cell border */
2643 psppire_sheet_draw_active_cell (sheet);
2645 sheet->active_cell.row = row;
2646 sheet->active_cell.col = col;
2648 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2650 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2652 psppire_sheet_draw_active_cell (sheet);
2653 psppire_sheet_show_entry_widget (sheet);
2655 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2657 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2658 row, col, old_row, old_col);
2663 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2665 GtkEntry *sheet_entry;
2666 PsppireSheetCellAttr attributes;
2670 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2672 row = sheet->active_cell.row;
2673 col = sheet->active_cell.col;
2675 /* Don't show the active cell, if there is no active cell: */
2676 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2679 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2680 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2681 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2683 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2685 sheet_entry = psppire_sheet_get_entry (sheet);
2687 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2689 if (GTK_IS_ENTRY (sheet_entry))
2691 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2692 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2695 text = g_strdup ("");
2697 if (strcmp (old_text, text) != 0)
2698 gtk_entry_set_text (sheet_entry, text);
2700 dispose_string (sheet, text);
2703 switch (attributes.justification)
2705 case GTK_JUSTIFY_RIGHT:
2706 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2708 case GTK_JUSTIFY_CENTER:
2709 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2711 case GTK_JUSTIFY_LEFT:
2713 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2719 psppire_sheet_size_allocate_entry (sheet);
2721 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2722 psppire_sheet_model_is_editable (sheet->model,
2724 gtk_widget_map (sheet->entry_widget);
2728 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2731 PsppireSheetRange range;
2733 row = sheet->active_cell.row;
2734 col = sheet->active_cell.col;
2736 if (row < 0 || col < 0) return FALSE;
2738 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2741 range.col0 = range.coli = col;
2742 range.row0 = range.rowi = row;
2744 psppire_sheet_draw_border (sheet, range);
2752 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
2756 rectangle_from_range (sheet, &new_range, &area);
2761 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
2763 area.x += sheet->cell_padding->left / 2;
2764 area.y += sheet->cell_padding->top / 2;
2765 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
2766 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
2768 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
2775 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
2780 psppire_sheet_real_select_range (PsppireSheet *sheet,
2781 const PsppireSheetRange *range)
2785 g_return_if_fail (sheet != NULL);
2787 if (range == NULL) range = &sheet->range;
2789 memcpy (&sheet->range, range, sizeof (*range));
2791 if (range->row0 < 0 || range->rowi < 0) return;
2792 if (range->col0 < 0 || range->coli < 0) return;
2794 state = sheet->select_status;
2796 psppire_sheet_update_primary_selection (sheet);
2798 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
2803 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
2805 g_return_if_fail (sheet != NULL);
2806 *range = sheet->range;
2811 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
2813 g_return_if_fail (sheet != NULL);
2815 if (range == NULL) range=&sheet->range;
2817 if (range->row0 < 0 || range->rowi < 0) return;
2818 if (range->col0 < 0 || range->coli < 0) return;
2821 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2822 psppire_sheet_real_unselect_range (sheet, NULL);
2824 sheet->range.row0 = range->row0;
2825 sheet->range.rowi = range->rowi;
2826 sheet->range.col0 = range->col0;
2827 sheet->range.coli = range->coli;
2829 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
2830 psppire_sheet_real_select_range (sheet, NULL);
2834 psppire_sheet_unselect_range (PsppireSheet *sheet)
2836 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2839 psppire_sheet_real_unselect_range (sheet, NULL);
2840 sheet->select_status = GTK_STATE_NORMAL;
2845 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
2846 const PsppireSheetRange *range)
2848 g_return_if_fail (sheet != NULL);
2849 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
2852 range = &sheet->range;
2854 if (range->row0 < 0 || range->rowi < 0) return;
2855 if (range->col0 < 0 || range->coli < 0) return;
2857 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
2858 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
2860 sheet->range.row0 = -1;
2861 sheet->range.rowi = -1;
2862 sheet->range.col0 = -1;
2863 sheet->range.coli = -1;
2868 psppire_sheet_expose (GtkWidget *widget, GdkEventExpose *event)
2870 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2872 g_return_val_if_fail (event != NULL, FALSE);
2874 if (!GTK_WIDGET_DRAWABLE (widget))
2877 /* exposure events on the sheet */
2878 if (event->window == sheet->row_title_window &&
2879 sheet->row_titles_visible)
2881 draw_row_title_buttons_range (sheet,
2882 min_visible_row (sheet),
2883 max_visible_row (sheet));
2886 if (event->window == sheet->column_title_window &&
2887 sheet->column_titles_visible)
2889 draw_column_title_buttons_range (sheet,
2890 min_visible_column (sheet),
2891 max_visible_column (sheet));
2894 if (event->window == sheet->sheet_window)
2896 draw_sheet_region (sheet, event->region);
2899 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2902 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2903 psppire_sheet_range_draw (sheet, &sheet->range);
2905 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2906 psppire_sheet_range_draw (sheet, &sheet->drag_range);
2909 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2913 rectangle_from_range (sheet, &sheet->range, &area);
2915 gdk_draw_rectangle (sheet->sheet_window,
2918 area.x + 1, area.y + 1,
2919 area.width, area.height);
2924 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2925 draw_xor_rectangle (sheet, sheet->drag_range);
2930 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
2933 PsppireSheetRange range;
2934 range.row0 = range.rowi = sheet->active_cell.row;
2935 range.col0 = range.coli = sheet->active_cell.col;
2937 rectangle_from_range (sheet, &range, &rect);
2939 if (GDK_OVERLAP_RECTANGLE_OUT !=
2940 gdk_region_rect_in (event->region, &rect))
2942 psppire_sheet_draw_active_cell (sheet);
2948 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2955 psppire_sheet_button_press (GtkWidget *widget, GdkEventButton *event)
2957 PsppireSheet *sheet;
2958 GdkModifierType mods;
2963 g_return_val_if_fail (widget != NULL, FALSE);
2964 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
2965 g_return_val_if_fail (event != NULL, FALSE);
2967 sheet = PSPPIRE_SHEET (widget);
2969 /* Cancel any pending tooltips */
2970 if (sheet->motion_timer)
2972 g_source_remove (sheet->motion_timer);
2973 sheet->motion_timer = 0;
2976 gtk_widget_get_pointer (widget, &x, &y);
2977 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2980 if (event->window == sheet->column_title_window)
2982 sheet->x_drag = event->x;
2983 g_signal_emit (sheet,
2984 sheet_signals[BUTTON_EVENT_COLUMN], 0,
2987 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2989 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2990 g_signal_emit (sheet,
2991 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
2994 else if (event->window == sheet->row_title_window)
2996 g_signal_emit (sheet,
2997 sheet_signals[BUTTON_EVENT_ROW], 0,
3000 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3002 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3003 g_signal_emit (sheet,
3004 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3008 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3010 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3013 /* press on resize windows */
3014 if (event->window == sheet->column_title_window)
3016 sheet->x_drag = event->x;
3018 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3020 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3021 gdk_pointer_grab (sheet->column_title_window, FALSE,
3022 GDK_POINTER_MOTION_HINT_MASK |
3023 GDK_BUTTON1_MOTION_MASK |
3024 GDK_BUTTON_RELEASE_MASK,
3025 NULL, NULL, event->time);
3027 draw_xor_vline (sheet);
3032 if (event->window == sheet->row_title_window)
3034 sheet->y_drag = event->y;
3036 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3038 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3039 gdk_pointer_grab (sheet->row_title_window, FALSE,
3040 GDK_POINTER_MOTION_HINT_MASK |
3041 GDK_BUTTON1_MOTION_MASK |
3042 GDK_BUTTON_RELEASE_MASK,
3043 NULL, NULL, event->time);
3045 draw_xor_hline (sheet);
3050 /* the sheet itself does not handle other than single click events */
3051 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3053 /* selections on the sheet */
3054 if (event->window == sheet->sheet_window)
3056 gtk_widget_get_pointer (widget, &x, &y);
3057 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3058 gdk_pointer_grab (sheet->sheet_window, FALSE,
3059 GDK_POINTER_MOTION_HINT_MASK |
3060 GDK_BUTTON1_MOTION_MASK |
3061 GDK_BUTTON_RELEASE_MASK,
3062 NULL, NULL, event->time);
3063 gtk_grab_add (GTK_WIDGET (sheet));
3065 if (psppire_sheet_click_cell (sheet, row, column))
3067 if ( sheet->select_status == PSPPIRE_SHEET_NORMAL)
3069 sheet->range.row0 = row;
3070 sheet->range.col0 = column;
3074 sheet->select_status = PSPPIRE_SHEET_NORMAL;
3079 if (event->window == sheet->column_title_window)
3081 gtk_widget_get_pointer (widget, &x, &y);
3082 if ( sheet->row_titles_visible)
3083 x -= sheet->row_title_area.width;
3085 x += sheet->hadjustment->value;
3087 column = column_from_xpixel (sheet, x);
3089 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3091 gtk_grab_add (GTK_WIDGET (sheet));
3092 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3096 if (event->window == sheet->row_title_window)
3098 gtk_widget_get_pointer (widget, &x, &y);
3099 if ( sheet->column_titles_visible)
3100 y -= sheet->column_title_area.height;
3102 y += sheet->vadjustment->value;
3104 row = row_from_ypixel (sheet, y);
3105 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3107 gtk_grab_add (GTK_WIDGET (sheet));
3108 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3116 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3118 PsppireSheetCell cell;
3119 gboolean forbid_move;
3124 if (row >= psppire_axis_unit_count (sheet->vaxis)
3125 || column >= psppire_axis_unit_count (sheet->haxis))
3130 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3131 &sheet->active_cell,
3137 if (sheet->select_status == GTK_STATE_NORMAL)
3140 row = sheet->active_cell.row;
3141 column = sheet->active_cell.col;
3143 change_active_cell (sheet, row, column);
3147 if (row == -1 && column >= 0)
3149 psppire_sheet_select_column (sheet, column);
3153 if (column == -1 && row >= 0)
3155 psppire_sheet_select_row (sheet, row);
3159 if (row == -1 && column == -1)
3161 sheet->range.row0 = 0;
3162 sheet->range.col0 = 0;
3163 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3165 psppire_axis_unit_count (sheet->haxis) - 1;
3166 psppire_sheet_select_range (sheet, NULL);
3170 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3171 change_active_cell (sheet, row, column);
3173 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3179 psppire_sheet_button_release (GtkWidget *widget,
3180 GdkEventButton *event)
3182 GdkDisplay *display = gtk_widget_get_display (widget);
3184 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3186 /* release on resize windows */
3187 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3190 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3191 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3193 gdk_display_pointer_ungrab (display, event->time);
3194 draw_xor_vline (sheet);
3197 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3198 + sheet->hadjustment->value;
3200 set_column_width (sheet, sheet->drag_cell.col, width);
3205 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3208 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3209 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3211 gdk_display_pointer_ungrab (display, event->time);
3212 draw_xor_hline (sheet);
3215 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3216 sheet->vadjustment->value;
3218 set_row_height (sheet, sheet->drag_cell.row, height);
3223 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3225 PsppireSheetRange old_range;
3226 draw_xor_rectangle (sheet, sheet->drag_range);
3227 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3228 gdk_display_pointer_ungrab (display, event->time);
3230 psppire_sheet_real_unselect_range (sheet, NULL);
3232 old_range = sheet->range;
3233 sheet->range = sheet->drag_range;
3234 sheet->drag_range = old_range;
3235 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3236 &sheet->drag_range, &sheet->range);
3237 psppire_sheet_select_range (sheet, &sheet->range);
3240 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3242 PsppireSheetRange old_range;
3243 draw_xor_rectangle (sheet, sheet->drag_range);
3244 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3245 gdk_display_pointer_ungrab (display, event->time);
3247 psppire_sheet_real_unselect_range (sheet, NULL);
3249 old_range = sheet->range;
3250 sheet->range = sheet->drag_range;
3251 sheet->drag_range = old_range;
3253 if (sheet->select_status == GTK_STATE_NORMAL)
3254 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3256 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3257 &sheet->drag_range, &sheet->range);
3258 psppire_sheet_select_range (sheet, &sheet->range);
3261 if (PSPPIRE_SHEET_IN_SELECTION (sheet))
3263 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3264 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3266 change_active_cell (sheet, sheet->active_cell.row,
3267 sheet->active_cell.col);
3270 gdk_display_pointer_ungrab (display, event->time);
3271 gtk_grab_remove (GTK_WIDGET (sheet));
3273 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3282 /* Shamelessly lifted from gtktooltips */
3284 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3288 gtk_widget_size_request (tip_window, &req);
3289 gtk_paint_flat_box (tip_window->style, tip_window->window,
3290 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3291 NULL, GTK_WIDGET(tip_window), "tooltip",
3292 0, 0, req.width, req.height);
3298 destroy_hover_window (PsppireSheetHoverTitle *h)
3300 gtk_widget_destroy (h->window);
3304 static PsppireSheetHoverTitle *
3305 create_hover_window (void)
3307 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3309 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3311 #if GTK_CHECK_VERSION (2, 9, 0)
3312 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3313 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3316 gtk_widget_set_app_paintable (hw->window, TRUE);
3317 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3318 gtk_widget_set_name (hw->window, "gtk-tooltips");
3319 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3321 g_signal_connect (hw->window,
3323 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3326 hw->label = gtk_label_new (NULL);
3329 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3330 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3332 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3334 gtk_widget_show (hw->label);
3336 g_signal_connect (hw->window,
3338 G_CALLBACK (gtk_widget_destroyed),
3344 #define HOVER_WINDOW_Y_OFFSET 2
3347 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3348 const gchar *subtitle)
3357 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3361 sheet->hover_window->row = row;
3362 sheet->hover_window->column = column;
3364 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3366 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3368 gtk_widget_show (sheet->hover_window->window);
3370 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3376 y += sheet->column_title_area.y;
3377 y += sheet->column_title_area.height;
3378 y += HOVER_WINDOW_Y_OFFSET;
3384 x += sheet->row_title_area.x;
3385 x += sheet->row_title_area.width * 2 / 3.0;
3388 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3393 motion_timeout_callback (gpointer data)
3395 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3399 gdk_threads_enter ();
3400 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3402 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3404 if (sheet->row_title_under && row >= 0)
3406 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3408 show_subtitle (sheet, row, -1, text);
3412 if (sheet->column_title_under && column >= 0)
3414 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3417 show_subtitle (sheet, -1, column, text);
3423 gdk_threads_leave ();
3428 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3430 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3431 GdkModifierType mods;
3432 GdkCursorType new_cursor;
3435 GdkDisplay *display;
3437 g_return_val_if_fail (event != NULL, FALSE);
3439 display = gtk_widget_get_display (widget);
3441 /* selections on the sheet */
3445 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3447 if ( sheet->motion_timer > 0 )
3448 g_source_remove (sheet->motion_timer);
3449 sheet->motion_timer =
3450 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3456 gtk_widget_get_pointer (widget, &wx, &wy);
3458 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3460 if ( row != sheet->hover_window->row ||
3461 column != sheet->hover_window->column)
3463 gtk_widget_hide (sheet->hover_window->window);
3468 if (event->window == sheet->column_title_window)
3470 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3471 on_column_boundary (sheet, x, &column))
3473 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3474 if (new_cursor != sheet->cursor_drag->type)
3476 gdk_cursor_unref (sheet->cursor_drag);
3477 sheet->cursor_drag =
3478 gdk_cursor_new_for_display (display, new_cursor);
3480 gdk_window_set_cursor (sheet->column_title_window,
3481 sheet->cursor_drag);
3486 new_cursor = GDK_TOP_LEFT_ARROW;
3487 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3488 new_cursor != sheet->cursor_drag->type)
3490 gdk_cursor_unref (sheet->cursor_drag);
3491 sheet->cursor_drag =
3492 gdk_cursor_new_for_display (display, new_cursor);
3493 gdk_window_set_cursor (sheet->column_title_window,
3494 sheet->cursor_drag);
3498 else if (event->window == sheet->row_title_window)
3500 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3501 on_row_boundary (sheet, y, &row))
3503 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3504 if (new_cursor != sheet->cursor_drag->type)
3506 gdk_cursor_unref (sheet->cursor_drag);
3507 sheet->cursor_drag =
3508 gdk_cursor_new_for_display (display, new_cursor);
3509 gdk_window_set_cursor (sheet->row_title_window,
3510 sheet->cursor_drag);
3515 new_cursor = GDK_TOP_LEFT_ARROW;
3516 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3517 new_cursor != sheet->cursor_drag->type)
3519 gdk_cursor_unref (sheet->cursor_drag);
3520 sheet->cursor_drag =
3521 gdk_cursor_new_for_display (display, new_cursor);
3522 gdk_window_set_cursor (sheet->row_title_window,
3523 sheet->cursor_drag);
3528 new_cursor = GDK_PLUS;
3529 if ( event->window == sheet->sheet_window &&
3530 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3531 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3532 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3533 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3534 new_cursor != sheet->cursor_drag->type)
3536 gdk_cursor_unref (sheet->cursor_drag);
3537 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3538 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3541 new_cursor = GDK_TOP_LEFT_ARROW;
3542 if ( event->window == sheet->sheet_window &&
3543 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3544 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3545 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3546 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3547 new_cursor != sheet->cursor_drag->type)
3549 gdk_cursor_unref (sheet->cursor_drag);
3550 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3551 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3554 new_cursor = GDK_SIZING;
3555 if ( event->window == sheet->sheet_window &&
3556 sheet->selection_mode != GTK_SELECTION_NONE &&
3557 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3558 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3559 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3560 new_cursor != sheet->cursor_drag->type)
3562 gdk_cursor_unref (sheet->cursor_drag);
3563 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3564 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3568 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3569 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3571 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3573 if (event->x != sheet->x_drag)
3575 draw_xor_vline (sheet);
3576 sheet->x_drag = event->x;
3577 draw_xor_vline (sheet);
3583 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3585 if (event->y != sheet->y_drag)
3587 draw_xor_hline (sheet);
3588 sheet->y_drag = event->y;
3589 draw_xor_hline (sheet);
3595 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3597 PsppireSheetRange aux;
3598 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3599 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3600 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3601 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3605 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3606 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3608 aux = sheet->drag_range;
3609 sheet->drag_range.row0 = sheet->range.row0 + row;
3610 sheet->drag_range.col0 = sheet->range.col0 + column;
3611 sheet->drag_range.rowi = sheet->range.rowi + row;
3612 sheet->drag_range.coli = sheet->range.coli + column;
3613 if (aux.row0 != sheet->drag_range.row0 ||
3614 aux.col0 != sheet->drag_range.col0)
3616 draw_xor_rectangle (sheet, aux);
3617 draw_xor_rectangle (sheet, sheet->drag_range);
3623 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3625 PsppireSheetRange aux;
3626 gint v_h, current_col, current_row, col_threshold, row_threshold;
3628 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
3629 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
3631 current_col = column_from_xpixel (sheet, x);
3632 current_row = row_from_ypixel (sheet, y);
3633 column = current_col - sheet->drag_cell.col;
3634 row = current_row - sheet->drag_cell.row;
3636 /*use half of column width resp. row height as threshold to
3638 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
3639 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
3642 if (x < col_threshold)
3645 else if (column < 0)
3647 if (x > col_threshold)
3650 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
3651 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
3654 if (y < row_threshold)
3659 if (y > row_threshold)
3663 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3664 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3674 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3675 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3677 aux = sheet->drag_range;
3678 sheet->drag_range = sheet->range;
3680 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
3681 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
3682 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
3683 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
3685 if (aux.row0 != sheet->drag_range.row0 ||
3686 aux.rowi != sheet->drag_range.rowi ||
3687 aux.col0 != sheet->drag_range.col0 ||
3688 aux.coli != sheet->drag_range.coli)
3690 draw_xor_rectangle (sheet, aux);
3691 draw_xor_rectangle (sheet, sheet->drag_range);
3697 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3699 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
3700 column == sheet->active_cell.col) return TRUE;
3702 if ( mods & GDK_BUTTON1_MASK)
3704 if (PSPPIRE_SHEET_IN_SELECTION (sheet) )
3707 sheet->range.rowi = row;
3708 sheet->range.coli = column;
3709 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3711 rectangle_from_range (sheet, &sheet->range, &area);
3712 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
3716 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3724 psppire_sheet_crossing_notify (GtkWidget *widget,
3725 GdkEventCrossing *event)
3727 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3729 if (event->window == sheet->column_title_window)
3730 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
3731 else if (event->window == sheet->row_title_window)
3732 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
3734 if (event->type == GDK_LEAVE_NOTIFY)
3735 gtk_widget_hide (sheet->hover_window->window);
3742 psppire_sheet_focus_in (GtkWidget *w,
3743 GdkEventFocus *event)
3745 PsppireSheet *sheet = PSPPIRE_SHEET (w);
3747 gtk_widget_grab_focus (sheet->entry_widget);
3755 psppire_sheet_entry_key_press (GtkWidget *widget,
3759 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
3764 /* Number of rows in a step-increment */
3765 #define ROWS_PER_STEP 1
3769 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
3771 gint old_row = sheet->active_cell.row ;
3772 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
3776 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
3777 min_visible_row (sheet));
3781 case GTK_SCROLL_PAGE_DOWN:
3782 gtk_adjustment_set_value (sheet->vadjustment,
3783 sheet->vadjustment->value +
3784 sheet->vadjustment->page_increment);
3786 case GTK_SCROLL_PAGE_UP:
3787 gtk_adjustment_set_value (sheet->vadjustment,
3788 sheet->vadjustment->value -
3789 sheet->vadjustment->page_increment);
3793 g_assert_not_reached ();
3798 vpixel += psppire_axis_start_pixel (sheet->vaxis,
3799 min_visible_row (sheet));
3801 new_row = row_from_ypixel (sheet, vpixel);
3803 change_active_cell (sheet, new_row,
3804 sheet->active_cell.col);
3809 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
3811 gint current_row = sheet->active_cell.row;
3812 gint current_col = sheet->active_cell.col;
3813 PsppireSheetCell new_cell ;
3814 gboolean forbidden = FALSE;
3816 new_cell.row = current_row;
3817 new_cell.col = current_col;
3821 case GTK_SCROLL_STEP_DOWN:
3824 case GTK_SCROLL_STEP_UP:
3827 case GTK_SCROLL_STEP_RIGHT:
3830 case GTK_SCROLL_STEP_LEFT:
3833 case GTK_SCROLL_STEP_FORWARD:
3836 psppire_sheet_model_get_column_count (sheet->model))
3842 case GTK_SCROLL_STEP_BACKWARD:
3844 if (new_cell.col < 0)
3847 psppire_sheet_model_get_column_count (sheet->model) - 1;
3852 g_assert_not_reached ();
3856 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3857 &sheet->active_cell,
3865 maximize_int (&new_cell.row, 0);
3866 maximize_int (&new_cell.col, 0);
3868 minimize_int (&new_cell.row,
3869 psppire_axis_unit_count (sheet->vaxis) - 1);
3871 minimize_int (&new_cell.col,
3872 psppire_axis_unit_count (sheet->haxis) - 1);
3874 change_active_cell (sheet, new_cell.row, new_cell.col);
3877 if ( new_cell.col > max_fully_visible_column (sheet))
3880 psppire_axis_start_pixel (sheet->haxis,
3882 hpos -= sheet->hadjustment->page_size;
3884 gtk_adjustment_set_value (sheet->hadjustment,
3887 else if ( new_cell.col < min_fully_visible_column (sheet))
3890 psppire_axis_start_pixel (sheet->haxis,
3893 gtk_adjustment_set_value (sheet->hadjustment,
3898 if ( new_cell.row > max_fully_visible_row (sheet))
3901 psppire_axis_start_pixel (sheet->vaxis,
3903 vpos -= sheet->vadjustment->page_size;
3905 gtk_adjustment_set_value (sheet->vadjustment,
3908 else if ( new_cell.row < min_fully_visible_row (sheet))
3911 psppire_axis_start_pixel (sheet->vaxis,
3914 gtk_adjustment_set_value (sheet->vadjustment,
3918 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3923 psppire_sheet_key_press (GtkWidget *widget,
3926 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3928 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3930 switch (key->keyval)
3933 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
3936 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
3938 case GDK_ISO_Left_Tab:
3939 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
3942 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
3946 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3949 step_sheet (sheet, GTK_SCROLL_STEP_UP);
3953 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
3956 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
3960 gtk_adjustment_set_value (sheet->vadjustment,
3961 sheet->vadjustment->lower);
3963 change_active_cell (sheet, 0,
3964 sheet->active_cell.col);
3969 gtk_adjustment_set_value (sheet->vadjustment,
3970 sheet->vadjustment->upper -
3971 sheet->vadjustment->page_size -
3972 sheet->vadjustment->page_increment);
3975 change_active_cellx (sheet,
3976 psppire_axis_unit_count (sheet->vaxis) - 1,
3977 sheet->active_cell.col);
3981 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
3992 psppire_sheet_size_request (GtkWidget *widget,
3993 GtkRequisition *requisition)
3995 PsppireSheet *sheet;
3997 g_return_if_fail (widget != NULL);
3998 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3999 g_return_if_fail (requisition != NULL);
4001 sheet = PSPPIRE_SHEET (widget);
4003 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4004 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4006 /* compute the size of the column title area */
4007 if (sheet->column_titles_visible)
4008 requisition->height += sheet->column_title_area.height;
4010 /* compute the size of the row title area */
4011 if (sheet->row_titles_visible)
4012 requisition->width += sheet->row_title_area.width;
4017 psppire_sheet_size_allocate (GtkWidget *widget,
4018 GtkAllocation *allocation)
4020 PsppireSheet *sheet;
4021 GtkAllocation sheet_allocation;
4024 g_return_if_fail (widget != NULL);
4025 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4026 g_return_if_fail (allocation != NULL);
4028 sheet = PSPPIRE_SHEET (widget);
4029 widget->allocation = *allocation;
4030 border_width = GTK_CONTAINER (widget)->border_width;
4032 if (GTK_WIDGET_REALIZED (widget))
4033 gdk_window_move_resize (widget->window,
4034 allocation->x + border_width,
4035 allocation->y + border_width,
4036 allocation->width - 2 * border_width,
4037 allocation->height - 2 * border_width);
4039 sheet_allocation.x = 0;
4040 sheet_allocation.y = 0;
4041 sheet_allocation.width = allocation->width - 2 * border_width;
4042 sheet_allocation.height = allocation->height - 2 * border_width;
4044 if (GTK_WIDGET_REALIZED (widget))
4045 gdk_window_move_resize (sheet->sheet_window,
4048 sheet_allocation.width,
4049 sheet_allocation.height);
4051 /* position the window which holds the column title buttons */
4052 sheet->column_title_area.x = 0;
4053 sheet->column_title_area.y = 0;
4054 sheet->column_title_area.width = sheet_allocation.width ;
4057 /* position the window which holds the row title buttons */
4058 sheet->row_title_area.x = 0;
4059 sheet->row_title_area.y = 0;
4060 sheet->row_title_area.height = sheet_allocation.height;
4062 if (sheet->row_titles_visible)
4063 sheet->column_title_area.x += sheet->row_title_area.width;
4065 if (sheet->column_titles_visible)
4066 sheet->row_title_area.y += sheet->column_title_area.height;
4068 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4069 gdk_window_move_resize (sheet->column_title_window,
4070 sheet->column_title_area.x,
4071 sheet->column_title_area.y,
4072 sheet->column_title_area.width,
4073 sheet->column_title_area.height);
4076 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4077 gdk_window_move_resize (sheet->row_title_window,
4078 sheet->row_title_area.x,
4079 sheet->row_title_area.y,
4080 sheet->row_title_area.width,
4081 sheet->row_title_area.height);
4083 size_allocate_global_button (sheet);
4087 gint width = sheet->column_title_area.width;
4089 if ( sheet->row_titles_visible)
4090 width -= sheet->row_title_area.width;
4092 g_object_set (sheet->haxis,
4093 "minimum-extent", width,
4100 gint height = sheet->row_title_area.height;
4102 if ( sheet->column_titles_visible)
4103 height -= sheet->column_title_area.height;
4105 g_object_set (sheet->vaxis,
4106 "minimum-extent", height,
4111 /* set the scrollbars adjustments */
4112 adjust_scrollbars (sheet);
4116 draw_column_title_buttons (PsppireSheet *sheet)
4120 if (!sheet->column_titles_visible) return;
4121 if (!GTK_WIDGET_REALIZED (sheet))
4124 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4127 if (sheet->row_titles_visible)
4129 x = sheet->row_title_area.width;
4132 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4134 sheet->column_title_area.width = width;
4135 sheet->column_title_area.x = x;
4136 gdk_window_move_resize (sheet->column_title_window,
4137 sheet->column_title_area.x,
4138 sheet->column_title_area.y,
4139 sheet->column_title_area.width,
4140 sheet->column_title_area.height);
4143 if (max_visible_column (sheet) ==
4144 psppire_axis_unit_count (sheet->haxis) - 1)
4145 gdk_window_clear_area (sheet->column_title_window,
4147 sheet->column_title_area.width,
4148 sheet->column_title_area.height);
4150 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4152 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4153 max_visible_column (sheet));
4157 draw_row_title_buttons (PsppireSheet *sheet)
4162 if (!sheet->row_titles_visible) return;
4163 if (!GTK_WIDGET_REALIZED (sheet))
4166 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4168 if (sheet->column_titles_visible)
4170 y = sheet->column_title_area.height;
4173 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4175 sheet->row_title_area.y = y;
4176 sheet->row_title_area.height = height;
4177 gdk_window_move_resize (sheet->row_title_window,
4178 sheet->row_title_area.x,
4179 sheet->row_title_area.y,
4180 sheet->row_title_area.width,
4181 sheet->row_title_area.height);
4184 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4185 gdk_window_clear_area (sheet->row_title_window,
4187 sheet->row_title_area.width,
4188 sheet->row_title_area.height);
4190 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4192 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4193 max_visible_row (sheet));
4198 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4200 GtkAllocation entry_alloc;
4201 PsppireSheetCellAttr attributes = { 0 };
4202 GtkEntry *sheet_entry;
4204 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4205 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4207 sheet_entry = psppire_sheet_get_entry (sheet);
4209 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4210 sheet->active_cell.col,
4214 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4216 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4218 style->bg[GTK_STATE_NORMAL] = attributes.background;
4219 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4220 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4221 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4222 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4223 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4226 rectangle_from_cell (sheet, sheet->active_cell.row,
4227 sheet->active_cell.col, &entry_alloc);
4229 entry_alloc.x += sheet->cell_padding->left;
4230 entry_alloc.y += sheet->cell_padding->right;
4231 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4232 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4235 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4236 entry_alloc.height);
4237 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4241 /* Copy the sheet's font to the entry widget */
4243 set_entry_widget_font (PsppireSheet *sheet)
4245 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4247 pango_font_description_free (style->font_desc);
4248 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4250 gtk_widget_modify_style (sheet->entry_widget, style);
4254 create_sheet_entry (PsppireSheet *sheet)
4256 if (sheet->entry_widget)
4258 gtk_widget_unparent (sheet->entry_widget);
4261 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4262 g_object_ref_sink (sheet->entry_widget);
4264 gtk_widget_size_request (sheet->entry_widget, NULL);
4266 if ( GTK_IS_ENTRY (sheet->entry_widget))
4268 g_object_set (sheet->entry_widget,
4273 if (GTK_WIDGET_REALIZED (sheet))
4275 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4276 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4277 gtk_widget_realize (sheet->entry_widget);
4280 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4281 G_CALLBACK (psppire_sheet_entry_key_press),
4284 set_entry_widget_font (sheet);
4286 gtk_widget_show (sheet->entry_widget);
4290 /* Finds the last child widget that happens to be of type GtkEntry */
4292 find_entry (GtkWidget *w, gpointer user_data)
4294 GtkWidget **entry = user_data;
4295 if ( GTK_IS_ENTRY (w))
4303 psppire_sheet_get_entry (PsppireSheet *sheet)
4305 GtkWidget *w = sheet->entry_widget;
4307 g_return_val_if_fail (sheet != NULL, NULL);
4308 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4309 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4311 while (! GTK_IS_ENTRY (w))
4313 GtkWidget *entry = NULL;
4315 if (GTK_IS_CONTAINER (w))
4317 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4326 return GTK_ENTRY (w);
4331 draw_button (PsppireSheet *sheet, GdkWindow *window,
4332 PsppireSheetButton *button, gboolean is_sensitive,
4333 GdkRectangle allocation)
4335 GtkShadowType shadow_type;
4336 gint text_width = 0, text_height = 0;
4337 PangoAlignment align = PANGO_ALIGN_LEFT;
4343 g_return_if_fail (sheet != NULL);
4344 g_return_if_fail (button != NULL);
4347 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4349 gdk_window_clear_area (window,
4350 allocation.x, allocation.y,
4351 allocation.width, allocation.height);
4353 gtk_widget_ensure_style (sheet->button);
4355 gtk_paint_box (sheet->button->style, window,
4356 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4358 GTK_WIDGET (sheet->button),
4360 allocation.x, allocation.y,
4361 allocation.width, allocation.height);
4363 state = button->state;
4364 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4366 if (state == GTK_STATE_ACTIVE)
4367 shadow_type = GTK_SHADOW_IN;
4369 shadow_type = GTK_SHADOW_OUT;
4371 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4372 gtk_paint_box (sheet->button->style, window,
4373 button->state, shadow_type,
4374 &allocation, GTK_WIDGET (sheet->button),
4376 allocation.x, allocation.y,
4377 allocation.width, allocation.height);
4379 if ( button->overstruck)
4381 GdkPoint points[2] = {
4382 {allocation.x, allocation.y},
4383 {allocation.x + allocation.width,
4384 allocation.y + allocation.height}
4387 gtk_paint_polygon (sheet->button->style,
4399 if (button->label_visible)
4401 text_height = DEFAULT_ROW_HEIGHT -
4402 2 * COLUMN_TITLES_HEIGHT;
4404 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4406 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4409 allocation.y += 2 * sheet->button->style->ythickness;
4411 if (button->label && strlen (button->label) > 0)
4413 PangoRectangle rect;
4414 gchar *line = button->label;
4416 PangoLayout *layout = NULL;
4417 gint real_x = allocation.x;
4418 gint real_y = allocation.y;
4420 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4421 pango_layout_get_extents (layout, NULL, &rect);
4423 text_width = PANGO_PIXELS (rect.width);
4424 switch (button->justification)
4426 case GTK_JUSTIFY_LEFT:
4427 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4428 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4430 case GTK_JUSTIFY_RIGHT:
4431 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4432 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4434 case GTK_JUSTIFY_CENTER:
4436 real_x = allocation.x + (allocation.width - text_width)/2;
4437 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4438 pango_layout_set_justify (layout, TRUE);
4440 pango_layout_set_alignment (layout, align);
4441 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4450 g_object_unref (layout);
4453 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4455 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4459 psppire_sheet_button_free (button);
4463 /* Draw the column title buttons FIRST through to LAST */
4465 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4469 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4471 if (!sheet->column_titles_visible) return;
4473 g_return_if_fail (first >= min_visible_column (sheet));
4474 g_return_if_fail (last <= max_visible_column (sheet));
4477 rect.height = sheet->column_title_area.height;
4478 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4479 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4480 + psppire_axis_unit_size (sheet->haxis, last);
4482 rect.x -= sheet->hadjustment->value;
4484 minimize_int (&rect.width, sheet->column_title_area.width);
4485 maximize_int (&rect.x, 0);
4487 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4489 for (col = first ; col <= last ; ++col)
4491 GdkRectangle allocation;
4492 gboolean is_sensitive = FALSE;
4494 PsppireSheetButton *
4495 button = psppire_sheet_model_get_column_button (sheet->model, col);
4497 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4499 allocation.x -= sheet->hadjustment->value;
4501 allocation.height = sheet->column_title_area.height;
4502 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4503 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4505 draw_button (sheet, sheet->column_title_window,
4506 button, is_sensitive, allocation);
4509 gdk_window_end_paint (sheet->column_title_window);
4514 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4518 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4520 if (!sheet->row_titles_visible) return;
4522 g_return_if_fail (first >= min_visible_row (sheet));
4523 g_return_if_fail (last <= max_visible_row (sheet));
4526 rect.width = sheet->row_title_area.width;
4527 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4528 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4529 + psppire_axis_unit_size (sheet->vaxis, last);
4531 rect.y -= sheet->vadjustment->value;
4533 minimize_int (&rect.height, sheet->row_title_area.height);
4534 maximize_int (&rect.y, 0);
4536 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4537 for (row = first; row <= last; ++row)
4539 GdkRectangle allocation;
4541 gboolean is_sensitive = FALSE;
4543 PsppireSheetButton *button =
4544 psppire_sheet_model_get_row_button (sheet->model, row);
4546 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4548 allocation.y -= sheet->vadjustment->value;
4550 allocation.width = sheet->row_title_area.width;
4551 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4552 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4554 draw_button (sheet, sheet->row_title_window,
4555 button, is_sensitive, allocation);
4558 gdk_window_end_paint (sheet->row_title_window);
4565 * vadjustment_value_changed
4566 * hadjustment_value_changed */
4570 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4573 (adj->value + adj->page_size)
4575 (adj->upper - adj->lower);
4577 const glong last_item = psppire_axis_unit_count (axis) - 1;
4579 if (isnan (position) || position < 0)
4583 psppire_axis_start_pixel (axis, last_item)
4585 psppire_axis_unit_size (axis, last_item)
4589 adj->page_size = page_size;
4592 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4594 if ( adj->value < adj->lower)
4595 adj->value = adj->lower;
4598 gtk_adjustment_changed (adj);
4603 adjust_scrollbars (PsppireSheet *sheet)
4607 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4610 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4612 if ( sheet->row_titles_visible)
4613 width -= sheet->row_title_area.width;
4615 if (sheet->column_titles_visible)
4616 height -= sheet->column_title_area.height;
4618 if (sheet->vadjustment)
4620 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4622 sheet->vadjustment->step_increment =
4624 psppire_axis_unit_size (sheet->vaxis, last_row);
4626 sheet->vadjustment->page_increment =
4628 sheet->column_title_area.height -
4629 psppire_axis_unit_size (sheet->vaxis, last_row);
4631 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4634 if (sheet->hadjustment)
4636 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4637 sheet->hadjustment->step_increment = 1;
4639 sheet->hadjustment->page_increment = width;
4641 sheet->hadjustment->upper =
4642 psppire_axis_start_pixel (sheet->haxis, last_col)
4644 psppire_axis_unit_size (sheet->haxis, last_col)
4647 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4651 /* Subtracts the region of WIDGET from REGION */
4653 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4656 GdkRectangle intersect;
4659 gdk_region_get_clipbox (region, &rect);
4660 gtk_widget_intersect (widget,
4664 region2 = gdk_region_rectangle (&intersect);
4665 gdk_region_subtract (region, region2);
4666 gdk_region_destroy (region2);
4670 vadjustment_value_changed (GtkAdjustment *adjustment,
4674 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4676 g_return_if_fail (adjustment != NULL);
4678 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4680 gtk_widget_hide (sheet->entry_widget);
4683 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4685 subtract_widget_region (region, sheet->button);
4686 gdk_window_begin_paint_region (sheet->sheet_window, region);
4688 draw_sheet_region (sheet, region);
4690 draw_row_title_buttons (sheet);
4691 psppire_sheet_draw_active_cell (sheet);
4693 gdk_window_end_paint (sheet->sheet_window);
4694 gdk_region_destroy (region);
4699 hadjustment_value_changed (GtkAdjustment *adjustment,
4703 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4705 g_return_if_fail (adjustment != NULL);
4707 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4709 gtk_widget_hide (sheet->entry_widget);
4713 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4715 subtract_widget_region (region, sheet->button);
4716 gdk_window_begin_paint_region (sheet->sheet_window, region);
4718 draw_sheet_region (sheet, region);
4720 draw_column_title_buttons (sheet);
4722 psppire_sheet_draw_active_cell (sheet);
4724 gdk_window_end_paint (sheet->sheet_window);
4726 gdk_region_destroy (region);
4730 /* COLUMN RESIZING */
4732 draw_xor_vline (PsppireSheet *sheet)
4735 gint xpos = sheet->x_drag;
4736 gdk_drawable_get_size (sheet->sheet_window,
4739 if (sheet->row_titles_visible)
4740 xpos += sheet->row_title_area.width;
4742 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4744 sheet->column_title_area.height,
4746 height + CELL_SPACING);
4751 draw_xor_hline (PsppireSheet *sheet)
4755 gint ypos = sheet->y_drag;
4757 gdk_drawable_get_size (sheet->sheet_window,
4761 if (sheet->column_titles_visible)
4762 ypos += sheet->column_title_area.height;
4764 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4765 sheet->row_title_area.width,
4767 width + CELL_SPACING,
4771 /* SELECTED RANGE */
4773 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
4776 GdkRectangle clip_area, area;
4779 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
4780 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
4781 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
4782 psppire_axis_unit_size (sheet->haxis, range.coli);
4783 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
4784 psppire_axis_unit_size (sheet->vaxis, range.rowi);
4786 clip_area.x = sheet->row_title_area.width;
4787 clip_area.y = sheet->column_title_area.height;
4789 gdk_drawable_get_size (sheet->sheet_window,
4790 &clip_area.width, &clip_area.height);
4792 if (!sheet->row_titles_visible) clip_area.x = 0;
4793 if (!sheet->column_titles_visible) clip_area.y = 0;
4797 area.width = area.width + area.x;
4800 if (area.width > clip_area.width) area.width = clip_area.width + 10;
4803 area.height = area.height + area.y;
4806 if (area.height > clip_area.height) area.height = clip_area.height + 10;
4810 clip_area.width += 3;
4811 clip_area.height += 3;
4813 gdk_gc_get_values (sheet->xor_gc, &values);
4815 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
4817 gdk_draw_rectangle (sheet->sheet_window,
4820 area.x + i, area.y + i,
4821 area.width - 2 * i, area.height - 2 * i);
4824 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4826 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
4831 set_column_width (PsppireSheet *sheet,
4835 g_return_if_fail (sheet != NULL);
4836 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4838 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
4844 psppire_axis_resize (sheet->haxis, column,
4845 width - sheet->cell_padding->left -
4846 sheet->cell_padding->right);
4848 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4850 draw_column_title_buttons (sheet);
4851 adjust_scrollbars (sheet);
4852 psppire_sheet_size_allocate_entry (sheet);
4853 redraw_range (sheet, NULL);
4858 set_row_height (PsppireSheet *sheet,
4862 g_return_if_fail (sheet != NULL);
4863 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4865 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
4871 psppire_axis_resize (sheet->vaxis, row,
4872 height - sheet->cell_padding->top -
4873 sheet->cell_padding->bottom);
4875 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
4877 draw_row_title_buttons (sheet);
4878 adjust_scrollbars (sheet);
4879 psppire_sheet_size_allocate_entry (sheet);
4880 redraw_range (sheet, NULL);
4885 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
4886 PsppireSheetCellAttr *attr)
4889 const GtkJustification *j ;
4890 GdkColormap *colormap;
4892 g_return_val_if_fail (sheet != NULL, FALSE);
4893 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
4895 if (row < 0 || col < 0) return FALSE;
4897 attr->foreground = GTK_WIDGET (sheet)->style->black;
4898 attr->background = sheet->color[BG_COLOR];
4900 attr->border.width = 0;
4901 attr->border.line_style = GDK_LINE_SOLID;
4902 attr->border.cap_style = GDK_CAP_NOT_LAST;
4903 attr->border.join_style = GDK_JOIN_MITER;
4904 attr->border.mask = 0;
4905 attr->border.color = GTK_WIDGET (sheet)->style->black;
4907 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
4908 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
4911 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
4912 attr->foreground = *fg;
4915 bg = psppire_sheet_model_get_background (sheet->model, row, col);
4918 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
4919 attr->background = *bg;
4922 attr->justification =
4923 psppire_sheet_model_get_column_justification (sheet->model, col);
4925 j = psppire_sheet_model_get_justification (sheet->model, row, col);
4927 attr->justification = *j;
4933 psppire_sheet_button_size_request (PsppireSheet *sheet,
4934 const PsppireSheetButton *button,
4935 GtkRequisition *button_requisition)
4937 GtkRequisition requisition;
4938 GtkRequisition label_requisition;
4940 label_requisition.height = DEFAULT_ROW_HEIGHT;
4941 label_requisition.width = COLUMN_MIN_WIDTH;
4943 requisition.height = DEFAULT_ROW_HEIGHT;
4944 requisition.width = COLUMN_MIN_WIDTH;
4947 *button_requisition = requisition;
4948 button_requisition->width = MAX (requisition.width, label_requisition.width);
4949 button_requisition->height = MAX (requisition.height, label_requisition.height);
4954 psppire_sheet_forall (GtkContainer *container,
4955 gboolean include_internals,
4956 GtkCallback callback,
4957 gpointer callback_data)
4959 PsppireSheet *sheet = PSPPIRE_SHEET (container);
4961 g_return_if_fail (callback != NULL);
4963 if (sheet->button && sheet->button->parent)
4964 (* callback) (sheet->button, callback_data);
4966 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
4967 (* callback) (sheet->entry_widget, callback_data);
4972 psppire_sheet_get_model (const PsppireSheet *sheet)
4974 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4976 return sheet->model;
4980 PsppireSheetButton *
4981 psppire_sheet_button_new (void)
4983 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
4985 button->state = GTK_STATE_NORMAL;
4986 button->label = NULL;
4987 button->label_visible = TRUE;
4988 button->justification = GTK_JUSTIFY_FILL;
4989 button->overstruck = FALSE;
4996 psppire_sheet_button_free (PsppireSheetButton *button)
4998 if (!button) return ;
5000 g_free (button->label);
5005 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5007 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5009 if ( NULL == celltext)
5012 g_string_append (string, celltext);
5018 range_to_text (const PsppireSheet *sheet)
5023 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5026 string = g_string_sized_new (80);
5028 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5030 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5032 append_cell_text (string, sheet, r, c);
5033 g_string_append (string, "\t");
5035 append_cell_text (string, sheet, r, c);
5036 if ( r < sheet->range.rowi)
5037 g_string_append (string, "\n");
5044 range_to_html (const PsppireSheet *sheet)
5049 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5052 string = g_string_sized_new (480);
5054 g_string_append (string, "<html>\n");
5055 g_string_append (string, "<body>\n");
5056 g_string_append (string, "<table>\n");
5057 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5059 g_string_append (string, "<tr>\n");
5060 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5062 g_string_append (string, "<td>");
5063 append_cell_text (string, sheet, r, c);
5064 g_string_append (string, "</td>\n");
5066 g_string_append (string, "</tr>\n");
5068 g_string_append (string, "</table>\n");
5069 g_string_append (string, "</body>\n");
5070 g_string_append (string, "</html>\n");
5082 primary_get_cb (GtkClipboard *clipboard,
5083 GtkSelectionData *selection_data,
5087 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5088 GString *string = NULL;
5092 case SELECT_FMT_TEXT:
5093 string = range_to_text (sheet);
5095 case SELECT_FMT_HTML:
5096 string = range_to_html (sheet);
5099 g_assert_not_reached ();
5102 gtk_selection_data_set (selection_data, selection_data->target,
5104 (const guchar *) string->str, string->len);
5105 g_string_free (string, TRUE);
5109 primary_clear_cb (GtkClipboard *clipboard,
5112 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5113 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5116 psppire_sheet_real_unselect_range (sheet, NULL);
5120 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5122 static const GtkTargetEntry targets[] = {
5123 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5124 { "STRING", 0, SELECT_FMT_TEXT },
5125 { "TEXT", 0, SELECT_FMT_TEXT },
5126 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5127 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5128 { "text/plain", 0, SELECT_FMT_TEXT },
5129 { "text/html", 0, SELECT_FMT_HTML }
5132 GtkClipboard *clipboard;
5134 if (!GTK_WIDGET_REALIZED (sheet))
5137 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5138 GDK_SELECTION_PRIMARY);
5140 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5142 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5143 G_N_ELEMENTS (targets),
5144 primary_get_cb, primary_clear_cb,
5146 primary_clear_cb (clipboard, sheet);
5150 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5151 gtk_clipboard_clear (clipboard);