2 Copyright (C) 2006, 2008, 2009, 2011 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;
430 rectangle_from_cell (PsppireSheet *sheet, gint row, gint col,
433 PsppireSheetRange range;
434 g_return_if_fail (row >= 0);
435 g_return_if_fail (col >= 0);
437 range.row0 = range.rowi = row;
438 range.col0 = range.coli = col;
440 rectangle_from_range (sheet, &range, r);
444 static void psppire_sheet_class_init (PsppireSheetClass *klass);
445 static void psppire_sheet_init (PsppireSheet *sheet);
446 static void psppire_sheet_dispose (GObject *object);
447 static void psppire_sheet_finalize (GObject *object);
448 static void psppire_sheet_style_set (GtkWidget *widget,
449 GtkStyle *previous_style);
450 static void psppire_sheet_realize (GtkWidget *widget);
451 static void psppire_sheet_unrealize (GtkWidget *widget);
452 static void psppire_sheet_map (GtkWidget *widget);
453 static void psppire_sheet_unmap (GtkWidget *widget);
454 static gint psppire_sheet_expose (GtkWidget *widget,
455 GdkEventExpose *event);
457 static void psppire_sheet_forall (GtkContainer *container,
458 gboolean include_internals,
459 GtkCallback callback,
460 gpointer callback_data);
462 static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
463 GtkAdjustment *hadjustment,
464 GtkAdjustment *vadjustment);
466 static gint psppire_sheet_button_press (GtkWidget *widget,
467 GdkEventButton *event);
468 static gint psppire_sheet_button_release (GtkWidget *widget,
469 GdkEventButton *event);
470 static gint psppire_sheet_motion (GtkWidget *widget,
471 GdkEventMotion *event);
472 static gboolean psppire_sheet_crossing_notify (GtkWidget *widget,
473 GdkEventCrossing *event);
474 static gint psppire_sheet_entry_key_press (GtkWidget *widget,
476 static gboolean psppire_sheet_key_press (GtkWidget *widget,
478 static void psppire_sheet_size_request (GtkWidget *widget,
479 GtkRequisition *requisition);
480 static void psppire_sheet_size_allocate (GtkWidget *widget,
481 GtkAllocation *allocation);
483 static gboolean psppire_sheet_focus_in (GtkWidget *widget,
484 GdkEventFocus *event);
488 static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
489 const PsppireSheetRange *range);
490 static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet,
491 gint row, gint column);
492 /* Drawing Routines */
495 static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column);
498 /* draw visible part of range. */
499 static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region);
503 static void psppire_sheet_draw_border (PsppireSheet *sheet,
504 PsppireSheetRange range);
506 /* Active Cell handling */
508 static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet);
509 static void change_active_cell (PsppireSheet *sheet, gint row, gint col);
510 static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet);
511 static void psppire_sheet_show_entry_widget (PsppireSheet *sheet);
512 static gboolean psppire_sheet_click_cell (PsppireSheet *sheet,
519 static void adjust_scrollbars (PsppireSheet *sheet);
520 static void vadjustment_value_changed (GtkAdjustment *adjustment,
522 static void hadjustment_value_changed (GtkAdjustment *adjustment,
526 static void draw_xor_vline (PsppireSheet *sheet);
527 static void draw_xor_hline (PsppireSheet *sheet);
528 static void draw_xor_rectangle (PsppireSheet *sheet,
529 PsppireSheetRange range);
533 static void create_global_button (PsppireSheet *sheet);
534 static void global_button_clicked (GtkWidget *widget,
538 static void create_sheet_entry (PsppireSheet *sheet);
539 static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet);
541 /* Sheet button gadgets */
543 static void draw_column_title_buttons (PsppireSheet *sheet);
544 static void draw_row_title_buttons (PsppireSheet *sheet);
547 static void size_allocate_global_button (PsppireSheet *sheet);
549 static void psppire_sheet_real_cell_clear (PsppireSheet *sheet,
571 static GtkContainerClass *parent_class = NULL;
572 static guint sheet_signals[LAST_SIGNAL] = { 0 };
576 psppire_sheet_get_type ()
578 static GType sheet_type = 0;
582 static const GTypeInfo sheet_info =
584 sizeof (PsppireSheetClass),
587 (GClassInitFunc) psppire_sheet_class_init,
590 sizeof (PsppireSheet),
592 (GInstanceInitFunc) psppire_sheet_init,
597 g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
605 static PsppireSheetRange*
606 psppire_sheet_range_copy (const PsppireSheetRange *range)
608 PsppireSheetRange *new_range;
610 g_return_val_if_fail (range != NULL, NULL);
612 new_range = g_new (PsppireSheetRange, 1);
620 psppire_sheet_range_free (PsppireSheetRange *range)
622 g_return_if_fail (range != NULL);
628 psppire_sheet_range_get_type (void)
630 static GType sheet_range_type = 0;
632 if (!sheet_range_type)
635 g_boxed_type_register_static ("PsppireSheetRange",
636 (GBoxedCopyFunc) psppire_sheet_range_copy,
637 (GBoxedFreeFunc) psppire_sheet_range_free);
640 return sheet_range_type;
643 static PsppireSheetCell*
644 psppire_sheet_cell_copy (const PsppireSheetCell *cell)
646 PsppireSheetCell *new_cell;
648 g_return_val_if_fail (cell != NULL, NULL);
650 new_cell = g_new (PsppireSheetCell, 1);
658 psppire_sheet_cell_free (PsppireSheetCell *cell)
660 g_return_if_fail (cell != NULL);
666 psppire_sheet_cell_get_type (void)
668 static GType sheet_cell_type = 0;
670 if (!sheet_cell_type)
673 g_boxed_type_register_static ("PsppireSheetCell",
674 (GBoxedCopyFunc) psppire_sheet_cell_copy,
675 (GBoxedFreeFunc) psppire_sheet_cell_free);
678 return sheet_cell_type;
693 resize_column (PsppireSheet *sheet, gint unit, glong size)
695 PsppireSheetRange range;
697 range.coli = max_visible_column (sheet);
698 range.row0 = min_visible_row (sheet);
699 range.rowi = max_visible_row (sheet);
701 redraw_range (sheet, &range);
703 draw_column_title_buttons_range (sheet, range.col0, range.coli);
708 psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
711 g_object_unref (sheet->haxis);
714 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
717 g_object_ref (sheet->haxis);
721 resize_row (PsppireSheet *sheet, gint unit, glong size)
723 PsppireSheetRange range;
724 range.col0 = min_visible_column (sheet);
725 range.coli = max_visible_column (sheet);
727 range.rowi = max_visible_row (sheet);
729 redraw_range (sheet, &range);
731 draw_row_title_buttons_range (sheet, range.row0, range.rowi);
735 psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
738 g_object_unref (sheet->vaxis);
742 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
745 g_object_ref (sheet->vaxis);
748 static const GtkBorder default_cell_padding = {3, 3, 3, 3};
751 psppire_sheet_set_property (GObject *object,
757 PsppireSheet *sheet = PSPPIRE_SHEET (object);
761 case PROP_CELL_PADDING:
762 if ( sheet->cell_padding)
763 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
765 sheet->cell_padding = g_value_dup_boxed (value);
767 if (NULL == sheet->cell_padding)
768 sheet->cell_padding = g_boxed_copy (GTK_TYPE_BORDER,
769 &default_cell_padding);
772 g_object_set (sheet->vaxis, "padding",
773 sheet->cell_padding->top + sheet->cell_padding->bottom,
777 g_object_set (sheet->haxis, "padding",
778 sheet->cell_padding->left + sheet->cell_padding->right,
782 psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
783 g_object_set (sheet->vaxis, "padding",
784 sheet->cell_padding->top + sheet->cell_padding->bottom,
788 psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
789 g_object_set (sheet->haxis, "padding",
790 sheet->cell_padding->left + sheet->cell_padding->right,
794 psppire_sheet_set_model (sheet, g_value_get_pointer (value));
797 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
803 psppire_sheet_get_property (GObject *object,
808 PsppireSheet *sheet = PSPPIRE_SHEET (object);
812 case PROP_CELL_PADDING:
813 g_value_set_boxed (value, sheet->cell_padding);
816 g_value_set_pointer (value, sheet->vaxis);
819 g_value_set_pointer (value, sheet->haxis);
822 g_value_set_pointer (value, sheet->model);
825 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
832 psppire_sheet_class_init (PsppireSheetClass *klass)
834 GObjectClass *object_class = G_OBJECT_CLASS (klass);
836 GParamSpec *haxis_spec ;
837 GParamSpec *vaxis_spec ;
838 GParamSpec *model_spec ;
839 GParamSpec *cell_padding_spec ;
841 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
842 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
844 parent_class = g_type_class_peek_parent (klass);
847 * PsppireSheet::select-row
848 * @sheet: the sheet widget that emitted the signal
849 * @row: the newly selected row index, or -1 if no row is selected.
851 * A row has been selected.
853 sheet_signals[SELECT_ROW] =
854 g_signal_new ("select-row",
855 G_TYPE_FROM_CLASS (object_class),
857 offsetof (PsppireSheetClass, select_row),
859 g_cclosure_marshal_VOID__INT,
866 * PsppireSheet::select - column
867 * @sheet: the sheet widget that emitted the signal
868 * @column: the newly selected column index, or -1 if no column is selected.
870 * A column has been selected.
872 sheet_signals[SELECT_COLUMN] =
873 g_signal_new ("select-column",
874 G_TYPE_FROM_CLASS (object_class),
876 offsetof (PsppireSheetClass, select_column),
878 g_cclosure_marshal_VOID__INT,
885 * PsppireSheet::double-click-row
886 * @sheet: the sheet widget that emitted the signal
887 * @row: the row that was double clicked.
889 * A row's title button has been double clicked
891 sheet_signals[DOUBLE_CLICK_ROW] =
892 g_signal_new ("double-click-row",
893 G_TYPE_FROM_CLASS (object_class),
897 g_cclosure_marshal_VOID__INT,
904 * PsppireSheet::double-click-column
905 * @sheet: the sheet widget that emitted the signal
906 * @column: the column that was double clicked.
908 * A column's title button has been double clicked
910 sheet_signals[DOUBLE_CLICK_COLUMN] =
911 g_signal_new ("double-click-column",
912 G_TYPE_FROM_CLASS (object_class),
916 g_cclosure_marshal_VOID__INT,
923 * PsppireSheet::button-event-column
924 * @sheet: the sheet widget that emitted the signal
925 * @column: the column on which the event occured.
927 * A button event occured on a column title button
929 sheet_signals[BUTTON_EVENT_COLUMN] =
930 g_signal_new ("button-event-column",
931 G_TYPE_FROM_CLASS (object_class),
935 psppire_marshal_VOID__INT_POINTER,
944 * PsppireSheet::button-event-row
945 * @sheet: the sheet widget that emitted the signal
946 * @column: the column on which the event occured.
948 * A button event occured on a row title button
950 sheet_signals[BUTTON_EVENT_ROW] =
951 g_signal_new ("button-event-row",
952 G_TYPE_FROM_CLASS (object_class),
956 psppire_marshal_VOID__INT_POINTER,
964 sheet_signals[SELECT_RANGE] =
965 g_signal_new ("select-range",
966 G_TYPE_FROM_CLASS (object_class),
968 offsetof (PsppireSheetClass, select_range),
970 g_cclosure_marshal_VOID__BOXED,
973 PSPPIRE_TYPE_SHEET_RANGE);
976 sheet_signals[RESIZE_RANGE] =
977 g_signal_new ("resize-range",
978 G_TYPE_FROM_CLASS (object_class),
980 offsetof (PsppireSheetClass, resize_range),
982 psppire_marshal_VOID__BOXED_BOXED,
985 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
988 sheet_signals[MOVE_RANGE] =
989 g_signal_new ("move-range",
990 G_TYPE_FROM_CLASS (object_class),
992 offsetof (PsppireSheetClass, move_range),
994 psppire_marshal_VOID__BOXED_BOXED,
997 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
1000 sheet_signals[TRAVERSE] =
1001 g_signal_new ("traverse",
1002 G_TYPE_FROM_CLASS (object_class),
1004 offsetof (PsppireSheetClass, traverse),
1006 psppire_marshal_BOOLEAN__BOXED_POINTER,
1008 PSPPIRE_TYPE_SHEET_CELL,
1012 sheet_signals[ACTIVATE] =
1013 g_signal_new ("activate",
1014 G_TYPE_FROM_CLASS (object_class),
1016 offsetof (PsppireSheetClass, activate),
1018 psppire_marshal_VOID__INT_INT_INT_INT,
1020 G_TYPE_INT, G_TYPE_INT,
1021 G_TYPE_INT, G_TYPE_INT);
1023 widget_class->set_scroll_adjustments_signal =
1024 g_signal_new ("set-scroll-adjustments",
1025 G_TYPE_FROM_CLASS (object_class),
1027 offsetof (PsppireSheetClass, set_scroll_adjustments),
1029 psppire_marshal_VOID__OBJECT_OBJECT,
1030 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1033 container_class->add = NULL;
1034 container_class->remove = NULL;
1035 container_class->forall = psppire_sheet_forall;
1036 container_class->set_focus_child = NULL;
1038 object_class->dispose = psppire_sheet_dispose;
1039 object_class->finalize = psppire_sheet_finalize;
1042 g_param_spec_boxed ("cell-padding",
1044 "The space between a cell's contents and its border",
1046 G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
1049 g_param_spec_pointer ("vertical-axis",
1051 "A pointer to the PsppireAxis object for the rows",
1052 G_PARAM_READABLE | G_PARAM_WRITABLE );
1055 g_param_spec_pointer ("horizontal-axis",
1057 "A pointer to the PsppireAxis object for the columns",
1058 G_PARAM_READABLE | G_PARAM_WRITABLE );
1061 g_param_spec_pointer ("model",
1063 "A pointer to the data model",
1064 G_PARAM_READABLE | G_PARAM_WRITABLE );
1067 object_class->set_property = psppire_sheet_set_property;
1068 object_class->get_property = psppire_sheet_get_property;
1070 g_object_class_install_property (object_class,
1074 g_object_class_install_property (object_class,
1078 g_object_class_install_property (object_class,
1082 g_object_class_install_property (object_class,
1087 widget_class->realize = psppire_sheet_realize;
1088 widget_class->unrealize = psppire_sheet_unrealize;
1089 widget_class->map = psppire_sheet_map;
1090 widget_class->unmap = psppire_sheet_unmap;
1091 widget_class->style_set = psppire_sheet_style_set;
1092 widget_class->button_press_event = psppire_sheet_button_press;
1093 widget_class->button_release_event = psppire_sheet_button_release;
1094 widget_class->motion_notify_event = psppire_sheet_motion;
1095 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1096 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1097 widget_class->key_press_event = psppire_sheet_key_press;
1098 widget_class->expose_event = psppire_sheet_expose;
1099 widget_class->size_request = psppire_sheet_size_request;
1100 widget_class->size_allocate = psppire_sheet_size_allocate;
1101 widget_class->focus_in_event = psppire_sheet_focus_in;
1102 widget_class->focus_out_event = NULL;
1104 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1105 klass->select_row = NULL;
1106 klass->select_column = NULL;
1107 klass->select_range = NULL;
1108 klass->resize_range = NULL;
1109 klass->move_range = NULL;
1110 klass->traverse = NULL;
1111 klass->activate = NULL;
1112 klass->changed = NULL;
1116 psppire_sheet_init (PsppireSheet *sheet)
1118 sheet->model = NULL;
1119 sheet->haxis = NULL;
1120 sheet->vaxis = NULL;
1123 sheet->select_status = PSPPIRE_SHEET_NORMAL;
1125 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1126 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1128 sheet->column_title_window = NULL;
1129 sheet->column_title_area.x = 0;
1130 sheet->column_title_area.y = 0;
1131 sheet->column_title_area.width = 0;
1132 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1134 sheet->row_title_window = NULL;
1135 sheet->row_title_area.x = 0;
1136 sheet->row_title_area.y = 0;
1137 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1138 sheet->row_title_area.height = 0;
1141 sheet->active_cell.row = 0;
1142 sheet->active_cell.col = 0;
1144 sheet->range.row0 = 0;
1145 sheet->range.rowi = 0;
1146 sheet->range.col0 = 0;
1147 sheet->range.coli = 0;
1149 sheet->sheet_window = NULL;
1150 sheet->entry_widget = NULL;
1151 sheet->button = NULL;
1153 sheet->hadjustment = NULL;
1154 sheet->vadjustment = NULL;
1156 sheet->cursor_drag = NULL;
1158 sheet->xor_gc = NULL;
1159 sheet->fg_gc = NULL;
1160 sheet->bg_gc = NULL;
1163 sheet->show_grid = TRUE;
1165 sheet->motion_timer = 0;
1167 sheet->row_titles_visible = TRUE;
1168 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1170 sheet->column_titles_visible = TRUE;
1173 /* create sheet entry */
1174 sheet->entry_type = GTK_TYPE_ENTRY;
1175 create_sheet_entry (sheet);
1177 /* create global selection button */
1178 create_global_button (sheet);
1182 /* Cause RANGE to be redrawn. If RANGE is null, then the
1183 entire visible range will be redrawn.
1186 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1190 if ( ! GTK_WIDGET_REALIZED (sheet))
1193 if ( NULL != range )
1194 rectangle_from_range (sheet, range, &rect);
1197 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1198 gdk_region_get_clipbox (r, &rect);
1200 if ( sheet->column_titles_visible)
1202 rect.y += sheet->column_title_area.height;
1203 rect.height -= sheet->column_title_area.height;
1206 if ( sheet->row_titles_visible)
1208 rect.x += sheet->row_title_area.width;
1209 rect.width -= sheet->row_title_area.width;
1213 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1217 /* Callback which occurs whenever columns are inserted / deleted in the model */
1219 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1223 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1225 PsppireSheetRange range;
1226 gint model_columns = psppire_sheet_model_get_column_count (model);
1229 /* Need to update all the columns starting from the first column and onwards.
1230 * Previous column are unchanged, so don't need to be updated.
1232 range.col0 = first_column;
1234 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1235 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1237 adjust_scrollbars (sheet);
1239 if (sheet->active_cell.col >= model_columns)
1240 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1242 draw_column_title_buttons_range (sheet,
1243 first_column, max_visible_column (sheet));
1246 redraw_range (sheet, &range);
1252 /* Callback which occurs whenever rows are inserted / deleted in the model */
1254 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1255 gint n_rows, gpointer data)
1257 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1259 PsppireSheetRange range;
1261 gint model_rows = psppire_sheet_model_get_row_count (model);
1263 /* Need to update all the rows starting from the first row and onwards.
1264 * Previous rows are unchanged, so don't need to be updated.
1266 range.row0 = first_row;
1268 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1269 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1271 adjust_scrollbars (sheet);
1273 if (sheet->active_cell.row >= model_rows)
1274 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1276 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1278 redraw_range (sheet, &range);
1282 If row0 or rowi are negative, then all rows will be updated.
1283 If col0 or coli are negative, then all columns will be updated.
1286 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1287 gint rowi, gint coli, gpointer data)
1289 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1291 PsppireSheetRange range;
1298 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1301 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1303 redraw_range (sheet, NULL);
1304 adjust_scrollbars (sheet);
1306 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1307 max_visible_row (sheet));
1309 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1310 max_visible_column (sheet));
1314 else if ( row0 < 0 || rowi < 0 )
1316 range.row0 = min_visible_row (sheet);
1317 range.rowi = max_visible_row (sheet);
1319 else if ( col0 < 0 || coli < 0 )
1321 range.col0 = min_visible_column (sheet);
1322 range.coli = max_visible_column (sheet);
1325 redraw_range (sheet, &range);
1330 * psppire_sheet_new:
1331 * @rows: initial number of rows
1332 * @columns: initial number of columns
1333 * @title: sheet title
1334 * @model: the model to use for the sheet data
1336 * Creates a new sheet widget with the given number of rows and columns.
1338 * Returns: the new sheet widget
1341 psppire_sheet_new (PsppireSheetModel *model)
1343 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1351 * psppire_sheet_set_model
1352 * @sheet: the sheet to set the model for
1353 * @model: the model to use for the sheet data
1355 * Sets the model for a PsppireSheet
1359 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1361 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1363 if (sheet->model ) g_object_unref (sheet->model);
1365 sheet->model = model;
1369 g_object_ref (model);
1371 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1372 G_CALLBACK (range_update_callback),
1375 g_signal_connect (model, "rows_inserted",
1376 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1378 g_signal_connect (model, "rows_deleted",
1379 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1381 g_signal_connect (model, "columns_inserted",
1382 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1384 g_signal_connect (model, "columns_deleted",
1385 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1391 psppire_sheet_change_entry (PsppireSheet *sheet, GType entry_type)
1393 g_return_if_fail (sheet != NULL);
1394 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1396 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1397 psppire_sheet_hide_entry_widget (sheet);
1399 sheet->entry_type = entry_type;
1401 create_sheet_entry (sheet);
1403 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1404 psppire_sheet_show_entry_widget (sheet);
1408 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1410 g_return_if_fail (sheet != NULL);
1411 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1413 if (show == sheet->show_grid) return;
1415 sheet->show_grid = show;
1417 redraw_range (sheet, NULL);
1421 psppire_sheet_grid_visible (PsppireSheet *sheet)
1423 g_return_val_if_fail (sheet != NULL, 0);
1424 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1426 return sheet->show_grid;
1430 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1432 g_return_val_if_fail (sheet != NULL, 0);
1433 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1435 return psppire_axis_unit_count (sheet->haxis);
1438 static void set_column_width (PsppireSheet *sheet,
1444 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1446 if (sheet->column_titles_visible) return;
1448 sheet->column_titles_visible = TRUE;
1450 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1453 gdk_window_show (sheet->column_title_window);
1454 gdk_window_move_resize (sheet->column_title_window,
1455 sheet->column_title_area.x,
1456 sheet->column_title_area.y,
1457 sheet->column_title_area.width,
1458 sheet->column_title_area.height);
1460 adjust_scrollbars (sheet);
1462 if (sheet->vadjustment)
1463 g_signal_emit_by_name (sheet->vadjustment,
1466 size_allocate_global_button (sheet);
1468 if ( sheet->row_titles_visible)
1469 gtk_widget_show (sheet->button);
1474 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1476 if (sheet->row_titles_visible) return;
1478 sheet->row_titles_visible = TRUE;
1481 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1483 gdk_window_show (sheet->row_title_window);
1484 gdk_window_move_resize (sheet->row_title_window,
1485 sheet->row_title_area.x,
1486 sheet->row_title_area.y,
1487 sheet->row_title_area.width,
1488 sheet->row_title_area.height);
1490 adjust_scrollbars (sheet);
1493 if (sheet->hadjustment)
1494 g_signal_emit_by_name (sheet->hadjustment,
1497 size_allocate_global_button (sheet);
1499 if ( sheet->column_titles_visible)
1500 gtk_widget_show (sheet->button);
1504 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1506 if (!sheet->column_titles_visible) return;
1508 sheet->column_titles_visible = FALSE;
1510 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1512 if (sheet->column_title_window)
1513 gdk_window_hide (sheet->column_title_window);
1515 gtk_widget_hide (sheet->button);
1517 adjust_scrollbars (sheet);
1520 if (sheet->vadjustment)
1521 g_signal_emit_by_name (sheet->vadjustment,
1526 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1528 if (!sheet->row_titles_visible) return;
1530 sheet->row_titles_visible = FALSE;
1532 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1534 if (sheet->row_title_window)
1535 gdk_window_hide (sheet->row_title_window);
1537 gtk_widget_hide (sheet->button);
1539 adjust_scrollbars (sheet);
1542 if (sheet->hadjustment)
1543 g_signal_emit_by_name (sheet->hadjustment,
1548 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1549 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1550 at the {top,left} of the sheet. If it's 1, then it'll
1551 be placed at the {bottom,right}.
1552 ROW or COL may be -1, in which case scrolling in that dimension
1556 psppire_sheet_moveto (PsppireSheet *sheet,
1564 g_return_if_fail (row_align >= 0);
1565 g_return_if_fail (col_align >= 0);
1567 g_return_if_fail (row_align <= 1);
1568 g_return_if_fail (col_align <= 1);
1570 g_return_if_fail (col <
1571 psppire_axis_unit_count (sheet->haxis));
1572 g_return_if_fail (row <
1573 psppire_axis_unit_count (sheet->vaxis));
1575 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1580 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1582 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1588 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1590 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1598 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1599 const PsppireSheetRange *range)
1601 g_return_val_if_fail (sheet != NULL, FALSE);
1603 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1606 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1609 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1612 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1615 if (range->rowi < min_visible_row (sheet))
1618 if (range->row0 > max_visible_row (sheet))
1621 if (range->coli < min_visible_column (sheet))
1624 if (range->col0 > max_visible_column (sheet))
1631 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1632 gint row, gint column)
1634 PsppireSheetRange range;
1637 range.col0 = column;
1639 range.coli = column;
1641 return psppire_sheet_range_isvisible (sheet, &range);
1645 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1647 g_return_if_fail (sheet != NULL);
1648 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1649 g_return_if_fail (range != NULL);
1651 range->row0 = min_visible_row (sheet);
1652 range->col0 = min_visible_column (sheet);
1653 range->rowi = max_visible_row (sheet);
1654 range->coli = max_visible_column (sheet);
1659 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1660 GtkAdjustment *hadjustment,
1661 GtkAdjustment *vadjustment)
1663 if ( sheet->vadjustment != vadjustment )
1665 if (sheet->vadjustment)
1666 g_object_unref (sheet->vadjustment);
1667 sheet->vadjustment = vadjustment;
1671 g_object_ref (vadjustment);
1673 g_signal_connect (sheet->vadjustment, "value_changed",
1674 G_CALLBACK (vadjustment_value_changed),
1679 if ( sheet->hadjustment != hadjustment )
1681 if (sheet->hadjustment)
1682 g_object_unref (sheet->hadjustment);
1684 sheet->hadjustment = hadjustment;
1688 g_object_ref (hadjustment);
1690 g_signal_connect (sheet->hadjustment, "value_changed",
1691 G_CALLBACK (hadjustment_value_changed),
1699 psppire_sheet_finalize (GObject *object)
1701 PsppireSheet *sheet;
1703 g_return_if_fail (object != NULL);
1704 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1706 sheet = PSPPIRE_SHEET (object);
1708 if (G_OBJECT_CLASS (parent_class)->finalize)
1709 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1713 psppire_sheet_dispose (GObject *object)
1715 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1717 g_return_if_fail (object != NULL);
1718 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1720 if ( sheet->dispose_has_run )
1723 sheet->dispose_has_run = TRUE;
1725 if ( sheet->cell_padding)
1726 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1728 if (sheet->model) g_object_unref (sheet->model);
1729 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1730 if (sheet->haxis) g_object_unref (sheet->haxis);
1732 g_object_unref (sheet->button);
1733 sheet->button = NULL;
1735 /* unref adjustments */
1736 if (sheet->hadjustment)
1738 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1739 G_SIGNAL_MATCH_DATA,
1743 g_object_unref (sheet->hadjustment);
1744 sheet->hadjustment = NULL;
1747 if (sheet->vadjustment)
1749 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1750 G_SIGNAL_MATCH_DATA,
1754 g_object_unref (sheet->vadjustment);
1756 sheet->vadjustment = NULL;
1759 if (G_OBJECT_CLASS (parent_class)->dispose)
1760 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1764 psppire_sheet_style_set (GtkWidget *widget,
1765 GtkStyle *previous_style)
1767 PsppireSheet *sheet;
1769 g_return_if_fail (widget != NULL);
1770 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1772 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1773 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1775 sheet = PSPPIRE_SHEET (widget);
1777 if (GTK_WIDGET_REALIZED (widget))
1779 gtk_style_set_background (widget->style, widget->window, widget->state);
1782 set_entry_widget_font (sheet);
1787 psppire_sheet_realize (GtkWidget *widget)
1789 PsppireSheet *sheet;
1790 GdkWindowAttr attributes;
1791 const gint attributes_mask =
1792 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1795 GdkColormap *colormap;
1796 GdkDisplay *display;
1798 g_return_if_fail (widget != NULL);
1799 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1801 sheet = PSPPIRE_SHEET (widget);
1803 colormap = gtk_widget_get_colormap (widget);
1804 display = gtk_widget_get_display (widget);
1806 attributes.window_type = GDK_WINDOW_CHILD;
1807 attributes.x = widget->allocation.x;
1808 attributes.y = widget->allocation.y;
1809 attributes.width = widget->allocation.width;
1810 attributes.height = widget->allocation.height;
1811 attributes.wclass = GDK_INPUT_OUTPUT;
1813 attributes.visual = gtk_widget_get_visual (widget);
1814 attributes.colormap = colormap;
1816 attributes.event_mask = gtk_widget_get_events (widget);
1817 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1818 GDK_BUTTON_PRESS_MASK |
1819 GDK_BUTTON_RELEASE_MASK |
1820 GDK_KEY_PRESS_MASK |
1821 GDK_ENTER_NOTIFY_MASK |
1822 GDK_LEAVE_NOTIFY_MASK |
1823 GDK_POINTER_MOTION_MASK |
1824 GDK_POINTER_MOTION_HINT_MASK);
1826 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1829 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1831 gdk_window_set_user_data (widget->window, sheet);
1833 widget->style = gtk_style_attach (widget->style, widget->window);
1835 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1837 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1838 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1840 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1841 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1846 attributes.width = sheet->column_title_area.width;
1847 attributes.height = sheet->column_title_area.height;
1850 /* column - title window */
1851 sheet->column_title_window =
1852 gdk_window_new (widget->window, &attributes, attributes_mask);
1853 gdk_window_set_user_data (sheet->column_title_window, sheet);
1854 gtk_style_set_background (widget->style, sheet->column_title_window,
1860 attributes.width = sheet->row_title_area.width;
1861 attributes.height = sheet->row_title_area.height;
1863 /* row - title window */
1864 sheet->row_title_window = gdk_window_new (widget->window,
1865 &attributes, attributes_mask);
1866 gdk_window_set_user_data (sheet->row_title_window, sheet);
1867 gtk_style_set_background (widget->style, sheet->row_title_window,
1870 /* sheet - window */
1871 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1876 sheet->sheet_window = gdk_window_new (widget->window,
1877 &attributes, attributes_mask);
1878 gdk_window_set_user_data (sheet->sheet_window, sheet);
1880 gdk_cursor_unref (attributes.cursor);
1882 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1883 gdk_window_show (sheet->sheet_window);
1886 sheet->fg_gc = gdk_gc_new (widget->window);
1887 sheet->bg_gc = gdk_gc_new (widget->window);
1889 values.foreground = widget->style->white;
1890 values.function = GDK_INVERT;
1891 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1892 values.line_width = MAX (sheet->cell_padding->left,
1893 MAX (sheet->cell_padding->right,
1894 MAX (sheet->cell_padding->top,
1895 sheet->cell_padding->bottom)));
1897 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1905 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1906 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1908 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1909 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1911 sheet->button->style = gtk_style_attach (sheet->button->style,
1912 sheet->sheet_window);
1915 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1917 if (sheet->column_titles_visible)
1918 gdk_window_show (sheet->column_title_window);
1919 if (sheet->row_titles_visible)
1920 gdk_window_show (sheet->row_title_window);
1922 sheet->hover_window = create_hover_window ();
1924 draw_row_title_buttons (sheet);
1925 draw_column_title_buttons (sheet);
1927 psppire_sheet_update_primary_selection (sheet);
1930 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1934 create_global_button (PsppireSheet *sheet)
1936 sheet->button = gtk_button_new_with_label (" ");
1938 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1940 g_object_ref_sink (sheet->button);
1942 g_signal_connect (sheet->button,
1944 G_CALLBACK (global_button_clicked),
1949 size_allocate_global_button (PsppireSheet *sheet)
1951 GtkAllocation allocation;
1953 if (!sheet->column_titles_visible) return;
1954 if (!sheet->row_titles_visible) return;
1956 gtk_widget_size_request (sheet->button, NULL);
1960 allocation.width = sheet->row_title_area.width;
1961 allocation.height = sheet->column_title_area.height;
1963 gtk_widget_size_allocate (sheet->button, &allocation);
1967 global_button_clicked (GtkWidget *widget, gpointer data)
1969 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
1974 psppire_sheet_unrealize (GtkWidget *widget)
1976 PsppireSheet *sheet;
1978 g_return_if_fail (widget != NULL);
1979 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1981 sheet = PSPPIRE_SHEET (widget);
1983 gdk_cursor_unref (sheet->cursor_drag);
1984 sheet->cursor_drag = NULL;
1986 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
1987 sheet->color, n_COLORS);
1989 g_object_unref (sheet->xor_gc);
1990 g_object_unref (sheet->fg_gc);
1991 g_object_unref (sheet->bg_gc);
1993 destroy_hover_window (sheet->hover_window);
1995 gdk_window_destroy (sheet->sheet_window);
1996 gdk_window_destroy (sheet->column_title_window);
1997 gdk_window_destroy (sheet->row_title_window);
1999 gtk_widget_unparent (sheet->entry_widget);
2000 if (sheet->button != NULL)
2001 gtk_widget_unparent (sheet->button);
2003 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2004 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2008 psppire_sheet_map (GtkWidget *widget)
2010 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2012 g_return_if_fail (widget != NULL);
2013 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2015 if (!GTK_WIDGET_MAPPED (widget))
2017 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2019 gdk_window_show (widget->window);
2020 gdk_window_show (sheet->sheet_window);
2022 if (sheet->column_titles_visible)
2024 draw_column_title_buttons (sheet);
2025 gdk_window_show (sheet->column_title_window);
2027 if (sheet->row_titles_visible)
2029 draw_row_title_buttons (sheet);
2030 gdk_window_show (sheet->row_title_window);
2033 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2034 && sheet->active_cell.row >= 0
2035 && sheet->active_cell.col >= 0 )
2037 gtk_widget_show (sheet->entry_widget);
2038 gtk_widget_map (sheet->entry_widget);
2041 if (!GTK_WIDGET_MAPPED (sheet->button))
2043 gtk_widget_show (sheet->button);
2044 gtk_widget_map (sheet->button);
2047 redraw_range (sheet, NULL);
2048 change_active_cell (sheet,
2049 sheet->active_cell.row,
2050 sheet->active_cell.col);
2055 psppire_sheet_unmap (GtkWidget *widget)
2057 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2059 if (!GTK_WIDGET_MAPPED (widget))
2062 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2064 gdk_window_hide (sheet->sheet_window);
2065 if (sheet->column_titles_visible)
2066 gdk_window_hide (sheet->column_title_window);
2067 if (sheet->row_titles_visible)
2068 gdk_window_hide (sheet->row_title_window);
2069 gdk_window_hide (widget->window);
2071 gtk_widget_unmap (sheet->entry_widget);
2072 gtk_widget_unmap (sheet->button);
2073 gtk_widget_unmap (sheet->hover_window->window);
2076 /* get cell attributes of the given cell */
2077 /* TRUE means that the cell is currently allocated */
2078 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2080 PsppireSheetCellAttr *attributes);
2085 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2087 PangoLayout *layout;
2088 PangoRectangle text;
2089 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2094 PsppireSheetCellAttr attributes;
2097 g_return_if_fail (sheet != NULL);
2099 /* bail now if we aren't yet drawable */
2100 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2103 row >= psppire_axis_unit_count (sheet->vaxis))
2107 col >= psppire_axis_unit_count (sheet->haxis))
2110 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2112 /* select GC for background rectangle */
2113 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2114 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2116 rectangle_from_cell (sheet, row, col, &area);
2118 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2120 if (sheet->show_grid)
2122 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2124 gdk_draw_rectangle (sheet->sheet_window,
2128 area.width, area.height);
2132 label = psppire_sheet_cell_get_text (sheet, row, col);
2137 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2138 dispose_string (sheet, label);
2141 pango_layout_set_font_description (layout, font_desc);
2143 pango_layout_get_pixel_extents (layout, NULL, &text);
2145 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2147 font_height = pango_font_description_get_size (font_desc);
2148 if ( !pango_font_description_get_size_is_absolute (font_desc))
2149 font_height /= PANGO_SCALE;
2152 if ( sheet->cell_padding )
2154 area.x += sheet->cell_padding->left;
2155 area.width -= sheet->cell_padding->right
2156 + sheet->cell_padding->left;
2158 area.y += sheet->cell_padding->top;
2159 area.height -= sheet->cell_padding->bottom
2161 sheet->cell_padding->top;
2164 /* Centre the text vertically */
2165 area.y += (area.height - font_height) / 2.0;
2167 switch (attributes.justification)
2169 case GTK_JUSTIFY_RIGHT:
2170 area.x += area.width - text.width;
2172 case GTK_JUSTIFY_CENTER:
2173 area.x += (area.width - text.width) / 2.0;
2175 case GTK_JUSTIFY_LEFT:
2179 g_critical ("Unhandled justification %d in column %d\n",
2180 attributes.justification, col);
2184 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2189 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2190 g_object_unref (layout);
2195 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2197 PsppireSheetRange range;
2202 PsppireSheetRange drawing_range;
2204 gdk_region_get_clipbox (region, &area);
2206 y = area.y + sheet->vadjustment->value;
2207 x = area.x + sheet->hadjustment->value;
2209 if ( sheet->column_titles_visible)
2210 y -= sheet->column_title_area.height;
2212 if ( sheet->row_titles_visible)
2213 x -= sheet->row_title_area.width;
2215 maximize_int (&x, 0);
2216 maximize_int (&y, 0);
2218 range.row0 = row_from_ypixel (sheet, y);
2219 range.rowi = row_from_ypixel (sheet, y + area.height);
2221 range.col0 = column_from_xpixel (sheet, x);
2222 range.coli = column_from_xpixel (sheet, x + area.width);
2224 g_return_if_fail (sheet != NULL);
2225 g_return_if_fail (PSPPIRE_SHEET (sheet));
2227 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2228 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2229 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2232 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2233 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2234 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2235 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2237 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2238 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2240 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2242 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2243 psppire_sheet_cell_draw (sheet, i, j);
2246 if (sheet->select_status == PSPPIRE_SHEET_NORMAL &&
2247 sheet->active_cell.row >= drawing_range.row0 &&
2248 sheet->active_cell.row <= drawing_range.rowi &&
2249 sheet->active_cell.col >= drawing_range.col0 &&
2250 sheet->active_cell.col <= drawing_range.coli)
2251 psppire_sheet_show_entry_widget (sheet);
2255 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2256 GtkJustification justification,
2259 PsppireSheetModel *model ;
2262 g_return_if_fail (sheet != NULL);
2263 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2265 if (col >= psppire_axis_unit_count (sheet->haxis)
2266 || row >= psppire_axis_unit_count (sheet->vaxis))
2269 if (col < 0 || row < 0) return;
2271 model = psppire_sheet_get_model (sheet);
2273 old_text = psppire_sheet_model_get_string (model, row, col);
2275 if (0 != g_strcmp0 (old_text, text))
2277 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2278 psppire_sheet_model_set_string (model, text, row, col);
2279 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2282 if ( psppire_sheet_model_free_strings (model))
2288 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2290 PsppireSheetRange range;
2292 g_return_if_fail (sheet != NULL);
2293 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2294 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2295 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2297 if (column < 0 || row < 0) return;
2301 range.col0 = min_visible_column (sheet);
2302 range.coli = max_visible_column (sheet);
2304 psppire_sheet_real_cell_clear (sheet, row, column);
2306 redraw_range (sheet, &range);
2310 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2312 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2314 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2316 if (old_text && strlen (old_text) > 0 )
2318 psppire_sheet_model_datum_clear (model, row, column);
2321 dispose_string (sheet, old_text);
2325 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2327 PsppireSheetModel *model;
2328 g_return_val_if_fail (sheet != NULL, NULL);
2329 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2331 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2333 if (col < 0 || row < 0) return NULL;
2335 model = psppire_sheet_get_model (sheet);
2340 return psppire_sheet_model_get_string (model, row, col);
2344 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2345 If the function returns FALSE, then the results will be unreliable.
2348 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2356 *column = -G_MAXINT;
2358 g_return_val_if_fail (sheet != NULL, 0);
2359 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2361 /* bounds checking, return false if the user clicked
2369 if ( sheet->column_titles_visible)
2370 y -= sheet->column_title_area.height;
2372 y += sheet->vadjustment->value;
2374 if ( y < 0 && sheet->column_titles_visible)
2380 trow = row_from_ypixel (sheet, y);
2381 if (trow > psppire_axis_unit_count (sheet->vaxis))
2387 if ( sheet->row_titles_visible)
2388 x -= sheet->row_title_area.width;
2390 x += sheet->hadjustment->value;
2392 if ( x < 0 && sheet->row_titles_visible)
2398 tcol = column_from_xpixel (sheet, x);
2399 if (tcol > psppire_axis_unit_count (sheet->haxis))
2409 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2414 g_return_val_if_fail (sheet != NULL, 0);
2415 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2417 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2420 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2421 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2423 area->width= (column == -1) ? sheet->row_title_area.width
2424 : psppire_axis_unit_size (sheet->haxis, column);
2426 area->height= (row == -1) ? sheet->column_title_area.height
2427 : psppire_axis_unit_size (sheet->vaxis, row);
2433 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2435 g_return_if_fail (sheet != NULL);
2436 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2438 if (row < -1 || col < -1)
2441 if (row >= psppire_axis_unit_count (sheet->vaxis)
2443 col >= psppire_axis_unit_count (sheet->haxis))
2446 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2449 if ( row == -1 || col == -1)
2451 psppire_sheet_hide_entry_widget (sheet);
2455 change_active_cell (sheet, row, col);
2459 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2461 g_return_if_fail (sheet != NULL);
2462 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2464 if ( row ) *row = sheet->active_cell.row;
2465 if (column) *column = sheet->active_cell.col;
2469 entry_load_text (PsppireSheet *sheet)
2473 GtkJustification justification;
2474 PsppireSheetCellAttr attributes;
2476 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2477 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2479 row = sheet->active_cell.row;
2480 col = sheet->active_cell.col;
2482 if (row < 0 || col < 0) return;
2484 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2486 if (text && strlen (text) > 0)
2488 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2489 justification = attributes.justification;
2490 psppire_sheet_set_cell (sheet, row, col, justification, text);
2496 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2498 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2501 if (sheet->active_cell.row < 0 ||
2502 sheet->active_cell.col < 0) return;
2504 gtk_widget_hide (sheet->entry_widget);
2505 gtk_widget_unmap (sheet->entry_widget);
2507 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2511 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2513 gint old_row, old_col;
2515 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2517 if (row < 0 || col < 0)
2520 if ( row > psppire_axis_unit_count (sheet->vaxis)
2521 || col > psppire_axis_unit_count (sheet->haxis))
2524 old_row = sheet->active_cell.row;
2525 old_col = sheet->active_cell.col;
2527 entry_load_text (sheet);
2529 /* Erase the old cell border */
2530 psppire_sheet_draw_active_cell (sheet);
2532 sheet->active_cell.row = row;
2533 sheet->active_cell.col = col;
2535 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2537 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2539 psppire_sheet_draw_active_cell (sheet);
2540 psppire_sheet_show_entry_widget (sheet);
2542 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2544 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2545 row, col, old_row, old_col);
2550 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2552 GtkEntry *sheet_entry;
2553 PsppireSheetCellAttr attributes;
2557 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2559 row = sheet->active_cell.row;
2560 col = sheet->active_cell.col;
2562 /* Don't show the active cell, if there is no active cell: */
2563 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2566 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2567 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2568 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2570 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2572 sheet_entry = psppire_sheet_get_entry (sheet);
2574 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2576 if (GTK_IS_ENTRY (sheet_entry))
2578 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2579 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2582 text = g_strdup ("");
2584 if (strcmp (old_text, text) != 0)
2585 gtk_entry_set_text (sheet_entry, text);
2587 dispose_string (sheet, text);
2590 switch (attributes.justification)
2592 case GTK_JUSTIFY_RIGHT:
2593 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2595 case GTK_JUSTIFY_CENTER:
2596 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2598 case GTK_JUSTIFY_LEFT:
2600 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2606 psppire_sheet_size_allocate_entry (sheet);
2608 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2609 psppire_sheet_model_is_editable (sheet->model,
2611 gtk_widget_map (sheet->entry_widget);
2615 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2618 PsppireSheetRange range;
2620 row = sheet->active_cell.row;
2621 col = sheet->active_cell.col;
2623 if (row < 0 || col < 0) return FALSE;
2625 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2628 range.col0 = range.coli = col;
2629 range.row0 = range.rowi = row;
2631 psppire_sheet_draw_border (sheet, range);
2639 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
2643 rectangle_from_range (sheet, &new_range, &area);
2648 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
2650 area.x += sheet->cell_padding->left / 2;
2651 area.y += sheet->cell_padding->top / 2;
2652 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
2653 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
2655 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
2662 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
2667 /* Selection related functions */
2670 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
2673 sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
2675 sheet->range.col0 = sheet->range.coli = -1;
2676 sheet->range.row0 = sheet->range.rowi = row;
2678 rectangle_from_range (sheet, &sheet->range, &area);
2682 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2684 g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, row);
2688 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
2691 sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
2693 sheet->range.col0 = sheet->range.coli = column;
2694 sheet->range.row0 = sheet->range.rowi = -1;
2696 rectangle_from_range (sheet, &sheet->range, &area);
2700 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2702 g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, column);
2707 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
2710 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
2712 sheet->range = *range;
2714 rectangle_from_range (sheet, range, &area);
2717 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2722 psppire_sheet_unselect_range (PsppireSheet *sheet)
2724 sheet->select_status = PSPPIRE_SHEET_NORMAL;
2726 if (sheet->sheet_window != NULL)
2730 rectangle_from_range (sheet, &sheet->range, &area);
2733 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2736 g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, -1);
2737 g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, -1);
2741 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
2743 g_return_if_fail (sheet != NULL);
2744 *range = sheet->range;
2749 psppire_sheet_expose (GtkWidget *widget, GdkEventExpose *event)
2751 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2753 g_return_val_if_fail (event != NULL, FALSE);
2755 if (!GTK_WIDGET_DRAWABLE (widget))
2758 /* exposure events on the sheet */
2759 if (event->window == sheet->row_title_window &&
2760 sheet->row_titles_visible)
2762 draw_row_title_buttons_range (sheet,
2763 min_visible_row (sheet),
2764 max_visible_row (sheet));
2767 if (event->window == sheet->column_title_window &&
2768 sheet->column_titles_visible)
2770 draw_column_title_buttons_range (sheet,
2771 min_visible_column (sheet),
2772 max_visible_column (sheet));
2775 if (event->window == sheet->sheet_window)
2777 draw_sheet_region (sheet, event->region);
2779 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2783 rectangle_from_range (sheet, &sheet->range, &area);
2785 gdk_draw_rectangle (sheet->sheet_window,
2788 area.x + 1, area.y + 1,
2789 area.width, area.height);
2793 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
2796 PsppireSheetRange range;
2797 range.row0 = range.rowi = sheet->active_cell.row;
2798 range.col0 = range.coli = sheet->active_cell.col;
2800 rectangle_from_range (sheet, &range, &rect);
2802 if (GDK_OVERLAP_RECTANGLE_OUT !=
2803 gdk_region_rect_in (event->region, &rect))
2805 psppire_sheet_draw_active_cell (sheet);
2811 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2818 psppire_sheet_button_press (GtkWidget *widget, GdkEventButton *event)
2820 PsppireSheet *sheet;
2821 GdkModifierType mods;
2826 g_return_val_if_fail (widget != NULL, FALSE);
2827 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
2828 g_return_val_if_fail (event != NULL, FALSE);
2830 sheet = PSPPIRE_SHEET (widget);
2832 /* Cancel any pending tooltips */
2833 if (sheet->motion_timer)
2835 g_source_remove (sheet->motion_timer);
2836 sheet->motion_timer = 0;
2839 gtk_widget_get_pointer (widget, &x, &y);
2840 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2843 if (event->window == sheet->column_title_window)
2845 sheet->x_drag = event->x;
2846 g_signal_emit (sheet,
2847 sheet_signals[BUTTON_EVENT_COLUMN], 0,
2850 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2852 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2853 g_signal_emit (sheet,
2854 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
2858 if (event->window == sheet->row_title_window)
2860 g_signal_emit (sheet,
2861 sheet_signals[BUTTON_EVENT_ROW], 0,
2864 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2866 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2867 g_signal_emit (sheet,
2868 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
2872 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
2874 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
2877 /* press on resize windows */
2878 if (event->window == sheet->column_title_window)
2880 sheet->x_drag = event->x;
2882 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
2884 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
2885 gdk_pointer_grab (sheet->column_title_window, FALSE,
2886 GDK_POINTER_MOTION_HINT_MASK |
2887 GDK_BUTTON1_MOTION_MASK |
2888 GDK_BUTTON_RELEASE_MASK,
2889 NULL, NULL, event->time);
2891 draw_xor_vline (sheet);
2896 if (event->window == sheet->row_title_window)
2898 sheet->y_drag = event->y;
2900 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
2902 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
2903 gdk_pointer_grab (sheet->row_title_window, FALSE,
2904 GDK_POINTER_MOTION_HINT_MASK |
2905 GDK_BUTTON1_MOTION_MASK |
2906 GDK_BUTTON_RELEASE_MASK,
2907 NULL, NULL, event->time);
2909 draw_xor_hline (sheet);
2914 /* the sheet itself does not handle other than single click events */
2915 if (event->type != GDK_BUTTON_PRESS) return FALSE;
2917 /* selections on the sheet */
2918 if (event->window == sheet->sheet_window)
2920 gtk_widget_get_pointer (widget, &x, &y);
2921 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2922 gdk_pointer_grab (sheet->sheet_window, FALSE,
2923 GDK_POINTER_MOTION_HINT_MASK |
2924 GDK_BUTTON1_MOTION_MASK |
2925 GDK_BUTTON_RELEASE_MASK,
2926 NULL, NULL, event->time);
2927 gtk_grab_add (GTK_WIDGET (sheet));
2929 if ( sheet->select_status == PSPPIRE_SHEET_NORMAL)
2931 sheet->range.row0 = row;
2932 sheet->range.col0 = column;
2936 psppire_sheet_unselect_range (sheet);
2938 psppire_sheet_click_cell (sheet, row, column);
2941 if (event->window == sheet->column_title_window)
2943 gtk_widget_get_pointer (widget, &x, &y);
2944 if ( sheet->row_titles_visible)
2945 x -= sheet->row_title_area.width;
2947 x += sheet->hadjustment->value;
2949 column = column_from_xpixel (sheet, x);
2951 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2953 gtk_grab_add (GTK_WIDGET (sheet));
2954 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2958 if (event->window == sheet->row_title_window)
2960 gtk_widget_get_pointer (widget, &x, &y);
2961 if ( sheet->column_titles_visible)
2962 y -= sheet->column_title_area.height;
2964 y += sheet->vadjustment->value;
2966 row = row_from_ypixel (sheet, y);
2967 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2969 gtk_grab_add (GTK_WIDGET (sheet));
2970 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2978 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
2980 PsppireSheetCell cell;
2981 gboolean forbid_move;
2986 if (row >= psppire_axis_unit_count (sheet->vaxis)
2987 || column >= psppire_axis_unit_count (sheet->haxis))
2992 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
2993 &sheet->active_cell,
2999 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3002 row = sheet->active_cell.row;
3003 column = sheet->active_cell.col;
3005 change_active_cell (sheet, row, column);
3009 if (row == -1 && column >= 0)
3011 psppire_sheet_select_column (sheet, column);
3015 if (column == -1 && row >= 0)
3017 psppire_sheet_select_row (sheet, row);
3021 if (row == -1 && column == -1)
3023 sheet->range.row0 = 0;
3024 sheet->range.col0 = 0;
3025 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3026 sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
3030 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3031 change_active_cell (sheet, row, column);
3033 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3039 psppire_sheet_button_release (GtkWidget *widget,
3040 GdkEventButton *event)
3042 GdkDisplay *display = gtk_widget_get_display (widget);
3044 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3046 /* release on resize windows */
3047 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3050 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3051 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3053 gdk_display_pointer_ungrab (display, event->time);
3054 draw_xor_vline (sheet);
3057 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3058 + sheet->hadjustment->value;
3060 set_column_width (sheet, sheet->drag_cell.col, width);
3065 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3068 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3069 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3071 gdk_display_pointer_ungrab (display, event->time);
3072 draw_xor_hline (sheet);
3075 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3076 sheet->vadjustment->value;
3078 set_row_height (sheet, sheet->drag_cell.row, height);
3083 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3085 PsppireSheetRange old_range;
3086 draw_xor_rectangle (sheet, sheet->drag_range);
3087 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3088 gdk_display_pointer_ungrab (display, event->time);
3090 psppire_sheet_unselect_range (sheet);
3092 old_range = sheet->range;
3093 sheet->range = sheet->drag_range;
3094 sheet->drag_range = old_range;
3095 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3096 &sheet->drag_range, &sheet->range);
3097 psppire_sheet_select_range (sheet, &sheet->range);
3100 if (PSPPIRE_SHEET_IN_SELECTION (sheet))
3102 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3103 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3105 change_active_cell (sheet, sheet->active_cell.row,
3106 sheet->active_cell.col);
3109 gdk_display_pointer_ungrab (display, event->time);
3110 gtk_grab_remove (GTK_WIDGET (sheet));
3112 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3121 /* Shamelessly lifted from gtktooltips */
3123 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3127 gtk_widget_size_request (tip_window, &req);
3128 gtk_paint_flat_box (tip_window->style, tip_window->window,
3129 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3130 NULL, GTK_WIDGET(tip_window), "tooltip",
3131 0, 0, req.width, req.height);
3137 destroy_hover_window (PsppireSheetHoverTitle *h)
3139 gtk_widget_destroy (h->window);
3143 static PsppireSheetHoverTitle *
3144 create_hover_window (void)
3146 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3148 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3150 #if GTK_CHECK_VERSION (2, 9, 0)
3151 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3152 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3155 gtk_widget_set_app_paintable (hw->window, TRUE);
3156 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3157 gtk_widget_set_name (hw->window, "gtk-tooltips");
3158 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3160 g_signal_connect (hw->window,
3162 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3165 hw->label = gtk_label_new (NULL);
3168 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3169 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3171 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3173 gtk_widget_show (hw->label);
3175 g_signal_connect (hw->window,
3177 G_CALLBACK (gtk_widget_destroyed),
3183 #define HOVER_WINDOW_Y_OFFSET 2
3186 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3187 const gchar *subtitle)
3196 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3200 sheet->hover_window->row = row;
3201 sheet->hover_window->column = column;
3203 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3205 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3207 gtk_widget_show (sheet->hover_window->window);
3209 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3215 y += sheet->column_title_area.y;
3216 y += sheet->column_title_area.height;
3217 y += HOVER_WINDOW_Y_OFFSET;
3223 x += sheet->row_title_area.x;
3224 x += sheet->row_title_area.width * 2 / 3.0;
3227 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3232 motion_timeout_callback (gpointer data)
3234 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3238 gdk_threads_enter ();
3239 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3241 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3243 if (sheet->row_title_under && row >= 0)
3245 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3247 show_subtitle (sheet, row, -1, text);
3251 if (sheet->column_title_under && column >= 0)
3253 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3256 show_subtitle (sheet, -1, column, text);
3262 gdk_threads_leave ();
3267 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3269 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3270 GdkModifierType mods;
3271 GdkCursorType new_cursor;
3274 GdkDisplay *display;
3276 g_return_val_if_fail (event != NULL, FALSE);
3278 display = gtk_widget_get_display (widget);
3280 /* selections on the sheet */
3284 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3286 if ( sheet->motion_timer > 0 )
3287 g_source_remove (sheet->motion_timer);
3288 sheet->motion_timer =
3289 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3295 gtk_widget_get_pointer (widget, &wx, &wy);
3297 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3299 if ( row != sheet->hover_window->row ||
3300 column != sheet->hover_window->column)
3302 gtk_widget_hide (sheet->hover_window->window);
3307 if (event->window == sheet->column_title_window)
3309 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3310 on_column_boundary (sheet, x, &column))
3312 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3313 if (new_cursor != sheet->cursor_drag->type)
3315 gdk_cursor_unref (sheet->cursor_drag);
3316 sheet->cursor_drag =
3317 gdk_cursor_new_for_display (display, new_cursor);
3319 gdk_window_set_cursor (sheet->column_title_window,
3320 sheet->cursor_drag);
3325 new_cursor = GDK_TOP_LEFT_ARROW;
3326 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3327 new_cursor != sheet->cursor_drag->type)
3329 gdk_cursor_unref (sheet->cursor_drag);
3330 sheet->cursor_drag =
3331 gdk_cursor_new_for_display (display, new_cursor);
3332 gdk_window_set_cursor (sheet->column_title_window,
3333 sheet->cursor_drag);
3337 else if (event->window == sheet->row_title_window)
3339 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3340 on_row_boundary (sheet, y, &row))
3342 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3343 if (new_cursor != sheet->cursor_drag->type)
3345 gdk_cursor_unref (sheet->cursor_drag);
3346 sheet->cursor_drag =
3347 gdk_cursor_new_for_display (display, new_cursor);
3348 gdk_window_set_cursor (sheet->row_title_window,
3349 sheet->cursor_drag);
3354 new_cursor = GDK_TOP_LEFT_ARROW;
3355 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3356 new_cursor != sheet->cursor_drag->type)
3358 gdk_cursor_unref (sheet->cursor_drag);
3359 sheet->cursor_drag =
3360 gdk_cursor_new_for_display (display, new_cursor);
3361 gdk_window_set_cursor (sheet->row_title_window,
3362 sheet->cursor_drag);
3367 new_cursor = GDK_PLUS;
3368 if ( event->window == sheet->sheet_window &&
3369 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3370 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3371 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3372 new_cursor != sheet->cursor_drag->type)
3374 gdk_cursor_unref (sheet->cursor_drag);
3375 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3376 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3379 new_cursor = GDK_TOP_LEFT_ARROW;
3380 if ( event->window == sheet->sheet_window &&
3381 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ) &&
3382 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3383 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3384 new_cursor != sheet->cursor_drag->type)
3386 gdk_cursor_unref (sheet->cursor_drag);
3387 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3388 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3391 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3392 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3394 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3396 if (event->x != sheet->x_drag)
3398 draw_xor_vline (sheet);
3399 sheet->x_drag = event->x;
3400 draw_xor_vline (sheet);
3406 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3408 if (event->y != sheet->y_drag)
3410 draw_xor_hline (sheet);
3411 sheet->y_drag = event->y;
3412 draw_xor_hline (sheet);
3418 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3420 PsppireSheetRange aux;
3421 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3422 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3423 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3424 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3428 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3429 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3431 aux = sheet->drag_range;
3432 sheet->drag_range.row0 = sheet->range.row0 + row;
3433 sheet->drag_range.col0 = sheet->range.col0 + column;
3434 sheet->drag_range.rowi = sheet->range.rowi + row;
3435 sheet->drag_range.coli = sheet->range.coli + column;
3436 if (aux.row0 != sheet->drag_range.row0 ||
3437 aux.col0 != sheet->drag_range.col0)
3439 draw_xor_rectangle (sheet, aux);
3440 draw_xor_rectangle (sheet, sheet->drag_range);
3446 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3448 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
3449 column == sheet->active_cell.col) return TRUE;
3451 if ( mods & GDK_BUTTON1_MASK)
3453 if (PSPPIRE_SHEET_IN_SELECTION (sheet) )
3455 /* Redraw the old range */
3456 psppire_sheet_unselect_range (sheet);
3458 sheet->range.rowi = row;
3459 sheet->range.coli = column;
3461 /* Redraw the new range */
3462 psppire_sheet_select_range (sheet, &sheet->range);
3466 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3474 psppire_sheet_crossing_notify (GtkWidget *widget,
3475 GdkEventCrossing *event)
3477 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3479 if (event->window == sheet->column_title_window)
3480 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
3481 else if (event->window == sheet->row_title_window)
3482 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
3484 if (event->type == GDK_LEAVE_NOTIFY)
3485 gtk_widget_hide (sheet->hover_window->window);
3492 psppire_sheet_focus_in (GtkWidget *w,
3493 GdkEventFocus *event)
3495 PsppireSheet *sheet = PSPPIRE_SHEET (w);
3497 gtk_widget_grab_focus (sheet->entry_widget);
3505 psppire_sheet_entry_key_press (GtkWidget *widget,
3509 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
3514 /* Number of rows in a step-increment */
3515 #define ROWS_PER_STEP 1
3519 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
3521 gint old_row = sheet->active_cell.row ;
3522 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
3526 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
3527 min_visible_row (sheet));
3531 case GTK_SCROLL_PAGE_DOWN:
3532 gtk_adjustment_set_value (sheet->vadjustment,
3533 sheet->vadjustment->value +
3534 sheet->vadjustment->page_increment);
3536 case GTK_SCROLL_PAGE_UP:
3537 gtk_adjustment_set_value (sheet->vadjustment,
3538 sheet->vadjustment->value -
3539 sheet->vadjustment->page_increment);
3543 g_assert_not_reached ();
3548 vpixel += psppire_axis_start_pixel (sheet->vaxis,
3549 min_visible_row (sheet));
3551 new_row = row_from_ypixel (sheet, vpixel);
3553 change_active_cell (sheet, new_row,
3554 sheet->active_cell.col);
3559 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
3561 gint current_row = sheet->active_cell.row;
3562 gint current_col = sheet->active_cell.col;
3563 PsppireSheetCell new_cell ;
3564 gboolean forbidden = FALSE;
3566 new_cell.row = current_row;
3567 new_cell.col = current_col;
3571 case GTK_SCROLL_STEP_DOWN:
3574 case GTK_SCROLL_STEP_UP:
3577 case GTK_SCROLL_STEP_RIGHT:
3580 case GTK_SCROLL_STEP_LEFT:
3583 case GTK_SCROLL_STEP_FORWARD:
3586 psppire_sheet_model_get_column_count (sheet->model))
3592 case GTK_SCROLL_STEP_BACKWARD:
3594 if (new_cell.col < 0)
3597 psppire_sheet_model_get_column_count (sheet->model) - 1;
3602 g_assert_not_reached ();
3606 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3607 &sheet->active_cell,
3615 maximize_int (&new_cell.row, 0);
3616 maximize_int (&new_cell.col, 0);
3618 minimize_int (&new_cell.row,
3619 psppire_axis_unit_count (sheet->vaxis) - 1);
3621 minimize_int (&new_cell.col,
3622 psppire_axis_unit_count (sheet->haxis) - 1);
3624 change_active_cell (sheet, new_cell.row, new_cell.col);
3627 if ( new_cell.col > max_fully_visible_column (sheet))
3630 psppire_axis_start_pixel (sheet->haxis,
3632 hpos -= sheet->hadjustment->page_size;
3634 gtk_adjustment_set_value (sheet->hadjustment,
3637 else if ( new_cell.col < min_fully_visible_column (sheet))
3640 psppire_axis_start_pixel (sheet->haxis,
3643 gtk_adjustment_set_value (sheet->hadjustment,
3648 if ( new_cell.row > max_fully_visible_row (sheet))
3651 psppire_axis_start_pixel (sheet->vaxis,
3653 vpos -= sheet->vadjustment->page_size;
3655 gtk_adjustment_set_value (sheet->vadjustment,
3658 else if ( new_cell.row < min_fully_visible_row (sheet))
3661 psppire_axis_start_pixel (sheet->vaxis,
3664 gtk_adjustment_set_value (sheet->vadjustment,
3668 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3672 /* Move to row 0 keeping the current column */
3674 row0 (PsppireSheet *sheet)
3676 gtk_adjustment_set_value (sheet->vadjustment,
3677 sheet->vadjustment->lower);
3679 change_active_cell (sheet, 0, sheet->active_cell.col);
3682 /* Move to column 0 keeping the current row */
3684 col0 (PsppireSheet *sheet)
3686 gtk_adjustment_set_value (sheet->hadjustment,
3687 sheet->hadjustment->lower);
3689 change_active_cell (sheet, sheet->active_cell.row, 0);
3692 /* Move to last column keeping the current row */
3694 col_last (PsppireSheet *sheet)
3696 glong last_col = psppire_axis_unit_count (sheet->haxis) - 1;
3698 gtk_adjustment_set_value (sheet->hadjustment,
3699 sheet->hadjustment->upper - sheet->hadjustment->page_size);
3701 change_active_cell (sheet, sheet->active_cell.row,
3706 /* Move to last row keeping the current column */
3708 row_last (PsppireSheet *sheet)
3710 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
3712 gtk_adjustment_set_value (sheet->vadjustment,
3713 sheet->vadjustment->upper- sheet->vadjustment->page_size);
3715 change_active_cell (sheet,
3717 sheet->active_cell.col);
3723 psppire_sheet_key_press (GtkWidget *widget,
3726 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3728 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3730 switch (key->keyval)
3733 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
3737 if (key->state & GDK_CONTROL_MASK)
3740 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
3743 case GDK_ISO_Left_Tab:
3744 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
3748 if (key->state & GDK_CONTROL_MASK)
3751 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
3755 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3759 if (key->state & GDK_CONTROL_MASK)
3762 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3766 if (key->state & GDK_CONTROL_MASK)
3769 step_sheet (sheet, GTK_SCROLL_STEP_UP);
3773 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
3776 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
3788 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
3800 psppire_sheet_size_request (GtkWidget *widget,
3801 GtkRequisition *requisition)
3803 PsppireSheet *sheet;
3805 g_return_if_fail (widget != NULL);
3806 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3807 g_return_if_fail (requisition != NULL);
3809 sheet = PSPPIRE_SHEET (widget);
3811 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
3812 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
3814 /* compute the size of the column title area */
3815 if (sheet->column_titles_visible)
3816 requisition->height += sheet->column_title_area.height;
3818 /* compute the size of the row title area */
3819 if (sheet->row_titles_visible)
3820 requisition->width += sheet->row_title_area.width;
3825 psppire_sheet_size_allocate (GtkWidget *widget,
3826 GtkAllocation *allocation)
3828 PsppireSheet *sheet;
3829 GtkAllocation sheet_allocation;
3832 g_return_if_fail (widget != NULL);
3833 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3834 g_return_if_fail (allocation != NULL);
3836 sheet = PSPPIRE_SHEET (widget);
3837 widget->allocation = *allocation;
3838 border_width = GTK_CONTAINER (widget)->border_width;
3840 if (GTK_WIDGET_REALIZED (widget))
3841 gdk_window_move_resize (widget->window,
3842 allocation->x + border_width,
3843 allocation->y + border_width,
3844 allocation->width - 2 * border_width,
3845 allocation->height - 2 * border_width);
3847 sheet_allocation.x = 0;
3848 sheet_allocation.y = 0;
3849 sheet_allocation.width = allocation->width - 2 * border_width;
3850 sheet_allocation.height = allocation->height - 2 * border_width;
3852 if (GTK_WIDGET_REALIZED (widget))
3853 gdk_window_move_resize (sheet->sheet_window,
3856 sheet_allocation.width,
3857 sheet_allocation.height);
3859 /* position the window which holds the column title buttons */
3860 sheet->column_title_area.x = 0;
3861 sheet->column_title_area.y = 0;
3862 sheet->column_title_area.width = sheet_allocation.width ;
3865 /* position the window which holds the row title buttons */
3866 sheet->row_title_area.x = 0;
3867 sheet->row_title_area.y = 0;
3868 sheet->row_title_area.height = sheet_allocation.height;
3870 if (sheet->row_titles_visible)
3871 sheet->column_title_area.x += sheet->row_title_area.width;
3873 if (sheet->column_titles_visible)
3874 sheet->row_title_area.y += sheet->column_title_area.height;
3876 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
3877 gdk_window_move_resize (sheet->column_title_window,
3878 sheet->column_title_area.x,
3879 sheet->column_title_area.y,
3880 sheet->column_title_area.width,
3881 sheet->column_title_area.height);
3884 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
3885 gdk_window_move_resize (sheet->row_title_window,
3886 sheet->row_title_area.x,
3887 sheet->row_title_area.y,
3888 sheet->row_title_area.width,
3889 sheet->row_title_area.height);
3891 size_allocate_global_button (sheet);
3895 gint width = sheet->column_title_area.width;
3897 if ( sheet->row_titles_visible)
3898 width -= sheet->row_title_area.width;
3900 g_object_set (sheet->haxis,
3901 "minimum-extent", width,
3908 gint height = sheet->row_title_area.height;
3910 if ( sheet->column_titles_visible)
3911 height -= sheet->column_title_area.height;
3913 g_object_set (sheet->vaxis,
3914 "minimum-extent", height,
3919 /* set the scrollbars adjustments */
3920 adjust_scrollbars (sheet);
3924 draw_column_title_buttons (PsppireSheet *sheet)
3928 if (!sheet->column_titles_visible) return;
3929 if (!GTK_WIDGET_REALIZED (sheet))
3932 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
3935 if (sheet->row_titles_visible)
3937 x = sheet->row_title_area.width;
3940 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
3942 sheet->column_title_area.width = width;
3943 sheet->column_title_area.x = x;
3944 gdk_window_move_resize (sheet->column_title_window,
3945 sheet->column_title_area.x,
3946 sheet->column_title_area.y,
3947 sheet->column_title_area.width,
3948 sheet->column_title_area.height);
3951 if (max_visible_column (sheet) ==
3952 psppire_axis_unit_count (sheet->haxis) - 1)
3953 gdk_window_clear_area (sheet->column_title_window,
3955 sheet->column_title_area.width,
3956 sheet->column_title_area.height);
3958 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
3960 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
3961 max_visible_column (sheet));
3965 draw_row_title_buttons (PsppireSheet *sheet)
3970 if (!sheet->row_titles_visible) return;
3971 if (!GTK_WIDGET_REALIZED (sheet))
3974 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
3976 if (sheet->column_titles_visible)
3978 y = sheet->column_title_area.height;
3981 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
3983 sheet->row_title_area.y = y;
3984 sheet->row_title_area.height = height;
3985 gdk_window_move_resize (sheet->row_title_window,
3986 sheet->row_title_area.x,
3987 sheet->row_title_area.y,
3988 sheet->row_title_area.width,
3989 sheet->row_title_area.height);
3992 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
3993 gdk_window_clear_area (sheet->row_title_window,
3995 sheet->row_title_area.width,
3996 sheet->row_title_area.height);
3998 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4000 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4001 max_visible_row (sheet));
4006 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4008 GtkAllocation entry_alloc;
4009 PsppireSheetCellAttr attributes = { 0 };
4010 GtkEntry *sheet_entry;
4012 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4013 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4015 sheet_entry = psppire_sheet_get_entry (sheet);
4017 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4018 sheet->active_cell.col,
4022 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4024 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4026 style->bg[GTK_STATE_NORMAL] = attributes.background;
4027 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4028 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4029 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4030 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4031 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4034 rectangle_from_cell (sheet, sheet->active_cell.row,
4035 sheet->active_cell.col, &entry_alloc);
4037 entry_alloc.x += sheet->cell_padding->left;
4038 entry_alloc.y += sheet->cell_padding->right;
4039 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4040 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4043 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4044 entry_alloc.height);
4045 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4049 /* Copy the sheet's font to the entry widget */
4051 set_entry_widget_font (PsppireSheet *sheet)
4053 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4055 pango_font_description_free (style->font_desc);
4056 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4058 gtk_widget_modify_style (sheet->entry_widget, style);
4062 create_sheet_entry (PsppireSheet *sheet)
4064 if (sheet->entry_widget)
4066 gtk_widget_unparent (sheet->entry_widget);
4069 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4070 g_object_ref_sink (sheet->entry_widget);
4072 gtk_widget_size_request (sheet->entry_widget, NULL);
4074 if ( GTK_IS_ENTRY (sheet->entry_widget))
4076 g_object_set (sheet->entry_widget,
4081 if (GTK_WIDGET_REALIZED (sheet))
4083 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4084 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4085 gtk_widget_realize (sheet->entry_widget);
4088 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4089 G_CALLBACK (psppire_sheet_entry_key_press),
4092 set_entry_widget_font (sheet);
4094 gtk_widget_show (sheet->entry_widget);
4098 /* Finds the last child widget that happens to be of type GtkEntry */
4100 find_entry (GtkWidget *w, gpointer user_data)
4102 GtkWidget **entry = user_data;
4103 if ( GTK_IS_ENTRY (w))
4111 psppire_sheet_get_entry (PsppireSheet *sheet)
4113 GtkWidget *w = sheet->entry_widget;
4115 g_return_val_if_fail (sheet != NULL, NULL);
4116 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4117 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4119 while (! GTK_IS_ENTRY (w))
4121 GtkWidget *entry = NULL;
4123 if (GTK_IS_CONTAINER (w))
4125 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4134 return GTK_ENTRY (w);
4139 draw_button (PsppireSheet *sheet, GdkWindow *window,
4140 PsppireSheetButton *button, gboolean is_sensitive,
4141 GdkRectangle allocation)
4143 GtkShadowType shadow_type;
4144 gint text_width = 0, text_height = 0;
4145 PangoAlignment align = PANGO_ALIGN_LEFT;
4151 g_return_if_fail (sheet != NULL);
4152 g_return_if_fail (button != NULL);
4155 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4157 gdk_window_clear_area (window,
4158 allocation.x, allocation.y,
4159 allocation.width, allocation.height);
4161 gtk_widget_ensure_style (sheet->button);
4163 gtk_paint_box (sheet->button->style, window,
4164 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4166 GTK_WIDGET (sheet->button),
4168 allocation.x, allocation.y,
4169 allocation.width, allocation.height);
4171 state = button->state;
4172 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4174 if (state == GTK_STATE_ACTIVE)
4175 shadow_type = GTK_SHADOW_IN;
4177 shadow_type = GTK_SHADOW_OUT;
4179 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4180 gtk_paint_box (sheet->button->style, window,
4181 button->state, shadow_type,
4182 &allocation, GTK_WIDGET (sheet->button),
4184 allocation.x, allocation.y,
4185 allocation.width, allocation.height);
4187 if ( button->overstruck)
4189 GdkPoint points[2] = {
4190 {allocation.x, allocation.y},
4191 {allocation.x + allocation.width,
4192 allocation.y + allocation.height}
4195 gtk_paint_polygon (sheet->button->style,
4207 if (button->label_visible)
4209 text_height = DEFAULT_ROW_HEIGHT -
4210 2 * COLUMN_TITLES_HEIGHT;
4212 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4214 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4217 allocation.y += 2 * sheet->button->style->ythickness;
4219 if (button->label && strlen (button->label) > 0)
4221 PangoRectangle rect;
4222 gchar *line = button->label;
4224 PangoLayout *layout = NULL;
4225 gint real_x = allocation.x;
4226 gint real_y = allocation.y;
4228 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4229 pango_layout_get_extents (layout, NULL, &rect);
4231 text_width = PANGO_PIXELS (rect.width);
4232 switch (button->justification)
4234 case GTK_JUSTIFY_LEFT:
4235 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4236 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4238 case GTK_JUSTIFY_RIGHT:
4239 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4240 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4242 case GTK_JUSTIFY_CENTER:
4244 real_x = allocation.x + (allocation.width - text_width)/2;
4245 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4246 pango_layout_set_justify (layout, TRUE);
4248 pango_layout_set_alignment (layout, align);
4249 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4258 g_object_unref (layout);
4261 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4263 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4267 psppire_sheet_button_free (button);
4271 /* Draw the column title buttons FIRST through to LAST */
4273 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4277 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4279 if (!sheet->column_titles_visible) return;
4281 g_return_if_fail (first >= min_visible_column (sheet));
4282 g_return_if_fail (last <= max_visible_column (sheet));
4285 rect.height = sheet->column_title_area.height;
4286 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4287 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4288 + psppire_axis_unit_size (sheet->haxis, last);
4290 rect.x -= sheet->hadjustment->value;
4292 minimize_int (&rect.width, sheet->column_title_area.width);
4293 maximize_int (&rect.x, 0);
4295 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4297 for (col = first ; col <= last ; ++col)
4299 GdkRectangle allocation;
4300 gboolean is_sensitive = FALSE;
4302 PsppireSheetButton *
4303 button = psppire_sheet_model_get_column_button (sheet->model, col);
4305 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4307 allocation.x -= sheet->hadjustment->value;
4309 allocation.height = sheet->column_title_area.height;
4310 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4311 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4313 draw_button (sheet, sheet->column_title_window,
4314 button, is_sensitive, allocation);
4317 gdk_window_end_paint (sheet->column_title_window);
4322 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4326 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4328 if (!sheet->row_titles_visible) return;
4330 g_return_if_fail (first >= min_visible_row (sheet));
4331 g_return_if_fail (last <= max_visible_row (sheet));
4334 rect.width = sheet->row_title_area.width;
4335 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4336 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4337 + psppire_axis_unit_size (sheet->vaxis, last);
4339 rect.y -= sheet->vadjustment->value;
4341 minimize_int (&rect.height, sheet->row_title_area.height);
4342 maximize_int (&rect.y, 0);
4344 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4345 for (row = first; row <= last; ++row)
4347 GdkRectangle allocation;
4349 gboolean is_sensitive = FALSE;
4351 PsppireSheetButton *button =
4352 psppire_sheet_model_get_row_button (sheet->model, row);
4354 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4356 allocation.y -= sheet->vadjustment->value;
4358 allocation.width = sheet->row_title_area.width;
4359 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4360 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4362 draw_button (sheet, sheet->row_title_window,
4363 button, is_sensitive, allocation);
4366 gdk_window_end_paint (sheet->row_title_window);
4373 * vadjustment_value_changed
4374 * hadjustment_value_changed */
4378 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4381 (adj->value + adj->page_size)
4383 (adj->upper - adj->lower);
4385 const glong last_item = psppire_axis_unit_count (axis) - 1;
4387 if (isnan (position) || position < 0)
4391 psppire_axis_start_pixel (axis, last_item)
4393 psppire_axis_unit_size (axis, last_item)
4397 adj->page_size = page_size;
4400 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4402 if ( adj->value < adj->lower)
4403 adj->value = adj->lower;
4406 gtk_adjustment_changed (adj);
4411 adjust_scrollbars (PsppireSheet *sheet)
4415 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4418 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4420 if ( sheet->row_titles_visible)
4421 width -= sheet->row_title_area.width;
4423 if (sheet->column_titles_visible)
4424 height -= sheet->column_title_area.height;
4426 if (sheet->vadjustment)
4428 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4430 sheet->vadjustment->step_increment =
4432 psppire_axis_unit_size (sheet->vaxis, last_row);
4434 sheet->vadjustment->page_increment =
4436 sheet->column_title_area.height -
4437 psppire_axis_unit_size (sheet->vaxis, last_row);
4439 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4442 if (sheet->hadjustment)
4444 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4445 sheet->hadjustment->step_increment = 1;
4447 sheet->hadjustment->page_increment = width;
4449 sheet->hadjustment->upper =
4450 psppire_axis_start_pixel (sheet->haxis, last_col)
4452 psppire_axis_unit_size (sheet->haxis, last_col)
4455 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4459 /* Subtracts the region of WIDGET from REGION */
4461 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4464 GdkRectangle intersect;
4467 gdk_region_get_clipbox (region, &rect);
4468 gtk_widget_intersect (widget,
4472 region2 = gdk_region_rectangle (&intersect);
4473 gdk_region_subtract (region, region2);
4474 gdk_region_destroy (region2);
4478 vadjustment_value_changed (GtkAdjustment *adjustment,
4482 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4484 g_return_if_fail (adjustment != NULL);
4486 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4488 gtk_widget_hide (sheet->entry_widget);
4491 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4493 subtract_widget_region (region, sheet->button);
4494 gdk_window_begin_paint_region (sheet->sheet_window, region);
4496 draw_sheet_region (sheet, region);
4498 draw_row_title_buttons (sheet);
4499 psppire_sheet_draw_active_cell (sheet);
4501 gdk_window_end_paint (sheet->sheet_window);
4502 gdk_region_destroy (region);
4507 hadjustment_value_changed (GtkAdjustment *adjustment,
4511 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4513 g_return_if_fail (adjustment != NULL);
4515 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4517 gtk_widget_hide (sheet->entry_widget);
4521 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4523 subtract_widget_region (region, sheet->button);
4524 gdk_window_begin_paint_region (sheet->sheet_window, region);
4526 draw_sheet_region (sheet, region);
4528 draw_column_title_buttons (sheet);
4530 psppire_sheet_draw_active_cell (sheet);
4532 gdk_window_end_paint (sheet->sheet_window);
4534 gdk_region_destroy (region);
4538 /* COLUMN RESIZING */
4540 draw_xor_vline (PsppireSheet *sheet)
4543 gint xpos = sheet->x_drag;
4544 gdk_drawable_get_size (sheet->sheet_window,
4547 if (sheet->row_titles_visible)
4548 xpos += sheet->row_title_area.width;
4550 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4552 sheet->column_title_area.height,
4554 height + CELL_SPACING);
4559 draw_xor_hline (PsppireSheet *sheet)
4563 gint ypos = sheet->y_drag;
4565 gdk_drawable_get_size (sheet->sheet_window,
4569 if (sheet->column_titles_visible)
4570 ypos += sheet->column_title_area.height;
4572 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4573 sheet->row_title_area.width,
4575 width + CELL_SPACING,
4579 /* SELECTED RANGE */
4581 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
4584 GdkRectangle clip_area, area;
4587 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
4588 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
4589 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
4590 psppire_axis_unit_size (sheet->haxis, range.coli);
4591 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
4592 psppire_axis_unit_size (sheet->vaxis, range.rowi);
4594 clip_area.x = sheet->row_title_area.width;
4595 clip_area.y = sheet->column_title_area.height;
4597 gdk_drawable_get_size (sheet->sheet_window,
4598 &clip_area.width, &clip_area.height);
4600 if (!sheet->row_titles_visible) clip_area.x = 0;
4601 if (!sheet->column_titles_visible) clip_area.y = 0;
4605 area.width = area.width + area.x;
4608 if (area.width > clip_area.width) area.width = clip_area.width + 10;
4611 area.height = area.height + area.y;
4614 if (area.height > clip_area.height) area.height = clip_area.height + 10;
4618 clip_area.width += 3;
4619 clip_area.height += 3;
4621 gdk_gc_get_values (sheet->xor_gc, &values);
4623 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
4625 gdk_draw_rectangle (sheet->sheet_window,
4628 area.x + i, area.y + i,
4629 area.width - 2 * i, area.height - 2 * i);
4632 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4634 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
4639 set_column_width (PsppireSheet *sheet,
4643 g_return_if_fail (sheet != NULL);
4644 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4646 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
4652 psppire_axis_resize (sheet->haxis, column,
4653 width - sheet->cell_padding->left -
4654 sheet->cell_padding->right);
4656 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4658 draw_column_title_buttons (sheet);
4659 adjust_scrollbars (sheet);
4660 psppire_sheet_size_allocate_entry (sheet);
4661 redraw_range (sheet, NULL);
4666 set_row_height (PsppireSheet *sheet,
4670 g_return_if_fail (sheet != NULL);
4671 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4673 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
4679 psppire_axis_resize (sheet->vaxis, row,
4680 height - sheet->cell_padding->top -
4681 sheet->cell_padding->bottom);
4683 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
4685 draw_row_title_buttons (sheet);
4686 adjust_scrollbars (sheet);
4687 psppire_sheet_size_allocate_entry (sheet);
4688 redraw_range (sheet, NULL);
4693 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
4694 PsppireSheetCellAttr *attr)
4697 const GtkJustification *j ;
4698 GdkColormap *colormap;
4700 g_return_val_if_fail (sheet != NULL, FALSE);
4701 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
4703 if (row < 0 || col < 0) return FALSE;
4705 attr->foreground = GTK_WIDGET (sheet)->style->black;
4706 attr->background = sheet->color[BG_COLOR];
4708 attr->border.width = 0;
4709 attr->border.line_style = GDK_LINE_SOLID;
4710 attr->border.cap_style = GDK_CAP_NOT_LAST;
4711 attr->border.join_style = GDK_JOIN_MITER;
4712 attr->border.mask = 0;
4713 attr->border.color = GTK_WIDGET (sheet)->style->black;
4715 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
4716 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
4719 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
4720 attr->foreground = *fg;
4723 bg = psppire_sheet_model_get_background (sheet->model, row, col);
4726 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
4727 attr->background = *bg;
4730 attr->justification =
4731 psppire_sheet_model_get_column_justification (sheet->model, col);
4733 j = psppire_sheet_model_get_justification (sheet->model, row, col);
4735 attr->justification = *j;
4741 psppire_sheet_forall (GtkContainer *container,
4742 gboolean include_internals,
4743 GtkCallback callback,
4744 gpointer callback_data)
4746 PsppireSheet *sheet = PSPPIRE_SHEET (container);
4748 g_return_if_fail (callback != NULL);
4750 if (sheet->button && sheet->button->parent)
4751 (* callback) (sheet->button, callback_data);
4753 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
4754 (* callback) (sheet->entry_widget, callback_data);
4759 psppire_sheet_get_model (const PsppireSheet *sheet)
4761 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4763 return sheet->model;
4767 PsppireSheetButton *
4768 psppire_sheet_button_new (void)
4770 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
4772 button->state = GTK_STATE_NORMAL;
4773 button->label = NULL;
4774 button->label_visible = TRUE;
4775 button->justification = GTK_JUSTIFY_FILL;
4776 button->overstruck = FALSE;
4783 psppire_sheet_button_free (PsppireSheetButton *button)
4785 if (!button) return ;
4787 g_free (button->label);
4792 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
4794 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
4796 if ( NULL == celltext)
4799 g_string_append (string, celltext);
4805 range_to_text (const PsppireSheet *sheet)
4810 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4813 string = g_string_sized_new (80);
4815 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4817 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
4819 append_cell_text (string, sheet, r, c);
4820 g_string_append (string, "\t");
4822 append_cell_text (string, sheet, r, c);
4823 if ( r < sheet->range.rowi)
4824 g_string_append (string, "\n");
4831 range_to_html (const PsppireSheet *sheet)
4836 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4839 string = g_string_sized_new (480);
4841 g_string_append (string, "<html>\n");
4842 g_string_append (string, "<body>\n");
4843 g_string_append (string, "<table>\n");
4844 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4846 g_string_append (string, "<tr>\n");
4847 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
4849 g_string_append (string, "<td>");
4850 append_cell_text (string, sheet, r, c);
4851 g_string_append (string, "</td>\n");
4853 g_string_append (string, "</tr>\n");
4855 g_string_append (string, "</table>\n");
4856 g_string_append (string, "</body>\n");
4857 g_string_append (string, "</html>\n");
4869 primary_get_cb (GtkClipboard *clipboard,
4870 GtkSelectionData *selection_data,
4874 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4875 GString *string = NULL;
4879 case SELECT_FMT_TEXT:
4880 string = range_to_text (sheet);
4882 case SELECT_FMT_HTML:
4883 string = range_to_html (sheet);
4886 g_assert_not_reached ();
4889 gtk_selection_data_set (selection_data, selection_data->target,
4891 (const guchar *) string->str, string->len);
4892 g_string_free (string, TRUE);
4896 primary_clear_cb (GtkClipboard *clipboard,
4899 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4900 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4903 psppire_sheet_unselect_range (sheet);
4907 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
4909 static const GtkTargetEntry targets[] = {
4910 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
4911 { "STRING", 0, SELECT_FMT_TEXT },
4912 { "TEXT", 0, SELECT_FMT_TEXT },
4913 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
4914 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
4915 { "text/plain", 0, SELECT_FMT_TEXT },
4916 { "text/html", 0, SELECT_FMT_HTML }
4919 GtkClipboard *clipboard;
4921 if (!GTK_WIDGET_REALIZED (sheet))
4924 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
4925 GDK_SELECTION_PRIMARY);
4927 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
4929 if (!gtk_clipboard_set_with_owner (clipboard, targets,
4930 G_N_ELEMENTS (targets),
4931 primary_get_cb, primary_clear_cb,
4933 primary_clear_cb (clipboard, sheet);
4937 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
4938 gtk_clipboard_clear (clipboard);