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);
2101 /* get cell attributes of the given cell */
2102 /* TRUE means that the cell is currently allocated */
2103 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2105 PsppireSheetCellAttr *attributes);
2110 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2112 PangoLayout *layout;
2113 PangoRectangle text;
2114 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2119 PsppireSheetCellAttr attributes;
2122 g_return_if_fail (sheet != NULL);
2124 /* bail now if we aren't yet drawable */
2125 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2128 row >= psppire_axis_unit_count (sheet->vaxis))
2132 col >= psppire_axis_unit_count (sheet->haxis))
2135 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2137 /* select GC for background rectangle */
2138 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2139 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2141 rectangle_from_cell (sheet, row, col, &area);
2143 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2145 if (sheet->show_grid)
2147 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2149 gdk_draw_rectangle (sheet->sheet_window,
2153 area.width, area.height);
2157 label = psppire_sheet_cell_get_text (sheet, row, col);
2162 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2163 dispose_string (sheet, label);
2166 pango_layout_set_font_description (layout, font_desc);
2168 pango_layout_get_pixel_extents (layout, NULL, &text);
2170 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2172 font_height = pango_font_description_get_size (font_desc);
2173 if ( !pango_font_description_get_size_is_absolute (font_desc))
2174 font_height /= PANGO_SCALE;
2176 /* Centre the text vertically */
2177 area.y += (area.height - font_height) / 2.0;
2179 switch (attributes.justification)
2181 case GTK_JUSTIFY_RIGHT:
2182 area.x += area.width - text.width;
2184 case GTK_JUSTIFY_CENTER:
2185 area.x += (area.width - text.width) / 2.0;
2187 case GTK_JUSTIFY_LEFT:
2191 g_critical ("Unhandled justification %d in column %d\n",
2192 attributes.justification, col);
2196 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2201 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2202 g_object_unref (layout);
2207 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2209 PsppireSheetRange range;
2214 PsppireSheetRange drawing_range;
2216 gdk_region_get_clipbox (region, &area);
2218 y = area.y + sheet->vadjustment->value;
2219 x = area.x + sheet->hadjustment->value;
2221 if ( sheet->column_titles_visible)
2222 y -= sheet->column_title_area.height;
2224 if ( sheet->row_titles_visible)
2225 x -= sheet->row_title_area.width;
2227 maximize_int (&x, 0);
2228 maximize_int (&y, 0);
2230 range.row0 = row_from_ypixel (sheet, y);
2231 range.rowi = row_from_ypixel (sheet, y + area.height);
2233 range.col0 = column_from_xpixel (sheet, x);
2234 range.coli = column_from_xpixel (sheet, x + area.width);
2236 g_return_if_fail (sheet != NULL);
2237 g_return_if_fail (PSPPIRE_SHEET (sheet));
2239 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2240 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2241 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2244 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2245 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2246 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2247 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2249 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2250 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2252 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2254 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2255 psppire_sheet_cell_draw (sheet, i, j);
2258 if (sheet->state != PSPPIRE_SHEET_NORMAL &&
2259 psppire_sheet_range_isvisible (sheet, &sheet->range))
2260 psppire_sheet_range_draw_selection (sheet, drawing_range);
2263 if (sheet->state == GTK_STATE_NORMAL &&
2264 sheet->active_cell.row >= drawing_range.row0 &&
2265 sheet->active_cell.row <= drawing_range.rowi &&
2266 sheet->active_cell.col >= drawing_range.col0 &&
2267 sheet->active_cell.col <= drawing_range.coli)
2268 psppire_sheet_show_entry_widget (sheet);
2273 psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range)
2277 PsppireSheetRange aux;
2279 if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
2280 range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
2283 if (!psppire_sheet_range_isvisible (sheet, &range)) return;
2284 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2288 range.col0 = MAX (sheet->range.col0, range.col0);
2289 range.coli = MIN (sheet->range.coli, range.coli);
2290 range.row0 = MAX (sheet->range.row0, range.row0);
2291 range.rowi = MIN (sheet->range.rowi, range.rowi);
2293 range.col0 = MAX (range.col0, min_visible_column (sheet));
2294 range.coli = MIN (range.coli, max_visible_column (sheet));
2295 range.row0 = MAX (range.row0, min_visible_row (sheet));
2296 range.rowi = MIN (range.rowi, max_visible_row (sheet));
2298 for (i = range.row0; i <= range.rowi; i++)
2300 for (j = range.col0; j <= range.coli; j++)
2302 if (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED)
2304 rectangle_from_cell (sheet, i, j, &area);
2306 if (i == sheet->range.row0)
2308 area.y = area.y + 2;
2309 area.height = area.height - 2;
2311 if (i == sheet->range.rowi) area.height = area.height - 3;
2312 if (j == sheet->range.col0)
2314 area.x = area.x + 2;
2315 area.width = area.width - 2;
2317 if (j == sheet->range.coli) area.width = area.width - 3;
2319 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2321 gdk_draw_rectangle (sheet->sheet_window,
2324 area.x + 1, area.y + 1,
2325 area.width, area.height);
2332 psppire_sheet_draw_border (sheet, sheet->range);
2336 safe_strcmp (const gchar *s1, const gchar *s2)
2338 if ( !s1 && !s2) return 0;
2339 if ( !s1) return -1;
2340 if ( !s2) return +1;
2341 return strcmp (s1, s2);
2345 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2346 GtkJustification justification,
2349 PsppireSheetModel *model ;
2352 g_return_if_fail (sheet != NULL);
2353 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2355 if (col >= psppire_axis_unit_count (sheet->haxis)
2356 || row >= psppire_axis_unit_count (sheet->vaxis))
2359 if (col < 0 || row < 0) return;
2361 model = psppire_sheet_get_model (sheet);
2363 old_text = psppire_sheet_model_get_string (model, row, col);
2365 if (0 != safe_strcmp (old_text, text))
2367 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2368 psppire_sheet_model_set_string (model, text, row, col);
2369 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2372 if ( psppire_sheet_model_free_strings (model))
2378 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2380 PsppireSheetRange range;
2382 g_return_if_fail (sheet != NULL);
2383 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2384 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2385 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2387 if (column < 0 || row < 0) return;
2391 range.col0 = min_visible_column (sheet);
2392 range.coli = max_visible_column (sheet);
2394 psppire_sheet_real_cell_clear (sheet, row, column);
2396 redraw_range (sheet, &range);
2400 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2402 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2404 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2406 if (old_text && strlen (old_text) > 0 )
2408 psppire_sheet_model_datum_clear (model, row, column);
2411 dispose_string (sheet, old_text);
2415 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2417 PsppireSheetModel *model;
2418 g_return_val_if_fail (sheet != NULL, NULL);
2419 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2421 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2423 if (col < 0 || row < 0) return NULL;
2425 model = psppire_sheet_get_model (sheet);
2430 return psppire_sheet_model_get_string (model, row, col);
2435 psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
2438 PsppireSheetRange *range;
2440 g_return_val_if_fail (sheet != NULL, 0);
2441 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2442 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
2443 if (col < 0 || row < 0) return 0;
2445 state = sheet->state;
2446 range = &sheet->range;
2450 case PSPPIRE_SHEET_NORMAL:
2451 return GTK_STATE_NORMAL;
2453 case PSPPIRE_SHEET_ROW_SELECTED:
2454 if (row >= range->row0 && row <= range->rowi)
2455 return GTK_STATE_SELECTED;
2457 case PSPPIRE_SHEET_COLUMN_SELECTED:
2458 if (col >= range->col0 && col <= range->coli)
2459 return GTK_STATE_SELECTED;
2461 case PSPPIRE_SHEET_RANGE_SELECTED:
2462 if (row >= range->row0 && row <= range->rowi && \
2463 col >= range->col0 && col <= range->coli)
2464 return GTK_STATE_SELECTED;
2467 return GTK_STATE_NORMAL;
2470 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2471 If the function returns FALSE, then the results will be unreliable.
2474 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2482 *column = -G_MAXINT;
2484 g_return_val_if_fail (sheet != NULL, 0);
2485 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2487 /* bounds checking, return false if the user clicked
2495 if ( sheet->column_titles_visible)
2496 y -= sheet->column_title_area.height;
2498 y += sheet->vadjustment->value;
2500 if ( y < 0 && sheet->column_titles_visible)
2506 trow = row_from_ypixel (sheet, y);
2507 if (trow > psppire_axis_unit_count (sheet->vaxis))
2513 if ( sheet->row_titles_visible)
2514 x -= sheet->row_title_area.width;
2516 x += sheet->hadjustment->value;
2518 if ( x < 0 && sheet->row_titles_visible)
2524 tcol = column_from_xpixel (sheet, x);
2525 if (tcol > psppire_axis_unit_count (sheet->haxis))
2535 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2540 g_return_val_if_fail (sheet != NULL, 0);
2541 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2543 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2546 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2547 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2549 area->width= (column == -1) ? sheet->row_title_area.width
2550 : psppire_axis_unit_size (sheet->haxis, column);
2552 area->height= (row == -1) ? sheet->column_title_area.height
2553 : psppire_axis_unit_size (sheet->vaxis, row);
2559 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2561 g_return_if_fail (sheet != NULL);
2562 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2564 if (row < -1 || col < -1)
2567 if (row >= psppire_axis_unit_count (sheet->vaxis)
2569 col >= psppire_axis_unit_count (sheet->haxis))
2572 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2575 if ( row == -1 || col == -1)
2577 psppire_sheet_hide_entry_widget (sheet);
2581 change_active_cell (sheet, row, col);
2585 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2587 g_return_if_fail (sheet != NULL);
2588 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2590 if ( row ) *row = sheet->active_cell.row;
2591 if (column) *column = sheet->active_cell.col;
2595 entry_load_text (PsppireSheet *sheet)
2599 GtkJustification justification;
2600 PsppireSheetCellAttr attributes;
2602 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2603 if (sheet->state != GTK_STATE_NORMAL) return;
2605 row = sheet->active_cell.row;
2606 col = sheet->active_cell.col;
2608 if (row < 0 || col < 0) return;
2610 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2612 if (text && strlen (text) > 0)
2614 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2615 justification = attributes.justification;
2616 psppire_sheet_set_cell (sheet, row, col, justification, text);
2622 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2624 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2627 if (sheet->active_cell.row < 0 ||
2628 sheet->active_cell.col < 0) return;
2630 gtk_widget_hide (sheet->entry_widget);
2631 gtk_widget_unmap (sheet->entry_widget);
2633 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2637 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2639 gint old_row, old_col;
2641 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2643 if (row < 0 || col < 0)
2646 if ( row > psppire_axis_unit_count (sheet->vaxis)
2647 || col > psppire_axis_unit_count (sheet->haxis))
2650 if (sheet->state != PSPPIRE_SHEET_NORMAL)
2652 sheet->state = PSPPIRE_SHEET_NORMAL;
2653 psppire_sheet_real_unselect_range (sheet, NULL);
2656 old_row = sheet->active_cell.row;
2657 old_col = sheet->active_cell.col;
2659 /* Erase the old cell */
2660 psppire_sheet_draw_active_cell (sheet);
2662 entry_load_text (sheet);
2664 sheet->range.row0 = row;
2665 sheet->range.col0 = col;
2666 sheet->range.rowi = row;
2667 sheet->range.coli = col;
2668 sheet->active_cell.row = row;
2669 sheet->active_cell.col = col;
2670 sheet->selection_cell.row = row;
2671 sheet->selection_cell.col = col;
2673 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2675 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2677 psppire_sheet_draw_active_cell (sheet);
2678 psppire_sheet_show_entry_widget (sheet);
2680 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2682 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2683 row, col, old_row, old_col);
2688 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2690 GtkEntry *sheet_entry;
2691 PsppireSheetCellAttr attributes;
2695 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2697 row = sheet->active_cell.row;
2698 col = sheet->active_cell.col;
2700 /* Don't show the active cell, if there is no active cell: */
2701 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2704 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2705 if (sheet->state != PSPPIRE_SHEET_NORMAL) return;
2706 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2708 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2710 sheet_entry = psppire_sheet_get_entry (sheet);
2712 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2714 if (GTK_IS_ENTRY (sheet_entry))
2716 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2717 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2720 text = g_strdup ("");
2722 if (strcmp (old_text, text) != 0)
2723 gtk_entry_set_text (sheet_entry, text);
2725 dispose_string (sheet, text);
2728 switch (attributes.justification)
2730 case GTK_JUSTIFY_RIGHT:
2731 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2733 case GTK_JUSTIFY_CENTER:
2734 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2736 case GTK_JUSTIFY_LEFT:
2738 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2744 psppire_sheet_size_allocate_entry (sheet);
2746 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2747 psppire_sheet_model_is_editable (sheet->model,
2749 gtk_widget_map (sheet->entry_widget);
2753 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2756 PsppireSheetRange range;
2758 row = sheet->active_cell.row;
2759 col = sheet->active_cell.col;
2761 if (row < 0 || col < 0) return FALSE;
2763 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2766 range.col0 = range.coli = col;
2767 range.row0 = range.rowi = row;
2769 psppire_sheet_draw_border (sheet, range);
2777 psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range)
2779 gint i, j, mask1, mask2;
2780 gint state, selected;
2781 gint x, y, width, height;
2782 PsppireSheetRange new_range, aux_range;
2784 g_return_if_fail (sheet != NULL);
2786 if (range == NULL) range=&sheet->range;
2790 range->row0 = MIN (range->row0, sheet->range.row0);
2791 range->rowi = MAX (range->rowi, sheet->range.rowi);
2792 range->col0 = MIN (range->col0, sheet->range.col0);
2793 range->coli = MAX (range->coli, sheet->range.coli);
2795 range->row0 = MAX (range->row0, min_visible_row (sheet));
2796 range->rowi = MIN (range->rowi, max_visible_row (sheet));
2797 range->col0 = MAX (range->col0, min_visible_column (sheet));
2798 range->coli = MIN (range->coli, max_visible_column (sheet));
2800 aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
2801 aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
2802 aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
2803 aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
2805 for (i = range->row0; i <= range->rowi; i++)
2807 for (j = range->col0; j <= range->coli; j++)
2810 state = psppire_sheet_cell_get_state (sheet, i, j);
2811 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2812 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2814 if (state == GTK_STATE_SELECTED && selected &&
2815 (i == sheet->range.row0 || i == sheet->range.rowi ||
2816 j == sheet->range.col0 || j == sheet->range.coli ||
2817 i == new_range.row0 || i == new_range.rowi ||
2818 j == new_range.col0 || j == new_range.coli))
2821 mask1 = i == sheet->range.row0 ? 1 : 0;
2822 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2823 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2824 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2826 mask2 = i == new_range.row0 ? 1 : 0;
2827 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2828 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2829 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2833 x = psppire_axis_start_pixel (sheet->haxis, j);
2834 y = psppire_axis_start_pixel (sheet->vaxis, i);
2835 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2836 psppire_axis_unit_size (sheet->haxis, j);
2837 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2839 if (i == sheet->range.row0)
2842 height = height + 3;
2844 if (i == sheet->range.rowi) height = height + 3;
2845 if (j == sheet->range.col0)
2850 if (j == sheet->range.coli) width = width + 3;
2852 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2854 x = psppire_axis_start_pixel (sheet->haxis, j);
2855 y = psppire_axis_start_pixel (sheet->vaxis, i);
2856 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2857 psppire_axis_unit_size (sheet->haxis, j);
2859 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2861 if (i == new_range.row0)
2864 height = height - 2;
2866 if (i == new_range.rowi) height = height - 3;
2867 if (j == new_range.col0)
2872 if (j == new_range.coli) width = width - 3;
2874 gdk_draw_rectangle (sheet->sheet_window,
2885 for (i = range->row0; i <= range->rowi; i++)
2887 for (j = range->col0; j <= range->coli; j++)
2890 state = psppire_sheet_cell_get_state (sheet, i, j);
2891 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2892 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2894 if (state == GTK_STATE_SELECTED && !selected)
2897 x = psppire_axis_start_pixel (sheet->haxis, j);
2898 y = psppire_axis_start_pixel (sheet->vaxis, i);
2899 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2900 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2902 if (i == sheet->range.row0)
2905 height = height + 3;
2907 if (i == sheet->range.rowi) height = height + 3;
2908 if (j == sheet->range.col0)
2913 if (j == sheet->range.coli) width = width + 3;
2919 for (i = range->row0; i <= range->rowi; i++)
2921 for (j = range->col0; j <= range->coli; j++)
2924 state = psppire_sheet_cell_get_state (sheet, i, j);
2925 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2926 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2928 if (state != GTK_STATE_SELECTED && selected &&
2929 (i != sheet->active_cell.row || j != sheet->active_cell.col))
2932 x = psppire_axis_start_pixel (sheet->haxis, j);
2933 y = psppire_axis_start_pixel (sheet->vaxis, i);
2934 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2935 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2937 if (i == new_range.row0)
2940 height = height - 2;
2942 if (i == new_range.rowi) height = height - 3;
2943 if (j == new_range.col0)
2948 if (j == new_range.coli) width = width - 3;
2950 gdk_draw_rectangle (sheet->sheet_window,
2961 for (i = aux_range.row0; i <= aux_range.rowi; i++)
2963 for (j = aux_range.col0; j <= aux_range.coli; j++)
2965 state = psppire_sheet_cell_get_state (sheet, i, j);
2967 mask1 = i == sheet->range.row0 ? 1 : 0;
2968 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2969 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2970 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2972 mask2 = i == new_range.row0 ? 1 : 0;
2973 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2974 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2975 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2976 if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
2978 x = psppire_axis_start_pixel (sheet->haxis, j);
2979 y = psppire_axis_start_pixel (sheet->vaxis, i);
2980 width = psppire_axis_unit_size (sheet->haxis, j);
2981 height = psppire_axis_unit_size (sheet->vaxis, i);
2983 gdk_draw_rectangle (sheet->sheet_window,
2991 gdk_draw_rectangle (sheet->sheet_window,
2994 x + 1, y + height - 1,
2998 gdk_draw_rectangle (sheet->sheet_window,
3006 gdk_draw_rectangle (sheet->sheet_window,
3009 x + width - 1, y + 1,
3021 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
3025 rectangle_from_range (sheet, &new_range, &area);
3027 gdk_draw_rectangle (sheet->sheet_window,
3031 area.width, area.height);
3036 psppire_sheet_real_select_range (PsppireSheet *sheet,
3037 const PsppireSheetRange *range)
3041 g_return_if_fail (sheet != NULL);
3043 if (range == NULL) range = &sheet->range;
3045 memcpy (&sheet->range, range, sizeof (*range));
3047 if (range->row0 < 0 || range->rowi < 0) return;
3048 if (range->col0 < 0 || range->coli < 0) return;
3050 state = sheet->state;
3053 if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3054 range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3056 psppire_sheet_new_selection (sheet, &sheet->range);
3060 psppire_sheet_range_draw_selection (sheet, sheet->range);
3064 psppire_sheet_update_primary_selection (sheet);
3066 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3071 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
3073 g_return_if_fail (sheet != NULL);
3074 *range = sheet->range;
3079 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
3081 g_return_if_fail (sheet != NULL);
3083 if (range == NULL) range=&sheet->range;
3085 if (range->row0 < 0 || range->rowi < 0) return;
3086 if (range->col0 < 0 || range->coli < 0) return;
3089 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3090 psppire_sheet_real_unselect_range (sheet, NULL);
3092 sheet->range.row0 = range->row0;
3093 sheet->range.rowi = range->rowi;
3094 sheet->range.col0 = range->col0;
3095 sheet->range.coli = range->coli;
3096 sheet->active_cell.row = range->row0;
3097 sheet->active_cell.col = range->col0;
3098 sheet->selection_cell.row = range->rowi;
3099 sheet->selection_cell.col = range->coli;
3101 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3102 psppire_sheet_real_select_range (sheet, NULL);
3106 psppire_sheet_unselect_range (PsppireSheet *sheet)
3108 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3111 psppire_sheet_real_unselect_range (sheet, NULL);
3112 sheet->state = GTK_STATE_NORMAL;
3114 change_active_cell (sheet,
3115 sheet->active_cell.row, sheet->active_cell.col);
3120 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
3121 const PsppireSheetRange *range)
3123 g_return_if_fail (sheet != NULL);
3124 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3127 range = &sheet->range;
3129 if (range->row0 < 0 || range->rowi < 0) return;
3130 if (range->col0 < 0 || range->coli < 0) return;
3132 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3133 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3135 sheet->range.row0 = -1;
3136 sheet->range.rowi = -1;
3137 sheet->range.col0 = -1;
3138 sheet->range.coli = -1;
3143 psppire_sheet_expose (GtkWidget *widget,
3144 GdkEventExpose *event)
3146 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3148 g_return_val_if_fail (event != NULL, FALSE);
3150 if (!GTK_WIDGET_DRAWABLE (widget))
3153 /* exposure events on the sheet */
3154 if (event->window == sheet->row_title_window &&
3155 sheet->row_titles_visible)
3157 draw_row_title_buttons_range (sheet,
3158 min_visible_row (sheet),
3159 max_visible_row (sheet));
3162 if (event->window == sheet->column_title_window &&
3163 sheet->column_titles_visible)
3165 draw_column_title_buttons_range (sheet,
3166 min_visible_column (sheet),
3167 max_visible_column (sheet));
3170 if (event->window == sheet->sheet_window)
3172 draw_sheet_region (sheet, event->region);
3175 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3177 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3178 psppire_sheet_range_draw (sheet, &sheet->range);
3180 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3181 psppire_sheet_range_draw (sheet, &sheet->drag_range);
3183 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3184 psppire_sheet_range_draw_selection (sheet, sheet->range);
3185 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3186 draw_xor_rectangle (sheet, sheet->drag_range);
3190 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
3193 PsppireSheetRange range;
3194 range.row0 = range.rowi = sheet->active_cell.row;
3195 range.col0 = range.coli = sheet->active_cell.col;
3197 rectangle_from_range (sheet, &range, &rect);
3199 if (GDK_OVERLAP_RECTANGLE_OUT !=
3200 gdk_region_rect_in (event->region, &rect))
3202 psppire_sheet_draw_active_cell (sheet);
3208 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3215 psppire_sheet_button_press (GtkWidget *widget,
3216 GdkEventButton *event)
3218 PsppireSheet *sheet;
3219 GdkModifierType mods;
3224 g_return_val_if_fail (widget != NULL, FALSE);
3225 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
3226 g_return_val_if_fail (event != NULL, FALSE);
3228 sheet = PSPPIRE_SHEET (widget);
3230 /* Cancel any pending tooltips */
3231 if (sheet->motion_timer)
3233 g_source_remove (sheet->motion_timer);
3234 sheet->motion_timer = 0;
3237 gtk_widget_get_pointer (widget, &x, &y);
3238 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3241 if (event->window == sheet->column_title_window)
3243 sheet->x_drag = event->x;
3244 g_signal_emit (sheet,
3245 sheet_signals[BUTTON_EVENT_COLUMN], 0,
3248 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3250 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3251 g_signal_emit (sheet,
3252 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3255 else if (event->window == sheet->row_title_window)
3257 g_signal_emit (sheet,
3258 sheet_signals[BUTTON_EVENT_ROW], 0,
3261 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3263 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3264 g_signal_emit (sheet,
3265 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3269 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3271 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3274 /* press on resize windows */
3275 if (event->window == sheet->column_title_window)
3277 sheet->x_drag = event->x;
3279 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3281 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3282 gdk_pointer_grab (sheet->column_title_window, FALSE,
3283 GDK_POINTER_MOTION_HINT_MASK |
3284 GDK_BUTTON1_MOTION_MASK |
3285 GDK_BUTTON_RELEASE_MASK,
3286 NULL, NULL, event->time);
3288 draw_xor_vline (sheet);
3293 if (event->window == sheet->row_title_window)
3295 sheet->y_drag = event->y;
3297 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3299 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3300 gdk_pointer_grab (sheet->row_title_window, FALSE,
3301 GDK_POINTER_MOTION_HINT_MASK |
3302 GDK_BUTTON1_MOTION_MASK |
3303 GDK_BUTTON_RELEASE_MASK,
3304 NULL, NULL, event->time);
3306 draw_xor_hline (sheet);
3311 /* the sheet itself does not handle other than single click events */
3312 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3314 /* selections on the sheet */
3315 if (event->window == sheet->sheet_window)
3317 gtk_widget_get_pointer (widget, &x, &y);
3318 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3319 gdk_pointer_grab (sheet->sheet_window, FALSE,
3320 GDK_POINTER_MOTION_HINT_MASK |
3321 GDK_BUTTON1_MOTION_MASK |
3322 GDK_BUTTON_RELEASE_MASK,
3323 NULL, NULL, event->time);
3324 gtk_grab_add (GTK_WIDGET (sheet));
3326 if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
3327 sheet->selection_mode != GTK_SELECTION_NONE &&
3328 sheet->cursor_drag->type == GDK_SIZING &&
3329 !PSPPIRE_SHEET_IN_SELECTION (sheet) && !PSPPIRE_SHEET_IN_RESIZE (sheet))
3331 if (sheet->state == GTK_STATE_NORMAL)
3333 row = sheet->active_cell.row;
3334 column = sheet->active_cell.col;
3335 sheet->active_cell.row = row;
3336 sheet->active_cell.col = column;
3337 sheet->drag_range = sheet->range;
3338 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3339 psppire_sheet_select_range (sheet, &sheet->drag_range);
3343 if (row > sheet->range.rowi) row--;
3344 if (column > sheet->range.coli) column--;
3345 sheet->drag_cell.row = row;
3346 sheet->drag_cell.col = column;
3347 sheet->drag_range = sheet->range;
3348 draw_xor_rectangle (sheet, sheet->drag_range);
3349 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3351 else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
3352 !PSPPIRE_SHEET_IN_SELECTION (sheet)
3353 && ! PSPPIRE_SHEET_IN_DRAG (sheet)
3354 && sheet->active_cell.row >= 0
3355 && sheet->active_cell.col >= 0
3358 if (sheet->state == GTK_STATE_NORMAL)
3360 row = sheet->active_cell.row;
3361 column = sheet->active_cell.col;
3362 sheet->active_cell.row = row;
3363 sheet->active_cell.col = column;
3364 sheet->drag_range = sheet->range;
3365 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3366 psppire_sheet_select_range (sheet, &sheet->drag_range);
3370 if (row < sheet->range.row0) row++;
3371 if (row > sheet->range.rowi) row--;
3372 if (column < sheet->range.col0) column++;
3373 if (column > sheet->range.coli) column--;
3374 sheet->drag_cell.row = row;
3375 sheet->drag_cell.col = column;
3376 sheet->drag_range = sheet->range;
3377 draw_xor_rectangle (sheet, sheet->drag_range);
3378 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3382 veto = psppire_sheet_click_cell (sheet, row, column);
3383 if (veto) PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3387 if (event->window == sheet->column_title_window)
3389 gtk_widget_get_pointer (widget, &x, &y);
3390 if ( sheet->row_titles_visible)
3391 x -= sheet->row_title_area.width;
3393 x += sheet->hadjustment->value;
3395 column = column_from_xpixel (sheet, x);
3397 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3399 veto = psppire_sheet_click_cell (sheet, -1, column);
3400 gtk_grab_add (GTK_WIDGET (sheet));
3401 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3405 if (event->window == sheet->row_title_window)
3407 gtk_widget_get_pointer (widget, &x, &y);
3408 if ( sheet->column_titles_visible)
3409 y -= sheet->column_title_area.height;
3411 y += sheet->vadjustment->value;
3413 row = row_from_ypixel (sheet, y);
3414 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3416 veto = psppire_sheet_click_cell (sheet, row, -1);
3417 gtk_grab_add (GTK_WIDGET (sheet));
3418 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3426 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3428 PsppireSheetCell cell;
3429 gboolean forbid_move;
3434 if (row >= psppire_axis_unit_count (sheet->vaxis)
3435 || column >= psppire_axis_unit_count (sheet->haxis))
3440 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3441 &sheet->active_cell,
3447 if (sheet->state == GTK_STATE_NORMAL)
3450 row = sheet->active_cell.row;
3451 column = sheet->active_cell.col;
3453 change_active_cell (sheet, row, column);
3457 if (row == -1 && column >= 0)
3459 psppire_sheet_select_column (sheet, column);
3463 if (column == -1 && row >= 0)
3465 psppire_sheet_select_row (sheet, row);
3469 if (row == -1 && column == -1)
3471 sheet->range.row0 = 0;
3472 sheet->range.col0 = 0;
3473 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3475 psppire_axis_unit_count (sheet->haxis) - 1;
3476 sheet->active_cell.row = 0;
3477 sheet->active_cell.col = 0;
3478 psppire_sheet_select_range (sheet, NULL);
3482 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3484 sheet->state = PSPPIRE_SHEET_NORMAL;
3485 psppire_sheet_real_unselect_range (sheet, NULL);
3489 change_active_cell (sheet, row, column);
3492 sheet->active_cell.row = row;
3493 sheet->active_cell.col = column;
3494 sheet->selection_cell.row = row;
3495 sheet->selection_cell.col = column;
3496 sheet->range.row0 = row;
3497 sheet->range.col0 = column;
3498 sheet->range.rowi = row;
3499 sheet->range.coli = column;
3500 sheet->state = PSPPIRE_SHEET_NORMAL;
3501 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3503 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3509 psppire_sheet_button_release (GtkWidget *widget,
3510 GdkEventButton *event)
3512 GdkDisplay *display = gtk_widget_get_display (widget);
3514 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3516 /* release on resize windows */
3517 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3520 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3521 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3523 gdk_display_pointer_ungrab (display, event->time);
3524 draw_xor_vline (sheet);
3527 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3528 + sheet->hadjustment->value;
3530 set_column_width (sheet, sheet->drag_cell.col, width);
3535 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3538 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3539 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3541 gdk_display_pointer_ungrab (display, event->time);
3542 draw_xor_hline (sheet);
3545 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3546 sheet->vadjustment->value;
3548 set_row_height (sheet, sheet->drag_cell.row, height);
3553 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3555 PsppireSheetRange old_range;
3556 draw_xor_rectangle (sheet, sheet->drag_range);
3557 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3558 gdk_display_pointer_ungrab (display, event->time);
3560 psppire_sheet_real_unselect_range (sheet, NULL);
3562 sheet->active_cell.row = sheet->active_cell.row +
3563 (sheet->drag_range.row0 - sheet->range.row0);
3564 sheet->active_cell.col = sheet->active_cell.col +
3565 (sheet->drag_range.col0 - sheet->range.col0);
3566 sheet->selection_cell.row = sheet->selection_cell.row +
3567 (sheet->drag_range.row0 - sheet->range.row0);
3568 sheet->selection_cell.col = sheet->selection_cell.col +
3569 (sheet->drag_range.col0 - sheet->range.col0);
3570 old_range = sheet->range;
3571 sheet->range = sheet->drag_range;
3572 sheet->drag_range = old_range;
3573 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3574 &sheet->drag_range, &sheet->range);
3575 psppire_sheet_select_range (sheet, &sheet->range);
3578 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3580 PsppireSheetRange old_range;
3581 draw_xor_rectangle (sheet, sheet->drag_range);
3582 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3583 gdk_display_pointer_ungrab (display, event->time);
3585 psppire_sheet_real_unselect_range (sheet, NULL);
3587 sheet->active_cell.row = sheet->active_cell.row +
3588 (sheet->drag_range.row0 - sheet->range.row0);
3589 sheet->active_cell.col = sheet->active_cell.col +
3590 (sheet->drag_range.col0 - sheet->range.col0);
3591 if (sheet->drag_range.row0 < sheet->range.row0)
3592 sheet->selection_cell.row = sheet->drag_range.row0;
3593 if (sheet->drag_range.rowi >= sheet->range.rowi)
3594 sheet->selection_cell.row = sheet->drag_range.rowi;
3595 if (sheet->drag_range.col0 < sheet->range.col0)
3596 sheet->selection_cell.col = sheet->drag_range.col0;
3597 if (sheet->drag_range.coli >= sheet->range.coli)
3598 sheet->selection_cell.col = sheet->drag_range.coli;
3599 old_range = sheet->range;
3600 sheet->range = sheet->drag_range;
3601 sheet->drag_range = old_range;
3603 if (sheet->state == GTK_STATE_NORMAL) sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3604 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3605 &sheet->drag_range, &sheet->range);
3606 psppire_sheet_select_range (sheet, &sheet->range);
3609 if (sheet->state == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet))
3611 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3612 gdk_display_pointer_ungrab (display, event->time);
3613 change_active_cell (sheet, sheet->active_cell.row,
3614 sheet->active_cell.col);
3617 if (PSPPIRE_SHEET_IN_SELECTION)
3618 gdk_display_pointer_ungrab (display, event->time);
3619 gtk_grab_remove (GTK_WIDGET (sheet));
3621 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3630 /* Shamelessly lifted from gtktooltips */
3632 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3636 gtk_widget_size_request (tip_window, &req);
3637 gtk_paint_flat_box (tip_window->style, tip_window->window,
3638 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3639 NULL, GTK_WIDGET(tip_window), "tooltip",
3640 0, 0, req.width, req.height);
3646 destroy_hover_window (PsppireSheetHoverTitle *h)
3648 gtk_widget_destroy (h->window);
3652 static PsppireSheetHoverTitle *
3653 create_hover_window (void)
3655 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3657 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3659 #if GTK_CHECK_VERSION (2, 9, 0)
3660 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3661 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3664 gtk_widget_set_app_paintable (hw->window, TRUE);
3665 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3666 gtk_widget_set_name (hw->window, "gtk-tooltips");
3667 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3669 g_signal_connect (hw->window,
3671 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3674 hw->label = gtk_label_new (NULL);
3677 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3678 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3680 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3682 gtk_widget_show (hw->label);
3684 g_signal_connect (hw->window,
3686 G_CALLBACK (gtk_widget_destroyed),
3692 #define HOVER_WINDOW_Y_OFFSET 2
3695 show_subtitle (PsppireSheet *sheet, gint row, gint column, const gchar *subtitle)
3704 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3708 sheet->hover_window->row = row;
3709 sheet->hover_window->column = column;
3711 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3713 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3715 gtk_widget_show (sheet->hover_window->window);
3717 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3723 y += sheet->column_title_area.y;
3724 y += sheet->column_title_area.height;
3725 y += HOVER_WINDOW_Y_OFFSET;
3731 x += sheet->row_title_area.x;
3732 x += sheet->row_title_area.width * 2 / 3.0;
3735 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3740 motion_timeout_callback (gpointer data)
3742 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3745 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3747 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3749 if (sheet->row_title_under && row >= 0)
3751 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3753 show_subtitle (sheet, row, -1, text);
3757 if (sheet->column_title_under && column >= 0)
3759 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3762 show_subtitle (sheet, -1, column, text);
3772 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3774 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3775 GdkModifierType mods;
3776 GdkCursorType new_cursor;
3779 GdkDisplay *display;
3781 g_return_val_if_fail (event != NULL, FALSE);
3783 display = gtk_widget_get_display (widget);
3785 /* selections on the sheet */
3789 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3791 if ( sheet->motion_timer > 0 )
3792 g_source_remove (sheet->motion_timer);
3793 sheet->motion_timer =
3794 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3800 gtk_widget_get_pointer (widget, &wx, &wy);
3802 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3804 if ( row != sheet->hover_window->row ||
3805 column != sheet->hover_window->column)
3807 gtk_widget_hide (sheet->hover_window->window);
3812 if (event->window == sheet->column_title_window)
3814 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3815 on_column_boundary (sheet, x, &column))
3817 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3818 if (new_cursor != sheet->cursor_drag->type)
3820 gdk_cursor_unref (sheet->cursor_drag);
3821 sheet->cursor_drag =
3822 gdk_cursor_new_for_display (display, new_cursor);
3824 gdk_window_set_cursor (sheet->column_title_window,
3825 sheet->cursor_drag);
3830 new_cursor = GDK_TOP_LEFT_ARROW;
3831 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3832 new_cursor != sheet->cursor_drag->type)
3834 gdk_cursor_unref (sheet->cursor_drag);
3835 sheet->cursor_drag =
3836 gdk_cursor_new_for_display (display, new_cursor);
3837 gdk_window_set_cursor (sheet->column_title_window,
3838 sheet->cursor_drag);
3842 else if (event->window == sheet->row_title_window)
3844 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3845 on_row_boundary (sheet, y, &row))
3847 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3848 if (new_cursor != sheet->cursor_drag->type)
3850 gdk_cursor_unref (sheet->cursor_drag);
3851 sheet->cursor_drag =
3852 gdk_cursor_new_for_display (display, new_cursor);
3853 gdk_window_set_cursor (sheet->row_title_window,
3854 sheet->cursor_drag);
3859 new_cursor = GDK_TOP_LEFT_ARROW;
3860 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3861 new_cursor != sheet->cursor_drag->type)
3863 gdk_cursor_unref (sheet->cursor_drag);
3864 sheet->cursor_drag =
3865 gdk_cursor_new_for_display (display, new_cursor);
3866 gdk_window_set_cursor (sheet->row_title_window,
3867 sheet->cursor_drag);
3872 new_cursor = GDK_PLUS;
3873 if ( event->window == sheet->sheet_window &&
3874 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3875 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3876 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3877 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3878 new_cursor != sheet->cursor_drag->type)
3880 gdk_cursor_unref (sheet->cursor_drag);
3881 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3882 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3885 new_cursor = GDK_TOP_LEFT_ARROW;
3886 if ( event->window == sheet->sheet_window &&
3887 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3888 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3889 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3890 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3891 new_cursor != sheet->cursor_drag->type)
3893 gdk_cursor_unref (sheet->cursor_drag);
3894 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3895 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3898 new_cursor = GDK_SIZING;
3899 if ( event->window == sheet->sheet_window &&
3900 sheet->selection_mode != GTK_SELECTION_NONE &&
3901 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3902 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3903 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3904 new_cursor != sheet->cursor_drag->type)
3906 gdk_cursor_unref (sheet->cursor_drag);
3907 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3908 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3912 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3913 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3915 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3917 if (event->x != sheet->x_drag)
3919 draw_xor_vline (sheet);
3920 sheet->x_drag = event->x;
3921 draw_xor_vline (sheet);
3927 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3929 if (event->y != sheet->y_drag)
3931 draw_xor_hline (sheet);
3932 sheet->y_drag = event->y;
3933 draw_xor_hline (sheet);
3939 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3941 PsppireSheetRange aux;
3942 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3943 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3944 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3945 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3949 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3950 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3952 aux = sheet->drag_range;
3953 sheet->drag_range.row0 = sheet->range.row0 + row;
3954 sheet->drag_range.col0 = sheet->range.col0 + column;
3955 sheet->drag_range.rowi = sheet->range.rowi + row;
3956 sheet->drag_range.coli = sheet->range.coli + column;
3957 if (aux.row0 != sheet->drag_range.row0 ||
3958 aux.col0 != sheet->drag_range.col0)
3960 draw_xor_rectangle (sheet, aux);
3961 draw_xor_rectangle (sheet, sheet->drag_range);
3967 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3969 PsppireSheetRange aux;
3970 gint v_h, current_col, current_row, col_threshold, row_threshold;
3972 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
3973 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
3975 current_col = column_from_xpixel (sheet, x);
3976 current_row = row_from_ypixel (sheet, y);
3977 column = current_col - sheet->drag_cell.col;
3978 row = current_row - sheet->drag_cell.row;
3980 /*use half of column width resp. row height as threshold to
3982 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
3983 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
3986 if (x < col_threshold)
3989 else if (column < 0)
3991 if (x > col_threshold)
3994 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
3995 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
3998 if (y < row_threshold)
4003 if (y > row_threshold)
4007 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
4008 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4018 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4019 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4021 aux = sheet->drag_range;
4022 sheet->drag_range = sheet->range;
4024 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4025 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4026 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4027 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4029 if (aux.row0 != sheet->drag_range.row0 ||
4030 aux.rowi != sheet->drag_range.rowi ||
4031 aux.col0 != sheet->drag_range.col0 ||
4032 aux.coli != sheet->drag_range.coli)
4034 draw_xor_rectangle (sheet, aux);
4035 draw_xor_rectangle (sheet, sheet->drag_range);
4041 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
4043 if (sheet->state == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
4044 column == sheet->active_cell.col) return TRUE;
4046 if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4047 psppire_sheet_extend_selection (sheet, row, column);
4053 psppire_sheet_crossing_notify (GtkWidget *widget,
4054 GdkEventCrossing *event)
4056 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4058 if (event->window == sheet->column_title_window)
4059 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4060 else if (event->window == sheet->row_title_window)
4061 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4068 psppire_sheet_focus_in (GtkWidget *w,
4069 GdkEventFocus *event)
4071 PsppireSheet *sheet = PSPPIRE_SHEET (w);
4073 gtk_widget_grab_focus (sheet->entry_widget);
4080 psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column)
4082 PsppireSheetRange range;
4086 if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
4089 if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
4091 gtk_widget_grab_focus (GTK_WIDGET (sheet));
4093 if (PSPPIRE_SHEET_IN_DRAG (sheet)) return;
4095 state = sheet->state;
4097 switch (sheet->state)
4099 case PSPPIRE_SHEET_ROW_SELECTED:
4100 column = psppire_axis_unit_count (sheet->haxis) - 1;
4102 case PSPPIRE_SHEET_COLUMN_SELECTED:
4103 row = psppire_axis_unit_count (sheet->vaxis) - 1;
4105 case PSPPIRE_SHEET_NORMAL:
4106 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4107 r = sheet->active_cell.row;
4108 c = sheet->active_cell.col;
4109 sheet->range.col0 = c;
4110 sheet->range.row0 = r;
4111 sheet->range.coli = c;
4112 sheet->range.rowi = r;
4113 psppire_sheet_range_draw_selection (sheet, sheet->range);
4114 case PSPPIRE_SHEET_RANGE_SELECTED:
4115 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4118 sheet->selection_cell.row = row;
4119 sheet->selection_cell.col = column;
4121 range.col0 = MIN (column, sheet->active_cell.col);
4122 range.coli = MAX (column, sheet->active_cell.col);
4123 range.row0 = MIN (row, sheet->active_cell.row);
4124 range.rowi = MAX (row, sheet->active_cell.row);
4126 if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
4127 range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
4128 state == PSPPIRE_SHEET_NORMAL)
4129 psppire_sheet_real_select_range (sheet, &range);
4134 psppire_sheet_entry_key_press (GtkWidget *widget,
4138 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
4143 /* Number of rows in a step-increment */
4144 #define ROWS_PER_STEP 1
4148 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
4150 gint old_row = sheet->active_cell.row ;
4151 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
4155 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
4156 min_visible_row (sheet));
4160 case GTK_SCROLL_PAGE_DOWN:
4161 gtk_adjustment_set_value (sheet->vadjustment,
4162 sheet->vadjustment->value +
4163 sheet->vadjustment->page_increment);
4165 case GTK_SCROLL_PAGE_UP:
4166 gtk_adjustment_set_value (sheet->vadjustment,
4167 sheet->vadjustment->value -
4168 sheet->vadjustment->page_increment);
4172 g_assert_not_reached ();
4177 vpixel += psppire_axis_start_pixel (sheet->vaxis,
4178 min_visible_row (sheet));
4180 new_row = row_from_ypixel (sheet, vpixel);
4182 change_active_cell (sheet, new_row,
4183 sheet->active_cell.col);
4188 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
4190 gint current_row = sheet->active_cell.row;
4191 gint current_col = sheet->active_cell.col;
4192 PsppireSheetCell new_cell ;
4193 gboolean forbidden = FALSE;
4195 new_cell.row = current_row;
4196 new_cell.col = current_col;
4200 case GTK_SCROLL_STEP_DOWN:
4203 case GTK_SCROLL_STEP_UP:
4206 case GTK_SCROLL_STEP_RIGHT:
4209 case GTK_SCROLL_STEP_LEFT:
4212 case GTK_SCROLL_STEP_FORWARD:
4215 psppire_sheet_model_get_column_count (sheet->model))
4221 case GTK_SCROLL_STEP_BACKWARD:
4223 if (new_cell.col < 0)
4226 psppire_sheet_model_get_column_count (sheet->model) - 1;
4231 g_assert_not_reached ();
4235 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
4236 &sheet->active_cell,
4244 maximize_int (&new_cell.row, 0);
4245 maximize_int (&new_cell.col, 0);
4247 minimize_int (&new_cell.row,
4248 psppire_axis_unit_count (sheet->vaxis) - 1);
4250 minimize_int (&new_cell.col,
4251 psppire_axis_unit_count (sheet->haxis) - 1);
4253 change_active_cell (sheet, new_cell.row, new_cell.col);
4256 if ( new_cell.col > max_fully_visible_column (sheet))
4259 psppire_axis_start_pixel (sheet->haxis,
4261 hpos -= sheet->hadjustment->page_size;
4263 gtk_adjustment_set_value (sheet->hadjustment,
4266 else if ( new_cell.col < min_fully_visible_column (sheet))
4269 psppire_axis_start_pixel (sheet->haxis,
4272 gtk_adjustment_set_value (sheet->hadjustment,
4277 if ( new_cell.row > max_fully_visible_row (sheet))
4280 psppire_axis_start_pixel (sheet->vaxis,
4282 vpos -= sheet->vadjustment->page_size;
4284 gtk_adjustment_set_value (sheet->vadjustment,
4287 else if ( new_cell.row < min_fully_visible_row (sheet))
4290 psppire_axis_start_pixel (sheet->vaxis,
4293 gtk_adjustment_set_value (sheet->vadjustment,
4297 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
4302 psppire_sheet_key_press (GtkWidget *widget,
4305 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4307 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
4309 switch (key->keyval)
4312 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
4315 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
4317 case GDK_ISO_Left_Tab:
4318 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
4321 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
4325 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
4328 step_sheet (sheet, GTK_SCROLL_STEP_UP);
4332 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
4335 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
4339 gtk_adjustment_set_value (sheet->vadjustment,
4340 sheet->vadjustment->lower);
4342 change_active_cell (sheet, 0,
4343 sheet->active_cell.col);
4348 gtk_adjustment_set_value (sheet->vadjustment,
4349 sheet->vadjustment->upper -
4350 sheet->vadjustment->page_size -
4351 sheet->vadjustment->page_increment);
4354 change_active_cellx (sheet,
4355 psppire_axis_unit_count (sheet->vaxis) - 1,
4356 sheet->active_cell.col);
4360 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
4371 psppire_sheet_size_request (GtkWidget *widget,
4372 GtkRequisition *requisition)
4374 PsppireSheet *sheet;
4376 g_return_if_fail (widget != NULL);
4377 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4378 g_return_if_fail (requisition != NULL);
4380 sheet = PSPPIRE_SHEET (widget);
4382 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4383 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4385 /* compute the size of the column title area */
4386 if (sheet->column_titles_visible)
4387 requisition->height += sheet->column_title_area.height;
4389 /* compute the size of the row title area */
4390 if (sheet->row_titles_visible)
4391 requisition->width += sheet->row_title_area.width;
4396 psppire_sheet_size_allocate (GtkWidget *widget,
4397 GtkAllocation *allocation)
4399 PsppireSheet *sheet;
4400 GtkAllocation sheet_allocation;
4403 g_return_if_fail (widget != NULL);
4404 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4405 g_return_if_fail (allocation != NULL);
4407 sheet = PSPPIRE_SHEET (widget);
4408 widget->allocation = *allocation;
4409 border_width = GTK_CONTAINER (widget)->border_width;
4411 if (GTK_WIDGET_REALIZED (widget))
4412 gdk_window_move_resize (widget->window,
4413 allocation->x + border_width,
4414 allocation->y + border_width,
4415 allocation->width - 2 * border_width,
4416 allocation->height - 2 * border_width);
4418 sheet_allocation.x = 0;
4419 sheet_allocation.y = 0;
4420 sheet_allocation.width = allocation->width - 2 * border_width;
4421 sheet_allocation.height = allocation->height - 2 * border_width;
4423 if (GTK_WIDGET_REALIZED (widget))
4424 gdk_window_move_resize (sheet->sheet_window,
4427 sheet_allocation.width,
4428 sheet_allocation.height);
4430 /* position the window which holds the column title buttons */
4431 sheet->column_title_area.x = 0;
4432 sheet->column_title_area.y = 0;
4433 sheet->column_title_area.width = sheet_allocation.width ;
4436 /* position the window which holds the row title buttons */
4437 sheet->row_title_area.x = 0;
4438 sheet->row_title_area.y = 0;
4439 sheet->row_title_area.height = sheet_allocation.height;
4441 if (sheet->row_titles_visible)
4442 sheet->column_title_area.x += sheet->row_title_area.width;
4444 if (sheet->column_titles_visible)
4445 sheet->row_title_area.y += sheet->column_title_area.height;
4447 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4448 gdk_window_move_resize (sheet->column_title_window,
4449 sheet->column_title_area.x,
4450 sheet->column_title_area.y,
4451 sheet->column_title_area.width,
4452 sheet->column_title_area.height);
4455 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4456 gdk_window_move_resize (sheet->row_title_window,
4457 sheet->row_title_area.x,
4458 sheet->row_title_area.y,
4459 sheet->row_title_area.width,
4460 sheet->row_title_area.height);
4462 size_allocate_global_button (sheet);
4466 gint width = sheet->column_title_area.width;
4468 if ( sheet->row_titles_visible)
4469 width -= sheet->row_title_area.width;
4471 g_object_set (sheet->haxis,
4472 "minimum-extent", width,
4479 gint height = sheet->row_title_area.height;
4481 if ( sheet->column_titles_visible)
4482 height -= sheet->column_title_area.height;
4484 g_object_set (sheet->vaxis,
4485 "minimum-extent", height,
4490 /* set the scrollbars adjustments */
4491 adjust_scrollbars (sheet);
4495 draw_column_title_buttons (PsppireSheet *sheet)
4499 if (!sheet->column_titles_visible) return;
4500 if (!GTK_WIDGET_REALIZED (sheet))
4503 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4506 if (sheet->row_titles_visible)
4508 x = sheet->row_title_area.width;
4511 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4513 sheet->column_title_area.width = width;
4514 sheet->column_title_area.x = x;
4515 gdk_window_move_resize (sheet->column_title_window,
4516 sheet->column_title_area.x,
4517 sheet->column_title_area.y,
4518 sheet->column_title_area.width,
4519 sheet->column_title_area.height);
4522 if (max_visible_column (sheet) ==
4523 psppire_axis_unit_count (sheet->haxis) - 1)
4524 gdk_window_clear_area (sheet->column_title_window,
4526 sheet->column_title_area.width,
4527 sheet->column_title_area.height);
4529 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4531 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4532 max_visible_column (sheet));
4536 draw_row_title_buttons (PsppireSheet *sheet)
4541 if (!sheet->row_titles_visible) return;
4542 if (!GTK_WIDGET_REALIZED (sheet))
4545 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4547 if (sheet->column_titles_visible)
4549 y = sheet->column_title_area.height;
4552 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4554 sheet->row_title_area.y = y;
4555 sheet->row_title_area.height = height;
4556 gdk_window_move_resize (sheet->row_title_window,
4557 sheet->row_title_area.x,
4558 sheet->row_title_area.y,
4559 sheet->row_title_area.width,
4560 sheet->row_title_area.height);
4563 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4564 gdk_window_clear_area (sheet->row_title_window,
4566 sheet->row_title_area.width,
4567 sheet->row_title_area.height);
4569 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4571 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4572 max_visible_row (sheet));
4577 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4579 GtkAllocation entry_alloc;
4580 PsppireSheetCellAttr attributes = { 0 };
4581 GtkEntry *sheet_entry;
4583 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4584 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4586 sheet_entry = psppire_sheet_get_entry (sheet);
4588 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4589 sheet->active_cell.col,
4593 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4595 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4597 style->bg[GTK_STATE_NORMAL] = attributes.background;
4598 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4599 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4600 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4601 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4602 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4605 rectangle_from_cell (sheet, sheet->active_cell.row,
4606 sheet->active_cell.col, &entry_alloc);
4608 entry_alloc.width -= BORDER_WIDTH ;
4609 entry_alloc.height -= BORDER_WIDTH ;
4610 entry_alloc.x += DIV_RND_UP (BORDER_WIDTH, 2);
4611 entry_alloc.y += DIV_RND_UP (BORDER_WIDTH, 2);
4614 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4615 entry_alloc.height);
4616 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4620 /* Copy the sheet's font to the entry widget */
4622 set_entry_widget_font (PsppireSheet *sheet)
4624 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4626 pango_font_description_free (style->font_desc);
4627 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4629 gtk_widget_modify_style (sheet->entry_widget, style);
4633 create_sheet_entry (PsppireSheet *sheet)
4635 if (sheet->entry_widget)
4637 gtk_widget_unparent (sheet->entry_widget);
4640 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4641 g_object_ref_sink (sheet->entry_widget);
4643 gtk_widget_size_request (sheet->entry_widget, NULL);
4645 if ( GTK_IS_ENTRY (sheet->entry_widget))
4647 g_object_set (sheet->entry_widget,
4652 if (GTK_WIDGET_REALIZED (sheet))
4654 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4655 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4656 gtk_widget_realize (sheet->entry_widget);
4659 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4660 G_CALLBACK (psppire_sheet_entry_key_press),
4663 set_entry_widget_font (sheet);
4665 gtk_widget_show (sheet->entry_widget);
4669 /* Finds the last child widget that happens to be of type GtkEntry */
4671 find_entry (GtkWidget *w, gpointer user_data)
4673 GtkWidget **entry = user_data;
4674 if ( GTK_IS_ENTRY (w))
4682 psppire_sheet_get_entry (PsppireSheet *sheet)
4684 GtkWidget *w = sheet->entry_widget;
4686 g_return_val_if_fail (sheet != NULL, NULL);
4687 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4688 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4690 while (! GTK_IS_ENTRY (w))
4692 GtkWidget *entry = NULL;
4694 if (GTK_IS_CONTAINER (w))
4696 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4705 return GTK_ENTRY (w);
4710 draw_button (PsppireSheet *sheet, GdkWindow *window,
4711 PsppireSheetButton *button, gboolean is_sensitive,
4712 GdkRectangle allocation)
4714 GtkShadowType shadow_type;
4715 gint text_width = 0, text_height = 0;
4716 PangoAlignment align = PANGO_ALIGN_LEFT;
4722 g_return_if_fail (sheet != NULL);
4723 g_return_if_fail (button != NULL);
4726 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4728 gdk_window_clear_area (window,
4729 allocation.x, allocation.y,
4730 allocation.width, allocation.height);
4732 gtk_widget_ensure_style (sheet->button);
4734 gtk_paint_box (sheet->button->style, window,
4735 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4736 &allocation, GTK_WIDGET (sheet->button),
4738 allocation.x, allocation.y,
4739 allocation.width, allocation.height);
4741 state = button->state;
4742 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4744 if (state == GTK_STATE_ACTIVE)
4745 shadow_type = GTK_SHADOW_IN;
4747 shadow_type = GTK_SHADOW_OUT;
4749 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4750 gtk_paint_box (sheet->button->style, window,
4751 button->state, shadow_type,
4752 &allocation, GTK_WIDGET (sheet->button),
4754 allocation.x, allocation.y,
4755 allocation.width, allocation.height);
4757 if ( button->overstruck)
4759 GdkPoint points[2] = {
4760 {allocation.x, allocation.y},
4761 {allocation.x + allocation.width,
4762 allocation.y + allocation.height}
4765 gtk_paint_polygon (sheet->button->style,
4777 if (button->label_visible)
4779 text_height = DEFAULT_ROW_HEIGHT -
4780 2 * COLUMN_TITLES_HEIGHT;
4782 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4784 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4787 allocation.y += 2 * sheet->button->style->ythickness;
4789 if (button->label && strlen (button->label) > 0)
4791 PangoRectangle rect;
4792 gchar *line = button->label;
4794 PangoLayout *layout = NULL;
4795 gint real_x = allocation.x;
4796 gint real_y = allocation.y;
4798 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4799 pango_layout_get_extents (layout, NULL, &rect);
4801 text_width = PANGO_PIXELS (rect.width);
4802 switch (button->justification)
4804 case GTK_JUSTIFY_LEFT:
4805 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4806 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4808 case GTK_JUSTIFY_RIGHT:
4809 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4810 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4812 case GTK_JUSTIFY_CENTER:
4814 real_x = allocation.x + (allocation.width - text_width)/2;
4815 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4816 pango_layout_set_justify (layout, TRUE);
4818 pango_layout_set_alignment (layout, align);
4819 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4828 g_object_unref (layout);
4831 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4833 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4837 psppire_sheet_button_free (button);
4841 /* Draw the column title buttons FIRST through to LAST */
4843 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4847 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4849 if (!sheet->column_titles_visible) return;
4851 g_return_if_fail (first >= min_visible_column (sheet));
4852 g_return_if_fail (last <= max_visible_column (sheet));
4855 rect.height = sheet->column_title_area.height;
4856 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4857 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4858 + psppire_axis_unit_size (sheet->haxis, last);
4860 rect.x -= sheet->hadjustment->value;
4862 minimize_int (&rect.width, sheet->column_title_area.width);
4863 maximize_int (&rect.x, 0);
4865 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4867 for (col = first ; col <= last ; ++col)
4869 GdkRectangle allocation;
4870 gboolean is_sensitive = FALSE;
4872 PsppireSheetButton *
4873 button = psppire_sheet_model_get_column_button (sheet->model, col);
4875 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4877 allocation.x -= sheet->hadjustment->value;
4879 allocation.height = sheet->column_title_area.height;
4880 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4881 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4883 draw_button (sheet, sheet->column_title_window,
4884 button, is_sensitive, allocation);
4887 gdk_window_end_paint (sheet->column_title_window);
4892 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4896 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4898 if (!sheet->row_titles_visible) return;
4900 g_return_if_fail (first >= min_visible_row (sheet));
4901 g_return_if_fail (last <= max_visible_row (sheet));
4904 rect.width = sheet->row_title_area.width;
4905 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4906 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4907 + psppire_axis_unit_size (sheet->vaxis, last);
4909 rect.y -= sheet->vadjustment->value;
4911 minimize_int (&rect.height, sheet->row_title_area.height);
4912 maximize_int (&rect.y, 0);
4914 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4915 for (row = first; row <= last; ++row)
4917 GdkRectangle allocation;
4919 gboolean is_sensitive = FALSE;
4921 PsppireSheetButton *button =
4922 psppire_sheet_model_get_row_button (sheet->model, row);
4924 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4926 allocation.y -= sheet->vadjustment->value;
4928 allocation.width = sheet->row_title_area.width;
4929 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4930 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4932 draw_button (sheet, sheet->row_title_window,
4933 button, is_sensitive, allocation);
4936 gdk_window_end_paint (sheet->row_title_window);
4943 * vadjustment_value_changed
4944 * hadjustment_value_changed */
4948 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4951 (adj->value + adj->page_size)
4953 (adj->upper - adj->lower);
4955 const glong last_item = psppire_axis_unit_count (axis) - 1;
4957 if (isnan (position) || position < 0)
4961 psppire_axis_start_pixel (axis, last_item)
4963 psppire_axis_unit_size (axis, last_item)
4967 adj->page_size = page_size;
4970 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4972 if ( adj->value < adj->lower)
4973 adj->value = adj->lower;
4976 gtk_adjustment_changed (adj);
4981 adjust_scrollbars (PsppireSheet *sheet)
4985 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4988 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4990 if ( sheet->row_titles_visible)
4991 width -= sheet->row_title_area.width;
4993 if (sheet->column_titles_visible)
4994 height -= sheet->column_title_area.height;
4996 if (sheet->vadjustment)
4998 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
5000 sheet->vadjustment->step_increment =
5002 psppire_axis_unit_size (sheet->vaxis, last_row);
5004 sheet->vadjustment->page_increment =
5006 sheet->column_title_area.height -
5007 psppire_axis_unit_size (sheet->vaxis, last_row);
5009 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
5012 if (sheet->hadjustment)
5014 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
5015 sheet->hadjustment->step_increment = 1;
5017 sheet->hadjustment->page_increment = width;
5019 sheet->hadjustment->upper =
5020 psppire_axis_start_pixel (sheet->haxis, last_col)
5022 psppire_axis_unit_size (sheet->haxis, last_col)
5025 update_adjustment (sheet->hadjustment, sheet->haxis, width);
5029 /* Subtracts the region of WIDGET from REGION */
5031 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
5034 GdkRectangle intersect;
5037 gdk_region_get_clipbox (region, &rect);
5038 gtk_widget_intersect (widget,
5042 region2 = gdk_region_rectangle (&intersect);
5043 gdk_region_subtract (region, region2);
5044 gdk_region_destroy (region2);
5048 vadjustment_value_changed (GtkAdjustment *adjustment,
5052 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5054 g_return_if_fail (adjustment != NULL);
5056 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5058 gtk_widget_hide (sheet->entry_widget);
5061 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5063 subtract_widget_region (region, sheet->button);
5064 gdk_window_begin_paint_region (sheet->sheet_window, region);
5066 draw_sheet_region (sheet, region);
5068 draw_row_title_buttons (sheet);
5069 psppire_sheet_draw_active_cell (sheet);
5071 gdk_window_end_paint (sheet->sheet_window);
5072 gdk_region_destroy (region);
5077 hadjustment_value_changed (GtkAdjustment *adjustment,
5081 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5083 g_return_if_fail (adjustment != NULL);
5085 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5087 gtk_widget_hide (sheet->entry_widget);
5091 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5093 subtract_widget_region (region, sheet->button);
5094 gdk_window_begin_paint_region (sheet->sheet_window, region);
5096 draw_sheet_region (sheet, region);
5098 draw_column_title_buttons (sheet);
5100 psppire_sheet_draw_active_cell (sheet);
5102 gdk_window_end_paint (sheet->sheet_window);
5104 gdk_region_destroy (region);
5108 /* COLUMN RESIZING */
5110 draw_xor_vline (PsppireSheet *sheet)
5113 gint xpos = sheet->x_drag;
5114 gdk_drawable_get_size (sheet->sheet_window,
5117 if (sheet->row_titles_visible)
5118 xpos += sheet->row_title_area.width;
5120 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5122 sheet->column_title_area.height,
5124 height + CELL_SPACING);
5129 draw_xor_hline (PsppireSheet *sheet)
5133 gint ypos = sheet->y_drag;
5135 gdk_drawable_get_size (sheet->sheet_window,
5139 if (sheet->column_titles_visible)
5140 ypos += sheet->column_title_area.height;
5142 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5143 sheet->row_title_area.width,
5145 width + CELL_SPACING,
5149 /* SELECTED RANGE */
5151 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
5154 GdkRectangle clip_area, area;
5157 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
5158 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
5159 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
5160 psppire_axis_unit_size (sheet->haxis, range.coli);
5161 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
5162 psppire_axis_unit_size (sheet->vaxis, range.rowi);
5164 clip_area.x = sheet->row_title_area.width;
5165 clip_area.y = sheet->column_title_area.height;
5167 gdk_drawable_get_size (sheet->sheet_window,
5168 &clip_area.width, &clip_area.height);
5170 if (!sheet->row_titles_visible) clip_area.x = 0;
5171 if (!sheet->column_titles_visible) clip_area.y = 0;
5175 area.width = area.width + area.x;
5178 if (area.width > clip_area.width) area.width = clip_area.width + 10;
5181 area.height = area.height + area.y;
5184 if (area.height > clip_area.height) area.height = clip_area.height + 10;
5188 clip_area.width += 3;
5189 clip_area.height += 3;
5191 gdk_gc_get_values (sheet->xor_gc, &values);
5193 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5195 gdk_draw_rectangle (sheet->sheet_window,
5198 area.x + i, area.y + i,
5199 area.width - 2 * i, area.height - 2 * i);
5202 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5204 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5209 set_column_width (PsppireSheet *sheet,
5213 g_return_if_fail (sheet != NULL);
5214 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5216 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
5222 psppire_axis_resize (sheet->haxis, column, width);
5224 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5226 draw_column_title_buttons (sheet);
5227 adjust_scrollbars (sheet);
5228 psppire_sheet_size_allocate_entry (sheet);
5229 redraw_range (sheet, NULL);
5234 set_row_height (PsppireSheet *sheet,
5238 g_return_if_fail (sheet != NULL);
5239 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5241 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
5247 psppire_axis_resize (sheet->vaxis, row, height);
5249 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
5251 draw_row_title_buttons (sheet);
5252 adjust_scrollbars (sheet);
5253 psppire_sheet_size_allocate_entry (sheet);
5254 redraw_range (sheet, NULL);
5259 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
5260 PsppireSheetCellAttr *attr)
5263 const GtkJustification *j ;
5264 GdkColormap *colormap;
5266 g_return_val_if_fail (sheet != NULL, FALSE);
5267 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
5269 if (row < 0 || col < 0) return FALSE;
5271 attr->foreground = GTK_WIDGET (sheet)->style->black;
5272 attr->background = sheet->color[BG_COLOR];
5274 attr->border.width = 0;
5275 attr->border.line_style = GDK_LINE_SOLID;
5276 attr->border.cap_style = GDK_CAP_NOT_LAST;
5277 attr->border.join_style = GDK_JOIN_MITER;
5278 attr->border.mask = 0;
5279 attr->border.color = GTK_WIDGET (sheet)->style->black;
5281 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
5282 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
5285 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
5286 attr->foreground = *fg;
5289 bg = psppire_sheet_model_get_background (sheet->model, row, col);
5292 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
5293 attr->background = *bg;
5296 attr->justification =
5297 psppire_sheet_model_get_column_justification (sheet->model, col);
5299 j = psppire_sheet_model_get_justification (sheet->model, row, col);
5301 attr->justification = *j;
5307 psppire_sheet_button_size_request (PsppireSheet *sheet,
5308 const PsppireSheetButton *button,
5309 GtkRequisition *button_requisition)
5311 GtkRequisition requisition;
5312 GtkRequisition label_requisition;
5314 label_requisition.height = DEFAULT_ROW_HEIGHT;
5315 label_requisition.width = COLUMN_MIN_WIDTH;
5317 requisition.height = DEFAULT_ROW_HEIGHT;
5318 requisition.width = COLUMN_MIN_WIDTH;
5321 *button_requisition = requisition;
5322 button_requisition->width = MAX (requisition.width, label_requisition.width);
5323 button_requisition->height = MAX (requisition.height, label_requisition.height);
5328 psppire_sheet_forall (GtkContainer *container,
5329 gboolean include_internals,
5330 GtkCallback callback,
5331 gpointer callback_data)
5333 PsppireSheet *sheet = PSPPIRE_SHEET (container);
5335 g_return_if_fail (callback != NULL);
5337 if (sheet->button && sheet->button->parent)
5338 (* callback) (sheet->button, callback_data);
5340 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
5341 (* callback) (sheet->entry_widget, callback_data);
5346 psppire_sheet_get_model (const PsppireSheet *sheet)
5348 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
5350 return sheet->model;
5354 PsppireSheetButton *
5355 psppire_sheet_button_new (void)
5357 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
5359 button->state = GTK_STATE_NORMAL;
5360 button->label = NULL;
5361 button->label_visible = TRUE;
5362 button->justification = GTK_JUSTIFY_FILL;
5363 button->overstruck = FALSE;
5370 psppire_sheet_button_free (PsppireSheetButton *button)
5372 if (!button) return ;
5374 g_free (button->label);
5379 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5381 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5383 if ( NULL == celltext)
5386 g_string_append (string, celltext);
5392 range_to_text (const PsppireSheet *sheet)
5397 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5400 string = g_string_sized_new (80);
5402 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5404 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5406 append_cell_text (string, sheet, r, c);
5407 g_string_append (string, "\t");
5409 append_cell_text (string, sheet, r, c);
5410 if ( r < sheet->range.rowi)
5411 g_string_append (string, "\n");
5418 range_to_html (const PsppireSheet *sheet)
5423 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5426 string = g_string_sized_new (480);
5428 g_string_append (string, "<html>\n");
5429 g_string_append (string, "<body>\n");
5430 g_string_append (string, "<table>\n");
5431 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5433 g_string_append (string, "<tr>\n");
5434 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5436 g_string_append (string, "<td>");
5437 append_cell_text (string, sheet, r, c);
5438 g_string_append (string, "</td>\n");
5440 g_string_append (string, "</tr>\n");
5442 g_string_append (string, "</table>\n");
5443 g_string_append (string, "</body>\n");
5444 g_string_append (string, "</html>\n");
5456 primary_get_cb (GtkClipboard *clipboard,
5457 GtkSelectionData *selection_data,
5461 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5462 GString *string = NULL;
5466 case SELECT_FMT_TEXT:
5467 string = range_to_text (sheet);
5469 case SELECT_FMT_HTML:
5470 string = range_to_html (sheet);
5473 g_assert_not_reached ();
5476 gtk_selection_data_set (selection_data, selection_data->target,
5478 (const guchar *) string->str, string->len);
5479 g_string_free (string, TRUE);
5483 primary_clear_cb (GtkClipboard *clipboard,
5486 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5487 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5490 psppire_sheet_real_unselect_range (sheet, NULL);
5494 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5496 static const GtkTargetEntry targets[] = {
5497 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5498 { "STRING", 0, SELECT_FMT_TEXT },
5499 { "TEXT", 0, SELECT_FMT_TEXT },
5500 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5501 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5502 { "text/plain", 0, SELECT_FMT_TEXT },
5503 { "text/html", 0, SELECT_FMT_HTML }
5506 GtkClipboard *clipboard;
5508 if (!GTK_WIDGET_REALIZED (sheet))
5511 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5512 GDK_SELECTION_PRIMARY);
5514 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5516 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5517 G_N_ELEMENTS (targets),
5518 primary_get_cb, primary_clear_cb,
5520 primary_clear_cb (clipboard, sheet);
5524 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5525 gtk_clipboard_clear (clipboard);