2 Copyright (C) 2006, 2008 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.
61 #include <gdk/gdkkeysyms.h>
62 #include <gtk/gtksignal.h>
63 #include <gtk/gtkbutton.h>
64 #include <gtk/gtkadjustment.h>
65 #include <gtk/gtktypeutils.h>
66 #include <gtk/gtkentry.h>
67 #include <gtk/gtkcontainer.h>
68 #include <pango/pango.h>
69 #include "psppire-sheet.h"
70 #include <ui/gui/psppire-marshal.h>
71 #include <ui/gui/sheet/psppire-sheetmodel.h>
72 #include <libpspp/misc.h>
78 PSPPIRE_SHEET_IN_XDRAG = 1 << 1,
79 PSPPIRE_SHEET_IN_YDRAG = 1 << 2,
80 PSPPIRE_SHEET_IN_DRAG = 1 << 3,
81 PSPPIRE_SHEET_IN_SELECTION = 1 << 4,
82 PSPPIRE_SHEET_IN_RESIZE = 1 << 5
85 #define PSPPIRE_SHEET_FLAGS(sheet) (PSPPIRE_SHEET (sheet)->flags)
86 #define PSPPIRE_SHEET_SET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) |= (flag))
87 #define PSPPIRE_SHEET_UNSET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) &= ~ (flag))
89 #define PSPPIRE_SHEET_IN_XDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_XDRAG)
90 #define PSPPIRE_SHEET_IN_YDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_YDRAG)
91 #define PSPPIRE_SHEET_IN_DRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_DRAG)
92 #define PSPPIRE_SHEET_IN_SELECTION(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_SELECTION)
93 #define PSPPIRE_SHEET_IN_RESIZE(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_RESIZE)
95 #define CELL_SPACING 1
97 #define TIMEOUT_HOVER 300
98 #define COLUMN_MIN_WIDTH 10
99 #define COLUMN_TITLES_HEIGHT 4
100 #define DEFAULT_COLUMN_WIDTH 80
101 #define DEFAULT_ROW_HEIGHT 25
103 static void set_entry_widget_font (PsppireSheet *sheet);
105 static void psppire_sheet_update_primary_selection (PsppireSheet *sheet);
106 static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
107 static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
108 static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range);
111 static void set_row_height (PsppireSheet *sheet,
115 static void destroy_hover_window (PsppireSheetHoverTitle *);
116 static PsppireSheetHoverTitle *create_hover_window (void);
118 static GtkStateType psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col);
122 dispose_string (const PsppireSheet *sheet, gchar *text)
124 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
129 if (psppire_sheet_model_free_strings (model))
134 /* FIXME: Why bother with these two ? */
136 /* returns the column index from a pixel location */
138 column_from_xpixel (const PsppireSheet *sheet, gint pixel)
140 return psppire_axis_unit_at_pixel (sheet->haxis, pixel);
144 row_from_ypixel (const PsppireSheet *sheet, gint pixel)
146 return psppire_axis_unit_at_pixel (sheet->vaxis, pixel);
150 /* Return the lowest row number which is wholly or partially on
151 the visible range of the sheet */
153 min_visible_row (const PsppireSheet *sheet)
155 return row_from_ypixel (sheet, sheet->vadjustment->value);
159 min_fully_visible_row (const PsppireSheet *sheet)
161 glong row = min_visible_row (sheet);
163 if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value)
170 max_visible_row (const PsppireSheet *sheet)
172 return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size);
177 max_fully_visible_row (const PsppireSheet *sheet)
179 glong row = max_visible_row (sheet);
181 if ( psppire_axis_start_pixel (sheet->vaxis, row)
183 psppire_axis_unit_size (sheet->vaxis, row)
184 > sheet->vadjustment->value)
191 /* Returns the lowest column number which is wholly or partially
194 min_visible_column (const PsppireSheet *sheet)
196 return column_from_xpixel (sheet, sheet->hadjustment->value);
200 min_fully_visible_column (const PsppireSheet *sheet)
202 glong col = min_visible_column (sheet);
204 if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value)
211 /* Returns the highest column number which is wholly or partially
214 max_visible_column (const PsppireSheet *sheet)
216 return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size);
220 max_fully_visible_column (const PsppireSheet *sheet)
222 glong col = max_visible_column (sheet);
224 if ( psppire_axis_start_pixel (sheet->haxis, col)
226 psppire_axis_unit_size (sheet->haxis, col)
227 > sheet->hadjustment->value)
235 /* The size of the region (in pixels) around the row/column boundaries
236 where the height/width may be grabbed to change size */
240 on_column_boundary (const PsppireSheet *sheet, gint x, gint *column)
245 x += sheet->hadjustment->value;
250 col = column_from_xpixel (sheet, x);
252 pixel = x - DRAG_WIDTH / 2;
256 if ( column_from_xpixel (sheet, pixel) < col )
262 if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col )
272 on_row_boundary (const PsppireSheet *sheet, gint y, gint *row)
277 y += sheet->vadjustment->value;
282 r = row_from_ypixel (sheet, y);
284 pixel = y - DRAG_WIDTH / 2;
288 if ( row_from_ypixel (sheet, pixel) < r )
294 if ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r )
304 static inline gboolean
305 POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y,
306 gint *drag_row, gint *drag_column)
310 /* Can't drag if nothing is selected */
311 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
312 sheet->range.col0 < 0 || sheet->range.coli < 0 )
315 *drag_column = column_from_xpixel (sheet, x);
316 *drag_row = row_from_ypixel (sheet, y);
318 if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 &&
319 x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
320 psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2)
322 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0);
323 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
325 *drag_row = sheet->range.row0;
328 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
329 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
330 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
332 *drag_row = sheet->range.rowi;
337 if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 &&
338 y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
339 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2)
341 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0);
342 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
344 *drag_column = sheet->range.col0;
347 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
348 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
349 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
351 *drag_column = sheet->range.coli;
359 static inline gboolean
360 POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y,
361 gint *drag_row, gint *drag_column)
365 /* Can't drag if nothing is selected */
366 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
367 sheet->range.col0 < 0 || sheet->range.coli < 0 )
370 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+
371 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
373 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
374 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
376 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED)
377 ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet));
379 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED)
380 xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet));
382 *drag_column = column_from_xpixel (sheet, x);
383 *drag_row = row_from_ypixel (sheet, y);
385 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
386 y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
393 rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *range,
396 g_return_val_if_fail (range, FALSE);
398 r->x = psppire_axis_start_pixel (sheet->haxis, range->col0);
399 r->x -= round (sheet->hadjustment->value);
401 r->y = psppire_axis_start_pixel (sheet->vaxis, range->row0);
402 r->y -= round (sheet->vadjustment->value);
404 r->width = psppire_axis_start_pixel (sheet->haxis, range->coli) -
405 psppire_axis_start_pixel (sheet->haxis, range->col0) +
406 psppire_axis_unit_size (sheet->haxis, range->coli);
408 r->height = psppire_axis_start_pixel (sheet->vaxis, range->rowi) -
409 psppire_axis_start_pixel (sheet->vaxis, range->row0) +
410 psppire_axis_unit_size (sheet->vaxis, range->rowi);
412 if ( sheet->column_titles_visible)
414 r->y += sheet->column_title_area.height;
417 if ( sheet->row_titles_visible)
419 r->x += sheet->row_title_area.width;
426 rectangle_from_cell (PsppireSheet *sheet, gint row, gint col,
429 PsppireSheetRange range;
430 g_return_val_if_fail (row >= 0, FALSE);
431 g_return_val_if_fail (col >= 0, FALSE);
433 range.row0 = range.rowi = row;
434 range.col0 = range.coli = col;
436 return rectangle_from_range (sheet, &range, r);
440 static void psppire_sheet_class_init (PsppireSheetClass *klass);
441 static void psppire_sheet_init (PsppireSheet *sheet);
442 static void psppire_sheet_dispose (GObject *object);
443 static void psppire_sheet_finalize (GObject *object);
444 static void psppire_sheet_style_set (GtkWidget *widget,
445 GtkStyle *previous_style);
446 static void psppire_sheet_realize (GtkWidget *widget);
447 static void psppire_sheet_unrealize (GtkWidget *widget);
448 static void psppire_sheet_map (GtkWidget *widget);
449 static void psppire_sheet_unmap (GtkWidget *widget);
450 static gint psppire_sheet_expose (GtkWidget *widget,
451 GdkEventExpose *event);
453 static void psppire_sheet_forall (GtkContainer *container,
454 gboolean include_internals,
455 GtkCallback callback,
456 gpointer callback_data);
458 static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
459 GtkAdjustment *hadjustment,
460 GtkAdjustment *vadjustment);
462 static gint psppire_sheet_button_press (GtkWidget *widget,
463 GdkEventButton *event);
464 static gint psppire_sheet_button_release (GtkWidget *widget,
465 GdkEventButton *event);
466 static gint psppire_sheet_motion (GtkWidget *widget,
467 GdkEventMotion *event);
468 static gboolean psppire_sheet_crossing_notify (GtkWidget *widget,
469 GdkEventCrossing *event);
470 static gint psppire_sheet_entry_key_press (GtkWidget *widget,
472 static gboolean psppire_sheet_key_press (GtkWidget *widget,
474 static void psppire_sheet_size_request (GtkWidget *widget,
475 GtkRequisition *requisition);
476 static void psppire_sheet_size_allocate (GtkWidget *widget,
477 GtkAllocation *allocation);
481 static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
482 const PsppireSheetRange *range);
483 static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet,
484 gint row, gint column);
485 /* Drawing Routines */
488 static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column);
491 /* draw visible part of range. */
492 static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region);
495 /* highlight the visible part of the selected range */
496 static void psppire_sheet_range_draw_selection (PsppireSheet *sheet,
497 PsppireSheetRange range);
501 static void psppire_sheet_real_select_range (PsppireSheet *sheet,
502 const PsppireSheetRange *range);
503 static void psppire_sheet_real_unselect_range (PsppireSheet *sheet,
504 const PsppireSheetRange *range);
505 static void psppire_sheet_extend_selection (PsppireSheet *sheet,
506 gint row, gint column);
507 static void psppire_sheet_new_selection (PsppireSheet *sheet,
508 PsppireSheetRange *range);
509 static void psppire_sheet_draw_border (PsppireSheet *sheet,
510 PsppireSheetRange range);
512 /* Active Cell handling */
514 static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet);
515 static void change_active_cell (PsppireSheet *sheet, gint row, gint col);
516 static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet);
517 static void psppire_sheet_show_entry_widget (PsppireSheet *sheet);
518 static gboolean psppire_sheet_click_cell (PsppireSheet *sheet,
525 static void adjust_scrollbars (PsppireSheet *sheet);
526 static void vadjustment_value_changed (GtkAdjustment *adjustment,
528 static void hadjustment_value_changed (GtkAdjustment *adjustment,
532 static void draw_xor_vline (PsppireSheet *sheet);
533 static void draw_xor_hline (PsppireSheet *sheet);
534 static void draw_xor_rectangle (PsppireSheet *sheet,
535 PsppireSheetRange range);
539 static void create_global_button (PsppireSheet *sheet);
540 static void global_button_clicked (GtkWidget *widget,
544 static void create_sheet_entry (PsppireSheet *sheet);
545 static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet);
547 /* Sheet button gadgets */
549 static void draw_column_title_buttons (PsppireSheet *sheet);
550 static void draw_row_title_buttons (PsppireSheet *sheet);
553 static void size_allocate_global_button (PsppireSheet *sheet);
554 static void psppire_sheet_button_size_request (PsppireSheet *sheet,
555 const PsppireSheetButton *button,
556 GtkRequisition *requisition);
558 static void psppire_sheet_real_cell_clear (PsppireSheet *sheet,
580 static GtkContainerClass *parent_class = NULL;
581 static guint sheet_signals[LAST_SIGNAL] = { 0 };
585 psppire_sheet_get_type ()
587 static GType sheet_type = 0;
591 static const GTypeInfo sheet_info =
593 sizeof (PsppireSheetClass),
596 (GClassInitFunc) psppire_sheet_class_init,
599 sizeof (PsppireSheet),
601 (GInstanceInitFunc) psppire_sheet_init,
606 g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
614 static PsppireSheetRange*
615 psppire_sheet_range_copy (const PsppireSheetRange *range)
617 PsppireSheetRange *new_range;
619 g_return_val_if_fail (range != NULL, NULL);
621 new_range = g_new (PsppireSheetRange, 1);
629 psppire_sheet_range_free (PsppireSheetRange *range)
631 g_return_if_fail (range != NULL);
637 psppire_sheet_range_get_type (void)
639 static GType sheet_range_type = 0;
641 if (!sheet_range_type)
644 g_boxed_type_register_static ("PsppireSheetRange",
645 (GBoxedCopyFunc) psppire_sheet_range_copy,
646 (GBoxedFreeFunc) psppire_sheet_range_free);
649 return sheet_range_type;
652 static PsppireSheetCell*
653 psppire_sheet_cell_copy (const PsppireSheetCell *cell)
655 PsppireSheetCell *new_cell;
657 g_return_val_if_fail (cell != NULL, NULL);
659 new_cell = g_new (PsppireSheetCell, 1);
667 psppire_sheet_cell_free (PsppireSheetCell *cell)
669 g_return_if_fail (cell != NULL);
675 psppire_sheet_cell_get_type (void)
677 static GType sheet_cell_type = 0;
679 if (!sheet_cell_type)
682 g_boxed_type_register_static ("PsppireSheetCell",
683 (GBoxedCopyFunc) psppire_sheet_cell_copy,
684 (GBoxedFreeFunc) psppire_sheet_cell_free);
687 return sheet_cell_type;
701 resize_column (PsppireSheet *sheet, gint unit, glong size)
703 PsppireSheetRange range;
705 range.coli = max_visible_column (sheet);
706 range.row0 = min_visible_row (sheet);
707 range.rowi = max_visible_row (sheet);
709 redraw_range (sheet, &range);
711 draw_column_title_buttons_range (sheet, range.col0, range.coli);
716 psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
719 g_object_unref (sheet->haxis);
722 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
725 g_object_ref (sheet->haxis);
729 resize_row (PsppireSheet *sheet, gint unit, glong size)
731 PsppireSheetRange range;
732 range.col0 = min_visible_column (sheet);
733 range.coli = max_visible_column (sheet);
735 range.rowi = max_visible_row (sheet);
737 redraw_range (sheet, &range);
739 draw_row_title_buttons_range (sheet, range.row0, range.rowi);
743 psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
746 g_object_unref (sheet->vaxis);
750 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
753 g_object_ref (sheet->vaxis);
758 psppire_sheet_set_property (GObject *object,
764 PsppireSheet *sheet = PSPPIRE_SHEET (object);
769 psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
772 psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
775 psppire_sheet_set_model (sheet, g_value_get_pointer (value));
778 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
784 psppire_sheet_get_property (GObject *object,
789 PsppireSheet *sheet = PSPPIRE_SHEET (object);
794 g_value_set_pointer (value, sheet->vaxis);
797 g_value_set_pointer (value, sheet->haxis);
800 g_value_set_pointer (value, sheet->model);
803 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
810 psppire_sheet_class_init (PsppireSheetClass *klass)
812 GObjectClass *object_class = G_OBJECT_CLASS (klass);
814 GParamSpec *haxis_spec ;
815 GParamSpec *vaxis_spec ;
816 GParamSpec *model_spec ;
818 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
819 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
821 parent_class = g_type_class_peek_parent (klass);
824 * PsppireSheet::select-row
825 * @sheet: the sheet widget that emitted the signal
826 * @row: the newly selected row index
828 * A row has been selected.
830 sheet_signals[SELECT_ROW] =
831 g_signal_new ("select-row",
832 G_TYPE_FROM_CLASS (object_class),
834 offsetof (PsppireSheetClass, select_row),
836 g_cclosure_marshal_VOID__INT,
843 * PsppireSheet::select - column
844 * @sheet: the sheet widget that emitted the signal
845 * @column: the newly selected column index
847 * A column has been selected.
849 sheet_signals[SELECT_COLUMN] =
850 g_signal_new ("select-column",
851 G_TYPE_FROM_CLASS (object_class),
853 offsetof (PsppireSheetClass, select_column),
855 g_cclosure_marshal_VOID__INT,
862 * PsppireSheet::double-click-row
863 * @sheet: the sheet widget that emitted the signal
864 * @row: the row that was double clicked.
866 * A row's title button has been double clicked
868 sheet_signals[DOUBLE_CLICK_ROW] =
869 g_signal_new ("double-click-row",
870 G_TYPE_FROM_CLASS (object_class),
874 g_cclosure_marshal_VOID__INT,
881 * PsppireSheet::double-click-column
882 * @sheet: the sheet widget that emitted the signal
883 * @column: the column that was double clicked.
885 * A column's title button has been double clicked
887 sheet_signals[DOUBLE_CLICK_COLUMN] =
888 g_signal_new ("double-click-column",
889 G_TYPE_FROM_CLASS (object_class),
893 g_cclosure_marshal_VOID__INT,
900 * PsppireSheet::button-event-column
901 * @sheet: the sheet widget that emitted the signal
902 * @column: the column on which the event occured.
904 * A button event occured on a column title button
906 sheet_signals[BUTTON_EVENT_COLUMN] =
907 g_signal_new ("button-event-column",
908 G_TYPE_FROM_CLASS (object_class),
912 psppire_marshal_VOID__INT_POINTER,
921 * PsppireSheet::button-event-row
922 * @sheet: the sheet widget that emitted the signal
923 * @column: the column on which the event occured.
925 * A button event occured on a row title button
927 sheet_signals[BUTTON_EVENT_ROW] =
928 g_signal_new ("button-event-row",
929 G_TYPE_FROM_CLASS (object_class),
933 psppire_marshal_VOID__INT_POINTER,
941 sheet_signals[SELECT_RANGE] =
942 g_signal_new ("select-range",
943 G_TYPE_FROM_CLASS (object_class),
945 offsetof (PsppireSheetClass, select_range),
947 g_cclosure_marshal_VOID__BOXED,
950 PSPPIRE_TYPE_SHEET_RANGE);
953 sheet_signals[RESIZE_RANGE] =
954 g_signal_new ("resize-range",
955 G_TYPE_FROM_CLASS (object_class),
957 offsetof (PsppireSheetClass, resize_range),
959 psppire_marshal_VOID__BOXED_BOXED,
962 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
965 sheet_signals[MOVE_RANGE] =
966 g_signal_new ("move-range",
967 G_TYPE_FROM_CLASS (object_class),
969 offsetof (PsppireSheetClass, move_range),
971 psppire_marshal_VOID__BOXED_BOXED,
974 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
977 sheet_signals[TRAVERSE] =
978 g_signal_new ("traverse",
979 G_TYPE_FROM_CLASS (object_class),
981 offsetof (PsppireSheetClass, traverse),
983 psppire_marshal_BOOLEAN__BOXED_POINTER,
985 PSPPIRE_TYPE_SHEET_CELL,
989 sheet_signals[ACTIVATE] =
990 g_signal_new ("activate",
991 G_TYPE_FROM_CLASS (object_class),
993 offsetof (PsppireSheetClass, activate),
995 psppire_marshal_VOID__INT_INT_INT_INT,
997 G_TYPE_INT, G_TYPE_INT,
998 G_TYPE_INT, G_TYPE_INT);
1000 widget_class->set_scroll_adjustments_signal =
1001 g_signal_new ("set-scroll-adjustments",
1002 G_TYPE_FROM_CLASS (object_class),
1004 offsetof (PsppireSheetClass, set_scroll_adjustments),
1006 psppire_marshal_VOID__OBJECT_OBJECT,
1007 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1010 container_class->add = NULL;
1011 container_class->remove = NULL;
1012 container_class->forall = psppire_sheet_forall;
1014 object_class->dispose = psppire_sheet_dispose;
1015 object_class->finalize = psppire_sheet_finalize;
1019 g_param_spec_pointer ("vertical-axis",
1021 "A pointer to the PsppireAxis object for the rows",
1022 G_PARAM_READABLE | G_PARAM_WRITABLE );
1025 g_param_spec_pointer ("horizontal-axis",
1027 "A pointer to the PsppireAxis object for the columns",
1028 G_PARAM_READABLE | G_PARAM_WRITABLE );
1031 g_param_spec_pointer ("model",
1033 "A pointer to the data model",
1034 G_PARAM_READABLE | G_PARAM_WRITABLE );
1037 object_class->set_property = psppire_sheet_set_property;
1038 object_class->get_property = psppire_sheet_get_property;
1040 g_object_class_install_property (object_class,
1044 g_object_class_install_property (object_class,
1048 g_object_class_install_property (object_class,
1053 widget_class->realize = psppire_sheet_realize;
1054 widget_class->unrealize = psppire_sheet_unrealize;
1055 widget_class->map = psppire_sheet_map;
1056 widget_class->unmap = psppire_sheet_unmap;
1057 widget_class->style_set = psppire_sheet_style_set;
1058 widget_class->button_press_event = psppire_sheet_button_press;
1059 widget_class->button_release_event = psppire_sheet_button_release;
1060 widget_class->motion_notify_event = psppire_sheet_motion;
1061 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1062 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1063 widget_class->key_press_event = psppire_sheet_key_press;
1064 widget_class->expose_event = psppire_sheet_expose;
1065 widget_class->size_request = psppire_sheet_size_request;
1066 widget_class->size_allocate = psppire_sheet_size_allocate;
1067 widget_class->focus_in_event = NULL;
1068 widget_class->focus_out_event = NULL;
1070 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1071 klass->select_row = NULL;
1072 klass->select_column = NULL;
1073 klass->select_range = NULL;
1074 klass->resize_range = NULL;
1075 klass->move_range = NULL;
1076 klass->traverse = NULL;
1077 klass->activate = NULL;
1078 klass->changed = NULL;
1082 psppire_sheet_init (PsppireSheet *sheet)
1084 sheet->model = NULL;
1085 sheet->haxis = NULL;
1086 sheet->vaxis = NULL;
1089 sheet->selection_mode = GTK_SELECTION_NONE;
1090 sheet->state = PSPPIRE_SHEET_NORMAL;
1092 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1093 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1095 sheet->column_title_window = NULL;
1096 sheet->column_title_area.x = 0;
1097 sheet->column_title_area.y = 0;
1098 sheet->column_title_area.width = 0;
1099 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1101 sheet->row_title_window = NULL;
1102 sheet->row_title_area.x = 0;
1103 sheet->row_title_area.y = 0;
1104 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1105 sheet->row_title_area.height = 0;
1108 sheet->active_cell.row = 0;
1109 sheet->active_cell.col = 0;
1110 sheet->selection_cell.row = 0;
1111 sheet->selection_cell.col = 0;
1113 sheet->range.row0 = 0;
1114 sheet->range.rowi = 0;
1115 sheet->range.col0 = 0;
1116 sheet->range.coli = 0;
1118 sheet->state = PSPPIRE_SHEET_NORMAL;
1120 sheet->sheet_window = NULL;
1121 sheet->entry_widget = NULL;
1122 sheet->button = NULL;
1124 sheet->hadjustment = NULL;
1125 sheet->vadjustment = NULL;
1127 sheet->cursor_drag = NULL;
1129 sheet->xor_gc = NULL;
1130 sheet->fg_gc = NULL;
1131 sheet->bg_gc = NULL;
1134 sheet->show_grid = TRUE;
1136 sheet->motion_timer = 0;
1138 sheet->row_titles_visible = TRUE;
1139 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1141 sheet->column_titles_visible = TRUE;
1144 /* create sheet entry */
1145 sheet->entry_type = GTK_TYPE_ENTRY;
1146 create_sheet_entry (sheet);
1148 /* create global selection button */
1149 create_global_button (sheet);
1153 /* Cause RANGE to be redrawn. If RANGE is null, then the
1154 entire visible range will be redrawn.
1157 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1161 if ( ! GTK_WIDGET_REALIZED (sheet))
1164 if ( NULL != range )
1165 rectangle_from_range (sheet, range, &rect);
1168 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1169 gdk_region_get_clipbox (r, &rect);
1171 if ( sheet->column_titles_visible)
1173 rect.y += sheet->column_title_area.height;
1174 rect.height -= sheet->column_title_area.height;
1177 if ( sheet->row_titles_visible)
1179 rect.x += sheet->row_title_area.width;
1180 rect.width -= sheet->row_title_area.width;
1184 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1188 /* Callback which occurs whenever columns are inserted / deleted in the model */
1190 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1194 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1196 PsppireSheetRange range;
1197 gint model_columns = psppire_sheet_model_get_column_count (model);
1200 /* Need to update all the columns starting from the first column and onwards.
1201 * Previous column are unchanged, so don't need to be updated.
1203 range.col0 = first_column;
1205 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1206 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1208 adjust_scrollbars (sheet);
1210 if (sheet->active_cell.col >= model_columns)
1211 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1213 draw_column_title_buttons_range (sheet,
1214 first_column, max_visible_column (sheet));
1217 redraw_range (sheet, &range);
1223 /* Callback which occurs whenever rows are inserted / deleted in the model */
1225 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1226 gint n_rows, gpointer data)
1228 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1230 PsppireSheetRange range;
1232 gint model_rows = psppire_sheet_model_get_row_count (model);
1234 /* Need to update all the rows starting from the first row and onwards.
1235 * Previous rows are unchanged, so don't need to be updated.
1237 range.row0 = first_row;
1239 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1240 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1242 adjust_scrollbars (sheet);
1244 if (sheet->active_cell.row >= model_rows)
1245 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1247 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1249 redraw_range (sheet, &range);
1253 If row0 or rowi are negative, then all rows will be updated.
1254 If col0 or coli are negative, then all columns will be updated.
1257 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1258 gint rowi, gint coli, gpointer data)
1260 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1262 PsppireSheetRange range;
1269 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1272 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1274 redraw_range (sheet, NULL);
1275 adjust_scrollbars (sheet);
1277 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1278 max_visible_row (sheet));
1280 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1281 max_visible_column (sheet));
1285 else if ( row0 < 0 || rowi < 0 )
1287 range.row0 = min_visible_row (sheet);
1288 range.rowi = max_visible_row (sheet);
1290 else if ( col0 < 0 || coli < 0 )
1292 range.col0 = min_visible_column (sheet);
1293 range.coli = max_visible_column (sheet);
1296 redraw_range (sheet, &range);
1301 * psppire_sheet_new:
1302 * @rows: initial number of rows
1303 * @columns: initial number of columns
1304 * @title: sheet title
1305 * @model: the model to use for the sheet data
1307 * Creates a new sheet widget with the given number of rows and columns.
1309 * Returns: the new sheet widget
1312 psppire_sheet_new (PsppireSheetModel *model)
1314 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1322 * psppire_sheet_set_model
1323 * @sheet: the sheet to set the model for
1324 * @model: the model to use for the sheet data
1326 * Sets the model for a PsppireSheet
1330 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1332 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1334 if (sheet->model ) g_object_unref (sheet->model);
1336 sheet->model = model;
1340 g_object_ref (model);
1342 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1343 G_CALLBACK (range_update_callback),
1346 g_signal_connect (model, "rows_inserted",
1347 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1349 g_signal_connect (model, "rows_deleted",
1350 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1352 g_signal_connect (model, "columns_inserted",
1353 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1355 g_signal_connect (model, "columns_deleted",
1356 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1362 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1366 g_return_if_fail (sheet != NULL);
1367 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1369 state = sheet->state;
1371 if (sheet->state == PSPPIRE_SHEET_NORMAL)
1372 psppire_sheet_hide_entry_widget (sheet);
1374 sheet->entry_type = entry_type;
1376 create_sheet_entry (sheet);
1378 if (state == PSPPIRE_SHEET_NORMAL)
1380 psppire_sheet_show_entry_widget (sheet);
1386 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1388 g_return_if_fail (sheet != NULL);
1389 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1391 if (show == sheet->show_grid) return;
1393 sheet->show_grid = show;
1395 redraw_range (sheet, NULL);
1399 psppire_sheet_grid_visible (PsppireSheet *sheet)
1401 g_return_val_if_fail (sheet != NULL, 0);
1402 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1404 return sheet->show_grid;
1408 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1410 g_return_val_if_fail (sheet != NULL, 0);
1411 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1413 return psppire_axis_unit_count (sheet->haxis);
1416 static void set_column_width (PsppireSheet *sheet,
1422 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1424 if (sheet->column_titles_visible) return;
1426 sheet->column_titles_visible = TRUE;
1428 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1431 gdk_window_show (sheet->column_title_window);
1432 gdk_window_move_resize (sheet->column_title_window,
1433 sheet->column_title_area.x,
1434 sheet->column_title_area.y,
1435 sheet->column_title_area.width,
1436 sheet->column_title_area.height);
1438 adjust_scrollbars (sheet);
1440 if (sheet->vadjustment)
1441 g_signal_emit_by_name (sheet->vadjustment,
1444 size_allocate_global_button (sheet);
1446 if ( sheet->row_titles_visible)
1447 gtk_widget_show (sheet->button);
1452 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1454 if (sheet->row_titles_visible) return;
1456 sheet->row_titles_visible = TRUE;
1459 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1461 gdk_window_show (sheet->row_title_window);
1462 gdk_window_move_resize (sheet->row_title_window,
1463 sheet->row_title_area.x,
1464 sheet->row_title_area.y,
1465 sheet->row_title_area.width,
1466 sheet->row_title_area.height);
1468 adjust_scrollbars (sheet);
1471 if (sheet->hadjustment)
1472 g_signal_emit_by_name (sheet->hadjustment,
1475 size_allocate_global_button (sheet);
1477 if ( sheet->column_titles_visible)
1478 gtk_widget_show (sheet->button);
1482 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1484 if (!sheet->column_titles_visible) return;
1486 sheet->column_titles_visible = FALSE;
1488 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1490 if (sheet->column_title_window)
1491 gdk_window_hide (sheet->column_title_window);
1493 gtk_widget_hide (sheet->button);
1495 adjust_scrollbars (sheet);
1498 if (sheet->vadjustment)
1499 g_signal_emit_by_name (sheet->vadjustment,
1504 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1506 if (!sheet->row_titles_visible) return;
1508 sheet->row_titles_visible = FALSE;
1510 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1512 if (sheet->row_title_window)
1513 gdk_window_hide (sheet->row_title_window);
1515 gtk_widget_hide (sheet->button);
1517 adjust_scrollbars (sheet);
1520 if (sheet->hadjustment)
1521 g_signal_emit_by_name (sheet->hadjustment,
1526 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1527 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1528 at the {top,left} of the sheet. If it's 1, then it'll
1529 be placed at the {bottom,right}.
1530 ROW or COL may be -1, in which case scrolling in that dimension
1534 psppire_sheet_moveto (PsppireSheet *sheet,
1542 g_return_if_fail (row_align >= 0);
1543 g_return_if_fail (col_align >= 0);
1545 g_return_if_fail (row_align <= 1);
1546 g_return_if_fail (col_align <= 1);
1548 g_return_if_fail (col <
1549 psppire_axis_unit_count (sheet->haxis));
1550 g_return_if_fail (row <
1551 psppire_axis_unit_count (sheet->vaxis));
1553 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1558 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1560 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1566 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1568 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1574 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
1576 g_return_if_fail (sheet != NULL);
1577 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1579 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
1582 if (sheet->state != PSPPIRE_SHEET_NORMAL)
1583 psppire_sheet_real_unselect_range (sheet, NULL);
1585 sheet->state = PSPPIRE_SHEET_ROW_SELECTED;
1586 sheet->range.row0 = row;
1587 sheet->range.col0 = 0;
1588 sheet->range.rowi = row;
1589 sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1590 sheet->active_cell.row = row;
1592 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
1593 psppire_sheet_real_select_range (sheet, NULL);
1598 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
1600 g_return_if_fail (sheet != NULL);
1601 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1603 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
1606 if (sheet->state != PSPPIRE_SHEET_NORMAL)
1607 psppire_sheet_real_unselect_range (sheet, NULL);
1609 sheet->state = PSPPIRE_SHEET_COLUMN_SELECTED;
1610 sheet->range.row0 = 0;
1611 sheet->range.col0 = column;
1612 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1613 sheet->range.coli = column;
1614 sheet->active_cell.col = column;
1616 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
1617 psppire_sheet_real_select_range (sheet, NULL);
1624 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1625 const PsppireSheetRange *range)
1627 g_return_val_if_fail (sheet != NULL, FALSE);
1629 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1632 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1635 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1638 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1641 if (range->rowi < min_visible_row (sheet))
1644 if (range->row0 > max_visible_row (sheet))
1647 if (range->coli < min_visible_column (sheet))
1650 if (range->col0 > max_visible_column (sheet))
1657 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1658 gint row, gint column)
1660 PsppireSheetRange range;
1663 range.col0 = column;
1665 range.coli = column;
1667 return psppire_sheet_range_isvisible (sheet, &range);
1671 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1673 g_return_if_fail (sheet != NULL);
1674 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1675 g_return_if_fail (range != NULL);
1677 range->row0 = min_visible_row (sheet);
1678 range->col0 = min_visible_column (sheet);
1679 range->rowi = max_visible_row (sheet);
1680 range->coli = max_visible_column (sheet);
1685 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1686 GtkAdjustment *hadjustment,
1687 GtkAdjustment *vadjustment)
1689 if ( sheet->vadjustment != vadjustment )
1691 if (sheet->vadjustment)
1692 g_object_unref (sheet->vadjustment);
1693 sheet->vadjustment = vadjustment;
1697 g_object_ref (vadjustment);
1699 g_signal_connect (sheet->vadjustment, "value_changed",
1700 G_CALLBACK (vadjustment_value_changed),
1705 if ( sheet->hadjustment != hadjustment )
1707 if (sheet->hadjustment)
1708 g_object_unref (sheet->hadjustment);
1710 sheet->hadjustment = hadjustment;
1714 g_object_ref (hadjustment);
1716 g_signal_connect (sheet->hadjustment, "value_changed",
1717 G_CALLBACK (hadjustment_value_changed),
1725 psppire_sheet_finalize (GObject *object)
1727 PsppireSheet *sheet;
1729 g_return_if_fail (object != NULL);
1730 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1732 sheet = PSPPIRE_SHEET (object);
1734 if (G_OBJECT_CLASS (parent_class)->finalize)
1735 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1739 psppire_sheet_dispose (GObject *object)
1741 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1743 g_return_if_fail (object != NULL);
1744 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1746 if ( sheet->dispose_has_run )
1749 sheet->dispose_has_run = TRUE;
1751 if (sheet->model) g_object_unref (sheet->model);
1752 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1753 if (sheet->haxis) g_object_unref (sheet->haxis);
1755 g_object_unref (sheet->button);
1756 sheet->button = NULL;
1758 /* unref adjustments */
1759 if (sheet->hadjustment)
1761 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1762 G_SIGNAL_MATCH_DATA,
1766 g_object_unref (sheet->hadjustment);
1767 sheet->hadjustment = NULL;
1770 if (sheet->vadjustment)
1772 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1773 G_SIGNAL_MATCH_DATA,
1777 g_object_unref (sheet->vadjustment);
1779 sheet->vadjustment = NULL;
1782 if (G_OBJECT_CLASS (parent_class)->dispose)
1783 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1787 psppire_sheet_style_set (GtkWidget *widget,
1788 GtkStyle *previous_style)
1790 PsppireSheet *sheet;
1792 g_return_if_fail (widget != NULL);
1793 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1795 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1796 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1798 sheet = PSPPIRE_SHEET (widget);
1800 if (GTK_WIDGET_REALIZED (widget))
1802 gtk_style_set_background (widget->style, widget->window, widget->state);
1805 set_entry_widget_font (sheet);
1808 #define BORDER_WIDTH 3
1811 psppire_sheet_realize (GtkWidget *widget)
1813 PsppireSheet *sheet;
1814 GdkWindowAttr attributes;
1815 const gint attributes_mask =
1816 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1819 GdkColormap *colormap;
1820 GdkDisplay *display;
1822 g_return_if_fail (widget != NULL);
1823 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1825 sheet = PSPPIRE_SHEET (widget);
1827 colormap = gtk_widget_get_colormap (widget);
1828 display = gtk_widget_get_display (widget);
1830 attributes.window_type = GDK_WINDOW_CHILD;
1831 attributes.x = widget->allocation.x;
1832 attributes.y = widget->allocation.y;
1833 attributes.width = widget->allocation.width;
1834 attributes.height = widget->allocation.height;
1835 attributes.wclass = GDK_INPUT_OUTPUT;
1837 attributes.visual = gtk_widget_get_visual (widget);
1838 attributes.colormap = colormap;
1840 attributes.event_mask = gtk_widget_get_events (widget);
1841 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1842 GDK_BUTTON_PRESS_MASK |
1843 GDK_BUTTON_RELEASE_MASK |
1844 GDK_KEY_PRESS_MASK |
1845 GDK_ENTER_NOTIFY_MASK |
1846 GDK_LEAVE_NOTIFY_MASK |
1847 GDK_POINTER_MOTION_MASK |
1848 GDK_POINTER_MOTION_HINT_MASK);
1850 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1853 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1855 gdk_window_set_user_data (widget->window, sheet);
1857 widget->style = gtk_style_attach (widget->style, widget->window);
1859 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1861 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1862 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1864 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1865 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1870 attributes.width = sheet->column_title_area.width;
1871 attributes.height = sheet->column_title_area.height;
1874 /* column - title window */
1875 sheet->column_title_window =
1876 gdk_window_new (widget->window, &attributes, attributes_mask);
1877 gdk_window_set_user_data (sheet->column_title_window, sheet);
1878 gtk_style_set_background (widget->style, sheet->column_title_window,
1884 attributes.width = sheet->row_title_area.width;
1885 attributes.height = sheet->row_title_area.height;
1887 /* row - title window */
1888 sheet->row_title_window = gdk_window_new (widget->window,
1889 &attributes, attributes_mask);
1890 gdk_window_set_user_data (sheet->row_title_window, sheet);
1891 gtk_style_set_background (widget->style, sheet->row_title_window,
1894 /* sheet - window */
1895 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1900 sheet->sheet_window = gdk_window_new (widget->window,
1901 &attributes, attributes_mask);
1902 gdk_window_set_user_data (sheet->sheet_window, sheet);
1904 gdk_cursor_unref (attributes.cursor);
1906 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1907 gdk_window_show (sheet->sheet_window);
1910 sheet->fg_gc = gdk_gc_new (widget->window);
1911 sheet->bg_gc = gdk_gc_new (widget->window);
1913 values.foreground = widget->style->white;
1914 values.function = GDK_INVERT;
1915 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1916 values.line_width = BORDER_WIDTH;
1918 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1927 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1928 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1930 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1931 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1934 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1936 if (sheet->column_titles_visible)
1937 gdk_window_show (sheet->column_title_window);
1938 if (sheet->row_titles_visible)
1939 gdk_window_show (sheet->row_title_window);
1941 sheet->hover_window = create_hover_window ();
1943 draw_row_title_buttons (sheet);
1944 draw_column_title_buttons (sheet);
1946 psppire_sheet_update_primary_selection (sheet);
1949 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1953 create_global_button (PsppireSheet *sheet)
1955 sheet->button = gtk_button_new_with_label (" ");
1957 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1959 g_object_ref_sink (sheet->button);
1961 g_signal_connect (sheet->button,
1963 G_CALLBACK (global_button_clicked),
1968 size_allocate_global_button (PsppireSheet *sheet)
1970 GtkAllocation allocation;
1972 if (!sheet->column_titles_visible) return;
1973 if (!sheet->row_titles_visible) return;
1975 gtk_widget_size_request (sheet->button, NULL);
1979 allocation.width = sheet->row_title_area.width;
1980 allocation.height = sheet->column_title_area.height;
1982 gtk_widget_size_allocate (sheet->button, &allocation);
1986 global_button_clicked (GtkWidget *widget, gpointer data)
1988 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
1993 psppire_sheet_unrealize (GtkWidget *widget)
1995 PsppireSheet *sheet;
1997 g_return_if_fail (widget != NULL);
1998 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2000 sheet = PSPPIRE_SHEET (widget);
2002 gdk_cursor_unref (sheet->cursor_drag);
2003 sheet->cursor_drag = NULL;
2005 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2006 sheet->color, n_COLORS);
2008 g_object_unref (sheet->xor_gc);
2009 g_object_unref (sheet->fg_gc);
2010 g_object_unref (sheet->bg_gc);
2012 destroy_hover_window (sheet->hover_window);
2014 gdk_window_destroy (sheet->sheet_window);
2015 gdk_window_destroy (sheet->column_title_window);
2016 gdk_window_destroy (sheet->row_title_window);
2018 gtk_widget_unparent (sheet->entry_widget);
2019 if (sheet->button != NULL)
2020 gtk_widget_unparent (sheet->button);
2022 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2023 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2027 psppire_sheet_map (GtkWidget *widget)
2029 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2031 g_return_if_fail (widget != NULL);
2032 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2034 if (!GTK_WIDGET_MAPPED (widget))
2036 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2038 gdk_window_show (widget->window);
2039 gdk_window_show (sheet->sheet_window);
2041 if (sheet->column_titles_visible)
2043 draw_column_title_buttons (sheet);
2044 gdk_window_show (sheet->column_title_window);
2046 if (sheet->row_titles_visible)
2048 draw_row_title_buttons (sheet);
2049 gdk_window_show (sheet->row_title_window);
2052 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2053 && sheet->active_cell.row >= 0
2054 && sheet->active_cell.col >= 0 )
2056 gtk_widget_show (sheet->entry_widget);
2057 gtk_widget_map (sheet->entry_widget);
2060 if (!GTK_WIDGET_MAPPED (sheet->button))
2062 gtk_widget_show (sheet->button);
2063 gtk_widget_map (sheet->button);
2066 redraw_range (sheet, NULL);
2067 change_active_cell (sheet,
2068 sheet->active_cell.row,
2069 sheet->active_cell.col);
2074 psppire_sheet_unmap (GtkWidget *widget)
2076 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2078 if (!GTK_WIDGET_MAPPED (widget))
2081 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2083 gdk_window_hide (sheet->sheet_window);
2084 if (sheet->column_titles_visible)
2085 gdk_window_hide (sheet->column_title_window);
2086 if (sheet->row_titles_visible)
2087 gdk_window_hide (sheet->row_title_window);
2088 gdk_window_hide (widget->window);
2090 if (GTK_WIDGET_MAPPED (sheet->entry_widget))
2091 gtk_widget_unmap (sheet->entry_widget);
2093 if (GTK_WIDGET_MAPPED (sheet->button))
2094 gtk_widget_unmap (sheet->button);
2099 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2101 PangoLayout *layout;
2102 PangoRectangle text;
2103 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2108 PsppireSheetCellAttr attributes;
2111 g_return_if_fail (sheet != NULL);
2113 /* bail now if we aren't yet drawable */
2114 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2117 row >= psppire_axis_unit_count (sheet->vaxis))
2121 col >= psppire_axis_unit_count (sheet->haxis))
2124 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2126 /* select GC for background rectangle */
2127 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2128 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2130 rectangle_from_cell (sheet, row, col, &area);
2132 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2134 if (sheet->show_grid)
2136 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2138 gdk_draw_rectangle (sheet->sheet_window,
2142 area.width, area.height);
2146 label = psppire_sheet_cell_get_text (sheet, row, col);
2151 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2152 dispose_string (sheet, label);
2155 pango_layout_set_font_description (layout, font_desc);
2157 pango_layout_get_pixel_extents (layout, NULL, &text);
2159 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2161 font_height = pango_font_description_get_size (font_desc);
2162 if ( !pango_font_description_get_size_is_absolute (font_desc))
2163 font_height /= PANGO_SCALE;
2165 /* Centre the text vertically */
2166 area.y += (area.height - font_height) / 2.0;
2168 switch (attributes.justification)
2170 case GTK_JUSTIFY_RIGHT:
2171 area.x += area.width - text.width;
2173 case GTK_JUSTIFY_CENTER:
2174 area.x += (area.width - text.width) / 2.0;
2176 case GTK_JUSTIFY_LEFT:
2180 g_critical ("Unhandled justification %d in column %d\n",
2181 attributes.justification, col);
2185 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2190 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2191 g_object_unref (layout);
2196 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2198 PsppireSheetRange range;
2203 PsppireSheetRange drawing_range;
2205 gdk_region_get_clipbox (region, &area);
2207 y = area.y + sheet->vadjustment->value;
2208 x = area.x + sheet->hadjustment->value;
2210 if ( sheet->column_titles_visible)
2211 y -= sheet->column_title_area.height;
2213 if ( sheet->row_titles_visible)
2214 x -= sheet->row_title_area.width;
2216 maximize_int (&x, 0);
2217 maximize_int (&y, 0);
2219 range.row0 = row_from_ypixel (sheet, y);
2220 range.rowi = row_from_ypixel (sheet, y + area.height);
2222 range.col0 = column_from_xpixel (sheet, x);
2223 range.coli = column_from_xpixel (sheet, x + area.width);
2225 g_return_if_fail (sheet != NULL);
2226 g_return_if_fail (PSPPIRE_SHEET (sheet));
2228 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2229 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2230 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2233 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2234 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2235 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2236 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2238 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2239 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2241 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2243 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2244 psppire_sheet_cell_draw (sheet, i, j);
2247 if (sheet->state != PSPPIRE_SHEET_NORMAL &&
2248 psppire_sheet_range_isvisible (sheet, &sheet->range))
2249 psppire_sheet_range_draw_selection (sheet, drawing_range);
2252 if (sheet->state == GTK_STATE_NORMAL &&
2253 sheet->active_cell.row >= drawing_range.row0 &&
2254 sheet->active_cell.row <= drawing_range.rowi &&
2255 sheet->active_cell.col >= drawing_range.col0 &&
2256 sheet->active_cell.col <= drawing_range.coli)
2257 psppire_sheet_show_entry_widget (sheet);
2262 psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range)
2266 PsppireSheetRange aux;
2268 if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
2269 range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
2272 if (!psppire_sheet_range_isvisible (sheet, &range)) return;
2273 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2277 range.col0 = MAX (sheet->range.col0, range.col0);
2278 range.coli = MIN (sheet->range.coli, range.coli);
2279 range.row0 = MAX (sheet->range.row0, range.row0);
2280 range.rowi = MIN (sheet->range.rowi, range.rowi);
2282 range.col0 = MAX (range.col0, min_visible_column (sheet));
2283 range.coli = MIN (range.coli, max_visible_column (sheet));
2284 range.row0 = MAX (range.row0, min_visible_row (sheet));
2285 range.rowi = MIN (range.rowi, max_visible_row (sheet));
2287 for (i = range.row0; i <= range.rowi; i++)
2289 for (j = range.col0; j <= range.coli; j++)
2291 if (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED)
2293 rectangle_from_cell (sheet, i, j, &area);
2295 if (i == sheet->range.row0)
2297 area.y = area.y + 2;
2298 area.height = area.height - 2;
2300 if (i == sheet->range.rowi) area.height = area.height - 3;
2301 if (j == sheet->range.col0)
2303 area.x = area.x + 2;
2304 area.width = area.width - 2;
2306 if (j == sheet->range.coli) area.width = area.width - 3;
2308 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2310 gdk_draw_rectangle (sheet->sheet_window,
2313 area.x + 1, area.y + 1,
2314 area.width, area.height);
2321 psppire_sheet_draw_border (sheet, sheet->range);
2325 safe_strcmp (const gchar *s1, const gchar *s2)
2327 if ( !s1 && !s2) return 0;
2328 if ( !s1) return -1;
2329 if ( !s2) return +1;
2330 return strcmp (s1, s2);
2334 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2335 GtkJustification justification,
2338 PsppireSheetModel *model ;
2341 g_return_if_fail (sheet != NULL);
2342 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2344 if (col >= psppire_axis_unit_count (sheet->haxis)
2345 || row >= psppire_axis_unit_count (sheet->vaxis))
2348 if (col < 0 || row < 0) return;
2350 model = psppire_sheet_get_model (sheet);
2352 old_text = psppire_sheet_model_get_string (model, row, col);
2354 if (0 != safe_strcmp (old_text, text))
2356 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2357 psppire_sheet_model_set_string (model, text, row, col);
2358 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2361 if ( psppire_sheet_model_free_strings (model))
2367 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2369 PsppireSheetRange range;
2371 g_return_if_fail (sheet != NULL);
2372 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2373 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2374 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2376 if (column < 0 || row < 0) return;
2380 range.col0 = min_visible_column (sheet);
2381 range.coli = max_visible_column (sheet);
2383 psppire_sheet_real_cell_clear (sheet, row, column);
2385 redraw_range (sheet, &range);
2389 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2391 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2393 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2395 if (old_text && strlen (old_text) > 0 )
2397 psppire_sheet_model_datum_clear (model, row, column);
2400 dispose_string (sheet, old_text);
2404 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2406 PsppireSheetModel *model;
2407 g_return_val_if_fail (sheet != NULL, NULL);
2408 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2410 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2412 if (col < 0 || row < 0) return NULL;
2414 model = psppire_sheet_get_model (sheet);
2419 return psppire_sheet_model_get_string (model, row, col);
2424 psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
2427 PsppireSheetRange *range;
2429 g_return_val_if_fail (sheet != NULL, 0);
2430 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2431 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
2432 if (col < 0 || row < 0) return 0;
2434 state = sheet->state;
2435 range = &sheet->range;
2439 case PSPPIRE_SHEET_NORMAL:
2440 return GTK_STATE_NORMAL;
2442 case PSPPIRE_SHEET_ROW_SELECTED:
2443 if (row >= range->row0 && row <= range->rowi)
2444 return GTK_STATE_SELECTED;
2446 case PSPPIRE_SHEET_COLUMN_SELECTED:
2447 if (col >= range->col0 && col <= range->coli)
2448 return GTK_STATE_SELECTED;
2450 case PSPPIRE_SHEET_RANGE_SELECTED:
2451 if (row >= range->row0 && row <= range->rowi && \
2452 col >= range->col0 && col <= range->coli)
2453 return GTK_STATE_SELECTED;
2456 return GTK_STATE_NORMAL;
2459 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2460 If the function returns FALSE, then the results will be unreliable.
2463 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2471 *column = -G_MAXINT;
2473 g_return_val_if_fail (sheet != NULL, 0);
2474 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2476 /* bounds checking, return false if the user clicked
2484 if ( sheet->column_titles_visible)
2485 y -= sheet->column_title_area.height;
2487 y += sheet->vadjustment->value;
2489 if ( y < 0 && sheet->column_titles_visible)
2495 trow = row_from_ypixel (sheet, y);
2496 if (trow > psppire_axis_unit_count (sheet->vaxis))
2502 if ( sheet->row_titles_visible)
2503 x -= sheet->row_title_area.width;
2505 x += sheet->hadjustment->value;
2507 if ( x < 0 && sheet->row_titles_visible)
2513 tcol = column_from_xpixel (sheet, x);
2514 if (tcol > psppire_axis_unit_count (sheet->haxis))
2524 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2529 g_return_val_if_fail (sheet != NULL, 0);
2530 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2532 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2535 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2536 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2538 area->width= (column == -1) ? sheet->row_title_area.width
2539 : psppire_axis_unit_size (sheet->haxis, column);
2541 area->height= (row == -1) ? sheet->column_title_area.height
2542 : psppire_axis_unit_size (sheet->vaxis, row);
2548 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2550 g_return_if_fail (sheet != NULL);
2551 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2553 if (row < -1 || col < -1)
2556 if (row >= psppire_axis_unit_count (sheet->vaxis)
2558 col >= psppire_axis_unit_count (sheet->haxis))
2561 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2564 if ( row == -1 || col == -1)
2566 psppire_sheet_hide_entry_widget (sheet);
2570 change_active_cell (sheet, row, col);
2574 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2576 g_return_if_fail (sheet != NULL);
2577 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2579 if ( row ) *row = sheet->active_cell.row;
2580 if (column) *column = sheet->active_cell.col;
2584 entry_load_text (PsppireSheet *sheet)
2588 GtkJustification justification;
2589 PsppireSheetCellAttr attributes;
2591 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2592 if (sheet->state != GTK_STATE_NORMAL) return;
2594 row = sheet->active_cell.row;
2595 col = sheet->active_cell.col;
2597 if (row < 0 || col < 0) return;
2599 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2601 if (text && strlen (text) > 0)
2603 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2604 justification = attributes.justification;
2605 psppire_sheet_set_cell (sheet, row, col, justification, text);
2611 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2613 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2616 if (sheet->active_cell.row < 0 ||
2617 sheet->active_cell.col < 0) return;
2619 gtk_widget_hide (sheet->entry_widget);
2620 gtk_widget_unmap (sheet->entry_widget);
2622 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2626 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2628 gint old_row, old_col;
2630 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2632 if (row < 0 || col < 0)
2635 if ( row > psppire_axis_unit_count (sheet->vaxis)
2636 || col > psppire_axis_unit_count (sheet->haxis))
2639 if (sheet->state != PSPPIRE_SHEET_NORMAL)
2641 sheet->state = PSPPIRE_SHEET_NORMAL;
2642 psppire_sheet_real_unselect_range (sheet, NULL);
2645 old_row = sheet->active_cell.row;
2646 old_col = sheet->active_cell.col;
2648 /* Erase the old cell */
2649 psppire_sheet_draw_active_cell (sheet);
2651 entry_load_text (sheet);
2653 sheet->range.row0 = row;
2654 sheet->range.col0 = col;
2655 sheet->range.rowi = row;
2656 sheet->range.coli = col;
2657 sheet->active_cell.row = row;
2658 sheet->active_cell.col = col;
2659 sheet->selection_cell.row = row;
2660 sheet->selection_cell.col = col;
2662 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2664 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2666 psppire_sheet_draw_active_cell (sheet);
2667 psppire_sheet_show_entry_widget (sheet);
2669 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2671 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2672 row, col, old_row, old_col);
2677 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2679 GtkEntry *sheet_entry;
2680 PsppireSheetCellAttr attributes;
2684 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2686 row = sheet->active_cell.row;
2687 col = sheet->active_cell.col;
2689 /* Don't show the active cell, if there is no active cell: */
2690 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2693 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2694 if (sheet->state != PSPPIRE_SHEET_NORMAL) return;
2695 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2697 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2699 sheet_entry = psppire_sheet_get_entry (sheet);
2701 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2703 if (GTK_IS_ENTRY (sheet_entry))
2705 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2706 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2709 text = g_strdup ("");
2711 if (strcmp (old_text, text) != 0)
2712 gtk_entry_set_text (sheet_entry, text);
2714 dispose_string (sheet, text);
2717 switch (attributes.justification)
2719 case GTK_JUSTIFY_RIGHT:
2720 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2722 case GTK_JUSTIFY_CENTER:
2723 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2725 case GTK_JUSTIFY_LEFT:
2727 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2733 psppire_sheet_size_allocate_entry (sheet);
2735 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2736 psppire_sheet_model_is_editable (sheet->model,
2738 gtk_widget_map (sheet->entry_widget);
2742 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2745 PsppireSheetRange range;
2747 row = sheet->active_cell.row;
2748 col = sheet->active_cell.col;
2750 if (row < 0 || col < 0) return FALSE;
2752 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2755 range.col0 = range.coli = col;
2756 range.row0 = range.rowi = row;
2758 psppire_sheet_draw_border (sheet, range);
2766 psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range)
2768 gint i, j, mask1, mask2;
2769 gint state, selected;
2770 gint x, y, width, height;
2771 PsppireSheetRange new_range, aux_range;
2773 g_return_if_fail (sheet != NULL);
2775 if (range == NULL) range=&sheet->range;
2779 range->row0 = MIN (range->row0, sheet->range.row0);
2780 range->rowi = MAX (range->rowi, sheet->range.rowi);
2781 range->col0 = MIN (range->col0, sheet->range.col0);
2782 range->coli = MAX (range->coli, sheet->range.coli);
2784 range->row0 = MAX (range->row0, min_visible_row (sheet));
2785 range->rowi = MIN (range->rowi, max_visible_row (sheet));
2786 range->col0 = MAX (range->col0, min_visible_column (sheet));
2787 range->coli = MIN (range->coli, max_visible_column (sheet));
2789 aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
2790 aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
2791 aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
2792 aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
2794 for (i = range->row0; i <= range->rowi; i++)
2796 for (j = range->col0; j <= range->coli; j++)
2799 state = psppire_sheet_cell_get_state (sheet, i, j);
2800 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2801 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2803 if (state == GTK_STATE_SELECTED && selected &&
2804 (i == sheet->range.row0 || i == sheet->range.rowi ||
2805 j == sheet->range.col0 || j == sheet->range.coli ||
2806 i == new_range.row0 || i == new_range.rowi ||
2807 j == new_range.col0 || j == new_range.coli))
2810 mask1 = i == sheet->range.row0 ? 1 : 0;
2811 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2812 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2813 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2815 mask2 = i == new_range.row0 ? 1 : 0;
2816 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2817 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2818 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2822 x = psppire_axis_start_pixel (sheet->haxis, j);
2823 y = psppire_axis_start_pixel (sheet->vaxis, i);
2824 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2825 psppire_axis_unit_size (sheet->haxis, j);
2826 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2828 if (i == sheet->range.row0)
2831 height = height + 3;
2833 if (i == sheet->range.rowi) height = height + 3;
2834 if (j == sheet->range.col0)
2839 if (j == sheet->range.coli) width = width + 3;
2841 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2843 x = psppire_axis_start_pixel (sheet->haxis, j);
2844 y = psppire_axis_start_pixel (sheet->vaxis, i);
2845 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2846 psppire_axis_unit_size (sheet->haxis, j);
2848 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2850 if (i == new_range.row0)
2853 height = height - 2;
2855 if (i == new_range.rowi) height = height - 3;
2856 if (j == new_range.col0)
2861 if (j == new_range.coli) width = width - 3;
2863 gdk_draw_rectangle (sheet->sheet_window,
2874 for (i = range->row0; i <= range->rowi; i++)
2876 for (j = range->col0; j <= range->coli; j++)
2879 state = psppire_sheet_cell_get_state (sheet, i, j);
2880 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2881 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2883 if (state == GTK_STATE_SELECTED && !selected)
2886 x = psppire_axis_start_pixel (sheet->haxis, j);
2887 y = psppire_axis_start_pixel (sheet->vaxis, i);
2888 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2889 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2891 if (i == sheet->range.row0)
2894 height = height + 3;
2896 if (i == sheet->range.rowi) height = height + 3;
2897 if (j == sheet->range.col0)
2902 if (j == sheet->range.coli) width = width + 3;
2908 for (i = range->row0; i <= range->rowi; i++)
2910 for (j = range->col0; j <= range->coli; j++)
2913 state = psppire_sheet_cell_get_state (sheet, i, j);
2914 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2915 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2917 if (state != GTK_STATE_SELECTED && selected &&
2918 (i != sheet->active_cell.row || j != sheet->active_cell.col))
2921 x = psppire_axis_start_pixel (sheet->haxis, j);
2922 y = psppire_axis_start_pixel (sheet->vaxis, i);
2923 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2924 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2926 if (i == new_range.row0)
2929 height = height - 2;
2931 if (i == new_range.rowi) height = height - 3;
2932 if (j == new_range.col0)
2937 if (j == new_range.coli) width = width - 3;
2939 gdk_draw_rectangle (sheet->sheet_window,
2950 for (i = aux_range.row0; i <= aux_range.rowi; i++)
2952 for (j = aux_range.col0; j <= aux_range.coli; j++)
2954 state = psppire_sheet_cell_get_state (sheet, i, j);
2956 mask1 = i == sheet->range.row0 ? 1 : 0;
2957 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2958 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2959 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2961 mask2 = i == new_range.row0 ? 1 : 0;
2962 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2963 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2964 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2965 if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
2967 x = psppire_axis_start_pixel (sheet->haxis, j);
2968 y = psppire_axis_start_pixel (sheet->vaxis, i);
2969 width = psppire_axis_unit_size (sheet->haxis, j);
2970 height = psppire_axis_unit_size (sheet->vaxis, i);
2972 gdk_draw_rectangle (sheet->sheet_window,
2980 gdk_draw_rectangle (sheet->sheet_window,
2983 x + 1, y + height - 1,
2987 gdk_draw_rectangle (sheet->sheet_window,
2995 gdk_draw_rectangle (sheet->sheet_window,
2998 x + width - 1, y + 1,
3010 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
3014 rectangle_from_range (sheet, &new_range, &area);
3016 gdk_draw_rectangle (sheet->sheet_window,
3020 area.width, area.height);
3025 psppire_sheet_real_select_range (PsppireSheet *sheet,
3026 const PsppireSheetRange *range)
3030 g_return_if_fail (sheet != NULL);
3032 if (range == NULL) range = &sheet->range;
3034 memcpy (&sheet->range, range, sizeof (*range));
3036 if (range->row0 < 0 || range->rowi < 0) return;
3037 if (range->col0 < 0 || range->coli < 0) return;
3039 state = sheet->state;
3042 if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3043 range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3045 psppire_sheet_new_selection (sheet, &sheet->range);
3049 psppire_sheet_range_draw_selection (sheet, sheet->range);
3053 psppire_sheet_update_primary_selection (sheet);
3055 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3060 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
3062 g_return_if_fail (sheet != NULL);
3063 *range = sheet->range;
3068 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
3070 g_return_if_fail (sheet != NULL);
3072 if (range == NULL) range=&sheet->range;
3074 if (range->row0 < 0 || range->rowi < 0) return;
3075 if (range->col0 < 0 || range->coli < 0) return;
3078 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3079 psppire_sheet_real_unselect_range (sheet, NULL);
3081 sheet->range.row0 = range->row0;
3082 sheet->range.rowi = range->rowi;
3083 sheet->range.col0 = range->col0;
3084 sheet->range.coli = range->coli;
3085 sheet->active_cell.row = range->row0;
3086 sheet->active_cell.col = range->col0;
3087 sheet->selection_cell.row = range->rowi;
3088 sheet->selection_cell.col = range->coli;
3090 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3091 psppire_sheet_real_select_range (sheet, NULL);
3095 psppire_sheet_unselect_range (PsppireSheet *sheet)
3097 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3100 psppire_sheet_real_unselect_range (sheet, NULL);
3101 sheet->state = GTK_STATE_NORMAL;
3103 change_active_cell (sheet,
3104 sheet->active_cell.row, sheet->active_cell.col);
3109 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
3110 const PsppireSheetRange *range)
3112 g_return_if_fail (sheet != NULL);
3113 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3116 range = &sheet->range;
3118 if (range->row0 < 0 || range->rowi < 0) return;
3119 if (range->col0 < 0 || range->coli < 0) return;
3121 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3122 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3124 sheet->range.row0 = -1;
3125 sheet->range.rowi = -1;
3126 sheet->range.col0 = -1;
3127 sheet->range.coli = -1;
3132 psppire_sheet_expose (GtkWidget *widget,
3133 GdkEventExpose *event)
3135 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3137 g_return_val_if_fail (event != NULL, FALSE);
3139 if (!GTK_WIDGET_DRAWABLE (widget))
3142 /* exposure events on the sheet */
3143 if (event->window == sheet->row_title_window &&
3144 sheet->row_titles_visible)
3146 draw_row_title_buttons_range (sheet,
3147 min_visible_row (sheet),
3148 max_visible_row (sheet));
3151 if (event->window == sheet->column_title_window &&
3152 sheet->column_titles_visible)
3154 draw_column_title_buttons_range (sheet,
3155 min_visible_column (sheet),
3156 max_visible_column (sheet));
3159 if (event->window == sheet->sheet_window)
3161 draw_sheet_region (sheet, event->region);
3164 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3166 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3167 psppire_sheet_range_draw (sheet, &sheet->range);
3169 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3170 psppire_sheet_range_draw (sheet, &sheet->drag_range);
3172 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3173 psppire_sheet_range_draw_selection (sheet, sheet->range);
3174 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3175 draw_xor_rectangle (sheet, sheet->drag_range);
3179 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
3182 PsppireSheetRange range;
3183 range.row0 = range.rowi = sheet->active_cell.row;
3184 range.col0 = range.coli = sheet->active_cell.col;
3186 rectangle_from_range (sheet, &range, &rect);
3188 if (GDK_OVERLAP_RECTANGLE_OUT !=
3189 gdk_region_rect_in (event->region, &rect))
3191 psppire_sheet_draw_active_cell (sheet);
3197 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3204 psppire_sheet_button_press (GtkWidget *widget,
3205 GdkEventButton *event)
3207 PsppireSheet *sheet;
3208 GdkModifierType mods;
3213 g_return_val_if_fail (widget != NULL, FALSE);
3214 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
3215 g_return_val_if_fail (event != NULL, FALSE);
3217 sheet = PSPPIRE_SHEET (widget);
3219 /* Cancel any pending tooltips */
3220 if (sheet->motion_timer)
3222 g_source_remove (sheet->motion_timer);
3223 sheet->motion_timer = 0;
3226 gtk_widget_get_pointer (widget, &x, &y);
3227 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3230 if (event->window == sheet->column_title_window)
3232 sheet->x_drag = event->x;
3233 g_signal_emit (sheet,
3234 sheet_signals[BUTTON_EVENT_COLUMN], 0,
3237 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3239 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3240 g_signal_emit (sheet,
3241 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3244 else if (event->window == sheet->row_title_window)
3246 g_signal_emit (sheet,
3247 sheet_signals[BUTTON_EVENT_ROW], 0,
3250 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3252 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3253 g_signal_emit (sheet,
3254 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3258 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3260 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3263 /* press on resize windows */
3264 if (event->window == sheet->column_title_window)
3266 sheet->x_drag = event->x;
3268 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3270 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3271 gdk_pointer_grab (sheet->column_title_window, FALSE,
3272 GDK_POINTER_MOTION_HINT_MASK |
3273 GDK_BUTTON1_MOTION_MASK |
3274 GDK_BUTTON_RELEASE_MASK,
3275 NULL, NULL, event->time);
3277 draw_xor_vline (sheet);
3282 if (event->window == sheet->row_title_window)
3284 sheet->y_drag = event->y;
3286 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3288 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3289 gdk_pointer_grab (sheet->row_title_window, FALSE,
3290 GDK_POINTER_MOTION_HINT_MASK |
3291 GDK_BUTTON1_MOTION_MASK |
3292 GDK_BUTTON_RELEASE_MASK,
3293 NULL, NULL, event->time);
3295 draw_xor_hline (sheet);
3300 /* the sheet itself does not handle other than single click events */
3301 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3303 /* selections on the sheet */
3304 if (event->window == sheet->sheet_window)
3306 gtk_widget_get_pointer (widget, &x, &y);
3307 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3308 gdk_pointer_grab (sheet->sheet_window, FALSE,
3309 GDK_POINTER_MOTION_HINT_MASK |
3310 GDK_BUTTON1_MOTION_MASK |
3311 GDK_BUTTON_RELEASE_MASK,
3312 NULL, NULL, event->time);
3313 gtk_grab_add (GTK_WIDGET (sheet));
3315 if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
3316 sheet->selection_mode != GTK_SELECTION_NONE &&
3317 sheet->cursor_drag->type == GDK_SIZING &&
3318 !PSPPIRE_SHEET_IN_SELECTION (sheet) && !PSPPIRE_SHEET_IN_RESIZE (sheet))
3320 if (sheet->state == GTK_STATE_NORMAL)
3322 row = sheet->active_cell.row;
3323 column = sheet->active_cell.col;
3324 sheet->active_cell.row = row;
3325 sheet->active_cell.col = column;
3326 sheet->drag_range = sheet->range;
3327 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3328 psppire_sheet_select_range (sheet, &sheet->drag_range);
3332 if (row > sheet->range.rowi) row--;
3333 if (column > sheet->range.coli) column--;
3334 sheet->drag_cell.row = row;
3335 sheet->drag_cell.col = column;
3336 sheet->drag_range = sheet->range;
3337 draw_xor_rectangle (sheet, sheet->drag_range);
3338 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3340 else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
3341 !PSPPIRE_SHEET_IN_SELECTION (sheet)
3342 && ! PSPPIRE_SHEET_IN_DRAG (sheet)
3343 && sheet->active_cell.row >= 0
3344 && sheet->active_cell.col >= 0
3347 if (sheet->state == GTK_STATE_NORMAL)
3349 row = sheet->active_cell.row;
3350 column = sheet->active_cell.col;
3351 sheet->active_cell.row = row;
3352 sheet->active_cell.col = column;
3353 sheet->drag_range = sheet->range;
3354 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3355 psppire_sheet_select_range (sheet, &sheet->drag_range);
3359 if (row < sheet->range.row0) row++;
3360 if (row > sheet->range.rowi) row--;
3361 if (column < sheet->range.col0) column++;
3362 if (column > sheet->range.coli) column--;
3363 sheet->drag_cell.row = row;
3364 sheet->drag_cell.col = column;
3365 sheet->drag_range = sheet->range;
3366 draw_xor_rectangle (sheet, sheet->drag_range);
3367 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3371 veto = psppire_sheet_click_cell (sheet, row, column);
3372 if (veto) PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3376 if (event->window == sheet->column_title_window)
3378 gtk_widget_get_pointer (widget, &x, &y);
3379 if ( sheet->row_titles_visible)
3380 x -= sheet->row_title_area.width;
3382 x += sheet->hadjustment->value;
3384 column = column_from_xpixel (sheet, x);
3386 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3388 veto = psppire_sheet_click_cell (sheet, -1, column);
3389 gtk_grab_add (GTK_WIDGET (sheet));
3390 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3394 if (event->window == sheet->row_title_window)
3396 gtk_widget_get_pointer (widget, &x, &y);
3397 if ( sheet->column_titles_visible)
3398 y -= sheet->column_title_area.height;
3400 y += sheet->vadjustment->value;
3402 row = row_from_ypixel (sheet, y);
3403 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3405 veto = psppire_sheet_click_cell (sheet, row, -1);
3406 gtk_grab_add (GTK_WIDGET (sheet));
3407 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3415 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3417 PsppireSheetCell cell;
3418 gboolean forbid_move;
3423 if (row >= psppire_axis_unit_count (sheet->vaxis)
3424 || column >= psppire_axis_unit_count (sheet->haxis))
3429 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3430 &sheet->active_cell,
3436 if (sheet->state == GTK_STATE_NORMAL)
3439 row = sheet->active_cell.row;
3440 column = sheet->active_cell.col;
3442 change_active_cell (sheet, row, column);
3446 if (row == -1 && column >= 0)
3448 psppire_sheet_select_column (sheet, column);
3452 if (column == -1 && row >= 0)
3454 psppire_sheet_select_row (sheet, row);
3458 if (row == -1 && column == -1)
3460 sheet->range.row0 = 0;
3461 sheet->range.col0 = 0;
3462 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3464 psppire_axis_unit_count (sheet->haxis) - 1;
3465 sheet->active_cell.row = 0;
3466 sheet->active_cell.col = 0;
3467 psppire_sheet_select_range (sheet, NULL);
3471 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3473 sheet->state = PSPPIRE_SHEET_NORMAL;
3474 psppire_sheet_real_unselect_range (sheet, NULL);
3478 change_active_cell (sheet, row, column);
3481 sheet->active_cell.row = row;
3482 sheet->active_cell.col = column;
3483 sheet->selection_cell.row = row;
3484 sheet->selection_cell.col = column;
3485 sheet->range.row0 = row;
3486 sheet->range.col0 = column;
3487 sheet->range.rowi = row;
3488 sheet->range.coli = column;
3489 sheet->state = PSPPIRE_SHEET_NORMAL;
3490 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3492 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3498 psppire_sheet_button_release (GtkWidget *widget,
3499 GdkEventButton *event)
3501 GdkDisplay *display = gtk_widget_get_display (widget);
3503 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3505 /* release on resize windows */
3506 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3509 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3510 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3512 gdk_display_pointer_ungrab (display, event->time);
3513 draw_xor_vline (sheet);
3516 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3517 + sheet->hadjustment->value;
3519 set_column_width (sheet, sheet->drag_cell.col, width);
3524 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3527 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3528 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3530 gdk_display_pointer_ungrab (display, event->time);
3531 draw_xor_hline (sheet);
3534 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3535 sheet->vadjustment->value;
3537 set_row_height (sheet, sheet->drag_cell.row, height);
3542 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3544 PsppireSheetRange old_range;
3545 draw_xor_rectangle (sheet, sheet->drag_range);
3546 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3547 gdk_display_pointer_ungrab (display, event->time);
3549 psppire_sheet_real_unselect_range (sheet, NULL);
3551 sheet->active_cell.row = sheet->active_cell.row +
3552 (sheet->drag_range.row0 - sheet->range.row0);
3553 sheet->active_cell.col = sheet->active_cell.col +
3554 (sheet->drag_range.col0 - sheet->range.col0);
3555 sheet->selection_cell.row = sheet->selection_cell.row +
3556 (sheet->drag_range.row0 - sheet->range.row0);
3557 sheet->selection_cell.col = sheet->selection_cell.col +
3558 (sheet->drag_range.col0 - sheet->range.col0);
3559 old_range = sheet->range;
3560 sheet->range = sheet->drag_range;
3561 sheet->drag_range = old_range;
3562 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3563 &sheet->drag_range, &sheet->range);
3564 psppire_sheet_select_range (sheet, &sheet->range);
3567 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3569 PsppireSheetRange old_range;
3570 draw_xor_rectangle (sheet, sheet->drag_range);
3571 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3572 gdk_display_pointer_ungrab (display, event->time);
3574 psppire_sheet_real_unselect_range (sheet, NULL);
3576 sheet->active_cell.row = sheet->active_cell.row +
3577 (sheet->drag_range.row0 - sheet->range.row0);
3578 sheet->active_cell.col = sheet->active_cell.col +
3579 (sheet->drag_range.col0 - sheet->range.col0);
3580 if (sheet->drag_range.row0 < sheet->range.row0)
3581 sheet->selection_cell.row = sheet->drag_range.row0;
3582 if (sheet->drag_range.rowi >= sheet->range.rowi)
3583 sheet->selection_cell.row = sheet->drag_range.rowi;
3584 if (sheet->drag_range.col0 < sheet->range.col0)
3585 sheet->selection_cell.col = sheet->drag_range.col0;
3586 if (sheet->drag_range.coli >= sheet->range.coli)
3587 sheet->selection_cell.col = sheet->drag_range.coli;
3588 old_range = sheet->range;
3589 sheet->range = sheet->drag_range;
3590 sheet->drag_range = old_range;
3592 if (sheet->state == GTK_STATE_NORMAL) sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3593 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3594 &sheet->drag_range, &sheet->range);
3595 psppire_sheet_select_range (sheet, &sheet->range);
3598 if (sheet->state == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet))
3600 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3601 gdk_display_pointer_ungrab (display, event->time);
3602 change_active_cell (sheet, sheet->active_cell.row,
3603 sheet->active_cell.col);
3606 if (PSPPIRE_SHEET_IN_SELECTION)
3607 gdk_display_pointer_ungrab (display, event->time);
3608 gtk_grab_remove (GTK_WIDGET (sheet));
3610 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3619 /* Shamelessly lifted from gtktooltips */
3621 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3625 gtk_widget_size_request (tip_window, &req);
3626 gtk_paint_flat_box (tip_window->style, tip_window->window,
3627 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3628 NULL, GTK_WIDGET(tip_window), "tooltip",
3629 0, 0, req.width, req.height);
3635 destroy_hover_window (PsppireSheetHoverTitle *h)
3637 gtk_widget_destroy (h->window);
3641 static PsppireSheetHoverTitle *
3642 create_hover_window (void)
3644 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3646 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3648 #if GTK_CHECK_VERSION (2, 9, 0)
3649 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3650 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3653 gtk_widget_set_app_paintable (hw->window, TRUE);
3654 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3655 gtk_widget_set_name (hw->window, "gtk-tooltips");
3656 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3658 g_signal_connect (hw->window,
3660 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3663 hw->label = gtk_label_new (NULL);
3666 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3667 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3669 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3671 gtk_widget_show (hw->label);
3673 g_signal_connect (hw->window,
3675 G_CALLBACK (gtk_widget_destroyed),
3681 #define HOVER_WINDOW_Y_OFFSET 2
3684 show_subtitle (PsppireSheet *sheet, gint row, gint column, const gchar *subtitle)
3693 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3697 sheet->hover_window->row = row;
3698 sheet->hover_window->column = column;
3700 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3702 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3704 gtk_widget_show (sheet->hover_window->window);
3706 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3712 y += sheet->column_title_area.y;
3713 y += sheet->column_title_area.height;
3714 y += HOVER_WINDOW_Y_OFFSET;
3720 x += sheet->row_title_area.x;
3721 x += sheet->row_title_area.width * 2 / 3.0;
3724 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3729 motion_timeout_callback (gpointer data)
3731 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3734 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3736 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3738 if (sheet->row_title_under && row >= 0)
3740 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3742 show_subtitle (sheet, row, -1, text);
3746 if (sheet->column_title_under && column >= 0)
3748 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3751 show_subtitle (sheet, -1, column, text);
3761 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3763 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3764 GdkModifierType mods;
3765 GdkCursorType new_cursor;
3768 GdkDisplay *display;
3770 g_return_val_if_fail (event != NULL, FALSE);
3772 display = gtk_widget_get_display (widget);
3774 /* selections on the sheet */
3778 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3780 if ( sheet->motion_timer > 0 )
3781 g_source_remove (sheet->motion_timer);
3782 sheet->motion_timer =
3783 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3789 gtk_widget_get_pointer (widget, &wx, &wy);
3791 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3793 if ( row != sheet->hover_window->row ||
3794 column != sheet->hover_window->column)
3796 gtk_widget_hide (sheet->hover_window->window);
3801 if (event->window == sheet->column_title_window)
3803 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3804 on_column_boundary (sheet, x, &column))
3806 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3807 if (new_cursor != sheet->cursor_drag->type)
3809 gdk_cursor_unref (sheet->cursor_drag);
3810 sheet->cursor_drag =
3811 gdk_cursor_new_for_display (display, new_cursor);
3813 gdk_window_set_cursor (sheet->column_title_window,
3814 sheet->cursor_drag);
3819 new_cursor = GDK_TOP_LEFT_ARROW;
3820 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3821 new_cursor != sheet->cursor_drag->type)
3823 gdk_cursor_unref (sheet->cursor_drag);
3824 sheet->cursor_drag =
3825 gdk_cursor_new_for_display (display, new_cursor);
3826 gdk_window_set_cursor (sheet->column_title_window,
3827 sheet->cursor_drag);
3831 else if (event->window == sheet->row_title_window)
3833 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3834 on_row_boundary (sheet, y, &row))
3836 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3837 if (new_cursor != sheet->cursor_drag->type)
3839 gdk_cursor_unref (sheet->cursor_drag);
3840 sheet->cursor_drag =
3841 gdk_cursor_new_for_display (display, new_cursor);
3842 gdk_window_set_cursor (sheet->row_title_window,
3843 sheet->cursor_drag);
3848 new_cursor = GDK_TOP_LEFT_ARROW;
3849 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3850 new_cursor != sheet->cursor_drag->type)
3852 gdk_cursor_unref (sheet->cursor_drag);
3853 sheet->cursor_drag =
3854 gdk_cursor_new_for_display (display, new_cursor);
3855 gdk_window_set_cursor (sheet->row_title_window,
3856 sheet->cursor_drag);
3861 new_cursor = GDK_PLUS;
3862 if ( event->window == sheet->sheet_window &&
3863 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3864 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3865 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3866 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3867 new_cursor != sheet->cursor_drag->type)
3869 gdk_cursor_unref (sheet->cursor_drag);
3870 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3871 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3874 new_cursor = GDK_TOP_LEFT_ARROW;
3875 if ( event->window == sheet->sheet_window &&
3876 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3877 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3878 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3879 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3880 new_cursor != sheet->cursor_drag->type)
3882 gdk_cursor_unref (sheet->cursor_drag);
3883 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3884 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3887 new_cursor = GDK_SIZING;
3888 if ( event->window == sheet->sheet_window &&
3889 sheet->selection_mode != GTK_SELECTION_NONE &&
3890 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3891 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3892 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3893 new_cursor != sheet->cursor_drag->type)
3895 gdk_cursor_unref (sheet->cursor_drag);
3896 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3897 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3901 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3902 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3904 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3906 if (event->x != sheet->x_drag)
3908 draw_xor_vline (sheet);
3909 sheet->x_drag = event->x;
3910 draw_xor_vline (sheet);
3916 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3918 if (event->y != sheet->y_drag)
3920 draw_xor_hline (sheet);
3921 sheet->y_drag = event->y;
3922 draw_xor_hline (sheet);
3928 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3930 PsppireSheetRange aux;
3931 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3932 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3933 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3934 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3938 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3939 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3941 aux = sheet->drag_range;
3942 sheet->drag_range.row0 = sheet->range.row0 + row;
3943 sheet->drag_range.col0 = sheet->range.col0 + column;
3944 sheet->drag_range.rowi = sheet->range.rowi + row;
3945 sheet->drag_range.coli = sheet->range.coli + column;
3946 if (aux.row0 != sheet->drag_range.row0 ||
3947 aux.col0 != sheet->drag_range.col0)
3949 draw_xor_rectangle (sheet, aux);
3950 draw_xor_rectangle (sheet, sheet->drag_range);
3956 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3958 PsppireSheetRange aux;
3959 gint v_h, current_col, current_row, col_threshold, row_threshold;
3961 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
3962 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
3964 current_col = column_from_xpixel (sheet, x);
3965 current_row = row_from_ypixel (sheet, y);
3966 column = current_col - sheet->drag_cell.col;
3967 row = current_row - sheet->drag_cell.row;
3969 /*use half of column width resp. row height as threshold to
3971 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
3972 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
3975 if (x < col_threshold)
3978 else if (column < 0)
3980 if (x > col_threshold)
3983 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
3984 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
3987 if (y < row_threshold)
3992 if (y > row_threshold)
3996 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3997 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4007 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4008 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4010 aux = sheet->drag_range;
4011 sheet->drag_range = sheet->range;
4013 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4014 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4015 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4016 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4018 if (aux.row0 != sheet->drag_range.row0 ||
4019 aux.rowi != sheet->drag_range.rowi ||
4020 aux.col0 != sheet->drag_range.col0 ||
4021 aux.coli != sheet->drag_range.coli)
4023 draw_xor_rectangle (sheet, aux);
4024 draw_xor_rectangle (sheet, sheet->drag_range);
4030 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
4032 if (sheet->state == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
4033 column == sheet->active_cell.col) return TRUE;
4035 if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4036 psppire_sheet_extend_selection (sheet, row, column);
4042 psppire_sheet_crossing_notify (GtkWidget *widget,
4043 GdkEventCrossing *event)
4045 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4047 if (event->window == sheet->column_title_window)
4048 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4049 else if (event->window == sheet->row_title_window)
4050 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4056 psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column)
4058 PsppireSheetRange range;
4062 if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
4065 if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
4067 gtk_widget_grab_focus (GTK_WIDGET (sheet));
4069 if (PSPPIRE_SHEET_IN_DRAG (sheet)) return;
4071 state = sheet->state;
4073 switch (sheet->state)
4075 case PSPPIRE_SHEET_ROW_SELECTED:
4076 column = psppire_axis_unit_count (sheet->haxis) - 1;
4078 case PSPPIRE_SHEET_COLUMN_SELECTED:
4079 row = psppire_axis_unit_count (sheet->vaxis) - 1;
4081 case PSPPIRE_SHEET_NORMAL:
4082 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4083 r = sheet->active_cell.row;
4084 c = sheet->active_cell.col;
4085 sheet->range.col0 = c;
4086 sheet->range.row0 = r;
4087 sheet->range.coli = c;
4088 sheet->range.rowi = r;
4089 psppire_sheet_range_draw_selection (sheet, sheet->range);
4090 case PSPPIRE_SHEET_RANGE_SELECTED:
4091 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4094 sheet->selection_cell.row = row;
4095 sheet->selection_cell.col = column;
4097 range.col0 = MIN (column, sheet->active_cell.col);
4098 range.coli = MAX (column, sheet->active_cell.col);
4099 range.row0 = MIN (row, sheet->active_cell.row);
4100 range.rowi = MAX (row, sheet->active_cell.row);
4102 if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
4103 range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
4104 state == PSPPIRE_SHEET_NORMAL)
4105 psppire_sheet_real_select_range (sheet, &range);
4110 psppire_sheet_entry_key_press (GtkWidget *widget,
4114 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
4119 /* Number of rows in a step-increment */
4120 #define ROWS_PER_STEP 1
4124 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
4126 gint old_row = sheet->active_cell.row ;
4127 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
4131 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
4132 min_visible_row (sheet));
4136 case GTK_SCROLL_PAGE_DOWN:
4137 gtk_adjustment_set_value (sheet->vadjustment,
4138 sheet->vadjustment->value +
4139 sheet->vadjustment->page_increment);
4141 case GTK_SCROLL_PAGE_UP:
4142 gtk_adjustment_set_value (sheet->vadjustment,
4143 sheet->vadjustment->value -
4144 sheet->vadjustment->page_increment);
4148 g_assert_not_reached ();
4153 vpixel += psppire_axis_start_pixel (sheet->vaxis,
4154 min_visible_row (sheet));
4156 new_row = row_from_ypixel (sheet, vpixel);
4158 change_active_cell (sheet, new_row,
4159 sheet->active_cell.col);
4164 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
4166 gint current_row = sheet->active_cell.row;
4167 gint current_col = sheet->active_cell.col;
4168 PsppireSheetCell new_cell ;
4169 gboolean forbidden = FALSE;
4171 new_cell.row = current_row;
4172 new_cell.col = current_col;
4176 case GTK_SCROLL_STEP_DOWN:
4179 case GTK_SCROLL_STEP_UP:
4182 case GTK_SCROLL_STEP_RIGHT:
4185 case GTK_SCROLL_STEP_LEFT:
4189 g_assert_not_reached ();
4194 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
4195 &sheet->active_cell,
4203 maximize_int (&new_cell.row, 0);
4204 maximize_int (&new_cell.col, 0);
4206 minimize_int (&new_cell.row,
4207 psppire_axis_unit_count (sheet->vaxis) - 1);
4209 minimize_int (&new_cell.col,
4210 psppire_axis_unit_count (sheet->haxis) - 1);
4212 change_active_cell (sheet, new_cell.row, new_cell.col);
4215 if ( new_cell.col > max_fully_visible_column (sheet))
4218 psppire_axis_start_pixel (sheet->haxis,
4220 hpos -= sheet->hadjustment->page_size;
4222 gtk_adjustment_set_value (sheet->hadjustment,
4225 else if ( new_cell.col < min_fully_visible_column (sheet))
4228 psppire_axis_start_pixel (sheet->haxis,
4231 gtk_adjustment_set_value (sheet->hadjustment,
4236 if ( new_cell.row > max_fully_visible_row (sheet))
4239 psppire_axis_start_pixel (sheet->vaxis,
4241 vpos -= sheet->vadjustment->page_size;
4243 gtk_adjustment_set_value (sheet->vadjustment,
4246 else if ( new_cell.row < min_fully_visible_row (sheet))
4249 psppire_axis_start_pixel (sheet->vaxis,
4252 gtk_adjustment_set_value (sheet->vadjustment,
4256 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
4261 psppire_sheet_key_press (GtkWidget *widget,
4264 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4266 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
4268 switch (key->keyval)
4272 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
4274 case GDK_ISO_Left_Tab:
4276 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
4280 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
4283 step_sheet (sheet, GTK_SCROLL_STEP_UP);
4287 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
4290 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
4294 gtk_adjustment_set_value (sheet->vadjustment,
4295 sheet->vadjustment->lower);
4297 change_active_cell (sheet, 0,
4298 sheet->active_cell.col);
4303 gtk_adjustment_set_value (sheet->vadjustment,
4304 sheet->vadjustment->upper -
4305 sheet->vadjustment->page_size -
4306 sheet->vadjustment->page_increment);
4309 change_active_cellx (sheet,
4310 psppire_axis_unit_count (sheet->vaxis) - 1,
4311 sheet->active_cell.col);
4315 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
4326 psppire_sheet_size_request (GtkWidget *widget,
4327 GtkRequisition *requisition)
4329 PsppireSheet *sheet;
4331 g_return_if_fail (widget != NULL);
4332 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4333 g_return_if_fail (requisition != NULL);
4335 sheet = PSPPIRE_SHEET (widget);
4337 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4338 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4340 /* compute the size of the column title area */
4341 if (sheet->column_titles_visible)
4342 requisition->height += sheet->column_title_area.height;
4344 /* compute the size of the row title area */
4345 if (sheet->row_titles_visible)
4346 requisition->width += sheet->row_title_area.width;
4351 psppire_sheet_size_allocate (GtkWidget *widget,
4352 GtkAllocation *allocation)
4354 PsppireSheet *sheet;
4355 GtkAllocation sheet_allocation;
4358 g_return_if_fail (widget != NULL);
4359 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4360 g_return_if_fail (allocation != NULL);
4362 sheet = PSPPIRE_SHEET (widget);
4363 widget->allocation = *allocation;
4364 border_width = GTK_CONTAINER (widget)->border_width;
4366 if (GTK_WIDGET_REALIZED (widget))
4367 gdk_window_move_resize (widget->window,
4368 allocation->x + border_width,
4369 allocation->y + border_width,
4370 allocation->width - 2 * border_width,
4371 allocation->height - 2 * border_width);
4373 sheet_allocation.x = 0;
4374 sheet_allocation.y = 0;
4375 sheet_allocation.width = allocation->width - 2 * border_width;
4376 sheet_allocation.height = allocation->height - 2 * border_width;
4378 if (GTK_WIDGET_REALIZED (widget))
4379 gdk_window_move_resize (sheet->sheet_window,
4382 sheet_allocation.width,
4383 sheet_allocation.height);
4385 /* position the window which holds the column title buttons */
4386 sheet->column_title_area.x = 0;
4387 sheet->column_title_area.y = 0;
4388 sheet->column_title_area.width = sheet_allocation.width ;
4391 /* position the window which holds the row title buttons */
4392 sheet->row_title_area.x = 0;
4393 sheet->row_title_area.y = 0;
4394 sheet->row_title_area.height = sheet_allocation.height;
4396 if (sheet->row_titles_visible)
4397 sheet->column_title_area.x += sheet->row_title_area.width;
4399 if (sheet->column_titles_visible)
4400 sheet->row_title_area.y += sheet->column_title_area.height;
4402 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4403 gdk_window_move_resize (sheet->column_title_window,
4404 sheet->column_title_area.x,
4405 sheet->column_title_area.y,
4406 sheet->column_title_area.width,
4407 sheet->column_title_area.height);
4410 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4411 gdk_window_move_resize (sheet->row_title_window,
4412 sheet->row_title_area.x,
4413 sheet->row_title_area.y,
4414 sheet->row_title_area.width,
4415 sheet->row_title_area.height);
4417 size_allocate_global_button (sheet);
4421 gint width = sheet->column_title_area.width;
4423 if ( sheet->row_titles_visible)
4424 width -= sheet->row_title_area.width;
4426 g_object_set (sheet->haxis,
4427 "minimum-extent", width,
4434 gint height = sheet->row_title_area.height;
4436 if ( sheet->column_titles_visible)
4437 height -= sheet->column_title_area.height;
4439 g_object_set (sheet->vaxis,
4440 "minimum-extent", height,
4445 /* set the scrollbars adjustments */
4446 adjust_scrollbars (sheet);
4450 draw_column_title_buttons (PsppireSheet *sheet)
4454 if (!sheet->column_titles_visible) return;
4455 if (!GTK_WIDGET_REALIZED (sheet))
4458 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4461 if (sheet->row_titles_visible)
4463 x = sheet->row_title_area.width;
4466 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4468 sheet->column_title_area.width = width;
4469 sheet->column_title_area.x = x;
4470 gdk_window_move_resize (sheet->column_title_window,
4471 sheet->column_title_area.x,
4472 sheet->column_title_area.y,
4473 sheet->column_title_area.width,
4474 sheet->column_title_area.height);
4477 if (max_visible_column (sheet) ==
4478 psppire_axis_unit_count (sheet->haxis) - 1)
4479 gdk_window_clear_area (sheet->column_title_window,
4481 sheet->column_title_area.width,
4482 sheet->column_title_area.height);
4484 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4486 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4487 max_visible_column (sheet));
4491 draw_row_title_buttons (PsppireSheet *sheet)
4496 if (!sheet->row_titles_visible) return;
4497 if (!GTK_WIDGET_REALIZED (sheet))
4500 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4502 if (sheet->column_titles_visible)
4504 y = sheet->column_title_area.height;
4507 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4509 sheet->row_title_area.y = y;
4510 sheet->row_title_area.height = height;
4511 gdk_window_move_resize (sheet->row_title_window,
4512 sheet->row_title_area.x,
4513 sheet->row_title_area.y,
4514 sheet->row_title_area.width,
4515 sheet->row_title_area.height);
4518 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4519 gdk_window_clear_area (sheet->row_title_window,
4521 sheet->row_title_area.width,
4522 sheet->row_title_area.height);
4524 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4526 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4527 max_visible_row (sheet));
4532 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4534 GtkAllocation entry_alloc;
4535 PsppireSheetCellAttr attributes = { 0 };
4536 GtkEntry *sheet_entry;
4538 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4539 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4541 sheet_entry = psppire_sheet_get_entry (sheet);
4543 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4544 sheet->active_cell.col,
4548 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4550 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4552 style->bg[GTK_STATE_NORMAL] = attributes.background;
4553 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4554 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4555 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4556 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4557 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4560 rectangle_from_cell (sheet, sheet->active_cell.row,
4561 sheet->active_cell.col, &entry_alloc);
4563 entry_alloc.width -= BORDER_WIDTH ;
4564 entry_alloc.height -= BORDER_WIDTH ;
4565 entry_alloc.x += DIV_RND_UP (BORDER_WIDTH, 2);
4566 entry_alloc.y += DIV_RND_UP (BORDER_WIDTH, 2);
4569 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4570 entry_alloc.height);
4571 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4575 /* Copy the sheet's font to the entry widget */
4577 set_entry_widget_font (PsppireSheet *sheet)
4579 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4581 pango_font_description_free (style->font_desc);
4582 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4584 gtk_widget_modify_style (sheet->entry_widget, style);
4590 create_sheet_entry (PsppireSheet *sheet)
4592 if (sheet->entry_widget)
4594 gtk_widget_unparent (sheet->entry_widget);
4597 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4598 g_object_ref_sink (sheet->entry_widget);
4600 gtk_widget_size_request (sheet->entry_widget, NULL);
4602 if ( GTK_IS_ENTRY (sheet->entry_widget))
4604 g_object_set (sheet->entry_widget,
4609 if (GTK_WIDGET_REALIZED (sheet))
4611 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4612 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4613 gtk_widget_realize (sheet->entry_widget);
4616 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4617 G_CALLBACK (psppire_sheet_entry_key_press),
4620 set_entry_widget_font (sheet);
4622 gtk_widget_show (sheet->entry_widget);
4626 /* Finds the last child widget that happens to be of type GtkEntry */
4628 find_entry (GtkWidget *w, gpointer user_data)
4630 GtkWidget **entry = user_data;
4631 if ( GTK_IS_ENTRY (w))
4639 psppire_sheet_get_entry (PsppireSheet *sheet)
4641 GtkWidget *w = sheet->entry_widget;
4643 g_return_val_if_fail (sheet != NULL, NULL);
4644 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4645 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4647 while (! GTK_IS_ENTRY (w))
4649 GtkWidget *entry = NULL;
4651 if (GTK_IS_CONTAINER (w))
4653 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4662 return GTK_ENTRY (w);
4667 draw_button (PsppireSheet *sheet, GdkWindow *window,
4668 PsppireSheetButton *button, gboolean is_sensitive,
4669 GdkRectangle allocation)
4671 GtkShadowType shadow_type;
4672 gint text_width = 0, text_height = 0;
4673 PangoAlignment align = PANGO_ALIGN_LEFT;
4679 g_return_if_fail (sheet != NULL);
4680 g_return_if_fail (button != NULL);
4683 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4685 gdk_window_clear_area (window,
4686 allocation.x, allocation.y,
4687 allocation.width, allocation.height);
4689 gtk_widget_ensure_style (sheet->button);
4691 gtk_paint_box (sheet->button->style, window,
4692 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4693 &allocation, GTK_WIDGET (sheet->button),
4695 allocation.x, allocation.y,
4696 allocation.width, allocation.height);
4698 state = button->state;
4699 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4701 if (state == GTK_STATE_ACTIVE)
4702 shadow_type = GTK_SHADOW_IN;
4704 shadow_type = GTK_SHADOW_OUT;
4706 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4707 gtk_paint_box (sheet->button->style, window,
4708 button->state, shadow_type,
4709 &allocation, GTK_WIDGET (sheet->button),
4711 allocation.x, allocation.y,
4712 allocation.width, allocation.height);
4714 if (button->label_visible)
4716 text_height = DEFAULT_ROW_HEIGHT -
4717 2 * COLUMN_TITLES_HEIGHT;
4719 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4721 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4724 allocation.y += 2 * sheet->button->style->ythickness;
4726 if (button->label && strlen (button->label) > 0)
4728 PangoRectangle rect;
4729 gchar *line = button->label;
4731 PangoLayout *layout = NULL;
4732 gint real_x = allocation.x;
4733 gint real_y = allocation.y;
4735 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4736 pango_layout_get_extents (layout, NULL, &rect);
4738 text_width = PANGO_PIXELS (rect.width);
4739 switch (button->justification)
4741 case GTK_JUSTIFY_LEFT:
4742 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4743 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4745 case GTK_JUSTIFY_RIGHT:
4746 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4747 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4749 case GTK_JUSTIFY_CENTER:
4751 real_x = allocation.x + (allocation.width - text_width)/2;
4752 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4753 pango_layout_set_justify (layout, TRUE);
4755 pango_layout_set_alignment (layout, align);
4756 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4765 g_object_unref (layout);
4768 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4770 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4774 psppire_sheet_button_free (button);
4778 /* Draw the column title buttons FIRST through to LAST */
4780 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4784 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4786 if (!sheet->column_titles_visible) return;
4788 g_return_if_fail (first >= min_visible_column (sheet));
4789 g_return_if_fail (last <= max_visible_column (sheet));
4792 rect.height = sheet->column_title_area.height;
4793 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4794 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4795 + psppire_axis_unit_size (sheet->haxis, last);
4797 rect.x -= sheet->hadjustment->value;
4799 minimize_int (&rect.width, sheet->column_title_area.width);
4800 maximize_int (&rect.x, 0);
4802 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4804 for (col = first ; col <= last ; ++col)
4806 GdkRectangle allocation;
4807 gboolean is_sensitive = FALSE;
4809 PsppireSheetButton *
4810 button = psppire_sheet_model_get_column_button (sheet->model, col);
4812 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4814 allocation.x -= sheet->hadjustment->value;
4816 allocation.height = sheet->column_title_area.height;
4817 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4818 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4820 draw_button (sheet, sheet->column_title_window,
4821 button, is_sensitive, allocation);
4824 gdk_window_end_paint (sheet->column_title_window);
4829 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4833 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4835 if (!sheet->row_titles_visible) return;
4837 g_return_if_fail (first >= min_visible_row (sheet));
4838 g_return_if_fail (last <= max_visible_row (sheet));
4841 rect.width = sheet->row_title_area.width;
4842 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4843 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4844 + psppire_axis_unit_size (sheet->vaxis, last);
4846 rect.y -= sheet->vadjustment->value;
4848 minimize_int (&rect.height, sheet->row_title_area.height);
4849 maximize_int (&rect.y, 0);
4851 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4852 for (row = first; row <= last; ++row)
4854 GdkRectangle allocation;
4856 gboolean is_sensitive = FALSE;
4858 PsppireSheetButton *button =
4859 psppire_sheet_model_get_row_button (sheet->model, row);
4861 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4863 allocation.y -= sheet->vadjustment->value;
4865 allocation.width = sheet->row_title_area.width;
4866 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4867 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4869 draw_button (sheet, sheet->row_title_window,
4870 button, is_sensitive, allocation);
4873 gdk_window_end_paint (sheet->row_title_window);
4880 * vadjustment_value_changed
4881 * hadjustment_value_changed */
4885 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4888 (adj->value + adj->page_size)
4890 (adj->upper - adj->lower);
4892 const glong last_item = psppire_axis_unit_count (axis) - 1;
4894 if (isnan (position) || position < 0)
4898 psppire_axis_start_pixel (axis, last_item)
4900 psppire_axis_unit_size (axis, last_item)
4904 adj->page_size = page_size;
4907 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4909 if ( adj->value < adj->lower)
4910 adj->value = adj->lower;
4913 gtk_adjustment_changed (adj);
4918 adjust_scrollbars (PsppireSheet *sheet)
4922 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4925 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4927 if ( sheet->row_titles_visible)
4928 width -= sheet->row_title_area.width;
4930 if (sheet->column_titles_visible)
4931 height -= sheet->column_title_area.height;
4933 if (sheet->vadjustment)
4935 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4937 sheet->vadjustment->step_increment =
4939 psppire_axis_unit_size (sheet->vaxis, last_row);
4941 sheet->vadjustment->page_increment =
4943 sheet->column_title_area.height -
4944 psppire_axis_unit_size (sheet->vaxis, last_row);
4946 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4949 if (sheet->hadjustment)
4951 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4952 sheet->hadjustment->step_increment = 1;
4954 sheet->hadjustment->page_increment = width;
4956 sheet->hadjustment->upper =
4957 psppire_axis_start_pixel (sheet->haxis, last_col)
4959 psppire_axis_unit_size (sheet->haxis, last_col)
4962 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4966 /* Subtracts the region of WIDGET from REGION */
4968 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4971 GdkRectangle intersect;
4974 gdk_region_get_clipbox (region, &rect);
4975 gtk_widget_intersect (widget,
4979 region2 = gdk_region_rectangle (&intersect);
4980 gdk_region_subtract (region, region2);
4981 gdk_region_destroy (region2);
4985 vadjustment_value_changed (GtkAdjustment *adjustment,
4989 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4991 g_return_if_fail (adjustment != NULL);
4993 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4995 gtk_widget_hide (sheet->entry_widget);
4998 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5000 subtract_widget_region (region, sheet->button);
5001 gdk_window_begin_paint_region (sheet->sheet_window, region);
5003 draw_sheet_region (sheet, region);
5005 draw_row_title_buttons (sheet);
5006 psppire_sheet_draw_active_cell (sheet);
5008 gdk_window_end_paint (sheet->sheet_window);
5009 gdk_region_destroy (region);
5014 hadjustment_value_changed (GtkAdjustment *adjustment,
5018 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5020 g_return_if_fail (adjustment != NULL);
5022 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5024 gtk_widget_hide (sheet->entry_widget);
5028 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5030 subtract_widget_region (region, sheet->button);
5031 gdk_window_begin_paint_region (sheet->sheet_window, region);
5033 draw_sheet_region (sheet, region);
5035 draw_column_title_buttons (sheet);
5037 psppire_sheet_draw_active_cell (sheet);
5039 gdk_window_end_paint (sheet->sheet_window);
5041 gdk_region_destroy (region);
5045 /* COLUMN RESIZING */
5047 draw_xor_vline (PsppireSheet *sheet)
5050 gint xpos = sheet->x_drag;
5051 gdk_drawable_get_size (sheet->sheet_window,
5054 if (sheet->row_titles_visible)
5055 xpos += sheet->row_title_area.width;
5057 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5059 sheet->column_title_area.height,
5061 height + CELL_SPACING);
5066 draw_xor_hline (PsppireSheet *sheet)
5070 gint ypos = sheet->y_drag;
5072 gdk_drawable_get_size (sheet->sheet_window,
5076 if (sheet->column_titles_visible)
5077 ypos += sheet->column_title_area.height;
5079 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5080 sheet->row_title_area.width,
5082 width + CELL_SPACING,
5086 /* SELECTED RANGE */
5088 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
5091 GdkRectangle clip_area, area;
5094 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
5095 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
5096 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
5097 psppire_axis_unit_size (sheet->haxis, range.coli);
5098 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
5099 psppire_axis_unit_size (sheet->vaxis, range.rowi);
5101 clip_area.x = sheet->row_title_area.width;
5102 clip_area.y = sheet->column_title_area.height;
5104 gdk_drawable_get_size (sheet->sheet_window,
5105 &clip_area.width, &clip_area.height);
5107 if (!sheet->row_titles_visible) clip_area.x = 0;
5108 if (!sheet->column_titles_visible) clip_area.y = 0;
5112 area.width = area.width + area.x;
5115 if (area.width > clip_area.width) area.width = clip_area.width + 10;
5118 area.height = area.height + area.y;
5121 if (area.height > clip_area.height) area.height = clip_area.height + 10;
5125 clip_area.width += 3;
5126 clip_area.height += 3;
5128 gdk_gc_get_values (sheet->xor_gc, &values);
5130 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5132 gdk_draw_rectangle (sheet->sheet_window,
5135 area.x + i, area.y + i,
5136 area.width - 2 * i, area.height - 2 * i);
5139 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5141 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5146 set_column_width (PsppireSheet *sheet,
5150 g_return_if_fail (sheet != NULL);
5151 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5153 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
5159 psppire_axis_resize (sheet->haxis, column, width);
5161 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5163 draw_column_title_buttons (sheet);
5164 adjust_scrollbars (sheet);
5165 psppire_sheet_size_allocate_entry (sheet);
5166 redraw_range (sheet, NULL);
5171 set_row_height (PsppireSheet *sheet,
5175 g_return_if_fail (sheet != NULL);
5176 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5178 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
5184 psppire_axis_resize (sheet->vaxis, row, height);
5186 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
5188 draw_row_title_buttons (sheet);
5189 adjust_scrollbars (sheet);
5190 psppire_sheet_size_allocate_entry (sheet);
5191 redraw_range (sheet, NULL);
5196 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
5197 PsppireSheetCellAttr *attr)
5200 const GtkJustification *j ;
5201 GdkColormap *colormap;
5203 g_return_val_if_fail (sheet != NULL, FALSE);
5204 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
5206 if (row < 0 || col < 0) return FALSE;
5208 attr->foreground = GTK_WIDGET (sheet)->style->black;
5209 attr->background = sheet->color[BG_COLOR];
5211 attr->border.width = 0;
5212 attr->border.line_style = GDK_LINE_SOLID;
5213 attr->border.cap_style = GDK_CAP_NOT_LAST;
5214 attr->border.join_style = GDK_JOIN_MITER;
5215 attr->border.mask = 0;
5216 attr->border.color = GTK_WIDGET (sheet)->style->black;
5218 attr->is_editable = psppire_sheet_model_is_editable (sheet->model, row, col);
5220 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
5221 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
5224 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
5225 attr->foreground = *fg;
5228 bg = psppire_sheet_model_get_background (sheet->model, row, col);
5231 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
5232 attr->background = *bg;
5235 attr->justification =
5236 psppire_sheet_model_get_column_justification (sheet->model, col);
5238 j = psppire_sheet_model_get_justification (sheet->model, row, col);
5240 attr->justification = *j;
5246 psppire_sheet_button_size_request (PsppireSheet *sheet,
5247 const PsppireSheetButton *button,
5248 GtkRequisition *button_requisition)
5250 GtkRequisition requisition;
5251 GtkRequisition label_requisition;
5253 label_requisition.height = DEFAULT_ROW_HEIGHT;
5254 label_requisition.width = COLUMN_MIN_WIDTH;
5256 requisition.height = DEFAULT_ROW_HEIGHT;
5257 requisition.width = COLUMN_MIN_WIDTH;
5260 *button_requisition = requisition;
5261 button_requisition->width = MAX (requisition.width, label_requisition.width);
5262 button_requisition->height = MAX (requisition.height, label_requisition.height);
5267 psppire_sheet_forall (GtkContainer *container,
5268 gboolean include_internals,
5269 GtkCallback callback,
5270 gpointer callback_data)
5272 PsppireSheet *sheet = PSPPIRE_SHEET (container);
5274 g_return_if_fail (callback != NULL);
5276 if (sheet->button && sheet->button->parent)
5277 (* callback) (sheet->button, callback_data);
5279 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
5280 (* callback) (sheet->entry_widget, callback_data);
5285 psppire_sheet_get_model (const PsppireSheet *sheet)
5287 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
5289 return sheet->model;
5293 PsppireSheetButton *
5294 psppire_sheet_button_new (void)
5296 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
5298 button->state = GTK_STATE_NORMAL;
5299 button->label = NULL;
5300 button->label_visible = TRUE;
5301 button->justification = GTK_JUSTIFY_FILL;
5308 psppire_sheet_button_free (PsppireSheetButton *button)
5310 if (!button) return ;
5312 g_free (button->label);
5317 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5319 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5321 if ( NULL == celltext)
5324 g_string_append (string, celltext);
5330 range_to_text (const PsppireSheet *sheet)
5335 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5338 string = g_string_sized_new (80);
5340 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5342 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5344 append_cell_text (string, sheet, r, c);
5345 g_string_append (string, "\t");
5347 append_cell_text (string, sheet, r, c);
5348 if ( r < sheet->range.rowi)
5349 g_string_append (string, "\n");
5356 range_to_html (const PsppireSheet *sheet)
5361 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5364 string = g_string_sized_new (480);
5366 g_string_append (string, "<html>\n");
5367 g_string_append (string, "<body>\n");
5368 g_string_append (string, "<table>\n");
5369 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5371 g_string_append (string, "<tr>\n");
5372 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5374 g_string_append (string, "<td>");
5375 append_cell_text (string, sheet, r, c);
5376 g_string_append (string, "</td>\n");
5378 g_string_append (string, "</tr>\n");
5380 g_string_append (string, "</table>\n");
5381 g_string_append (string, "</body>\n");
5382 g_string_append (string, "</html>\n");
5394 primary_get_cb (GtkClipboard *clipboard,
5395 GtkSelectionData *selection_data,
5399 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5400 GString *string = NULL;
5404 case SELECT_FMT_TEXT:
5405 string = range_to_text (sheet);
5407 case SELECT_FMT_HTML:
5408 string = range_to_html (sheet);
5411 g_assert_not_reached ();
5414 gtk_selection_data_set (selection_data, selection_data->target,
5416 (const guchar *) string->str, string->len);
5417 g_string_free (string, TRUE);
5421 primary_clear_cb (GtkClipboard *clipboard,
5424 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5425 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5428 psppire_sheet_real_unselect_range (sheet, NULL);
5432 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5434 static const GtkTargetEntry targets[] = {
5435 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5436 { "STRING", 0, SELECT_FMT_TEXT },
5437 { "TEXT", 0, SELECT_FMT_TEXT },
5438 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5439 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5440 { "text/plain", 0, SELECT_FMT_TEXT },
5441 { "text/html", 0, SELECT_FMT_HTML }
5444 GtkClipboard *clipboard;
5446 if (!GTK_WIDGET_REALIZED (sheet))
5449 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5450 GDK_SELECTION_PRIMARY);
5452 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5454 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5455 G_N_ELEMENTS (targets),
5456 primary_get_cb, primary_clear_cb,
5458 primary_clear_cb (clipboard, sheet);
5462 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5463 gtk_clipboard_clear (clipboard);