2 Copyright (C) 2006, 2008, 2009, 2011, 2012 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_style_set (GtkWidget *widget,
448 GtkStyle *previous_style);
449 static void psppire_sheet_realize (GtkWidget *widget);
450 static void psppire_sheet_unrealize (GtkWidget *widget);
451 static void psppire_sheet_map (GtkWidget *widget);
452 static void psppire_sheet_unmap (GtkWidget *widget);
453 static gint psppire_sheet_expose (GtkWidget *widget,
454 GdkEventExpose *event);
456 static void psppire_sheet_forall (GtkContainer *container,
457 gboolean include_internals,
458 GtkCallback callback,
459 gpointer callback_data);
461 static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
462 GtkAdjustment *hadjustment,
463 GtkAdjustment *vadjustment);
465 static gint psppire_sheet_button_press (GtkWidget *widget,
466 GdkEventButton *event);
467 static gint psppire_sheet_button_release (GtkWidget *widget,
468 GdkEventButton *event);
469 static gint psppire_sheet_motion (GtkWidget *widget,
470 GdkEventMotion *event);
471 static gboolean psppire_sheet_crossing_notify (GtkWidget *widget,
472 GdkEventCrossing *event);
473 static gint psppire_sheet_entry_key_press (GtkWidget *widget,
475 static gboolean psppire_sheet_key_press (GtkWidget *widget,
477 static void psppire_sheet_size_request (GtkWidget *widget,
478 GtkRequisition *requisition);
479 static void psppire_sheet_size_allocate (GtkWidget *widget,
480 GtkAllocation *allocation);
482 static gboolean psppire_sheet_focus_in (GtkWidget *widget,
483 GdkEventFocus *event);
487 static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
488 const PsppireSheetRange *range);
489 static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet,
490 gint row, gint column);
491 /* Drawing Routines */
494 static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column);
497 /* draw visible part of range. */
498 static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region);
502 static void psppire_sheet_draw_border (PsppireSheet *sheet,
503 PsppireSheetRange range);
505 /* Active Cell handling */
507 static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet);
508 static void change_active_cell (PsppireSheet *sheet, gint row, gint col);
509 static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet);
510 static void psppire_sheet_show_entry_widget (PsppireSheet *sheet);
511 static gboolean psppire_sheet_click_cell (PsppireSheet *sheet,
518 static void adjust_scrollbars (PsppireSheet *sheet);
519 static void vadjustment_value_changed (GtkAdjustment *adjustment,
521 static void hadjustment_value_changed (GtkAdjustment *adjustment,
525 static void draw_xor_vline (PsppireSheet *sheet);
526 static void draw_xor_hline (PsppireSheet *sheet);
527 static void draw_xor_rectangle (PsppireSheet *sheet,
528 PsppireSheetRange range);
532 static void create_global_button (PsppireSheet *sheet);
533 static void global_button_clicked (GtkWidget *widget,
537 static void create_sheet_entry (PsppireSheet *sheet);
538 static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet);
540 /* Sheet button gadgets */
542 static void draw_column_title_buttons (PsppireSheet *sheet);
543 static void draw_row_title_buttons (PsppireSheet *sheet);
546 static void size_allocate_global_button (PsppireSheet *sheet);
548 static void psppire_sheet_real_cell_clear (PsppireSheet *sheet,
570 static GtkContainerClass *parent_class = NULL;
571 static guint sheet_signals[LAST_SIGNAL] = { 0 };
575 psppire_sheet_get_type ()
577 static GType sheet_type = 0;
581 static const GTypeInfo sheet_info =
583 sizeof (PsppireSheetClass),
586 (GClassInitFunc) psppire_sheet_class_init,
589 sizeof (PsppireSheet),
591 (GInstanceInitFunc) psppire_sheet_init,
596 g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
604 static PsppireSheetRange*
605 psppire_sheet_range_copy (const PsppireSheetRange *range)
607 PsppireSheetRange *new_range;
609 g_return_val_if_fail (range != NULL, NULL);
611 new_range = g_new (PsppireSheetRange, 1);
619 psppire_sheet_range_free (PsppireSheetRange *range)
621 g_return_if_fail (range != NULL);
627 psppire_sheet_range_get_type (void)
629 static GType sheet_range_type = 0;
631 if (!sheet_range_type)
634 g_boxed_type_register_static ("PsppireSheetRange",
635 (GBoxedCopyFunc) psppire_sheet_range_copy,
636 (GBoxedFreeFunc) psppire_sheet_range_free);
639 return sheet_range_type;
642 static PsppireSheetCell*
643 psppire_sheet_cell_copy (const PsppireSheetCell *cell)
645 PsppireSheetCell *new_cell;
647 g_return_val_if_fail (cell != NULL, NULL);
649 new_cell = g_new (PsppireSheetCell, 1);
657 psppire_sheet_cell_free (PsppireSheetCell *cell)
659 g_return_if_fail (cell != NULL);
665 psppire_sheet_cell_get_type (void)
667 static GType sheet_cell_type = 0;
669 if (!sheet_cell_type)
672 g_boxed_type_register_static ("PsppireSheetCell",
673 (GBoxedCopyFunc) psppire_sheet_cell_copy,
674 (GBoxedFreeFunc) psppire_sheet_cell_free);
677 return sheet_cell_type;
692 resize_column (PsppireSheet *sheet, gint unit, glong size)
694 PsppireSheetRange range;
696 range.coli = max_visible_column (sheet);
697 range.row0 = min_visible_row (sheet);
698 range.rowi = max_visible_row (sheet);
700 redraw_range (sheet, &range);
702 draw_column_title_buttons_range (sheet, range.col0, range.coli);
707 psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
710 g_object_unref (sheet->haxis);
713 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
716 g_object_ref (sheet->haxis);
720 resize_row (PsppireSheet *sheet, gint unit, glong size)
722 PsppireSheetRange range;
723 range.col0 = min_visible_column (sheet);
724 range.coli = max_visible_column (sheet);
726 range.rowi = max_visible_row (sheet);
728 redraw_range (sheet, &range);
730 draw_row_title_buttons_range (sheet, range.row0, range.rowi);
734 psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
737 g_object_unref (sheet->vaxis);
741 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
744 g_object_ref (sheet->vaxis);
747 static const GtkBorder default_cell_padding = {3, 3, 3, 3};
750 psppire_sheet_set_property (GObject *object,
756 PsppireSheet *sheet = PSPPIRE_SHEET (object);
760 case PROP_CELL_PADDING:
761 if ( sheet->cell_padding)
762 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
764 sheet->cell_padding = g_value_dup_boxed (value);
766 if (NULL == sheet->cell_padding)
767 sheet->cell_padding = g_boxed_copy (GTK_TYPE_BORDER,
768 &default_cell_padding);
771 g_object_set (sheet->vaxis, "padding",
772 sheet->cell_padding->top + sheet->cell_padding->bottom,
776 g_object_set (sheet->haxis, "padding",
777 sheet->cell_padding->left + sheet->cell_padding->right,
781 psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
782 g_object_set (sheet->vaxis, "padding",
783 sheet->cell_padding->top + sheet->cell_padding->bottom,
787 psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
788 g_object_set (sheet->haxis, "padding",
789 sheet->cell_padding->left + sheet->cell_padding->right,
793 psppire_sheet_set_model (sheet, g_value_get_pointer (value));
796 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
802 psppire_sheet_get_property (GObject *object,
807 PsppireSheet *sheet = PSPPIRE_SHEET (object);
811 case PROP_CELL_PADDING:
812 g_value_set_boxed (value, sheet->cell_padding);
815 g_value_set_pointer (value, sheet->vaxis);
818 g_value_set_pointer (value, sheet->haxis);
821 g_value_set_pointer (value, sheet->model);
824 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
831 psppire_sheet_class_init (PsppireSheetClass *klass)
833 GObjectClass *object_class = G_OBJECT_CLASS (klass);
835 GParamSpec *haxis_spec ;
836 GParamSpec *vaxis_spec ;
837 GParamSpec *model_spec ;
838 GParamSpec *cell_padding_spec ;
840 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
841 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
843 parent_class = g_type_class_peek_parent (klass);
846 * PsppireSheet::select-row
847 * @sheet: the sheet widget that emitted the signal
848 * @row: the newly selected row index, or -1 if no row is selected.
850 * A row has been selected.
852 sheet_signals[SELECT_ROW] =
853 g_signal_new ("select-row",
854 G_TYPE_FROM_CLASS (object_class),
856 offsetof (PsppireSheetClass, select_row),
858 g_cclosure_marshal_VOID__INT,
865 * PsppireSheet::select - column
866 * @sheet: the sheet widget that emitted the signal
867 * @column: the newly selected column index, or -1 if no column is selected.
869 * A column has been selected.
871 sheet_signals[SELECT_COLUMN] =
872 g_signal_new ("select-column",
873 G_TYPE_FROM_CLASS (object_class),
875 offsetof (PsppireSheetClass, select_column),
877 g_cclosure_marshal_VOID__INT,
884 * PsppireSheet::double-click-row
885 * @sheet: the sheet widget that emitted the signal
886 * @row: the row that was double clicked.
888 * A row's title button has been double clicked
890 sheet_signals[DOUBLE_CLICK_ROW] =
891 g_signal_new ("double-click-row",
892 G_TYPE_FROM_CLASS (object_class),
896 g_cclosure_marshal_VOID__INT,
903 * PsppireSheet::double-click-column
904 * @sheet: the sheet widget that emitted the signal
905 * @column: the column that was double clicked.
907 * A column's title button has been double clicked
909 sheet_signals[DOUBLE_CLICK_COLUMN] =
910 g_signal_new ("double-click-column",
911 G_TYPE_FROM_CLASS (object_class),
915 g_cclosure_marshal_VOID__INT,
922 * PsppireSheet::button-event-column
923 * @sheet: the sheet widget that emitted the signal
924 * @column: the column on which the event occured.
926 * A button event occured on a column title button
928 sheet_signals[BUTTON_EVENT_COLUMN] =
929 g_signal_new ("button-event-column",
930 G_TYPE_FROM_CLASS (object_class),
934 psppire_marshal_VOID__INT_POINTER,
943 * PsppireSheet::button-event-row
944 * @sheet: the sheet widget that emitted the signal
945 * @column: the column on which the event occured.
947 * A button event occured on a row title button
949 sheet_signals[BUTTON_EVENT_ROW] =
950 g_signal_new ("button-event-row",
951 G_TYPE_FROM_CLASS (object_class),
955 psppire_marshal_VOID__INT_POINTER,
963 sheet_signals[SELECT_RANGE] =
964 g_signal_new ("select-range",
965 G_TYPE_FROM_CLASS (object_class),
967 offsetof (PsppireSheetClass, select_range),
969 g_cclosure_marshal_VOID__BOXED,
972 PSPPIRE_TYPE_SHEET_RANGE);
975 sheet_signals[RESIZE_RANGE] =
976 g_signal_new ("resize-range",
977 G_TYPE_FROM_CLASS (object_class),
979 offsetof (PsppireSheetClass, resize_range),
981 psppire_marshal_VOID__BOXED_BOXED,
984 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
987 sheet_signals[MOVE_RANGE] =
988 g_signal_new ("move-range",
989 G_TYPE_FROM_CLASS (object_class),
991 offsetof (PsppireSheetClass, move_range),
993 psppire_marshal_VOID__BOXED_BOXED,
996 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
999 sheet_signals[TRAVERSE] =
1000 g_signal_new ("traverse",
1001 G_TYPE_FROM_CLASS (object_class),
1003 offsetof (PsppireSheetClass, traverse),
1005 psppire_marshal_BOOLEAN__BOXED_POINTER,
1007 PSPPIRE_TYPE_SHEET_CELL,
1011 sheet_signals[ACTIVATE] =
1012 g_signal_new ("activate",
1013 G_TYPE_FROM_CLASS (object_class),
1015 offsetof (PsppireSheetClass, activate),
1017 psppire_marshal_VOID__INT_INT_INT_INT,
1019 G_TYPE_INT, G_TYPE_INT,
1020 G_TYPE_INT, G_TYPE_INT);
1022 widget_class->set_scroll_adjustments_signal =
1023 g_signal_new ("set-scroll-adjustments",
1024 G_TYPE_FROM_CLASS (object_class),
1026 offsetof (PsppireSheetClass, set_scroll_adjustments),
1028 psppire_marshal_VOID__OBJECT_OBJECT,
1029 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1032 container_class->add = NULL;
1033 container_class->remove = NULL;
1034 container_class->forall = psppire_sheet_forall;
1035 container_class->set_focus_child = NULL;
1037 object_class->dispose = psppire_sheet_dispose;
1040 g_param_spec_boxed ("cell-padding",
1042 "The space between a cell's contents and its border",
1044 G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
1047 g_param_spec_pointer ("vertical-axis",
1049 "A pointer to the PsppireAxis object for the rows",
1050 G_PARAM_READABLE | G_PARAM_WRITABLE );
1053 g_param_spec_pointer ("horizontal-axis",
1055 "A pointer to the PsppireAxis object for the columns",
1056 G_PARAM_READABLE | G_PARAM_WRITABLE );
1059 g_param_spec_pointer ("model",
1061 "A pointer to the data model",
1062 G_PARAM_READABLE | G_PARAM_WRITABLE );
1065 object_class->set_property = psppire_sheet_set_property;
1066 object_class->get_property = psppire_sheet_get_property;
1068 g_object_class_install_property (object_class,
1072 g_object_class_install_property (object_class,
1076 g_object_class_install_property (object_class,
1080 g_object_class_install_property (object_class,
1085 widget_class->realize = psppire_sheet_realize;
1086 widget_class->unrealize = psppire_sheet_unrealize;
1087 widget_class->map = psppire_sheet_map;
1088 widget_class->unmap = psppire_sheet_unmap;
1089 widget_class->style_set = psppire_sheet_style_set;
1090 widget_class->button_press_event = psppire_sheet_button_press;
1091 widget_class->button_release_event = psppire_sheet_button_release;
1092 widget_class->motion_notify_event = psppire_sheet_motion;
1093 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1094 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1095 widget_class->key_press_event = psppire_sheet_key_press;
1096 widget_class->expose_event = psppire_sheet_expose;
1097 widget_class->size_request = psppire_sheet_size_request;
1098 widget_class->size_allocate = psppire_sheet_size_allocate;
1099 widget_class->focus_in_event = psppire_sheet_focus_in;
1100 widget_class->focus_out_event = NULL;
1102 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1103 klass->select_row = NULL;
1104 klass->select_column = NULL;
1105 klass->select_range = NULL;
1106 klass->resize_range = NULL;
1107 klass->move_range = NULL;
1108 klass->traverse = NULL;
1109 klass->activate = NULL;
1110 klass->changed = NULL;
1114 psppire_sheet_init (PsppireSheet *sheet)
1116 sheet->model = NULL;
1117 sheet->haxis = NULL;
1118 sheet->vaxis = NULL;
1121 sheet->select_status = PSPPIRE_SHEET_NORMAL;
1123 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1124 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1126 sheet->column_title_window = NULL;
1127 sheet->column_title_area.x = 0;
1128 sheet->column_title_area.y = 0;
1129 sheet->column_title_area.width = 0;
1130 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1132 sheet->row_title_window = NULL;
1133 sheet->row_title_area.x = 0;
1134 sheet->row_title_area.y = 0;
1135 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1136 sheet->row_title_area.height = 0;
1139 sheet->active_cell.row = 0;
1140 sheet->active_cell.col = 0;
1142 sheet->range.row0 = 0;
1143 sheet->range.rowi = 0;
1144 sheet->range.col0 = 0;
1145 sheet->range.coli = 0;
1147 sheet->sheet_window = NULL;
1148 sheet->entry_widget = NULL;
1149 sheet->button = NULL;
1151 sheet->hadjustment = NULL;
1152 sheet->vadjustment = NULL;
1154 sheet->cursor_drag = NULL;
1156 sheet->xor_gc = NULL;
1157 sheet->fg_gc = NULL;
1158 sheet->bg_gc = NULL;
1161 sheet->show_grid = TRUE;
1163 sheet->motion_timer = 0;
1165 sheet->row_titles_visible = TRUE;
1166 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1168 sheet->column_titles_visible = TRUE;
1171 /* create sheet entry */
1172 sheet->entry_type = GTK_TYPE_ENTRY;
1173 create_sheet_entry (sheet);
1175 /* create global selection button */
1176 create_global_button (sheet);
1180 /* Cause RANGE to be redrawn. If RANGE is null, then the
1181 entire visible range will be redrawn.
1184 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1188 if ( ! GTK_WIDGET_REALIZED (sheet))
1191 if ( NULL != range )
1192 rectangle_from_range (sheet, range, &rect);
1195 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1196 gdk_region_get_clipbox (r, &rect);
1198 if ( sheet->column_titles_visible)
1200 rect.y += sheet->column_title_area.height;
1201 rect.height -= sheet->column_title_area.height;
1204 if ( sheet->row_titles_visible)
1206 rect.x += sheet->row_title_area.width;
1207 rect.width -= sheet->row_title_area.width;
1211 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1215 /* Callback which occurs whenever columns are inserted / deleted in the model */
1217 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1221 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1223 PsppireSheetRange range;
1224 gint model_columns = psppire_sheet_model_get_column_count (model);
1227 /* Need to update all the columns starting from the first column and onwards.
1228 * Previous column are unchanged, so don't need to be updated.
1230 range.col0 = first_column;
1232 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1233 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1235 adjust_scrollbars (sheet);
1237 if (sheet->active_cell.col >= model_columns)
1238 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1240 draw_column_title_buttons_range (sheet,
1241 first_column, max_visible_column (sheet));
1244 redraw_range (sheet, &range);
1250 /* Callback which occurs whenever rows are inserted / deleted in the model */
1252 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1253 gint n_rows, gpointer data)
1255 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1257 PsppireSheetRange range;
1259 gint model_rows = psppire_sheet_model_get_row_count (model);
1261 /* Need to update all the rows starting from the first row and onwards.
1262 * Previous rows are unchanged, so don't need to be updated.
1264 range.row0 = first_row;
1266 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1267 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1269 adjust_scrollbars (sheet);
1271 if (sheet->active_cell.row >= model_rows)
1272 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1274 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1276 redraw_range (sheet, &range);
1280 If row0 or rowi are negative, then all rows will be updated.
1281 If col0 or coli are negative, then all columns will be updated.
1284 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1285 gint rowi, gint coli, gpointer data)
1287 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1289 PsppireSheetRange range;
1296 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1299 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1301 redraw_range (sheet, NULL);
1302 adjust_scrollbars (sheet);
1304 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1305 max_visible_row (sheet));
1307 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1308 max_visible_column (sheet));
1312 else if ( row0 < 0 || rowi < 0 )
1314 range.row0 = min_visible_row (sheet);
1315 range.rowi = max_visible_row (sheet);
1317 else if ( col0 < 0 || coli < 0 )
1319 range.col0 = min_visible_column (sheet);
1320 range.coli = max_visible_column (sheet);
1323 redraw_range (sheet, &range);
1328 * psppire_sheet_new:
1329 * @rows: initial number of rows
1330 * @columns: initial number of columns
1331 * @title: sheet title
1332 * @model: the model to use for the sheet data
1334 * Creates a new sheet widget with the given number of rows and columns.
1336 * Returns: the new sheet widget
1339 psppire_sheet_new (PsppireSheetModel *model)
1341 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1349 * psppire_sheet_set_model
1350 * @sheet: the sheet to set the model for
1351 * @model: the model to use for the sheet data
1353 * Sets the model for a PsppireSheet
1357 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1359 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1361 if (sheet->model ) g_object_unref (sheet->model);
1363 sheet->model = model;
1367 g_object_ref (model);
1369 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1370 G_CALLBACK (range_update_callback),
1373 g_signal_connect (model, "rows_inserted",
1374 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1376 g_signal_connect (model, "rows_deleted",
1377 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1379 g_signal_connect (model, "columns_inserted",
1380 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1382 g_signal_connect (model, "columns_deleted",
1383 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1389 psppire_sheet_change_entry (PsppireSheet *sheet, GType entry_type)
1391 g_return_if_fail (sheet != NULL);
1392 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1394 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1395 psppire_sheet_hide_entry_widget (sheet);
1397 sheet->entry_type = entry_type;
1399 create_sheet_entry (sheet);
1401 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1402 psppire_sheet_show_entry_widget (sheet);
1406 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1408 g_return_if_fail (sheet != NULL);
1409 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1411 if (show == sheet->show_grid) return;
1413 sheet->show_grid = show;
1415 redraw_range (sheet, NULL);
1419 psppire_sheet_grid_visible (PsppireSheet *sheet)
1421 g_return_val_if_fail (sheet != NULL, 0);
1422 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1424 return sheet->show_grid;
1428 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1430 g_return_val_if_fail (sheet != NULL, 0);
1431 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1433 return psppire_axis_unit_count (sheet->haxis);
1436 static void set_column_width (PsppireSheet *sheet,
1442 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1444 if (sheet->column_titles_visible) return;
1446 sheet->column_titles_visible = TRUE;
1448 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1451 gdk_window_show (sheet->column_title_window);
1452 gdk_window_move_resize (sheet->column_title_window,
1453 sheet->column_title_area.x,
1454 sheet->column_title_area.y,
1455 sheet->column_title_area.width,
1456 sheet->column_title_area.height);
1458 adjust_scrollbars (sheet);
1460 if (sheet->vadjustment)
1461 g_signal_emit_by_name (sheet->vadjustment,
1464 size_allocate_global_button (sheet);
1466 if ( sheet->row_titles_visible)
1467 gtk_widget_show (sheet->button);
1472 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1474 if (sheet->row_titles_visible) return;
1476 sheet->row_titles_visible = TRUE;
1479 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1481 gdk_window_show (sheet->row_title_window);
1482 gdk_window_move_resize (sheet->row_title_window,
1483 sheet->row_title_area.x,
1484 sheet->row_title_area.y,
1485 sheet->row_title_area.width,
1486 sheet->row_title_area.height);
1488 adjust_scrollbars (sheet);
1491 if (sheet->hadjustment)
1492 g_signal_emit_by_name (sheet->hadjustment,
1495 size_allocate_global_button (sheet);
1497 if ( sheet->column_titles_visible)
1498 gtk_widget_show (sheet->button);
1502 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1504 if (!sheet->column_titles_visible) return;
1506 sheet->column_titles_visible = FALSE;
1508 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1510 if (sheet->column_title_window)
1511 gdk_window_hide (sheet->column_title_window);
1513 gtk_widget_hide (sheet->button);
1515 adjust_scrollbars (sheet);
1518 if (sheet->vadjustment)
1519 g_signal_emit_by_name (sheet->vadjustment,
1524 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1526 if (!sheet->row_titles_visible) return;
1528 sheet->row_titles_visible = FALSE;
1530 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1532 if (sheet->row_title_window)
1533 gdk_window_hide (sheet->row_title_window);
1535 gtk_widget_hide (sheet->button);
1537 adjust_scrollbars (sheet);
1540 if (sheet->hadjustment)
1541 g_signal_emit_by_name (sheet->hadjustment,
1546 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1547 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1548 at the {top,left} of the sheet. If it's 1, then it'll
1549 be placed at the {bottom,right}.
1550 ROW or COL may be -1, in which case scrolling in that dimension
1554 psppire_sheet_moveto (PsppireSheet *sheet,
1562 g_return_if_fail (row_align >= 0);
1563 g_return_if_fail (col_align >= 0);
1565 g_return_if_fail (row_align <= 1);
1566 g_return_if_fail (col_align <= 1);
1568 g_return_if_fail (col <
1569 psppire_axis_unit_count (sheet->haxis));
1570 g_return_if_fail (row <
1571 psppire_axis_unit_count (sheet->vaxis));
1573 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1578 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1580 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1586 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1588 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1596 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1597 const PsppireSheetRange *range)
1599 g_return_val_if_fail (sheet != NULL, FALSE);
1601 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1604 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1607 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1610 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1613 if (range->rowi < min_visible_row (sheet))
1616 if (range->row0 > max_visible_row (sheet))
1619 if (range->coli < min_visible_column (sheet))
1622 if (range->col0 > max_visible_column (sheet))
1629 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1630 gint row, gint column)
1632 PsppireSheetRange range;
1635 range.col0 = column;
1637 range.coli = column;
1639 return psppire_sheet_range_isvisible (sheet, &range);
1643 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1645 g_return_if_fail (sheet != NULL);
1646 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1647 g_return_if_fail (range != NULL);
1649 range->row0 = min_visible_row (sheet);
1650 range->col0 = min_visible_column (sheet);
1651 range->rowi = max_visible_row (sheet);
1652 range->coli = max_visible_column (sheet);
1657 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1658 GtkAdjustment *hadjustment,
1659 GtkAdjustment *vadjustment)
1661 if ( sheet->vadjustment != vadjustment )
1663 if (sheet->vadjustment)
1664 g_object_unref (sheet->vadjustment);
1665 sheet->vadjustment = vadjustment;
1669 g_object_ref (vadjustment);
1671 g_signal_connect (sheet->vadjustment, "value_changed",
1672 G_CALLBACK (vadjustment_value_changed),
1677 if ( sheet->hadjustment != hadjustment )
1679 if (sheet->hadjustment)
1680 g_object_unref (sheet->hadjustment);
1682 sheet->hadjustment = hadjustment;
1686 g_object_ref (hadjustment);
1688 g_signal_connect (sheet->hadjustment, "value_changed",
1689 G_CALLBACK (hadjustment_value_changed),
1697 psppire_sheet_dispose (GObject *object)
1699 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1701 g_return_if_fail (object != NULL);
1702 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1704 if ( sheet->dispose_has_run )
1707 sheet->dispose_has_run = TRUE;
1709 if ( sheet->cell_padding)
1710 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1712 if (sheet->model) g_object_unref (sheet->model);
1713 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1714 if (sheet->haxis) g_object_unref (sheet->haxis);
1716 g_object_unref (sheet->button);
1717 sheet->button = NULL;
1719 /* unref adjustments */
1720 if (sheet->hadjustment)
1722 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1723 G_SIGNAL_MATCH_DATA,
1727 g_object_unref (sheet->hadjustment);
1728 sheet->hadjustment = NULL;
1731 if (sheet->vadjustment)
1733 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1734 G_SIGNAL_MATCH_DATA,
1738 g_object_unref (sheet->vadjustment);
1740 sheet->vadjustment = NULL;
1743 if (G_OBJECT_CLASS (parent_class)->dispose)
1744 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1748 psppire_sheet_style_set (GtkWidget *widget,
1749 GtkStyle *previous_style)
1751 PsppireSheet *sheet;
1753 g_return_if_fail (widget != NULL);
1754 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1756 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1757 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1759 sheet = PSPPIRE_SHEET (widget);
1761 if (GTK_WIDGET_REALIZED (widget))
1763 gtk_style_set_background (widget->style, widget->window, widget->state);
1766 set_entry_widget_font (sheet);
1771 psppire_sheet_realize (GtkWidget *widget)
1773 PsppireSheet *sheet;
1774 GdkWindowAttr attributes;
1775 const gint attributes_mask =
1776 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1779 GdkColormap *colormap;
1780 GdkDisplay *display;
1782 g_return_if_fail (widget != NULL);
1783 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1785 sheet = PSPPIRE_SHEET (widget);
1787 colormap = gtk_widget_get_colormap (widget);
1788 display = gtk_widget_get_display (widget);
1790 attributes.window_type = GDK_WINDOW_CHILD;
1791 attributes.x = widget->allocation.x;
1792 attributes.y = widget->allocation.y;
1793 attributes.width = widget->allocation.width;
1794 attributes.height = widget->allocation.height;
1795 attributes.wclass = GDK_INPUT_OUTPUT;
1797 attributes.visual = gtk_widget_get_visual (widget);
1798 attributes.colormap = colormap;
1800 attributes.event_mask = gtk_widget_get_events (widget);
1801 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1802 GDK_BUTTON_PRESS_MASK |
1803 GDK_BUTTON_RELEASE_MASK |
1804 GDK_KEY_PRESS_MASK |
1805 GDK_ENTER_NOTIFY_MASK |
1806 GDK_LEAVE_NOTIFY_MASK |
1807 GDK_POINTER_MOTION_MASK |
1808 GDK_POINTER_MOTION_HINT_MASK);
1810 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1813 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1815 gdk_window_set_user_data (widget->window, sheet);
1817 widget->style = gtk_style_attach (widget->style, widget->window);
1819 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1821 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1822 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1824 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1825 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1830 attributes.width = sheet->column_title_area.width;
1831 attributes.height = sheet->column_title_area.height;
1834 /* column - title window */
1835 sheet->column_title_window =
1836 gdk_window_new (widget->window, &attributes, attributes_mask);
1837 gdk_window_set_user_data (sheet->column_title_window, sheet);
1838 gtk_style_set_background (widget->style, sheet->column_title_window,
1844 attributes.width = sheet->row_title_area.width;
1845 attributes.height = sheet->row_title_area.height;
1847 /* row - title window */
1848 sheet->row_title_window = gdk_window_new (widget->window,
1849 &attributes, attributes_mask);
1850 gdk_window_set_user_data (sheet->row_title_window, sheet);
1851 gtk_style_set_background (widget->style, sheet->row_title_window,
1854 /* sheet - window */
1855 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1860 sheet->sheet_window = gdk_window_new (widget->window,
1861 &attributes, attributes_mask);
1862 gdk_window_set_user_data (sheet->sheet_window, sheet);
1864 gdk_cursor_unref (attributes.cursor);
1866 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1867 gdk_window_show (sheet->sheet_window);
1870 sheet->fg_gc = gdk_gc_new (widget->window);
1871 sheet->bg_gc = gdk_gc_new (widget->window);
1873 values.foreground = widget->style->white;
1874 values.function = GDK_INVERT;
1875 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1876 values.line_width = MAX (sheet->cell_padding->left,
1877 MAX (sheet->cell_padding->right,
1878 MAX (sheet->cell_padding->top,
1879 sheet->cell_padding->bottom)));
1881 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1889 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1890 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1892 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1893 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1895 sheet->button->style = gtk_style_attach (sheet->button->style,
1896 sheet->sheet_window);
1899 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1901 if (sheet->column_titles_visible)
1902 gdk_window_show (sheet->column_title_window);
1903 if (sheet->row_titles_visible)
1904 gdk_window_show (sheet->row_title_window);
1906 sheet->hover_window = create_hover_window ();
1908 draw_row_title_buttons (sheet);
1909 draw_column_title_buttons (sheet);
1911 psppire_sheet_update_primary_selection (sheet);
1914 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1918 create_global_button (PsppireSheet *sheet)
1920 sheet->button = gtk_button_new_with_label (" ");
1922 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1924 g_object_ref_sink (sheet->button);
1926 g_signal_connect (sheet->button,
1928 G_CALLBACK (global_button_clicked),
1933 size_allocate_global_button (PsppireSheet *sheet)
1935 GtkAllocation allocation;
1937 if (!sheet->column_titles_visible) return;
1938 if (!sheet->row_titles_visible) return;
1940 gtk_widget_size_request (sheet->button, NULL);
1944 allocation.width = sheet->row_title_area.width;
1945 allocation.height = sheet->column_title_area.height;
1947 gtk_widget_size_allocate (sheet->button, &allocation);
1951 global_button_clicked (GtkWidget *widget, gpointer data)
1953 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
1958 psppire_sheet_unrealize (GtkWidget *widget)
1960 PsppireSheet *sheet;
1962 g_return_if_fail (widget != NULL);
1963 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1965 sheet = PSPPIRE_SHEET (widget);
1967 gdk_cursor_unref (sheet->cursor_drag);
1968 sheet->cursor_drag = NULL;
1970 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
1971 sheet->color, n_COLORS);
1973 g_object_unref (sheet->xor_gc);
1974 g_object_unref (sheet->fg_gc);
1975 g_object_unref (sheet->bg_gc);
1977 destroy_hover_window (sheet->hover_window);
1979 gdk_window_destroy (sheet->sheet_window);
1980 gdk_window_destroy (sheet->column_title_window);
1981 gdk_window_destroy (sheet->row_title_window);
1983 gtk_widget_unparent (sheet->entry_widget);
1984 if (sheet->button != NULL)
1985 gtk_widget_unparent (sheet->button);
1987 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
1988 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1992 psppire_sheet_map (GtkWidget *widget)
1994 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
1996 g_return_if_fail (widget != NULL);
1997 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1999 if (!GTK_WIDGET_MAPPED (widget))
2001 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2003 gdk_window_show (widget->window);
2004 gdk_window_show (sheet->sheet_window);
2006 if (sheet->column_titles_visible)
2008 draw_column_title_buttons (sheet);
2009 gdk_window_show (sheet->column_title_window);
2011 if (sheet->row_titles_visible)
2013 draw_row_title_buttons (sheet);
2014 gdk_window_show (sheet->row_title_window);
2017 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2018 && sheet->active_cell.row >= 0
2019 && sheet->active_cell.col >= 0 )
2021 gtk_widget_show (sheet->entry_widget);
2022 gtk_widget_map (sheet->entry_widget);
2025 if (!GTK_WIDGET_MAPPED (sheet->button))
2027 gtk_widget_show (sheet->button);
2028 gtk_widget_map (sheet->button);
2031 redraw_range (sheet, NULL);
2032 change_active_cell (sheet,
2033 sheet->active_cell.row,
2034 sheet->active_cell.col);
2039 psppire_sheet_unmap (GtkWidget *widget)
2041 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2043 if (!GTK_WIDGET_MAPPED (widget))
2046 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2048 gdk_window_hide (sheet->sheet_window);
2049 if (sheet->column_titles_visible)
2050 gdk_window_hide (sheet->column_title_window);
2051 if (sheet->row_titles_visible)
2052 gdk_window_hide (sheet->row_title_window);
2053 gdk_window_hide (widget->window);
2055 gtk_widget_unmap (sheet->entry_widget);
2056 gtk_widget_unmap (sheet->button);
2057 gtk_widget_unmap (sheet->hover_window->window);
2060 /* get cell attributes of the given cell */
2061 /* TRUE means that the cell is currently allocated */
2062 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2064 PsppireSheetCellAttr *attributes);
2069 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2071 PangoLayout *layout;
2072 PangoRectangle text;
2073 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2078 PsppireSheetCellAttr attributes;
2081 g_return_if_fail (sheet != NULL);
2083 /* bail now if we aren't yet drawable */
2084 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2087 row >= psppire_axis_unit_count (sheet->vaxis))
2091 col >= psppire_axis_unit_count (sheet->haxis))
2094 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2096 /* select GC for background rectangle */
2097 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2098 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2100 rectangle_from_cell (sheet, row, col, &area);
2102 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2104 if (sheet->show_grid)
2106 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2108 gdk_draw_rectangle (sheet->sheet_window,
2112 area.width, area.height);
2116 label = psppire_sheet_cell_get_text (sheet, row, col);
2121 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2122 dispose_string (sheet, label);
2125 pango_layout_set_font_description (layout, font_desc);
2127 pango_layout_get_pixel_extents (layout, NULL, &text);
2129 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2131 font_height = pango_font_description_get_size (font_desc);
2132 if ( !pango_font_description_get_size_is_absolute (font_desc))
2133 font_height /= PANGO_SCALE;
2136 if ( sheet->cell_padding )
2138 area.x += sheet->cell_padding->left;
2139 area.width -= sheet->cell_padding->right
2140 + sheet->cell_padding->left;
2142 area.y += sheet->cell_padding->top;
2143 area.height -= sheet->cell_padding->bottom
2145 sheet->cell_padding->top;
2148 /* Centre the text vertically */
2149 area.y += (area.height - font_height) / 2.0;
2151 switch (attributes.justification)
2153 case GTK_JUSTIFY_RIGHT:
2154 area.x += area.width - text.width;
2156 case GTK_JUSTIFY_CENTER:
2157 area.x += (area.width - text.width) / 2.0;
2159 case GTK_JUSTIFY_LEFT:
2163 g_critical ("Unhandled justification %d in column %d\n",
2164 attributes.justification, col);
2168 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2173 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2174 g_object_unref (layout);
2179 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2181 PsppireSheetRange range;
2186 PsppireSheetRange drawing_range;
2188 gdk_region_get_clipbox (region, &area);
2190 y = area.y + sheet->vadjustment->value;
2191 x = area.x + sheet->hadjustment->value;
2193 if ( sheet->column_titles_visible)
2194 y -= sheet->column_title_area.height;
2196 if ( sheet->row_titles_visible)
2197 x -= sheet->row_title_area.width;
2199 maximize_int (&x, 0);
2200 maximize_int (&y, 0);
2202 range.row0 = row_from_ypixel (sheet, y);
2203 range.rowi = row_from_ypixel (sheet, y + area.height);
2205 range.col0 = column_from_xpixel (sheet, x);
2206 range.coli = column_from_xpixel (sheet, x + area.width);
2208 g_return_if_fail (sheet != NULL);
2209 g_return_if_fail (PSPPIRE_SHEET (sheet));
2211 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2212 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2213 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2216 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2217 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2218 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2219 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2221 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2222 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2224 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2226 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2227 psppire_sheet_cell_draw (sheet, i, j);
2230 if (sheet->select_status == PSPPIRE_SHEET_NORMAL &&
2231 sheet->active_cell.row >= drawing_range.row0 &&
2232 sheet->active_cell.row <= drawing_range.rowi &&
2233 sheet->active_cell.col >= drawing_range.col0 &&
2234 sheet->active_cell.col <= drawing_range.coli)
2235 psppire_sheet_show_entry_widget (sheet);
2239 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2240 GtkJustification justification,
2243 PsppireSheetModel *model ;
2246 g_return_if_fail (sheet != NULL);
2247 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2249 if (col >= psppire_axis_unit_count (sheet->haxis)
2250 || row >= psppire_axis_unit_count (sheet->vaxis))
2253 if (col < 0 || row < 0) return;
2255 model = psppire_sheet_get_model (sheet);
2257 old_text = psppire_sheet_model_get_string (model, row, col);
2259 if (0 != g_strcmp0 (old_text, text))
2261 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2262 psppire_sheet_model_set_string (model, text, row, col);
2263 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2266 if ( psppire_sheet_model_free_strings (model))
2272 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2274 PsppireSheetRange range;
2276 g_return_if_fail (sheet != NULL);
2277 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2278 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2279 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2281 if (column < 0 || row < 0) return;
2285 range.col0 = min_visible_column (sheet);
2286 range.coli = max_visible_column (sheet);
2288 psppire_sheet_real_cell_clear (sheet, row, column);
2290 redraw_range (sheet, &range);
2294 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2296 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2298 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2300 if (old_text && strlen (old_text) > 0 )
2302 psppire_sheet_model_datum_clear (model, row, column);
2305 dispose_string (sheet, old_text);
2309 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2311 PsppireSheetModel *model;
2312 g_return_val_if_fail (sheet != NULL, NULL);
2313 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2315 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2317 if (col < 0 || row < 0) return NULL;
2319 model = psppire_sheet_get_model (sheet);
2324 return psppire_sheet_model_get_string (model, row, col);
2328 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2329 If the function returns FALSE, then the results will be unreliable.
2332 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2340 *column = -G_MAXINT;
2342 g_return_val_if_fail (sheet != NULL, 0);
2343 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2345 /* bounds checking, return false if the user clicked
2353 if ( sheet->column_titles_visible)
2354 y -= sheet->column_title_area.height;
2356 y += sheet->vadjustment->value;
2358 if ( y < 0 && sheet->column_titles_visible)
2364 trow = row_from_ypixel (sheet, y);
2365 if (trow > psppire_axis_unit_count (sheet->vaxis))
2371 if ( sheet->row_titles_visible)
2372 x -= sheet->row_title_area.width;
2374 x += sheet->hadjustment->value;
2376 if ( x < 0 && sheet->row_titles_visible)
2382 tcol = column_from_xpixel (sheet, x);
2383 if (tcol > psppire_axis_unit_count (sheet->haxis))
2393 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2398 g_return_val_if_fail (sheet != NULL, 0);
2399 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2401 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2404 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2405 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2407 area->width= (column == -1) ? sheet->row_title_area.width
2408 : psppire_axis_unit_size (sheet->haxis, column);
2410 area->height= (row == -1) ? sheet->column_title_area.height
2411 : psppire_axis_unit_size (sheet->vaxis, row);
2417 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2419 g_return_if_fail (sheet != NULL);
2420 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2422 if (row < -1 || col < -1)
2425 if (row >= psppire_axis_unit_count (sheet->vaxis)
2427 col >= psppire_axis_unit_count (sheet->haxis))
2430 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2433 if ( row == -1 || col == -1)
2435 psppire_sheet_hide_entry_widget (sheet);
2439 change_active_cell (sheet, row, col);
2443 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2445 g_return_if_fail (sheet != NULL);
2446 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2448 if ( row ) *row = sheet->active_cell.row;
2449 if (column) *column = sheet->active_cell.col;
2453 entry_load_text (PsppireSheet *sheet)
2457 GtkJustification justification;
2458 PsppireSheetCellAttr attributes;
2460 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2461 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2463 row = sheet->active_cell.row;
2464 col = sheet->active_cell.col;
2466 if (row < 0 || col < 0) return;
2468 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2470 if (text && strlen (text) > 0)
2472 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2473 justification = attributes.justification;
2474 psppire_sheet_set_cell (sheet, row, col, justification, text);
2480 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2482 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2485 if (sheet->active_cell.row < 0 ||
2486 sheet->active_cell.col < 0) return;
2488 gtk_widget_hide (sheet->entry_widget);
2489 gtk_widget_unmap (sheet->entry_widget);
2491 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2495 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2497 gint old_row, old_col;
2499 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2501 if (row < 0 || col < 0)
2504 if ( row > psppire_axis_unit_count (sheet->vaxis)
2505 || col > psppire_axis_unit_count (sheet->haxis))
2508 old_row = sheet->active_cell.row;
2509 old_col = sheet->active_cell.col;
2511 entry_load_text (sheet);
2513 /* Erase the old cell border */
2514 psppire_sheet_draw_active_cell (sheet);
2516 sheet->active_cell.row = row;
2517 sheet->active_cell.col = col;
2519 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2521 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2523 psppire_sheet_draw_active_cell (sheet);
2524 psppire_sheet_show_entry_widget (sheet);
2526 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2528 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2529 row, col, old_row, old_col);
2534 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2536 GtkEntry *sheet_entry;
2537 PsppireSheetCellAttr attributes;
2541 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2543 row = sheet->active_cell.row;
2544 col = sheet->active_cell.col;
2546 /* Don't show the active cell, if there is no active cell: */
2547 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2550 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2551 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2552 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2554 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2556 sheet_entry = psppire_sheet_get_entry (sheet);
2558 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2560 if (GTK_IS_ENTRY (sheet_entry))
2562 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2563 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2566 text = g_strdup ("");
2568 if (strcmp (old_text, text) != 0)
2569 gtk_entry_set_text (sheet_entry, text);
2571 dispose_string (sheet, text);
2574 switch (attributes.justification)
2576 case GTK_JUSTIFY_RIGHT:
2577 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2579 case GTK_JUSTIFY_CENTER:
2580 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2582 case GTK_JUSTIFY_LEFT:
2584 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2590 psppire_sheet_size_allocate_entry (sheet);
2592 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2593 psppire_sheet_model_is_editable (sheet->model,
2595 gtk_widget_map (sheet->entry_widget);
2599 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2602 PsppireSheetRange range;
2604 row = sheet->active_cell.row;
2605 col = sheet->active_cell.col;
2607 if (row < 0 || col < 0) return FALSE;
2609 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2612 range.col0 = range.coli = col;
2613 range.row0 = range.rowi = row;
2615 psppire_sheet_draw_border (sheet, range);
2623 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
2627 rectangle_from_range (sheet, &new_range, &area);
2632 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
2634 area.x += sheet->cell_padding->left / 2;
2635 area.y += sheet->cell_padding->top / 2;
2636 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
2637 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
2639 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
2646 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
2651 /* Selection related functions */
2654 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
2657 sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
2659 sheet->range.col0 = sheet->range.coli = -1;
2660 sheet->range.row0 = sheet->range.rowi = row;
2662 rectangle_from_range (sheet, &sheet->range, &area);
2666 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2668 g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, row);
2672 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
2675 sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
2677 sheet->range.col0 = sheet->range.coli = column;
2678 sheet->range.row0 = sheet->range.rowi = -1;
2680 rectangle_from_range (sheet, &sheet->range, &area);
2684 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2686 g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, column);
2691 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
2694 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
2696 sheet->range = *range;
2698 rectangle_from_range (sheet, range, &area);
2701 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2706 psppire_sheet_unselect_range (PsppireSheet *sheet)
2708 sheet->select_status = PSPPIRE_SHEET_NORMAL;
2710 if (sheet->sheet_window != NULL)
2714 rectangle_from_range (sheet, &sheet->range, &area);
2717 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2720 g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, -1);
2721 g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, -1);
2725 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
2727 g_return_if_fail (sheet != NULL);
2728 *range = sheet->range;
2733 psppire_sheet_expose (GtkWidget *widget, GdkEventExpose *event)
2735 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2737 g_return_val_if_fail (event != NULL, FALSE);
2739 if (!GTK_WIDGET_DRAWABLE (widget))
2742 /* exposure events on the sheet */
2743 if (event->window == sheet->row_title_window &&
2744 sheet->row_titles_visible)
2746 draw_row_title_buttons_range (sheet,
2747 min_visible_row (sheet),
2748 max_visible_row (sheet));
2751 if (event->window == sheet->column_title_window &&
2752 sheet->column_titles_visible)
2754 draw_column_title_buttons_range (sheet,
2755 min_visible_column (sheet),
2756 max_visible_column (sheet));
2759 if (event->window == sheet->sheet_window)
2761 draw_sheet_region (sheet, event->region);
2763 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2767 rectangle_from_range (sheet, &sheet->range, &area);
2769 gdk_draw_rectangle (sheet->sheet_window,
2772 area.x + 1, area.y + 1,
2773 area.width, area.height);
2777 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
2780 PsppireSheetRange range;
2781 range.row0 = range.rowi = sheet->active_cell.row;
2782 range.col0 = range.coli = sheet->active_cell.col;
2784 rectangle_from_range (sheet, &range, &rect);
2786 if (GDK_OVERLAP_RECTANGLE_OUT !=
2787 gdk_region_rect_in (event->region, &rect))
2789 psppire_sheet_draw_active_cell (sheet);
2795 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2802 psppire_sheet_button_press (GtkWidget *widget, GdkEventButton *event)
2804 PsppireSheet *sheet;
2805 GdkModifierType mods;
2810 g_return_val_if_fail (widget != NULL, FALSE);
2811 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
2812 g_return_val_if_fail (event != NULL, FALSE);
2814 sheet = PSPPIRE_SHEET (widget);
2816 /* Cancel any pending tooltips */
2817 if (sheet->motion_timer)
2819 g_source_remove (sheet->motion_timer);
2820 sheet->motion_timer = 0;
2823 gtk_widget_get_pointer (widget, &x, &y);
2824 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2827 if (event->window == sheet->column_title_window)
2829 sheet->x_drag = event->x;
2830 g_signal_emit (sheet,
2831 sheet_signals[BUTTON_EVENT_COLUMN], 0,
2834 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2836 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2837 g_signal_emit (sheet,
2838 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
2842 if (event->window == sheet->row_title_window)
2844 g_signal_emit (sheet,
2845 sheet_signals[BUTTON_EVENT_ROW], 0,
2848 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2850 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2851 g_signal_emit (sheet,
2852 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
2856 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
2858 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
2861 /* press on resize windows */
2862 if (event->window == sheet->column_title_window)
2864 sheet->x_drag = event->x;
2866 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
2868 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
2869 gdk_pointer_grab (sheet->column_title_window, FALSE,
2870 GDK_POINTER_MOTION_HINT_MASK |
2871 GDK_BUTTON1_MOTION_MASK |
2872 GDK_BUTTON_RELEASE_MASK,
2873 NULL, NULL, event->time);
2875 draw_xor_vline (sheet);
2880 if (event->window == sheet->row_title_window)
2882 sheet->y_drag = event->y;
2884 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
2886 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
2887 gdk_pointer_grab (sheet->row_title_window, FALSE,
2888 GDK_POINTER_MOTION_HINT_MASK |
2889 GDK_BUTTON1_MOTION_MASK |
2890 GDK_BUTTON_RELEASE_MASK,
2891 NULL, NULL, event->time);
2893 draw_xor_hline (sheet);
2898 /* the sheet itself does not handle other than single click events */
2899 if (event->type != GDK_BUTTON_PRESS) return FALSE;
2901 /* selections on the sheet */
2902 if (event->window == sheet->sheet_window)
2904 gtk_widget_get_pointer (widget, &x, &y);
2905 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2906 gdk_pointer_grab (sheet->sheet_window, FALSE,
2907 GDK_POINTER_MOTION_HINT_MASK |
2908 GDK_BUTTON1_MOTION_MASK |
2909 GDK_BUTTON_RELEASE_MASK,
2910 NULL, NULL, event->time);
2911 gtk_grab_add (GTK_WIDGET (sheet));
2913 if ( sheet->select_status == PSPPIRE_SHEET_NORMAL)
2915 sheet->range.row0 = row;
2916 sheet->range.col0 = column;
2920 psppire_sheet_unselect_range (sheet);
2922 psppire_sheet_click_cell (sheet, row, column);
2925 if (event->window == sheet->column_title_window)
2927 gtk_widget_get_pointer (widget, &x, &y);
2928 if ( sheet->row_titles_visible)
2929 x -= sheet->row_title_area.width;
2931 x += sheet->hadjustment->value;
2933 column = column_from_xpixel (sheet, x);
2935 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2937 gtk_grab_add (GTK_WIDGET (sheet));
2938 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2942 if (event->window == sheet->row_title_window)
2944 gtk_widget_get_pointer (widget, &x, &y);
2945 if ( sheet->column_titles_visible)
2946 y -= sheet->column_title_area.height;
2948 y += sheet->vadjustment->value;
2950 row = row_from_ypixel (sheet, y);
2951 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2953 gtk_grab_add (GTK_WIDGET (sheet));
2954 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2962 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
2964 PsppireSheetCell cell;
2965 gboolean forbid_move;
2970 if (row >= psppire_axis_unit_count (sheet->vaxis)
2971 || column >= psppire_axis_unit_count (sheet->haxis))
2976 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
2977 &sheet->active_cell,
2983 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
2986 row = sheet->active_cell.row;
2987 column = sheet->active_cell.col;
2989 change_active_cell (sheet, row, column);
2993 if (row == -1 && column >= 0)
2995 psppire_sheet_select_column (sheet, column);
2999 if (column == -1 && row >= 0)
3001 psppire_sheet_select_row (sheet, row);
3005 if (row == -1 && column == -1)
3007 sheet->range.row0 = 0;
3008 sheet->range.col0 = 0;
3009 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3010 sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
3014 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3015 change_active_cell (sheet, row, column);
3017 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3023 psppire_sheet_button_release (GtkWidget *widget,
3024 GdkEventButton *event)
3026 GdkDisplay *display = gtk_widget_get_display (widget);
3028 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3030 /* release on resize windows */
3031 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3034 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3035 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3037 gdk_display_pointer_ungrab (display, event->time);
3038 draw_xor_vline (sheet);
3041 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3042 + sheet->hadjustment->value;
3044 set_column_width (sheet, sheet->drag_cell.col, width);
3049 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3052 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3053 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3055 gdk_display_pointer_ungrab (display, event->time);
3056 draw_xor_hline (sheet);
3059 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3060 sheet->vadjustment->value;
3062 set_row_height (sheet, sheet->drag_cell.row, height);
3067 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3069 PsppireSheetRange old_range;
3070 draw_xor_rectangle (sheet, sheet->drag_range);
3071 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3072 gdk_display_pointer_ungrab (display, event->time);
3074 psppire_sheet_unselect_range (sheet);
3076 old_range = sheet->range;
3077 sheet->range = sheet->drag_range;
3078 sheet->drag_range = old_range;
3079 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3080 &sheet->drag_range, &sheet->range);
3081 psppire_sheet_select_range (sheet, &sheet->range);
3084 if (PSPPIRE_SHEET_IN_SELECTION (sheet))
3086 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3087 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3089 change_active_cell (sheet, sheet->active_cell.row,
3090 sheet->active_cell.col);
3093 gdk_display_pointer_ungrab (display, event->time);
3094 gtk_grab_remove (GTK_WIDGET (sheet));
3096 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3105 /* Shamelessly lifted from gtktooltips */
3107 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3111 gtk_widget_size_request (tip_window, &req);
3112 gtk_paint_flat_box (tip_window->style, tip_window->window,
3113 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3114 NULL, GTK_WIDGET(tip_window), "tooltip",
3115 0, 0, req.width, req.height);
3121 destroy_hover_window (PsppireSheetHoverTitle *h)
3123 gtk_widget_destroy (h->window);
3127 static PsppireSheetHoverTitle *
3128 create_hover_window (void)
3130 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3132 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3134 #if GTK_CHECK_VERSION (2, 9, 0)
3135 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3136 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3139 gtk_widget_set_app_paintable (hw->window, TRUE);
3140 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3141 gtk_widget_set_name (hw->window, "gtk-tooltips");
3142 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3144 g_signal_connect (hw->window,
3146 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3149 hw->label = gtk_label_new (NULL);
3152 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3153 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3155 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3157 gtk_widget_show (hw->label);
3159 g_signal_connect (hw->window,
3161 G_CALLBACK (gtk_widget_destroyed),
3167 #define HOVER_WINDOW_Y_OFFSET 2
3170 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3171 const gchar *subtitle)
3180 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3184 sheet->hover_window->row = row;
3185 sheet->hover_window->column = column;
3187 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3189 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3191 gtk_widget_show (sheet->hover_window->window);
3193 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3199 y += sheet->column_title_area.y;
3200 y += sheet->column_title_area.height;
3201 y += HOVER_WINDOW_Y_OFFSET;
3207 x += sheet->row_title_area.x;
3208 x += sheet->row_title_area.width * 2 / 3.0;
3211 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3216 motion_timeout_callback (gpointer data)
3218 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3222 gdk_threads_enter ();
3223 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3225 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3227 if (sheet->row_title_under && row >= 0)
3229 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3231 show_subtitle (sheet, row, -1, text);
3235 if (sheet->column_title_under && column >= 0)
3237 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3240 show_subtitle (sheet, -1, column, text);
3246 gdk_threads_leave ();
3251 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3253 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3254 GdkModifierType mods;
3255 GdkCursorType new_cursor;
3258 GdkDisplay *display;
3260 g_return_val_if_fail (event != NULL, FALSE);
3262 display = gtk_widget_get_display (widget);
3264 /* selections on the sheet */
3268 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3270 if ( sheet->motion_timer > 0 )
3271 g_source_remove (sheet->motion_timer);
3272 sheet->motion_timer =
3273 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3279 gtk_widget_get_pointer (widget, &wx, &wy);
3281 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3283 if ( row != sheet->hover_window->row ||
3284 column != sheet->hover_window->column)
3286 gtk_widget_hide (sheet->hover_window->window);
3291 if (event->window == sheet->column_title_window)
3293 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3294 on_column_boundary (sheet, x, &column))
3296 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3297 if (new_cursor != sheet->cursor_drag->type)
3299 gdk_cursor_unref (sheet->cursor_drag);
3300 sheet->cursor_drag =
3301 gdk_cursor_new_for_display (display, new_cursor);
3303 gdk_window_set_cursor (sheet->column_title_window,
3304 sheet->cursor_drag);
3309 new_cursor = GDK_TOP_LEFT_ARROW;
3310 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3311 new_cursor != sheet->cursor_drag->type)
3313 gdk_cursor_unref (sheet->cursor_drag);
3314 sheet->cursor_drag =
3315 gdk_cursor_new_for_display (display, new_cursor);
3316 gdk_window_set_cursor (sheet->column_title_window,
3317 sheet->cursor_drag);
3321 else if (event->window == sheet->row_title_window)
3323 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3324 on_row_boundary (sheet, y, &row))
3326 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3327 if (new_cursor != sheet->cursor_drag->type)
3329 gdk_cursor_unref (sheet->cursor_drag);
3330 sheet->cursor_drag =
3331 gdk_cursor_new_for_display (display, new_cursor);
3332 gdk_window_set_cursor (sheet->row_title_window,
3333 sheet->cursor_drag);
3338 new_cursor = GDK_TOP_LEFT_ARROW;
3339 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3340 new_cursor != sheet->cursor_drag->type)
3342 gdk_cursor_unref (sheet->cursor_drag);
3343 sheet->cursor_drag =
3344 gdk_cursor_new_for_display (display, new_cursor);
3345 gdk_window_set_cursor (sheet->row_title_window,
3346 sheet->cursor_drag);
3351 new_cursor = GDK_PLUS;
3352 if ( event->window == sheet->sheet_window &&
3353 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3354 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3355 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3356 new_cursor != sheet->cursor_drag->type)
3358 gdk_cursor_unref (sheet->cursor_drag);
3359 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3360 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3363 new_cursor = GDK_TOP_LEFT_ARROW;
3364 if ( event->window == sheet->sheet_window &&
3365 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ) &&
3366 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3367 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
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_TOP_LEFT_ARROW);
3372 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3375 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3376 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3378 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3380 if (event->x != sheet->x_drag)
3382 draw_xor_vline (sheet);
3383 sheet->x_drag = event->x;
3384 draw_xor_vline (sheet);
3390 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3392 if (event->y != sheet->y_drag)
3394 draw_xor_hline (sheet);
3395 sheet->y_drag = event->y;
3396 draw_xor_hline (sheet);
3402 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3404 PsppireSheetRange aux;
3405 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3406 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3407 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3408 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3412 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3413 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3415 aux = sheet->drag_range;
3416 sheet->drag_range.row0 = sheet->range.row0 + row;
3417 sheet->drag_range.col0 = sheet->range.col0 + column;
3418 sheet->drag_range.rowi = sheet->range.rowi + row;
3419 sheet->drag_range.coli = sheet->range.coli + column;
3420 if (aux.row0 != sheet->drag_range.row0 ||
3421 aux.col0 != sheet->drag_range.col0)
3423 draw_xor_rectangle (sheet, aux);
3424 draw_xor_rectangle (sheet, sheet->drag_range);
3430 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3432 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
3433 column == sheet->active_cell.col) return TRUE;
3435 if ( mods & GDK_BUTTON1_MASK)
3437 if (PSPPIRE_SHEET_IN_SELECTION (sheet) )
3439 /* Redraw the old range */
3440 psppire_sheet_unselect_range (sheet);
3442 sheet->range.rowi = row;
3443 sheet->range.coli = column;
3445 /* Redraw the new range */
3446 psppire_sheet_select_range (sheet, &sheet->range);
3450 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3458 psppire_sheet_crossing_notify (GtkWidget *widget,
3459 GdkEventCrossing *event)
3461 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3463 if (event->window == sheet->column_title_window)
3464 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
3465 else if (event->window == sheet->row_title_window)
3466 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
3468 if (event->type == GDK_LEAVE_NOTIFY)
3469 gtk_widget_hide (sheet->hover_window->window);
3476 psppire_sheet_focus_in (GtkWidget *w,
3477 GdkEventFocus *event)
3479 PsppireSheet *sheet = PSPPIRE_SHEET (w);
3481 gtk_widget_grab_focus (sheet->entry_widget);
3489 psppire_sheet_entry_key_press (GtkWidget *widget,
3493 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
3498 /* Number of rows in a step-increment */
3499 #define ROWS_PER_STEP 1
3503 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
3505 gint old_row = sheet->active_cell.row ;
3506 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
3510 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
3511 min_visible_row (sheet));
3515 case GTK_SCROLL_PAGE_DOWN:
3516 gtk_adjustment_set_value (sheet->vadjustment,
3517 sheet->vadjustment->value +
3518 sheet->vadjustment->page_increment);
3520 case GTK_SCROLL_PAGE_UP:
3521 gtk_adjustment_set_value (sheet->vadjustment,
3522 sheet->vadjustment->value -
3523 sheet->vadjustment->page_increment);
3527 g_assert_not_reached ();
3532 vpixel += psppire_axis_start_pixel (sheet->vaxis,
3533 min_visible_row (sheet));
3535 new_row = row_from_ypixel (sheet, vpixel);
3537 change_active_cell (sheet, new_row,
3538 sheet->active_cell.col);
3543 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
3545 gint current_row = sheet->active_cell.row;
3546 gint current_col = sheet->active_cell.col;
3547 PsppireSheetCell new_cell ;
3548 gboolean forbidden = FALSE;
3550 new_cell.row = current_row;
3551 new_cell.col = current_col;
3555 case GTK_SCROLL_STEP_DOWN:
3558 case GTK_SCROLL_STEP_UP:
3561 case GTK_SCROLL_STEP_RIGHT:
3564 case GTK_SCROLL_STEP_LEFT:
3567 case GTK_SCROLL_STEP_FORWARD:
3570 psppire_sheet_model_get_column_count (sheet->model))
3576 case GTK_SCROLL_STEP_BACKWARD:
3578 if (new_cell.col < 0)
3581 psppire_sheet_model_get_column_count (sheet->model) - 1;
3586 g_assert_not_reached ();
3590 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3591 &sheet->active_cell,
3599 maximize_int (&new_cell.row, 0);
3600 maximize_int (&new_cell.col, 0);
3602 minimize_int (&new_cell.row,
3603 psppire_axis_unit_count (sheet->vaxis) - 1);
3605 minimize_int (&new_cell.col,
3606 psppire_axis_unit_count (sheet->haxis) - 1);
3608 change_active_cell (sheet, new_cell.row, new_cell.col);
3611 if ( new_cell.col > max_fully_visible_column (sheet))
3614 psppire_axis_start_pixel (sheet->haxis,
3616 hpos -= sheet->hadjustment->page_size;
3618 gtk_adjustment_set_value (sheet->hadjustment,
3621 else if ( new_cell.col < min_fully_visible_column (sheet))
3624 psppire_axis_start_pixel (sheet->haxis,
3627 gtk_adjustment_set_value (sheet->hadjustment,
3632 if ( new_cell.row > max_fully_visible_row (sheet))
3635 psppire_axis_start_pixel (sheet->vaxis,
3637 vpos -= sheet->vadjustment->page_size;
3639 gtk_adjustment_set_value (sheet->vadjustment,
3642 else if ( new_cell.row < min_fully_visible_row (sheet))
3645 psppire_axis_start_pixel (sheet->vaxis,
3648 gtk_adjustment_set_value (sheet->vadjustment,
3652 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3656 /* Move to row 0 keeping the current column */
3658 row0 (PsppireSheet *sheet)
3660 gtk_adjustment_set_value (sheet->vadjustment,
3661 sheet->vadjustment->lower);
3663 change_active_cell (sheet, 0, sheet->active_cell.col);
3666 /* Move to column 0 keeping the current row */
3668 col0 (PsppireSheet *sheet)
3670 gtk_adjustment_set_value (sheet->hadjustment,
3671 sheet->hadjustment->lower);
3673 change_active_cell (sheet, sheet->active_cell.row, 0);
3676 /* Move to last column keeping the current row */
3678 col_last (PsppireSheet *sheet)
3680 glong last_col = psppire_axis_unit_count (sheet->haxis) - 1;
3682 gtk_adjustment_set_value (sheet->hadjustment,
3683 sheet->hadjustment->upper - sheet->hadjustment->page_size);
3685 change_active_cell (sheet, sheet->active_cell.row,
3690 /* Move to last row keeping the current column */
3692 row_last (PsppireSheet *sheet)
3694 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
3696 gtk_adjustment_set_value (sheet->vadjustment,
3697 sheet->vadjustment->upper- sheet->vadjustment->page_size);
3699 change_active_cell (sheet,
3701 sheet->active_cell.col);
3707 psppire_sheet_key_press (GtkWidget *widget,
3710 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3712 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3714 switch (key->keyval)
3717 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
3721 if (key->state & GDK_CONTROL_MASK)
3724 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
3727 case GDK_ISO_Left_Tab:
3728 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
3732 if (key->state & GDK_CONTROL_MASK)
3735 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
3739 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3743 if (key->state & GDK_CONTROL_MASK)
3746 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3750 if (key->state & GDK_CONTROL_MASK)
3753 step_sheet (sheet, GTK_SCROLL_STEP_UP);
3757 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
3760 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
3772 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
3784 psppire_sheet_size_request (GtkWidget *widget,
3785 GtkRequisition *requisition)
3787 PsppireSheet *sheet;
3789 g_return_if_fail (widget != NULL);
3790 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3791 g_return_if_fail (requisition != NULL);
3793 sheet = PSPPIRE_SHEET (widget);
3795 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
3796 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
3798 /* compute the size of the column title area */
3799 if (sheet->column_titles_visible)
3800 requisition->height += sheet->column_title_area.height;
3802 /* compute the size of the row title area */
3803 if (sheet->row_titles_visible)
3804 requisition->width += sheet->row_title_area.width;
3809 psppire_sheet_size_allocate (GtkWidget *widget,
3810 GtkAllocation *allocation)
3812 PsppireSheet *sheet;
3813 GtkAllocation sheet_allocation;
3816 g_return_if_fail (widget != NULL);
3817 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3818 g_return_if_fail (allocation != NULL);
3820 sheet = PSPPIRE_SHEET (widget);
3821 widget->allocation = *allocation;
3822 border_width = GTK_CONTAINER (widget)->border_width;
3824 if (GTK_WIDGET_REALIZED (widget))
3825 gdk_window_move_resize (widget->window,
3826 allocation->x + border_width,
3827 allocation->y + border_width,
3828 allocation->width - 2 * border_width,
3829 allocation->height - 2 * border_width);
3831 sheet_allocation.x = 0;
3832 sheet_allocation.y = 0;
3833 sheet_allocation.width = allocation->width - 2 * border_width;
3834 sheet_allocation.height = allocation->height - 2 * border_width;
3836 if (GTK_WIDGET_REALIZED (widget))
3837 gdk_window_move_resize (sheet->sheet_window,
3840 sheet_allocation.width,
3841 sheet_allocation.height);
3843 /* position the window which holds the column title buttons */
3844 sheet->column_title_area.x = 0;
3845 sheet->column_title_area.y = 0;
3846 sheet->column_title_area.width = sheet_allocation.width ;
3849 /* position the window which holds the row title buttons */
3850 sheet->row_title_area.x = 0;
3851 sheet->row_title_area.y = 0;
3852 sheet->row_title_area.height = sheet_allocation.height;
3854 if (sheet->row_titles_visible)
3855 sheet->column_title_area.x += sheet->row_title_area.width;
3857 if (sheet->column_titles_visible)
3858 sheet->row_title_area.y += sheet->column_title_area.height;
3860 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
3861 gdk_window_move_resize (sheet->column_title_window,
3862 sheet->column_title_area.x,
3863 sheet->column_title_area.y,
3864 sheet->column_title_area.width,
3865 sheet->column_title_area.height);
3868 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
3869 gdk_window_move_resize (sheet->row_title_window,
3870 sheet->row_title_area.x,
3871 sheet->row_title_area.y,
3872 sheet->row_title_area.width,
3873 sheet->row_title_area.height);
3875 size_allocate_global_button (sheet);
3879 gint width = sheet->column_title_area.width;
3881 if ( sheet->row_titles_visible)
3882 width -= sheet->row_title_area.width;
3884 g_object_set (sheet->haxis,
3885 "minimum-extent", width,
3892 gint height = sheet->row_title_area.height;
3894 if ( sheet->column_titles_visible)
3895 height -= sheet->column_title_area.height;
3897 g_object_set (sheet->vaxis,
3898 "minimum-extent", height,
3903 /* set the scrollbars adjustments */
3904 adjust_scrollbars (sheet);
3908 draw_column_title_buttons (PsppireSheet *sheet)
3912 if (!sheet->column_titles_visible) return;
3913 if (!GTK_WIDGET_REALIZED (sheet))
3916 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
3919 if (sheet->row_titles_visible)
3921 x = sheet->row_title_area.width;
3924 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
3926 sheet->column_title_area.width = width;
3927 sheet->column_title_area.x = x;
3928 gdk_window_move_resize (sheet->column_title_window,
3929 sheet->column_title_area.x,
3930 sheet->column_title_area.y,
3931 sheet->column_title_area.width,
3932 sheet->column_title_area.height);
3935 if (max_visible_column (sheet) ==
3936 psppire_axis_unit_count (sheet->haxis) - 1)
3937 gdk_window_clear_area (sheet->column_title_window,
3939 sheet->column_title_area.width,
3940 sheet->column_title_area.height);
3942 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
3944 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
3945 max_visible_column (sheet));
3949 draw_row_title_buttons (PsppireSheet *sheet)
3954 if (!sheet->row_titles_visible) return;
3955 if (!GTK_WIDGET_REALIZED (sheet))
3958 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
3960 if (sheet->column_titles_visible)
3962 y = sheet->column_title_area.height;
3965 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
3967 sheet->row_title_area.y = y;
3968 sheet->row_title_area.height = height;
3969 gdk_window_move_resize (sheet->row_title_window,
3970 sheet->row_title_area.x,
3971 sheet->row_title_area.y,
3972 sheet->row_title_area.width,
3973 sheet->row_title_area.height);
3976 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
3977 gdk_window_clear_area (sheet->row_title_window,
3979 sheet->row_title_area.width,
3980 sheet->row_title_area.height);
3982 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
3984 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
3985 max_visible_row (sheet));
3990 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
3992 GtkAllocation entry_alloc;
3993 PsppireSheetCellAttr attributes = { 0 };
3994 GtkEntry *sheet_entry;
3996 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3997 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
3999 sheet_entry = psppire_sheet_get_entry (sheet);
4001 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4002 sheet->active_cell.col,
4006 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4008 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4010 style->bg[GTK_STATE_NORMAL] = attributes.background;
4011 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4012 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4013 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4014 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4015 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4018 rectangle_from_cell (sheet, sheet->active_cell.row,
4019 sheet->active_cell.col, &entry_alloc);
4021 entry_alloc.x += sheet->cell_padding->left;
4022 entry_alloc.y += sheet->cell_padding->right;
4023 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4024 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4027 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4028 entry_alloc.height);
4029 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4033 /* Copy the sheet's font to the entry widget */
4035 set_entry_widget_font (PsppireSheet *sheet)
4037 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4039 pango_font_description_free (style->font_desc);
4040 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4042 gtk_widget_modify_style (sheet->entry_widget, style);
4046 create_sheet_entry (PsppireSheet *sheet)
4048 if (sheet->entry_widget)
4050 gtk_widget_unparent (sheet->entry_widget);
4053 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4054 g_object_ref_sink (sheet->entry_widget);
4056 gtk_widget_size_request (sheet->entry_widget, NULL);
4058 if ( GTK_IS_ENTRY (sheet->entry_widget))
4060 g_object_set (sheet->entry_widget,
4065 if (GTK_WIDGET_REALIZED (sheet))
4067 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4068 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4069 gtk_widget_realize (sheet->entry_widget);
4072 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4073 G_CALLBACK (psppire_sheet_entry_key_press),
4076 set_entry_widget_font (sheet);
4078 gtk_widget_show (sheet->entry_widget);
4082 /* Finds the last child widget that happens to be of type GtkEntry */
4084 find_entry (GtkWidget *w, gpointer user_data)
4086 GtkWidget **entry = user_data;
4087 if ( GTK_IS_ENTRY (w))
4095 psppire_sheet_get_entry (PsppireSheet *sheet)
4097 GtkWidget *w = sheet->entry_widget;
4099 g_return_val_if_fail (sheet != NULL, NULL);
4100 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4101 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4103 while (! GTK_IS_ENTRY (w))
4105 GtkWidget *entry = NULL;
4107 if (GTK_IS_CONTAINER (w))
4109 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4118 return GTK_ENTRY (w);
4123 draw_button (PsppireSheet *sheet, GdkWindow *window,
4124 PsppireSheetButton *button, gboolean is_sensitive,
4125 GdkRectangle allocation)
4127 GtkShadowType shadow_type;
4128 gint text_width = 0;
4129 PangoAlignment align = PANGO_ALIGN_LEFT;
4135 g_return_if_fail (sheet != NULL);
4136 g_return_if_fail (button != NULL);
4139 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4141 gdk_window_clear_area (window,
4142 allocation.x, allocation.y,
4143 allocation.width, allocation.height);
4145 gtk_widget_ensure_style (sheet->button);
4147 gtk_paint_box (sheet->button->style, window,
4148 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4150 GTK_WIDGET (sheet->button),
4152 allocation.x, allocation.y,
4153 allocation.width, allocation.height);
4155 state = button->state;
4156 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4158 if (state == GTK_STATE_ACTIVE)
4159 shadow_type = GTK_SHADOW_IN;
4161 shadow_type = GTK_SHADOW_OUT;
4163 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4164 gtk_paint_box (sheet->button->style, window,
4165 button->state, shadow_type,
4166 &allocation, GTK_WIDGET (sheet->button),
4168 allocation.x, allocation.y,
4169 allocation.width, allocation.height);
4171 if ( button->overstruck)
4173 GdkPoint points[2] = {
4174 {allocation.x, allocation.y},
4175 {allocation.x + allocation.width,
4176 allocation.y + allocation.height}
4179 gtk_paint_polygon (sheet->button->style,
4191 if (button->label_visible)
4193 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4195 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4198 allocation.y += 2 * sheet->button->style->ythickness;
4200 if (button->label && strlen (button->label) > 0)
4202 PangoRectangle rect;
4203 gchar *line = button->label;
4205 PangoLayout *layout = NULL;
4206 gint real_x = allocation.x;
4207 gint real_y = allocation.y;
4209 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4210 pango_layout_get_extents (layout, NULL, &rect);
4212 text_width = PANGO_PIXELS (rect.width);
4213 switch (button->justification)
4215 case GTK_JUSTIFY_LEFT:
4216 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4217 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4219 case GTK_JUSTIFY_RIGHT:
4220 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4221 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4223 case GTK_JUSTIFY_CENTER:
4225 real_x = allocation.x + (allocation.width - text_width)/2;
4226 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4227 pango_layout_set_justify (layout, TRUE);
4229 pango_layout_set_alignment (layout, align);
4230 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4239 g_object_unref (layout);
4242 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4244 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4248 psppire_sheet_button_free (button);
4252 /* Draw the column title buttons FIRST through to LAST */
4254 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4258 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4260 if (!sheet->column_titles_visible) return;
4262 g_return_if_fail (first >= min_visible_column (sheet));
4263 g_return_if_fail (last <= max_visible_column (sheet));
4266 rect.height = sheet->column_title_area.height;
4267 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4268 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4269 + psppire_axis_unit_size (sheet->haxis, last);
4271 rect.x -= sheet->hadjustment->value;
4273 minimize_int (&rect.width, sheet->column_title_area.width);
4274 maximize_int (&rect.x, 0);
4276 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4278 for (col = first ; col <= last ; ++col)
4280 GdkRectangle allocation;
4281 gboolean is_sensitive = FALSE;
4283 PsppireSheetButton *
4284 button = psppire_sheet_model_get_column_button (sheet->model, col);
4286 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4288 allocation.x -= sheet->hadjustment->value;
4290 allocation.height = sheet->column_title_area.height;
4291 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4292 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4294 draw_button (sheet, sheet->column_title_window,
4295 button, is_sensitive, allocation);
4298 gdk_window_end_paint (sheet->column_title_window);
4303 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4307 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4309 if (!sheet->row_titles_visible) return;
4311 g_return_if_fail (first >= min_visible_row (sheet));
4312 g_return_if_fail (last <= max_visible_row (sheet));
4315 rect.width = sheet->row_title_area.width;
4316 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4317 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4318 + psppire_axis_unit_size (sheet->vaxis, last);
4320 rect.y -= sheet->vadjustment->value;
4322 minimize_int (&rect.height, sheet->row_title_area.height);
4323 maximize_int (&rect.y, 0);
4325 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4326 for (row = first; row <= last; ++row)
4328 GdkRectangle allocation;
4330 gboolean is_sensitive = FALSE;
4332 PsppireSheetButton *button =
4333 psppire_sheet_model_get_row_button (sheet->model, row);
4335 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4337 allocation.y -= sheet->vadjustment->value;
4339 allocation.width = sheet->row_title_area.width;
4340 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4341 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4343 draw_button (sheet, sheet->row_title_window,
4344 button, is_sensitive, allocation);
4347 gdk_window_end_paint (sheet->row_title_window);
4354 * vadjustment_value_changed
4355 * hadjustment_value_changed */
4359 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4362 (adj->value + adj->page_size)
4364 (adj->upper - adj->lower);
4366 const glong last_item = psppire_axis_unit_count (axis) - 1;
4368 if (isnan (position) || position < 0)
4372 psppire_axis_start_pixel (axis, last_item)
4374 psppire_axis_unit_size (axis, last_item)
4378 adj->page_size = page_size;
4381 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4383 if ( adj->value < adj->lower)
4384 adj->value = adj->lower;
4387 gtk_adjustment_changed (adj);
4392 adjust_scrollbars (PsppireSheet *sheet)
4396 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4399 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4401 if ( sheet->row_titles_visible)
4402 width -= sheet->row_title_area.width;
4404 if (sheet->column_titles_visible)
4405 height -= sheet->column_title_area.height;
4407 if (sheet->vadjustment)
4409 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4411 sheet->vadjustment->step_increment =
4413 psppire_axis_unit_size (sheet->vaxis, last_row);
4415 sheet->vadjustment->page_increment =
4417 sheet->column_title_area.height -
4418 psppire_axis_unit_size (sheet->vaxis, last_row);
4420 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4423 if (sheet->hadjustment)
4425 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4426 sheet->hadjustment->step_increment = 1;
4428 sheet->hadjustment->page_increment = width;
4430 sheet->hadjustment->upper =
4431 psppire_axis_start_pixel (sheet->haxis, last_col)
4433 psppire_axis_unit_size (sheet->haxis, last_col)
4436 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4440 /* Subtracts the region of WIDGET from REGION */
4442 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4445 GdkRectangle intersect;
4448 gdk_region_get_clipbox (region, &rect);
4449 gtk_widget_intersect (widget,
4453 region2 = gdk_region_rectangle (&intersect);
4454 gdk_region_subtract (region, region2);
4455 gdk_region_destroy (region2);
4459 vadjustment_value_changed (GtkAdjustment *adjustment,
4463 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4465 g_return_if_fail (adjustment != NULL);
4467 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4469 gtk_widget_hide (sheet->entry_widget);
4472 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4474 subtract_widget_region (region, sheet->button);
4475 gdk_window_begin_paint_region (sheet->sheet_window, region);
4477 draw_sheet_region (sheet, region);
4479 draw_row_title_buttons (sheet);
4480 psppire_sheet_draw_active_cell (sheet);
4482 gdk_window_end_paint (sheet->sheet_window);
4483 gdk_region_destroy (region);
4488 hadjustment_value_changed (GtkAdjustment *adjustment,
4492 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4494 g_return_if_fail (adjustment != NULL);
4496 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4498 gtk_widget_hide (sheet->entry_widget);
4502 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4504 subtract_widget_region (region, sheet->button);
4505 gdk_window_begin_paint_region (sheet->sheet_window, region);
4507 draw_sheet_region (sheet, region);
4509 draw_column_title_buttons (sheet);
4511 psppire_sheet_draw_active_cell (sheet);
4513 gdk_window_end_paint (sheet->sheet_window);
4515 gdk_region_destroy (region);
4519 /* COLUMN RESIZING */
4521 draw_xor_vline (PsppireSheet *sheet)
4524 gint xpos = sheet->x_drag;
4525 gdk_drawable_get_size (sheet->sheet_window,
4528 if (sheet->row_titles_visible)
4529 xpos += sheet->row_title_area.width;
4531 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4533 sheet->column_title_area.height,
4535 height + CELL_SPACING);
4540 draw_xor_hline (PsppireSheet *sheet)
4544 gint ypos = sheet->y_drag;
4546 gdk_drawable_get_size (sheet->sheet_window,
4550 if (sheet->column_titles_visible)
4551 ypos += sheet->column_title_area.height;
4553 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4554 sheet->row_title_area.width,
4556 width + CELL_SPACING,
4560 /* SELECTED RANGE */
4562 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
4565 GdkRectangle clip_area, area;
4568 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
4569 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
4570 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
4571 psppire_axis_unit_size (sheet->haxis, range.coli);
4572 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
4573 psppire_axis_unit_size (sheet->vaxis, range.rowi);
4575 clip_area.x = sheet->row_title_area.width;
4576 clip_area.y = sheet->column_title_area.height;
4578 gdk_drawable_get_size (sheet->sheet_window,
4579 &clip_area.width, &clip_area.height);
4581 if (!sheet->row_titles_visible) clip_area.x = 0;
4582 if (!sheet->column_titles_visible) clip_area.y = 0;
4586 area.width = area.width + area.x;
4589 if (area.width > clip_area.width) area.width = clip_area.width + 10;
4592 area.height = area.height + area.y;
4595 if (area.height > clip_area.height) area.height = clip_area.height + 10;
4599 clip_area.width += 3;
4600 clip_area.height += 3;
4602 gdk_gc_get_values (sheet->xor_gc, &values);
4604 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
4606 gdk_draw_rectangle (sheet->sheet_window,
4609 area.x + i, area.y + i,
4610 area.width - 2 * i, area.height - 2 * i);
4613 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4615 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
4620 set_column_width (PsppireSheet *sheet,
4624 g_return_if_fail (sheet != NULL);
4625 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4627 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
4633 psppire_axis_resize (sheet->haxis, column,
4634 width - sheet->cell_padding->left -
4635 sheet->cell_padding->right);
4637 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4639 draw_column_title_buttons (sheet);
4640 adjust_scrollbars (sheet);
4641 psppire_sheet_size_allocate_entry (sheet);
4642 redraw_range (sheet, NULL);
4647 set_row_height (PsppireSheet *sheet,
4651 g_return_if_fail (sheet != NULL);
4652 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4654 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
4660 psppire_axis_resize (sheet->vaxis, row,
4661 height - sheet->cell_padding->top -
4662 sheet->cell_padding->bottom);
4664 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
4666 draw_row_title_buttons (sheet);
4667 adjust_scrollbars (sheet);
4668 psppire_sheet_size_allocate_entry (sheet);
4669 redraw_range (sheet, NULL);
4674 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
4675 PsppireSheetCellAttr *attr)
4678 const GtkJustification *j ;
4679 GdkColormap *colormap;
4681 g_return_val_if_fail (sheet != NULL, FALSE);
4682 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
4684 if (row < 0 || col < 0) return FALSE;
4686 attr->foreground = GTK_WIDGET (sheet)->style->black;
4687 attr->background = sheet->color[BG_COLOR];
4689 attr->border.width = 0;
4690 attr->border.line_style = GDK_LINE_SOLID;
4691 attr->border.cap_style = GDK_CAP_NOT_LAST;
4692 attr->border.join_style = GDK_JOIN_MITER;
4693 attr->border.mask = 0;
4694 attr->border.color = GTK_WIDGET (sheet)->style->black;
4696 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
4697 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
4700 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
4701 attr->foreground = *fg;
4704 bg = psppire_sheet_model_get_background (sheet->model, row, col);
4707 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
4708 attr->background = *bg;
4711 attr->justification =
4712 psppire_sheet_model_get_column_justification (sheet->model, col);
4714 j = psppire_sheet_model_get_justification (sheet->model, row, col);
4716 attr->justification = *j;
4722 psppire_sheet_forall (GtkContainer *container,
4723 gboolean include_internals,
4724 GtkCallback callback,
4725 gpointer callback_data)
4727 PsppireSheet *sheet = PSPPIRE_SHEET (container);
4729 g_return_if_fail (callback != NULL);
4731 if (sheet->button && sheet->button->parent)
4732 (* callback) (sheet->button, callback_data);
4734 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
4735 (* callback) (sheet->entry_widget, callback_data);
4740 psppire_sheet_get_model (const PsppireSheet *sheet)
4742 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4744 return sheet->model;
4748 PsppireSheetButton *
4749 psppire_sheet_button_new (void)
4751 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
4753 button->state = GTK_STATE_NORMAL;
4754 button->label = NULL;
4755 button->label_visible = TRUE;
4756 button->justification = GTK_JUSTIFY_FILL;
4757 button->overstruck = FALSE;
4764 psppire_sheet_button_free (PsppireSheetButton *button)
4766 if (!button) return ;
4768 g_free (button->label);
4773 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
4775 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
4777 if ( NULL == celltext)
4780 g_string_append (string, celltext);
4786 range_to_text (const PsppireSheet *sheet)
4791 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4794 string = g_string_sized_new (80);
4796 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4798 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
4800 append_cell_text (string, sheet, r, c);
4801 g_string_append (string, "\t");
4803 append_cell_text (string, sheet, r, c);
4804 if ( r < sheet->range.rowi)
4805 g_string_append (string, "\n");
4812 range_to_html (const PsppireSheet *sheet)
4817 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4820 string = g_string_sized_new (480);
4822 g_string_append (string, "<html>\n");
4823 g_string_append (string, "<body>\n");
4824 g_string_append (string, "<table>\n");
4825 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4827 g_string_append (string, "<tr>\n");
4828 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
4830 g_string_append (string, "<td>");
4831 append_cell_text (string, sheet, r, c);
4832 g_string_append (string, "</td>\n");
4834 g_string_append (string, "</tr>\n");
4836 g_string_append (string, "</table>\n");
4837 g_string_append (string, "</body>\n");
4838 g_string_append (string, "</html>\n");
4850 primary_get_cb (GtkClipboard *clipboard,
4851 GtkSelectionData *selection_data,
4855 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4856 GString *string = NULL;
4860 case SELECT_FMT_TEXT:
4861 string = range_to_text (sheet);
4863 case SELECT_FMT_HTML:
4864 string = range_to_html (sheet);
4867 g_assert_not_reached ();
4870 gtk_selection_data_set (selection_data, selection_data->target,
4872 (const guchar *) string->str, string->len);
4873 g_string_free (string, TRUE);
4877 primary_clear_cb (GtkClipboard *clipboard,
4880 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4881 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4884 psppire_sheet_unselect_range (sheet);
4888 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
4890 static const GtkTargetEntry targets[] = {
4891 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
4892 { "STRING", 0, SELECT_FMT_TEXT },
4893 { "TEXT", 0, SELECT_FMT_TEXT },
4894 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
4895 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
4896 { "text/plain", 0, SELECT_FMT_TEXT },
4897 { "text/html", 0, SELECT_FMT_HTML }
4900 GtkClipboard *clipboard;
4902 if (!GTK_WIDGET_REALIZED (sheet))
4905 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
4906 GDK_SELECTION_PRIMARY);
4908 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
4910 if (!gtk_clipboard_set_with_owner (clipboard, targets,
4911 G_N_ELEMENTS (targets),
4912 primary_get_cb, primary_clear_cb,
4914 primary_clear_cb (clipboard, sheet);
4918 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
4919 gtk_clipboard_clear (clipboard);