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.
59 #include <gdk/gdkkeysyms.h>
60 #include <gtk/gtksignal.h>
61 #include <gtk/gtkbutton.h>
62 #include <gtk/gtkadjustment.h>
63 #include <gtk/gtktypeutils.h>
64 #include <gtk/gtkentry.h>
65 #include <gtk/gtkcontainer.h>
66 #include <pango/pango.h>
67 #include "psppire-sheet.h"
68 #include <ui/gui/psppire-marshal.h>
69 #include <ui/gui/sheet/psppire-sheetmodel.h>
70 #include <ui/gui/sheet/psppire-axis.h>
71 #include <libpspp/misc.h>
76 PSPPIRE_SHEET_IN_XDRAG = 1 << 1,
77 PSPPIRE_SHEET_IN_YDRAG = 1 << 2,
78 PSPPIRE_SHEET_IN_DRAG = 1 << 3,
80 /* This flag is set when the user is actually in the process
81 of making a selection - ie while the mouse button is
84 PSPPIRE_SHEET_IN_SELECTION = 1 << 4
87 #define PSPPIRE_SHEET_FLAGS(sheet) (PSPPIRE_SHEET (sheet)->flags)
88 #define PSPPIRE_SHEET_SET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) |= (flag))
89 #define PSPPIRE_SHEET_UNSET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) &= ~ (flag))
91 #define PSPPIRE_SHEET_IN_XDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_XDRAG)
92 #define PSPPIRE_SHEET_IN_YDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_YDRAG)
93 #define PSPPIRE_SHEET_IN_DRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_DRAG)
94 #define PSPPIRE_SHEET_IN_SELECTION(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_SELECTION)
96 #define CELL_SPACING 1
98 #define TIMEOUT_HOVER 300
99 #define COLUMN_MIN_WIDTH 10
100 #define COLUMN_TITLES_HEIGHT 4
101 #define DEFAULT_COLUMN_WIDTH 80
102 #define DEFAULT_ROW_HEIGHT 25
104 static void set_entry_widget_font (PsppireSheet *sheet);
106 static void psppire_sheet_update_primary_selection (PsppireSheet *sheet);
107 static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
108 static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
109 static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range);
112 static void set_row_height (PsppireSheet *sheet,
116 static void destroy_hover_window (PsppireSheetHoverTitle *);
117 static PsppireSheetHoverTitle *create_hover_window (void);
120 dispose_string (const PsppireSheet *sheet, gchar *text)
122 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
127 if (psppire_sheet_model_free_strings (model))
132 /* FIXME: Why bother with these two ? */
134 /* returns the column index from a pixel location */
136 column_from_xpixel (const PsppireSheet *sheet, gint pixel)
138 return psppire_axis_unit_at_pixel (sheet->haxis, pixel);
142 row_from_ypixel (const PsppireSheet *sheet, gint pixel)
144 return psppire_axis_unit_at_pixel (sheet->vaxis, pixel);
148 /* Return the lowest row number which is wholly or partially on
149 the visible range of the sheet */
151 min_visible_row (const PsppireSheet *sheet)
153 return row_from_ypixel (sheet, sheet->vadjustment->value);
157 min_fully_visible_row (const PsppireSheet *sheet)
159 glong row = min_visible_row (sheet);
161 if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value)
168 max_visible_row (const PsppireSheet *sheet)
170 return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size);
175 max_fully_visible_row (const PsppireSheet *sheet)
177 glong row = max_visible_row (sheet);
179 if ( psppire_axis_start_pixel (sheet->vaxis, row)
181 psppire_axis_unit_size (sheet->vaxis, row)
182 > sheet->vadjustment->value)
189 /* Returns the lowest column number which is wholly or partially
192 min_visible_column (const PsppireSheet *sheet)
194 return column_from_xpixel (sheet, sheet->hadjustment->value);
198 min_fully_visible_column (const PsppireSheet *sheet)
200 glong col = min_visible_column (sheet);
202 if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value)
209 /* Returns the highest column number which is wholly or partially
212 max_visible_column (const PsppireSheet *sheet)
214 return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size);
218 max_fully_visible_column (const PsppireSheet *sheet)
220 glong col = max_visible_column (sheet);
222 if ( psppire_axis_start_pixel (sheet->haxis, col)
224 psppire_axis_unit_size (sheet->haxis, col)
225 > sheet->hadjustment->value)
233 /* The size of the region (in pixels) around the row/column boundaries
234 where the height/width may be grabbed to change size */
238 on_column_boundary (const PsppireSheet *sheet, gint x, gint *column)
243 x += sheet->hadjustment->value;
248 col = column_from_xpixel (sheet, x);
250 pixel = x - DRAG_WIDTH / 2;
254 if ( column_from_xpixel (sheet, pixel) < col )
260 if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col )
270 on_row_boundary (const PsppireSheet *sheet, gint y, gint *row)
275 y += sheet->vadjustment->value;
280 r = row_from_ypixel (sheet, y);
282 pixel = y - DRAG_WIDTH / 2;
286 if ( row_from_ypixel (sheet, pixel) < r )
292 if ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r )
302 static inline gboolean
303 POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y,
304 gint *drag_row, gint *drag_column)
308 /* Can't drag if nothing is selected */
309 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
310 sheet->range.col0 < 0 || sheet->range.coli < 0 )
313 *drag_column = column_from_xpixel (sheet, x);
314 *drag_row = row_from_ypixel (sheet, y);
316 if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 &&
317 x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
318 psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2)
320 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0);
321 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
323 *drag_row = sheet->range.row0;
326 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
327 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
328 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
330 *drag_row = sheet->range.rowi;
335 if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 &&
336 y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
337 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2)
339 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0);
340 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
342 *drag_column = sheet->range.col0;
345 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
346 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
347 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
349 *drag_column = sheet->range.coli;
357 static inline gboolean
358 POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y,
359 gint *drag_row, gint *drag_column)
363 /* Can't drag if nothing is selected */
364 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
365 sheet->range.col0 < 0 || sheet->range.coli < 0 )
368 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+
369 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
371 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
372 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
374 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED)
375 ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet));
377 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED)
378 xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet));
380 *drag_column = column_from_xpixel (sheet, x);
381 *drag_row = row_from_ypixel (sheet, y);
383 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
384 y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
391 rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *range,
394 gint col0 = MIN (range->col0, range->coli);
395 gint coli = MAX (range->col0, range->coli);
396 gint row0 = MIN (range->row0, range->rowi);
397 gint rowi = MAX (range->row0, range->rowi);
399 if ( row0 == -1 ) row0 = min_visible_row (sheet);
400 if ( rowi == -1 ) rowi = max_visible_row (sheet);
401 if ( col0 == -1 ) col0 = min_visible_column (sheet);
402 if ( coli == -1 ) coli = max_visible_column (sheet);
404 r->x = psppire_axis_start_pixel (sheet->haxis, col0);
405 r->x -= round (sheet->hadjustment->value);
407 r->y = psppire_axis_start_pixel (sheet->vaxis, row0);
408 r->y -= round (sheet->vadjustment->value);
410 r->width = psppire_axis_start_pixel (sheet->haxis, coli) -
411 psppire_axis_start_pixel (sheet->haxis, col0) +
412 psppire_axis_unit_size (sheet->haxis, coli);
414 r->height = psppire_axis_start_pixel (sheet->vaxis, rowi) -
415 psppire_axis_start_pixel (sheet->vaxis, row0) +
416 psppire_axis_unit_size (sheet->vaxis, rowi);
418 if ( sheet->column_titles_visible)
420 r->y += sheet->column_title_area.height;
423 if ( sheet->row_titles_visible)
425 r->x += sheet->row_title_area.width;
432 rectangle_from_cell (PsppireSheet *sheet, gint row, gint col,
435 PsppireSheetRange range;
436 g_return_val_if_fail (row >= 0, FALSE);
437 g_return_val_if_fail (col >= 0, FALSE);
439 range.row0 = range.rowi = row;
440 range.col0 = range.coli = col;
442 return rectangle_from_range (sheet, &range, r);
446 static void psppire_sheet_class_init (PsppireSheetClass *klass);
447 static void psppire_sheet_init (PsppireSheet *sheet);
448 static void psppire_sheet_dispose (GObject *object);
449 static void psppire_sheet_finalize (GObject *object);
450 static void psppire_sheet_style_set (GtkWidget *widget,
451 GtkStyle *previous_style);
452 static void psppire_sheet_realize (GtkWidget *widget);
453 static void psppire_sheet_unrealize (GtkWidget *widget);
454 static void psppire_sheet_map (GtkWidget *widget);
455 static void psppire_sheet_unmap (GtkWidget *widget);
456 static gint psppire_sheet_expose (GtkWidget *widget,
457 GdkEventExpose *event);
459 static void psppire_sheet_forall (GtkContainer *container,
460 gboolean include_internals,
461 GtkCallback callback,
462 gpointer callback_data);
464 static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
465 GtkAdjustment *hadjustment,
466 GtkAdjustment *vadjustment);
468 static gint psppire_sheet_button_press (GtkWidget *widget,
469 GdkEventButton *event);
470 static gint psppire_sheet_button_release (GtkWidget *widget,
471 GdkEventButton *event);
472 static gint psppire_sheet_motion (GtkWidget *widget,
473 GdkEventMotion *event);
474 static gboolean psppire_sheet_crossing_notify (GtkWidget *widget,
475 GdkEventCrossing *event);
476 static gint psppire_sheet_entry_key_press (GtkWidget *widget,
478 static gboolean psppire_sheet_key_press (GtkWidget *widget,
480 static void psppire_sheet_size_request (GtkWidget *widget,
481 GtkRequisition *requisition);
482 static void psppire_sheet_size_allocate (GtkWidget *widget,
483 GtkAllocation *allocation);
485 static gboolean psppire_sheet_focus_in (GtkWidget *widget,
486 GdkEventFocus *event);
490 static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
491 const PsppireSheetRange *range);
492 static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet,
493 gint row, gint column);
494 /* Drawing Routines */
497 static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column);
500 /* draw visible part of range. */
501 static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region);
505 static void psppire_sheet_draw_border (PsppireSheet *sheet,
506 PsppireSheetRange range);
508 /* Active Cell handling */
510 static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet);
511 static void change_active_cell (PsppireSheet *sheet, gint row, gint col);
512 static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet);
513 static void psppire_sheet_show_entry_widget (PsppireSheet *sheet);
514 static gboolean psppire_sheet_click_cell (PsppireSheet *sheet,
521 static void adjust_scrollbars (PsppireSheet *sheet);
522 static void vadjustment_value_changed (GtkAdjustment *adjustment,
524 static void hadjustment_value_changed (GtkAdjustment *adjustment,
528 static void draw_xor_vline (PsppireSheet *sheet);
529 static void draw_xor_hline (PsppireSheet *sheet);
530 static void draw_xor_rectangle (PsppireSheet *sheet,
531 PsppireSheetRange range);
535 static void create_global_button (PsppireSheet *sheet);
536 static void global_button_clicked (GtkWidget *widget,
540 static void create_sheet_entry (PsppireSheet *sheet);
541 static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet);
543 /* Sheet button gadgets */
545 static void draw_column_title_buttons (PsppireSheet *sheet);
546 static void draw_row_title_buttons (PsppireSheet *sheet);
549 static void size_allocate_global_button (PsppireSheet *sheet);
550 static void psppire_sheet_button_size_request (PsppireSheet *sheet,
551 const PsppireSheetButton *button,
552 GtkRequisition *requisition);
554 static void psppire_sheet_real_cell_clear (PsppireSheet *sheet,
576 static GtkContainerClass *parent_class = NULL;
577 static guint sheet_signals[LAST_SIGNAL] = { 0 };
581 psppire_sheet_get_type ()
583 static GType sheet_type = 0;
587 static const GTypeInfo sheet_info =
589 sizeof (PsppireSheetClass),
592 (GClassInitFunc) psppire_sheet_class_init,
595 sizeof (PsppireSheet),
597 (GInstanceInitFunc) psppire_sheet_init,
602 g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
610 static PsppireSheetRange*
611 psppire_sheet_range_copy (const PsppireSheetRange *range)
613 PsppireSheetRange *new_range;
615 g_return_val_if_fail (range != NULL, NULL);
617 new_range = g_new (PsppireSheetRange, 1);
625 psppire_sheet_range_free (PsppireSheetRange *range)
627 g_return_if_fail (range != NULL);
633 psppire_sheet_range_get_type (void)
635 static GType sheet_range_type = 0;
637 if (!sheet_range_type)
640 g_boxed_type_register_static ("PsppireSheetRange",
641 (GBoxedCopyFunc) psppire_sheet_range_copy,
642 (GBoxedFreeFunc) psppire_sheet_range_free);
645 return sheet_range_type;
648 static PsppireSheetCell*
649 psppire_sheet_cell_copy (const PsppireSheetCell *cell)
651 PsppireSheetCell *new_cell;
653 g_return_val_if_fail (cell != NULL, NULL);
655 new_cell = g_new (PsppireSheetCell, 1);
663 psppire_sheet_cell_free (PsppireSheetCell *cell)
665 g_return_if_fail (cell != NULL);
671 psppire_sheet_cell_get_type (void)
673 static GType sheet_cell_type = 0;
675 if (!sheet_cell_type)
678 g_boxed_type_register_static ("PsppireSheetCell",
679 (GBoxedCopyFunc) psppire_sheet_cell_copy,
680 (GBoxedFreeFunc) psppire_sheet_cell_free);
683 return sheet_cell_type;
698 resize_column (PsppireSheet *sheet, gint unit, glong size)
700 PsppireSheetRange range;
702 range.coli = max_visible_column (sheet);
703 range.row0 = min_visible_row (sheet);
704 range.rowi = max_visible_row (sheet);
706 redraw_range (sheet, &range);
708 draw_column_title_buttons_range (sheet, range.col0, range.coli);
713 psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
716 g_object_unref (sheet->haxis);
719 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
722 g_object_ref (sheet->haxis);
726 resize_row (PsppireSheet *sheet, gint unit, glong size)
728 PsppireSheetRange range;
729 range.col0 = min_visible_column (sheet);
730 range.coli = max_visible_column (sheet);
732 range.rowi = max_visible_row (sheet);
734 redraw_range (sheet, &range);
736 draw_row_title_buttons_range (sheet, range.row0, range.rowi);
740 psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
743 g_object_unref (sheet->vaxis);
747 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
750 g_object_ref (sheet->vaxis);
753 static const GtkBorder default_cell_padding = {3, 3, 3, 3};
756 psppire_sheet_set_property (GObject *object,
762 PsppireSheet *sheet = PSPPIRE_SHEET (object);
766 case PROP_CELL_PADDING:
767 if ( sheet->cell_padding)
768 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
770 sheet->cell_padding = g_value_dup_boxed (value);
772 if (NULL == sheet->cell_padding)
773 sheet->cell_padding = g_boxed_copy (GTK_TYPE_BORDER,
774 &default_cell_padding);
777 g_object_set (sheet->vaxis, "padding",
778 sheet->cell_padding->top + sheet->cell_padding->bottom,
782 g_object_set (sheet->haxis, "padding",
783 sheet->cell_padding->left + sheet->cell_padding->right,
787 psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
788 g_object_set (sheet->vaxis, "padding",
789 sheet->cell_padding->top + sheet->cell_padding->bottom,
793 psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
794 g_object_set (sheet->haxis, "padding",
795 sheet->cell_padding->left + sheet->cell_padding->right,
799 psppire_sheet_set_model (sheet, g_value_get_pointer (value));
802 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
808 psppire_sheet_get_property (GObject *object,
813 PsppireSheet *sheet = PSPPIRE_SHEET (object);
817 case PROP_CELL_PADDING:
818 g_value_set_boxed (value, sheet->cell_padding);
821 g_value_set_pointer (value, sheet->vaxis);
824 g_value_set_pointer (value, sheet->haxis);
827 g_value_set_pointer (value, sheet->model);
830 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
837 psppire_sheet_class_init (PsppireSheetClass *klass)
839 GObjectClass *object_class = G_OBJECT_CLASS (klass);
841 GParamSpec *haxis_spec ;
842 GParamSpec *vaxis_spec ;
843 GParamSpec *model_spec ;
844 GParamSpec *cell_padding_spec ;
846 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
847 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
849 parent_class = g_type_class_peek_parent (klass);
852 * PsppireSheet::select-row
853 * @sheet: the sheet widget that emitted the signal
854 * @row: the newly selected row index, or -1 if no row is selected.
856 * A row has been selected.
858 sheet_signals[SELECT_ROW] =
859 g_signal_new ("select-row",
860 G_TYPE_FROM_CLASS (object_class),
862 offsetof (PsppireSheetClass, select_row),
864 g_cclosure_marshal_VOID__INT,
871 * PsppireSheet::select - column
872 * @sheet: the sheet widget that emitted the signal
873 * @column: the newly selected column index, or -1 if no column is selected.
875 * A column has been selected.
877 sheet_signals[SELECT_COLUMN] =
878 g_signal_new ("select-column",
879 G_TYPE_FROM_CLASS (object_class),
881 offsetof (PsppireSheetClass, select_column),
883 g_cclosure_marshal_VOID__INT,
890 * PsppireSheet::double-click-row
891 * @sheet: the sheet widget that emitted the signal
892 * @row: the row that was double clicked.
894 * A row's title button has been double clicked
896 sheet_signals[DOUBLE_CLICK_ROW] =
897 g_signal_new ("double-click-row",
898 G_TYPE_FROM_CLASS (object_class),
902 g_cclosure_marshal_VOID__INT,
909 * PsppireSheet::double-click-column
910 * @sheet: the sheet widget that emitted the signal
911 * @column: the column that was double clicked.
913 * A column's title button has been double clicked
915 sheet_signals[DOUBLE_CLICK_COLUMN] =
916 g_signal_new ("double-click-column",
917 G_TYPE_FROM_CLASS (object_class),
921 g_cclosure_marshal_VOID__INT,
928 * PsppireSheet::button-event-column
929 * @sheet: the sheet widget that emitted the signal
930 * @column: the column on which the event occured.
932 * A button event occured on a column title button
934 sheet_signals[BUTTON_EVENT_COLUMN] =
935 g_signal_new ("button-event-column",
936 G_TYPE_FROM_CLASS (object_class),
940 psppire_marshal_VOID__INT_POINTER,
949 * PsppireSheet::button-event-row
950 * @sheet: the sheet widget that emitted the signal
951 * @column: the column on which the event occured.
953 * A button event occured on a row title button
955 sheet_signals[BUTTON_EVENT_ROW] =
956 g_signal_new ("button-event-row",
957 G_TYPE_FROM_CLASS (object_class),
961 psppire_marshal_VOID__INT_POINTER,
969 sheet_signals[SELECT_RANGE] =
970 g_signal_new ("select-range",
971 G_TYPE_FROM_CLASS (object_class),
973 offsetof (PsppireSheetClass, select_range),
975 g_cclosure_marshal_VOID__BOXED,
978 PSPPIRE_TYPE_SHEET_RANGE);
981 sheet_signals[RESIZE_RANGE] =
982 g_signal_new ("resize-range",
983 G_TYPE_FROM_CLASS (object_class),
985 offsetof (PsppireSheetClass, resize_range),
987 psppire_marshal_VOID__BOXED_BOXED,
990 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
993 sheet_signals[MOVE_RANGE] =
994 g_signal_new ("move-range",
995 G_TYPE_FROM_CLASS (object_class),
997 offsetof (PsppireSheetClass, move_range),
999 psppire_marshal_VOID__BOXED_BOXED,
1002 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
1005 sheet_signals[TRAVERSE] =
1006 g_signal_new ("traverse",
1007 G_TYPE_FROM_CLASS (object_class),
1009 offsetof (PsppireSheetClass, traverse),
1011 psppire_marshal_BOOLEAN__BOXED_POINTER,
1013 PSPPIRE_TYPE_SHEET_CELL,
1017 sheet_signals[ACTIVATE] =
1018 g_signal_new ("activate",
1019 G_TYPE_FROM_CLASS (object_class),
1021 offsetof (PsppireSheetClass, activate),
1023 psppire_marshal_VOID__INT_INT_INT_INT,
1025 G_TYPE_INT, G_TYPE_INT,
1026 G_TYPE_INT, G_TYPE_INT);
1028 widget_class->set_scroll_adjustments_signal =
1029 g_signal_new ("set-scroll-adjustments",
1030 G_TYPE_FROM_CLASS (object_class),
1032 offsetof (PsppireSheetClass, set_scroll_adjustments),
1034 psppire_marshal_VOID__OBJECT_OBJECT,
1035 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1038 container_class->add = NULL;
1039 container_class->remove = NULL;
1040 container_class->forall = psppire_sheet_forall;
1041 container_class->set_focus_child = NULL;
1043 object_class->dispose = psppire_sheet_dispose;
1044 object_class->finalize = psppire_sheet_finalize;
1047 g_param_spec_boxed ("cell-padding",
1049 "The space between a cell's contents and its border",
1051 G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
1054 g_param_spec_pointer ("vertical-axis",
1056 "A pointer to the PsppireAxis object for the rows",
1057 G_PARAM_READABLE | G_PARAM_WRITABLE );
1060 g_param_spec_pointer ("horizontal-axis",
1062 "A pointer to the PsppireAxis object for the columns",
1063 G_PARAM_READABLE | G_PARAM_WRITABLE );
1066 g_param_spec_pointer ("model",
1068 "A pointer to the data model",
1069 G_PARAM_READABLE | G_PARAM_WRITABLE );
1072 object_class->set_property = psppire_sheet_set_property;
1073 object_class->get_property = psppire_sheet_get_property;
1075 g_object_class_install_property (object_class,
1079 g_object_class_install_property (object_class,
1083 g_object_class_install_property (object_class,
1087 g_object_class_install_property (object_class,
1092 widget_class->realize = psppire_sheet_realize;
1093 widget_class->unrealize = psppire_sheet_unrealize;
1094 widget_class->map = psppire_sheet_map;
1095 widget_class->unmap = psppire_sheet_unmap;
1096 widget_class->style_set = psppire_sheet_style_set;
1097 widget_class->button_press_event = psppire_sheet_button_press;
1098 widget_class->button_release_event = psppire_sheet_button_release;
1099 widget_class->motion_notify_event = psppire_sheet_motion;
1100 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1101 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1102 widget_class->key_press_event = psppire_sheet_key_press;
1103 widget_class->expose_event = psppire_sheet_expose;
1104 widget_class->size_request = psppire_sheet_size_request;
1105 widget_class->size_allocate = psppire_sheet_size_allocate;
1106 widget_class->focus_in_event = psppire_sheet_focus_in;
1107 widget_class->focus_out_event = NULL;
1109 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1110 klass->select_row = NULL;
1111 klass->select_column = NULL;
1112 klass->select_range = NULL;
1113 klass->resize_range = NULL;
1114 klass->move_range = NULL;
1115 klass->traverse = NULL;
1116 klass->activate = NULL;
1117 klass->changed = NULL;
1121 psppire_sheet_init (PsppireSheet *sheet)
1123 sheet->model = NULL;
1124 sheet->haxis = NULL;
1125 sheet->vaxis = NULL;
1128 sheet->select_status = PSPPIRE_SHEET_NORMAL;
1130 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1131 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1133 sheet->column_title_window = NULL;
1134 sheet->column_title_area.x = 0;
1135 sheet->column_title_area.y = 0;
1136 sheet->column_title_area.width = 0;
1137 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1139 sheet->row_title_window = NULL;
1140 sheet->row_title_area.x = 0;
1141 sheet->row_title_area.y = 0;
1142 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1143 sheet->row_title_area.height = 0;
1146 sheet->active_cell.row = 0;
1147 sheet->active_cell.col = 0;
1149 sheet->range.row0 = 0;
1150 sheet->range.rowi = 0;
1151 sheet->range.col0 = 0;
1152 sheet->range.coli = 0;
1154 sheet->sheet_window = NULL;
1155 sheet->entry_widget = NULL;
1156 sheet->button = NULL;
1158 sheet->hadjustment = NULL;
1159 sheet->vadjustment = NULL;
1161 sheet->cursor_drag = NULL;
1163 sheet->xor_gc = NULL;
1164 sheet->fg_gc = NULL;
1165 sheet->bg_gc = NULL;
1168 sheet->show_grid = TRUE;
1170 sheet->motion_timer = 0;
1172 sheet->row_titles_visible = TRUE;
1173 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1175 sheet->column_titles_visible = TRUE;
1178 /* create sheet entry */
1179 sheet->entry_type = GTK_TYPE_ENTRY;
1180 create_sheet_entry (sheet);
1182 /* create global selection button */
1183 create_global_button (sheet);
1187 /* Cause RANGE to be redrawn. If RANGE is null, then the
1188 entire visible range will be redrawn.
1191 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1195 if ( ! GTK_WIDGET_REALIZED (sheet))
1198 if ( NULL != range )
1199 rectangle_from_range (sheet, range, &rect);
1202 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1203 gdk_region_get_clipbox (r, &rect);
1205 if ( sheet->column_titles_visible)
1207 rect.y += sheet->column_title_area.height;
1208 rect.height -= sheet->column_title_area.height;
1211 if ( sheet->row_titles_visible)
1213 rect.x += sheet->row_title_area.width;
1214 rect.width -= sheet->row_title_area.width;
1218 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1222 /* Callback which occurs whenever columns are inserted / deleted in the model */
1224 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1228 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1230 PsppireSheetRange range;
1231 gint model_columns = psppire_sheet_model_get_column_count (model);
1234 /* Need to update all the columns starting from the first column and onwards.
1235 * Previous column are unchanged, so don't need to be updated.
1237 range.col0 = first_column;
1239 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1240 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1242 adjust_scrollbars (sheet);
1244 if (sheet->active_cell.col >= model_columns)
1245 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1247 draw_column_title_buttons_range (sheet,
1248 first_column, max_visible_column (sheet));
1251 redraw_range (sheet, &range);
1257 /* Callback which occurs whenever rows are inserted / deleted in the model */
1259 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1260 gint n_rows, gpointer data)
1262 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1264 PsppireSheetRange range;
1266 gint model_rows = psppire_sheet_model_get_row_count (model);
1268 /* Need to update all the rows starting from the first row and onwards.
1269 * Previous rows are unchanged, so don't need to be updated.
1271 range.row0 = first_row;
1273 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1274 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1276 adjust_scrollbars (sheet);
1278 if (sheet->active_cell.row >= model_rows)
1279 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1281 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1283 redraw_range (sheet, &range);
1287 If row0 or rowi are negative, then all rows will be updated.
1288 If col0 or coli are negative, then all columns will be updated.
1291 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1292 gint rowi, gint coli, gpointer data)
1294 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1296 PsppireSheetRange range;
1303 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1306 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1308 redraw_range (sheet, NULL);
1309 adjust_scrollbars (sheet);
1311 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1312 max_visible_row (sheet));
1314 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1315 max_visible_column (sheet));
1319 else if ( row0 < 0 || rowi < 0 )
1321 range.row0 = min_visible_row (sheet);
1322 range.rowi = max_visible_row (sheet);
1324 else if ( col0 < 0 || coli < 0 )
1326 range.col0 = min_visible_column (sheet);
1327 range.coli = max_visible_column (sheet);
1330 redraw_range (sheet, &range);
1335 * psppire_sheet_new:
1336 * @rows: initial number of rows
1337 * @columns: initial number of columns
1338 * @title: sheet title
1339 * @model: the model to use for the sheet data
1341 * Creates a new sheet widget with the given number of rows and columns.
1343 * Returns: the new sheet widget
1346 psppire_sheet_new (PsppireSheetModel *model)
1348 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1356 * psppire_sheet_set_model
1357 * @sheet: the sheet to set the model for
1358 * @model: the model to use for the sheet data
1360 * Sets the model for a PsppireSheet
1364 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1366 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1368 if (sheet->model ) g_object_unref (sheet->model);
1370 sheet->model = model;
1374 g_object_ref (model);
1376 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1377 G_CALLBACK (range_update_callback),
1380 g_signal_connect (model, "rows_inserted",
1381 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1383 g_signal_connect (model, "rows_deleted",
1384 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1386 g_signal_connect (model, "columns_inserted",
1387 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1389 g_signal_connect (model, "columns_deleted",
1390 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1396 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1398 g_return_if_fail (sheet != NULL);
1399 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1401 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1402 psppire_sheet_hide_entry_widget (sheet);
1404 sheet->entry_type = entry_type;
1406 create_sheet_entry (sheet);
1408 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1409 psppire_sheet_show_entry_widget (sheet);
1413 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1415 g_return_if_fail (sheet != NULL);
1416 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1418 if (show == sheet->show_grid) return;
1420 sheet->show_grid = show;
1422 redraw_range (sheet, NULL);
1426 psppire_sheet_grid_visible (PsppireSheet *sheet)
1428 g_return_val_if_fail (sheet != NULL, 0);
1429 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1431 return sheet->show_grid;
1435 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1437 g_return_val_if_fail (sheet != NULL, 0);
1438 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1440 return psppire_axis_unit_count (sheet->haxis);
1443 static void set_column_width (PsppireSheet *sheet,
1449 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1451 if (sheet->column_titles_visible) return;
1453 sheet->column_titles_visible = TRUE;
1455 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1458 gdk_window_show (sheet->column_title_window);
1459 gdk_window_move_resize (sheet->column_title_window,
1460 sheet->column_title_area.x,
1461 sheet->column_title_area.y,
1462 sheet->column_title_area.width,
1463 sheet->column_title_area.height);
1465 adjust_scrollbars (sheet);
1467 if (sheet->vadjustment)
1468 g_signal_emit_by_name (sheet->vadjustment,
1471 size_allocate_global_button (sheet);
1473 if ( sheet->row_titles_visible)
1474 gtk_widget_show (sheet->button);
1479 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1481 if (sheet->row_titles_visible) return;
1483 sheet->row_titles_visible = TRUE;
1486 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1488 gdk_window_show (sheet->row_title_window);
1489 gdk_window_move_resize (sheet->row_title_window,
1490 sheet->row_title_area.x,
1491 sheet->row_title_area.y,
1492 sheet->row_title_area.width,
1493 sheet->row_title_area.height);
1495 adjust_scrollbars (sheet);
1498 if (sheet->hadjustment)
1499 g_signal_emit_by_name (sheet->hadjustment,
1502 size_allocate_global_button (sheet);
1504 if ( sheet->column_titles_visible)
1505 gtk_widget_show (sheet->button);
1509 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1511 if (!sheet->column_titles_visible) return;
1513 sheet->column_titles_visible = FALSE;
1515 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1517 if (sheet->column_title_window)
1518 gdk_window_hide (sheet->column_title_window);
1520 gtk_widget_hide (sheet->button);
1522 adjust_scrollbars (sheet);
1525 if (sheet->vadjustment)
1526 g_signal_emit_by_name (sheet->vadjustment,
1531 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1533 if (!sheet->row_titles_visible) return;
1535 sheet->row_titles_visible = FALSE;
1537 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1539 if (sheet->row_title_window)
1540 gdk_window_hide (sheet->row_title_window);
1542 gtk_widget_hide (sheet->button);
1544 adjust_scrollbars (sheet);
1547 if (sheet->hadjustment)
1548 g_signal_emit_by_name (sheet->hadjustment,
1553 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1554 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1555 at the {top,left} of the sheet. If it's 1, then it'll
1556 be placed at the {bottom,right}.
1557 ROW or COL may be -1, in which case scrolling in that dimension
1561 psppire_sheet_moveto (PsppireSheet *sheet,
1569 g_return_if_fail (row_align >= 0);
1570 g_return_if_fail (col_align >= 0);
1572 g_return_if_fail (row_align <= 1);
1573 g_return_if_fail (col_align <= 1);
1575 g_return_if_fail (col <
1576 psppire_axis_unit_count (sheet->haxis));
1577 g_return_if_fail (row <
1578 psppire_axis_unit_count (sheet->vaxis));
1580 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1585 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1587 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1593 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1595 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1603 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1604 const PsppireSheetRange *range)
1606 g_return_val_if_fail (sheet != NULL, FALSE);
1608 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1611 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1614 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1617 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1620 if (range->rowi < min_visible_row (sheet))
1623 if (range->row0 > max_visible_row (sheet))
1626 if (range->coli < min_visible_column (sheet))
1629 if (range->col0 > max_visible_column (sheet))
1636 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1637 gint row, gint column)
1639 PsppireSheetRange range;
1642 range.col0 = column;
1644 range.coli = column;
1646 return psppire_sheet_range_isvisible (sheet, &range);
1650 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1652 g_return_if_fail (sheet != NULL);
1653 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1654 g_return_if_fail (range != NULL);
1656 range->row0 = min_visible_row (sheet);
1657 range->col0 = min_visible_column (sheet);
1658 range->rowi = max_visible_row (sheet);
1659 range->coli = max_visible_column (sheet);
1664 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1665 GtkAdjustment *hadjustment,
1666 GtkAdjustment *vadjustment)
1668 if ( sheet->vadjustment != vadjustment )
1670 if (sheet->vadjustment)
1671 g_object_unref (sheet->vadjustment);
1672 sheet->vadjustment = vadjustment;
1676 g_object_ref (vadjustment);
1678 g_signal_connect (sheet->vadjustment, "value_changed",
1679 G_CALLBACK (vadjustment_value_changed),
1684 if ( sheet->hadjustment != hadjustment )
1686 if (sheet->hadjustment)
1687 g_object_unref (sheet->hadjustment);
1689 sheet->hadjustment = hadjustment;
1693 g_object_ref (hadjustment);
1695 g_signal_connect (sheet->hadjustment, "value_changed",
1696 G_CALLBACK (hadjustment_value_changed),
1704 psppire_sheet_finalize (GObject *object)
1706 PsppireSheet *sheet;
1708 g_return_if_fail (object != NULL);
1709 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1711 sheet = PSPPIRE_SHEET (object);
1713 if (G_OBJECT_CLASS (parent_class)->finalize)
1714 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1718 psppire_sheet_dispose (GObject *object)
1720 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1722 g_return_if_fail (object != NULL);
1723 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1725 if ( sheet->dispose_has_run )
1728 sheet->dispose_has_run = TRUE;
1730 if ( sheet->cell_padding)
1731 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1733 if (sheet->model) g_object_unref (sheet->model);
1734 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1735 if (sheet->haxis) g_object_unref (sheet->haxis);
1737 g_object_unref (sheet->button);
1738 sheet->button = NULL;
1740 /* unref adjustments */
1741 if (sheet->hadjustment)
1743 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1744 G_SIGNAL_MATCH_DATA,
1748 g_object_unref (sheet->hadjustment);
1749 sheet->hadjustment = NULL;
1752 if (sheet->vadjustment)
1754 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1755 G_SIGNAL_MATCH_DATA,
1759 g_object_unref (sheet->vadjustment);
1761 sheet->vadjustment = NULL;
1764 if (G_OBJECT_CLASS (parent_class)->dispose)
1765 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1769 psppire_sheet_style_set (GtkWidget *widget,
1770 GtkStyle *previous_style)
1772 PsppireSheet *sheet;
1774 g_return_if_fail (widget != NULL);
1775 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1777 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1778 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1780 sheet = PSPPIRE_SHEET (widget);
1782 if (GTK_WIDGET_REALIZED (widget))
1784 gtk_style_set_background (widget->style, widget->window, widget->state);
1787 set_entry_widget_font (sheet);
1792 psppire_sheet_realize (GtkWidget *widget)
1794 PsppireSheet *sheet;
1795 GdkWindowAttr attributes;
1796 const gint attributes_mask =
1797 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1800 GdkColormap *colormap;
1801 GdkDisplay *display;
1803 g_return_if_fail (widget != NULL);
1804 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1806 sheet = PSPPIRE_SHEET (widget);
1808 colormap = gtk_widget_get_colormap (widget);
1809 display = gtk_widget_get_display (widget);
1811 attributes.window_type = GDK_WINDOW_CHILD;
1812 attributes.x = widget->allocation.x;
1813 attributes.y = widget->allocation.y;
1814 attributes.width = widget->allocation.width;
1815 attributes.height = widget->allocation.height;
1816 attributes.wclass = GDK_INPUT_OUTPUT;
1818 attributes.visual = gtk_widget_get_visual (widget);
1819 attributes.colormap = colormap;
1821 attributes.event_mask = gtk_widget_get_events (widget);
1822 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1823 GDK_BUTTON_PRESS_MASK |
1824 GDK_BUTTON_RELEASE_MASK |
1825 GDK_KEY_PRESS_MASK |
1826 GDK_ENTER_NOTIFY_MASK |
1827 GDK_LEAVE_NOTIFY_MASK |
1828 GDK_POINTER_MOTION_MASK |
1829 GDK_POINTER_MOTION_HINT_MASK);
1831 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1834 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1836 gdk_window_set_user_data (widget->window, sheet);
1838 widget->style = gtk_style_attach (widget->style, widget->window);
1840 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1842 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1843 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1845 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1846 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1851 attributes.width = sheet->column_title_area.width;
1852 attributes.height = sheet->column_title_area.height;
1855 /* column - title window */
1856 sheet->column_title_window =
1857 gdk_window_new (widget->window, &attributes, attributes_mask);
1858 gdk_window_set_user_data (sheet->column_title_window, sheet);
1859 gtk_style_set_background (widget->style, sheet->column_title_window,
1865 attributes.width = sheet->row_title_area.width;
1866 attributes.height = sheet->row_title_area.height;
1868 /* row - title window */
1869 sheet->row_title_window = gdk_window_new (widget->window,
1870 &attributes, attributes_mask);
1871 gdk_window_set_user_data (sheet->row_title_window, sheet);
1872 gtk_style_set_background (widget->style, sheet->row_title_window,
1875 /* sheet - window */
1876 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1881 sheet->sheet_window = gdk_window_new (widget->window,
1882 &attributes, attributes_mask);
1883 gdk_window_set_user_data (sheet->sheet_window, sheet);
1885 gdk_cursor_unref (attributes.cursor);
1887 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1888 gdk_window_show (sheet->sheet_window);
1891 sheet->fg_gc = gdk_gc_new (widget->window);
1892 sheet->bg_gc = gdk_gc_new (widget->window);
1894 values.foreground = widget->style->white;
1895 values.function = GDK_INVERT;
1896 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1897 values.line_width = MAX (sheet->cell_padding->left,
1898 MAX (sheet->cell_padding->right,
1899 MAX (sheet->cell_padding->top,
1900 sheet->cell_padding->bottom)));
1902 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1910 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1911 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1913 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1914 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1916 sheet->button->style = gtk_style_attach (sheet->button->style,
1917 sheet->sheet_window);
1920 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1922 if (sheet->column_titles_visible)
1923 gdk_window_show (sheet->column_title_window);
1924 if (sheet->row_titles_visible)
1925 gdk_window_show (sheet->row_title_window);
1927 sheet->hover_window = create_hover_window ();
1929 draw_row_title_buttons (sheet);
1930 draw_column_title_buttons (sheet);
1932 psppire_sheet_update_primary_selection (sheet);
1935 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1939 create_global_button (PsppireSheet *sheet)
1941 sheet->button = gtk_button_new_with_label (" ");
1943 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1945 g_object_ref_sink (sheet->button);
1947 g_signal_connect (sheet->button,
1949 G_CALLBACK (global_button_clicked),
1954 size_allocate_global_button (PsppireSheet *sheet)
1956 GtkAllocation allocation;
1958 if (!sheet->column_titles_visible) return;
1959 if (!sheet->row_titles_visible) return;
1961 gtk_widget_size_request (sheet->button, NULL);
1965 allocation.width = sheet->row_title_area.width;
1966 allocation.height = sheet->column_title_area.height;
1968 gtk_widget_size_allocate (sheet->button, &allocation);
1972 global_button_clicked (GtkWidget *widget, gpointer data)
1974 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
1979 psppire_sheet_unrealize (GtkWidget *widget)
1981 PsppireSheet *sheet;
1983 g_return_if_fail (widget != NULL);
1984 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1986 sheet = PSPPIRE_SHEET (widget);
1988 gdk_cursor_unref (sheet->cursor_drag);
1989 sheet->cursor_drag = NULL;
1991 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
1992 sheet->color, n_COLORS);
1994 g_object_unref (sheet->xor_gc);
1995 g_object_unref (sheet->fg_gc);
1996 g_object_unref (sheet->bg_gc);
1998 destroy_hover_window (sheet->hover_window);
2000 gdk_window_destroy (sheet->sheet_window);
2001 gdk_window_destroy (sheet->column_title_window);
2002 gdk_window_destroy (sheet->row_title_window);
2004 gtk_widget_unparent (sheet->entry_widget);
2005 if (sheet->button != NULL)
2006 gtk_widget_unparent (sheet->button);
2008 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2009 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2013 psppire_sheet_map (GtkWidget *widget)
2015 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2017 g_return_if_fail (widget != NULL);
2018 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2020 if (!GTK_WIDGET_MAPPED (widget))
2022 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2024 gdk_window_show (widget->window);
2025 gdk_window_show (sheet->sheet_window);
2027 if (sheet->column_titles_visible)
2029 draw_column_title_buttons (sheet);
2030 gdk_window_show (sheet->column_title_window);
2032 if (sheet->row_titles_visible)
2034 draw_row_title_buttons (sheet);
2035 gdk_window_show (sheet->row_title_window);
2038 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2039 && sheet->active_cell.row >= 0
2040 && sheet->active_cell.col >= 0 )
2042 gtk_widget_show (sheet->entry_widget);
2043 gtk_widget_map (sheet->entry_widget);
2046 if (!GTK_WIDGET_MAPPED (sheet->button))
2048 gtk_widget_show (sheet->button);
2049 gtk_widget_map (sheet->button);
2052 redraw_range (sheet, NULL);
2053 change_active_cell (sheet,
2054 sheet->active_cell.row,
2055 sheet->active_cell.col);
2060 psppire_sheet_unmap (GtkWidget *widget)
2062 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2064 if (!GTK_WIDGET_MAPPED (widget))
2067 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2069 gdk_window_hide (sheet->sheet_window);
2070 if (sheet->column_titles_visible)
2071 gdk_window_hide (sheet->column_title_window);
2072 if (sheet->row_titles_visible)
2073 gdk_window_hide (sheet->row_title_window);
2074 gdk_window_hide (widget->window);
2076 gtk_widget_unmap (sheet->entry_widget);
2077 gtk_widget_unmap (sheet->button);
2078 gtk_widget_unmap (sheet->hover_window->window);
2081 /* get cell attributes of the given cell */
2082 /* TRUE means that the cell is currently allocated */
2083 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2085 PsppireSheetCellAttr *attributes);
2090 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2092 PangoLayout *layout;
2093 PangoRectangle text;
2094 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2099 PsppireSheetCellAttr attributes;
2102 g_return_if_fail (sheet != NULL);
2104 /* bail now if we aren't yet drawable */
2105 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2108 row >= psppire_axis_unit_count (sheet->vaxis))
2112 col >= psppire_axis_unit_count (sheet->haxis))
2115 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2117 /* select GC for background rectangle */
2118 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2119 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2121 rectangle_from_cell (sheet, row, col, &area);
2123 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2125 if (sheet->show_grid)
2127 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2129 gdk_draw_rectangle (sheet->sheet_window,
2133 area.width, area.height);
2137 label = psppire_sheet_cell_get_text (sheet, row, col);
2142 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2143 dispose_string (sheet, label);
2146 pango_layout_set_font_description (layout, font_desc);
2148 pango_layout_get_pixel_extents (layout, NULL, &text);
2150 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2152 font_height = pango_font_description_get_size (font_desc);
2153 if ( !pango_font_description_get_size_is_absolute (font_desc))
2154 font_height /= PANGO_SCALE;
2157 if ( sheet->cell_padding )
2159 area.x += sheet->cell_padding->left;
2160 area.width -= sheet->cell_padding->right
2161 + sheet->cell_padding->left;
2163 area.y += sheet->cell_padding->top;
2164 area.height -= sheet->cell_padding->bottom
2166 sheet->cell_padding->top;
2169 /* Centre the text vertically */
2170 area.y += (area.height - font_height) / 2.0;
2172 switch (attributes.justification)
2174 case GTK_JUSTIFY_RIGHT:
2175 area.x += area.width - text.width;
2177 case GTK_JUSTIFY_CENTER:
2178 area.x += (area.width - text.width) / 2.0;
2180 case GTK_JUSTIFY_LEFT:
2184 g_critical ("Unhandled justification %d in column %d\n",
2185 attributes.justification, col);
2189 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2194 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2195 g_object_unref (layout);
2200 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2202 PsppireSheetRange range;
2207 PsppireSheetRange drawing_range;
2209 gdk_region_get_clipbox (region, &area);
2211 y = area.y + sheet->vadjustment->value;
2212 x = area.x + sheet->hadjustment->value;
2214 if ( sheet->column_titles_visible)
2215 y -= sheet->column_title_area.height;
2217 if ( sheet->row_titles_visible)
2218 x -= sheet->row_title_area.width;
2220 maximize_int (&x, 0);
2221 maximize_int (&y, 0);
2223 range.row0 = row_from_ypixel (sheet, y);
2224 range.rowi = row_from_ypixel (sheet, y + area.height);
2226 range.col0 = column_from_xpixel (sheet, x);
2227 range.coli = column_from_xpixel (sheet, x + area.width);
2229 g_return_if_fail (sheet != NULL);
2230 g_return_if_fail (PSPPIRE_SHEET (sheet));
2232 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2233 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2234 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2237 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2238 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2239 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2240 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2242 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2243 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2245 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2247 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2248 psppire_sheet_cell_draw (sheet, i, j);
2251 if (sheet->select_status == PSPPIRE_SHEET_NORMAL &&
2252 sheet->active_cell.row >= drawing_range.row0 &&
2253 sheet->active_cell.row <= drawing_range.rowi &&
2254 sheet->active_cell.col >= drawing_range.col0 &&
2255 sheet->active_cell.col <= drawing_range.coli)
2256 psppire_sheet_show_entry_widget (sheet);
2260 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2261 GtkJustification justification,
2264 PsppireSheetModel *model ;
2267 g_return_if_fail (sheet != NULL);
2268 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2270 if (col >= psppire_axis_unit_count (sheet->haxis)
2271 || row >= psppire_axis_unit_count (sheet->vaxis))
2274 if (col < 0 || row < 0) return;
2276 model = psppire_sheet_get_model (sheet);
2278 old_text = psppire_sheet_model_get_string (model, row, col);
2280 if (0 != g_strcmp0 (old_text, text))
2282 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2283 psppire_sheet_model_set_string (model, text, row, col);
2284 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2287 if ( psppire_sheet_model_free_strings (model))
2293 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2295 PsppireSheetRange range;
2297 g_return_if_fail (sheet != NULL);
2298 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2299 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2300 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2302 if (column < 0 || row < 0) return;
2306 range.col0 = min_visible_column (sheet);
2307 range.coli = max_visible_column (sheet);
2309 psppire_sheet_real_cell_clear (sheet, row, column);
2311 redraw_range (sheet, &range);
2315 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2317 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2319 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2321 if (old_text && strlen (old_text) > 0 )
2323 psppire_sheet_model_datum_clear (model, row, column);
2326 dispose_string (sheet, old_text);
2330 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2332 PsppireSheetModel *model;
2333 g_return_val_if_fail (sheet != NULL, NULL);
2334 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2336 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2338 if (col < 0 || row < 0) return NULL;
2340 model = psppire_sheet_get_model (sheet);
2345 return psppire_sheet_model_get_string (model, row, col);
2349 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2350 If the function returns FALSE, then the results will be unreliable.
2353 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2361 *column = -G_MAXINT;
2363 g_return_val_if_fail (sheet != NULL, 0);
2364 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2366 /* bounds checking, return false if the user clicked
2374 if ( sheet->column_titles_visible)
2375 y -= sheet->column_title_area.height;
2377 y += sheet->vadjustment->value;
2379 if ( y < 0 && sheet->column_titles_visible)
2385 trow = row_from_ypixel (sheet, y);
2386 if (trow > psppire_axis_unit_count (sheet->vaxis))
2392 if ( sheet->row_titles_visible)
2393 x -= sheet->row_title_area.width;
2395 x += sheet->hadjustment->value;
2397 if ( x < 0 && sheet->row_titles_visible)
2403 tcol = column_from_xpixel (sheet, x);
2404 if (tcol > psppire_axis_unit_count (sheet->haxis))
2414 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2419 g_return_val_if_fail (sheet != NULL, 0);
2420 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2422 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2425 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2426 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2428 area->width= (column == -1) ? sheet->row_title_area.width
2429 : psppire_axis_unit_size (sheet->haxis, column);
2431 area->height= (row == -1) ? sheet->column_title_area.height
2432 : psppire_axis_unit_size (sheet->vaxis, row);
2438 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2440 g_return_if_fail (sheet != NULL);
2441 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2443 if (row < -1 || col < -1)
2446 if (row >= psppire_axis_unit_count (sheet->vaxis)
2448 col >= psppire_axis_unit_count (sheet->haxis))
2451 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2454 if ( row == -1 || col == -1)
2456 psppire_sheet_hide_entry_widget (sheet);
2460 change_active_cell (sheet, row, col);
2464 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2466 g_return_if_fail (sheet != NULL);
2467 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2469 if ( row ) *row = sheet->active_cell.row;
2470 if (column) *column = sheet->active_cell.col;
2474 entry_load_text (PsppireSheet *sheet)
2478 GtkJustification justification;
2479 PsppireSheetCellAttr attributes;
2481 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2482 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2484 row = sheet->active_cell.row;
2485 col = sheet->active_cell.col;
2487 if (row < 0 || col < 0) return;
2489 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2491 if (text && strlen (text) > 0)
2493 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2494 justification = attributes.justification;
2495 psppire_sheet_set_cell (sheet, row, col, justification, text);
2501 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2503 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2506 if (sheet->active_cell.row < 0 ||
2507 sheet->active_cell.col < 0) return;
2509 gtk_widget_hide (sheet->entry_widget);
2510 gtk_widget_unmap (sheet->entry_widget);
2512 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2516 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2518 gint old_row, old_col;
2520 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2522 if (row < 0 || col < 0)
2525 if ( row > psppire_axis_unit_count (sheet->vaxis)
2526 || col > psppire_axis_unit_count (sheet->haxis))
2529 old_row = sheet->active_cell.row;
2530 old_col = sheet->active_cell.col;
2532 entry_load_text (sheet);
2534 /* Erase the old cell border */
2535 psppire_sheet_draw_active_cell (sheet);
2537 sheet->active_cell.row = row;
2538 sheet->active_cell.col = col;
2540 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2542 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2544 psppire_sheet_draw_active_cell (sheet);
2545 psppire_sheet_show_entry_widget (sheet);
2547 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2549 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2550 row, col, old_row, old_col);
2555 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2557 GtkEntry *sheet_entry;
2558 PsppireSheetCellAttr attributes;
2562 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2564 row = sheet->active_cell.row;
2565 col = sheet->active_cell.col;
2567 /* Don't show the active cell, if there is no active cell: */
2568 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2571 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2572 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2573 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2575 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2577 sheet_entry = psppire_sheet_get_entry (sheet);
2579 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2581 if (GTK_IS_ENTRY (sheet_entry))
2583 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2584 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2587 text = g_strdup ("");
2589 if (strcmp (old_text, text) != 0)
2590 gtk_entry_set_text (sheet_entry, text);
2592 dispose_string (sheet, text);
2595 switch (attributes.justification)
2597 case GTK_JUSTIFY_RIGHT:
2598 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2600 case GTK_JUSTIFY_CENTER:
2601 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2603 case GTK_JUSTIFY_LEFT:
2605 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2611 psppire_sheet_size_allocate_entry (sheet);
2613 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2614 psppire_sheet_model_is_editable (sheet->model,
2616 gtk_widget_map (sheet->entry_widget);
2620 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2623 PsppireSheetRange range;
2625 row = sheet->active_cell.row;
2626 col = sheet->active_cell.col;
2628 if (row < 0 || col < 0) return FALSE;
2630 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2633 range.col0 = range.coli = col;
2634 range.row0 = range.rowi = row;
2636 psppire_sheet_draw_border (sheet, range);
2644 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
2648 rectangle_from_range (sheet, &new_range, &area);
2653 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
2655 area.x += sheet->cell_padding->left / 2;
2656 area.y += sheet->cell_padding->top / 2;
2657 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
2658 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
2660 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
2667 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
2672 /* Selection related functions */
2675 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
2678 sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
2680 sheet->range.col0 = sheet->range.coli = -1;
2681 sheet->range.row0 = sheet->range.rowi = row;
2683 rectangle_from_range (sheet, &sheet->range, &area);
2687 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2689 g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, row);
2693 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
2696 sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
2698 sheet->range.col0 = sheet->range.coli = column;
2699 sheet->range.row0 = sheet->range.rowi = -1;
2701 rectangle_from_range (sheet, &sheet->range, &area);
2705 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2707 g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, column);
2712 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
2715 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
2717 sheet->range = *range;
2719 rectangle_from_range (sheet, range, &area);
2722 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2727 psppire_sheet_unselect_range (PsppireSheet *sheet)
2730 sheet->select_status = PSPPIRE_SHEET_NORMAL;
2732 rectangle_from_range (sheet, &sheet->range, &area);
2735 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2737 g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, -1);
2738 g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, -1);
2742 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
2744 g_return_if_fail (sheet != NULL);
2745 *range = sheet->range;
2750 psppire_sheet_expose (GtkWidget *widget, GdkEventExpose *event)
2752 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2754 g_return_val_if_fail (event != NULL, FALSE);
2756 if (!GTK_WIDGET_DRAWABLE (widget))
2759 /* exposure events on the sheet */
2760 if (event->window == sheet->row_title_window &&
2761 sheet->row_titles_visible)
2763 draw_row_title_buttons_range (sheet,
2764 min_visible_row (sheet),
2765 max_visible_row (sheet));
2768 if (event->window == sheet->column_title_window &&
2769 sheet->column_titles_visible)
2771 draw_column_title_buttons_range (sheet,
2772 min_visible_column (sheet),
2773 max_visible_column (sheet));
2776 if (event->window == sheet->sheet_window)
2778 draw_sheet_region (sheet, event->region);
2780 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2783 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2784 psppire_sheet_range_draw (sheet, &sheet->range);
2786 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2787 psppire_sheet_range_draw (sheet, &sheet->drag_range);
2793 rectangle_from_range (sheet, &sheet->range, &area);
2795 gdk_draw_rectangle (sheet->sheet_window,
2798 area.x + 1, area.y + 1,
2799 area.width, area.height);
2803 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2804 draw_xor_rectangle (sheet, sheet->drag_range);
2809 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
2812 PsppireSheetRange range;
2813 range.row0 = range.rowi = sheet->active_cell.row;
2814 range.col0 = range.coli = sheet->active_cell.col;
2816 rectangle_from_range (sheet, &range, &rect);
2818 if (GDK_OVERLAP_RECTANGLE_OUT !=
2819 gdk_region_rect_in (event->region, &rect))
2821 psppire_sheet_draw_active_cell (sheet);
2827 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2834 psppire_sheet_button_press (GtkWidget *widget, GdkEventButton *event)
2836 PsppireSheet *sheet;
2837 GdkModifierType mods;
2842 g_return_val_if_fail (widget != NULL, FALSE);
2843 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
2844 g_return_val_if_fail (event != NULL, FALSE);
2846 sheet = PSPPIRE_SHEET (widget);
2848 /* Cancel any pending tooltips */
2849 if (sheet->motion_timer)
2851 g_source_remove (sheet->motion_timer);
2852 sheet->motion_timer = 0;
2855 gtk_widget_get_pointer (widget, &x, &y);
2856 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2859 if (event->window == sheet->column_title_window)
2861 sheet->x_drag = event->x;
2862 g_signal_emit (sheet,
2863 sheet_signals[BUTTON_EVENT_COLUMN], 0,
2866 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2868 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2869 g_signal_emit (sheet,
2870 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
2874 if (event->window == sheet->row_title_window)
2876 g_signal_emit (sheet,
2877 sheet_signals[BUTTON_EVENT_ROW], 0,
2880 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2882 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2883 g_signal_emit (sheet,
2884 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
2888 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
2890 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
2893 /* press on resize windows */
2894 if (event->window == sheet->column_title_window)
2896 sheet->x_drag = event->x;
2898 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
2900 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
2901 gdk_pointer_grab (sheet->column_title_window, FALSE,
2902 GDK_POINTER_MOTION_HINT_MASK |
2903 GDK_BUTTON1_MOTION_MASK |
2904 GDK_BUTTON_RELEASE_MASK,
2905 NULL, NULL, event->time);
2907 draw_xor_vline (sheet);
2912 if (event->window == sheet->row_title_window)
2914 sheet->y_drag = event->y;
2916 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
2918 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
2919 gdk_pointer_grab (sheet->row_title_window, FALSE,
2920 GDK_POINTER_MOTION_HINT_MASK |
2921 GDK_BUTTON1_MOTION_MASK |
2922 GDK_BUTTON_RELEASE_MASK,
2923 NULL, NULL, event->time);
2925 draw_xor_hline (sheet);
2930 /* the sheet itself does not handle other than single click events */
2931 if (event->type != GDK_BUTTON_PRESS) return FALSE;
2933 /* selections on the sheet */
2934 if (event->window == sheet->sheet_window)
2936 gtk_widget_get_pointer (widget, &x, &y);
2937 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2938 gdk_pointer_grab (sheet->sheet_window, FALSE,
2939 GDK_POINTER_MOTION_HINT_MASK |
2940 GDK_BUTTON1_MOTION_MASK |
2941 GDK_BUTTON_RELEASE_MASK,
2942 NULL, NULL, event->time);
2943 gtk_grab_add (GTK_WIDGET (sheet));
2945 if ( sheet->select_status == PSPPIRE_SHEET_NORMAL)
2947 sheet->range.row0 = row;
2948 sheet->range.col0 = column;
2952 psppire_sheet_unselect_range (sheet);
2954 psppire_sheet_click_cell (sheet, row, column);
2957 if (event->window == sheet->column_title_window)
2959 gtk_widget_get_pointer (widget, &x, &y);
2960 if ( sheet->row_titles_visible)
2961 x -= sheet->row_title_area.width;
2963 x += sheet->hadjustment->value;
2965 column = column_from_xpixel (sheet, x);
2967 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2969 gtk_grab_add (GTK_WIDGET (sheet));
2970 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2974 if (event->window == sheet->row_title_window)
2976 gtk_widget_get_pointer (widget, &x, &y);
2977 if ( sheet->column_titles_visible)
2978 y -= sheet->column_title_area.height;
2980 y += sheet->vadjustment->value;
2982 row = row_from_ypixel (sheet, y);
2983 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2985 gtk_grab_add (GTK_WIDGET (sheet));
2986 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2994 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
2996 PsppireSheetCell cell;
2997 gboolean forbid_move;
3002 if (row >= psppire_axis_unit_count (sheet->vaxis)
3003 || column >= psppire_axis_unit_count (sheet->haxis))
3008 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3009 &sheet->active_cell,
3015 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3018 row = sheet->active_cell.row;
3019 column = sheet->active_cell.col;
3021 change_active_cell (sheet, row, column);
3025 if (row == -1 && column >= 0)
3027 psppire_sheet_select_column (sheet, column);
3031 if (column == -1 && row >= 0)
3033 psppire_sheet_select_row (sheet, row);
3037 if (row == -1 && column == -1)
3039 sheet->range.row0 = 0;
3040 sheet->range.col0 = 0;
3041 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3043 psppire_axis_unit_count (sheet->haxis) - 1;
3044 psppire_sheet_select_range (sheet, NULL);
3048 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3049 change_active_cell (sheet, row, column);
3051 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3057 psppire_sheet_button_release (GtkWidget *widget,
3058 GdkEventButton *event)
3060 GdkDisplay *display = gtk_widget_get_display (widget);
3062 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3064 /* release on resize windows */
3065 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3068 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3069 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3071 gdk_display_pointer_ungrab (display, event->time);
3072 draw_xor_vline (sheet);
3075 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3076 + sheet->hadjustment->value;
3078 set_column_width (sheet, sheet->drag_cell.col, width);
3083 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3086 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3087 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3089 gdk_display_pointer_ungrab (display, event->time);
3090 draw_xor_hline (sheet);
3093 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3094 sheet->vadjustment->value;
3096 set_row_height (sheet, sheet->drag_cell.row, height);
3101 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3103 PsppireSheetRange old_range;
3104 draw_xor_rectangle (sheet, sheet->drag_range);
3105 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3106 gdk_display_pointer_ungrab (display, event->time);
3108 psppire_sheet_unselect_range (sheet);
3110 old_range = sheet->range;
3111 sheet->range = sheet->drag_range;
3112 sheet->drag_range = old_range;
3113 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3114 &sheet->drag_range, &sheet->range);
3115 psppire_sheet_select_range (sheet, &sheet->range);
3118 if (PSPPIRE_SHEET_IN_SELECTION (sheet))
3120 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3121 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3123 change_active_cell (sheet, sheet->active_cell.row,
3124 sheet->active_cell.col);
3127 gdk_display_pointer_ungrab (display, event->time);
3128 gtk_grab_remove (GTK_WIDGET (sheet));
3130 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3139 /* Shamelessly lifted from gtktooltips */
3141 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3145 gtk_widget_size_request (tip_window, &req);
3146 gtk_paint_flat_box (tip_window->style, tip_window->window,
3147 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3148 NULL, GTK_WIDGET(tip_window), "tooltip",
3149 0, 0, req.width, req.height);
3155 destroy_hover_window (PsppireSheetHoverTitle *h)
3157 gtk_widget_destroy (h->window);
3161 static PsppireSheetHoverTitle *
3162 create_hover_window (void)
3164 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3166 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3168 #if GTK_CHECK_VERSION (2, 9, 0)
3169 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3170 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3173 gtk_widget_set_app_paintable (hw->window, TRUE);
3174 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3175 gtk_widget_set_name (hw->window, "gtk-tooltips");
3176 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3178 g_signal_connect (hw->window,
3180 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3183 hw->label = gtk_label_new (NULL);
3186 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3187 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3189 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3191 gtk_widget_show (hw->label);
3193 g_signal_connect (hw->window,
3195 G_CALLBACK (gtk_widget_destroyed),
3201 #define HOVER_WINDOW_Y_OFFSET 2
3204 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3205 const gchar *subtitle)
3214 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3218 sheet->hover_window->row = row;
3219 sheet->hover_window->column = column;
3221 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3223 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3225 gtk_widget_show (sheet->hover_window->window);
3227 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3233 y += sheet->column_title_area.y;
3234 y += sheet->column_title_area.height;
3235 y += HOVER_WINDOW_Y_OFFSET;
3241 x += sheet->row_title_area.x;
3242 x += sheet->row_title_area.width * 2 / 3.0;
3245 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3250 motion_timeout_callback (gpointer data)
3252 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3256 gdk_threads_enter ();
3257 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3259 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3261 if (sheet->row_title_under && row >= 0)
3263 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3265 show_subtitle (sheet, row, -1, text);
3269 if (sheet->column_title_under && column >= 0)
3271 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3274 show_subtitle (sheet, -1, column, text);
3280 gdk_threads_leave ();
3285 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3287 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3288 GdkModifierType mods;
3289 GdkCursorType new_cursor;
3292 GdkDisplay *display;
3294 g_return_val_if_fail (event != NULL, FALSE);
3296 display = gtk_widget_get_display (widget);
3298 /* selections on the sheet */
3302 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3304 if ( sheet->motion_timer > 0 )
3305 g_source_remove (sheet->motion_timer);
3306 sheet->motion_timer =
3307 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3313 gtk_widget_get_pointer (widget, &wx, &wy);
3315 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3317 if ( row != sheet->hover_window->row ||
3318 column != sheet->hover_window->column)
3320 gtk_widget_hide (sheet->hover_window->window);
3325 if (event->window == sheet->column_title_window)
3327 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3328 on_column_boundary (sheet, x, &column))
3330 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3331 if (new_cursor != sheet->cursor_drag->type)
3333 gdk_cursor_unref (sheet->cursor_drag);
3334 sheet->cursor_drag =
3335 gdk_cursor_new_for_display (display, new_cursor);
3337 gdk_window_set_cursor (sheet->column_title_window,
3338 sheet->cursor_drag);
3343 new_cursor = GDK_TOP_LEFT_ARROW;
3344 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3345 new_cursor != sheet->cursor_drag->type)
3347 gdk_cursor_unref (sheet->cursor_drag);
3348 sheet->cursor_drag =
3349 gdk_cursor_new_for_display (display, new_cursor);
3350 gdk_window_set_cursor (sheet->column_title_window,
3351 sheet->cursor_drag);
3355 else if (event->window == sheet->row_title_window)
3357 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3358 on_row_boundary (sheet, y, &row))
3360 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3361 if (new_cursor != sheet->cursor_drag->type)
3363 gdk_cursor_unref (sheet->cursor_drag);
3364 sheet->cursor_drag =
3365 gdk_cursor_new_for_display (display, new_cursor);
3366 gdk_window_set_cursor (sheet->row_title_window,
3367 sheet->cursor_drag);
3372 new_cursor = GDK_TOP_LEFT_ARROW;
3373 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3374 new_cursor != sheet->cursor_drag->type)
3376 gdk_cursor_unref (sheet->cursor_drag);
3377 sheet->cursor_drag =
3378 gdk_cursor_new_for_display (display, new_cursor);
3379 gdk_window_set_cursor (sheet->row_title_window,
3380 sheet->cursor_drag);
3385 new_cursor = GDK_PLUS;
3386 if ( event->window == sheet->sheet_window &&
3387 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3388 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3389 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3390 new_cursor != sheet->cursor_drag->type)
3392 gdk_cursor_unref (sheet->cursor_drag);
3393 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3394 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3397 new_cursor = GDK_TOP_LEFT_ARROW;
3398 if ( event->window == sheet->sheet_window &&
3399 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ) &&
3400 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3401 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3402 new_cursor != sheet->cursor_drag->type)
3404 gdk_cursor_unref (sheet->cursor_drag);
3405 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3406 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3409 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3410 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3412 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3414 if (event->x != sheet->x_drag)
3416 draw_xor_vline (sheet);
3417 sheet->x_drag = event->x;
3418 draw_xor_vline (sheet);
3424 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3426 if (event->y != sheet->y_drag)
3428 draw_xor_hline (sheet);
3429 sheet->y_drag = event->y;
3430 draw_xor_hline (sheet);
3436 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3438 PsppireSheetRange aux;
3439 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3440 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3441 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3442 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3446 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3447 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3449 aux = sheet->drag_range;
3450 sheet->drag_range.row0 = sheet->range.row0 + row;
3451 sheet->drag_range.col0 = sheet->range.col0 + column;
3452 sheet->drag_range.rowi = sheet->range.rowi + row;
3453 sheet->drag_range.coli = sheet->range.coli + column;
3454 if (aux.row0 != sheet->drag_range.row0 ||
3455 aux.col0 != sheet->drag_range.col0)
3457 draw_xor_rectangle (sheet, aux);
3458 draw_xor_rectangle (sheet, sheet->drag_range);
3464 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3466 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
3467 column == sheet->active_cell.col) return TRUE;
3469 if ( mods & GDK_BUTTON1_MASK)
3471 if (PSPPIRE_SHEET_IN_SELECTION (sheet) )
3473 /* Redraw the old range */
3474 psppire_sheet_unselect_range (sheet);
3476 sheet->range.rowi = row;
3477 sheet->range.coli = column;
3479 /* Redraw the new range */
3480 psppire_sheet_select_range (sheet, &sheet->range);
3484 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3492 psppire_sheet_crossing_notify (GtkWidget *widget,
3493 GdkEventCrossing *event)
3495 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3497 if (event->window == sheet->column_title_window)
3498 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
3499 else if (event->window == sheet->row_title_window)
3500 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
3502 if (event->type == GDK_LEAVE_NOTIFY)
3503 gtk_widget_hide (sheet->hover_window->window);
3510 psppire_sheet_focus_in (GtkWidget *w,
3511 GdkEventFocus *event)
3513 PsppireSheet *sheet = PSPPIRE_SHEET (w);
3515 gtk_widget_grab_focus (sheet->entry_widget);
3523 psppire_sheet_entry_key_press (GtkWidget *widget,
3527 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
3532 /* Number of rows in a step-increment */
3533 #define ROWS_PER_STEP 1
3537 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
3539 gint old_row = sheet->active_cell.row ;
3540 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
3544 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
3545 min_visible_row (sheet));
3549 case GTK_SCROLL_PAGE_DOWN:
3550 gtk_adjustment_set_value (sheet->vadjustment,
3551 sheet->vadjustment->value +
3552 sheet->vadjustment->page_increment);
3554 case GTK_SCROLL_PAGE_UP:
3555 gtk_adjustment_set_value (sheet->vadjustment,
3556 sheet->vadjustment->value -
3557 sheet->vadjustment->page_increment);
3561 g_assert_not_reached ();
3566 vpixel += psppire_axis_start_pixel (sheet->vaxis,
3567 min_visible_row (sheet));
3569 new_row = row_from_ypixel (sheet, vpixel);
3571 change_active_cell (sheet, new_row,
3572 sheet->active_cell.col);
3577 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
3579 gint current_row = sheet->active_cell.row;
3580 gint current_col = sheet->active_cell.col;
3581 PsppireSheetCell new_cell ;
3582 gboolean forbidden = FALSE;
3584 new_cell.row = current_row;
3585 new_cell.col = current_col;
3589 case GTK_SCROLL_STEP_DOWN:
3592 case GTK_SCROLL_STEP_UP:
3595 case GTK_SCROLL_STEP_RIGHT:
3598 case GTK_SCROLL_STEP_LEFT:
3601 case GTK_SCROLL_STEP_FORWARD:
3604 psppire_sheet_model_get_column_count (sheet->model))
3610 case GTK_SCROLL_STEP_BACKWARD:
3612 if (new_cell.col < 0)
3615 psppire_sheet_model_get_column_count (sheet->model) - 1;
3620 g_assert_not_reached ();
3624 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3625 &sheet->active_cell,
3633 maximize_int (&new_cell.row, 0);
3634 maximize_int (&new_cell.col, 0);
3636 minimize_int (&new_cell.row,
3637 psppire_axis_unit_count (sheet->vaxis) - 1);
3639 minimize_int (&new_cell.col,
3640 psppire_axis_unit_count (sheet->haxis) - 1);
3642 change_active_cell (sheet, new_cell.row, new_cell.col);
3645 if ( new_cell.col > max_fully_visible_column (sheet))
3648 psppire_axis_start_pixel (sheet->haxis,
3650 hpos -= sheet->hadjustment->page_size;
3652 gtk_adjustment_set_value (sheet->hadjustment,
3655 else if ( new_cell.col < min_fully_visible_column (sheet))
3658 psppire_axis_start_pixel (sheet->haxis,
3661 gtk_adjustment_set_value (sheet->hadjustment,
3666 if ( new_cell.row > max_fully_visible_row (sheet))
3669 psppire_axis_start_pixel (sheet->vaxis,
3671 vpos -= sheet->vadjustment->page_size;
3673 gtk_adjustment_set_value (sheet->vadjustment,
3676 else if ( new_cell.row < min_fully_visible_row (sheet))
3679 psppire_axis_start_pixel (sheet->vaxis,
3682 gtk_adjustment_set_value (sheet->vadjustment,
3686 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3691 psppire_sheet_key_press (GtkWidget *widget,
3694 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3696 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3698 switch (key->keyval)
3701 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
3704 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
3706 case GDK_ISO_Left_Tab:
3707 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
3710 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
3714 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3717 step_sheet (sheet, GTK_SCROLL_STEP_UP);
3721 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
3724 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
3728 gtk_adjustment_set_value (sheet->vadjustment,
3729 sheet->vadjustment->lower);
3731 change_active_cell (sheet, 0,
3732 sheet->active_cell.col);
3737 gtk_adjustment_set_value (sheet->vadjustment,
3738 sheet->vadjustment->upper -
3739 sheet->vadjustment->page_size -
3740 sheet->vadjustment->page_increment);
3743 change_active_cellx (sheet,
3744 psppire_axis_unit_count (sheet->vaxis) - 1,
3745 sheet->active_cell.col);
3749 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
3760 psppire_sheet_size_request (GtkWidget *widget,
3761 GtkRequisition *requisition)
3763 PsppireSheet *sheet;
3765 g_return_if_fail (widget != NULL);
3766 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3767 g_return_if_fail (requisition != NULL);
3769 sheet = PSPPIRE_SHEET (widget);
3771 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
3772 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
3774 /* compute the size of the column title area */
3775 if (sheet->column_titles_visible)
3776 requisition->height += sheet->column_title_area.height;
3778 /* compute the size of the row title area */
3779 if (sheet->row_titles_visible)
3780 requisition->width += sheet->row_title_area.width;
3785 psppire_sheet_size_allocate (GtkWidget *widget,
3786 GtkAllocation *allocation)
3788 PsppireSheet *sheet;
3789 GtkAllocation sheet_allocation;
3792 g_return_if_fail (widget != NULL);
3793 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3794 g_return_if_fail (allocation != NULL);
3796 sheet = PSPPIRE_SHEET (widget);
3797 widget->allocation = *allocation;
3798 border_width = GTK_CONTAINER (widget)->border_width;
3800 if (GTK_WIDGET_REALIZED (widget))
3801 gdk_window_move_resize (widget->window,
3802 allocation->x + border_width,
3803 allocation->y + border_width,
3804 allocation->width - 2 * border_width,
3805 allocation->height - 2 * border_width);
3807 sheet_allocation.x = 0;
3808 sheet_allocation.y = 0;
3809 sheet_allocation.width = allocation->width - 2 * border_width;
3810 sheet_allocation.height = allocation->height - 2 * border_width;
3812 if (GTK_WIDGET_REALIZED (widget))
3813 gdk_window_move_resize (sheet->sheet_window,
3816 sheet_allocation.width,
3817 sheet_allocation.height);
3819 /* position the window which holds the column title buttons */
3820 sheet->column_title_area.x = 0;
3821 sheet->column_title_area.y = 0;
3822 sheet->column_title_area.width = sheet_allocation.width ;
3825 /* position the window which holds the row title buttons */
3826 sheet->row_title_area.x = 0;
3827 sheet->row_title_area.y = 0;
3828 sheet->row_title_area.height = sheet_allocation.height;
3830 if (sheet->row_titles_visible)
3831 sheet->column_title_area.x += sheet->row_title_area.width;
3833 if (sheet->column_titles_visible)
3834 sheet->row_title_area.y += sheet->column_title_area.height;
3836 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
3837 gdk_window_move_resize (sheet->column_title_window,
3838 sheet->column_title_area.x,
3839 sheet->column_title_area.y,
3840 sheet->column_title_area.width,
3841 sheet->column_title_area.height);
3844 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
3845 gdk_window_move_resize (sheet->row_title_window,
3846 sheet->row_title_area.x,
3847 sheet->row_title_area.y,
3848 sheet->row_title_area.width,
3849 sheet->row_title_area.height);
3851 size_allocate_global_button (sheet);
3855 gint width = sheet->column_title_area.width;
3857 if ( sheet->row_titles_visible)
3858 width -= sheet->row_title_area.width;
3860 g_object_set (sheet->haxis,
3861 "minimum-extent", width,
3868 gint height = sheet->row_title_area.height;
3870 if ( sheet->column_titles_visible)
3871 height -= sheet->column_title_area.height;
3873 g_object_set (sheet->vaxis,
3874 "minimum-extent", height,
3879 /* set the scrollbars adjustments */
3880 adjust_scrollbars (sheet);
3884 draw_column_title_buttons (PsppireSheet *sheet)
3888 if (!sheet->column_titles_visible) return;
3889 if (!GTK_WIDGET_REALIZED (sheet))
3892 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
3895 if (sheet->row_titles_visible)
3897 x = sheet->row_title_area.width;
3900 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
3902 sheet->column_title_area.width = width;
3903 sheet->column_title_area.x = x;
3904 gdk_window_move_resize (sheet->column_title_window,
3905 sheet->column_title_area.x,
3906 sheet->column_title_area.y,
3907 sheet->column_title_area.width,
3908 sheet->column_title_area.height);
3911 if (max_visible_column (sheet) ==
3912 psppire_axis_unit_count (sheet->haxis) - 1)
3913 gdk_window_clear_area (sheet->column_title_window,
3915 sheet->column_title_area.width,
3916 sheet->column_title_area.height);
3918 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
3920 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
3921 max_visible_column (sheet));
3925 draw_row_title_buttons (PsppireSheet *sheet)
3930 if (!sheet->row_titles_visible) return;
3931 if (!GTK_WIDGET_REALIZED (sheet))
3934 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
3936 if (sheet->column_titles_visible)
3938 y = sheet->column_title_area.height;
3941 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
3943 sheet->row_title_area.y = y;
3944 sheet->row_title_area.height = height;
3945 gdk_window_move_resize (sheet->row_title_window,
3946 sheet->row_title_area.x,
3947 sheet->row_title_area.y,
3948 sheet->row_title_area.width,
3949 sheet->row_title_area.height);
3952 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
3953 gdk_window_clear_area (sheet->row_title_window,
3955 sheet->row_title_area.width,
3956 sheet->row_title_area.height);
3958 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
3960 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
3961 max_visible_row (sheet));
3966 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
3968 GtkAllocation entry_alloc;
3969 PsppireSheetCellAttr attributes = { 0 };
3970 GtkEntry *sheet_entry;
3972 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3973 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
3975 sheet_entry = psppire_sheet_get_entry (sheet);
3977 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
3978 sheet->active_cell.col,
3982 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
3984 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
3986 style->bg[GTK_STATE_NORMAL] = attributes.background;
3987 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
3988 style->text[GTK_STATE_NORMAL] = attributes.foreground;
3989 style->bg[GTK_STATE_ACTIVE] = attributes.background;
3990 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
3991 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
3994 rectangle_from_cell (sheet, sheet->active_cell.row,
3995 sheet->active_cell.col, &entry_alloc);
3997 entry_alloc.x += sheet->cell_padding->left;
3998 entry_alloc.y += sheet->cell_padding->right;
3999 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4000 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4003 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4004 entry_alloc.height);
4005 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4009 /* Copy the sheet's font to the entry widget */
4011 set_entry_widget_font (PsppireSheet *sheet)
4013 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4015 pango_font_description_free (style->font_desc);
4016 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4018 gtk_widget_modify_style (sheet->entry_widget, style);
4022 create_sheet_entry (PsppireSheet *sheet)
4024 if (sheet->entry_widget)
4026 gtk_widget_unparent (sheet->entry_widget);
4029 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4030 g_object_ref_sink (sheet->entry_widget);
4032 gtk_widget_size_request (sheet->entry_widget, NULL);
4034 if ( GTK_IS_ENTRY (sheet->entry_widget))
4036 g_object_set (sheet->entry_widget,
4041 if (GTK_WIDGET_REALIZED (sheet))
4043 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4044 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4045 gtk_widget_realize (sheet->entry_widget);
4048 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4049 G_CALLBACK (psppire_sheet_entry_key_press),
4052 set_entry_widget_font (sheet);
4054 gtk_widget_show (sheet->entry_widget);
4058 /* Finds the last child widget that happens to be of type GtkEntry */
4060 find_entry (GtkWidget *w, gpointer user_data)
4062 GtkWidget **entry = user_data;
4063 if ( GTK_IS_ENTRY (w))
4071 psppire_sheet_get_entry (PsppireSheet *sheet)
4073 GtkWidget *w = sheet->entry_widget;
4075 g_return_val_if_fail (sheet != NULL, NULL);
4076 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4077 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4079 while (! GTK_IS_ENTRY (w))
4081 GtkWidget *entry = NULL;
4083 if (GTK_IS_CONTAINER (w))
4085 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4094 return GTK_ENTRY (w);
4099 draw_button (PsppireSheet *sheet, GdkWindow *window,
4100 PsppireSheetButton *button, gboolean is_sensitive,
4101 GdkRectangle allocation)
4103 GtkShadowType shadow_type;
4104 gint text_width = 0, text_height = 0;
4105 PangoAlignment align = PANGO_ALIGN_LEFT;
4111 g_return_if_fail (sheet != NULL);
4112 g_return_if_fail (button != NULL);
4115 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4117 gdk_window_clear_area (window,
4118 allocation.x, allocation.y,
4119 allocation.width, allocation.height);
4121 gtk_widget_ensure_style (sheet->button);
4123 gtk_paint_box (sheet->button->style, window,
4124 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4126 GTK_WIDGET (sheet->button),
4128 allocation.x, allocation.y,
4129 allocation.width, allocation.height);
4131 state = button->state;
4132 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4134 if (state == GTK_STATE_ACTIVE)
4135 shadow_type = GTK_SHADOW_IN;
4137 shadow_type = GTK_SHADOW_OUT;
4139 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4140 gtk_paint_box (sheet->button->style, window,
4141 button->state, shadow_type,
4142 &allocation, GTK_WIDGET (sheet->button),
4144 allocation.x, allocation.y,
4145 allocation.width, allocation.height);
4147 if ( button->overstruck)
4149 GdkPoint points[2] = {
4150 {allocation.x, allocation.y},
4151 {allocation.x + allocation.width,
4152 allocation.y + allocation.height}
4155 gtk_paint_polygon (sheet->button->style,
4167 if (button->label_visible)
4169 text_height = DEFAULT_ROW_HEIGHT -
4170 2 * COLUMN_TITLES_HEIGHT;
4172 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4174 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4177 allocation.y += 2 * sheet->button->style->ythickness;
4179 if (button->label && strlen (button->label) > 0)
4181 PangoRectangle rect;
4182 gchar *line = button->label;
4184 PangoLayout *layout = NULL;
4185 gint real_x = allocation.x;
4186 gint real_y = allocation.y;
4188 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4189 pango_layout_get_extents (layout, NULL, &rect);
4191 text_width = PANGO_PIXELS (rect.width);
4192 switch (button->justification)
4194 case GTK_JUSTIFY_LEFT:
4195 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4196 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4198 case GTK_JUSTIFY_RIGHT:
4199 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4200 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4202 case GTK_JUSTIFY_CENTER:
4204 real_x = allocation.x + (allocation.width - text_width)/2;
4205 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4206 pango_layout_set_justify (layout, TRUE);
4208 pango_layout_set_alignment (layout, align);
4209 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4218 g_object_unref (layout);
4221 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4223 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4227 psppire_sheet_button_free (button);
4231 /* Draw the column title buttons FIRST through to LAST */
4233 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4237 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4239 if (!sheet->column_titles_visible) return;
4241 g_return_if_fail (first >= min_visible_column (sheet));
4242 g_return_if_fail (last <= max_visible_column (sheet));
4245 rect.height = sheet->column_title_area.height;
4246 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4247 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4248 + psppire_axis_unit_size (sheet->haxis, last);
4250 rect.x -= sheet->hadjustment->value;
4252 minimize_int (&rect.width, sheet->column_title_area.width);
4253 maximize_int (&rect.x, 0);
4255 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4257 for (col = first ; col <= last ; ++col)
4259 GdkRectangle allocation;
4260 gboolean is_sensitive = FALSE;
4262 PsppireSheetButton *
4263 button = psppire_sheet_model_get_column_button (sheet->model, col);
4265 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4267 allocation.x -= sheet->hadjustment->value;
4269 allocation.height = sheet->column_title_area.height;
4270 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4271 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4273 draw_button (sheet, sheet->column_title_window,
4274 button, is_sensitive, allocation);
4277 gdk_window_end_paint (sheet->column_title_window);
4282 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4286 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4288 if (!sheet->row_titles_visible) return;
4290 g_return_if_fail (first >= min_visible_row (sheet));
4291 g_return_if_fail (last <= max_visible_row (sheet));
4294 rect.width = sheet->row_title_area.width;
4295 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4296 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4297 + psppire_axis_unit_size (sheet->vaxis, last);
4299 rect.y -= sheet->vadjustment->value;
4301 minimize_int (&rect.height, sheet->row_title_area.height);
4302 maximize_int (&rect.y, 0);
4304 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4305 for (row = first; row <= last; ++row)
4307 GdkRectangle allocation;
4309 gboolean is_sensitive = FALSE;
4311 PsppireSheetButton *button =
4312 psppire_sheet_model_get_row_button (sheet->model, row);
4314 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4316 allocation.y -= sheet->vadjustment->value;
4318 allocation.width = sheet->row_title_area.width;
4319 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4320 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4322 draw_button (sheet, sheet->row_title_window,
4323 button, is_sensitive, allocation);
4326 gdk_window_end_paint (sheet->row_title_window);
4333 * vadjustment_value_changed
4334 * hadjustment_value_changed */
4338 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4341 (adj->value + adj->page_size)
4343 (adj->upper - adj->lower);
4345 const glong last_item = psppire_axis_unit_count (axis) - 1;
4347 if (isnan (position) || position < 0)
4351 psppire_axis_start_pixel (axis, last_item)
4353 psppire_axis_unit_size (axis, last_item)
4357 adj->page_size = page_size;
4360 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4362 if ( adj->value < adj->lower)
4363 adj->value = adj->lower;
4366 gtk_adjustment_changed (adj);
4371 adjust_scrollbars (PsppireSheet *sheet)
4375 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4378 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4380 if ( sheet->row_titles_visible)
4381 width -= sheet->row_title_area.width;
4383 if (sheet->column_titles_visible)
4384 height -= sheet->column_title_area.height;
4386 if (sheet->vadjustment)
4388 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4390 sheet->vadjustment->step_increment =
4392 psppire_axis_unit_size (sheet->vaxis, last_row);
4394 sheet->vadjustment->page_increment =
4396 sheet->column_title_area.height -
4397 psppire_axis_unit_size (sheet->vaxis, last_row);
4399 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4402 if (sheet->hadjustment)
4404 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4405 sheet->hadjustment->step_increment = 1;
4407 sheet->hadjustment->page_increment = width;
4409 sheet->hadjustment->upper =
4410 psppire_axis_start_pixel (sheet->haxis, last_col)
4412 psppire_axis_unit_size (sheet->haxis, last_col)
4415 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4419 /* Subtracts the region of WIDGET from REGION */
4421 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4424 GdkRectangle intersect;
4427 gdk_region_get_clipbox (region, &rect);
4428 gtk_widget_intersect (widget,
4432 region2 = gdk_region_rectangle (&intersect);
4433 gdk_region_subtract (region, region2);
4434 gdk_region_destroy (region2);
4438 vadjustment_value_changed (GtkAdjustment *adjustment,
4442 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4444 g_return_if_fail (adjustment != NULL);
4446 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4448 gtk_widget_hide (sheet->entry_widget);
4451 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4453 subtract_widget_region (region, sheet->button);
4454 gdk_window_begin_paint_region (sheet->sheet_window, region);
4456 draw_sheet_region (sheet, region);
4458 draw_row_title_buttons (sheet);
4459 psppire_sheet_draw_active_cell (sheet);
4461 gdk_window_end_paint (sheet->sheet_window);
4462 gdk_region_destroy (region);
4467 hadjustment_value_changed (GtkAdjustment *adjustment,
4471 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4473 g_return_if_fail (adjustment != NULL);
4475 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4477 gtk_widget_hide (sheet->entry_widget);
4481 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4483 subtract_widget_region (region, sheet->button);
4484 gdk_window_begin_paint_region (sheet->sheet_window, region);
4486 draw_sheet_region (sheet, region);
4488 draw_column_title_buttons (sheet);
4490 psppire_sheet_draw_active_cell (sheet);
4492 gdk_window_end_paint (sheet->sheet_window);
4494 gdk_region_destroy (region);
4498 /* COLUMN RESIZING */
4500 draw_xor_vline (PsppireSheet *sheet)
4503 gint xpos = sheet->x_drag;
4504 gdk_drawable_get_size (sheet->sheet_window,
4507 if (sheet->row_titles_visible)
4508 xpos += sheet->row_title_area.width;
4510 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4512 sheet->column_title_area.height,
4514 height + CELL_SPACING);
4519 draw_xor_hline (PsppireSheet *sheet)
4523 gint ypos = sheet->y_drag;
4525 gdk_drawable_get_size (sheet->sheet_window,
4529 if (sheet->column_titles_visible)
4530 ypos += sheet->column_title_area.height;
4532 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4533 sheet->row_title_area.width,
4535 width + CELL_SPACING,
4539 /* SELECTED RANGE */
4541 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
4544 GdkRectangle clip_area, area;
4547 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
4548 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
4549 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
4550 psppire_axis_unit_size (sheet->haxis, range.coli);
4551 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
4552 psppire_axis_unit_size (sheet->vaxis, range.rowi);
4554 clip_area.x = sheet->row_title_area.width;
4555 clip_area.y = sheet->column_title_area.height;
4557 gdk_drawable_get_size (sheet->sheet_window,
4558 &clip_area.width, &clip_area.height);
4560 if (!sheet->row_titles_visible) clip_area.x = 0;
4561 if (!sheet->column_titles_visible) clip_area.y = 0;
4565 area.width = area.width + area.x;
4568 if (area.width > clip_area.width) area.width = clip_area.width + 10;
4571 area.height = area.height + area.y;
4574 if (area.height > clip_area.height) area.height = clip_area.height + 10;
4578 clip_area.width += 3;
4579 clip_area.height += 3;
4581 gdk_gc_get_values (sheet->xor_gc, &values);
4583 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
4585 gdk_draw_rectangle (sheet->sheet_window,
4588 area.x + i, area.y + i,
4589 area.width - 2 * i, area.height - 2 * i);
4592 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4594 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
4599 set_column_width (PsppireSheet *sheet,
4603 g_return_if_fail (sheet != NULL);
4604 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4606 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
4612 psppire_axis_resize (sheet->haxis, column,
4613 width - sheet->cell_padding->left -
4614 sheet->cell_padding->right);
4616 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4618 draw_column_title_buttons (sheet);
4619 adjust_scrollbars (sheet);
4620 psppire_sheet_size_allocate_entry (sheet);
4621 redraw_range (sheet, NULL);
4626 set_row_height (PsppireSheet *sheet,
4630 g_return_if_fail (sheet != NULL);
4631 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4633 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
4639 psppire_axis_resize (sheet->vaxis, row,
4640 height - sheet->cell_padding->top -
4641 sheet->cell_padding->bottom);
4643 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
4645 draw_row_title_buttons (sheet);
4646 adjust_scrollbars (sheet);
4647 psppire_sheet_size_allocate_entry (sheet);
4648 redraw_range (sheet, NULL);
4653 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
4654 PsppireSheetCellAttr *attr)
4657 const GtkJustification *j ;
4658 GdkColormap *colormap;
4660 g_return_val_if_fail (sheet != NULL, FALSE);
4661 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
4663 if (row < 0 || col < 0) return FALSE;
4665 attr->foreground = GTK_WIDGET (sheet)->style->black;
4666 attr->background = sheet->color[BG_COLOR];
4668 attr->border.width = 0;
4669 attr->border.line_style = GDK_LINE_SOLID;
4670 attr->border.cap_style = GDK_CAP_NOT_LAST;
4671 attr->border.join_style = GDK_JOIN_MITER;
4672 attr->border.mask = 0;
4673 attr->border.color = GTK_WIDGET (sheet)->style->black;
4675 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
4676 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
4679 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
4680 attr->foreground = *fg;
4683 bg = psppire_sheet_model_get_background (sheet->model, row, col);
4686 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
4687 attr->background = *bg;
4690 attr->justification =
4691 psppire_sheet_model_get_column_justification (sheet->model, col);
4693 j = psppire_sheet_model_get_justification (sheet->model, row, col);
4695 attr->justification = *j;
4701 psppire_sheet_button_size_request (PsppireSheet *sheet,
4702 const PsppireSheetButton *button,
4703 GtkRequisition *button_requisition)
4705 GtkRequisition requisition;
4706 GtkRequisition label_requisition;
4708 label_requisition.height = DEFAULT_ROW_HEIGHT;
4709 label_requisition.width = COLUMN_MIN_WIDTH;
4711 requisition.height = DEFAULT_ROW_HEIGHT;
4712 requisition.width = COLUMN_MIN_WIDTH;
4715 *button_requisition = requisition;
4716 button_requisition->width = MAX (requisition.width, label_requisition.width);
4717 button_requisition->height = MAX (requisition.height, label_requisition.height);
4722 psppire_sheet_forall (GtkContainer *container,
4723 gboolean include_internals,
4724 GtkCallback callback,
4725 gpointer callback_data)
4727 PsppireSheet *sheet = PSPPIRE_SHEET (container);
4729 g_return_if_fail (callback != NULL);
4731 if (sheet->button && sheet->button->parent)
4732 (* callback) (sheet->button, callback_data);
4734 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
4735 (* callback) (sheet->entry_widget, callback_data);
4740 psppire_sheet_get_model (const PsppireSheet *sheet)
4742 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4744 return sheet->model;
4748 PsppireSheetButton *
4749 psppire_sheet_button_new (void)
4751 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
4753 button->state = GTK_STATE_NORMAL;
4754 button->label = NULL;
4755 button->label_visible = TRUE;
4756 button->justification = GTK_JUSTIFY_FILL;
4757 button->overstruck = FALSE;
4764 psppire_sheet_button_free (PsppireSheetButton *button)
4766 if (!button) return ;
4768 g_free (button->label);
4773 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
4775 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
4777 if ( NULL == celltext)
4780 g_string_append (string, celltext);
4786 range_to_text (const PsppireSheet *sheet)
4791 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4794 string = g_string_sized_new (80);
4796 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4798 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
4800 append_cell_text (string, sheet, r, c);
4801 g_string_append (string, "\t");
4803 append_cell_text (string, sheet, r, c);
4804 if ( r < sheet->range.rowi)
4805 g_string_append (string, "\n");
4812 range_to_html (const PsppireSheet *sheet)
4817 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4820 string = g_string_sized_new (480);
4822 g_string_append (string, "<html>\n");
4823 g_string_append (string, "<body>\n");
4824 g_string_append (string, "<table>\n");
4825 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4827 g_string_append (string, "<tr>\n");
4828 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
4830 g_string_append (string, "<td>");
4831 append_cell_text (string, sheet, r, c);
4832 g_string_append (string, "</td>\n");
4834 g_string_append (string, "</tr>\n");
4836 g_string_append (string, "</table>\n");
4837 g_string_append (string, "</body>\n");
4838 g_string_append (string, "</html>\n");
4850 primary_get_cb (GtkClipboard *clipboard,
4851 GtkSelectionData *selection_data,
4855 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4856 GString *string = NULL;
4860 case SELECT_FMT_TEXT:
4861 string = range_to_text (sheet);
4863 case SELECT_FMT_HTML:
4864 string = range_to_html (sheet);
4867 g_assert_not_reached ();
4870 gtk_selection_data_set (selection_data, selection_data->target,
4872 (const guchar *) string->str, string->len);
4873 g_string_free (string, TRUE);
4877 primary_clear_cb (GtkClipboard *clipboard,
4880 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4881 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4884 psppire_sheet_unselect_range (sheet);
4888 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
4890 static const GtkTargetEntry targets[] = {
4891 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
4892 { "STRING", 0, SELECT_FMT_TEXT },
4893 { "TEXT", 0, SELECT_FMT_TEXT },
4894 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
4895 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
4896 { "text/plain", 0, SELECT_FMT_TEXT },
4897 { "text/html", 0, SELECT_FMT_HTML }
4900 GtkClipboard *clipboard;
4902 if (!GTK_WIDGET_REALIZED (sheet))
4905 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
4906 GDK_SELECTION_PRIMARY);
4908 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
4910 if (!gtk_clipboard_set_with_owner (clipboard, targets,
4911 G_N_ELEMENTS (targets),
4912 primary_get_cb, primary_clear_cb,
4914 primary_clear_cb (clipboard, sheet);
4918 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
4919 gtk_clipboard_clear (clipboard);