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 g_return_if_fail (object != NULL);
1702 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1704 if (G_OBJECT_CLASS (parent_class)->finalize)
1705 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1709 psppire_sheet_dispose (GObject *object)
1711 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1713 g_return_if_fail (object != NULL);
1714 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1716 if ( sheet->dispose_has_run )
1719 sheet->dispose_has_run = TRUE;
1721 if ( sheet->cell_padding)
1722 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1724 if (sheet->model) g_object_unref (sheet->model);
1725 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1726 if (sheet->haxis) g_object_unref (sheet->haxis);
1728 g_object_unref (sheet->button);
1729 sheet->button = NULL;
1731 /* unref adjustments */
1732 if (sheet->hadjustment)
1734 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1735 G_SIGNAL_MATCH_DATA,
1739 g_object_unref (sheet->hadjustment);
1740 sheet->hadjustment = NULL;
1743 if (sheet->vadjustment)
1745 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1746 G_SIGNAL_MATCH_DATA,
1750 g_object_unref (sheet->vadjustment);
1752 sheet->vadjustment = NULL;
1755 if (G_OBJECT_CLASS (parent_class)->dispose)
1756 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1760 psppire_sheet_style_set (GtkWidget *widget,
1761 GtkStyle *previous_style)
1763 PsppireSheet *sheet;
1765 g_return_if_fail (widget != NULL);
1766 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1768 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1769 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1771 sheet = PSPPIRE_SHEET (widget);
1773 if (GTK_WIDGET_REALIZED (widget))
1775 gtk_style_set_background (widget->style, widget->window, widget->state);
1778 set_entry_widget_font (sheet);
1783 psppire_sheet_realize (GtkWidget *widget)
1785 PsppireSheet *sheet;
1786 GdkWindowAttr attributes;
1787 const gint attributes_mask =
1788 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1791 GdkColormap *colormap;
1792 GdkDisplay *display;
1794 g_return_if_fail (widget != NULL);
1795 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1797 sheet = PSPPIRE_SHEET (widget);
1799 colormap = gtk_widget_get_colormap (widget);
1800 display = gtk_widget_get_display (widget);
1802 attributes.window_type = GDK_WINDOW_CHILD;
1803 attributes.x = widget->allocation.x;
1804 attributes.y = widget->allocation.y;
1805 attributes.width = widget->allocation.width;
1806 attributes.height = widget->allocation.height;
1807 attributes.wclass = GDK_INPUT_OUTPUT;
1809 attributes.visual = gtk_widget_get_visual (widget);
1810 attributes.colormap = colormap;
1812 attributes.event_mask = gtk_widget_get_events (widget);
1813 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1814 GDK_BUTTON_PRESS_MASK |
1815 GDK_BUTTON_RELEASE_MASK |
1816 GDK_KEY_PRESS_MASK |
1817 GDK_ENTER_NOTIFY_MASK |
1818 GDK_LEAVE_NOTIFY_MASK |
1819 GDK_POINTER_MOTION_MASK |
1820 GDK_POINTER_MOTION_HINT_MASK);
1822 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1825 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1827 gdk_window_set_user_data (widget->window, sheet);
1829 widget->style = gtk_style_attach (widget->style, widget->window);
1831 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1833 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1834 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1836 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1837 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1842 attributes.width = sheet->column_title_area.width;
1843 attributes.height = sheet->column_title_area.height;
1846 /* column - title window */
1847 sheet->column_title_window =
1848 gdk_window_new (widget->window, &attributes, attributes_mask);
1849 gdk_window_set_user_data (sheet->column_title_window, sheet);
1850 gtk_style_set_background (widget->style, sheet->column_title_window,
1856 attributes.width = sheet->row_title_area.width;
1857 attributes.height = sheet->row_title_area.height;
1859 /* row - title window */
1860 sheet->row_title_window = gdk_window_new (widget->window,
1861 &attributes, attributes_mask);
1862 gdk_window_set_user_data (sheet->row_title_window, sheet);
1863 gtk_style_set_background (widget->style, sheet->row_title_window,
1866 /* sheet - window */
1867 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1872 sheet->sheet_window = gdk_window_new (widget->window,
1873 &attributes, attributes_mask);
1874 gdk_window_set_user_data (sheet->sheet_window, sheet);
1876 gdk_cursor_unref (attributes.cursor);
1878 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1879 gdk_window_show (sheet->sheet_window);
1882 sheet->fg_gc = gdk_gc_new (widget->window);
1883 sheet->bg_gc = gdk_gc_new (widget->window);
1885 values.foreground = widget->style->white;
1886 values.function = GDK_INVERT;
1887 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1888 values.line_width = MAX (sheet->cell_padding->left,
1889 MAX (sheet->cell_padding->right,
1890 MAX (sheet->cell_padding->top,
1891 sheet->cell_padding->bottom)));
1893 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1901 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1902 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1904 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1905 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1907 sheet->button->style = gtk_style_attach (sheet->button->style,
1908 sheet->sheet_window);
1911 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1913 if (sheet->column_titles_visible)
1914 gdk_window_show (sheet->column_title_window);
1915 if (sheet->row_titles_visible)
1916 gdk_window_show (sheet->row_title_window);
1918 sheet->hover_window = create_hover_window ();
1920 draw_row_title_buttons (sheet);
1921 draw_column_title_buttons (sheet);
1923 psppire_sheet_update_primary_selection (sheet);
1926 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1930 create_global_button (PsppireSheet *sheet)
1932 sheet->button = gtk_button_new_with_label (" ");
1934 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1936 g_object_ref_sink (sheet->button);
1938 g_signal_connect (sheet->button,
1940 G_CALLBACK (global_button_clicked),
1945 size_allocate_global_button (PsppireSheet *sheet)
1947 GtkAllocation allocation;
1949 if (!sheet->column_titles_visible) return;
1950 if (!sheet->row_titles_visible) return;
1952 gtk_widget_size_request (sheet->button, NULL);
1956 allocation.width = sheet->row_title_area.width;
1957 allocation.height = sheet->column_title_area.height;
1959 gtk_widget_size_allocate (sheet->button, &allocation);
1963 global_button_clicked (GtkWidget *widget, gpointer data)
1965 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
1970 psppire_sheet_unrealize (GtkWidget *widget)
1972 PsppireSheet *sheet;
1974 g_return_if_fail (widget != NULL);
1975 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1977 sheet = PSPPIRE_SHEET (widget);
1979 gdk_cursor_unref (sheet->cursor_drag);
1980 sheet->cursor_drag = NULL;
1982 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
1983 sheet->color, n_COLORS);
1985 g_object_unref (sheet->xor_gc);
1986 g_object_unref (sheet->fg_gc);
1987 g_object_unref (sheet->bg_gc);
1989 destroy_hover_window (sheet->hover_window);
1991 gdk_window_destroy (sheet->sheet_window);
1992 gdk_window_destroy (sheet->column_title_window);
1993 gdk_window_destroy (sheet->row_title_window);
1995 gtk_widget_unparent (sheet->entry_widget);
1996 if (sheet->button != NULL)
1997 gtk_widget_unparent (sheet->button);
1999 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2000 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2004 psppire_sheet_map (GtkWidget *widget)
2006 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2008 g_return_if_fail (widget != NULL);
2009 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2011 if (!GTK_WIDGET_MAPPED (widget))
2013 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2015 gdk_window_show (widget->window);
2016 gdk_window_show (sheet->sheet_window);
2018 if (sheet->column_titles_visible)
2020 draw_column_title_buttons (sheet);
2021 gdk_window_show (sheet->column_title_window);
2023 if (sheet->row_titles_visible)
2025 draw_row_title_buttons (sheet);
2026 gdk_window_show (sheet->row_title_window);
2029 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2030 && sheet->active_cell.row >= 0
2031 && sheet->active_cell.col >= 0 )
2033 gtk_widget_show (sheet->entry_widget);
2034 gtk_widget_map (sheet->entry_widget);
2037 if (!GTK_WIDGET_MAPPED (sheet->button))
2039 gtk_widget_show (sheet->button);
2040 gtk_widget_map (sheet->button);
2043 redraw_range (sheet, NULL);
2044 change_active_cell (sheet,
2045 sheet->active_cell.row,
2046 sheet->active_cell.col);
2051 psppire_sheet_unmap (GtkWidget *widget)
2053 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2055 if (!GTK_WIDGET_MAPPED (widget))
2058 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2060 gdk_window_hide (sheet->sheet_window);
2061 if (sheet->column_titles_visible)
2062 gdk_window_hide (sheet->column_title_window);
2063 if (sheet->row_titles_visible)
2064 gdk_window_hide (sheet->row_title_window);
2065 gdk_window_hide (widget->window);
2067 gtk_widget_unmap (sheet->entry_widget);
2068 gtk_widget_unmap (sheet->button);
2069 gtk_widget_unmap (sheet->hover_window->window);
2072 /* get cell attributes of the given cell */
2073 /* TRUE means that the cell is currently allocated */
2074 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2076 PsppireSheetCellAttr *attributes);
2081 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2083 PangoLayout *layout;
2084 PangoRectangle text;
2085 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2090 PsppireSheetCellAttr attributes;
2093 g_return_if_fail (sheet != NULL);
2095 /* bail now if we aren't yet drawable */
2096 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2099 row >= psppire_axis_unit_count (sheet->vaxis))
2103 col >= psppire_axis_unit_count (sheet->haxis))
2106 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2108 /* select GC for background rectangle */
2109 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2110 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2112 rectangle_from_cell (sheet, row, col, &area);
2114 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2116 if (sheet->show_grid)
2118 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2120 gdk_draw_rectangle (sheet->sheet_window,
2124 area.width, area.height);
2128 label = psppire_sheet_cell_get_text (sheet, row, col);
2133 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2134 dispose_string (sheet, label);
2137 pango_layout_set_font_description (layout, font_desc);
2139 pango_layout_get_pixel_extents (layout, NULL, &text);
2141 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2143 font_height = pango_font_description_get_size (font_desc);
2144 if ( !pango_font_description_get_size_is_absolute (font_desc))
2145 font_height /= PANGO_SCALE;
2148 if ( sheet->cell_padding )
2150 area.x += sheet->cell_padding->left;
2151 area.width -= sheet->cell_padding->right
2152 + sheet->cell_padding->left;
2154 area.y += sheet->cell_padding->top;
2155 area.height -= sheet->cell_padding->bottom
2157 sheet->cell_padding->top;
2160 /* Centre the text vertically */
2161 area.y += (area.height - font_height) / 2.0;
2163 switch (attributes.justification)
2165 case GTK_JUSTIFY_RIGHT:
2166 area.x += area.width - text.width;
2168 case GTK_JUSTIFY_CENTER:
2169 area.x += (area.width - text.width) / 2.0;
2171 case GTK_JUSTIFY_LEFT:
2175 g_critical ("Unhandled justification %d in column %d\n",
2176 attributes.justification, col);
2180 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2185 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2186 g_object_unref (layout);
2191 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2193 PsppireSheetRange range;
2198 PsppireSheetRange drawing_range;
2200 gdk_region_get_clipbox (region, &area);
2202 y = area.y + sheet->vadjustment->value;
2203 x = area.x + sheet->hadjustment->value;
2205 if ( sheet->column_titles_visible)
2206 y -= sheet->column_title_area.height;
2208 if ( sheet->row_titles_visible)
2209 x -= sheet->row_title_area.width;
2211 maximize_int (&x, 0);
2212 maximize_int (&y, 0);
2214 range.row0 = row_from_ypixel (sheet, y);
2215 range.rowi = row_from_ypixel (sheet, y + area.height);
2217 range.col0 = column_from_xpixel (sheet, x);
2218 range.coli = column_from_xpixel (sheet, x + area.width);
2220 g_return_if_fail (sheet != NULL);
2221 g_return_if_fail (PSPPIRE_SHEET (sheet));
2223 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2224 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2225 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2228 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2229 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2230 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2231 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2233 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2234 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2236 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2238 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2239 psppire_sheet_cell_draw (sheet, i, j);
2242 if (sheet->select_status == PSPPIRE_SHEET_NORMAL &&
2243 sheet->active_cell.row >= drawing_range.row0 &&
2244 sheet->active_cell.row <= drawing_range.rowi &&
2245 sheet->active_cell.col >= drawing_range.col0 &&
2246 sheet->active_cell.col <= drawing_range.coli)
2247 psppire_sheet_show_entry_widget (sheet);
2251 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2252 GtkJustification justification,
2255 PsppireSheetModel *model ;
2258 g_return_if_fail (sheet != NULL);
2259 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2261 if (col >= psppire_axis_unit_count (sheet->haxis)
2262 || row >= psppire_axis_unit_count (sheet->vaxis))
2265 if (col < 0 || row < 0) return;
2267 model = psppire_sheet_get_model (sheet);
2269 old_text = psppire_sheet_model_get_string (model, row, col);
2271 if (0 != g_strcmp0 (old_text, text))
2273 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2274 psppire_sheet_model_set_string (model, text, row, col);
2275 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2278 if ( psppire_sheet_model_free_strings (model))
2284 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2286 PsppireSheetRange range;
2288 g_return_if_fail (sheet != NULL);
2289 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2290 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2291 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2293 if (column < 0 || row < 0) return;
2297 range.col0 = min_visible_column (sheet);
2298 range.coli = max_visible_column (sheet);
2300 psppire_sheet_real_cell_clear (sheet, row, column);
2302 redraw_range (sheet, &range);
2306 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2308 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2310 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2312 if (old_text && strlen (old_text) > 0 )
2314 psppire_sheet_model_datum_clear (model, row, column);
2317 dispose_string (sheet, old_text);
2321 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2323 PsppireSheetModel *model;
2324 g_return_val_if_fail (sheet != NULL, NULL);
2325 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2327 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2329 if (col < 0 || row < 0) return NULL;
2331 model = psppire_sheet_get_model (sheet);
2336 return psppire_sheet_model_get_string (model, row, col);
2340 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2341 If the function returns FALSE, then the results will be unreliable.
2344 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2352 *column = -G_MAXINT;
2354 g_return_val_if_fail (sheet != NULL, 0);
2355 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2357 /* bounds checking, return false if the user clicked
2365 if ( sheet->column_titles_visible)
2366 y -= sheet->column_title_area.height;
2368 y += sheet->vadjustment->value;
2370 if ( y < 0 && sheet->column_titles_visible)
2376 trow = row_from_ypixel (sheet, y);
2377 if (trow > psppire_axis_unit_count (sheet->vaxis))
2383 if ( sheet->row_titles_visible)
2384 x -= sheet->row_title_area.width;
2386 x += sheet->hadjustment->value;
2388 if ( x < 0 && sheet->row_titles_visible)
2394 tcol = column_from_xpixel (sheet, x);
2395 if (tcol > psppire_axis_unit_count (sheet->haxis))
2405 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2410 g_return_val_if_fail (sheet != NULL, 0);
2411 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2413 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2416 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2417 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2419 area->width= (column == -1) ? sheet->row_title_area.width
2420 : psppire_axis_unit_size (sheet->haxis, column);
2422 area->height= (row == -1) ? sheet->column_title_area.height
2423 : psppire_axis_unit_size (sheet->vaxis, row);
2429 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2431 g_return_if_fail (sheet != NULL);
2432 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2434 if (row < -1 || col < -1)
2437 if (row >= psppire_axis_unit_count (sheet->vaxis)
2439 col >= psppire_axis_unit_count (sheet->haxis))
2442 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2445 if ( row == -1 || col == -1)
2447 psppire_sheet_hide_entry_widget (sheet);
2451 change_active_cell (sheet, row, col);
2455 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2457 g_return_if_fail (sheet != NULL);
2458 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2460 if ( row ) *row = sheet->active_cell.row;
2461 if (column) *column = sheet->active_cell.col;
2465 entry_load_text (PsppireSheet *sheet)
2469 GtkJustification justification;
2470 PsppireSheetCellAttr attributes;
2472 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2473 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2475 row = sheet->active_cell.row;
2476 col = sheet->active_cell.col;
2478 if (row < 0 || col < 0) return;
2480 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2482 if (text && strlen (text) > 0)
2484 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2485 justification = attributes.justification;
2486 psppire_sheet_set_cell (sheet, row, col, justification, text);
2492 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2494 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2497 if (sheet->active_cell.row < 0 ||
2498 sheet->active_cell.col < 0) return;
2500 gtk_widget_hide (sheet->entry_widget);
2501 gtk_widget_unmap (sheet->entry_widget);
2503 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2507 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2509 gint old_row, old_col;
2511 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2513 if (row < 0 || col < 0)
2516 if ( row > psppire_axis_unit_count (sheet->vaxis)
2517 || col > psppire_axis_unit_count (sheet->haxis))
2520 old_row = sheet->active_cell.row;
2521 old_col = sheet->active_cell.col;
2523 entry_load_text (sheet);
2525 /* Erase the old cell border */
2526 psppire_sheet_draw_active_cell (sheet);
2528 sheet->active_cell.row = row;
2529 sheet->active_cell.col = col;
2531 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2533 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2535 psppire_sheet_draw_active_cell (sheet);
2536 psppire_sheet_show_entry_widget (sheet);
2538 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2540 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2541 row, col, old_row, old_col);
2546 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2548 GtkEntry *sheet_entry;
2549 PsppireSheetCellAttr attributes;
2553 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2555 row = sheet->active_cell.row;
2556 col = sheet->active_cell.col;
2558 /* Don't show the active cell, if there is no active cell: */
2559 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2562 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2563 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2564 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2566 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2568 sheet_entry = psppire_sheet_get_entry (sheet);
2570 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2572 if (GTK_IS_ENTRY (sheet_entry))
2574 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2575 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2578 text = g_strdup ("");
2580 if (strcmp (old_text, text) != 0)
2581 gtk_entry_set_text (sheet_entry, text);
2583 dispose_string (sheet, text);
2586 switch (attributes.justification)
2588 case GTK_JUSTIFY_RIGHT:
2589 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2591 case GTK_JUSTIFY_CENTER:
2592 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2594 case GTK_JUSTIFY_LEFT:
2596 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2602 psppire_sheet_size_allocate_entry (sheet);
2604 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2605 psppire_sheet_model_is_editable (sheet->model,
2607 gtk_widget_map (sheet->entry_widget);
2611 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2614 PsppireSheetRange range;
2616 row = sheet->active_cell.row;
2617 col = sheet->active_cell.col;
2619 if (row < 0 || col < 0) return FALSE;
2621 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2624 range.col0 = range.coli = col;
2625 range.row0 = range.rowi = row;
2627 psppire_sheet_draw_border (sheet, range);
2635 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
2639 rectangle_from_range (sheet, &new_range, &area);
2644 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
2646 area.x += sheet->cell_padding->left / 2;
2647 area.y += sheet->cell_padding->top / 2;
2648 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
2649 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
2651 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
2658 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
2663 /* Selection related functions */
2666 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
2669 sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
2671 sheet->range.col0 = sheet->range.coli = -1;
2672 sheet->range.row0 = sheet->range.rowi = row;
2674 rectangle_from_range (sheet, &sheet->range, &area);
2678 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2680 g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, row);
2684 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
2687 sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
2689 sheet->range.col0 = sheet->range.coli = column;
2690 sheet->range.row0 = sheet->range.rowi = -1;
2692 rectangle_from_range (sheet, &sheet->range, &area);
2696 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2698 g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, column);
2703 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
2706 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
2708 sheet->range = *range;
2710 rectangle_from_range (sheet, range, &area);
2713 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2718 psppire_sheet_unselect_range (PsppireSheet *sheet)
2720 sheet->select_status = PSPPIRE_SHEET_NORMAL;
2722 if (sheet->sheet_window != NULL)
2726 rectangle_from_range (sheet, &sheet->range, &area);
2729 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2732 g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, -1);
2733 g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, -1);
2737 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
2739 g_return_if_fail (sheet != NULL);
2740 *range = sheet->range;
2745 psppire_sheet_expose (GtkWidget *widget, GdkEventExpose *event)
2747 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2749 g_return_val_if_fail (event != NULL, FALSE);
2751 if (!GTK_WIDGET_DRAWABLE (widget))
2754 /* exposure events on the sheet */
2755 if (event->window == sheet->row_title_window &&
2756 sheet->row_titles_visible)
2758 draw_row_title_buttons_range (sheet,
2759 min_visible_row (sheet),
2760 max_visible_row (sheet));
2763 if (event->window == sheet->column_title_window &&
2764 sheet->column_titles_visible)
2766 draw_column_title_buttons_range (sheet,
2767 min_visible_column (sheet),
2768 max_visible_column (sheet));
2771 if (event->window == sheet->sheet_window)
2773 draw_sheet_region (sheet, event->region);
2775 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2779 rectangle_from_range (sheet, &sheet->range, &area);
2781 gdk_draw_rectangle (sheet->sheet_window,
2784 area.x + 1, area.y + 1,
2785 area.width, area.height);
2789 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
2792 PsppireSheetRange range;
2793 range.row0 = range.rowi = sheet->active_cell.row;
2794 range.col0 = range.coli = sheet->active_cell.col;
2796 rectangle_from_range (sheet, &range, &rect);
2798 if (GDK_OVERLAP_RECTANGLE_OUT !=
2799 gdk_region_rect_in (event->region, &rect))
2801 psppire_sheet_draw_active_cell (sheet);
2807 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2814 psppire_sheet_button_press (GtkWidget *widget, GdkEventButton *event)
2816 PsppireSheet *sheet;
2817 GdkModifierType mods;
2822 g_return_val_if_fail (widget != NULL, FALSE);
2823 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
2824 g_return_val_if_fail (event != NULL, FALSE);
2826 sheet = PSPPIRE_SHEET (widget);
2828 /* Cancel any pending tooltips */
2829 if (sheet->motion_timer)
2831 g_source_remove (sheet->motion_timer);
2832 sheet->motion_timer = 0;
2835 gtk_widget_get_pointer (widget, &x, &y);
2836 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2839 if (event->window == sheet->column_title_window)
2841 sheet->x_drag = event->x;
2842 g_signal_emit (sheet,
2843 sheet_signals[BUTTON_EVENT_COLUMN], 0,
2846 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2848 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2849 g_signal_emit (sheet,
2850 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
2854 if (event->window == sheet->row_title_window)
2856 g_signal_emit (sheet,
2857 sheet_signals[BUTTON_EVENT_ROW], 0,
2860 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2862 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2863 g_signal_emit (sheet,
2864 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
2868 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
2870 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
2873 /* press on resize windows */
2874 if (event->window == sheet->column_title_window)
2876 sheet->x_drag = event->x;
2878 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
2880 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
2881 gdk_pointer_grab (sheet->column_title_window, FALSE,
2882 GDK_POINTER_MOTION_HINT_MASK |
2883 GDK_BUTTON1_MOTION_MASK |
2884 GDK_BUTTON_RELEASE_MASK,
2885 NULL, NULL, event->time);
2887 draw_xor_vline (sheet);
2892 if (event->window == sheet->row_title_window)
2894 sheet->y_drag = event->y;
2896 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
2898 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
2899 gdk_pointer_grab (sheet->row_title_window, FALSE,
2900 GDK_POINTER_MOTION_HINT_MASK |
2901 GDK_BUTTON1_MOTION_MASK |
2902 GDK_BUTTON_RELEASE_MASK,
2903 NULL, NULL, event->time);
2905 draw_xor_hline (sheet);
2910 /* the sheet itself does not handle other than single click events */
2911 if (event->type != GDK_BUTTON_PRESS) return FALSE;
2913 /* selections on the sheet */
2914 if (event->window == sheet->sheet_window)
2916 gtk_widget_get_pointer (widget, &x, &y);
2917 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2918 gdk_pointer_grab (sheet->sheet_window, FALSE,
2919 GDK_POINTER_MOTION_HINT_MASK |
2920 GDK_BUTTON1_MOTION_MASK |
2921 GDK_BUTTON_RELEASE_MASK,
2922 NULL, NULL, event->time);
2923 gtk_grab_add (GTK_WIDGET (sheet));
2925 if ( sheet->select_status == PSPPIRE_SHEET_NORMAL)
2927 sheet->range.row0 = row;
2928 sheet->range.col0 = column;
2932 psppire_sheet_unselect_range (sheet);
2934 psppire_sheet_click_cell (sheet, row, column);
2937 if (event->window == sheet->column_title_window)
2939 gtk_widget_get_pointer (widget, &x, &y);
2940 if ( sheet->row_titles_visible)
2941 x -= sheet->row_title_area.width;
2943 x += sheet->hadjustment->value;
2945 column = column_from_xpixel (sheet, x);
2947 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2949 gtk_grab_add (GTK_WIDGET (sheet));
2950 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2954 if (event->window == sheet->row_title_window)
2956 gtk_widget_get_pointer (widget, &x, &y);
2957 if ( sheet->column_titles_visible)
2958 y -= sheet->column_title_area.height;
2960 y += sheet->vadjustment->value;
2962 row = row_from_ypixel (sheet, y);
2963 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2965 gtk_grab_add (GTK_WIDGET (sheet));
2966 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2974 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
2976 PsppireSheetCell cell;
2977 gboolean forbid_move;
2982 if (row >= psppire_axis_unit_count (sheet->vaxis)
2983 || column >= psppire_axis_unit_count (sheet->haxis))
2988 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
2989 &sheet->active_cell,
2995 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
2998 row = sheet->active_cell.row;
2999 column = sheet->active_cell.col;
3001 change_active_cell (sheet, row, column);
3005 if (row == -1 && column >= 0)
3007 psppire_sheet_select_column (sheet, column);
3011 if (column == -1 && row >= 0)
3013 psppire_sheet_select_row (sheet, row);
3017 if (row == -1 && column == -1)
3019 sheet->range.row0 = 0;
3020 sheet->range.col0 = 0;
3021 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3022 sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
3026 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3027 change_active_cell (sheet, row, column);
3029 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3035 psppire_sheet_button_release (GtkWidget *widget,
3036 GdkEventButton *event)
3038 GdkDisplay *display = gtk_widget_get_display (widget);
3040 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3042 /* release on resize windows */
3043 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3046 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3047 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3049 gdk_display_pointer_ungrab (display, event->time);
3050 draw_xor_vline (sheet);
3053 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3054 + sheet->hadjustment->value;
3056 set_column_width (sheet, sheet->drag_cell.col, width);
3061 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3064 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3065 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3067 gdk_display_pointer_ungrab (display, event->time);
3068 draw_xor_hline (sheet);
3071 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3072 sheet->vadjustment->value;
3074 set_row_height (sheet, sheet->drag_cell.row, height);
3079 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3081 PsppireSheetRange old_range;
3082 draw_xor_rectangle (sheet, sheet->drag_range);
3083 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3084 gdk_display_pointer_ungrab (display, event->time);
3086 psppire_sheet_unselect_range (sheet);
3088 old_range = sheet->range;
3089 sheet->range = sheet->drag_range;
3090 sheet->drag_range = old_range;
3091 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3092 &sheet->drag_range, &sheet->range);
3093 psppire_sheet_select_range (sheet, &sheet->range);
3096 if (PSPPIRE_SHEET_IN_SELECTION (sheet))
3098 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3099 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3101 change_active_cell (sheet, sheet->active_cell.row,
3102 sheet->active_cell.col);
3105 gdk_display_pointer_ungrab (display, event->time);
3106 gtk_grab_remove (GTK_WIDGET (sheet));
3108 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3117 /* Shamelessly lifted from gtktooltips */
3119 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3123 gtk_widget_size_request (tip_window, &req);
3124 gtk_paint_flat_box (tip_window->style, tip_window->window,
3125 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3126 NULL, GTK_WIDGET(tip_window), "tooltip",
3127 0, 0, req.width, req.height);
3133 destroy_hover_window (PsppireSheetHoverTitle *h)
3135 gtk_widget_destroy (h->window);
3139 static PsppireSheetHoverTitle *
3140 create_hover_window (void)
3142 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3144 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3146 #if GTK_CHECK_VERSION (2, 9, 0)
3147 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3148 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3151 gtk_widget_set_app_paintable (hw->window, TRUE);
3152 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3153 gtk_widget_set_name (hw->window, "gtk-tooltips");
3154 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3156 g_signal_connect (hw->window,
3158 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3161 hw->label = gtk_label_new (NULL);
3164 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3165 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3167 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3169 gtk_widget_show (hw->label);
3171 g_signal_connect (hw->window,
3173 G_CALLBACK (gtk_widget_destroyed),
3179 #define HOVER_WINDOW_Y_OFFSET 2
3182 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3183 const gchar *subtitle)
3192 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3196 sheet->hover_window->row = row;
3197 sheet->hover_window->column = column;
3199 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3201 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3203 gtk_widget_show (sheet->hover_window->window);
3205 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3211 y += sheet->column_title_area.y;
3212 y += sheet->column_title_area.height;
3213 y += HOVER_WINDOW_Y_OFFSET;
3219 x += sheet->row_title_area.x;
3220 x += sheet->row_title_area.width * 2 / 3.0;
3223 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3228 motion_timeout_callback (gpointer data)
3230 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3234 gdk_threads_enter ();
3235 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3237 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3239 if (sheet->row_title_under && row >= 0)
3241 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3243 show_subtitle (sheet, row, -1, text);
3247 if (sheet->column_title_under && column >= 0)
3249 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3252 show_subtitle (sheet, -1, column, text);
3258 gdk_threads_leave ();
3263 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3265 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3266 GdkModifierType mods;
3267 GdkCursorType new_cursor;
3270 GdkDisplay *display;
3272 g_return_val_if_fail (event != NULL, FALSE);
3274 display = gtk_widget_get_display (widget);
3276 /* selections on the sheet */
3280 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3282 if ( sheet->motion_timer > 0 )
3283 g_source_remove (sheet->motion_timer);
3284 sheet->motion_timer =
3285 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3291 gtk_widget_get_pointer (widget, &wx, &wy);
3293 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3295 if ( row != sheet->hover_window->row ||
3296 column != sheet->hover_window->column)
3298 gtk_widget_hide (sheet->hover_window->window);
3303 if (event->window == sheet->column_title_window)
3305 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3306 on_column_boundary (sheet, x, &column))
3308 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3309 if (new_cursor != sheet->cursor_drag->type)
3311 gdk_cursor_unref (sheet->cursor_drag);
3312 sheet->cursor_drag =
3313 gdk_cursor_new_for_display (display, new_cursor);
3315 gdk_window_set_cursor (sheet->column_title_window,
3316 sheet->cursor_drag);
3321 new_cursor = GDK_TOP_LEFT_ARROW;
3322 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3323 new_cursor != sheet->cursor_drag->type)
3325 gdk_cursor_unref (sheet->cursor_drag);
3326 sheet->cursor_drag =
3327 gdk_cursor_new_for_display (display, new_cursor);
3328 gdk_window_set_cursor (sheet->column_title_window,
3329 sheet->cursor_drag);
3333 else if (event->window == sheet->row_title_window)
3335 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3336 on_row_boundary (sheet, y, &row))
3338 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3339 if (new_cursor != sheet->cursor_drag->type)
3341 gdk_cursor_unref (sheet->cursor_drag);
3342 sheet->cursor_drag =
3343 gdk_cursor_new_for_display (display, new_cursor);
3344 gdk_window_set_cursor (sheet->row_title_window,
3345 sheet->cursor_drag);
3350 new_cursor = GDK_TOP_LEFT_ARROW;
3351 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3352 new_cursor != sheet->cursor_drag->type)
3354 gdk_cursor_unref (sheet->cursor_drag);
3355 sheet->cursor_drag =
3356 gdk_cursor_new_for_display (display, new_cursor);
3357 gdk_window_set_cursor (sheet->row_title_window,
3358 sheet->cursor_drag);
3363 new_cursor = GDK_PLUS;
3364 if ( event->window == sheet->sheet_window &&
3365 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3366 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3367 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3368 new_cursor != sheet->cursor_drag->type)
3370 gdk_cursor_unref (sheet->cursor_drag);
3371 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3372 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3375 new_cursor = GDK_TOP_LEFT_ARROW;
3376 if ( event->window == sheet->sheet_window &&
3377 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ) &&
3378 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3379 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3380 new_cursor != sheet->cursor_drag->type)
3382 gdk_cursor_unref (sheet->cursor_drag);
3383 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3384 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3387 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3388 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3390 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3392 if (event->x != sheet->x_drag)
3394 draw_xor_vline (sheet);
3395 sheet->x_drag = event->x;
3396 draw_xor_vline (sheet);
3402 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3404 if (event->y != sheet->y_drag)
3406 draw_xor_hline (sheet);
3407 sheet->y_drag = event->y;
3408 draw_xor_hline (sheet);
3414 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3416 PsppireSheetRange aux;
3417 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3418 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3419 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3420 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3424 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3425 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3427 aux = sheet->drag_range;
3428 sheet->drag_range.row0 = sheet->range.row0 + row;
3429 sheet->drag_range.col0 = sheet->range.col0 + column;
3430 sheet->drag_range.rowi = sheet->range.rowi + row;
3431 sheet->drag_range.coli = sheet->range.coli + column;
3432 if (aux.row0 != sheet->drag_range.row0 ||
3433 aux.col0 != sheet->drag_range.col0)
3435 draw_xor_rectangle (sheet, aux);
3436 draw_xor_rectangle (sheet, sheet->drag_range);
3442 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3444 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
3445 column == sheet->active_cell.col) return TRUE;
3447 if ( mods & GDK_BUTTON1_MASK)
3449 if (PSPPIRE_SHEET_IN_SELECTION (sheet) )
3451 /* Redraw the old range */
3452 psppire_sheet_unselect_range (sheet);
3454 sheet->range.rowi = row;
3455 sheet->range.coli = column;
3457 /* Redraw the new range */
3458 psppire_sheet_select_range (sheet, &sheet->range);
3462 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3470 psppire_sheet_crossing_notify (GtkWidget *widget,
3471 GdkEventCrossing *event)
3473 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3475 if (event->window == sheet->column_title_window)
3476 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
3477 else if (event->window == sheet->row_title_window)
3478 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
3480 if (event->type == GDK_LEAVE_NOTIFY)
3481 gtk_widget_hide (sheet->hover_window->window);
3488 psppire_sheet_focus_in (GtkWidget *w,
3489 GdkEventFocus *event)
3491 PsppireSheet *sheet = PSPPIRE_SHEET (w);
3493 gtk_widget_grab_focus (sheet->entry_widget);
3501 psppire_sheet_entry_key_press (GtkWidget *widget,
3505 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
3510 /* Number of rows in a step-increment */
3511 #define ROWS_PER_STEP 1
3515 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
3517 gint old_row = sheet->active_cell.row ;
3518 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
3522 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
3523 min_visible_row (sheet));
3527 case GTK_SCROLL_PAGE_DOWN:
3528 gtk_adjustment_set_value (sheet->vadjustment,
3529 sheet->vadjustment->value +
3530 sheet->vadjustment->page_increment);
3532 case GTK_SCROLL_PAGE_UP:
3533 gtk_adjustment_set_value (sheet->vadjustment,
3534 sheet->vadjustment->value -
3535 sheet->vadjustment->page_increment);
3539 g_assert_not_reached ();
3544 vpixel += psppire_axis_start_pixel (sheet->vaxis,
3545 min_visible_row (sheet));
3547 new_row = row_from_ypixel (sheet, vpixel);
3549 change_active_cell (sheet, new_row,
3550 sheet->active_cell.col);
3555 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
3557 gint current_row = sheet->active_cell.row;
3558 gint current_col = sheet->active_cell.col;
3559 PsppireSheetCell new_cell ;
3560 gboolean forbidden = FALSE;
3562 new_cell.row = current_row;
3563 new_cell.col = current_col;
3567 case GTK_SCROLL_STEP_DOWN:
3570 case GTK_SCROLL_STEP_UP:
3573 case GTK_SCROLL_STEP_RIGHT:
3576 case GTK_SCROLL_STEP_LEFT:
3579 case GTK_SCROLL_STEP_FORWARD:
3582 psppire_sheet_model_get_column_count (sheet->model))
3588 case GTK_SCROLL_STEP_BACKWARD:
3590 if (new_cell.col < 0)
3593 psppire_sheet_model_get_column_count (sheet->model) - 1;
3598 g_assert_not_reached ();
3602 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3603 &sheet->active_cell,
3611 maximize_int (&new_cell.row, 0);
3612 maximize_int (&new_cell.col, 0);
3614 minimize_int (&new_cell.row,
3615 psppire_axis_unit_count (sheet->vaxis) - 1);
3617 minimize_int (&new_cell.col,
3618 psppire_axis_unit_count (sheet->haxis) - 1);
3620 change_active_cell (sheet, new_cell.row, new_cell.col);
3623 if ( new_cell.col > max_fully_visible_column (sheet))
3626 psppire_axis_start_pixel (sheet->haxis,
3628 hpos -= sheet->hadjustment->page_size;
3630 gtk_adjustment_set_value (sheet->hadjustment,
3633 else if ( new_cell.col < min_fully_visible_column (sheet))
3636 psppire_axis_start_pixel (sheet->haxis,
3639 gtk_adjustment_set_value (sheet->hadjustment,
3644 if ( new_cell.row > max_fully_visible_row (sheet))
3647 psppire_axis_start_pixel (sheet->vaxis,
3649 vpos -= sheet->vadjustment->page_size;
3651 gtk_adjustment_set_value (sheet->vadjustment,
3654 else if ( new_cell.row < min_fully_visible_row (sheet))
3657 psppire_axis_start_pixel (sheet->vaxis,
3660 gtk_adjustment_set_value (sheet->vadjustment,
3664 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3668 /* Move to row 0 keeping the current column */
3670 row0 (PsppireSheet *sheet)
3672 gtk_adjustment_set_value (sheet->vadjustment,
3673 sheet->vadjustment->lower);
3675 change_active_cell (sheet, 0, sheet->active_cell.col);
3678 /* Move to column 0 keeping the current row */
3680 col0 (PsppireSheet *sheet)
3682 gtk_adjustment_set_value (sheet->hadjustment,
3683 sheet->hadjustment->lower);
3685 change_active_cell (sheet, sheet->active_cell.row, 0);
3688 /* Move to last column keeping the current row */
3690 col_last (PsppireSheet *sheet)
3692 glong last_col = psppire_axis_unit_count (sheet->haxis) - 1;
3694 gtk_adjustment_set_value (sheet->hadjustment,
3695 sheet->hadjustment->upper - sheet->hadjustment->page_size);
3697 change_active_cell (sheet, sheet->active_cell.row,
3702 /* Move to last row keeping the current column */
3704 row_last (PsppireSheet *sheet)
3706 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
3708 gtk_adjustment_set_value (sheet->vadjustment,
3709 sheet->vadjustment->upper- sheet->vadjustment->page_size);
3711 change_active_cell (sheet,
3713 sheet->active_cell.col);
3719 psppire_sheet_key_press (GtkWidget *widget,
3722 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3724 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3726 switch (key->keyval)
3729 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
3733 if (key->state & GDK_CONTROL_MASK)
3736 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
3739 case GDK_ISO_Left_Tab:
3740 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
3744 if (key->state & GDK_CONTROL_MASK)
3747 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
3751 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3755 if (key->state & GDK_CONTROL_MASK)
3758 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3762 if (key->state & GDK_CONTROL_MASK)
3765 step_sheet (sheet, GTK_SCROLL_STEP_UP);
3769 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
3772 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
3784 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
3796 psppire_sheet_size_request (GtkWidget *widget,
3797 GtkRequisition *requisition)
3799 PsppireSheet *sheet;
3801 g_return_if_fail (widget != NULL);
3802 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3803 g_return_if_fail (requisition != NULL);
3805 sheet = PSPPIRE_SHEET (widget);
3807 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
3808 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
3810 /* compute the size of the column title area */
3811 if (sheet->column_titles_visible)
3812 requisition->height += sheet->column_title_area.height;
3814 /* compute the size of the row title area */
3815 if (sheet->row_titles_visible)
3816 requisition->width += sheet->row_title_area.width;
3821 psppire_sheet_size_allocate (GtkWidget *widget,
3822 GtkAllocation *allocation)
3824 PsppireSheet *sheet;
3825 GtkAllocation sheet_allocation;
3828 g_return_if_fail (widget != NULL);
3829 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3830 g_return_if_fail (allocation != NULL);
3832 sheet = PSPPIRE_SHEET (widget);
3833 widget->allocation = *allocation;
3834 border_width = GTK_CONTAINER (widget)->border_width;
3836 if (GTK_WIDGET_REALIZED (widget))
3837 gdk_window_move_resize (widget->window,
3838 allocation->x + border_width,
3839 allocation->y + border_width,
3840 allocation->width - 2 * border_width,
3841 allocation->height - 2 * border_width);
3843 sheet_allocation.x = 0;
3844 sheet_allocation.y = 0;
3845 sheet_allocation.width = allocation->width - 2 * border_width;
3846 sheet_allocation.height = allocation->height - 2 * border_width;
3848 if (GTK_WIDGET_REALIZED (widget))
3849 gdk_window_move_resize (sheet->sheet_window,
3852 sheet_allocation.width,
3853 sheet_allocation.height);
3855 /* position the window which holds the column title buttons */
3856 sheet->column_title_area.x = 0;
3857 sheet->column_title_area.y = 0;
3858 sheet->column_title_area.width = sheet_allocation.width ;
3861 /* position the window which holds the row title buttons */
3862 sheet->row_title_area.x = 0;
3863 sheet->row_title_area.y = 0;
3864 sheet->row_title_area.height = sheet_allocation.height;
3866 if (sheet->row_titles_visible)
3867 sheet->column_title_area.x += sheet->row_title_area.width;
3869 if (sheet->column_titles_visible)
3870 sheet->row_title_area.y += sheet->column_title_area.height;
3872 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
3873 gdk_window_move_resize (sheet->column_title_window,
3874 sheet->column_title_area.x,
3875 sheet->column_title_area.y,
3876 sheet->column_title_area.width,
3877 sheet->column_title_area.height);
3880 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
3881 gdk_window_move_resize (sheet->row_title_window,
3882 sheet->row_title_area.x,
3883 sheet->row_title_area.y,
3884 sheet->row_title_area.width,
3885 sheet->row_title_area.height);
3887 size_allocate_global_button (sheet);
3891 gint width = sheet->column_title_area.width;
3893 if ( sheet->row_titles_visible)
3894 width -= sheet->row_title_area.width;
3896 g_object_set (sheet->haxis,
3897 "minimum-extent", width,
3904 gint height = sheet->row_title_area.height;
3906 if ( sheet->column_titles_visible)
3907 height -= sheet->column_title_area.height;
3909 g_object_set (sheet->vaxis,
3910 "minimum-extent", height,
3915 /* set the scrollbars adjustments */
3916 adjust_scrollbars (sheet);
3920 draw_column_title_buttons (PsppireSheet *sheet)
3924 if (!sheet->column_titles_visible) return;
3925 if (!GTK_WIDGET_REALIZED (sheet))
3928 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
3931 if (sheet->row_titles_visible)
3933 x = sheet->row_title_area.width;
3936 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
3938 sheet->column_title_area.width = width;
3939 sheet->column_title_area.x = x;
3940 gdk_window_move_resize (sheet->column_title_window,
3941 sheet->column_title_area.x,
3942 sheet->column_title_area.y,
3943 sheet->column_title_area.width,
3944 sheet->column_title_area.height);
3947 if (max_visible_column (sheet) ==
3948 psppire_axis_unit_count (sheet->haxis) - 1)
3949 gdk_window_clear_area (sheet->column_title_window,
3951 sheet->column_title_area.width,
3952 sheet->column_title_area.height);
3954 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
3956 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
3957 max_visible_column (sheet));
3961 draw_row_title_buttons (PsppireSheet *sheet)
3966 if (!sheet->row_titles_visible) return;
3967 if (!GTK_WIDGET_REALIZED (sheet))
3970 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
3972 if (sheet->column_titles_visible)
3974 y = sheet->column_title_area.height;
3977 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
3979 sheet->row_title_area.y = y;
3980 sheet->row_title_area.height = height;
3981 gdk_window_move_resize (sheet->row_title_window,
3982 sheet->row_title_area.x,
3983 sheet->row_title_area.y,
3984 sheet->row_title_area.width,
3985 sheet->row_title_area.height);
3988 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
3989 gdk_window_clear_area (sheet->row_title_window,
3991 sheet->row_title_area.width,
3992 sheet->row_title_area.height);
3994 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
3996 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
3997 max_visible_row (sheet));
4002 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4004 GtkAllocation entry_alloc;
4005 PsppireSheetCellAttr attributes = { 0 };
4006 GtkEntry *sheet_entry;
4008 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4009 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4011 sheet_entry = psppire_sheet_get_entry (sheet);
4013 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4014 sheet->active_cell.col,
4018 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4020 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4022 style->bg[GTK_STATE_NORMAL] = attributes.background;
4023 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4024 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4025 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4026 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4027 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4030 rectangle_from_cell (sheet, sheet->active_cell.row,
4031 sheet->active_cell.col, &entry_alloc);
4033 entry_alloc.x += sheet->cell_padding->left;
4034 entry_alloc.y += sheet->cell_padding->right;
4035 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4036 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4039 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4040 entry_alloc.height);
4041 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4045 /* Copy the sheet's font to the entry widget */
4047 set_entry_widget_font (PsppireSheet *sheet)
4049 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4051 pango_font_description_free (style->font_desc);
4052 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4054 gtk_widget_modify_style (sheet->entry_widget, style);
4058 create_sheet_entry (PsppireSheet *sheet)
4060 if (sheet->entry_widget)
4062 gtk_widget_unparent (sheet->entry_widget);
4065 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4066 g_object_ref_sink (sheet->entry_widget);
4068 gtk_widget_size_request (sheet->entry_widget, NULL);
4070 if ( GTK_IS_ENTRY (sheet->entry_widget))
4072 g_object_set (sheet->entry_widget,
4077 if (GTK_WIDGET_REALIZED (sheet))
4079 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4080 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4081 gtk_widget_realize (sheet->entry_widget);
4084 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4085 G_CALLBACK (psppire_sheet_entry_key_press),
4088 set_entry_widget_font (sheet);
4090 gtk_widget_show (sheet->entry_widget);
4094 /* Finds the last child widget that happens to be of type GtkEntry */
4096 find_entry (GtkWidget *w, gpointer user_data)
4098 GtkWidget **entry = user_data;
4099 if ( GTK_IS_ENTRY (w))
4107 psppire_sheet_get_entry (PsppireSheet *sheet)
4109 GtkWidget *w = sheet->entry_widget;
4111 g_return_val_if_fail (sheet != NULL, NULL);
4112 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4113 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4115 while (! GTK_IS_ENTRY (w))
4117 GtkWidget *entry = NULL;
4119 if (GTK_IS_CONTAINER (w))
4121 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4130 return GTK_ENTRY (w);
4135 draw_button (PsppireSheet *sheet, GdkWindow *window,
4136 PsppireSheetButton *button, gboolean is_sensitive,
4137 GdkRectangle allocation)
4139 GtkShadowType shadow_type;
4140 gint text_width = 0;
4141 PangoAlignment align = PANGO_ALIGN_LEFT;
4147 g_return_if_fail (sheet != NULL);
4148 g_return_if_fail (button != NULL);
4151 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4153 gdk_window_clear_area (window,
4154 allocation.x, allocation.y,
4155 allocation.width, allocation.height);
4157 gtk_widget_ensure_style (sheet->button);
4159 gtk_paint_box (sheet->button->style, window,
4160 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4162 GTK_WIDGET (sheet->button),
4164 allocation.x, allocation.y,
4165 allocation.width, allocation.height);
4167 state = button->state;
4168 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4170 if (state == GTK_STATE_ACTIVE)
4171 shadow_type = GTK_SHADOW_IN;
4173 shadow_type = GTK_SHADOW_OUT;
4175 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4176 gtk_paint_box (sheet->button->style, window,
4177 button->state, shadow_type,
4178 &allocation, GTK_WIDGET (sheet->button),
4180 allocation.x, allocation.y,
4181 allocation.width, allocation.height);
4183 if ( button->overstruck)
4185 GdkPoint points[2] = {
4186 {allocation.x, allocation.y},
4187 {allocation.x + allocation.width,
4188 allocation.y + allocation.height}
4191 gtk_paint_polygon (sheet->button->style,
4203 if (button->label_visible)
4205 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4207 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4210 allocation.y += 2 * sheet->button->style->ythickness;
4212 if (button->label && strlen (button->label) > 0)
4214 PangoRectangle rect;
4215 gchar *line = button->label;
4217 PangoLayout *layout = NULL;
4218 gint real_x = allocation.x;
4219 gint real_y = allocation.y;
4221 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4222 pango_layout_get_extents (layout, NULL, &rect);
4224 text_width = PANGO_PIXELS (rect.width);
4225 switch (button->justification)
4227 case GTK_JUSTIFY_LEFT:
4228 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4229 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4231 case GTK_JUSTIFY_RIGHT:
4232 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4233 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4235 case GTK_JUSTIFY_CENTER:
4237 real_x = allocation.x + (allocation.width - text_width)/2;
4238 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4239 pango_layout_set_justify (layout, TRUE);
4241 pango_layout_set_alignment (layout, align);
4242 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4251 g_object_unref (layout);
4254 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4256 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4260 psppire_sheet_button_free (button);
4264 /* Draw the column title buttons FIRST through to LAST */
4266 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4270 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4272 if (!sheet->column_titles_visible) return;
4274 g_return_if_fail (first >= min_visible_column (sheet));
4275 g_return_if_fail (last <= max_visible_column (sheet));
4278 rect.height = sheet->column_title_area.height;
4279 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4280 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4281 + psppire_axis_unit_size (sheet->haxis, last);
4283 rect.x -= sheet->hadjustment->value;
4285 minimize_int (&rect.width, sheet->column_title_area.width);
4286 maximize_int (&rect.x, 0);
4288 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4290 for (col = first ; col <= last ; ++col)
4292 GdkRectangle allocation;
4293 gboolean is_sensitive = FALSE;
4295 PsppireSheetButton *
4296 button = psppire_sheet_model_get_column_button (sheet->model, col);
4298 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4300 allocation.x -= sheet->hadjustment->value;
4302 allocation.height = sheet->column_title_area.height;
4303 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4304 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4306 draw_button (sheet, sheet->column_title_window,
4307 button, is_sensitive, allocation);
4310 gdk_window_end_paint (sheet->column_title_window);
4315 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4319 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4321 if (!sheet->row_titles_visible) return;
4323 g_return_if_fail (first >= min_visible_row (sheet));
4324 g_return_if_fail (last <= max_visible_row (sheet));
4327 rect.width = sheet->row_title_area.width;
4328 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4329 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4330 + psppire_axis_unit_size (sheet->vaxis, last);
4332 rect.y -= sheet->vadjustment->value;
4334 minimize_int (&rect.height, sheet->row_title_area.height);
4335 maximize_int (&rect.y, 0);
4337 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4338 for (row = first; row <= last; ++row)
4340 GdkRectangle allocation;
4342 gboolean is_sensitive = FALSE;
4344 PsppireSheetButton *button =
4345 psppire_sheet_model_get_row_button (sheet->model, row);
4347 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4349 allocation.y -= sheet->vadjustment->value;
4351 allocation.width = sheet->row_title_area.width;
4352 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4353 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4355 draw_button (sheet, sheet->row_title_window,
4356 button, is_sensitive, allocation);
4359 gdk_window_end_paint (sheet->row_title_window);
4366 * vadjustment_value_changed
4367 * hadjustment_value_changed */
4371 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4374 (adj->value + adj->page_size)
4376 (adj->upper - adj->lower);
4378 const glong last_item = psppire_axis_unit_count (axis) - 1;
4380 if (isnan (position) || position < 0)
4384 psppire_axis_start_pixel (axis, last_item)
4386 psppire_axis_unit_size (axis, last_item)
4390 adj->page_size = page_size;
4393 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4395 if ( adj->value < adj->lower)
4396 adj->value = adj->lower;
4399 gtk_adjustment_changed (adj);
4404 adjust_scrollbars (PsppireSheet *sheet)
4408 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4411 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4413 if ( sheet->row_titles_visible)
4414 width -= sheet->row_title_area.width;
4416 if (sheet->column_titles_visible)
4417 height -= sheet->column_title_area.height;
4419 if (sheet->vadjustment)
4421 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4423 sheet->vadjustment->step_increment =
4425 psppire_axis_unit_size (sheet->vaxis, last_row);
4427 sheet->vadjustment->page_increment =
4429 sheet->column_title_area.height -
4430 psppire_axis_unit_size (sheet->vaxis, last_row);
4432 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4435 if (sheet->hadjustment)
4437 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4438 sheet->hadjustment->step_increment = 1;
4440 sheet->hadjustment->page_increment = width;
4442 sheet->hadjustment->upper =
4443 psppire_axis_start_pixel (sheet->haxis, last_col)
4445 psppire_axis_unit_size (sheet->haxis, last_col)
4448 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4452 /* Subtracts the region of WIDGET from REGION */
4454 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4457 GdkRectangle intersect;
4460 gdk_region_get_clipbox (region, &rect);
4461 gtk_widget_intersect (widget,
4465 region2 = gdk_region_rectangle (&intersect);
4466 gdk_region_subtract (region, region2);
4467 gdk_region_destroy (region2);
4471 vadjustment_value_changed (GtkAdjustment *adjustment,
4475 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4477 g_return_if_fail (adjustment != NULL);
4479 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4481 gtk_widget_hide (sheet->entry_widget);
4484 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4486 subtract_widget_region (region, sheet->button);
4487 gdk_window_begin_paint_region (sheet->sheet_window, region);
4489 draw_sheet_region (sheet, region);
4491 draw_row_title_buttons (sheet);
4492 psppire_sheet_draw_active_cell (sheet);
4494 gdk_window_end_paint (sheet->sheet_window);
4495 gdk_region_destroy (region);
4500 hadjustment_value_changed (GtkAdjustment *adjustment,
4504 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4506 g_return_if_fail (adjustment != NULL);
4508 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4510 gtk_widget_hide (sheet->entry_widget);
4514 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4516 subtract_widget_region (region, sheet->button);
4517 gdk_window_begin_paint_region (sheet->sheet_window, region);
4519 draw_sheet_region (sheet, region);
4521 draw_column_title_buttons (sheet);
4523 psppire_sheet_draw_active_cell (sheet);
4525 gdk_window_end_paint (sheet->sheet_window);
4527 gdk_region_destroy (region);
4531 /* COLUMN RESIZING */
4533 draw_xor_vline (PsppireSheet *sheet)
4536 gint xpos = sheet->x_drag;
4537 gdk_drawable_get_size (sheet->sheet_window,
4540 if (sheet->row_titles_visible)
4541 xpos += sheet->row_title_area.width;
4543 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4545 sheet->column_title_area.height,
4547 height + CELL_SPACING);
4552 draw_xor_hline (PsppireSheet *sheet)
4556 gint ypos = sheet->y_drag;
4558 gdk_drawable_get_size (sheet->sheet_window,
4562 if (sheet->column_titles_visible)
4563 ypos += sheet->column_title_area.height;
4565 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4566 sheet->row_title_area.width,
4568 width + CELL_SPACING,
4572 /* SELECTED RANGE */
4574 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
4577 GdkRectangle clip_area, area;
4580 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
4581 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
4582 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
4583 psppire_axis_unit_size (sheet->haxis, range.coli);
4584 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
4585 psppire_axis_unit_size (sheet->vaxis, range.rowi);
4587 clip_area.x = sheet->row_title_area.width;
4588 clip_area.y = sheet->column_title_area.height;
4590 gdk_drawable_get_size (sheet->sheet_window,
4591 &clip_area.width, &clip_area.height);
4593 if (!sheet->row_titles_visible) clip_area.x = 0;
4594 if (!sheet->column_titles_visible) clip_area.y = 0;
4598 area.width = area.width + area.x;
4601 if (area.width > clip_area.width) area.width = clip_area.width + 10;
4604 area.height = area.height + area.y;
4607 if (area.height > clip_area.height) area.height = clip_area.height + 10;
4611 clip_area.width += 3;
4612 clip_area.height += 3;
4614 gdk_gc_get_values (sheet->xor_gc, &values);
4616 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
4618 gdk_draw_rectangle (sheet->sheet_window,
4621 area.x + i, area.y + i,
4622 area.width - 2 * i, area.height - 2 * i);
4625 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4627 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
4632 set_column_width (PsppireSheet *sheet,
4636 g_return_if_fail (sheet != NULL);
4637 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4639 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
4645 psppire_axis_resize (sheet->haxis, column,
4646 width - sheet->cell_padding->left -
4647 sheet->cell_padding->right);
4649 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4651 draw_column_title_buttons (sheet);
4652 adjust_scrollbars (sheet);
4653 psppire_sheet_size_allocate_entry (sheet);
4654 redraw_range (sheet, NULL);
4659 set_row_height (PsppireSheet *sheet,
4663 g_return_if_fail (sheet != NULL);
4664 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4666 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
4672 psppire_axis_resize (sheet->vaxis, row,
4673 height - sheet->cell_padding->top -
4674 sheet->cell_padding->bottom);
4676 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
4678 draw_row_title_buttons (sheet);
4679 adjust_scrollbars (sheet);
4680 psppire_sheet_size_allocate_entry (sheet);
4681 redraw_range (sheet, NULL);
4686 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
4687 PsppireSheetCellAttr *attr)
4690 const GtkJustification *j ;
4691 GdkColormap *colormap;
4693 g_return_val_if_fail (sheet != NULL, FALSE);
4694 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
4696 if (row < 0 || col < 0) return FALSE;
4698 attr->foreground = GTK_WIDGET (sheet)->style->black;
4699 attr->background = sheet->color[BG_COLOR];
4701 attr->border.width = 0;
4702 attr->border.line_style = GDK_LINE_SOLID;
4703 attr->border.cap_style = GDK_CAP_NOT_LAST;
4704 attr->border.join_style = GDK_JOIN_MITER;
4705 attr->border.mask = 0;
4706 attr->border.color = GTK_WIDGET (sheet)->style->black;
4708 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
4709 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
4712 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
4713 attr->foreground = *fg;
4716 bg = psppire_sheet_model_get_background (sheet->model, row, col);
4719 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
4720 attr->background = *bg;
4723 attr->justification =
4724 psppire_sheet_model_get_column_justification (sheet->model, col);
4726 j = psppire_sheet_model_get_justification (sheet->model, row, col);
4728 attr->justification = *j;
4734 psppire_sheet_forall (GtkContainer *container,
4735 gboolean include_internals,
4736 GtkCallback callback,
4737 gpointer callback_data)
4739 PsppireSheet *sheet = PSPPIRE_SHEET (container);
4741 g_return_if_fail (callback != NULL);
4743 if (sheet->button && sheet->button->parent)
4744 (* callback) (sheet->button, callback_data);
4746 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
4747 (* callback) (sheet->entry_widget, callback_data);
4752 psppire_sheet_get_model (const PsppireSheet *sheet)
4754 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4756 return sheet->model;
4760 PsppireSheetButton *
4761 psppire_sheet_button_new (void)
4763 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
4765 button->state = GTK_STATE_NORMAL;
4766 button->label = NULL;
4767 button->label_visible = TRUE;
4768 button->justification = GTK_JUSTIFY_FILL;
4769 button->overstruck = FALSE;
4776 psppire_sheet_button_free (PsppireSheetButton *button)
4778 if (!button) return ;
4780 g_free (button->label);
4785 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
4787 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
4789 if ( NULL == celltext)
4792 g_string_append (string, celltext);
4798 range_to_text (const PsppireSheet *sheet)
4803 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4806 string = g_string_sized_new (80);
4808 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4810 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
4812 append_cell_text (string, sheet, r, c);
4813 g_string_append (string, "\t");
4815 append_cell_text (string, sheet, r, c);
4816 if ( r < sheet->range.rowi)
4817 g_string_append (string, "\n");
4824 range_to_html (const PsppireSheet *sheet)
4829 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4832 string = g_string_sized_new (480);
4834 g_string_append (string, "<html>\n");
4835 g_string_append (string, "<body>\n");
4836 g_string_append (string, "<table>\n");
4837 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4839 g_string_append (string, "<tr>\n");
4840 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
4842 g_string_append (string, "<td>");
4843 append_cell_text (string, sheet, r, c);
4844 g_string_append (string, "</td>\n");
4846 g_string_append (string, "</tr>\n");
4848 g_string_append (string, "</table>\n");
4849 g_string_append (string, "</body>\n");
4850 g_string_append (string, "</html>\n");
4862 primary_get_cb (GtkClipboard *clipboard,
4863 GtkSelectionData *selection_data,
4867 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4868 GString *string = NULL;
4872 case SELECT_FMT_TEXT:
4873 string = range_to_text (sheet);
4875 case SELECT_FMT_HTML:
4876 string = range_to_html (sheet);
4879 g_assert_not_reached ();
4882 gtk_selection_data_set (selection_data, selection_data->target,
4884 (const guchar *) string->str, string->len);
4885 g_string_free (string, TRUE);
4889 primary_clear_cb (GtkClipboard *clipboard,
4892 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4893 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4896 psppire_sheet_unselect_range (sheet);
4900 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
4902 static const GtkTargetEntry targets[] = {
4903 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
4904 { "STRING", 0, SELECT_FMT_TEXT },
4905 { "TEXT", 0, SELECT_FMT_TEXT },
4906 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
4907 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
4908 { "text/plain", 0, SELECT_FMT_TEXT },
4909 { "text/html", 0, SELECT_FMT_HTML }
4912 GtkClipboard *clipboard;
4914 if (!GTK_WIDGET_REALIZED (sheet))
4917 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
4918 GDK_SELECTION_PRIMARY);
4920 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
4922 if (!gtk_clipboard_set_with_owner (clipboard, targets,
4923 G_N_ELEMENTS (targets),
4924 primary_get_cb, primary_clear_cb,
4926 primary_clear_cb (clipboard, sheet);
4930 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
4931 gtk_clipboard_clear (clipboard);