2 Copyright (C) 2006, 2008, 2009 Free Software Foundation
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 This file is derived from the gtksheet.c and extensively modified for the
19 requirements of PSPPIRE. The changes are copyright by the
20 Free Software Foundation. The copyright notice for the original work is
24 /* GtkSheet widget for Gtk+.
25 * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
27 * Based on GtkClist widget by Jay Painter, but major changes.
28 * Memory allocation routines inspired on SC (Spreadsheet Calculator)
30 * This library is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Lesser General Public
32 * License as published by the Free Software Foundation; either
33 * version 2.1 of the License, or (at your option) any later version.
35 * This library is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38 * Lesser General Public License for more details.
40 * You should have received a copy of the GNU Lesser General Public
41 * License along with this library; if not, write to the Free Software
42 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
46 * SECTION:psppiresheet
47 * @short_description: spreadsheet widget for gtk2
49 * PsppireSheet is a matrix widget for GTK+. It consists of an scrollable grid of
50 * cells where you can allocate text. Cell contents can be edited interactively
51 * through a specially designed entry, GtkItemEntry.
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 <ui/gui/sheet/psppire-axis.h>
73 #include <libpspp/misc.h>
80 PSPPIRE_SHEET_IN_XDRAG = 1 << 1,
81 PSPPIRE_SHEET_IN_YDRAG = 1 << 2,
82 PSPPIRE_SHEET_IN_DRAG = 1 << 3,
83 PSPPIRE_SHEET_IN_SELECTION = 1 << 4,
84 PSPPIRE_SHEET_IN_RESIZE = 1 << 5
87 #define PSPPIRE_SHEET_FLAGS(sheet) (PSPPIRE_SHEET (sheet)->flags)
88 #define PSPPIRE_SHEET_SET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) |= (flag))
89 #define PSPPIRE_SHEET_UNSET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) &= ~ (flag))
91 #define PSPPIRE_SHEET_IN_XDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_XDRAG)
92 #define PSPPIRE_SHEET_IN_YDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_YDRAG)
93 #define PSPPIRE_SHEET_IN_DRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_DRAG)
94 #define PSPPIRE_SHEET_IN_SELECTION(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_SELECTION)
95 #define PSPPIRE_SHEET_IN_RESIZE(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_RESIZE)
97 #define CELL_SPACING 1
99 #define TIMEOUT_HOVER 300
100 #define COLUMN_MIN_WIDTH 10
101 #define COLUMN_TITLES_HEIGHT 4
102 #define DEFAULT_COLUMN_WIDTH 80
103 #define DEFAULT_ROW_HEIGHT 25
105 static void set_entry_widget_font (PsppireSheet *sheet);
107 static void psppire_sheet_update_primary_selection (PsppireSheet *sheet);
108 static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
109 static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
110 static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range);
113 static void set_row_height (PsppireSheet *sheet,
117 static void destroy_hover_window (PsppireSheetHoverTitle *);
118 static PsppireSheetHoverTitle *create_hover_window (void);
120 static GtkStateType psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col);
124 dispose_string (const PsppireSheet *sheet, gchar *text)
126 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
131 if (psppire_sheet_model_free_strings (model))
136 /* FIXME: Why bother with these two ? */
138 /* returns the column index from a pixel location */
140 column_from_xpixel (const PsppireSheet *sheet, gint pixel)
142 return psppire_axis_unit_at_pixel (sheet->haxis, pixel);
146 row_from_ypixel (const PsppireSheet *sheet, gint pixel)
148 return psppire_axis_unit_at_pixel (sheet->vaxis, pixel);
152 /* Return the lowest row number which is wholly or partially on
153 the visible range of the sheet */
155 min_visible_row (const PsppireSheet *sheet)
157 return row_from_ypixel (sheet, sheet->vadjustment->value);
161 min_fully_visible_row (const PsppireSheet *sheet)
163 glong row = min_visible_row (sheet);
165 if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value)
172 max_visible_row (const PsppireSheet *sheet)
174 return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size);
179 max_fully_visible_row (const PsppireSheet *sheet)
181 glong row = max_visible_row (sheet);
183 if ( psppire_axis_start_pixel (sheet->vaxis, row)
185 psppire_axis_unit_size (sheet->vaxis, row)
186 > sheet->vadjustment->value)
193 /* Returns the lowest column number which is wholly or partially
196 min_visible_column (const PsppireSheet *sheet)
198 return column_from_xpixel (sheet, sheet->hadjustment->value);
202 min_fully_visible_column (const PsppireSheet *sheet)
204 glong col = min_visible_column (sheet);
206 if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value)
213 /* Returns the highest column number which is wholly or partially
216 max_visible_column (const PsppireSheet *sheet)
218 return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size);
222 max_fully_visible_column (const PsppireSheet *sheet)
224 glong col = max_visible_column (sheet);
226 if ( psppire_axis_start_pixel (sheet->haxis, col)
228 psppire_axis_unit_size (sheet->haxis, col)
229 > sheet->hadjustment->value)
237 /* The size of the region (in pixels) around the row/column boundaries
238 where the height/width may be grabbed to change size */
242 on_column_boundary (const PsppireSheet *sheet, gint x, gint *column)
247 x += sheet->hadjustment->value;
252 col = column_from_xpixel (sheet, x);
254 pixel = x - DRAG_WIDTH / 2;
258 if ( column_from_xpixel (sheet, pixel) < col )
264 if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col )
274 on_row_boundary (const PsppireSheet *sheet, gint y, gint *row)
279 y += sheet->vadjustment->value;
284 r = row_from_ypixel (sheet, y);
286 pixel = y - DRAG_WIDTH / 2;
290 if ( row_from_ypixel (sheet, pixel) < r )
296 if ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r )
306 static inline gboolean
307 POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y,
308 gint *drag_row, gint *drag_column)
312 /* Can't drag if nothing is selected */
313 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
314 sheet->range.col0 < 0 || sheet->range.coli < 0 )
317 *drag_column = column_from_xpixel (sheet, x);
318 *drag_row = row_from_ypixel (sheet, y);
320 if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 &&
321 x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
322 psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2)
324 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0);
325 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
327 *drag_row = sheet->range.row0;
330 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
331 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
332 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
334 *drag_row = sheet->range.rowi;
339 if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 &&
340 y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
341 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2)
343 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0);
344 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
346 *drag_column = sheet->range.col0;
349 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
350 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
351 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
353 *drag_column = sheet->range.coli;
361 static inline gboolean
362 POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y,
363 gint *drag_row, gint *drag_column)
367 /* Can't drag if nothing is selected */
368 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
369 sheet->range.col0 < 0 || sheet->range.coli < 0 )
372 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+
373 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
375 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
376 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
378 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED)
379 ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet));
381 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED)
382 xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet));
384 *drag_column = column_from_xpixel (sheet, x);
385 *drag_row = row_from_ypixel (sheet, y);
387 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
388 y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
395 rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *range,
398 g_return_val_if_fail (range, FALSE);
400 r->x = psppire_axis_start_pixel (sheet->haxis, range->col0);
401 r->x -= round (sheet->hadjustment->value);
403 r->y = psppire_axis_start_pixel (sheet->vaxis, range->row0);
404 r->y -= round (sheet->vadjustment->value);
406 r->width = psppire_axis_start_pixel (sheet->haxis, range->coli) -
407 psppire_axis_start_pixel (sheet->haxis, range->col0) +
408 psppire_axis_unit_size (sheet->haxis, range->coli);
410 r->height = psppire_axis_start_pixel (sheet->vaxis, range->rowi) -
411 psppire_axis_start_pixel (sheet->vaxis, range->row0) +
412 psppire_axis_unit_size (sheet->vaxis, range->rowi);
414 if ( sheet->column_titles_visible)
416 r->y += sheet->column_title_area.height;
419 if ( sheet->row_titles_visible)
421 r->x += sheet->row_title_area.width;
428 rectangle_from_cell (PsppireSheet *sheet, gint row, gint col,
431 PsppireSheetRange range;
432 g_return_val_if_fail (row >= 0, FALSE);
433 g_return_val_if_fail (col >= 0, FALSE);
435 range.row0 = range.rowi = row;
436 range.col0 = range.coli = col;
438 return rectangle_from_range (sheet, &range, r);
442 static void psppire_sheet_class_init (PsppireSheetClass *klass);
443 static void psppire_sheet_init (PsppireSheet *sheet);
444 static void psppire_sheet_dispose (GObject *object);
445 static void psppire_sheet_finalize (GObject *object);
446 static void psppire_sheet_style_set (GtkWidget *widget,
447 GtkStyle *previous_style);
448 static void psppire_sheet_realize (GtkWidget *widget);
449 static void psppire_sheet_unrealize (GtkWidget *widget);
450 static void psppire_sheet_map (GtkWidget *widget);
451 static void psppire_sheet_unmap (GtkWidget *widget);
452 static gint psppire_sheet_expose (GtkWidget *widget,
453 GdkEventExpose *event);
455 static void psppire_sheet_forall (GtkContainer *container,
456 gboolean include_internals,
457 GtkCallback callback,
458 gpointer callback_data);
460 static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
461 GtkAdjustment *hadjustment,
462 GtkAdjustment *vadjustment);
464 static gint psppire_sheet_button_press (GtkWidget *widget,
465 GdkEventButton *event);
466 static gint psppire_sheet_button_release (GtkWidget *widget,
467 GdkEventButton *event);
468 static gint psppire_sheet_motion (GtkWidget *widget,
469 GdkEventMotion *event);
470 static gboolean psppire_sheet_crossing_notify (GtkWidget *widget,
471 GdkEventCrossing *event);
472 static gint psppire_sheet_entry_key_press (GtkWidget *widget,
474 static gboolean psppire_sheet_key_press (GtkWidget *widget,
476 static void psppire_sheet_size_request (GtkWidget *widget,
477 GtkRequisition *requisition);
478 static void psppire_sheet_size_allocate (GtkWidget *widget,
479 GtkAllocation *allocation);
481 static gboolean psppire_sheet_focus_in (GtkWidget *widget,
482 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;
1018 container_class->set_focus_child = NULL;
1020 object_class->dispose = psppire_sheet_dispose;
1021 object_class->finalize = psppire_sheet_finalize;
1025 g_param_spec_pointer ("vertical-axis",
1027 "A pointer to the PsppireAxis object for the rows",
1028 G_PARAM_READABLE | G_PARAM_WRITABLE );
1031 g_param_spec_pointer ("horizontal-axis",
1033 "A pointer to the PsppireAxis object for the columns",
1034 G_PARAM_READABLE | G_PARAM_WRITABLE );
1037 g_param_spec_pointer ("model",
1039 "A pointer to the data model",
1040 G_PARAM_READABLE | G_PARAM_WRITABLE );
1043 object_class->set_property = psppire_sheet_set_property;
1044 object_class->get_property = psppire_sheet_get_property;
1046 g_object_class_install_property (object_class,
1050 g_object_class_install_property (object_class,
1054 g_object_class_install_property (object_class,
1059 widget_class->realize = psppire_sheet_realize;
1060 widget_class->unrealize = psppire_sheet_unrealize;
1061 widget_class->map = psppire_sheet_map;
1062 widget_class->unmap = psppire_sheet_unmap;
1063 widget_class->style_set = psppire_sheet_style_set;
1064 widget_class->button_press_event = psppire_sheet_button_press;
1065 widget_class->button_release_event = psppire_sheet_button_release;
1066 widget_class->motion_notify_event = psppire_sheet_motion;
1067 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1068 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1069 widget_class->key_press_event = psppire_sheet_key_press;
1070 widget_class->expose_event = psppire_sheet_expose;
1071 widget_class->size_request = psppire_sheet_size_request;
1072 widget_class->size_allocate = psppire_sheet_size_allocate;
1073 widget_class->focus_in_event = psppire_sheet_focus_in;
1074 widget_class->focus_out_event = NULL;
1076 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1077 klass->select_row = NULL;
1078 klass->select_column = NULL;
1079 klass->select_range = NULL;
1080 klass->resize_range = NULL;
1081 klass->move_range = NULL;
1082 klass->traverse = NULL;
1083 klass->activate = NULL;
1084 klass->changed = NULL;
1088 psppire_sheet_init (PsppireSheet *sheet)
1090 sheet->model = NULL;
1091 sheet->haxis = NULL;
1092 sheet->vaxis = NULL;
1095 sheet->selection_mode = GTK_SELECTION_NONE;
1096 sheet->state = PSPPIRE_SHEET_NORMAL;
1098 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1099 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1101 sheet->column_title_window = NULL;
1102 sheet->column_title_area.x = 0;
1103 sheet->column_title_area.y = 0;
1104 sheet->column_title_area.width = 0;
1105 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1107 sheet->row_title_window = NULL;
1108 sheet->row_title_area.x = 0;
1109 sheet->row_title_area.y = 0;
1110 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1111 sheet->row_title_area.height = 0;
1114 sheet->active_cell.row = 0;
1115 sheet->active_cell.col = 0;
1116 sheet->selection_cell.row = 0;
1117 sheet->selection_cell.col = 0;
1119 sheet->range.row0 = 0;
1120 sheet->range.rowi = 0;
1121 sheet->range.col0 = 0;
1122 sheet->range.coli = 0;
1124 sheet->state = PSPPIRE_SHEET_NORMAL;
1126 sheet->sheet_window = NULL;
1127 sheet->entry_widget = NULL;
1128 sheet->button = NULL;
1130 sheet->hadjustment = NULL;
1131 sheet->vadjustment = NULL;
1133 sheet->cursor_drag = NULL;
1135 sheet->xor_gc = NULL;
1136 sheet->fg_gc = NULL;
1137 sheet->bg_gc = NULL;
1140 sheet->show_grid = TRUE;
1142 sheet->motion_timer = 0;
1144 sheet->row_titles_visible = TRUE;
1145 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1147 sheet->column_titles_visible = TRUE;
1150 /* create sheet entry */
1151 sheet->entry_type = GTK_TYPE_ENTRY;
1152 create_sheet_entry (sheet);
1154 /* create global selection button */
1155 create_global_button (sheet);
1159 /* Cause RANGE to be redrawn. If RANGE is null, then the
1160 entire visible range will be redrawn.
1163 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1167 if ( ! GTK_WIDGET_REALIZED (sheet))
1170 if ( NULL != range )
1171 rectangle_from_range (sheet, range, &rect);
1174 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1175 gdk_region_get_clipbox (r, &rect);
1177 if ( sheet->column_titles_visible)
1179 rect.y += sheet->column_title_area.height;
1180 rect.height -= sheet->column_title_area.height;
1183 if ( sheet->row_titles_visible)
1185 rect.x += sheet->row_title_area.width;
1186 rect.width -= sheet->row_title_area.width;
1190 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1194 /* Callback which occurs whenever columns are inserted / deleted in the model */
1196 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1200 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1202 PsppireSheetRange range;
1203 gint model_columns = psppire_sheet_model_get_column_count (model);
1206 /* Need to update all the columns starting from the first column and onwards.
1207 * Previous column are unchanged, so don't need to be updated.
1209 range.col0 = first_column;
1211 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1212 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1214 adjust_scrollbars (sheet);
1216 if (sheet->active_cell.col >= model_columns)
1217 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1219 draw_column_title_buttons_range (sheet,
1220 first_column, max_visible_column (sheet));
1223 redraw_range (sheet, &range);
1229 /* Callback which occurs whenever rows are inserted / deleted in the model */
1231 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1232 gint n_rows, gpointer data)
1234 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1236 PsppireSheetRange range;
1238 gint model_rows = psppire_sheet_model_get_row_count (model);
1240 /* Need to update all the rows starting from the first row and onwards.
1241 * Previous rows are unchanged, so don't need to be updated.
1243 range.row0 = first_row;
1245 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1246 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1248 adjust_scrollbars (sheet);
1250 if (sheet->active_cell.row >= model_rows)
1251 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1253 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1255 redraw_range (sheet, &range);
1259 If row0 or rowi are negative, then all rows will be updated.
1260 If col0 or coli are negative, then all columns will be updated.
1263 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1264 gint rowi, gint coli, gpointer data)
1266 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1268 PsppireSheetRange range;
1275 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1278 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1280 redraw_range (sheet, NULL);
1281 adjust_scrollbars (sheet);
1283 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1284 max_visible_row (sheet));
1286 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1287 max_visible_column (sheet));
1291 else if ( row0 < 0 || rowi < 0 )
1293 range.row0 = min_visible_row (sheet);
1294 range.rowi = max_visible_row (sheet);
1296 else if ( col0 < 0 || coli < 0 )
1298 range.col0 = min_visible_column (sheet);
1299 range.coli = max_visible_column (sheet);
1302 redraw_range (sheet, &range);
1307 * psppire_sheet_new:
1308 * @rows: initial number of rows
1309 * @columns: initial number of columns
1310 * @title: sheet title
1311 * @model: the model to use for the sheet data
1313 * Creates a new sheet widget with the given number of rows and columns.
1315 * Returns: the new sheet widget
1318 psppire_sheet_new (PsppireSheetModel *model)
1320 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1328 * psppire_sheet_set_model
1329 * @sheet: the sheet to set the model for
1330 * @model: the model to use for the sheet data
1332 * Sets the model for a PsppireSheet
1336 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1338 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1340 if (sheet->model ) g_object_unref (sheet->model);
1342 sheet->model = model;
1346 g_object_ref (model);
1348 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1349 G_CALLBACK (range_update_callback),
1352 g_signal_connect (model, "rows_inserted",
1353 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1355 g_signal_connect (model, "rows_deleted",
1356 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1358 g_signal_connect (model, "columns_inserted",
1359 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1361 g_signal_connect (model, "columns_deleted",
1362 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1368 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1372 g_return_if_fail (sheet != NULL);
1373 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1375 state = sheet->state;
1377 if (sheet->state == PSPPIRE_SHEET_NORMAL)
1378 psppire_sheet_hide_entry_widget (sheet);
1380 sheet->entry_type = entry_type;
1382 create_sheet_entry (sheet);
1384 if (state == PSPPIRE_SHEET_NORMAL)
1386 psppire_sheet_show_entry_widget (sheet);
1392 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1394 g_return_if_fail (sheet != NULL);
1395 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1397 if (show == sheet->show_grid) return;
1399 sheet->show_grid = show;
1401 redraw_range (sheet, NULL);
1405 psppire_sheet_grid_visible (PsppireSheet *sheet)
1407 g_return_val_if_fail (sheet != NULL, 0);
1408 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1410 return sheet->show_grid;
1414 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1416 g_return_val_if_fail (sheet != NULL, 0);
1417 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1419 return psppire_axis_unit_count (sheet->haxis);
1422 static void set_column_width (PsppireSheet *sheet,
1428 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1430 if (sheet->column_titles_visible) return;
1432 sheet->column_titles_visible = TRUE;
1434 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1437 gdk_window_show (sheet->column_title_window);
1438 gdk_window_move_resize (sheet->column_title_window,
1439 sheet->column_title_area.x,
1440 sheet->column_title_area.y,
1441 sheet->column_title_area.width,
1442 sheet->column_title_area.height);
1444 adjust_scrollbars (sheet);
1446 if (sheet->vadjustment)
1447 g_signal_emit_by_name (sheet->vadjustment,
1450 size_allocate_global_button (sheet);
1452 if ( sheet->row_titles_visible)
1453 gtk_widget_show (sheet->button);
1458 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1460 if (sheet->row_titles_visible) return;
1462 sheet->row_titles_visible = TRUE;
1465 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1467 gdk_window_show (sheet->row_title_window);
1468 gdk_window_move_resize (sheet->row_title_window,
1469 sheet->row_title_area.x,
1470 sheet->row_title_area.y,
1471 sheet->row_title_area.width,
1472 sheet->row_title_area.height);
1474 adjust_scrollbars (sheet);
1477 if (sheet->hadjustment)
1478 g_signal_emit_by_name (sheet->hadjustment,
1481 size_allocate_global_button (sheet);
1483 if ( sheet->column_titles_visible)
1484 gtk_widget_show (sheet->button);
1488 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1490 if (!sheet->column_titles_visible) return;
1492 sheet->column_titles_visible = FALSE;
1494 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1496 if (sheet->column_title_window)
1497 gdk_window_hide (sheet->column_title_window);
1499 gtk_widget_hide (sheet->button);
1501 adjust_scrollbars (sheet);
1504 if (sheet->vadjustment)
1505 g_signal_emit_by_name (sheet->vadjustment,
1510 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1512 if (!sheet->row_titles_visible) return;
1514 sheet->row_titles_visible = FALSE;
1516 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1518 if (sheet->row_title_window)
1519 gdk_window_hide (sheet->row_title_window);
1521 gtk_widget_hide (sheet->button);
1523 adjust_scrollbars (sheet);
1526 if (sheet->hadjustment)
1527 g_signal_emit_by_name (sheet->hadjustment,
1532 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1533 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1534 at the {top,left} of the sheet. If it's 1, then it'll
1535 be placed at the {bottom,right}.
1536 ROW or COL may be -1, in which case scrolling in that dimension
1540 psppire_sheet_moveto (PsppireSheet *sheet,
1548 g_return_if_fail (row_align >= 0);
1549 g_return_if_fail (col_align >= 0);
1551 g_return_if_fail (row_align <= 1);
1552 g_return_if_fail (col_align <= 1);
1554 g_return_if_fail (col <
1555 psppire_axis_unit_count (sheet->haxis));
1556 g_return_if_fail (row <
1557 psppire_axis_unit_count (sheet->vaxis));
1559 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1564 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1566 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1572 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1574 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1580 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
1582 g_return_if_fail (sheet != NULL);
1583 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1585 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
1588 if (sheet->state != PSPPIRE_SHEET_NORMAL)
1589 psppire_sheet_real_unselect_range (sheet, NULL);
1591 sheet->state = PSPPIRE_SHEET_ROW_SELECTED;
1592 sheet->range.row0 = row;
1593 sheet->range.col0 = 0;
1594 sheet->range.rowi = row;
1595 sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1596 sheet->active_cell.row = row;
1598 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
1599 psppire_sheet_real_select_range (sheet, NULL);
1604 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
1606 g_return_if_fail (sheet != NULL);
1607 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1609 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
1612 if (sheet->state != PSPPIRE_SHEET_NORMAL)
1613 psppire_sheet_real_unselect_range (sheet, NULL);
1615 sheet->state = PSPPIRE_SHEET_COLUMN_SELECTED;
1616 sheet->range.row0 = 0;
1617 sheet->range.col0 = column;
1618 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1619 sheet->range.coli = column;
1620 sheet->active_cell.col = column;
1622 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
1623 psppire_sheet_real_select_range (sheet, NULL);
1630 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1631 const PsppireSheetRange *range)
1633 g_return_val_if_fail (sheet != NULL, FALSE);
1635 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1638 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1641 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1644 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1647 if (range->rowi < min_visible_row (sheet))
1650 if (range->row0 > max_visible_row (sheet))
1653 if (range->coli < min_visible_column (sheet))
1656 if (range->col0 > max_visible_column (sheet))
1663 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1664 gint row, gint column)
1666 PsppireSheetRange range;
1669 range.col0 = column;
1671 range.coli = column;
1673 return psppire_sheet_range_isvisible (sheet, &range);
1677 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1679 g_return_if_fail (sheet != NULL);
1680 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1681 g_return_if_fail (range != NULL);
1683 range->row0 = min_visible_row (sheet);
1684 range->col0 = min_visible_column (sheet);
1685 range->rowi = max_visible_row (sheet);
1686 range->coli = max_visible_column (sheet);
1691 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1692 GtkAdjustment *hadjustment,
1693 GtkAdjustment *vadjustment)
1695 if ( sheet->vadjustment != vadjustment )
1697 if (sheet->vadjustment)
1698 g_object_unref (sheet->vadjustment);
1699 sheet->vadjustment = vadjustment;
1703 g_object_ref (vadjustment);
1705 g_signal_connect (sheet->vadjustment, "value_changed",
1706 G_CALLBACK (vadjustment_value_changed),
1711 if ( sheet->hadjustment != hadjustment )
1713 if (sheet->hadjustment)
1714 g_object_unref (sheet->hadjustment);
1716 sheet->hadjustment = hadjustment;
1720 g_object_ref (hadjustment);
1722 g_signal_connect (sheet->hadjustment, "value_changed",
1723 G_CALLBACK (hadjustment_value_changed),
1731 psppire_sheet_finalize (GObject *object)
1733 PsppireSheet *sheet;
1735 g_return_if_fail (object != NULL);
1736 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1738 sheet = PSPPIRE_SHEET (object);
1740 if (G_OBJECT_CLASS (parent_class)->finalize)
1741 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1745 psppire_sheet_dispose (GObject *object)
1747 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1749 g_return_if_fail (object != NULL);
1750 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1752 if ( sheet->dispose_has_run )
1755 sheet->dispose_has_run = TRUE;
1757 if (sheet->model) g_object_unref (sheet->model);
1758 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1759 if (sheet->haxis) g_object_unref (sheet->haxis);
1761 g_object_unref (sheet->button);
1762 sheet->button = NULL;
1764 /* unref adjustments */
1765 if (sheet->hadjustment)
1767 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1768 G_SIGNAL_MATCH_DATA,
1772 g_object_unref (sheet->hadjustment);
1773 sheet->hadjustment = NULL;
1776 if (sheet->vadjustment)
1778 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1779 G_SIGNAL_MATCH_DATA,
1783 g_object_unref (sheet->vadjustment);
1785 sheet->vadjustment = NULL;
1788 if (G_OBJECT_CLASS (parent_class)->dispose)
1789 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1793 psppire_sheet_style_set (GtkWidget *widget,
1794 GtkStyle *previous_style)
1796 PsppireSheet *sheet;
1798 g_return_if_fail (widget != NULL);
1799 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1801 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1802 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1804 sheet = PSPPIRE_SHEET (widget);
1806 if (GTK_WIDGET_REALIZED (widget))
1808 gtk_style_set_background (widget->style, widget->window, widget->state);
1811 set_entry_widget_font (sheet);
1814 #define BORDER_WIDTH 3
1817 psppire_sheet_realize (GtkWidget *widget)
1819 PsppireSheet *sheet;
1820 GdkWindowAttr attributes;
1821 const gint attributes_mask =
1822 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1825 GdkColormap *colormap;
1826 GdkDisplay *display;
1828 g_return_if_fail (widget != NULL);
1829 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1831 sheet = PSPPIRE_SHEET (widget);
1833 colormap = gtk_widget_get_colormap (widget);
1834 display = gtk_widget_get_display (widget);
1836 attributes.window_type = GDK_WINDOW_CHILD;
1837 attributes.x = widget->allocation.x;
1838 attributes.y = widget->allocation.y;
1839 attributes.width = widget->allocation.width;
1840 attributes.height = widget->allocation.height;
1841 attributes.wclass = GDK_INPUT_OUTPUT;
1843 attributes.visual = gtk_widget_get_visual (widget);
1844 attributes.colormap = colormap;
1846 attributes.event_mask = gtk_widget_get_events (widget);
1847 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1848 GDK_BUTTON_PRESS_MASK |
1849 GDK_BUTTON_RELEASE_MASK |
1850 GDK_KEY_PRESS_MASK |
1851 GDK_ENTER_NOTIFY_MASK |
1852 GDK_LEAVE_NOTIFY_MASK |
1853 GDK_POINTER_MOTION_MASK |
1854 GDK_POINTER_MOTION_HINT_MASK);
1856 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1859 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1861 gdk_window_set_user_data (widget->window, sheet);
1863 widget->style = gtk_style_attach (widget->style, widget->window);
1865 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1867 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1868 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1870 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1871 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1876 attributes.width = sheet->column_title_area.width;
1877 attributes.height = sheet->column_title_area.height;
1880 /* column - title window */
1881 sheet->column_title_window =
1882 gdk_window_new (widget->window, &attributes, attributes_mask);
1883 gdk_window_set_user_data (sheet->column_title_window, sheet);
1884 gtk_style_set_background (widget->style, sheet->column_title_window,
1890 attributes.width = sheet->row_title_area.width;
1891 attributes.height = sheet->row_title_area.height;
1893 /* row - title window */
1894 sheet->row_title_window = gdk_window_new (widget->window,
1895 &attributes, attributes_mask);
1896 gdk_window_set_user_data (sheet->row_title_window, sheet);
1897 gtk_style_set_background (widget->style, sheet->row_title_window,
1900 /* sheet - window */
1901 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1906 sheet->sheet_window = gdk_window_new (widget->window,
1907 &attributes, attributes_mask);
1908 gdk_window_set_user_data (sheet->sheet_window, sheet);
1910 gdk_cursor_unref (attributes.cursor);
1912 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1913 gdk_window_show (sheet->sheet_window);
1916 sheet->fg_gc = gdk_gc_new (widget->window);
1917 sheet->bg_gc = gdk_gc_new (widget->window);
1919 values.foreground = widget->style->white;
1920 values.function = GDK_INVERT;
1921 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1922 values.line_width = BORDER_WIDTH;
1924 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1933 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1934 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1936 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1937 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1940 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1942 if (sheet->column_titles_visible)
1943 gdk_window_show (sheet->column_title_window);
1944 if (sheet->row_titles_visible)
1945 gdk_window_show (sheet->row_title_window);
1947 sheet->hover_window = create_hover_window ();
1949 draw_row_title_buttons (sheet);
1950 draw_column_title_buttons (sheet);
1952 psppire_sheet_update_primary_selection (sheet);
1955 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1959 create_global_button (PsppireSheet *sheet)
1961 sheet->button = gtk_button_new_with_label (" ");
1963 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1965 g_object_ref_sink (sheet->button);
1967 g_signal_connect (sheet->button,
1969 G_CALLBACK (global_button_clicked),
1974 size_allocate_global_button (PsppireSheet *sheet)
1976 GtkAllocation allocation;
1978 if (!sheet->column_titles_visible) return;
1979 if (!sheet->row_titles_visible) return;
1981 gtk_widget_size_request (sheet->button, NULL);
1985 allocation.width = sheet->row_title_area.width;
1986 allocation.height = sheet->column_title_area.height;
1988 gtk_widget_size_allocate (sheet->button, &allocation);
1992 global_button_clicked (GtkWidget *widget, gpointer data)
1994 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
1999 psppire_sheet_unrealize (GtkWidget *widget)
2001 PsppireSheet *sheet;
2003 g_return_if_fail (widget != NULL);
2004 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2006 sheet = PSPPIRE_SHEET (widget);
2008 gdk_cursor_unref (sheet->cursor_drag);
2009 sheet->cursor_drag = NULL;
2011 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2012 sheet->color, n_COLORS);
2014 g_object_unref (sheet->xor_gc);
2015 g_object_unref (sheet->fg_gc);
2016 g_object_unref (sheet->bg_gc);
2018 destroy_hover_window (sheet->hover_window);
2020 gdk_window_destroy (sheet->sheet_window);
2021 gdk_window_destroy (sheet->column_title_window);
2022 gdk_window_destroy (sheet->row_title_window);
2024 gtk_widget_unparent (sheet->entry_widget);
2025 if (sheet->button != NULL)
2026 gtk_widget_unparent (sheet->button);
2028 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2029 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2033 psppire_sheet_map (GtkWidget *widget)
2035 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2037 g_return_if_fail (widget != NULL);
2038 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2040 if (!GTK_WIDGET_MAPPED (widget))
2042 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2044 gdk_window_show (widget->window);
2045 gdk_window_show (sheet->sheet_window);
2047 if (sheet->column_titles_visible)
2049 draw_column_title_buttons (sheet);
2050 gdk_window_show (sheet->column_title_window);
2052 if (sheet->row_titles_visible)
2054 draw_row_title_buttons (sheet);
2055 gdk_window_show (sheet->row_title_window);
2058 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2059 && sheet->active_cell.row >= 0
2060 && sheet->active_cell.col >= 0 )
2062 gtk_widget_show (sheet->entry_widget);
2063 gtk_widget_map (sheet->entry_widget);
2066 if (!GTK_WIDGET_MAPPED (sheet->button))
2068 gtk_widget_show (sheet->button);
2069 gtk_widget_map (sheet->button);
2072 redraw_range (sheet, NULL);
2073 change_active_cell (sheet,
2074 sheet->active_cell.row,
2075 sheet->active_cell.col);
2080 psppire_sheet_unmap (GtkWidget *widget)
2082 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2084 if (!GTK_WIDGET_MAPPED (widget))
2087 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2089 gdk_window_hide (sheet->sheet_window);
2090 if (sheet->column_titles_visible)
2091 gdk_window_hide (sheet->column_title_window);
2092 if (sheet->row_titles_visible)
2093 gdk_window_hide (sheet->row_title_window);
2094 gdk_window_hide (widget->window);
2096 if (GTK_WIDGET_MAPPED (sheet->entry_widget))
2097 gtk_widget_unmap (sheet->entry_widget);
2099 if (GTK_WIDGET_MAPPED (sheet->button))
2100 gtk_widget_unmap (sheet->button);
2103 /* get cell attributes of the given cell */
2104 /* TRUE means that the cell is currently allocated */
2105 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2107 PsppireSheetCellAttr *attributes);
2112 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2114 PangoLayout *layout;
2115 PangoRectangle text;
2116 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2121 PsppireSheetCellAttr attributes;
2124 g_return_if_fail (sheet != NULL);
2126 /* bail now if we aren't yet drawable */
2127 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2130 row >= psppire_axis_unit_count (sheet->vaxis))
2134 col >= psppire_axis_unit_count (sheet->haxis))
2137 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2139 /* select GC for background rectangle */
2140 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2141 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2143 rectangle_from_cell (sheet, row, col, &area);
2145 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2147 if (sheet->show_grid)
2149 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2151 gdk_draw_rectangle (sheet->sheet_window,
2155 area.width, area.height);
2159 label = psppire_sheet_cell_get_text (sheet, row, col);
2164 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2165 dispose_string (sheet, label);
2168 pango_layout_set_font_description (layout, font_desc);
2170 pango_layout_get_pixel_extents (layout, NULL, &text);
2172 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2174 font_height = pango_font_description_get_size (font_desc);
2175 if ( !pango_font_description_get_size_is_absolute (font_desc))
2176 font_height /= PANGO_SCALE;
2178 /* Centre the text vertically */
2179 area.y += (area.height - font_height) / 2.0;
2181 switch (attributes.justification)
2183 case GTK_JUSTIFY_RIGHT:
2184 area.x += area.width - text.width;
2186 case GTK_JUSTIFY_CENTER:
2187 area.x += (area.width - text.width) / 2.0;
2189 case GTK_JUSTIFY_LEFT:
2193 g_critical ("Unhandled justification %d in column %d\n",
2194 attributes.justification, col);
2198 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2203 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2204 g_object_unref (layout);
2209 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2211 PsppireSheetRange range;
2216 PsppireSheetRange drawing_range;
2218 gdk_region_get_clipbox (region, &area);
2220 y = area.y + sheet->vadjustment->value;
2221 x = area.x + sheet->hadjustment->value;
2223 if ( sheet->column_titles_visible)
2224 y -= sheet->column_title_area.height;
2226 if ( sheet->row_titles_visible)
2227 x -= sheet->row_title_area.width;
2229 maximize_int (&x, 0);
2230 maximize_int (&y, 0);
2232 range.row0 = row_from_ypixel (sheet, y);
2233 range.rowi = row_from_ypixel (sheet, y + area.height);
2235 range.col0 = column_from_xpixel (sheet, x);
2236 range.coli = column_from_xpixel (sheet, x + area.width);
2238 g_return_if_fail (sheet != NULL);
2239 g_return_if_fail (PSPPIRE_SHEET (sheet));
2241 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2242 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2243 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2246 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2247 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2248 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2249 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2251 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2252 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2254 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2256 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2257 psppire_sheet_cell_draw (sheet, i, j);
2260 if (sheet->state != PSPPIRE_SHEET_NORMAL &&
2261 psppire_sheet_range_isvisible (sheet, &sheet->range))
2262 psppire_sheet_range_draw_selection (sheet, drawing_range);
2265 if (sheet->state == GTK_STATE_NORMAL &&
2266 sheet->active_cell.row >= drawing_range.row0 &&
2267 sheet->active_cell.row <= drawing_range.rowi &&
2268 sheet->active_cell.col >= drawing_range.col0 &&
2269 sheet->active_cell.col <= drawing_range.coli)
2270 psppire_sheet_show_entry_widget (sheet);
2275 psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range)
2279 PsppireSheetRange aux;
2281 if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
2282 range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
2285 if (!psppire_sheet_range_isvisible (sheet, &range)) return;
2286 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2290 range.col0 = MAX (sheet->range.col0, range.col0);
2291 range.coli = MIN (sheet->range.coli, range.coli);
2292 range.row0 = MAX (sheet->range.row0, range.row0);
2293 range.rowi = MIN (sheet->range.rowi, range.rowi);
2295 range.col0 = MAX (range.col0, min_visible_column (sheet));
2296 range.coli = MIN (range.coli, max_visible_column (sheet));
2297 range.row0 = MAX (range.row0, min_visible_row (sheet));
2298 range.rowi = MIN (range.rowi, max_visible_row (sheet));
2300 for (i = range.row0; i <= range.rowi; i++)
2302 for (j = range.col0; j <= range.coli; j++)
2304 if (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED)
2306 rectangle_from_cell (sheet, i, j, &area);
2308 if (i == sheet->range.row0)
2310 area.y = area.y + 2;
2311 area.height = area.height - 2;
2313 if (i == sheet->range.rowi) area.height = area.height - 3;
2314 if (j == sheet->range.col0)
2316 area.x = area.x + 2;
2317 area.width = area.width - 2;
2319 if (j == sheet->range.coli) area.width = area.width - 3;
2321 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2323 gdk_draw_rectangle (sheet->sheet_window,
2326 area.x + 1, area.y + 1,
2327 area.width, area.height);
2334 psppire_sheet_draw_border (sheet, sheet->range);
2338 safe_strcmp (const gchar *s1, const gchar *s2)
2340 if ( !s1 && !s2) return 0;
2341 if ( !s1) return -1;
2342 if ( !s2) return +1;
2343 return strcmp (s1, s2);
2347 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2348 GtkJustification justification,
2351 PsppireSheetModel *model ;
2354 g_return_if_fail (sheet != NULL);
2355 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2357 if (col >= psppire_axis_unit_count (sheet->haxis)
2358 || row >= psppire_axis_unit_count (sheet->vaxis))
2361 if (col < 0 || row < 0) return;
2363 model = psppire_sheet_get_model (sheet);
2365 old_text = psppire_sheet_model_get_string (model, row, col);
2367 if (0 != safe_strcmp (old_text, text))
2369 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2370 psppire_sheet_model_set_string (model, text, row, col);
2371 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2374 if ( psppire_sheet_model_free_strings (model))
2380 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2382 PsppireSheetRange range;
2384 g_return_if_fail (sheet != NULL);
2385 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2386 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2387 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2389 if (column < 0 || row < 0) return;
2393 range.col0 = min_visible_column (sheet);
2394 range.coli = max_visible_column (sheet);
2396 psppire_sheet_real_cell_clear (sheet, row, column);
2398 redraw_range (sheet, &range);
2402 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2404 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2406 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2408 if (old_text && strlen (old_text) > 0 )
2410 psppire_sheet_model_datum_clear (model, row, column);
2413 dispose_string (sheet, old_text);
2417 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2419 PsppireSheetModel *model;
2420 g_return_val_if_fail (sheet != NULL, NULL);
2421 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2423 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2425 if (col < 0 || row < 0) return NULL;
2427 model = psppire_sheet_get_model (sheet);
2432 return psppire_sheet_model_get_string (model, row, col);
2437 psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
2440 PsppireSheetRange *range;
2442 g_return_val_if_fail (sheet != NULL, 0);
2443 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2444 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
2445 if (col < 0 || row < 0) return 0;
2447 state = sheet->state;
2448 range = &sheet->range;
2452 case PSPPIRE_SHEET_NORMAL:
2453 return GTK_STATE_NORMAL;
2455 case PSPPIRE_SHEET_ROW_SELECTED:
2456 if (row >= range->row0 && row <= range->rowi)
2457 return GTK_STATE_SELECTED;
2459 case PSPPIRE_SHEET_COLUMN_SELECTED:
2460 if (col >= range->col0 && col <= range->coli)
2461 return GTK_STATE_SELECTED;
2463 case PSPPIRE_SHEET_RANGE_SELECTED:
2464 if (row >= range->row0 && row <= range->rowi && \
2465 col >= range->col0 && col <= range->coli)
2466 return GTK_STATE_SELECTED;
2469 return GTK_STATE_NORMAL;
2472 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2473 If the function returns FALSE, then the results will be unreliable.
2476 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2484 *column = -G_MAXINT;
2486 g_return_val_if_fail (sheet != NULL, 0);
2487 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2489 /* bounds checking, return false if the user clicked
2497 if ( sheet->column_titles_visible)
2498 y -= sheet->column_title_area.height;
2500 y += sheet->vadjustment->value;
2502 if ( y < 0 && sheet->column_titles_visible)
2508 trow = row_from_ypixel (sheet, y);
2509 if (trow > psppire_axis_unit_count (sheet->vaxis))
2515 if ( sheet->row_titles_visible)
2516 x -= sheet->row_title_area.width;
2518 x += sheet->hadjustment->value;
2520 if ( x < 0 && sheet->row_titles_visible)
2526 tcol = column_from_xpixel (sheet, x);
2527 if (tcol > psppire_axis_unit_count (sheet->haxis))
2537 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2542 g_return_val_if_fail (sheet != NULL, 0);
2543 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2545 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2548 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2549 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2551 area->width= (column == -1) ? sheet->row_title_area.width
2552 : psppire_axis_unit_size (sheet->haxis, column);
2554 area->height= (row == -1) ? sheet->column_title_area.height
2555 : psppire_axis_unit_size (sheet->vaxis, row);
2561 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2563 g_return_if_fail (sheet != NULL);
2564 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2566 if (row < -1 || col < -1)
2569 if (row >= psppire_axis_unit_count (sheet->vaxis)
2571 col >= psppire_axis_unit_count (sheet->haxis))
2574 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2577 if ( row == -1 || col == -1)
2579 psppire_sheet_hide_entry_widget (sheet);
2583 change_active_cell (sheet, row, col);
2587 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2589 g_return_if_fail (sheet != NULL);
2590 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2592 if ( row ) *row = sheet->active_cell.row;
2593 if (column) *column = sheet->active_cell.col;
2597 entry_load_text (PsppireSheet *sheet)
2601 GtkJustification justification;
2602 PsppireSheetCellAttr attributes;
2604 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2605 if (sheet->state != GTK_STATE_NORMAL) return;
2607 row = sheet->active_cell.row;
2608 col = sheet->active_cell.col;
2610 if (row < 0 || col < 0) return;
2612 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2614 if (text && strlen (text) > 0)
2616 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2617 justification = attributes.justification;
2618 psppire_sheet_set_cell (sheet, row, col, justification, text);
2624 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2626 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2629 if (sheet->active_cell.row < 0 ||
2630 sheet->active_cell.col < 0) return;
2632 gtk_widget_hide (sheet->entry_widget);
2633 gtk_widget_unmap (sheet->entry_widget);
2635 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2639 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2641 gint old_row, old_col;
2643 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2645 if (row < 0 || col < 0)
2648 if ( row > psppire_axis_unit_count (sheet->vaxis)
2649 || col > psppire_axis_unit_count (sheet->haxis))
2652 if (sheet->state != PSPPIRE_SHEET_NORMAL)
2654 sheet->state = PSPPIRE_SHEET_NORMAL;
2655 psppire_sheet_real_unselect_range (sheet, NULL);
2658 old_row = sheet->active_cell.row;
2659 old_col = sheet->active_cell.col;
2661 /* Erase the old cell */
2662 psppire_sheet_draw_active_cell (sheet);
2664 entry_load_text (sheet);
2666 sheet->range.row0 = row;
2667 sheet->range.col0 = col;
2668 sheet->range.rowi = row;
2669 sheet->range.coli = col;
2670 sheet->active_cell.row = row;
2671 sheet->active_cell.col = col;
2672 sheet->selection_cell.row = row;
2673 sheet->selection_cell.col = col;
2675 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2677 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2679 psppire_sheet_draw_active_cell (sheet);
2680 psppire_sheet_show_entry_widget (sheet);
2682 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2684 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2685 row, col, old_row, old_col);
2690 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2692 GtkEntry *sheet_entry;
2693 PsppireSheetCellAttr attributes;
2697 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2699 row = sheet->active_cell.row;
2700 col = sheet->active_cell.col;
2702 /* Don't show the active cell, if there is no active cell: */
2703 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2706 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2707 if (sheet->state != PSPPIRE_SHEET_NORMAL) return;
2708 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2710 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2712 sheet_entry = psppire_sheet_get_entry (sheet);
2714 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2716 if (GTK_IS_ENTRY (sheet_entry))
2718 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2719 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2722 text = g_strdup ("");
2724 if (strcmp (old_text, text) != 0)
2725 gtk_entry_set_text (sheet_entry, text);
2727 dispose_string (sheet, text);
2730 switch (attributes.justification)
2732 case GTK_JUSTIFY_RIGHT:
2733 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2735 case GTK_JUSTIFY_CENTER:
2736 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2738 case GTK_JUSTIFY_LEFT:
2740 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2746 psppire_sheet_size_allocate_entry (sheet);
2748 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2749 psppire_sheet_model_is_editable (sheet->model,
2751 gtk_widget_map (sheet->entry_widget);
2755 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2758 PsppireSheetRange range;
2760 row = sheet->active_cell.row;
2761 col = sheet->active_cell.col;
2763 if (row < 0 || col < 0) return FALSE;
2765 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2768 range.col0 = range.coli = col;
2769 range.row0 = range.rowi = row;
2771 psppire_sheet_draw_border (sheet, range);
2779 psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range)
2781 gint i, j, mask1, mask2;
2782 gint state, selected;
2783 gint x, y, width, height;
2784 PsppireSheetRange new_range, aux_range;
2786 g_return_if_fail (sheet != NULL);
2788 if (range == NULL) range=&sheet->range;
2792 range->row0 = MIN (range->row0, sheet->range.row0);
2793 range->rowi = MAX (range->rowi, sheet->range.rowi);
2794 range->col0 = MIN (range->col0, sheet->range.col0);
2795 range->coli = MAX (range->coli, sheet->range.coli);
2797 range->row0 = MAX (range->row0, min_visible_row (sheet));
2798 range->rowi = MIN (range->rowi, max_visible_row (sheet));
2799 range->col0 = MAX (range->col0, min_visible_column (sheet));
2800 range->coli = MIN (range->coli, max_visible_column (sheet));
2802 aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
2803 aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
2804 aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
2805 aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
2807 for (i = range->row0; i <= range->rowi; i++)
2809 for (j = range->col0; j <= range->coli; j++)
2812 state = psppire_sheet_cell_get_state (sheet, i, j);
2813 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2814 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2816 if (state == GTK_STATE_SELECTED && selected &&
2817 (i == sheet->range.row0 || i == sheet->range.rowi ||
2818 j == sheet->range.col0 || j == sheet->range.coli ||
2819 i == new_range.row0 || i == new_range.rowi ||
2820 j == new_range.col0 || j == new_range.coli))
2823 mask1 = i == sheet->range.row0 ? 1 : 0;
2824 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2825 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2826 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2828 mask2 = i == new_range.row0 ? 1 : 0;
2829 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2830 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2831 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2835 x = psppire_axis_start_pixel (sheet->haxis, j);
2836 y = psppire_axis_start_pixel (sheet->vaxis, i);
2837 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2838 psppire_axis_unit_size (sheet->haxis, j);
2839 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2841 if (i == sheet->range.row0)
2844 height = height + 3;
2846 if (i == sheet->range.rowi) height = height + 3;
2847 if (j == sheet->range.col0)
2852 if (j == sheet->range.coli) width = width + 3;
2854 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2856 x = psppire_axis_start_pixel (sheet->haxis, j);
2857 y = psppire_axis_start_pixel (sheet->vaxis, i);
2858 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2859 psppire_axis_unit_size (sheet->haxis, j);
2861 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2863 if (i == new_range.row0)
2866 height = height - 2;
2868 if (i == new_range.rowi) height = height - 3;
2869 if (j == new_range.col0)
2874 if (j == new_range.coli) width = width - 3;
2876 gdk_draw_rectangle (sheet->sheet_window,
2887 for (i = range->row0; i <= range->rowi; i++)
2889 for (j = range->col0; j <= range->coli; j++)
2892 state = psppire_sheet_cell_get_state (sheet, i, j);
2893 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2894 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2896 if (state == GTK_STATE_SELECTED && !selected)
2899 x = psppire_axis_start_pixel (sheet->haxis, j);
2900 y = psppire_axis_start_pixel (sheet->vaxis, i);
2901 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2902 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2904 if (i == sheet->range.row0)
2907 height = height + 3;
2909 if (i == sheet->range.rowi) height = height + 3;
2910 if (j == sheet->range.col0)
2915 if (j == sheet->range.coli) width = width + 3;
2921 for (i = range->row0; i <= range->rowi; i++)
2923 for (j = range->col0; j <= range->coli; j++)
2926 state = psppire_sheet_cell_get_state (sheet, i, j);
2927 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2928 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2930 if (state != GTK_STATE_SELECTED && selected &&
2931 (i != sheet->active_cell.row || j != sheet->active_cell.col))
2934 x = psppire_axis_start_pixel (sheet->haxis, j);
2935 y = psppire_axis_start_pixel (sheet->vaxis, i);
2936 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2937 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2939 if (i == new_range.row0)
2942 height = height - 2;
2944 if (i == new_range.rowi) height = height - 3;
2945 if (j == new_range.col0)
2950 if (j == new_range.coli) width = width - 3;
2952 gdk_draw_rectangle (sheet->sheet_window,
2963 for (i = aux_range.row0; i <= aux_range.rowi; i++)
2965 for (j = aux_range.col0; j <= aux_range.coli; j++)
2967 state = psppire_sheet_cell_get_state (sheet, i, j);
2969 mask1 = i == sheet->range.row0 ? 1 : 0;
2970 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2971 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2972 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2974 mask2 = i == new_range.row0 ? 1 : 0;
2975 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2976 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2977 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2978 if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
2980 x = psppire_axis_start_pixel (sheet->haxis, j);
2981 y = psppire_axis_start_pixel (sheet->vaxis, i);
2982 width = psppire_axis_unit_size (sheet->haxis, j);
2983 height = psppire_axis_unit_size (sheet->vaxis, i);
2985 gdk_draw_rectangle (sheet->sheet_window,
2993 gdk_draw_rectangle (sheet->sheet_window,
2996 x + 1, y + height - 1,
3000 gdk_draw_rectangle (sheet->sheet_window,
3008 gdk_draw_rectangle (sheet->sheet_window,
3011 x + width - 1, y + 1,
3023 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
3027 rectangle_from_range (sheet, &new_range, &area);
3029 gdk_draw_rectangle (sheet->sheet_window,
3033 area.width, area.height);
3038 psppire_sheet_real_select_range (PsppireSheet *sheet,
3039 const PsppireSheetRange *range)
3043 g_return_if_fail (sheet != NULL);
3045 if (range == NULL) range = &sheet->range;
3047 memcpy (&sheet->range, range, sizeof (*range));
3049 if (range->row0 < 0 || range->rowi < 0) return;
3050 if (range->col0 < 0 || range->coli < 0) return;
3052 state = sheet->state;
3055 if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3056 range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3058 psppire_sheet_new_selection (sheet, &sheet->range);
3062 psppire_sheet_range_draw_selection (sheet, sheet->range);
3066 psppire_sheet_update_primary_selection (sheet);
3068 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3073 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
3075 g_return_if_fail (sheet != NULL);
3076 *range = sheet->range;
3081 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
3083 g_return_if_fail (sheet != NULL);
3085 if (range == NULL) range=&sheet->range;
3087 if (range->row0 < 0 || range->rowi < 0) return;
3088 if (range->col0 < 0 || range->coli < 0) return;
3091 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3092 psppire_sheet_real_unselect_range (sheet, NULL);
3094 sheet->range.row0 = range->row0;
3095 sheet->range.rowi = range->rowi;
3096 sheet->range.col0 = range->col0;
3097 sheet->range.coli = range->coli;
3098 sheet->active_cell.row = range->row0;
3099 sheet->active_cell.col = range->col0;
3100 sheet->selection_cell.row = range->rowi;
3101 sheet->selection_cell.col = range->coli;
3103 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3104 psppire_sheet_real_select_range (sheet, NULL);
3108 psppire_sheet_unselect_range (PsppireSheet *sheet)
3110 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3113 psppire_sheet_real_unselect_range (sheet, NULL);
3114 sheet->state = GTK_STATE_NORMAL;
3116 change_active_cell (sheet,
3117 sheet->active_cell.row, sheet->active_cell.col);
3122 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
3123 const PsppireSheetRange *range)
3125 g_return_if_fail (sheet != NULL);
3126 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3129 range = &sheet->range;
3131 if (range->row0 < 0 || range->rowi < 0) return;
3132 if (range->col0 < 0 || range->coli < 0) return;
3134 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3135 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3137 sheet->range.row0 = -1;
3138 sheet->range.rowi = -1;
3139 sheet->range.col0 = -1;
3140 sheet->range.coli = -1;
3145 psppire_sheet_expose (GtkWidget *widget,
3146 GdkEventExpose *event)
3148 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3150 g_return_val_if_fail (event != NULL, FALSE);
3152 if (!GTK_WIDGET_DRAWABLE (widget))
3155 /* exposure events on the sheet */
3156 if (event->window == sheet->row_title_window &&
3157 sheet->row_titles_visible)
3159 draw_row_title_buttons_range (sheet,
3160 min_visible_row (sheet),
3161 max_visible_row (sheet));
3164 if (event->window == sheet->column_title_window &&
3165 sheet->column_titles_visible)
3167 draw_column_title_buttons_range (sheet,
3168 min_visible_column (sheet),
3169 max_visible_column (sheet));
3172 if (event->window == sheet->sheet_window)
3174 draw_sheet_region (sheet, event->region);
3177 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3179 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3180 psppire_sheet_range_draw (sheet, &sheet->range);
3182 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3183 psppire_sheet_range_draw (sheet, &sheet->drag_range);
3185 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3186 psppire_sheet_range_draw_selection (sheet, sheet->range);
3187 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3188 draw_xor_rectangle (sheet, sheet->drag_range);
3192 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
3195 PsppireSheetRange range;
3196 range.row0 = range.rowi = sheet->active_cell.row;
3197 range.col0 = range.coli = sheet->active_cell.col;
3199 rectangle_from_range (sheet, &range, &rect);
3201 if (GDK_OVERLAP_RECTANGLE_OUT !=
3202 gdk_region_rect_in (event->region, &rect))
3204 psppire_sheet_draw_active_cell (sheet);
3210 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3217 psppire_sheet_button_press (GtkWidget *widget,
3218 GdkEventButton *event)
3220 PsppireSheet *sheet;
3221 GdkModifierType mods;
3226 g_return_val_if_fail (widget != NULL, FALSE);
3227 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
3228 g_return_val_if_fail (event != NULL, FALSE);
3230 sheet = PSPPIRE_SHEET (widget);
3232 /* Cancel any pending tooltips */
3233 if (sheet->motion_timer)
3235 g_source_remove (sheet->motion_timer);
3236 sheet->motion_timer = 0;
3239 gtk_widget_get_pointer (widget, &x, &y);
3240 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3243 if (event->window == sheet->column_title_window)
3245 sheet->x_drag = event->x;
3246 g_signal_emit (sheet,
3247 sheet_signals[BUTTON_EVENT_COLUMN], 0,
3250 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3252 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3253 g_signal_emit (sheet,
3254 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3257 else if (event->window == sheet->row_title_window)
3259 g_signal_emit (sheet,
3260 sheet_signals[BUTTON_EVENT_ROW], 0,
3263 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3265 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3266 g_signal_emit (sheet,
3267 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3271 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3273 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3276 /* press on resize windows */
3277 if (event->window == sheet->column_title_window)
3279 sheet->x_drag = event->x;
3281 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3283 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3284 gdk_pointer_grab (sheet->column_title_window, FALSE,
3285 GDK_POINTER_MOTION_HINT_MASK |
3286 GDK_BUTTON1_MOTION_MASK |
3287 GDK_BUTTON_RELEASE_MASK,
3288 NULL, NULL, event->time);
3290 draw_xor_vline (sheet);
3295 if (event->window == sheet->row_title_window)
3297 sheet->y_drag = event->y;
3299 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3301 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3302 gdk_pointer_grab (sheet->row_title_window, FALSE,
3303 GDK_POINTER_MOTION_HINT_MASK |
3304 GDK_BUTTON1_MOTION_MASK |
3305 GDK_BUTTON_RELEASE_MASK,
3306 NULL, NULL, event->time);
3308 draw_xor_hline (sheet);
3313 /* the sheet itself does not handle other than single click events */
3314 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3316 /* selections on the sheet */
3317 if (event->window == sheet->sheet_window)
3319 gtk_widget_get_pointer (widget, &x, &y);
3320 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3321 gdk_pointer_grab (sheet->sheet_window, FALSE,
3322 GDK_POINTER_MOTION_HINT_MASK |
3323 GDK_BUTTON1_MOTION_MASK |
3324 GDK_BUTTON_RELEASE_MASK,
3325 NULL, NULL, event->time);
3326 gtk_grab_add (GTK_WIDGET (sheet));
3328 if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
3329 sheet->selection_mode != GTK_SELECTION_NONE &&
3330 sheet->cursor_drag->type == GDK_SIZING &&
3331 !PSPPIRE_SHEET_IN_SELECTION (sheet) && !PSPPIRE_SHEET_IN_RESIZE (sheet))
3333 if (sheet->state == GTK_STATE_NORMAL)
3335 row = sheet->active_cell.row;
3336 column = sheet->active_cell.col;
3337 sheet->active_cell.row = row;
3338 sheet->active_cell.col = column;
3339 sheet->drag_range = sheet->range;
3340 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3341 psppire_sheet_select_range (sheet, &sheet->drag_range);
3345 if (row > sheet->range.rowi) row--;
3346 if (column > sheet->range.coli) column--;
3347 sheet->drag_cell.row = row;
3348 sheet->drag_cell.col = column;
3349 sheet->drag_range = sheet->range;
3350 draw_xor_rectangle (sheet, sheet->drag_range);
3351 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3353 else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
3354 !PSPPIRE_SHEET_IN_SELECTION (sheet)
3355 && ! PSPPIRE_SHEET_IN_DRAG (sheet)
3356 && sheet->active_cell.row >= 0
3357 && sheet->active_cell.col >= 0
3360 if (sheet->state == GTK_STATE_NORMAL)
3362 row = sheet->active_cell.row;
3363 column = sheet->active_cell.col;
3364 sheet->active_cell.row = row;
3365 sheet->active_cell.col = column;
3366 sheet->drag_range = sheet->range;
3367 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3368 psppire_sheet_select_range (sheet, &sheet->drag_range);
3372 if (row < sheet->range.row0) row++;
3373 if (row > sheet->range.rowi) row--;
3374 if (column < sheet->range.col0) column++;
3375 if (column > sheet->range.coli) column--;
3376 sheet->drag_cell.row = row;
3377 sheet->drag_cell.col = column;
3378 sheet->drag_range = sheet->range;
3379 draw_xor_rectangle (sheet, sheet->drag_range);
3380 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3384 veto = psppire_sheet_click_cell (sheet, row, column);
3385 if (veto) PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3389 if (event->window == sheet->column_title_window)
3391 gtk_widget_get_pointer (widget, &x, &y);
3392 if ( sheet->row_titles_visible)
3393 x -= sheet->row_title_area.width;
3395 x += sheet->hadjustment->value;
3397 column = column_from_xpixel (sheet, x);
3399 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3401 veto = psppire_sheet_click_cell (sheet, -1, column);
3402 gtk_grab_add (GTK_WIDGET (sheet));
3403 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3407 if (event->window == sheet->row_title_window)
3409 gtk_widget_get_pointer (widget, &x, &y);
3410 if ( sheet->column_titles_visible)
3411 y -= sheet->column_title_area.height;
3413 y += sheet->vadjustment->value;
3415 row = row_from_ypixel (sheet, y);
3416 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3418 veto = psppire_sheet_click_cell (sheet, row, -1);
3419 gtk_grab_add (GTK_WIDGET (sheet));
3420 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3428 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3430 PsppireSheetCell cell;
3431 gboolean forbid_move;
3436 if (row >= psppire_axis_unit_count (sheet->vaxis)
3437 || column >= psppire_axis_unit_count (sheet->haxis))
3442 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3443 &sheet->active_cell,
3449 if (sheet->state == GTK_STATE_NORMAL)
3452 row = sheet->active_cell.row;
3453 column = sheet->active_cell.col;
3455 change_active_cell (sheet, row, column);
3459 if (row == -1 && column >= 0)
3461 psppire_sheet_select_column (sheet, column);
3465 if (column == -1 && row >= 0)
3467 psppire_sheet_select_row (sheet, row);
3471 if (row == -1 && column == -1)
3473 sheet->range.row0 = 0;
3474 sheet->range.col0 = 0;
3475 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3477 psppire_axis_unit_count (sheet->haxis) - 1;
3478 sheet->active_cell.row = 0;
3479 sheet->active_cell.col = 0;
3480 psppire_sheet_select_range (sheet, NULL);
3484 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3486 sheet->state = PSPPIRE_SHEET_NORMAL;
3487 psppire_sheet_real_unselect_range (sheet, NULL);
3491 change_active_cell (sheet, row, column);
3494 sheet->active_cell.row = row;
3495 sheet->active_cell.col = column;
3496 sheet->selection_cell.row = row;
3497 sheet->selection_cell.col = column;
3498 sheet->range.row0 = row;
3499 sheet->range.col0 = column;
3500 sheet->range.rowi = row;
3501 sheet->range.coli = column;
3502 sheet->state = PSPPIRE_SHEET_NORMAL;
3503 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3505 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3511 psppire_sheet_button_release (GtkWidget *widget,
3512 GdkEventButton *event)
3514 GdkDisplay *display = gtk_widget_get_display (widget);
3516 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3518 /* release on resize windows */
3519 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3522 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3523 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3525 gdk_display_pointer_ungrab (display, event->time);
3526 draw_xor_vline (sheet);
3529 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3530 + sheet->hadjustment->value;
3532 set_column_width (sheet, sheet->drag_cell.col, width);
3537 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3540 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3541 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3543 gdk_display_pointer_ungrab (display, event->time);
3544 draw_xor_hline (sheet);
3547 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3548 sheet->vadjustment->value;
3550 set_row_height (sheet, sheet->drag_cell.row, height);
3555 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3557 PsppireSheetRange old_range;
3558 draw_xor_rectangle (sheet, sheet->drag_range);
3559 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3560 gdk_display_pointer_ungrab (display, event->time);
3562 psppire_sheet_real_unselect_range (sheet, NULL);
3564 sheet->active_cell.row = sheet->active_cell.row +
3565 (sheet->drag_range.row0 - sheet->range.row0);
3566 sheet->active_cell.col = sheet->active_cell.col +
3567 (sheet->drag_range.col0 - sheet->range.col0);
3568 sheet->selection_cell.row = sheet->selection_cell.row +
3569 (sheet->drag_range.row0 - sheet->range.row0);
3570 sheet->selection_cell.col = sheet->selection_cell.col +
3571 (sheet->drag_range.col0 - sheet->range.col0);
3572 old_range = sheet->range;
3573 sheet->range = sheet->drag_range;
3574 sheet->drag_range = old_range;
3575 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3576 &sheet->drag_range, &sheet->range);
3577 psppire_sheet_select_range (sheet, &sheet->range);
3580 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3582 PsppireSheetRange old_range;
3583 draw_xor_rectangle (sheet, sheet->drag_range);
3584 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3585 gdk_display_pointer_ungrab (display, event->time);
3587 psppire_sheet_real_unselect_range (sheet, NULL);
3589 sheet->active_cell.row = sheet->active_cell.row +
3590 (sheet->drag_range.row0 - sheet->range.row0);
3591 sheet->active_cell.col = sheet->active_cell.col +
3592 (sheet->drag_range.col0 - sheet->range.col0);
3593 if (sheet->drag_range.row0 < sheet->range.row0)
3594 sheet->selection_cell.row = sheet->drag_range.row0;
3595 if (sheet->drag_range.rowi >= sheet->range.rowi)
3596 sheet->selection_cell.row = sheet->drag_range.rowi;
3597 if (sheet->drag_range.col0 < sheet->range.col0)
3598 sheet->selection_cell.col = sheet->drag_range.col0;
3599 if (sheet->drag_range.coli >= sheet->range.coli)
3600 sheet->selection_cell.col = sheet->drag_range.coli;
3601 old_range = sheet->range;
3602 sheet->range = sheet->drag_range;
3603 sheet->drag_range = old_range;
3605 if (sheet->state == GTK_STATE_NORMAL) sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3606 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3607 &sheet->drag_range, &sheet->range);
3608 psppire_sheet_select_range (sheet, &sheet->range);
3611 if (sheet->state == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet))
3613 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3614 gdk_display_pointer_ungrab (display, event->time);
3615 change_active_cell (sheet, sheet->active_cell.row,
3616 sheet->active_cell.col);
3619 if (PSPPIRE_SHEET_IN_SELECTION)
3620 gdk_display_pointer_ungrab (display, event->time);
3621 gtk_grab_remove (GTK_WIDGET (sheet));
3623 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3632 /* Shamelessly lifted from gtktooltips */
3634 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3638 gtk_widget_size_request (tip_window, &req);
3639 gtk_paint_flat_box (tip_window->style, tip_window->window,
3640 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3641 NULL, GTK_WIDGET(tip_window), "tooltip",
3642 0, 0, req.width, req.height);
3648 destroy_hover_window (PsppireSheetHoverTitle *h)
3650 gtk_widget_destroy (h->window);
3654 static PsppireSheetHoverTitle *
3655 create_hover_window (void)
3657 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3659 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3661 #if GTK_CHECK_VERSION (2, 9, 0)
3662 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3663 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3666 gtk_widget_set_app_paintable (hw->window, TRUE);
3667 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3668 gtk_widget_set_name (hw->window, "gtk-tooltips");
3669 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3671 g_signal_connect (hw->window,
3673 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3676 hw->label = gtk_label_new (NULL);
3679 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3680 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3682 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3684 gtk_widget_show (hw->label);
3686 g_signal_connect (hw->window,
3688 G_CALLBACK (gtk_widget_destroyed),
3694 #define HOVER_WINDOW_Y_OFFSET 2
3697 show_subtitle (PsppireSheet *sheet, gint row, gint column, const gchar *subtitle)
3706 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3710 sheet->hover_window->row = row;
3711 sheet->hover_window->column = column;
3713 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3715 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3717 gtk_widget_show (sheet->hover_window->window);
3719 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3725 y += sheet->column_title_area.y;
3726 y += sheet->column_title_area.height;
3727 y += HOVER_WINDOW_Y_OFFSET;
3733 x += sheet->row_title_area.x;
3734 x += sheet->row_title_area.width * 2 / 3.0;
3737 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3742 motion_timeout_callback (gpointer data)
3744 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3747 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3749 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3751 if (sheet->row_title_under && row >= 0)
3753 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3755 show_subtitle (sheet, row, -1, text);
3759 if (sheet->column_title_under && column >= 0)
3761 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3764 show_subtitle (sheet, -1, column, text);
3774 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3776 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3777 GdkModifierType mods;
3778 GdkCursorType new_cursor;
3781 GdkDisplay *display;
3783 g_return_val_if_fail (event != NULL, FALSE);
3785 display = gtk_widget_get_display (widget);
3787 /* selections on the sheet */
3791 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3793 if ( sheet->motion_timer > 0 )
3794 g_source_remove (sheet->motion_timer);
3795 sheet->motion_timer =
3796 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3802 gtk_widget_get_pointer (widget, &wx, &wy);
3804 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3806 if ( row != sheet->hover_window->row ||
3807 column != sheet->hover_window->column)
3809 gtk_widget_hide (sheet->hover_window->window);
3814 if (event->window == sheet->column_title_window)
3816 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3817 on_column_boundary (sheet, x, &column))
3819 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3820 if (new_cursor != sheet->cursor_drag->type)
3822 gdk_cursor_unref (sheet->cursor_drag);
3823 sheet->cursor_drag =
3824 gdk_cursor_new_for_display (display, new_cursor);
3826 gdk_window_set_cursor (sheet->column_title_window,
3827 sheet->cursor_drag);
3832 new_cursor = GDK_TOP_LEFT_ARROW;
3833 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3834 new_cursor != sheet->cursor_drag->type)
3836 gdk_cursor_unref (sheet->cursor_drag);
3837 sheet->cursor_drag =
3838 gdk_cursor_new_for_display (display, new_cursor);
3839 gdk_window_set_cursor (sheet->column_title_window,
3840 sheet->cursor_drag);
3844 else if (event->window == sheet->row_title_window)
3846 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3847 on_row_boundary (sheet, y, &row))
3849 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3850 if (new_cursor != sheet->cursor_drag->type)
3852 gdk_cursor_unref (sheet->cursor_drag);
3853 sheet->cursor_drag =
3854 gdk_cursor_new_for_display (display, new_cursor);
3855 gdk_window_set_cursor (sheet->row_title_window,
3856 sheet->cursor_drag);
3861 new_cursor = GDK_TOP_LEFT_ARROW;
3862 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3863 new_cursor != sheet->cursor_drag->type)
3865 gdk_cursor_unref (sheet->cursor_drag);
3866 sheet->cursor_drag =
3867 gdk_cursor_new_for_display (display, new_cursor);
3868 gdk_window_set_cursor (sheet->row_title_window,
3869 sheet->cursor_drag);
3874 new_cursor = GDK_PLUS;
3875 if ( event->window == sheet->sheet_window &&
3876 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3877 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3878 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3879 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3880 new_cursor != sheet->cursor_drag->type)
3882 gdk_cursor_unref (sheet->cursor_drag);
3883 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3884 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3887 new_cursor = GDK_TOP_LEFT_ARROW;
3888 if ( event->window == sheet->sheet_window &&
3889 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3890 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3891 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3892 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3893 new_cursor != sheet->cursor_drag->type)
3895 gdk_cursor_unref (sheet->cursor_drag);
3896 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3897 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3900 new_cursor = GDK_SIZING;
3901 if ( event->window == sheet->sheet_window &&
3902 sheet->selection_mode != GTK_SELECTION_NONE &&
3903 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3904 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3905 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3906 new_cursor != sheet->cursor_drag->type)
3908 gdk_cursor_unref (sheet->cursor_drag);
3909 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3910 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3914 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3915 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3917 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3919 if (event->x != sheet->x_drag)
3921 draw_xor_vline (sheet);
3922 sheet->x_drag = event->x;
3923 draw_xor_vline (sheet);
3929 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3931 if (event->y != sheet->y_drag)
3933 draw_xor_hline (sheet);
3934 sheet->y_drag = event->y;
3935 draw_xor_hline (sheet);
3941 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3943 PsppireSheetRange aux;
3944 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3945 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3946 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3947 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3951 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3952 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3954 aux = sheet->drag_range;
3955 sheet->drag_range.row0 = sheet->range.row0 + row;
3956 sheet->drag_range.col0 = sheet->range.col0 + column;
3957 sheet->drag_range.rowi = sheet->range.rowi + row;
3958 sheet->drag_range.coli = sheet->range.coli + column;
3959 if (aux.row0 != sheet->drag_range.row0 ||
3960 aux.col0 != sheet->drag_range.col0)
3962 draw_xor_rectangle (sheet, aux);
3963 draw_xor_rectangle (sheet, sheet->drag_range);
3969 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3971 PsppireSheetRange aux;
3972 gint v_h, current_col, current_row, col_threshold, row_threshold;
3974 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
3975 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
3977 current_col = column_from_xpixel (sheet, x);
3978 current_row = row_from_ypixel (sheet, y);
3979 column = current_col - sheet->drag_cell.col;
3980 row = current_row - sheet->drag_cell.row;
3982 /*use half of column width resp. row height as threshold to
3984 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
3985 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
3988 if (x < col_threshold)
3991 else if (column < 0)
3993 if (x > col_threshold)
3996 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
3997 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
4000 if (y < row_threshold)
4005 if (y > row_threshold)
4009 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
4010 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4020 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4021 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4023 aux = sheet->drag_range;
4024 sheet->drag_range = sheet->range;
4026 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4027 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4028 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4029 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4031 if (aux.row0 != sheet->drag_range.row0 ||
4032 aux.rowi != sheet->drag_range.rowi ||
4033 aux.col0 != sheet->drag_range.col0 ||
4034 aux.coli != sheet->drag_range.coli)
4036 draw_xor_rectangle (sheet, aux);
4037 draw_xor_rectangle (sheet, sheet->drag_range);
4043 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
4045 if (sheet->state == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
4046 column == sheet->active_cell.col) return TRUE;
4048 if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4049 psppire_sheet_extend_selection (sheet, row, column);
4055 psppire_sheet_crossing_notify (GtkWidget *widget,
4056 GdkEventCrossing *event)
4058 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4060 if (event->window == sheet->column_title_window)
4061 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4062 else if (event->window == sheet->row_title_window)
4063 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4070 psppire_sheet_focus_in (GtkWidget *w,
4071 GdkEventFocus *event)
4073 PsppireSheet *sheet = PSPPIRE_SHEET (w);
4075 gtk_widget_grab_focus (sheet->entry_widget);
4082 psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column)
4084 PsppireSheetRange range;
4088 if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
4091 if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
4093 gtk_widget_grab_focus (GTK_WIDGET (sheet));
4095 if (PSPPIRE_SHEET_IN_DRAG (sheet)) return;
4097 state = sheet->state;
4099 switch (sheet->state)
4101 case PSPPIRE_SHEET_ROW_SELECTED:
4102 column = psppire_axis_unit_count (sheet->haxis) - 1;
4104 case PSPPIRE_SHEET_COLUMN_SELECTED:
4105 row = psppire_axis_unit_count (sheet->vaxis) - 1;
4107 case PSPPIRE_SHEET_NORMAL:
4108 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4109 r = sheet->active_cell.row;
4110 c = sheet->active_cell.col;
4111 sheet->range.col0 = c;
4112 sheet->range.row0 = r;
4113 sheet->range.coli = c;
4114 sheet->range.rowi = r;
4115 psppire_sheet_range_draw_selection (sheet, sheet->range);
4116 case PSPPIRE_SHEET_RANGE_SELECTED:
4117 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4120 sheet->selection_cell.row = row;
4121 sheet->selection_cell.col = column;
4123 range.col0 = MIN (column, sheet->active_cell.col);
4124 range.coli = MAX (column, sheet->active_cell.col);
4125 range.row0 = MIN (row, sheet->active_cell.row);
4126 range.rowi = MAX (row, sheet->active_cell.row);
4128 if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
4129 range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
4130 state == PSPPIRE_SHEET_NORMAL)
4131 psppire_sheet_real_select_range (sheet, &range);
4136 psppire_sheet_entry_key_press (GtkWidget *widget,
4140 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
4145 /* Number of rows in a step-increment */
4146 #define ROWS_PER_STEP 1
4150 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
4152 gint old_row = sheet->active_cell.row ;
4153 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
4157 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
4158 min_visible_row (sheet));
4162 case GTK_SCROLL_PAGE_DOWN:
4163 gtk_adjustment_set_value (sheet->vadjustment,
4164 sheet->vadjustment->value +
4165 sheet->vadjustment->page_increment);
4167 case GTK_SCROLL_PAGE_UP:
4168 gtk_adjustment_set_value (sheet->vadjustment,
4169 sheet->vadjustment->value -
4170 sheet->vadjustment->page_increment);
4174 g_assert_not_reached ();
4179 vpixel += psppire_axis_start_pixel (sheet->vaxis,
4180 min_visible_row (sheet));
4182 new_row = row_from_ypixel (sheet, vpixel);
4184 change_active_cell (sheet, new_row,
4185 sheet->active_cell.col);
4190 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
4192 gint current_row = sheet->active_cell.row;
4193 gint current_col = sheet->active_cell.col;
4194 PsppireSheetCell new_cell ;
4195 gboolean forbidden = FALSE;
4197 new_cell.row = current_row;
4198 new_cell.col = current_col;
4202 case GTK_SCROLL_STEP_DOWN:
4205 case GTK_SCROLL_STEP_UP:
4208 case GTK_SCROLL_STEP_RIGHT:
4211 case GTK_SCROLL_STEP_LEFT:
4214 case GTK_SCROLL_STEP_FORWARD:
4217 psppire_sheet_model_get_column_count (sheet->model))
4223 case GTK_SCROLL_STEP_BACKWARD:
4225 if (new_cell.col < 0)
4228 psppire_sheet_model_get_column_count (sheet->model) - 1;
4233 g_assert_not_reached ();
4237 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
4238 &sheet->active_cell,
4246 maximize_int (&new_cell.row, 0);
4247 maximize_int (&new_cell.col, 0);
4249 minimize_int (&new_cell.row,
4250 psppire_axis_unit_count (sheet->vaxis) - 1);
4252 minimize_int (&new_cell.col,
4253 psppire_axis_unit_count (sheet->haxis) - 1);
4255 change_active_cell (sheet, new_cell.row, new_cell.col);
4258 if ( new_cell.col > max_fully_visible_column (sheet))
4261 psppire_axis_start_pixel (sheet->haxis,
4263 hpos -= sheet->hadjustment->page_size;
4265 gtk_adjustment_set_value (sheet->hadjustment,
4268 else if ( new_cell.col < min_fully_visible_column (sheet))
4271 psppire_axis_start_pixel (sheet->haxis,
4274 gtk_adjustment_set_value (sheet->hadjustment,
4279 if ( new_cell.row > max_fully_visible_row (sheet))
4282 psppire_axis_start_pixel (sheet->vaxis,
4284 vpos -= sheet->vadjustment->page_size;
4286 gtk_adjustment_set_value (sheet->vadjustment,
4289 else if ( new_cell.row < min_fully_visible_row (sheet))
4292 psppire_axis_start_pixel (sheet->vaxis,
4295 gtk_adjustment_set_value (sheet->vadjustment,
4299 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
4304 psppire_sheet_key_press (GtkWidget *widget,
4307 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4309 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
4311 switch (key->keyval)
4314 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
4317 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
4319 case GDK_ISO_Left_Tab:
4320 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
4323 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
4327 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
4330 step_sheet (sheet, GTK_SCROLL_STEP_UP);
4334 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
4337 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
4341 gtk_adjustment_set_value (sheet->vadjustment,
4342 sheet->vadjustment->lower);
4344 change_active_cell (sheet, 0,
4345 sheet->active_cell.col);
4350 gtk_adjustment_set_value (sheet->vadjustment,
4351 sheet->vadjustment->upper -
4352 sheet->vadjustment->page_size -
4353 sheet->vadjustment->page_increment);
4356 change_active_cellx (sheet,
4357 psppire_axis_unit_count (sheet->vaxis) - 1,
4358 sheet->active_cell.col);
4362 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
4373 psppire_sheet_size_request (GtkWidget *widget,
4374 GtkRequisition *requisition)
4376 PsppireSheet *sheet;
4378 g_return_if_fail (widget != NULL);
4379 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4380 g_return_if_fail (requisition != NULL);
4382 sheet = PSPPIRE_SHEET (widget);
4384 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4385 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4387 /* compute the size of the column title area */
4388 if (sheet->column_titles_visible)
4389 requisition->height += sheet->column_title_area.height;
4391 /* compute the size of the row title area */
4392 if (sheet->row_titles_visible)
4393 requisition->width += sheet->row_title_area.width;
4398 psppire_sheet_size_allocate (GtkWidget *widget,
4399 GtkAllocation *allocation)
4401 PsppireSheet *sheet;
4402 GtkAllocation sheet_allocation;
4405 g_return_if_fail (widget != NULL);
4406 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4407 g_return_if_fail (allocation != NULL);
4409 sheet = PSPPIRE_SHEET (widget);
4410 widget->allocation = *allocation;
4411 border_width = GTK_CONTAINER (widget)->border_width;
4413 if (GTK_WIDGET_REALIZED (widget))
4414 gdk_window_move_resize (widget->window,
4415 allocation->x + border_width,
4416 allocation->y + border_width,
4417 allocation->width - 2 * border_width,
4418 allocation->height - 2 * border_width);
4420 sheet_allocation.x = 0;
4421 sheet_allocation.y = 0;
4422 sheet_allocation.width = allocation->width - 2 * border_width;
4423 sheet_allocation.height = allocation->height - 2 * border_width;
4425 if (GTK_WIDGET_REALIZED (widget))
4426 gdk_window_move_resize (sheet->sheet_window,
4429 sheet_allocation.width,
4430 sheet_allocation.height);
4432 /* position the window which holds the column title buttons */
4433 sheet->column_title_area.x = 0;
4434 sheet->column_title_area.y = 0;
4435 sheet->column_title_area.width = sheet_allocation.width ;
4438 /* position the window which holds the row title buttons */
4439 sheet->row_title_area.x = 0;
4440 sheet->row_title_area.y = 0;
4441 sheet->row_title_area.height = sheet_allocation.height;
4443 if (sheet->row_titles_visible)
4444 sheet->column_title_area.x += sheet->row_title_area.width;
4446 if (sheet->column_titles_visible)
4447 sheet->row_title_area.y += sheet->column_title_area.height;
4449 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4450 gdk_window_move_resize (sheet->column_title_window,
4451 sheet->column_title_area.x,
4452 sheet->column_title_area.y,
4453 sheet->column_title_area.width,
4454 sheet->column_title_area.height);
4457 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4458 gdk_window_move_resize (sheet->row_title_window,
4459 sheet->row_title_area.x,
4460 sheet->row_title_area.y,
4461 sheet->row_title_area.width,
4462 sheet->row_title_area.height);
4464 size_allocate_global_button (sheet);
4468 gint width = sheet->column_title_area.width;
4470 if ( sheet->row_titles_visible)
4471 width -= sheet->row_title_area.width;
4473 g_object_set (sheet->haxis,
4474 "minimum-extent", width,
4481 gint height = sheet->row_title_area.height;
4483 if ( sheet->column_titles_visible)
4484 height -= sheet->column_title_area.height;
4486 g_object_set (sheet->vaxis,
4487 "minimum-extent", height,
4492 /* set the scrollbars adjustments */
4493 adjust_scrollbars (sheet);
4497 draw_column_title_buttons (PsppireSheet *sheet)
4501 if (!sheet->column_titles_visible) return;
4502 if (!GTK_WIDGET_REALIZED (sheet))
4505 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4508 if (sheet->row_titles_visible)
4510 x = sheet->row_title_area.width;
4513 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4515 sheet->column_title_area.width = width;
4516 sheet->column_title_area.x = x;
4517 gdk_window_move_resize (sheet->column_title_window,
4518 sheet->column_title_area.x,
4519 sheet->column_title_area.y,
4520 sheet->column_title_area.width,
4521 sheet->column_title_area.height);
4524 if (max_visible_column (sheet) ==
4525 psppire_axis_unit_count (sheet->haxis) - 1)
4526 gdk_window_clear_area (sheet->column_title_window,
4528 sheet->column_title_area.width,
4529 sheet->column_title_area.height);
4531 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4533 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4534 max_visible_column (sheet));
4538 draw_row_title_buttons (PsppireSheet *sheet)
4543 if (!sheet->row_titles_visible) return;
4544 if (!GTK_WIDGET_REALIZED (sheet))
4547 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4549 if (sheet->column_titles_visible)
4551 y = sheet->column_title_area.height;
4554 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4556 sheet->row_title_area.y = y;
4557 sheet->row_title_area.height = height;
4558 gdk_window_move_resize (sheet->row_title_window,
4559 sheet->row_title_area.x,
4560 sheet->row_title_area.y,
4561 sheet->row_title_area.width,
4562 sheet->row_title_area.height);
4565 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4566 gdk_window_clear_area (sheet->row_title_window,
4568 sheet->row_title_area.width,
4569 sheet->row_title_area.height);
4571 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4573 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4574 max_visible_row (sheet));
4579 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4581 GtkAllocation entry_alloc;
4582 PsppireSheetCellAttr attributes = { 0 };
4583 GtkEntry *sheet_entry;
4585 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4586 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4588 sheet_entry = psppire_sheet_get_entry (sheet);
4590 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4591 sheet->active_cell.col,
4595 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4597 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4599 style->bg[GTK_STATE_NORMAL] = attributes.background;
4600 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4601 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4602 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4603 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4604 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4607 rectangle_from_cell (sheet, sheet->active_cell.row,
4608 sheet->active_cell.col, &entry_alloc);
4610 entry_alloc.width -= BORDER_WIDTH ;
4611 entry_alloc.height -= BORDER_WIDTH ;
4612 entry_alloc.x += DIV_RND_UP (BORDER_WIDTH, 2);
4613 entry_alloc.y += DIV_RND_UP (BORDER_WIDTH, 2);
4616 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4617 entry_alloc.height);
4618 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4622 /* Copy the sheet's font to the entry widget */
4624 set_entry_widget_font (PsppireSheet *sheet)
4626 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4628 pango_font_description_free (style->font_desc);
4629 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4631 gtk_widget_modify_style (sheet->entry_widget, style);
4635 create_sheet_entry (PsppireSheet *sheet)
4637 if (sheet->entry_widget)
4639 gtk_widget_unparent (sheet->entry_widget);
4642 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4643 g_object_ref_sink (sheet->entry_widget);
4645 gtk_widget_size_request (sheet->entry_widget, NULL);
4647 if ( GTK_IS_ENTRY (sheet->entry_widget))
4649 g_object_set (sheet->entry_widget,
4654 if (GTK_WIDGET_REALIZED (sheet))
4656 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4657 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4658 gtk_widget_realize (sheet->entry_widget);
4661 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4662 G_CALLBACK (psppire_sheet_entry_key_press),
4665 set_entry_widget_font (sheet);
4667 gtk_widget_show (sheet->entry_widget);
4671 /* Finds the last child widget that happens to be of type GtkEntry */
4673 find_entry (GtkWidget *w, gpointer user_data)
4675 GtkWidget **entry = user_data;
4676 if ( GTK_IS_ENTRY (w))
4684 psppire_sheet_get_entry (PsppireSheet *sheet)
4686 GtkWidget *w = sheet->entry_widget;
4688 g_return_val_if_fail (sheet != NULL, NULL);
4689 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4690 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4692 while (! GTK_IS_ENTRY (w))
4694 GtkWidget *entry = NULL;
4696 if (GTK_IS_CONTAINER (w))
4698 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4707 return GTK_ENTRY (w);
4712 draw_button (PsppireSheet *sheet, GdkWindow *window,
4713 PsppireSheetButton *button, gboolean is_sensitive,
4714 GdkRectangle allocation)
4716 GtkShadowType shadow_type;
4717 gint text_width = 0, text_height = 0;
4718 PangoAlignment align = PANGO_ALIGN_LEFT;
4724 g_return_if_fail (sheet != NULL);
4725 g_return_if_fail (button != NULL);
4728 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4730 gdk_window_clear_area (window,
4731 allocation.x, allocation.y,
4732 allocation.width, allocation.height);
4734 gtk_widget_ensure_style (sheet->button);
4736 gtk_paint_box (sheet->button->style, window,
4737 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4738 &allocation, GTK_WIDGET (sheet->button),
4740 allocation.x, allocation.y,
4741 allocation.width, allocation.height);
4743 state = button->state;
4744 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4746 if (state == GTK_STATE_ACTIVE)
4747 shadow_type = GTK_SHADOW_IN;
4749 shadow_type = GTK_SHADOW_OUT;
4751 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4752 gtk_paint_box (sheet->button->style, window,
4753 button->state, shadow_type,
4754 &allocation, GTK_WIDGET (sheet->button),
4756 allocation.x, allocation.y,
4757 allocation.width, allocation.height);
4759 if ( button->overstruck)
4761 GdkPoint points[2] = {
4762 {allocation.x, allocation.y},
4763 {allocation.x + allocation.width,
4764 allocation.y + allocation.height}
4767 gtk_paint_polygon (sheet->button->style,
4779 if (button->label_visible)
4781 text_height = DEFAULT_ROW_HEIGHT -
4782 2 * COLUMN_TITLES_HEIGHT;
4784 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4786 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4789 allocation.y += 2 * sheet->button->style->ythickness;
4791 if (button->label && strlen (button->label) > 0)
4793 PangoRectangle rect;
4794 gchar *line = button->label;
4796 PangoLayout *layout = NULL;
4797 gint real_x = allocation.x;
4798 gint real_y = allocation.y;
4800 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4801 pango_layout_get_extents (layout, NULL, &rect);
4803 text_width = PANGO_PIXELS (rect.width);
4804 switch (button->justification)
4806 case GTK_JUSTIFY_LEFT:
4807 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4808 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4810 case GTK_JUSTIFY_RIGHT:
4811 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4812 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4814 case GTK_JUSTIFY_CENTER:
4816 real_x = allocation.x + (allocation.width - text_width)/2;
4817 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4818 pango_layout_set_justify (layout, TRUE);
4820 pango_layout_set_alignment (layout, align);
4821 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4830 g_object_unref (layout);
4833 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4835 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4839 psppire_sheet_button_free (button);
4843 /* Draw the column title buttons FIRST through to LAST */
4845 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4849 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4851 if (!sheet->column_titles_visible) return;
4853 g_return_if_fail (first >= min_visible_column (sheet));
4854 g_return_if_fail (last <= max_visible_column (sheet));
4857 rect.height = sheet->column_title_area.height;
4858 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4859 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4860 + psppire_axis_unit_size (sheet->haxis, last);
4862 rect.x -= sheet->hadjustment->value;
4864 minimize_int (&rect.width, sheet->column_title_area.width);
4865 maximize_int (&rect.x, 0);
4867 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4869 for (col = first ; col <= last ; ++col)
4871 GdkRectangle allocation;
4872 gboolean is_sensitive = FALSE;
4874 PsppireSheetButton *
4875 button = psppire_sheet_model_get_column_button (sheet->model, col);
4877 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4879 allocation.x -= sheet->hadjustment->value;
4881 allocation.height = sheet->column_title_area.height;
4882 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4883 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4885 draw_button (sheet, sheet->column_title_window,
4886 button, is_sensitive, allocation);
4889 gdk_window_end_paint (sheet->column_title_window);
4894 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4898 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4900 if (!sheet->row_titles_visible) return;
4902 g_return_if_fail (first >= min_visible_row (sheet));
4903 g_return_if_fail (last <= max_visible_row (sheet));
4906 rect.width = sheet->row_title_area.width;
4907 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4908 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4909 + psppire_axis_unit_size (sheet->vaxis, last);
4911 rect.y -= sheet->vadjustment->value;
4913 minimize_int (&rect.height, sheet->row_title_area.height);
4914 maximize_int (&rect.y, 0);
4916 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4917 for (row = first; row <= last; ++row)
4919 GdkRectangle allocation;
4921 gboolean is_sensitive = FALSE;
4923 PsppireSheetButton *button =
4924 psppire_sheet_model_get_row_button (sheet->model, row);
4926 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4928 allocation.y -= sheet->vadjustment->value;
4930 allocation.width = sheet->row_title_area.width;
4931 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4932 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4934 draw_button (sheet, sheet->row_title_window,
4935 button, is_sensitive, allocation);
4938 gdk_window_end_paint (sheet->row_title_window);
4945 * vadjustment_value_changed
4946 * hadjustment_value_changed */
4950 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4953 (adj->value + adj->page_size)
4955 (adj->upper - adj->lower);
4957 const glong last_item = psppire_axis_unit_count (axis) - 1;
4959 if (isnan (position) || position < 0)
4963 psppire_axis_start_pixel (axis, last_item)
4965 psppire_axis_unit_size (axis, last_item)
4969 adj->page_size = page_size;
4972 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4974 if ( adj->value < adj->lower)
4975 adj->value = adj->lower;
4978 gtk_adjustment_changed (adj);
4983 adjust_scrollbars (PsppireSheet *sheet)
4987 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4990 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4992 if ( sheet->row_titles_visible)
4993 width -= sheet->row_title_area.width;
4995 if (sheet->column_titles_visible)
4996 height -= sheet->column_title_area.height;
4998 if (sheet->vadjustment)
5000 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
5002 sheet->vadjustment->step_increment =
5004 psppire_axis_unit_size (sheet->vaxis, last_row);
5006 sheet->vadjustment->page_increment =
5008 sheet->column_title_area.height -
5009 psppire_axis_unit_size (sheet->vaxis, last_row);
5011 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
5014 if (sheet->hadjustment)
5016 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
5017 sheet->hadjustment->step_increment = 1;
5019 sheet->hadjustment->page_increment = width;
5021 sheet->hadjustment->upper =
5022 psppire_axis_start_pixel (sheet->haxis, last_col)
5024 psppire_axis_unit_size (sheet->haxis, last_col)
5027 update_adjustment (sheet->hadjustment, sheet->haxis, width);
5031 /* Subtracts the region of WIDGET from REGION */
5033 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
5036 GdkRectangle intersect;
5039 gdk_region_get_clipbox (region, &rect);
5040 gtk_widget_intersect (widget,
5044 region2 = gdk_region_rectangle (&intersect);
5045 gdk_region_subtract (region, region2);
5046 gdk_region_destroy (region2);
5050 vadjustment_value_changed (GtkAdjustment *adjustment,
5054 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5056 g_return_if_fail (adjustment != NULL);
5058 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5060 gtk_widget_hide (sheet->entry_widget);
5063 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5065 subtract_widget_region (region, sheet->button);
5066 gdk_window_begin_paint_region (sheet->sheet_window, region);
5068 draw_sheet_region (sheet, region);
5070 draw_row_title_buttons (sheet);
5071 psppire_sheet_draw_active_cell (sheet);
5073 gdk_window_end_paint (sheet->sheet_window);
5074 gdk_region_destroy (region);
5079 hadjustment_value_changed (GtkAdjustment *adjustment,
5083 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5085 g_return_if_fail (adjustment != NULL);
5087 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5089 gtk_widget_hide (sheet->entry_widget);
5093 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5095 subtract_widget_region (region, sheet->button);
5096 gdk_window_begin_paint_region (sheet->sheet_window, region);
5098 draw_sheet_region (sheet, region);
5100 draw_column_title_buttons (sheet);
5102 psppire_sheet_draw_active_cell (sheet);
5104 gdk_window_end_paint (sheet->sheet_window);
5106 gdk_region_destroy (region);
5110 /* COLUMN RESIZING */
5112 draw_xor_vline (PsppireSheet *sheet)
5115 gint xpos = sheet->x_drag;
5116 gdk_drawable_get_size (sheet->sheet_window,
5119 if (sheet->row_titles_visible)
5120 xpos += sheet->row_title_area.width;
5122 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5124 sheet->column_title_area.height,
5126 height + CELL_SPACING);
5131 draw_xor_hline (PsppireSheet *sheet)
5135 gint ypos = sheet->y_drag;
5137 gdk_drawable_get_size (sheet->sheet_window,
5141 if (sheet->column_titles_visible)
5142 ypos += sheet->column_title_area.height;
5144 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5145 sheet->row_title_area.width,
5147 width + CELL_SPACING,
5151 /* SELECTED RANGE */
5153 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
5156 GdkRectangle clip_area, area;
5159 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
5160 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
5161 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
5162 psppire_axis_unit_size (sheet->haxis, range.coli);
5163 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
5164 psppire_axis_unit_size (sheet->vaxis, range.rowi);
5166 clip_area.x = sheet->row_title_area.width;
5167 clip_area.y = sheet->column_title_area.height;
5169 gdk_drawable_get_size (sheet->sheet_window,
5170 &clip_area.width, &clip_area.height);
5172 if (!sheet->row_titles_visible) clip_area.x = 0;
5173 if (!sheet->column_titles_visible) clip_area.y = 0;
5177 area.width = area.width + area.x;
5180 if (area.width > clip_area.width) area.width = clip_area.width + 10;
5183 area.height = area.height + area.y;
5186 if (area.height > clip_area.height) area.height = clip_area.height + 10;
5190 clip_area.width += 3;
5191 clip_area.height += 3;
5193 gdk_gc_get_values (sheet->xor_gc, &values);
5195 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5197 gdk_draw_rectangle (sheet->sheet_window,
5200 area.x + i, area.y + i,
5201 area.width - 2 * i, area.height - 2 * i);
5204 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5206 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5211 set_column_width (PsppireSheet *sheet,
5215 g_return_if_fail (sheet != NULL);
5216 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5218 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
5224 psppire_axis_resize (sheet->haxis, column, width);
5226 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5228 draw_column_title_buttons (sheet);
5229 adjust_scrollbars (sheet);
5230 psppire_sheet_size_allocate_entry (sheet);
5231 redraw_range (sheet, NULL);
5236 set_row_height (PsppireSheet *sheet,
5240 g_return_if_fail (sheet != NULL);
5241 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5243 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
5249 psppire_axis_resize (sheet->vaxis, row, height);
5251 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
5253 draw_row_title_buttons (sheet);
5254 adjust_scrollbars (sheet);
5255 psppire_sheet_size_allocate_entry (sheet);
5256 redraw_range (sheet, NULL);
5261 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
5262 PsppireSheetCellAttr *attr)
5265 const GtkJustification *j ;
5266 GdkColormap *colormap;
5268 g_return_val_if_fail (sheet != NULL, FALSE);
5269 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
5271 if (row < 0 || col < 0) return FALSE;
5273 attr->foreground = GTK_WIDGET (sheet)->style->black;
5274 attr->background = sheet->color[BG_COLOR];
5276 attr->border.width = 0;
5277 attr->border.line_style = GDK_LINE_SOLID;
5278 attr->border.cap_style = GDK_CAP_NOT_LAST;
5279 attr->border.join_style = GDK_JOIN_MITER;
5280 attr->border.mask = 0;
5281 attr->border.color = GTK_WIDGET (sheet)->style->black;
5283 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
5284 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
5287 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
5288 attr->foreground = *fg;
5291 bg = psppire_sheet_model_get_background (sheet->model, row, col);
5294 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
5295 attr->background = *bg;
5298 attr->justification =
5299 psppire_sheet_model_get_column_justification (sheet->model, col);
5301 j = psppire_sheet_model_get_justification (sheet->model, row, col);
5303 attr->justification = *j;
5309 psppire_sheet_button_size_request (PsppireSheet *sheet,
5310 const PsppireSheetButton *button,
5311 GtkRequisition *button_requisition)
5313 GtkRequisition requisition;
5314 GtkRequisition label_requisition;
5316 label_requisition.height = DEFAULT_ROW_HEIGHT;
5317 label_requisition.width = COLUMN_MIN_WIDTH;
5319 requisition.height = DEFAULT_ROW_HEIGHT;
5320 requisition.width = COLUMN_MIN_WIDTH;
5323 *button_requisition = requisition;
5324 button_requisition->width = MAX (requisition.width, label_requisition.width);
5325 button_requisition->height = MAX (requisition.height, label_requisition.height);
5330 psppire_sheet_forall (GtkContainer *container,
5331 gboolean include_internals,
5332 GtkCallback callback,
5333 gpointer callback_data)
5335 PsppireSheet *sheet = PSPPIRE_SHEET (container);
5337 g_return_if_fail (callback != NULL);
5339 if (sheet->button && sheet->button->parent)
5340 (* callback) (sheet->button, callback_data);
5342 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
5343 (* callback) (sheet->entry_widget, callback_data);
5348 psppire_sheet_get_model (const PsppireSheet *sheet)
5350 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
5352 return sheet->model;
5356 PsppireSheetButton *
5357 psppire_sheet_button_new (void)
5359 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
5361 button->state = GTK_STATE_NORMAL;
5362 button->label = NULL;
5363 button->label_visible = TRUE;
5364 button->justification = GTK_JUSTIFY_FILL;
5365 button->overstruck = FALSE;
5372 psppire_sheet_button_free (PsppireSheetButton *button)
5374 if (!button) return ;
5376 g_free (button->label);
5381 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5383 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5385 if ( NULL == celltext)
5388 g_string_append (string, celltext);
5394 range_to_text (const PsppireSheet *sheet)
5399 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5402 string = g_string_sized_new (80);
5404 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5406 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5408 append_cell_text (string, sheet, r, c);
5409 g_string_append (string, "\t");
5411 append_cell_text (string, sheet, r, c);
5412 if ( r < sheet->range.rowi)
5413 g_string_append (string, "\n");
5420 range_to_html (const PsppireSheet *sheet)
5425 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5428 string = g_string_sized_new (480);
5430 g_string_append (string, "<html>\n");
5431 g_string_append (string, "<body>\n");
5432 g_string_append (string, "<table>\n");
5433 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5435 g_string_append (string, "<tr>\n");
5436 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5438 g_string_append (string, "<td>");
5439 append_cell_text (string, sheet, r, c);
5440 g_string_append (string, "</td>\n");
5442 g_string_append (string, "</tr>\n");
5444 g_string_append (string, "</table>\n");
5445 g_string_append (string, "</body>\n");
5446 g_string_append (string, "</html>\n");
5458 primary_get_cb (GtkClipboard *clipboard,
5459 GtkSelectionData *selection_data,
5463 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5464 GString *string = NULL;
5468 case SELECT_FMT_TEXT:
5469 string = range_to_text (sheet);
5471 case SELECT_FMT_HTML:
5472 string = range_to_html (sheet);
5475 g_assert_not_reached ();
5478 gtk_selection_data_set (selection_data, selection_data->target,
5480 (const guchar *) string->str, string->len);
5481 g_string_free (string, TRUE);
5485 primary_clear_cb (GtkClipboard *clipboard,
5488 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5489 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5492 psppire_sheet_real_unselect_range (sheet, NULL);
5496 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5498 static const GtkTargetEntry targets[] = {
5499 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5500 { "STRING", 0, SELECT_FMT_TEXT },
5501 { "TEXT", 0, SELECT_FMT_TEXT },
5502 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5503 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5504 { "text/plain", 0, SELECT_FMT_TEXT },
5505 { "text/html", 0, SELECT_FMT_HTML }
5508 GtkClipboard *clipboard;
5510 if (!GTK_WIDGET_REALIZED (sheet))
5513 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5514 GDK_SELECTION_PRIMARY);
5516 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5518 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5519 G_N_ELEMENTS (targets),
5520 primary_get_cb, primary_clear_cb,
5522 primary_clear_cb (clipboard, sheet);
5526 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5527 gtk_clipboard_clear (clipboard);