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,
486 static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
487 const PsppireSheetRange *range);
488 static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet,
489 gint row, gint column);
490 /* Drawing Routines */
493 static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column);
496 /* draw visible part of range. */
497 static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region);
500 /* highlight the visible part of the selected range */
501 static void psppire_sheet_range_draw_selection (PsppireSheet *sheet,
502 PsppireSheetRange range);
506 static void psppire_sheet_real_select_range (PsppireSheet *sheet,
507 const PsppireSheetRange *range);
508 static void psppire_sheet_real_unselect_range (PsppireSheet *sheet,
509 const PsppireSheetRange *range);
510 static void psppire_sheet_extend_selection (PsppireSheet *sheet,
511 gint row, gint column);
512 static void psppire_sheet_new_selection (PsppireSheet *sheet,
513 PsppireSheetRange *range);
514 static void psppire_sheet_draw_border (PsppireSheet *sheet,
515 PsppireSheetRange range);
517 /* Active Cell handling */
519 static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet);
520 static void change_active_cell (PsppireSheet *sheet, gint row, gint col);
521 static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet);
522 static void psppire_sheet_show_entry_widget (PsppireSheet *sheet);
523 static gboolean psppire_sheet_click_cell (PsppireSheet *sheet,
530 static void adjust_scrollbars (PsppireSheet *sheet);
531 static void vadjustment_value_changed (GtkAdjustment *adjustment,
533 static void hadjustment_value_changed (GtkAdjustment *adjustment,
537 static void draw_xor_vline (PsppireSheet *sheet);
538 static void draw_xor_hline (PsppireSheet *sheet);
539 static void draw_xor_rectangle (PsppireSheet *sheet,
540 PsppireSheetRange range);
544 static void create_global_button (PsppireSheet *sheet);
545 static void global_button_clicked (GtkWidget *widget,
549 static void create_sheet_entry (PsppireSheet *sheet);
550 static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet);
552 /* Sheet button gadgets */
554 static void draw_column_title_buttons (PsppireSheet *sheet);
555 static void draw_row_title_buttons (PsppireSheet *sheet);
558 static void size_allocate_global_button (PsppireSheet *sheet);
559 static void psppire_sheet_button_size_request (PsppireSheet *sheet,
560 const PsppireSheetButton *button,
561 GtkRequisition *requisition);
563 static void psppire_sheet_real_cell_clear (PsppireSheet *sheet,
585 static GtkContainerClass *parent_class = NULL;
586 static guint sheet_signals[LAST_SIGNAL] = { 0 };
590 psppire_sheet_get_type ()
592 static GType sheet_type = 0;
596 static const GTypeInfo sheet_info =
598 sizeof (PsppireSheetClass),
601 (GClassInitFunc) psppire_sheet_class_init,
604 sizeof (PsppireSheet),
606 (GInstanceInitFunc) psppire_sheet_init,
611 g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
619 static PsppireSheetRange*
620 psppire_sheet_range_copy (const PsppireSheetRange *range)
622 PsppireSheetRange *new_range;
624 g_return_val_if_fail (range != NULL, NULL);
626 new_range = g_new (PsppireSheetRange, 1);
634 psppire_sheet_range_free (PsppireSheetRange *range)
636 g_return_if_fail (range != NULL);
642 psppire_sheet_range_get_type (void)
644 static GType sheet_range_type = 0;
646 if (!sheet_range_type)
649 g_boxed_type_register_static ("PsppireSheetRange",
650 (GBoxedCopyFunc) psppire_sheet_range_copy,
651 (GBoxedFreeFunc) psppire_sheet_range_free);
654 return sheet_range_type;
657 static PsppireSheetCell*
658 psppire_sheet_cell_copy (const PsppireSheetCell *cell)
660 PsppireSheetCell *new_cell;
662 g_return_val_if_fail (cell != NULL, NULL);
664 new_cell = g_new (PsppireSheetCell, 1);
672 psppire_sheet_cell_free (PsppireSheetCell *cell)
674 g_return_if_fail (cell != NULL);
680 psppire_sheet_cell_get_type (void)
682 static GType sheet_cell_type = 0;
684 if (!sheet_cell_type)
687 g_boxed_type_register_static ("PsppireSheetCell",
688 (GBoxedCopyFunc) psppire_sheet_cell_copy,
689 (GBoxedFreeFunc) psppire_sheet_cell_free);
692 return sheet_cell_type;
706 resize_column (PsppireSheet *sheet, gint unit, glong size)
708 PsppireSheetRange range;
710 range.coli = max_visible_column (sheet);
711 range.row0 = min_visible_row (sheet);
712 range.rowi = max_visible_row (sheet);
714 redraw_range (sheet, &range);
716 draw_column_title_buttons_range (sheet, range.col0, range.coli);
721 psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
724 g_object_unref (sheet->haxis);
727 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
730 g_object_ref (sheet->haxis);
734 resize_row (PsppireSheet *sheet, gint unit, glong size)
736 PsppireSheetRange range;
737 range.col0 = min_visible_column (sheet);
738 range.coli = max_visible_column (sheet);
740 range.rowi = max_visible_row (sheet);
742 redraw_range (sheet, &range);
744 draw_row_title_buttons_range (sheet, range.row0, range.rowi);
748 psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
751 g_object_unref (sheet->vaxis);
755 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
758 g_object_ref (sheet->vaxis);
763 psppire_sheet_set_property (GObject *object,
769 PsppireSheet *sheet = PSPPIRE_SHEET (object);
774 psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
777 psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
780 psppire_sheet_set_model (sheet, g_value_get_pointer (value));
783 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
789 psppire_sheet_get_property (GObject *object,
794 PsppireSheet *sheet = PSPPIRE_SHEET (object);
799 g_value_set_pointer (value, sheet->vaxis);
802 g_value_set_pointer (value, sheet->haxis);
805 g_value_set_pointer (value, sheet->model);
808 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
815 psppire_sheet_class_init (PsppireSheetClass *klass)
817 GObjectClass *object_class = G_OBJECT_CLASS (klass);
819 GParamSpec *haxis_spec ;
820 GParamSpec *vaxis_spec ;
821 GParamSpec *model_spec ;
823 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
824 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
826 parent_class = g_type_class_peek_parent (klass);
829 * PsppireSheet::select-row
830 * @sheet: the sheet widget that emitted the signal
831 * @row: the newly selected row index
833 * A row has been selected.
835 sheet_signals[SELECT_ROW] =
836 g_signal_new ("select-row",
837 G_TYPE_FROM_CLASS (object_class),
839 offsetof (PsppireSheetClass, select_row),
841 g_cclosure_marshal_VOID__INT,
848 * PsppireSheet::select - column
849 * @sheet: the sheet widget that emitted the signal
850 * @column: the newly selected column index
852 * A column has been selected.
854 sheet_signals[SELECT_COLUMN] =
855 g_signal_new ("select-column",
856 G_TYPE_FROM_CLASS (object_class),
858 offsetof (PsppireSheetClass, select_column),
860 g_cclosure_marshal_VOID__INT,
867 * PsppireSheet::double-click-row
868 * @sheet: the sheet widget that emitted the signal
869 * @row: the row that was double clicked.
871 * A row's title button has been double clicked
873 sheet_signals[DOUBLE_CLICK_ROW] =
874 g_signal_new ("double-click-row",
875 G_TYPE_FROM_CLASS (object_class),
879 g_cclosure_marshal_VOID__INT,
886 * PsppireSheet::double-click-column
887 * @sheet: the sheet widget that emitted the signal
888 * @column: the column that was double clicked.
890 * A column's title button has been double clicked
892 sheet_signals[DOUBLE_CLICK_COLUMN] =
893 g_signal_new ("double-click-column",
894 G_TYPE_FROM_CLASS (object_class),
898 g_cclosure_marshal_VOID__INT,
905 * PsppireSheet::button-event-column
906 * @sheet: the sheet widget that emitted the signal
907 * @column: the column on which the event occured.
909 * A button event occured on a column title button
911 sheet_signals[BUTTON_EVENT_COLUMN] =
912 g_signal_new ("button-event-column",
913 G_TYPE_FROM_CLASS (object_class),
917 psppire_marshal_VOID__INT_POINTER,
926 * PsppireSheet::button-event-row
927 * @sheet: the sheet widget that emitted the signal
928 * @column: the column on which the event occured.
930 * A button event occured on a row title button
932 sheet_signals[BUTTON_EVENT_ROW] =
933 g_signal_new ("button-event-row",
934 G_TYPE_FROM_CLASS (object_class),
938 psppire_marshal_VOID__INT_POINTER,
946 sheet_signals[SELECT_RANGE] =
947 g_signal_new ("select-range",
948 G_TYPE_FROM_CLASS (object_class),
950 offsetof (PsppireSheetClass, select_range),
952 g_cclosure_marshal_VOID__BOXED,
955 PSPPIRE_TYPE_SHEET_RANGE);
958 sheet_signals[RESIZE_RANGE] =
959 g_signal_new ("resize-range",
960 G_TYPE_FROM_CLASS (object_class),
962 offsetof (PsppireSheetClass, resize_range),
964 psppire_marshal_VOID__BOXED_BOXED,
967 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
970 sheet_signals[MOVE_RANGE] =
971 g_signal_new ("move-range",
972 G_TYPE_FROM_CLASS (object_class),
974 offsetof (PsppireSheetClass, move_range),
976 psppire_marshal_VOID__BOXED_BOXED,
979 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
982 sheet_signals[TRAVERSE] =
983 g_signal_new ("traverse",
984 G_TYPE_FROM_CLASS (object_class),
986 offsetof (PsppireSheetClass, traverse),
988 psppire_marshal_BOOLEAN__BOXED_POINTER,
990 PSPPIRE_TYPE_SHEET_CELL,
994 sheet_signals[ACTIVATE] =
995 g_signal_new ("activate",
996 G_TYPE_FROM_CLASS (object_class),
998 offsetof (PsppireSheetClass, activate),
1000 psppire_marshal_VOID__INT_INT_INT_INT,
1002 G_TYPE_INT, G_TYPE_INT,
1003 G_TYPE_INT, G_TYPE_INT);
1005 widget_class->set_scroll_adjustments_signal =
1006 g_signal_new ("set-scroll-adjustments",
1007 G_TYPE_FROM_CLASS (object_class),
1009 offsetof (PsppireSheetClass, set_scroll_adjustments),
1011 psppire_marshal_VOID__OBJECT_OBJECT,
1012 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1015 container_class->add = NULL;
1016 container_class->remove = NULL;
1017 container_class->forall = psppire_sheet_forall;
1019 object_class->dispose = psppire_sheet_dispose;
1020 object_class->finalize = psppire_sheet_finalize;
1024 g_param_spec_pointer ("vertical-axis",
1026 "A pointer to the PsppireAxis object for the rows",
1027 G_PARAM_READABLE | G_PARAM_WRITABLE );
1030 g_param_spec_pointer ("horizontal-axis",
1032 "A pointer to the PsppireAxis object for the columns",
1033 G_PARAM_READABLE | G_PARAM_WRITABLE );
1036 g_param_spec_pointer ("model",
1038 "A pointer to the data model",
1039 G_PARAM_READABLE | G_PARAM_WRITABLE );
1042 object_class->set_property = psppire_sheet_set_property;
1043 object_class->get_property = psppire_sheet_get_property;
1045 g_object_class_install_property (object_class,
1049 g_object_class_install_property (object_class,
1053 g_object_class_install_property (object_class,
1058 widget_class->realize = psppire_sheet_realize;
1059 widget_class->unrealize = psppire_sheet_unrealize;
1060 widget_class->map = psppire_sheet_map;
1061 widget_class->unmap = psppire_sheet_unmap;
1062 widget_class->style_set = psppire_sheet_style_set;
1063 widget_class->button_press_event = psppire_sheet_button_press;
1064 widget_class->button_release_event = psppire_sheet_button_release;
1065 widget_class->motion_notify_event = psppire_sheet_motion;
1066 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1067 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1068 widget_class->key_press_event = psppire_sheet_key_press;
1069 widget_class->expose_event = psppire_sheet_expose;
1070 widget_class->size_request = psppire_sheet_size_request;
1071 widget_class->size_allocate = psppire_sheet_size_allocate;
1072 widget_class->focus_in_event = psppire_sheet_focus_in;
1073 widget_class->focus_out_event = NULL;
1075 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1076 klass->select_row = NULL;
1077 klass->select_column = NULL;
1078 klass->select_range = NULL;
1079 klass->resize_range = NULL;
1080 klass->move_range = NULL;
1081 klass->traverse = NULL;
1082 klass->activate = NULL;
1083 klass->changed = NULL;
1087 psppire_sheet_init (PsppireSheet *sheet)
1089 sheet->model = NULL;
1090 sheet->haxis = NULL;
1091 sheet->vaxis = NULL;
1094 sheet->selection_mode = GTK_SELECTION_NONE;
1095 sheet->state = PSPPIRE_SHEET_NORMAL;
1097 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1098 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1100 sheet->column_title_window = NULL;
1101 sheet->column_title_area.x = 0;
1102 sheet->column_title_area.y = 0;
1103 sheet->column_title_area.width = 0;
1104 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1106 sheet->row_title_window = NULL;
1107 sheet->row_title_area.x = 0;
1108 sheet->row_title_area.y = 0;
1109 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1110 sheet->row_title_area.height = 0;
1113 sheet->active_cell.row = 0;
1114 sheet->active_cell.col = 0;
1115 sheet->selection_cell.row = 0;
1116 sheet->selection_cell.col = 0;
1118 sheet->range.row0 = 0;
1119 sheet->range.rowi = 0;
1120 sheet->range.col0 = 0;
1121 sheet->range.coli = 0;
1123 sheet->state = PSPPIRE_SHEET_NORMAL;
1125 sheet->sheet_window = NULL;
1126 sheet->entry_widget = NULL;
1127 sheet->button = NULL;
1129 sheet->hadjustment = NULL;
1130 sheet->vadjustment = NULL;
1132 sheet->cursor_drag = NULL;
1134 sheet->xor_gc = NULL;
1135 sheet->fg_gc = NULL;
1136 sheet->bg_gc = NULL;
1139 sheet->show_grid = TRUE;
1141 sheet->motion_timer = 0;
1143 sheet->row_titles_visible = TRUE;
1144 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1146 sheet->column_titles_visible = TRUE;
1149 /* create sheet entry */
1150 sheet->entry_type = GTK_TYPE_ENTRY;
1151 create_sheet_entry (sheet);
1153 /* create global selection button */
1154 create_global_button (sheet);
1158 /* Cause RANGE to be redrawn. If RANGE is null, then the
1159 entire visible range will be redrawn.
1162 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1166 if ( ! GTK_WIDGET_REALIZED (sheet))
1169 if ( NULL != range )
1170 rectangle_from_range (sheet, range, &rect);
1173 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1174 gdk_region_get_clipbox (r, &rect);
1176 if ( sheet->column_titles_visible)
1178 rect.y += sheet->column_title_area.height;
1179 rect.height -= sheet->column_title_area.height;
1182 if ( sheet->row_titles_visible)
1184 rect.x += sheet->row_title_area.width;
1185 rect.width -= sheet->row_title_area.width;
1189 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1193 /* Callback which occurs whenever columns are inserted / deleted in the model */
1195 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1199 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1201 PsppireSheetRange range;
1202 gint model_columns = psppire_sheet_model_get_column_count (model);
1205 /* Need to update all the columns starting from the first column and onwards.
1206 * Previous column are unchanged, so don't need to be updated.
1208 range.col0 = first_column;
1210 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1211 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1213 adjust_scrollbars (sheet);
1215 if (sheet->active_cell.col >= model_columns)
1216 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1218 draw_column_title_buttons_range (sheet,
1219 first_column, max_visible_column (sheet));
1222 redraw_range (sheet, &range);
1228 /* Callback which occurs whenever rows are inserted / deleted in the model */
1230 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1231 gint n_rows, gpointer data)
1233 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1235 PsppireSheetRange range;
1237 gint model_rows = psppire_sheet_model_get_row_count (model);
1239 /* Need to update all the rows starting from the first row and onwards.
1240 * Previous rows are unchanged, so don't need to be updated.
1242 range.row0 = first_row;
1244 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1245 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1247 adjust_scrollbars (sheet);
1249 if (sheet->active_cell.row >= model_rows)
1250 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1252 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1254 redraw_range (sheet, &range);
1258 If row0 or rowi are negative, then all rows will be updated.
1259 If col0 or coli are negative, then all columns will be updated.
1262 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1263 gint rowi, gint coli, gpointer data)
1265 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1267 PsppireSheetRange range;
1274 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1277 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1279 redraw_range (sheet, NULL);
1280 adjust_scrollbars (sheet);
1282 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1283 max_visible_row (sheet));
1285 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1286 max_visible_column (sheet));
1290 else if ( row0 < 0 || rowi < 0 )
1292 range.row0 = min_visible_row (sheet);
1293 range.rowi = max_visible_row (sheet);
1295 else if ( col0 < 0 || coli < 0 )
1297 range.col0 = min_visible_column (sheet);
1298 range.coli = max_visible_column (sheet);
1301 redraw_range (sheet, &range);
1306 * psppire_sheet_new:
1307 * @rows: initial number of rows
1308 * @columns: initial number of columns
1309 * @title: sheet title
1310 * @model: the model to use for the sheet data
1312 * Creates a new sheet widget with the given number of rows and columns.
1314 * Returns: the new sheet widget
1317 psppire_sheet_new (PsppireSheetModel *model)
1319 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1327 * psppire_sheet_set_model
1328 * @sheet: the sheet to set the model for
1329 * @model: the model to use for the sheet data
1331 * Sets the model for a PsppireSheet
1335 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1337 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1339 if (sheet->model ) g_object_unref (sheet->model);
1341 sheet->model = model;
1345 g_object_ref (model);
1347 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1348 G_CALLBACK (range_update_callback),
1351 g_signal_connect (model, "rows_inserted",
1352 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1354 g_signal_connect (model, "rows_deleted",
1355 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1357 g_signal_connect (model, "columns_inserted",
1358 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1360 g_signal_connect (model, "columns_deleted",
1361 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1367 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1371 g_return_if_fail (sheet != NULL);
1372 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1374 state = sheet->state;
1376 if (sheet->state == PSPPIRE_SHEET_NORMAL)
1377 psppire_sheet_hide_entry_widget (sheet);
1379 sheet->entry_type = entry_type;
1381 create_sheet_entry (sheet);
1383 if (state == PSPPIRE_SHEET_NORMAL)
1385 psppire_sheet_show_entry_widget (sheet);
1391 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1393 g_return_if_fail (sheet != NULL);
1394 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1396 if (show == sheet->show_grid) return;
1398 sheet->show_grid = show;
1400 redraw_range (sheet, NULL);
1404 psppire_sheet_grid_visible (PsppireSheet *sheet)
1406 g_return_val_if_fail (sheet != NULL, 0);
1407 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1409 return sheet->show_grid;
1413 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1415 g_return_val_if_fail (sheet != NULL, 0);
1416 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1418 return psppire_axis_unit_count (sheet->haxis);
1421 static void set_column_width (PsppireSheet *sheet,
1427 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1429 if (sheet->column_titles_visible) return;
1431 sheet->column_titles_visible = TRUE;
1433 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1436 gdk_window_show (sheet->column_title_window);
1437 gdk_window_move_resize (sheet->column_title_window,
1438 sheet->column_title_area.x,
1439 sheet->column_title_area.y,
1440 sheet->column_title_area.width,
1441 sheet->column_title_area.height);
1443 adjust_scrollbars (sheet);
1445 if (sheet->vadjustment)
1446 g_signal_emit_by_name (sheet->vadjustment,
1449 size_allocate_global_button (sheet);
1451 if ( sheet->row_titles_visible)
1452 gtk_widget_show (sheet->button);
1457 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1459 if (sheet->row_titles_visible) return;
1461 sheet->row_titles_visible = TRUE;
1464 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1466 gdk_window_show (sheet->row_title_window);
1467 gdk_window_move_resize (sheet->row_title_window,
1468 sheet->row_title_area.x,
1469 sheet->row_title_area.y,
1470 sheet->row_title_area.width,
1471 sheet->row_title_area.height);
1473 adjust_scrollbars (sheet);
1476 if (sheet->hadjustment)
1477 g_signal_emit_by_name (sheet->hadjustment,
1480 size_allocate_global_button (sheet);
1482 if ( sheet->column_titles_visible)
1483 gtk_widget_show (sheet->button);
1487 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1489 if (!sheet->column_titles_visible) return;
1491 sheet->column_titles_visible = FALSE;
1493 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1495 if (sheet->column_title_window)
1496 gdk_window_hide (sheet->column_title_window);
1498 gtk_widget_hide (sheet->button);
1500 adjust_scrollbars (sheet);
1503 if (sheet->vadjustment)
1504 g_signal_emit_by_name (sheet->vadjustment,
1509 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1511 if (!sheet->row_titles_visible) return;
1513 sheet->row_titles_visible = FALSE;
1515 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1517 if (sheet->row_title_window)
1518 gdk_window_hide (sheet->row_title_window);
1520 gtk_widget_hide (sheet->button);
1522 adjust_scrollbars (sheet);
1525 if (sheet->hadjustment)
1526 g_signal_emit_by_name (sheet->hadjustment,
1531 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1532 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1533 at the {top,left} of the sheet. If it's 1, then it'll
1534 be placed at the {bottom,right}.
1535 ROW or COL may be -1, in which case scrolling in that dimension
1539 psppire_sheet_moveto (PsppireSheet *sheet,
1547 g_return_if_fail (row_align >= 0);
1548 g_return_if_fail (col_align >= 0);
1550 g_return_if_fail (row_align <= 1);
1551 g_return_if_fail (col_align <= 1);
1553 g_return_if_fail (col <
1554 psppire_axis_unit_count (sheet->haxis));
1555 g_return_if_fail (row <
1556 psppire_axis_unit_count (sheet->vaxis));
1558 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1563 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1565 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1571 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1573 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1579 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
1581 g_return_if_fail (sheet != NULL);
1582 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1584 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
1587 if (sheet->state != PSPPIRE_SHEET_NORMAL)
1588 psppire_sheet_real_unselect_range (sheet, NULL);
1590 sheet->state = PSPPIRE_SHEET_ROW_SELECTED;
1591 sheet->range.row0 = row;
1592 sheet->range.col0 = 0;
1593 sheet->range.rowi = row;
1594 sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1595 sheet->active_cell.row = row;
1597 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
1598 psppire_sheet_real_select_range (sheet, NULL);
1603 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
1605 g_return_if_fail (sheet != NULL);
1606 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1608 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
1611 if (sheet->state != PSPPIRE_SHEET_NORMAL)
1612 psppire_sheet_real_unselect_range (sheet, NULL);
1614 sheet->state = PSPPIRE_SHEET_COLUMN_SELECTED;
1615 sheet->range.row0 = 0;
1616 sheet->range.col0 = column;
1617 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1618 sheet->range.coli = column;
1619 sheet->active_cell.col = column;
1621 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
1622 psppire_sheet_real_select_range (sheet, NULL);
1629 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1630 const PsppireSheetRange *range)
1632 g_return_val_if_fail (sheet != NULL, FALSE);
1634 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1637 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1640 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1643 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1646 if (range->rowi < min_visible_row (sheet))
1649 if (range->row0 > max_visible_row (sheet))
1652 if (range->coli < min_visible_column (sheet))
1655 if (range->col0 > max_visible_column (sheet))
1662 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1663 gint row, gint column)
1665 PsppireSheetRange range;
1668 range.col0 = column;
1670 range.coli = column;
1672 return psppire_sheet_range_isvisible (sheet, &range);
1676 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1678 g_return_if_fail (sheet != NULL);
1679 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1680 g_return_if_fail (range != NULL);
1682 range->row0 = min_visible_row (sheet);
1683 range->col0 = min_visible_column (sheet);
1684 range->rowi = max_visible_row (sheet);
1685 range->coli = max_visible_column (sheet);
1690 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1691 GtkAdjustment *hadjustment,
1692 GtkAdjustment *vadjustment)
1694 if ( sheet->vadjustment != vadjustment )
1696 if (sheet->vadjustment)
1697 g_object_unref (sheet->vadjustment);
1698 sheet->vadjustment = vadjustment;
1702 g_object_ref (vadjustment);
1704 g_signal_connect (sheet->vadjustment, "value_changed",
1705 G_CALLBACK (vadjustment_value_changed),
1710 if ( sheet->hadjustment != hadjustment )
1712 if (sheet->hadjustment)
1713 g_object_unref (sheet->hadjustment);
1715 sheet->hadjustment = hadjustment;
1719 g_object_ref (hadjustment);
1721 g_signal_connect (sheet->hadjustment, "value_changed",
1722 G_CALLBACK (hadjustment_value_changed),
1730 psppire_sheet_finalize (GObject *object)
1732 PsppireSheet *sheet;
1734 g_return_if_fail (object != NULL);
1735 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1737 sheet = PSPPIRE_SHEET (object);
1739 if (G_OBJECT_CLASS (parent_class)->finalize)
1740 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1744 psppire_sheet_dispose (GObject *object)
1746 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1748 g_return_if_fail (object != NULL);
1749 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1751 if ( sheet->dispose_has_run )
1754 sheet->dispose_has_run = TRUE;
1756 if (sheet->model) g_object_unref (sheet->model);
1757 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1758 if (sheet->haxis) g_object_unref (sheet->haxis);
1760 g_object_unref (sheet->button);
1761 sheet->button = NULL;
1763 /* unref adjustments */
1764 if (sheet->hadjustment)
1766 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1767 G_SIGNAL_MATCH_DATA,
1771 g_object_unref (sheet->hadjustment);
1772 sheet->hadjustment = NULL;
1775 if (sheet->vadjustment)
1777 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1778 G_SIGNAL_MATCH_DATA,
1782 g_object_unref (sheet->vadjustment);
1784 sheet->vadjustment = NULL;
1787 if (G_OBJECT_CLASS (parent_class)->dispose)
1788 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1792 psppire_sheet_style_set (GtkWidget *widget,
1793 GtkStyle *previous_style)
1795 PsppireSheet *sheet;
1797 g_return_if_fail (widget != NULL);
1798 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1800 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1801 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1803 sheet = PSPPIRE_SHEET (widget);
1805 if (GTK_WIDGET_REALIZED (widget))
1807 gtk_style_set_background (widget->style, widget->window, widget->state);
1810 set_entry_widget_font (sheet);
1813 #define BORDER_WIDTH 3
1816 psppire_sheet_realize (GtkWidget *widget)
1818 PsppireSheet *sheet;
1819 GdkWindowAttr attributes;
1820 const gint attributes_mask =
1821 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1824 GdkColormap *colormap;
1825 GdkDisplay *display;
1827 g_return_if_fail (widget != NULL);
1828 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1830 sheet = PSPPIRE_SHEET (widget);
1832 colormap = gtk_widget_get_colormap (widget);
1833 display = gtk_widget_get_display (widget);
1835 attributes.window_type = GDK_WINDOW_CHILD;
1836 attributes.x = widget->allocation.x;
1837 attributes.y = widget->allocation.y;
1838 attributes.width = widget->allocation.width;
1839 attributes.height = widget->allocation.height;
1840 attributes.wclass = GDK_INPUT_OUTPUT;
1842 attributes.visual = gtk_widget_get_visual (widget);
1843 attributes.colormap = colormap;
1845 attributes.event_mask = gtk_widget_get_events (widget);
1846 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1847 GDK_BUTTON_PRESS_MASK |
1848 GDK_BUTTON_RELEASE_MASK |
1849 GDK_KEY_PRESS_MASK |
1850 GDK_ENTER_NOTIFY_MASK |
1851 GDK_LEAVE_NOTIFY_MASK |
1852 GDK_POINTER_MOTION_MASK |
1853 GDK_POINTER_MOTION_HINT_MASK);
1855 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1858 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1860 gdk_window_set_user_data (widget->window, sheet);
1862 widget->style = gtk_style_attach (widget->style, widget->window);
1864 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1866 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1867 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1869 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1870 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1875 attributes.width = sheet->column_title_area.width;
1876 attributes.height = sheet->column_title_area.height;
1879 /* column - title window */
1880 sheet->column_title_window =
1881 gdk_window_new (widget->window, &attributes, attributes_mask);
1882 gdk_window_set_user_data (sheet->column_title_window, sheet);
1883 gtk_style_set_background (widget->style, sheet->column_title_window,
1889 attributes.width = sheet->row_title_area.width;
1890 attributes.height = sheet->row_title_area.height;
1892 /* row - title window */
1893 sheet->row_title_window = gdk_window_new (widget->window,
1894 &attributes, attributes_mask);
1895 gdk_window_set_user_data (sheet->row_title_window, sheet);
1896 gtk_style_set_background (widget->style, sheet->row_title_window,
1899 /* sheet - window */
1900 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1905 sheet->sheet_window = gdk_window_new (widget->window,
1906 &attributes, attributes_mask);
1907 gdk_window_set_user_data (sheet->sheet_window, sheet);
1909 gdk_cursor_unref (attributes.cursor);
1911 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1912 gdk_window_show (sheet->sheet_window);
1915 sheet->fg_gc = gdk_gc_new (widget->window);
1916 sheet->bg_gc = gdk_gc_new (widget->window);
1918 values.foreground = widget->style->white;
1919 values.function = GDK_INVERT;
1920 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1921 values.line_width = BORDER_WIDTH;
1923 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1932 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1933 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1935 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1936 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1939 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1941 if (sheet->column_titles_visible)
1942 gdk_window_show (sheet->column_title_window);
1943 if (sheet->row_titles_visible)
1944 gdk_window_show (sheet->row_title_window);
1946 sheet->hover_window = create_hover_window ();
1948 draw_row_title_buttons (sheet);
1949 draw_column_title_buttons (sheet);
1951 psppire_sheet_update_primary_selection (sheet);
1954 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1958 create_global_button (PsppireSheet *sheet)
1960 sheet->button = gtk_button_new_with_label (" ");
1962 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1964 g_object_ref_sink (sheet->button);
1966 g_signal_connect (sheet->button,
1968 G_CALLBACK (global_button_clicked),
1973 size_allocate_global_button (PsppireSheet *sheet)
1975 GtkAllocation allocation;
1977 if (!sheet->column_titles_visible) return;
1978 if (!sheet->row_titles_visible) return;
1980 gtk_widget_size_request (sheet->button, NULL);
1984 allocation.width = sheet->row_title_area.width;
1985 allocation.height = sheet->column_title_area.height;
1987 gtk_widget_size_allocate (sheet->button, &allocation);
1991 global_button_clicked (GtkWidget *widget, gpointer data)
1993 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
1998 psppire_sheet_unrealize (GtkWidget *widget)
2000 PsppireSheet *sheet;
2002 g_return_if_fail (widget != NULL);
2003 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2005 sheet = PSPPIRE_SHEET (widget);
2007 gdk_cursor_unref (sheet->cursor_drag);
2008 sheet->cursor_drag = NULL;
2010 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2011 sheet->color, n_COLORS);
2013 g_object_unref (sheet->xor_gc);
2014 g_object_unref (sheet->fg_gc);
2015 g_object_unref (sheet->bg_gc);
2017 destroy_hover_window (sheet->hover_window);
2019 gdk_window_destroy (sheet->sheet_window);
2020 gdk_window_destroy (sheet->column_title_window);
2021 gdk_window_destroy (sheet->row_title_window);
2023 gtk_widget_unparent (sheet->entry_widget);
2024 if (sheet->button != NULL)
2025 gtk_widget_unparent (sheet->button);
2027 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2028 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2032 psppire_sheet_map (GtkWidget *widget)
2034 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2036 g_return_if_fail (widget != NULL);
2037 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2039 if (!GTK_WIDGET_MAPPED (widget))
2041 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2043 gdk_window_show (widget->window);
2044 gdk_window_show (sheet->sheet_window);
2046 if (sheet->column_titles_visible)
2048 draw_column_title_buttons (sheet);
2049 gdk_window_show (sheet->column_title_window);
2051 if (sheet->row_titles_visible)
2053 draw_row_title_buttons (sheet);
2054 gdk_window_show (sheet->row_title_window);
2057 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2058 && sheet->active_cell.row >= 0
2059 && sheet->active_cell.col >= 0 )
2061 gtk_widget_show (sheet->entry_widget);
2062 gtk_widget_map (sheet->entry_widget);
2065 if (!GTK_WIDGET_MAPPED (sheet->button))
2067 gtk_widget_show (sheet->button);
2068 gtk_widget_map (sheet->button);
2071 redraw_range (sheet, NULL);
2072 change_active_cell (sheet,
2073 sheet->active_cell.row,
2074 sheet->active_cell.col);
2079 psppire_sheet_unmap (GtkWidget *widget)
2081 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2083 if (!GTK_WIDGET_MAPPED (widget))
2086 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2088 gdk_window_hide (sheet->sheet_window);
2089 if (sheet->column_titles_visible)
2090 gdk_window_hide (sheet->column_title_window);
2091 if (sheet->row_titles_visible)
2092 gdk_window_hide (sheet->row_title_window);
2093 gdk_window_hide (widget->window);
2095 if (GTK_WIDGET_MAPPED (sheet->entry_widget))
2096 gtk_widget_unmap (sheet->entry_widget);
2098 if (GTK_WIDGET_MAPPED (sheet->button))
2099 gtk_widget_unmap (sheet->button);
2104 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2106 PangoLayout *layout;
2107 PangoRectangle text;
2108 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2113 PsppireSheetCellAttr attributes;
2116 g_return_if_fail (sheet != NULL);
2118 /* bail now if we aren't yet drawable */
2119 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2122 row >= psppire_axis_unit_count (sheet->vaxis))
2126 col >= psppire_axis_unit_count (sheet->haxis))
2129 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2131 /* select GC for background rectangle */
2132 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2133 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2135 rectangle_from_cell (sheet, row, col, &area);
2137 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2139 if (sheet->show_grid)
2141 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2143 gdk_draw_rectangle (sheet->sheet_window,
2147 area.width, area.height);
2151 label = psppire_sheet_cell_get_text (sheet, row, col);
2156 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2157 dispose_string (sheet, label);
2160 pango_layout_set_font_description (layout, font_desc);
2162 pango_layout_get_pixel_extents (layout, NULL, &text);
2164 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2166 font_height = pango_font_description_get_size (font_desc);
2167 if ( !pango_font_description_get_size_is_absolute (font_desc))
2168 font_height /= PANGO_SCALE;
2170 /* Centre the text vertically */
2171 area.y += (area.height - font_height) / 2.0;
2173 switch (attributes.justification)
2175 case GTK_JUSTIFY_RIGHT:
2176 area.x += area.width - text.width;
2178 case GTK_JUSTIFY_CENTER:
2179 area.x += (area.width - text.width) / 2.0;
2181 case GTK_JUSTIFY_LEFT:
2185 g_critical ("Unhandled justification %d in column %d\n",
2186 attributes.justification, col);
2190 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2195 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2196 g_object_unref (layout);
2201 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2203 PsppireSheetRange range;
2208 PsppireSheetRange drawing_range;
2210 gdk_region_get_clipbox (region, &area);
2212 y = area.y + sheet->vadjustment->value;
2213 x = area.x + sheet->hadjustment->value;
2215 if ( sheet->column_titles_visible)
2216 y -= sheet->column_title_area.height;
2218 if ( sheet->row_titles_visible)
2219 x -= sheet->row_title_area.width;
2221 maximize_int (&x, 0);
2222 maximize_int (&y, 0);
2224 range.row0 = row_from_ypixel (sheet, y);
2225 range.rowi = row_from_ypixel (sheet, y + area.height);
2227 range.col0 = column_from_xpixel (sheet, x);
2228 range.coli = column_from_xpixel (sheet, x + area.width);
2230 g_return_if_fail (sheet != NULL);
2231 g_return_if_fail (PSPPIRE_SHEET (sheet));
2233 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2234 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2235 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2238 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2239 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2240 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2241 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2243 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2244 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2246 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2248 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2249 psppire_sheet_cell_draw (sheet, i, j);
2252 if (sheet->state != PSPPIRE_SHEET_NORMAL &&
2253 psppire_sheet_range_isvisible (sheet, &sheet->range))
2254 psppire_sheet_range_draw_selection (sheet, drawing_range);
2257 if (sheet->state == GTK_STATE_NORMAL &&
2258 sheet->active_cell.row >= drawing_range.row0 &&
2259 sheet->active_cell.row <= drawing_range.rowi &&
2260 sheet->active_cell.col >= drawing_range.col0 &&
2261 sheet->active_cell.col <= drawing_range.coli)
2262 psppire_sheet_show_entry_widget (sheet);
2267 psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range)
2271 PsppireSheetRange aux;
2273 if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
2274 range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
2277 if (!psppire_sheet_range_isvisible (sheet, &range)) return;
2278 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2282 range.col0 = MAX (sheet->range.col0, range.col0);
2283 range.coli = MIN (sheet->range.coli, range.coli);
2284 range.row0 = MAX (sheet->range.row0, range.row0);
2285 range.rowi = MIN (sheet->range.rowi, range.rowi);
2287 range.col0 = MAX (range.col0, min_visible_column (sheet));
2288 range.coli = MIN (range.coli, max_visible_column (sheet));
2289 range.row0 = MAX (range.row0, min_visible_row (sheet));
2290 range.rowi = MIN (range.rowi, max_visible_row (sheet));
2292 for (i = range.row0; i <= range.rowi; i++)
2294 for (j = range.col0; j <= range.coli; j++)
2296 if (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED)
2298 rectangle_from_cell (sheet, i, j, &area);
2300 if (i == sheet->range.row0)
2302 area.y = area.y + 2;
2303 area.height = area.height - 2;
2305 if (i == sheet->range.rowi) area.height = area.height - 3;
2306 if (j == sheet->range.col0)
2308 area.x = area.x + 2;
2309 area.width = area.width - 2;
2311 if (j == sheet->range.coli) area.width = area.width - 3;
2313 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2315 gdk_draw_rectangle (sheet->sheet_window,
2318 area.x + 1, area.y + 1,
2319 area.width, area.height);
2326 psppire_sheet_draw_border (sheet, sheet->range);
2330 safe_strcmp (const gchar *s1, const gchar *s2)
2332 if ( !s1 && !s2) return 0;
2333 if ( !s1) return -1;
2334 if ( !s2) return +1;
2335 return strcmp (s1, s2);
2339 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2340 GtkJustification justification,
2343 PsppireSheetModel *model ;
2346 g_return_if_fail (sheet != NULL);
2347 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2349 if (col >= psppire_axis_unit_count (sheet->haxis)
2350 || row >= psppire_axis_unit_count (sheet->vaxis))
2353 if (col < 0 || row < 0) return;
2355 model = psppire_sheet_get_model (sheet);
2357 old_text = psppire_sheet_model_get_string (model, row, col);
2359 if (0 != safe_strcmp (old_text, text))
2361 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2362 psppire_sheet_model_set_string (model, text, row, col);
2363 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2366 if ( psppire_sheet_model_free_strings (model))
2372 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2374 PsppireSheetRange range;
2376 g_return_if_fail (sheet != NULL);
2377 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2378 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2379 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2381 if (column < 0 || row < 0) return;
2385 range.col0 = min_visible_column (sheet);
2386 range.coli = max_visible_column (sheet);
2388 psppire_sheet_real_cell_clear (sheet, row, column);
2390 redraw_range (sheet, &range);
2394 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2396 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2398 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2400 if (old_text && strlen (old_text) > 0 )
2402 psppire_sheet_model_datum_clear (model, row, column);
2405 dispose_string (sheet, old_text);
2409 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2411 PsppireSheetModel *model;
2412 g_return_val_if_fail (sheet != NULL, NULL);
2413 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2415 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2417 if (col < 0 || row < 0) return NULL;
2419 model = psppire_sheet_get_model (sheet);
2424 return psppire_sheet_model_get_string (model, row, col);
2429 psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
2432 PsppireSheetRange *range;
2434 g_return_val_if_fail (sheet != NULL, 0);
2435 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2436 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
2437 if (col < 0 || row < 0) return 0;
2439 state = sheet->state;
2440 range = &sheet->range;
2444 case PSPPIRE_SHEET_NORMAL:
2445 return GTK_STATE_NORMAL;
2447 case PSPPIRE_SHEET_ROW_SELECTED:
2448 if (row >= range->row0 && row <= range->rowi)
2449 return GTK_STATE_SELECTED;
2451 case PSPPIRE_SHEET_COLUMN_SELECTED:
2452 if (col >= range->col0 && col <= range->coli)
2453 return GTK_STATE_SELECTED;
2455 case PSPPIRE_SHEET_RANGE_SELECTED:
2456 if (row >= range->row0 && row <= range->rowi && \
2457 col >= range->col0 && col <= range->coli)
2458 return GTK_STATE_SELECTED;
2461 return GTK_STATE_NORMAL;
2464 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2465 If the function returns FALSE, then the results will be unreliable.
2468 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2476 *column = -G_MAXINT;
2478 g_return_val_if_fail (sheet != NULL, 0);
2479 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2481 /* bounds checking, return false if the user clicked
2489 if ( sheet->column_titles_visible)
2490 y -= sheet->column_title_area.height;
2492 y += sheet->vadjustment->value;
2494 if ( y < 0 && sheet->column_titles_visible)
2500 trow = row_from_ypixel (sheet, y);
2501 if (trow > psppire_axis_unit_count (sheet->vaxis))
2507 if ( sheet->row_titles_visible)
2508 x -= sheet->row_title_area.width;
2510 x += sheet->hadjustment->value;
2512 if ( x < 0 && sheet->row_titles_visible)
2518 tcol = column_from_xpixel (sheet, x);
2519 if (tcol > psppire_axis_unit_count (sheet->haxis))
2529 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2534 g_return_val_if_fail (sheet != NULL, 0);
2535 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2537 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2540 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2541 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2543 area->width= (column == -1) ? sheet->row_title_area.width
2544 : psppire_axis_unit_size (sheet->haxis, column);
2546 area->height= (row == -1) ? sheet->column_title_area.height
2547 : psppire_axis_unit_size (sheet->vaxis, row);
2553 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2555 g_return_if_fail (sheet != NULL);
2556 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2558 if (row < -1 || col < -1)
2561 if (row >= psppire_axis_unit_count (sheet->vaxis)
2563 col >= psppire_axis_unit_count (sheet->haxis))
2566 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2569 if ( row == -1 || col == -1)
2571 psppire_sheet_hide_entry_widget (sheet);
2575 change_active_cell (sheet, row, col);
2579 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2581 g_return_if_fail (sheet != NULL);
2582 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2584 if ( row ) *row = sheet->active_cell.row;
2585 if (column) *column = sheet->active_cell.col;
2589 entry_load_text (PsppireSheet *sheet)
2593 GtkJustification justification;
2594 PsppireSheetCellAttr attributes;
2596 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2597 if (sheet->state != GTK_STATE_NORMAL) return;
2599 row = sheet->active_cell.row;
2600 col = sheet->active_cell.col;
2602 if (row < 0 || col < 0) return;
2604 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2606 if (text && strlen (text) > 0)
2608 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2609 justification = attributes.justification;
2610 psppire_sheet_set_cell (sheet, row, col, justification, text);
2616 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2618 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2621 if (sheet->active_cell.row < 0 ||
2622 sheet->active_cell.col < 0) return;
2624 gtk_widget_hide (sheet->entry_widget);
2625 gtk_widget_unmap (sheet->entry_widget);
2627 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2631 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2633 gint old_row, old_col;
2635 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2637 if (row < 0 || col < 0)
2640 if ( row > psppire_axis_unit_count (sheet->vaxis)
2641 || col > psppire_axis_unit_count (sheet->haxis))
2644 if (sheet->state != PSPPIRE_SHEET_NORMAL)
2646 sheet->state = PSPPIRE_SHEET_NORMAL;
2647 psppire_sheet_real_unselect_range (sheet, NULL);
2650 old_row = sheet->active_cell.row;
2651 old_col = sheet->active_cell.col;
2653 /* Erase the old cell */
2654 psppire_sheet_draw_active_cell (sheet);
2656 entry_load_text (sheet);
2658 sheet->range.row0 = row;
2659 sheet->range.col0 = col;
2660 sheet->range.rowi = row;
2661 sheet->range.coli = col;
2662 sheet->active_cell.row = row;
2663 sheet->active_cell.col = col;
2664 sheet->selection_cell.row = row;
2665 sheet->selection_cell.col = col;
2667 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2669 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2671 psppire_sheet_draw_active_cell (sheet);
2672 psppire_sheet_show_entry_widget (sheet);
2674 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2676 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2677 row, col, old_row, old_col);
2682 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2684 GtkEntry *sheet_entry;
2685 PsppireSheetCellAttr attributes;
2689 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2691 row = sheet->active_cell.row;
2692 col = sheet->active_cell.col;
2694 /* Don't show the active cell, if there is no active cell: */
2695 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2698 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2699 if (sheet->state != PSPPIRE_SHEET_NORMAL) return;
2700 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2702 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2704 sheet_entry = psppire_sheet_get_entry (sheet);
2706 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2708 if (GTK_IS_ENTRY (sheet_entry))
2710 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2711 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2714 text = g_strdup ("");
2716 if (strcmp (old_text, text) != 0)
2717 gtk_entry_set_text (sheet_entry, text);
2719 dispose_string (sheet, text);
2722 switch (attributes.justification)
2724 case GTK_JUSTIFY_RIGHT:
2725 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2727 case GTK_JUSTIFY_CENTER:
2728 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2730 case GTK_JUSTIFY_LEFT:
2732 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2738 psppire_sheet_size_allocate_entry (sheet);
2740 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2741 psppire_sheet_model_is_editable (sheet->model,
2743 gtk_widget_map (sheet->entry_widget);
2747 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2750 PsppireSheetRange range;
2752 row = sheet->active_cell.row;
2753 col = sheet->active_cell.col;
2755 if (row < 0 || col < 0) return FALSE;
2757 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2760 range.col0 = range.coli = col;
2761 range.row0 = range.rowi = row;
2763 psppire_sheet_draw_border (sheet, range);
2771 psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range)
2773 gint i, j, mask1, mask2;
2774 gint state, selected;
2775 gint x, y, width, height;
2776 PsppireSheetRange new_range, aux_range;
2778 g_return_if_fail (sheet != NULL);
2780 if (range == NULL) range=&sheet->range;
2784 range->row0 = MIN (range->row0, sheet->range.row0);
2785 range->rowi = MAX (range->rowi, sheet->range.rowi);
2786 range->col0 = MIN (range->col0, sheet->range.col0);
2787 range->coli = MAX (range->coli, sheet->range.coli);
2789 range->row0 = MAX (range->row0, min_visible_row (sheet));
2790 range->rowi = MIN (range->rowi, max_visible_row (sheet));
2791 range->col0 = MAX (range->col0, min_visible_column (sheet));
2792 range->coli = MIN (range->coli, max_visible_column (sheet));
2794 aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
2795 aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
2796 aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
2797 aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
2799 for (i = range->row0; i <= range->rowi; i++)
2801 for (j = range->col0; j <= range->coli; j++)
2804 state = psppire_sheet_cell_get_state (sheet, i, j);
2805 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2806 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2808 if (state == GTK_STATE_SELECTED && selected &&
2809 (i == sheet->range.row0 || i == sheet->range.rowi ||
2810 j == sheet->range.col0 || j == sheet->range.coli ||
2811 i == new_range.row0 || i == new_range.rowi ||
2812 j == new_range.col0 || j == new_range.coli))
2815 mask1 = i == sheet->range.row0 ? 1 : 0;
2816 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2817 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2818 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2820 mask2 = i == new_range.row0 ? 1 : 0;
2821 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2822 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2823 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2827 x = psppire_axis_start_pixel (sheet->haxis, j);
2828 y = psppire_axis_start_pixel (sheet->vaxis, i);
2829 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2830 psppire_axis_unit_size (sheet->haxis, j);
2831 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2833 if (i == sheet->range.row0)
2836 height = height + 3;
2838 if (i == sheet->range.rowi) height = height + 3;
2839 if (j == sheet->range.col0)
2844 if (j == sheet->range.coli) width = width + 3;
2846 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2848 x = psppire_axis_start_pixel (sheet->haxis, j);
2849 y = psppire_axis_start_pixel (sheet->vaxis, i);
2850 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2851 psppire_axis_unit_size (sheet->haxis, j);
2853 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2855 if (i == new_range.row0)
2858 height = height - 2;
2860 if (i == new_range.rowi) height = height - 3;
2861 if (j == new_range.col0)
2866 if (j == new_range.coli) width = width - 3;
2868 gdk_draw_rectangle (sheet->sheet_window,
2879 for (i = range->row0; i <= range->rowi; i++)
2881 for (j = range->col0; j <= range->coli; j++)
2884 state = psppire_sheet_cell_get_state (sheet, i, j);
2885 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2886 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2888 if (state == GTK_STATE_SELECTED && !selected)
2891 x = psppire_axis_start_pixel (sheet->haxis, j);
2892 y = psppire_axis_start_pixel (sheet->vaxis, i);
2893 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2894 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2896 if (i == sheet->range.row0)
2899 height = height + 3;
2901 if (i == sheet->range.rowi) height = height + 3;
2902 if (j == sheet->range.col0)
2907 if (j == sheet->range.coli) width = width + 3;
2913 for (i = range->row0; i <= range->rowi; i++)
2915 for (j = range->col0; j <= range->coli; j++)
2918 state = psppire_sheet_cell_get_state (sheet, i, j);
2919 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2920 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2922 if (state != GTK_STATE_SELECTED && selected &&
2923 (i != sheet->active_cell.row || j != sheet->active_cell.col))
2926 x = psppire_axis_start_pixel (sheet->haxis, j);
2927 y = psppire_axis_start_pixel (sheet->vaxis, i);
2928 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2929 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2931 if (i == new_range.row0)
2934 height = height - 2;
2936 if (i == new_range.rowi) height = height - 3;
2937 if (j == new_range.col0)
2942 if (j == new_range.coli) width = width - 3;
2944 gdk_draw_rectangle (sheet->sheet_window,
2955 for (i = aux_range.row0; i <= aux_range.rowi; i++)
2957 for (j = aux_range.col0; j <= aux_range.coli; j++)
2959 state = psppire_sheet_cell_get_state (sheet, i, j);
2961 mask1 = i == sheet->range.row0 ? 1 : 0;
2962 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2963 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2964 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2966 mask2 = i == new_range.row0 ? 1 : 0;
2967 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2968 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2969 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2970 if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
2972 x = psppire_axis_start_pixel (sheet->haxis, j);
2973 y = psppire_axis_start_pixel (sheet->vaxis, i);
2974 width = psppire_axis_unit_size (sheet->haxis, j);
2975 height = psppire_axis_unit_size (sheet->vaxis, i);
2977 gdk_draw_rectangle (sheet->sheet_window,
2985 gdk_draw_rectangle (sheet->sheet_window,
2988 x + 1, y + height - 1,
2992 gdk_draw_rectangle (sheet->sheet_window,
3000 gdk_draw_rectangle (sheet->sheet_window,
3003 x + width - 1, y + 1,
3015 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
3019 rectangle_from_range (sheet, &new_range, &area);
3021 gdk_draw_rectangle (sheet->sheet_window,
3025 area.width, area.height);
3030 psppire_sheet_real_select_range (PsppireSheet *sheet,
3031 const PsppireSheetRange *range)
3035 g_return_if_fail (sheet != NULL);
3037 if (range == NULL) range = &sheet->range;
3039 memcpy (&sheet->range, range, sizeof (*range));
3041 if (range->row0 < 0 || range->rowi < 0) return;
3042 if (range->col0 < 0 || range->coli < 0) return;
3044 state = sheet->state;
3047 if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3048 range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3050 psppire_sheet_new_selection (sheet, &sheet->range);
3054 psppire_sheet_range_draw_selection (sheet, sheet->range);
3058 psppire_sheet_update_primary_selection (sheet);
3060 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3065 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
3067 g_return_if_fail (sheet != NULL);
3068 *range = sheet->range;
3073 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
3075 g_return_if_fail (sheet != NULL);
3077 if (range == NULL) range=&sheet->range;
3079 if (range->row0 < 0 || range->rowi < 0) return;
3080 if (range->col0 < 0 || range->coli < 0) return;
3083 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3084 psppire_sheet_real_unselect_range (sheet, NULL);
3086 sheet->range.row0 = range->row0;
3087 sheet->range.rowi = range->rowi;
3088 sheet->range.col0 = range->col0;
3089 sheet->range.coli = range->coli;
3090 sheet->active_cell.row = range->row0;
3091 sheet->active_cell.col = range->col0;
3092 sheet->selection_cell.row = range->rowi;
3093 sheet->selection_cell.col = range->coli;
3095 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3096 psppire_sheet_real_select_range (sheet, NULL);
3100 psppire_sheet_unselect_range (PsppireSheet *sheet)
3102 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3105 psppire_sheet_real_unselect_range (sheet, NULL);
3106 sheet->state = GTK_STATE_NORMAL;
3108 change_active_cell (sheet,
3109 sheet->active_cell.row, sheet->active_cell.col);
3114 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
3115 const PsppireSheetRange *range)
3117 g_return_if_fail (sheet != NULL);
3118 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3121 range = &sheet->range;
3123 if (range->row0 < 0 || range->rowi < 0) return;
3124 if (range->col0 < 0 || range->coli < 0) return;
3126 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3127 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3129 sheet->range.row0 = -1;
3130 sheet->range.rowi = -1;
3131 sheet->range.col0 = -1;
3132 sheet->range.coli = -1;
3137 psppire_sheet_expose (GtkWidget *widget,
3138 GdkEventExpose *event)
3140 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3142 g_return_val_if_fail (event != NULL, FALSE);
3144 if (!GTK_WIDGET_DRAWABLE (widget))
3147 /* exposure events on the sheet */
3148 if (event->window == sheet->row_title_window &&
3149 sheet->row_titles_visible)
3151 draw_row_title_buttons_range (sheet,
3152 min_visible_row (sheet),
3153 max_visible_row (sheet));
3156 if (event->window == sheet->column_title_window &&
3157 sheet->column_titles_visible)
3159 draw_column_title_buttons_range (sheet,
3160 min_visible_column (sheet),
3161 max_visible_column (sheet));
3164 if (event->window == sheet->sheet_window)
3166 draw_sheet_region (sheet, event->region);
3169 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3171 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3172 psppire_sheet_range_draw (sheet, &sheet->range);
3174 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3175 psppire_sheet_range_draw (sheet, &sheet->drag_range);
3177 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3178 psppire_sheet_range_draw_selection (sheet, sheet->range);
3179 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3180 draw_xor_rectangle (sheet, sheet->drag_range);
3184 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
3187 PsppireSheetRange range;
3188 range.row0 = range.rowi = sheet->active_cell.row;
3189 range.col0 = range.coli = sheet->active_cell.col;
3191 rectangle_from_range (sheet, &range, &rect);
3193 if (GDK_OVERLAP_RECTANGLE_OUT !=
3194 gdk_region_rect_in (event->region, &rect))
3196 psppire_sheet_draw_active_cell (sheet);
3202 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3209 psppire_sheet_button_press (GtkWidget *widget,
3210 GdkEventButton *event)
3212 PsppireSheet *sheet;
3213 GdkModifierType mods;
3218 g_return_val_if_fail (widget != NULL, FALSE);
3219 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
3220 g_return_val_if_fail (event != NULL, FALSE);
3222 sheet = PSPPIRE_SHEET (widget);
3224 /* Cancel any pending tooltips */
3225 if (sheet->motion_timer)
3227 g_source_remove (sheet->motion_timer);
3228 sheet->motion_timer = 0;
3231 gtk_widget_get_pointer (widget, &x, &y);
3232 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3235 if (event->window == sheet->column_title_window)
3237 sheet->x_drag = event->x;
3238 g_signal_emit (sheet,
3239 sheet_signals[BUTTON_EVENT_COLUMN], 0,
3242 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3244 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3245 g_signal_emit (sheet,
3246 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3249 else if (event->window == sheet->row_title_window)
3251 g_signal_emit (sheet,
3252 sheet_signals[BUTTON_EVENT_ROW], 0,
3255 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3257 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3258 g_signal_emit (sheet,
3259 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3263 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3265 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3268 /* press on resize windows */
3269 if (event->window == sheet->column_title_window)
3271 sheet->x_drag = event->x;
3273 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3275 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3276 gdk_pointer_grab (sheet->column_title_window, FALSE,
3277 GDK_POINTER_MOTION_HINT_MASK |
3278 GDK_BUTTON1_MOTION_MASK |
3279 GDK_BUTTON_RELEASE_MASK,
3280 NULL, NULL, event->time);
3282 draw_xor_vline (sheet);
3287 if (event->window == sheet->row_title_window)
3289 sheet->y_drag = event->y;
3291 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3293 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3294 gdk_pointer_grab (sheet->row_title_window, FALSE,
3295 GDK_POINTER_MOTION_HINT_MASK |
3296 GDK_BUTTON1_MOTION_MASK |
3297 GDK_BUTTON_RELEASE_MASK,
3298 NULL, NULL, event->time);
3300 draw_xor_hline (sheet);
3305 /* the sheet itself does not handle other than single click events */
3306 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3308 /* selections on the sheet */
3309 if (event->window == sheet->sheet_window)
3311 gtk_widget_get_pointer (widget, &x, &y);
3312 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3313 gdk_pointer_grab (sheet->sheet_window, FALSE,
3314 GDK_POINTER_MOTION_HINT_MASK |
3315 GDK_BUTTON1_MOTION_MASK |
3316 GDK_BUTTON_RELEASE_MASK,
3317 NULL, NULL, event->time);
3318 gtk_grab_add (GTK_WIDGET (sheet));
3320 if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
3321 sheet->selection_mode != GTK_SELECTION_NONE &&
3322 sheet->cursor_drag->type == GDK_SIZING &&
3323 !PSPPIRE_SHEET_IN_SELECTION (sheet) && !PSPPIRE_SHEET_IN_RESIZE (sheet))
3325 if (sheet->state == GTK_STATE_NORMAL)
3327 row = sheet->active_cell.row;
3328 column = sheet->active_cell.col;
3329 sheet->active_cell.row = row;
3330 sheet->active_cell.col = column;
3331 sheet->drag_range = sheet->range;
3332 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3333 psppire_sheet_select_range (sheet, &sheet->drag_range);
3337 if (row > sheet->range.rowi) row--;
3338 if (column > sheet->range.coli) column--;
3339 sheet->drag_cell.row = row;
3340 sheet->drag_cell.col = column;
3341 sheet->drag_range = sheet->range;
3342 draw_xor_rectangle (sheet, sheet->drag_range);
3343 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3345 else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
3346 !PSPPIRE_SHEET_IN_SELECTION (sheet)
3347 && ! PSPPIRE_SHEET_IN_DRAG (sheet)
3348 && sheet->active_cell.row >= 0
3349 && sheet->active_cell.col >= 0
3352 if (sheet->state == GTK_STATE_NORMAL)
3354 row = sheet->active_cell.row;
3355 column = sheet->active_cell.col;
3356 sheet->active_cell.row = row;
3357 sheet->active_cell.col = column;
3358 sheet->drag_range = sheet->range;
3359 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3360 psppire_sheet_select_range (sheet, &sheet->drag_range);
3364 if (row < sheet->range.row0) row++;
3365 if (row > sheet->range.rowi) row--;
3366 if (column < sheet->range.col0) column++;
3367 if (column > sheet->range.coli) column--;
3368 sheet->drag_cell.row = row;
3369 sheet->drag_cell.col = column;
3370 sheet->drag_range = sheet->range;
3371 draw_xor_rectangle (sheet, sheet->drag_range);
3372 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3376 veto = psppire_sheet_click_cell (sheet, row, column);
3377 if (veto) PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3381 if (event->window == sheet->column_title_window)
3383 gtk_widget_get_pointer (widget, &x, &y);
3384 if ( sheet->row_titles_visible)
3385 x -= sheet->row_title_area.width;
3387 x += sheet->hadjustment->value;
3389 column = column_from_xpixel (sheet, x);
3391 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3393 veto = psppire_sheet_click_cell (sheet, -1, column);
3394 gtk_grab_add (GTK_WIDGET (sheet));
3395 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3399 if (event->window == sheet->row_title_window)
3401 gtk_widget_get_pointer (widget, &x, &y);
3402 if ( sheet->column_titles_visible)
3403 y -= sheet->column_title_area.height;
3405 y += sheet->vadjustment->value;
3407 row = row_from_ypixel (sheet, y);
3408 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3410 veto = psppire_sheet_click_cell (sheet, row, -1);
3411 gtk_grab_add (GTK_WIDGET (sheet));
3412 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3420 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3422 PsppireSheetCell cell;
3423 gboolean forbid_move;
3428 if (row >= psppire_axis_unit_count (sheet->vaxis)
3429 || column >= psppire_axis_unit_count (sheet->haxis))
3434 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3435 &sheet->active_cell,
3441 if (sheet->state == GTK_STATE_NORMAL)
3444 row = sheet->active_cell.row;
3445 column = sheet->active_cell.col;
3447 change_active_cell (sheet, row, column);
3451 if (row == -1 && column >= 0)
3453 psppire_sheet_select_column (sheet, column);
3457 if (column == -1 && row >= 0)
3459 psppire_sheet_select_row (sheet, row);
3463 if (row == -1 && column == -1)
3465 sheet->range.row0 = 0;
3466 sheet->range.col0 = 0;
3467 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3469 psppire_axis_unit_count (sheet->haxis) - 1;
3470 sheet->active_cell.row = 0;
3471 sheet->active_cell.col = 0;
3472 psppire_sheet_select_range (sheet, NULL);
3476 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3478 sheet->state = PSPPIRE_SHEET_NORMAL;
3479 psppire_sheet_real_unselect_range (sheet, NULL);
3483 change_active_cell (sheet, row, column);
3486 sheet->active_cell.row = row;
3487 sheet->active_cell.col = column;
3488 sheet->selection_cell.row = row;
3489 sheet->selection_cell.col = column;
3490 sheet->range.row0 = row;
3491 sheet->range.col0 = column;
3492 sheet->range.rowi = row;
3493 sheet->range.coli = column;
3494 sheet->state = PSPPIRE_SHEET_NORMAL;
3495 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3497 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3503 psppire_sheet_button_release (GtkWidget *widget,
3504 GdkEventButton *event)
3506 GdkDisplay *display = gtk_widget_get_display (widget);
3508 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3510 /* release on resize windows */
3511 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3514 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3515 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3517 gdk_display_pointer_ungrab (display, event->time);
3518 draw_xor_vline (sheet);
3521 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3522 + sheet->hadjustment->value;
3524 set_column_width (sheet, sheet->drag_cell.col, width);
3529 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3532 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3533 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3535 gdk_display_pointer_ungrab (display, event->time);
3536 draw_xor_hline (sheet);
3539 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3540 sheet->vadjustment->value;
3542 set_row_height (sheet, sheet->drag_cell.row, height);
3547 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3549 PsppireSheetRange old_range;
3550 draw_xor_rectangle (sheet, sheet->drag_range);
3551 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3552 gdk_display_pointer_ungrab (display, event->time);
3554 psppire_sheet_real_unselect_range (sheet, NULL);
3556 sheet->active_cell.row = sheet->active_cell.row +
3557 (sheet->drag_range.row0 - sheet->range.row0);
3558 sheet->active_cell.col = sheet->active_cell.col +
3559 (sheet->drag_range.col0 - sheet->range.col0);
3560 sheet->selection_cell.row = sheet->selection_cell.row +
3561 (sheet->drag_range.row0 - sheet->range.row0);
3562 sheet->selection_cell.col = sheet->selection_cell.col +
3563 (sheet->drag_range.col0 - sheet->range.col0);
3564 old_range = sheet->range;
3565 sheet->range = sheet->drag_range;
3566 sheet->drag_range = old_range;
3567 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3568 &sheet->drag_range, &sheet->range);
3569 psppire_sheet_select_range (sheet, &sheet->range);
3572 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3574 PsppireSheetRange old_range;
3575 draw_xor_rectangle (sheet, sheet->drag_range);
3576 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3577 gdk_display_pointer_ungrab (display, event->time);
3579 psppire_sheet_real_unselect_range (sheet, NULL);
3581 sheet->active_cell.row = sheet->active_cell.row +
3582 (sheet->drag_range.row0 - sheet->range.row0);
3583 sheet->active_cell.col = sheet->active_cell.col +
3584 (sheet->drag_range.col0 - sheet->range.col0);
3585 if (sheet->drag_range.row0 < sheet->range.row0)
3586 sheet->selection_cell.row = sheet->drag_range.row0;
3587 if (sheet->drag_range.rowi >= sheet->range.rowi)
3588 sheet->selection_cell.row = sheet->drag_range.rowi;
3589 if (sheet->drag_range.col0 < sheet->range.col0)
3590 sheet->selection_cell.col = sheet->drag_range.col0;
3591 if (sheet->drag_range.coli >= sheet->range.coli)
3592 sheet->selection_cell.col = sheet->drag_range.coli;
3593 old_range = sheet->range;
3594 sheet->range = sheet->drag_range;
3595 sheet->drag_range = old_range;
3597 if (sheet->state == GTK_STATE_NORMAL) sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3598 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3599 &sheet->drag_range, &sheet->range);
3600 psppire_sheet_select_range (sheet, &sheet->range);
3603 if (sheet->state == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet))
3605 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3606 gdk_display_pointer_ungrab (display, event->time);
3607 change_active_cell (sheet, sheet->active_cell.row,
3608 sheet->active_cell.col);
3611 if (PSPPIRE_SHEET_IN_SELECTION)
3612 gdk_display_pointer_ungrab (display, event->time);
3613 gtk_grab_remove (GTK_WIDGET (sheet));
3615 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3624 /* Shamelessly lifted from gtktooltips */
3626 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3630 gtk_widget_size_request (tip_window, &req);
3631 gtk_paint_flat_box (tip_window->style, tip_window->window,
3632 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3633 NULL, GTK_WIDGET(tip_window), "tooltip",
3634 0, 0, req.width, req.height);
3640 destroy_hover_window (PsppireSheetHoverTitle *h)
3642 gtk_widget_destroy (h->window);
3646 static PsppireSheetHoverTitle *
3647 create_hover_window (void)
3649 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3651 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3653 #if GTK_CHECK_VERSION (2, 9, 0)
3654 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3655 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3658 gtk_widget_set_app_paintable (hw->window, TRUE);
3659 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3660 gtk_widget_set_name (hw->window, "gtk-tooltips");
3661 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3663 g_signal_connect (hw->window,
3665 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3668 hw->label = gtk_label_new (NULL);
3671 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3672 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3674 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3676 gtk_widget_show (hw->label);
3678 g_signal_connect (hw->window,
3680 G_CALLBACK (gtk_widget_destroyed),
3686 #define HOVER_WINDOW_Y_OFFSET 2
3689 show_subtitle (PsppireSheet *sheet, gint row, gint column, const gchar *subtitle)
3698 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3702 sheet->hover_window->row = row;
3703 sheet->hover_window->column = column;
3705 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3707 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3709 gtk_widget_show (sheet->hover_window->window);
3711 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3717 y += sheet->column_title_area.y;
3718 y += sheet->column_title_area.height;
3719 y += HOVER_WINDOW_Y_OFFSET;
3725 x += sheet->row_title_area.x;
3726 x += sheet->row_title_area.width * 2 / 3.0;
3729 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3734 motion_timeout_callback (gpointer data)
3736 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3739 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3741 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3743 if (sheet->row_title_under && row >= 0)
3745 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3747 show_subtitle (sheet, row, -1, text);
3751 if (sheet->column_title_under && column >= 0)
3753 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3756 show_subtitle (sheet, -1, column, text);
3766 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3768 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3769 GdkModifierType mods;
3770 GdkCursorType new_cursor;
3773 GdkDisplay *display;
3775 g_return_val_if_fail (event != NULL, FALSE);
3777 display = gtk_widget_get_display (widget);
3779 /* selections on the sheet */
3783 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3785 if ( sheet->motion_timer > 0 )
3786 g_source_remove (sheet->motion_timer);
3787 sheet->motion_timer =
3788 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3794 gtk_widget_get_pointer (widget, &wx, &wy);
3796 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3798 if ( row != sheet->hover_window->row ||
3799 column != sheet->hover_window->column)
3801 gtk_widget_hide (sheet->hover_window->window);
3806 if (event->window == sheet->column_title_window)
3808 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3809 on_column_boundary (sheet, x, &column))
3811 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3812 if (new_cursor != sheet->cursor_drag->type)
3814 gdk_cursor_unref (sheet->cursor_drag);
3815 sheet->cursor_drag =
3816 gdk_cursor_new_for_display (display, new_cursor);
3818 gdk_window_set_cursor (sheet->column_title_window,
3819 sheet->cursor_drag);
3824 new_cursor = GDK_TOP_LEFT_ARROW;
3825 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3826 new_cursor != sheet->cursor_drag->type)
3828 gdk_cursor_unref (sheet->cursor_drag);
3829 sheet->cursor_drag =
3830 gdk_cursor_new_for_display (display, new_cursor);
3831 gdk_window_set_cursor (sheet->column_title_window,
3832 sheet->cursor_drag);
3836 else if (event->window == sheet->row_title_window)
3838 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3839 on_row_boundary (sheet, y, &row))
3841 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3842 if (new_cursor != sheet->cursor_drag->type)
3844 gdk_cursor_unref (sheet->cursor_drag);
3845 sheet->cursor_drag =
3846 gdk_cursor_new_for_display (display, new_cursor);
3847 gdk_window_set_cursor (sheet->row_title_window,
3848 sheet->cursor_drag);
3853 new_cursor = GDK_TOP_LEFT_ARROW;
3854 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3855 new_cursor != sheet->cursor_drag->type)
3857 gdk_cursor_unref (sheet->cursor_drag);
3858 sheet->cursor_drag =
3859 gdk_cursor_new_for_display (display, new_cursor);
3860 gdk_window_set_cursor (sheet->row_title_window,
3861 sheet->cursor_drag);
3866 new_cursor = GDK_PLUS;
3867 if ( event->window == sheet->sheet_window &&
3868 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3869 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3870 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3871 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3872 new_cursor != sheet->cursor_drag->type)
3874 gdk_cursor_unref (sheet->cursor_drag);
3875 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3876 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3879 new_cursor = GDK_TOP_LEFT_ARROW;
3880 if ( event->window == sheet->sheet_window &&
3881 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3882 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3883 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3884 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3885 new_cursor != sheet->cursor_drag->type)
3887 gdk_cursor_unref (sheet->cursor_drag);
3888 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3889 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3892 new_cursor = GDK_SIZING;
3893 if ( event->window == sheet->sheet_window &&
3894 sheet->selection_mode != GTK_SELECTION_NONE &&
3895 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3896 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3897 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3898 new_cursor != sheet->cursor_drag->type)
3900 gdk_cursor_unref (sheet->cursor_drag);
3901 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3902 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3906 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3907 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3909 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3911 if (event->x != sheet->x_drag)
3913 draw_xor_vline (sheet);
3914 sheet->x_drag = event->x;
3915 draw_xor_vline (sheet);
3921 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3923 if (event->y != sheet->y_drag)
3925 draw_xor_hline (sheet);
3926 sheet->y_drag = event->y;
3927 draw_xor_hline (sheet);
3933 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3935 PsppireSheetRange aux;
3936 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3937 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3938 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3939 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3943 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3944 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3946 aux = sheet->drag_range;
3947 sheet->drag_range.row0 = sheet->range.row0 + row;
3948 sheet->drag_range.col0 = sheet->range.col0 + column;
3949 sheet->drag_range.rowi = sheet->range.rowi + row;
3950 sheet->drag_range.coli = sheet->range.coli + column;
3951 if (aux.row0 != sheet->drag_range.row0 ||
3952 aux.col0 != sheet->drag_range.col0)
3954 draw_xor_rectangle (sheet, aux);
3955 draw_xor_rectangle (sheet, sheet->drag_range);
3961 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3963 PsppireSheetRange aux;
3964 gint v_h, current_col, current_row, col_threshold, row_threshold;
3966 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
3967 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
3969 current_col = column_from_xpixel (sheet, x);
3970 current_row = row_from_ypixel (sheet, y);
3971 column = current_col - sheet->drag_cell.col;
3972 row = current_row - sheet->drag_cell.row;
3974 /*use half of column width resp. row height as threshold to
3976 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
3977 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
3980 if (x < col_threshold)
3983 else if (column < 0)
3985 if (x > col_threshold)
3988 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
3989 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
3992 if (y < row_threshold)
3997 if (y > row_threshold)
4001 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
4002 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4012 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4013 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4015 aux = sheet->drag_range;
4016 sheet->drag_range = sheet->range;
4018 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4019 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4020 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4021 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4023 if (aux.row0 != sheet->drag_range.row0 ||
4024 aux.rowi != sheet->drag_range.rowi ||
4025 aux.col0 != sheet->drag_range.col0 ||
4026 aux.coli != sheet->drag_range.coli)
4028 draw_xor_rectangle (sheet, aux);
4029 draw_xor_rectangle (sheet, sheet->drag_range);
4035 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
4037 if (sheet->state == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
4038 column == sheet->active_cell.col) return TRUE;
4040 if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4041 psppire_sheet_extend_selection (sheet, row, column);
4047 psppire_sheet_crossing_notify (GtkWidget *widget,
4048 GdkEventCrossing *event)
4050 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4052 if (event->window == sheet->column_title_window)
4053 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4054 else if (event->window == sheet->row_title_window)
4055 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4062 psppire_sheet_focus_in (GtkWidget *w,
4063 GdkEventFocus *event,
4066 PsppireSheet *sheet = PSPPIRE_SHEET (w);
4068 gtk_widget_grab_focus (sheet->entry_widget);
4075 psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column)
4077 PsppireSheetRange range;
4081 if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
4084 if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
4086 gtk_widget_grab_focus (GTK_WIDGET (sheet));
4088 if (PSPPIRE_SHEET_IN_DRAG (sheet)) return;
4090 state = sheet->state;
4092 switch (sheet->state)
4094 case PSPPIRE_SHEET_ROW_SELECTED:
4095 column = psppire_axis_unit_count (sheet->haxis) - 1;
4097 case PSPPIRE_SHEET_COLUMN_SELECTED:
4098 row = psppire_axis_unit_count (sheet->vaxis) - 1;
4100 case PSPPIRE_SHEET_NORMAL:
4101 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4102 r = sheet->active_cell.row;
4103 c = sheet->active_cell.col;
4104 sheet->range.col0 = c;
4105 sheet->range.row0 = r;
4106 sheet->range.coli = c;
4107 sheet->range.rowi = r;
4108 psppire_sheet_range_draw_selection (sheet, sheet->range);
4109 case PSPPIRE_SHEET_RANGE_SELECTED:
4110 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4113 sheet->selection_cell.row = row;
4114 sheet->selection_cell.col = column;
4116 range.col0 = MIN (column, sheet->active_cell.col);
4117 range.coli = MAX (column, sheet->active_cell.col);
4118 range.row0 = MIN (row, sheet->active_cell.row);
4119 range.rowi = MAX (row, sheet->active_cell.row);
4121 if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
4122 range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
4123 state == PSPPIRE_SHEET_NORMAL)
4124 psppire_sheet_real_select_range (sheet, &range);
4129 psppire_sheet_entry_key_press (GtkWidget *widget,
4133 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
4138 /* Number of rows in a step-increment */
4139 #define ROWS_PER_STEP 1
4143 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
4145 gint old_row = sheet->active_cell.row ;
4146 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
4150 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
4151 min_visible_row (sheet));
4155 case GTK_SCROLL_PAGE_DOWN:
4156 gtk_adjustment_set_value (sheet->vadjustment,
4157 sheet->vadjustment->value +
4158 sheet->vadjustment->page_increment);
4160 case GTK_SCROLL_PAGE_UP:
4161 gtk_adjustment_set_value (sheet->vadjustment,
4162 sheet->vadjustment->value -
4163 sheet->vadjustment->page_increment);
4167 g_assert_not_reached ();
4172 vpixel += psppire_axis_start_pixel (sheet->vaxis,
4173 min_visible_row (sheet));
4175 new_row = row_from_ypixel (sheet, vpixel);
4177 change_active_cell (sheet, new_row,
4178 sheet->active_cell.col);
4183 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
4185 gint current_row = sheet->active_cell.row;
4186 gint current_col = sheet->active_cell.col;
4187 PsppireSheetCell new_cell ;
4188 gboolean forbidden = FALSE;
4190 new_cell.row = current_row;
4191 new_cell.col = current_col;
4195 case GTK_SCROLL_STEP_DOWN:
4198 case GTK_SCROLL_STEP_UP:
4201 case GTK_SCROLL_STEP_RIGHT:
4204 case GTK_SCROLL_STEP_LEFT:
4207 case GTK_SCROLL_STEP_FORWARD:
4210 psppire_sheet_model_get_column_count (sheet->model))
4216 case GTK_SCROLL_STEP_BACKWARD:
4218 if (new_cell.col < 0)
4221 psppire_sheet_model_get_column_count (sheet->model) - 1;
4226 g_assert_not_reached ();
4230 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
4231 &sheet->active_cell,
4239 maximize_int (&new_cell.row, 0);
4240 maximize_int (&new_cell.col, 0);
4242 minimize_int (&new_cell.row,
4243 psppire_axis_unit_count (sheet->vaxis) - 1);
4245 minimize_int (&new_cell.col,
4246 psppire_axis_unit_count (sheet->haxis) - 1);
4248 change_active_cell (sheet, new_cell.row, new_cell.col);
4251 if ( new_cell.col > max_fully_visible_column (sheet))
4254 psppire_axis_start_pixel (sheet->haxis,
4256 hpos -= sheet->hadjustment->page_size;
4258 gtk_adjustment_set_value (sheet->hadjustment,
4261 else if ( new_cell.col < min_fully_visible_column (sheet))
4264 psppire_axis_start_pixel (sheet->haxis,
4267 gtk_adjustment_set_value (sheet->hadjustment,
4272 if ( new_cell.row > max_fully_visible_row (sheet))
4275 psppire_axis_start_pixel (sheet->vaxis,
4277 vpos -= sheet->vadjustment->page_size;
4279 gtk_adjustment_set_value (sheet->vadjustment,
4282 else if ( new_cell.row < min_fully_visible_row (sheet))
4285 psppire_axis_start_pixel (sheet->vaxis,
4288 gtk_adjustment_set_value (sheet->vadjustment,
4292 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
4297 psppire_sheet_key_press (GtkWidget *widget,
4300 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4302 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
4304 switch (key->keyval)
4307 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
4310 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
4312 case GDK_ISO_Left_Tab:
4313 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
4316 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
4320 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
4323 step_sheet (sheet, GTK_SCROLL_STEP_UP);
4327 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
4330 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
4334 gtk_adjustment_set_value (sheet->vadjustment,
4335 sheet->vadjustment->lower);
4337 change_active_cell (sheet, 0,
4338 sheet->active_cell.col);
4343 gtk_adjustment_set_value (sheet->vadjustment,
4344 sheet->vadjustment->upper -
4345 sheet->vadjustment->page_size -
4346 sheet->vadjustment->page_increment);
4349 change_active_cellx (sheet,
4350 psppire_axis_unit_count (sheet->vaxis) - 1,
4351 sheet->active_cell.col);
4355 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
4366 psppire_sheet_size_request (GtkWidget *widget,
4367 GtkRequisition *requisition)
4369 PsppireSheet *sheet;
4371 g_return_if_fail (widget != NULL);
4372 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4373 g_return_if_fail (requisition != NULL);
4375 sheet = PSPPIRE_SHEET (widget);
4377 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4378 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4380 /* compute the size of the column title area */
4381 if (sheet->column_titles_visible)
4382 requisition->height += sheet->column_title_area.height;
4384 /* compute the size of the row title area */
4385 if (sheet->row_titles_visible)
4386 requisition->width += sheet->row_title_area.width;
4391 psppire_sheet_size_allocate (GtkWidget *widget,
4392 GtkAllocation *allocation)
4394 PsppireSheet *sheet;
4395 GtkAllocation sheet_allocation;
4398 g_return_if_fail (widget != NULL);
4399 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4400 g_return_if_fail (allocation != NULL);
4402 sheet = PSPPIRE_SHEET (widget);
4403 widget->allocation = *allocation;
4404 border_width = GTK_CONTAINER (widget)->border_width;
4406 if (GTK_WIDGET_REALIZED (widget))
4407 gdk_window_move_resize (widget->window,
4408 allocation->x + border_width,
4409 allocation->y + border_width,
4410 allocation->width - 2 * border_width,
4411 allocation->height - 2 * border_width);
4413 sheet_allocation.x = 0;
4414 sheet_allocation.y = 0;
4415 sheet_allocation.width = allocation->width - 2 * border_width;
4416 sheet_allocation.height = allocation->height - 2 * border_width;
4418 if (GTK_WIDGET_REALIZED (widget))
4419 gdk_window_move_resize (sheet->sheet_window,
4422 sheet_allocation.width,
4423 sheet_allocation.height);
4425 /* position the window which holds the column title buttons */
4426 sheet->column_title_area.x = 0;
4427 sheet->column_title_area.y = 0;
4428 sheet->column_title_area.width = sheet_allocation.width ;
4431 /* position the window which holds the row title buttons */
4432 sheet->row_title_area.x = 0;
4433 sheet->row_title_area.y = 0;
4434 sheet->row_title_area.height = sheet_allocation.height;
4436 if (sheet->row_titles_visible)
4437 sheet->column_title_area.x += sheet->row_title_area.width;
4439 if (sheet->column_titles_visible)
4440 sheet->row_title_area.y += sheet->column_title_area.height;
4442 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4443 gdk_window_move_resize (sheet->column_title_window,
4444 sheet->column_title_area.x,
4445 sheet->column_title_area.y,
4446 sheet->column_title_area.width,
4447 sheet->column_title_area.height);
4450 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4451 gdk_window_move_resize (sheet->row_title_window,
4452 sheet->row_title_area.x,
4453 sheet->row_title_area.y,
4454 sheet->row_title_area.width,
4455 sheet->row_title_area.height);
4457 size_allocate_global_button (sheet);
4461 gint width = sheet->column_title_area.width;
4463 if ( sheet->row_titles_visible)
4464 width -= sheet->row_title_area.width;
4466 g_object_set (sheet->haxis,
4467 "minimum-extent", width,
4474 gint height = sheet->row_title_area.height;
4476 if ( sheet->column_titles_visible)
4477 height -= sheet->column_title_area.height;
4479 g_object_set (sheet->vaxis,
4480 "minimum-extent", height,
4485 /* set the scrollbars adjustments */
4486 adjust_scrollbars (sheet);
4490 draw_column_title_buttons (PsppireSheet *sheet)
4494 if (!sheet->column_titles_visible) return;
4495 if (!GTK_WIDGET_REALIZED (sheet))
4498 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4501 if (sheet->row_titles_visible)
4503 x = sheet->row_title_area.width;
4506 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4508 sheet->column_title_area.width = width;
4509 sheet->column_title_area.x = x;
4510 gdk_window_move_resize (sheet->column_title_window,
4511 sheet->column_title_area.x,
4512 sheet->column_title_area.y,
4513 sheet->column_title_area.width,
4514 sheet->column_title_area.height);
4517 if (max_visible_column (sheet) ==
4518 psppire_axis_unit_count (sheet->haxis) - 1)
4519 gdk_window_clear_area (sheet->column_title_window,
4521 sheet->column_title_area.width,
4522 sheet->column_title_area.height);
4524 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4526 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4527 max_visible_column (sheet));
4531 draw_row_title_buttons (PsppireSheet *sheet)
4536 if (!sheet->row_titles_visible) return;
4537 if (!GTK_WIDGET_REALIZED (sheet))
4540 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4542 if (sheet->column_titles_visible)
4544 y = sheet->column_title_area.height;
4547 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4549 sheet->row_title_area.y = y;
4550 sheet->row_title_area.height = height;
4551 gdk_window_move_resize (sheet->row_title_window,
4552 sheet->row_title_area.x,
4553 sheet->row_title_area.y,
4554 sheet->row_title_area.width,
4555 sheet->row_title_area.height);
4558 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4559 gdk_window_clear_area (sheet->row_title_window,
4561 sheet->row_title_area.width,
4562 sheet->row_title_area.height);
4564 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4566 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4567 max_visible_row (sheet));
4572 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4574 GtkAllocation entry_alloc;
4575 PsppireSheetCellAttr attributes = { 0 };
4576 GtkEntry *sheet_entry;
4578 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4579 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4581 sheet_entry = psppire_sheet_get_entry (sheet);
4583 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4584 sheet->active_cell.col,
4588 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4590 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4592 style->bg[GTK_STATE_NORMAL] = attributes.background;
4593 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4594 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4595 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4596 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4597 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4600 rectangle_from_cell (sheet, sheet->active_cell.row,
4601 sheet->active_cell.col, &entry_alloc);
4603 entry_alloc.width -= BORDER_WIDTH ;
4604 entry_alloc.height -= BORDER_WIDTH ;
4605 entry_alloc.x += DIV_RND_UP (BORDER_WIDTH, 2);
4606 entry_alloc.y += DIV_RND_UP (BORDER_WIDTH, 2);
4609 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4610 entry_alloc.height);
4611 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4615 /* Copy the sheet's font to the entry widget */
4617 set_entry_widget_font (PsppireSheet *sheet)
4619 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4621 pango_font_description_free (style->font_desc);
4622 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4624 gtk_widget_modify_style (sheet->entry_widget, style);
4628 create_sheet_entry (PsppireSheet *sheet)
4630 if (sheet->entry_widget)
4632 gtk_widget_unparent (sheet->entry_widget);
4635 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4636 g_object_ref_sink (sheet->entry_widget);
4638 gtk_widget_size_request (sheet->entry_widget, NULL);
4640 if ( GTK_IS_ENTRY (sheet->entry_widget))
4642 g_object_set (sheet->entry_widget,
4647 if (GTK_WIDGET_REALIZED (sheet))
4649 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4650 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4651 gtk_widget_realize (sheet->entry_widget);
4654 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4655 G_CALLBACK (psppire_sheet_entry_key_press),
4658 set_entry_widget_font (sheet);
4660 gtk_widget_show (sheet->entry_widget);
4664 /* Finds the last child widget that happens to be of type GtkEntry */
4666 find_entry (GtkWidget *w, gpointer user_data)
4668 GtkWidget **entry = user_data;
4669 if ( GTK_IS_ENTRY (w))
4677 psppire_sheet_get_entry (PsppireSheet *sheet)
4679 GtkWidget *w = sheet->entry_widget;
4681 g_return_val_if_fail (sheet != NULL, NULL);
4682 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4683 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4685 while (! GTK_IS_ENTRY (w))
4687 GtkWidget *entry = NULL;
4689 if (GTK_IS_CONTAINER (w))
4691 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4700 return GTK_ENTRY (w);
4705 draw_button (PsppireSheet *sheet, GdkWindow *window,
4706 PsppireSheetButton *button, gboolean is_sensitive,
4707 GdkRectangle allocation)
4709 GtkShadowType shadow_type;
4710 gint text_width = 0, text_height = 0;
4711 PangoAlignment align = PANGO_ALIGN_LEFT;
4717 g_return_if_fail (sheet != NULL);
4718 g_return_if_fail (button != NULL);
4721 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4723 gdk_window_clear_area (window,
4724 allocation.x, allocation.y,
4725 allocation.width, allocation.height);
4727 gtk_widget_ensure_style (sheet->button);
4729 gtk_paint_box (sheet->button->style, window,
4730 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4731 &allocation, GTK_WIDGET (sheet->button),
4733 allocation.x, allocation.y,
4734 allocation.width, allocation.height);
4736 state = button->state;
4737 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4739 if (state == GTK_STATE_ACTIVE)
4740 shadow_type = GTK_SHADOW_IN;
4742 shadow_type = GTK_SHADOW_OUT;
4744 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4745 gtk_paint_box (sheet->button->style, window,
4746 button->state, shadow_type,
4747 &allocation, GTK_WIDGET (sheet->button),
4749 allocation.x, allocation.y,
4750 allocation.width, allocation.height);
4752 if (button->label_visible)
4754 text_height = DEFAULT_ROW_HEIGHT -
4755 2 * COLUMN_TITLES_HEIGHT;
4757 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4759 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4762 allocation.y += 2 * sheet->button->style->ythickness;
4764 if (button->label && strlen (button->label) > 0)
4766 PangoRectangle rect;
4767 gchar *line = button->label;
4769 PangoLayout *layout = NULL;
4770 gint real_x = allocation.x;
4771 gint real_y = allocation.y;
4773 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4774 pango_layout_get_extents (layout, NULL, &rect);
4776 text_width = PANGO_PIXELS (rect.width);
4777 switch (button->justification)
4779 case GTK_JUSTIFY_LEFT:
4780 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4781 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4783 case GTK_JUSTIFY_RIGHT:
4784 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4785 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4787 case GTK_JUSTIFY_CENTER:
4789 real_x = allocation.x + (allocation.width - text_width)/2;
4790 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4791 pango_layout_set_justify (layout, TRUE);
4793 pango_layout_set_alignment (layout, align);
4794 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4803 g_object_unref (layout);
4806 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4808 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4812 psppire_sheet_button_free (button);
4816 /* Draw the column title buttons FIRST through to LAST */
4818 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4822 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4824 if (!sheet->column_titles_visible) return;
4826 g_return_if_fail (first >= min_visible_column (sheet));
4827 g_return_if_fail (last <= max_visible_column (sheet));
4830 rect.height = sheet->column_title_area.height;
4831 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4832 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4833 + psppire_axis_unit_size (sheet->haxis, last);
4835 rect.x -= sheet->hadjustment->value;
4837 minimize_int (&rect.width, sheet->column_title_area.width);
4838 maximize_int (&rect.x, 0);
4840 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4842 for (col = first ; col <= last ; ++col)
4844 GdkRectangle allocation;
4845 gboolean is_sensitive = FALSE;
4847 PsppireSheetButton *
4848 button = psppire_sheet_model_get_column_button (sheet->model, col);
4850 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4852 allocation.x -= sheet->hadjustment->value;
4854 allocation.height = sheet->column_title_area.height;
4855 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4856 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4858 draw_button (sheet, sheet->column_title_window,
4859 button, is_sensitive, allocation);
4862 gdk_window_end_paint (sheet->column_title_window);
4867 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4871 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4873 if (!sheet->row_titles_visible) return;
4875 g_return_if_fail (first >= min_visible_row (sheet));
4876 g_return_if_fail (last <= max_visible_row (sheet));
4879 rect.width = sheet->row_title_area.width;
4880 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4881 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4882 + psppire_axis_unit_size (sheet->vaxis, last);
4884 rect.y -= sheet->vadjustment->value;
4886 minimize_int (&rect.height, sheet->row_title_area.height);
4887 maximize_int (&rect.y, 0);
4889 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4890 for (row = first; row <= last; ++row)
4892 GdkRectangle allocation;
4894 gboolean is_sensitive = FALSE;
4896 PsppireSheetButton *button =
4897 psppire_sheet_model_get_row_button (sheet->model, row);
4899 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4901 allocation.y -= sheet->vadjustment->value;
4903 allocation.width = sheet->row_title_area.width;
4904 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4905 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4907 draw_button (sheet, sheet->row_title_window,
4908 button, is_sensitive, allocation);
4911 gdk_window_end_paint (sheet->row_title_window);
4918 * vadjustment_value_changed
4919 * hadjustment_value_changed */
4923 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4926 (adj->value + adj->page_size)
4928 (adj->upper - adj->lower);
4930 const glong last_item = psppire_axis_unit_count (axis) - 1;
4932 if (isnan (position) || position < 0)
4936 psppire_axis_start_pixel (axis, last_item)
4938 psppire_axis_unit_size (axis, last_item)
4942 adj->page_size = page_size;
4945 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4947 if ( adj->value < adj->lower)
4948 adj->value = adj->lower;
4951 gtk_adjustment_changed (adj);
4956 adjust_scrollbars (PsppireSheet *sheet)
4960 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4963 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4965 if ( sheet->row_titles_visible)
4966 width -= sheet->row_title_area.width;
4968 if (sheet->column_titles_visible)
4969 height -= sheet->column_title_area.height;
4971 if (sheet->vadjustment)
4973 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4975 sheet->vadjustment->step_increment =
4977 psppire_axis_unit_size (sheet->vaxis, last_row);
4979 sheet->vadjustment->page_increment =
4981 sheet->column_title_area.height -
4982 psppire_axis_unit_size (sheet->vaxis, last_row);
4984 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4987 if (sheet->hadjustment)
4989 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4990 sheet->hadjustment->step_increment = 1;
4992 sheet->hadjustment->page_increment = width;
4994 sheet->hadjustment->upper =
4995 psppire_axis_start_pixel (sheet->haxis, last_col)
4997 psppire_axis_unit_size (sheet->haxis, last_col)
5000 update_adjustment (sheet->hadjustment, sheet->haxis, width);
5004 /* Subtracts the region of WIDGET from REGION */
5006 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
5009 GdkRectangle intersect;
5012 gdk_region_get_clipbox (region, &rect);
5013 gtk_widget_intersect (widget,
5017 region2 = gdk_region_rectangle (&intersect);
5018 gdk_region_subtract (region, region2);
5019 gdk_region_destroy (region2);
5023 vadjustment_value_changed (GtkAdjustment *adjustment,
5027 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5029 g_return_if_fail (adjustment != NULL);
5031 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5033 gtk_widget_hide (sheet->entry_widget);
5036 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5038 subtract_widget_region (region, sheet->button);
5039 gdk_window_begin_paint_region (sheet->sheet_window, region);
5041 draw_sheet_region (sheet, region);
5043 draw_row_title_buttons (sheet);
5044 psppire_sheet_draw_active_cell (sheet);
5046 gdk_window_end_paint (sheet->sheet_window);
5047 gdk_region_destroy (region);
5052 hadjustment_value_changed (GtkAdjustment *adjustment,
5056 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5058 g_return_if_fail (adjustment != NULL);
5060 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5062 gtk_widget_hide (sheet->entry_widget);
5066 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5068 subtract_widget_region (region, sheet->button);
5069 gdk_window_begin_paint_region (sheet->sheet_window, region);
5071 draw_sheet_region (sheet, region);
5073 draw_column_title_buttons (sheet);
5075 psppire_sheet_draw_active_cell (sheet);
5077 gdk_window_end_paint (sheet->sheet_window);
5079 gdk_region_destroy (region);
5083 /* COLUMN RESIZING */
5085 draw_xor_vline (PsppireSheet *sheet)
5088 gint xpos = sheet->x_drag;
5089 gdk_drawable_get_size (sheet->sheet_window,
5092 if (sheet->row_titles_visible)
5093 xpos += sheet->row_title_area.width;
5095 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5097 sheet->column_title_area.height,
5099 height + CELL_SPACING);
5104 draw_xor_hline (PsppireSheet *sheet)
5108 gint ypos = sheet->y_drag;
5110 gdk_drawable_get_size (sheet->sheet_window,
5114 if (sheet->column_titles_visible)
5115 ypos += sheet->column_title_area.height;
5117 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5118 sheet->row_title_area.width,
5120 width + CELL_SPACING,
5124 /* SELECTED RANGE */
5126 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
5129 GdkRectangle clip_area, area;
5132 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
5133 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
5134 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
5135 psppire_axis_unit_size (sheet->haxis, range.coli);
5136 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
5137 psppire_axis_unit_size (sheet->vaxis, range.rowi);
5139 clip_area.x = sheet->row_title_area.width;
5140 clip_area.y = sheet->column_title_area.height;
5142 gdk_drawable_get_size (sheet->sheet_window,
5143 &clip_area.width, &clip_area.height);
5145 if (!sheet->row_titles_visible) clip_area.x = 0;
5146 if (!sheet->column_titles_visible) clip_area.y = 0;
5150 area.width = area.width + area.x;
5153 if (area.width > clip_area.width) area.width = clip_area.width + 10;
5156 area.height = area.height + area.y;
5159 if (area.height > clip_area.height) area.height = clip_area.height + 10;
5163 clip_area.width += 3;
5164 clip_area.height += 3;
5166 gdk_gc_get_values (sheet->xor_gc, &values);
5168 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5170 gdk_draw_rectangle (sheet->sheet_window,
5173 area.x + i, area.y + i,
5174 area.width - 2 * i, area.height - 2 * i);
5177 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5179 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5184 set_column_width (PsppireSheet *sheet,
5188 g_return_if_fail (sheet != NULL);
5189 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5191 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
5197 psppire_axis_resize (sheet->haxis, column, width);
5199 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5201 draw_column_title_buttons (sheet);
5202 adjust_scrollbars (sheet);
5203 psppire_sheet_size_allocate_entry (sheet);
5204 redraw_range (sheet, NULL);
5209 set_row_height (PsppireSheet *sheet,
5213 g_return_if_fail (sheet != NULL);
5214 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5216 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
5222 psppire_axis_resize (sheet->vaxis, row, height);
5224 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
5226 draw_row_title_buttons (sheet);
5227 adjust_scrollbars (sheet);
5228 psppire_sheet_size_allocate_entry (sheet);
5229 redraw_range (sheet, NULL);
5234 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
5235 PsppireSheetCellAttr *attr)
5238 const GtkJustification *j ;
5239 GdkColormap *colormap;
5241 g_return_val_if_fail (sheet != NULL, FALSE);
5242 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
5244 if (row < 0 || col < 0) return FALSE;
5246 attr->foreground = GTK_WIDGET (sheet)->style->black;
5247 attr->background = sheet->color[BG_COLOR];
5249 attr->border.width = 0;
5250 attr->border.line_style = GDK_LINE_SOLID;
5251 attr->border.cap_style = GDK_CAP_NOT_LAST;
5252 attr->border.join_style = GDK_JOIN_MITER;
5253 attr->border.mask = 0;
5254 attr->border.color = GTK_WIDGET (sheet)->style->black;
5256 attr->is_editable = psppire_sheet_model_is_editable (sheet->model, row, col);
5258 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
5259 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
5262 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
5263 attr->foreground = *fg;
5266 bg = psppire_sheet_model_get_background (sheet->model, row, col);
5269 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
5270 attr->background = *bg;
5273 attr->justification =
5274 psppire_sheet_model_get_column_justification (sheet->model, col);
5276 j = psppire_sheet_model_get_justification (sheet->model, row, col);
5278 attr->justification = *j;
5284 psppire_sheet_button_size_request (PsppireSheet *sheet,
5285 const PsppireSheetButton *button,
5286 GtkRequisition *button_requisition)
5288 GtkRequisition requisition;
5289 GtkRequisition label_requisition;
5291 label_requisition.height = DEFAULT_ROW_HEIGHT;
5292 label_requisition.width = COLUMN_MIN_WIDTH;
5294 requisition.height = DEFAULT_ROW_HEIGHT;
5295 requisition.width = COLUMN_MIN_WIDTH;
5298 *button_requisition = requisition;
5299 button_requisition->width = MAX (requisition.width, label_requisition.width);
5300 button_requisition->height = MAX (requisition.height, label_requisition.height);
5305 psppire_sheet_forall (GtkContainer *container,
5306 gboolean include_internals,
5307 GtkCallback callback,
5308 gpointer callback_data)
5310 PsppireSheet *sheet = PSPPIRE_SHEET (container);
5312 g_return_if_fail (callback != NULL);
5314 if (sheet->button && sheet->button->parent)
5315 (* callback) (sheet->button, callback_data);
5317 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
5318 (* callback) (sheet->entry_widget, callback_data);
5323 psppire_sheet_get_model (const PsppireSheet *sheet)
5325 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
5327 return sheet->model;
5331 PsppireSheetButton *
5332 psppire_sheet_button_new (void)
5334 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
5336 button->state = GTK_STATE_NORMAL;
5337 button->label = NULL;
5338 button->label_visible = TRUE;
5339 button->justification = GTK_JUSTIFY_FILL;
5346 psppire_sheet_button_free (PsppireSheetButton *button)
5348 if (!button) return ;
5350 g_free (button->label);
5355 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5357 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5359 if ( NULL == celltext)
5362 g_string_append (string, celltext);
5368 range_to_text (const PsppireSheet *sheet)
5373 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5376 string = g_string_sized_new (80);
5378 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5380 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5382 append_cell_text (string, sheet, r, c);
5383 g_string_append (string, "\t");
5385 append_cell_text (string, sheet, r, c);
5386 if ( r < sheet->range.rowi)
5387 g_string_append (string, "\n");
5394 range_to_html (const PsppireSheet *sheet)
5399 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5402 string = g_string_sized_new (480);
5404 g_string_append (string, "<html>\n");
5405 g_string_append (string, "<body>\n");
5406 g_string_append (string, "<table>\n");
5407 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5409 g_string_append (string, "<tr>\n");
5410 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5412 g_string_append (string, "<td>");
5413 append_cell_text (string, sheet, r, c);
5414 g_string_append (string, "</td>\n");
5416 g_string_append (string, "</tr>\n");
5418 g_string_append (string, "</table>\n");
5419 g_string_append (string, "</body>\n");
5420 g_string_append (string, "</html>\n");
5432 primary_get_cb (GtkClipboard *clipboard,
5433 GtkSelectionData *selection_data,
5437 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5438 GString *string = NULL;
5442 case SELECT_FMT_TEXT:
5443 string = range_to_text (sheet);
5445 case SELECT_FMT_HTML:
5446 string = range_to_html (sheet);
5449 g_assert_not_reached ();
5452 gtk_selection_data_set (selection_data, selection_data->target,
5454 (const guchar *) string->str, string->len);
5455 g_string_free (string, TRUE);
5459 primary_clear_cb (GtkClipboard *clipboard,
5462 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5463 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5466 psppire_sheet_real_unselect_range (sheet, NULL);
5470 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5472 static const GtkTargetEntry targets[] = {
5473 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5474 { "STRING", 0, SELECT_FMT_TEXT },
5475 { "TEXT", 0, SELECT_FMT_TEXT },
5476 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5477 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5478 { "text/plain", 0, SELECT_FMT_TEXT },
5479 { "text/html", 0, SELECT_FMT_HTML }
5482 GtkClipboard *clipboard;
5484 if (!GTK_WIDGET_REALIZED (sheet))
5487 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5488 GDK_SELECTION_PRIMARY);
5490 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5492 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5493 G_N_ELEMENTS (targets),
5494 primary_get_cb, primary_clear_cb,
5496 primary_clear_cb (clipboard, sheet);
5500 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5501 gtk_clipboard_clear (clipboard);