2 Copyright (C) 2006, 2008, 2009 Free Software Foundation
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 This file is derived from the gtksheet.c and extensively modified for the
19 requirements of PSPPIRE. The changes are copyright by the
20 Free Software Foundation. The copyright notice for the original work is
24 /* GtkSheet widget for Gtk+.
25 * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
27 * Based on GtkClist widget by Jay Painter, but major changes.
28 * Memory allocation routines inspired on SC (Spreadsheet Calculator)
30 * This library is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Lesser General Public
32 * License as published by the Free Software Foundation; either
33 * version 2.1 of the License, or (at your option) any later version.
35 * This library is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38 * Lesser General Public License for more details.
40 * You should have received a copy of the GNU Lesser General Public
41 * License along with this library; if not, write to the Free Software
42 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
46 * SECTION:psppiresheet
47 * @short_description: spreadsheet widget for gtk2
49 * PsppireSheet is a matrix widget for GTK+. It consists of an scrollable grid of
50 * cells where you can allocate text. Cell contents can be edited interactively
51 * through a specially designed entry, GtkItemEntry.
59 #include <gdk/gdkkeysyms.h>
60 #include <gtk/gtksignal.h>
61 #include <gtk/gtkbutton.h>
62 #include <gtk/gtkadjustment.h>
63 #include <gtk/gtktypeutils.h>
64 #include <gtk/gtkentry.h>
65 #include <gtk/gtkcontainer.h>
66 #include <pango/pango.h>
67 #include "psppire-sheet.h"
68 #include <ui/gui/psppire-marshal.h>
69 #include <ui/gui/sheet/psppire-sheetmodel.h>
70 #include <ui/gui/sheet/psppire-axis.h>
71 #include <libpspp/misc.h>
76 PSPPIRE_SHEET_IN_XDRAG = 1 << 1,
77 PSPPIRE_SHEET_IN_YDRAG = 1 << 2,
78 PSPPIRE_SHEET_IN_DRAG = 1 << 3,
80 /* This flag is set when the user is actually in the process
81 of making a selection - ie while the mouse button is
84 PSPPIRE_SHEET_IN_SELECTION = 1 << 4
87 #define PSPPIRE_SHEET_FLAGS(sheet) (PSPPIRE_SHEET (sheet)->flags)
88 #define PSPPIRE_SHEET_SET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) |= (flag))
89 #define PSPPIRE_SHEET_UNSET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) &= ~ (flag))
91 #define PSPPIRE_SHEET_IN_XDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_XDRAG)
92 #define PSPPIRE_SHEET_IN_YDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_YDRAG)
93 #define PSPPIRE_SHEET_IN_DRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_DRAG)
94 #define PSPPIRE_SHEET_IN_SELECTION(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_SELECTION)
96 #define CELL_SPACING 1
98 #define TIMEOUT_HOVER 300
99 #define COLUMN_MIN_WIDTH 10
100 #define COLUMN_TITLES_HEIGHT 4
101 #define DEFAULT_COLUMN_WIDTH 80
102 #define DEFAULT_ROW_HEIGHT 25
104 static void set_entry_widget_font (PsppireSheet *sheet);
106 static void psppire_sheet_update_primary_selection (PsppireSheet *sheet);
107 static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
108 static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
109 static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range);
112 static void set_row_height (PsppireSheet *sheet,
116 static void destroy_hover_window (PsppireSheetHoverTitle *);
117 static PsppireSheetHoverTitle *create_hover_window (void);
120 dispose_string (const PsppireSheet *sheet, gchar *text)
122 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
127 if (psppire_sheet_model_free_strings (model))
132 /* FIXME: Why bother with these two ? */
134 /* returns the column index from a pixel location */
136 column_from_xpixel (const PsppireSheet *sheet, gint pixel)
138 return psppire_axis_unit_at_pixel (sheet->haxis, pixel);
142 row_from_ypixel (const PsppireSheet *sheet, gint pixel)
144 return psppire_axis_unit_at_pixel (sheet->vaxis, pixel);
148 /* Return the lowest row number which is wholly or partially on
149 the visible range of the sheet */
151 min_visible_row (const PsppireSheet *sheet)
153 return row_from_ypixel (sheet, sheet->vadjustment->value);
157 min_fully_visible_row (const PsppireSheet *sheet)
159 glong row = min_visible_row (sheet);
161 if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value)
168 max_visible_row (const PsppireSheet *sheet)
170 return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size);
175 max_fully_visible_row (const PsppireSheet *sheet)
177 glong row = max_visible_row (sheet);
179 if ( psppire_axis_start_pixel (sheet->vaxis, row)
181 psppire_axis_unit_size (sheet->vaxis, row)
182 > sheet->vadjustment->value)
189 /* Returns the lowest column number which is wholly or partially
192 min_visible_column (const PsppireSheet *sheet)
194 return column_from_xpixel (sheet, sheet->hadjustment->value);
198 min_fully_visible_column (const PsppireSheet *sheet)
200 glong col = min_visible_column (sheet);
202 if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value)
209 /* Returns the highest column number which is wholly or partially
212 max_visible_column (const PsppireSheet *sheet)
214 return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size);
218 max_fully_visible_column (const PsppireSheet *sheet)
220 glong col = max_visible_column (sheet);
222 if ( psppire_axis_start_pixel (sheet->haxis, col)
224 psppire_axis_unit_size (sheet->haxis, col)
225 > sheet->hadjustment->value)
233 /* The size of the region (in pixels) around the row/column boundaries
234 where the height/width may be grabbed to change size */
238 on_column_boundary (const PsppireSheet *sheet, gint x, gint *column)
243 x += sheet->hadjustment->value;
248 col = column_from_xpixel (sheet, x);
250 pixel = x - DRAG_WIDTH / 2;
254 if ( column_from_xpixel (sheet, pixel) < col )
260 if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col )
270 on_row_boundary (const PsppireSheet *sheet, gint y, gint *row)
275 y += sheet->vadjustment->value;
280 r = row_from_ypixel (sheet, y);
282 pixel = y - DRAG_WIDTH / 2;
286 if ( row_from_ypixel (sheet, pixel) < r )
292 if ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r )
302 static inline gboolean
303 POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y,
304 gint *drag_row, gint *drag_column)
308 /* Can't drag if nothing is selected */
309 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
310 sheet->range.col0 < 0 || sheet->range.coli < 0 )
313 *drag_column = column_from_xpixel (sheet, x);
314 *drag_row = row_from_ypixel (sheet, y);
316 if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 &&
317 x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
318 psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2)
320 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0);
321 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
323 *drag_row = sheet->range.row0;
326 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
327 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
328 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
330 *drag_row = sheet->range.rowi;
335 if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 &&
336 y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
337 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2)
339 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0);
340 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
342 *drag_column = sheet->range.col0;
345 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
346 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
347 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
349 *drag_column = sheet->range.coli;
357 static inline gboolean
358 POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y,
359 gint *drag_row, gint *drag_column)
363 /* Can't drag if nothing is selected */
364 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
365 sheet->range.col0 < 0 || sheet->range.coli < 0 )
368 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+
369 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
371 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
372 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
374 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED)
375 ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet));
377 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED)
378 xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet));
380 *drag_column = column_from_xpixel (sheet, x);
381 *drag_row = row_from_ypixel (sheet, y);
383 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
384 y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
391 rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *input,
394 PsppireSheetRange range = *input;
396 if ( range.row0 == -1 ) range.row0 = min_visible_row (sheet);
397 if ( range.rowi == -1 ) range.rowi = max_visible_row (sheet);
398 if ( range.col0 == -1 ) range.col0 = min_visible_column (sheet);
399 if ( range.coli == -1 ) range.coli = max_visible_column (sheet);
401 r->x = psppire_axis_start_pixel (sheet->haxis, range.col0);
402 r->x -= round (sheet->hadjustment->value);
404 r->y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
405 r->y -= round (sheet->vadjustment->value);
407 r->width = psppire_axis_start_pixel (sheet->haxis, range.coli) -
408 psppire_axis_start_pixel (sheet->haxis, range.col0) +
409 psppire_axis_unit_size (sheet->haxis, range.coli);
411 r->height = psppire_axis_start_pixel (sheet->vaxis, range.rowi) -
412 psppire_axis_start_pixel (sheet->vaxis, range.row0) +
413 psppire_axis_unit_size (sheet->vaxis, range.rowi);
415 if ( sheet->column_titles_visible)
417 r->y += sheet->column_title_area.height;
420 if ( sheet->row_titles_visible)
422 r->x += sheet->row_title_area.width;
429 rectangle_from_cell (PsppireSheet *sheet, gint row, gint col,
432 PsppireSheetRange range;
433 g_return_val_if_fail (row >= 0, FALSE);
434 g_return_val_if_fail (col >= 0, FALSE);
436 range.row0 = range.rowi = row;
437 range.col0 = range.coli = col;
439 return rectangle_from_range (sheet, &range, r);
443 static void psppire_sheet_class_init (PsppireSheetClass *klass);
444 static void psppire_sheet_init (PsppireSheet *sheet);
445 static void psppire_sheet_dispose (GObject *object);
446 static void psppire_sheet_finalize (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);
547 static void psppire_sheet_button_size_request (PsppireSheet *sheet,
548 const PsppireSheetButton *button,
549 GtkRequisition *requisition);
551 static void psppire_sheet_real_cell_clear (PsppireSheet *sheet,
573 static GtkContainerClass *parent_class = NULL;
574 static guint sheet_signals[LAST_SIGNAL] = { 0 };
578 psppire_sheet_get_type ()
580 static GType sheet_type = 0;
584 static const GTypeInfo sheet_info =
586 sizeof (PsppireSheetClass),
589 (GClassInitFunc) psppire_sheet_class_init,
592 sizeof (PsppireSheet),
594 (GInstanceInitFunc) psppire_sheet_init,
599 g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
607 static PsppireSheetRange*
608 psppire_sheet_range_copy (const PsppireSheetRange *range)
610 PsppireSheetRange *new_range;
612 g_return_val_if_fail (range != NULL, NULL);
614 new_range = g_new (PsppireSheetRange, 1);
622 psppire_sheet_range_free (PsppireSheetRange *range)
624 g_return_if_fail (range != NULL);
630 psppire_sheet_range_get_type (void)
632 static GType sheet_range_type = 0;
634 if (!sheet_range_type)
637 g_boxed_type_register_static ("PsppireSheetRange",
638 (GBoxedCopyFunc) psppire_sheet_range_copy,
639 (GBoxedFreeFunc) psppire_sheet_range_free);
642 return sheet_range_type;
645 static PsppireSheetCell*
646 psppire_sheet_cell_copy (const PsppireSheetCell *cell)
648 PsppireSheetCell *new_cell;
650 g_return_val_if_fail (cell != NULL, NULL);
652 new_cell = g_new (PsppireSheetCell, 1);
660 psppire_sheet_cell_free (PsppireSheetCell *cell)
662 g_return_if_fail (cell != NULL);
668 psppire_sheet_cell_get_type (void)
670 static GType sheet_cell_type = 0;
672 if (!sheet_cell_type)
675 g_boxed_type_register_static ("PsppireSheetCell",
676 (GBoxedCopyFunc) psppire_sheet_cell_copy,
677 (GBoxedFreeFunc) psppire_sheet_cell_free);
680 return sheet_cell_type;
695 resize_column (PsppireSheet *sheet, gint unit, glong size)
697 PsppireSheetRange range;
699 range.coli = max_visible_column (sheet);
700 range.row0 = min_visible_row (sheet);
701 range.rowi = max_visible_row (sheet);
703 redraw_range (sheet, &range);
705 draw_column_title_buttons_range (sheet, range.col0, range.coli);
710 psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
713 g_object_unref (sheet->haxis);
716 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
719 g_object_ref (sheet->haxis);
723 resize_row (PsppireSheet *sheet, gint unit, glong size)
725 PsppireSheetRange range;
726 range.col0 = min_visible_column (sheet);
727 range.coli = max_visible_column (sheet);
729 range.rowi = max_visible_row (sheet);
731 redraw_range (sheet, &range);
733 draw_row_title_buttons_range (sheet, range.row0, range.rowi);
737 psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
740 g_object_unref (sheet->vaxis);
744 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
747 g_object_ref (sheet->vaxis);
750 static const GtkBorder default_cell_padding = {3, 3, 3, 3};
753 psppire_sheet_set_property (GObject *object,
759 PsppireSheet *sheet = PSPPIRE_SHEET (object);
763 case PROP_CELL_PADDING:
764 if ( sheet->cell_padding)
765 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
767 sheet->cell_padding = g_value_dup_boxed (value);
769 if (NULL == sheet->cell_padding)
770 sheet->cell_padding = g_boxed_copy (GTK_TYPE_BORDER,
771 &default_cell_padding);
774 g_object_set (sheet->vaxis, "padding",
775 sheet->cell_padding->top + sheet->cell_padding->bottom,
779 g_object_set (sheet->haxis, "padding",
780 sheet->cell_padding->left + sheet->cell_padding->right,
784 psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
785 g_object_set (sheet->vaxis, "padding",
786 sheet->cell_padding->top + sheet->cell_padding->bottom,
790 psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
791 g_object_set (sheet->haxis, "padding",
792 sheet->cell_padding->left + sheet->cell_padding->right,
796 psppire_sheet_set_model (sheet, g_value_get_pointer (value));
799 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
805 psppire_sheet_get_property (GObject *object,
810 PsppireSheet *sheet = PSPPIRE_SHEET (object);
814 case PROP_CELL_PADDING:
815 g_value_set_boxed (value, sheet->cell_padding);
818 g_value_set_pointer (value, sheet->vaxis);
821 g_value_set_pointer (value, sheet->haxis);
824 g_value_set_pointer (value, sheet->model);
827 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
834 psppire_sheet_class_init (PsppireSheetClass *klass)
836 GObjectClass *object_class = G_OBJECT_CLASS (klass);
838 GParamSpec *haxis_spec ;
839 GParamSpec *vaxis_spec ;
840 GParamSpec *model_spec ;
841 GParamSpec *cell_padding_spec ;
843 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
844 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
846 parent_class = g_type_class_peek_parent (klass);
849 * PsppireSheet::select-row
850 * @sheet: the sheet widget that emitted the signal
851 * @row: the newly selected row index, or -1 if no row is selected.
853 * A row has been selected.
855 sheet_signals[SELECT_ROW] =
856 g_signal_new ("select-row",
857 G_TYPE_FROM_CLASS (object_class),
859 offsetof (PsppireSheetClass, select_row),
861 g_cclosure_marshal_VOID__INT,
868 * PsppireSheet::select - column
869 * @sheet: the sheet widget that emitted the signal
870 * @column: the newly selected column index, or -1 if no column is selected.
872 * A column has been selected.
874 sheet_signals[SELECT_COLUMN] =
875 g_signal_new ("select-column",
876 G_TYPE_FROM_CLASS (object_class),
878 offsetof (PsppireSheetClass, select_column),
880 g_cclosure_marshal_VOID__INT,
887 * PsppireSheet::double-click-row
888 * @sheet: the sheet widget that emitted the signal
889 * @row: the row that was double clicked.
891 * A row's title button has been double clicked
893 sheet_signals[DOUBLE_CLICK_ROW] =
894 g_signal_new ("double-click-row",
895 G_TYPE_FROM_CLASS (object_class),
899 g_cclosure_marshal_VOID__INT,
906 * PsppireSheet::double-click-column
907 * @sheet: the sheet widget that emitted the signal
908 * @column: the column that was double clicked.
910 * A column's title button has been double clicked
912 sheet_signals[DOUBLE_CLICK_COLUMN] =
913 g_signal_new ("double-click-column",
914 G_TYPE_FROM_CLASS (object_class),
918 g_cclosure_marshal_VOID__INT,
925 * PsppireSheet::button-event-column
926 * @sheet: the sheet widget that emitted the signal
927 * @column: the column on which the event occured.
929 * A button event occured on a column title button
931 sheet_signals[BUTTON_EVENT_COLUMN] =
932 g_signal_new ("button-event-column",
933 G_TYPE_FROM_CLASS (object_class),
937 psppire_marshal_VOID__INT_POINTER,
946 * PsppireSheet::button-event-row
947 * @sheet: the sheet widget that emitted the signal
948 * @column: the column on which the event occured.
950 * A button event occured on a row title button
952 sheet_signals[BUTTON_EVENT_ROW] =
953 g_signal_new ("button-event-row",
954 G_TYPE_FROM_CLASS (object_class),
958 psppire_marshal_VOID__INT_POINTER,
966 sheet_signals[SELECT_RANGE] =
967 g_signal_new ("select-range",
968 G_TYPE_FROM_CLASS (object_class),
970 offsetof (PsppireSheetClass, select_range),
972 g_cclosure_marshal_VOID__BOXED,
975 PSPPIRE_TYPE_SHEET_RANGE);
978 sheet_signals[RESIZE_RANGE] =
979 g_signal_new ("resize-range",
980 G_TYPE_FROM_CLASS (object_class),
982 offsetof (PsppireSheetClass, resize_range),
984 psppire_marshal_VOID__BOXED_BOXED,
987 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
990 sheet_signals[MOVE_RANGE] =
991 g_signal_new ("move-range",
992 G_TYPE_FROM_CLASS (object_class),
994 offsetof (PsppireSheetClass, move_range),
996 psppire_marshal_VOID__BOXED_BOXED,
999 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
1002 sheet_signals[TRAVERSE] =
1003 g_signal_new ("traverse",
1004 G_TYPE_FROM_CLASS (object_class),
1006 offsetof (PsppireSheetClass, traverse),
1008 psppire_marshal_BOOLEAN__BOXED_POINTER,
1010 PSPPIRE_TYPE_SHEET_CELL,
1014 sheet_signals[ACTIVATE] =
1015 g_signal_new ("activate",
1016 G_TYPE_FROM_CLASS (object_class),
1018 offsetof (PsppireSheetClass, activate),
1020 psppire_marshal_VOID__INT_INT_INT_INT,
1022 G_TYPE_INT, G_TYPE_INT,
1023 G_TYPE_INT, G_TYPE_INT);
1025 widget_class->set_scroll_adjustments_signal =
1026 g_signal_new ("set-scroll-adjustments",
1027 G_TYPE_FROM_CLASS (object_class),
1029 offsetof (PsppireSheetClass, set_scroll_adjustments),
1031 psppire_marshal_VOID__OBJECT_OBJECT,
1032 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1035 container_class->add = NULL;
1036 container_class->remove = NULL;
1037 container_class->forall = psppire_sheet_forall;
1038 container_class->set_focus_child = NULL;
1040 object_class->dispose = psppire_sheet_dispose;
1041 object_class->finalize = psppire_sheet_finalize;
1044 g_param_spec_boxed ("cell-padding",
1046 "The space between a cell's contents and its border",
1048 G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
1051 g_param_spec_pointer ("vertical-axis",
1053 "A pointer to the PsppireAxis object for the rows",
1054 G_PARAM_READABLE | G_PARAM_WRITABLE );
1057 g_param_spec_pointer ("horizontal-axis",
1059 "A pointer to the PsppireAxis object for the columns",
1060 G_PARAM_READABLE | G_PARAM_WRITABLE );
1063 g_param_spec_pointer ("model",
1065 "A pointer to the data model",
1066 G_PARAM_READABLE | G_PARAM_WRITABLE );
1069 object_class->set_property = psppire_sheet_set_property;
1070 object_class->get_property = psppire_sheet_get_property;
1072 g_object_class_install_property (object_class,
1076 g_object_class_install_property (object_class,
1080 g_object_class_install_property (object_class,
1084 g_object_class_install_property (object_class,
1089 widget_class->realize = psppire_sheet_realize;
1090 widget_class->unrealize = psppire_sheet_unrealize;
1091 widget_class->map = psppire_sheet_map;
1092 widget_class->unmap = psppire_sheet_unmap;
1093 widget_class->style_set = psppire_sheet_style_set;
1094 widget_class->button_press_event = psppire_sheet_button_press;
1095 widget_class->button_release_event = psppire_sheet_button_release;
1096 widget_class->motion_notify_event = psppire_sheet_motion;
1097 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1098 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1099 widget_class->key_press_event = psppire_sheet_key_press;
1100 widget_class->expose_event = psppire_sheet_expose;
1101 widget_class->size_request = psppire_sheet_size_request;
1102 widget_class->size_allocate = psppire_sheet_size_allocate;
1103 widget_class->focus_in_event = psppire_sheet_focus_in;
1104 widget_class->focus_out_event = NULL;
1106 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1107 klass->select_row = NULL;
1108 klass->select_column = NULL;
1109 klass->select_range = NULL;
1110 klass->resize_range = NULL;
1111 klass->move_range = NULL;
1112 klass->traverse = NULL;
1113 klass->activate = NULL;
1114 klass->changed = NULL;
1118 psppire_sheet_init (PsppireSheet *sheet)
1120 sheet->model = NULL;
1121 sheet->haxis = NULL;
1122 sheet->vaxis = NULL;
1125 sheet->select_status = PSPPIRE_SHEET_NORMAL;
1127 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1128 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1130 sheet->column_title_window = NULL;
1131 sheet->column_title_area.x = 0;
1132 sheet->column_title_area.y = 0;
1133 sheet->column_title_area.width = 0;
1134 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1136 sheet->row_title_window = NULL;
1137 sheet->row_title_area.x = 0;
1138 sheet->row_title_area.y = 0;
1139 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1140 sheet->row_title_area.height = 0;
1143 sheet->active_cell.row = 0;
1144 sheet->active_cell.col = 0;
1146 sheet->range.row0 = 0;
1147 sheet->range.rowi = 0;
1148 sheet->range.col0 = 0;
1149 sheet->range.coli = 0;
1151 sheet->sheet_window = NULL;
1152 sheet->entry_widget = NULL;
1153 sheet->button = NULL;
1155 sheet->hadjustment = NULL;
1156 sheet->vadjustment = NULL;
1158 sheet->cursor_drag = NULL;
1160 sheet->xor_gc = NULL;
1161 sheet->fg_gc = NULL;
1162 sheet->bg_gc = NULL;
1165 sheet->show_grid = TRUE;
1167 sheet->motion_timer = 0;
1169 sheet->row_titles_visible = TRUE;
1170 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1172 sheet->column_titles_visible = TRUE;
1175 /* create sheet entry */
1176 sheet->entry_type = GTK_TYPE_ENTRY;
1177 create_sheet_entry (sheet);
1179 /* create global selection button */
1180 create_global_button (sheet);
1184 /* Cause RANGE to be redrawn. If RANGE is null, then the
1185 entire visible range will be redrawn.
1188 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1192 if ( ! GTK_WIDGET_REALIZED (sheet))
1195 if ( NULL != range )
1196 rectangle_from_range (sheet, range, &rect);
1199 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1200 gdk_region_get_clipbox (r, &rect);
1202 if ( sheet->column_titles_visible)
1204 rect.y += sheet->column_title_area.height;
1205 rect.height -= sheet->column_title_area.height;
1208 if ( sheet->row_titles_visible)
1210 rect.x += sheet->row_title_area.width;
1211 rect.width -= sheet->row_title_area.width;
1215 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1219 /* Callback which occurs whenever columns are inserted / deleted in the model */
1221 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1225 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1227 PsppireSheetRange range;
1228 gint model_columns = psppire_sheet_model_get_column_count (model);
1231 /* Need to update all the columns starting from the first column and onwards.
1232 * Previous column are unchanged, so don't need to be updated.
1234 range.col0 = first_column;
1236 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1237 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1239 adjust_scrollbars (sheet);
1241 if (sheet->active_cell.col >= model_columns)
1242 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1244 draw_column_title_buttons_range (sheet,
1245 first_column, max_visible_column (sheet));
1248 redraw_range (sheet, &range);
1254 /* Callback which occurs whenever rows are inserted / deleted in the model */
1256 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1257 gint n_rows, gpointer data)
1259 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1261 PsppireSheetRange range;
1263 gint model_rows = psppire_sheet_model_get_row_count (model);
1265 /* Need to update all the rows starting from the first row and onwards.
1266 * Previous rows are unchanged, so don't need to be updated.
1268 range.row0 = first_row;
1270 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1271 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1273 adjust_scrollbars (sheet);
1275 if (sheet->active_cell.row >= model_rows)
1276 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1278 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1280 redraw_range (sheet, &range);
1284 If row0 or rowi are negative, then all rows will be updated.
1285 If col0 or coli are negative, then all columns will be updated.
1288 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1289 gint rowi, gint coli, gpointer data)
1291 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1293 PsppireSheetRange range;
1300 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1303 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1305 redraw_range (sheet, NULL);
1306 adjust_scrollbars (sheet);
1308 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1309 max_visible_row (sheet));
1311 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1312 max_visible_column (sheet));
1316 else if ( row0 < 0 || rowi < 0 )
1318 range.row0 = min_visible_row (sheet);
1319 range.rowi = max_visible_row (sheet);
1321 else if ( col0 < 0 || coli < 0 )
1323 range.col0 = min_visible_column (sheet);
1324 range.coli = max_visible_column (sheet);
1327 redraw_range (sheet, &range);
1332 * psppire_sheet_new:
1333 * @rows: initial number of rows
1334 * @columns: initial number of columns
1335 * @title: sheet title
1336 * @model: the model to use for the sheet data
1338 * Creates a new sheet widget with the given number of rows and columns.
1340 * Returns: the new sheet widget
1343 psppire_sheet_new (PsppireSheetModel *model)
1345 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1353 * psppire_sheet_set_model
1354 * @sheet: the sheet to set the model for
1355 * @model: the model to use for the sheet data
1357 * Sets the model for a PsppireSheet
1361 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1363 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1365 if (sheet->model ) g_object_unref (sheet->model);
1367 sheet->model = model;
1371 g_object_ref (model);
1373 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1374 G_CALLBACK (range_update_callback),
1377 g_signal_connect (model, "rows_inserted",
1378 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1380 g_signal_connect (model, "rows_deleted",
1381 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1383 g_signal_connect (model, "columns_inserted",
1384 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1386 g_signal_connect (model, "columns_deleted",
1387 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1393 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1395 g_return_if_fail (sheet != NULL);
1396 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1398 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1399 psppire_sheet_hide_entry_widget (sheet);
1401 sheet->entry_type = entry_type;
1403 create_sheet_entry (sheet);
1405 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1406 psppire_sheet_show_entry_widget (sheet);
1410 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1412 g_return_if_fail (sheet != NULL);
1413 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1415 if (show == sheet->show_grid) return;
1417 sheet->show_grid = show;
1419 redraw_range (sheet, NULL);
1423 psppire_sheet_grid_visible (PsppireSheet *sheet)
1425 g_return_val_if_fail (sheet != NULL, 0);
1426 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1428 return sheet->show_grid;
1432 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1434 g_return_val_if_fail (sheet != NULL, 0);
1435 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1437 return psppire_axis_unit_count (sheet->haxis);
1440 static void set_column_width (PsppireSheet *sheet,
1446 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1448 if (sheet->column_titles_visible) return;
1450 sheet->column_titles_visible = TRUE;
1452 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1455 gdk_window_show (sheet->column_title_window);
1456 gdk_window_move_resize (sheet->column_title_window,
1457 sheet->column_title_area.x,
1458 sheet->column_title_area.y,
1459 sheet->column_title_area.width,
1460 sheet->column_title_area.height);
1462 adjust_scrollbars (sheet);
1464 if (sheet->vadjustment)
1465 g_signal_emit_by_name (sheet->vadjustment,
1468 size_allocate_global_button (sheet);
1470 if ( sheet->row_titles_visible)
1471 gtk_widget_show (sheet->button);
1476 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1478 if (sheet->row_titles_visible) return;
1480 sheet->row_titles_visible = TRUE;
1483 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1485 gdk_window_show (sheet->row_title_window);
1486 gdk_window_move_resize (sheet->row_title_window,
1487 sheet->row_title_area.x,
1488 sheet->row_title_area.y,
1489 sheet->row_title_area.width,
1490 sheet->row_title_area.height);
1492 adjust_scrollbars (sheet);
1495 if (sheet->hadjustment)
1496 g_signal_emit_by_name (sheet->hadjustment,
1499 size_allocate_global_button (sheet);
1501 if ( sheet->column_titles_visible)
1502 gtk_widget_show (sheet->button);
1506 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1508 if (!sheet->column_titles_visible) return;
1510 sheet->column_titles_visible = FALSE;
1512 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1514 if (sheet->column_title_window)
1515 gdk_window_hide (sheet->column_title_window);
1517 gtk_widget_hide (sheet->button);
1519 adjust_scrollbars (sheet);
1522 if (sheet->vadjustment)
1523 g_signal_emit_by_name (sheet->vadjustment,
1528 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1530 if (!sheet->row_titles_visible) return;
1532 sheet->row_titles_visible = FALSE;
1534 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1536 if (sheet->row_title_window)
1537 gdk_window_hide (sheet->row_title_window);
1539 gtk_widget_hide (sheet->button);
1541 adjust_scrollbars (sheet);
1544 if (sheet->hadjustment)
1545 g_signal_emit_by_name (sheet->hadjustment,
1550 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1551 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1552 at the {top,left} of the sheet. If it's 1, then it'll
1553 be placed at the {bottom,right}.
1554 ROW or COL may be -1, in which case scrolling in that dimension
1558 psppire_sheet_moveto (PsppireSheet *sheet,
1566 g_return_if_fail (row_align >= 0);
1567 g_return_if_fail (col_align >= 0);
1569 g_return_if_fail (row_align <= 1);
1570 g_return_if_fail (col_align <= 1);
1572 g_return_if_fail (col <
1573 psppire_axis_unit_count (sheet->haxis));
1574 g_return_if_fail (row <
1575 psppire_axis_unit_count (sheet->vaxis));
1577 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1582 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1584 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1590 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1592 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1600 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1601 const PsppireSheetRange *range)
1603 g_return_val_if_fail (sheet != NULL, FALSE);
1605 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1608 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1611 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1614 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1617 if (range->rowi < min_visible_row (sheet))
1620 if (range->row0 > max_visible_row (sheet))
1623 if (range->coli < min_visible_column (sheet))
1626 if (range->col0 > max_visible_column (sheet))
1633 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1634 gint row, gint column)
1636 PsppireSheetRange range;
1639 range.col0 = column;
1641 range.coli = column;
1643 return psppire_sheet_range_isvisible (sheet, &range);
1647 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1649 g_return_if_fail (sheet != NULL);
1650 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1651 g_return_if_fail (range != NULL);
1653 range->row0 = min_visible_row (sheet);
1654 range->col0 = min_visible_column (sheet);
1655 range->rowi = max_visible_row (sheet);
1656 range->coli = max_visible_column (sheet);
1661 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1662 GtkAdjustment *hadjustment,
1663 GtkAdjustment *vadjustment)
1665 if ( sheet->vadjustment != vadjustment )
1667 if (sheet->vadjustment)
1668 g_object_unref (sheet->vadjustment);
1669 sheet->vadjustment = vadjustment;
1673 g_object_ref (vadjustment);
1675 g_signal_connect (sheet->vadjustment, "value_changed",
1676 G_CALLBACK (vadjustment_value_changed),
1681 if ( sheet->hadjustment != hadjustment )
1683 if (sheet->hadjustment)
1684 g_object_unref (sheet->hadjustment);
1686 sheet->hadjustment = hadjustment;
1690 g_object_ref (hadjustment);
1692 g_signal_connect (sheet->hadjustment, "value_changed",
1693 G_CALLBACK (hadjustment_value_changed),
1701 psppire_sheet_finalize (GObject *object)
1703 PsppireSheet *sheet;
1705 g_return_if_fail (object != NULL);
1706 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1708 sheet = PSPPIRE_SHEET (object);
1710 if (G_OBJECT_CLASS (parent_class)->finalize)
1711 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1715 psppire_sheet_dispose (GObject *object)
1717 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1719 g_return_if_fail (object != NULL);
1720 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1722 if ( sheet->dispose_has_run )
1725 sheet->dispose_has_run = TRUE;
1727 if ( sheet->cell_padding)
1728 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1730 if (sheet->model) g_object_unref (sheet->model);
1731 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1732 if (sheet->haxis) g_object_unref (sheet->haxis);
1734 g_object_unref (sheet->button);
1735 sheet->button = NULL;
1737 /* unref adjustments */
1738 if (sheet->hadjustment)
1740 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1741 G_SIGNAL_MATCH_DATA,
1745 g_object_unref (sheet->hadjustment);
1746 sheet->hadjustment = NULL;
1749 if (sheet->vadjustment)
1751 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1752 G_SIGNAL_MATCH_DATA,
1756 g_object_unref (sheet->vadjustment);
1758 sheet->vadjustment = NULL;
1761 if (G_OBJECT_CLASS (parent_class)->dispose)
1762 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1766 psppire_sheet_style_set (GtkWidget *widget,
1767 GtkStyle *previous_style)
1769 PsppireSheet *sheet;
1771 g_return_if_fail (widget != NULL);
1772 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1774 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1775 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1777 sheet = PSPPIRE_SHEET (widget);
1779 if (GTK_WIDGET_REALIZED (widget))
1781 gtk_style_set_background (widget->style, widget->window, widget->state);
1784 set_entry_widget_font (sheet);
1789 psppire_sheet_realize (GtkWidget *widget)
1791 PsppireSheet *sheet;
1792 GdkWindowAttr attributes;
1793 const gint attributes_mask =
1794 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1797 GdkColormap *colormap;
1798 GdkDisplay *display;
1800 g_return_if_fail (widget != NULL);
1801 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1803 sheet = PSPPIRE_SHEET (widget);
1805 colormap = gtk_widget_get_colormap (widget);
1806 display = gtk_widget_get_display (widget);
1808 attributes.window_type = GDK_WINDOW_CHILD;
1809 attributes.x = widget->allocation.x;
1810 attributes.y = widget->allocation.y;
1811 attributes.width = widget->allocation.width;
1812 attributes.height = widget->allocation.height;
1813 attributes.wclass = GDK_INPUT_OUTPUT;
1815 attributes.visual = gtk_widget_get_visual (widget);
1816 attributes.colormap = colormap;
1818 attributes.event_mask = gtk_widget_get_events (widget);
1819 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1820 GDK_BUTTON_PRESS_MASK |
1821 GDK_BUTTON_RELEASE_MASK |
1822 GDK_KEY_PRESS_MASK |
1823 GDK_ENTER_NOTIFY_MASK |
1824 GDK_LEAVE_NOTIFY_MASK |
1825 GDK_POINTER_MOTION_MASK |
1826 GDK_POINTER_MOTION_HINT_MASK);
1828 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1831 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1833 gdk_window_set_user_data (widget->window, sheet);
1835 widget->style = gtk_style_attach (widget->style, widget->window);
1837 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1839 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1840 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1842 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1843 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1848 attributes.width = sheet->column_title_area.width;
1849 attributes.height = sheet->column_title_area.height;
1852 /* column - title window */
1853 sheet->column_title_window =
1854 gdk_window_new (widget->window, &attributes, attributes_mask);
1855 gdk_window_set_user_data (sheet->column_title_window, sheet);
1856 gtk_style_set_background (widget->style, sheet->column_title_window,
1862 attributes.width = sheet->row_title_area.width;
1863 attributes.height = sheet->row_title_area.height;
1865 /* row - title window */
1866 sheet->row_title_window = gdk_window_new (widget->window,
1867 &attributes, attributes_mask);
1868 gdk_window_set_user_data (sheet->row_title_window, sheet);
1869 gtk_style_set_background (widget->style, sheet->row_title_window,
1872 /* sheet - window */
1873 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1878 sheet->sheet_window = gdk_window_new (widget->window,
1879 &attributes, attributes_mask);
1880 gdk_window_set_user_data (sheet->sheet_window, sheet);
1882 gdk_cursor_unref (attributes.cursor);
1884 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1885 gdk_window_show (sheet->sheet_window);
1888 sheet->fg_gc = gdk_gc_new (widget->window);
1889 sheet->bg_gc = gdk_gc_new (widget->window);
1891 values.foreground = widget->style->white;
1892 values.function = GDK_INVERT;
1893 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1894 values.line_width = MAX (sheet->cell_padding->left,
1895 MAX (sheet->cell_padding->right,
1896 MAX (sheet->cell_padding->top,
1897 sheet->cell_padding->bottom)));
1899 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1907 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1908 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1910 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1911 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1913 sheet->button->style = gtk_style_attach (sheet->button->style,
1914 sheet->sheet_window);
1917 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1919 if (sheet->column_titles_visible)
1920 gdk_window_show (sheet->column_title_window);
1921 if (sheet->row_titles_visible)
1922 gdk_window_show (sheet->row_title_window);
1924 sheet->hover_window = create_hover_window ();
1926 draw_row_title_buttons (sheet);
1927 draw_column_title_buttons (sheet);
1929 psppire_sheet_update_primary_selection (sheet);
1932 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1936 create_global_button (PsppireSheet *sheet)
1938 sheet->button = gtk_button_new_with_label (" ");
1940 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1942 g_object_ref_sink (sheet->button);
1944 g_signal_connect (sheet->button,
1946 G_CALLBACK (global_button_clicked),
1951 size_allocate_global_button (PsppireSheet *sheet)
1953 GtkAllocation allocation;
1955 if (!sheet->column_titles_visible) return;
1956 if (!sheet->row_titles_visible) return;
1958 gtk_widget_size_request (sheet->button, NULL);
1962 allocation.width = sheet->row_title_area.width;
1963 allocation.height = sheet->column_title_area.height;
1965 gtk_widget_size_allocate (sheet->button, &allocation);
1969 global_button_clicked (GtkWidget *widget, gpointer data)
1971 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
1976 psppire_sheet_unrealize (GtkWidget *widget)
1978 PsppireSheet *sheet;
1980 g_return_if_fail (widget != NULL);
1981 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1983 sheet = PSPPIRE_SHEET (widget);
1985 gdk_cursor_unref (sheet->cursor_drag);
1986 sheet->cursor_drag = NULL;
1988 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
1989 sheet->color, n_COLORS);
1991 g_object_unref (sheet->xor_gc);
1992 g_object_unref (sheet->fg_gc);
1993 g_object_unref (sheet->bg_gc);
1995 destroy_hover_window (sheet->hover_window);
1997 gdk_window_destroy (sheet->sheet_window);
1998 gdk_window_destroy (sheet->column_title_window);
1999 gdk_window_destroy (sheet->row_title_window);
2001 gtk_widget_unparent (sheet->entry_widget);
2002 if (sheet->button != NULL)
2003 gtk_widget_unparent (sheet->button);
2005 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2006 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2010 psppire_sheet_map (GtkWidget *widget)
2012 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2014 g_return_if_fail (widget != NULL);
2015 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2017 if (!GTK_WIDGET_MAPPED (widget))
2019 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2021 gdk_window_show (widget->window);
2022 gdk_window_show (sheet->sheet_window);
2024 if (sheet->column_titles_visible)
2026 draw_column_title_buttons (sheet);
2027 gdk_window_show (sheet->column_title_window);
2029 if (sheet->row_titles_visible)
2031 draw_row_title_buttons (sheet);
2032 gdk_window_show (sheet->row_title_window);
2035 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2036 && sheet->active_cell.row >= 0
2037 && sheet->active_cell.col >= 0 )
2039 gtk_widget_show (sheet->entry_widget);
2040 gtk_widget_map (sheet->entry_widget);
2043 if (!GTK_WIDGET_MAPPED (sheet->button))
2045 gtk_widget_show (sheet->button);
2046 gtk_widget_map (sheet->button);
2049 redraw_range (sheet, NULL);
2050 change_active_cell (sheet,
2051 sheet->active_cell.row,
2052 sheet->active_cell.col);
2057 psppire_sheet_unmap (GtkWidget *widget)
2059 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2061 if (!GTK_WIDGET_MAPPED (widget))
2064 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2066 gdk_window_hide (sheet->sheet_window);
2067 if (sheet->column_titles_visible)
2068 gdk_window_hide (sheet->column_title_window);
2069 if (sheet->row_titles_visible)
2070 gdk_window_hide (sheet->row_title_window);
2071 gdk_window_hide (widget->window);
2073 gtk_widget_unmap (sheet->entry_widget);
2074 gtk_widget_unmap (sheet->button);
2075 gtk_widget_unmap (sheet->hover_window->window);
2078 /* get cell attributes of the given cell */
2079 /* TRUE means that the cell is currently allocated */
2080 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2082 PsppireSheetCellAttr *attributes);
2087 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2089 PangoLayout *layout;
2090 PangoRectangle text;
2091 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2096 PsppireSheetCellAttr attributes;
2099 g_return_if_fail (sheet != NULL);
2101 /* bail now if we aren't yet drawable */
2102 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2105 row >= psppire_axis_unit_count (sheet->vaxis))
2109 col >= psppire_axis_unit_count (sheet->haxis))
2112 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2114 /* select GC for background rectangle */
2115 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2116 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2118 rectangle_from_cell (sheet, row, col, &area);
2120 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2122 if (sheet->show_grid)
2124 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2126 gdk_draw_rectangle (sheet->sheet_window,
2130 area.width, area.height);
2134 label = psppire_sheet_cell_get_text (sheet, row, col);
2139 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2140 dispose_string (sheet, label);
2143 pango_layout_set_font_description (layout, font_desc);
2145 pango_layout_get_pixel_extents (layout, NULL, &text);
2147 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2149 font_height = pango_font_description_get_size (font_desc);
2150 if ( !pango_font_description_get_size_is_absolute (font_desc))
2151 font_height /= PANGO_SCALE;
2154 if ( sheet->cell_padding )
2156 area.x += sheet->cell_padding->left;
2157 area.width -= sheet->cell_padding->right
2158 + sheet->cell_padding->left;
2160 area.y += sheet->cell_padding->top;
2161 area.height -= sheet->cell_padding->bottom
2163 sheet->cell_padding->top;
2166 /* Centre the text vertically */
2167 area.y += (area.height - font_height) / 2.0;
2169 switch (attributes.justification)
2171 case GTK_JUSTIFY_RIGHT:
2172 area.x += area.width - text.width;
2174 case GTK_JUSTIFY_CENTER:
2175 area.x += (area.width - text.width) / 2.0;
2177 case GTK_JUSTIFY_LEFT:
2181 g_critical ("Unhandled justification %d in column %d\n",
2182 attributes.justification, col);
2186 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2191 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2192 g_object_unref (layout);
2197 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2199 PsppireSheetRange range;
2204 PsppireSheetRange drawing_range;
2206 gdk_region_get_clipbox (region, &area);
2208 y = area.y + sheet->vadjustment->value;
2209 x = area.x + sheet->hadjustment->value;
2211 if ( sheet->column_titles_visible)
2212 y -= sheet->column_title_area.height;
2214 if ( sheet->row_titles_visible)
2215 x -= sheet->row_title_area.width;
2217 maximize_int (&x, 0);
2218 maximize_int (&y, 0);
2220 range.row0 = row_from_ypixel (sheet, y);
2221 range.rowi = row_from_ypixel (sheet, y + area.height);
2223 range.col0 = column_from_xpixel (sheet, x);
2224 range.coli = column_from_xpixel (sheet, x + area.width);
2226 g_return_if_fail (sheet != NULL);
2227 g_return_if_fail (PSPPIRE_SHEET (sheet));
2229 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2230 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2231 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2234 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2235 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2236 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2237 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2239 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2240 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2242 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2244 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2245 psppire_sheet_cell_draw (sheet, i, j);
2248 if (sheet->select_status == PSPPIRE_SHEET_NORMAL &&
2249 sheet->active_cell.row >= drawing_range.row0 &&
2250 sheet->active_cell.row <= drawing_range.rowi &&
2251 sheet->active_cell.col >= drawing_range.col0 &&
2252 sheet->active_cell.col <= drawing_range.coli)
2253 psppire_sheet_show_entry_widget (sheet);
2257 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2258 GtkJustification justification,
2261 PsppireSheetModel *model ;
2264 g_return_if_fail (sheet != NULL);
2265 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2267 if (col >= psppire_axis_unit_count (sheet->haxis)
2268 || row >= psppire_axis_unit_count (sheet->vaxis))
2271 if (col < 0 || row < 0) return;
2273 model = psppire_sheet_get_model (sheet);
2275 old_text = psppire_sheet_model_get_string (model, row, col);
2277 if (0 != g_strcmp0 (old_text, text))
2279 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2280 psppire_sheet_model_set_string (model, text, row, col);
2281 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2284 if ( psppire_sheet_model_free_strings (model))
2290 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2292 PsppireSheetRange range;
2294 g_return_if_fail (sheet != NULL);
2295 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2296 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2297 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2299 if (column < 0 || row < 0) return;
2303 range.col0 = min_visible_column (sheet);
2304 range.coli = max_visible_column (sheet);
2306 psppire_sheet_real_cell_clear (sheet, row, column);
2308 redraw_range (sheet, &range);
2312 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2314 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2316 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2318 if (old_text && strlen (old_text) > 0 )
2320 psppire_sheet_model_datum_clear (model, row, column);
2323 dispose_string (sheet, old_text);
2327 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2329 PsppireSheetModel *model;
2330 g_return_val_if_fail (sheet != NULL, NULL);
2331 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2333 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2335 if (col < 0 || row < 0) return NULL;
2337 model = psppire_sheet_get_model (sheet);
2342 return psppire_sheet_model_get_string (model, row, col);
2346 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2347 If the function returns FALSE, then the results will be unreliable.
2350 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2358 *column = -G_MAXINT;
2360 g_return_val_if_fail (sheet != NULL, 0);
2361 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2363 /* bounds checking, return false if the user clicked
2371 if ( sheet->column_titles_visible)
2372 y -= sheet->column_title_area.height;
2374 y += sheet->vadjustment->value;
2376 if ( y < 0 && sheet->column_titles_visible)
2382 trow = row_from_ypixel (sheet, y);
2383 if (trow > psppire_axis_unit_count (sheet->vaxis))
2389 if ( sheet->row_titles_visible)
2390 x -= sheet->row_title_area.width;
2392 x += sheet->hadjustment->value;
2394 if ( x < 0 && sheet->row_titles_visible)
2400 tcol = column_from_xpixel (sheet, x);
2401 if (tcol > psppire_axis_unit_count (sheet->haxis))
2411 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2416 g_return_val_if_fail (sheet != NULL, 0);
2417 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2419 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2422 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2423 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2425 area->width= (column == -1) ? sheet->row_title_area.width
2426 : psppire_axis_unit_size (sheet->haxis, column);
2428 area->height= (row == -1) ? sheet->column_title_area.height
2429 : psppire_axis_unit_size (sheet->vaxis, row);
2435 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2437 g_return_if_fail (sheet != NULL);
2438 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2440 if (row < -1 || col < -1)
2443 if (row >= psppire_axis_unit_count (sheet->vaxis)
2445 col >= psppire_axis_unit_count (sheet->haxis))
2448 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2451 if ( row == -1 || col == -1)
2453 psppire_sheet_hide_entry_widget (sheet);
2457 change_active_cell (sheet, row, col);
2461 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2463 g_return_if_fail (sheet != NULL);
2464 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2466 if ( row ) *row = sheet->active_cell.row;
2467 if (column) *column = sheet->active_cell.col;
2471 entry_load_text (PsppireSheet *sheet)
2475 GtkJustification justification;
2476 PsppireSheetCellAttr attributes;
2478 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2479 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2481 row = sheet->active_cell.row;
2482 col = sheet->active_cell.col;
2484 if (row < 0 || col < 0) return;
2486 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2488 if (text && strlen (text) > 0)
2490 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2491 justification = attributes.justification;
2492 psppire_sheet_set_cell (sheet, row, col, justification, text);
2498 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2500 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2503 if (sheet->active_cell.row < 0 ||
2504 sheet->active_cell.col < 0) return;
2506 gtk_widget_hide (sheet->entry_widget);
2507 gtk_widget_unmap (sheet->entry_widget);
2509 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2513 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2515 gint old_row, old_col;
2517 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2519 if (row < 0 || col < 0)
2522 if ( row > psppire_axis_unit_count (sheet->vaxis)
2523 || col > psppire_axis_unit_count (sheet->haxis))
2526 old_row = sheet->active_cell.row;
2527 old_col = sheet->active_cell.col;
2529 entry_load_text (sheet);
2531 /* Erase the old cell border */
2532 psppire_sheet_draw_active_cell (sheet);
2534 sheet->active_cell.row = row;
2535 sheet->active_cell.col = col;
2537 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2539 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2541 psppire_sheet_draw_active_cell (sheet);
2542 psppire_sheet_show_entry_widget (sheet);
2544 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2546 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2547 row, col, old_row, old_col);
2552 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2554 GtkEntry *sheet_entry;
2555 PsppireSheetCellAttr attributes;
2559 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2561 row = sheet->active_cell.row;
2562 col = sheet->active_cell.col;
2564 /* Don't show the active cell, if there is no active cell: */
2565 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2568 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2569 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2570 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2572 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2574 sheet_entry = psppire_sheet_get_entry (sheet);
2576 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2578 if (GTK_IS_ENTRY (sheet_entry))
2580 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2581 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2584 text = g_strdup ("");
2586 if (strcmp (old_text, text) != 0)
2587 gtk_entry_set_text (sheet_entry, text);
2589 dispose_string (sheet, text);
2592 switch (attributes.justification)
2594 case GTK_JUSTIFY_RIGHT:
2595 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2597 case GTK_JUSTIFY_CENTER:
2598 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2600 case GTK_JUSTIFY_LEFT:
2602 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2608 psppire_sheet_size_allocate_entry (sheet);
2610 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2611 psppire_sheet_model_is_editable (sheet->model,
2613 gtk_widget_map (sheet->entry_widget);
2617 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2620 PsppireSheetRange range;
2622 row = sheet->active_cell.row;
2623 col = sheet->active_cell.col;
2625 if (row < 0 || col < 0) return FALSE;
2627 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2630 range.col0 = range.coli = col;
2631 range.row0 = range.rowi = row;
2633 psppire_sheet_draw_border (sheet, range);
2641 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
2645 rectangle_from_range (sheet, &new_range, &area);
2650 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
2652 area.x += sheet->cell_padding->left / 2;
2653 area.y += sheet->cell_padding->top / 2;
2654 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
2655 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
2657 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
2664 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
2669 /* Selection related functions */
2672 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
2675 sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
2677 sheet->range.col0 = sheet->range.coli = -1;
2678 sheet->range.row0 = sheet->range.rowi = row;
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_ROW], 0, row);
2690 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
2693 sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
2695 sheet->range.col0 = sheet->range.coli = column;
2696 sheet->range.row0 = sheet->range.rowi = -1;
2698 rectangle_from_range (sheet, &sheet->range, &area);
2702 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2704 g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, column);
2709 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
2712 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
2714 sheet->range = *range;
2716 rectangle_from_range (sheet, range, &area);
2719 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2724 psppire_sheet_unselect_range (PsppireSheet *sheet)
2727 sheet->select_status = PSPPIRE_SHEET_NORMAL;
2729 rectangle_from_range (sheet, &sheet->range, &area);
2732 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2734 g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, -1);
2735 g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, -1);
2739 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
2741 g_return_if_fail (sheet != NULL);
2742 *range = sheet->range;
2747 psppire_sheet_expose (GtkWidget *widget, GdkEventExpose *event)
2749 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2751 g_return_val_if_fail (event != NULL, FALSE);
2753 if (!GTK_WIDGET_DRAWABLE (widget))
2756 /* exposure events on the sheet */
2757 if (event->window == sheet->row_title_window &&
2758 sheet->row_titles_visible)
2760 draw_row_title_buttons_range (sheet,
2761 min_visible_row (sheet),
2762 max_visible_row (sheet));
2765 if (event->window == sheet->column_title_window &&
2766 sheet->column_titles_visible)
2768 draw_column_title_buttons_range (sheet,
2769 min_visible_column (sheet),
2770 max_visible_column (sheet));
2773 if (event->window == sheet->sheet_window)
2775 draw_sheet_region (sheet, event->region);
2777 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2780 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2781 psppire_sheet_range_draw (sheet, &sheet->range);
2783 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2784 psppire_sheet_range_draw (sheet, &sheet->drag_range);
2790 rectangle_from_range (sheet, &sheet->range, &area);
2792 gdk_draw_rectangle (sheet->sheet_window,
2795 area.x + 1, area.y + 1,
2796 area.width, area.height);
2800 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2801 draw_xor_rectangle (sheet, sheet->drag_range);
2806 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
2809 PsppireSheetRange range;
2810 range.row0 = range.rowi = sheet->active_cell.row;
2811 range.col0 = range.coli = sheet->active_cell.col;
2813 rectangle_from_range (sheet, &range, &rect);
2815 if (GDK_OVERLAP_RECTANGLE_OUT !=
2816 gdk_region_rect_in (event->region, &rect))
2818 psppire_sheet_draw_active_cell (sheet);
2824 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2831 psppire_sheet_button_press (GtkWidget *widget, GdkEventButton *event)
2833 PsppireSheet *sheet;
2834 GdkModifierType mods;
2839 g_return_val_if_fail (widget != NULL, FALSE);
2840 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
2841 g_return_val_if_fail (event != NULL, FALSE);
2843 sheet = PSPPIRE_SHEET (widget);
2845 /* Cancel any pending tooltips */
2846 if (sheet->motion_timer)
2848 g_source_remove (sheet->motion_timer);
2849 sheet->motion_timer = 0;
2852 gtk_widget_get_pointer (widget, &x, &y);
2853 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2856 if (event->window == sheet->column_title_window)
2858 sheet->x_drag = event->x;
2859 g_signal_emit (sheet,
2860 sheet_signals[BUTTON_EVENT_COLUMN], 0,
2863 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2865 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2866 g_signal_emit (sheet,
2867 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
2871 if (event->window == sheet->row_title_window)
2873 g_signal_emit (sheet,
2874 sheet_signals[BUTTON_EVENT_ROW], 0,
2877 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2879 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2880 g_signal_emit (sheet,
2881 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
2885 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
2887 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
2890 /* press on resize windows */
2891 if (event->window == sheet->column_title_window)
2893 sheet->x_drag = event->x;
2895 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
2897 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
2898 gdk_pointer_grab (sheet->column_title_window, FALSE,
2899 GDK_POINTER_MOTION_HINT_MASK |
2900 GDK_BUTTON1_MOTION_MASK |
2901 GDK_BUTTON_RELEASE_MASK,
2902 NULL, NULL, event->time);
2904 draw_xor_vline (sheet);
2909 if (event->window == sheet->row_title_window)
2911 sheet->y_drag = event->y;
2913 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
2915 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
2916 gdk_pointer_grab (sheet->row_title_window, FALSE,
2917 GDK_POINTER_MOTION_HINT_MASK |
2918 GDK_BUTTON1_MOTION_MASK |
2919 GDK_BUTTON_RELEASE_MASK,
2920 NULL, NULL, event->time);
2922 draw_xor_hline (sheet);
2927 /* the sheet itself does not handle other than single click events */
2928 if (event->type != GDK_BUTTON_PRESS) return FALSE;
2930 /* selections on the sheet */
2931 if (event->window == sheet->sheet_window)
2933 gtk_widget_get_pointer (widget, &x, &y);
2934 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2935 gdk_pointer_grab (sheet->sheet_window, FALSE,
2936 GDK_POINTER_MOTION_HINT_MASK |
2937 GDK_BUTTON1_MOTION_MASK |
2938 GDK_BUTTON_RELEASE_MASK,
2939 NULL, NULL, event->time);
2940 gtk_grab_add (GTK_WIDGET (sheet));
2942 if ( sheet->select_status == PSPPIRE_SHEET_NORMAL)
2944 sheet->range.row0 = row;
2945 sheet->range.col0 = column;
2949 psppire_sheet_unselect_range (sheet);
2951 psppire_sheet_click_cell (sheet, row, column);
2954 if (event->window == sheet->column_title_window)
2956 gtk_widget_get_pointer (widget, &x, &y);
2957 if ( sheet->row_titles_visible)
2958 x -= sheet->row_title_area.width;
2960 x += sheet->hadjustment->value;
2962 column = column_from_xpixel (sheet, x);
2964 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2966 gtk_grab_add (GTK_WIDGET (sheet));
2967 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2971 if (event->window == sheet->row_title_window)
2973 gtk_widget_get_pointer (widget, &x, &y);
2974 if ( sheet->column_titles_visible)
2975 y -= sheet->column_title_area.height;
2977 y += sheet->vadjustment->value;
2979 row = row_from_ypixel (sheet, y);
2980 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2982 gtk_grab_add (GTK_WIDGET (sheet));
2983 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2991 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
2993 PsppireSheetCell cell;
2994 gboolean forbid_move;
2999 if (row >= psppire_axis_unit_count (sheet->vaxis)
3000 || column >= psppire_axis_unit_count (sheet->haxis))
3005 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3006 &sheet->active_cell,
3012 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3015 row = sheet->active_cell.row;
3016 column = sheet->active_cell.col;
3018 change_active_cell (sheet, row, column);
3022 if (row == -1 && column >= 0)
3024 psppire_sheet_select_column (sheet, column);
3028 if (column == -1 && row >= 0)
3030 psppire_sheet_select_row (sheet, row);
3034 if (row == -1 && column == -1)
3036 sheet->range.row0 = 0;
3037 sheet->range.col0 = 0;
3038 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3040 psppire_axis_unit_count (sheet->haxis) - 1;
3041 psppire_sheet_select_range (sheet, NULL);
3045 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3046 change_active_cell (sheet, row, column);
3048 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3054 psppire_sheet_button_release (GtkWidget *widget,
3055 GdkEventButton *event)
3057 GdkDisplay *display = gtk_widget_get_display (widget);
3059 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3061 /* release on resize windows */
3062 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3065 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3066 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3068 gdk_display_pointer_ungrab (display, event->time);
3069 draw_xor_vline (sheet);
3072 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3073 + sheet->hadjustment->value;
3075 set_column_width (sheet, sheet->drag_cell.col, width);
3080 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3083 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3084 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3086 gdk_display_pointer_ungrab (display, event->time);
3087 draw_xor_hline (sheet);
3090 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3091 sheet->vadjustment->value;
3093 set_row_height (sheet, sheet->drag_cell.row, height);
3098 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3100 PsppireSheetRange old_range;
3101 draw_xor_rectangle (sheet, sheet->drag_range);
3102 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3103 gdk_display_pointer_ungrab (display, event->time);
3105 psppire_sheet_unselect_range (sheet);
3107 old_range = sheet->range;
3108 sheet->range = sheet->drag_range;
3109 sheet->drag_range = old_range;
3110 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3111 &sheet->drag_range, &sheet->range);
3112 psppire_sheet_select_range (sheet, &sheet->range);
3115 if (PSPPIRE_SHEET_IN_SELECTION (sheet))
3117 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3118 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3120 change_active_cell (sheet, sheet->active_cell.row,
3121 sheet->active_cell.col);
3124 gdk_display_pointer_ungrab (display, event->time);
3125 gtk_grab_remove (GTK_WIDGET (sheet));
3127 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3136 /* Shamelessly lifted from gtktooltips */
3138 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3142 gtk_widget_size_request (tip_window, &req);
3143 gtk_paint_flat_box (tip_window->style, tip_window->window,
3144 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3145 NULL, GTK_WIDGET(tip_window), "tooltip",
3146 0, 0, req.width, req.height);
3152 destroy_hover_window (PsppireSheetHoverTitle *h)
3154 gtk_widget_destroy (h->window);
3158 static PsppireSheetHoverTitle *
3159 create_hover_window (void)
3161 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3163 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3165 #if GTK_CHECK_VERSION (2, 9, 0)
3166 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3167 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3170 gtk_widget_set_app_paintable (hw->window, TRUE);
3171 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3172 gtk_widget_set_name (hw->window, "gtk-tooltips");
3173 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3175 g_signal_connect (hw->window,
3177 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3180 hw->label = gtk_label_new (NULL);
3183 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3184 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3186 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3188 gtk_widget_show (hw->label);
3190 g_signal_connect (hw->window,
3192 G_CALLBACK (gtk_widget_destroyed),
3198 #define HOVER_WINDOW_Y_OFFSET 2
3201 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3202 const gchar *subtitle)
3211 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3215 sheet->hover_window->row = row;
3216 sheet->hover_window->column = column;
3218 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3220 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3222 gtk_widget_show (sheet->hover_window->window);
3224 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3230 y += sheet->column_title_area.y;
3231 y += sheet->column_title_area.height;
3232 y += HOVER_WINDOW_Y_OFFSET;
3238 x += sheet->row_title_area.x;
3239 x += sheet->row_title_area.width * 2 / 3.0;
3242 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3247 motion_timeout_callback (gpointer data)
3249 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3253 gdk_threads_enter ();
3254 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3256 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3258 if (sheet->row_title_under && row >= 0)
3260 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3262 show_subtitle (sheet, row, -1, text);
3266 if (sheet->column_title_under && column >= 0)
3268 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3271 show_subtitle (sheet, -1, column, text);
3277 gdk_threads_leave ();
3282 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3284 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3285 GdkModifierType mods;
3286 GdkCursorType new_cursor;
3289 GdkDisplay *display;
3291 g_return_val_if_fail (event != NULL, FALSE);
3293 display = gtk_widget_get_display (widget);
3295 /* selections on the sheet */
3299 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3301 if ( sheet->motion_timer > 0 )
3302 g_source_remove (sheet->motion_timer);
3303 sheet->motion_timer =
3304 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3310 gtk_widget_get_pointer (widget, &wx, &wy);
3312 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3314 if ( row != sheet->hover_window->row ||
3315 column != sheet->hover_window->column)
3317 gtk_widget_hide (sheet->hover_window->window);
3322 if (event->window == sheet->column_title_window)
3324 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3325 on_column_boundary (sheet, x, &column))
3327 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3328 if (new_cursor != sheet->cursor_drag->type)
3330 gdk_cursor_unref (sheet->cursor_drag);
3331 sheet->cursor_drag =
3332 gdk_cursor_new_for_display (display, new_cursor);
3334 gdk_window_set_cursor (sheet->column_title_window,
3335 sheet->cursor_drag);
3340 new_cursor = GDK_TOP_LEFT_ARROW;
3341 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3342 new_cursor != sheet->cursor_drag->type)
3344 gdk_cursor_unref (sheet->cursor_drag);
3345 sheet->cursor_drag =
3346 gdk_cursor_new_for_display (display, new_cursor);
3347 gdk_window_set_cursor (sheet->column_title_window,
3348 sheet->cursor_drag);
3352 else if (event->window == sheet->row_title_window)
3354 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3355 on_row_boundary (sheet, y, &row))
3357 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3358 if (new_cursor != sheet->cursor_drag->type)
3360 gdk_cursor_unref (sheet->cursor_drag);
3361 sheet->cursor_drag =
3362 gdk_cursor_new_for_display (display, new_cursor);
3363 gdk_window_set_cursor (sheet->row_title_window,
3364 sheet->cursor_drag);
3369 new_cursor = GDK_TOP_LEFT_ARROW;
3370 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3371 new_cursor != sheet->cursor_drag->type)
3373 gdk_cursor_unref (sheet->cursor_drag);
3374 sheet->cursor_drag =
3375 gdk_cursor_new_for_display (display, new_cursor);
3376 gdk_window_set_cursor (sheet->row_title_window,
3377 sheet->cursor_drag);
3382 new_cursor = GDK_PLUS;
3383 if ( event->window == sheet->sheet_window &&
3384 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3385 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3386 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3387 new_cursor != sheet->cursor_drag->type)
3389 gdk_cursor_unref (sheet->cursor_drag);
3390 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3391 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3394 new_cursor = GDK_TOP_LEFT_ARROW;
3395 if ( event->window == sheet->sheet_window &&
3396 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ) &&
3397 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3398 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3399 new_cursor != sheet->cursor_drag->type)
3401 gdk_cursor_unref (sheet->cursor_drag);
3402 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3403 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3406 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3407 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3409 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3411 if (event->x != sheet->x_drag)
3413 draw_xor_vline (sheet);
3414 sheet->x_drag = event->x;
3415 draw_xor_vline (sheet);
3421 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3423 if (event->y != sheet->y_drag)
3425 draw_xor_hline (sheet);
3426 sheet->y_drag = event->y;
3427 draw_xor_hline (sheet);
3433 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3435 PsppireSheetRange aux;
3436 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3437 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3438 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3439 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3443 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3444 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3446 aux = sheet->drag_range;
3447 sheet->drag_range.row0 = sheet->range.row0 + row;
3448 sheet->drag_range.col0 = sheet->range.col0 + column;
3449 sheet->drag_range.rowi = sheet->range.rowi + row;
3450 sheet->drag_range.coli = sheet->range.coli + column;
3451 if (aux.row0 != sheet->drag_range.row0 ||
3452 aux.col0 != sheet->drag_range.col0)
3454 draw_xor_rectangle (sheet, aux);
3455 draw_xor_rectangle (sheet, sheet->drag_range);
3461 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3463 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
3464 column == sheet->active_cell.col) return TRUE;
3466 if ( mods & GDK_BUTTON1_MASK)
3468 if (PSPPIRE_SHEET_IN_SELECTION (sheet) )
3470 /* Redraw the old range */
3471 psppire_sheet_unselect_range (sheet);
3473 sheet->range.rowi = row;
3474 sheet->range.coli = column;
3476 /* Redraw the new range */
3477 psppire_sheet_select_range (sheet, &sheet->range);
3481 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3489 psppire_sheet_crossing_notify (GtkWidget *widget,
3490 GdkEventCrossing *event)
3492 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3494 if (event->window == sheet->column_title_window)
3495 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
3496 else if (event->window == sheet->row_title_window)
3497 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
3499 if (event->type == GDK_LEAVE_NOTIFY)
3500 gtk_widget_hide (sheet->hover_window->window);
3507 psppire_sheet_focus_in (GtkWidget *w,
3508 GdkEventFocus *event)
3510 PsppireSheet *sheet = PSPPIRE_SHEET (w);
3512 gtk_widget_grab_focus (sheet->entry_widget);
3520 psppire_sheet_entry_key_press (GtkWidget *widget,
3524 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
3529 /* Number of rows in a step-increment */
3530 #define ROWS_PER_STEP 1
3534 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
3536 gint old_row = sheet->active_cell.row ;
3537 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
3541 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
3542 min_visible_row (sheet));
3546 case GTK_SCROLL_PAGE_DOWN:
3547 gtk_adjustment_set_value (sheet->vadjustment,
3548 sheet->vadjustment->value +
3549 sheet->vadjustment->page_increment);
3551 case GTK_SCROLL_PAGE_UP:
3552 gtk_adjustment_set_value (sheet->vadjustment,
3553 sheet->vadjustment->value -
3554 sheet->vadjustment->page_increment);
3558 g_assert_not_reached ();
3563 vpixel += psppire_axis_start_pixel (sheet->vaxis,
3564 min_visible_row (sheet));
3566 new_row = row_from_ypixel (sheet, vpixel);
3568 change_active_cell (sheet, new_row,
3569 sheet->active_cell.col);
3574 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
3576 gint current_row = sheet->active_cell.row;
3577 gint current_col = sheet->active_cell.col;
3578 PsppireSheetCell new_cell ;
3579 gboolean forbidden = FALSE;
3581 new_cell.row = current_row;
3582 new_cell.col = current_col;
3586 case GTK_SCROLL_STEP_DOWN:
3589 case GTK_SCROLL_STEP_UP:
3592 case GTK_SCROLL_STEP_RIGHT:
3595 case GTK_SCROLL_STEP_LEFT:
3598 case GTK_SCROLL_STEP_FORWARD:
3601 psppire_sheet_model_get_column_count (sheet->model))
3607 case GTK_SCROLL_STEP_BACKWARD:
3609 if (new_cell.col < 0)
3612 psppire_sheet_model_get_column_count (sheet->model) - 1;
3617 g_assert_not_reached ();
3621 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3622 &sheet->active_cell,
3630 maximize_int (&new_cell.row, 0);
3631 maximize_int (&new_cell.col, 0);
3633 minimize_int (&new_cell.row,
3634 psppire_axis_unit_count (sheet->vaxis) - 1);
3636 minimize_int (&new_cell.col,
3637 psppire_axis_unit_count (sheet->haxis) - 1);
3639 change_active_cell (sheet, new_cell.row, new_cell.col);
3642 if ( new_cell.col > max_fully_visible_column (sheet))
3645 psppire_axis_start_pixel (sheet->haxis,
3647 hpos -= sheet->hadjustment->page_size;
3649 gtk_adjustment_set_value (sheet->hadjustment,
3652 else if ( new_cell.col < min_fully_visible_column (sheet))
3655 psppire_axis_start_pixel (sheet->haxis,
3658 gtk_adjustment_set_value (sheet->hadjustment,
3663 if ( new_cell.row > max_fully_visible_row (sheet))
3666 psppire_axis_start_pixel (sheet->vaxis,
3668 vpos -= sheet->vadjustment->page_size;
3670 gtk_adjustment_set_value (sheet->vadjustment,
3673 else if ( new_cell.row < min_fully_visible_row (sheet))
3676 psppire_axis_start_pixel (sheet->vaxis,
3679 gtk_adjustment_set_value (sheet->vadjustment,
3683 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3688 psppire_sheet_key_press (GtkWidget *widget,
3691 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3693 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3695 switch (key->keyval)
3698 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
3701 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
3703 case GDK_ISO_Left_Tab:
3704 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
3707 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
3711 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3714 step_sheet (sheet, GTK_SCROLL_STEP_UP);
3718 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
3721 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
3725 gtk_adjustment_set_value (sheet->vadjustment,
3726 sheet->vadjustment->lower);
3728 change_active_cell (sheet, 0,
3729 sheet->active_cell.col);
3734 gtk_adjustment_set_value (sheet->vadjustment,
3735 sheet->vadjustment->upper -
3736 sheet->vadjustment->page_size -
3737 sheet->vadjustment->page_increment);
3740 change_active_cellx (sheet,
3741 psppire_axis_unit_count (sheet->vaxis) - 1,
3742 sheet->active_cell.col);
3746 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
3757 psppire_sheet_size_request (GtkWidget *widget,
3758 GtkRequisition *requisition)
3760 PsppireSheet *sheet;
3762 g_return_if_fail (widget != NULL);
3763 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3764 g_return_if_fail (requisition != NULL);
3766 sheet = PSPPIRE_SHEET (widget);
3768 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
3769 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
3771 /* compute the size of the column title area */
3772 if (sheet->column_titles_visible)
3773 requisition->height += sheet->column_title_area.height;
3775 /* compute the size of the row title area */
3776 if (sheet->row_titles_visible)
3777 requisition->width += sheet->row_title_area.width;
3782 psppire_sheet_size_allocate (GtkWidget *widget,
3783 GtkAllocation *allocation)
3785 PsppireSheet *sheet;
3786 GtkAllocation sheet_allocation;
3789 g_return_if_fail (widget != NULL);
3790 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3791 g_return_if_fail (allocation != NULL);
3793 sheet = PSPPIRE_SHEET (widget);
3794 widget->allocation = *allocation;
3795 border_width = GTK_CONTAINER (widget)->border_width;
3797 if (GTK_WIDGET_REALIZED (widget))
3798 gdk_window_move_resize (widget->window,
3799 allocation->x + border_width,
3800 allocation->y + border_width,
3801 allocation->width - 2 * border_width,
3802 allocation->height - 2 * border_width);
3804 sheet_allocation.x = 0;
3805 sheet_allocation.y = 0;
3806 sheet_allocation.width = allocation->width - 2 * border_width;
3807 sheet_allocation.height = allocation->height - 2 * border_width;
3809 if (GTK_WIDGET_REALIZED (widget))
3810 gdk_window_move_resize (sheet->sheet_window,
3813 sheet_allocation.width,
3814 sheet_allocation.height);
3816 /* position the window which holds the column title buttons */
3817 sheet->column_title_area.x = 0;
3818 sheet->column_title_area.y = 0;
3819 sheet->column_title_area.width = sheet_allocation.width ;
3822 /* position the window which holds the row title buttons */
3823 sheet->row_title_area.x = 0;
3824 sheet->row_title_area.y = 0;
3825 sheet->row_title_area.height = sheet_allocation.height;
3827 if (sheet->row_titles_visible)
3828 sheet->column_title_area.x += sheet->row_title_area.width;
3830 if (sheet->column_titles_visible)
3831 sheet->row_title_area.y += sheet->column_title_area.height;
3833 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
3834 gdk_window_move_resize (sheet->column_title_window,
3835 sheet->column_title_area.x,
3836 sheet->column_title_area.y,
3837 sheet->column_title_area.width,
3838 sheet->column_title_area.height);
3841 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
3842 gdk_window_move_resize (sheet->row_title_window,
3843 sheet->row_title_area.x,
3844 sheet->row_title_area.y,
3845 sheet->row_title_area.width,
3846 sheet->row_title_area.height);
3848 size_allocate_global_button (sheet);
3852 gint width = sheet->column_title_area.width;
3854 if ( sheet->row_titles_visible)
3855 width -= sheet->row_title_area.width;
3857 g_object_set (sheet->haxis,
3858 "minimum-extent", width,
3865 gint height = sheet->row_title_area.height;
3867 if ( sheet->column_titles_visible)
3868 height -= sheet->column_title_area.height;
3870 g_object_set (sheet->vaxis,
3871 "minimum-extent", height,
3876 /* set the scrollbars adjustments */
3877 adjust_scrollbars (sheet);
3881 draw_column_title_buttons (PsppireSheet *sheet)
3885 if (!sheet->column_titles_visible) return;
3886 if (!GTK_WIDGET_REALIZED (sheet))
3889 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
3892 if (sheet->row_titles_visible)
3894 x = sheet->row_title_area.width;
3897 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
3899 sheet->column_title_area.width = width;
3900 sheet->column_title_area.x = x;
3901 gdk_window_move_resize (sheet->column_title_window,
3902 sheet->column_title_area.x,
3903 sheet->column_title_area.y,
3904 sheet->column_title_area.width,
3905 sheet->column_title_area.height);
3908 if (max_visible_column (sheet) ==
3909 psppire_axis_unit_count (sheet->haxis) - 1)
3910 gdk_window_clear_area (sheet->column_title_window,
3912 sheet->column_title_area.width,
3913 sheet->column_title_area.height);
3915 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
3917 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
3918 max_visible_column (sheet));
3922 draw_row_title_buttons (PsppireSheet *sheet)
3927 if (!sheet->row_titles_visible) return;
3928 if (!GTK_WIDGET_REALIZED (sheet))
3931 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
3933 if (sheet->column_titles_visible)
3935 y = sheet->column_title_area.height;
3938 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
3940 sheet->row_title_area.y = y;
3941 sheet->row_title_area.height = height;
3942 gdk_window_move_resize (sheet->row_title_window,
3943 sheet->row_title_area.x,
3944 sheet->row_title_area.y,
3945 sheet->row_title_area.width,
3946 sheet->row_title_area.height);
3949 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
3950 gdk_window_clear_area (sheet->row_title_window,
3952 sheet->row_title_area.width,
3953 sheet->row_title_area.height);
3955 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
3957 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
3958 max_visible_row (sheet));
3963 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
3965 GtkAllocation entry_alloc;
3966 PsppireSheetCellAttr attributes = { 0 };
3967 GtkEntry *sheet_entry;
3969 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3970 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
3972 sheet_entry = psppire_sheet_get_entry (sheet);
3974 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
3975 sheet->active_cell.col,
3979 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
3981 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
3983 style->bg[GTK_STATE_NORMAL] = attributes.background;
3984 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
3985 style->text[GTK_STATE_NORMAL] = attributes.foreground;
3986 style->bg[GTK_STATE_ACTIVE] = attributes.background;
3987 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
3988 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
3991 rectangle_from_cell (sheet, sheet->active_cell.row,
3992 sheet->active_cell.col, &entry_alloc);
3994 entry_alloc.x += sheet->cell_padding->left;
3995 entry_alloc.y += sheet->cell_padding->right;
3996 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
3997 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4000 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4001 entry_alloc.height);
4002 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4006 /* Copy the sheet's font to the entry widget */
4008 set_entry_widget_font (PsppireSheet *sheet)
4010 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4012 pango_font_description_free (style->font_desc);
4013 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4015 gtk_widget_modify_style (sheet->entry_widget, style);
4019 create_sheet_entry (PsppireSheet *sheet)
4021 if (sheet->entry_widget)
4023 gtk_widget_unparent (sheet->entry_widget);
4026 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4027 g_object_ref_sink (sheet->entry_widget);
4029 gtk_widget_size_request (sheet->entry_widget, NULL);
4031 if ( GTK_IS_ENTRY (sheet->entry_widget))
4033 g_object_set (sheet->entry_widget,
4038 if (GTK_WIDGET_REALIZED (sheet))
4040 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4041 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4042 gtk_widget_realize (sheet->entry_widget);
4045 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4046 G_CALLBACK (psppire_sheet_entry_key_press),
4049 set_entry_widget_font (sheet);
4051 gtk_widget_show (sheet->entry_widget);
4055 /* Finds the last child widget that happens to be of type GtkEntry */
4057 find_entry (GtkWidget *w, gpointer user_data)
4059 GtkWidget **entry = user_data;
4060 if ( GTK_IS_ENTRY (w))
4068 psppire_sheet_get_entry (PsppireSheet *sheet)
4070 GtkWidget *w = sheet->entry_widget;
4072 g_return_val_if_fail (sheet != NULL, NULL);
4073 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4074 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4076 while (! GTK_IS_ENTRY (w))
4078 GtkWidget *entry = NULL;
4080 if (GTK_IS_CONTAINER (w))
4082 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4091 return GTK_ENTRY (w);
4096 draw_button (PsppireSheet *sheet, GdkWindow *window,
4097 PsppireSheetButton *button, gboolean is_sensitive,
4098 GdkRectangle allocation)
4100 GtkShadowType shadow_type;
4101 gint text_width = 0, text_height = 0;
4102 PangoAlignment align = PANGO_ALIGN_LEFT;
4108 g_return_if_fail (sheet != NULL);
4109 g_return_if_fail (button != NULL);
4112 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4114 gdk_window_clear_area (window,
4115 allocation.x, allocation.y,
4116 allocation.width, allocation.height);
4118 gtk_widget_ensure_style (sheet->button);
4120 gtk_paint_box (sheet->button->style, window,
4121 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4123 GTK_WIDGET (sheet->button),
4125 allocation.x, allocation.y,
4126 allocation.width, allocation.height);
4128 state = button->state;
4129 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4131 if (state == GTK_STATE_ACTIVE)
4132 shadow_type = GTK_SHADOW_IN;
4134 shadow_type = GTK_SHADOW_OUT;
4136 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4137 gtk_paint_box (sheet->button->style, window,
4138 button->state, shadow_type,
4139 &allocation, GTK_WIDGET (sheet->button),
4141 allocation.x, allocation.y,
4142 allocation.width, allocation.height);
4144 if ( button->overstruck)
4146 GdkPoint points[2] = {
4147 {allocation.x, allocation.y},
4148 {allocation.x + allocation.width,
4149 allocation.y + allocation.height}
4152 gtk_paint_polygon (sheet->button->style,
4164 if (button->label_visible)
4166 text_height = DEFAULT_ROW_HEIGHT -
4167 2 * COLUMN_TITLES_HEIGHT;
4169 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4171 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4174 allocation.y += 2 * sheet->button->style->ythickness;
4176 if (button->label && strlen (button->label) > 0)
4178 PangoRectangle rect;
4179 gchar *line = button->label;
4181 PangoLayout *layout = NULL;
4182 gint real_x = allocation.x;
4183 gint real_y = allocation.y;
4185 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4186 pango_layout_get_extents (layout, NULL, &rect);
4188 text_width = PANGO_PIXELS (rect.width);
4189 switch (button->justification)
4191 case GTK_JUSTIFY_LEFT:
4192 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4193 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4195 case GTK_JUSTIFY_RIGHT:
4196 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4197 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4199 case GTK_JUSTIFY_CENTER:
4201 real_x = allocation.x + (allocation.width - text_width)/2;
4202 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4203 pango_layout_set_justify (layout, TRUE);
4205 pango_layout_set_alignment (layout, align);
4206 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4215 g_object_unref (layout);
4218 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4220 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4224 psppire_sheet_button_free (button);
4228 /* Draw the column title buttons FIRST through to LAST */
4230 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4234 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4236 if (!sheet->column_titles_visible) return;
4238 g_return_if_fail (first >= min_visible_column (sheet));
4239 g_return_if_fail (last <= max_visible_column (sheet));
4242 rect.height = sheet->column_title_area.height;
4243 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4244 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4245 + psppire_axis_unit_size (sheet->haxis, last);
4247 rect.x -= sheet->hadjustment->value;
4249 minimize_int (&rect.width, sheet->column_title_area.width);
4250 maximize_int (&rect.x, 0);
4252 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4254 for (col = first ; col <= last ; ++col)
4256 GdkRectangle allocation;
4257 gboolean is_sensitive = FALSE;
4259 PsppireSheetButton *
4260 button = psppire_sheet_model_get_column_button (sheet->model, col);
4262 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4264 allocation.x -= sheet->hadjustment->value;
4266 allocation.height = sheet->column_title_area.height;
4267 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4268 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4270 draw_button (sheet, sheet->column_title_window,
4271 button, is_sensitive, allocation);
4274 gdk_window_end_paint (sheet->column_title_window);
4279 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4283 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4285 if (!sheet->row_titles_visible) return;
4287 g_return_if_fail (first >= min_visible_row (sheet));
4288 g_return_if_fail (last <= max_visible_row (sheet));
4291 rect.width = sheet->row_title_area.width;
4292 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4293 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4294 + psppire_axis_unit_size (sheet->vaxis, last);
4296 rect.y -= sheet->vadjustment->value;
4298 minimize_int (&rect.height, sheet->row_title_area.height);
4299 maximize_int (&rect.y, 0);
4301 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4302 for (row = first; row <= last; ++row)
4304 GdkRectangle allocation;
4306 gboolean is_sensitive = FALSE;
4308 PsppireSheetButton *button =
4309 psppire_sheet_model_get_row_button (sheet->model, row);
4311 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4313 allocation.y -= sheet->vadjustment->value;
4315 allocation.width = sheet->row_title_area.width;
4316 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4317 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4319 draw_button (sheet, sheet->row_title_window,
4320 button, is_sensitive, allocation);
4323 gdk_window_end_paint (sheet->row_title_window);
4330 * vadjustment_value_changed
4331 * hadjustment_value_changed */
4335 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4338 (adj->value + adj->page_size)
4340 (adj->upper - adj->lower);
4342 const glong last_item = psppire_axis_unit_count (axis) - 1;
4344 if (isnan (position) || position < 0)
4348 psppire_axis_start_pixel (axis, last_item)
4350 psppire_axis_unit_size (axis, last_item)
4354 adj->page_size = page_size;
4357 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4359 if ( adj->value < adj->lower)
4360 adj->value = adj->lower;
4363 gtk_adjustment_changed (adj);
4368 adjust_scrollbars (PsppireSheet *sheet)
4372 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4375 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4377 if ( sheet->row_titles_visible)
4378 width -= sheet->row_title_area.width;
4380 if (sheet->column_titles_visible)
4381 height -= sheet->column_title_area.height;
4383 if (sheet->vadjustment)
4385 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4387 sheet->vadjustment->step_increment =
4389 psppire_axis_unit_size (sheet->vaxis, last_row);
4391 sheet->vadjustment->page_increment =
4393 sheet->column_title_area.height -
4394 psppire_axis_unit_size (sheet->vaxis, last_row);
4396 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4399 if (sheet->hadjustment)
4401 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4402 sheet->hadjustment->step_increment = 1;
4404 sheet->hadjustment->page_increment = width;
4406 sheet->hadjustment->upper =
4407 psppire_axis_start_pixel (sheet->haxis, last_col)
4409 psppire_axis_unit_size (sheet->haxis, last_col)
4412 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4416 /* Subtracts the region of WIDGET from REGION */
4418 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4421 GdkRectangle intersect;
4424 gdk_region_get_clipbox (region, &rect);
4425 gtk_widget_intersect (widget,
4429 region2 = gdk_region_rectangle (&intersect);
4430 gdk_region_subtract (region, region2);
4431 gdk_region_destroy (region2);
4435 vadjustment_value_changed (GtkAdjustment *adjustment,
4439 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4441 g_return_if_fail (adjustment != NULL);
4443 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4445 gtk_widget_hide (sheet->entry_widget);
4448 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4450 subtract_widget_region (region, sheet->button);
4451 gdk_window_begin_paint_region (sheet->sheet_window, region);
4453 draw_sheet_region (sheet, region);
4455 draw_row_title_buttons (sheet);
4456 psppire_sheet_draw_active_cell (sheet);
4458 gdk_window_end_paint (sheet->sheet_window);
4459 gdk_region_destroy (region);
4464 hadjustment_value_changed (GtkAdjustment *adjustment,
4468 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4470 g_return_if_fail (adjustment != NULL);
4472 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4474 gtk_widget_hide (sheet->entry_widget);
4478 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4480 subtract_widget_region (region, sheet->button);
4481 gdk_window_begin_paint_region (sheet->sheet_window, region);
4483 draw_sheet_region (sheet, region);
4485 draw_column_title_buttons (sheet);
4487 psppire_sheet_draw_active_cell (sheet);
4489 gdk_window_end_paint (sheet->sheet_window);
4491 gdk_region_destroy (region);
4495 /* COLUMN RESIZING */
4497 draw_xor_vline (PsppireSheet *sheet)
4500 gint xpos = sheet->x_drag;
4501 gdk_drawable_get_size (sheet->sheet_window,
4504 if (sheet->row_titles_visible)
4505 xpos += sheet->row_title_area.width;
4507 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4509 sheet->column_title_area.height,
4511 height + CELL_SPACING);
4516 draw_xor_hline (PsppireSheet *sheet)
4520 gint ypos = sheet->y_drag;
4522 gdk_drawable_get_size (sheet->sheet_window,
4526 if (sheet->column_titles_visible)
4527 ypos += sheet->column_title_area.height;
4529 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4530 sheet->row_title_area.width,
4532 width + CELL_SPACING,
4536 /* SELECTED RANGE */
4538 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
4541 GdkRectangle clip_area, area;
4544 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
4545 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
4546 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
4547 psppire_axis_unit_size (sheet->haxis, range.coli);
4548 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
4549 psppire_axis_unit_size (sheet->vaxis, range.rowi);
4551 clip_area.x = sheet->row_title_area.width;
4552 clip_area.y = sheet->column_title_area.height;
4554 gdk_drawable_get_size (sheet->sheet_window,
4555 &clip_area.width, &clip_area.height);
4557 if (!sheet->row_titles_visible) clip_area.x = 0;
4558 if (!sheet->column_titles_visible) clip_area.y = 0;
4562 area.width = area.width + area.x;
4565 if (area.width > clip_area.width) area.width = clip_area.width + 10;
4568 area.height = area.height + area.y;
4571 if (area.height > clip_area.height) area.height = clip_area.height + 10;
4575 clip_area.width += 3;
4576 clip_area.height += 3;
4578 gdk_gc_get_values (sheet->xor_gc, &values);
4580 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
4582 gdk_draw_rectangle (sheet->sheet_window,
4585 area.x + i, area.y + i,
4586 area.width - 2 * i, area.height - 2 * i);
4589 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4591 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
4596 set_column_width (PsppireSheet *sheet,
4600 g_return_if_fail (sheet != NULL);
4601 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4603 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
4609 psppire_axis_resize (sheet->haxis, column,
4610 width - sheet->cell_padding->left -
4611 sheet->cell_padding->right);
4613 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4615 draw_column_title_buttons (sheet);
4616 adjust_scrollbars (sheet);
4617 psppire_sheet_size_allocate_entry (sheet);
4618 redraw_range (sheet, NULL);
4623 set_row_height (PsppireSheet *sheet,
4627 g_return_if_fail (sheet != NULL);
4628 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4630 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
4636 psppire_axis_resize (sheet->vaxis, row,
4637 height - sheet->cell_padding->top -
4638 sheet->cell_padding->bottom);
4640 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
4642 draw_row_title_buttons (sheet);
4643 adjust_scrollbars (sheet);
4644 psppire_sheet_size_allocate_entry (sheet);
4645 redraw_range (sheet, NULL);
4650 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
4651 PsppireSheetCellAttr *attr)
4654 const GtkJustification *j ;
4655 GdkColormap *colormap;
4657 g_return_val_if_fail (sheet != NULL, FALSE);
4658 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
4660 if (row < 0 || col < 0) return FALSE;
4662 attr->foreground = GTK_WIDGET (sheet)->style->black;
4663 attr->background = sheet->color[BG_COLOR];
4665 attr->border.width = 0;
4666 attr->border.line_style = GDK_LINE_SOLID;
4667 attr->border.cap_style = GDK_CAP_NOT_LAST;
4668 attr->border.join_style = GDK_JOIN_MITER;
4669 attr->border.mask = 0;
4670 attr->border.color = GTK_WIDGET (sheet)->style->black;
4672 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
4673 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
4676 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
4677 attr->foreground = *fg;
4680 bg = psppire_sheet_model_get_background (sheet->model, row, col);
4683 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
4684 attr->background = *bg;
4687 attr->justification =
4688 psppire_sheet_model_get_column_justification (sheet->model, col);
4690 j = psppire_sheet_model_get_justification (sheet->model, row, col);
4692 attr->justification = *j;
4698 psppire_sheet_button_size_request (PsppireSheet *sheet,
4699 const PsppireSheetButton *button,
4700 GtkRequisition *button_requisition)
4702 GtkRequisition requisition;
4703 GtkRequisition label_requisition;
4705 label_requisition.height = DEFAULT_ROW_HEIGHT;
4706 label_requisition.width = COLUMN_MIN_WIDTH;
4708 requisition.height = DEFAULT_ROW_HEIGHT;
4709 requisition.width = COLUMN_MIN_WIDTH;
4712 *button_requisition = requisition;
4713 button_requisition->width = MAX (requisition.width, label_requisition.width);
4714 button_requisition->height = MAX (requisition.height, label_requisition.height);
4719 psppire_sheet_forall (GtkContainer *container,
4720 gboolean include_internals,
4721 GtkCallback callback,
4722 gpointer callback_data)
4724 PsppireSheet *sheet = PSPPIRE_SHEET (container);
4726 g_return_if_fail (callback != NULL);
4728 if (sheet->button && sheet->button->parent)
4729 (* callback) (sheet->button, callback_data);
4731 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
4732 (* callback) (sheet->entry_widget, callback_data);
4737 psppire_sheet_get_model (const PsppireSheet *sheet)
4739 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4741 return sheet->model;
4745 PsppireSheetButton *
4746 psppire_sheet_button_new (void)
4748 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
4750 button->state = GTK_STATE_NORMAL;
4751 button->label = NULL;
4752 button->label_visible = TRUE;
4753 button->justification = GTK_JUSTIFY_FILL;
4754 button->overstruck = FALSE;
4761 psppire_sheet_button_free (PsppireSheetButton *button)
4763 if (!button) return ;
4765 g_free (button->label);
4770 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
4772 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
4774 if ( NULL == celltext)
4777 g_string_append (string, celltext);
4783 range_to_text (const PsppireSheet *sheet)
4788 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4791 string = g_string_sized_new (80);
4793 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4795 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
4797 append_cell_text (string, sheet, r, c);
4798 g_string_append (string, "\t");
4800 append_cell_text (string, sheet, r, c);
4801 if ( r < sheet->range.rowi)
4802 g_string_append (string, "\n");
4809 range_to_html (const PsppireSheet *sheet)
4814 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4817 string = g_string_sized_new (480);
4819 g_string_append (string, "<html>\n");
4820 g_string_append (string, "<body>\n");
4821 g_string_append (string, "<table>\n");
4822 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4824 g_string_append (string, "<tr>\n");
4825 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
4827 g_string_append (string, "<td>");
4828 append_cell_text (string, sheet, r, c);
4829 g_string_append (string, "</td>\n");
4831 g_string_append (string, "</tr>\n");
4833 g_string_append (string, "</table>\n");
4834 g_string_append (string, "</body>\n");
4835 g_string_append (string, "</html>\n");
4847 primary_get_cb (GtkClipboard *clipboard,
4848 GtkSelectionData *selection_data,
4852 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4853 GString *string = NULL;
4857 case SELECT_FMT_TEXT:
4858 string = range_to_text (sheet);
4860 case SELECT_FMT_HTML:
4861 string = range_to_html (sheet);
4864 g_assert_not_reached ();
4867 gtk_selection_data_set (selection_data, selection_data->target,
4869 (const guchar *) string->str, string->len);
4870 g_string_free (string, TRUE);
4874 primary_clear_cb (GtkClipboard *clipboard,
4877 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4878 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4881 psppire_sheet_unselect_range (sheet);
4885 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
4887 static const GtkTargetEntry targets[] = {
4888 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
4889 { "STRING", 0, SELECT_FMT_TEXT },
4890 { "TEXT", 0, SELECT_FMT_TEXT },
4891 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
4892 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
4893 { "text/plain", 0, SELECT_FMT_TEXT },
4894 { "text/html", 0, SELECT_FMT_HTML }
4897 GtkClipboard *clipboard;
4899 if (!GTK_WIDGET_REALIZED (sheet))
4902 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
4903 GDK_SELECTION_PRIMARY);
4905 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
4907 if (!gtk_clipboard_set_with_owner (clipboard, targets,
4908 G_N_ELEMENTS (targets),
4909 primary_get_cb, primary_clear_cb,
4911 primary_clear_cb (clipboard, sheet);
4915 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
4916 gtk_clipboard_clear (clipboard);