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;
1156 sheet->selection_cell.row = 0;
1157 sheet->selection_cell.col = 0;
1159 sheet->range.row0 = 0;
1160 sheet->range.rowi = 0;
1161 sheet->range.col0 = 0;
1162 sheet->range.coli = 0;
1164 sheet->sheet_window = NULL;
1165 sheet->entry_widget = NULL;
1166 sheet->button = NULL;
1168 sheet->hadjustment = NULL;
1169 sheet->vadjustment = NULL;
1171 sheet->cursor_drag = NULL;
1173 sheet->xor_gc = NULL;
1174 sheet->fg_gc = NULL;
1175 sheet->bg_gc = NULL;
1178 sheet->show_grid = TRUE;
1180 sheet->motion_timer = 0;
1182 sheet->row_titles_visible = TRUE;
1183 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1185 sheet->column_titles_visible = TRUE;
1188 /* create sheet entry */
1189 sheet->entry_type = GTK_TYPE_ENTRY;
1190 create_sheet_entry (sheet);
1192 /* create global selection button */
1193 create_global_button (sheet);
1197 /* Cause RANGE to be redrawn. If RANGE is null, then the
1198 entire visible range will be redrawn.
1201 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1205 if ( ! GTK_WIDGET_REALIZED (sheet))
1208 if ( NULL != range )
1209 rectangle_from_range (sheet, range, &rect);
1212 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1213 gdk_region_get_clipbox (r, &rect);
1215 if ( sheet->column_titles_visible)
1217 rect.y += sheet->column_title_area.height;
1218 rect.height -= sheet->column_title_area.height;
1221 if ( sheet->row_titles_visible)
1223 rect.x += sheet->row_title_area.width;
1224 rect.width -= sheet->row_title_area.width;
1228 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1232 /* Callback which occurs whenever columns are inserted / deleted in the model */
1234 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1238 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1240 PsppireSheetRange range;
1241 gint model_columns = psppire_sheet_model_get_column_count (model);
1244 /* Need to update all the columns starting from the first column and onwards.
1245 * Previous column are unchanged, so don't need to be updated.
1247 range.col0 = first_column;
1249 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1250 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1252 adjust_scrollbars (sheet);
1254 if (sheet->active_cell.col >= model_columns)
1255 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1257 draw_column_title_buttons_range (sheet,
1258 first_column, max_visible_column (sheet));
1261 redraw_range (sheet, &range);
1267 /* Callback which occurs whenever rows are inserted / deleted in the model */
1269 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1270 gint n_rows, gpointer data)
1272 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1274 PsppireSheetRange range;
1276 gint model_rows = psppire_sheet_model_get_row_count (model);
1278 /* Need to update all the rows starting from the first row and onwards.
1279 * Previous rows are unchanged, so don't need to be updated.
1281 range.row0 = first_row;
1283 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1284 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1286 adjust_scrollbars (sheet);
1288 if (sheet->active_cell.row >= model_rows)
1289 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1291 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1293 redraw_range (sheet, &range);
1297 If row0 or rowi are negative, then all rows will be updated.
1298 If col0 or coli are negative, then all columns will be updated.
1301 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1302 gint rowi, gint coli, gpointer data)
1304 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1306 PsppireSheetRange range;
1313 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1316 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1318 redraw_range (sheet, NULL);
1319 adjust_scrollbars (sheet);
1321 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1322 max_visible_row (sheet));
1324 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1325 max_visible_column (sheet));
1329 else if ( row0 < 0 || rowi < 0 )
1331 range.row0 = min_visible_row (sheet);
1332 range.rowi = max_visible_row (sheet);
1334 else if ( col0 < 0 || coli < 0 )
1336 range.col0 = min_visible_column (sheet);
1337 range.coli = max_visible_column (sheet);
1340 redraw_range (sheet, &range);
1345 * psppire_sheet_new:
1346 * @rows: initial number of rows
1347 * @columns: initial number of columns
1348 * @title: sheet title
1349 * @model: the model to use for the sheet data
1351 * Creates a new sheet widget with the given number of rows and columns.
1353 * Returns: the new sheet widget
1356 psppire_sheet_new (PsppireSheetModel *model)
1358 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1366 * psppire_sheet_set_model
1367 * @sheet: the sheet to set the model for
1368 * @model: the model to use for the sheet data
1370 * Sets the model for a PsppireSheet
1374 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1376 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1378 if (sheet->model ) g_object_unref (sheet->model);
1380 sheet->model = model;
1384 g_object_ref (model);
1386 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1387 G_CALLBACK (range_update_callback),
1390 g_signal_connect (model, "rows_inserted",
1391 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1393 g_signal_connect (model, "rows_deleted",
1394 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1396 g_signal_connect (model, "columns_inserted",
1397 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1399 g_signal_connect (model, "columns_deleted",
1400 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1406 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1410 g_return_if_fail (sheet != NULL);
1411 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1413 state = sheet->select_status;
1415 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1416 psppire_sheet_hide_entry_widget (sheet);
1418 sheet->entry_type = entry_type;
1420 create_sheet_entry (sheet);
1422 if (state == PSPPIRE_SHEET_NORMAL)
1424 psppire_sheet_show_entry_widget (sheet);
1430 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1432 g_return_if_fail (sheet != NULL);
1433 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1435 if (show == sheet->show_grid) return;
1437 sheet->show_grid = show;
1439 redraw_range (sheet, NULL);
1443 psppire_sheet_grid_visible (PsppireSheet *sheet)
1445 g_return_val_if_fail (sheet != NULL, 0);
1446 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1448 return sheet->show_grid;
1452 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1454 g_return_val_if_fail (sheet != NULL, 0);
1455 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1457 return psppire_axis_unit_count (sheet->haxis);
1460 static void set_column_width (PsppireSheet *sheet,
1466 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1468 if (sheet->column_titles_visible) return;
1470 sheet->column_titles_visible = TRUE;
1472 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1475 gdk_window_show (sheet->column_title_window);
1476 gdk_window_move_resize (sheet->column_title_window,
1477 sheet->column_title_area.x,
1478 sheet->column_title_area.y,
1479 sheet->column_title_area.width,
1480 sheet->column_title_area.height);
1482 adjust_scrollbars (sheet);
1484 if (sheet->vadjustment)
1485 g_signal_emit_by_name (sheet->vadjustment,
1488 size_allocate_global_button (sheet);
1490 if ( sheet->row_titles_visible)
1491 gtk_widget_show (sheet->button);
1496 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1498 if (sheet->row_titles_visible) return;
1500 sheet->row_titles_visible = TRUE;
1503 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1505 gdk_window_show (sheet->row_title_window);
1506 gdk_window_move_resize (sheet->row_title_window,
1507 sheet->row_title_area.x,
1508 sheet->row_title_area.y,
1509 sheet->row_title_area.width,
1510 sheet->row_title_area.height);
1512 adjust_scrollbars (sheet);
1515 if (sheet->hadjustment)
1516 g_signal_emit_by_name (sheet->hadjustment,
1519 size_allocate_global_button (sheet);
1521 if ( sheet->column_titles_visible)
1522 gtk_widget_show (sheet->button);
1526 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1528 if (!sheet->column_titles_visible) return;
1530 sheet->column_titles_visible = FALSE;
1532 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1534 if (sheet->column_title_window)
1535 gdk_window_hide (sheet->column_title_window);
1537 gtk_widget_hide (sheet->button);
1539 adjust_scrollbars (sheet);
1542 if (sheet->vadjustment)
1543 g_signal_emit_by_name (sheet->vadjustment,
1548 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1550 if (!sheet->row_titles_visible) return;
1552 sheet->row_titles_visible = FALSE;
1554 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1556 if (sheet->row_title_window)
1557 gdk_window_hide (sheet->row_title_window);
1559 gtk_widget_hide (sheet->button);
1561 adjust_scrollbars (sheet);
1564 if (sheet->hadjustment)
1565 g_signal_emit_by_name (sheet->hadjustment,
1570 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1571 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1572 at the {top,left} of the sheet. If it's 1, then it'll
1573 be placed at the {bottom,right}.
1574 ROW or COL may be -1, in which case scrolling in that dimension
1578 psppire_sheet_moveto (PsppireSheet *sheet,
1586 g_return_if_fail (row_align >= 0);
1587 g_return_if_fail (col_align >= 0);
1589 g_return_if_fail (row_align <= 1);
1590 g_return_if_fail (col_align <= 1);
1592 g_return_if_fail (col <
1593 psppire_axis_unit_count (sheet->haxis));
1594 g_return_if_fail (row <
1595 psppire_axis_unit_count (sheet->vaxis));
1597 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1602 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1604 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1610 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1612 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1618 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
1620 g_return_if_fail (sheet != NULL);
1621 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1623 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
1626 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
1627 psppire_sheet_real_unselect_range (sheet, NULL);
1629 sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
1630 sheet->range.row0 = row;
1631 sheet->range.col0 = 0;
1632 sheet->range.rowi = row;
1633 sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1635 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
1636 psppire_sheet_real_select_range (sheet, NULL);
1641 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
1643 g_return_if_fail (sheet != NULL);
1644 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1646 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
1649 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
1650 psppire_sheet_real_unselect_range (sheet, NULL);
1652 sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
1653 sheet->range.row0 = 0;
1654 sheet->range.col0 = column;
1655 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1656 sheet->range.coli = column;
1658 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
1659 psppire_sheet_real_select_range (sheet, NULL);
1666 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1667 const PsppireSheetRange *range)
1669 g_return_val_if_fail (sheet != NULL, FALSE);
1671 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1674 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1677 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1680 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1683 if (range->rowi < min_visible_row (sheet))
1686 if (range->row0 > max_visible_row (sheet))
1689 if (range->coli < min_visible_column (sheet))
1692 if (range->col0 > max_visible_column (sheet))
1699 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1700 gint row, gint column)
1702 PsppireSheetRange range;
1705 range.col0 = column;
1707 range.coli = column;
1709 return psppire_sheet_range_isvisible (sheet, &range);
1713 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1715 g_return_if_fail (sheet != NULL);
1716 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1717 g_return_if_fail (range != NULL);
1719 range->row0 = min_visible_row (sheet);
1720 range->col0 = min_visible_column (sheet);
1721 range->rowi = max_visible_row (sheet);
1722 range->coli = max_visible_column (sheet);
1727 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1728 GtkAdjustment *hadjustment,
1729 GtkAdjustment *vadjustment)
1731 if ( sheet->vadjustment != vadjustment )
1733 if (sheet->vadjustment)
1734 g_object_unref (sheet->vadjustment);
1735 sheet->vadjustment = vadjustment;
1739 g_object_ref (vadjustment);
1741 g_signal_connect (sheet->vadjustment, "value_changed",
1742 G_CALLBACK (vadjustment_value_changed),
1747 if ( sheet->hadjustment != hadjustment )
1749 if (sheet->hadjustment)
1750 g_object_unref (sheet->hadjustment);
1752 sheet->hadjustment = hadjustment;
1756 g_object_ref (hadjustment);
1758 g_signal_connect (sheet->hadjustment, "value_changed",
1759 G_CALLBACK (hadjustment_value_changed),
1767 psppire_sheet_finalize (GObject *object)
1769 PsppireSheet *sheet;
1771 g_return_if_fail (object != NULL);
1772 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1774 sheet = PSPPIRE_SHEET (object);
1776 if (G_OBJECT_CLASS (parent_class)->finalize)
1777 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1781 psppire_sheet_dispose (GObject *object)
1783 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1785 g_return_if_fail (object != NULL);
1786 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1788 if ( sheet->dispose_has_run )
1791 sheet->dispose_has_run = TRUE;
1793 if ( sheet->cell_padding)
1794 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1796 if (sheet->model) g_object_unref (sheet->model);
1797 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1798 if (sheet->haxis) g_object_unref (sheet->haxis);
1800 g_object_unref (sheet->button);
1801 sheet->button = NULL;
1803 /* unref adjustments */
1804 if (sheet->hadjustment)
1806 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1807 G_SIGNAL_MATCH_DATA,
1811 g_object_unref (sheet->hadjustment);
1812 sheet->hadjustment = NULL;
1815 if (sheet->vadjustment)
1817 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1818 G_SIGNAL_MATCH_DATA,
1822 g_object_unref (sheet->vadjustment);
1824 sheet->vadjustment = NULL;
1827 if (G_OBJECT_CLASS (parent_class)->dispose)
1828 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1832 psppire_sheet_style_set (GtkWidget *widget,
1833 GtkStyle *previous_style)
1835 PsppireSheet *sheet;
1837 g_return_if_fail (widget != NULL);
1838 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1840 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1841 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1843 sheet = PSPPIRE_SHEET (widget);
1845 if (GTK_WIDGET_REALIZED (widget))
1847 gtk_style_set_background (widget->style, widget->window, widget->state);
1850 set_entry_widget_font (sheet);
1855 psppire_sheet_realize (GtkWidget *widget)
1857 PsppireSheet *sheet;
1858 GdkWindowAttr attributes;
1859 const gint attributes_mask =
1860 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1863 GdkColormap *colormap;
1864 GdkDisplay *display;
1866 g_return_if_fail (widget != NULL);
1867 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1869 sheet = PSPPIRE_SHEET (widget);
1871 colormap = gtk_widget_get_colormap (widget);
1872 display = gtk_widget_get_display (widget);
1874 attributes.window_type = GDK_WINDOW_CHILD;
1875 attributes.x = widget->allocation.x;
1876 attributes.y = widget->allocation.y;
1877 attributes.width = widget->allocation.width;
1878 attributes.height = widget->allocation.height;
1879 attributes.wclass = GDK_INPUT_OUTPUT;
1881 attributes.visual = gtk_widget_get_visual (widget);
1882 attributes.colormap = colormap;
1884 attributes.event_mask = gtk_widget_get_events (widget);
1885 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1886 GDK_BUTTON_PRESS_MASK |
1887 GDK_BUTTON_RELEASE_MASK |
1888 GDK_KEY_PRESS_MASK |
1889 GDK_ENTER_NOTIFY_MASK |
1890 GDK_LEAVE_NOTIFY_MASK |
1891 GDK_POINTER_MOTION_MASK |
1892 GDK_POINTER_MOTION_HINT_MASK);
1894 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1897 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1899 gdk_window_set_user_data (widget->window, sheet);
1901 widget->style = gtk_style_attach (widget->style, widget->window);
1903 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1905 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1906 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1908 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1909 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1914 attributes.width = sheet->column_title_area.width;
1915 attributes.height = sheet->column_title_area.height;
1918 /* column - title window */
1919 sheet->column_title_window =
1920 gdk_window_new (widget->window, &attributes, attributes_mask);
1921 gdk_window_set_user_data (sheet->column_title_window, sheet);
1922 gtk_style_set_background (widget->style, sheet->column_title_window,
1928 attributes.width = sheet->row_title_area.width;
1929 attributes.height = sheet->row_title_area.height;
1931 /* row - title window */
1932 sheet->row_title_window = gdk_window_new (widget->window,
1933 &attributes, attributes_mask);
1934 gdk_window_set_user_data (sheet->row_title_window, sheet);
1935 gtk_style_set_background (widget->style, sheet->row_title_window,
1938 /* sheet - window */
1939 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1944 sheet->sheet_window = gdk_window_new (widget->window,
1945 &attributes, attributes_mask);
1946 gdk_window_set_user_data (sheet->sheet_window, sheet);
1948 gdk_cursor_unref (attributes.cursor);
1950 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1951 gdk_window_show (sheet->sheet_window);
1954 sheet->fg_gc = gdk_gc_new (widget->window);
1955 sheet->bg_gc = gdk_gc_new (widget->window);
1957 values.foreground = widget->style->white;
1958 values.function = GDK_INVERT;
1959 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1960 values.line_width = MAX (sheet->cell_padding->left,
1961 MAX (sheet->cell_padding->right,
1962 MAX (sheet->cell_padding->top,
1963 sheet->cell_padding->bottom)));
1965 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1973 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1974 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1976 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1977 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1979 sheet->button->style = gtk_style_attach (sheet->button->style,
1980 sheet->sheet_window);
1983 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1985 if (sheet->column_titles_visible)
1986 gdk_window_show (sheet->column_title_window);
1987 if (sheet->row_titles_visible)
1988 gdk_window_show (sheet->row_title_window);
1990 sheet->hover_window = create_hover_window ();
1992 draw_row_title_buttons (sheet);
1993 draw_column_title_buttons (sheet);
1995 psppire_sheet_update_primary_selection (sheet);
1998 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
2002 create_global_button (PsppireSheet *sheet)
2004 sheet->button = gtk_button_new_with_label (" ");
2006 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
2008 g_object_ref_sink (sheet->button);
2010 g_signal_connect (sheet->button,
2012 G_CALLBACK (global_button_clicked),
2017 size_allocate_global_button (PsppireSheet *sheet)
2019 GtkAllocation allocation;
2021 if (!sheet->column_titles_visible) return;
2022 if (!sheet->row_titles_visible) return;
2024 gtk_widget_size_request (sheet->button, NULL);
2028 allocation.width = sheet->row_title_area.width;
2029 allocation.height = sheet->column_title_area.height;
2031 gtk_widget_size_allocate (sheet->button, &allocation);
2035 global_button_clicked (GtkWidget *widget, gpointer data)
2037 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
2042 psppire_sheet_unrealize (GtkWidget *widget)
2044 PsppireSheet *sheet;
2046 g_return_if_fail (widget != NULL);
2047 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2049 sheet = PSPPIRE_SHEET (widget);
2051 gdk_cursor_unref (sheet->cursor_drag);
2052 sheet->cursor_drag = NULL;
2054 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2055 sheet->color, n_COLORS);
2057 g_object_unref (sheet->xor_gc);
2058 g_object_unref (sheet->fg_gc);
2059 g_object_unref (sheet->bg_gc);
2061 destroy_hover_window (sheet->hover_window);
2063 gdk_window_destroy (sheet->sheet_window);
2064 gdk_window_destroy (sheet->column_title_window);
2065 gdk_window_destroy (sheet->row_title_window);
2067 gtk_widget_unparent (sheet->entry_widget);
2068 if (sheet->button != NULL)
2069 gtk_widget_unparent (sheet->button);
2071 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2072 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2076 psppire_sheet_map (GtkWidget *widget)
2078 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2080 g_return_if_fail (widget != NULL);
2081 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2083 if (!GTK_WIDGET_MAPPED (widget))
2085 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2087 gdk_window_show (widget->window);
2088 gdk_window_show (sheet->sheet_window);
2090 if (sheet->column_titles_visible)
2092 draw_column_title_buttons (sheet);
2093 gdk_window_show (sheet->column_title_window);
2095 if (sheet->row_titles_visible)
2097 draw_row_title_buttons (sheet);
2098 gdk_window_show (sheet->row_title_window);
2101 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2102 && sheet->active_cell.row >= 0
2103 && sheet->active_cell.col >= 0 )
2105 gtk_widget_show (sheet->entry_widget);
2106 gtk_widget_map (sheet->entry_widget);
2109 if (!GTK_WIDGET_MAPPED (sheet->button))
2111 gtk_widget_show (sheet->button);
2112 gtk_widget_map (sheet->button);
2115 redraw_range (sheet, NULL);
2116 change_active_cell (sheet,
2117 sheet->active_cell.row,
2118 sheet->active_cell.col);
2123 psppire_sheet_unmap (GtkWidget *widget)
2125 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2127 if (!GTK_WIDGET_MAPPED (widget))
2130 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2132 gdk_window_hide (sheet->sheet_window);
2133 if (sheet->column_titles_visible)
2134 gdk_window_hide (sheet->column_title_window);
2135 if (sheet->row_titles_visible)
2136 gdk_window_hide (sheet->row_title_window);
2137 gdk_window_hide (widget->window);
2139 gtk_widget_unmap (sheet->entry_widget);
2140 gtk_widget_unmap (sheet->button);
2141 gtk_widget_unmap (sheet->hover_window->window);
2144 /* get cell attributes of the given cell */
2145 /* TRUE means that the cell is currently allocated */
2146 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2148 PsppireSheetCellAttr *attributes);
2153 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2155 PangoLayout *layout;
2156 PangoRectangle text;
2157 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2162 PsppireSheetCellAttr attributes;
2165 g_return_if_fail (sheet != NULL);
2167 /* bail now if we aren't yet drawable */
2168 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2171 row >= psppire_axis_unit_count (sheet->vaxis))
2175 col >= psppire_axis_unit_count (sheet->haxis))
2178 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2180 /* select GC for background rectangle */
2181 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2182 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2184 rectangle_from_cell (sheet, row, col, &area);
2186 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2188 if (sheet->show_grid)
2190 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2192 gdk_draw_rectangle (sheet->sheet_window,
2196 area.width, area.height);
2200 label = psppire_sheet_cell_get_text (sheet, row, col);
2205 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2206 dispose_string (sheet, label);
2209 pango_layout_set_font_description (layout, font_desc);
2211 pango_layout_get_pixel_extents (layout, NULL, &text);
2213 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2215 font_height = pango_font_description_get_size (font_desc);
2216 if ( !pango_font_description_get_size_is_absolute (font_desc))
2217 font_height /= PANGO_SCALE;
2220 if ( sheet->cell_padding )
2222 area.x += sheet->cell_padding->left;
2223 area.width -= sheet->cell_padding->right
2224 + sheet->cell_padding->left;
2226 area.y += sheet->cell_padding->top;
2227 area.height -= sheet->cell_padding->bottom
2229 sheet->cell_padding->top;
2232 /* Centre the text vertically */
2233 area.y += (area.height - font_height) / 2.0;
2235 switch (attributes.justification)
2237 case GTK_JUSTIFY_RIGHT:
2238 area.x += area.width - text.width;
2240 case GTK_JUSTIFY_CENTER:
2241 area.x += (area.width - text.width) / 2.0;
2243 case GTK_JUSTIFY_LEFT:
2247 g_critical ("Unhandled justification %d in column %d\n",
2248 attributes.justification, col);
2252 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2257 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2258 g_object_unref (layout);
2263 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2265 PsppireSheetRange range;
2270 PsppireSheetRange drawing_range;
2272 gdk_region_get_clipbox (region, &area);
2274 y = area.y + sheet->vadjustment->value;
2275 x = area.x + sheet->hadjustment->value;
2277 if ( sheet->column_titles_visible)
2278 y -= sheet->column_title_area.height;
2280 if ( sheet->row_titles_visible)
2281 x -= sheet->row_title_area.width;
2283 maximize_int (&x, 0);
2284 maximize_int (&y, 0);
2286 range.row0 = row_from_ypixel (sheet, y);
2287 range.rowi = row_from_ypixel (sheet, y + area.height);
2289 range.col0 = column_from_xpixel (sheet, x);
2290 range.coli = column_from_xpixel (sheet, x + area.width);
2292 g_return_if_fail (sheet != NULL);
2293 g_return_if_fail (PSPPIRE_SHEET (sheet));
2295 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2296 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2297 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2300 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2301 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2302 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2303 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2305 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2306 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2308 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2310 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2311 psppire_sheet_cell_draw (sheet, i, j);
2314 if (sheet->select_status == GTK_STATE_NORMAL &&
2315 sheet->active_cell.row >= drawing_range.row0 &&
2316 sheet->active_cell.row <= drawing_range.rowi &&
2317 sheet->active_cell.col >= drawing_range.col0 &&
2318 sheet->active_cell.col <= drawing_range.coli)
2319 psppire_sheet_show_entry_widget (sheet);
2325 safe_strcmp (const gchar *s1, const gchar *s2)
2327 if ( !s1 && !s2) return 0;
2328 if ( !s1) return -1;
2329 if ( !s2) return +1;
2330 return strcmp (s1, s2);
2334 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2335 GtkJustification justification,
2338 PsppireSheetModel *model ;
2341 g_return_if_fail (sheet != NULL);
2342 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2344 if (col >= psppire_axis_unit_count (sheet->haxis)
2345 || row >= psppire_axis_unit_count (sheet->vaxis))
2348 if (col < 0 || row < 0) return;
2350 model = psppire_sheet_get_model (sheet);
2352 old_text = psppire_sheet_model_get_string (model, row, col);
2354 if (0 != safe_strcmp (old_text, text))
2356 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2357 psppire_sheet_model_set_string (model, text, row, col);
2358 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2361 if ( psppire_sheet_model_free_strings (model))
2367 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2369 PsppireSheetRange range;
2371 g_return_if_fail (sheet != NULL);
2372 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2373 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2374 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2376 if (column < 0 || row < 0) return;
2380 range.col0 = min_visible_column (sheet);
2381 range.coli = max_visible_column (sheet);
2383 psppire_sheet_real_cell_clear (sheet, row, column);
2385 redraw_range (sheet, &range);
2389 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2391 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2393 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2395 if (old_text && strlen (old_text) > 0 )
2397 psppire_sheet_model_datum_clear (model, row, column);
2400 dispose_string (sheet, old_text);
2404 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2406 PsppireSheetModel *model;
2407 g_return_val_if_fail (sheet != NULL, NULL);
2408 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2410 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2412 if (col < 0 || row < 0) return NULL;
2414 model = psppire_sheet_get_model (sheet);
2419 return psppire_sheet_model_get_string (model, row, col);
2424 psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
2427 PsppireSheetRange *range;
2429 g_return_val_if_fail (sheet != NULL, 0);
2430 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2431 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
2432 if (col < 0 || row < 0) return 0;
2434 state = sheet->select_status;
2435 range = &sheet->range;
2439 case PSPPIRE_SHEET_NORMAL:
2440 return GTK_STATE_NORMAL;
2442 case PSPPIRE_SHEET_ROW_SELECTED:
2443 if (row >= range->row0 && row <= range->rowi)
2444 return GTK_STATE_SELECTED;
2446 case PSPPIRE_SHEET_COLUMN_SELECTED:
2447 if (col >= range->col0 && col <= range->coli)
2448 return GTK_STATE_SELECTED;
2450 case PSPPIRE_SHEET_RANGE_SELECTED:
2451 if (row >= range->row0 && row <= range->rowi && \
2452 col >= range->col0 && col <= range->coli)
2453 return GTK_STATE_SELECTED;
2456 return GTK_STATE_NORMAL;
2459 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2460 If the function returns FALSE, then the results will be unreliable.
2463 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2471 *column = -G_MAXINT;
2473 g_return_val_if_fail (sheet != NULL, 0);
2474 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2476 /* bounds checking, return false if the user clicked
2484 if ( sheet->column_titles_visible)
2485 y -= sheet->column_title_area.height;
2487 y += sheet->vadjustment->value;
2489 if ( y < 0 && sheet->column_titles_visible)
2495 trow = row_from_ypixel (sheet, y);
2496 if (trow > psppire_axis_unit_count (sheet->vaxis))
2502 if ( sheet->row_titles_visible)
2503 x -= sheet->row_title_area.width;
2505 x += sheet->hadjustment->value;
2507 if ( x < 0 && sheet->row_titles_visible)
2513 tcol = column_from_xpixel (sheet, x);
2514 if (tcol > psppire_axis_unit_count (sheet->haxis))
2524 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2529 g_return_val_if_fail (sheet != NULL, 0);
2530 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2532 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2535 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2536 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2538 area->width= (column == -1) ? sheet->row_title_area.width
2539 : psppire_axis_unit_size (sheet->haxis, column);
2541 area->height= (row == -1) ? sheet->column_title_area.height
2542 : psppire_axis_unit_size (sheet->vaxis, row);
2548 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2550 g_return_if_fail (sheet != NULL);
2551 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2553 if (row < -1 || col < -1)
2556 if (row >= psppire_axis_unit_count (sheet->vaxis)
2558 col >= psppire_axis_unit_count (sheet->haxis))
2561 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2564 if ( row == -1 || col == -1)
2566 psppire_sheet_hide_entry_widget (sheet);
2570 change_active_cell (sheet, row, col);
2574 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2576 g_return_if_fail (sheet != NULL);
2577 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2579 if ( row ) *row = sheet->active_cell.row;
2580 if (column) *column = sheet->active_cell.col;
2584 entry_load_text (PsppireSheet *sheet)
2588 GtkJustification justification;
2589 PsppireSheetCellAttr attributes;
2591 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2592 if (sheet->select_status != GTK_STATE_NORMAL) return;
2594 row = sheet->active_cell.row;
2595 col = sheet->active_cell.col;
2597 if (row < 0 || col < 0) return;
2599 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2601 if (text && strlen (text) > 0)
2603 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2604 justification = attributes.justification;
2605 psppire_sheet_set_cell (sheet, row, col, justification, text);
2611 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2613 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2616 if (sheet->active_cell.row < 0 ||
2617 sheet->active_cell.col < 0) return;
2619 gtk_widget_hide (sheet->entry_widget);
2620 gtk_widget_unmap (sheet->entry_widget);
2622 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2626 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2628 gint old_row, old_col;
2630 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2632 if (row < 0 || col < 0)
2635 if ( row > psppire_axis_unit_count (sheet->vaxis)
2636 || col > psppire_axis_unit_count (sheet->haxis))
2639 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2641 sheet->select_status = PSPPIRE_SHEET_NORMAL;
2642 psppire_sheet_real_unselect_range (sheet, NULL);
2645 old_row = sheet->active_cell.row;
2646 old_col = sheet->active_cell.col;
2648 entry_load_text (sheet);
2650 /* Erase the old cell border */
2651 psppire_sheet_draw_active_cell (sheet);
2653 sheet->active_cell.row = row;
2654 sheet->active_cell.col = col;
2655 sheet->selection_cell.row = row;
2656 sheet->selection_cell.col = col;
2658 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2660 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2662 psppire_sheet_draw_active_cell (sheet);
2663 psppire_sheet_show_entry_widget (sheet);
2665 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2667 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2668 row, col, old_row, old_col);
2673 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2675 GtkEntry *sheet_entry;
2676 PsppireSheetCellAttr attributes;
2680 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2682 row = sheet->active_cell.row;
2683 col = sheet->active_cell.col;
2685 /* Don't show the active cell, if there is no active cell: */
2686 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2689 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2690 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2691 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2693 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2695 sheet_entry = psppire_sheet_get_entry (sheet);
2697 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2699 if (GTK_IS_ENTRY (sheet_entry))
2701 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2702 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2705 text = g_strdup ("");
2707 if (strcmp (old_text, text) != 0)
2708 gtk_entry_set_text (sheet_entry, text);
2710 dispose_string (sheet, text);
2713 switch (attributes.justification)
2715 case GTK_JUSTIFY_RIGHT:
2716 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2718 case GTK_JUSTIFY_CENTER:
2719 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2721 case GTK_JUSTIFY_LEFT:
2723 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2729 psppire_sheet_size_allocate_entry (sheet);
2731 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2732 psppire_sheet_model_is_editable (sheet->model,
2734 gtk_widget_map (sheet->entry_widget);
2738 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2741 PsppireSheetRange range;
2743 row = sheet->active_cell.row;
2744 col = sheet->active_cell.col;
2746 if (row < 0 || col < 0) return FALSE;
2748 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2751 range.col0 = range.coli = col;
2752 range.row0 = range.rowi = row;
2754 psppire_sheet_draw_border (sheet, range);
2762 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
2766 rectangle_from_range (sheet, &new_range, &area);
2771 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
2773 area.x += sheet->cell_padding->left / 2;
2774 area.y += sheet->cell_padding->top / 2;
2775 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
2776 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
2778 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
2785 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
2790 psppire_sheet_real_select_range (PsppireSheet *sheet,
2791 const PsppireSheetRange *range)
2795 g_return_if_fail (sheet != NULL);
2797 if (range == NULL) range = &sheet->range;
2799 memcpy (&sheet->range, range, sizeof (*range));
2801 if (range->row0 < 0 || range->rowi < 0) return;
2802 if (range->col0 < 0 || range->coli < 0) return;
2804 state = sheet->select_status;
2806 psppire_sheet_update_primary_selection (sheet);
2808 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
2813 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
2815 g_return_if_fail (sheet != NULL);
2816 *range = sheet->range;
2821 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
2823 g_return_if_fail (sheet != NULL);
2825 if (range == NULL) range=&sheet->range;
2827 if (range->row0 < 0 || range->rowi < 0) return;
2828 if (range->col0 < 0 || range->coli < 0) return;
2831 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2832 psppire_sheet_real_unselect_range (sheet, NULL);
2834 sheet->range.row0 = range->row0;
2835 sheet->range.rowi = range->rowi;
2836 sheet->range.col0 = range->col0;
2837 sheet->range.coli = range->coli;
2838 sheet->selection_cell.row = range->rowi;
2839 sheet->selection_cell.col = range->coli;
2841 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
2842 psppire_sheet_real_select_range (sheet, NULL);
2846 psppire_sheet_unselect_range (PsppireSheet *sheet)
2848 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2851 psppire_sheet_real_unselect_range (sheet, NULL);
2852 sheet->select_status = GTK_STATE_NORMAL;
2857 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
2858 const PsppireSheetRange *range)
2860 g_return_if_fail (sheet != NULL);
2861 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
2864 range = &sheet->range;
2866 if (range->row0 < 0 || range->rowi < 0) return;
2867 if (range->col0 < 0 || range->coli < 0) return;
2869 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
2870 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
2872 sheet->range.row0 = -1;
2873 sheet->range.rowi = -1;
2874 sheet->range.col0 = -1;
2875 sheet->range.coli = -1;
2880 psppire_sheet_expose (GtkWidget *widget, GdkEventExpose *event)
2882 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2884 g_return_val_if_fail (event != NULL, FALSE);
2886 if (!GTK_WIDGET_DRAWABLE (widget))
2889 /* exposure events on the sheet */
2890 if (event->window == sheet->row_title_window &&
2891 sheet->row_titles_visible)
2893 draw_row_title_buttons_range (sheet,
2894 min_visible_row (sheet),
2895 max_visible_row (sheet));
2898 if (event->window == sheet->column_title_window &&
2899 sheet->column_titles_visible)
2901 draw_column_title_buttons_range (sheet,
2902 min_visible_column (sheet),
2903 max_visible_column (sheet));
2906 if (event->window == sheet->sheet_window)
2908 draw_sheet_region (sheet, event->region);
2911 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2914 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2915 psppire_sheet_range_draw (sheet, &sheet->range);
2917 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2918 psppire_sheet_range_draw (sheet, &sheet->drag_range);
2921 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2925 rectangle_from_range (sheet, &sheet->range, &area);
2927 gdk_draw_rectangle (sheet->sheet_window,
2930 area.x + 1, area.y + 1,
2931 area.width, area.height);
2936 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2937 draw_xor_rectangle (sheet, sheet->drag_range);
2942 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
2945 PsppireSheetRange range;
2946 range.row0 = range.rowi = sheet->active_cell.row;
2947 range.col0 = range.coli = sheet->active_cell.col;
2949 rectangle_from_range (sheet, &range, &rect);
2951 if (GDK_OVERLAP_RECTANGLE_OUT !=
2952 gdk_region_rect_in (event->region, &rect))
2954 psppire_sheet_draw_active_cell (sheet);
2960 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2967 psppire_sheet_button_press (GtkWidget *widget,
2968 GdkEventButton *event)
2970 PsppireSheet *sheet;
2971 GdkModifierType mods;
2976 g_return_val_if_fail (widget != NULL, FALSE);
2977 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
2978 g_return_val_if_fail (event != NULL, FALSE);
2980 sheet = PSPPIRE_SHEET (widget);
2982 /* Cancel any pending tooltips */
2983 if (sheet->motion_timer)
2985 g_source_remove (sheet->motion_timer);
2986 sheet->motion_timer = 0;
2989 gtk_widget_get_pointer (widget, &x, &y);
2990 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2993 if (event->window == sheet->column_title_window)
2995 sheet->x_drag = event->x;
2996 g_signal_emit (sheet,
2997 sheet_signals[BUTTON_EVENT_COLUMN], 0,
3000 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3002 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3003 g_signal_emit (sheet,
3004 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3007 else if (event->window == sheet->row_title_window)
3009 g_signal_emit (sheet,
3010 sheet_signals[BUTTON_EVENT_ROW], 0,
3013 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3015 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3016 g_signal_emit (sheet,
3017 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3021 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3023 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3026 /* press on resize windows */
3027 if (event->window == sheet->column_title_window)
3029 sheet->x_drag = event->x;
3031 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3033 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3034 gdk_pointer_grab (sheet->column_title_window, FALSE,
3035 GDK_POINTER_MOTION_HINT_MASK |
3036 GDK_BUTTON1_MOTION_MASK |
3037 GDK_BUTTON_RELEASE_MASK,
3038 NULL, NULL, event->time);
3040 draw_xor_vline (sheet);
3045 if (event->window == sheet->row_title_window)
3047 sheet->y_drag = event->y;
3049 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3051 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3052 gdk_pointer_grab (sheet->row_title_window, FALSE,
3053 GDK_POINTER_MOTION_HINT_MASK |
3054 GDK_BUTTON1_MOTION_MASK |
3055 GDK_BUTTON_RELEASE_MASK,
3056 NULL, NULL, event->time);
3058 draw_xor_hline (sheet);
3063 /* the sheet itself does not handle other than single click events */
3064 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3066 /* selections on the sheet */
3067 if (event->window == sheet->sheet_window)
3069 gtk_widget_get_pointer (widget, &x, &y);
3070 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3071 gdk_pointer_grab (sheet->sheet_window, FALSE,
3072 GDK_POINTER_MOTION_HINT_MASK |
3073 GDK_BUTTON1_MOTION_MASK |
3074 GDK_BUTTON_RELEASE_MASK,
3075 NULL, NULL, event->time);
3076 gtk_grab_add (GTK_WIDGET (sheet));
3078 if (psppire_sheet_click_cell (sheet, row, column))
3079 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3082 if (event->window == sheet->column_title_window)
3084 gtk_widget_get_pointer (widget, &x, &y);
3085 if ( sheet->row_titles_visible)
3086 x -= sheet->row_title_area.width;
3088 x += sheet->hadjustment->value;
3090 column = column_from_xpixel (sheet, x);
3092 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3094 gtk_grab_add (GTK_WIDGET (sheet));
3095 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3099 if (event->window == sheet->row_title_window)
3101 gtk_widget_get_pointer (widget, &x, &y);
3102 if ( sheet->column_titles_visible)
3103 y -= sheet->column_title_area.height;
3105 y += sheet->vadjustment->value;
3107 row = row_from_ypixel (sheet, y);
3108 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3110 gtk_grab_add (GTK_WIDGET (sheet));
3111 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3119 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3121 PsppireSheetCell cell;
3122 gboolean forbid_move;
3127 if (row >= psppire_axis_unit_count (sheet->vaxis)
3128 || column >= psppire_axis_unit_count (sheet->haxis))
3133 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3134 &sheet->active_cell,
3140 if (sheet->select_status == GTK_STATE_NORMAL)
3143 row = sheet->active_cell.row;
3144 column = sheet->active_cell.col;
3146 change_active_cell (sheet, row, column);
3150 if (row == -1 && column >= 0)
3152 psppire_sheet_select_column (sheet, column);
3156 if (column == -1 && row >= 0)
3158 psppire_sheet_select_row (sheet, row);
3162 if (row == -1 && column == -1)
3164 sheet->range.row0 = 0;
3165 sheet->range.col0 = 0;
3166 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3168 psppire_axis_unit_count (sheet->haxis) - 1;
3169 psppire_sheet_select_range (sheet, NULL);
3173 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
3175 sheet->select_status = PSPPIRE_SHEET_NORMAL;
3176 psppire_sheet_real_unselect_range (sheet, NULL);
3180 change_active_cell (sheet, row, column);
3183 sheet->selection_cell.row = row;
3184 sheet->selection_cell.col = column;
3185 sheet->range.row0 = row;
3186 sheet->range.col0 = column;
3187 sheet->range.rowi = row;
3188 sheet->range.coli = column;
3189 sheet->select_status = PSPPIRE_SHEET_NORMAL;
3190 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3192 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3198 psppire_sheet_button_release (GtkWidget *widget,
3199 GdkEventButton *event)
3201 GdkDisplay *display = gtk_widget_get_display (widget);
3203 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3205 /* release on resize windows */
3206 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3209 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3210 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3212 gdk_display_pointer_ungrab (display, event->time);
3213 draw_xor_vline (sheet);
3216 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3217 + sheet->hadjustment->value;
3219 set_column_width (sheet, sheet->drag_cell.col, width);
3224 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3227 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3228 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3230 gdk_display_pointer_ungrab (display, event->time);
3231 draw_xor_hline (sheet);
3234 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3235 sheet->vadjustment->value;
3237 set_row_height (sheet, sheet->drag_cell.row, height);
3242 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3244 PsppireSheetRange old_range;
3245 draw_xor_rectangle (sheet, sheet->drag_range);
3246 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3247 gdk_display_pointer_ungrab (display, event->time);
3249 psppire_sheet_real_unselect_range (sheet, NULL);
3251 sheet->selection_cell.row = sheet->selection_cell.row +
3252 (sheet->drag_range.row0 - sheet->range.row0);
3253 sheet->selection_cell.col = sheet->selection_cell.col +
3254 (sheet->drag_range.col0 - sheet->range.col0);
3255 old_range = sheet->range;
3256 sheet->range = sheet->drag_range;
3257 sheet->drag_range = old_range;
3258 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3259 &sheet->drag_range, &sheet->range);
3260 psppire_sheet_select_range (sheet, &sheet->range);
3263 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3265 PsppireSheetRange old_range;
3266 draw_xor_rectangle (sheet, sheet->drag_range);
3267 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3268 gdk_display_pointer_ungrab (display, event->time);
3270 psppire_sheet_real_unselect_range (sheet, NULL);
3272 if (sheet->drag_range.row0 < sheet->range.row0)
3273 sheet->selection_cell.row = sheet->drag_range.row0;
3274 if (sheet->drag_range.rowi >= sheet->range.rowi)
3275 sheet->selection_cell.row = sheet->drag_range.rowi;
3276 if (sheet->drag_range.col0 < sheet->range.col0)
3277 sheet->selection_cell.col = sheet->drag_range.col0;
3278 if (sheet->drag_range.coli >= sheet->range.coli)
3279 sheet->selection_cell.col = sheet->drag_range.coli;
3280 old_range = sheet->range;
3281 sheet->range = sheet->drag_range;
3282 sheet->drag_range = old_range;
3284 if (sheet->select_status == GTK_STATE_NORMAL) sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3285 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3286 &sheet->drag_range, &sheet->range);
3287 psppire_sheet_select_range (sheet, &sheet->range);
3290 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet))
3292 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3293 gdk_display_pointer_ungrab (display, event->time);
3294 change_active_cell (sheet, sheet->active_cell.row,
3295 sheet->active_cell.col);
3298 if (PSPPIRE_SHEET_IN_SELECTION)
3299 gdk_display_pointer_ungrab (display, event->time);
3300 gtk_grab_remove (GTK_WIDGET (sheet));
3302 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3311 /* Shamelessly lifted from gtktooltips */
3313 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3317 gtk_widget_size_request (tip_window, &req);
3318 gtk_paint_flat_box (tip_window->style, tip_window->window,
3319 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3320 NULL, GTK_WIDGET(tip_window), "tooltip",
3321 0, 0, req.width, req.height);
3327 destroy_hover_window (PsppireSheetHoverTitle *h)
3329 gtk_widget_destroy (h->window);
3333 static PsppireSheetHoverTitle *
3334 create_hover_window (void)
3336 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3338 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3340 #if GTK_CHECK_VERSION (2, 9, 0)
3341 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3342 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3345 gtk_widget_set_app_paintable (hw->window, TRUE);
3346 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3347 gtk_widget_set_name (hw->window, "gtk-tooltips");
3348 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3350 g_signal_connect (hw->window,
3352 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3355 hw->label = gtk_label_new (NULL);
3358 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3359 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3361 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3363 gtk_widget_show (hw->label);
3365 g_signal_connect (hw->window,
3367 G_CALLBACK (gtk_widget_destroyed),
3373 #define HOVER_WINDOW_Y_OFFSET 2
3376 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3377 const gchar *subtitle)
3386 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3390 sheet->hover_window->row = row;
3391 sheet->hover_window->column = column;
3393 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3395 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3397 gtk_widget_show (sheet->hover_window->window);
3399 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3405 y += sheet->column_title_area.y;
3406 y += sheet->column_title_area.height;
3407 y += HOVER_WINDOW_Y_OFFSET;
3413 x += sheet->row_title_area.x;
3414 x += sheet->row_title_area.width * 2 / 3.0;
3417 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3422 motion_timeout_callback (gpointer data)
3424 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3428 gdk_threads_enter ();
3429 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3431 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3433 if (sheet->row_title_under && row >= 0)
3435 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3437 show_subtitle (sheet, row, -1, text);
3441 if (sheet->column_title_under && column >= 0)
3443 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3446 show_subtitle (sheet, -1, column, text);
3452 gdk_threads_leave ();
3457 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3459 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3460 GdkModifierType mods;
3461 GdkCursorType new_cursor;
3464 GdkDisplay *display;
3466 g_return_val_if_fail (event != NULL, FALSE);
3468 display = gtk_widget_get_display (widget);
3470 /* selections on the sheet */
3474 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3476 if ( sheet->motion_timer > 0 )
3477 g_source_remove (sheet->motion_timer);
3478 sheet->motion_timer =
3479 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3485 gtk_widget_get_pointer (widget, &wx, &wy);
3487 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3489 if ( row != sheet->hover_window->row ||
3490 column != sheet->hover_window->column)
3492 gtk_widget_hide (sheet->hover_window->window);
3497 if (event->window == sheet->column_title_window)
3499 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3500 on_column_boundary (sheet, x, &column))
3502 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3503 if (new_cursor != sheet->cursor_drag->type)
3505 gdk_cursor_unref (sheet->cursor_drag);
3506 sheet->cursor_drag =
3507 gdk_cursor_new_for_display (display, new_cursor);
3509 gdk_window_set_cursor (sheet->column_title_window,
3510 sheet->cursor_drag);
3515 new_cursor = GDK_TOP_LEFT_ARROW;
3516 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3517 new_cursor != sheet->cursor_drag->type)
3519 gdk_cursor_unref (sheet->cursor_drag);
3520 sheet->cursor_drag =
3521 gdk_cursor_new_for_display (display, new_cursor);
3522 gdk_window_set_cursor (sheet->column_title_window,
3523 sheet->cursor_drag);
3527 else if (event->window == sheet->row_title_window)
3529 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3530 on_row_boundary (sheet, y, &row))
3532 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3533 if (new_cursor != sheet->cursor_drag->type)
3535 gdk_cursor_unref (sheet->cursor_drag);
3536 sheet->cursor_drag =
3537 gdk_cursor_new_for_display (display, new_cursor);
3538 gdk_window_set_cursor (sheet->row_title_window,
3539 sheet->cursor_drag);
3544 new_cursor = GDK_TOP_LEFT_ARROW;
3545 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3546 new_cursor != sheet->cursor_drag->type)
3548 gdk_cursor_unref (sheet->cursor_drag);
3549 sheet->cursor_drag =
3550 gdk_cursor_new_for_display (display, new_cursor);
3551 gdk_window_set_cursor (sheet->row_title_window,
3552 sheet->cursor_drag);
3557 new_cursor = GDK_PLUS;
3558 if ( event->window == sheet->sheet_window &&
3559 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3560 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3561 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3562 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3563 new_cursor != sheet->cursor_drag->type)
3565 gdk_cursor_unref (sheet->cursor_drag);
3566 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3567 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3570 new_cursor = GDK_TOP_LEFT_ARROW;
3571 if ( event->window == sheet->sheet_window &&
3572 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3573 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3574 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3575 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3576 new_cursor != sheet->cursor_drag->type)
3578 gdk_cursor_unref (sheet->cursor_drag);
3579 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3580 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3583 new_cursor = GDK_SIZING;
3584 if ( event->window == sheet->sheet_window &&
3585 sheet->selection_mode != GTK_SELECTION_NONE &&
3586 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3587 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3588 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3589 new_cursor != sheet->cursor_drag->type)
3591 gdk_cursor_unref (sheet->cursor_drag);
3592 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3593 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3597 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3598 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3600 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3602 if (event->x != sheet->x_drag)
3604 draw_xor_vline (sheet);
3605 sheet->x_drag = event->x;
3606 draw_xor_vline (sheet);
3612 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3614 if (event->y != sheet->y_drag)
3616 draw_xor_hline (sheet);
3617 sheet->y_drag = event->y;
3618 draw_xor_hline (sheet);
3624 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3626 PsppireSheetRange aux;
3627 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3628 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3629 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3630 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3634 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3635 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3637 aux = sheet->drag_range;
3638 sheet->drag_range.row0 = sheet->range.row0 + row;
3639 sheet->drag_range.col0 = sheet->range.col0 + column;
3640 sheet->drag_range.rowi = sheet->range.rowi + row;
3641 sheet->drag_range.coli = sheet->range.coli + column;
3642 if (aux.row0 != sheet->drag_range.row0 ||
3643 aux.col0 != sheet->drag_range.col0)
3645 draw_xor_rectangle (sheet, aux);
3646 draw_xor_rectangle (sheet, sheet->drag_range);
3652 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3654 PsppireSheetRange aux;
3655 gint v_h, current_col, current_row, col_threshold, row_threshold;
3657 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
3658 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
3660 current_col = column_from_xpixel (sheet, x);
3661 current_row = row_from_ypixel (sheet, y);
3662 column = current_col - sheet->drag_cell.col;
3663 row = current_row - sheet->drag_cell.row;
3665 /*use half of column width resp. row height as threshold to
3667 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
3668 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
3671 if (x < col_threshold)
3674 else if (column < 0)
3676 if (x > col_threshold)
3679 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
3680 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
3683 if (y < row_threshold)
3688 if (y > row_threshold)
3692 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3693 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3703 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3704 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3706 aux = sheet->drag_range;
3707 sheet->drag_range = sheet->range;
3709 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
3710 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
3711 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
3712 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
3714 if (aux.row0 != sheet->drag_range.row0 ||
3715 aux.rowi != sheet->drag_range.rowi ||
3716 aux.col0 != sheet->drag_range.col0 ||
3717 aux.coli != sheet->drag_range.coli)
3719 draw_xor_rectangle (sheet, aux);
3720 draw_xor_rectangle (sheet, sheet->drag_range);
3726 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3728 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
3729 column == sheet->active_cell.col) return TRUE;
3731 if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
3734 sheet->range.rowi = row;
3735 sheet->range.coli = column;
3736 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3738 rectangle_from_range (sheet, &sheet->range, &area);
3739 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
3746 psppire_sheet_crossing_notify (GtkWidget *widget,
3747 GdkEventCrossing *event)
3749 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3751 if (event->window == sheet->column_title_window)
3752 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
3753 else if (event->window == sheet->row_title_window)
3754 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
3756 if (event->type == GDK_LEAVE_NOTIFY)
3757 gtk_widget_hide (sheet->hover_window->window);
3764 psppire_sheet_focus_in (GtkWidget *w,
3765 GdkEventFocus *event)
3767 PsppireSheet *sheet = PSPPIRE_SHEET (w);
3769 gtk_widget_grab_focus (sheet->entry_widget);
3777 psppire_sheet_entry_key_press (GtkWidget *widget,
3781 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
3786 /* Number of rows in a step-increment */
3787 #define ROWS_PER_STEP 1
3791 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
3793 gint old_row = sheet->active_cell.row ;
3794 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
3798 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
3799 min_visible_row (sheet));
3803 case GTK_SCROLL_PAGE_DOWN:
3804 gtk_adjustment_set_value (sheet->vadjustment,
3805 sheet->vadjustment->value +
3806 sheet->vadjustment->page_increment);
3808 case GTK_SCROLL_PAGE_UP:
3809 gtk_adjustment_set_value (sheet->vadjustment,
3810 sheet->vadjustment->value -
3811 sheet->vadjustment->page_increment);
3815 g_assert_not_reached ();
3820 vpixel += psppire_axis_start_pixel (sheet->vaxis,
3821 min_visible_row (sheet));
3823 new_row = row_from_ypixel (sheet, vpixel);
3825 change_active_cell (sheet, new_row,
3826 sheet->active_cell.col);
3831 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
3833 gint current_row = sheet->active_cell.row;
3834 gint current_col = sheet->active_cell.col;
3835 PsppireSheetCell new_cell ;
3836 gboolean forbidden = FALSE;
3838 new_cell.row = current_row;
3839 new_cell.col = current_col;
3843 case GTK_SCROLL_STEP_DOWN:
3846 case GTK_SCROLL_STEP_UP:
3849 case GTK_SCROLL_STEP_RIGHT:
3852 case GTK_SCROLL_STEP_LEFT:
3855 case GTK_SCROLL_STEP_FORWARD:
3858 psppire_sheet_model_get_column_count (sheet->model))
3864 case GTK_SCROLL_STEP_BACKWARD:
3866 if (new_cell.col < 0)
3869 psppire_sheet_model_get_column_count (sheet->model) - 1;
3874 g_assert_not_reached ();
3878 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3879 &sheet->active_cell,
3887 maximize_int (&new_cell.row, 0);
3888 maximize_int (&new_cell.col, 0);
3890 minimize_int (&new_cell.row,
3891 psppire_axis_unit_count (sheet->vaxis) - 1);
3893 minimize_int (&new_cell.col,
3894 psppire_axis_unit_count (sheet->haxis) - 1);
3896 change_active_cell (sheet, new_cell.row, new_cell.col);
3899 if ( new_cell.col > max_fully_visible_column (sheet))
3902 psppire_axis_start_pixel (sheet->haxis,
3904 hpos -= sheet->hadjustment->page_size;
3906 gtk_adjustment_set_value (sheet->hadjustment,
3909 else if ( new_cell.col < min_fully_visible_column (sheet))
3912 psppire_axis_start_pixel (sheet->haxis,
3915 gtk_adjustment_set_value (sheet->hadjustment,
3920 if ( new_cell.row > max_fully_visible_row (sheet))
3923 psppire_axis_start_pixel (sheet->vaxis,
3925 vpos -= sheet->vadjustment->page_size;
3927 gtk_adjustment_set_value (sheet->vadjustment,
3930 else if ( new_cell.row < min_fully_visible_row (sheet))
3933 psppire_axis_start_pixel (sheet->vaxis,
3936 gtk_adjustment_set_value (sheet->vadjustment,
3940 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3945 psppire_sheet_key_press (GtkWidget *widget,
3948 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3950 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3952 switch (key->keyval)
3955 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
3958 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
3960 case GDK_ISO_Left_Tab:
3961 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
3964 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
3968 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3971 step_sheet (sheet, GTK_SCROLL_STEP_UP);
3975 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
3978 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
3982 gtk_adjustment_set_value (sheet->vadjustment,
3983 sheet->vadjustment->lower);
3985 change_active_cell (sheet, 0,
3986 sheet->active_cell.col);
3991 gtk_adjustment_set_value (sheet->vadjustment,
3992 sheet->vadjustment->upper -
3993 sheet->vadjustment->page_size -
3994 sheet->vadjustment->page_increment);
3997 change_active_cellx (sheet,
3998 psppire_axis_unit_count (sheet->vaxis) - 1,
3999 sheet->active_cell.col);
4003 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
4014 psppire_sheet_size_request (GtkWidget *widget,
4015 GtkRequisition *requisition)
4017 PsppireSheet *sheet;
4019 g_return_if_fail (widget != NULL);
4020 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4021 g_return_if_fail (requisition != NULL);
4023 sheet = PSPPIRE_SHEET (widget);
4025 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4026 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4028 /* compute the size of the column title area */
4029 if (sheet->column_titles_visible)
4030 requisition->height += sheet->column_title_area.height;
4032 /* compute the size of the row title area */
4033 if (sheet->row_titles_visible)
4034 requisition->width += sheet->row_title_area.width;
4039 psppire_sheet_size_allocate (GtkWidget *widget,
4040 GtkAllocation *allocation)
4042 PsppireSheet *sheet;
4043 GtkAllocation sheet_allocation;
4046 g_return_if_fail (widget != NULL);
4047 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4048 g_return_if_fail (allocation != NULL);
4050 sheet = PSPPIRE_SHEET (widget);
4051 widget->allocation = *allocation;
4052 border_width = GTK_CONTAINER (widget)->border_width;
4054 if (GTK_WIDGET_REALIZED (widget))
4055 gdk_window_move_resize (widget->window,
4056 allocation->x + border_width,
4057 allocation->y + border_width,
4058 allocation->width - 2 * border_width,
4059 allocation->height - 2 * border_width);
4061 sheet_allocation.x = 0;
4062 sheet_allocation.y = 0;
4063 sheet_allocation.width = allocation->width - 2 * border_width;
4064 sheet_allocation.height = allocation->height - 2 * border_width;
4066 if (GTK_WIDGET_REALIZED (widget))
4067 gdk_window_move_resize (sheet->sheet_window,
4070 sheet_allocation.width,
4071 sheet_allocation.height);
4073 /* position the window which holds the column title buttons */
4074 sheet->column_title_area.x = 0;
4075 sheet->column_title_area.y = 0;
4076 sheet->column_title_area.width = sheet_allocation.width ;
4079 /* position the window which holds the row title buttons */
4080 sheet->row_title_area.x = 0;
4081 sheet->row_title_area.y = 0;
4082 sheet->row_title_area.height = sheet_allocation.height;
4084 if (sheet->row_titles_visible)
4085 sheet->column_title_area.x += sheet->row_title_area.width;
4087 if (sheet->column_titles_visible)
4088 sheet->row_title_area.y += sheet->column_title_area.height;
4090 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4091 gdk_window_move_resize (sheet->column_title_window,
4092 sheet->column_title_area.x,
4093 sheet->column_title_area.y,
4094 sheet->column_title_area.width,
4095 sheet->column_title_area.height);
4098 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4099 gdk_window_move_resize (sheet->row_title_window,
4100 sheet->row_title_area.x,
4101 sheet->row_title_area.y,
4102 sheet->row_title_area.width,
4103 sheet->row_title_area.height);
4105 size_allocate_global_button (sheet);
4109 gint width = sheet->column_title_area.width;
4111 if ( sheet->row_titles_visible)
4112 width -= sheet->row_title_area.width;
4114 g_object_set (sheet->haxis,
4115 "minimum-extent", width,
4122 gint height = sheet->row_title_area.height;
4124 if ( sheet->column_titles_visible)
4125 height -= sheet->column_title_area.height;
4127 g_object_set (sheet->vaxis,
4128 "minimum-extent", height,
4133 /* set the scrollbars adjustments */
4134 adjust_scrollbars (sheet);
4138 draw_column_title_buttons (PsppireSheet *sheet)
4142 if (!sheet->column_titles_visible) return;
4143 if (!GTK_WIDGET_REALIZED (sheet))
4146 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4149 if (sheet->row_titles_visible)
4151 x = sheet->row_title_area.width;
4154 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4156 sheet->column_title_area.width = width;
4157 sheet->column_title_area.x = x;
4158 gdk_window_move_resize (sheet->column_title_window,
4159 sheet->column_title_area.x,
4160 sheet->column_title_area.y,
4161 sheet->column_title_area.width,
4162 sheet->column_title_area.height);
4165 if (max_visible_column (sheet) ==
4166 psppire_axis_unit_count (sheet->haxis) - 1)
4167 gdk_window_clear_area (sheet->column_title_window,
4169 sheet->column_title_area.width,
4170 sheet->column_title_area.height);
4172 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4174 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4175 max_visible_column (sheet));
4179 draw_row_title_buttons (PsppireSheet *sheet)
4184 if (!sheet->row_titles_visible) return;
4185 if (!GTK_WIDGET_REALIZED (sheet))
4188 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4190 if (sheet->column_titles_visible)
4192 y = sheet->column_title_area.height;
4195 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4197 sheet->row_title_area.y = y;
4198 sheet->row_title_area.height = height;
4199 gdk_window_move_resize (sheet->row_title_window,
4200 sheet->row_title_area.x,
4201 sheet->row_title_area.y,
4202 sheet->row_title_area.width,
4203 sheet->row_title_area.height);
4206 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4207 gdk_window_clear_area (sheet->row_title_window,
4209 sheet->row_title_area.width,
4210 sheet->row_title_area.height);
4212 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4214 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4215 max_visible_row (sheet));
4220 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4222 GtkAllocation entry_alloc;
4223 PsppireSheetCellAttr attributes = { 0 };
4224 GtkEntry *sheet_entry;
4226 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4227 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4229 sheet_entry = psppire_sheet_get_entry (sheet);
4231 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4232 sheet->active_cell.col,
4236 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4238 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4240 style->bg[GTK_STATE_NORMAL] = attributes.background;
4241 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4242 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4243 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4244 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4245 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4248 rectangle_from_cell (sheet, sheet->active_cell.row,
4249 sheet->active_cell.col, &entry_alloc);
4251 entry_alloc.x += sheet->cell_padding->left;
4252 entry_alloc.y += sheet->cell_padding->right;
4253 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4254 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4257 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4258 entry_alloc.height);
4259 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4263 /* Copy the sheet's font to the entry widget */
4265 set_entry_widget_font (PsppireSheet *sheet)
4267 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4269 pango_font_description_free (style->font_desc);
4270 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4272 gtk_widget_modify_style (sheet->entry_widget, style);
4276 create_sheet_entry (PsppireSheet *sheet)
4278 if (sheet->entry_widget)
4280 gtk_widget_unparent (sheet->entry_widget);
4283 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4284 g_object_ref_sink (sheet->entry_widget);
4286 gtk_widget_size_request (sheet->entry_widget, NULL);
4288 if ( GTK_IS_ENTRY (sheet->entry_widget))
4290 g_object_set (sheet->entry_widget,
4295 if (GTK_WIDGET_REALIZED (sheet))
4297 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4298 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4299 gtk_widget_realize (sheet->entry_widget);
4302 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4303 G_CALLBACK (psppire_sheet_entry_key_press),
4306 set_entry_widget_font (sheet);
4308 gtk_widget_show (sheet->entry_widget);
4312 /* Finds the last child widget that happens to be of type GtkEntry */
4314 find_entry (GtkWidget *w, gpointer user_data)
4316 GtkWidget **entry = user_data;
4317 if ( GTK_IS_ENTRY (w))
4325 psppire_sheet_get_entry (PsppireSheet *sheet)
4327 GtkWidget *w = sheet->entry_widget;
4329 g_return_val_if_fail (sheet != NULL, NULL);
4330 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4331 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4333 while (! GTK_IS_ENTRY (w))
4335 GtkWidget *entry = NULL;
4337 if (GTK_IS_CONTAINER (w))
4339 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4348 return GTK_ENTRY (w);
4353 draw_button (PsppireSheet *sheet, GdkWindow *window,
4354 PsppireSheetButton *button, gboolean is_sensitive,
4355 GdkRectangle allocation)
4357 GtkShadowType shadow_type;
4358 gint text_width = 0, text_height = 0;
4359 PangoAlignment align = PANGO_ALIGN_LEFT;
4365 g_return_if_fail (sheet != NULL);
4366 g_return_if_fail (button != NULL);
4369 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4371 gdk_window_clear_area (window,
4372 allocation.x, allocation.y,
4373 allocation.width, allocation.height);
4375 gtk_widget_ensure_style (sheet->button);
4377 gtk_paint_box (sheet->button->style, window,
4378 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4380 GTK_WIDGET (sheet->button),
4382 allocation.x, allocation.y,
4383 allocation.width, allocation.height);
4385 state = button->state;
4386 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4388 if (state == GTK_STATE_ACTIVE)
4389 shadow_type = GTK_SHADOW_IN;
4391 shadow_type = GTK_SHADOW_OUT;
4393 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4394 gtk_paint_box (sheet->button->style, window,
4395 button->state, shadow_type,
4396 &allocation, GTK_WIDGET (sheet->button),
4398 allocation.x, allocation.y,
4399 allocation.width, allocation.height);
4401 if ( button->overstruck)
4403 GdkPoint points[2] = {
4404 {allocation.x, allocation.y},
4405 {allocation.x + allocation.width,
4406 allocation.y + allocation.height}
4409 gtk_paint_polygon (sheet->button->style,
4421 if (button->label_visible)
4423 text_height = DEFAULT_ROW_HEIGHT -
4424 2 * COLUMN_TITLES_HEIGHT;
4426 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4428 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4431 allocation.y += 2 * sheet->button->style->ythickness;
4433 if (button->label && strlen (button->label) > 0)
4435 PangoRectangle rect;
4436 gchar *line = button->label;
4438 PangoLayout *layout = NULL;
4439 gint real_x = allocation.x;
4440 gint real_y = allocation.y;
4442 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4443 pango_layout_get_extents (layout, NULL, &rect);
4445 text_width = PANGO_PIXELS (rect.width);
4446 switch (button->justification)
4448 case GTK_JUSTIFY_LEFT:
4449 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4450 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4452 case GTK_JUSTIFY_RIGHT:
4453 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4454 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4456 case GTK_JUSTIFY_CENTER:
4458 real_x = allocation.x + (allocation.width - text_width)/2;
4459 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4460 pango_layout_set_justify (layout, TRUE);
4462 pango_layout_set_alignment (layout, align);
4463 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4472 g_object_unref (layout);
4475 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4477 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4481 psppire_sheet_button_free (button);
4485 /* Draw the column title buttons FIRST through to LAST */
4487 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4491 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4493 if (!sheet->column_titles_visible) return;
4495 g_return_if_fail (first >= min_visible_column (sheet));
4496 g_return_if_fail (last <= max_visible_column (sheet));
4499 rect.height = sheet->column_title_area.height;
4500 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4501 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4502 + psppire_axis_unit_size (sheet->haxis, last);
4504 rect.x -= sheet->hadjustment->value;
4506 minimize_int (&rect.width, sheet->column_title_area.width);
4507 maximize_int (&rect.x, 0);
4509 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4511 for (col = first ; col <= last ; ++col)
4513 GdkRectangle allocation;
4514 gboolean is_sensitive = FALSE;
4516 PsppireSheetButton *
4517 button = psppire_sheet_model_get_column_button (sheet->model, col);
4519 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4521 allocation.x -= sheet->hadjustment->value;
4523 allocation.height = sheet->column_title_area.height;
4524 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4525 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4527 draw_button (sheet, sheet->column_title_window,
4528 button, is_sensitive, allocation);
4531 gdk_window_end_paint (sheet->column_title_window);
4536 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4540 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4542 if (!sheet->row_titles_visible) return;
4544 g_return_if_fail (first >= min_visible_row (sheet));
4545 g_return_if_fail (last <= max_visible_row (sheet));
4548 rect.width = sheet->row_title_area.width;
4549 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4550 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4551 + psppire_axis_unit_size (sheet->vaxis, last);
4553 rect.y -= sheet->vadjustment->value;
4555 minimize_int (&rect.height, sheet->row_title_area.height);
4556 maximize_int (&rect.y, 0);
4558 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4559 for (row = first; row <= last; ++row)
4561 GdkRectangle allocation;
4563 gboolean is_sensitive = FALSE;
4565 PsppireSheetButton *button =
4566 psppire_sheet_model_get_row_button (sheet->model, row);
4568 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4570 allocation.y -= sheet->vadjustment->value;
4572 allocation.width = sheet->row_title_area.width;
4573 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4574 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4576 draw_button (sheet, sheet->row_title_window,
4577 button, is_sensitive, allocation);
4580 gdk_window_end_paint (sheet->row_title_window);
4587 * vadjustment_value_changed
4588 * hadjustment_value_changed */
4592 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4595 (adj->value + adj->page_size)
4597 (adj->upper - adj->lower);
4599 const glong last_item = psppire_axis_unit_count (axis) - 1;
4601 if (isnan (position) || position < 0)
4605 psppire_axis_start_pixel (axis, last_item)
4607 psppire_axis_unit_size (axis, last_item)
4611 adj->page_size = page_size;
4614 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4616 if ( adj->value < adj->lower)
4617 adj->value = adj->lower;
4620 gtk_adjustment_changed (adj);
4625 adjust_scrollbars (PsppireSheet *sheet)
4629 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4632 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4634 if ( sheet->row_titles_visible)
4635 width -= sheet->row_title_area.width;
4637 if (sheet->column_titles_visible)
4638 height -= sheet->column_title_area.height;
4640 if (sheet->vadjustment)
4642 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4644 sheet->vadjustment->step_increment =
4646 psppire_axis_unit_size (sheet->vaxis, last_row);
4648 sheet->vadjustment->page_increment =
4650 sheet->column_title_area.height -
4651 psppire_axis_unit_size (sheet->vaxis, last_row);
4653 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4656 if (sheet->hadjustment)
4658 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4659 sheet->hadjustment->step_increment = 1;
4661 sheet->hadjustment->page_increment = width;
4663 sheet->hadjustment->upper =
4664 psppire_axis_start_pixel (sheet->haxis, last_col)
4666 psppire_axis_unit_size (sheet->haxis, last_col)
4669 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4673 /* Subtracts the region of WIDGET from REGION */
4675 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4678 GdkRectangle intersect;
4681 gdk_region_get_clipbox (region, &rect);
4682 gtk_widget_intersect (widget,
4686 region2 = gdk_region_rectangle (&intersect);
4687 gdk_region_subtract (region, region2);
4688 gdk_region_destroy (region2);
4692 vadjustment_value_changed (GtkAdjustment *adjustment,
4696 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4698 g_return_if_fail (adjustment != NULL);
4700 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4702 gtk_widget_hide (sheet->entry_widget);
4705 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4707 subtract_widget_region (region, sheet->button);
4708 gdk_window_begin_paint_region (sheet->sheet_window, region);
4710 draw_sheet_region (sheet, region);
4712 draw_row_title_buttons (sheet);
4713 psppire_sheet_draw_active_cell (sheet);
4715 gdk_window_end_paint (sheet->sheet_window);
4716 gdk_region_destroy (region);
4721 hadjustment_value_changed (GtkAdjustment *adjustment,
4725 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4727 g_return_if_fail (adjustment != NULL);
4729 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4731 gtk_widget_hide (sheet->entry_widget);
4735 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4737 subtract_widget_region (region, sheet->button);
4738 gdk_window_begin_paint_region (sheet->sheet_window, region);
4740 draw_sheet_region (sheet, region);
4742 draw_column_title_buttons (sheet);
4744 psppire_sheet_draw_active_cell (sheet);
4746 gdk_window_end_paint (sheet->sheet_window);
4748 gdk_region_destroy (region);
4752 /* COLUMN RESIZING */
4754 draw_xor_vline (PsppireSheet *sheet)
4757 gint xpos = sheet->x_drag;
4758 gdk_drawable_get_size (sheet->sheet_window,
4761 if (sheet->row_titles_visible)
4762 xpos += sheet->row_title_area.width;
4764 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4766 sheet->column_title_area.height,
4768 height + CELL_SPACING);
4773 draw_xor_hline (PsppireSheet *sheet)
4777 gint ypos = sheet->y_drag;
4779 gdk_drawable_get_size (sheet->sheet_window,
4783 if (sheet->column_titles_visible)
4784 ypos += sheet->column_title_area.height;
4786 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4787 sheet->row_title_area.width,
4789 width + CELL_SPACING,
4793 /* SELECTED RANGE */
4795 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
4798 GdkRectangle clip_area, area;
4801 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
4802 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
4803 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
4804 psppire_axis_unit_size (sheet->haxis, range.coli);
4805 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
4806 psppire_axis_unit_size (sheet->vaxis, range.rowi);
4808 clip_area.x = sheet->row_title_area.width;
4809 clip_area.y = sheet->column_title_area.height;
4811 gdk_drawable_get_size (sheet->sheet_window,
4812 &clip_area.width, &clip_area.height);
4814 if (!sheet->row_titles_visible) clip_area.x = 0;
4815 if (!sheet->column_titles_visible) clip_area.y = 0;
4819 area.width = area.width + area.x;
4822 if (area.width > clip_area.width) area.width = clip_area.width + 10;
4825 area.height = area.height + area.y;
4828 if (area.height > clip_area.height) area.height = clip_area.height + 10;
4832 clip_area.width += 3;
4833 clip_area.height += 3;
4835 gdk_gc_get_values (sheet->xor_gc, &values);
4837 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
4839 gdk_draw_rectangle (sheet->sheet_window,
4842 area.x + i, area.y + i,
4843 area.width - 2 * i, area.height - 2 * i);
4846 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4848 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
4853 set_column_width (PsppireSheet *sheet,
4857 g_return_if_fail (sheet != NULL);
4858 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4860 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
4866 psppire_axis_resize (sheet->haxis, column,
4867 width - sheet->cell_padding->left -
4868 sheet->cell_padding->right);
4870 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4872 draw_column_title_buttons (sheet);
4873 adjust_scrollbars (sheet);
4874 psppire_sheet_size_allocate_entry (sheet);
4875 redraw_range (sheet, NULL);
4880 set_row_height (PsppireSheet *sheet,
4884 g_return_if_fail (sheet != NULL);
4885 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4887 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
4893 psppire_axis_resize (sheet->vaxis, row,
4894 height - sheet->cell_padding->top -
4895 sheet->cell_padding->bottom);
4897 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
4899 draw_row_title_buttons (sheet);
4900 adjust_scrollbars (sheet);
4901 psppire_sheet_size_allocate_entry (sheet);
4902 redraw_range (sheet, NULL);
4907 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
4908 PsppireSheetCellAttr *attr)
4911 const GtkJustification *j ;
4912 GdkColormap *colormap;
4914 g_return_val_if_fail (sheet != NULL, FALSE);
4915 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
4917 if (row < 0 || col < 0) return FALSE;
4919 attr->foreground = GTK_WIDGET (sheet)->style->black;
4920 attr->background = sheet->color[BG_COLOR];
4922 attr->border.width = 0;
4923 attr->border.line_style = GDK_LINE_SOLID;
4924 attr->border.cap_style = GDK_CAP_NOT_LAST;
4925 attr->border.join_style = GDK_JOIN_MITER;
4926 attr->border.mask = 0;
4927 attr->border.color = GTK_WIDGET (sheet)->style->black;
4929 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
4930 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
4933 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
4934 attr->foreground = *fg;
4937 bg = psppire_sheet_model_get_background (sheet->model, row, col);
4940 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
4941 attr->background = *bg;
4944 attr->justification =
4945 psppire_sheet_model_get_column_justification (sheet->model, col);
4947 j = psppire_sheet_model_get_justification (sheet->model, row, col);
4949 attr->justification = *j;
4955 psppire_sheet_button_size_request (PsppireSheet *sheet,
4956 const PsppireSheetButton *button,
4957 GtkRequisition *button_requisition)
4959 GtkRequisition requisition;
4960 GtkRequisition label_requisition;
4962 label_requisition.height = DEFAULT_ROW_HEIGHT;
4963 label_requisition.width = COLUMN_MIN_WIDTH;
4965 requisition.height = DEFAULT_ROW_HEIGHT;
4966 requisition.width = COLUMN_MIN_WIDTH;
4969 *button_requisition = requisition;
4970 button_requisition->width = MAX (requisition.width, label_requisition.width);
4971 button_requisition->height = MAX (requisition.height, label_requisition.height);
4976 psppire_sheet_forall (GtkContainer *container,
4977 gboolean include_internals,
4978 GtkCallback callback,
4979 gpointer callback_data)
4981 PsppireSheet *sheet = PSPPIRE_SHEET (container);
4983 g_return_if_fail (callback != NULL);
4985 if (sheet->button && sheet->button->parent)
4986 (* callback) (sheet->button, callback_data);
4988 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
4989 (* callback) (sheet->entry_widget, callback_data);
4994 psppire_sheet_get_model (const PsppireSheet *sheet)
4996 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4998 return sheet->model;
5002 PsppireSheetButton *
5003 psppire_sheet_button_new (void)
5005 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
5007 button->state = GTK_STATE_NORMAL;
5008 button->label = NULL;
5009 button->label_visible = TRUE;
5010 button->justification = GTK_JUSTIFY_FILL;
5011 button->overstruck = FALSE;
5018 psppire_sheet_button_free (PsppireSheetButton *button)
5020 if (!button) return ;
5022 g_free (button->label);
5027 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5029 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5031 if ( NULL == celltext)
5034 g_string_append (string, celltext);
5040 range_to_text (const PsppireSheet *sheet)
5045 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5048 string = g_string_sized_new (80);
5050 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5052 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5054 append_cell_text (string, sheet, r, c);
5055 g_string_append (string, "\t");
5057 append_cell_text (string, sheet, r, c);
5058 if ( r < sheet->range.rowi)
5059 g_string_append (string, "\n");
5066 range_to_html (const PsppireSheet *sheet)
5071 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5074 string = g_string_sized_new (480);
5076 g_string_append (string, "<html>\n");
5077 g_string_append (string, "<body>\n");
5078 g_string_append (string, "<table>\n");
5079 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5081 g_string_append (string, "<tr>\n");
5082 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5084 g_string_append (string, "<td>");
5085 append_cell_text (string, sheet, r, c);
5086 g_string_append (string, "</td>\n");
5088 g_string_append (string, "</tr>\n");
5090 g_string_append (string, "</table>\n");
5091 g_string_append (string, "</body>\n");
5092 g_string_append (string, "</html>\n");
5104 primary_get_cb (GtkClipboard *clipboard,
5105 GtkSelectionData *selection_data,
5109 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5110 GString *string = NULL;
5114 case SELECT_FMT_TEXT:
5115 string = range_to_text (sheet);
5117 case SELECT_FMT_HTML:
5118 string = range_to_html (sheet);
5121 g_assert_not_reached ();
5124 gtk_selection_data_set (selection_data, selection_data->target,
5126 (const guchar *) string->str, string->len);
5127 g_string_free (string, TRUE);
5131 primary_clear_cb (GtkClipboard *clipboard,
5134 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5135 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5138 psppire_sheet_real_unselect_range (sheet, NULL);
5142 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5144 static const GtkTargetEntry targets[] = {
5145 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5146 { "STRING", 0, SELECT_FMT_TEXT },
5147 { "TEXT", 0, SELECT_FMT_TEXT },
5148 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5149 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5150 { "text/plain", 0, SELECT_FMT_TEXT },
5151 { "text/html", 0, SELECT_FMT_HTML }
5154 GtkClipboard *clipboard;
5156 if (!GTK_WIDGET_REALIZED (sheet))
5159 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5160 GDK_SELECTION_PRIMARY);
5162 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5164 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5165 G_N_ELEMENTS (targets),
5166 primary_get_cb, primary_clear_cb,
5168 primary_clear_cb (clipboard, sheet);
5172 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5173 gtk_clipboard_clear (clipboard);