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->select_status == PSPPIRE_SHEET_COLUMN_SELECTED)
379 ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet));
381 if (sheet->select_status == 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;
707 resize_column (PsppireSheet *sheet, gint unit, glong size)
709 PsppireSheetRange range;
711 range.coli = max_visible_column (sheet);
712 range.row0 = min_visible_row (sheet);
713 range.rowi = max_visible_row (sheet);
715 redraw_range (sheet, &range);
717 draw_column_title_buttons_range (sheet, range.col0, range.coli);
722 psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
725 g_object_unref (sheet->haxis);
728 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
731 g_object_ref (sheet->haxis);
735 resize_row (PsppireSheet *sheet, gint unit, glong size)
737 PsppireSheetRange range;
738 range.col0 = min_visible_column (sheet);
739 range.coli = max_visible_column (sheet);
741 range.rowi = max_visible_row (sheet);
743 redraw_range (sheet, &range);
745 draw_row_title_buttons_range (sheet, range.row0, range.rowi);
749 psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
752 g_object_unref (sheet->vaxis);
756 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
759 g_object_ref (sheet->vaxis);
762 static const GtkBorder default_cell_padding = {3, 3, 3, 3};
765 psppire_sheet_set_property (GObject *object,
771 PsppireSheet *sheet = PSPPIRE_SHEET (object);
775 case PROP_CELL_PADDING:
776 if ( sheet->cell_padding)
777 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
779 sheet->cell_padding = g_value_dup_boxed (value);
781 if (NULL == sheet->cell_padding)
782 sheet->cell_padding = g_boxed_copy (GTK_TYPE_BORDER,
783 &default_cell_padding);
786 g_object_set (sheet->vaxis, "padding",
787 sheet->cell_padding->top + sheet->cell_padding->bottom,
791 g_object_set (sheet->haxis, "padding",
792 sheet->cell_padding->left + sheet->cell_padding->right,
796 psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
797 g_object_set (sheet->vaxis, "padding",
798 sheet->cell_padding->top + sheet->cell_padding->bottom,
802 psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
803 g_object_set (sheet->haxis, "padding",
804 sheet->cell_padding->left + sheet->cell_padding->right,
808 psppire_sheet_set_model (sheet, g_value_get_pointer (value));
811 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
817 psppire_sheet_get_property (GObject *object,
822 PsppireSheet *sheet = PSPPIRE_SHEET (object);
826 case PROP_CELL_PADDING:
827 g_value_set_boxed (value, sheet->cell_padding);
830 g_value_set_pointer (value, sheet->vaxis);
833 g_value_set_pointer (value, sheet->haxis);
836 g_value_set_pointer (value, sheet->model);
839 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
846 psppire_sheet_class_init (PsppireSheetClass *klass)
848 GObjectClass *object_class = G_OBJECT_CLASS (klass);
850 GParamSpec *haxis_spec ;
851 GParamSpec *vaxis_spec ;
852 GParamSpec *model_spec ;
853 GParamSpec *cell_padding_spec ;
855 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
856 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
858 parent_class = g_type_class_peek_parent (klass);
861 * PsppireSheet::select-row
862 * @sheet: the sheet widget that emitted the signal
863 * @row: the newly selected row index
865 * A row has been selected.
867 sheet_signals[SELECT_ROW] =
868 g_signal_new ("select-row",
869 G_TYPE_FROM_CLASS (object_class),
871 offsetof (PsppireSheetClass, select_row),
873 g_cclosure_marshal_VOID__INT,
880 * PsppireSheet::select - column
881 * @sheet: the sheet widget that emitted the signal
882 * @column: the newly selected column index
884 * A column has been selected.
886 sheet_signals[SELECT_COLUMN] =
887 g_signal_new ("select-column",
888 G_TYPE_FROM_CLASS (object_class),
890 offsetof (PsppireSheetClass, select_column),
892 g_cclosure_marshal_VOID__INT,
899 * PsppireSheet::double-click-row
900 * @sheet: the sheet widget that emitted the signal
901 * @row: the row that was double clicked.
903 * A row's title button has been double clicked
905 sheet_signals[DOUBLE_CLICK_ROW] =
906 g_signal_new ("double-click-row",
907 G_TYPE_FROM_CLASS (object_class),
911 g_cclosure_marshal_VOID__INT,
918 * PsppireSheet::double-click-column
919 * @sheet: the sheet widget that emitted the signal
920 * @column: the column that was double clicked.
922 * A column's title button has been double clicked
924 sheet_signals[DOUBLE_CLICK_COLUMN] =
925 g_signal_new ("double-click-column",
926 G_TYPE_FROM_CLASS (object_class),
930 g_cclosure_marshal_VOID__INT,
937 * PsppireSheet::button-event-column
938 * @sheet: the sheet widget that emitted the signal
939 * @column: the column on which the event occured.
941 * A button event occured on a column title button
943 sheet_signals[BUTTON_EVENT_COLUMN] =
944 g_signal_new ("button-event-column",
945 G_TYPE_FROM_CLASS (object_class),
949 psppire_marshal_VOID__INT_POINTER,
958 * PsppireSheet::button-event-row
959 * @sheet: the sheet widget that emitted the signal
960 * @column: the column on which the event occured.
962 * A button event occured on a row title button
964 sheet_signals[BUTTON_EVENT_ROW] =
965 g_signal_new ("button-event-row",
966 G_TYPE_FROM_CLASS (object_class),
970 psppire_marshal_VOID__INT_POINTER,
978 sheet_signals[SELECT_RANGE] =
979 g_signal_new ("select-range",
980 G_TYPE_FROM_CLASS (object_class),
982 offsetof (PsppireSheetClass, select_range),
984 g_cclosure_marshal_VOID__BOXED,
987 PSPPIRE_TYPE_SHEET_RANGE);
990 sheet_signals[RESIZE_RANGE] =
991 g_signal_new ("resize-range",
992 G_TYPE_FROM_CLASS (object_class),
994 offsetof (PsppireSheetClass, resize_range),
996 psppire_marshal_VOID__BOXED_BOXED,
999 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
1002 sheet_signals[MOVE_RANGE] =
1003 g_signal_new ("move-range",
1004 G_TYPE_FROM_CLASS (object_class),
1006 offsetof (PsppireSheetClass, move_range),
1008 psppire_marshal_VOID__BOXED_BOXED,
1011 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
1014 sheet_signals[TRAVERSE] =
1015 g_signal_new ("traverse",
1016 G_TYPE_FROM_CLASS (object_class),
1018 offsetof (PsppireSheetClass, traverse),
1020 psppire_marshal_BOOLEAN__BOXED_POINTER,
1022 PSPPIRE_TYPE_SHEET_CELL,
1026 sheet_signals[ACTIVATE] =
1027 g_signal_new ("activate",
1028 G_TYPE_FROM_CLASS (object_class),
1030 offsetof (PsppireSheetClass, activate),
1032 psppire_marshal_VOID__INT_INT_INT_INT,
1034 G_TYPE_INT, G_TYPE_INT,
1035 G_TYPE_INT, G_TYPE_INT);
1037 widget_class->set_scroll_adjustments_signal =
1038 g_signal_new ("set-scroll-adjustments",
1039 G_TYPE_FROM_CLASS (object_class),
1041 offsetof (PsppireSheetClass, set_scroll_adjustments),
1043 psppire_marshal_VOID__OBJECT_OBJECT,
1044 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1047 container_class->add = NULL;
1048 container_class->remove = NULL;
1049 container_class->forall = psppire_sheet_forall;
1050 container_class->set_focus_child = NULL;
1052 object_class->dispose = psppire_sheet_dispose;
1053 object_class->finalize = psppire_sheet_finalize;
1056 g_param_spec_boxed ("cell-padding",
1058 "The space between a cell's contents and its border",
1060 G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
1063 g_param_spec_pointer ("vertical-axis",
1065 "A pointer to the PsppireAxis object for the rows",
1066 G_PARAM_READABLE | G_PARAM_WRITABLE );
1069 g_param_spec_pointer ("horizontal-axis",
1071 "A pointer to the PsppireAxis object for the columns",
1072 G_PARAM_READABLE | G_PARAM_WRITABLE );
1075 g_param_spec_pointer ("model",
1077 "A pointer to the data model",
1078 G_PARAM_READABLE | G_PARAM_WRITABLE );
1081 object_class->set_property = psppire_sheet_set_property;
1082 object_class->get_property = psppire_sheet_get_property;
1084 g_object_class_install_property (object_class,
1088 g_object_class_install_property (object_class,
1092 g_object_class_install_property (object_class,
1096 g_object_class_install_property (object_class,
1101 widget_class->realize = psppire_sheet_realize;
1102 widget_class->unrealize = psppire_sheet_unrealize;
1103 widget_class->map = psppire_sheet_map;
1104 widget_class->unmap = psppire_sheet_unmap;
1105 widget_class->style_set = psppire_sheet_style_set;
1106 widget_class->button_press_event = psppire_sheet_button_press;
1107 widget_class->button_release_event = psppire_sheet_button_release;
1108 widget_class->motion_notify_event = psppire_sheet_motion;
1109 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1110 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1111 widget_class->key_press_event = psppire_sheet_key_press;
1112 widget_class->expose_event = psppire_sheet_expose;
1113 widget_class->size_request = psppire_sheet_size_request;
1114 widget_class->size_allocate = psppire_sheet_size_allocate;
1115 widget_class->focus_in_event = psppire_sheet_focus_in;
1116 widget_class->focus_out_event = NULL;
1118 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1119 klass->select_row = NULL;
1120 klass->select_column = NULL;
1121 klass->select_range = NULL;
1122 klass->resize_range = NULL;
1123 klass->move_range = NULL;
1124 klass->traverse = NULL;
1125 klass->activate = NULL;
1126 klass->changed = NULL;
1130 psppire_sheet_init (PsppireSheet *sheet)
1132 sheet->model = NULL;
1133 sheet->haxis = NULL;
1134 sheet->vaxis = NULL;
1137 sheet->selection_mode = GTK_SELECTION_NONE;
1138 sheet->select_status = PSPPIRE_SHEET_NORMAL;
1140 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1141 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1143 sheet->column_title_window = NULL;
1144 sheet->column_title_area.x = 0;
1145 sheet->column_title_area.y = 0;
1146 sheet->column_title_area.width = 0;
1147 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1149 sheet->row_title_window = NULL;
1150 sheet->row_title_area.x = 0;
1151 sheet->row_title_area.y = 0;
1152 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1153 sheet->row_title_area.height = 0;
1156 sheet->active_cell.row = 0;
1157 sheet->active_cell.col = 0;
1158 sheet->selection_cell.row = 0;
1159 sheet->selection_cell.col = 0;
1161 sheet->range.row0 = 0;
1162 sheet->range.rowi = 0;
1163 sheet->range.col0 = 0;
1164 sheet->range.coli = 0;
1166 sheet->sheet_window = NULL;
1167 sheet->entry_widget = NULL;
1168 sheet->button = NULL;
1170 sheet->hadjustment = NULL;
1171 sheet->vadjustment = NULL;
1173 sheet->cursor_drag = NULL;
1175 sheet->xor_gc = NULL;
1176 sheet->fg_gc = NULL;
1177 sheet->bg_gc = NULL;
1180 sheet->show_grid = TRUE;
1182 sheet->motion_timer = 0;
1184 sheet->row_titles_visible = TRUE;
1185 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1187 sheet->column_titles_visible = TRUE;
1190 /* create sheet entry */
1191 sheet->entry_type = GTK_TYPE_ENTRY;
1192 create_sheet_entry (sheet);
1194 /* create global selection button */
1195 create_global_button (sheet);
1199 /* Cause RANGE to be redrawn. If RANGE is null, then the
1200 entire visible range will be redrawn.
1203 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1207 if ( ! GTK_WIDGET_REALIZED (sheet))
1210 if ( NULL != range )
1211 rectangle_from_range (sheet, range, &rect);
1214 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1215 gdk_region_get_clipbox (r, &rect);
1217 if ( sheet->column_titles_visible)
1219 rect.y += sheet->column_title_area.height;
1220 rect.height -= sheet->column_title_area.height;
1223 if ( sheet->row_titles_visible)
1225 rect.x += sheet->row_title_area.width;
1226 rect.width -= sheet->row_title_area.width;
1230 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1234 /* Callback which occurs whenever columns are inserted / deleted in the model */
1236 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1240 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1242 PsppireSheetRange range;
1243 gint model_columns = psppire_sheet_model_get_column_count (model);
1246 /* Need to update all the columns starting from the first column and onwards.
1247 * Previous column are unchanged, so don't need to be updated.
1249 range.col0 = first_column;
1251 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1252 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1254 adjust_scrollbars (sheet);
1256 if (sheet->active_cell.col >= model_columns)
1257 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1259 draw_column_title_buttons_range (sheet,
1260 first_column, max_visible_column (sheet));
1263 redraw_range (sheet, &range);
1269 /* Callback which occurs whenever rows are inserted / deleted in the model */
1271 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1272 gint n_rows, gpointer data)
1274 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1276 PsppireSheetRange range;
1278 gint model_rows = psppire_sheet_model_get_row_count (model);
1280 /* Need to update all the rows starting from the first row and onwards.
1281 * Previous rows are unchanged, so don't need to be updated.
1283 range.row0 = first_row;
1285 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1286 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1288 adjust_scrollbars (sheet);
1290 if (sheet->active_cell.row >= model_rows)
1291 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1293 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1295 redraw_range (sheet, &range);
1299 If row0 or rowi are negative, then all rows will be updated.
1300 If col0 or coli are negative, then all columns will be updated.
1303 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1304 gint rowi, gint coli, gpointer data)
1306 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1308 PsppireSheetRange range;
1315 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1318 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1320 redraw_range (sheet, NULL);
1321 adjust_scrollbars (sheet);
1323 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1324 max_visible_row (sheet));
1326 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1327 max_visible_column (sheet));
1331 else if ( row0 < 0 || rowi < 0 )
1333 range.row0 = min_visible_row (sheet);
1334 range.rowi = max_visible_row (sheet);
1336 else if ( col0 < 0 || coli < 0 )
1338 range.col0 = min_visible_column (sheet);
1339 range.coli = max_visible_column (sheet);
1342 redraw_range (sheet, &range);
1347 * psppire_sheet_new:
1348 * @rows: initial number of rows
1349 * @columns: initial number of columns
1350 * @title: sheet title
1351 * @model: the model to use for the sheet data
1353 * Creates a new sheet widget with the given number of rows and columns.
1355 * Returns: the new sheet widget
1358 psppire_sheet_new (PsppireSheetModel *model)
1360 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1368 * psppire_sheet_set_model
1369 * @sheet: the sheet to set the model for
1370 * @model: the model to use for the sheet data
1372 * Sets the model for a PsppireSheet
1376 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1378 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1380 if (sheet->model ) g_object_unref (sheet->model);
1382 sheet->model = model;
1386 g_object_ref (model);
1388 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1389 G_CALLBACK (range_update_callback),
1392 g_signal_connect (model, "rows_inserted",
1393 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1395 g_signal_connect (model, "rows_deleted",
1396 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1398 g_signal_connect (model, "columns_inserted",
1399 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1401 g_signal_connect (model, "columns_deleted",
1402 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1408 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1412 g_return_if_fail (sheet != NULL);
1413 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1415 state = sheet->select_status;
1417 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1418 psppire_sheet_hide_entry_widget (sheet);
1420 sheet->entry_type = entry_type;
1422 create_sheet_entry (sheet);
1424 if (state == PSPPIRE_SHEET_NORMAL)
1426 psppire_sheet_show_entry_widget (sheet);
1432 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1434 g_return_if_fail (sheet != NULL);
1435 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1437 if (show == sheet->show_grid) return;
1439 sheet->show_grid = show;
1441 redraw_range (sheet, NULL);
1445 psppire_sheet_grid_visible (PsppireSheet *sheet)
1447 g_return_val_if_fail (sheet != NULL, 0);
1448 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1450 return sheet->show_grid;
1454 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1456 g_return_val_if_fail (sheet != NULL, 0);
1457 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1459 return psppire_axis_unit_count (sheet->haxis);
1462 static void set_column_width (PsppireSheet *sheet,
1468 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1470 if (sheet->column_titles_visible) return;
1472 sheet->column_titles_visible = TRUE;
1474 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1477 gdk_window_show (sheet->column_title_window);
1478 gdk_window_move_resize (sheet->column_title_window,
1479 sheet->column_title_area.x,
1480 sheet->column_title_area.y,
1481 sheet->column_title_area.width,
1482 sheet->column_title_area.height);
1484 adjust_scrollbars (sheet);
1486 if (sheet->vadjustment)
1487 g_signal_emit_by_name (sheet->vadjustment,
1490 size_allocate_global_button (sheet);
1492 if ( sheet->row_titles_visible)
1493 gtk_widget_show (sheet->button);
1498 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1500 if (sheet->row_titles_visible) return;
1502 sheet->row_titles_visible = TRUE;
1505 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1507 gdk_window_show (sheet->row_title_window);
1508 gdk_window_move_resize (sheet->row_title_window,
1509 sheet->row_title_area.x,
1510 sheet->row_title_area.y,
1511 sheet->row_title_area.width,
1512 sheet->row_title_area.height);
1514 adjust_scrollbars (sheet);
1517 if (sheet->hadjustment)
1518 g_signal_emit_by_name (sheet->hadjustment,
1521 size_allocate_global_button (sheet);
1523 if ( sheet->column_titles_visible)
1524 gtk_widget_show (sheet->button);
1528 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1530 if (!sheet->column_titles_visible) return;
1532 sheet->column_titles_visible = FALSE;
1534 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1536 if (sheet->column_title_window)
1537 gdk_window_hide (sheet->column_title_window);
1539 gtk_widget_hide (sheet->button);
1541 adjust_scrollbars (sheet);
1544 if (sheet->vadjustment)
1545 g_signal_emit_by_name (sheet->vadjustment,
1550 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1552 if (!sheet->row_titles_visible) return;
1554 sheet->row_titles_visible = FALSE;
1556 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1558 if (sheet->row_title_window)
1559 gdk_window_hide (sheet->row_title_window);
1561 gtk_widget_hide (sheet->button);
1563 adjust_scrollbars (sheet);
1566 if (sheet->hadjustment)
1567 g_signal_emit_by_name (sheet->hadjustment,
1572 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1573 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1574 at the {top,left} of the sheet. If it's 1, then it'll
1575 be placed at the {bottom,right}.
1576 ROW or COL may be -1, in which case scrolling in that dimension
1580 psppire_sheet_moveto (PsppireSheet *sheet,
1588 g_return_if_fail (row_align >= 0);
1589 g_return_if_fail (col_align >= 0);
1591 g_return_if_fail (row_align <= 1);
1592 g_return_if_fail (col_align <= 1);
1594 g_return_if_fail (col <
1595 psppire_axis_unit_count (sheet->haxis));
1596 g_return_if_fail (row <
1597 psppire_axis_unit_count (sheet->vaxis));
1599 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1604 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1606 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1612 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1614 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1620 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
1622 g_return_if_fail (sheet != NULL);
1623 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1625 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
1628 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
1629 psppire_sheet_real_unselect_range (sheet, NULL);
1631 sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
1632 sheet->range.row0 = row;
1633 sheet->range.col0 = 0;
1634 sheet->range.rowi = row;
1635 sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1637 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
1638 psppire_sheet_real_select_range (sheet, NULL);
1643 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
1645 g_return_if_fail (sheet != NULL);
1646 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1648 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
1651 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
1652 psppire_sheet_real_unselect_range (sheet, NULL);
1654 sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
1655 sheet->range.row0 = 0;
1656 sheet->range.col0 = column;
1657 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1658 sheet->range.coli = column;
1660 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
1661 psppire_sheet_real_select_range (sheet, NULL);
1668 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1669 const PsppireSheetRange *range)
1671 g_return_val_if_fail (sheet != NULL, FALSE);
1673 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1676 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1679 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1682 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1685 if (range->rowi < min_visible_row (sheet))
1688 if (range->row0 > max_visible_row (sheet))
1691 if (range->coli < min_visible_column (sheet))
1694 if (range->col0 > max_visible_column (sheet))
1701 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1702 gint row, gint column)
1704 PsppireSheetRange range;
1707 range.col0 = column;
1709 range.coli = column;
1711 return psppire_sheet_range_isvisible (sheet, &range);
1715 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1717 g_return_if_fail (sheet != NULL);
1718 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1719 g_return_if_fail (range != NULL);
1721 range->row0 = min_visible_row (sheet);
1722 range->col0 = min_visible_column (sheet);
1723 range->rowi = max_visible_row (sheet);
1724 range->coli = max_visible_column (sheet);
1729 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1730 GtkAdjustment *hadjustment,
1731 GtkAdjustment *vadjustment)
1733 if ( sheet->vadjustment != vadjustment )
1735 if (sheet->vadjustment)
1736 g_object_unref (sheet->vadjustment);
1737 sheet->vadjustment = vadjustment;
1741 g_object_ref (vadjustment);
1743 g_signal_connect (sheet->vadjustment, "value_changed",
1744 G_CALLBACK (vadjustment_value_changed),
1749 if ( sheet->hadjustment != hadjustment )
1751 if (sheet->hadjustment)
1752 g_object_unref (sheet->hadjustment);
1754 sheet->hadjustment = hadjustment;
1758 g_object_ref (hadjustment);
1760 g_signal_connect (sheet->hadjustment, "value_changed",
1761 G_CALLBACK (hadjustment_value_changed),
1769 psppire_sheet_finalize (GObject *object)
1771 PsppireSheet *sheet;
1773 g_return_if_fail (object != NULL);
1774 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1776 sheet = PSPPIRE_SHEET (object);
1778 if (G_OBJECT_CLASS (parent_class)->finalize)
1779 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1783 psppire_sheet_dispose (GObject *object)
1785 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1787 g_return_if_fail (object != NULL);
1788 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1790 if ( sheet->dispose_has_run )
1793 sheet->dispose_has_run = TRUE;
1795 if ( sheet->cell_padding)
1796 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1798 if (sheet->model) g_object_unref (sheet->model);
1799 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1800 if (sheet->haxis) g_object_unref (sheet->haxis);
1802 g_object_unref (sheet->button);
1803 sheet->button = NULL;
1805 /* unref adjustments */
1806 if (sheet->hadjustment)
1808 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1809 G_SIGNAL_MATCH_DATA,
1813 g_object_unref (sheet->hadjustment);
1814 sheet->hadjustment = NULL;
1817 if (sheet->vadjustment)
1819 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1820 G_SIGNAL_MATCH_DATA,
1824 g_object_unref (sheet->vadjustment);
1826 sheet->vadjustment = NULL;
1829 if (G_OBJECT_CLASS (parent_class)->dispose)
1830 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1834 psppire_sheet_style_set (GtkWidget *widget,
1835 GtkStyle *previous_style)
1837 PsppireSheet *sheet;
1839 g_return_if_fail (widget != NULL);
1840 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1842 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1843 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1845 sheet = PSPPIRE_SHEET (widget);
1847 if (GTK_WIDGET_REALIZED (widget))
1849 gtk_style_set_background (widget->style, widget->window, widget->state);
1852 set_entry_widget_font (sheet);
1857 psppire_sheet_realize (GtkWidget *widget)
1859 PsppireSheet *sheet;
1860 GdkWindowAttr attributes;
1861 const gint attributes_mask =
1862 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1865 GdkColormap *colormap;
1866 GdkDisplay *display;
1868 g_return_if_fail (widget != NULL);
1869 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1871 sheet = PSPPIRE_SHEET (widget);
1873 colormap = gtk_widget_get_colormap (widget);
1874 display = gtk_widget_get_display (widget);
1876 attributes.window_type = GDK_WINDOW_CHILD;
1877 attributes.x = widget->allocation.x;
1878 attributes.y = widget->allocation.y;
1879 attributes.width = widget->allocation.width;
1880 attributes.height = widget->allocation.height;
1881 attributes.wclass = GDK_INPUT_OUTPUT;
1883 attributes.visual = gtk_widget_get_visual (widget);
1884 attributes.colormap = colormap;
1886 attributes.event_mask = gtk_widget_get_events (widget);
1887 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1888 GDK_BUTTON_PRESS_MASK |
1889 GDK_BUTTON_RELEASE_MASK |
1890 GDK_KEY_PRESS_MASK |
1891 GDK_ENTER_NOTIFY_MASK |
1892 GDK_LEAVE_NOTIFY_MASK |
1893 GDK_POINTER_MOTION_MASK |
1894 GDK_POINTER_MOTION_HINT_MASK);
1896 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1899 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1901 gdk_window_set_user_data (widget->window, sheet);
1903 widget->style = gtk_style_attach (widget->style, widget->window);
1905 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1907 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1908 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1910 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1911 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1916 attributes.width = sheet->column_title_area.width;
1917 attributes.height = sheet->column_title_area.height;
1920 /* column - title window */
1921 sheet->column_title_window =
1922 gdk_window_new (widget->window, &attributes, attributes_mask);
1923 gdk_window_set_user_data (sheet->column_title_window, sheet);
1924 gtk_style_set_background (widget->style, sheet->column_title_window,
1930 attributes.width = sheet->row_title_area.width;
1931 attributes.height = sheet->row_title_area.height;
1933 /* row - title window */
1934 sheet->row_title_window = gdk_window_new (widget->window,
1935 &attributes, attributes_mask);
1936 gdk_window_set_user_data (sheet->row_title_window, sheet);
1937 gtk_style_set_background (widget->style, sheet->row_title_window,
1940 /* sheet - window */
1941 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1946 sheet->sheet_window = gdk_window_new (widget->window,
1947 &attributes, attributes_mask);
1948 gdk_window_set_user_data (sheet->sheet_window, sheet);
1950 gdk_cursor_unref (attributes.cursor);
1952 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1953 gdk_window_show (sheet->sheet_window);
1956 sheet->fg_gc = gdk_gc_new (widget->window);
1957 sheet->bg_gc = gdk_gc_new (widget->window);
1959 values.foreground = widget->style->white;
1960 values.function = GDK_INVERT;
1961 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1962 values.line_width = MAX (sheet->cell_padding->left,
1963 MAX (sheet->cell_padding->right,
1964 MAX (sheet->cell_padding->top,
1965 sheet->cell_padding->bottom)));
1967 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1975 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1976 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1978 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1979 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1981 sheet->button->style = gtk_style_attach (sheet->button->style,
1982 sheet->sheet_window);
1985 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1987 if (sheet->column_titles_visible)
1988 gdk_window_show (sheet->column_title_window);
1989 if (sheet->row_titles_visible)
1990 gdk_window_show (sheet->row_title_window);
1992 sheet->hover_window = create_hover_window ();
1994 draw_row_title_buttons (sheet);
1995 draw_column_title_buttons (sheet);
1997 psppire_sheet_update_primary_selection (sheet);
2000 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
2004 create_global_button (PsppireSheet *sheet)
2006 sheet->button = gtk_button_new_with_label (" ");
2008 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
2010 g_object_ref_sink (sheet->button);
2012 g_signal_connect (sheet->button,
2014 G_CALLBACK (global_button_clicked),
2019 size_allocate_global_button (PsppireSheet *sheet)
2021 GtkAllocation allocation;
2023 if (!sheet->column_titles_visible) return;
2024 if (!sheet->row_titles_visible) return;
2026 gtk_widget_size_request (sheet->button, NULL);
2030 allocation.width = sheet->row_title_area.width;
2031 allocation.height = sheet->column_title_area.height;
2033 gtk_widget_size_allocate (sheet->button, &allocation);
2037 global_button_clicked (GtkWidget *widget, gpointer data)
2039 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
2044 psppire_sheet_unrealize (GtkWidget *widget)
2046 PsppireSheet *sheet;
2048 g_return_if_fail (widget != NULL);
2049 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2051 sheet = PSPPIRE_SHEET (widget);
2053 gdk_cursor_unref (sheet->cursor_drag);
2054 sheet->cursor_drag = NULL;
2056 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2057 sheet->color, n_COLORS);
2059 g_object_unref (sheet->xor_gc);
2060 g_object_unref (sheet->fg_gc);
2061 g_object_unref (sheet->bg_gc);
2063 destroy_hover_window (sheet->hover_window);
2065 gdk_window_destroy (sheet->sheet_window);
2066 gdk_window_destroy (sheet->column_title_window);
2067 gdk_window_destroy (sheet->row_title_window);
2069 gtk_widget_unparent (sheet->entry_widget);
2070 if (sheet->button != NULL)
2071 gtk_widget_unparent (sheet->button);
2073 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2074 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2078 psppire_sheet_map (GtkWidget *widget)
2080 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2082 g_return_if_fail (widget != NULL);
2083 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2085 if (!GTK_WIDGET_MAPPED (widget))
2087 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2089 gdk_window_show (widget->window);
2090 gdk_window_show (sheet->sheet_window);
2092 if (sheet->column_titles_visible)
2094 draw_column_title_buttons (sheet);
2095 gdk_window_show (sheet->column_title_window);
2097 if (sheet->row_titles_visible)
2099 draw_row_title_buttons (sheet);
2100 gdk_window_show (sheet->row_title_window);
2103 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2104 && sheet->active_cell.row >= 0
2105 && sheet->active_cell.col >= 0 )
2107 gtk_widget_show (sheet->entry_widget);
2108 gtk_widget_map (sheet->entry_widget);
2111 if (!GTK_WIDGET_MAPPED (sheet->button))
2113 gtk_widget_show (sheet->button);
2114 gtk_widget_map (sheet->button);
2117 redraw_range (sheet, NULL);
2118 change_active_cell (sheet,
2119 sheet->active_cell.row,
2120 sheet->active_cell.col);
2125 psppire_sheet_unmap (GtkWidget *widget)
2127 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2129 if (!GTK_WIDGET_MAPPED (widget))
2132 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2134 gdk_window_hide (sheet->sheet_window);
2135 if (sheet->column_titles_visible)
2136 gdk_window_hide (sheet->column_title_window);
2137 if (sheet->row_titles_visible)
2138 gdk_window_hide (sheet->row_title_window);
2139 gdk_window_hide (widget->window);
2141 gtk_widget_unmap (sheet->entry_widget);
2142 gtk_widget_unmap (sheet->button);
2143 gtk_widget_unmap (sheet->hover_window->window);
2146 /* get cell attributes of the given cell */
2147 /* TRUE means that the cell is currently allocated */
2148 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2150 PsppireSheetCellAttr *attributes);
2155 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2157 PangoLayout *layout;
2158 PangoRectangle text;
2159 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2164 PsppireSheetCellAttr attributes;
2167 g_return_if_fail (sheet != NULL);
2169 /* bail now if we aren't yet drawable */
2170 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2173 row >= psppire_axis_unit_count (sheet->vaxis))
2177 col >= psppire_axis_unit_count (sheet->haxis))
2180 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2182 /* select GC for background rectangle */
2183 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2184 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2186 rectangle_from_cell (sheet, row, col, &area);
2188 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2190 if (sheet->show_grid)
2192 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2194 gdk_draw_rectangle (sheet->sheet_window,
2198 area.width, area.height);
2202 label = psppire_sheet_cell_get_text (sheet, row, col);
2207 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2208 dispose_string (sheet, label);
2211 pango_layout_set_font_description (layout, font_desc);
2213 pango_layout_get_pixel_extents (layout, NULL, &text);
2215 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2217 font_height = pango_font_description_get_size (font_desc);
2218 if ( !pango_font_description_get_size_is_absolute (font_desc))
2219 font_height /= PANGO_SCALE;
2222 if ( sheet->cell_padding )
2224 area.x += sheet->cell_padding->left;
2225 area.width -= sheet->cell_padding->right
2226 + sheet->cell_padding->left;
2228 area.y += sheet->cell_padding->top;
2229 area.height -= sheet->cell_padding->bottom
2231 sheet->cell_padding->top;
2234 /* Centre the text vertically */
2235 area.y += (area.height - font_height) / 2.0;
2237 switch (attributes.justification)
2239 case GTK_JUSTIFY_RIGHT:
2240 area.x += area.width - text.width;
2242 case GTK_JUSTIFY_CENTER:
2243 area.x += (area.width - text.width) / 2.0;
2245 case GTK_JUSTIFY_LEFT:
2249 g_critical ("Unhandled justification %d in column %d\n",
2250 attributes.justification, col);
2254 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2259 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2260 g_object_unref (layout);
2265 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2267 PsppireSheetRange range;
2272 PsppireSheetRange drawing_range;
2274 gdk_region_get_clipbox (region, &area);
2276 y = area.y + sheet->vadjustment->value;
2277 x = area.x + sheet->hadjustment->value;
2279 if ( sheet->column_titles_visible)
2280 y -= sheet->column_title_area.height;
2282 if ( sheet->row_titles_visible)
2283 x -= sheet->row_title_area.width;
2285 maximize_int (&x, 0);
2286 maximize_int (&y, 0);
2288 range.row0 = row_from_ypixel (sheet, y);
2289 range.rowi = row_from_ypixel (sheet, y + area.height);
2291 range.col0 = column_from_xpixel (sheet, x);
2292 range.coli = column_from_xpixel (sheet, x + area.width);
2294 g_return_if_fail (sheet != NULL);
2295 g_return_if_fail (PSPPIRE_SHEET (sheet));
2297 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2298 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2299 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2302 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2303 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2304 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2305 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2307 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2308 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2310 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2312 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2313 psppire_sheet_cell_draw (sheet, i, j);
2316 if (sheet->select_status != PSPPIRE_SHEET_NORMAL &&
2317 psppire_sheet_range_isvisible (sheet, &sheet->range))
2318 psppire_sheet_range_draw_selection (sheet, drawing_range);
2321 if (sheet->select_status == GTK_STATE_NORMAL &&
2322 sheet->active_cell.row >= drawing_range.row0 &&
2323 sheet->active_cell.row <= drawing_range.rowi &&
2324 sheet->active_cell.col >= drawing_range.col0 &&
2325 sheet->active_cell.col <= drawing_range.coli)
2326 psppire_sheet_show_entry_widget (sheet);
2331 psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range)
2335 PsppireSheetRange aux;
2337 if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
2338 range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
2341 if (!psppire_sheet_range_isvisible (sheet, &range)) return;
2342 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2346 range.col0 = MAX (sheet->range.col0, range.col0);
2347 range.coli = MIN (sheet->range.coli, range.coli);
2348 range.row0 = MAX (sheet->range.row0, range.row0);
2349 range.rowi = MIN (sheet->range.rowi, range.rowi);
2351 range.col0 = MAX (range.col0, min_visible_column (sheet));
2352 range.coli = MIN (range.coli, max_visible_column (sheet));
2353 range.row0 = MAX (range.row0, min_visible_row (sheet));
2354 range.rowi = MIN (range.rowi, max_visible_row (sheet));
2356 for (i = range.row0; i <= range.rowi; i++)
2358 for (j = range.col0; j <= range.coli; j++)
2360 if (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED)
2362 rectangle_from_cell (sheet, i, j, &area);
2364 if (i == sheet->range.row0)
2366 area.y = area.y + 2;
2367 area.height = area.height - 2;
2369 if (i == sheet->range.rowi) area.height = area.height - 3;
2370 if (j == sheet->range.col0)
2372 area.x = area.x + 2;
2373 area.width = area.width - 2;
2375 if (j == sheet->range.coli) area.width = area.width - 3;
2377 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2379 gdk_draw_rectangle (sheet->sheet_window,
2382 area.x + 1, area.y + 1,
2383 area.width, area.height);
2390 psppire_sheet_draw_border (sheet, sheet->range);
2394 safe_strcmp (const gchar *s1, const gchar *s2)
2396 if ( !s1 && !s2) return 0;
2397 if ( !s1) return -1;
2398 if ( !s2) return +1;
2399 return strcmp (s1, s2);
2403 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2404 GtkJustification justification,
2407 PsppireSheetModel *model ;
2410 g_return_if_fail (sheet != NULL);
2411 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2413 if (col >= psppire_axis_unit_count (sheet->haxis)
2414 || row >= psppire_axis_unit_count (sheet->vaxis))
2417 if (col < 0 || row < 0) return;
2419 model = psppire_sheet_get_model (sheet);
2421 old_text = psppire_sheet_model_get_string (model, row, col);
2423 if (0 != safe_strcmp (old_text, text))
2425 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2426 psppire_sheet_model_set_string (model, text, row, col);
2427 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2430 if ( psppire_sheet_model_free_strings (model))
2436 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2438 PsppireSheetRange range;
2440 g_return_if_fail (sheet != NULL);
2441 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2442 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2443 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2445 if (column < 0 || row < 0) return;
2449 range.col0 = min_visible_column (sheet);
2450 range.coli = max_visible_column (sheet);
2452 psppire_sheet_real_cell_clear (sheet, row, column);
2454 redraw_range (sheet, &range);
2458 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2460 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2462 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2464 if (old_text && strlen (old_text) > 0 )
2466 psppire_sheet_model_datum_clear (model, row, column);
2469 dispose_string (sheet, old_text);
2473 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2475 PsppireSheetModel *model;
2476 g_return_val_if_fail (sheet != NULL, NULL);
2477 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2479 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2481 if (col < 0 || row < 0) return NULL;
2483 model = psppire_sheet_get_model (sheet);
2488 return psppire_sheet_model_get_string (model, row, col);
2493 psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
2496 PsppireSheetRange *range;
2498 g_return_val_if_fail (sheet != NULL, 0);
2499 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2500 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
2501 if (col < 0 || row < 0) return 0;
2503 state = sheet->select_status;
2504 range = &sheet->range;
2508 case PSPPIRE_SHEET_NORMAL:
2509 return GTK_STATE_NORMAL;
2511 case PSPPIRE_SHEET_ROW_SELECTED:
2512 if (row >= range->row0 && row <= range->rowi)
2513 return GTK_STATE_SELECTED;
2515 case PSPPIRE_SHEET_COLUMN_SELECTED:
2516 if (col >= range->col0 && col <= range->coli)
2517 return GTK_STATE_SELECTED;
2519 case PSPPIRE_SHEET_RANGE_SELECTED:
2520 if (row >= range->row0 && row <= range->rowi && \
2521 col >= range->col0 && col <= range->coli)
2522 return GTK_STATE_SELECTED;
2525 return GTK_STATE_NORMAL;
2528 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2529 If the function returns FALSE, then the results will be unreliable.
2532 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2540 *column = -G_MAXINT;
2542 g_return_val_if_fail (sheet != NULL, 0);
2543 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2545 /* bounds checking, return false if the user clicked
2553 if ( sheet->column_titles_visible)
2554 y -= sheet->column_title_area.height;
2556 y += sheet->vadjustment->value;
2558 if ( y < 0 && sheet->column_titles_visible)
2564 trow = row_from_ypixel (sheet, y);
2565 if (trow > psppire_axis_unit_count (sheet->vaxis))
2571 if ( sheet->row_titles_visible)
2572 x -= sheet->row_title_area.width;
2574 x += sheet->hadjustment->value;
2576 if ( x < 0 && sheet->row_titles_visible)
2582 tcol = column_from_xpixel (sheet, x);
2583 if (tcol > psppire_axis_unit_count (sheet->haxis))
2593 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2598 g_return_val_if_fail (sheet != NULL, 0);
2599 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2601 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2604 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2605 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2607 area->width= (column == -1) ? sheet->row_title_area.width
2608 : psppire_axis_unit_size (sheet->haxis, column);
2610 area->height= (row == -1) ? sheet->column_title_area.height
2611 : psppire_axis_unit_size (sheet->vaxis, row);
2617 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2619 g_return_if_fail (sheet != NULL);
2620 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2622 if (row < -1 || col < -1)
2625 if (row >= psppire_axis_unit_count (sheet->vaxis)
2627 col >= psppire_axis_unit_count (sheet->haxis))
2630 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2633 if ( row == -1 || col == -1)
2635 psppire_sheet_hide_entry_widget (sheet);
2639 change_active_cell (sheet, row, col);
2643 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2645 g_return_if_fail (sheet != NULL);
2646 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2648 if ( row ) *row = sheet->active_cell.row;
2649 if (column) *column = sheet->active_cell.col;
2653 entry_load_text (PsppireSheet *sheet)
2657 GtkJustification justification;
2658 PsppireSheetCellAttr attributes;
2660 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2661 if (sheet->select_status != GTK_STATE_NORMAL) return;
2663 row = sheet->active_cell.row;
2664 col = sheet->active_cell.col;
2666 if (row < 0 || col < 0) return;
2668 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2670 if (text && strlen (text) > 0)
2672 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2673 justification = attributes.justification;
2674 psppire_sheet_set_cell (sheet, row, col, justification, text);
2680 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2682 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2685 if (sheet->active_cell.row < 0 ||
2686 sheet->active_cell.col < 0) return;
2688 gtk_widget_hide (sheet->entry_widget);
2689 gtk_widget_unmap (sheet->entry_widget);
2691 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2695 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2697 gint old_row, old_col;
2699 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2701 if (row < 0 || col < 0)
2704 if ( row > psppire_axis_unit_count (sheet->vaxis)
2705 || col > psppire_axis_unit_count (sheet->haxis))
2708 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2710 sheet->select_status = PSPPIRE_SHEET_NORMAL;
2711 psppire_sheet_real_unselect_range (sheet, NULL);
2714 old_row = sheet->active_cell.row;
2715 old_col = sheet->active_cell.col;
2717 entry_load_text (sheet);
2719 /* Erase the old cell border */
2720 psppire_sheet_draw_active_cell (sheet);
2722 sheet->range.row0 = row;
2723 sheet->range.col0 = col;
2724 sheet->range.rowi = row;
2725 sheet->range.coli = col;
2726 sheet->active_cell.row = row;
2727 sheet->active_cell.col = col;
2728 sheet->selection_cell.row = row;
2729 sheet->selection_cell.col = col;
2731 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2733 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2735 psppire_sheet_draw_active_cell (sheet);
2736 psppire_sheet_show_entry_widget (sheet);
2738 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2740 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2741 row, col, old_row, old_col);
2746 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2748 GtkEntry *sheet_entry;
2749 PsppireSheetCellAttr attributes;
2753 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2755 row = sheet->active_cell.row;
2756 col = sheet->active_cell.col;
2758 /* Don't show the active cell, if there is no active cell: */
2759 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2762 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2763 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2764 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2766 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2768 sheet_entry = psppire_sheet_get_entry (sheet);
2770 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2772 if (GTK_IS_ENTRY (sheet_entry))
2774 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2775 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2778 text = g_strdup ("");
2780 if (strcmp (old_text, text) != 0)
2781 gtk_entry_set_text (sheet_entry, text);
2783 dispose_string (sheet, text);
2786 switch (attributes.justification)
2788 case GTK_JUSTIFY_RIGHT:
2789 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2791 case GTK_JUSTIFY_CENTER:
2792 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2794 case GTK_JUSTIFY_LEFT:
2796 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2802 psppire_sheet_size_allocate_entry (sheet);
2804 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2805 psppire_sheet_model_is_editable (sheet->model,
2807 gtk_widget_map (sheet->entry_widget);
2811 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2814 PsppireSheetRange range;
2816 row = sheet->active_cell.row;
2817 col = sheet->active_cell.col;
2819 if (row < 0 || col < 0) return FALSE;
2821 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2824 range.col0 = range.coli = col;
2825 range.row0 = range.rowi = row;
2827 psppire_sheet_draw_border (sheet, range);
2835 psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range)
2837 gint i, j, mask1, mask2;
2838 gint state, selected;
2839 gint x, y, width, height;
2840 PsppireSheetRange new_range, aux_range;
2842 g_return_if_fail (sheet != NULL);
2844 if (range == NULL) range=&sheet->range;
2848 range->row0 = MIN (range->row0, sheet->range.row0);
2849 range->rowi = MAX (range->rowi, sheet->range.rowi);
2850 range->col0 = MIN (range->col0, sheet->range.col0);
2851 range->coli = MAX (range->coli, sheet->range.coli);
2853 range->row0 = MAX (range->row0, min_visible_row (sheet));
2854 range->rowi = MIN (range->rowi, max_visible_row (sheet));
2855 range->col0 = MAX (range->col0, min_visible_column (sheet));
2856 range->coli = MIN (range->coli, max_visible_column (sheet));
2858 aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
2859 aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
2860 aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
2861 aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
2863 for (i = range->row0; i <= range->rowi; i++)
2865 for (j = range->col0; j <= range->coli; j++)
2868 state = psppire_sheet_cell_get_state (sheet, i, j);
2869 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2870 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2872 if (state == GTK_STATE_SELECTED && selected &&
2873 (i == sheet->range.row0 || i == sheet->range.rowi ||
2874 j == sheet->range.col0 || j == sheet->range.coli ||
2875 i == new_range.row0 || i == new_range.rowi ||
2876 j == new_range.col0 || j == new_range.coli))
2879 mask1 = i == sheet->range.row0 ? 1 : 0;
2880 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2881 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2882 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2884 mask2 = i == new_range.row0 ? 1 : 0;
2885 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2886 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2887 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2891 x = psppire_axis_start_pixel (sheet->haxis, j);
2892 y = psppire_axis_start_pixel (sheet->vaxis, i);
2893 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2894 psppire_axis_unit_size (sheet->haxis, j);
2895 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2897 if (i == sheet->range.row0)
2900 height = height + 3;
2902 if (i == sheet->range.rowi) height = height + 3;
2903 if (j == sheet->range.col0)
2908 if (j == sheet->range.coli) width = width + 3;
2910 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2912 x = psppire_axis_start_pixel (sheet->haxis, j);
2913 y = psppire_axis_start_pixel (sheet->vaxis, i);
2914 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2915 psppire_axis_unit_size (sheet->haxis, j);
2917 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2919 if (i == new_range.row0)
2922 height = height - 2;
2924 if (i == new_range.rowi) height = height - 3;
2925 if (j == new_range.col0)
2930 if (j == new_range.coli) width = width - 3;
2932 gdk_draw_rectangle (sheet->sheet_window,
2943 for (i = range->row0; i <= range->rowi; i++)
2945 for (j = range->col0; j <= range->coli; j++)
2948 state = psppire_sheet_cell_get_state (sheet, i, j);
2949 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2950 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2952 if (state == GTK_STATE_SELECTED && !selected)
2955 x = psppire_axis_start_pixel (sheet->haxis, j);
2956 y = psppire_axis_start_pixel (sheet->vaxis, i);
2957 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2958 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2960 if (i == sheet->range.row0)
2963 height = height + 3;
2965 if (i == sheet->range.rowi) height = height + 3;
2966 if (j == sheet->range.col0)
2971 if (j == sheet->range.coli) width = width + 3;
2977 for (i = range->row0; i <= range->rowi; i++)
2979 for (j = range->col0; j <= range->coli; j++)
2982 state = psppire_sheet_cell_get_state (sheet, i, j);
2983 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2984 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2986 if (state != GTK_STATE_SELECTED && selected &&
2987 (i != sheet->active_cell.row || j != sheet->active_cell.col))
2990 x = psppire_axis_start_pixel (sheet->haxis, j);
2991 y = psppire_axis_start_pixel (sheet->vaxis, i);
2992 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2993 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2995 if (i == new_range.row0)
2998 height = height - 2;
3000 if (i == new_range.rowi) height = height - 3;
3001 if (j == new_range.col0)
3006 if (j == new_range.coli) width = width - 3;
3008 gdk_draw_rectangle (sheet->sheet_window,
3019 for (i = aux_range.row0; i <= aux_range.rowi; i++)
3021 for (j = aux_range.col0; j <= aux_range.coli; j++)
3023 state = psppire_sheet_cell_get_state (sheet, i, j);
3025 mask1 = i == sheet->range.row0 ? 1 : 0;
3026 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
3027 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
3028 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
3030 mask2 = i == new_range.row0 ? 1 : 0;
3031 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
3032 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
3033 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
3034 if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
3036 x = psppire_axis_start_pixel (sheet->haxis, j);
3037 y = psppire_axis_start_pixel (sheet->vaxis, i);
3038 width = psppire_axis_unit_size (sheet->haxis, j);
3039 height = psppire_axis_unit_size (sheet->vaxis, i);
3041 gdk_draw_rectangle (sheet->sheet_window,
3049 gdk_draw_rectangle (sheet->sheet_window,
3052 x + 1, y + height - 1,
3056 gdk_draw_rectangle (sheet->sheet_window,
3064 gdk_draw_rectangle (sheet->sheet_window,
3067 x + width - 1, y + 1,
3079 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
3083 rectangle_from_range (sheet, &new_range, &area);
3088 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
3090 area.x += sheet->cell_padding->left / 2;
3091 area.y += sheet->cell_padding->top / 2;
3092 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
3093 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
3095 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
3102 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
3107 psppire_sheet_real_select_range (PsppireSheet *sheet,
3108 const PsppireSheetRange *range)
3112 g_return_if_fail (sheet != NULL);
3114 if (range == NULL) range = &sheet->range;
3116 memcpy (&sheet->range, range, sizeof (*range));
3118 if (range->row0 < 0 || range->rowi < 0) return;
3119 if (range->col0 < 0 || range->coli < 0) return;
3121 state = sheet->select_status;
3124 if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3125 range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3127 psppire_sheet_new_selection (sheet, &sheet->range);
3131 psppire_sheet_range_draw_selection (sheet, sheet->range);
3135 psppire_sheet_update_primary_selection (sheet);
3137 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3142 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
3144 g_return_if_fail (sheet != NULL);
3145 *range = sheet->range;
3150 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
3152 g_return_if_fail (sheet != NULL);
3154 if (range == NULL) range=&sheet->range;
3156 if (range->row0 < 0 || range->rowi < 0) return;
3157 if (range->col0 < 0 || range->coli < 0) return;
3160 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
3161 psppire_sheet_real_unselect_range (sheet, NULL);
3163 sheet->range.row0 = range->row0;
3164 sheet->range.rowi = range->rowi;
3165 sheet->range.col0 = range->col0;
3166 sheet->range.coli = range->coli;
3167 sheet->selection_cell.row = range->rowi;
3168 sheet->selection_cell.col = range->coli;
3170 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3171 psppire_sheet_real_select_range (sheet, NULL);
3175 psppire_sheet_unselect_range (PsppireSheet *sheet)
3177 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3180 psppire_sheet_real_unselect_range (sheet, NULL);
3181 sheet->select_status = GTK_STATE_NORMAL;
3186 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
3187 const PsppireSheetRange *range)
3189 g_return_if_fail (sheet != NULL);
3190 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3193 range = &sheet->range;
3195 if (range->row0 < 0 || range->rowi < 0) return;
3196 if (range->col0 < 0 || range->coli < 0) return;
3198 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3199 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3201 sheet->range.row0 = -1;
3202 sheet->range.rowi = -1;
3203 sheet->range.col0 = -1;
3204 sheet->range.coli = -1;
3209 psppire_sheet_expose (GtkWidget *widget,
3210 GdkEventExpose *event)
3212 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3214 g_return_val_if_fail (event != NULL, FALSE);
3216 if (!GTK_WIDGET_DRAWABLE (widget))
3219 /* exposure events on the sheet */
3220 if (event->window == sheet->row_title_window &&
3221 sheet->row_titles_visible)
3223 draw_row_title_buttons_range (sheet,
3224 min_visible_row (sheet),
3225 max_visible_row (sheet));
3228 if (event->window == sheet->column_title_window &&
3229 sheet->column_titles_visible)
3231 draw_column_title_buttons_range (sheet,
3232 min_visible_column (sheet),
3233 max_visible_column (sheet));
3236 if (event->window == sheet->sheet_window)
3238 draw_sheet_region (sheet, event->region);
3241 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
3243 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3244 psppire_sheet_range_draw (sheet, &sheet->range);
3246 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3247 psppire_sheet_range_draw (sheet, &sheet->drag_range);
3249 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3250 psppire_sheet_range_draw_selection (sheet, sheet->range);
3251 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3252 draw_xor_rectangle (sheet, sheet->drag_range);
3256 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
3259 PsppireSheetRange range;
3260 range.row0 = range.rowi = sheet->active_cell.row;
3261 range.col0 = range.coli = sheet->active_cell.col;
3263 rectangle_from_range (sheet, &range, &rect);
3265 if (GDK_OVERLAP_RECTANGLE_OUT !=
3266 gdk_region_rect_in (event->region, &rect))
3268 psppire_sheet_draw_active_cell (sheet);
3274 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3281 psppire_sheet_button_press (GtkWidget *widget,
3282 GdkEventButton *event)
3284 PsppireSheet *sheet;
3285 GdkModifierType mods;
3290 g_return_val_if_fail (widget != NULL, FALSE);
3291 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
3292 g_return_val_if_fail (event != NULL, FALSE);
3294 sheet = PSPPIRE_SHEET (widget);
3296 /* Cancel any pending tooltips */
3297 if (sheet->motion_timer)
3299 g_source_remove (sheet->motion_timer);
3300 sheet->motion_timer = 0;
3303 gtk_widget_get_pointer (widget, &x, &y);
3304 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3307 if (event->window == sheet->column_title_window)
3309 sheet->x_drag = event->x;
3310 g_signal_emit (sheet,
3311 sheet_signals[BUTTON_EVENT_COLUMN], 0,
3314 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3316 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3317 g_signal_emit (sheet,
3318 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3321 else if (event->window == sheet->row_title_window)
3323 g_signal_emit (sheet,
3324 sheet_signals[BUTTON_EVENT_ROW], 0,
3327 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3329 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3330 g_signal_emit (sheet,
3331 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3335 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3337 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3340 /* press on resize windows */
3341 if (event->window == sheet->column_title_window)
3343 sheet->x_drag = event->x;
3345 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3347 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3348 gdk_pointer_grab (sheet->column_title_window, FALSE,
3349 GDK_POINTER_MOTION_HINT_MASK |
3350 GDK_BUTTON1_MOTION_MASK |
3351 GDK_BUTTON_RELEASE_MASK,
3352 NULL, NULL, event->time);
3354 draw_xor_vline (sheet);
3359 if (event->window == sheet->row_title_window)
3361 sheet->y_drag = event->y;
3363 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3365 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3366 gdk_pointer_grab (sheet->row_title_window, FALSE,
3367 GDK_POINTER_MOTION_HINT_MASK |
3368 GDK_BUTTON1_MOTION_MASK |
3369 GDK_BUTTON_RELEASE_MASK,
3370 NULL, NULL, event->time);
3372 draw_xor_hline (sheet);
3377 /* the sheet itself does not handle other than single click events */
3378 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3380 /* selections on the sheet */
3381 if (event->window == sheet->sheet_window)
3383 gtk_widget_get_pointer (widget, &x, &y);
3384 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3385 gdk_pointer_grab (sheet->sheet_window, FALSE,
3386 GDK_POINTER_MOTION_HINT_MASK |
3387 GDK_BUTTON1_MOTION_MASK |
3388 GDK_BUTTON_RELEASE_MASK,
3389 NULL, NULL, event->time);
3390 gtk_grab_add (GTK_WIDGET (sheet));
3392 if (psppire_sheet_click_cell (sheet, row, column))
3393 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3396 if (event->window == sheet->column_title_window)
3398 gtk_widget_get_pointer (widget, &x, &y);
3399 if ( sheet->row_titles_visible)
3400 x -= sheet->row_title_area.width;
3402 x += sheet->hadjustment->value;
3404 column = column_from_xpixel (sheet, x);
3406 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3408 gtk_grab_add (GTK_WIDGET (sheet));
3409 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3413 if (event->window == sheet->row_title_window)
3415 gtk_widget_get_pointer (widget, &x, &y);
3416 if ( sheet->column_titles_visible)
3417 y -= sheet->column_title_area.height;
3419 y += sheet->vadjustment->value;
3421 row = row_from_ypixel (sheet, y);
3422 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3424 gtk_grab_add (GTK_WIDGET (sheet));
3425 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3433 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3435 PsppireSheetCell cell;
3436 gboolean forbid_move;
3441 if (row >= psppire_axis_unit_count (sheet->vaxis)
3442 || column >= psppire_axis_unit_count (sheet->haxis))
3447 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3448 &sheet->active_cell,
3454 if (sheet->select_status == GTK_STATE_NORMAL)
3457 row = sheet->active_cell.row;
3458 column = sheet->active_cell.col;
3460 change_active_cell (sheet, row, column);
3464 if (row == -1 && column >= 0)
3466 psppire_sheet_select_column (sheet, column);
3470 if (column == -1 && row >= 0)
3472 psppire_sheet_select_row (sheet, row);
3476 if (row == -1 && column == -1)
3478 sheet->range.row0 = 0;
3479 sheet->range.col0 = 0;
3480 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3482 psppire_axis_unit_count (sheet->haxis) - 1;
3483 psppire_sheet_select_range (sheet, NULL);
3487 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
3489 sheet->select_status = PSPPIRE_SHEET_NORMAL;
3490 psppire_sheet_real_unselect_range (sheet, NULL);
3494 change_active_cell (sheet, row, column);
3497 sheet->selection_cell.row = row;
3498 sheet->selection_cell.col = column;
3499 sheet->range.row0 = row;
3500 sheet->range.col0 = column;
3501 sheet->range.rowi = row;
3502 sheet->range.coli = column;
3503 sheet->select_status = PSPPIRE_SHEET_NORMAL;
3504 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3506 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3512 psppire_sheet_button_release (GtkWidget *widget,
3513 GdkEventButton *event)
3515 GdkDisplay *display = gtk_widget_get_display (widget);
3517 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3519 /* release on resize windows */
3520 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3523 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3524 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3526 gdk_display_pointer_ungrab (display, event->time);
3527 draw_xor_vline (sheet);
3530 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3531 + sheet->hadjustment->value;
3533 set_column_width (sheet, sheet->drag_cell.col, width);
3538 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3541 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3542 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3544 gdk_display_pointer_ungrab (display, event->time);
3545 draw_xor_hline (sheet);
3548 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3549 sheet->vadjustment->value;
3551 set_row_height (sheet, sheet->drag_cell.row, height);
3556 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3558 PsppireSheetRange old_range;
3559 draw_xor_rectangle (sheet, sheet->drag_range);
3560 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3561 gdk_display_pointer_ungrab (display, event->time);
3563 psppire_sheet_real_unselect_range (sheet, NULL);
3565 sheet->selection_cell.row = sheet->selection_cell.row +
3566 (sheet->drag_range.row0 - sheet->range.row0);
3567 sheet->selection_cell.col = sheet->selection_cell.col +
3568 (sheet->drag_range.col0 - sheet->range.col0);
3569 old_range = sheet->range;
3570 sheet->range = sheet->drag_range;
3571 sheet->drag_range = old_range;
3572 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3573 &sheet->drag_range, &sheet->range);
3574 psppire_sheet_select_range (sheet, &sheet->range);
3577 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3579 PsppireSheetRange old_range;
3580 draw_xor_rectangle (sheet, sheet->drag_range);
3581 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3582 gdk_display_pointer_ungrab (display, event->time);
3584 psppire_sheet_real_unselect_range (sheet, NULL);
3586 if (sheet->drag_range.row0 < sheet->range.row0)
3587 sheet->selection_cell.row = sheet->drag_range.row0;
3588 if (sheet->drag_range.rowi >= sheet->range.rowi)
3589 sheet->selection_cell.row = sheet->drag_range.rowi;
3590 if (sheet->drag_range.col0 < sheet->range.col0)
3591 sheet->selection_cell.col = sheet->drag_range.col0;
3592 if (sheet->drag_range.coli >= sheet->range.coli)
3593 sheet->selection_cell.col = sheet->drag_range.coli;
3594 old_range = sheet->range;
3595 sheet->range = sheet->drag_range;
3596 sheet->drag_range = old_range;
3598 if (sheet->select_status == GTK_STATE_NORMAL) sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3599 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3600 &sheet->drag_range, &sheet->range);
3601 psppire_sheet_select_range (sheet, &sheet->range);
3604 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet))
3606 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3607 gdk_display_pointer_ungrab (display, event->time);
3608 change_active_cell (sheet, sheet->active_cell.row,
3609 sheet->active_cell.col);
3612 if (PSPPIRE_SHEET_IN_SELECTION)
3613 gdk_display_pointer_ungrab (display, event->time);
3614 gtk_grab_remove (GTK_WIDGET (sheet));
3616 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3625 /* Shamelessly lifted from gtktooltips */
3627 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3631 gtk_widget_size_request (tip_window, &req);
3632 gtk_paint_flat_box (tip_window->style, tip_window->window,
3633 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3634 NULL, GTK_WIDGET(tip_window), "tooltip",
3635 0, 0, req.width, req.height);
3641 destroy_hover_window (PsppireSheetHoverTitle *h)
3643 gtk_widget_destroy (h->window);
3647 static PsppireSheetHoverTitle *
3648 create_hover_window (void)
3650 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3652 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3654 #if GTK_CHECK_VERSION (2, 9, 0)
3655 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3656 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3659 gtk_widget_set_app_paintable (hw->window, TRUE);
3660 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3661 gtk_widget_set_name (hw->window, "gtk-tooltips");
3662 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3664 g_signal_connect (hw->window,
3666 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3669 hw->label = gtk_label_new (NULL);
3672 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3673 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3675 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3677 gtk_widget_show (hw->label);
3679 g_signal_connect (hw->window,
3681 G_CALLBACK (gtk_widget_destroyed),
3687 #define HOVER_WINDOW_Y_OFFSET 2
3690 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3691 const gchar *subtitle)
3700 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3704 sheet->hover_window->row = row;
3705 sheet->hover_window->column = column;
3707 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3709 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3711 gtk_widget_show (sheet->hover_window->window);
3713 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3719 y += sheet->column_title_area.y;
3720 y += sheet->column_title_area.height;
3721 y += HOVER_WINDOW_Y_OFFSET;
3727 x += sheet->row_title_area.x;
3728 x += sheet->row_title_area.width * 2 / 3.0;
3731 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3736 motion_timeout_callback (gpointer data)
3738 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3742 gdk_threads_enter ();
3743 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3745 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3747 if (sheet->row_title_under && row >= 0)
3749 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3751 show_subtitle (sheet, row, -1, text);
3755 if (sheet->column_title_under && column >= 0)
3757 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3760 show_subtitle (sheet, -1, column, text);
3766 gdk_threads_leave ();
3771 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3773 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3774 GdkModifierType mods;
3775 GdkCursorType new_cursor;
3778 GdkDisplay *display;
3780 g_return_val_if_fail (event != NULL, FALSE);
3782 display = gtk_widget_get_display (widget);
3784 /* selections on the sheet */
3788 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3790 if ( sheet->motion_timer > 0 )
3791 g_source_remove (sheet->motion_timer);
3792 sheet->motion_timer =
3793 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3799 gtk_widget_get_pointer (widget, &wx, &wy);
3801 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3803 if ( row != sheet->hover_window->row ||
3804 column != sheet->hover_window->column)
3806 gtk_widget_hide (sheet->hover_window->window);
3811 if (event->window == sheet->column_title_window)
3813 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3814 on_column_boundary (sheet, x, &column))
3816 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3817 if (new_cursor != sheet->cursor_drag->type)
3819 gdk_cursor_unref (sheet->cursor_drag);
3820 sheet->cursor_drag =
3821 gdk_cursor_new_for_display (display, new_cursor);
3823 gdk_window_set_cursor (sheet->column_title_window,
3824 sheet->cursor_drag);
3829 new_cursor = GDK_TOP_LEFT_ARROW;
3830 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3831 new_cursor != sheet->cursor_drag->type)
3833 gdk_cursor_unref (sheet->cursor_drag);
3834 sheet->cursor_drag =
3835 gdk_cursor_new_for_display (display, new_cursor);
3836 gdk_window_set_cursor (sheet->column_title_window,
3837 sheet->cursor_drag);
3841 else if (event->window == sheet->row_title_window)
3843 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3844 on_row_boundary (sheet, y, &row))
3846 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3847 if (new_cursor != sheet->cursor_drag->type)
3849 gdk_cursor_unref (sheet->cursor_drag);
3850 sheet->cursor_drag =
3851 gdk_cursor_new_for_display (display, new_cursor);
3852 gdk_window_set_cursor (sheet->row_title_window,
3853 sheet->cursor_drag);
3858 new_cursor = GDK_TOP_LEFT_ARROW;
3859 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3860 new_cursor != sheet->cursor_drag->type)
3862 gdk_cursor_unref (sheet->cursor_drag);
3863 sheet->cursor_drag =
3864 gdk_cursor_new_for_display (display, new_cursor);
3865 gdk_window_set_cursor (sheet->row_title_window,
3866 sheet->cursor_drag);
3871 new_cursor = GDK_PLUS;
3872 if ( event->window == sheet->sheet_window &&
3873 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3874 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3875 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3876 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3877 new_cursor != sheet->cursor_drag->type)
3879 gdk_cursor_unref (sheet->cursor_drag);
3880 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3881 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3884 new_cursor = GDK_TOP_LEFT_ARROW;
3885 if ( event->window == sheet->sheet_window &&
3886 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3887 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3888 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3889 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3890 new_cursor != sheet->cursor_drag->type)
3892 gdk_cursor_unref (sheet->cursor_drag);
3893 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3894 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3897 new_cursor = GDK_SIZING;
3898 if ( event->window == sheet->sheet_window &&
3899 sheet->selection_mode != GTK_SELECTION_NONE &&
3900 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3901 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3902 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3903 new_cursor != sheet->cursor_drag->type)
3905 gdk_cursor_unref (sheet->cursor_drag);
3906 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3907 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3911 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3912 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3914 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3916 if (event->x != sheet->x_drag)
3918 draw_xor_vline (sheet);
3919 sheet->x_drag = event->x;
3920 draw_xor_vline (sheet);
3926 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3928 if (event->y != sheet->y_drag)
3930 draw_xor_hline (sheet);
3931 sheet->y_drag = event->y;
3932 draw_xor_hline (sheet);
3938 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3940 PsppireSheetRange aux;
3941 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3942 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3943 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3944 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3948 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3949 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3951 aux = sheet->drag_range;
3952 sheet->drag_range.row0 = sheet->range.row0 + row;
3953 sheet->drag_range.col0 = sheet->range.col0 + column;
3954 sheet->drag_range.rowi = sheet->range.rowi + row;
3955 sheet->drag_range.coli = sheet->range.coli + column;
3956 if (aux.row0 != sheet->drag_range.row0 ||
3957 aux.col0 != sheet->drag_range.col0)
3959 draw_xor_rectangle (sheet, aux);
3960 draw_xor_rectangle (sheet, sheet->drag_range);
3966 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3968 PsppireSheetRange aux;
3969 gint v_h, current_col, current_row, col_threshold, row_threshold;
3971 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
3972 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
3974 current_col = column_from_xpixel (sheet, x);
3975 current_row = row_from_ypixel (sheet, y);
3976 column = current_col - sheet->drag_cell.col;
3977 row = current_row - sheet->drag_cell.row;
3979 /*use half of column width resp. row height as threshold to
3981 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
3982 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
3985 if (x < col_threshold)
3988 else if (column < 0)
3990 if (x > col_threshold)
3993 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
3994 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
3997 if (y < row_threshold)
4002 if (y > row_threshold)
4006 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
4007 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4017 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4018 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4020 aux = sheet->drag_range;
4021 sheet->drag_range = sheet->range;
4023 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4024 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4025 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4026 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4028 if (aux.row0 != sheet->drag_range.row0 ||
4029 aux.rowi != sheet->drag_range.rowi ||
4030 aux.col0 != sheet->drag_range.col0 ||
4031 aux.coli != sheet->drag_range.coli)
4033 draw_xor_rectangle (sheet, aux);
4034 draw_xor_rectangle (sheet, sheet->drag_range);
4040 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
4042 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
4043 column == sheet->active_cell.col) return TRUE;
4045 if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4046 psppire_sheet_extend_selection (sheet, row, column);
4052 psppire_sheet_crossing_notify (GtkWidget *widget,
4053 GdkEventCrossing *event)
4055 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4057 if (event->window == sheet->column_title_window)
4058 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4059 else if (event->window == sheet->row_title_window)
4060 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4062 if (event->type == GDK_LEAVE_NOTIFY)
4063 gtk_widget_hide (sheet->hover_window->window);
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->select_status;
4099 switch (sheet->select_status)
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->select_status = 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->select_status = 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.x += sheet->cell_padding->left;
4611 entry_alloc.y += sheet->cell_padding->right;
4612 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4613 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
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,
4739 GTK_WIDGET (sheet->button),
4741 allocation.x, allocation.y,
4742 allocation.width, allocation.height);
4744 state = button->state;
4745 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4747 if (state == GTK_STATE_ACTIVE)
4748 shadow_type = GTK_SHADOW_IN;
4750 shadow_type = GTK_SHADOW_OUT;
4752 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4753 gtk_paint_box (sheet->button->style, window,
4754 button->state, shadow_type,
4755 &allocation, GTK_WIDGET (sheet->button),
4757 allocation.x, allocation.y,
4758 allocation.width, allocation.height);
4760 if ( button->overstruck)
4762 GdkPoint points[2] = {
4763 {allocation.x, allocation.y},
4764 {allocation.x + allocation.width,
4765 allocation.y + allocation.height}
4768 gtk_paint_polygon (sheet->button->style,
4780 if (button->label_visible)
4782 text_height = DEFAULT_ROW_HEIGHT -
4783 2 * COLUMN_TITLES_HEIGHT;
4785 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4787 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4790 allocation.y += 2 * sheet->button->style->ythickness;
4792 if (button->label && strlen (button->label) > 0)
4794 PangoRectangle rect;
4795 gchar *line = button->label;
4797 PangoLayout *layout = NULL;
4798 gint real_x = allocation.x;
4799 gint real_y = allocation.y;
4801 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4802 pango_layout_get_extents (layout, NULL, &rect);
4804 text_width = PANGO_PIXELS (rect.width);
4805 switch (button->justification)
4807 case GTK_JUSTIFY_LEFT:
4808 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4809 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4811 case GTK_JUSTIFY_RIGHT:
4812 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4813 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4815 case GTK_JUSTIFY_CENTER:
4817 real_x = allocation.x + (allocation.width - text_width)/2;
4818 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4819 pango_layout_set_justify (layout, TRUE);
4821 pango_layout_set_alignment (layout, align);
4822 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4831 g_object_unref (layout);
4834 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4836 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4840 psppire_sheet_button_free (button);
4844 /* Draw the column title buttons FIRST through to LAST */
4846 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4850 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4852 if (!sheet->column_titles_visible) return;
4854 g_return_if_fail (first >= min_visible_column (sheet));
4855 g_return_if_fail (last <= max_visible_column (sheet));
4858 rect.height = sheet->column_title_area.height;
4859 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4860 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4861 + psppire_axis_unit_size (sheet->haxis, last);
4863 rect.x -= sheet->hadjustment->value;
4865 minimize_int (&rect.width, sheet->column_title_area.width);
4866 maximize_int (&rect.x, 0);
4868 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4870 for (col = first ; col <= last ; ++col)
4872 GdkRectangle allocation;
4873 gboolean is_sensitive = FALSE;
4875 PsppireSheetButton *
4876 button = psppire_sheet_model_get_column_button (sheet->model, col);
4878 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4880 allocation.x -= sheet->hadjustment->value;
4882 allocation.height = sheet->column_title_area.height;
4883 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4884 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4886 draw_button (sheet, sheet->column_title_window,
4887 button, is_sensitive, allocation);
4890 gdk_window_end_paint (sheet->column_title_window);
4895 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4899 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4901 if (!sheet->row_titles_visible) return;
4903 g_return_if_fail (first >= min_visible_row (sheet));
4904 g_return_if_fail (last <= max_visible_row (sheet));
4907 rect.width = sheet->row_title_area.width;
4908 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4909 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4910 + psppire_axis_unit_size (sheet->vaxis, last);
4912 rect.y -= sheet->vadjustment->value;
4914 minimize_int (&rect.height, sheet->row_title_area.height);
4915 maximize_int (&rect.y, 0);
4917 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4918 for (row = first; row <= last; ++row)
4920 GdkRectangle allocation;
4922 gboolean is_sensitive = FALSE;
4924 PsppireSheetButton *button =
4925 psppire_sheet_model_get_row_button (sheet->model, row);
4927 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4929 allocation.y -= sheet->vadjustment->value;
4931 allocation.width = sheet->row_title_area.width;
4932 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4933 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4935 draw_button (sheet, sheet->row_title_window,
4936 button, is_sensitive, allocation);
4939 gdk_window_end_paint (sheet->row_title_window);
4946 * vadjustment_value_changed
4947 * hadjustment_value_changed */
4951 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4954 (adj->value + adj->page_size)
4956 (adj->upper - adj->lower);
4958 const glong last_item = psppire_axis_unit_count (axis) - 1;
4960 if (isnan (position) || position < 0)
4964 psppire_axis_start_pixel (axis, last_item)
4966 psppire_axis_unit_size (axis, last_item)
4970 adj->page_size = page_size;
4973 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4975 if ( adj->value < adj->lower)
4976 adj->value = adj->lower;
4979 gtk_adjustment_changed (adj);
4984 adjust_scrollbars (PsppireSheet *sheet)
4988 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4991 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4993 if ( sheet->row_titles_visible)
4994 width -= sheet->row_title_area.width;
4996 if (sheet->column_titles_visible)
4997 height -= sheet->column_title_area.height;
4999 if (sheet->vadjustment)
5001 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
5003 sheet->vadjustment->step_increment =
5005 psppire_axis_unit_size (sheet->vaxis, last_row);
5007 sheet->vadjustment->page_increment =
5009 sheet->column_title_area.height -
5010 psppire_axis_unit_size (sheet->vaxis, last_row);
5012 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
5015 if (sheet->hadjustment)
5017 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
5018 sheet->hadjustment->step_increment = 1;
5020 sheet->hadjustment->page_increment = width;
5022 sheet->hadjustment->upper =
5023 psppire_axis_start_pixel (sheet->haxis, last_col)
5025 psppire_axis_unit_size (sheet->haxis, last_col)
5028 update_adjustment (sheet->hadjustment, sheet->haxis, width);
5032 /* Subtracts the region of WIDGET from REGION */
5034 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
5037 GdkRectangle intersect;
5040 gdk_region_get_clipbox (region, &rect);
5041 gtk_widget_intersect (widget,
5045 region2 = gdk_region_rectangle (&intersect);
5046 gdk_region_subtract (region, region2);
5047 gdk_region_destroy (region2);
5051 vadjustment_value_changed (GtkAdjustment *adjustment,
5055 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5057 g_return_if_fail (adjustment != NULL);
5059 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5061 gtk_widget_hide (sheet->entry_widget);
5064 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5066 subtract_widget_region (region, sheet->button);
5067 gdk_window_begin_paint_region (sheet->sheet_window, region);
5069 draw_sheet_region (sheet, region);
5071 draw_row_title_buttons (sheet);
5072 psppire_sheet_draw_active_cell (sheet);
5074 gdk_window_end_paint (sheet->sheet_window);
5075 gdk_region_destroy (region);
5080 hadjustment_value_changed (GtkAdjustment *adjustment,
5084 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5086 g_return_if_fail (adjustment != NULL);
5088 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5090 gtk_widget_hide (sheet->entry_widget);
5094 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5096 subtract_widget_region (region, sheet->button);
5097 gdk_window_begin_paint_region (sheet->sheet_window, region);
5099 draw_sheet_region (sheet, region);
5101 draw_column_title_buttons (sheet);
5103 psppire_sheet_draw_active_cell (sheet);
5105 gdk_window_end_paint (sheet->sheet_window);
5107 gdk_region_destroy (region);
5111 /* COLUMN RESIZING */
5113 draw_xor_vline (PsppireSheet *sheet)
5116 gint xpos = sheet->x_drag;
5117 gdk_drawable_get_size (sheet->sheet_window,
5120 if (sheet->row_titles_visible)
5121 xpos += sheet->row_title_area.width;
5123 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5125 sheet->column_title_area.height,
5127 height + CELL_SPACING);
5132 draw_xor_hline (PsppireSheet *sheet)
5136 gint ypos = sheet->y_drag;
5138 gdk_drawable_get_size (sheet->sheet_window,
5142 if (sheet->column_titles_visible)
5143 ypos += sheet->column_title_area.height;
5145 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5146 sheet->row_title_area.width,
5148 width + CELL_SPACING,
5152 /* SELECTED RANGE */
5154 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
5157 GdkRectangle clip_area, area;
5160 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
5161 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
5162 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
5163 psppire_axis_unit_size (sheet->haxis, range.coli);
5164 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
5165 psppire_axis_unit_size (sheet->vaxis, range.rowi);
5167 clip_area.x = sheet->row_title_area.width;
5168 clip_area.y = sheet->column_title_area.height;
5170 gdk_drawable_get_size (sheet->sheet_window,
5171 &clip_area.width, &clip_area.height);
5173 if (!sheet->row_titles_visible) clip_area.x = 0;
5174 if (!sheet->column_titles_visible) clip_area.y = 0;
5178 area.width = area.width + area.x;
5181 if (area.width > clip_area.width) area.width = clip_area.width + 10;
5184 area.height = area.height + area.y;
5187 if (area.height > clip_area.height) area.height = clip_area.height + 10;
5191 clip_area.width += 3;
5192 clip_area.height += 3;
5194 gdk_gc_get_values (sheet->xor_gc, &values);
5196 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5198 gdk_draw_rectangle (sheet->sheet_window,
5201 area.x + i, area.y + i,
5202 area.width - 2 * i, area.height - 2 * i);
5205 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5207 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5212 set_column_width (PsppireSheet *sheet,
5216 g_return_if_fail (sheet != NULL);
5217 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5219 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
5225 psppire_axis_resize (sheet->haxis, column,
5226 width - sheet->cell_padding->left -
5227 sheet->cell_padding->right);
5229 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5231 draw_column_title_buttons (sheet);
5232 adjust_scrollbars (sheet);
5233 psppire_sheet_size_allocate_entry (sheet);
5234 redraw_range (sheet, NULL);
5239 set_row_height (PsppireSheet *sheet,
5243 g_return_if_fail (sheet != NULL);
5244 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5246 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
5252 psppire_axis_resize (sheet->vaxis, row,
5253 height - sheet->cell_padding->top -
5254 sheet->cell_padding->bottom);
5256 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
5258 draw_row_title_buttons (sheet);
5259 adjust_scrollbars (sheet);
5260 psppire_sheet_size_allocate_entry (sheet);
5261 redraw_range (sheet, NULL);
5266 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
5267 PsppireSheetCellAttr *attr)
5270 const GtkJustification *j ;
5271 GdkColormap *colormap;
5273 g_return_val_if_fail (sheet != NULL, FALSE);
5274 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
5276 if (row < 0 || col < 0) return FALSE;
5278 attr->foreground = GTK_WIDGET (sheet)->style->black;
5279 attr->background = sheet->color[BG_COLOR];
5281 attr->border.width = 0;
5282 attr->border.line_style = GDK_LINE_SOLID;
5283 attr->border.cap_style = GDK_CAP_NOT_LAST;
5284 attr->border.join_style = GDK_JOIN_MITER;
5285 attr->border.mask = 0;
5286 attr->border.color = GTK_WIDGET (sheet)->style->black;
5288 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
5289 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
5292 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
5293 attr->foreground = *fg;
5296 bg = psppire_sheet_model_get_background (sheet->model, row, col);
5299 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
5300 attr->background = *bg;
5303 attr->justification =
5304 psppire_sheet_model_get_column_justification (sheet->model, col);
5306 j = psppire_sheet_model_get_justification (sheet->model, row, col);
5308 attr->justification = *j;
5314 psppire_sheet_button_size_request (PsppireSheet *sheet,
5315 const PsppireSheetButton *button,
5316 GtkRequisition *button_requisition)
5318 GtkRequisition requisition;
5319 GtkRequisition label_requisition;
5321 label_requisition.height = DEFAULT_ROW_HEIGHT;
5322 label_requisition.width = COLUMN_MIN_WIDTH;
5324 requisition.height = DEFAULT_ROW_HEIGHT;
5325 requisition.width = COLUMN_MIN_WIDTH;
5328 *button_requisition = requisition;
5329 button_requisition->width = MAX (requisition.width, label_requisition.width);
5330 button_requisition->height = MAX (requisition.height, label_requisition.height);
5335 psppire_sheet_forall (GtkContainer *container,
5336 gboolean include_internals,
5337 GtkCallback callback,
5338 gpointer callback_data)
5340 PsppireSheet *sheet = PSPPIRE_SHEET (container);
5342 g_return_if_fail (callback != NULL);
5344 if (sheet->button && sheet->button->parent)
5345 (* callback) (sheet->button, callback_data);
5347 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
5348 (* callback) (sheet->entry_widget, callback_data);
5353 psppire_sheet_get_model (const PsppireSheet *sheet)
5355 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
5357 return sheet->model;
5361 PsppireSheetButton *
5362 psppire_sheet_button_new (void)
5364 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
5366 button->state = GTK_STATE_NORMAL;
5367 button->label = NULL;
5368 button->label_visible = TRUE;
5369 button->justification = GTK_JUSTIFY_FILL;
5370 button->overstruck = FALSE;
5377 psppire_sheet_button_free (PsppireSheetButton *button)
5379 if (!button) return ;
5381 g_free (button->label);
5386 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5388 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5390 if ( NULL == celltext)
5393 g_string_append (string, celltext);
5399 range_to_text (const PsppireSheet *sheet)
5404 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5407 string = g_string_sized_new (80);
5409 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5411 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5413 append_cell_text (string, sheet, r, c);
5414 g_string_append (string, "\t");
5416 append_cell_text (string, sheet, r, c);
5417 if ( r < sheet->range.rowi)
5418 g_string_append (string, "\n");
5425 range_to_html (const PsppireSheet *sheet)
5430 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5433 string = g_string_sized_new (480);
5435 g_string_append (string, "<html>\n");
5436 g_string_append (string, "<body>\n");
5437 g_string_append (string, "<table>\n");
5438 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5440 g_string_append (string, "<tr>\n");
5441 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5443 g_string_append (string, "<td>");
5444 append_cell_text (string, sheet, r, c);
5445 g_string_append (string, "</td>\n");
5447 g_string_append (string, "</tr>\n");
5449 g_string_append (string, "</table>\n");
5450 g_string_append (string, "</body>\n");
5451 g_string_append (string, "</html>\n");
5463 primary_get_cb (GtkClipboard *clipboard,
5464 GtkSelectionData *selection_data,
5468 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5469 GString *string = NULL;
5473 case SELECT_FMT_TEXT:
5474 string = range_to_text (sheet);
5476 case SELECT_FMT_HTML:
5477 string = range_to_html (sheet);
5480 g_assert_not_reached ();
5483 gtk_selection_data_set (selection_data, selection_data->target,
5485 (const guchar *) string->str, string->len);
5486 g_string_free (string, TRUE);
5490 primary_clear_cb (GtkClipboard *clipboard,
5493 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5494 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5497 psppire_sheet_real_unselect_range (sheet, NULL);
5501 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5503 static const GtkTargetEntry targets[] = {
5504 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5505 { "STRING", 0, SELECT_FMT_TEXT },
5506 { "TEXT", 0, SELECT_FMT_TEXT },
5507 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5508 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5509 { "text/plain", 0, SELECT_FMT_TEXT },
5510 { "text/html", 0, SELECT_FMT_HTML }
5513 GtkClipboard *clipboard;
5515 if (!GTK_WIDGET_REALIZED (sheet))
5518 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5519 GDK_SELECTION_PRIMARY);
5521 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5523 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5524 G_N_ELEMENTS (targets),
5525 primary_get_cb, primary_clear_cb,
5527 primary_clear_cb (clipboard, sheet);
5531 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5532 gtk_clipboard_clear (clipboard);