2 Copyright (C) 2006, 2008, 2009 Free Software Foundation
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 This file is derived from the gtksheet.c and extensively modified for the
19 requirements of PSPPIRE. The changes are copyright by the
20 Free Software Foundation. The copyright notice for the original work is
24 /* GtkSheet widget for Gtk+.
25 * Copyright (C) 1999-2001 Adrian E. Feiguin <adrian@ifir.ifir.edu.ar>
27 * Based on GtkClist widget by Jay Painter, but major changes.
28 * Memory allocation routines inspired on SC (Spreadsheet Calculator)
30 * This library is free software; you can redistribute it and/or
31 * modify it under the terms of the GNU Lesser General Public
32 * License as published by the Free Software Foundation; either
33 * version 2.1 of the License, or (at your option) any later version.
35 * This library is distributed in the hope that it will be useful,
36 * but WITHOUT ANY WARRANTY; without even the implied warranty of
37 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
38 * Lesser General Public License for more details.
40 * You should have received a copy of the GNU Lesser General Public
41 * License along with this library; if not, write to the Free Software
42 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
46 * SECTION:psppiresheet
47 * @short_description: spreadsheet widget for gtk2
49 * PsppireSheet is a matrix widget for GTK+. It consists of an scrollable grid of
50 * cells where you can allocate text. Cell contents can be edited interactively
51 * through a specially designed entry, GtkItemEntry.
61 #include <gdk/gdkkeysyms.h>
62 #include <gtk/gtksignal.h>
63 #include <gtk/gtkbutton.h>
64 #include <gtk/gtkadjustment.h>
65 #include <gtk/gtktypeutils.h>
66 #include <gtk/gtkentry.h>
67 #include <gtk/gtkcontainer.h>
68 #include <pango/pango.h>
69 #include "psppire-sheet.h"
70 #include <ui/gui/psppire-marshal.h>
71 #include <ui/gui/sheet/psppire-sheetmodel.h>
72 #include <ui/gui/sheet/psppire-axis.h>
73 #include <libpspp/misc.h>
80 PSPPIRE_SHEET_IN_XDRAG = 1 << 1,
81 PSPPIRE_SHEET_IN_YDRAG = 1 << 2,
82 PSPPIRE_SHEET_IN_DRAG = 1 << 3,
83 PSPPIRE_SHEET_IN_SELECTION = 1 << 4,
84 PSPPIRE_SHEET_IN_RESIZE = 1 << 5
87 #define PSPPIRE_SHEET_FLAGS(sheet) (PSPPIRE_SHEET (sheet)->flags)
88 #define PSPPIRE_SHEET_SET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) |= (flag))
89 #define PSPPIRE_SHEET_UNSET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) &= ~ (flag))
91 #define PSPPIRE_SHEET_IN_XDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_XDRAG)
92 #define PSPPIRE_SHEET_IN_YDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_YDRAG)
93 #define PSPPIRE_SHEET_IN_DRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_DRAG)
94 #define PSPPIRE_SHEET_IN_SELECTION(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_SELECTION)
95 #define PSPPIRE_SHEET_IN_RESIZE(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_RESIZE)
97 #define CELL_SPACING 1
99 #define TIMEOUT_HOVER 300
100 #define COLUMN_MIN_WIDTH 10
101 #define COLUMN_TITLES_HEIGHT 4
102 #define DEFAULT_COLUMN_WIDTH 80
103 #define DEFAULT_ROW_HEIGHT 25
105 static void set_entry_widget_font (PsppireSheet *sheet);
107 static void psppire_sheet_update_primary_selection (PsppireSheet *sheet);
108 static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
109 static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
110 static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range);
113 static void set_row_height (PsppireSheet *sheet,
117 static void destroy_hover_window (PsppireSheetHoverTitle *);
118 static PsppireSheetHoverTitle *create_hover_window (void);
120 static GtkStateType psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col);
124 dispose_string (const PsppireSheet *sheet, gchar *text)
126 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
131 if (psppire_sheet_model_free_strings (model))
136 /* FIXME: Why bother with these two ? */
138 /* returns the column index from a pixel location */
140 column_from_xpixel (const PsppireSheet *sheet, gint pixel)
142 return psppire_axis_unit_at_pixel (sheet->haxis, pixel);
146 row_from_ypixel (const PsppireSheet *sheet, gint pixel)
148 return psppire_axis_unit_at_pixel (sheet->vaxis, pixel);
152 /* Return the lowest row number which is wholly or partially on
153 the visible range of the sheet */
155 min_visible_row (const PsppireSheet *sheet)
157 return row_from_ypixel (sheet, sheet->vadjustment->value);
161 min_fully_visible_row (const PsppireSheet *sheet)
163 glong row = min_visible_row (sheet);
165 if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value)
172 max_visible_row (const PsppireSheet *sheet)
174 return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size);
179 max_fully_visible_row (const PsppireSheet *sheet)
181 glong row = max_visible_row (sheet);
183 if ( psppire_axis_start_pixel (sheet->vaxis, row)
185 psppire_axis_unit_size (sheet->vaxis, row)
186 > sheet->vadjustment->value)
193 /* Returns the lowest column number which is wholly or partially
196 min_visible_column (const PsppireSheet *sheet)
198 return column_from_xpixel (sheet, sheet->hadjustment->value);
202 min_fully_visible_column (const PsppireSheet *sheet)
204 glong col = min_visible_column (sheet);
206 if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value)
213 /* Returns the highest column number which is wholly or partially
216 max_visible_column (const PsppireSheet *sheet)
218 return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size);
222 max_fully_visible_column (const PsppireSheet *sheet)
224 glong col = max_visible_column (sheet);
226 if ( psppire_axis_start_pixel (sheet->haxis, col)
228 psppire_axis_unit_size (sheet->haxis, col)
229 > sheet->hadjustment->value)
237 /* The size of the region (in pixels) around the row/column boundaries
238 where the height/width may be grabbed to change size */
242 on_column_boundary (const PsppireSheet *sheet, gint x, gint *column)
247 x += sheet->hadjustment->value;
252 col = column_from_xpixel (sheet, x);
254 pixel = x - DRAG_WIDTH / 2;
258 if ( column_from_xpixel (sheet, pixel) < col )
264 if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col )
274 on_row_boundary (const PsppireSheet *sheet, gint y, gint *row)
279 y += sheet->vadjustment->value;
284 r = row_from_ypixel (sheet, y);
286 pixel = y - DRAG_WIDTH / 2;
290 if ( row_from_ypixel (sheet, pixel) < r )
296 if ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r )
306 static inline gboolean
307 POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y,
308 gint *drag_row, gint *drag_column)
312 /* Can't drag if nothing is selected */
313 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
314 sheet->range.col0 < 0 || sheet->range.coli < 0 )
317 *drag_column = column_from_xpixel (sheet, x);
318 *drag_row = row_from_ypixel (sheet, y);
320 if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 &&
321 x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
322 psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2)
324 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0);
325 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
327 *drag_row = sheet->range.row0;
330 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
331 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
332 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
334 *drag_row = sheet->range.rowi;
339 if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 &&
340 y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
341 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2)
343 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0);
344 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
346 *drag_column = sheet->range.col0;
349 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
350 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
351 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
353 *drag_column = sheet->range.coli;
361 static inline gboolean
362 POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y,
363 gint *drag_row, gint *drag_column)
367 /* Can't drag if nothing is selected */
368 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
369 sheet->range.col0 < 0 || sheet->range.coli < 0 )
372 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+
373 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
375 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
376 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
378 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED)
379 ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet));
381 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED)
382 xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet));
384 *drag_column = column_from_xpixel (sheet, x);
385 *drag_row = row_from_ypixel (sheet, y);
387 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
388 y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
395 rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *range,
398 g_return_val_if_fail (range, FALSE);
400 r->x = psppire_axis_start_pixel (sheet->haxis, range->col0);
401 r->x -= round (sheet->hadjustment->value);
403 r->y = psppire_axis_start_pixel (sheet->vaxis, range->row0);
404 r->y -= round (sheet->vadjustment->value);
406 r->width = psppire_axis_start_pixel (sheet->haxis, range->coli) -
407 psppire_axis_start_pixel (sheet->haxis, range->col0) +
408 psppire_axis_unit_size (sheet->haxis, range->coli);
410 r->height = psppire_axis_start_pixel (sheet->vaxis, range->rowi) -
411 psppire_axis_start_pixel (sheet->vaxis, range->row0) +
412 psppire_axis_unit_size (sheet->vaxis, range->rowi);
414 if ( sheet->column_titles_visible)
416 r->y += sheet->column_title_area.height;
419 if ( sheet->row_titles_visible)
421 r->x += sheet->row_title_area.width;
428 rectangle_from_cell (PsppireSheet *sheet, gint row, gint col,
431 PsppireSheetRange range;
432 g_return_val_if_fail (row >= 0, FALSE);
433 g_return_val_if_fail (col >= 0, FALSE);
435 range.row0 = range.rowi = row;
436 range.col0 = range.coli = col;
438 return rectangle_from_range (sheet, &range, r);
442 static void psppire_sheet_class_init (PsppireSheetClass *klass);
443 static void psppire_sheet_init (PsppireSheet *sheet);
444 static void psppire_sheet_dispose (GObject *object);
445 static void psppire_sheet_finalize (GObject *object);
446 static void psppire_sheet_style_set (GtkWidget *widget,
447 GtkStyle *previous_style);
448 static void psppire_sheet_realize (GtkWidget *widget);
449 static void psppire_sheet_unrealize (GtkWidget *widget);
450 static void psppire_sheet_map (GtkWidget *widget);
451 static void psppire_sheet_unmap (GtkWidget *widget);
452 static gint psppire_sheet_expose (GtkWidget *widget,
453 GdkEventExpose *event);
455 static void psppire_sheet_forall (GtkContainer *container,
456 gboolean include_internals,
457 GtkCallback callback,
458 gpointer callback_data);
460 static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
461 GtkAdjustment *hadjustment,
462 GtkAdjustment *vadjustment);
464 static gint psppire_sheet_button_press (GtkWidget *widget,
465 GdkEventButton *event);
466 static gint psppire_sheet_button_release (GtkWidget *widget,
467 GdkEventButton *event);
468 static gint psppire_sheet_motion (GtkWidget *widget,
469 GdkEventMotion *event);
470 static gboolean psppire_sheet_crossing_notify (GtkWidget *widget,
471 GdkEventCrossing *event);
472 static gint psppire_sheet_entry_key_press (GtkWidget *widget,
474 static gboolean psppire_sheet_key_press (GtkWidget *widget,
476 static void psppire_sheet_size_request (GtkWidget *widget,
477 GtkRequisition *requisition);
478 static void psppire_sheet_size_allocate (GtkWidget *widget,
479 GtkAllocation *allocation);
481 static gboolean psppire_sheet_focus_in (GtkWidget *widget,
482 GdkEventFocus *event);
486 static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
487 const PsppireSheetRange *range);
488 static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet,
489 gint row, gint column);
490 /* Drawing Routines */
493 static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column);
496 /* draw visible part of range. */
497 static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region);
500 /* highlight the visible part of the selected range */
501 static void psppire_sheet_range_draw_selection (PsppireSheet *sheet,
502 PsppireSheetRange range);
506 static void psppire_sheet_real_select_range (PsppireSheet *sheet,
507 const PsppireSheetRange *range);
508 static void psppire_sheet_real_unselect_range (PsppireSheet *sheet,
509 const PsppireSheetRange *range);
510 static void psppire_sheet_extend_selection (PsppireSheet *sheet,
511 gint row, gint column);
512 static void psppire_sheet_new_selection (PsppireSheet *sheet,
513 PsppireSheetRange *range);
514 static void psppire_sheet_draw_border (PsppireSheet *sheet,
515 PsppireSheetRange range);
517 /* Active Cell handling */
519 static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet);
520 static void change_active_cell (PsppireSheet *sheet, gint row, gint col);
521 static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet);
522 static void psppire_sheet_show_entry_widget (PsppireSheet *sheet);
523 static gboolean psppire_sheet_click_cell (PsppireSheet *sheet,
530 static void adjust_scrollbars (PsppireSheet *sheet);
531 static void vadjustment_value_changed (GtkAdjustment *adjustment,
533 static void hadjustment_value_changed (GtkAdjustment *adjustment,
537 static void draw_xor_vline (PsppireSheet *sheet);
538 static void draw_xor_hline (PsppireSheet *sheet);
539 static void draw_xor_rectangle (PsppireSheet *sheet,
540 PsppireSheetRange range);
544 static void create_global_button (PsppireSheet *sheet);
545 static void global_button_clicked (GtkWidget *widget,
549 static void create_sheet_entry (PsppireSheet *sheet);
550 static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet);
552 /* Sheet button gadgets */
554 static void draw_column_title_buttons (PsppireSheet *sheet);
555 static void draw_row_title_buttons (PsppireSheet *sheet);
558 static void size_allocate_global_button (PsppireSheet *sheet);
559 static void psppire_sheet_button_size_request (PsppireSheet *sheet,
560 const PsppireSheetButton *button,
561 GtkRequisition *requisition);
563 static void psppire_sheet_real_cell_clear (PsppireSheet *sheet,
585 static GtkContainerClass *parent_class = NULL;
586 static guint sheet_signals[LAST_SIGNAL] = { 0 };
590 psppire_sheet_get_type ()
592 static GType sheet_type = 0;
596 static const GTypeInfo sheet_info =
598 sizeof (PsppireSheetClass),
601 (GClassInitFunc) psppire_sheet_class_init,
604 sizeof (PsppireSheet),
606 (GInstanceInitFunc) psppire_sheet_init,
611 g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
619 static PsppireSheetRange*
620 psppire_sheet_range_copy (const PsppireSheetRange *range)
622 PsppireSheetRange *new_range;
624 g_return_val_if_fail (range != NULL, NULL);
626 new_range = g_new (PsppireSheetRange, 1);
634 psppire_sheet_range_free (PsppireSheetRange *range)
636 g_return_if_fail (range != NULL);
642 psppire_sheet_range_get_type (void)
644 static GType sheet_range_type = 0;
646 if (!sheet_range_type)
649 g_boxed_type_register_static ("PsppireSheetRange",
650 (GBoxedCopyFunc) psppire_sheet_range_copy,
651 (GBoxedFreeFunc) psppire_sheet_range_free);
654 return sheet_range_type;
657 static PsppireSheetCell*
658 psppire_sheet_cell_copy (const PsppireSheetCell *cell)
660 PsppireSheetCell *new_cell;
662 g_return_val_if_fail (cell != NULL, NULL);
664 new_cell = g_new (PsppireSheetCell, 1);
672 psppire_sheet_cell_free (PsppireSheetCell *cell)
674 g_return_if_fail (cell != NULL);
680 psppire_sheet_cell_get_type (void)
682 static GType sheet_cell_type = 0;
684 if (!sheet_cell_type)
687 g_boxed_type_register_static ("PsppireSheetCell",
688 (GBoxedCopyFunc) psppire_sheet_cell_copy,
689 (GBoxedFreeFunc) psppire_sheet_cell_free);
692 return sheet_cell_type;
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->state = 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->state = PSPPIRE_SHEET_NORMAL;
1168 sheet->sheet_window = NULL;
1169 sheet->entry_widget = NULL;
1170 sheet->button = NULL;
1172 sheet->hadjustment = NULL;
1173 sheet->vadjustment = NULL;
1175 sheet->cursor_drag = NULL;
1177 sheet->xor_gc = NULL;
1178 sheet->fg_gc = NULL;
1179 sheet->bg_gc = NULL;
1182 sheet->show_grid = TRUE;
1184 sheet->motion_timer = 0;
1186 sheet->row_titles_visible = TRUE;
1187 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1189 sheet->column_titles_visible = TRUE;
1192 /* create sheet entry */
1193 sheet->entry_type = GTK_TYPE_ENTRY;
1194 create_sheet_entry (sheet);
1196 /* create global selection button */
1197 create_global_button (sheet);
1201 /* Cause RANGE to be redrawn. If RANGE is null, then the
1202 entire visible range will be redrawn.
1205 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1209 if ( ! GTK_WIDGET_REALIZED (sheet))
1212 if ( NULL != range )
1213 rectangle_from_range (sheet, range, &rect);
1216 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1217 gdk_region_get_clipbox (r, &rect);
1219 if ( sheet->column_titles_visible)
1221 rect.y += sheet->column_title_area.height;
1222 rect.height -= sheet->column_title_area.height;
1225 if ( sheet->row_titles_visible)
1227 rect.x += sheet->row_title_area.width;
1228 rect.width -= sheet->row_title_area.width;
1232 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1236 /* Callback which occurs whenever columns are inserted / deleted in the model */
1238 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1242 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1244 PsppireSheetRange range;
1245 gint model_columns = psppire_sheet_model_get_column_count (model);
1248 /* Need to update all the columns starting from the first column and onwards.
1249 * Previous column are unchanged, so don't need to be updated.
1251 range.col0 = first_column;
1253 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1254 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1256 adjust_scrollbars (sheet);
1258 if (sheet->active_cell.col >= model_columns)
1259 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1261 draw_column_title_buttons_range (sheet,
1262 first_column, max_visible_column (sheet));
1265 redraw_range (sheet, &range);
1271 /* Callback which occurs whenever rows are inserted / deleted in the model */
1273 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1274 gint n_rows, gpointer data)
1276 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1278 PsppireSheetRange range;
1280 gint model_rows = psppire_sheet_model_get_row_count (model);
1282 /* Need to update all the rows starting from the first row and onwards.
1283 * Previous rows are unchanged, so don't need to be updated.
1285 range.row0 = first_row;
1287 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1288 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1290 adjust_scrollbars (sheet);
1292 if (sheet->active_cell.row >= model_rows)
1293 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1295 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1297 redraw_range (sheet, &range);
1301 If row0 or rowi are negative, then all rows will be updated.
1302 If col0 or coli are negative, then all columns will be updated.
1305 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1306 gint rowi, gint coli, gpointer data)
1308 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1310 PsppireSheetRange range;
1317 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1320 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1322 redraw_range (sheet, NULL);
1323 adjust_scrollbars (sheet);
1325 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1326 max_visible_row (sheet));
1328 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1329 max_visible_column (sheet));
1333 else if ( row0 < 0 || rowi < 0 )
1335 range.row0 = min_visible_row (sheet);
1336 range.rowi = max_visible_row (sheet);
1338 else if ( col0 < 0 || coli < 0 )
1340 range.col0 = min_visible_column (sheet);
1341 range.coli = max_visible_column (sheet);
1344 redraw_range (sheet, &range);
1349 * psppire_sheet_new:
1350 * @rows: initial number of rows
1351 * @columns: initial number of columns
1352 * @title: sheet title
1353 * @model: the model to use for the sheet data
1355 * Creates a new sheet widget with the given number of rows and columns.
1357 * Returns: the new sheet widget
1360 psppire_sheet_new (PsppireSheetModel *model)
1362 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1370 * psppire_sheet_set_model
1371 * @sheet: the sheet to set the model for
1372 * @model: the model to use for the sheet data
1374 * Sets the model for a PsppireSheet
1378 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1380 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1382 if (sheet->model ) g_object_unref (sheet->model);
1384 sheet->model = model;
1388 g_object_ref (model);
1390 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1391 G_CALLBACK (range_update_callback),
1394 g_signal_connect (model, "rows_inserted",
1395 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1397 g_signal_connect (model, "rows_deleted",
1398 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1400 g_signal_connect (model, "columns_inserted",
1401 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1403 g_signal_connect (model, "columns_deleted",
1404 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1410 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1414 g_return_if_fail (sheet != NULL);
1415 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1417 state = sheet->state;
1419 if (sheet->state == PSPPIRE_SHEET_NORMAL)
1420 psppire_sheet_hide_entry_widget (sheet);
1422 sheet->entry_type = entry_type;
1424 create_sheet_entry (sheet);
1426 if (state == PSPPIRE_SHEET_NORMAL)
1428 psppire_sheet_show_entry_widget (sheet);
1434 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1436 g_return_if_fail (sheet != NULL);
1437 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1439 if (show == sheet->show_grid) return;
1441 sheet->show_grid = show;
1443 redraw_range (sheet, NULL);
1447 psppire_sheet_grid_visible (PsppireSheet *sheet)
1449 g_return_val_if_fail (sheet != NULL, 0);
1450 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1452 return sheet->show_grid;
1456 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1458 g_return_val_if_fail (sheet != NULL, 0);
1459 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1461 return psppire_axis_unit_count (sheet->haxis);
1464 static void set_column_width (PsppireSheet *sheet,
1470 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1472 if (sheet->column_titles_visible) return;
1474 sheet->column_titles_visible = TRUE;
1476 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1479 gdk_window_show (sheet->column_title_window);
1480 gdk_window_move_resize (sheet->column_title_window,
1481 sheet->column_title_area.x,
1482 sheet->column_title_area.y,
1483 sheet->column_title_area.width,
1484 sheet->column_title_area.height);
1486 adjust_scrollbars (sheet);
1488 if (sheet->vadjustment)
1489 g_signal_emit_by_name (sheet->vadjustment,
1492 size_allocate_global_button (sheet);
1494 if ( sheet->row_titles_visible)
1495 gtk_widget_show (sheet->button);
1500 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1502 if (sheet->row_titles_visible) return;
1504 sheet->row_titles_visible = TRUE;
1507 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1509 gdk_window_show (sheet->row_title_window);
1510 gdk_window_move_resize (sheet->row_title_window,
1511 sheet->row_title_area.x,
1512 sheet->row_title_area.y,
1513 sheet->row_title_area.width,
1514 sheet->row_title_area.height);
1516 adjust_scrollbars (sheet);
1519 if (sheet->hadjustment)
1520 g_signal_emit_by_name (sheet->hadjustment,
1523 size_allocate_global_button (sheet);
1525 if ( sheet->column_titles_visible)
1526 gtk_widget_show (sheet->button);
1530 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1532 if (!sheet->column_titles_visible) return;
1534 sheet->column_titles_visible = FALSE;
1536 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1538 if (sheet->column_title_window)
1539 gdk_window_hide (sheet->column_title_window);
1541 gtk_widget_hide (sheet->button);
1543 adjust_scrollbars (sheet);
1546 if (sheet->vadjustment)
1547 g_signal_emit_by_name (sheet->vadjustment,
1552 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1554 if (!sheet->row_titles_visible) return;
1556 sheet->row_titles_visible = FALSE;
1558 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1560 if (sheet->row_title_window)
1561 gdk_window_hide (sheet->row_title_window);
1563 gtk_widget_hide (sheet->button);
1565 adjust_scrollbars (sheet);
1568 if (sheet->hadjustment)
1569 g_signal_emit_by_name (sheet->hadjustment,
1574 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1575 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1576 at the {top,left} of the sheet. If it's 1, then it'll
1577 be placed at the {bottom,right}.
1578 ROW or COL may be -1, in which case scrolling in that dimension
1582 psppire_sheet_moveto (PsppireSheet *sheet,
1590 g_return_if_fail (row_align >= 0);
1591 g_return_if_fail (col_align >= 0);
1593 g_return_if_fail (row_align <= 1);
1594 g_return_if_fail (col_align <= 1);
1596 g_return_if_fail (col <
1597 psppire_axis_unit_count (sheet->haxis));
1598 g_return_if_fail (row <
1599 psppire_axis_unit_count (sheet->vaxis));
1601 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1606 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1608 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1614 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1616 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1622 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
1624 g_return_if_fail (sheet != NULL);
1625 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1627 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
1630 if (sheet->state != PSPPIRE_SHEET_NORMAL)
1631 psppire_sheet_real_unselect_range (sheet, NULL);
1633 sheet->state = PSPPIRE_SHEET_ROW_SELECTED;
1634 sheet->range.row0 = row;
1635 sheet->range.col0 = 0;
1636 sheet->range.rowi = row;
1637 sheet->range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1638 sheet->active_cell.row = row;
1640 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, row);
1641 psppire_sheet_real_select_range (sheet, NULL);
1646 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
1648 g_return_if_fail (sheet != NULL);
1649 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1651 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
1654 if (sheet->state != PSPPIRE_SHEET_NORMAL)
1655 psppire_sheet_real_unselect_range (sheet, NULL);
1657 sheet->state = PSPPIRE_SHEET_COLUMN_SELECTED;
1658 sheet->range.row0 = 0;
1659 sheet->range.col0 = column;
1660 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1661 sheet->range.coli = column;
1662 sheet->active_cell.col = column;
1664 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, column);
1665 psppire_sheet_real_select_range (sheet, NULL);
1672 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1673 const PsppireSheetRange *range)
1675 g_return_val_if_fail (sheet != NULL, FALSE);
1677 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1680 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1683 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1686 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1689 if (range->rowi < min_visible_row (sheet))
1692 if (range->row0 > max_visible_row (sheet))
1695 if (range->coli < min_visible_column (sheet))
1698 if (range->col0 > max_visible_column (sheet))
1705 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1706 gint row, gint column)
1708 PsppireSheetRange range;
1711 range.col0 = column;
1713 range.coli = column;
1715 return psppire_sheet_range_isvisible (sheet, &range);
1719 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1721 g_return_if_fail (sheet != NULL);
1722 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1723 g_return_if_fail (range != NULL);
1725 range->row0 = min_visible_row (sheet);
1726 range->col0 = min_visible_column (sheet);
1727 range->rowi = max_visible_row (sheet);
1728 range->coli = max_visible_column (sheet);
1733 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1734 GtkAdjustment *hadjustment,
1735 GtkAdjustment *vadjustment)
1737 if ( sheet->vadjustment != vadjustment )
1739 if (sheet->vadjustment)
1740 g_object_unref (sheet->vadjustment);
1741 sheet->vadjustment = vadjustment;
1745 g_object_ref (vadjustment);
1747 g_signal_connect (sheet->vadjustment, "value_changed",
1748 G_CALLBACK (vadjustment_value_changed),
1753 if ( sheet->hadjustment != hadjustment )
1755 if (sheet->hadjustment)
1756 g_object_unref (sheet->hadjustment);
1758 sheet->hadjustment = hadjustment;
1762 g_object_ref (hadjustment);
1764 g_signal_connect (sheet->hadjustment, "value_changed",
1765 G_CALLBACK (hadjustment_value_changed),
1773 psppire_sheet_finalize (GObject *object)
1775 PsppireSheet *sheet;
1777 g_return_if_fail (object != NULL);
1778 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1780 sheet = PSPPIRE_SHEET (object);
1782 if (G_OBJECT_CLASS (parent_class)->finalize)
1783 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1787 psppire_sheet_dispose (GObject *object)
1789 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1791 g_return_if_fail (object != NULL);
1792 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1794 if ( sheet->dispose_has_run )
1797 sheet->dispose_has_run = TRUE;
1799 if ( sheet->cell_padding)
1800 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1802 if (sheet->model) g_object_unref (sheet->model);
1803 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1804 if (sheet->haxis) g_object_unref (sheet->haxis);
1806 g_object_unref (sheet->button);
1807 sheet->button = NULL;
1809 /* unref adjustments */
1810 if (sheet->hadjustment)
1812 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1813 G_SIGNAL_MATCH_DATA,
1817 g_object_unref (sheet->hadjustment);
1818 sheet->hadjustment = NULL;
1821 if (sheet->vadjustment)
1823 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1824 G_SIGNAL_MATCH_DATA,
1828 g_object_unref (sheet->vadjustment);
1830 sheet->vadjustment = NULL;
1833 if (G_OBJECT_CLASS (parent_class)->dispose)
1834 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1838 psppire_sheet_style_set (GtkWidget *widget,
1839 GtkStyle *previous_style)
1841 PsppireSheet *sheet;
1843 g_return_if_fail (widget != NULL);
1844 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1846 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1847 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1849 sheet = PSPPIRE_SHEET (widget);
1851 if (GTK_WIDGET_REALIZED (widget))
1853 gtk_style_set_background (widget->style, widget->window, widget->state);
1856 set_entry_widget_font (sheet);
1861 psppire_sheet_realize (GtkWidget *widget)
1863 PsppireSheet *sheet;
1864 GdkWindowAttr attributes;
1865 const gint attributes_mask =
1866 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1869 GdkColormap *colormap;
1870 GdkDisplay *display;
1872 g_return_if_fail (widget != NULL);
1873 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1875 sheet = PSPPIRE_SHEET (widget);
1877 colormap = gtk_widget_get_colormap (widget);
1878 display = gtk_widget_get_display (widget);
1880 attributes.window_type = GDK_WINDOW_CHILD;
1881 attributes.x = widget->allocation.x;
1882 attributes.y = widget->allocation.y;
1883 attributes.width = widget->allocation.width;
1884 attributes.height = widget->allocation.height;
1885 attributes.wclass = GDK_INPUT_OUTPUT;
1887 attributes.visual = gtk_widget_get_visual (widget);
1888 attributes.colormap = colormap;
1890 attributes.event_mask = gtk_widget_get_events (widget);
1891 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1892 GDK_BUTTON_PRESS_MASK |
1893 GDK_BUTTON_RELEASE_MASK |
1894 GDK_KEY_PRESS_MASK |
1895 GDK_ENTER_NOTIFY_MASK |
1896 GDK_LEAVE_NOTIFY_MASK |
1897 GDK_POINTER_MOTION_MASK |
1898 GDK_POINTER_MOTION_HINT_MASK);
1900 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1903 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1905 gdk_window_set_user_data (widget->window, sheet);
1907 widget->style = gtk_style_attach (widget->style, widget->window);
1909 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1911 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1912 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1914 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1915 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1920 attributes.width = sheet->column_title_area.width;
1921 attributes.height = sheet->column_title_area.height;
1924 /* column - title window */
1925 sheet->column_title_window =
1926 gdk_window_new (widget->window, &attributes, attributes_mask);
1927 gdk_window_set_user_data (sheet->column_title_window, sheet);
1928 gtk_style_set_background (widget->style, sheet->column_title_window,
1934 attributes.width = sheet->row_title_area.width;
1935 attributes.height = sheet->row_title_area.height;
1937 /* row - title window */
1938 sheet->row_title_window = gdk_window_new (widget->window,
1939 &attributes, attributes_mask);
1940 gdk_window_set_user_data (sheet->row_title_window, sheet);
1941 gtk_style_set_background (widget->style, sheet->row_title_window,
1944 /* sheet - window */
1945 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1950 sheet->sheet_window = gdk_window_new (widget->window,
1951 &attributes, attributes_mask);
1952 gdk_window_set_user_data (sheet->sheet_window, sheet);
1954 gdk_cursor_unref (attributes.cursor);
1956 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1957 gdk_window_show (sheet->sheet_window);
1960 sheet->fg_gc = gdk_gc_new (widget->window);
1961 sheet->bg_gc = gdk_gc_new (widget->window);
1963 values.foreground = widget->style->white;
1964 values.function = GDK_INVERT;
1965 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1966 values.line_width = MAX (sheet->cell_padding->left,
1967 MAX (sheet->cell_padding->right,
1968 MAX (sheet->cell_padding->top,
1969 sheet->cell_padding->bottom)));
1971 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1979 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1980 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1982 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1983 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1985 sheet->button->style = gtk_style_attach (sheet->button->style,
1986 sheet->sheet_window);
1989 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1991 if (sheet->column_titles_visible)
1992 gdk_window_show (sheet->column_title_window);
1993 if (sheet->row_titles_visible)
1994 gdk_window_show (sheet->row_title_window);
1996 sheet->hover_window = create_hover_window ();
1998 draw_row_title_buttons (sheet);
1999 draw_column_title_buttons (sheet);
2001 psppire_sheet_update_primary_selection (sheet);
2004 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
2008 create_global_button (PsppireSheet *sheet)
2010 sheet->button = gtk_button_new_with_label (" ");
2012 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
2014 g_object_ref_sink (sheet->button);
2016 g_signal_connect (sheet->button,
2018 G_CALLBACK (global_button_clicked),
2023 size_allocate_global_button (PsppireSheet *sheet)
2025 GtkAllocation allocation;
2027 if (!sheet->column_titles_visible) return;
2028 if (!sheet->row_titles_visible) return;
2030 gtk_widget_size_request (sheet->button, NULL);
2034 allocation.width = sheet->row_title_area.width;
2035 allocation.height = sheet->column_title_area.height;
2037 gtk_widget_size_allocate (sheet->button, &allocation);
2041 global_button_clicked (GtkWidget *widget, gpointer data)
2043 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
2048 psppire_sheet_unrealize (GtkWidget *widget)
2050 PsppireSheet *sheet;
2052 g_return_if_fail (widget != NULL);
2053 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2055 sheet = PSPPIRE_SHEET (widget);
2057 gdk_cursor_unref (sheet->cursor_drag);
2058 sheet->cursor_drag = NULL;
2060 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2061 sheet->color, n_COLORS);
2063 g_object_unref (sheet->xor_gc);
2064 g_object_unref (sheet->fg_gc);
2065 g_object_unref (sheet->bg_gc);
2067 destroy_hover_window (sheet->hover_window);
2069 gdk_window_destroy (sheet->sheet_window);
2070 gdk_window_destroy (sheet->column_title_window);
2071 gdk_window_destroy (sheet->row_title_window);
2073 gtk_widget_unparent (sheet->entry_widget);
2074 if (sheet->button != NULL)
2075 gtk_widget_unparent (sheet->button);
2077 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2078 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2082 psppire_sheet_map (GtkWidget *widget)
2084 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2086 g_return_if_fail (widget != NULL);
2087 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2089 if (!GTK_WIDGET_MAPPED (widget))
2091 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2093 gdk_window_show (widget->window);
2094 gdk_window_show (sheet->sheet_window);
2096 if (sheet->column_titles_visible)
2098 draw_column_title_buttons (sheet);
2099 gdk_window_show (sheet->column_title_window);
2101 if (sheet->row_titles_visible)
2103 draw_row_title_buttons (sheet);
2104 gdk_window_show (sheet->row_title_window);
2107 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2108 && sheet->active_cell.row >= 0
2109 && sheet->active_cell.col >= 0 )
2111 gtk_widget_show (sheet->entry_widget);
2112 gtk_widget_map (sheet->entry_widget);
2115 if (!GTK_WIDGET_MAPPED (sheet->button))
2117 gtk_widget_show (sheet->button);
2118 gtk_widget_map (sheet->button);
2121 redraw_range (sheet, NULL);
2122 change_active_cell (sheet,
2123 sheet->active_cell.row,
2124 sheet->active_cell.col);
2129 psppire_sheet_unmap (GtkWidget *widget)
2131 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2133 if (!GTK_WIDGET_MAPPED (widget))
2136 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2138 gdk_window_hide (sheet->sheet_window);
2139 if (sheet->column_titles_visible)
2140 gdk_window_hide (sheet->column_title_window);
2141 if (sheet->row_titles_visible)
2142 gdk_window_hide (sheet->row_title_window);
2143 gdk_window_hide (widget->window);
2145 gtk_widget_unmap (sheet->entry_widget);
2146 gtk_widget_unmap (sheet->button);
2147 gtk_widget_unmap (sheet->hover_window->window);
2150 /* get cell attributes of the given cell */
2151 /* TRUE means that the cell is currently allocated */
2152 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2154 PsppireSheetCellAttr *attributes);
2159 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2161 PangoLayout *layout;
2162 PangoRectangle text;
2163 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2168 PsppireSheetCellAttr attributes;
2171 g_return_if_fail (sheet != NULL);
2173 /* bail now if we aren't yet drawable */
2174 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2177 row >= psppire_axis_unit_count (sheet->vaxis))
2181 col >= psppire_axis_unit_count (sheet->haxis))
2184 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2186 /* select GC for background rectangle */
2187 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2188 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2190 rectangle_from_cell (sheet, row, col, &area);
2192 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2194 if (sheet->show_grid)
2196 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2198 gdk_draw_rectangle (sheet->sheet_window,
2202 area.width, area.height);
2206 label = psppire_sheet_cell_get_text (sheet, row, col);
2211 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2212 dispose_string (sheet, label);
2215 pango_layout_set_font_description (layout, font_desc);
2217 pango_layout_get_pixel_extents (layout, NULL, &text);
2219 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2221 font_height = pango_font_description_get_size (font_desc);
2222 if ( !pango_font_description_get_size_is_absolute (font_desc))
2223 font_height /= PANGO_SCALE;
2226 if ( sheet->cell_padding )
2228 area.x += sheet->cell_padding->left;
2229 area.width -= sheet->cell_padding->right
2230 + sheet->cell_padding->left;
2232 area.y += sheet->cell_padding->top;
2233 area.height -= sheet->cell_padding->bottom
2235 sheet->cell_padding->top;
2238 /* Centre the text vertically */
2239 area.y += (area.height - font_height) / 2.0;
2241 switch (attributes.justification)
2243 case GTK_JUSTIFY_RIGHT:
2244 area.x += area.width - text.width;
2246 case GTK_JUSTIFY_CENTER:
2247 area.x += (area.width - text.width) / 2.0;
2249 case GTK_JUSTIFY_LEFT:
2253 g_critical ("Unhandled justification %d in column %d\n",
2254 attributes.justification, col);
2258 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2263 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2264 g_object_unref (layout);
2269 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2271 PsppireSheetRange range;
2276 PsppireSheetRange drawing_range;
2278 gdk_region_get_clipbox (region, &area);
2280 y = area.y + sheet->vadjustment->value;
2281 x = area.x + sheet->hadjustment->value;
2283 if ( sheet->column_titles_visible)
2284 y -= sheet->column_title_area.height;
2286 if ( sheet->row_titles_visible)
2287 x -= sheet->row_title_area.width;
2289 maximize_int (&x, 0);
2290 maximize_int (&y, 0);
2292 range.row0 = row_from_ypixel (sheet, y);
2293 range.rowi = row_from_ypixel (sheet, y + area.height);
2295 range.col0 = column_from_xpixel (sheet, x);
2296 range.coli = column_from_xpixel (sheet, x + area.width);
2298 g_return_if_fail (sheet != NULL);
2299 g_return_if_fail (PSPPIRE_SHEET (sheet));
2301 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2302 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2303 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2306 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2307 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2308 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2309 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2311 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2312 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2314 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2316 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2317 psppire_sheet_cell_draw (sheet, i, j);
2320 if (sheet->state != PSPPIRE_SHEET_NORMAL &&
2321 psppire_sheet_range_isvisible (sheet, &sheet->range))
2322 psppire_sheet_range_draw_selection (sheet, drawing_range);
2325 if (sheet->state == GTK_STATE_NORMAL &&
2326 sheet->active_cell.row >= drawing_range.row0 &&
2327 sheet->active_cell.row <= drawing_range.rowi &&
2328 sheet->active_cell.col >= drawing_range.col0 &&
2329 sheet->active_cell.col <= drawing_range.coli)
2330 psppire_sheet_show_entry_widget (sheet);
2335 psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range)
2339 PsppireSheetRange aux;
2341 if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
2342 range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
2345 if (!psppire_sheet_range_isvisible (sheet, &range)) return;
2346 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2350 range.col0 = MAX (sheet->range.col0, range.col0);
2351 range.coli = MIN (sheet->range.coli, range.coli);
2352 range.row0 = MAX (sheet->range.row0, range.row0);
2353 range.rowi = MIN (sheet->range.rowi, range.rowi);
2355 range.col0 = MAX (range.col0, min_visible_column (sheet));
2356 range.coli = MIN (range.coli, max_visible_column (sheet));
2357 range.row0 = MAX (range.row0, min_visible_row (sheet));
2358 range.rowi = MIN (range.rowi, max_visible_row (sheet));
2360 for (i = range.row0; i <= range.rowi; i++)
2362 for (j = range.col0; j <= range.coli; j++)
2364 if (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED)
2366 rectangle_from_cell (sheet, i, j, &area);
2368 if (i == sheet->range.row0)
2370 area.y = area.y + 2;
2371 area.height = area.height - 2;
2373 if (i == sheet->range.rowi) area.height = area.height - 3;
2374 if (j == sheet->range.col0)
2376 area.x = area.x + 2;
2377 area.width = area.width - 2;
2379 if (j == sheet->range.coli) area.width = area.width - 3;
2381 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2383 gdk_draw_rectangle (sheet->sheet_window,
2386 area.x + 1, area.y + 1,
2387 area.width, area.height);
2394 psppire_sheet_draw_border (sheet, sheet->range);
2398 safe_strcmp (const gchar *s1, const gchar *s2)
2400 if ( !s1 && !s2) return 0;
2401 if ( !s1) return -1;
2402 if ( !s2) return +1;
2403 return strcmp (s1, s2);
2407 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2408 GtkJustification justification,
2411 PsppireSheetModel *model ;
2414 g_return_if_fail (sheet != NULL);
2415 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2417 if (col >= psppire_axis_unit_count (sheet->haxis)
2418 || row >= psppire_axis_unit_count (sheet->vaxis))
2421 if (col < 0 || row < 0) return;
2423 model = psppire_sheet_get_model (sheet);
2425 old_text = psppire_sheet_model_get_string (model, row, col);
2427 if (0 != safe_strcmp (old_text, text))
2429 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2430 psppire_sheet_model_set_string (model, text, row, col);
2431 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2434 if ( psppire_sheet_model_free_strings (model))
2440 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2442 PsppireSheetRange range;
2444 g_return_if_fail (sheet != NULL);
2445 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2446 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2447 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2449 if (column < 0 || row < 0) return;
2453 range.col0 = min_visible_column (sheet);
2454 range.coli = max_visible_column (sheet);
2456 psppire_sheet_real_cell_clear (sheet, row, column);
2458 redraw_range (sheet, &range);
2462 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2464 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2466 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2468 if (old_text && strlen (old_text) > 0 )
2470 psppire_sheet_model_datum_clear (model, row, column);
2473 dispose_string (sheet, old_text);
2477 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2479 PsppireSheetModel *model;
2480 g_return_val_if_fail (sheet != NULL, NULL);
2481 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2483 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2485 if (col < 0 || row < 0) return NULL;
2487 model = psppire_sheet_get_model (sheet);
2492 return psppire_sheet_model_get_string (model, row, col);
2497 psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
2500 PsppireSheetRange *range;
2502 g_return_val_if_fail (sheet != NULL, 0);
2503 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2504 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
2505 if (col < 0 || row < 0) return 0;
2507 state = sheet->state;
2508 range = &sheet->range;
2512 case PSPPIRE_SHEET_NORMAL:
2513 return GTK_STATE_NORMAL;
2515 case PSPPIRE_SHEET_ROW_SELECTED:
2516 if (row >= range->row0 && row <= range->rowi)
2517 return GTK_STATE_SELECTED;
2519 case PSPPIRE_SHEET_COLUMN_SELECTED:
2520 if (col >= range->col0 && col <= range->coli)
2521 return GTK_STATE_SELECTED;
2523 case PSPPIRE_SHEET_RANGE_SELECTED:
2524 if (row >= range->row0 && row <= range->rowi && \
2525 col >= range->col0 && col <= range->coli)
2526 return GTK_STATE_SELECTED;
2529 return GTK_STATE_NORMAL;
2532 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2533 If the function returns FALSE, then the results will be unreliable.
2536 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2544 *column = -G_MAXINT;
2546 g_return_val_if_fail (sheet != NULL, 0);
2547 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2549 /* bounds checking, return false if the user clicked
2557 if ( sheet->column_titles_visible)
2558 y -= sheet->column_title_area.height;
2560 y += sheet->vadjustment->value;
2562 if ( y < 0 && sheet->column_titles_visible)
2568 trow = row_from_ypixel (sheet, y);
2569 if (trow > psppire_axis_unit_count (sheet->vaxis))
2575 if ( sheet->row_titles_visible)
2576 x -= sheet->row_title_area.width;
2578 x += sheet->hadjustment->value;
2580 if ( x < 0 && sheet->row_titles_visible)
2586 tcol = column_from_xpixel (sheet, x);
2587 if (tcol > psppire_axis_unit_count (sheet->haxis))
2597 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2602 g_return_val_if_fail (sheet != NULL, 0);
2603 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2605 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2608 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2609 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2611 area->width= (column == -1) ? sheet->row_title_area.width
2612 : psppire_axis_unit_size (sheet->haxis, column);
2614 area->height= (row == -1) ? sheet->column_title_area.height
2615 : psppire_axis_unit_size (sheet->vaxis, row);
2621 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2623 g_return_if_fail (sheet != NULL);
2624 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2626 if (row < -1 || col < -1)
2629 if (row >= psppire_axis_unit_count (sheet->vaxis)
2631 col >= psppire_axis_unit_count (sheet->haxis))
2634 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2637 if ( row == -1 || col == -1)
2639 psppire_sheet_hide_entry_widget (sheet);
2643 change_active_cell (sheet, row, col);
2647 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2649 g_return_if_fail (sheet != NULL);
2650 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2652 if ( row ) *row = sheet->active_cell.row;
2653 if (column) *column = sheet->active_cell.col;
2657 entry_load_text (PsppireSheet *sheet)
2661 GtkJustification justification;
2662 PsppireSheetCellAttr attributes;
2664 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2665 if (sheet->state != GTK_STATE_NORMAL) return;
2667 row = sheet->active_cell.row;
2668 col = sheet->active_cell.col;
2670 if (row < 0 || col < 0) return;
2672 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2674 if (text && strlen (text) > 0)
2676 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2677 justification = attributes.justification;
2678 psppire_sheet_set_cell (sheet, row, col, justification, text);
2684 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2686 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2689 if (sheet->active_cell.row < 0 ||
2690 sheet->active_cell.col < 0) return;
2692 gtk_widget_hide (sheet->entry_widget);
2693 gtk_widget_unmap (sheet->entry_widget);
2695 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2699 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2701 gint old_row, old_col;
2703 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2705 if (row < 0 || col < 0)
2708 if ( row > psppire_axis_unit_count (sheet->vaxis)
2709 || col > psppire_axis_unit_count (sheet->haxis))
2712 if (sheet->state != PSPPIRE_SHEET_NORMAL)
2714 sheet->state = PSPPIRE_SHEET_NORMAL;
2715 psppire_sheet_real_unselect_range (sheet, NULL);
2718 old_row = sheet->active_cell.row;
2719 old_col = sheet->active_cell.col;
2721 /* Erase the old cell */
2722 psppire_sheet_draw_active_cell (sheet);
2724 entry_load_text (sheet);
2726 sheet->range.row0 = row;
2727 sheet->range.col0 = col;
2728 sheet->range.rowi = row;
2729 sheet->range.coli = col;
2730 sheet->active_cell.row = row;
2731 sheet->active_cell.col = col;
2732 sheet->selection_cell.row = row;
2733 sheet->selection_cell.col = col;
2735 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2737 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2739 psppire_sheet_draw_active_cell (sheet);
2740 psppire_sheet_show_entry_widget (sheet);
2742 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2744 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2745 row, col, old_row, old_col);
2750 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2752 GtkEntry *sheet_entry;
2753 PsppireSheetCellAttr attributes;
2757 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2759 row = sheet->active_cell.row;
2760 col = sheet->active_cell.col;
2762 /* Don't show the active cell, if there is no active cell: */
2763 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2766 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2767 if (sheet->state != PSPPIRE_SHEET_NORMAL) return;
2768 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2770 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2772 sheet_entry = psppire_sheet_get_entry (sheet);
2774 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2776 if (GTK_IS_ENTRY (sheet_entry))
2778 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2779 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2782 text = g_strdup ("");
2784 if (strcmp (old_text, text) != 0)
2785 gtk_entry_set_text (sheet_entry, text);
2787 dispose_string (sheet, text);
2790 switch (attributes.justification)
2792 case GTK_JUSTIFY_RIGHT:
2793 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2795 case GTK_JUSTIFY_CENTER:
2796 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2798 case GTK_JUSTIFY_LEFT:
2800 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2806 psppire_sheet_size_allocate_entry (sheet);
2808 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2809 psppire_sheet_model_is_editable (sheet->model,
2811 gtk_widget_map (sheet->entry_widget);
2815 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2818 PsppireSheetRange range;
2820 row = sheet->active_cell.row;
2821 col = sheet->active_cell.col;
2823 if (row < 0 || col < 0) return FALSE;
2825 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2828 range.col0 = range.coli = col;
2829 range.row0 = range.rowi = row;
2831 psppire_sheet_draw_border (sheet, range);
2839 psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range)
2841 gint i, j, mask1, mask2;
2842 gint state, selected;
2843 gint x, y, width, height;
2844 PsppireSheetRange new_range, aux_range;
2846 g_return_if_fail (sheet != NULL);
2848 if (range == NULL) range=&sheet->range;
2852 range->row0 = MIN (range->row0, sheet->range.row0);
2853 range->rowi = MAX (range->rowi, sheet->range.rowi);
2854 range->col0 = MIN (range->col0, sheet->range.col0);
2855 range->coli = MAX (range->coli, sheet->range.coli);
2857 range->row0 = MAX (range->row0, min_visible_row (sheet));
2858 range->rowi = MIN (range->rowi, max_visible_row (sheet));
2859 range->col0 = MAX (range->col0, min_visible_column (sheet));
2860 range->coli = MIN (range->coli, max_visible_column (sheet));
2862 aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
2863 aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
2864 aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
2865 aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
2867 for (i = range->row0; i <= range->rowi; i++)
2869 for (j = range->col0; j <= range->coli; j++)
2872 state = psppire_sheet_cell_get_state (sheet, i, j);
2873 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2874 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2876 if (state == GTK_STATE_SELECTED && selected &&
2877 (i == sheet->range.row0 || i == sheet->range.rowi ||
2878 j == sheet->range.col0 || j == sheet->range.coli ||
2879 i == new_range.row0 || i == new_range.rowi ||
2880 j == new_range.col0 || j == new_range.coli))
2883 mask1 = i == sheet->range.row0 ? 1 : 0;
2884 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2885 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2886 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2888 mask2 = i == new_range.row0 ? 1 : 0;
2889 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2890 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2891 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2895 x = psppire_axis_start_pixel (sheet->haxis, j);
2896 y = psppire_axis_start_pixel (sheet->vaxis, i);
2897 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2898 psppire_axis_unit_size (sheet->haxis, j);
2899 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2901 if (i == sheet->range.row0)
2904 height = height + 3;
2906 if (i == sheet->range.rowi) height = height + 3;
2907 if (j == sheet->range.col0)
2912 if (j == sheet->range.coli) width = width + 3;
2914 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2916 x = psppire_axis_start_pixel (sheet->haxis, j);
2917 y = psppire_axis_start_pixel (sheet->vaxis, i);
2918 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2919 psppire_axis_unit_size (sheet->haxis, j);
2921 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2923 if (i == new_range.row0)
2926 height = height - 2;
2928 if (i == new_range.rowi) height = height - 3;
2929 if (j == new_range.col0)
2934 if (j == new_range.coli) width = width - 3;
2936 gdk_draw_rectangle (sheet->sheet_window,
2947 for (i = range->row0; i <= range->rowi; i++)
2949 for (j = range->col0; j <= range->coli; j++)
2952 state = psppire_sheet_cell_get_state (sheet, i, j);
2953 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2954 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2956 if (state == GTK_STATE_SELECTED && !selected)
2959 x = psppire_axis_start_pixel (sheet->haxis, j);
2960 y = psppire_axis_start_pixel (sheet->vaxis, i);
2961 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2962 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2964 if (i == sheet->range.row0)
2967 height = height + 3;
2969 if (i == sheet->range.rowi) height = height + 3;
2970 if (j == sheet->range.col0)
2975 if (j == sheet->range.coli) width = width + 3;
2981 for (i = range->row0; i <= range->rowi; i++)
2983 for (j = range->col0; j <= range->coli; j++)
2986 state = psppire_sheet_cell_get_state (sheet, i, j);
2987 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2988 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2990 if (state != GTK_STATE_SELECTED && selected &&
2991 (i != sheet->active_cell.row || j != sheet->active_cell.col))
2994 x = psppire_axis_start_pixel (sheet->haxis, j);
2995 y = psppire_axis_start_pixel (sheet->vaxis, i);
2996 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2997 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2999 if (i == new_range.row0)
3002 height = height - 2;
3004 if (i == new_range.rowi) height = height - 3;
3005 if (j == new_range.col0)
3010 if (j == new_range.coli) width = width - 3;
3012 gdk_draw_rectangle (sheet->sheet_window,
3023 for (i = aux_range.row0; i <= aux_range.rowi; i++)
3025 for (j = aux_range.col0; j <= aux_range.coli; j++)
3027 state = psppire_sheet_cell_get_state (sheet, i, j);
3029 mask1 = i == sheet->range.row0 ? 1 : 0;
3030 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
3031 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
3032 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
3034 mask2 = i == new_range.row0 ? 1 : 0;
3035 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
3036 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
3037 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
3038 if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
3040 x = psppire_axis_start_pixel (sheet->haxis, j);
3041 y = psppire_axis_start_pixel (sheet->vaxis, i);
3042 width = psppire_axis_unit_size (sheet->haxis, j);
3043 height = psppire_axis_unit_size (sheet->vaxis, i);
3045 gdk_draw_rectangle (sheet->sheet_window,
3053 gdk_draw_rectangle (sheet->sheet_window,
3056 x + 1, y + height - 1,
3060 gdk_draw_rectangle (sheet->sheet_window,
3068 gdk_draw_rectangle (sheet->sheet_window,
3071 x + width - 1, y + 1,
3083 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
3087 rectangle_from_range (sheet, &new_range, &area);
3092 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
3094 area.x += sheet->cell_padding->left / 2;
3095 area.y += sheet->cell_padding->top / 2;
3096 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
3097 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
3099 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
3106 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
3111 psppire_sheet_real_select_range (PsppireSheet *sheet,
3112 const PsppireSheetRange *range)
3116 g_return_if_fail (sheet != NULL);
3118 if (range == NULL) range = &sheet->range;
3120 memcpy (&sheet->range, range, sizeof (*range));
3122 if (range->row0 < 0 || range->rowi < 0) return;
3123 if (range->col0 < 0 || range->coli < 0) return;
3125 state = sheet->state;
3128 if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3129 range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3131 psppire_sheet_new_selection (sheet, &sheet->range);
3135 psppire_sheet_range_draw_selection (sheet, sheet->range);
3139 psppire_sheet_update_primary_selection (sheet);
3141 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3146 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
3148 g_return_if_fail (sheet != NULL);
3149 *range = sheet->range;
3154 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
3156 g_return_if_fail (sheet != NULL);
3158 if (range == NULL) range=&sheet->range;
3160 if (range->row0 < 0 || range->rowi < 0) return;
3161 if (range->col0 < 0 || range->coli < 0) return;
3164 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3165 psppire_sheet_real_unselect_range (sheet, NULL);
3167 sheet->range.row0 = range->row0;
3168 sheet->range.rowi = range->rowi;
3169 sheet->range.col0 = range->col0;
3170 sheet->range.coli = range->coli;
3171 sheet->active_cell.row = range->row0;
3172 sheet->active_cell.col = range->col0;
3173 sheet->selection_cell.row = range->rowi;
3174 sheet->selection_cell.col = range->coli;
3176 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3177 psppire_sheet_real_select_range (sheet, NULL);
3181 psppire_sheet_unselect_range (PsppireSheet *sheet)
3183 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3186 psppire_sheet_real_unselect_range (sheet, NULL);
3187 sheet->state = GTK_STATE_NORMAL;
3189 change_active_cell (sheet,
3190 sheet->active_cell.row, sheet->active_cell.col);
3195 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
3196 const PsppireSheetRange *range)
3198 g_return_if_fail (sheet != NULL);
3199 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3202 range = &sheet->range;
3204 if (range->row0 < 0 || range->rowi < 0) return;
3205 if (range->col0 < 0 || range->coli < 0) return;
3207 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3208 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3210 sheet->range.row0 = -1;
3211 sheet->range.rowi = -1;
3212 sheet->range.col0 = -1;
3213 sheet->range.coli = -1;
3218 psppire_sheet_expose (GtkWidget *widget,
3219 GdkEventExpose *event)
3221 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3223 g_return_val_if_fail (event != NULL, FALSE);
3225 if (!GTK_WIDGET_DRAWABLE (widget))
3228 /* exposure events on the sheet */
3229 if (event->window == sheet->row_title_window &&
3230 sheet->row_titles_visible)
3232 draw_row_title_buttons_range (sheet,
3233 min_visible_row (sheet),
3234 max_visible_row (sheet));
3237 if (event->window == sheet->column_title_window &&
3238 sheet->column_titles_visible)
3240 draw_column_title_buttons_range (sheet,
3241 min_visible_column (sheet),
3242 max_visible_column (sheet));
3245 if (event->window == sheet->sheet_window)
3247 draw_sheet_region (sheet, event->region);
3250 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3252 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3253 psppire_sheet_range_draw (sheet, &sheet->range);
3255 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3256 psppire_sheet_range_draw (sheet, &sheet->drag_range);
3258 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3259 psppire_sheet_range_draw_selection (sheet, sheet->range);
3260 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3261 draw_xor_rectangle (sheet, sheet->drag_range);
3265 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
3268 PsppireSheetRange range;
3269 range.row0 = range.rowi = sheet->active_cell.row;
3270 range.col0 = range.coli = sheet->active_cell.col;
3272 rectangle_from_range (sheet, &range, &rect);
3274 if (GDK_OVERLAP_RECTANGLE_OUT !=
3275 gdk_region_rect_in (event->region, &rect))
3277 psppire_sheet_draw_active_cell (sheet);
3283 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3290 psppire_sheet_button_press (GtkWidget *widget,
3291 GdkEventButton *event)
3293 PsppireSheet *sheet;
3294 GdkModifierType mods;
3299 g_return_val_if_fail (widget != NULL, FALSE);
3300 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
3301 g_return_val_if_fail (event != NULL, FALSE);
3303 sheet = PSPPIRE_SHEET (widget);
3305 /* Cancel any pending tooltips */
3306 if (sheet->motion_timer)
3308 g_source_remove (sheet->motion_timer);
3309 sheet->motion_timer = 0;
3312 gtk_widget_get_pointer (widget, &x, &y);
3313 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3316 if (event->window == sheet->column_title_window)
3318 sheet->x_drag = event->x;
3319 g_signal_emit (sheet,
3320 sheet_signals[BUTTON_EVENT_COLUMN], 0,
3323 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3325 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3326 g_signal_emit (sheet,
3327 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3330 else if (event->window == sheet->row_title_window)
3332 g_signal_emit (sheet,
3333 sheet_signals[BUTTON_EVENT_ROW], 0,
3336 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3338 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3339 g_signal_emit (sheet,
3340 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3344 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3346 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3349 /* press on resize windows */
3350 if (event->window == sheet->column_title_window)
3352 sheet->x_drag = event->x;
3354 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3356 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3357 gdk_pointer_grab (sheet->column_title_window, FALSE,
3358 GDK_POINTER_MOTION_HINT_MASK |
3359 GDK_BUTTON1_MOTION_MASK |
3360 GDK_BUTTON_RELEASE_MASK,
3361 NULL, NULL, event->time);
3363 draw_xor_vline (sheet);
3368 if (event->window == sheet->row_title_window)
3370 sheet->y_drag = event->y;
3372 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3374 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3375 gdk_pointer_grab (sheet->row_title_window, FALSE,
3376 GDK_POINTER_MOTION_HINT_MASK |
3377 GDK_BUTTON1_MOTION_MASK |
3378 GDK_BUTTON_RELEASE_MASK,
3379 NULL, NULL, event->time);
3381 draw_xor_hline (sheet);
3386 /* the sheet itself does not handle other than single click events */
3387 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3389 /* selections on the sheet */
3390 if (event->window == sheet->sheet_window)
3392 gtk_widget_get_pointer (widget, &x, &y);
3393 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3394 gdk_pointer_grab (sheet->sheet_window, FALSE,
3395 GDK_POINTER_MOTION_HINT_MASK |
3396 GDK_BUTTON1_MOTION_MASK |
3397 GDK_BUTTON_RELEASE_MASK,
3398 NULL, NULL, event->time);
3399 gtk_grab_add (GTK_WIDGET (sheet));
3401 if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
3402 sheet->selection_mode != GTK_SELECTION_NONE &&
3403 sheet->cursor_drag->type == GDK_SIZING &&
3404 !PSPPIRE_SHEET_IN_SELECTION (sheet) && !PSPPIRE_SHEET_IN_RESIZE (sheet))
3406 if (sheet->state == GTK_STATE_NORMAL)
3408 row = sheet->active_cell.row;
3409 column = sheet->active_cell.col;
3410 sheet->active_cell.row = row;
3411 sheet->active_cell.col = column;
3412 sheet->drag_range = sheet->range;
3413 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3414 psppire_sheet_select_range (sheet, &sheet->drag_range);
3418 if (row > sheet->range.rowi) row--;
3419 if (column > sheet->range.coli) column--;
3420 sheet->drag_cell.row = row;
3421 sheet->drag_cell.col = column;
3422 sheet->drag_range = sheet->range;
3423 draw_xor_rectangle (sheet, sheet->drag_range);
3424 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3426 else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
3427 !PSPPIRE_SHEET_IN_SELECTION (sheet)
3428 && ! PSPPIRE_SHEET_IN_DRAG (sheet)
3429 && sheet->active_cell.row >= 0
3430 && sheet->active_cell.col >= 0
3433 if (sheet->state == GTK_STATE_NORMAL)
3435 row = sheet->active_cell.row;
3436 column = sheet->active_cell.col;
3437 sheet->active_cell.row = row;
3438 sheet->active_cell.col = column;
3439 sheet->drag_range = sheet->range;
3440 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3441 psppire_sheet_select_range (sheet, &sheet->drag_range);
3445 if (row < sheet->range.row0) row++;
3446 if (row > sheet->range.rowi) row--;
3447 if (column < sheet->range.col0) column++;
3448 if (column > sheet->range.coli) column--;
3449 sheet->drag_cell.row = row;
3450 sheet->drag_cell.col = column;
3451 sheet->drag_range = sheet->range;
3452 draw_xor_rectangle (sheet, sheet->drag_range);
3453 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3457 veto = psppire_sheet_click_cell (sheet, row, column);
3458 if (veto) PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3462 if (event->window == sheet->column_title_window)
3464 gtk_widget_get_pointer (widget, &x, &y);
3465 if ( sheet->row_titles_visible)
3466 x -= sheet->row_title_area.width;
3468 x += sheet->hadjustment->value;
3470 column = column_from_xpixel (sheet, x);
3472 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3474 veto = psppire_sheet_click_cell (sheet, -1, column);
3475 gtk_grab_add (GTK_WIDGET (sheet));
3476 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3480 if (event->window == sheet->row_title_window)
3482 gtk_widget_get_pointer (widget, &x, &y);
3483 if ( sheet->column_titles_visible)
3484 y -= sheet->column_title_area.height;
3486 y += sheet->vadjustment->value;
3488 row = row_from_ypixel (sheet, y);
3489 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3491 veto = psppire_sheet_click_cell (sheet, row, -1);
3492 gtk_grab_add (GTK_WIDGET (sheet));
3493 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3501 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3503 PsppireSheetCell cell;
3504 gboolean forbid_move;
3509 if (row >= psppire_axis_unit_count (sheet->vaxis)
3510 || column >= psppire_axis_unit_count (sheet->haxis))
3515 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3516 &sheet->active_cell,
3522 if (sheet->state == GTK_STATE_NORMAL)
3525 row = sheet->active_cell.row;
3526 column = sheet->active_cell.col;
3528 change_active_cell (sheet, row, column);
3532 if (row == -1 && column >= 0)
3534 psppire_sheet_select_column (sheet, column);
3538 if (column == -1 && row >= 0)
3540 psppire_sheet_select_row (sheet, row);
3544 if (row == -1 && column == -1)
3546 sheet->range.row0 = 0;
3547 sheet->range.col0 = 0;
3548 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3550 psppire_axis_unit_count (sheet->haxis) - 1;
3551 sheet->active_cell.row = 0;
3552 sheet->active_cell.col = 0;
3553 psppire_sheet_select_range (sheet, NULL);
3557 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3559 sheet->state = PSPPIRE_SHEET_NORMAL;
3560 psppire_sheet_real_unselect_range (sheet, NULL);
3564 change_active_cell (sheet, row, column);
3567 sheet->active_cell.row = row;
3568 sheet->active_cell.col = column;
3569 sheet->selection_cell.row = row;
3570 sheet->selection_cell.col = column;
3571 sheet->range.row0 = row;
3572 sheet->range.col0 = column;
3573 sheet->range.rowi = row;
3574 sheet->range.coli = column;
3575 sheet->state = PSPPIRE_SHEET_NORMAL;
3576 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3578 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3584 psppire_sheet_button_release (GtkWidget *widget,
3585 GdkEventButton *event)
3587 GdkDisplay *display = gtk_widget_get_display (widget);
3589 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3591 /* release on resize windows */
3592 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3595 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3596 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3598 gdk_display_pointer_ungrab (display, event->time);
3599 draw_xor_vline (sheet);
3602 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3603 + sheet->hadjustment->value;
3605 set_column_width (sheet, sheet->drag_cell.col, width);
3610 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3613 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3614 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3616 gdk_display_pointer_ungrab (display, event->time);
3617 draw_xor_hline (sheet);
3620 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3621 sheet->vadjustment->value;
3623 set_row_height (sheet, sheet->drag_cell.row, height);
3628 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3630 PsppireSheetRange old_range;
3631 draw_xor_rectangle (sheet, sheet->drag_range);
3632 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3633 gdk_display_pointer_ungrab (display, event->time);
3635 psppire_sheet_real_unselect_range (sheet, NULL);
3637 sheet->active_cell.row = sheet->active_cell.row +
3638 (sheet->drag_range.row0 - sheet->range.row0);
3639 sheet->active_cell.col = sheet->active_cell.col +
3640 (sheet->drag_range.col0 - sheet->range.col0);
3641 sheet->selection_cell.row = sheet->selection_cell.row +
3642 (sheet->drag_range.row0 - sheet->range.row0);
3643 sheet->selection_cell.col = sheet->selection_cell.col +
3644 (sheet->drag_range.col0 - sheet->range.col0);
3645 old_range = sheet->range;
3646 sheet->range = sheet->drag_range;
3647 sheet->drag_range = old_range;
3648 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3649 &sheet->drag_range, &sheet->range);
3650 psppire_sheet_select_range (sheet, &sheet->range);
3653 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3655 PsppireSheetRange old_range;
3656 draw_xor_rectangle (sheet, sheet->drag_range);
3657 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3658 gdk_display_pointer_ungrab (display, event->time);
3660 psppire_sheet_real_unselect_range (sheet, NULL);
3662 sheet->active_cell.row = sheet->active_cell.row +
3663 (sheet->drag_range.row0 - sheet->range.row0);
3664 sheet->active_cell.col = sheet->active_cell.col +
3665 (sheet->drag_range.col0 - sheet->range.col0);
3666 if (sheet->drag_range.row0 < sheet->range.row0)
3667 sheet->selection_cell.row = sheet->drag_range.row0;
3668 if (sheet->drag_range.rowi >= sheet->range.rowi)
3669 sheet->selection_cell.row = sheet->drag_range.rowi;
3670 if (sheet->drag_range.col0 < sheet->range.col0)
3671 sheet->selection_cell.col = sheet->drag_range.col0;
3672 if (sheet->drag_range.coli >= sheet->range.coli)
3673 sheet->selection_cell.col = sheet->drag_range.coli;
3674 old_range = sheet->range;
3675 sheet->range = sheet->drag_range;
3676 sheet->drag_range = old_range;
3678 if (sheet->state == GTK_STATE_NORMAL) sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3679 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3680 &sheet->drag_range, &sheet->range);
3681 psppire_sheet_select_range (sheet, &sheet->range);
3684 if (sheet->state == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet))
3686 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3687 gdk_display_pointer_ungrab (display, event->time);
3688 change_active_cell (sheet, sheet->active_cell.row,
3689 sheet->active_cell.col);
3692 if (PSPPIRE_SHEET_IN_SELECTION)
3693 gdk_display_pointer_ungrab (display, event->time);
3694 gtk_grab_remove (GTK_WIDGET (sheet));
3696 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3705 /* Shamelessly lifted from gtktooltips */
3707 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3711 gtk_widget_size_request (tip_window, &req);
3712 gtk_paint_flat_box (tip_window->style, tip_window->window,
3713 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3714 NULL, GTK_WIDGET(tip_window), "tooltip",
3715 0, 0, req.width, req.height);
3721 destroy_hover_window (PsppireSheetHoverTitle *h)
3723 gtk_widget_destroy (h->window);
3727 static PsppireSheetHoverTitle *
3728 create_hover_window (void)
3730 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3732 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3734 #if GTK_CHECK_VERSION (2, 9, 0)
3735 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3736 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3739 gtk_widget_set_app_paintable (hw->window, TRUE);
3740 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3741 gtk_widget_set_name (hw->window, "gtk-tooltips");
3742 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3744 g_signal_connect (hw->window,
3746 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3749 hw->label = gtk_label_new (NULL);
3752 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3753 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3755 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3757 gtk_widget_show (hw->label);
3759 g_signal_connect (hw->window,
3761 G_CALLBACK (gtk_widget_destroyed),
3767 #define HOVER_WINDOW_Y_OFFSET 2
3770 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3771 const gchar *subtitle)
3780 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3784 sheet->hover_window->row = row;
3785 sheet->hover_window->column = column;
3787 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3789 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3791 gtk_widget_show (sheet->hover_window->window);
3793 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3799 y += sheet->column_title_area.y;
3800 y += sheet->column_title_area.height;
3801 y += HOVER_WINDOW_Y_OFFSET;
3807 x += sheet->row_title_area.x;
3808 x += sheet->row_title_area.width * 2 / 3.0;
3811 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3816 motion_timeout_callback (gpointer data)
3818 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3822 gdk_threads_enter ();
3823 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3825 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3827 if (sheet->row_title_under && row >= 0)
3829 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3831 show_subtitle (sheet, row, -1, text);
3835 if (sheet->column_title_under && column >= 0)
3837 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3840 show_subtitle (sheet, -1, column, text);
3846 gdk_threads_leave ();
3851 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3853 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3854 GdkModifierType mods;
3855 GdkCursorType new_cursor;
3858 GdkDisplay *display;
3860 g_return_val_if_fail (event != NULL, FALSE);
3862 display = gtk_widget_get_display (widget);
3864 /* selections on the sheet */
3868 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3870 if ( sheet->motion_timer > 0 )
3871 g_source_remove (sheet->motion_timer);
3872 sheet->motion_timer =
3873 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3879 gtk_widget_get_pointer (widget, &wx, &wy);
3881 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3883 if ( row != sheet->hover_window->row ||
3884 column != sheet->hover_window->column)
3886 gtk_widget_hide (sheet->hover_window->window);
3891 if (event->window == sheet->column_title_window)
3893 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3894 on_column_boundary (sheet, x, &column))
3896 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3897 if (new_cursor != sheet->cursor_drag->type)
3899 gdk_cursor_unref (sheet->cursor_drag);
3900 sheet->cursor_drag =
3901 gdk_cursor_new_for_display (display, new_cursor);
3903 gdk_window_set_cursor (sheet->column_title_window,
3904 sheet->cursor_drag);
3909 new_cursor = GDK_TOP_LEFT_ARROW;
3910 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3911 new_cursor != sheet->cursor_drag->type)
3913 gdk_cursor_unref (sheet->cursor_drag);
3914 sheet->cursor_drag =
3915 gdk_cursor_new_for_display (display, new_cursor);
3916 gdk_window_set_cursor (sheet->column_title_window,
3917 sheet->cursor_drag);
3921 else if (event->window == sheet->row_title_window)
3923 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3924 on_row_boundary (sheet, y, &row))
3926 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3927 if (new_cursor != sheet->cursor_drag->type)
3929 gdk_cursor_unref (sheet->cursor_drag);
3930 sheet->cursor_drag =
3931 gdk_cursor_new_for_display (display, new_cursor);
3932 gdk_window_set_cursor (sheet->row_title_window,
3933 sheet->cursor_drag);
3938 new_cursor = GDK_TOP_LEFT_ARROW;
3939 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3940 new_cursor != sheet->cursor_drag->type)
3942 gdk_cursor_unref (sheet->cursor_drag);
3943 sheet->cursor_drag =
3944 gdk_cursor_new_for_display (display, new_cursor);
3945 gdk_window_set_cursor (sheet->row_title_window,
3946 sheet->cursor_drag);
3951 new_cursor = GDK_PLUS;
3952 if ( event->window == sheet->sheet_window &&
3953 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3954 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3955 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3956 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3957 new_cursor != sheet->cursor_drag->type)
3959 gdk_cursor_unref (sheet->cursor_drag);
3960 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3961 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3964 new_cursor = GDK_TOP_LEFT_ARROW;
3965 if ( event->window == sheet->sheet_window &&
3966 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3967 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3968 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3969 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3970 new_cursor != sheet->cursor_drag->type)
3972 gdk_cursor_unref (sheet->cursor_drag);
3973 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3974 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3977 new_cursor = GDK_SIZING;
3978 if ( event->window == sheet->sheet_window &&
3979 sheet->selection_mode != GTK_SELECTION_NONE &&
3980 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3981 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3982 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3983 new_cursor != sheet->cursor_drag->type)
3985 gdk_cursor_unref (sheet->cursor_drag);
3986 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_SIZING);
3987 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3991 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3992 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3994 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3996 if (event->x != sheet->x_drag)
3998 draw_xor_vline (sheet);
3999 sheet->x_drag = event->x;
4000 draw_xor_vline (sheet);
4006 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
4008 if (event->y != sheet->y_drag)
4010 draw_xor_hline (sheet);
4011 sheet->y_drag = event->y;
4012 draw_xor_hline (sheet);
4018 if (PSPPIRE_SHEET_IN_DRAG (sheet))
4020 PsppireSheetRange aux;
4021 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
4022 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
4023 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
4024 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4028 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4029 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4031 aux = sheet->drag_range;
4032 sheet->drag_range.row0 = sheet->range.row0 + row;
4033 sheet->drag_range.col0 = sheet->range.col0 + column;
4034 sheet->drag_range.rowi = sheet->range.rowi + row;
4035 sheet->drag_range.coli = sheet->range.coli + column;
4036 if (aux.row0 != sheet->drag_range.row0 ||
4037 aux.col0 != sheet->drag_range.col0)
4039 draw_xor_rectangle (sheet, aux);
4040 draw_xor_rectangle (sheet, sheet->drag_range);
4046 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
4048 PsppireSheetRange aux;
4049 gint v_h, current_col, current_row, col_threshold, row_threshold;
4051 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
4052 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
4054 current_col = column_from_xpixel (sheet, x);
4055 current_row = row_from_ypixel (sheet, y);
4056 column = current_col - sheet->drag_cell.col;
4057 row = current_row - sheet->drag_cell.row;
4059 /*use half of column width resp. row height as threshold to
4061 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
4062 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
4065 if (x < col_threshold)
4068 else if (column < 0)
4070 if (x > col_threshold)
4073 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
4074 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
4077 if (y < row_threshold)
4082 if (y > row_threshold)
4086 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
4087 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4097 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4098 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4100 aux = sheet->drag_range;
4101 sheet->drag_range = sheet->range;
4103 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4104 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4105 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4106 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4108 if (aux.row0 != sheet->drag_range.row0 ||
4109 aux.rowi != sheet->drag_range.rowi ||
4110 aux.col0 != sheet->drag_range.col0 ||
4111 aux.coli != sheet->drag_range.coli)
4113 draw_xor_rectangle (sheet, aux);
4114 draw_xor_rectangle (sheet, sheet->drag_range);
4120 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
4122 if (sheet->state == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
4123 column == sheet->active_cell.col) return TRUE;
4125 if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4126 psppire_sheet_extend_selection (sheet, row, column);
4132 psppire_sheet_crossing_notify (GtkWidget *widget,
4133 GdkEventCrossing *event)
4135 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4137 if (event->window == sheet->column_title_window)
4138 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4139 else if (event->window == sheet->row_title_window)
4140 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4142 if (event->type == GDK_LEAVE_NOTIFY)
4143 gtk_widget_hide (sheet->hover_window->window);
4150 psppire_sheet_focus_in (GtkWidget *w,
4151 GdkEventFocus *event)
4153 PsppireSheet *sheet = PSPPIRE_SHEET (w);
4155 gtk_widget_grab_focus (sheet->entry_widget);
4162 psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column)
4164 PsppireSheetRange range;
4168 if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
4171 if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
4173 gtk_widget_grab_focus (GTK_WIDGET (sheet));
4175 if (PSPPIRE_SHEET_IN_DRAG (sheet)) return;
4177 state = sheet->state;
4179 switch (sheet->state)
4181 case PSPPIRE_SHEET_ROW_SELECTED:
4182 column = psppire_axis_unit_count (sheet->haxis) - 1;
4184 case PSPPIRE_SHEET_COLUMN_SELECTED:
4185 row = psppire_axis_unit_count (sheet->vaxis) - 1;
4187 case PSPPIRE_SHEET_NORMAL:
4188 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4189 r = sheet->active_cell.row;
4190 c = sheet->active_cell.col;
4191 sheet->range.col0 = c;
4192 sheet->range.row0 = r;
4193 sheet->range.coli = c;
4194 sheet->range.rowi = r;
4195 psppire_sheet_range_draw_selection (sheet, sheet->range);
4196 case PSPPIRE_SHEET_RANGE_SELECTED:
4197 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4200 sheet->selection_cell.row = row;
4201 sheet->selection_cell.col = column;
4203 range.col0 = MIN (column, sheet->active_cell.col);
4204 range.coli = MAX (column, sheet->active_cell.col);
4205 range.row0 = MIN (row, sheet->active_cell.row);
4206 range.rowi = MAX (row, sheet->active_cell.row);
4208 if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
4209 range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
4210 state == PSPPIRE_SHEET_NORMAL)
4211 psppire_sheet_real_select_range (sheet, &range);
4216 psppire_sheet_entry_key_press (GtkWidget *widget,
4220 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
4225 /* Number of rows in a step-increment */
4226 #define ROWS_PER_STEP 1
4230 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
4232 gint old_row = sheet->active_cell.row ;
4233 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
4237 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
4238 min_visible_row (sheet));
4242 case GTK_SCROLL_PAGE_DOWN:
4243 gtk_adjustment_set_value (sheet->vadjustment,
4244 sheet->vadjustment->value +
4245 sheet->vadjustment->page_increment);
4247 case GTK_SCROLL_PAGE_UP:
4248 gtk_adjustment_set_value (sheet->vadjustment,
4249 sheet->vadjustment->value -
4250 sheet->vadjustment->page_increment);
4254 g_assert_not_reached ();
4259 vpixel += psppire_axis_start_pixel (sheet->vaxis,
4260 min_visible_row (sheet));
4262 new_row = row_from_ypixel (sheet, vpixel);
4264 change_active_cell (sheet, new_row,
4265 sheet->active_cell.col);
4270 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
4272 gint current_row = sheet->active_cell.row;
4273 gint current_col = sheet->active_cell.col;
4274 PsppireSheetCell new_cell ;
4275 gboolean forbidden = FALSE;
4277 new_cell.row = current_row;
4278 new_cell.col = current_col;
4282 case GTK_SCROLL_STEP_DOWN:
4285 case GTK_SCROLL_STEP_UP:
4288 case GTK_SCROLL_STEP_RIGHT:
4291 case GTK_SCROLL_STEP_LEFT:
4294 case GTK_SCROLL_STEP_FORWARD:
4297 psppire_sheet_model_get_column_count (sheet->model))
4303 case GTK_SCROLL_STEP_BACKWARD:
4305 if (new_cell.col < 0)
4308 psppire_sheet_model_get_column_count (sheet->model) - 1;
4313 g_assert_not_reached ();
4317 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
4318 &sheet->active_cell,
4326 maximize_int (&new_cell.row, 0);
4327 maximize_int (&new_cell.col, 0);
4329 minimize_int (&new_cell.row,
4330 psppire_axis_unit_count (sheet->vaxis) - 1);
4332 minimize_int (&new_cell.col,
4333 psppire_axis_unit_count (sheet->haxis) - 1);
4335 change_active_cell (sheet, new_cell.row, new_cell.col);
4338 if ( new_cell.col > max_fully_visible_column (sheet))
4341 psppire_axis_start_pixel (sheet->haxis,
4343 hpos -= sheet->hadjustment->page_size;
4345 gtk_adjustment_set_value (sheet->hadjustment,
4348 else if ( new_cell.col < min_fully_visible_column (sheet))
4351 psppire_axis_start_pixel (sheet->haxis,
4354 gtk_adjustment_set_value (sheet->hadjustment,
4359 if ( new_cell.row > max_fully_visible_row (sheet))
4362 psppire_axis_start_pixel (sheet->vaxis,
4364 vpos -= sheet->vadjustment->page_size;
4366 gtk_adjustment_set_value (sheet->vadjustment,
4369 else if ( new_cell.row < min_fully_visible_row (sheet))
4372 psppire_axis_start_pixel (sheet->vaxis,
4375 gtk_adjustment_set_value (sheet->vadjustment,
4379 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
4384 psppire_sheet_key_press (GtkWidget *widget,
4387 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4389 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
4391 switch (key->keyval)
4394 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
4397 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
4399 case GDK_ISO_Left_Tab:
4400 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
4403 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
4407 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
4410 step_sheet (sheet, GTK_SCROLL_STEP_UP);
4414 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
4417 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
4421 gtk_adjustment_set_value (sheet->vadjustment,
4422 sheet->vadjustment->lower);
4424 change_active_cell (sheet, 0,
4425 sheet->active_cell.col);
4430 gtk_adjustment_set_value (sheet->vadjustment,
4431 sheet->vadjustment->upper -
4432 sheet->vadjustment->page_size -
4433 sheet->vadjustment->page_increment);
4436 change_active_cellx (sheet,
4437 psppire_axis_unit_count (sheet->vaxis) - 1,
4438 sheet->active_cell.col);
4442 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
4453 psppire_sheet_size_request (GtkWidget *widget,
4454 GtkRequisition *requisition)
4456 PsppireSheet *sheet;
4458 g_return_if_fail (widget != NULL);
4459 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4460 g_return_if_fail (requisition != NULL);
4462 sheet = PSPPIRE_SHEET (widget);
4464 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4465 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4467 /* compute the size of the column title area */
4468 if (sheet->column_titles_visible)
4469 requisition->height += sheet->column_title_area.height;
4471 /* compute the size of the row title area */
4472 if (sheet->row_titles_visible)
4473 requisition->width += sheet->row_title_area.width;
4478 psppire_sheet_size_allocate (GtkWidget *widget,
4479 GtkAllocation *allocation)
4481 PsppireSheet *sheet;
4482 GtkAllocation sheet_allocation;
4485 g_return_if_fail (widget != NULL);
4486 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4487 g_return_if_fail (allocation != NULL);
4489 sheet = PSPPIRE_SHEET (widget);
4490 widget->allocation = *allocation;
4491 border_width = GTK_CONTAINER (widget)->border_width;
4493 if (GTK_WIDGET_REALIZED (widget))
4494 gdk_window_move_resize (widget->window,
4495 allocation->x + border_width,
4496 allocation->y + border_width,
4497 allocation->width - 2 * border_width,
4498 allocation->height - 2 * border_width);
4500 sheet_allocation.x = 0;
4501 sheet_allocation.y = 0;
4502 sheet_allocation.width = allocation->width - 2 * border_width;
4503 sheet_allocation.height = allocation->height - 2 * border_width;
4505 if (GTK_WIDGET_REALIZED (widget))
4506 gdk_window_move_resize (sheet->sheet_window,
4509 sheet_allocation.width,
4510 sheet_allocation.height);
4512 /* position the window which holds the column title buttons */
4513 sheet->column_title_area.x = 0;
4514 sheet->column_title_area.y = 0;
4515 sheet->column_title_area.width = sheet_allocation.width ;
4518 /* position the window which holds the row title buttons */
4519 sheet->row_title_area.x = 0;
4520 sheet->row_title_area.y = 0;
4521 sheet->row_title_area.height = sheet_allocation.height;
4523 if (sheet->row_titles_visible)
4524 sheet->column_title_area.x += sheet->row_title_area.width;
4526 if (sheet->column_titles_visible)
4527 sheet->row_title_area.y += sheet->column_title_area.height;
4529 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4530 gdk_window_move_resize (sheet->column_title_window,
4531 sheet->column_title_area.x,
4532 sheet->column_title_area.y,
4533 sheet->column_title_area.width,
4534 sheet->column_title_area.height);
4537 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4538 gdk_window_move_resize (sheet->row_title_window,
4539 sheet->row_title_area.x,
4540 sheet->row_title_area.y,
4541 sheet->row_title_area.width,
4542 sheet->row_title_area.height);
4544 size_allocate_global_button (sheet);
4548 gint width = sheet->column_title_area.width;
4550 if ( sheet->row_titles_visible)
4551 width -= sheet->row_title_area.width;
4553 g_object_set (sheet->haxis,
4554 "minimum-extent", width,
4561 gint height = sheet->row_title_area.height;
4563 if ( sheet->column_titles_visible)
4564 height -= sheet->column_title_area.height;
4566 g_object_set (sheet->vaxis,
4567 "minimum-extent", height,
4572 /* set the scrollbars adjustments */
4573 adjust_scrollbars (sheet);
4577 draw_column_title_buttons (PsppireSheet *sheet)
4581 if (!sheet->column_titles_visible) return;
4582 if (!GTK_WIDGET_REALIZED (sheet))
4585 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4588 if (sheet->row_titles_visible)
4590 x = sheet->row_title_area.width;
4593 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4595 sheet->column_title_area.width = width;
4596 sheet->column_title_area.x = x;
4597 gdk_window_move_resize (sheet->column_title_window,
4598 sheet->column_title_area.x,
4599 sheet->column_title_area.y,
4600 sheet->column_title_area.width,
4601 sheet->column_title_area.height);
4604 if (max_visible_column (sheet) ==
4605 psppire_axis_unit_count (sheet->haxis) - 1)
4606 gdk_window_clear_area (sheet->column_title_window,
4608 sheet->column_title_area.width,
4609 sheet->column_title_area.height);
4611 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4613 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4614 max_visible_column (sheet));
4618 draw_row_title_buttons (PsppireSheet *sheet)
4623 if (!sheet->row_titles_visible) return;
4624 if (!GTK_WIDGET_REALIZED (sheet))
4627 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4629 if (sheet->column_titles_visible)
4631 y = sheet->column_title_area.height;
4634 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4636 sheet->row_title_area.y = y;
4637 sheet->row_title_area.height = height;
4638 gdk_window_move_resize (sheet->row_title_window,
4639 sheet->row_title_area.x,
4640 sheet->row_title_area.y,
4641 sheet->row_title_area.width,
4642 sheet->row_title_area.height);
4645 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4646 gdk_window_clear_area (sheet->row_title_window,
4648 sheet->row_title_area.width,
4649 sheet->row_title_area.height);
4651 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4653 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4654 max_visible_row (sheet));
4659 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4661 GtkAllocation entry_alloc;
4662 PsppireSheetCellAttr attributes = { 0 };
4663 GtkEntry *sheet_entry;
4665 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4666 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4668 sheet_entry = psppire_sheet_get_entry (sheet);
4670 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4671 sheet->active_cell.col,
4675 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4677 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4679 style->bg[GTK_STATE_NORMAL] = attributes.background;
4680 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4681 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4682 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4683 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4684 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4687 rectangle_from_cell (sheet, sheet->active_cell.row,
4688 sheet->active_cell.col, &entry_alloc);
4690 entry_alloc.x += sheet->cell_padding->left;
4691 entry_alloc.y += sheet->cell_padding->right;
4692 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4693 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4696 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4697 entry_alloc.height);
4698 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4702 /* Copy the sheet's font to the entry widget */
4704 set_entry_widget_font (PsppireSheet *sheet)
4706 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4708 pango_font_description_free (style->font_desc);
4709 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4711 gtk_widget_modify_style (sheet->entry_widget, style);
4715 create_sheet_entry (PsppireSheet *sheet)
4717 if (sheet->entry_widget)
4719 gtk_widget_unparent (sheet->entry_widget);
4722 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4723 g_object_ref_sink (sheet->entry_widget);
4725 gtk_widget_size_request (sheet->entry_widget, NULL);
4727 if ( GTK_IS_ENTRY (sheet->entry_widget))
4729 g_object_set (sheet->entry_widget,
4734 if (GTK_WIDGET_REALIZED (sheet))
4736 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4737 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4738 gtk_widget_realize (sheet->entry_widget);
4741 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4742 G_CALLBACK (psppire_sheet_entry_key_press),
4745 set_entry_widget_font (sheet);
4747 gtk_widget_show (sheet->entry_widget);
4751 /* Finds the last child widget that happens to be of type GtkEntry */
4753 find_entry (GtkWidget *w, gpointer user_data)
4755 GtkWidget **entry = user_data;
4756 if ( GTK_IS_ENTRY (w))
4764 psppire_sheet_get_entry (PsppireSheet *sheet)
4766 GtkWidget *w = sheet->entry_widget;
4768 g_return_val_if_fail (sheet != NULL, NULL);
4769 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4770 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4772 while (! GTK_IS_ENTRY (w))
4774 GtkWidget *entry = NULL;
4776 if (GTK_IS_CONTAINER (w))
4778 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4787 return GTK_ENTRY (w);
4792 draw_button (PsppireSheet *sheet, GdkWindow *window,
4793 PsppireSheetButton *button, gboolean is_sensitive,
4794 GdkRectangle allocation)
4796 GtkShadowType shadow_type;
4797 gint text_width = 0, text_height = 0;
4798 PangoAlignment align = PANGO_ALIGN_LEFT;
4804 g_return_if_fail (sheet != NULL);
4805 g_return_if_fail (button != NULL);
4808 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4810 gdk_window_clear_area (window,
4811 allocation.x, allocation.y,
4812 allocation.width, allocation.height);
4814 gtk_widget_ensure_style (sheet->button);
4816 gtk_paint_box (sheet->button->style, window,
4817 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4819 GTK_WIDGET (sheet->button),
4821 allocation.x, allocation.y,
4822 allocation.width, allocation.height);
4824 state = button->state;
4825 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4827 if (state == GTK_STATE_ACTIVE)
4828 shadow_type = GTK_SHADOW_IN;
4830 shadow_type = GTK_SHADOW_OUT;
4832 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4833 gtk_paint_box (sheet->button->style, window,
4834 button->state, shadow_type,
4835 &allocation, GTK_WIDGET (sheet->button),
4837 allocation.x, allocation.y,
4838 allocation.width, allocation.height);
4840 if ( button->overstruck)
4842 GdkPoint points[2] = {
4843 {allocation.x, allocation.y},
4844 {allocation.x + allocation.width,
4845 allocation.y + allocation.height}
4848 gtk_paint_polygon (sheet->button->style,
4860 if (button->label_visible)
4862 text_height = DEFAULT_ROW_HEIGHT -
4863 2 * COLUMN_TITLES_HEIGHT;
4865 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4867 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4870 allocation.y += 2 * sheet->button->style->ythickness;
4872 if (button->label && strlen (button->label) > 0)
4874 PangoRectangle rect;
4875 gchar *line = button->label;
4877 PangoLayout *layout = NULL;
4878 gint real_x = allocation.x;
4879 gint real_y = allocation.y;
4881 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4882 pango_layout_get_extents (layout, NULL, &rect);
4884 text_width = PANGO_PIXELS (rect.width);
4885 switch (button->justification)
4887 case GTK_JUSTIFY_LEFT:
4888 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4889 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4891 case GTK_JUSTIFY_RIGHT:
4892 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4893 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4895 case GTK_JUSTIFY_CENTER:
4897 real_x = allocation.x + (allocation.width - text_width)/2;
4898 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4899 pango_layout_set_justify (layout, TRUE);
4901 pango_layout_set_alignment (layout, align);
4902 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4911 g_object_unref (layout);
4914 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4916 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4920 psppire_sheet_button_free (button);
4924 /* Draw the column title buttons FIRST through to LAST */
4926 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4930 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4932 if (!sheet->column_titles_visible) return;
4934 g_return_if_fail (first >= min_visible_column (sheet));
4935 g_return_if_fail (last <= max_visible_column (sheet));
4938 rect.height = sheet->column_title_area.height;
4939 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4940 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4941 + psppire_axis_unit_size (sheet->haxis, last);
4943 rect.x -= sheet->hadjustment->value;
4945 minimize_int (&rect.width, sheet->column_title_area.width);
4946 maximize_int (&rect.x, 0);
4948 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4950 for (col = first ; col <= last ; ++col)
4952 GdkRectangle allocation;
4953 gboolean is_sensitive = FALSE;
4955 PsppireSheetButton *
4956 button = psppire_sheet_model_get_column_button (sheet->model, col);
4958 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4960 allocation.x -= sheet->hadjustment->value;
4962 allocation.height = sheet->column_title_area.height;
4963 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4964 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4966 draw_button (sheet, sheet->column_title_window,
4967 button, is_sensitive, allocation);
4970 gdk_window_end_paint (sheet->column_title_window);
4975 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4979 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4981 if (!sheet->row_titles_visible) return;
4983 g_return_if_fail (first >= min_visible_row (sheet));
4984 g_return_if_fail (last <= max_visible_row (sheet));
4987 rect.width = sheet->row_title_area.width;
4988 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4989 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4990 + psppire_axis_unit_size (sheet->vaxis, last);
4992 rect.y -= sheet->vadjustment->value;
4994 minimize_int (&rect.height, sheet->row_title_area.height);
4995 maximize_int (&rect.y, 0);
4997 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4998 for (row = first; row <= last; ++row)
5000 GdkRectangle allocation;
5002 gboolean is_sensitive = FALSE;
5004 PsppireSheetButton *button =
5005 psppire_sheet_model_get_row_button (sheet->model, row);
5007 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
5009 allocation.y -= sheet->vadjustment->value;
5011 allocation.width = sheet->row_title_area.width;
5012 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
5013 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
5015 draw_button (sheet, sheet->row_title_window,
5016 button, is_sensitive, allocation);
5019 gdk_window_end_paint (sheet->row_title_window);
5026 * vadjustment_value_changed
5027 * hadjustment_value_changed */
5031 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
5034 (adj->value + adj->page_size)
5036 (adj->upper - adj->lower);
5038 const glong last_item = psppire_axis_unit_count (axis) - 1;
5040 if (isnan (position) || position < 0)
5044 psppire_axis_start_pixel (axis, last_item)
5046 psppire_axis_unit_size (axis, last_item)
5050 adj->page_size = page_size;
5053 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
5055 if ( adj->value < adj->lower)
5056 adj->value = adj->lower;
5059 gtk_adjustment_changed (adj);
5064 adjust_scrollbars (PsppireSheet *sheet)
5068 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5071 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
5073 if ( sheet->row_titles_visible)
5074 width -= sheet->row_title_area.width;
5076 if (sheet->column_titles_visible)
5077 height -= sheet->column_title_area.height;
5079 if (sheet->vadjustment)
5081 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
5083 sheet->vadjustment->step_increment =
5085 psppire_axis_unit_size (sheet->vaxis, last_row);
5087 sheet->vadjustment->page_increment =
5089 sheet->column_title_area.height -
5090 psppire_axis_unit_size (sheet->vaxis, last_row);
5092 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
5095 if (sheet->hadjustment)
5097 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
5098 sheet->hadjustment->step_increment = 1;
5100 sheet->hadjustment->page_increment = width;
5102 sheet->hadjustment->upper =
5103 psppire_axis_start_pixel (sheet->haxis, last_col)
5105 psppire_axis_unit_size (sheet->haxis, last_col)
5108 update_adjustment (sheet->hadjustment, sheet->haxis, width);
5112 /* Subtracts the region of WIDGET from REGION */
5114 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
5117 GdkRectangle intersect;
5120 gdk_region_get_clipbox (region, &rect);
5121 gtk_widget_intersect (widget,
5125 region2 = gdk_region_rectangle (&intersect);
5126 gdk_region_subtract (region, region2);
5127 gdk_region_destroy (region2);
5131 vadjustment_value_changed (GtkAdjustment *adjustment,
5135 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5137 g_return_if_fail (adjustment != NULL);
5139 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5141 gtk_widget_hide (sheet->entry_widget);
5144 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5146 subtract_widget_region (region, sheet->button);
5147 gdk_window_begin_paint_region (sheet->sheet_window, region);
5149 draw_sheet_region (sheet, region);
5151 draw_row_title_buttons (sheet);
5152 psppire_sheet_draw_active_cell (sheet);
5154 gdk_window_end_paint (sheet->sheet_window);
5155 gdk_region_destroy (region);
5160 hadjustment_value_changed (GtkAdjustment *adjustment,
5164 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5166 g_return_if_fail (adjustment != NULL);
5168 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5170 gtk_widget_hide (sheet->entry_widget);
5174 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5176 subtract_widget_region (region, sheet->button);
5177 gdk_window_begin_paint_region (sheet->sheet_window, region);
5179 draw_sheet_region (sheet, region);
5181 draw_column_title_buttons (sheet);
5183 psppire_sheet_draw_active_cell (sheet);
5185 gdk_window_end_paint (sheet->sheet_window);
5187 gdk_region_destroy (region);
5191 /* COLUMN RESIZING */
5193 draw_xor_vline (PsppireSheet *sheet)
5196 gint xpos = sheet->x_drag;
5197 gdk_drawable_get_size (sheet->sheet_window,
5200 if (sheet->row_titles_visible)
5201 xpos += sheet->row_title_area.width;
5203 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5205 sheet->column_title_area.height,
5207 height + CELL_SPACING);
5212 draw_xor_hline (PsppireSheet *sheet)
5216 gint ypos = sheet->y_drag;
5218 gdk_drawable_get_size (sheet->sheet_window,
5222 if (sheet->column_titles_visible)
5223 ypos += sheet->column_title_area.height;
5225 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5226 sheet->row_title_area.width,
5228 width + CELL_SPACING,
5232 /* SELECTED RANGE */
5234 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
5237 GdkRectangle clip_area, area;
5240 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
5241 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
5242 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
5243 psppire_axis_unit_size (sheet->haxis, range.coli);
5244 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
5245 psppire_axis_unit_size (sheet->vaxis, range.rowi);
5247 clip_area.x = sheet->row_title_area.width;
5248 clip_area.y = sheet->column_title_area.height;
5250 gdk_drawable_get_size (sheet->sheet_window,
5251 &clip_area.width, &clip_area.height);
5253 if (!sheet->row_titles_visible) clip_area.x = 0;
5254 if (!sheet->column_titles_visible) clip_area.y = 0;
5258 area.width = area.width + area.x;
5261 if (area.width > clip_area.width) area.width = clip_area.width + 10;
5264 area.height = area.height + area.y;
5267 if (area.height > clip_area.height) area.height = clip_area.height + 10;
5271 clip_area.width += 3;
5272 clip_area.height += 3;
5274 gdk_gc_get_values (sheet->xor_gc, &values);
5276 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5278 gdk_draw_rectangle (sheet->sheet_window,
5281 area.x + i, area.y + i,
5282 area.width - 2 * i, area.height - 2 * i);
5285 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5287 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5292 set_column_width (PsppireSheet *sheet,
5296 g_return_if_fail (sheet != NULL);
5297 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5299 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
5305 psppire_axis_resize (sheet->haxis, column,
5306 width - sheet->cell_padding->left -
5307 sheet->cell_padding->right);
5309 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5311 draw_column_title_buttons (sheet);
5312 adjust_scrollbars (sheet);
5313 psppire_sheet_size_allocate_entry (sheet);
5314 redraw_range (sheet, NULL);
5319 set_row_height (PsppireSheet *sheet,
5323 g_return_if_fail (sheet != NULL);
5324 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5326 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
5332 psppire_axis_resize (sheet->vaxis, row,
5333 height - sheet->cell_padding->top -
5334 sheet->cell_padding->bottom);
5336 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
5338 draw_row_title_buttons (sheet);
5339 adjust_scrollbars (sheet);
5340 psppire_sheet_size_allocate_entry (sheet);
5341 redraw_range (sheet, NULL);
5346 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
5347 PsppireSheetCellAttr *attr)
5350 const GtkJustification *j ;
5351 GdkColormap *colormap;
5353 g_return_val_if_fail (sheet != NULL, FALSE);
5354 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
5356 if (row < 0 || col < 0) return FALSE;
5358 attr->foreground = GTK_WIDGET (sheet)->style->black;
5359 attr->background = sheet->color[BG_COLOR];
5361 attr->border.width = 0;
5362 attr->border.line_style = GDK_LINE_SOLID;
5363 attr->border.cap_style = GDK_CAP_NOT_LAST;
5364 attr->border.join_style = GDK_JOIN_MITER;
5365 attr->border.mask = 0;
5366 attr->border.color = GTK_WIDGET (sheet)->style->black;
5368 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
5369 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
5372 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
5373 attr->foreground = *fg;
5376 bg = psppire_sheet_model_get_background (sheet->model, row, col);
5379 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
5380 attr->background = *bg;
5383 attr->justification =
5384 psppire_sheet_model_get_column_justification (sheet->model, col);
5386 j = psppire_sheet_model_get_justification (sheet->model, row, col);
5388 attr->justification = *j;
5394 psppire_sheet_button_size_request (PsppireSheet *sheet,
5395 const PsppireSheetButton *button,
5396 GtkRequisition *button_requisition)
5398 GtkRequisition requisition;
5399 GtkRequisition label_requisition;
5401 label_requisition.height = DEFAULT_ROW_HEIGHT;
5402 label_requisition.width = COLUMN_MIN_WIDTH;
5404 requisition.height = DEFAULT_ROW_HEIGHT;
5405 requisition.width = COLUMN_MIN_WIDTH;
5408 *button_requisition = requisition;
5409 button_requisition->width = MAX (requisition.width, label_requisition.width);
5410 button_requisition->height = MAX (requisition.height, label_requisition.height);
5415 psppire_sheet_forall (GtkContainer *container,
5416 gboolean include_internals,
5417 GtkCallback callback,
5418 gpointer callback_data)
5420 PsppireSheet *sheet = PSPPIRE_SHEET (container);
5422 g_return_if_fail (callback != NULL);
5424 if (sheet->button && sheet->button->parent)
5425 (* callback) (sheet->button, callback_data);
5427 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
5428 (* callback) (sheet->entry_widget, callback_data);
5433 psppire_sheet_get_model (const PsppireSheet *sheet)
5435 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
5437 return sheet->model;
5441 PsppireSheetButton *
5442 psppire_sheet_button_new (void)
5444 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
5446 button->state = GTK_STATE_NORMAL;
5447 button->label = NULL;
5448 button->label_visible = TRUE;
5449 button->justification = GTK_JUSTIFY_FILL;
5450 button->overstruck = FALSE;
5457 psppire_sheet_button_free (PsppireSheetButton *button)
5459 if (!button) return ;
5461 g_free (button->label);
5466 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5468 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5470 if ( NULL == celltext)
5473 g_string_append (string, celltext);
5479 range_to_text (const PsppireSheet *sheet)
5484 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5487 string = g_string_sized_new (80);
5489 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5491 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5493 append_cell_text (string, sheet, r, c);
5494 g_string_append (string, "\t");
5496 append_cell_text (string, sheet, r, c);
5497 if ( r < sheet->range.rowi)
5498 g_string_append (string, "\n");
5505 range_to_html (const PsppireSheet *sheet)
5510 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5513 string = g_string_sized_new (480);
5515 g_string_append (string, "<html>\n");
5516 g_string_append (string, "<body>\n");
5517 g_string_append (string, "<table>\n");
5518 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5520 g_string_append (string, "<tr>\n");
5521 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5523 g_string_append (string, "<td>");
5524 append_cell_text (string, sheet, r, c);
5525 g_string_append (string, "</td>\n");
5527 g_string_append (string, "</tr>\n");
5529 g_string_append (string, "</table>\n");
5530 g_string_append (string, "</body>\n");
5531 g_string_append (string, "</html>\n");
5543 primary_get_cb (GtkClipboard *clipboard,
5544 GtkSelectionData *selection_data,
5548 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5549 GString *string = NULL;
5553 case SELECT_FMT_TEXT:
5554 string = range_to_text (sheet);
5556 case SELECT_FMT_HTML:
5557 string = range_to_html (sheet);
5560 g_assert_not_reached ();
5563 gtk_selection_data_set (selection_data, selection_data->target,
5565 (const guchar *) string->str, string->len);
5566 g_string_free (string, TRUE);
5570 primary_clear_cb (GtkClipboard *clipboard,
5573 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5574 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5577 psppire_sheet_real_unselect_range (sheet, NULL);
5581 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5583 static const GtkTargetEntry targets[] = {
5584 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5585 { "STRING", 0, SELECT_FMT_TEXT },
5586 { "TEXT", 0, SELECT_FMT_TEXT },
5587 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5588 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5589 { "text/plain", 0, SELECT_FMT_TEXT },
5590 { "text/html", 0, SELECT_FMT_HTML }
5593 GtkClipboard *clipboard;
5595 if (!GTK_WIDGET_REALIZED (sheet))
5598 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5599 GDK_SELECTION_PRIMARY);
5601 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5603 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5604 G_N_ELEMENTS (targets),
5605 primary_get_cb, primary_clear_cb,
5607 primary_clear_cb (clipboard, sheet);
5611 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5612 gtk_clipboard_clear (clipboard);