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)
1406 g_return_if_fail (sheet != NULL);
1407 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1409 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1410 psppire_sheet_hide_entry_widget (sheet);
1412 sheet->entry_type = entry_type;
1414 create_sheet_entry (sheet);
1416 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1417 psppire_sheet_show_entry_widget (sheet);
1421 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1423 g_return_if_fail (sheet != NULL);
1424 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1426 if (show == sheet->show_grid) return;
1428 sheet->show_grid = show;
1430 redraw_range (sheet, NULL);
1434 psppire_sheet_grid_visible (PsppireSheet *sheet)
1436 g_return_val_if_fail (sheet != NULL, 0);
1437 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1439 return sheet->show_grid;
1443 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1445 g_return_val_if_fail (sheet != NULL, 0);
1446 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1448 return psppire_axis_unit_count (sheet->haxis);
1451 static void set_column_width (PsppireSheet *sheet,
1457 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1459 if (sheet->column_titles_visible) return;
1461 sheet->column_titles_visible = TRUE;
1463 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1466 gdk_window_show (sheet->column_title_window);
1467 gdk_window_move_resize (sheet->column_title_window,
1468 sheet->column_title_area.x,
1469 sheet->column_title_area.y,
1470 sheet->column_title_area.width,
1471 sheet->column_title_area.height);
1473 adjust_scrollbars (sheet);
1475 if (sheet->vadjustment)
1476 g_signal_emit_by_name (sheet->vadjustment,
1479 size_allocate_global_button (sheet);
1481 if ( sheet->row_titles_visible)
1482 gtk_widget_show (sheet->button);
1487 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1489 if (sheet->row_titles_visible) return;
1491 sheet->row_titles_visible = TRUE;
1494 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1496 gdk_window_show (sheet->row_title_window);
1497 gdk_window_move_resize (sheet->row_title_window,
1498 sheet->row_title_area.x,
1499 sheet->row_title_area.y,
1500 sheet->row_title_area.width,
1501 sheet->row_title_area.height);
1503 adjust_scrollbars (sheet);
1506 if (sheet->hadjustment)
1507 g_signal_emit_by_name (sheet->hadjustment,
1510 size_allocate_global_button (sheet);
1512 if ( sheet->column_titles_visible)
1513 gtk_widget_show (sheet->button);
1517 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1519 if (!sheet->column_titles_visible) return;
1521 sheet->column_titles_visible = FALSE;
1523 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1525 if (sheet->column_title_window)
1526 gdk_window_hide (sheet->column_title_window);
1528 gtk_widget_hide (sheet->button);
1530 adjust_scrollbars (sheet);
1533 if (sheet->vadjustment)
1534 g_signal_emit_by_name (sheet->vadjustment,
1539 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1541 if (!sheet->row_titles_visible) return;
1543 sheet->row_titles_visible = FALSE;
1545 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1547 if (sheet->row_title_window)
1548 gdk_window_hide (sheet->row_title_window);
1550 gtk_widget_hide (sheet->button);
1552 adjust_scrollbars (sheet);
1555 if (sheet->hadjustment)
1556 g_signal_emit_by_name (sheet->hadjustment,
1561 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1562 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1563 at the {top,left} of the sheet. If it's 1, then it'll
1564 be placed at the {bottom,right}.
1565 ROW or COL may be -1, in which case scrolling in that dimension
1569 psppire_sheet_moveto (PsppireSheet *sheet,
1577 g_return_if_fail (row_align >= 0);
1578 g_return_if_fail (col_align >= 0);
1580 g_return_if_fail (row_align <= 1);
1581 g_return_if_fail (col_align <= 1);
1583 g_return_if_fail (col <
1584 psppire_axis_unit_count (sheet->haxis));
1585 g_return_if_fail (row <
1586 psppire_axis_unit_count (sheet->vaxis));
1588 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1593 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1595 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1601 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1603 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1609 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
1611 g_return_if_fail (sheet != NULL);
1612 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1614 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
1617 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
1618 psppire_sheet_real_unselect_range (sheet, NULL);
1620 sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
1621 sheet->range.row0 = row;
1622 sheet->range.col0 = 0;
1623 sheet->range.rowi = row;
1624 sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1626 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
1627 psppire_sheet_real_select_range (sheet, NULL);
1632 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
1634 g_return_if_fail (sheet != NULL);
1635 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1637 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
1640 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
1641 psppire_sheet_real_unselect_range (sheet, NULL);
1643 sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
1644 sheet->range.row0 = 0;
1645 sheet->range.col0 = column;
1646 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1647 sheet->range.coli = column;
1649 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
1650 psppire_sheet_real_select_range (sheet, NULL);
1657 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1658 const PsppireSheetRange *range)
1660 g_return_val_if_fail (sheet != NULL, FALSE);
1662 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1665 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1668 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1671 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1674 if (range->rowi < min_visible_row (sheet))
1677 if (range->row0 > max_visible_row (sheet))
1680 if (range->coli < min_visible_column (sheet))
1683 if (range->col0 > max_visible_column (sheet))
1690 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1691 gint row, gint column)
1693 PsppireSheetRange range;
1696 range.col0 = column;
1698 range.coli = column;
1700 return psppire_sheet_range_isvisible (sheet, &range);
1704 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1706 g_return_if_fail (sheet != NULL);
1707 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1708 g_return_if_fail (range != NULL);
1710 range->row0 = min_visible_row (sheet);
1711 range->col0 = min_visible_column (sheet);
1712 range->rowi = max_visible_row (sheet);
1713 range->coli = max_visible_column (sheet);
1718 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1719 GtkAdjustment *hadjustment,
1720 GtkAdjustment *vadjustment)
1722 if ( sheet->vadjustment != vadjustment )
1724 if (sheet->vadjustment)
1725 g_object_unref (sheet->vadjustment);
1726 sheet->vadjustment = vadjustment;
1730 g_object_ref (vadjustment);
1732 g_signal_connect (sheet->vadjustment, "value_changed",
1733 G_CALLBACK (vadjustment_value_changed),
1738 if ( sheet->hadjustment != hadjustment )
1740 if (sheet->hadjustment)
1741 g_object_unref (sheet->hadjustment);
1743 sheet->hadjustment = hadjustment;
1747 g_object_ref (hadjustment);
1749 g_signal_connect (sheet->hadjustment, "value_changed",
1750 G_CALLBACK (hadjustment_value_changed),
1758 psppire_sheet_finalize (GObject *object)
1760 PsppireSheet *sheet;
1762 g_return_if_fail (object != NULL);
1763 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1765 sheet = PSPPIRE_SHEET (object);
1767 if (G_OBJECT_CLASS (parent_class)->finalize)
1768 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1772 psppire_sheet_dispose (GObject *object)
1774 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1776 g_return_if_fail (object != NULL);
1777 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1779 if ( sheet->dispose_has_run )
1782 sheet->dispose_has_run = TRUE;
1784 if ( sheet->cell_padding)
1785 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1787 if (sheet->model) g_object_unref (sheet->model);
1788 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1789 if (sheet->haxis) g_object_unref (sheet->haxis);
1791 g_object_unref (sheet->button);
1792 sheet->button = NULL;
1794 /* unref adjustments */
1795 if (sheet->hadjustment)
1797 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1798 G_SIGNAL_MATCH_DATA,
1802 g_object_unref (sheet->hadjustment);
1803 sheet->hadjustment = NULL;
1806 if (sheet->vadjustment)
1808 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1809 G_SIGNAL_MATCH_DATA,
1813 g_object_unref (sheet->vadjustment);
1815 sheet->vadjustment = NULL;
1818 if (G_OBJECT_CLASS (parent_class)->dispose)
1819 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1823 psppire_sheet_style_set (GtkWidget *widget,
1824 GtkStyle *previous_style)
1826 PsppireSheet *sheet;
1828 g_return_if_fail (widget != NULL);
1829 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1831 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1832 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1834 sheet = PSPPIRE_SHEET (widget);
1836 if (GTK_WIDGET_REALIZED (widget))
1838 gtk_style_set_background (widget->style, widget->window, widget->state);
1841 set_entry_widget_font (sheet);
1846 psppire_sheet_realize (GtkWidget *widget)
1848 PsppireSheet *sheet;
1849 GdkWindowAttr attributes;
1850 const gint attributes_mask =
1851 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1854 GdkColormap *colormap;
1855 GdkDisplay *display;
1857 g_return_if_fail (widget != NULL);
1858 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1860 sheet = PSPPIRE_SHEET (widget);
1862 colormap = gtk_widget_get_colormap (widget);
1863 display = gtk_widget_get_display (widget);
1865 attributes.window_type = GDK_WINDOW_CHILD;
1866 attributes.x = widget->allocation.x;
1867 attributes.y = widget->allocation.y;
1868 attributes.width = widget->allocation.width;
1869 attributes.height = widget->allocation.height;
1870 attributes.wclass = GDK_INPUT_OUTPUT;
1872 attributes.visual = gtk_widget_get_visual (widget);
1873 attributes.colormap = colormap;
1875 attributes.event_mask = gtk_widget_get_events (widget);
1876 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1877 GDK_BUTTON_PRESS_MASK |
1878 GDK_BUTTON_RELEASE_MASK |
1879 GDK_KEY_PRESS_MASK |
1880 GDK_ENTER_NOTIFY_MASK |
1881 GDK_LEAVE_NOTIFY_MASK |
1882 GDK_POINTER_MOTION_MASK |
1883 GDK_POINTER_MOTION_HINT_MASK);
1885 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1888 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1890 gdk_window_set_user_data (widget->window, sheet);
1892 widget->style = gtk_style_attach (widget->style, widget->window);
1894 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1896 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1897 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1899 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1900 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1905 attributes.width = sheet->column_title_area.width;
1906 attributes.height = sheet->column_title_area.height;
1909 /* column - title window */
1910 sheet->column_title_window =
1911 gdk_window_new (widget->window, &attributes, attributes_mask);
1912 gdk_window_set_user_data (sheet->column_title_window, sheet);
1913 gtk_style_set_background (widget->style, sheet->column_title_window,
1919 attributes.width = sheet->row_title_area.width;
1920 attributes.height = sheet->row_title_area.height;
1922 /* row - title window */
1923 sheet->row_title_window = gdk_window_new (widget->window,
1924 &attributes, attributes_mask);
1925 gdk_window_set_user_data (sheet->row_title_window, sheet);
1926 gtk_style_set_background (widget->style, sheet->row_title_window,
1929 /* sheet - window */
1930 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1935 sheet->sheet_window = gdk_window_new (widget->window,
1936 &attributes, attributes_mask);
1937 gdk_window_set_user_data (sheet->sheet_window, sheet);
1939 gdk_cursor_unref (attributes.cursor);
1941 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1942 gdk_window_show (sheet->sheet_window);
1945 sheet->fg_gc = gdk_gc_new (widget->window);
1946 sheet->bg_gc = gdk_gc_new (widget->window);
1948 values.foreground = widget->style->white;
1949 values.function = GDK_INVERT;
1950 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1951 values.line_width = MAX (sheet->cell_padding->left,
1952 MAX (sheet->cell_padding->right,
1953 MAX (sheet->cell_padding->top,
1954 sheet->cell_padding->bottom)));
1956 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1964 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1965 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1967 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1968 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1970 sheet->button->style = gtk_style_attach (sheet->button->style,
1971 sheet->sheet_window);
1974 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1976 if (sheet->column_titles_visible)
1977 gdk_window_show (sheet->column_title_window);
1978 if (sheet->row_titles_visible)
1979 gdk_window_show (sheet->row_title_window);
1981 sheet->hover_window = create_hover_window ();
1983 draw_row_title_buttons (sheet);
1984 draw_column_title_buttons (sheet);
1986 psppire_sheet_update_primary_selection (sheet);
1989 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1993 create_global_button (PsppireSheet *sheet)
1995 sheet->button = gtk_button_new_with_label (" ");
1997 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1999 g_object_ref_sink (sheet->button);
2001 g_signal_connect (sheet->button,
2003 G_CALLBACK (global_button_clicked),
2008 size_allocate_global_button (PsppireSheet *sheet)
2010 GtkAllocation allocation;
2012 if (!sheet->column_titles_visible) return;
2013 if (!sheet->row_titles_visible) return;
2015 gtk_widget_size_request (sheet->button, NULL);
2019 allocation.width = sheet->row_title_area.width;
2020 allocation.height = sheet->column_title_area.height;
2022 gtk_widget_size_allocate (sheet->button, &allocation);
2026 global_button_clicked (GtkWidget *widget, gpointer data)
2028 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
2033 psppire_sheet_unrealize (GtkWidget *widget)
2035 PsppireSheet *sheet;
2037 g_return_if_fail (widget != NULL);
2038 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2040 sheet = PSPPIRE_SHEET (widget);
2042 gdk_cursor_unref (sheet->cursor_drag);
2043 sheet->cursor_drag = NULL;
2045 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2046 sheet->color, n_COLORS);
2048 g_object_unref (sheet->xor_gc);
2049 g_object_unref (sheet->fg_gc);
2050 g_object_unref (sheet->bg_gc);
2052 destroy_hover_window (sheet->hover_window);
2054 gdk_window_destroy (sheet->sheet_window);
2055 gdk_window_destroy (sheet->column_title_window);
2056 gdk_window_destroy (sheet->row_title_window);
2058 gtk_widget_unparent (sheet->entry_widget);
2059 if (sheet->button != NULL)
2060 gtk_widget_unparent (sheet->button);
2062 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2063 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2067 psppire_sheet_map (GtkWidget *widget)
2069 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2071 g_return_if_fail (widget != NULL);
2072 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2074 if (!GTK_WIDGET_MAPPED (widget))
2076 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2078 gdk_window_show (widget->window);
2079 gdk_window_show (sheet->sheet_window);
2081 if (sheet->column_titles_visible)
2083 draw_column_title_buttons (sheet);
2084 gdk_window_show (sheet->column_title_window);
2086 if (sheet->row_titles_visible)
2088 draw_row_title_buttons (sheet);
2089 gdk_window_show (sheet->row_title_window);
2092 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2093 && sheet->active_cell.row >= 0
2094 && sheet->active_cell.col >= 0 )
2096 gtk_widget_show (sheet->entry_widget);
2097 gtk_widget_map (sheet->entry_widget);
2100 if (!GTK_WIDGET_MAPPED (sheet->button))
2102 gtk_widget_show (sheet->button);
2103 gtk_widget_map (sheet->button);
2106 redraw_range (sheet, NULL);
2107 change_active_cell (sheet,
2108 sheet->active_cell.row,
2109 sheet->active_cell.col);
2114 psppire_sheet_unmap (GtkWidget *widget)
2116 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2118 if (!GTK_WIDGET_MAPPED (widget))
2121 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2123 gdk_window_hide (sheet->sheet_window);
2124 if (sheet->column_titles_visible)
2125 gdk_window_hide (sheet->column_title_window);
2126 if (sheet->row_titles_visible)
2127 gdk_window_hide (sheet->row_title_window);
2128 gdk_window_hide (widget->window);
2130 gtk_widget_unmap (sheet->entry_widget);
2131 gtk_widget_unmap (sheet->button);
2132 gtk_widget_unmap (sheet->hover_window->window);
2135 /* get cell attributes of the given cell */
2136 /* TRUE means that the cell is currently allocated */
2137 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2139 PsppireSheetCellAttr *attributes);
2144 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2146 PangoLayout *layout;
2147 PangoRectangle text;
2148 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2153 PsppireSheetCellAttr attributes;
2156 g_return_if_fail (sheet != NULL);
2158 /* bail now if we aren't yet drawable */
2159 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2162 row >= psppire_axis_unit_count (sheet->vaxis))
2166 col >= psppire_axis_unit_count (sheet->haxis))
2169 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2171 /* select GC for background rectangle */
2172 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2173 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2175 rectangle_from_cell (sheet, row, col, &area);
2177 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2179 if (sheet->show_grid)
2181 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2183 gdk_draw_rectangle (sheet->sheet_window,
2187 area.width, area.height);
2191 label = psppire_sheet_cell_get_text (sheet, row, col);
2196 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2197 dispose_string (sheet, label);
2200 pango_layout_set_font_description (layout, font_desc);
2202 pango_layout_get_pixel_extents (layout, NULL, &text);
2204 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2206 font_height = pango_font_description_get_size (font_desc);
2207 if ( !pango_font_description_get_size_is_absolute (font_desc))
2208 font_height /= PANGO_SCALE;
2211 if ( sheet->cell_padding )
2213 area.x += sheet->cell_padding->left;
2214 area.width -= sheet->cell_padding->right
2215 + sheet->cell_padding->left;
2217 area.y += sheet->cell_padding->top;
2218 area.height -= sheet->cell_padding->bottom
2220 sheet->cell_padding->top;
2223 /* Centre the text vertically */
2224 area.y += (area.height - font_height) / 2.0;
2226 switch (attributes.justification)
2228 case GTK_JUSTIFY_RIGHT:
2229 area.x += area.width - text.width;
2231 case GTK_JUSTIFY_CENTER:
2232 area.x += (area.width - text.width) / 2.0;
2234 case GTK_JUSTIFY_LEFT:
2238 g_critical ("Unhandled justification %d in column %d\n",
2239 attributes.justification, col);
2243 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2248 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2249 g_object_unref (layout);
2254 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2256 PsppireSheetRange range;
2261 PsppireSheetRange drawing_range;
2263 gdk_region_get_clipbox (region, &area);
2265 y = area.y + sheet->vadjustment->value;
2266 x = area.x + sheet->hadjustment->value;
2268 if ( sheet->column_titles_visible)
2269 y -= sheet->column_title_area.height;
2271 if ( sheet->row_titles_visible)
2272 x -= sheet->row_title_area.width;
2274 maximize_int (&x, 0);
2275 maximize_int (&y, 0);
2277 range.row0 = row_from_ypixel (sheet, y);
2278 range.rowi = row_from_ypixel (sheet, y + area.height);
2280 range.col0 = column_from_xpixel (sheet, x);
2281 range.coli = column_from_xpixel (sheet, x + area.width);
2283 g_return_if_fail (sheet != NULL);
2284 g_return_if_fail (PSPPIRE_SHEET (sheet));
2286 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2287 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2288 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2291 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2292 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2293 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2294 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2296 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2297 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2299 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2301 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2302 psppire_sheet_cell_draw (sheet, i, j);
2305 if (sheet->select_status == PSPPIRE_SHEET_NORMAL &&
2306 sheet->active_cell.row >= drawing_range.row0 &&
2307 sheet->active_cell.row <= drawing_range.rowi &&
2308 sheet->active_cell.col >= drawing_range.col0 &&
2309 sheet->active_cell.col <= drawing_range.coli)
2310 psppire_sheet_show_entry_widget (sheet);
2316 safe_strcmp (const gchar *s1, const gchar *s2)
2318 if ( !s1 && !s2) return 0;
2319 if ( !s1) return -1;
2320 if ( !s2) return +1;
2321 return strcmp (s1, s2);
2325 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2326 GtkJustification justification,
2329 PsppireSheetModel *model ;
2332 g_return_if_fail (sheet != NULL);
2333 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2335 if (col >= psppire_axis_unit_count (sheet->haxis)
2336 || row >= psppire_axis_unit_count (sheet->vaxis))
2339 if (col < 0 || row < 0) return;
2341 model = psppire_sheet_get_model (sheet);
2343 old_text = psppire_sheet_model_get_string (model, row, col);
2345 if (0 != safe_strcmp (old_text, text))
2347 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2348 psppire_sheet_model_set_string (model, text, row, col);
2349 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2352 if ( psppire_sheet_model_free_strings (model))
2358 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2360 PsppireSheetRange range;
2362 g_return_if_fail (sheet != NULL);
2363 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2364 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2365 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2367 if (column < 0 || row < 0) return;
2371 range.col0 = min_visible_column (sheet);
2372 range.coli = max_visible_column (sheet);
2374 psppire_sheet_real_cell_clear (sheet, row, column);
2376 redraw_range (sheet, &range);
2380 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2382 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2384 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2386 if (old_text && strlen (old_text) > 0 )
2388 psppire_sheet_model_datum_clear (model, row, column);
2391 dispose_string (sheet, old_text);
2395 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2397 PsppireSheetModel *model;
2398 g_return_val_if_fail (sheet != NULL, NULL);
2399 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2401 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2403 if (col < 0 || row < 0) return NULL;
2405 model = psppire_sheet_get_model (sheet);
2410 return psppire_sheet_model_get_string (model, row, col);
2415 psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
2417 PsppireSheetRange *range;
2419 g_return_val_if_fail (sheet != NULL, 0);
2420 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2421 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
2422 if (col < 0 || row < 0) return 0;
2424 range = &sheet->range;
2426 switch (sheet->select_status)
2428 case PSPPIRE_SHEET_NORMAL:
2429 return GTK_STATE_NORMAL;
2431 case PSPPIRE_SHEET_ROW_SELECTED:
2432 if (row >= range->row0 && row <= range->rowi)
2433 return GTK_STATE_SELECTED;
2435 case PSPPIRE_SHEET_COLUMN_SELECTED:
2436 if (col >= range->col0 && col <= range->coli)
2437 return GTK_STATE_SELECTED;
2439 case PSPPIRE_SHEET_RANGE_SELECTED:
2440 if (row >= range->row0 && row <= range->rowi && \
2441 col >= range->col0 && col <= range->coli)
2442 return GTK_STATE_SELECTED;
2445 return GTK_STATE_NORMAL;
2448 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2449 If the function returns FALSE, then the results will be unreliable.
2452 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2460 *column = -G_MAXINT;
2462 g_return_val_if_fail (sheet != NULL, 0);
2463 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2465 /* bounds checking, return false if the user clicked
2473 if ( sheet->column_titles_visible)
2474 y -= sheet->column_title_area.height;
2476 y += sheet->vadjustment->value;
2478 if ( y < 0 && sheet->column_titles_visible)
2484 trow = row_from_ypixel (sheet, y);
2485 if (trow > psppire_axis_unit_count (sheet->vaxis))
2491 if ( sheet->row_titles_visible)
2492 x -= sheet->row_title_area.width;
2494 x += sheet->hadjustment->value;
2496 if ( x < 0 && sheet->row_titles_visible)
2502 tcol = column_from_xpixel (sheet, x);
2503 if (tcol > psppire_axis_unit_count (sheet->haxis))
2513 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2518 g_return_val_if_fail (sheet != NULL, 0);
2519 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2521 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2524 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2525 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2527 area->width= (column == -1) ? sheet->row_title_area.width
2528 : psppire_axis_unit_size (sheet->haxis, column);
2530 area->height= (row == -1) ? sheet->column_title_area.height
2531 : psppire_axis_unit_size (sheet->vaxis, row);
2537 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2539 g_return_if_fail (sheet != NULL);
2540 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2542 if (row < -1 || col < -1)
2545 if (row >= psppire_axis_unit_count (sheet->vaxis)
2547 col >= psppire_axis_unit_count (sheet->haxis))
2550 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2553 if ( row == -1 || col == -1)
2555 psppire_sheet_hide_entry_widget (sheet);
2559 change_active_cell (sheet, row, col);
2563 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2565 g_return_if_fail (sheet != NULL);
2566 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2568 if ( row ) *row = sheet->active_cell.row;
2569 if (column) *column = sheet->active_cell.col;
2573 entry_load_text (PsppireSheet *sheet)
2577 GtkJustification justification;
2578 PsppireSheetCellAttr attributes;
2580 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2581 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2583 row = sheet->active_cell.row;
2584 col = sheet->active_cell.col;
2586 if (row < 0 || col < 0) return;
2588 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2590 if (text && strlen (text) > 0)
2592 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2593 justification = attributes.justification;
2594 psppire_sheet_set_cell (sheet, row, col, justification, text);
2600 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2602 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2605 if (sheet->active_cell.row < 0 ||
2606 sheet->active_cell.col < 0) return;
2608 gtk_widget_hide (sheet->entry_widget);
2609 gtk_widget_unmap (sheet->entry_widget);
2611 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2615 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2617 gint old_row, old_col;
2619 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2621 if (row < 0 || col < 0)
2624 if ( row > psppire_axis_unit_count (sheet->vaxis)
2625 || col > psppire_axis_unit_count (sheet->haxis))
2628 old_row = sheet->active_cell.row;
2629 old_col = sheet->active_cell.col;
2631 entry_load_text (sheet);
2633 /* Erase the old cell border */
2634 psppire_sheet_draw_active_cell (sheet);
2636 sheet->active_cell.row = row;
2637 sheet->active_cell.col = col;
2639 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2641 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2643 psppire_sheet_draw_active_cell (sheet);
2644 psppire_sheet_show_entry_widget (sheet);
2646 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2648 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2649 row, col, old_row, old_col);
2654 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2656 GtkEntry *sheet_entry;
2657 PsppireSheetCellAttr attributes;
2661 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2663 row = sheet->active_cell.row;
2664 col = sheet->active_cell.col;
2666 /* Don't show the active cell, if there is no active cell: */
2667 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2670 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2671 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2672 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2674 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2676 sheet_entry = psppire_sheet_get_entry (sheet);
2678 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2680 if (GTK_IS_ENTRY (sheet_entry))
2682 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2683 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2686 text = g_strdup ("");
2688 if (strcmp (old_text, text) != 0)
2689 gtk_entry_set_text (sheet_entry, text);
2691 dispose_string (sheet, text);
2694 switch (attributes.justification)
2696 case GTK_JUSTIFY_RIGHT:
2697 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2699 case GTK_JUSTIFY_CENTER:
2700 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2702 case GTK_JUSTIFY_LEFT:
2704 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2710 psppire_sheet_size_allocate_entry (sheet);
2712 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2713 psppire_sheet_model_is_editable (sheet->model,
2715 gtk_widget_map (sheet->entry_widget);
2719 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2722 PsppireSheetRange range;
2724 row = sheet->active_cell.row;
2725 col = sheet->active_cell.col;
2727 if (row < 0 || col < 0) return FALSE;
2729 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2732 range.col0 = range.coli = col;
2733 range.row0 = range.rowi = row;
2735 psppire_sheet_draw_border (sheet, range);
2743 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
2747 rectangle_from_range (sheet, &new_range, &area);
2752 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
2754 area.x += sheet->cell_padding->left / 2;
2755 area.y += sheet->cell_padding->top / 2;
2756 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
2757 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
2759 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
2766 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
2771 psppire_sheet_real_select_range (PsppireSheet *sheet,
2772 const PsppireSheetRange *range)
2774 g_return_if_fail (sheet != NULL);
2776 if (range == NULL) range = &sheet->range;
2778 memcpy (&sheet->range, range, sizeof (*range));
2780 if (range->row0 < 0 || range->rowi < 0) return;
2781 if (range->col0 < 0 || range->coli < 0) return;
2783 psppire_sheet_update_primary_selection (sheet);
2785 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
2790 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
2792 g_return_if_fail (sheet != NULL);
2793 *range = sheet->range;
2798 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
2800 g_return_if_fail (sheet != NULL);
2802 if (range == NULL) range=&sheet->range;
2804 if (range->row0 < 0 || range->rowi < 0) return;
2805 if (range->col0 < 0 || range->coli < 0) return;
2808 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2809 psppire_sheet_real_unselect_range (sheet, NULL);
2811 sheet->range.row0 = range->row0;
2812 sheet->range.rowi = range->rowi;
2813 sheet->range.col0 = range->col0;
2814 sheet->range.coli = range->coli;
2816 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
2817 psppire_sheet_real_select_range (sheet, NULL);
2821 psppire_sheet_unselect_range (PsppireSheet *sheet)
2823 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2826 psppire_sheet_real_unselect_range (sheet, NULL);
2827 sheet->select_status = PSPPIRE_SHEET_NORMAL;
2832 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
2833 const PsppireSheetRange *range)
2835 g_return_if_fail (sheet != NULL);
2836 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
2839 range = &sheet->range;
2841 if (range->row0 < 0 || range->rowi < 0) return;
2842 if (range->col0 < 0 || range->coli < 0) return;
2844 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
2845 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
2847 sheet->range.row0 = -1;
2848 sheet->range.rowi = -1;
2849 sheet->range.col0 = -1;
2850 sheet->range.coli = -1;
2855 psppire_sheet_expose (GtkWidget *widget, GdkEventExpose *event)
2857 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2859 g_return_val_if_fail (event != NULL, FALSE);
2861 if (!GTK_WIDGET_DRAWABLE (widget))
2864 /* exposure events on the sheet */
2865 if (event->window == sheet->row_title_window &&
2866 sheet->row_titles_visible)
2868 draw_row_title_buttons_range (sheet,
2869 min_visible_row (sheet),
2870 max_visible_row (sheet));
2873 if (event->window == sheet->column_title_window &&
2874 sheet->column_titles_visible)
2876 draw_column_title_buttons_range (sheet,
2877 min_visible_column (sheet),
2878 max_visible_column (sheet));
2881 if (event->window == sheet->sheet_window)
2883 draw_sheet_region (sheet, event->region);
2885 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2888 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2889 psppire_sheet_range_draw (sheet, &sheet->range);
2891 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2892 psppire_sheet_range_draw (sheet, &sheet->drag_range);
2895 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2899 rectangle_from_range (sheet, &sheet->range, &area);
2901 gdk_draw_rectangle (sheet->sheet_window,
2904 area.x + 1, area.y + 1,
2905 area.width, area.height);
2909 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2910 draw_xor_rectangle (sheet, sheet->drag_range);
2915 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
2918 PsppireSheetRange range;
2919 range.row0 = range.rowi = sheet->active_cell.row;
2920 range.col0 = range.coli = sheet->active_cell.col;
2922 rectangle_from_range (sheet, &range, &rect);
2924 if (GDK_OVERLAP_RECTANGLE_OUT !=
2925 gdk_region_rect_in (event->region, &rect))
2927 psppire_sheet_draw_active_cell (sheet);
2933 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2940 psppire_sheet_button_press (GtkWidget *widget, GdkEventButton *event)
2942 PsppireSheet *sheet;
2943 GdkModifierType mods;
2948 g_return_val_if_fail (widget != NULL, FALSE);
2949 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
2950 g_return_val_if_fail (event != NULL, FALSE);
2952 sheet = PSPPIRE_SHEET (widget);
2954 /* Cancel any pending tooltips */
2955 if (sheet->motion_timer)
2957 g_source_remove (sheet->motion_timer);
2958 sheet->motion_timer = 0;
2961 gtk_widget_get_pointer (widget, &x, &y);
2962 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2965 if (event->window == sheet->column_title_window)
2967 sheet->x_drag = event->x;
2968 g_signal_emit (sheet,
2969 sheet_signals[BUTTON_EVENT_COLUMN], 0,
2972 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2974 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2975 g_signal_emit (sheet,
2976 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
2979 else if (event->window == sheet->row_title_window)
2981 g_signal_emit (sheet,
2982 sheet_signals[BUTTON_EVENT_ROW], 0,
2985 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2987 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2988 g_signal_emit (sheet,
2989 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
2993 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
2995 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
2998 /* press on resize windows */
2999 if (event->window == sheet->column_title_window)
3001 sheet->x_drag = event->x;
3003 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3005 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3006 gdk_pointer_grab (sheet->column_title_window, FALSE,
3007 GDK_POINTER_MOTION_HINT_MASK |
3008 GDK_BUTTON1_MOTION_MASK |
3009 GDK_BUTTON_RELEASE_MASK,
3010 NULL, NULL, event->time);
3012 draw_xor_vline (sheet);
3017 if (event->window == sheet->row_title_window)
3019 sheet->y_drag = event->y;
3021 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3023 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3024 gdk_pointer_grab (sheet->row_title_window, FALSE,
3025 GDK_POINTER_MOTION_HINT_MASK |
3026 GDK_BUTTON1_MOTION_MASK |
3027 GDK_BUTTON_RELEASE_MASK,
3028 NULL, NULL, event->time);
3030 draw_xor_hline (sheet);
3035 /* the sheet itself does not handle other than single click events */
3036 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3038 /* selections on the sheet */
3039 if (event->window == sheet->sheet_window)
3041 gtk_widget_get_pointer (widget, &x, &y);
3042 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3043 gdk_pointer_grab (sheet->sheet_window, FALSE,
3044 GDK_POINTER_MOTION_HINT_MASK |
3045 GDK_BUTTON1_MOTION_MASK |
3046 GDK_BUTTON_RELEASE_MASK,
3047 NULL, NULL, event->time);
3048 gtk_grab_add (GTK_WIDGET (sheet));
3050 if (psppire_sheet_click_cell (sheet, row, column))
3052 if ( sheet->select_status == PSPPIRE_SHEET_NORMAL)
3054 sheet->range.row0 = row;
3055 sheet->range.col0 = column;
3060 sheet->select_status = PSPPIRE_SHEET_NORMAL;
3062 rectangle_from_range (sheet, &sheet->range, &area);
3065 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
3070 if (event->window == sheet->column_title_window)
3072 gtk_widget_get_pointer (widget, &x, &y);
3073 if ( sheet->row_titles_visible)
3074 x -= sheet->row_title_area.width;
3076 x += sheet->hadjustment->value;
3078 column = column_from_xpixel (sheet, x);
3080 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3082 gtk_grab_add (GTK_WIDGET (sheet));
3083 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3087 if (event->window == sheet->row_title_window)
3089 gtk_widget_get_pointer (widget, &x, &y);
3090 if ( sheet->column_titles_visible)
3091 y -= sheet->column_title_area.height;
3093 y += sheet->vadjustment->value;
3095 row = row_from_ypixel (sheet, y);
3096 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3098 gtk_grab_add (GTK_WIDGET (sheet));
3099 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3107 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3109 PsppireSheetCell cell;
3110 gboolean forbid_move;
3115 if (row >= psppire_axis_unit_count (sheet->vaxis)
3116 || column >= psppire_axis_unit_count (sheet->haxis))
3121 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3122 &sheet->active_cell,
3128 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3131 row = sheet->active_cell.row;
3132 column = sheet->active_cell.col;
3134 change_active_cell (sheet, row, column);
3138 if (row == -1 && column >= 0)
3140 psppire_sheet_select_column (sheet, column);
3144 if (column == -1 && row >= 0)
3146 psppire_sheet_select_row (sheet, row);
3150 if (row == -1 && column == -1)
3152 sheet->range.row0 = 0;
3153 sheet->range.col0 = 0;
3154 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3156 psppire_axis_unit_count (sheet->haxis) - 1;
3157 psppire_sheet_select_range (sheet, NULL);
3161 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3162 change_active_cell (sheet, row, column);
3164 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3170 psppire_sheet_button_release (GtkWidget *widget,
3171 GdkEventButton *event)
3173 GdkDisplay *display = gtk_widget_get_display (widget);
3175 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3177 /* release on resize windows */
3178 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3181 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3182 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3184 gdk_display_pointer_ungrab (display, event->time);
3185 draw_xor_vline (sheet);
3188 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3189 + sheet->hadjustment->value;
3191 set_column_width (sheet, sheet->drag_cell.col, width);
3196 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3199 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3200 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3202 gdk_display_pointer_ungrab (display, event->time);
3203 draw_xor_hline (sheet);
3206 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3207 sheet->vadjustment->value;
3209 set_row_height (sheet, sheet->drag_cell.row, height);
3214 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3216 PsppireSheetRange old_range;
3217 draw_xor_rectangle (sheet, sheet->drag_range);
3218 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3219 gdk_display_pointer_ungrab (display, event->time);
3221 psppire_sheet_real_unselect_range (sheet, NULL);
3223 old_range = sheet->range;
3224 sheet->range = sheet->drag_range;
3225 sheet->drag_range = old_range;
3226 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3227 &sheet->drag_range, &sheet->range);
3228 psppire_sheet_select_range (sheet, &sheet->range);
3231 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3233 PsppireSheetRange old_range;
3234 draw_xor_rectangle (sheet, sheet->drag_range);
3235 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3236 gdk_display_pointer_ungrab (display, event->time);
3238 psppire_sheet_real_unselect_range (sheet, NULL);
3240 old_range = sheet->range;
3241 sheet->range = sheet->drag_range;
3242 sheet->drag_range = old_range;
3244 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3245 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3247 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3248 &sheet->drag_range, &sheet->range);
3249 psppire_sheet_select_range (sheet, &sheet->range);
3252 if (PSPPIRE_SHEET_IN_SELECTION (sheet))
3254 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3255 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3257 change_active_cell (sheet, sheet->active_cell.row,
3258 sheet->active_cell.col);
3261 gdk_display_pointer_ungrab (display, event->time);
3262 gtk_grab_remove (GTK_WIDGET (sheet));
3264 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3273 /* Shamelessly lifted from gtktooltips */
3275 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3279 gtk_widget_size_request (tip_window, &req);
3280 gtk_paint_flat_box (tip_window->style, tip_window->window,
3281 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3282 NULL, GTK_WIDGET(tip_window), "tooltip",
3283 0, 0, req.width, req.height);
3289 destroy_hover_window (PsppireSheetHoverTitle *h)
3291 gtk_widget_destroy (h->window);
3295 static PsppireSheetHoverTitle *
3296 create_hover_window (void)
3298 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3300 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3302 #if GTK_CHECK_VERSION (2, 9, 0)
3303 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3304 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3307 gtk_widget_set_app_paintable (hw->window, TRUE);
3308 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3309 gtk_widget_set_name (hw->window, "gtk-tooltips");
3310 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3312 g_signal_connect (hw->window,
3314 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3317 hw->label = gtk_label_new (NULL);
3320 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3321 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3323 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3325 gtk_widget_show (hw->label);
3327 g_signal_connect (hw->window,
3329 G_CALLBACK (gtk_widget_destroyed),
3335 #define HOVER_WINDOW_Y_OFFSET 2
3338 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3339 const gchar *subtitle)
3348 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3352 sheet->hover_window->row = row;
3353 sheet->hover_window->column = column;
3355 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3357 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3359 gtk_widget_show (sheet->hover_window->window);
3361 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3367 y += sheet->column_title_area.y;
3368 y += sheet->column_title_area.height;
3369 y += HOVER_WINDOW_Y_OFFSET;
3375 x += sheet->row_title_area.x;
3376 x += sheet->row_title_area.width * 2 / 3.0;
3379 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3384 motion_timeout_callback (gpointer data)
3386 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3390 gdk_threads_enter ();
3391 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3393 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3395 if (sheet->row_title_under && row >= 0)
3397 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3399 show_subtitle (sheet, row, -1, text);
3403 if (sheet->column_title_under && column >= 0)
3405 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3408 show_subtitle (sheet, -1, column, text);
3414 gdk_threads_leave ();
3419 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3421 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3422 GdkModifierType mods;
3423 GdkCursorType new_cursor;
3426 GdkDisplay *display;
3428 g_return_val_if_fail (event != NULL, FALSE);
3430 display = gtk_widget_get_display (widget);
3432 /* selections on the sheet */
3436 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3438 if ( sheet->motion_timer > 0 )
3439 g_source_remove (sheet->motion_timer);
3440 sheet->motion_timer =
3441 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3447 gtk_widget_get_pointer (widget, &wx, &wy);
3449 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3451 if ( row != sheet->hover_window->row ||
3452 column != sheet->hover_window->column)
3454 gtk_widget_hide (sheet->hover_window->window);
3459 if (event->window == sheet->column_title_window)
3461 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3462 on_column_boundary (sheet, x, &column))
3464 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3465 if (new_cursor != sheet->cursor_drag->type)
3467 gdk_cursor_unref (sheet->cursor_drag);
3468 sheet->cursor_drag =
3469 gdk_cursor_new_for_display (display, new_cursor);
3471 gdk_window_set_cursor (sheet->column_title_window,
3472 sheet->cursor_drag);
3477 new_cursor = GDK_TOP_LEFT_ARROW;
3478 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3479 new_cursor != sheet->cursor_drag->type)
3481 gdk_cursor_unref (sheet->cursor_drag);
3482 sheet->cursor_drag =
3483 gdk_cursor_new_for_display (display, new_cursor);
3484 gdk_window_set_cursor (sheet->column_title_window,
3485 sheet->cursor_drag);
3489 else if (event->window == sheet->row_title_window)
3491 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3492 on_row_boundary (sheet, y, &row))
3494 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3495 if (new_cursor != sheet->cursor_drag->type)
3497 gdk_cursor_unref (sheet->cursor_drag);
3498 sheet->cursor_drag =
3499 gdk_cursor_new_for_display (display, new_cursor);
3500 gdk_window_set_cursor (sheet->row_title_window,
3501 sheet->cursor_drag);
3506 new_cursor = GDK_TOP_LEFT_ARROW;
3507 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3508 new_cursor != sheet->cursor_drag->type)
3510 gdk_cursor_unref (sheet->cursor_drag);
3511 sheet->cursor_drag =
3512 gdk_cursor_new_for_display (display, new_cursor);
3513 gdk_window_set_cursor (sheet->row_title_window,
3514 sheet->cursor_drag);
3519 new_cursor = GDK_PLUS;
3520 if ( event->window == sheet->sheet_window &&
3521 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3522 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3523 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3524 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3525 new_cursor != sheet->cursor_drag->type)
3527 gdk_cursor_unref (sheet->cursor_drag);
3528 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3529 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3532 new_cursor = GDK_TOP_LEFT_ARROW;
3533 if ( event->window == sheet->sheet_window &&
3534 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3535 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3536 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3537 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3538 new_cursor != sheet->cursor_drag->type)
3540 gdk_cursor_unref (sheet->cursor_drag);
3541 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3542 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3545 new_cursor = GDK_SIZING;
3546 if ( event->window == sheet->sheet_window &&
3547 sheet->selection_mode != GTK_SELECTION_NONE &&
3548 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3549 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3550 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3551 new_cursor != sheet->cursor_drag->type)
3553 gdk_cursor_unref (sheet->cursor_drag);
3554 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3555 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3559 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3560 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3562 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3564 if (event->x != sheet->x_drag)
3566 draw_xor_vline (sheet);
3567 sheet->x_drag = event->x;
3568 draw_xor_vline (sheet);
3574 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3576 if (event->y != sheet->y_drag)
3578 draw_xor_hline (sheet);
3579 sheet->y_drag = event->y;
3580 draw_xor_hline (sheet);
3586 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3588 PsppireSheetRange aux;
3589 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3590 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3591 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3592 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3596 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3597 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3599 aux = sheet->drag_range;
3600 sheet->drag_range.row0 = sheet->range.row0 + row;
3601 sheet->drag_range.col0 = sheet->range.col0 + column;
3602 sheet->drag_range.rowi = sheet->range.rowi + row;
3603 sheet->drag_range.coli = sheet->range.coli + column;
3604 if (aux.row0 != sheet->drag_range.row0 ||
3605 aux.col0 != sheet->drag_range.col0)
3607 draw_xor_rectangle (sheet, aux);
3608 draw_xor_rectangle (sheet, sheet->drag_range);
3614 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3616 PsppireSheetRange aux;
3617 gint v_h, current_col, current_row, col_threshold, row_threshold;
3619 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
3620 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
3622 current_col = column_from_xpixel (sheet, x);
3623 current_row = row_from_ypixel (sheet, y);
3624 column = current_col - sheet->drag_cell.col;
3625 row = current_row - sheet->drag_cell.row;
3627 /*use half of column width resp. row height as threshold to
3629 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
3630 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
3633 if (x < col_threshold)
3636 else if (column < 0)
3638 if (x > col_threshold)
3641 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
3642 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
3645 if (y < row_threshold)
3650 if (y > row_threshold)
3654 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3655 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3665 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3666 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3668 aux = sheet->drag_range;
3669 sheet->drag_range = sheet->range;
3671 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
3672 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
3673 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
3674 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
3676 if (aux.row0 != sheet->drag_range.row0 ||
3677 aux.rowi != sheet->drag_range.rowi ||
3678 aux.col0 != sheet->drag_range.col0 ||
3679 aux.coli != sheet->drag_range.coli)
3681 draw_xor_rectangle (sheet, aux);
3682 draw_xor_rectangle (sheet, sheet->drag_range);
3688 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3690 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
3691 column == sheet->active_cell.col) return TRUE;
3693 if ( mods & GDK_BUTTON1_MASK)
3695 if (PSPPIRE_SHEET_IN_SELECTION (sheet) )
3699 /* Redraw the old range */
3700 rectangle_from_range (sheet, &sheet->range, &area);
3703 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
3705 sheet->range.rowi = row;
3706 sheet->range.coli = column;
3707 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3709 /* Redraw the new range */
3710 rectangle_from_range (sheet, &sheet->range, &area);
3713 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
3717 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3725 psppire_sheet_crossing_notify (GtkWidget *widget,
3726 GdkEventCrossing *event)
3728 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3730 if (event->window == sheet->column_title_window)
3731 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
3732 else if (event->window == sheet->row_title_window)
3733 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
3735 if (event->type == GDK_LEAVE_NOTIFY)
3736 gtk_widget_hide (sheet->hover_window->window);
3743 psppire_sheet_focus_in (GtkWidget *w,
3744 GdkEventFocus *event)
3746 PsppireSheet *sheet = PSPPIRE_SHEET (w);
3748 gtk_widget_grab_focus (sheet->entry_widget);
3756 psppire_sheet_entry_key_press (GtkWidget *widget,
3760 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
3765 /* Number of rows in a step-increment */
3766 #define ROWS_PER_STEP 1
3770 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
3772 gint old_row = sheet->active_cell.row ;
3773 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
3777 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
3778 min_visible_row (sheet));
3782 case GTK_SCROLL_PAGE_DOWN:
3783 gtk_adjustment_set_value (sheet->vadjustment,
3784 sheet->vadjustment->value +
3785 sheet->vadjustment->page_increment);
3787 case GTK_SCROLL_PAGE_UP:
3788 gtk_adjustment_set_value (sheet->vadjustment,
3789 sheet->vadjustment->value -
3790 sheet->vadjustment->page_increment);
3794 g_assert_not_reached ();
3799 vpixel += psppire_axis_start_pixel (sheet->vaxis,
3800 min_visible_row (sheet));
3802 new_row = row_from_ypixel (sheet, vpixel);
3804 change_active_cell (sheet, new_row,
3805 sheet->active_cell.col);
3810 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
3812 gint current_row = sheet->active_cell.row;
3813 gint current_col = sheet->active_cell.col;
3814 PsppireSheetCell new_cell ;
3815 gboolean forbidden = FALSE;
3817 new_cell.row = current_row;
3818 new_cell.col = current_col;
3822 case GTK_SCROLL_STEP_DOWN:
3825 case GTK_SCROLL_STEP_UP:
3828 case GTK_SCROLL_STEP_RIGHT:
3831 case GTK_SCROLL_STEP_LEFT:
3834 case GTK_SCROLL_STEP_FORWARD:
3837 psppire_sheet_model_get_column_count (sheet->model))
3843 case GTK_SCROLL_STEP_BACKWARD:
3845 if (new_cell.col < 0)
3848 psppire_sheet_model_get_column_count (sheet->model) - 1;
3853 g_assert_not_reached ();
3857 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3858 &sheet->active_cell,
3866 maximize_int (&new_cell.row, 0);
3867 maximize_int (&new_cell.col, 0);
3869 minimize_int (&new_cell.row,
3870 psppire_axis_unit_count (sheet->vaxis) - 1);
3872 minimize_int (&new_cell.col,
3873 psppire_axis_unit_count (sheet->haxis) - 1);
3875 change_active_cell (sheet, new_cell.row, new_cell.col);
3878 if ( new_cell.col > max_fully_visible_column (sheet))
3881 psppire_axis_start_pixel (sheet->haxis,
3883 hpos -= sheet->hadjustment->page_size;
3885 gtk_adjustment_set_value (sheet->hadjustment,
3888 else if ( new_cell.col < min_fully_visible_column (sheet))
3891 psppire_axis_start_pixel (sheet->haxis,
3894 gtk_adjustment_set_value (sheet->hadjustment,
3899 if ( new_cell.row > max_fully_visible_row (sheet))
3902 psppire_axis_start_pixel (sheet->vaxis,
3904 vpos -= sheet->vadjustment->page_size;
3906 gtk_adjustment_set_value (sheet->vadjustment,
3909 else if ( new_cell.row < min_fully_visible_row (sheet))
3912 psppire_axis_start_pixel (sheet->vaxis,
3915 gtk_adjustment_set_value (sheet->vadjustment,
3919 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3924 psppire_sheet_key_press (GtkWidget *widget,
3927 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3929 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3931 switch (key->keyval)
3934 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
3937 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
3939 case GDK_ISO_Left_Tab:
3940 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
3943 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
3947 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3950 step_sheet (sheet, GTK_SCROLL_STEP_UP);
3954 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
3957 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
3961 gtk_adjustment_set_value (sheet->vadjustment,
3962 sheet->vadjustment->lower);
3964 change_active_cell (sheet, 0,
3965 sheet->active_cell.col);
3970 gtk_adjustment_set_value (sheet->vadjustment,
3971 sheet->vadjustment->upper -
3972 sheet->vadjustment->page_size -
3973 sheet->vadjustment->page_increment);
3976 change_active_cellx (sheet,
3977 psppire_axis_unit_count (sheet->vaxis) - 1,
3978 sheet->active_cell.col);
3982 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
3993 psppire_sheet_size_request (GtkWidget *widget,
3994 GtkRequisition *requisition)
3996 PsppireSheet *sheet;
3998 g_return_if_fail (widget != NULL);
3999 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4000 g_return_if_fail (requisition != NULL);
4002 sheet = PSPPIRE_SHEET (widget);
4004 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4005 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4007 /* compute the size of the column title area */
4008 if (sheet->column_titles_visible)
4009 requisition->height += sheet->column_title_area.height;
4011 /* compute the size of the row title area */
4012 if (sheet->row_titles_visible)
4013 requisition->width += sheet->row_title_area.width;
4018 psppire_sheet_size_allocate (GtkWidget *widget,
4019 GtkAllocation *allocation)
4021 PsppireSheet *sheet;
4022 GtkAllocation sheet_allocation;
4025 g_return_if_fail (widget != NULL);
4026 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4027 g_return_if_fail (allocation != NULL);
4029 sheet = PSPPIRE_SHEET (widget);
4030 widget->allocation = *allocation;
4031 border_width = GTK_CONTAINER (widget)->border_width;
4033 if (GTK_WIDGET_REALIZED (widget))
4034 gdk_window_move_resize (widget->window,
4035 allocation->x + border_width,
4036 allocation->y + border_width,
4037 allocation->width - 2 * border_width,
4038 allocation->height - 2 * border_width);
4040 sheet_allocation.x = 0;
4041 sheet_allocation.y = 0;
4042 sheet_allocation.width = allocation->width - 2 * border_width;
4043 sheet_allocation.height = allocation->height - 2 * border_width;
4045 if (GTK_WIDGET_REALIZED (widget))
4046 gdk_window_move_resize (sheet->sheet_window,
4049 sheet_allocation.width,
4050 sheet_allocation.height);
4052 /* position the window which holds the column title buttons */
4053 sheet->column_title_area.x = 0;
4054 sheet->column_title_area.y = 0;
4055 sheet->column_title_area.width = sheet_allocation.width ;
4058 /* position the window which holds the row title buttons */
4059 sheet->row_title_area.x = 0;
4060 sheet->row_title_area.y = 0;
4061 sheet->row_title_area.height = sheet_allocation.height;
4063 if (sheet->row_titles_visible)
4064 sheet->column_title_area.x += sheet->row_title_area.width;
4066 if (sheet->column_titles_visible)
4067 sheet->row_title_area.y += sheet->column_title_area.height;
4069 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4070 gdk_window_move_resize (sheet->column_title_window,
4071 sheet->column_title_area.x,
4072 sheet->column_title_area.y,
4073 sheet->column_title_area.width,
4074 sheet->column_title_area.height);
4077 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4078 gdk_window_move_resize (sheet->row_title_window,
4079 sheet->row_title_area.x,
4080 sheet->row_title_area.y,
4081 sheet->row_title_area.width,
4082 sheet->row_title_area.height);
4084 size_allocate_global_button (sheet);
4088 gint width = sheet->column_title_area.width;
4090 if ( sheet->row_titles_visible)
4091 width -= sheet->row_title_area.width;
4093 g_object_set (sheet->haxis,
4094 "minimum-extent", width,
4101 gint height = sheet->row_title_area.height;
4103 if ( sheet->column_titles_visible)
4104 height -= sheet->column_title_area.height;
4106 g_object_set (sheet->vaxis,
4107 "minimum-extent", height,
4112 /* set the scrollbars adjustments */
4113 adjust_scrollbars (sheet);
4117 draw_column_title_buttons (PsppireSheet *sheet)
4121 if (!sheet->column_titles_visible) return;
4122 if (!GTK_WIDGET_REALIZED (sheet))
4125 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4128 if (sheet->row_titles_visible)
4130 x = sheet->row_title_area.width;
4133 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4135 sheet->column_title_area.width = width;
4136 sheet->column_title_area.x = x;
4137 gdk_window_move_resize (sheet->column_title_window,
4138 sheet->column_title_area.x,
4139 sheet->column_title_area.y,
4140 sheet->column_title_area.width,
4141 sheet->column_title_area.height);
4144 if (max_visible_column (sheet) ==
4145 psppire_axis_unit_count (sheet->haxis) - 1)
4146 gdk_window_clear_area (sheet->column_title_window,
4148 sheet->column_title_area.width,
4149 sheet->column_title_area.height);
4151 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4153 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4154 max_visible_column (sheet));
4158 draw_row_title_buttons (PsppireSheet *sheet)
4163 if (!sheet->row_titles_visible) return;
4164 if (!GTK_WIDGET_REALIZED (sheet))
4167 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4169 if (sheet->column_titles_visible)
4171 y = sheet->column_title_area.height;
4174 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4176 sheet->row_title_area.y = y;
4177 sheet->row_title_area.height = height;
4178 gdk_window_move_resize (sheet->row_title_window,
4179 sheet->row_title_area.x,
4180 sheet->row_title_area.y,
4181 sheet->row_title_area.width,
4182 sheet->row_title_area.height);
4185 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4186 gdk_window_clear_area (sheet->row_title_window,
4188 sheet->row_title_area.width,
4189 sheet->row_title_area.height);
4191 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4193 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4194 max_visible_row (sheet));
4199 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4201 GtkAllocation entry_alloc;
4202 PsppireSheetCellAttr attributes = { 0 };
4203 GtkEntry *sheet_entry;
4205 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4206 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4208 sheet_entry = psppire_sheet_get_entry (sheet);
4210 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4211 sheet->active_cell.col,
4215 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4217 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4219 style->bg[GTK_STATE_NORMAL] = attributes.background;
4220 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4221 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4222 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4223 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4224 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4227 rectangle_from_cell (sheet, sheet->active_cell.row,
4228 sheet->active_cell.col, &entry_alloc);
4230 entry_alloc.x += sheet->cell_padding->left;
4231 entry_alloc.y += sheet->cell_padding->right;
4232 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4233 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4236 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4237 entry_alloc.height);
4238 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4242 /* Copy the sheet's font to the entry widget */
4244 set_entry_widget_font (PsppireSheet *sheet)
4246 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4248 pango_font_description_free (style->font_desc);
4249 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4251 gtk_widget_modify_style (sheet->entry_widget, style);
4255 create_sheet_entry (PsppireSheet *sheet)
4257 if (sheet->entry_widget)
4259 gtk_widget_unparent (sheet->entry_widget);
4262 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4263 g_object_ref_sink (sheet->entry_widget);
4265 gtk_widget_size_request (sheet->entry_widget, NULL);
4267 if ( GTK_IS_ENTRY (sheet->entry_widget))
4269 g_object_set (sheet->entry_widget,
4274 if (GTK_WIDGET_REALIZED (sheet))
4276 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4277 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4278 gtk_widget_realize (sheet->entry_widget);
4281 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4282 G_CALLBACK (psppire_sheet_entry_key_press),
4285 set_entry_widget_font (sheet);
4287 gtk_widget_show (sheet->entry_widget);
4291 /* Finds the last child widget that happens to be of type GtkEntry */
4293 find_entry (GtkWidget *w, gpointer user_data)
4295 GtkWidget **entry = user_data;
4296 if ( GTK_IS_ENTRY (w))
4304 psppire_sheet_get_entry (PsppireSheet *sheet)
4306 GtkWidget *w = sheet->entry_widget;
4308 g_return_val_if_fail (sheet != NULL, NULL);
4309 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4310 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4312 while (! GTK_IS_ENTRY (w))
4314 GtkWidget *entry = NULL;
4316 if (GTK_IS_CONTAINER (w))
4318 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4327 return GTK_ENTRY (w);
4332 draw_button (PsppireSheet *sheet, GdkWindow *window,
4333 PsppireSheetButton *button, gboolean is_sensitive,
4334 GdkRectangle allocation)
4336 GtkShadowType shadow_type;
4337 gint text_width = 0, text_height = 0;
4338 PangoAlignment align = PANGO_ALIGN_LEFT;
4344 g_return_if_fail (sheet != NULL);
4345 g_return_if_fail (button != NULL);
4348 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4350 gdk_window_clear_area (window,
4351 allocation.x, allocation.y,
4352 allocation.width, allocation.height);
4354 gtk_widget_ensure_style (sheet->button);
4356 gtk_paint_box (sheet->button->style, window,
4357 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4359 GTK_WIDGET (sheet->button),
4361 allocation.x, allocation.y,
4362 allocation.width, allocation.height);
4364 state = button->state;
4365 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4367 if (state == GTK_STATE_ACTIVE)
4368 shadow_type = GTK_SHADOW_IN;
4370 shadow_type = GTK_SHADOW_OUT;
4372 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4373 gtk_paint_box (sheet->button->style, window,
4374 button->state, shadow_type,
4375 &allocation, GTK_WIDGET (sheet->button),
4377 allocation.x, allocation.y,
4378 allocation.width, allocation.height);
4380 if ( button->overstruck)
4382 GdkPoint points[2] = {
4383 {allocation.x, allocation.y},
4384 {allocation.x + allocation.width,
4385 allocation.y + allocation.height}
4388 gtk_paint_polygon (sheet->button->style,
4400 if (button->label_visible)
4402 text_height = DEFAULT_ROW_HEIGHT -
4403 2 * COLUMN_TITLES_HEIGHT;
4405 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4407 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4410 allocation.y += 2 * sheet->button->style->ythickness;
4412 if (button->label && strlen (button->label) > 0)
4414 PangoRectangle rect;
4415 gchar *line = button->label;
4417 PangoLayout *layout = NULL;
4418 gint real_x = allocation.x;
4419 gint real_y = allocation.y;
4421 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4422 pango_layout_get_extents (layout, NULL, &rect);
4424 text_width = PANGO_PIXELS (rect.width);
4425 switch (button->justification)
4427 case GTK_JUSTIFY_LEFT:
4428 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4429 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4431 case GTK_JUSTIFY_RIGHT:
4432 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4433 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4435 case GTK_JUSTIFY_CENTER:
4437 real_x = allocation.x + (allocation.width - text_width)/2;
4438 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4439 pango_layout_set_justify (layout, TRUE);
4441 pango_layout_set_alignment (layout, align);
4442 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4451 g_object_unref (layout);
4454 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4456 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4460 psppire_sheet_button_free (button);
4464 /* Draw the column title buttons FIRST through to LAST */
4466 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4470 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4472 if (!sheet->column_titles_visible) return;
4474 g_return_if_fail (first >= min_visible_column (sheet));
4475 g_return_if_fail (last <= max_visible_column (sheet));
4478 rect.height = sheet->column_title_area.height;
4479 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4480 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4481 + psppire_axis_unit_size (sheet->haxis, last);
4483 rect.x -= sheet->hadjustment->value;
4485 minimize_int (&rect.width, sheet->column_title_area.width);
4486 maximize_int (&rect.x, 0);
4488 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4490 for (col = first ; col <= last ; ++col)
4492 GdkRectangle allocation;
4493 gboolean is_sensitive = FALSE;
4495 PsppireSheetButton *
4496 button = psppire_sheet_model_get_column_button (sheet->model, col);
4498 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4500 allocation.x -= sheet->hadjustment->value;
4502 allocation.height = sheet->column_title_area.height;
4503 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4504 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4506 draw_button (sheet, sheet->column_title_window,
4507 button, is_sensitive, allocation);
4510 gdk_window_end_paint (sheet->column_title_window);
4515 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4519 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4521 if (!sheet->row_titles_visible) return;
4523 g_return_if_fail (first >= min_visible_row (sheet));
4524 g_return_if_fail (last <= max_visible_row (sheet));
4527 rect.width = sheet->row_title_area.width;
4528 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4529 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4530 + psppire_axis_unit_size (sheet->vaxis, last);
4532 rect.y -= sheet->vadjustment->value;
4534 minimize_int (&rect.height, sheet->row_title_area.height);
4535 maximize_int (&rect.y, 0);
4537 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4538 for (row = first; row <= last; ++row)
4540 GdkRectangle allocation;
4542 gboolean is_sensitive = FALSE;
4544 PsppireSheetButton *button =
4545 psppire_sheet_model_get_row_button (sheet->model, row);
4547 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4549 allocation.y -= sheet->vadjustment->value;
4551 allocation.width = sheet->row_title_area.width;
4552 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4553 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4555 draw_button (sheet, sheet->row_title_window,
4556 button, is_sensitive, allocation);
4559 gdk_window_end_paint (sheet->row_title_window);
4566 * vadjustment_value_changed
4567 * hadjustment_value_changed */
4571 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4574 (adj->value + adj->page_size)
4576 (adj->upper - adj->lower);
4578 const glong last_item = psppire_axis_unit_count (axis) - 1;
4580 if (isnan (position) || position < 0)
4584 psppire_axis_start_pixel (axis, last_item)
4586 psppire_axis_unit_size (axis, last_item)
4590 adj->page_size = page_size;
4593 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4595 if ( adj->value < adj->lower)
4596 adj->value = adj->lower;
4599 gtk_adjustment_changed (adj);
4604 adjust_scrollbars (PsppireSheet *sheet)
4608 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4611 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4613 if ( sheet->row_titles_visible)
4614 width -= sheet->row_title_area.width;
4616 if (sheet->column_titles_visible)
4617 height -= sheet->column_title_area.height;
4619 if (sheet->vadjustment)
4621 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4623 sheet->vadjustment->step_increment =
4625 psppire_axis_unit_size (sheet->vaxis, last_row);
4627 sheet->vadjustment->page_increment =
4629 sheet->column_title_area.height -
4630 psppire_axis_unit_size (sheet->vaxis, last_row);
4632 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4635 if (sheet->hadjustment)
4637 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4638 sheet->hadjustment->step_increment = 1;
4640 sheet->hadjustment->page_increment = width;
4642 sheet->hadjustment->upper =
4643 psppire_axis_start_pixel (sheet->haxis, last_col)
4645 psppire_axis_unit_size (sheet->haxis, last_col)
4648 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4652 /* Subtracts the region of WIDGET from REGION */
4654 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4657 GdkRectangle intersect;
4660 gdk_region_get_clipbox (region, &rect);
4661 gtk_widget_intersect (widget,
4665 region2 = gdk_region_rectangle (&intersect);
4666 gdk_region_subtract (region, region2);
4667 gdk_region_destroy (region2);
4671 vadjustment_value_changed (GtkAdjustment *adjustment,
4675 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4677 g_return_if_fail (adjustment != NULL);
4679 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4681 gtk_widget_hide (sheet->entry_widget);
4684 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4686 subtract_widget_region (region, sheet->button);
4687 gdk_window_begin_paint_region (sheet->sheet_window, region);
4689 draw_sheet_region (sheet, region);
4691 draw_row_title_buttons (sheet);
4692 psppire_sheet_draw_active_cell (sheet);
4694 gdk_window_end_paint (sheet->sheet_window);
4695 gdk_region_destroy (region);
4700 hadjustment_value_changed (GtkAdjustment *adjustment,
4704 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4706 g_return_if_fail (adjustment != NULL);
4708 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4710 gtk_widget_hide (sheet->entry_widget);
4714 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4716 subtract_widget_region (region, sheet->button);
4717 gdk_window_begin_paint_region (sheet->sheet_window, region);
4719 draw_sheet_region (sheet, region);
4721 draw_column_title_buttons (sheet);
4723 psppire_sheet_draw_active_cell (sheet);
4725 gdk_window_end_paint (sheet->sheet_window);
4727 gdk_region_destroy (region);
4731 /* COLUMN RESIZING */
4733 draw_xor_vline (PsppireSheet *sheet)
4736 gint xpos = sheet->x_drag;
4737 gdk_drawable_get_size (sheet->sheet_window,
4740 if (sheet->row_titles_visible)
4741 xpos += sheet->row_title_area.width;
4743 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4745 sheet->column_title_area.height,
4747 height + CELL_SPACING);
4752 draw_xor_hline (PsppireSheet *sheet)
4756 gint ypos = sheet->y_drag;
4758 gdk_drawable_get_size (sheet->sheet_window,
4762 if (sheet->column_titles_visible)
4763 ypos += sheet->column_title_area.height;
4765 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4766 sheet->row_title_area.width,
4768 width + CELL_SPACING,
4772 /* SELECTED RANGE */
4774 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
4777 GdkRectangle clip_area, area;
4780 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
4781 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
4782 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
4783 psppire_axis_unit_size (sheet->haxis, range.coli);
4784 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
4785 psppire_axis_unit_size (sheet->vaxis, range.rowi);
4787 clip_area.x = sheet->row_title_area.width;
4788 clip_area.y = sheet->column_title_area.height;
4790 gdk_drawable_get_size (sheet->sheet_window,
4791 &clip_area.width, &clip_area.height);
4793 if (!sheet->row_titles_visible) clip_area.x = 0;
4794 if (!sheet->column_titles_visible) clip_area.y = 0;
4798 area.width = area.width + area.x;
4801 if (area.width > clip_area.width) area.width = clip_area.width + 10;
4804 area.height = area.height + area.y;
4807 if (area.height > clip_area.height) area.height = clip_area.height + 10;
4811 clip_area.width += 3;
4812 clip_area.height += 3;
4814 gdk_gc_get_values (sheet->xor_gc, &values);
4816 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
4818 gdk_draw_rectangle (sheet->sheet_window,
4821 area.x + i, area.y + i,
4822 area.width - 2 * i, area.height - 2 * i);
4825 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4827 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
4832 set_column_width (PsppireSheet *sheet,
4836 g_return_if_fail (sheet != NULL);
4837 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4839 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
4845 psppire_axis_resize (sheet->haxis, column,
4846 width - sheet->cell_padding->left -
4847 sheet->cell_padding->right);
4849 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4851 draw_column_title_buttons (sheet);
4852 adjust_scrollbars (sheet);
4853 psppire_sheet_size_allocate_entry (sheet);
4854 redraw_range (sheet, NULL);
4859 set_row_height (PsppireSheet *sheet,
4863 g_return_if_fail (sheet != NULL);
4864 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4866 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
4872 psppire_axis_resize (sheet->vaxis, row,
4873 height - sheet->cell_padding->top -
4874 sheet->cell_padding->bottom);
4876 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
4878 draw_row_title_buttons (sheet);
4879 adjust_scrollbars (sheet);
4880 psppire_sheet_size_allocate_entry (sheet);
4881 redraw_range (sheet, NULL);
4886 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
4887 PsppireSheetCellAttr *attr)
4890 const GtkJustification *j ;
4891 GdkColormap *colormap;
4893 g_return_val_if_fail (sheet != NULL, FALSE);
4894 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
4896 if (row < 0 || col < 0) return FALSE;
4898 attr->foreground = GTK_WIDGET (sheet)->style->black;
4899 attr->background = sheet->color[BG_COLOR];
4901 attr->border.width = 0;
4902 attr->border.line_style = GDK_LINE_SOLID;
4903 attr->border.cap_style = GDK_CAP_NOT_LAST;
4904 attr->border.join_style = GDK_JOIN_MITER;
4905 attr->border.mask = 0;
4906 attr->border.color = GTK_WIDGET (sheet)->style->black;
4908 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
4909 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
4912 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
4913 attr->foreground = *fg;
4916 bg = psppire_sheet_model_get_background (sheet->model, row, col);
4919 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
4920 attr->background = *bg;
4923 attr->justification =
4924 psppire_sheet_model_get_column_justification (sheet->model, col);
4926 j = psppire_sheet_model_get_justification (sheet->model, row, col);
4928 attr->justification = *j;
4934 psppire_sheet_button_size_request (PsppireSheet *sheet,
4935 const PsppireSheetButton *button,
4936 GtkRequisition *button_requisition)
4938 GtkRequisition requisition;
4939 GtkRequisition label_requisition;
4941 label_requisition.height = DEFAULT_ROW_HEIGHT;
4942 label_requisition.width = COLUMN_MIN_WIDTH;
4944 requisition.height = DEFAULT_ROW_HEIGHT;
4945 requisition.width = COLUMN_MIN_WIDTH;
4948 *button_requisition = requisition;
4949 button_requisition->width = MAX (requisition.width, label_requisition.width);
4950 button_requisition->height = MAX (requisition.height, label_requisition.height);
4955 psppire_sheet_forall (GtkContainer *container,
4956 gboolean include_internals,
4957 GtkCallback callback,
4958 gpointer callback_data)
4960 PsppireSheet *sheet = PSPPIRE_SHEET (container);
4962 g_return_if_fail (callback != NULL);
4964 if (sheet->button && sheet->button->parent)
4965 (* callback) (sheet->button, callback_data);
4967 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
4968 (* callback) (sheet->entry_widget, callback_data);
4973 psppire_sheet_get_model (const PsppireSheet *sheet)
4975 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4977 return sheet->model;
4981 PsppireSheetButton *
4982 psppire_sheet_button_new (void)
4984 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
4986 button->state = GTK_STATE_NORMAL;
4987 button->label = NULL;
4988 button->label_visible = TRUE;
4989 button->justification = GTK_JUSTIFY_FILL;
4990 button->overstruck = FALSE;
4997 psppire_sheet_button_free (PsppireSheetButton *button)
4999 if (!button) return ;
5001 g_free (button->label);
5006 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5008 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5010 if ( NULL == celltext)
5013 g_string_append (string, celltext);
5019 range_to_text (const PsppireSheet *sheet)
5024 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5027 string = g_string_sized_new (80);
5029 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5031 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5033 append_cell_text (string, sheet, r, c);
5034 g_string_append (string, "\t");
5036 append_cell_text (string, sheet, r, c);
5037 if ( r < sheet->range.rowi)
5038 g_string_append (string, "\n");
5045 range_to_html (const PsppireSheet *sheet)
5050 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5053 string = g_string_sized_new (480);
5055 g_string_append (string, "<html>\n");
5056 g_string_append (string, "<body>\n");
5057 g_string_append (string, "<table>\n");
5058 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5060 g_string_append (string, "<tr>\n");
5061 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5063 g_string_append (string, "<td>");
5064 append_cell_text (string, sheet, r, c);
5065 g_string_append (string, "</td>\n");
5067 g_string_append (string, "</tr>\n");
5069 g_string_append (string, "</table>\n");
5070 g_string_append (string, "</body>\n");
5071 g_string_append (string, "</html>\n");
5083 primary_get_cb (GtkClipboard *clipboard,
5084 GtkSelectionData *selection_data,
5088 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5089 GString *string = NULL;
5093 case SELECT_FMT_TEXT:
5094 string = range_to_text (sheet);
5096 case SELECT_FMT_HTML:
5097 string = range_to_html (sheet);
5100 g_assert_not_reached ();
5103 gtk_selection_data_set (selection_data, selection_data->target,
5105 (const guchar *) string->str, string->len);
5106 g_string_free (string, TRUE);
5110 primary_clear_cb (GtkClipboard *clipboard,
5113 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5114 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5117 psppire_sheet_real_unselect_range (sheet, NULL);
5121 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5123 static const GtkTargetEntry targets[] = {
5124 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5125 { "STRING", 0, SELECT_FMT_TEXT },
5126 { "TEXT", 0, SELECT_FMT_TEXT },
5127 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5128 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5129 { "text/plain", 0, SELECT_FMT_TEXT },
5130 { "text/html", 0, SELECT_FMT_HTML }
5133 GtkClipboard *clipboard;
5135 if (!GTK_WIDGET_REALIZED (sheet))
5138 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5139 GDK_SELECTION_PRIMARY);
5141 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5143 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5144 G_N_ELEMENTS (targets),
5145 primary_get_cb, primary_clear_cb,
5147 primary_clear_cb (clipboard, sheet);
5151 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5152 gtk_clipboard_clear (clipboard);