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);
479 static gboolean psppire_sheet_focus_in (GtkWidget *widget,
480 GdkEventFocus *event);
484 static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
485 const PsppireSheetRange *range);
486 static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet,
487 gint row, gint column);
488 /* Drawing Routines */
491 static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column);
494 /* draw visible part of range. */
495 static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region);
498 /* highlight the visible part of the selected range */
499 static void psppire_sheet_range_draw_selection (PsppireSheet *sheet,
500 PsppireSheetRange range);
504 static void psppire_sheet_real_select_range (PsppireSheet *sheet,
505 const PsppireSheetRange *range);
506 static void psppire_sheet_real_unselect_range (PsppireSheet *sheet,
507 const PsppireSheetRange *range);
508 static void psppire_sheet_extend_selection (PsppireSheet *sheet,
509 gint row, gint column);
510 static void psppire_sheet_new_selection (PsppireSheet *sheet,
511 PsppireSheetRange *range);
512 static void psppire_sheet_draw_border (PsppireSheet *sheet,
513 PsppireSheetRange range);
515 /* Active Cell handling */
517 static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet);
518 static void change_active_cell (PsppireSheet *sheet, gint row, gint col);
519 static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet);
520 static void psppire_sheet_show_entry_widget (PsppireSheet *sheet);
521 static gboolean psppire_sheet_click_cell (PsppireSheet *sheet,
528 static void adjust_scrollbars (PsppireSheet *sheet);
529 static void vadjustment_value_changed (GtkAdjustment *adjustment,
531 static void hadjustment_value_changed (GtkAdjustment *adjustment,
535 static void draw_xor_vline (PsppireSheet *sheet);
536 static void draw_xor_hline (PsppireSheet *sheet);
537 static void draw_xor_rectangle (PsppireSheet *sheet,
538 PsppireSheetRange range);
542 static void create_global_button (PsppireSheet *sheet);
543 static void global_button_clicked (GtkWidget *widget,
547 static void create_sheet_entry (PsppireSheet *sheet);
548 static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet);
550 /* Sheet button gadgets */
552 static void draw_column_title_buttons (PsppireSheet *sheet);
553 static void draw_row_title_buttons (PsppireSheet *sheet);
556 static void size_allocate_global_button (PsppireSheet *sheet);
557 static void psppire_sheet_button_size_request (PsppireSheet *sheet,
558 const PsppireSheetButton *button,
559 GtkRequisition *requisition);
561 static void psppire_sheet_real_cell_clear (PsppireSheet *sheet,
583 static GtkContainerClass *parent_class = NULL;
584 static guint sheet_signals[LAST_SIGNAL] = { 0 };
588 psppire_sheet_get_type ()
590 static GType sheet_type = 0;
594 static const GTypeInfo sheet_info =
596 sizeof (PsppireSheetClass),
599 (GClassInitFunc) psppire_sheet_class_init,
602 sizeof (PsppireSheet),
604 (GInstanceInitFunc) psppire_sheet_init,
609 g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
617 static PsppireSheetRange*
618 psppire_sheet_range_copy (const PsppireSheetRange *range)
620 PsppireSheetRange *new_range;
622 g_return_val_if_fail (range != NULL, NULL);
624 new_range = g_new (PsppireSheetRange, 1);
632 psppire_sheet_range_free (PsppireSheetRange *range)
634 g_return_if_fail (range != NULL);
640 psppire_sheet_range_get_type (void)
642 static GType sheet_range_type = 0;
644 if (!sheet_range_type)
647 g_boxed_type_register_static ("PsppireSheetRange",
648 (GBoxedCopyFunc) psppire_sheet_range_copy,
649 (GBoxedFreeFunc) psppire_sheet_range_free);
652 return sheet_range_type;
655 static PsppireSheetCell*
656 psppire_sheet_cell_copy (const PsppireSheetCell *cell)
658 PsppireSheetCell *new_cell;
660 g_return_val_if_fail (cell != NULL, NULL);
662 new_cell = g_new (PsppireSheetCell, 1);
670 psppire_sheet_cell_free (PsppireSheetCell *cell)
672 g_return_if_fail (cell != NULL);
678 psppire_sheet_cell_get_type (void)
680 static GType sheet_cell_type = 0;
682 if (!sheet_cell_type)
685 g_boxed_type_register_static ("PsppireSheetCell",
686 (GBoxedCopyFunc) psppire_sheet_cell_copy,
687 (GBoxedFreeFunc) psppire_sheet_cell_free);
690 return sheet_cell_type;
704 resize_column (PsppireSheet *sheet, gint unit, glong size)
706 PsppireSheetRange range;
708 range.coli = max_visible_column (sheet);
709 range.row0 = min_visible_row (sheet);
710 range.rowi = max_visible_row (sheet);
712 redraw_range (sheet, &range);
714 draw_column_title_buttons_range (sheet, range.col0, range.coli);
719 psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
722 g_object_unref (sheet->haxis);
725 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
728 g_object_ref (sheet->haxis);
732 resize_row (PsppireSheet *sheet, gint unit, glong size)
734 PsppireSheetRange range;
735 range.col0 = min_visible_column (sheet);
736 range.coli = max_visible_column (sheet);
738 range.rowi = max_visible_row (sheet);
740 redraw_range (sheet, &range);
742 draw_row_title_buttons_range (sheet, range.row0, range.rowi);
746 psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
749 g_object_unref (sheet->vaxis);
753 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
756 g_object_ref (sheet->vaxis);
761 psppire_sheet_set_property (GObject *object,
767 PsppireSheet *sheet = PSPPIRE_SHEET (object);
772 psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
775 psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
778 psppire_sheet_set_model (sheet, g_value_get_pointer (value));
781 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
787 psppire_sheet_get_property (GObject *object,
792 PsppireSheet *sheet = PSPPIRE_SHEET (object);
797 g_value_set_pointer (value, sheet->vaxis);
800 g_value_set_pointer (value, sheet->haxis);
803 g_value_set_pointer (value, sheet->model);
806 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
813 psppire_sheet_class_init (PsppireSheetClass *klass)
815 GObjectClass *object_class = G_OBJECT_CLASS (klass);
817 GParamSpec *haxis_spec ;
818 GParamSpec *vaxis_spec ;
819 GParamSpec *model_spec ;
821 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
822 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
824 parent_class = g_type_class_peek_parent (klass);
827 * PsppireSheet::select-row
828 * @sheet: the sheet widget that emitted the signal
829 * @row: the newly selected row index
831 * A row has been selected.
833 sheet_signals[SELECT_ROW] =
834 g_signal_new ("select-row",
835 G_TYPE_FROM_CLASS (object_class),
837 offsetof (PsppireSheetClass, select_row),
839 g_cclosure_marshal_VOID__INT,
846 * PsppireSheet::select - column
847 * @sheet: the sheet widget that emitted the signal
848 * @column: the newly selected column index
850 * A column has been selected.
852 sheet_signals[SELECT_COLUMN] =
853 g_signal_new ("select-column",
854 G_TYPE_FROM_CLASS (object_class),
856 offsetof (PsppireSheetClass, select_column),
858 g_cclosure_marshal_VOID__INT,
865 * PsppireSheet::double-click-row
866 * @sheet: the sheet widget that emitted the signal
867 * @row: the row that was double clicked.
869 * A row's title button has been double clicked
871 sheet_signals[DOUBLE_CLICK_ROW] =
872 g_signal_new ("double-click-row",
873 G_TYPE_FROM_CLASS (object_class),
877 g_cclosure_marshal_VOID__INT,
884 * PsppireSheet::double-click-column
885 * @sheet: the sheet widget that emitted the signal
886 * @column: the column that was double clicked.
888 * A column's title button has been double clicked
890 sheet_signals[DOUBLE_CLICK_COLUMN] =
891 g_signal_new ("double-click-column",
892 G_TYPE_FROM_CLASS (object_class),
896 g_cclosure_marshal_VOID__INT,
903 * PsppireSheet::button-event-column
904 * @sheet: the sheet widget that emitted the signal
905 * @column: the column on which the event occured.
907 * A button event occured on a column title button
909 sheet_signals[BUTTON_EVENT_COLUMN] =
910 g_signal_new ("button-event-column",
911 G_TYPE_FROM_CLASS (object_class),
915 psppire_marshal_VOID__INT_POINTER,
924 * PsppireSheet::button-event-row
925 * @sheet: the sheet widget that emitted the signal
926 * @column: the column on which the event occured.
928 * A button event occured on a row title button
930 sheet_signals[BUTTON_EVENT_ROW] =
931 g_signal_new ("button-event-row",
932 G_TYPE_FROM_CLASS (object_class),
936 psppire_marshal_VOID__INT_POINTER,
944 sheet_signals[SELECT_RANGE] =
945 g_signal_new ("select-range",
946 G_TYPE_FROM_CLASS (object_class),
948 offsetof (PsppireSheetClass, select_range),
950 g_cclosure_marshal_VOID__BOXED,
953 PSPPIRE_TYPE_SHEET_RANGE);
956 sheet_signals[RESIZE_RANGE] =
957 g_signal_new ("resize-range",
958 G_TYPE_FROM_CLASS (object_class),
960 offsetof (PsppireSheetClass, resize_range),
962 psppire_marshal_VOID__BOXED_BOXED,
965 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
968 sheet_signals[MOVE_RANGE] =
969 g_signal_new ("move-range",
970 G_TYPE_FROM_CLASS (object_class),
972 offsetof (PsppireSheetClass, move_range),
974 psppire_marshal_VOID__BOXED_BOXED,
977 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
980 sheet_signals[TRAVERSE] =
981 g_signal_new ("traverse",
982 G_TYPE_FROM_CLASS (object_class),
984 offsetof (PsppireSheetClass, traverse),
986 psppire_marshal_BOOLEAN__BOXED_POINTER,
988 PSPPIRE_TYPE_SHEET_CELL,
992 sheet_signals[ACTIVATE] =
993 g_signal_new ("activate",
994 G_TYPE_FROM_CLASS (object_class),
996 offsetof (PsppireSheetClass, activate),
998 psppire_marshal_VOID__INT_INT_INT_INT,
1000 G_TYPE_INT, G_TYPE_INT,
1001 G_TYPE_INT, G_TYPE_INT);
1003 widget_class->set_scroll_adjustments_signal =
1004 g_signal_new ("set-scroll-adjustments",
1005 G_TYPE_FROM_CLASS (object_class),
1007 offsetof (PsppireSheetClass, set_scroll_adjustments),
1009 psppire_marshal_VOID__OBJECT_OBJECT,
1010 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1013 container_class->add = NULL;
1014 container_class->remove = NULL;
1015 container_class->forall = psppire_sheet_forall;
1016 container_class->set_focus_child = NULL;
1018 object_class->dispose = psppire_sheet_dispose;
1019 object_class->finalize = psppire_sheet_finalize;
1023 g_param_spec_pointer ("vertical-axis",
1025 "A pointer to the PsppireAxis object for the rows",
1026 G_PARAM_READABLE | G_PARAM_WRITABLE );
1029 g_param_spec_pointer ("horizontal-axis",
1031 "A pointer to the PsppireAxis object for the columns",
1032 G_PARAM_READABLE | G_PARAM_WRITABLE );
1035 g_param_spec_pointer ("model",
1037 "A pointer to the data model",
1038 G_PARAM_READABLE | G_PARAM_WRITABLE );
1041 object_class->set_property = psppire_sheet_set_property;
1042 object_class->get_property = psppire_sheet_get_property;
1044 g_object_class_install_property (object_class,
1048 g_object_class_install_property (object_class,
1052 g_object_class_install_property (object_class,
1057 widget_class->realize = psppire_sheet_realize;
1058 widget_class->unrealize = psppire_sheet_unrealize;
1059 widget_class->map = psppire_sheet_map;
1060 widget_class->unmap = psppire_sheet_unmap;
1061 widget_class->style_set = psppire_sheet_style_set;
1062 widget_class->button_press_event = psppire_sheet_button_press;
1063 widget_class->button_release_event = psppire_sheet_button_release;
1064 widget_class->motion_notify_event = psppire_sheet_motion;
1065 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1066 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1067 widget_class->key_press_event = psppire_sheet_key_press;
1068 widget_class->expose_event = psppire_sheet_expose;
1069 widget_class->size_request = psppire_sheet_size_request;
1070 widget_class->size_allocate = psppire_sheet_size_allocate;
1071 widget_class->focus_in_event = psppire_sheet_focus_in;
1072 widget_class->focus_out_event = NULL;
1074 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1075 klass->select_row = NULL;
1076 klass->select_column = NULL;
1077 klass->select_range = NULL;
1078 klass->resize_range = NULL;
1079 klass->move_range = NULL;
1080 klass->traverse = NULL;
1081 klass->activate = NULL;
1082 klass->changed = NULL;
1086 psppire_sheet_init (PsppireSheet *sheet)
1088 sheet->model = NULL;
1089 sheet->haxis = NULL;
1090 sheet->vaxis = NULL;
1093 sheet->selection_mode = GTK_SELECTION_NONE;
1094 sheet->state = PSPPIRE_SHEET_NORMAL;
1096 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1097 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1099 sheet->column_title_window = NULL;
1100 sheet->column_title_area.x = 0;
1101 sheet->column_title_area.y = 0;
1102 sheet->column_title_area.width = 0;
1103 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1105 sheet->row_title_window = NULL;
1106 sheet->row_title_area.x = 0;
1107 sheet->row_title_area.y = 0;
1108 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1109 sheet->row_title_area.height = 0;
1112 sheet->active_cell.row = 0;
1113 sheet->active_cell.col = 0;
1114 sheet->selection_cell.row = 0;
1115 sheet->selection_cell.col = 0;
1117 sheet->range.row0 = 0;
1118 sheet->range.rowi = 0;
1119 sheet->range.col0 = 0;
1120 sheet->range.coli = 0;
1122 sheet->state = PSPPIRE_SHEET_NORMAL;
1124 sheet->sheet_window = NULL;
1125 sheet->entry_widget = NULL;
1126 sheet->button = NULL;
1128 sheet->hadjustment = NULL;
1129 sheet->vadjustment = NULL;
1131 sheet->cursor_drag = NULL;
1133 sheet->xor_gc = NULL;
1134 sheet->fg_gc = NULL;
1135 sheet->bg_gc = NULL;
1138 sheet->show_grid = TRUE;
1140 sheet->motion_timer = 0;
1142 sheet->row_titles_visible = TRUE;
1143 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1145 sheet->column_titles_visible = TRUE;
1148 /* create sheet entry */
1149 sheet->entry_type = GTK_TYPE_ENTRY;
1150 create_sheet_entry (sheet);
1152 /* create global selection button */
1153 create_global_button (sheet);
1157 /* Cause RANGE to be redrawn. If RANGE is null, then the
1158 entire visible range will be redrawn.
1161 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1165 if ( ! GTK_WIDGET_REALIZED (sheet))
1168 if ( NULL != range )
1169 rectangle_from_range (sheet, range, &rect);
1172 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1173 gdk_region_get_clipbox (r, &rect);
1175 if ( sheet->column_titles_visible)
1177 rect.y += sheet->column_title_area.height;
1178 rect.height -= sheet->column_title_area.height;
1181 if ( sheet->row_titles_visible)
1183 rect.x += sheet->row_title_area.width;
1184 rect.width -= sheet->row_title_area.width;
1188 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1192 /* Callback which occurs whenever columns are inserted / deleted in the model */
1194 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1198 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1200 PsppireSheetRange range;
1201 gint model_columns = psppire_sheet_model_get_column_count (model);
1204 /* Need to update all the columns starting from the first column and onwards.
1205 * Previous column are unchanged, so don't need to be updated.
1207 range.col0 = first_column;
1209 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1210 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1212 adjust_scrollbars (sheet);
1214 if (sheet->active_cell.col >= model_columns)
1215 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1217 draw_column_title_buttons_range (sheet,
1218 first_column, max_visible_column (sheet));
1221 redraw_range (sheet, &range);
1227 /* Callback which occurs whenever rows are inserted / deleted in the model */
1229 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1230 gint n_rows, gpointer data)
1232 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1234 PsppireSheetRange range;
1236 gint model_rows = psppire_sheet_model_get_row_count (model);
1238 /* Need to update all the rows starting from the first row and onwards.
1239 * Previous rows are unchanged, so don't need to be updated.
1241 range.row0 = first_row;
1243 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1244 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1246 adjust_scrollbars (sheet);
1248 if (sheet->active_cell.row >= model_rows)
1249 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1251 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1253 redraw_range (sheet, &range);
1257 If row0 or rowi are negative, then all rows will be updated.
1258 If col0 or coli are negative, then all columns will be updated.
1261 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1262 gint rowi, gint coli, gpointer data)
1264 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1266 PsppireSheetRange range;
1273 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1276 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1278 redraw_range (sheet, NULL);
1279 adjust_scrollbars (sheet);
1281 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1282 max_visible_row (sheet));
1284 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1285 max_visible_column (sheet));
1289 else if ( row0 < 0 || rowi < 0 )
1291 range.row0 = min_visible_row (sheet);
1292 range.rowi = max_visible_row (sheet);
1294 else if ( col0 < 0 || coli < 0 )
1296 range.col0 = min_visible_column (sheet);
1297 range.coli = max_visible_column (sheet);
1300 redraw_range (sheet, &range);
1305 * psppire_sheet_new:
1306 * @rows: initial number of rows
1307 * @columns: initial number of columns
1308 * @title: sheet title
1309 * @model: the model to use for the sheet data
1311 * Creates a new sheet widget with the given number of rows and columns.
1313 * Returns: the new sheet widget
1316 psppire_sheet_new (PsppireSheetModel *model)
1318 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1326 * psppire_sheet_set_model
1327 * @sheet: the sheet to set the model for
1328 * @model: the model to use for the sheet data
1330 * Sets the model for a PsppireSheet
1334 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1336 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1338 if (sheet->model ) g_object_unref (sheet->model);
1340 sheet->model = model;
1344 g_object_ref (model);
1346 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1347 G_CALLBACK (range_update_callback),
1350 g_signal_connect (model, "rows_inserted",
1351 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1353 g_signal_connect (model, "rows_deleted",
1354 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1356 g_signal_connect (model, "columns_inserted",
1357 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1359 g_signal_connect (model, "columns_deleted",
1360 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1366 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1370 g_return_if_fail (sheet != NULL);
1371 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1373 state = sheet->state;
1375 if (sheet->state == PSPPIRE_SHEET_NORMAL)
1376 psppire_sheet_hide_entry_widget (sheet);
1378 sheet->entry_type = entry_type;
1380 create_sheet_entry (sheet);
1382 if (state == PSPPIRE_SHEET_NORMAL)
1384 psppire_sheet_show_entry_widget (sheet);
1390 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1392 g_return_if_fail (sheet != NULL);
1393 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1395 if (show == sheet->show_grid) return;
1397 sheet->show_grid = show;
1399 redraw_range (sheet, NULL);
1403 psppire_sheet_grid_visible (PsppireSheet *sheet)
1405 g_return_val_if_fail (sheet != NULL, 0);
1406 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1408 return sheet->show_grid;
1412 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1414 g_return_val_if_fail (sheet != NULL, 0);
1415 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1417 return psppire_axis_unit_count (sheet->haxis);
1420 static void set_column_width (PsppireSheet *sheet,
1426 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1428 if (sheet->column_titles_visible) return;
1430 sheet->column_titles_visible = TRUE;
1432 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1435 gdk_window_show (sheet->column_title_window);
1436 gdk_window_move_resize (sheet->column_title_window,
1437 sheet->column_title_area.x,
1438 sheet->column_title_area.y,
1439 sheet->column_title_area.width,
1440 sheet->column_title_area.height);
1442 adjust_scrollbars (sheet);
1444 if (sheet->vadjustment)
1445 g_signal_emit_by_name (sheet->vadjustment,
1448 size_allocate_global_button (sheet);
1450 if ( sheet->row_titles_visible)
1451 gtk_widget_show (sheet->button);
1456 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1458 if (sheet->row_titles_visible) return;
1460 sheet->row_titles_visible = TRUE;
1463 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1465 gdk_window_show (sheet->row_title_window);
1466 gdk_window_move_resize (sheet->row_title_window,
1467 sheet->row_title_area.x,
1468 sheet->row_title_area.y,
1469 sheet->row_title_area.width,
1470 sheet->row_title_area.height);
1472 adjust_scrollbars (sheet);
1475 if (sheet->hadjustment)
1476 g_signal_emit_by_name (sheet->hadjustment,
1479 size_allocate_global_button (sheet);
1481 if ( sheet->column_titles_visible)
1482 gtk_widget_show (sheet->button);
1486 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1488 if (!sheet->column_titles_visible) return;
1490 sheet->column_titles_visible = FALSE;
1492 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1494 if (sheet->column_title_window)
1495 gdk_window_hide (sheet->column_title_window);
1497 gtk_widget_hide (sheet->button);
1499 adjust_scrollbars (sheet);
1502 if (sheet->vadjustment)
1503 g_signal_emit_by_name (sheet->vadjustment,
1508 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1510 if (!sheet->row_titles_visible) return;
1512 sheet->row_titles_visible = FALSE;
1514 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1516 if (sheet->row_title_window)
1517 gdk_window_hide (sheet->row_title_window);
1519 gtk_widget_hide (sheet->button);
1521 adjust_scrollbars (sheet);
1524 if (sheet->hadjustment)
1525 g_signal_emit_by_name (sheet->hadjustment,
1530 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1531 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1532 at the {top,left} of the sheet. If it's 1, then it'll
1533 be placed at the {bottom,right}.
1534 ROW or COL may be -1, in which case scrolling in that dimension
1538 psppire_sheet_moveto (PsppireSheet *sheet,
1546 g_return_if_fail (row_align >= 0);
1547 g_return_if_fail (col_align >= 0);
1549 g_return_if_fail (row_align <= 1);
1550 g_return_if_fail (col_align <= 1);
1552 g_return_if_fail (col <
1553 psppire_axis_unit_count (sheet->haxis));
1554 g_return_if_fail (row <
1555 psppire_axis_unit_count (sheet->vaxis));
1557 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1562 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1564 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1570 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1572 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1578 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
1580 g_return_if_fail (sheet != NULL);
1581 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1583 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
1586 if (sheet->state != PSPPIRE_SHEET_NORMAL)
1587 psppire_sheet_real_unselect_range (sheet, NULL);
1589 sheet->state = PSPPIRE_SHEET_ROW_SELECTED;
1590 sheet->range.row0 = row;
1591 sheet->range.col0 = 0;
1592 sheet->range.rowi = row;
1593 sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1594 sheet->active_cell.row = row;
1596 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
1597 psppire_sheet_real_select_range (sheet, NULL);
1602 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
1604 g_return_if_fail (sheet != NULL);
1605 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1607 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
1610 if (sheet->state != PSPPIRE_SHEET_NORMAL)
1611 psppire_sheet_real_unselect_range (sheet, NULL);
1613 sheet->state = PSPPIRE_SHEET_COLUMN_SELECTED;
1614 sheet->range.row0 = 0;
1615 sheet->range.col0 = column;
1616 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1617 sheet->range.coli = column;
1618 sheet->active_cell.col = column;
1620 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
1621 psppire_sheet_real_select_range (sheet, NULL);
1628 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1629 const PsppireSheetRange *range)
1631 g_return_val_if_fail (sheet != NULL, FALSE);
1633 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1636 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1639 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1642 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1645 if (range->rowi < min_visible_row (sheet))
1648 if (range->row0 > max_visible_row (sheet))
1651 if (range->coli < min_visible_column (sheet))
1654 if (range->col0 > max_visible_column (sheet))
1661 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1662 gint row, gint column)
1664 PsppireSheetRange range;
1667 range.col0 = column;
1669 range.coli = column;
1671 return psppire_sheet_range_isvisible (sheet, &range);
1675 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1677 g_return_if_fail (sheet != NULL);
1678 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1679 g_return_if_fail (range != NULL);
1681 range->row0 = min_visible_row (sheet);
1682 range->col0 = min_visible_column (sheet);
1683 range->rowi = max_visible_row (sheet);
1684 range->coli = max_visible_column (sheet);
1689 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1690 GtkAdjustment *hadjustment,
1691 GtkAdjustment *vadjustment)
1693 if ( sheet->vadjustment != vadjustment )
1695 if (sheet->vadjustment)
1696 g_object_unref (sheet->vadjustment);
1697 sheet->vadjustment = vadjustment;
1701 g_object_ref (vadjustment);
1703 g_signal_connect (sheet->vadjustment, "value_changed",
1704 G_CALLBACK (vadjustment_value_changed),
1709 if ( sheet->hadjustment != hadjustment )
1711 if (sheet->hadjustment)
1712 g_object_unref (sheet->hadjustment);
1714 sheet->hadjustment = hadjustment;
1718 g_object_ref (hadjustment);
1720 g_signal_connect (sheet->hadjustment, "value_changed",
1721 G_CALLBACK (hadjustment_value_changed),
1729 psppire_sheet_finalize (GObject *object)
1731 PsppireSheet *sheet;
1733 g_return_if_fail (object != NULL);
1734 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1736 sheet = PSPPIRE_SHEET (object);
1738 if (G_OBJECT_CLASS (parent_class)->finalize)
1739 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1743 psppire_sheet_dispose (GObject *object)
1745 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1747 g_return_if_fail (object != NULL);
1748 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1750 if ( sheet->dispose_has_run )
1753 sheet->dispose_has_run = TRUE;
1755 if (sheet->model) g_object_unref (sheet->model);
1756 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1757 if (sheet->haxis) g_object_unref (sheet->haxis);
1759 g_object_unref (sheet->button);
1760 sheet->button = NULL;
1762 /* unref adjustments */
1763 if (sheet->hadjustment)
1765 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1766 G_SIGNAL_MATCH_DATA,
1770 g_object_unref (sheet->hadjustment);
1771 sheet->hadjustment = NULL;
1774 if (sheet->vadjustment)
1776 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1777 G_SIGNAL_MATCH_DATA,
1781 g_object_unref (sheet->vadjustment);
1783 sheet->vadjustment = NULL;
1786 if (G_OBJECT_CLASS (parent_class)->dispose)
1787 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1791 psppire_sheet_style_set (GtkWidget *widget,
1792 GtkStyle *previous_style)
1794 PsppireSheet *sheet;
1796 g_return_if_fail (widget != NULL);
1797 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1799 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1800 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1802 sheet = PSPPIRE_SHEET (widget);
1804 if (GTK_WIDGET_REALIZED (widget))
1806 gtk_style_set_background (widget->style, widget->window, widget->state);
1809 set_entry_widget_font (sheet);
1812 #define BORDER_WIDTH 3
1815 psppire_sheet_realize (GtkWidget *widget)
1817 PsppireSheet *sheet;
1818 GdkWindowAttr attributes;
1819 const gint attributes_mask =
1820 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1823 GdkColormap *colormap;
1824 GdkDisplay *display;
1826 g_return_if_fail (widget != NULL);
1827 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1829 sheet = PSPPIRE_SHEET (widget);
1831 colormap = gtk_widget_get_colormap (widget);
1832 display = gtk_widget_get_display (widget);
1834 attributes.window_type = GDK_WINDOW_CHILD;
1835 attributes.x = widget->allocation.x;
1836 attributes.y = widget->allocation.y;
1837 attributes.width = widget->allocation.width;
1838 attributes.height = widget->allocation.height;
1839 attributes.wclass = GDK_INPUT_OUTPUT;
1841 attributes.visual = gtk_widget_get_visual (widget);
1842 attributes.colormap = colormap;
1844 attributes.event_mask = gtk_widget_get_events (widget);
1845 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1846 GDK_BUTTON_PRESS_MASK |
1847 GDK_BUTTON_RELEASE_MASK |
1848 GDK_KEY_PRESS_MASK |
1849 GDK_ENTER_NOTIFY_MASK |
1850 GDK_LEAVE_NOTIFY_MASK |
1851 GDK_POINTER_MOTION_MASK |
1852 GDK_POINTER_MOTION_HINT_MASK);
1854 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1857 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1859 gdk_window_set_user_data (widget->window, sheet);
1861 widget->style = gtk_style_attach (widget->style, widget->window);
1863 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1865 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1866 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1868 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1869 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1874 attributes.width = sheet->column_title_area.width;
1875 attributes.height = sheet->column_title_area.height;
1878 /* column - title window */
1879 sheet->column_title_window =
1880 gdk_window_new (widget->window, &attributes, attributes_mask);
1881 gdk_window_set_user_data (sheet->column_title_window, sheet);
1882 gtk_style_set_background (widget->style, sheet->column_title_window,
1888 attributes.width = sheet->row_title_area.width;
1889 attributes.height = sheet->row_title_area.height;
1891 /* row - title window */
1892 sheet->row_title_window = gdk_window_new (widget->window,
1893 &attributes, attributes_mask);
1894 gdk_window_set_user_data (sheet->row_title_window, sheet);
1895 gtk_style_set_background (widget->style, sheet->row_title_window,
1898 /* sheet - window */
1899 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1904 sheet->sheet_window = gdk_window_new (widget->window,
1905 &attributes, attributes_mask);
1906 gdk_window_set_user_data (sheet->sheet_window, sheet);
1908 gdk_cursor_unref (attributes.cursor);
1910 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1911 gdk_window_show (sheet->sheet_window);
1914 sheet->fg_gc = gdk_gc_new (widget->window);
1915 sheet->bg_gc = gdk_gc_new (widget->window);
1917 values.foreground = widget->style->white;
1918 values.function = GDK_INVERT;
1919 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1920 values.line_width = BORDER_WIDTH;
1922 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1931 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1932 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1934 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1935 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1938 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1940 if (sheet->column_titles_visible)
1941 gdk_window_show (sheet->column_title_window);
1942 if (sheet->row_titles_visible)
1943 gdk_window_show (sheet->row_title_window);
1945 sheet->hover_window = create_hover_window ();
1947 draw_row_title_buttons (sheet);
1948 draw_column_title_buttons (sheet);
1950 psppire_sheet_update_primary_selection (sheet);
1953 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1957 create_global_button (PsppireSheet *sheet)
1959 sheet->button = gtk_button_new_with_label (" ");
1961 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1963 g_object_ref_sink (sheet->button);
1965 g_signal_connect (sheet->button,
1967 G_CALLBACK (global_button_clicked),
1972 size_allocate_global_button (PsppireSheet *sheet)
1974 GtkAllocation allocation;
1976 if (!sheet->column_titles_visible) return;
1977 if (!sheet->row_titles_visible) return;
1979 gtk_widget_size_request (sheet->button, NULL);
1983 allocation.width = sheet->row_title_area.width;
1984 allocation.height = sheet->column_title_area.height;
1986 gtk_widget_size_allocate (sheet->button, &allocation);
1990 global_button_clicked (GtkWidget *widget, gpointer data)
1992 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
1997 psppire_sheet_unrealize (GtkWidget *widget)
1999 PsppireSheet *sheet;
2001 g_return_if_fail (widget != NULL);
2002 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2004 sheet = PSPPIRE_SHEET (widget);
2006 gdk_cursor_unref (sheet->cursor_drag);
2007 sheet->cursor_drag = NULL;
2009 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2010 sheet->color, n_COLORS);
2012 g_object_unref (sheet->xor_gc);
2013 g_object_unref (sheet->fg_gc);
2014 g_object_unref (sheet->bg_gc);
2016 destroy_hover_window (sheet->hover_window);
2018 gdk_window_destroy (sheet->sheet_window);
2019 gdk_window_destroy (sheet->column_title_window);
2020 gdk_window_destroy (sheet->row_title_window);
2022 gtk_widget_unparent (sheet->entry_widget);
2023 if (sheet->button != NULL)
2024 gtk_widget_unparent (sheet->button);
2026 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2027 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2031 psppire_sheet_map (GtkWidget *widget)
2033 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2035 g_return_if_fail (widget != NULL);
2036 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2038 if (!GTK_WIDGET_MAPPED (widget))
2040 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2042 gdk_window_show (widget->window);
2043 gdk_window_show (sheet->sheet_window);
2045 if (sheet->column_titles_visible)
2047 draw_column_title_buttons (sheet);
2048 gdk_window_show (sheet->column_title_window);
2050 if (sheet->row_titles_visible)
2052 draw_row_title_buttons (sheet);
2053 gdk_window_show (sheet->row_title_window);
2056 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2057 && sheet->active_cell.row >= 0
2058 && sheet->active_cell.col >= 0 )
2060 gtk_widget_show (sheet->entry_widget);
2061 gtk_widget_map (sheet->entry_widget);
2064 if (!GTK_WIDGET_MAPPED (sheet->button))
2066 gtk_widget_show (sheet->button);
2067 gtk_widget_map (sheet->button);
2070 redraw_range (sheet, NULL);
2071 change_active_cell (sheet,
2072 sheet->active_cell.row,
2073 sheet->active_cell.col);
2078 psppire_sheet_unmap (GtkWidget *widget)
2080 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2082 if (!GTK_WIDGET_MAPPED (widget))
2085 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2087 gdk_window_hide (sheet->sheet_window);
2088 if (sheet->column_titles_visible)
2089 gdk_window_hide (sheet->column_title_window);
2090 if (sheet->row_titles_visible)
2091 gdk_window_hide (sheet->row_title_window);
2092 gdk_window_hide (widget->window);
2094 if (GTK_WIDGET_MAPPED (sheet->entry_widget))
2095 gtk_widget_unmap (sheet->entry_widget);
2097 if (GTK_WIDGET_MAPPED (sheet->button))
2098 gtk_widget_unmap (sheet->button);
2103 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2105 PangoLayout *layout;
2106 PangoRectangle text;
2107 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2112 PsppireSheetCellAttr attributes;
2115 g_return_if_fail (sheet != NULL);
2117 /* bail now if we aren't yet drawable */
2118 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2121 row >= psppire_axis_unit_count (sheet->vaxis))
2125 col >= psppire_axis_unit_count (sheet->haxis))
2128 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2130 /* select GC for background rectangle */
2131 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2132 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2134 rectangle_from_cell (sheet, row, col, &area);
2136 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2138 if (sheet->show_grid)
2140 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2142 gdk_draw_rectangle (sheet->sheet_window,
2146 area.width, area.height);
2150 label = psppire_sheet_cell_get_text (sheet, row, col);
2155 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2156 dispose_string (sheet, label);
2159 pango_layout_set_font_description (layout, font_desc);
2161 pango_layout_get_pixel_extents (layout, NULL, &text);
2163 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2165 font_height = pango_font_description_get_size (font_desc);
2166 if ( !pango_font_description_get_size_is_absolute (font_desc))
2167 font_height /= PANGO_SCALE;
2169 /* Centre the text vertically */
2170 area.y += (area.height - font_height) / 2.0;
2172 switch (attributes.justification)
2174 case GTK_JUSTIFY_RIGHT:
2175 area.x += area.width - text.width;
2177 case GTK_JUSTIFY_CENTER:
2178 area.x += (area.width - text.width) / 2.0;
2180 case GTK_JUSTIFY_LEFT:
2184 g_critical ("Unhandled justification %d in column %d\n",
2185 attributes.justification, col);
2189 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2194 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2195 g_object_unref (layout);
2200 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2202 PsppireSheetRange range;
2207 PsppireSheetRange drawing_range;
2209 gdk_region_get_clipbox (region, &area);
2211 y = area.y + sheet->vadjustment->value;
2212 x = area.x + sheet->hadjustment->value;
2214 if ( sheet->column_titles_visible)
2215 y -= sheet->column_title_area.height;
2217 if ( sheet->row_titles_visible)
2218 x -= sheet->row_title_area.width;
2220 maximize_int (&x, 0);
2221 maximize_int (&y, 0);
2223 range.row0 = row_from_ypixel (sheet, y);
2224 range.rowi = row_from_ypixel (sheet, y + area.height);
2226 range.col0 = column_from_xpixel (sheet, x);
2227 range.coli = column_from_xpixel (sheet, x + area.width);
2229 g_return_if_fail (sheet != NULL);
2230 g_return_if_fail (PSPPIRE_SHEET (sheet));
2232 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2233 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2234 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2237 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2238 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2239 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2240 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2242 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2243 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2245 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2247 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2248 psppire_sheet_cell_draw (sheet, i, j);
2251 if (sheet->state != PSPPIRE_SHEET_NORMAL &&
2252 psppire_sheet_range_isvisible (sheet, &sheet->range))
2253 psppire_sheet_range_draw_selection (sheet, drawing_range);
2256 if (sheet->state == GTK_STATE_NORMAL &&
2257 sheet->active_cell.row >= drawing_range.row0 &&
2258 sheet->active_cell.row <= drawing_range.rowi &&
2259 sheet->active_cell.col >= drawing_range.col0 &&
2260 sheet->active_cell.col <= drawing_range.coli)
2261 psppire_sheet_show_entry_widget (sheet);
2266 psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range)
2270 PsppireSheetRange aux;
2272 if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
2273 range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
2276 if (!psppire_sheet_range_isvisible (sheet, &range)) return;
2277 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2281 range.col0 = MAX (sheet->range.col0, range.col0);
2282 range.coli = MIN (sheet->range.coli, range.coli);
2283 range.row0 = MAX (sheet->range.row0, range.row0);
2284 range.rowi = MIN (sheet->range.rowi, range.rowi);
2286 range.col0 = MAX (range.col0, min_visible_column (sheet));
2287 range.coli = MIN (range.coli, max_visible_column (sheet));
2288 range.row0 = MAX (range.row0, min_visible_row (sheet));
2289 range.rowi = MIN (range.rowi, max_visible_row (sheet));
2291 for (i = range.row0; i <= range.rowi; i++)
2293 for (j = range.col0; j <= range.coli; j++)
2295 if (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED)
2297 rectangle_from_cell (sheet, i, j, &area);
2299 if (i == sheet->range.row0)
2301 area.y = area.y + 2;
2302 area.height = area.height - 2;
2304 if (i == sheet->range.rowi) area.height = area.height - 3;
2305 if (j == sheet->range.col0)
2307 area.x = area.x + 2;
2308 area.width = area.width - 2;
2310 if (j == sheet->range.coli) area.width = area.width - 3;
2312 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2314 gdk_draw_rectangle (sheet->sheet_window,
2317 area.x + 1, area.y + 1,
2318 area.width, area.height);
2325 psppire_sheet_draw_border (sheet, sheet->range);
2329 safe_strcmp (const gchar *s1, const gchar *s2)
2331 if ( !s1 && !s2) return 0;
2332 if ( !s1) return -1;
2333 if ( !s2) return +1;
2334 return strcmp (s1, s2);
2338 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2339 GtkJustification justification,
2342 PsppireSheetModel *model ;
2345 g_return_if_fail (sheet != NULL);
2346 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2348 if (col >= psppire_axis_unit_count (sheet->haxis)
2349 || row >= psppire_axis_unit_count (sheet->vaxis))
2352 if (col < 0 || row < 0) return;
2354 model = psppire_sheet_get_model (sheet);
2356 old_text = psppire_sheet_model_get_string (model, row, col);
2358 if (0 != safe_strcmp (old_text, text))
2360 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2361 psppire_sheet_model_set_string (model, text, row, col);
2362 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2365 if ( psppire_sheet_model_free_strings (model))
2371 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2373 PsppireSheetRange range;
2375 g_return_if_fail (sheet != NULL);
2376 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2377 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2378 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2380 if (column < 0 || row < 0) return;
2384 range.col0 = min_visible_column (sheet);
2385 range.coli = max_visible_column (sheet);
2387 psppire_sheet_real_cell_clear (sheet, row, column);
2389 redraw_range (sheet, &range);
2393 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2395 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2397 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2399 if (old_text && strlen (old_text) > 0 )
2401 psppire_sheet_model_datum_clear (model, row, column);
2404 dispose_string (sheet, old_text);
2408 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2410 PsppireSheetModel *model;
2411 g_return_val_if_fail (sheet != NULL, NULL);
2412 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2414 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2416 if (col < 0 || row < 0) return NULL;
2418 model = psppire_sheet_get_model (sheet);
2423 return psppire_sheet_model_get_string (model, row, col);
2428 psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
2431 PsppireSheetRange *range;
2433 g_return_val_if_fail (sheet != NULL, 0);
2434 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2435 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
2436 if (col < 0 || row < 0) return 0;
2438 state = sheet->state;
2439 range = &sheet->range;
2443 case PSPPIRE_SHEET_NORMAL:
2444 return GTK_STATE_NORMAL;
2446 case PSPPIRE_SHEET_ROW_SELECTED:
2447 if (row >= range->row0 && row <= range->rowi)
2448 return GTK_STATE_SELECTED;
2450 case PSPPIRE_SHEET_COLUMN_SELECTED:
2451 if (col >= range->col0 && col <= range->coli)
2452 return GTK_STATE_SELECTED;
2454 case PSPPIRE_SHEET_RANGE_SELECTED:
2455 if (row >= range->row0 && row <= range->rowi && \
2456 col >= range->col0 && col <= range->coli)
2457 return GTK_STATE_SELECTED;
2460 return GTK_STATE_NORMAL;
2463 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2464 If the function returns FALSE, then the results will be unreliable.
2467 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2475 *column = -G_MAXINT;
2477 g_return_val_if_fail (sheet != NULL, 0);
2478 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2480 /* bounds checking, return false if the user clicked
2488 if ( sheet->column_titles_visible)
2489 y -= sheet->column_title_area.height;
2491 y += sheet->vadjustment->value;
2493 if ( y < 0 && sheet->column_titles_visible)
2499 trow = row_from_ypixel (sheet, y);
2500 if (trow > psppire_axis_unit_count (sheet->vaxis))
2506 if ( sheet->row_titles_visible)
2507 x -= sheet->row_title_area.width;
2509 x += sheet->hadjustment->value;
2511 if ( x < 0 && sheet->row_titles_visible)
2517 tcol = column_from_xpixel (sheet, x);
2518 if (tcol > psppire_axis_unit_count (sheet->haxis))
2528 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2533 g_return_val_if_fail (sheet != NULL, 0);
2534 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2536 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2539 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2540 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2542 area->width= (column == -1) ? sheet->row_title_area.width
2543 : psppire_axis_unit_size (sheet->haxis, column);
2545 area->height= (row == -1) ? sheet->column_title_area.height
2546 : psppire_axis_unit_size (sheet->vaxis, row);
2552 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2554 g_return_if_fail (sheet != NULL);
2555 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2557 if (row < -1 || col < -1)
2560 if (row >= psppire_axis_unit_count (sheet->vaxis)
2562 col >= psppire_axis_unit_count (sheet->haxis))
2565 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2568 if ( row == -1 || col == -1)
2570 psppire_sheet_hide_entry_widget (sheet);
2574 change_active_cell (sheet, row, col);
2578 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2580 g_return_if_fail (sheet != NULL);
2581 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2583 if ( row ) *row = sheet->active_cell.row;
2584 if (column) *column = sheet->active_cell.col;
2588 entry_load_text (PsppireSheet *sheet)
2592 GtkJustification justification;
2593 PsppireSheetCellAttr attributes;
2595 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2596 if (sheet->state != GTK_STATE_NORMAL) return;
2598 row = sheet->active_cell.row;
2599 col = sheet->active_cell.col;
2601 if (row < 0 || col < 0) return;
2603 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2605 if (text && strlen (text) > 0)
2607 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2608 justification = attributes.justification;
2609 psppire_sheet_set_cell (sheet, row, col, justification, text);
2615 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2617 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2620 if (sheet->active_cell.row < 0 ||
2621 sheet->active_cell.col < 0) return;
2623 gtk_widget_hide (sheet->entry_widget);
2624 gtk_widget_unmap (sheet->entry_widget);
2626 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2630 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2632 gint old_row, old_col;
2634 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2636 if (row < 0 || col < 0)
2639 if ( row > psppire_axis_unit_count (sheet->vaxis)
2640 || col > psppire_axis_unit_count (sheet->haxis))
2643 if (sheet->state != PSPPIRE_SHEET_NORMAL)
2645 sheet->state = PSPPIRE_SHEET_NORMAL;
2646 psppire_sheet_real_unselect_range (sheet, NULL);
2649 old_row = sheet->active_cell.row;
2650 old_col = sheet->active_cell.col;
2652 /* Erase the old cell */
2653 psppire_sheet_draw_active_cell (sheet);
2655 entry_load_text (sheet);
2657 sheet->range.row0 = row;
2658 sheet->range.col0 = col;
2659 sheet->range.rowi = row;
2660 sheet->range.coli = col;
2661 sheet->active_cell.row = row;
2662 sheet->active_cell.col = col;
2663 sheet->selection_cell.row = row;
2664 sheet->selection_cell.col = col;
2666 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2668 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2670 psppire_sheet_draw_active_cell (sheet);
2671 psppire_sheet_show_entry_widget (sheet);
2673 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2675 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2676 row, col, old_row, old_col);
2681 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2683 GtkEntry *sheet_entry;
2684 PsppireSheetCellAttr attributes;
2688 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2690 row = sheet->active_cell.row;
2691 col = sheet->active_cell.col;
2693 /* Don't show the active cell, if there is no active cell: */
2694 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2697 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2698 if (sheet->state != PSPPIRE_SHEET_NORMAL) return;
2699 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2701 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2703 sheet_entry = psppire_sheet_get_entry (sheet);
2705 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2707 if (GTK_IS_ENTRY (sheet_entry))
2709 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2710 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2713 text = g_strdup ("");
2715 if (strcmp (old_text, text) != 0)
2716 gtk_entry_set_text (sheet_entry, text);
2718 dispose_string (sheet, text);
2721 switch (attributes.justification)
2723 case GTK_JUSTIFY_RIGHT:
2724 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2726 case GTK_JUSTIFY_CENTER:
2727 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2729 case GTK_JUSTIFY_LEFT:
2731 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2737 psppire_sheet_size_allocate_entry (sheet);
2739 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2740 psppire_sheet_model_is_editable (sheet->model,
2742 gtk_widget_map (sheet->entry_widget);
2746 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2749 PsppireSheetRange range;
2751 row = sheet->active_cell.row;
2752 col = sheet->active_cell.col;
2754 if (row < 0 || col < 0) return FALSE;
2756 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2759 range.col0 = range.coli = col;
2760 range.row0 = range.rowi = row;
2762 psppire_sheet_draw_border (sheet, range);
2770 psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range)
2772 gint i, j, mask1, mask2;
2773 gint state, selected;
2774 gint x, y, width, height;
2775 PsppireSheetRange new_range, aux_range;
2777 g_return_if_fail (sheet != NULL);
2779 if (range == NULL) range=&sheet->range;
2783 range->row0 = MIN (range->row0, sheet->range.row0);
2784 range->rowi = MAX (range->rowi, sheet->range.rowi);
2785 range->col0 = MIN (range->col0, sheet->range.col0);
2786 range->coli = MAX (range->coli, sheet->range.coli);
2788 range->row0 = MAX (range->row0, min_visible_row (sheet));
2789 range->rowi = MIN (range->rowi, max_visible_row (sheet));
2790 range->col0 = MAX (range->col0, min_visible_column (sheet));
2791 range->coli = MIN (range->coli, max_visible_column (sheet));
2793 aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
2794 aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
2795 aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
2796 aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
2798 for (i = range->row0; i <= range->rowi; i++)
2800 for (j = range->col0; j <= range->coli; j++)
2803 state = psppire_sheet_cell_get_state (sheet, i, j);
2804 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2805 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2807 if (state == GTK_STATE_SELECTED && selected &&
2808 (i == sheet->range.row0 || i == sheet->range.rowi ||
2809 j == sheet->range.col0 || j == sheet->range.coli ||
2810 i == new_range.row0 || i == new_range.rowi ||
2811 j == new_range.col0 || j == new_range.coli))
2814 mask1 = i == sheet->range.row0 ? 1 : 0;
2815 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2816 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2817 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2819 mask2 = i == new_range.row0 ? 1 : 0;
2820 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2821 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2822 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2826 x = psppire_axis_start_pixel (sheet->haxis, j);
2827 y = psppire_axis_start_pixel (sheet->vaxis, i);
2828 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2829 psppire_axis_unit_size (sheet->haxis, j);
2830 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2832 if (i == sheet->range.row0)
2835 height = height + 3;
2837 if (i == sheet->range.rowi) height = height + 3;
2838 if (j == sheet->range.col0)
2843 if (j == sheet->range.coli) width = width + 3;
2845 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2847 x = psppire_axis_start_pixel (sheet->haxis, j);
2848 y = psppire_axis_start_pixel (sheet->vaxis, i);
2849 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2850 psppire_axis_unit_size (sheet->haxis, j);
2852 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2854 if (i == new_range.row0)
2857 height = height - 2;
2859 if (i == new_range.rowi) height = height - 3;
2860 if (j == new_range.col0)
2865 if (j == new_range.coli) width = width - 3;
2867 gdk_draw_rectangle (sheet->sheet_window,
2878 for (i = range->row0; i <= range->rowi; i++)
2880 for (j = range->col0; j <= range->coli; j++)
2883 state = psppire_sheet_cell_get_state (sheet, i, j);
2884 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2885 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2887 if (state == GTK_STATE_SELECTED && !selected)
2890 x = psppire_axis_start_pixel (sheet->haxis, j);
2891 y = psppire_axis_start_pixel (sheet->vaxis, i);
2892 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2893 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2895 if (i == sheet->range.row0)
2898 height = height + 3;
2900 if (i == sheet->range.rowi) height = height + 3;
2901 if (j == sheet->range.col0)
2906 if (j == sheet->range.coli) width = width + 3;
2912 for (i = range->row0; i <= range->rowi; i++)
2914 for (j = range->col0; j <= range->coli; j++)
2917 state = psppire_sheet_cell_get_state (sheet, i, j);
2918 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2919 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2921 if (state != GTK_STATE_SELECTED && selected &&
2922 (i != sheet->active_cell.row || j != sheet->active_cell.col))
2925 x = psppire_axis_start_pixel (sheet->haxis, j);
2926 y = psppire_axis_start_pixel (sheet->vaxis, i);
2927 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2928 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2930 if (i == new_range.row0)
2933 height = height - 2;
2935 if (i == new_range.rowi) height = height - 3;
2936 if (j == new_range.col0)
2941 if (j == new_range.coli) width = width - 3;
2943 gdk_draw_rectangle (sheet->sheet_window,
2954 for (i = aux_range.row0; i <= aux_range.rowi; i++)
2956 for (j = aux_range.col0; j <= aux_range.coli; j++)
2958 state = psppire_sheet_cell_get_state (sheet, i, j);
2960 mask1 = i == sheet->range.row0 ? 1 : 0;
2961 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2962 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2963 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2965 mask2 = i == new_range.row0 ? 1 : 0;
2966 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2967 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2968 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2969 if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
2971 x = psppire_axis_start_pixel (sheet->haxis, j);
2972 y = psppire_axis_start_pixel (sheet->vaxis, i);
2973 width = psppire_axis_unit_size (sheet->haxis, j);
2974 height = psppire_axis_unit_size (sheet->vaxis, i);
2976 gdk_draw_rectangle (sheet->sheet_window,
2984 gdk_draw_rectangle (sheet->sheet_window,
2987 x + 1, y + height - 1,
2991 gdk_draw_rectangle (sheet->sheet_window,
2999 gdk_draw_rectangle (sheet->sheet_window,
3002 x + width - 1, y + 1,
3014 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
3018 rectangle_from_range (sheet, &new_range, &area);
3020 gdk_draw_rectangle (sheet->sheet_window,
3024 area.width, area.height);
3029 psppire_sheet_real_select_range (PsppireSheet *sheet,
3030 const PsppireSheetRange *range)
3034 g_return_if_fail (sheet != NULL);
3036 if (range == NULL) range = &sheet->range;
3038 memcpy (&sheet->range, range, sizeof (*range));
3040 if (range->row0 < 0 || range->rowi < 0) return;
3041 if (range->col0 < 0 || range->coli < 0) return;
3043 state = sheet->state;
3046 if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3047 range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3049 psppire_sheet_new_selection (sheet, &sheet->range);
3053 psppire_sheet_range_draw_selection (sheet, sheet->range);
3057 psppire_sheet_update_primary_selection (sheet);
3059 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3064 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
3066 g_return_if_fail (sheet != NULL);
3067 *range = sheet->range;
3072 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
3074 g_return_if_fail (sheet != NULL);
3076 if (range == NULL) range=&sheet->range;
3078 if (range->row0 < 0 || range->rowi < 0) return;
3079 if (range->col0 < 0 || range->coli < 0) return;
3082 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3083 psppire_sheet_real_unselect_range (sheet, NULL);
3085 sheet->range.row0 = range->row0;
3086 sheet->range.rowi = range->rowi;
3087 sheet->range.col0 = range->col0;
3088 sheet->range.coli = range->coli;
3089 sheet->active_cell.row = range->row0;
3090 sheet->active_cell.col = range->col0;
3091 sheet->selection_cell.row = range->rowi;
3092 sheet->selection_cell.col = range->coli;
3094 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3095 psppire_sheet_real_select_range (sheet, NULL);
3099 psppire_sheet_unselect_range (PsppireSheet *sheet)
3101 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3104 psppire_sheet_real_unselect_range (sheet, NULL);
3105 sheet->state = GTK_STATE_NORMAL;
3107 change_active_cell (sheet,
3108 sheet->active_cell.row, sheet->active_cell.col);
3113 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
3114 const PsppireSheetRange *range)
3116 g_return_if_fail (sheet != NULL);
3117 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3120 range = &sheet->range;
3122 if (range->row0 < 0 || range->rowi < 0) return;
3123 if (range->col0 < 0 || range->coli < 0) return;
3125 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3126 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3128 sheet->range.row0 = -1;
3129 sheet->range.rowi = -1;
3130 sheet->range.col0 = -1;
3131 sheet->range.coli = -1;
3136 psppire_sheet_expose (GtkWidget *widget,
3137 GdkEventExpose *event)
3139 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3141 g_return_val_if_fail (event != NULL, FALSE);
3143 if (!GTK_WIDGET_DRAWABLE (widget))
3146 /* exposure events on the sheet */
3147 if (event->window == sheet->row_title_window &&
3148 sheet->row_titles_visible)
3150 draw_row_title_buttons_range (sheet,
3151 min_visible_row (sheet),
3152 max_visible_row (sheet));
3155 if (event->window == sheet->column_title_window &&
3156 sheet->column_titles_visible)
3158 draw_column_title_buttons_range (sheet,
3159 min_visible_column (sheet),
3160 max_visible_column (sheet));
3163 if (event->window == sheet->sheet_window)
3165 draw_sheet_region (sheet, event->region);
3168 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3170 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3171 psppire_sheet_range_draw (sheet, &sheet->range);
3173 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3174 psppire_sheet_range_draw (sheet, &sheet->drag_range);
3176 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3177 psppire_sheet_range_draw_selection (sheet, sheet->range);
3178 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3179 draw_xor_rectangle (sheet, sheet->drag_range);
3183 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
3186 PsppireSheetRange range;
3187 range.row0 = range.rowi = sheet->active_cell.row;
3188 range.col0 = range.coli = sheet->active_cell.col;
3190 rectangle_from_range (sheet, &range, &rect);
3192 if (GDK_OVERLAP_RECTANGLE_OUT !=
3193 gdk_region_rect_in (event->region, &rect))
3195 psppire_sheet_draw_active_cell (sheet);
3201 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3208 psppire_sheet_button_press (GtkWidget *widget,
3209 GdkEventButton *event)
3211 PsppireSheet *sheet;
3212 GdkModifierType mods;
3217 g_return_val_if_fail (widget != NULL, FALSE);
3218 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
3219 g_return_val_if_fail (event != NULL, FALSE);
3221 sheet = PSPPIRE_SHEET (widget);
3223 /* Cancel any pending tooltips */
3224 if (sheet->motion_timer)
3226 g_source_remove (sheet->motion_timer);
3227 sheet->motion_timer = 0;
3230 gtk_widget_get_pointer (widget, &x, &y);
3231 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3234 if (event->window == sheet->column_title_window)
3236 sheet->x_drag = event->x;
3237 g_signal_emit (sheet,
3238 sheet_signals[BUTTON_EVENT_COLUMN], 0,
3241 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3243 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3244 g_signal_emit (sheet,
3245 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3248 else if (event->window == sheet->row_title_window)
3250 g_signal_emit (sheet,
3251 sheet_signals[BUTTON_EVENT_ROW], 0,
3254 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3256 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3257 g_signal_emit (sheet,
3258 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3262 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3264 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3267 /* press on resize windows */
3268 if (event->window == sheet->column_title_window)
3270 sheet->x_drag = event->x;
3272 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3274 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3275 gdk_pointer_grab (sheet->column_title_window, FALSE,
3276 GDK_POINTER_MOTION_HINT_MASK |
3277 GDK_BUTTON1_MOTION_MASK |
3278 GDK_BUTTON_RELEASE_MASK,
3279 NULL, NULL, event->time);
3281 draw_xor_vline (sheet);
3286 if (event->window == sheet->row_title_window)
3288 sheet->y_drag = event->y;
3290 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3292 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3293 gdk_pointer_grab (sheet->row_title_window, FALSE,
3294 GDK_POINTER_MOTION_HINT_MASK |
3295 GDK_BUTTON1_MOTION_MASK |
3296 GDK_BUTTON_RELEASE_MASK,
3297 NULL, NULL, event->time);
3299 draw_xor_hline (sheet);
3304 /* the sheet itself does not handle other than single click events */
3305 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3307 /* selections on the sheet */
3308 if (event->window == sheet->sheet_window)
3310 gtk_widget_get_pointer (widget, &x, &y);
3311 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3312 gdk_pointer_grab (sheet->sheet_window, FALSE,
3313 GDK_POINTER_MOTION_HINT_MASK |
3314 GDK_BUTTON1_MOTION_MASK |
3315 GDK_BUTTON_RELEASE_MASK,
3316 NULL, NULL, event->time);
3317 gtk_grab_add (GTK_WIDGET (sheet));
3319 if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
3320 sheet->selection_mode != GTK_SELECTION_NONE &&
3321 sheet->cursor_drag->type == GDK_SIZING &&
3322 !PSPPIRE_SHEET_IN_SELECTION (sheet) && !PSPPIRE_SHEET_IN_RESIZE (sheet))
3324 if (sheet->state == GTK_STATE_NORMAL)
3326 row = sheet->active_cell.row;
3327 column = sheet->active_cell.col;
3328 sheet->active_cell.row = row;
3329 sheet->active_cell.col = column;
3330 sheet->drag_range = sheet->range;
3331 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3332 psppire_sheet_select_range (sheet, &sheet->drag_range);
3336 if (row > sheet->range.rowi) row--;
3337 if (column > sheet->range.coli) column--;
3338 sheet->drag_cell.row = row;
3339 sheet->drag_cell.col = column;
3340 sheet->drag_range = sheet->range;
3341 draw_xor_rectangle (sheet, sheet->drag_range);
3342 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3344 else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
3345 !PSPPIRE_SHEET_IN_SELECTION (sheet)
3346 && ! PSPPIRE_SHEET_IN_DRAG (sheet)
3347 && sheet->active_cell.row >= 0
3348 && sheet->active_cell.col >= 0
3351 if (sheet->state == GTK_STATE_NORMAL)
3353 row = sheet->active_cell.row;
3354 column = sheet->active_cell.col;
3355 sheet->active_cell.row = row;
3356 sheet->active_cell.col = column;
3357 sheet->drag_range = sheet->range;
3358 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3359 psppire_sheet_select_range (sheet, &sheet->drag_range);
3363 if (row < sheet->range.row0) row++;
3364 if (row > sheet->range.rowi) row--;
3365 if (column < sheet->range.col0) column++;
3366 if (column > sheet->range.coli) column--;
3367 sheet->drag_cell.row = row;
3368 sheet->drag_cell.col = column;
3369 sheet->drag_range = sheet->range;
3370 draw_xor_rectangle (sheet, sheet->drag_range);
3371 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3375 veto = psppire_sheet_click_cell (sheet, row, column);
3376 if (veto) PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3380 if (event->window == sheet->column_title_window)
3382 gtk_widget_get_pointer (widget, &x, &y);
3383 if ( sheet->row_titles_visible)
3384 x -= sheet->row_title_area.width;
3386 x += sheet->hadjustment->value;
3388 column = column_from_xpixel (sheet, x);
3390 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3392 veto = psppire_sheet_click_cell (sheet, -1, column);
3393 gtk_grab_add (GTK_WIDGET (sheet));
3394 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3398 if (event->window == sheet->row_title_window)
3400 gtk_widget_get_pointer (widget, &x, &y);
3401 if ( sheet->column_titles_visible)
3402 y -= sheet->column_title_area.height;
3404 y += sheet->vadjustment->value;
3406 row = row_from_ypixel (sheet, y);
3407 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3409 veto = psppire_sheet_click_cell (sheet, row, -1);
3410 gtk_grab_add (GTK_WIDGET (sheet));
3411 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3419 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3421 PsppireSheetCell cell;
3422 gboolean forbid_move;
3427 if (row >= psppire_axis_unit_count (sheet->vaxis)
3428 || column >= psppire_axis_unit_count (sheet->haxis))
3433 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3434 &sheet->active_cell,
3440 if (sheet->state == GTK_STATE_NORMAL)
3443 row = sheet->active_cell.row;
3444 column = sheet->active_cell.col;
3446 change_active_cell (sheet, row, column);
3450 if (row == -1 && column >= 0)
3452 psppire_sheet_select_column (sheet, column);
3456 if (column == -1 && row >= 0)
3458 psppire_sheet_select_row (sheet, row);
3462 if (row == -1 && column == -1)
3464 sheet->range.row0 = 0;
3465 sheet->range.col0 = 0;
3466 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3468 psppire_axis_unit_count (sheet->haxis) - 1;
3469 sheet->active_cell.row = 0;
3470 sheet->active_cell.col = 0;
3471 psppire_sheet_select_range (sheet, NULL);
3475 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3477 sheet->state = PSPPIRE_SHEET_NORMAL;
3478 psppire_sheet_real_unselect_range (sheet, NULL);
3482 change_active_cell (sheet, row, column);
3485 sheet->active_cell.row = row;
3486 sheet->active_cell.col = column;
3487 sheet->selection_cell.row = row;
3488 sheet->selection_cell.col = column;
3489 sheet->range.row0 = row;
3490 sheet->range.col0 = column;
3491 sheet->range.rowi = row;
3492 sheet->range.coli = column;
3493 sheet->state = PSPPIRE_SHEET_NORMAL;
3494 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3496 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3502 psppire_sheet_button_release (GtkWidget *widget,
3503 GdkEventButton *event)
3505 GdkDisplay *display = gtk_widget_get_display (widget);
3507 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3509 /* release on resize windows */
3510 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3513 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3514 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3516 gdk_display_pointer_ungrab (display, event->time);
3517 draw_xor_vline (sheet);
3520 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3521 + sheet->hadjustment->value;
3523 set_column_width (sheet, sheet->drag_cell.col, width);
3528 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3531 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3532 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3534 gdk_display_pointer_ungrab (display, event->time);
3535 draw_xor_hline (sheet);
3538 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3539 sheet->vadjustment->value;
3541 set_row_height (sheet, sheet->drag_cell.row, height);
3546 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3548 PsppireSheetRange old_range;
3549 draw_xor_rectangle (sheet, sheet->drag_range);
3550 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3551 gdk_display_pointer_ungrab (display, event->time);
3553 psppire_sheet_real_unselect_range (sheet, NULL);
3555 sheet->active_cell.row = sheet->active_cell.row +
3556 (sheet->drag_range.row0 - sheet->range.row0);
3557 sheet->active_cell.col = sheet->active_cell.col +
3558 (sheet->drag_range.col0 - sheet->range.col0);
3559 sheet->selection_cell.row = sheet->selection_cell.row +
3560 (sheet->drag_range.row0 - sheet->range.row0);
3561 sheet->selection_cell.col = sheet->selection_cell.col +
3562 (sheet->drag_range.col0 - sheet->range.col0);
3563 old_range = sheet->range;
3564 sheet->range = sheet->drag_range;
3565 sheet->drag_range = old_range;
3566 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3567 &sheet->drag_range, &sheet->range);
3568 psppire_sheet_select_range (sheet, &sheet->range);
3571 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3573 PsppireSheetRange old_range;
3574 draw_xor_rectangle (sheet, sheet->drag_range);
3575 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3576 gdk_display_pointer_ungrab (display, event->time);
3578 psppire_sheet_real_unselect_range (sheet, NULL);
3580 sheet->active_cell.row = sheet->active_cell.row +
3581 (sheet->drag_range.row0 - sheet->range.row0);
3582 sheet->active_cell.col = sheet->active_cell.col +
3583 (sheet->drag_range.col0 - sheet->range.col0);
3584 if (sheet->drag_range.row0 < sheet->range.row0)
3585 sheet->selection_cell.row = sheet->drag_range.row0;
3586 if (sheet->drag_range.rowi >= sheet->range.rowi)
3587 sheet->selection_cell.row = sheet->drag_range.rowi;
3588 if (sheet->drag_range.col0 < sheet->range.col0)
3589 sheet->selection_cell.col = sheet->drag_range.col0;
3590 if (sheet->drag_range.coli >= sheet->range.coli)
3591 sheet->selection_cell.col = sheet->drag_range.coli;
3592 old_range = sheet->range;
3593 sheet->range = sheet->drag_range;
3594 sheet->drag_range = old_range;
3596 if (sheet->state == GTK_STATE_NORMAL) sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3597 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3598 &sheet->drag_range, &sheet->range);
3599 psppire_sheet_select_range (sheet, &sheet->range);
3602 if (sheet->state == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet))
3604 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3605 gdk_display_pointer_ungrab (display, event->time);
3606 change_active_cell (sheet, sheet->active_cell.row,
3607 sheet->active_cell.col);
3610 if (PSPPIRE_SHEET_IN_SELECTION)
3611 gdk_display_pointer_ungrab (display, event->time);
3612 gtk_grab_remove (GTK_WIDGET (sheet));
3614 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3623 /* Shamelessly lifted from gtktooltips */
3625 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3629 gtk_widget_size_request (tip_window, &req);
3630 gtk_paint_flat_box (tip_window->style, tip_window->window,
3631 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3632 NULL, GTK_WIDGET(tip_window), "tooltip",
3633 0, 0, req.width, req.height);
3639 destroy_hover_window (PsppireSheetHoverTitle *h)
3641 gtk_widget_destroy (h->window);
3645 static PsppireSheetHoverTitle *
3646 create_hover_window (void)
3648 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3650 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3652 #if GTK_CHECK_VERSION (2, 9, 0)
3653 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3654 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3657 gtk_widget_set_app_paintable (hw->window, TRUE);
3658 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3659 gtk_widget_set_name (hw->window, "gtk-tooltips");
3660 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3662 g_signal_connect (hw->window,
3664 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3667 hw->label = gtk_label_new (NULL);
3670 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3671 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3673 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3675 gtk_widget_show (hw->label);
3677 g_signal_connect (hw->window,
3679 G_CALLBACK (gtk_widget_destroyed),
3685 #define HOVER_WINDOW_Y_OFFSET 2
3688 show_subtitle (PsppireSheet *sheet, gint row, gint column, const gchar *subtitle)
3697 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3701 sheet->hover_window->row = row;
3702 sheet->hover_window->column = column;
3704 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3706 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3708 gtk_widget_show (sheet->hover_window->window);
3710 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3716 y += sheet->column_title_area.y;
3717 y += sheet->column_title_area.height;
3718 y += HOVER_WINDOW_Y_OFFSET;
3724 x += sheet->row_title_area.x;
3725 x += sheet->row_title_area.width * 2 / 3.0;
3728 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3733 motion_timeout_callback (gpointer data)
3735 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3738 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3740 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3742 if (sheet->row_title_under && row >= 0)
3744 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3746 show_subtitle (sheet, row, -1, text);
3750 if (sheet->column_title_under && column >= 0)
3752 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3755 show_subtitle (sheet, -1, column, text);
3765 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3767 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3768 GdkModifierType mods;
3769 GdkCursorType new_cursor;
3772 GdkDisplay *display;
3774 g_return_val_if_fail (event != NULL, FALSE);
3776 display = gtk_widget_get_display (widget);
3778 /* selections on the sheet */
3782 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3784 if ( sheet->motion_timer > 0 )
3785 g_source_remove (sheet->motion_timer);
3786 sheet->motion_timer =
3787 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3793 gtk_widget_get_pointer (widget, &wx, &wy);
3795 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3797 if ( row != sheet->hover_window->row ||
3798 column != sheet->hover_window->column)
3800 gtk_widget_hide (sheet->hover_window->window);
3805 if (event->window == sheet->column_title_window)
3807 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3808 on_column_boundary (sheet, x, &column))
3810 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3811 if (new_cursor != sheet->cursor_drag->type)
3813 gdk_cursor_unref (sheet->cursor_drag);
3814 sheet->cursor_drag =
3815 gdk_cursor_new_for_display (display, new_cursor);
3817 gdk_window_set_cursor (sheet->column_title_window,
3818 sheet->cursor_drag);
3823 new_cursor = GDK_TOP_LEFT_ARROW;
3824 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3825 new_cursor != sheet->cursor_drag->type)
3827 gdk_cursor_unref (sheet->cursor_drag);
3828 sheet->cursor_drag =
3829 gdk_cursor_new_for_display (display, new_cursor);
3830 gdk_window_set_cursor (sheet->column_title_window,
3831 sheet->cursor_drag);
3835 else if (event->window == sheet->row_title_window)
3837 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3838 on_row_boundary (sheet, y, &row))
3840 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3841 if (new_cursor != sheet->cursor_drag->type)
3843 gdk_cursor_unref (sheet->cursor_drag);
3844 sheet->cursor_drag =
3845 gdk_cursor_new_for_display (display, new_cursor);
3846 gdk_window_set_cursor (sheet->row_title_window,
3847 sheet->cursor_drag);
3852 new_cursor = GDK_TOP_LEFT_ARROW;
3853 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3854 new_cursor != sheet->cursor_drag->type)
3856 gdk_cursor_unref (sheet->cursor_drag);
3857 sheet->cursor_drag =
3858 gdk_cursor_new_for_display (display, new_cursor);
3859 gdk_window_set_cursor (sheet->row_title_window,
3860 sheet->cursor_drag);
3865 new_cursor = GDK_PLUS;
3866 if ( event->window == sheet->sheet_window &&
3867 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3868 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3869 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3870 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3871 new_cursor != sheet->cursor_drag->type)
3873 gdk_cursor_unref (sheet->cursor_drag);
3874 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3875 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3878 new_cursor = GDK_TOP_LEFT_ARROW;
3879 if ( event->window == sheet->sheet_window &&
3880 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3881 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3882 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3883 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3884 new_cursor != sheet->cursor_drag->type)
3886 gdk_cursor_unref (sheet->cursor_drag);
3887 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3888 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3891 new_cursor = GDK_SIZING;
3892 if ( event->window == sheet->sheet_window &&
3893 sheet->selection_mode != GTK_SELECTION_NONE &&
3894 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3895 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3896 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3897 new_cursor != sheet->cursor_drag->type)
3899 gdk_cursor_unref (sheet->cursor_drag);
3900 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3901 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3905 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3906 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3908 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3910 if (event->x != sheet->x_drag)
3912 draw_xor_vline (sheet);
3913 sheet->x_drag = event->x;
3914 draw_xor_vline (sheet);
3920 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3922 if (event->y != sheet->y_drag)
3924 draw_xor_hline (sheet);
3925 sheet->y_drag = event->y;
3926 draw_xor_hline (sheet);
3932 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3934 PsppireSheetRange aux;
3935 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3936 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3937 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3938 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3942 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3943 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3945 aux = sheet->drag_range;
3946 sheet->drag_range.row0 = sheet->range.row0 + row;
3947 sheet->drag_range.col0 = sheet->range.col0 + column;
3948 sheet->drag_range.rowi = sheet->range.rowi + row;
3949 sheet->drag_range.coli = sheet->range.coli + column;
3950 if (aux.row0 != sheet->drag_range.row0 ||
3951 aux.col0 != sheet->drag_range.col0)
3953 draw_xor_rectangle (sheet, aux);
3954 draw_xor_rectangle (sheet, sheet->drag_range);
3960 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3962 PsppireSheetRange aux;
3963 gint v_h, current_col, current_row, col_threshold, row_threshold;
3965 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
3966 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
3968 current_col = column_from_xpixel (sheet, x);
3969 current_row = row_from_ypixel (sheet, y);
3970 column = current_col - sheet->drag_cell.col;
3971 row = current_row - sheet->drag_cell.row;
3973 /*use half of column width resp. row height as threshold to
3975 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
3976 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
3979 if (x < col_threshold)
3982 else if (column < 0)
3984 if (x > col_threshold)
3987 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
3988 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
3991 if (y < row_threshold)
3996 if (y > row_threshold)
4000 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
4001 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4011 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4012 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4014 aux = sheet->drag_range;
4015 sheet->drag_range = sheet->range;
4017 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4018 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4019 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4020 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4022 if (aux.row0 != sheet->drag_range.row0 ||
4023 aux.rowi != sheet->drag_range.rowi ||
4024 aux.col0 != sheet->drag_range.col0 ||
4025 aux.coli != sheet->drag_range.coli)
4027 draw_xor_rectangle (sheet, aux);
4028 draw_xor_rectangle (sheet, sheet->drag_range);
4034 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
4036 if (sheet->state == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
4037 column == sheet->active_cell.col) return TRUE;
4039 if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4040 psppire_sheet_extend_selection (sheet, row, column);
4046 psppire_sheet_crossing_notify (GtkWidget *widget,
4047 GdkEventCrossing *event)
4049 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4051 if (event->window == sheet->column_title_window)
4052 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4053 else if (event->window == sheet->row_title_window)
4054 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4061 psppire_sheet_focus_in (GtkWidget *w,
4062 GdkEventFocus *event)
4064 PsppireSheet *sheet = PSPPIRE_SHEET (w);
4066 gtk_widget_grab_focus (sheet->entry_widget);
4073 psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column)
4075 PsppireSheetRange range;
4079 if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
4082 if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
4084 gtk_widget_grab_focus (GTK_WIDGET (sheet));
4086 if (PSPPIRE_SHEET_IN_DRAG (sheet)) return;
4088 state = sheet->state;
4090 switch (sheet->state)
4092 case PSPPIRE_SHEET_ROW_SELECTED:
4093 column = psppire_axis_unit_count (sheet->haxis) - 1;
4095 case PSPPIRE_SHEET_COLUMN_SELECTED:
4096 row = psppire_axis_unit_count (sheet->vaxis) - 1;
4098 case PSPPIRE_SHEET_NORMAL:
4099 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4100 r = sheet->active_cell.row;
4101 c = sheet->active_cell.col;
4102 sheet->range.col0 = c;
4103 sheet->range.row0 = r;
4104 sheet->range.coli = c;
4105 sheet->range.rowi = r;
4106 psppire_sheet_range_draw_selection (sheet, sheet->range);
4107 case PSPPIRE_SHEET_RANGE_SELECTED:
4108 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4111 sheet->selection_cell.row = row;
4112 sheet->selection_cell.col = column;
4114 range.col0 = MIN (column, sheet->active_cell.col);
4115 range.coli = MAX (column, sheet->active_cell.col);
4116 range.row0 = MIN (row, sheet->active_cell.row);
4117 range.rowi = MAX (row, sheet->active_cell.row);
4119 if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
4120 range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
4121 state == PSPPIRE_SHEET_NORMAL)
4122 psppire_sheet_real_select_range (sheet, &range);
4127 psppire_sheet_entry_key_press (GtkWidget *widget,
4131 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
4136 /* Number of rows in a step-increment */
4137 #define ROWS_PER_STEP 1
4141 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
4143 gint old_row = sheet->active_cell.row ;
4144 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
4148 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
4149 min_visible_row (sheet));
4153 case GTK_SCROLL_PAGE_DOWN:
4154 gtk_adjustment_set_value (sheet->vadjustment,
4155 sheet->vadjustment->value +
4156 sheet->vadjustment->page_increment);
4158 case GTK_SCROLL_PAGE_UP:
4159 gtk_adjustment_set_value (sheet->vadjustment,
4160 sheet->vadjustment->value -
4161 sheet->vadjustment->page_increment);
4165 g_assert_not_reached ();
4170 vpixel += psppire_axis_start_pixel (sheet->vaxis,
4171 min_visible_row (sheet));
4173 new_row = row_from_ypixel (sheet, vpixel);
4175 change_active_cell (sheet, new_row,
4176 sheet->active_cell.col);
4181 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
4183 gint current_row = sheet->active_cell.row;
4184 gint current_col = sheet->active_cell.col;
4185 PsppireSheetCell new_cell ;
4186 gboolean forbidden = FALSE;
4188 new_cell.row = current_row;
4189 new_cell.col = current_col;
4193 case GTK_SCROLL_STEP_DOWN:
4196 case GTK_SCROLL_STEP_UP:
4199 case GTK_SCROLL_STEP_RIGHT:
4202 case GTK_SCROLL_STEP_LEFT:
4205 case GTK_SCROLL_STEP_FORWARD:
4208 psppire_sheet_model_get_column_count (sheet->model))
4214 case GTK_SCROLL_STEP_BACKWARD:
4216 if (new_cell.col < 0)
4219 psppire_sheet_model_get_column_count (sheet->model) - 1;
4224 g_assert_not_reached ();
4228 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
4229 &sheet->active_cell,
4237 maximize_int (&new_cell.row, 0);
4238 maximize_int (&new_cell.col, 0);
4240 minimize_int (&new_cell.row,
4241 psppire_axis_unit_count (sheet->vaxis) - 1);
4243 minimize_int (&new_cell.col,
4244 psppire_axis_unit_count (sheet->haxis) - 1);
4246 change_active_cell (sheet, new_cell.row, new_cell.col);
4249 if ( new_cell.col > max_fully_visible_column (sheet))
4252 psppire_axis_start_pixel (sheet->haxis,
4254 hpos -= sheet->hadjustment->page_size;
4256 gtk_adjustment_set_value (sheet->hadjustment,
4259 else if ( new_cell.col < min_fully_visible_column (sheet))
4262 psppire_axis_start_pixel (sheet->haxis,
4265 gtk_adjustment_set_value (sheet->hadjustment,
4270 if ( new_cell.row > max_fully_visible_row (sheet))
4273 psppire_axis_start_pixel (sheet->vaxis,
4275 vpos -= sheet->vadjustment->page_size;
4277 gtk_adjustment_set_value (sheet->vadjustment,
4280 else if ( new_cell.row < min_fully_visible_row (sheet))
4283 psppire_axis_start_pixel (sheet->vaxis,
4286 gtk_adjustment_set_value (sheet->vadjustment,
4290 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
4295 psppire_sheet_key_press (GtkWidget *widget,
4298 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4300 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
4302 switch (key->keyval)
4305 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
4308 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
4310 case GDK_ISO_Left_Tab:
4311 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
4314 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
4318 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
4321 step_sheet (sheet, GTK_SCROLL_STEP_UP);
4325 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
4328 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
4332 gtk_adjustment_set_value (sheet->vadjustment,
4333 sheet->vadjustment->lower);
4335 change_active_cell (sheet, 0,
4336 sheet->active_cell.col);
4341 gtk_adjustment_set_value (sheet->vadjustment,
4342 sheet->vadjustment->upper -
4343 sheet->vadjustment->page_size -
4344 sheet->vadjustment->page_increment);
4347 change_active_cellx (sheet,
4348 psppire_axis_unit_count (sheet->vaxis) - 1,
4349 sheet->active_cell.col);
4353 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
4364 psppire_sheet_size_request (GtkWidget *widget,
4365 GtkRequisition *requisition)
4367 PsppireSheet *sheet;
4369 g_return_if_fail (widget != NULL);
4370 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4371 g_return_if_fail (requisition != NULL);
4373 sheet = PSPPIRE_SHEET (widget);
4375 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4376 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4378 /* compute the size of the column title area */
4379 if (sheet->column_titles_visible)
4380 requisition->height += sheet->column_title_area.height;
4382 /* compute the size of the row title area */
4383 if (sheet->row_titles_visible)
4384 requisition->width += sheet->row_title_area.width;
4389 psppire_sheet_size_allocate (GtkWidget *widget,
4390 GtkAllocation *allocation)
4392 PsppireSheet *sheet;
4393 GtkAllocation sheet_allocation;
4396 g_return_if_fail (widget != NULL);
4397 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4398 g_return_if_fail (allocation != NULL);
4400 sheet = PSPPIRE_SHEET (widget);
4401 widget->allocation = *allocation;
4402 border_width = GTK_CONTAINER (widget)->border_width;
4404 if (GTK_WIDGET_REALIZED (widget))
4405 gdk_window_move_resize (widget->window,
4406 allocation->x + border_width,
4407 allocation->y + border_width,
4408 allocation->width - 2 * border_width,
4409 allocation->height - 2 * border_width);
4411 sheet_allocation.x = 0;
4412 sheet_allocation.y = 0;
4413 sheet_allocation.width = allocation->width - 2 * border_width;
4414 sheet_allocation.height = allocation->height - 2 * border_width;
4416 if (GTK_WIDGET_REALIZED (widget))
4417 gdk_window_move_resize (sheet->sheet_window,
4420 sheet_allocation.width,
4421 sheet_allocation.height);
4423 /* position the window which holds the column title buttons */
4424 sheet->column_title_area.x = 0;
4425 sheet->column_title_area.y = 0;
4426 sheet->column_title_area.width = sheet_allocation.width ;
4429 /* position the window which holds the row title buttons */
4430 sheet->row_title_area.x = 0;
4431 sheet->row_title_area.y = 0;
4432 sheet->row_title_area.height = sheet_allocation.height;
4434 if (sheet->row_titles_visible)
4435 sheet->column_title_area.x += sheet->row_title_area.width;
4437 if (sheet->column_titles_visible)
4438 sheet->row_title_area.y += sheet->column_title_area.height;
4440 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4441 gdk_window_move_resize (sheet->column_title_window,
4442 sheet->column_title_area.x,
4443 sheet->column_title_area.y,
4444 sheet->column_title_area.width,
4445 sheet->column_title_area.height);
4448 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4449 gdk_window_move_resize (sheet->row_title_window,
4450 sheet->row_title_area.x,
4451 sheet->row_title_area.y,
4452 sheet->row_title_area.width,
4453 sheet->row_title_area.height);
4455 size_allocate_global_button (sheet);
4459 gint width = sheet->column_title_area.width;
4461 if ( sheet->row_titles_visible)
4462 width -= sheet->row_title_area.width;
4464 g_object_set (sheet->haxis,
4465 "minimum-extent", width,
4472 gint height = sheet->row_title_area.height;
4474 if ( sheet->column_titles_visible)
4475 height -= sheet->column_title_area.height;
4477 g_object_set (sheet->vaxis,
4478 "minimum-extent", height,
4483 /* set the scrollbars adjustments */
4484 adjust_scrollbars (sheet);
4488 draw_column_title_buttons (PsppireSheet *sheet)
4492 if (!sheet->column_titles_visible) return;
4493 if (!GTK_WIDGET_REALIZED (sheet))
4496 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4499 if (sheet->row_titles_visible)
4501 x = sheet->row_title_area.width;
4504 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4506 sheet->column_title_area.width = width;
4507 sheet->column_title_area.x = x;
4508 gdk_window_move_resize (sheet->column_title_window,
4509 sheet->column_title_area.x,
4510 sheet->column_title_area.y,
4511 sheet->column_title_area.width,
4512 sheet->column_title_area.height);
4515 if (max_visible_column (sheet) ==
4516 psppire_axis_unit_count (sheet->haxis) - 1)
4517 gdk_window_clear_area (sheet->column_title_window,
4519 sheet->column_title_area.width,
4520 sheet->column_title_area.height);
4522 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4524 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4525 max_visible_column (sheet));
4529 draw_row_title_buttons (PsppireSheet *sheet)
4534 if (!sheet->row_titles_visible) return;
4535 if (!GTK_WIDGET_REALIZED (sheet))
4538 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4540 if (sheet->column_titles_visible)
4542 y = sheet->column_title_area.height;
4545 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4547 sheet->row_title_area.y = y;
4548 sheet->row_title_area.height = height;
4549 gdk_window_move_resize (sheet->row_title_window,
4550 sheet->row_title_area.x,
4551 sheet->row_title_area.y,
4552 sheet->row_title_area.width,
4553 sheet->row_title_area.height);
4556 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4557 gdk_window_clear_area (sheet->row_title_window,
4559 sheet->row_title_area.width,
4560 sheet->row_title_area.height);
4562 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4564 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4565 max_visible_row (sheet));
4570 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4572 GtkAllocation entry_alloc;
4573 PsppireSheetCellAttr attributes = { 0 };
4574 GtkEntry *sheet_entry;
4576 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4577 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4579 sheet_entry = psppire_sheet_get_entry (sheet);
4581 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4582 sheet->active_cell.col,
4586 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4588 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4590 style->bg[GTK_STATE_NORMAL] = attributes.background;
4591 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4592 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4593 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4594 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4595 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4598 rectangle_from_cell (sheet, sheet->active_cell.row,
4599 sheet->active_cell.col, &entry_alloc);
4601 entry_alloc.width -= BORDER_WIDTH ;
4602 entry_alloc.height -= BORDER_WIDTH ;
4603 entry_alloc.x += DIV_RND_UP (BORDER_WIDTH, 2);
4604 entry_alloc.y += DIV_RND_UP (BORDER_WIDTH, 2);
4607 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4608 entry_alloc.height);
4609 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4613 /* Copy the sheet's font to the entry widget */
4615 set_entry_widget_font (PsppireSheet *sheet)
4617 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4619 pango_font_description_free (style->font_desc);
4620 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4622 gtk_widget_modify_style (sheet->entry_widget, style);
4626 create_sheet_entry (PsppireSheet *sheet)
4628 if (sheet->entry_widget)
4630 gtk_widget_unparent (sheet->entry_widget);
4633 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4634 g_object_ref_sink (sheet->entry_widget);
4636 gtk_widget_size_request (sheet->entry_widget, NULL);
4638 if ( GTK_IS_ENTRY (sheet->entry_widget))
4640 g_object_set (sheet->entry_widget,
4645 if (GTK_WIDGET_REALIZED (sheet))
4647 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4648 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4649 gtk_widget_realize (sheet->entry_widget);
4652 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4653 G_CALLBACK (psppire_sheet_entry_key_press),
4656 set_entry_widget_font (sheet);
4658 gtk_widget_show (sheet->entry_widget);
4662 /* Finds the last child widget that happens to be of type GtkEntry */
4664 find_entry (GtkWidget *w, gpointer user_data)
4666 GtkWidget **entry = user_data;
4667 if ( GTK_IS_ENTRY (w))
4675 psppire_sheet_get_entry (PsppireSheet *sheet)
4677 GtkWidget *w = sheet->entry_widget;
4679 g_return_val_if_fail (sheet != NULL, NULL);
4680 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4681 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4683 while (! GTK_IS_ENTRY (w))
4685 GtkWidget *entry = NULL;
4687 if (GTK_IS_CONTAINER (w))
4689 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4698 return GTK_ENTRY (w);
4703 draw_button (PsppireSheet *sheet, GdkWindow *window,
4704 PsppireSheetButton *button, gboolean is_sensitive,
4705 GdkRectangle allocation)
4707 GtkShadowType shadow_type;
4708 gint text_width = 0, text_height = 0;
4709 PangoAlignment align = PANGO_ALIGN_LEFT;
4715 g_return_if_fail (sheet != NULL);
4716 g_return_if_fail (button != NULL);
4719 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4721 gdk_window_clear_area (window,
4722 allocation.x, allocation.y,
4723 allocation.width, allocation.height);
4725 gtk_widget_ensure_style (sheet->button);
4727 gtk_paint_box (sheet->button->style, window,
4728 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4729 &allocation, GTK_WIDGET (sheet->button),
4731 allocation.x, allocation.y,
4732 allocation.width, allocation.height);
4734 state = button->state;
4735 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4737 if (state == GTK_STATE_ACTIVE)
4738 shadow_type = GTK_SHADOW_IN;
4740 shadow_type = GTK_SHADOW_OUT;
4742 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4743 gtk_paint_box (sheet->button->style, window,
4744 button->state, shadow_type,
4745 &allocation, GTK_WIDGET (sheet->button),
4747 allocation.x, allocation.y,
4748 allocation.width, allocation.height);
4750 if ( button->overstruck)
4752 GdkPoint points[2] = {
4753 {allocation.x, allocation.y},
4754 {allocation.x + allocation.width,
4755 allocation.y + allocation.height}
4758 gtk_paint_polygon (sheet->button->style,
4770 if (button->label_visible)
4772 text_height = DEFAULT_ROW_HEIGHT -
4773 2 * COLUMN_TITLES_HEIGHT;
4775 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4777 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4780 allocation.y += 2 * sheet->button->style->ythickness;
4782 if (button->label && strlen (button->label) > 0)
4784 PangoRectangle rect;
4785 gchar *line = button->label;
4787 PangoLayout *layout = NULL;
4788 gint real_x = allocation.x;
4789 gint real_y = allocation.y;
4791 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4792 pango_layout_get_extents (layout, NULL, &rect);
4794 text_width = PANGO_PIXELS (rect.width);
4795 switch (button->justification)
4797 case GTK_JUSTIFY_LEFT:
4798 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4799 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4801 case GTK_JUSTIFY_RIGHT:
4802 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4803 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4805 case GTK_JUSTIFY_CENTER:
4807 real_x = allocation.x + (allocation.width - text_width)/2;
4808 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4809 pango_layout_set_justify (layout, TRUE);
4811 pango_layout_set_alignment (layout, align);
4812 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4821 g_object_unref (layout);
4824 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4826 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4830 psppire_sheet_button_free (button);
4834 /* Draw the column title buttons FIRST through to LAST */
4836 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4840 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4842 if (!sheet->column_titles_visible) return;
4844 g_return_if_fail (first >= min_visible_column (sheet));
4845 g_return_if_fail (last <= max_visible_column (sheet));
4848 rect.height = sheet->column_title_area.height;
4849 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4850 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4851 + psppire_axis_unit_size (sheet->haxis, last);
4853 rect.x -= sheet->hadjustment->value;
4855 minimize_int (&rect.width, sheet->column_title_area.width);
4856 maximize_int (&rect.x, 0);
4858 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4860 for (col = first ; col <= last ; ++col)
4862 GdkRectangle allocation;
4863 gboolean is_sensitive = FALSE;
4865 PsppireSheetButton *
4866 button = psppire_sheet_model_get_column_button (sheet->model, col);
4868 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4870 allocation.x -= sheet->hadjustment->value;
4872 allocation.height = sheet->column_title_area.height;
4873 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4874 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4876 draw_button (sheet, sheet->column_title_window,
4877 button, is_sensitive, allocation);
4880 gdk_window_end_paint (sheet->column_title_window);
4885 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4889 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4891 if (!sheet->row_titles_visible) return;
4893 g_return_if_fail (first >= min_visible_row (sheet));
4894 g_return_if_fail (last <= max_visible_row (sheet));
4897 rect.width = sheet->row_title_area.width;
4898 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4899 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4900 + psppire_axis_unit_size (sheet->vaxis, last);
4902 rect.y -= sheet->vadjustment->value;
4904 minimize_int (&rect.height, sheet->row_title_area.height);
4905 maximize_int (&rect.y, 0);
4907 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4908 for (row = first; row <= last; ++row)
4910 GdkRectangle allocation;
4912 gboolean is_sensitive = FALSE;
4914 PsppireSheetButton *button =
4915 psppire_sheet_model_get_row_button (sheet->model, row);
4917 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4919 allocation.y -= sheet->vadjustment->value;
4921 allocation.width = sheet->row_title_area.width;
4922 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4923 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4925 draw_button (sheet, sheet->row_title_window,
4926 button, is_sensitive, allocation);
4929 gdk_window_end_paint (sheet->row_title_window);
4936 * vadjustment_value_changed
4937 * hadjustment_value_changed */
4941 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4944 (adj->value + adj->page_size)
4946 (adj->upper - adj->lower);
4948 const glong last_item = psppire_axis_unit_count (axis) - 1;
4950 if (isnan (position) || position < 0)
4954 psppire_axis_start_pixel (axis, last_item)
4956 psppire_axis_unit_size (axis, last_item)
4960 adj->page_size = page_size;
4963 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4965 if ( adj->value < adj->lower)
4966 adj->value = adj->lower;
4969 gtk_adjustment_changed (adj);
4974 adjust_scrollbars (PsppireSheet *sheet)
4978 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4981 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4983 if ( sheet->row_titles_visible)
4984 width -= sheet->row_title_area.width;
4986 if (sheet->column_titles_visible)
4987 height -= sheet->column_title_area.height;
4989 if (sheet->vadjustment)
4991 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4993 sheet->vadjustment->step_increment =
4995 psppire_axis_unit_size (sheet->vaxis, last_row);
4997 sheet->vadjustment->page_increment =
4999 sheet->column_title_area.height -
5000 psppire_axis_unit_size (sheet->vaxis, last_row);
5002 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
5005 if (sheet->hadjustment)
5007 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
5008 sheet->hadjustment->step_increment = 1;
5010 sheet->hadjustment->page_increment = width;
5012 sheet->hadjustment->upper =
5013 psppire_axis_start_pixel (sheet->haxis, last_col)
5015 psppire_axis_unit_size (sheet->haxis, last_col)
5018 update_adjustment (sheet->hadjustment, sheet->haxis, width);
5022 /* Subtracts the region of WIDGET from REGION */
5024 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
5027 GdkRectangle intersect;
5030 gdk_region_get_clipbox (region, &rect);
5031 gtk_widget_intersect (widget,
5035 region2 = gdk_region_rectangle (&intersect);
5036 gdk_region_subtract (region, region2);
5037 gdk_region_destroy (region2);
5041 vadjustment_value_changed (GtkAdjustment *adjustment,
5045 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5047 g_return_if_fail (adjustment != NULL);
5049 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5051 gtk_widget_hide (sheet->entry_widget);
5054 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5056 subtract_widget_region (region, sheet->button);
5057 gdk_window_begin_paint_region (sheet->sheet_window, region);
5059 draw_sheet_region (sheet, region);
5061 draw_row_title_buttons (sheet);
5062 psppire_sheet_draw_active_cell (sheet);
5064 gdk_window_end_paint (sheet->sheet_window);
5065 gdk_region_destroy (region);
5070 hadjustment_value_changed (GtkAdjustment *adjustment,
5074 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5076 g_return_if_fail (adjustment != NULL);
5078 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5080 gtk_widget_hide (sheet->entry_widget);
5084 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5086 subtract_widget_region (region, sheet->button);
5087 gdk_window_begin_paint_region (sheet->sheet_window, region);
5089 draw_sheet_region (sheet, region);
5091 draw_column_title_buttons (sheet);
5093 psppire_sheet_draw_active_cell (sheet);
5095 gdk_window_end_paint (sheet->sheet_window);
5097 gdk_region_destroy (region);
5101 /* COLUMN RESIZING */
5103 draw_xor_vline (PsppireSheet *sheet)
5106 gint xpos = sheet->x_drag;
5107 gdk_drawable_get_size (sheet->sheet_window,
5110 if (sheet->row_titles_visible)
5111 xpos += sheet->row_title_area.width;
5113 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5115 sheet->column_title_area.height,
5117 height + CELL_SPACING);
5122 draw_xor_hline (PsppireSheet *sheet)
5126 gint ypos = sheet->y_drag;
5128 gdk_drawable_get_size (sheet->sheet_window,
5132 if (sheet->column_titles_visible)
5133 ypos += sheet->column_title_area.height;
5135 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5136 sheet->row_title_area.width,
5138 width + CELL_SPACING,
5142 /* SELECTED RANGE */
5144 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
5147 GdkRectangle clip_area, area;
5150 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
5151 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
5152 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
5153 psppire_axis_unit_size (sheet->haxis, range.coli);
5154 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
5155 psppire_axis_unit_size (sheet->vaxis, range.rowi);
5157 clip_area.x = sheet->row_title_area.width;
5158 clip_area.y = sheet->column_title_area.height;
5160 gdk_drawable_get_size (sheet->sheet_window,
5161 &clip_area.width, &clip_area.height);
5163 if (!sheet->row_titles_visible) clip_area.x = 0;
5164 if (!sheet->column_titles_visible) clip_area.y = 0;
5168 area.width = area.width + area.x;
5171 if (area.width > clip_area.width) area.width = clip_area.width + 10;
5174 area.height = area.height + area.y;
5177 if (area.height > clip_area.height) area.height = clip_area.height + 10;
5181 clip_area.width += 3;
5182 clip_area.height += 3;
5184 gdk_gc_get_values (sheet->xor_gc, &values);
5186 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5188 gdk_draw_rectangle (sheet->sheet_window,
5191 area.x + i, area.y + i,
5192 area.width - 2 * i, area.height - 2 * i);
5195 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5197 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5202 set_column_width (PsppireSheet *sheet,
5206 g_return_if_fail (sheet != NULL);
5207 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5209 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
5215 psppire_axis_resize (sheet->haxis, column, width);
5217 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5219 draw_column_title_buttons (sheet);
5220 adjust_scrollbars (sheet);
5221 psppire_sheet_size_allocate_entry (sheet);
5222 redraw_range (sheet, NULL);
5227 set_row_height (PsppireSheet *sheet,
5231 g_return_if_fail (sheet != NULL);
5232 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5234 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
5240 psppire_axis_resize (sheet->vaxis, row, height);
5242 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
5244 draw_row_title_buttons (sheet);
5245 adjust_scrollbars (sheet);
5246 psppire_sheet_size_allocate_entry (sheet);
5247 redraw_range (sheet, NULL);
5252 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
5253 PsppireSheetCellAttr *attr)
5256 const GtkJustification *j ;
5257 GdkColormap *colormap;
5259 g_return_val_if_fail (sheet != NULL, FALSE);
5260 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
5262 if (row < 0 || col < 0) return FALSE;
5264 attr->foreground = GTK_WIDGET (sheet)->style->black;
5265 attr->background = sheet->color[BG_COLOR];
5267 attr->border.width = 0;
5268 attr->border.line_style = GDK_LINE_SOLID;
5269 attr->border.cap_style = GDK_CAP_NOT_LAST;
5270 attr->border.join_style = GDK_JOIN_MITER;
5271 attr->border.mask = 0;
5272 attr->border.color = GTK_WIDGET (sheet)->style->black;
5274 attr->is_editable = psppire_sheet_model_is_editable (sheet->model, row, col);
5276 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
5277 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
5280 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
5281 attr->foreground = *fg;
5284 bg = psppire_sheet_model_get_background (sheet->model, row, col);
5287 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
5288 attr->background = *bg;
5291 attr->justification =
5292 psppire_sheet_model_get_column_justification (sheet->model, col);
5294 j = psppire_sheet_model_get_justification (sheet->model, row, col);
5296 attr->justification = *j;
5302 psppire_sheet_button_size_request (PsppireSheet *sheet,
5303 const PsppireSheetButton *button,
5304 GtkRequisition *button_requisition)
5306 GtkRequisition requisition;
5307 GtkRequisition label_requisition;
5309 label_requisition.height = DEFAULT_ROW_HEIGHT;
5310 label_requisition.width = COLUMN_MIN_WIDTH;
5312 requisition.height = DEFAULT_ROW_HEIGHT;
5313 requisition.width = COLUMN_MIN_WIDTH;
5316 *button_requisition = requisition;
5317 button_requisition->width = MAX (requisition.width, label_requisition.width);
5318 button_requisition->height = MAX (requisition.height, label_requisition.height);
5323 psppire_sheet_forall (GtkContainer *container,
5324 gboolean include_internals,
5325 GtkCallback callback,
5326 gpointer callback_data)
5328 PsppireSheet *sheet = PSPPIRE_SHEET (container);
5330 g_return_if_fail (callback != NULL);
5332 if (sheet->button && sheet->button->parent)
5333 (* callback) (sheet->button, callback_data);
5335 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
5336 (* callback) (sheet->entry_widget, callback_data);
5341 psppire_sheet_get_model (const PsppireSheet *sheet)
5343 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
5345 return sheet->model;
5349 PsppireSheetButton *
5350 psppire_sheet_button_new (void)
5352 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
5354 button->state = GTK_STATE_NORMAL;
5355 button->label = NULL;
5356 button->label_visible = TRUE;
5357 button->justification = GTK_JUSTIFY_FILL;
5358 button->overstruck = FALSE;
5365 psppire_sheet_button_free (PsppireSheetButton *button)
5367 if (!button) return ;
5369 g_free (button->label);
5374 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5376 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5378 if ( NULL == celltext)
5381 g_string_append (string, celltext);
5387 range_to_text (const PsppireSheet *sheet)
5392 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5395 string = g_string_sized_new (80);
5397 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5399 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5401 append_cell_text (string, sheet, r, c);
5402 g_string_append (string, "\t");
5404 append_cell_text (string, sheet, r, c);
5405 if ( r < sheet->range.rowi)
5406 g_string_append (string, "\n");
5413 range_to_html (const PsppireSheet *sheet)
5418 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5421 string = g_string_sized_new (480);
5423 g_string_append (string, "<html>\n");
5424 g_string_append (string, "<body>\n");
5425 g_string_append (string, "<table>\n");
5426 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5428 g_string_append (string, "<tr>\n");
5429 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5431 g_string_append (string, "<td>");
5432 append_cell_text (string, sheet, r, c);
5433 g_string_append (string, "</td>\n");
5435 g_string_append (string, "</tr>\n");
5437 g_string_append (string, "</table>\n");
5438 g_string_append (string, "</body>\n");
5439 g_string_append (string, "</html>\n");
5451 primary_get_cb (GtkClipboard *clipboard,
5452 GtkSelectionData *selection_data,
5456 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5457 GString *string = NULL;
5461 case SELECT_FMT_TEXT:
5462 string = range_to_text (sheet);
5464 case SELECT_FMT_HTML:
5465 string = range_to_html (sheet);
5468 g_assert_not_reached ();
5471 gtk_selection_data_set (selection_data, selection_data->target,
5473 (const guchar *) string->str, string->len);
5474 g_string_free (string, TRUE);
5478 primary_clear_cb (GtkClipboard *clipboard,
5481 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5482 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5485 psppire_sheet_real_unselect_range (sheet, NULL);
5489 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5491 static const GtkTargetEntry targets[] = {
5492 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5493 { "STRING", 0, SELECT_FMT_TEXT },
5494 { "TEXT", 0, SELECT_FMT_TEXT },
5495 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5496 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5497 { "text/plain", 0, SELECT_FMT_TEXT },
5498 { "text/html", 0, SELECT_FMT_HTML }
5501 GtkClipboard *clipboard;
5503 if (!GTK_WIDGET_REALIZED (sheet))
5506 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5507 GDK_SELECTION_PRIMARY);
5509 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5511 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5512 G_N_ELEMENTS (targets),
5513 primary_get_cb, primary_clear_cb,
5515 primary_clear_cb (clipboard, sheet);
5519 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5520 gtk_clipboard_clear (clipboard);