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, 2, 2 };
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);
1859 #define BORDER_WIDTH 3
1862 psppire_sheet_realize (GtkWidget *widget)
1864 PsppireSheet *sheet;
1865 GdkWindowAttr attributes;
1866 const gint attributes_mask =
1867 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1870 GdkColormap *colormap;
1871 GdkDisplay *display;
1873 g_return_if_fail (widget != NULL);
1874 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1876 sheet = PSPPIRE_SHEET (widget);
1878 colormap = gtk_widget_get_colormap (widget);
1879 display = gtk_widget_get_display (widget);
1881 attributes.window_type = GDK_WINDOW_CHILD;
1882 attributes.x = widget->allocation.x;
1883 attributes.y = widget->allocation.y;
1884 attributes.width = widget->allocation.width;
1885 attributes.height = widget->allocation.height;
1886 attributes.wclass = GDK_INPUT_OUTPUT;
1888 attributes.visual = gtk_widget_get_visual (widget);
1889 attributes.colormap = colormap;
1891 attributes.event_mask = gtk_widget_get_events (widget);
1892 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1893 GDK_BUTTON_PRESS_MASK |
1894 GDK_BUTTON_RELEASE_MASK |
1895 GDK_KEY_PRESS_MASK |
1896 GDK_ENTER_NOTIFY_MASK |
1897 GDK_LEAVE_NOTIFY_MASK |
1898 GDK_POINTER_MOTION_MASK |
1899 GDK_POINTER_MOTION_HINT_MASK);
1901 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1904 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1906 gdk_window_set_user_data (widget->window, sheet);
1908 widget->style = gtk_style_attach (widget->style, widget->window);
1910 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1912 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1913 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1915 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1916 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1921 attributes.width = sheet->column_title_area.width;
1922 attributes.height = sheet->column_title_area.height;
1925 /* column - title window */
1926 sheet->column_title_window =
1927 gdk_window_new (widget->window, &attributes, attributes_mask);
1928 gdk_window_set_user_data (sheet->column_title_window, sheet);
1929 gtk_style_set_background (widget->style, sheet->column_title_window,
1935 attributes.width = sheet->row_title_area.width;
1936 attributes.height = sheet->row_title_area.height;
1938 /* row - title window */
1939 sheet->row_title_window = gdk_window_new (widget->window,
1940 &attributes, attributes_mask);
1941 gdk_window_set_user_data (sheet->row_title_window, sheet);
1942 gtk_style_set_background (widget->style, sheet->row_title_window,
1945 /* sheet - window */
1946 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1951 sheet->sheet_window = gdk_window_new (widget->window,
1952 &attributes, attributes_mask);
1953 gdk_window_set_user_data (sheet->sheet_window, sheet);
1955 gdk_cursor_unref (attributes.cursor);
1957 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1958 gdk_window_show (sheet->sheet_window);
1961 sheet->fg_gc = gdk_gc_new (widget->window);
1962 sheet->bg_gc = gdk_gc_new (widget->window);
1964 values.foreground = widget->style->white;
1965 values.function = GDK_INVERT;
1966 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1967 values.line_width = BORDER_WIDTH;
1969 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1978 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1979 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1981 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1982 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1984 sheet->button->style = gtk_style_attach (sheet->button->style,
1985 sheet->sheet_window);
1988 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1990 if (sheet->column_titles_visible)
1991 gdk_window_show (sheet->column_title_window);
1992 if (sheet->row_titles_visible)
1993 gdk_window_show (sheet->row_title_window);
1995 sheet->hover_window = create_hover_window ();
1997 draw_row_title_buttons (sheet);
1998 draw_column_title_buttons (sheet);
2000 psppire_sheet_update_primary_selection (sheet);
2003 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
2007 create_global_button (PsppireSheet *sheet)
2009 sheet->button = gtk_button_new_with_label (" ");
2011 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
2013 g_object_ref_sink (sheet->button);
2015 g_signal_connect (sheet->button,
2017 G_CALLBACK (global_button_clicked),
2022 size_allocate_global_button (PsppireSheet *sheet)
2024 GtkAllocation allocation;
2026 if (!sheet->column_titles_visible) return;
2027 if (!sheet->row_titles_visible) return;
2029 gtk_widget_size_request (sheet->button, NULL);
2033 allocation.width = sheet->row_title_area.width;
2034 allocation.height = sheet->column_title_area.height;
2036 gtk_widget_size_allocate (sheet->button, &allocation);
2040 global_button_clicked (GtkWidget *widget, gpointer data)
2042 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
2047 psppire_sheet_unrealize (GtkWidget *widget)
2049 PsppireSheet *sheet;
2051 g_return_if_fail (widget != NULL);
2052 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2054 sheet = PSPPIRE_SHEET (widget);
2056 gdk_cursor_unref (sheet->cursor_drag);
2057 sheet->cursor_drag = NULL;
2059 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
2060 sheet->color, n_COLORS);
2062 g_object_unref (sheet->xor_gc);
2063 g_object_unref (sheet->fg_gc);
2064 g_object_unref (sheet->bg_gc);
2066 destroy_hover_window (sheet->hover_window);
2068 gdk_window_destroy (sheet->sheet_window);
2069 gdk_window_destroy (sheet->column_title_window);
2070 gdk_window_destroy (sheet->row_title_window);
2072 gtk_widget_unparent (sheet->entry_widget);
2073 if (sheet->button != NULL)
2074 gtk_widget_unparent (sheet->button);
2076 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2077 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2081 psppire_sheet_map (GtkWidget *widget)
2083 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2085 g_return_if_fail (widget != NULL);
2086 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2088 if (!GTK_WIDGET_MAPPED (widget))
2090 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2092 gdk_window_show (widget->window);
2093 gdk_window_show (sheet->sheet_window);
2095 if (sheet->column_titles_visible)
2097 draw_column_title_buttons (sheet);
2098 gdk_window_show (sheet->column_title_window);
2100 if (sheet->row_titles_visible)
2102 draw_row_title_buttons (sheet);
2103 gdk_window_show (sheet->row_title_window);
2106 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2107 && sheet->active_cell.row >= 0
2108 && sheet->active_cell.col >= 0 )
2110 gtk_widget_show (sheet->entry_widget);
2111 gtk_widget_map (sheet->entry_widget);
2114 if (!GTK_WIDGET_MAPPED (sheet->button))
2116 gtk_widget_show (sheet->button);
2117 gtk_widget_map (sheet->button);
2120 redraw_range (sheet, NULL);
2121 change_active_cell (sheet,
2122 sheet->active_cell.row,
2123 sheet->active_cell.col);
2128 psppire_sheet_unmap (GtkWidget *widget)
2130 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2132 if (!GTK_WIDGET_MAPPED (widget))
2135 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2137 gdk_window_hide (sheet->sheet_window);
2138 if (sheet->column_titles_visible)
2139 gdk_window_hide (sheet->column_title_window);
2140 if (sheet->row_titles_visible)
2141 gdk_window_hide (sheet->row_title_window);
2142 gdk_window_hide (widget->window);
2144 if (GTK_WIDGET_MAPPED (sheet->entry_widget))
2145 gtk_widget_unmap (sheet->entry_widget);
2147 if (GTK_WIDGET_MAPPED (sheet->button))
2148 gtk_widget_unmap (sheet->button);
2151 /* get cell attributes of the given cell */
2152 /* TRUE means that the cell is currently allocated */
2153 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2155 PsppireSheetCellAttr *attributes);
2160 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2162 PangoLayout *layout;
2163 PangoRectangle text;
2164 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2169 PsppireSheetCellAttr attributes;
2172 g_return_if_fail (sheet != NULL);
2174 /* bail now if we aren't yet drawable */
2175 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2178 row >= psppire_axis_unit_count (sheet->vaxis))
2182 col >= psppire_axis_unit_count (sheet->haxis))
2185 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2187 /* select GC for background rectangle */
2188 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2189 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2191 rectangle_from_cell (sheet, row, col, &area);
2193 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2195 if (sheet->show_grid)
2197 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2199 gdk_draw_rectangle (sheet->sheet_window,
2203 area.width, area.height);
2207 label = psppire_sheet_cell_get_text (sheet, row, col);
2212 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2213 dispose_string (sheet, label);
2216 pango_layout_set_font_description (layout, font_desc);
2218 pango_layout_get_pixel_extents (layout, NULL, &text);
2220 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2222 font_height = pango_font_description_get_size (font_desc);
2223 if ( !pango_font_description_get_size_is_absolute (font_desc))
2224 font_height /= PANGO_SCALE;
2227 if ( sheet->cell_padding )
2229 area.x += sheet->cell_padding->left;
2230 area.width -= sheet->cell_padding->right
2231 + sheet->cell_padding->left;
2233 area.y += sheet->cell_padding->top;
2234 area.height -= sheet->cell_padding->bottom
2236 sheet->cell_padding->top;
2239 /* Centre the text vertically */
2240 area.y += (area.height - font_height) / 2.0;
2242 switch (attributes.justification)
2244 case GTK_JUSTIFY_RIGHT:
2245 area.x += area.width - text.width;
2247 case GTK_JUSTIFY_CENTER:
2248 area.x += (area.width - text.width) / 2.0;
2250 case GTK_JUSTIFY_LEFT:
2254 g_critical ("Unhandled justification %d in column %d\n",
2255 attributes.justification, col);
2259 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2264 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2265 g_object_unref (layout);
2270 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2272 PsppireSheetRange range;
2277 PsppireSheetRange drawing_range;
2279 gdk_region_get_clipbox (region, &area);
2281 y = area.y + sheet->vadjustment->value;
2282 x = area.x + sheet->hadjustment->value;
2284 if ( sheet->column_titles_visible)
2285 y -= sheet->column_title_area.height;
2287 if ( sheet->row_titles_visible)
2288 x -= sheet->row_title_area.width;
2290 maximize_int (&x, 0);
2291 maximize_int (&y, 0);
2293 range.row0 = row_from_ypixel (sheet, y);
2294 range.rowi = row_from_ypixel (sheet, y + area.height);
2296 range.col0 = column_from_xpixel (sheet, x);
2297 range.coli = column_from_xpixel (sheet, x + area.width);
2299 g_return_if_fail (sheet != NULL);
2300 g_return_if_fail (PSPPIRE_SHEET (sheet));
2302 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2303 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2304 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2307 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2308 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2309 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2310 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2312 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2313 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2315 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2317 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2318 psppire_sheet_cell_draw (sheet, i, j);
2321 if (sheet->state != PSPPIRE_SHEET_NORMAL &&
2322 psppire_sheet_range_isvisible (sheet, &sheet->range))
2323 psppire_sheet_range_draw_selection (sheet, drawing_range);
2326 if (sheet->state == GTK_STATE_NORMAL &&
2327 sheet->active_cell.row >= drawing_range.row0 &&
2328 sheet->active_cell.row <= drawing_range.rowi &&
2329 sheet->active_cell.col >= drawing_range.col0 &&
2330 sheet->active_cell.col <= drawing_range.coli)
2331 psppire_sheet_show_entry_widget (sheet);
2336 psppire_sheet_range_draw_selection (PsppireSheet *sheet, PsppireSheetRange range)
2340 PsppireSheetRange aux;
2342 if (range.col0 > sheet->range.coli || range.coli < sheet->range.col0 ||
2343 range.row0 > sheet->range.rowi || range.rowi < sheet->range.row0)
2346 if (!psppire_sheet_range_isvisible (sheet, &range)) return;
2347 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2351 range.col0 = MAX (sheet->range.col0, range.col0);
2352 range.coli = MIN (sheet->range.coli, range.coli);
2353 range.row0 = MAX (sheet->range.row0, range.row0);
2354 range.rowi = MIN (sheet->range.rowi, range.rowi);
2356 range.col0 = MAX (range.col0, min_visible_column (sheet));
2357 range.coli = MIN (range.coli, max_visible_column (sheet));
2358 range.row0 = MAX (range.row0, min_visible_row (sheet));
2359 range.rowi = MIN (range.rowi, max_visible_row (sheet));
2361 for (i = range.row0; i <= range.rowi; i++)
2363 for (j = range.col0; j <= range.coli; j++)
2365 if (psppire_sheet_cell_get_state (sheet, i, j) == GTK_STATE_SELECTED)
2367 rectangle_from_cell (sheet, i, j, &area);
2369 if (i == sheet->range.row0)
2371 area.y = area.y + 2;
2372 area.height = area.height - 2;
2374 if (i == sheet->range.rowi) area.height = area.height - 3;
2375 if (j == sheet->range.col0)
2377 area.x = area.x + 2;
2378 area.width = area.width - 2;
2380 if (j == sheet->range.coli) area.width = area.width - 3;
2382 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2384 gdk_draw_rectangle (sheet->sheet_window,
2387 area.x + 1, area.y + 1,
2388 area.width, area.height);
2395 psppire_sheet_draw_border (sheet, sheet->range);
2399 safe_strcmp (const gchar *s1, const gchar *s2)
2401 if ( !s1 && !s2) return 0;
2402 if ( !s1) return -1;
2403 if ( !s2) return +1;
2404 return strcmp (s1, s2);
2408 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2409 GtkJustification justification,
2412 PsppireSheetModel *model ;
2415 g_return_if_fail (sheet != NULL);
2416 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2418 if (col >= psppire_axis_unit_count (sheet->haxis)
2419 || row >= psppire_axis_unit_count (sheet->vaxis))
2422 if (col < 0 || row < 0) return;
2424 model = psppire_sheet_get_model (sheet);
2426 old_text = psppire_sheet_model_get_string (model, row, col);
2428 if (0 != safe_strcmp (old_text, text))
2430 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2431 psppire_sheet_model_set_string (model, text, row, col);
2432 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2435 if ( psppire_sheet_model_free_strings (model))
2441 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2443 PsppireSheetRange range;
2445 g_return_if_fail (sheet != NULL);
2446 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2447 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2448 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2450 if (column < 0 || row < 0) return;
2454 range.col0 = min_visible_column (sheet);
2455 range.coli = max_visible_column (sheet);
2457 psppire_sheet_real_cell_clear (sheet, row, column);
2459 redraw_range (sheet, &range);
2463 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2465 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2467 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2469 if (old_text && strlen (old_text) > 0 )
2471 psppire_sheet_model_datum_clear (model, row, column);
2474 dispose_string (sheet, old_text);
2478 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2480 PsppireSheetModel *model;
2481 g_return_val_if_fail (sheet != NULL, NULL);
2482 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2484 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2486 if (col < 0 || row < 0) return NULL;
2488 model = psppire_sheet_get_model (sheet);
2493 return psppire_sheet_model_get_string (model, row, col);
2498 psppire_sheet_cell_get_state (PsppireSheet *sheet, gint row, gint col)
2501 PsppireSheetRange *range;
2503 g_return_val_if_fail (sheet != NULL, 0);
2504 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2505 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis)) return 0;
2506 if (col < 0 || row < 0) return 0;
2508 state = sheet->state;
2509 range = &sheet->range;
2513 case PSPPIRE_SHEET_NORMAL:
2514 return GTK_STATE_NORMAL;
2516 case PSPPIRE_SHEET_ROW_SELECTED:
2517 if (row >= range->row0 && row <= range->rowi)
2518 return GTK_STATE_SELECTED;
2520 case PSPPIRE_SHEET_COLUMN_SELECTED:
2521 if (col >= range->col0 && col <= range->coli)
2522 return GTK_STATE_SELECTED;
2524 case PSPPIRE_SHEET_RANGE_SELECTED:
2525 if (row >= range->row0 && row <= range->rowi && \
2526 col >= range->col0 && col <= range->coli)
2527 return GTK_STATE_SELECTED;
2530 return GTK_STATE_NORMAL;
2533 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2534 If the function returns FALSE, then the results will be unreliable.
2537 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2545 *column = -G_MAXINT;
2547 g_return_val_if_fail (sheet != NULL, 0);
2548 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2550 /* bounds checking, return false if the user clicked
2558 if ( sheet->column_titles_visible)
2559 y -= sheet->column_title_area.height;
2561 y += sheet->vadjustment->value;
2563 if ( y < 0 && sheet->column_titles_visible)
2569 trow = row_from_ypixel (sheet, y);
2570 if (trow > psppire_axis_unit_count (sheet->vaxis))
2576 if ( sheet->row_titles_visible)
2577 x -= sheet->row_title_area.width;
2579 x += sheet->hadjustment->value;
2581 if ( x < 0 && sheet->row_titles_visible)
2587 tcol = column_from_xpixel (sheet, x);
2588 if (tcol > psppire_axis_unit_count (sheet->haxis))
2598 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2603 g_return_val_if_fail (sheet != NULL, 0);
2604 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2606 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2609 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2610 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2612 area->width= (column == -1) ? sheet->row_title_area.width
2613 : psppire_axis_unit_size (sheet->haxis, column);
2615 area->height= (row == -1) ? sheet->column_title_area.height
2616 : psppire_axis_unit_size (sheet->vaxis, row);
2622 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2624 g_return_if_fail (sheet != NULL);
2625 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2627 if (row < -1 || col < -1)
2630 if (row >= psppire_axis_unit_count (sheet->vaxis)
2632 col >= psppire_axis_unit_count (sheet->haxis))
2635 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2638 if ( row == -1 || col == -1)
2640 psppire_sheet_hide_entry_widget (sheet);
2644 change_active_cell (sheet, row, col);
2648 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2650 g_return_if_fail (sheet != NULL);
2651 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2653 if ( row ) *row = sheet->active_cell.row;
2654 if (column) *column = sheet->active_cell.col;
2658 entry_load_text (PsppireSheet *sheet)
2662 GtkJustification justification;
2663 PsppireSheetCellAttr attributes;
2665 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2666 if (sheet->state != GTK_STATE_NORMAL) return;
2668 row = sheet->active_cell.row;
2669 col = sheet->active_cell.col;
2671 if (row < 0 || col < 0) return;
2673 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2675 if (text && strlen (text) > 0)
2677 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2678 justification = attributes.justification;
2679 psppire_sheet_set_cell (sheet, row, col, justification, text);
2685 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2687 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2690 if (sheet->active_cell.row < 0 ||
2691 sheet->active_cell.col < 0) return;
2693 gtk_widget_hide (sheet->entry_widget);
2694 gtk_widget_unmap (sheet->entry_widget);
2696 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2700 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2702 gint old_row, old_col;
2704 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2706 if (row < 0 || col < 0)
2709 if ( row > psppire_axis_unit_count (sheet->vaxis)
2710 || col > psppire_axis_unit_count (sheet->haxis))
2713 if (sheet->state != PSPPIRE_SHEET_NORMAL)
2715 sheet->state = PSPPIRE_SHEET_NORMAL;
2716 psppire_sheet_real_unselect_range (sheet, NULL);
2719 old_row = sheet->active_cell.row;
2720 old_col = sheet->active_cell.col;
2722 /* Erase the old cell */
2723 psppire_sheet_draw_active_cell (sheet);
2725 entry_load_text (sheet);
2727 sheet->range.row0 = row;
2728 sheet->range.col0 = col;
2729 sheet->range.rowi = row;
2730 sheet->range.coli = col;
2731 sheet->active_cell.row = row;
2732 sheet->active_cell.col = col;
2733 sheet->selection_cell.row = row;
2734 sheet->selection_cell.col = col;
2736 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2738 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2740 psppire_sheet_draw_active_cell (sheet);
2741 psppire_sheet_show_entry_widget (sheet);
2743 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2745 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2746 row, col, old_row, old_col);
2751 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2753 GtkEntry *sheet_entry;
2754 PsppireSheetCellAttr attributes;
2758 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2760 row = sheet->active_cell.row;
2761 col = sheet->active_cell.col;
2763 /* Don't show the active cell, if there is no active cell: */
2764 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2767 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2768 if (sheet->state != PSPPIRE_SHEET_NORMAL) return;
2769 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2771 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2773 sheet_entry = psppire_sheet_get_entry (sheet);
2775 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2777 if (GTK_IS_ENTRY (sheet_entry))
2779 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2780 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2783 text = g_strdup ("");
2785 if (strcmp (old_text, text) != 0)
2786 gtk_entry_set_text (sheet_entry, text);
2788 dispose_string (sheet, text);
2791 switch (attributes.justification)
2793 case GTK_JUSTIFY_RIGHT:
2794 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2796 case GTK_JUSTIFY_CENTER:
2797 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2799 case GTK_JUSTIFY_LEFT:
2801 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2807 psppire_sheet_size_allocate_entry (sheet);
2809 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2810 psppire_sheet_model_is_editable (sheet->model,
2812 gtk_widget_map (sheet->entry_widget);
2816 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2819 PsppireSheetRange range;
2821 row = sheet->active_cell.row;
2822 col = sheet->active_cell.col;
2824 if (row < 0 || col < 0) return FALSE;
2826 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2829 range.col0 = range.coli = col;
2830 range.row0 = range.rowi = row;
2832 psppire_sheet_draw_border (sheet, range);
2840 psppire_sheet_new_selection (PsppireSheet *sheet, PsppireSheetRange *range)
2842 gint i, j, mask1, mask2;
2843 gint state, selected;
2844 gint x, y, width, height;
2845 PsppireSheetRange new_range, aux_range;
2847 g_return_if_fail (sheet != NULL);
2849 if (range == NULL) range=&sheet->range;
2853 range->row0 = MIN (range->row0, sheet->range.row0);
2854 range->rowi = MAX (range->rowi, sheet->range.rowi);
2855 range->col0 = MIN (range->col0, sheet->range.col0);
2856 range->coli = MAX (range->coli, sheet->range.coli);
2858 range->row0 = MAX (range->row0, min_visible_row (sheet));
2859 range->rowi = MIN (range->rowi, max_visible_row (sheet));
2860 range->col0 = MAX (range->col0, min_visible_column (sheet));
2861 range->coli = MIN (range->coli, max_visible_column (sheet));
2863 aux_range.row0 = MAX (new_range.row0, min_visible_row (sheet));
2864 aux_range.rowi = MIN (new_range.rowi, max_visible_row (sheet));
2865 aux_range.col0 = MAX (new_range.col0, min_visible_column (sheet));
2866 aux_range.coli = MIN (new_range.coli, max_visible_column (sheet));
2868 for (i = range->row0; i <= range->rowi; i++)
2870 for (j = range->col0; j <= range->coli; j++)
2873 state = psppire_sheet_cell_get_state (sheet, i, j);
2874 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2875 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2877 if (state == GTK_STATE_SELECTED && selected &&
2878 (i == sheet->range.row0 || i == sheet->range.rowi ||
2879 j == sheet->range.col0 || j == sheet->range.coli ||
2880 i == new_range.row0 || i == new_range.rowi ||
2881 j == new_range.col0 || j == new_range.coli))
2884 mask1 = i == sheet->range.row0 ? 1 : 0;
2885 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
2886 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
2887 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
2889 mask2 = i == new_range.row0 ? 1 : 0;
2890 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
2891 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
2892 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
2896 x = psppire_axis_start_pixel (sheet->haxis, j);
2897 y = psppire_axis_start_pixel (sheet->vaxis, i);
2898 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2899 psppire_axis_unit_size (sheet->haxis, j);
2900 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2902 if (i == sheet->range.row0)
2905 height = height + 3;
2907 if (i == sheet->range.rowi) height = height + 3;
2908 if (j == sheet->range.col0)
2913 if (j == sheet->range.coli) width = width + 3;
2915 if (i != sheet->active_cell.row || j != sheet->active_cell.col)
2917 x = psppire_axis_start_pixel (sheet->haxis, j);
2918 y = psppire_axis_start_pixel (sheet->vaxis, i);
2919 width = psppire_axis_start_pixel (sheet->haxis, j)- x+
2920 psppire_axis_unit_size (sheet->haxis, j);
2922 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2924 if (i == new_range.row0)
2927 height = height - 2;
2929 if (i == new_range.rowi) height = height - 3;
2930 if (j == new_range.col0)
2935 if (j == new_range.coli) width = width - 3;
2937 gdk_draw_rectangle (sheet->sheet_window,
2948 for (i = range->row0; i <= range->rowi; i++)
2950 for (j = range->col0; j <= range->coli; j++)
2953 state = psppire_sheet_cell_get_state (sheet, i, j);
2954 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2955 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2957 if (state == GTK_STATE_SELECTED && !selected)
2960 x = psppire_axis_start_pixel (sheet->haxis, j);
2961 y = psppire_axis_start_pixel (sheet->vaxis, i);
2962 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2963 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
2965 if (i == sheet->range.row0)
2968 height = height + 3;
2970 if (i == sheet->range.rowi) height = height + 3;
2971 if (j == sheet->range.col0)
2976 if (j == sheet->range.coli) width = width + 3;
2982 for (i = range->row0; i <= range->rowi; i++)
2984 for (j = range->col0; j <= range->coli; j++)
2987 state = psppire_sheet_cell_get_state (sheet, i, j);
2988 selected= (i <= new_range.rowi && i >= new_range.row0 &&
2989 j <= new_range.coli && j >= new_range.col0) ? TRUE : FALSE;
2991 if (state != GTK_STATE_SELECTED && selected &&
2992 (i != sheet->active_cell.row || j != sheet->active_cell.col))
2995 x = psppire_axis_start_pixel (sheet->haxis, j);
2996 y = psppire_axis_start_pixel (sheet->vaxis, i);
2997 width = psppire_axis_start_pixel (sheet->haxis, j) - x + psppire_axis_unit_size (sheet->haxis, j);
2998 height = psppire_axis_start_pixel (sheet->vaxis, i) - y + psppire_axis_unit_size (sheet->vaxis, i);
3000 if (i == new_range.row0)
3003 height = height - 2;
3005 if (i == new_range.rowi) height = height - 3;
3006 if (j == new_range.col0)
3011 if (j == new_range.coli) width = width - 3;
3013 gdk_draw_rectangle (sheet->sheet_window,
3024 for (i = aux_range.row0; i <= aux_range.rowi; i++)
3026 for (j = aux_range.col0; j <= aux_range.coli; j++)
3028 state = psppire_sheet_cell_get_state (sheet, i, j);
3030 mask1 = i == sheet->range.row0 ? 1 : 0;
3031 mask1 = i == sheet->range.rowi ? mask1 + 2 : mask1;
3032 mask1 = j == sheet->range.col0 ? mask1 + 4 : mask1;
3033 mask1 = j == sheet->range.coli ? mask1 + 8 : mask1;
3035 mask2 = i == new_range.row0 ? 1 : 0;
3036 mask2 = i == new_range.rowi ? mask2 + 2 : mask2;
3037 mask2 = j == new_range.col0 ? mask2 + 4 : mask2;
3038 mask2 = j == new_range.coli ? mask2 + 8 : mask2;
3039 if (mask2 != mask1 || (mask2 == mask1 && state != GTK_STATE_SELECTED))
3041 x = psppire_axis_start_pixel (sheet->haxis, j);
3042 y = psppire_axis_start_pixel (sheet->vaxis, i);
3043 width = psppire_axis_unit_size (sheet->haxis, j);
3044 height = psppire_axis_unit_size (sheet->vaxis, i);
3046 gdk_draw_rectangle (sheet->sheet_window,
3054 gdk_draw_rectangle (sheet->sheet_window,
3057 x + 1, y + height - 1,
3061 gdk_draw_rectangle (sheet->sheet_window,
3069 gdk_draw_rectangle (sheet->sheet_window,
3072 x + width - 1, y + 1,
3084 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
3088 rectangle_from_range (sheet, &new_range, &area);
3090 gdk_draw_rectangle (sheet->sheet_window,
3094 area.width, area.height);
3099 psppire_sheet_real_select_range (PsppireSheet *sheet,
3100 const PsppireSheetRange *range)
3104 g_return_if_fail (sheet != NULL);
3106 if (range == NULL) range = &sheet->range;
3108 memcpy (&sheet->range, range, sizeof (*range));
3110 if (range->row0 < 0 || range->rowi < 0) return;
3111 if (range->col0 < 0 || range->coli < 0) return;
3113 state = sheet->state;
3116 if (range->coli != sheet->range.coli || range->col0 != sheet->range.col0 ||
3117 range->rowi != sheet->range.rowi || range->row0 != sheet->range.row0)
3119 psppire_sheet_new_selection (sheet, &sheet->range);
3123 psppire_sheet_range_draw_selection (sheet, sheet->range);
3127 psppire_sheet_update_primary_selection (sheet);
3129 g_signal_emit (sheet, sheet_signals[SELECT_RANGE], 0, &sheet->range);
3134 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
3136 g_return_if_fail (sheet != NULL);
3137 *range = sheet->range;
3142 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
3144 g_return_if_fail (sheet != NULL);
3146 if (range == NULL) range=&sheet->range;
3148 if (range->row0 < 0 || range->rowi < 0) return;
3149 if (range->col0 < 0 || range->coli < 0) return;
3152 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3153 psppire_sheet_real_unselect_range (sheet, NULL);
3155 sheet->range.row0 = range->row0;
3156 sheet->range.rowi = range->rowi;
3157 sheet->range.col0 = range->col0;
3158 sheet->range.coli = range->coli;
3159 sheet->active_cell.row = range->row0;
3160 sheet->active_cell.col = range->col0;
3161 sheet->selection_cell.row = range->rowi;
3162 sheet->selection_cell.col = range->coli;
3164 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3165 psppire_sheet_real_select_range (sheet, NULL);
3169 psppire_sheet_unselect_range (PsppireSheet *sheet)
3171 if (! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
3174 psppire_sheet_real_unselect_range (sheet, NULL);
3175 sheet->state = GTK_STATE_NORMAL;
3177 change_active_cell (sheet,
3178 sheet->active_cell.row, sheet->active_cell.col);
3183 psppire_sheet_real_unselect_range (PsppireSheet *sheet,
3184 const PsppireSheetRange *range)
3186 g_return_if_fail (sheet != NULL);
3187 g_return_if_fail (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)));
3190 range = &sheet->range;
3192 if (range->row0 < 0 || range->rowi < 0) return;
3193 if (range->col0 < 0 || range->coli < 0) return;
3195 g_signal_emit (sheet, sheet_signals[SELECT_COLUMN], 0, -1);
3196 g_signal_emit (sheet, sheet_signals[SELECT_ROW], 0, -1);
3198 sheet->range.row0 = -1;
3199 sheet->range.rowi = -1;
3200 sheet->range.col0 = -1;
3201 sheet->range.coli = -1;
3206 psppire_sheet_expose (GtkWidget *widget,
3207 GdkEventExpose *event)
3209 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3211 g_return_val_if_fail (event != NULL, FALSE);
3213 if (!GTK_WIDGET_DRAWABLE (widget))
3216 /* exposure events on the sheet */
3217 if (event->window == sheet->row_title_window &&
3218 sheet->row_titles_visible)
3220 draw_row_title_buttons_range (sheet,
3221 min_visible_row (sheet),
3222 max_visible_row (sheet));
3225 if (event->window == sheet->column_title_window &&
3226 sheet->column_titles_visible)
3228 draw_column_title_buttons_range (sheet,
3229 min_visible_column (sheet),
3230 max_visible_column (sheet));
3233 if (event->window == sheet->sheet_window)
3235 draw_sheet_region (sheet, event->region);
3238 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3240 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3241 psppire_sheet_range_draw (sheet, &sheet->range);
3243 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3244 psppire_sheet_range_draw (sheet, &sheet->drag_range);
3246 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
3247 psppire_sheet_range_draw_selection (sheet, sheet->range);
3248 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
3249 draw_xor_rectangle (sheet, sheet->drag_range);
3253 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
3256 PsppireSheetRange range;
3257 range.row0 = range.rowi = sheet->active_cell.row;
3258 range.col0 = range.coli = sheet->active_cell.col;
3260 rectangle_from_range (sheet, &range, &rect);
3262 if (GDK_OVERLAP_RECTANGLE_OUT !=
3263 gdk_region_rect_in (event->region, &rect))
3265 psppire_sheet_draw_active_cell (sheet);
3271 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
3278 psppire_sheet_button_press (GtkWidget *widget,
3279 GdkEventButton *event)
3281 PsppireSheet *sheet;
3282 GdkModifierType mods;
3287 g_return_val_if_fail (widget != NULL, FALSE);
3288 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
3289 g_return_val_if_fail (event != NULL, FALSE);
3291 sheet = PSPPIRE_SHEET (widget);
3293 /* Cancel any pending tooltips */
3294 if (sheet->motion_timer)
3296 g_source_remove (sheet->motion_timer);
3297 sheet->motion_timer = 0;
3300 gtk_widget_get_pointer (widget, &x, &y);
3301 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3304 if (event->window == sheet->column_title_window)
3306 sheet->x_drag = event->x;
3307 g_signal_emit (sheet,
3308 sheet_signals[BUTTON_EVENT_COLUMN], 0,
3311 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3313 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3314 g_signal_emit (sheet,
3315 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
3318 else if (event->window == sheet->row_title_window)
3320 g_signal_emit (sheet,
3321 sheet_signals[BUTTON_EVENT_ROW], 0,
3324 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3326 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
3327 g_signal_emit (sheet,
3328 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
3332 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
3334 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
3337 /* press on resize windows */
3338 if (event->window == sheet->column_title_window)
3340 sheet->x_drag = event->x;
3342 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
3344 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3345 gdk_pointer_grab (sheet->column_title_window, FALSE,
3346 GDK_POINTER_MOTION_HINT_MASK |
3347 GDK_BUTTON1_MOTION_MASK |
3348 GDK_BUTTON_RELEASE_MASK,
3349 NULL, NULL, event->time);
3351 draw_xor_vline (sheet);
3356 if (event->window == sheet->row_title_window)
3358 sheet->y_drag = event->y;
3360 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
3362 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3363 gdk_pointer_grab (sheet->row_title_window, FALSE,
3364 GDK_POINTER_MOTION_HINT_MASK |
3365 GDK_BUTTON1_MOTION_MASK |
3366 GDK_BUTTON_RELEASE_MASK,
3367 NULL, NULL, event->time);
3369 draw_xor_hline (sheet);
3374 /* the sheet itself does not handle other than single click events */
3375 if (event->type != GDK_BUTTON_PRESS) return FALSE;
3377 /* selections on the sheet */
3378 if (event->window == sheet->sheet_window)
3380 gtk_widget_get_pointer (widget, &x, &y);
3381 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3382 gdk_pointer_grab (sheet->sheet_window, FALSE,
3383 GDK_POINTER_MOTION_HINT_MASK |
3384 GDK_BUTTON1_MOTION_MASK |
3385 GDK_BUTTON_RELEASE_MASK,
3386 NULL, NULL, event->time);
3387 gtk_grab_add (GTK_WIDGET (sheet));
3389 if (sheet->selection_mode != GTK_SELECTION_SINGLE &&
3390 sheet->selection_mode != GTK_SELECTION_NONE &&
3391 sheet->cursor_drag->type == GDK_SIZING &&
3392 !PSPPIRE_SHEET_IN_SELECTION (sheet) && !PSPPIRE_SHEET_IN_RESIZE (sheet))
3394 if (sheet->state == GTK_STATE_NORMAL)
3396 row = sheet->active_cell.row;
3397 column = sheet->active_cell.col;
3398 sheet->active_cell.row = row;
3399 sheet->active_cell.col = column;
3400 sheet->drag_range = sheet->range;
3401 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3402 psppire_sheet_select_range (sheet, &sheet->drag_range);
3406 if (row > sheet->range.rowi) row--;
3407 if (column > sheet->range.coli) column--;
3408 sheet->drag_cell.row = row;
3409 sheet->drag_cell.col = column;
3410 sheet->drag_range = sheet->range;
3411 draw_xor_rectangle (sheet, sheet->drag_range);
3412 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3414 else if (sheet->cursor_drag->type == GDK_TOP_LEFT_ARROW &&
3415 !PSPPIRE_SHEET_IN_SELECTION (sheet)
3416 && ! PSPPIRE_SHEET_IN_DRAG (sheet)
3417 && sheet->active_cell.row >= 0
3418 && sheet->active_cell.col >= 0
3421 if (sheet->state == GTK_STATE_NORMAL)
3423 row = sheet->active_cell.row;
3424 column = sheet->active_cell.col;
3425 sheet->active_cell.row = row;
3426 sheet->active_cell.col = column;
3427 sheet->drag_range = sheet->range;
3428 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3429 psppire_sheet_select_range (sheet, &sheet->drag_range);
3433 if (row < sheet->range.row0) row++;
3434 if (row > sheet->range.rowi) row--;
3435 if (column < sheet->range.col0) column++;
3436 if (column > sheet->range.coli) column--;
3437 sheet->drag_cell.row = row;
3438 sheet->drag_cell.col = column;
3439 sheet->drag_range = sheet->range;
3440 draw_xor_rectangle (sheet, sheet->drag_range);
3441 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3445 veto = psppire_sheet_click_cell (sheet, row, column);
3446 if (veto) PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3450 if (event->window == sheet->column_title_window)
3452 gtk_widget_get_pointer (widget, &x, &y);
3453 if ( sheet->row_titles_visible)
3454 x -= sheet->row_title_area.width;
3456 x += sheet->hadjustment->value;
3458 column = column_from_xpixel (sheet, x);
3460 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
3462 veto = psppire_sheet_click_cell (sheet, -1, column);
3463 gtk_grab_add (GTK_WIDGET (sheet));
3464 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3468 if (event->window == sheet->row_title_window)
3470 gtk_widget_get_pointer (widget, &x, &y);
3471 if ( sheet->column_titles_visible)
3472 y -= sheet->column_title_area.height;
3474 y += sheet->vadjustment->value;
3476 row = row_from_ypixel (sheet, y);
3477 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
3479 veto = psppire_sheet_click_cell (sheet, row, -1);
3480 gtk_grab_add (GTK_WIDGET (sheet));
3481 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3489 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3491 PsppireSheetCell cell;
3492 gboolean forbid_move;
3497 if (row >= psppire_axis_unit_count (sheet->vaxis)
3498 || column >= psppire_axis_unit_count (sheet->haxis))
3503 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3504 &sheet->active_cell,
3510 if (sheet->state == GTK_STATE_NORMAL)
3513 row = sheet->active_cell.row;
3514 column = sheet->active_cell.col;
3516 change_active_cell (sheet, row, column);
3520 if (row == -1 && column >= 0)
3522 psppire_sheet_select_column (sheet, column);
3526 if (column == -1 && row >= 0)
3528 psppire_sheet_select_row (sheet, row);
3532 if (row == -1 && column == -1)
3534 sheet->range.row0 = 0;
3535 sheet->range.col0 = 0;
3536 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3538 psppire_axis_unit_count (sheet->haxis) - 1;
3539 sheet->active_cell.row = 0;
3540 sheet->active_cell.col = 0;
3541 psppire_sheet_select_range (sheet, NULL);
3545 if (sheet->state != PSPPIRE_SHEET_NORMAL)
3547 sheet->state = PSPPIRE_SHEET_NORMAL;
3548 psppire_sheet_real_unselect_range (sheet, NULL);
3552 change_active_cell (sheet, row, column);
3555 sheet->active_cell.row = row;
3556 sheet->active_cell.col = column;
3557 sheet->selection_cell.row = row;
3558 sheet->selection_cell.col = column;
3559 sheet->range.row0 = row;
3560 sheet->range.col0 = column;
3561 sheet->range.rowi = row;
3562 sheet->range.coli = column;
3563 sheet->state = PSPPIRE_SHEET_NORMAL;
3564 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3566 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3572 psppire_sheet_button_release (GtkWidget *widget,
3573 GdkEventButton *event)
3575 GdkDisplay *display = gtk_widget_get_display (widget);
3577 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3579 /* release on resize windows */
3580 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3583 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3584 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3586 gdk_display_pointer_ungrab (display, event->time);
3587 draw_xor_vline (sheet);
3590 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3591 + sheet->hadjustment->value;
3593 set_column_width (sheet, sheet->drag_cell.col, width);
3598 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3601 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3602 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3604 gdk_display_pointer_ungrab (display, event->time);
3605 draw_xor_hline (sheet);
3608 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3609 sheet->vadjustment->value;
3611 set_row_height (sheet, sheet->drag_cell.row, height);
3616 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3618 PsppireSheetRange old_range;
3619 draw_xor_rectangle (sheet, sheet->drag_range);
3620 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3621 gdk_display_pointer_ungrab (display, event->time);
3623 psppire_sheet_real_unselect_range (sheet, NULL);
3625 sheet->active_cell.row = sheet->active_cell.row +
3626 (sheet->drag_range.row0 - sheet->range.row0);
3627 sheet->active_cell.col = sheet->active_cell.col +
3628 (sheet->drag_range.col0 - sheet->range.col0);
3629 sheet->selection_cell.row = sheet->selection_cell.row +
3630 (sheet->drag_range.row0 - sheet->range.row0);
3631 sheet->selection_cell.col = sheet->selection_cell.col +
3632 (sheet->drag_range.col0 - sheet->range.col0);
3633 old_range = sheet->range;
3634 sheet->range = sheet->drag_range;
3635 sheet->drag_range = old_range;
3636 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3637 &sheet->drag_range, &sheet->range);
3638 psppire_sheet_select_range (sheet, &sheet->range);
3641 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
3643 PsppireSheetRange old_range;
3644 draw_xor_rectangle (sheet, sheet->drag_range);
3645 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_RESIZE);
3646 gdk_display_pointer_ungrab (display, event->time);
3648 psppire_sheet_real_unselect_range (sheet, NULL);
3650 sheet->active_cell.row = sheet->active_cell.row +
3651 (sheet->drag_range.row0 - sheet->range.row0);
3652 sheet->active_cell.col = sheet->active_cell.col +
3653 (sheet->drag_range.col0 - sheet->range.col0);
3654 if (sheet->drag_range.row0 < sheet->range.row0)
3655 sheet->selection_cell.row = sheet->drag_range.row0;
3656 if (sheet->drag_range.rowi >= sheet->range.rowi)
3657 sheet->selection_cell.row = sheet->drag_range.rowi;
3658 if (sheet->drag_range.col0 < sheet->range.col0)
3659 sheet->selection_cell.col = sheet->drag_range.col0;
3660 if (sheet->drag_range.coli >= sheet->range.coli)
3661 sheet->selection_cell.col = sheet->drag_range.coli;
3662 old_range = sheet->range;
3663 sheet->range = sheet->drag_range;
3664 sheet->drag_range = old_range;
3666 if (sheet->state == GTK_STATE_NORMAL) sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
3667 g_signal_emit (sheet, sheet_signals[RESIZE_RANGE], 0,
3668 &sheet->drag_range, &sheet->range);
3669 psppire_sheet_select_range (sheet, &sheet->range);
3672 if (sheet->state == PSPPIRE_SHEET_NORMAL && PSPPIRE_SHEET_IN_SELECTION (sheet))
3674 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3675 gdk_display_pointer_ungrab (display, event->time);
3676 change_active_cell (sheet, sheet->active_cell.row,
3677 sheet->active_cell.col);
3680 if (PSPPIRE_SHEET_IN_SELECTION)
3681 gdk_display_pointer_ungrab (display, event->time);
3682 gtk_grab_remove (GTK_WIDGET (sheet));
3684 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3693 /* Shamelessly lifted from gtktooltips */
3695 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3699 gtk_widget_size_request (tip_window, &req);
3700 gtk_paint_flat_box (tip_window->style, tip_window->window,
3701 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3702 NULL, GTK_WIDGET(tip_window), "tooltip",
3703 0, 0, req.width, req.height);
3709 destroy_hover_window (PsppireSheetHoverTitle *h)
3711 gtk_widget_destroy (h->window);
3715 static PsppireSheetHoverTitle *
3716 create_hover_window (void)
3718 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3720 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3722 #if GTK_CHECK_VERSION (2, 9, 0)
3723 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3724 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3727 gtk_widget_set_app_paintable (hw->window, TRUE);
3728 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3729 gtk_widget_set_name (hw->window, "gtk-tooltips");
3730 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3732 g_signal_connect (hw->window,
3734 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3737 hw->label = gtk_label_new (NULL);
3740 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3741 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3743 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3745 gtk_widget_show (hw->label);
3747 g_signal_connect (hw->window,
3749 G_CALLBACK (gtk_widget_destroyed),
3755 #define HOVER_WINDOW_Y_OFFSET 2
3758 show_subtitle (PsppireSheet *sheet, gint row, gint column, const gchar *subtitle)
3767 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3771 sheet->hover_window->row = row;
3772 sheet->hover_window->column = column;
3774 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3776 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3778 gtk_widget_show (sheet->hover_window->window);
3780 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3786 y += sheet->column_title_area.y;
3787 y += sheet->column_title_area.height;
3788 y += HOVER_WINDOW_Y_OFFSET;
3794 x += sheet->row_title_area.x;
3795 x += sheet->row_title_area.width * 2 / 3.0;
3798 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3803 motion_timeout_callback (gpointer data)
3805 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3809 gdk_threads_enter ();
3810 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3812 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3814 if (sheet->row_title_under && row >= 0)
3816 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3818 show_subtitle (sheet, row, -1, text);
3822 if (sheet->column_title_under && column >= 0)
3824 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3827 show_subtitle (sheet, -1, column, text);
3833 gdk_threads_leave ();
3838 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3840 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3841 GdkModifierType mods;
3842 GdkCursorType new_cursor;
3845 GdkDisplay *display;
3847 g_return_val_if_fail (event != NULL, FALSE);
3849 display = gtk_widget_get_display (widget);
3851 /* selections on the sheet */
3855 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3857 if ( sheet->motion_timer > 0 )
3858 g_source_remove (sheet->motion_timer);
3859 sheet->motion_timer =
3860 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3866 gtk_widget_get_pointer (widget, &wx, &wy);
3868 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3870 if ( row != sheet->hover_window->row ||
3871 column != sheet->hover_window->column)
3873 gtk_widget_hide (sheet->hover_window->window);
3878 if (event->window == sheet->column_title_window)
3880 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3881 on_column_boundary (sheet, x, &column))
3883 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3884 if (new_cursor != sheet->cursor_drag->type)
3886 gdk_cursor_unref (sheet->cursor_drag);
3887 sheet->cursor_drag =
3888 gdk_cursor_new_for_display (display, new_cursor);
3890 gdk_window_set_cursor (sheet->column_title_window,
3891 sheet->cursor_drag);
3896 new_cursor = GDK_TOP_LEFT_ARROW;
3897 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3898 new_cursor != sheet->cursor_drag->type)
3900 gdk_cursor_unref (sheet->cursor_drag);
3901 sheet->cursor_drag =
3902 gdk_cursor_new_for_display (display, new_cursor);
3903 gdk_window_set_cursor (sheet->column_title_window,
3904 sheet->cursor_drag);
3908 else if (event->window == sheet->row_title_window)
3910 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3911 on_row_boundary (sheet, y, &row))
3913 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3914 if (new_cursor != sheet->cursor_drag->type)
3916 gdk_cursor_unref (sheet->cursor_drag);
3917 sheet->cursor_drag =
3918 gdk_cursor_new_for_display (display, new_cursor);
3919 gdk_window_set_cursor (sheet->row_title_window,
3920 sheet->cursor_drag);
3925 new_cursor = GDK_TOP_LEFT_ARROW;
3926 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3927 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_PLUS;
3939 if ( event->window == sheet->sheet_window &&
3940 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3941 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3942 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3943 !PSPPIRE_SHEET_IN_RESIZE (sheet) &&
3944 new_cursor != sheet->cursor_drag->type)
3946 gdk_cursor_unref (sheet->cursor_drag);
3947 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3948 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3951 new_cursor = GDK_TOP_LEFT_ARROW;
3952 if ( event->window == sheet->sheet_window &&
3953 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3954 PSPPIRE_SHEET_IN_RESIZE (sheet)) &&
3955 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3956 PSPPIRE_SHEET_IN_DRAG (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_TOP_LEFT_ARROW);
3961 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3964 new_cursor = GDK_SIZING;
3965 if ( event->window == sheet->sheet_window &&
3966 sheet->selection_mode != GTK_SELECTION_NONE &&
3967 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3968 (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ||
3969 PSPPIRE_SHEET_IN_RESIZE (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_SIZING);
3974 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3978 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3979 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3981 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3983 if (event->x != sheet->x_drag)
3985 draw_xor_vline (sheet);
3986 sheet->x_drag = event->x;
3987 draw_xor_vline (sheet);
3993 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3995 if (event->y != sheet->y_drag)
3997 draw_xor_hline (sheet);
3998 sheet->y_drag = event->y;
3999 draw_xor_hline (sheet);
4005 if (PSPPIRE_SHEET_IN_DRAG (sheet))
4007 PsppireSheetRange aux;
4008 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
4009 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
4010 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
4011 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4015 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4016 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4018 aux = sheet->drag_range;
4019 sheet->drag_range.row0 = sheet->range.row0 + row;
4020 sheet->drag_range.col0 = sheet->range.col0 + column;
4021 sheet->drag_range.rowi = sheet->range.rowi + row;
4022 sheet->drag_range.coli = sheet->range.coli + column;
4023 if (aux.row0 != sheet->drag_range.row0 ||
4024 aux.col0 != sheet->drag_range.col0)
4026 draw_xor_rectangle (sheet, aux);
4027 draw_xor_rectangle (sheet, sheet->drag_range);
4033 if (PSPPIRE_SHEET_IN_RESIZE (sheet))
4035 PsppireSheetRange aux;
4036 gint v_h, current_col, current_row, col_threshold, row_threshold;
4038 if (abs (x - psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)) >
4039 abs (y - psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row))) v_h = 2;
4041 current_col = column_from_xpixel (sheet, x);
4042 current_row = row_from_ypixel (sheet, y);
4043 column = current_col - sheet->drag_cell.col;
4044 row = current_row - sheet->drag_cell.row;
4046 /*use half of column width resp. row height as threshold to
4048 col_threshold = psppire_axis_start_pixel (sheet->haxis, current_col) +
4049 psppire_axis_unit_size (sheet->haxis, current_col) / 2;
4052 if (x < col_threshold)
4055 else if (column < 0)
4057 if (x > col_threshold)
4060 row_threshold = psppire_axis_start_pixel (sheet->vaxis, current_row) +
4061 psppire_axis_unit_size (sheet->vaxis, current_row)/2;
4064 if (y < row_threshold)
4069 if (y > row_threshold)
4073 if (sheet->state == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
4074 if (sheet->state == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
4084 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
4085 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
4087 aux = sheet->drag_range;
4088 sheet->drag_range = sheet->range;
4090 if (row < 0) sheet->drag_range.row0 = sheet->range.row0 + row;
4091 if (row > 0) sheet->drag_range.rowi = sheet->range.rowi + row;
4092 if (column < 0) sheet->drag_range.col0 = sheet->range.col0 + column;
4093 if (column > 0) sheet->drag_range.coli = sheet->range.coli + column;
4095 if (aux.row0 != sheet->drag_range.row0 ||
4096 aux.rowi != sheet->drag_range.rowi ||
4097 aux.col0 != sheet->drag_range.col0 ||
4098 aux.coli != sheet->drag_range.coli)
4100 draw_xor_rectangle (sheet, aux);
4101 draw_xor_rectangle (sheet, sheet->drag_range);
4107 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
4109 if (sheet->state == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
4110 column == sheet->active_cell.col) return TRUE;
4112 if (PSPPIRE_SHEET_IN_SELECTION (sheet) && mods&GDK_BUTTON1_MASK)
4113 psppire_sheet_extend_selection (sheet, row, column);
4119 psppire_sheet_crossing_notify (GtkWidget *widget,
4120 GdkEventCrossing *event)
4122 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4124 if (event->window == sheet->column_title_window)
4125 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
4126 else if (event->window == sheet->row_title_window)
4127 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
4134 psppire_sheet_focus_in (GtkWidget *w,
4135 GdkEventFocus *event)
4137 PsppireSheet *sheet = PSPPIRE_SHEET (w);
4139 gtk_widget_grab_focus (sheet->entry_widget);
4146 psppire_sheet_extend_selection (PsppireSheet *sheet, gint row, gint column)
4148 PsppireSheetRange range;
4152 if (row == sheet->selection_cell.row && column == sheet->selection_cell.col)
4155 if (sheet->selection_mode == GTK_SELECTION_SINGLE) return;
4157 gtk_widget_grab_focus (GTK_WIDGET (sheet));
4159 if (PSPPIRE_SHEET_IN_DRAG (sheet)) return;
4161 state = sheet->state;
4163 switch (sheet->state)
4165 case PSPPIRE_SHEET_ROW_SELECTED:
4166 column = psppire_axis_unit_count (sheet->haxis) - 1;
4168 case PSPPIRE_SHEET_COLUMN_SELECTED:
4169 row = psppire_axis_unit_count (sheet->vaxis) - 1;
4171 case PSPPIRE_SHEET_NORMAL:
4172 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4173 r = sheet->active_cell.row;
4174 c = sheet->active_cell.col;
4175 sheet->range.col0 = c;
4176 sheet->range.row0 = r;
4177 sheet->range.coli = c;
4178 sheet->range.rowi = r;
4179 psppire_sheet_range_draw_selection (sheet, sheet->range);
4180 case PSPPIRE_SHEET_RANGE_SELECTED:
4181 sheet->state = PSPPIRE_SHEET_RANGE_SELECTED;
4184 sheet->selection_cell.row = row;
4185 sheet->selection_cell.col = column;
4187 range.col0 = MIN (column, sheet->active_cell.col);
4188 range.coli = MAX (column, sheet->active_cell.col);
4189 range.row0 = MIN (row, sheet->active_cell.row);
4190 range.rowi = MAX (row, sheet->active_cell.row);
4192 if (range.row0 != sheet->range.row0 || range.rowi != sheet->range.rowi ||
4193 range.col0 != sheet->range.col0 || range.coli != sheet->range.coli ||
4194 state == PSPPIRE_SHEET_NORMAL)
4195 psppire_sheet_real_select_range (sheet, &range);
4200 psppire_sheet_entry_key_press (GtkWidget *widget,
4204 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
4209 /* Number of rows in a step-increment */
4210 #define ROWS_PER_STEP 1
4214 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
4216 gint old_row = sheet->active_cell.row ;
4217 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
4221 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
4222 min_visible_row (sheet));
4226 case GTK_SCROLL_PAGE_DOWN:
4227 gtk_adjustment_set_value (sheet->vadjustment,
4228 sheet->vadjustment->value +
4229 sheet->vadjustment->page_increment);
4231 case GTK_SCROLL_PAGE_UP:
4232 gtk_adjustment_set_value (sheet->vadjustment,
4233 sheet->vadjustment->value -
4234 sheet->vadjustment->page_increment);
4238 g_assert_not_reached ();
4243 vpixel += psppire_axis_start_pixel (sheet->vaxis,
4244 min_visible_row (sheet));
4246 new_row = row_from_ypixel (sheet, vpixel);
4248 change_active_cell (sheet, new_row,
4249 sheet->active_cell.col);
4254 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
4256 gint current_row = sheet->active_cell.row;
4257 gint current_col = sheet->active_cell.col;
4258 PsppireSheetCell new_cell ;
4259 gboolean forbidden = FALSE;
4261 new_cell.row = current_row;
4262 new_cell.col = current_col;
4266 case GTK_SCROLL_STEP_DOWN:
4269 case GTK_SCROLL_STEP_UP:
4272 case GTK_SCROLL_STEP_RIGHT:
4275 case GTK_SCROLL_STEP_LEFT:
4278 case GTK_SCROLL_STEP_FORWARD:
4281 psppire_sheet_model_get_column_count (sheet->model))
4287 case GTK_SCROLL_STEP_BACKWARD:
4289 if (new_cell.col < 0)
4292 psppire_sheet_model_get_column_count (sheet->model) - 1;
4297 g_assert_not_reached ();
4301 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
4302 &sheet->active_cell,
4310 maximize_int (&new_cell.row, 0);
4311 maximize_int (&new_cell.col, 0);
4313 minimize_int (&new_cell.row,
4314 psppire_axis_unit_count (sheet->vaxis) - 1);
4316 minimize_int (&new_cell.col,
4317 psppire_axis_unit_count (sheet->haxis) - 1);
4319 change_active_cell (sheet, new_cell.row, new_cell.col);
4322 if ( new_cell.col > max_fully_visible_column (sheet))
4325 psppire_axis_start_pixel (sheet->haxis,
4327 hpos -= sheet->hadjustment->page_size;
4329 gtk_adjustment_set_value (sheet->hadjustment,
4332 else if ( new_cell.col < min_fully_visible_column (sheet))
4335 psppire_axis_start_pixel (sheet->haxis,
4338 gtk_adjustment_set_value (sheet->hadjustment,
4343 if ( new_cell.row > max_fully_visible_row (sheet))
4346 psppire_axis_start_pixel (sheet->vaxis,
4348 vpos -= sheet->vadjustment->page_size;
4350 gtk_adjustment_set_value (sheet->vadjustment,
4353 else if ( new_cell.row < min_fully_visible_row (sheet))
4356 psppire_axis_start_pixel (sheet->vaxis,
4359 gtk_adjustment_set_value (sheet->vadjustment,
4363 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
4368 psppire_sheet_key_press (GtkWidget *widget,
4371 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
4373 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
4375 switch (key->keyval)
4378 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
4381 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
4383 case GDK_ISO_Left_Tab:
4384 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
4387 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
4391 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
4394 step_sheet (sheet, GTK_SCROLL_STEP_UP);
4398 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
4401 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
4405 gtk_adjustment_set_value (sheet->vadjustment,
4406 sheet->vadjustment->lower);
4408 change_active_cell (sheet, 0,
4409 sheet->active_cell.col);
4414 gtk_adjustment_set_value (sheet->vadjustment,
4415 sheet->vadjustment->upper -
4416 sheet->vadjustment->page_size -
4417 sheet->vadjustment->page_increment);
4420 change_active_cellx (sheet,
4421 psppire_axis_unit_count (sheet->vaxis) - 1,
4422 sheet->active_cell.col);
4426 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
4437 psppire_sheet_size_request (GtkWidget *widget,
4438 GtkRequisition *requisition)
4440 PsppireSheet *sheet;
4442 g_return_if_fail (widget != NULL);
4443 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4444 g_return_if_fail (requisition != NULL);
4446 sheet = PSPPIRE_SHEET (widget);
4448 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
4449 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
4451 /* compute the size of the column title area */
4452 if (sheet->column_titles_visible)
4453 requisition->height += sheet->column_title_area.height;
4455 /* compute the size of the row title area */
4456 if (sheet->row_titles_visible)
4457 requisition->width += sheet->row_title_area.width;
4462 psppire_sheet_size_allocate (GtkWidget *widget,
4463 GtkAllocation *allocation)
4465 PsppireSheet *sheet;
4466 GtkAllocation sheet_allocation;
4469 g_return_if_fail (widget != NULL);
4470 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
4471 g_return_if_fail (allocation != NULL);
4473 sheet = PSPPIRE_SHEET (widget);
4474 widget->allocation = *allocation;
4475 border_width = GTK_CONTAINER (widget)->border_width;
4477 if (GTK_WIDGET_REALIZED (widget))
4478 gdk_window_move_resize (widget->window,
4479 allocation->x + border_width,
4480 allocation->y + border_width,
4481 allocation->width - 2 * border_width,
4482 allocation->height - 2 * border_width);
4484 sheet_allocation.x = 0;
4485 sheet_allocation.y = 0;
4486 sheet_allocation.width = allocation->width - 2 * border_width;
4487 sheet_allocation.height = allocation->height - 2 * border_width;
4489 if (GTK_WIDGET_REALIZED (widget))
4490 gdk_window_move_resize (sheet->sheet_window,
4493 sheet_allocation.width,
4494 sheet_allocation.height);
4496 /* position the window which holds the column title buttons */
4497 sheet->column_title_area.x = 0;
4498 sheet->column_title_area.y = 0;
4499 sheet->column_title_area.width = sheet_allocation.width ;
4502 /* position the window which holds the row title buttons */
4503 sheet->row_title_area.x = 0;
4504 sheet->row_title_area.y = 0;
4505 sheet->row_title_area.height = sheet_allocation.height;
4507 if (sheet->row_titles_visible)
4508 sheet->column_title_area.x += sheet->row_title_area.width;
4510 if (sheet->column_titles_visible)
4511 sheet->row_title_area.y += sheet->column_title_area.height;
4513 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
4514 gdk_window_move_resize (sheet->column_title_window,
4515 sheet->column_title_area.x,
4516 sheet->column_title_area.y,
4517 sheet->column_title_area.width,
4518 sheet->column_title_area.height);
4521 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
4522 gdk_window_move_resize (sheet->row_title_window,
4523 sheet->row_title_area.x,
4524 sheet->row_title_area.y,
4525 sheet->row_title_area.width,
4526 sheet->row_title_area.height);
4528 size_allocate_global_button (sheet);
4532 gint width = sheet->column_title_area.width;
4534 if ( sheet->row_titles_visible)
4535 width -= sheet->row_title_area.width;
4537 g_object_set (sheet->haxis,
4538 "minimum-extent", width,
4545 gint height = sheet->row_title_area.height;
4547 if ( sheet->column_titles_visible)
4548 height -= sheet->column_title_area.height;
4550 g_object_set (sheet->vaxis,
4551 "minimum-extent", height,
4556 /* set the scrollbars adjustments */
4557 adjust_scrollbars (sheet);
4561 draw_column_title_buttons (PsppireSheet *sheet)
4565 if (!sheet->column_titles_visible) return;
4566 if (!GTK_WIDGET_REALIZED (sheet))
4569 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
4572 if (sheet->row_titles_visible)
4574 x = sheet->row_title_area.width;
4577 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
4579 sheet->column_title_area.width = width;
4580 sheet->column_title_area.x = x;
4581 gdk_window_move_resize (sheet->column_title_window,
4582 sheet->column_title_area.x,
4583 sheet->column_title_area.y,
4584 sheet->column_title_area.width,
4585 sheet->column_title_area.height);
4588 if (max_visible_column (sheet) ==
4589 psppire_axis_unit_count (sheet->haxis) - 1)
4590 gdk_window_clear_area (sheet->column_title_window,
4592 sheet->column_title_area.width,
4593 sheet->column_title_area.height);
4595 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4597 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
4598 max_visible_column (sheet));
4602 draw_row_title_buttons (PsppireSheet *sheet)
4607 if (!sheet->row_titles_visible) return;
4608 if (!GTK_WIDGET_REALIZED (sheet))
4611 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
4613 if (sheet->column_titles_visible)
4615 y = sheet->column_title_area.height;
4618 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
4620 sheet->row_title_area.y = y;
4621 sheet->row_title_area.height = height;
4622 gdk_window_move_resize (sheet->row_title_window,
4623 sheet->row_title_area.x,
4624 sheet->row_title_area.y,
4625 sheet->row_title_area.width,
4626 sheet->row_title_area.height);
4629 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
4630 gdk_window_clear_area (sheet->row_title_window,
4632 sheet->row_title_area.width,
4633 sheet->row_title_area.height);
4635 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
4637 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
4638 max_visible_row (sheet));
4643 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
4645 GtkAllocation entry_alloc;
4646 PsppireSheetCellAttr attributes = { 0 };
4647 GtkEntry *sheet_entry;
4649 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4650 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
4652 sheet_entry = psppire_sheet_get_entry (sheet);
4654 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
4655 sheet->active_cell.col,
4659 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
4661 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
4663 style->bg[GTK_STATE_NORMAL] = attributes.background;
4664 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4665 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4666 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4667 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4668 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4671 rectangle_from_cell (sheet, sheet->active_cell.row,
4672 sheet->active_cell.col, &entry_alloc);
4674 entry_alloc.width -= BORDER_WIDTH ;
4675 entry_alloc.height -= BORDER_WIDTH ;
4676 entry_alloc.x += DIV_RND_UP (BORDER_WIDTH, 2);
4677 entry_alloc.y += DIV_RND_UP (BORDER_WIDTH, 2);
4680 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4681 entry_alloc.height);
4682 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4686 /* Copy the sheet's font to the entry widget */
4688 set_entry_widget_font (PsppireSheet *sheet)
4690 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4692 pango_font_description_free (style->font_desc);
4693 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4695 gtk_widget_modify_style (sheet->entry_widget, style);
4699 create_sheet_entry (PsppireSheet *sheet)
4701 if (sheet->entry_widget)
4703 gtk_widget_unparent (sheet->entry_widget);
4706 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4707 g_object_ref_sink (sheet->entry_widget);
4709 gtk_widget_size_request (sheet->entry_widget, NULL);
4711 if ( GTK_IS_ENTRY (sheet->entry_widget))
4713 g_object_set (sheet->entry_widget,
4718 if (GTK_WIDGET_REALIZED (sheet))
4720 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4721 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4722 gtk_widget_realize (sheet->entry_widget);
4725 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4726 G_CALLBACK (psppire_sheet_entry_key_press),
4729 set_entry_widget_font (sheet);
4731 gtk_widget_show (sheet->entry_widget);
4735 /* Finds the last child widget that happens to be of type GtkEntry */
4737 find_entry (GtkWidget *w, gpointer user_data)
4739 GtkWidget **entry = user_data;
4740 if ( GTK_IS_ENTRY (w))
4748 psppire_sheet_get_entry (PsppireSheet *sheet)
4750 GtkWidget *w = sheet->entry_widget;
4752 g_return_val_if_fail (sheet != NULL, NULL);
4753 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4754 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4756 while (! GTK_IS_ENTRY (w))
4758 GtkWidget *entry = NULL;
4760 if (GTK_IS_CONTAINER (w))
4762 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4771 return GTK_ENTRY (w);
4776 draw_button (PsppireSheet *sheet, GdkWindow *window,
4777 PsppireSheetButton *button, gboolean is_sensitive,
4778 GdkRectangle allocation)
4780 GtkShadowType shadow_type;
4781 gint text_width = 0, text_height = 0;
4782 PangoAlignment align = PANGO_ALIGN_LEFT;
4788 g_return_if_fail (sheet != NULL);
4789 g_return_if_fail (button != NULL);
4792 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4794 gdk_window_clear_area (window,
4795 allocation.x, allocation.y,
4796 allocation.width, allocation.height);
4798 gtk_widget_ensure_style (sheet->button);
4800 gtk_paint_box (sheet->button->style, window,
4801 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4803 GTK_WIDGET (sheet->button),
4805 allocation.x, allocation.y,
4806 allocation.width, allocation.height);
4808 state = button->state;
4809 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4811 if (state == GTK_STATE_ACTIVE)
4812 shadow_type = GTK_SHADOW_IN;
4814 shadow_type = GTK_SHADOW_OUT;
4816 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4817 gtk_paint_box (sheet->button->style, window,
4818 button->state, shadow_type,
4819 &allocation, GTK_WIDGET (sheet->button),
4821 allocation.x, allocation.y,
4822 allocation.width, allocation.height);
4824 if ( button->overstruck)
4826 GdkPoint points[2] = {
4827 {allocation.x, allocation.y},
4828 {allocation.x + allocation.width,
4829 allocation.y + allocation.height}
4832 gtk_paint_polygon (sheet->button->style,
4844 if (button->label_visible)
4846 text_height = DEFAULT_ROW_HEIGHT -
4847 2 * COLUMN_TITLES_HEIGHT;
4849 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4851 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4854 allocation.y += 2 * sheet->button->style->ythickness;
4856 if (button->label && strlen (button->label) > 0)
4858 PangoRectangle rect;
4859 gchar *line = button->label;
4861 PangoLayout *layout = NULL;
4862 gint real_x = allocation.x;
4863 gint real_y = allocation.y;
4865 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4866 pango_layout_get_extents (layout, NULL, &rect);
4868 text_width = PANGO_PIXELS (rect.width);
4869 switch (button->justification)
4871 case GTK_JUSTIFY_LEFT:
4872 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4873 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4875 case GTK_JUSTIFY_RIGHT:
4876 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4877 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4879 case GTK_JUSTIFY_CENTER:
4881 real_x = allocation.x + (allocation.width - text_width)/2;
4882 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4883 pango_layout_set_justify (layout, TRUE);
4885 pango_layout_set_alignment (layout, align);
4886 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4895 g_object_unref (layout);
4898 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4900 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4904 psppire_sheet_button_free (button);
4908 /* Draw the column title buttons FIRST through to LAST */
4910 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4914 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4916 if (!sheet->column_titles_visible) return;
4918 g_return_if_fail (first >= min_visible_column (sheet));
4919 g_return_if_fail (last <= max_visible_column (sheet));
4922 rect.height = sheet->column_title_area.height;
4923 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4924 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4925 + psppire_axis_unit_size (sheet->haxis, last);
4927 rect.x -= sheet->hadjustment->value;
4929 minimize_int (&rect.width, sheet->column_title_area.width);
4930 maximize_int (&rect.x, 0);
4932 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4934 for (col = first ; col <= last ; ++col)
4936 GdkRectangle allocation;
4937 gboolean is_sensitive = FALSE;
4939 PsppireSheetButton *
4940 button = psppire_sheet_model_get_column_button (sheet->model, col);
4942 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4944 allocation.x -= sheet->hadjustment->value;
4946 allocation.height = sheet->column_title_area.height;
4947 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4948 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4950 draw_button (sheet, sheet->column_title_window,
4951 button, is_sensitive, allocation);
4954 gdk_window_end_paint (sheet->column_title_window);
4959 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4963 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4965 if (!sheet->row_titles_visible) return;
4967 g_return_if_fail (first >= min_visible_row (sheet));
4968 g_return_if_fail (last <= max_visible_row (sheet));
4971 rect.width = sheet->row_title_area.width;
4972 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4973 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4974 + psppire_axis_unit_size (sheet->vaxis, last);
4976 rect.y -= sheet->vadjustment->value;
4978 minimize_int (&rect.height, sheet->row_title_area.height);
4979 maximize_int (&rect.y, 0);
4981 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4982 for (row = first; row <= last; ++row)
4984 GdkRectangle allocation;
4986 gboolean is_sensitive = FALSE;
4988 PsppireSheetButton *button =
4989 psppire_sheet_model_get_row_button (sheet->model, row);
4991 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4993 allocation.y -= sheet->vadjustment->value;
4995 allocation.width = sheet->row_title_area.width;
4996 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4997 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4999 draw_button (sheet, sheet->row_title_window,
5000 button, is_sensitive, allocation);
5003 gdk_window_end_paint (sheet->row_title_window);
5010 * vadjustment_value_changed
5011 * hadjustment_value_changed */
5015 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
5018 (adj->value + adj->page_size)
5020 (adj->upper - adj->lower);
5022 const glong last_item = psppire_axis_unit_count (axis) - 1;
5024 if (isnan (position) || position < 0)
5028 psppire_axis_start_pixel (axis, last_item)
5030 psppire_axis_unit_size (axis, last_item)
5034 adj->page_size = page_size;
5037 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
5039 if ( adj->value < adj->lower)
5040 adj->value = adj->lower;
5043 gtk_adjustment_changed (adj);
5048 adjust_scrollbars (PsppireSheet *sheet)
5052 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5055 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
5057 if ( sheet->row_titles_visible)
5058 width -= sheet->row_title_area.width;
5060 if (sheet->column_titles_visible)
5061 height -= sheet->column_title_area.height;
5063 if (sheet->vadjustment)
5065 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
5067 sheet->vadjustment->step_increment =
5069 psppire_axis_unit_size (sheet->vaxis, last_row);
5071 sheet->vadjustment->page_increment =
5073 sheet->column_title_area.height -
5074 psppire_axis_unit_size (sheet->vaxis, last_row);
5076 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
5079 if (sheet->hadjustment)
5081 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
5082 sheet->hadjustment->step_increment = 1;
5084 sheet->hadjustment->page_increment = width;
5086 sheet->hadjustment->upper =
5087 psppire_axis_start_pixel (sheet->haxis, last_col)
5089 psppire_axis_unit_size (sheet->haxis, last_col)
5092 update_adjustment (sheet->hadjustment, sheet->haxis, width);
5096 /* Subtracts the region of WIDGET from REGION */
5098 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
5101 GdkRectangle intersect;
5104 gdk_region_get_clipbox (region, &rect);
5105 gtk_widget_intersect (widget,
5109 region2 = gdk_region_rectangle (&intersect);
5110 gdk_region_subtract (region, region2);
5111 gdk_region_destroy (region2);
5115 vadjustment_value_changed (GtkAdjustment *adjustment,
5119 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5121 g_return_if_fail (adjustment != NULL);
5123 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5125 gtk_widget_hide (sheet->entry_widget);
5128 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5130 subtract_widget_region (region, sheet->button);
5131 gdk_window_begin_paint_region (sheet->sheet_window, region);
5133 draw_sheet_region (sheet, region);
5135 draw_row_title_buttons (sheet);
5136 psppire_sheet_draw_active_cell (sheet);
5138 gdk_window_end_paint (sheet->sheet_window);
5139 gdk_region_destroy (region);
5144 hadjustment_value_changed (GtkAdjustment *adjustment,
5148 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5150 g_return_if_fail (adjustment != NULL);
5152 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
5154 gtk_widget_hide (sheet->entry_widget);
5158 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
5160 subtract_widget_region (region, sheet->button);
5161 gdk_window_begin_paint_region (sheet->sheet_window, region);
5163 draw_sheet_region (sheet, region);
5165 draw_column_title_buttons (sheet);
5167 psppire_sheet_draw_active_cell (sheet);
5169 gdk_window_end_paint (sheet->sheet_window);
5171 gdk_region_destroy (region);
5175 /* COLUMN RESIZING */
5177 draw_xor_vline (PsppireSheet *sheet)
5180 gint xpos = sheet->x_drag;
5181 gdk_drawable_get_size (sheet->sheet_window,
5184 if (sheet->row_titles_visible)
5185 xpos += sheet->row_title_area.width;
5187 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5189 sheet->column_title_area.height,
5191 height + CELL_SPACING);
5196 draw_xor_hline (PsppireSheet *sheet)
5200 gint ypos = sheet->y_drag;
5202 gdk_drawable_get_size (sheet->sheet_window,
5206 if (sheet->column_titles_visible)
5207 ypos += sheet->column_title_area.height;
5209 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
5210 sheet->row_title_area.width,
5212 width + CELL_SPACING,
5216 /* SELECTED RANGE */
5218 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
5221 GdkRectangle clip_area, area;
5224 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
5225 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
5226 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
5227 psppire_axis_unit_size (sheet->haxis, range.coli);
5228 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
5229 psppire_axis_unit_size (sheet->vaxis, range.rowi);
5231 clip_area.x = sheet->row_title_area.width;
5232 clip_area.y = sheet->column_title_area.height;
5234 gdk_drawable_get_size (sheet->sheet_window,
5235 &clip_area.width, &clip_area.height);
5237 if (!sheet->row_titles_visible) clip_area.x = 0;
5238 if (!sheet->column_titles_visible) clip_area.y = 0;
5242 area.width = area.width + area.x;
5245 if (area.width > clip_area.width) area.width = clip_area.width + 10;
5248 area.height = area.height + area.y;
5251 if (area.height > clip_area.height) area.height = clip_area.height + 10;
5255 clip_area.width += 3;
5256 clip_area.height += 3;
5258 gdk_gc_get_values (sheet->xor_gc, &values);
5260 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
5262 gdk_draw_rectangle (sheet->sheet_window,
5265 area.x + i, area.y + i,
5266 area.width - 2 * i, area.height - 2 * i);
5269 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
5271 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
5276 set_column_width (PsppireSheet *sheet,
5280 g_return_if_fail (sheet != NULL);
5281 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5283 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
5289 psppire_axis_resize (sheet->haxis, column,
5290 width - sheet->cell_padding->left -
5291 sheet->cell_padding->right);
5293 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5295 draw_column_title_buttons (sheet);
5296 adjust_scrollbars (sheet);
5297 psppire_sheet_size_allocate_entry (sheet);
5298 redraw_range (sheet, NULL);
5303 set_row_height (PsppireSheet *sheet,
5307 g_return_if_fail (sheet != NULL);
5308 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
5310 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
5316 psppire_axis_resize (sheet->vaxis, row,
5317 height - sheet->cell_padding->top -
5318 sheet->cell_padding->bottom);
5320 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
5322 draw_row_title_buttons (sheet);
5323 adjust_scrollbars (sheet);
5324 psppire_sheet_size_allocate_entry (sheet);
5325 redraw_range (sheet, NULL);
5330 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
5331 PsppireSheetCellAttr *attr)
5334 const GtkJustification *j ;
5335 GdkColormap *colormap;
5337 g_return_val_if_fail (sheet != NULL, FALSE);
5338 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
5340 if (row < 0 || col < 0) return FALSE;
5342 attr->foreground = GTK_WIDGET (sheet)->style->black;
5343 attr->background = sheet->color[BG_COLOR];
5345 attr->border.width = 0;
5346 attr->border.line_style = GDK_LINE_SOLID;
5347 attr->border.cap_style = GDK_CAP_NOT_LAST;
5348 attr->border.join_style = GDK_JOIN_MITER;
5349 attr->border.mask = 0;
5350 attr->border.color = GTK_WIDGET (sheet)->style->black;
5352 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
5353 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
5356 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
5357 attr->foreground = *fg;
5360 bg = psppire_sheet_model_get_background (sheet->model, row, col);
5363 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
5364 attr->background = *bg;
5367 attr->justification =
5368 psppire_sheet_model_get_column_justification (sheet->model, col);
5370 j = psppire_sheet_model_get_justification (sheet->model, row, col);
5372 attr->justification = *j;
5378 psppire_sheet_button_size_request (PsppireSheet *sheet,
5379 const PsppireSheetButton *button,
5380 GtkRequisition *button_requisition)
5382 GtkRequisition requisition;
5383 GtkRequisition label_requisition;
5385 label_requisition.height = DEFAULT_ROW_HEIGHT;
5386 label_requisition.width = COLUMN_MIN_WIDTH;
5388 requisition.height = DEFAULT_ROW_HEIGHT;
5389 requisition.width = COLUMN_MIN_WIDTH;
5392 *button_requisition = requisition;
5393 button_requisition->width = MAX (requisition.width, label_requisition.width);
5394 button_requisition->height = MAX (requisition.height, label_requisition.height);
5399 psppire_sheet_forall (GtkContainer *container,
5400 gboolean include_internals,
5401 GtkCallback callback,
5402 gpointer callback_data)
5404 PsppireSheet *sheet = PSPPIRE_SHEET (container);
5406 g_return_if_fail (callback != NULL);
5408 if (sheet->button && sheet->button->parent)
5409 (* callback) (sheet->button, callback_data);
5411 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
5412 (* callback) (sheet->entry_widget, callback_data);
5417 psppire_sheet_get_model (const PsppireSheet *sheet)
5419 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
5421 return sheet->model;
5425 PsppireSheetButton *
5426 psppire_sheet_button_new (void)
5428 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
5430 button->state = GTK_STATE_NORMAL;
5431 button->label = NULL;
5432 button->label_visible = TRUE;
5433 button->justification = GTK_JUSTIFY_FILL;
5434 button->overstruck = FALSE;
5441 psppire_sheet_button_free (PsppireSheetButton *button)
5443 if (!button) return ;
5445 g_free (button->label);
5450 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
5452 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
5454 if ( NULL == celltext)
5457 g_string_append (string, celltext);
5463 range_to_text (const PsppireSheet *sheet)
5468 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5471 string = g_string_sized_new (80);
5473 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5475 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
5477 append_cell_text (string, sheet, r, c);
5478 g_string_append (string, "\t");
5480 append_cell_text (string, sheet, r, c);
5481 if ( r < sheet->range.rowi)
5482 g_string_append (string, "\n");
5489 range_to_html (const PsppireSheet *sheet)
5494 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
5497 string = g_string_sized_new (480);
5499 g_string_append (string, "<html>\n");
5500 g_string_append (string, "<body>\n");
5501 g_string_append (string, "<table>\n");
5502 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
5504 g_string_append (string, "<tr>\n");
5505 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
5507 g_string_append (string, "<td>");
5508 append_cell_text (string, sheet, r, c);
5509 g_string_append (string, "</td>\n");
5511 g_string_append (string, "</tr>\n");
5513 g_string_append (string, "</table>\n");
5514 g_string_append (string, "</body>\n");
5515 g_string_append (string, "</html>\n");
5527 primary_get_cb (GtkClipboard *clipboard,
5528 GtkSelectionData *selection_data,
5532 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5533 GString *string = NULL;
5537 case SELECT_FMT_TEXT:
5538 string = range_to_text (sheet);
5540 case SELECT_FMT_HTML:
5541 string = range_to_html (sheet);
5544 g_assert_not_reached ();
5547 gtk_selection_data_set (selection_data, selection_data->target,
5549 (const guchar *) string->str, string->len);
5550 g_string_free (string, TRUE);
5554 primary_clear_cb (GtkClipboard *clipboard,
5557 PsppireSheet *sheet = PSPPIRE_SHEET (data);
5558 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
5561 psppire_sheet_real_unselect_range (sheet, NULL);
5565 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
5567 static const GtkTargetEntry targets[] = {
5568 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
5569 { "STRING", 0, SELECT_FMT_TEXT },
5570 { "TEXT", 0, SELECT_FMT_TEXT },
5571 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
5572 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
5573 { "text/plain", 0, SELECT_FMT_TEXT },
5574 { "text/html", 0, SELECT_FMT_HTML }
5577 GtkClipboard *clipboard;
5579 if (!GTK_WIDGET_REALIZED (sheet))
5582 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
5583 GDK_SELECTION_PRIMARY);
5585 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
5587 if (!gtk_clipboard_set_with_owner (clipboard, targets,
5588 G_N_ELEMENTS (targets),
5589 primary_get_cb, primary_clear_cb,
5591 primary_clear_cb (clipboard, sheet);
5595 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
5596 gtk_clipboard_clear (clipboard);