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,
84 /* This flag is set when the user is actually in the process
85 of making a selection - ie while the mouse button is
88 PSPPIRE_SHEET_IN_SELECTION = 1 << 4
91 #define PSPPIRE_SHEET_FLAGS(sheet) (PSPPIRE_SHEET (sheet)->flags)
92 #define PSPPIRE_SHEET_SET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) |= (flag))
93 #define PSPPIRE_SHEET_UNSET_FLAGS(sheet,flag) (PSPPIRE_SHEET_FLAGS (sheet) &= ~ (flag))
95 #define PSPPIRE_SHEET_IN_XDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_XDRAG)
96 #define PSPPIRE_SHEET_IN_YDRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_YDRAG)
97 #define PSPPIRE_SHEET_IN_DRAG(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_DRAG)
98 #define PSPPIRE_SHEET_IN_SELECTION(sheet) (PSPPIRE_SHEET_FLAGS (sheet) & PSPPIRE_SHEET_IN_SELECTION)
100 #define CELL_SPACING 1
102 #define TIMEOUT_HOVER 300
103 #define COLUMN_MIN_WIDTH 10
104 #define COLUMN_TITLES_HEIGHT 4
105 #define DEFAULT_COLUMN_WIDTH 80
106 #define DEFAULT_ROW_HEIGHT 25
108 static void set_entry_widget_font (PsppireSheet *sheet);
110 static void psppire_sheet_update_primary_selection (PsppireSheet *sheet);
111 static void draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
112 static void draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint n);
113 static void redraw_range (PsppireSheet *sheet, PsppireSheetRange *range);
116 static void set_row_height (PsppireSheet *sheet,
120 static void destroy_hover_window (PsppireSheetHoverTitle *);
121 static PsppireSheetHoverTitle *create_hover_window (void);
124 dispose_string (const PsppireSheet *sheet, gchar *text)
126 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
131 if (psppire_sheet_model_free_strings (model))
136 /* FIXME: Why bother with these two ? */
138 /* returns the column index from a pixel location */
140 column_from_xpixel (const PsppireSheet *sheet, gint pixel)
142 return psppire_axis_unit_at_pixel (sheet->haxis, pixel);
146 row_from_ypixel (const PsppireSheet *sheet, gint pixel)
148 return psppire_axis_unit_at_pixel (sheet->vaxis, pixel);
152 /* Return the lowest row number which is wholly or partially on
153 the visible range of the sheet */
155 min_visible_row (const PsppireSheet *sheet)
157 return row_from_ypixel (sheet, sheet->vadjustment->value);
161 min_fully_visible_row (const PsppireSheet *sheet)
163 glong row = min_visible_row (sheet);
165 if ( psppire_axis_start_pixel (sheet->vaxis, row) < sheet->vadjustment->value)
172 max_visible_row (const PsppireSheet *sheet)
174 return row_from_ypixel (sheet, sheet->vadjustment->value + sheet->vadjustment->page_size);
179 max_fully_visible_row (const PsppireSheet *sheet)
181 glong row = max_visible_row (sheet);
183 if ( psppire_axis_start_pixel (sheet->vaxis, row)
185 psppire_axis_unit_size (sheet->vaxis, row)
186 > sheet->vadjustment->value)
193 /* Returns the lowest column number which is wholly or partially
196 min_visible_column (const PsppireSheet *sheet)
198 return column_from_xpixel (sheet, sheet->hadjustment->value);
202 min_fully_visible_column (const PsppireSheet *sheet)
204 glong col = min_visible_column (sheet);
206 if ( psppire_axis_start_pixel (sheet->haxis, col) < sheet->hadjustment->value)
213 /* Returns the highest column number which is wholly or partially
216 max_visible_column (const PsppireSheet *sheet)
218 return column_from_xpixel (sheet, sheet->hadjustment->value + sheet->hadjustment->page_size);
222 max_fully_visible_column (const PsppireSheet *sheet)
224 glong col = max_visible_column (sheet);
226 if ( psppire_axis_start_pixel (sheet->haxis, col)
228 psppire_axis_unit_size (sheet->haxis, col)
229 > sheet->hadjustment->value)
237 /* The size of the region (in pixels) around the row/column boundaries
238 where the height/width may be grabbed to change size */
242 on_column_boundary (const PsppireSheet *sheet, gint x, gint *column)
247 x += sheet->hadjustment->value;
252 col = column_from_xpixel (sheet, x);
254 pixel = x - DRAG_WIDTH / 2;
258 if ( column_from_xpixel (sheet, pixel) < col )
264 if ( column_from_xpixel (sheet, x + DRAG_WIDTH / 2) > col )
274 on_row_boundary (const PsppireSheet *sheet, gint y, gint *row)
279 y += sheet->vadjustment->value;
284 r = row_from_ypixel (sheet, y);
286 pixel = y - DRAG_WIDTH / 2;
290 if ( row_from_ypixel (sheet, pixel) < r )
296 if ( row_from_ypixel (sheet, y + DRAG_WIDTH / 2) > r )
306 static inline gboolean
307 POSSIBLE_DRAG (const PsppireSheet *sheet, gint x, gint y,
308 gint *drag_row, gint *drag_column)
312 /* Can't drag if nothing is selected */
313 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
314 sheet->range.col0 < 0 || sheet->range.coli < 0 )
317 *drag_column = column_from_xpixel (sheet, x);
318 *drag_row = row_from_ypixel (sheet, y);
320 if (x >= psppire_axis_start_pixel (sheet->haxis, sheet->range.col0) - DRAG_WIDTH / 2 &&
321 x <= psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
322 psppire_axis_unit_size (sheet->haxis, sheet->range.coli) + DRAG_WIDTH / 2)
324 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0);
325 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
327 *drag_row = sheet->range.row0;
330 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
331 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
332 if (y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2)
334 *drag_row = sheet->range.rowi;
339 if (y >= psppire_axis_start_pixel (sheet->vaxis, sheet->range.row0) - DRAG_WIDTH / 2 &&
340 y <= psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
341 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi) + DRAG_WIDTH / 2)
343 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.col0);
344 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
346 *drag_column = sheet->range.col0;
349 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli) +
350 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
351 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2)
353 *drag_column = sheet->range.coli;
361 static inline gboolean
362 POSSIBLE_RESIZE (const PsppireSheet *sheet, gint x, gint y,
363 gint *drag_row, gint *drag_column)
367 /* Can't drag if nothing is selected */
368 if ( sheet->range.row0 < 0 || sheet->range.rowi < 0 ||
369 sheet->range.col0 < 0 || sheet->range.coli < 0 )
372 xdrag = psppire_axis_start_pixel (sheet->haxis, sheet->range.coli)+
373 psppire_axis_unit_size (sheet->haxis, sheet->range.coli);
375 ydrag = psppire_axis_start_pixel (sheet->vaxis, sheet->range.rowi) +
376 psppire_axis_unit_size (sheet->vaxis, sheet->range.rowi);
378 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED)
379 ydrag = psppire_axis_start_pixel (sheet->vaxis, min_visible_row (sheet));
381 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED)
382 xdrag = psppire_axis_start_pixel (sheet->haxis, min_visible_column (sheet));
384 *drag_column = column_from_xpixel (sheet, x);
385 *drag_row = row_from_ypixel (sheet, y);
387 if (x >= xdrag - DRAG_WIDTH / 2 && x <= xdrag + DRAG_WIDTH / 2 &&
388 y >= ydrag - DRAG_WIDTH / 2 && y <= ydrag + DRAG_WIDTH / 2) return TRUE;
395 rectangle_from_range (PsppireSheet *sheet, const PsppireSheetRange *input,
398 PsppireSheetRange range = *input;
400 if ( range.row0 == -1 ) range.row0 = min_visible_row (sheet);
401 if ( range.rowi == -1 ) range.rowi = max_visible_row (sheet);
402 if ( range.col0 == -1 ) range.col0 = min_visible_column (sheet);
403 if ( range.coli == -1 ) range.coli = max_visible_column (sheet);
405 r->x = psppire_axis_start_pixel (sheet->haxis, range.col0);
406 r->x -= round (sheet->hadjustment->value);
408 r->y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
409 r->y -= round (sheet->vadjustment->value);
411 r->width = psppire_axis_start_pixel (sheet->haxis, range.coli) -
412 psppire_axis_start_pixel (sheet->haxis, range.col0) +
413 psppire_axis_unit_size (sheet->haxis, range.coli);
415 r->height = psppire_axis_start_pixel (sheet->vaxis, range.rowi) -
416 psppire_axis_start_pixel (sheet->vaxis, range.row0) +
417 psppire_axis_unit_size (sheet->vaxis, range.rowi);
419 if ( sheet->column_titles_visible)
421 r->y += sheet->column_title_area.height;
424 if ( sheet->row_titles_visible)
426 r->x += sheet->row_title_area.width;
433 rectangle_from_cell (PsppireSheet *sheet, gint row, gint col,
436 PsppireSheetRange range;
437 g_return_val_if_fail (row >= 0, FALSE);
438 g_return_val_if_fail (col >= 0, FALSE);
440 range.row0 = range.rowi = row;
441 range.col0 = range.coli = col;
443 return rectangle_from_range (sheet, &range, r);
447 static void psppire_sheet_class_init (PsppireSheetClass *klass);
448 static void psppire_sheet_init (PsppireSheet *sheet);
449 static void psppire_sheet_dispose (GObject *object);
450 static void psppire_sheet_finalize (GObject *object);
451 static void psppire_sheet_style_set (GtkWidget *widget,
452 GtkStyle *previous_style);
453 static void psppire_sheet_realize (GtkWidget *widget);
454 static void psppire_sheet_unrealize (GtkWidget *widget);
455 static void psppire_sheet_map (GtkWidget *widget);
456 static void psppire_sheet_unmap (GtkWidget *widget);
457 static gint psppire_sheet_expose (GtkWidget *widget,
458 GdkEventExpose *event);
460 static void psppire_sheet_forall (GtkContainer *container,
461 gboolean include_internals,
462 GtkCallback callback,
463 gpointer callback_data);
465 static gboolean psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
466 GtkAdjustment *hadjustment,
467 GtkAdjustment *vadjustment);
469 static gint psppire_sheet_button_press (GtkWidget *widget,
470 GdkEventButton *event);
471 static gint psppire_sheet_button_release (GtkWidget *widget,
472 GdkEventButton *event);
473 static gint psppire_sheet_motion (GtkWidget *widget,
474 GdkEventMotion *event);
475 static gboolean psppire_sheet_crossing_notify (GtkWidget *widget,
476 GdkEventCrossing *event);
477 static gint psppire_sheet_entry_key_press (GtkWidget *widget,
479 static gboolean psppire_sheet_key_press (GtkWidget *widget,
481 static void psppire_sheet_size_request (GtkWidget *widget,
482 GtkRequisition *requisition);
483 static void psppire_sheet_size_allocate (GtkWidget *widget,
484 GtkAllocation *allocation);
486 static gboolean psppire_sheet_focus_in (GtkWidget *widget,
487 GdkEventFocus *event);
491 static gboolean psppire_sheet_range_isvisible (const PsppireSheet *sheet,
492 const PsppireSheetRange *range);
493 static gboolean psppire_sheet_cell_isvisible (PsppireSheet *sheet,
494 gint row, gint column);
495 /* Drawing Routines */
498 static void psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint column);
501 /* draw visible part of range. */
502 static void draw_sheet_region (PsppireSheet *sheet, GdkRegion *region);
506 static void psppire_sheet_draw_border (PsppireSheet *sheet,
507 PsppireSheetRange range);
509 /* Active Cell handling */
511 static void psppire_sheet_hide_entry_widget (PsppireSheet *sheet);
512 static void change_active_cell (PsppireSheet *sheet, gint row, gint col);
513 static gboolean psppire_sheet_draw_active_cell (PsppireSheet *sheet);
514 static void psppire_sheet_show_entry_widget (PsppireSheet *sheet);
515 static gboolean psppire_sheet_click_cell (PsppireSheet *sheet,
522 static void adjust_scrollbars (PsppireSheet *sheet);
523 static void vadjustment_value_changed (GtkAdjustment *adjustment,
525 static void hadjustment_value_changed (GtkAdjustment *adjustment,
529 static void draw_xor_vline (PsppireSheet *sheet);
530 static void draw_xor_hline (PsppireSheet *sheet);
531 static void draw_xor_rectangle (PsppireSheet *sheet,
532 PsppireSheetRange range);
536 static void create_global_button (PsppireSheet *sheet);
537 static void global_button_clicked (GtkWidget *widget,
541 static void create_sheet_entry (PsppireSheet *sheet);
542 static void psppire_sheet_size_allocate_entry (PsppireSheet *sheet);
544 /* Sheet button gadgets */
546 static void draw_column_title_buttons (PsppireSheet *sheet);
547 static void draw_row_title_buttons (PsppireSheet *sheet);
550 static void size_allocate_global_button (PsppireSheet *sheet);
551 static void psppire_sheet_button_size_request (PsppireSheet *sheet,
552 const PsppireSheetButton *button,
553 GtkRequisition *requisition);
555 static void psppire_sheet_real_cell_clear (PsppireSheet *sheet,
577 static GtkContainerClass *parent_class = NULL;
578 static guint sheet_signals[LAST_SIGNAL] = { 0 };
582 psppire_sheet_get_type ()
584 static GType sheet_type = 0;
588 static const GTypeInfo sheet_info =
590 sizeof (PsppireSheetClass),
593 (GClassInitFunc) psppire_sheet_class_init,
596 sizeof (PsppireSheet),
598 (GInstanceInitFunc) psppire_sheet_init,
603 g_type_register_static (GTK_TYPE_BIN, "PsppireSheet",
611 static PsppireSheetRange*
612 psppire_sheet_range_copy (const PsppireSheetRange *range)
614 PsppireSheetRange *new_range;
616 g_return_val_if_fail (range != NULL, NULL);
618 new_range = g_new (PsppireSheetRange, 1);
626 psppire_sheet_range_free (PsppireSheetRange *range)
628 g_return_if_fail (range != NULL);
634 psppire_sheet_range_get_type (void)
636 static GType sheet_range_type = 0;
638 if (!sheet_range_type)
641 g_boxed_type_register_static ("PsppireSheetRange",
642 (GBoxedCopyFunc) psppire_sheet_range_copy,
643 (GBoxedFreeFunc) psppire_sheet_range_free);
646 return sheet_range_type;
649 static PsppireSheetCell*
650 psppire_sheet_cell_copy (const PsppireSheetCell *cell)
652 PsppireSheetCell *new_cell;
654 g_return_val_if_fail (cell != NULL, NULL);
656 new_cell = g_new (PsppireSheetCell, 1);
664 psppire_sheet_cell_free (PsppireSheetCell *cell)
666 g_return_if_fail (cell != NULL);
672 psppire_sheet_cell_get_type (void)
674 static GType sheet_cell_type = 0;
676 if (!sheet_cell_type)
679 g_boxed_type_register_static ("PsppireSheetCell",
680 (GBoxedCopyFunc) psppire_sheet_cell_copy,
681 (GBoxedFreeFunc) psppire_sheet_cell_free);
684 return sheet_cell_type;
699 resize_column (PsppireSheet *sheet, gint unit, glong size)
701 PsppireSheetRange range;
703 range.coli = max_visible_column (sheet);
704 range.row0 = min_visible_row (sheet);
705 range.rowi = max_visible_row (sheet);
707 redraw_range (sheet, &range);
709 draw_column_title_buttons_range (sheet, range.col0, range.coli);
714 psppire_sheet_set_horizontal_axis (PsppireSheet *sheet, PsppireAxis *a)
717 g_object_unref (sheet->haxis);
720 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_column), sheet);
723 g_object_ref (sheet->haxis);
727 resize_row (PsppireSheet *sheet, gint unit, glong size)
729 PsppireSheetRange range;
730 range.col0 = min_visible_column (sheet);
731 range.coli = max_visible_column (sheet);
733 range.rowi = max_visible_row (sheet);
735 redraw_range (sheet, &range);
737 draw_row_title_buttons_range (sheet, range.row0, range.rowi);
741 psppire_sheet_set_vertical_axis (PsppireSheet *sheet, PsppireAxis *a)
744 g_object_unref (sheet->vaxis);
748 g_signal_connect_swapped (a, "resize-unit", G_CALLBACK (resize_row), sheet);
751 g_object_ref (sheet->vaxis);
754 static const GtkBorder default_cell_padding = {3, 3, 3, 3};
757 psppire_sheet_set_property (GObject *object,
763 PsppireSheet *sheet = PSPPIRE_SHEET (object);
767 case PROP_CELL_PADDING:
768 if ( sheet->cell_padding)
769 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
771 sheet->cell_padding = g_value_dup_boxed (value);
773 if (NULL == sheet->cell_padding)
774 sheet->cell_padding = g_boxed_copy (GTK_TYPE_BORDER,
775 &default_cell_padding);
778 g_object_set (sheet->vaxis, "padding",
779 sheet->cell_padding->top + sheet->cell_padding->bottom,
783 g_object_set (sheet->haxis, "padding",
784 sheet->cell_padding->left + sheet->cell_padding->right,
788 psppire_sheet_set_vertical_axis (sheet, g_value_get_pointer (value));
789 g_object_set (sheet->vaxis, "padding",
790 sheet->cell_padding->top + sheet->cell_padding->bottom,
794 psppire_sheet_set_horizontal_axis (sheet, g_value_get_pointer (value));
795 g_object_set (sheet->haxis, "padding",
796 sheet->cell_padding->left + sheet->cell_padding->right,
800 psppire_sheet_set_model (sheet, g_value_get_pointer (value));
803 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
809 psppire_sheet_get_property (GObject *object,
814 PsppireSheet *sheet = PSPPIRE_SHEET (object);
818 case PROP_CELL_PADDING:
819 g_value_set_boxed (value, sheet->cell_padding);
822 g_value_set_pointer (value, sheet->vaxis);
825 g_value_set_pointer (value, sheet->haxis);
828 g_value_set_pointer (value, sheet->model);
831 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
838 psppire_sheet_class_init (PsppireSheetClass *klass)
840 GObjectClass *object_class = G_OBJECT_CLASS (klass);
842 GParamSpec *haxis_spec ;
843 GParamSpec *vaxis_spec ;
844 GParamSpec *model_spec ;
845 GParamSpec *cell_padding_spec ;
847 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
848 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
850 parent_class = g_type_class_peek_parent (klass);
853 * PsppireSheet::select-row
854 * @sheet: the sheet widget that emitted the signal
855 * @row: the newly selected row index, or -1 if no row is selected.
857 * A row has been selected.
859 sheet_signals[SELECT_ROW] =
860 g_signal_new ("select-row",
861 G_TYPE_FROM_CLASS (object_class),
863 offsetof (PsppireSheetClass, select_row),
865 g_cclosure_marshal_VOID__INT,
872 * PsppireSheet::select - column
873 * @sheet: the sheet widget that emitted the signal
874 * @column: the newly selected column index, or -1 if no column is selected.
876 * A column has been selected.
878 sheet_signals[SELECT_COLUMN] =
879 g_signal_new ("select-column",
880 G_TYPE_FROM_CLASS (object_class),
882 offsetof (PsppireSheetClass, select_column),
884 g_cclosure_marshal_VOID__INT,
891 * PsppireSheet::double-click-row
892 * @sheet: the sheet widget that emitted the signal
893 * @row: the row that was double clicked.
895 * A row's title button has been double clicked
897 sheet_signals[DOUBLE_CLICK_ROW] =
898 g_signal_new ("double-click-row",
899 G_TYPE_FROM_CLASS (object_class),
903 g_cclosure_marshal_VOID__INT,
910 * PsppireSheet::double-click-column
911 * @sheet: the sheet widget that emitted the signal
912 * @column: the column that was double clicked.
914 * A column's title button has been double clicked
916 sheet_signals[DOUBLE_CLICK_COLUMN] =
917 g_signal_new ("double-click-column",
918 G_TYPE_FROM_CLASS (object_class),
922 g_cclosure_marshal_VOID__INT,
929 * PsppireSheet::button-event-column
930 * @sheet: the sheet widget that emitted the signal
931 * @column: the column on which the event occured.
933 * A button event occured on a column title button
935 sheet_signals[BUTTON_EVENT_COLUMN] =
936 g_signal_new ("button-event-column",
937 G_TYPE_FROM_CLASS (object_class),
941 psppire_marshal_VOID__INT_POINTER,
950 * PsppireSheet::button-event-row
951 * @sheet: the sheet widget that emitted the signal
952 * @column: the column on which the event occured.
954 * A button event occured on a row title button
956 sheet_signals[BUTTON_EVENT_ROW] =
957 g_signal_new ("button-event-row",
958 G_TYPE_FROM_CLASS (object_class),
962 psppire_marshal_VOID__INT_POINTER,
970 sheet_signals[SELECT_RANGE] =
971 g_signal_new ("select-range",
972 G_TYPE_FROM_CLASS (object_class),
974 offsetof (PsppireSheetClass, select_range),
976 g_cclosure_marshal_VOID__BOXED,
979 PSPPIRE_TYPE_SHEET_RANGE);
982 sheet_signals[RESIZE_RANGE] =
983 g_signal_new ("resize-range",
984 G_TYPE_FROM_CLASS (object_class),
986 offsetof (PsppireSheetClass, resize_range),
988 psppire_marshal_VOID__BOXED_BOXED,
991 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
994 sheet_signals[MOVE_RANGE] =
995 g_signal_new ("move-range",
996 G_TYPE_FROM_CLASS (object_class),
998 offsetof (PsppireSheetClass, move_range),
1000 psppire_marshal_VOID__BOXED_BOXED,
1003 PSPPIRE_TYPE_SHEET_RANGE, PSPPIRE_TYPE_SHEET_RANGE
1006 sheet_signals[TRAVERSE] =
1007 g_signal_new ("traverse",
1008 G_TYPE_FROM_CLASS (object_class),
1010 offsetof (PsppireSheetClass, traverse),
1012 psppire_marshal_BOOLEAN__BOXED_POINTER,
1014 PSPPIRE_TYPE_SHEET_CELL,
1018 sheet_signals[ACTIVATE] =
1019 g_signal_new ("activate",
1020 G_TYPE_FROM_CLASS (object_class),
1022 offsetof (PsppireSheetClass, activate),
1024 psppire_marshal_VOID__INT_INT_INT_INT,
1026 G_TYPE_INT, G_TYPE_INT,
1027 G_TYPE_INT, G_TYPE_INT);
1029 widget_class->set_scroll_adjustments_signal =
1030 g_signal_new ("set-scroll-adjustments",
1031 G_TYPE_FROM_CLASS (object_class),
1033 offsetof (PsppireSheetClass, set_scroll_adjustments),
1035 psppire_marshal_VOID__OBJECT_OBJECT,
1036 G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
1039 container_class->add = NULL;
1040 container_class->remove = NULL;
1041 container_class->forall = psppire_sheet_forall;
1042 container_class->set_focus_child = NULL;
1044 object_class->dispose = psppire_sheet_dispose;
1045 object_class->finalize = psppire_sheet_finalize;
1048 g_param_spec_boxed ("cell-padding",
1050 "The space between a cell's contents and its border",
1052 G_PARAM_CONSTRUCT | G_PARAM_READABLE | G_PARAM_WRITABLE);
1055 g_param_spec_pointer ("vertical-axis",
1057 "A pointer to the PsppireAxis object for the rows",
1058 G_PARAM_READABLE | G_PARAM_WRITABLE );
1061 g_param_spec_pointer ("horizontal-axis",
1063 "A pointer to the PsppireAxis object for the columns",
1064 G_PARAM_READABLE | G_PARAM_WRITABLE );
1067 g_param_spec_pointer ("model",
1069 "A pointer to the data model",
1070 G_PARAM_READABLE | G_PARAM_WRITABLE );
1073 object_class->set_property = psppire_sheet_set_property;
1074 object_class->get_property = psppire_sheet_get_property;
1076 g_object_class_install_property (object_class,
1080 g_object_class_install_property (object_class,
1084 g_object_class_install_property (object_class,
1088 g_object_class_install_property (object_class,
1093 widget_class->realize = psppire_sheet_realize;
1094 widget_class->unrealize = psppire_sheet_unrealize;
1095 widget_class->map = psppire_sheet_map;
1096 widget_class->unmap = psppire_sheet_unmap;
1097 widget_class->style_set = psppire_sheet_style_set;
1098 widget_class->button_press_event = psppire_sheet_button_press;
1099 widget_class->button_release_event = psppire_sheet_button_release;
1100 widget_class->motion_notify_event = psppire_sheet_motion;
1101 widget_class->enter_notify_event = psppire_sheet_crossing_notify;
1102 widget_class->leave_notify_event = psppire_sheet_crossing_notify;
1103 widget_class->key_press_event = psppire_sheet_key_press;
1104 widget_class->expose_event = psppire_sheet_expose;
1105 widget_class->size_request = psppire_sheet_size_request;
1106 widget_class->size_allocate = psppire_sheet_size_allocate;
1107 widget_class->focus_in_event = psppire_sheet_focus_in;
1108 widget_class->focus_out_event = NULL;
1110 klass->set_scroll_adjustments = psppire_sheet_set_scroll_adjustments;
1111 klass->select_row = NULL;
1112 klass->select_column = NULL;
1113 klass->select_range = NULL;
1114 klass->resize_range = NULL;
1115 klass->move_range = NULL;
1116 klass->traverse = NULL;
1117 klass->activate = NULL;
1118 klass->changed = NULL;
1122 psppire_sheet_init (PsppireSheet *sheet)
1124 sheet->model = NULL;
1125 sheet->haxis = NULL;
1126 sheet->vaxis = NULL;
1129 sheet->select_status = PSPPIRE_SHEET_NORMAL;
1131 GTK_WIDGET_UNSET_FLAGS (sheet, GTK_NO_WINDOW);
1132 GTK_WIDGET_SET_FLAGS (sheet, GTK_CAN_FOCUS);
1134 sheet->column_title_window = NULL;
1135 sheet->column_title_area.x = 0;
1136 sheet->column_title_area.y = 0;
1137 sheet->column_title_area.width = 0;
1138 sheet->column_title_area.height = DEFAULT_ROW_HEIGHT;
1140 sheet->row_title_window = NULL;
1141 sheet->row_title_area.x = 0;
1142 sheet->row_title_area.y = 0;
1143 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1144 sheet->row_title_area.height = 0;
1147 sheet->active_cell.row = 0;
1148 sheet->active_cell.col = 0;
1150 sheet->range.row0 = 0;
1151 sheet->range.rowi = 0;
1152 sheet->range.col0 = 0;
1153 sheet->range.coli = 0;
1155 sheet->sheet_window = NULL;
1156 sheet->entry_widget = NULL;
1157 sheet->button = NULL;
1159 sheet->hadjustment = NULL;
1160 sheet->vadjustment = NULL;
1162 sheet->cursor_drag = NULL;
1164 sheet->xor_gc = NULL;
1165 sheet->fg_gc = NULL;
1166 sheet->bg_gc = NULL;
1169 sheet->show_grid = TRUE;
1171 sheet->motion_timer = 0;
1173 sheet->row_titles_visible = TRUE;
1174 sheet->row_title_area.width = DEFAULT_COLUMN_WIDTH;
1176 sheet->column_titles_visible = TRUE;
1179 /* create sheet entry */
1180 sheet->entry_type = GTK_TYPE_ENTRY;
1181 create_sheet_entry (sheet);
1183 /* create global selection button */
1184 create_global_button (sheet);
1188 /* Cause RANGE to be redrawn. If RANGE is null, then the
1189 entire visible range will be redrawn.
1192 redraw_range (PsppireSheet *sheet, PsppireSheetRange *range)
1196 if ( ! GTK_WIDGET_REALIZED (sheet))
1199 if ( NULL != range )
1200 rectangle_from_range (sheet, range, &rect);
1203 GdkRegion *r = gdk_drawable_get_visible_region (sheet->sheet_window);
1204 gdk_region_get_clipbox (r, &rect);
1206 if ( sheet->column_titles_visible)
1208 rect.y += sheet->column_title_area.height;
1209 rect.height -= sheet->column_title_area.height;
1212 if ( sheet->row_titles_visible)
1214 rect.x += sheet->row_title_area.width;
1215 rect.width -= sheet->row_title_area.width;
1219 gdk_window_invalidate_rect (sheet->sheet_window, &rect, FALSE);
1223 /* Callback which occurs whenever columns are inserted / deleted in the model */
1225 columns_inserted_deleted_callback (PsppireSheetModel *model, gint first_column,
1229 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1231 PsppireSheetRange range;
1232 gint model_columns = psppire_sheet_model_get_column_count (model);
1235 /* Need to update all the columns starting from the first column and onwards.
1236 * Previous column are unchanged, so don't need to be updated.
1238 range.col0 = first_column;
1240 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1241 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1243 adjust_scrollbars (sheet);
1245 if (sheet->active_cell.col >= model_columns)
1246 change_active_cell (sheet, sheet->active_cell.row, model_columns - 1);
1248 draw_column_title_buttons_range (sheet,
1249 first_column, max_visible_column (sheet));
1252 redraw_range (sheet, &range);
1258 /* Callback which occurs whenever rows are inserted / deleted in the model */
1260 rows_inserted_deleted_callback (PsppireSheetModel *model, gint first_row,
1261 gint n_rows, gpointer data)
1263 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1265 PsppireSheetRange range;
1267 gint model_rows = psppire_sheet_model_get_row_count (model);
1269 /* Need to update all the rows starting from the first row and onwards.
1270 * Previous rows are unchanged, so don't need to be updated.
1272 range.row0 = first_row;
1274 range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
1275 range.coli = psppire_axis_unit_count (sheet->haxis) - 1;
1277 adjust_scrollbars (sheet);
1279 if (sheet->active_cell.row >= model_rows)
1280 change_active_cell (sheet, model_rows - 1, sheet->active_cell.col);
1282 draw_row_title_buttons_range (sheet, first_row, max_visible_row (sheet));
1284 redraw_range (sheet, &range);
1288 If row0 or rowi are negative, then all rows will be updated.
1289 If col0 or coli are negative, then all columns will be updated.
1292 range_update_callback (PsppireSheetModel *m, gint row0, gint col0,
1293 gint rowi, gint coli, gpointer data)
1295 PsppireSheet *sheet = PSPPIRE_SHEET (data);
1297 PsppireSheetRange range;
1304 if ( !GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1307 if ( ( row0 < 0 && col0 < 0 ) || ( rowi < 0 && coli < 0 ) )
1309 redraw_range (sheet, NULL);
1310 adjust_scrollbars (sheet);
1312 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
1313 max_visible_row (sheet));
1315 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
1316 max_visible_column (sheet));
1320 else if ( row0 < 0 || rowi < 0 )
1322 range.row0 = min_visible_row (sheet);
1323 range.rowi = max_visible_row (sheet);
1325 else if ( col0 < 0 || coli < 0 )
1327 range.col0 = min_visible_column (sheet);
1328 range.coli = max_visible_column (sheet);
1331 redraw_range (sheet, &range);
1336 * psppire_sheet_new:
1337 * @rows: initial number of rows
1338 * @columns: initial number of columns
1339 * @title: sheet title
1340 * @model: the model to use for the sheet data
1342 * Creates a new sheet widget with the given number of rows and columns.
1344 * Returns: the new sheet widget
1347 psppire_sheet_new (PsppireSheetModel *model)
1349 GtkWidget *widget = g_object_new (PSPPIRE_TYPE_SHEET,
1357 * psppire_sheet_set_model
1358 * @sheet: the sheet to set the model for
1359 * @model: the model to use for the sheet data
1361 * Sets the model for a PsppireSheet
1365 psppire_sheet_set_model (PsppireSheet *sheet, PsppireSheetModel *model)
1367 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1369 if (sheet->model ) g_object_unref (sheet->model);
1371 sheet->model = model;
1375 g_object_ref (model);
1377 sheet->update_handler_id = g_signal_connect (model, "range_changed",
1378 G_CALLBACK (range_update_callback),
1381 g_signal_connect (model, "rows_inserted",
1382 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1384 g_signal_connect (model, "rows_deleted",
1385 G_CALLBACK (rows_inserted_deleted_callback), sheet);
1387 g_signal_connect (model, "columns_inserted",
1388 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1390 g_signal_connect (model, "columns_deleted",
1391 G_CALLBACK (columns_inserted_deleted_callback), sheet);
1397 psppire_sheet_change_entry (PsppireSheet *sheet, GtkType entry_type)
1399 g_return_if_fail (sheet != NULL);
1400 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1402 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1403 psppire_sheet_hide_entry_widget (sheet);
1405 sheet->entry_type = entry_type;
1407 create_sheet_entry (sheet);
1409 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
1410 psppire_sheet_show_entry_widget (sheet);
1414 psppire_sheet_show_grid (PsppireSheet *sheet, gboolean show)
1416 g_return_if_fail (sheet != NULL);
1417 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
1419 if (show == sheet->show_grid) return;
1421 sheet->show_grid = show;
1423 redraw_range (sheet, NULL);
1427 psppire_sheet_grid_visible (PsppireSheet *sheet)
1429 g_return_val_if_fail (sheet != NULL, 0);
1430 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1432 return sheet->show_grid;
1436 psppire_sheet_get_columns_count (PsppireSheet *sheet)
1438 g_return_val_if_fail (sheet != NULL, 0);
1439 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
1441 return psppire_axis_unit_count (sheet->haxis);
1444 static void set_column_width (PsppireSheet *sheet,
1450 psppire_sheet_show_column_titles (PsppireSheet *sheet)
1452 if (sheet->column_titles_visible) return;
1454 sheet->column_titles_visible = TRUE;
1456 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1459 gdk_window_show (sheet->column_title_window);
1460 gdk_window_move_resize (sheet->column_title_window,
1461 sheet->column_title_area.x,
1462 sheet->column_title_area.y,
1463 sheet->column_title_area.width,
1464 sheet->column_title_area.height);
1466 adjust_scrollbars (sheet);
1468 if (sheet->vadjustment)
1469 g_signal_emit_by_name (sheet->vadjustment,
1472 size_allocate_global_button (sheet);
1474 if ( sheet->row_titles_visible)
1475 gtk_widget_show (sheet->button);
1480 psppire_sheet_show_row_titles (PsppireSheet *sheet)
1482 if (sheet->row_titles_visible) return;
1484 sheet->row_titles_visible = TRUE;
1487 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1489 gdk_window_show (sheet->row_title_window);
1490 gdk_window_move_resize (sheet->row_title_window,
1491 sheet->row_title_area.x,
1492 sheet->row_title_area.y,
1493 sheet->row_title_area.width,
1494 sheet->row_title_area.height);
1496 adjust_scrollbars (sheet);
1499 if (sheet->hadjustment)
1500 g_signal_emit_by_name (sheet->hadjustment,
1503 size_allocate_global_button (sheet);
1505 if ( sheet->column_titles_visible)
1506 gtk_widget_show (sheet->button);
1510 psppire_sheet_hide_column_titles (PsppireSheet *sheet)
1512 if (!sheet->column_titles_visible) return;
1514 sheet->column_titles_visible = FALSE;
1516 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1518 if (sheet->column_title_window)
1519 gdk_window_hide (sheet->column_title_window);
1521 gtk_widget_hide (sheet->button);
1523 adjust_scrollbars (sheet);
1526 if (sheet->vadjustment)
1527 g_signal_emit_by_name (sheet->vadjustment,
1532 psppire_sheet_hide_row_titles (PsppireSheet *sheet)
1534 if (!sheet->row_titles_visible) return;
1536 sheet->row_titles_visible = FALSE;
1538 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
1540 if (sheet->row_title_window)
1541 gdk_window_hide (sheet->row_title_window);
1543 gtk_widget_hide (sheet->button);
1545 adjust_scrollbars (sheet);
1548 if (sheet->hadjustment)
1549 g_signal_emit_by_name (sheet->hadjustment,
1554 /* Scroll the sheet so that the cell ROW, COLUMN is visible.
1555 If {ROW,COL}_ALIGN is zero, then the cell will be placed
1556 at the {top,left} of the sheet. If it's 1, then it'll
1557 be placed at the {bottom,right}.
1558 ROW or COL may be -1, in which case scrolling in that dimension
1562 psppire_sheet_moveto (PsppireSheet *sheet,
1570 g_return_if_fail (row_align >= 0);
1571 g_return_if_fail (col_align >= 0);
1573 g_return_if_fail (row_align <= 1);
1574 g_return_if_fail (col_align <= 1);
1576 g_return_if_fail (col <
1577 psppire_axis_unit_count (sheet->haxis));
1578 g_return_if_fail (row <
1579 psppire_axis_unit_count (sheet->vaxis));
1581 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
1586 gint y = psppire_axis_start_pixel (sheet->vaxis, row);
1588 gtk_adjustment_set_value (sheet->vadjustment, y - height * row_align);
1594 gint x = psppire_axis_start_pixel (sheet->haxis, col);
1596 gtk_adjustment_set_value (sheet->hadjustment, x - width * col_align);
1604 psppire_sheet_range_isvisible (const PsppireSheet *sheet,
1605 const PsppireSheetRange *range)
1607 g_return_val_if_fail (sheet != NULL, FALSE);
1609 if (range->row0 < 0 || range->row0 >= psppire_axis_unit_count (sheet->vaxis))
1612 if (range->rowi < 0 || range->rowi >= psppire_axis_unit_count (sheet->vaxis))
1615 if (range->col0 < 0 || range->col0 >= psppire_axis_unit_count (sheet->haxis))
1618 if (range->coli < 0 || range->coli >= psppire_axis_unit_count (sheet->haxis))
1621 if (range->rowi < min_visible_row (sheet))
1624 if (range->row0 > max_visible_row (sheet))
1627 if (range->coli < min_visible_column (sheet))
1630 if (range->col0 > max_visible_column (sheet))
1637 psppire_sheet_cell_isvisible (PsppireSheet *sheet,
1638 gint row, gint column)
1640 PsppireSheetRange range;
1643 range.col0 = column;
1645 range.coli = column;
1647 return psppire_sheet_range_isvisible (sheet, &range);
1651 psppire_sheet_get_visible_range (PsppireSheet *sheet, PsppireSheetRange *range)
1653 g_return_if_fail (sheet != NULL);
1654 g_return_if_fail (PSPPIRE_IS_SHEET (sheet)) ;
1655 g_return_if_fail (range != NULL);
1657 range->row0 = min_visible_row (sheet);
1658 range->col0 = min_visible_column (sheet);
1659 range->rowi = max_visible_row (sheet);
1660 range->coli = max_visible_column (sheet);
1665 psppire_sheet_set_scroll_adjustments (PsppireSheet *sheet,
1666 GtkAdjustment *hadjustment,
1667 GtkAdjustment *vadjustment)
1669 if ( sheet->vadjustment != vadjustment )
1671 if (sheet->vadjustment)
1672 g_object_unref (sheet->vadjustment);
1673 sheet->vadjustment = vadjustment;
1677 g_object_ref (vadjustment);
1679 g_signal_connect (sheet->vadjustment, "value_changed",
1680 G_CALLBACK (vadjustment_value_changed),
1685 if ( sheet->hadjustment != hadjustment )
1687 if (sheet->hadjustment)
1688 g_object_unref (sheet->hadjustment);
1690 sheet->hadjustment = hadjustment;
1694 g_object_ref (hadjustment);
1696 g_signal_connect (sheet->hadjustment, "value_changed",
1697 G_CALLBACK (hadjustment_value_changed),
1705 psppire_sheet_finalize (GObject *object)
1707 PsppireSheet *sheet;
1709 g_return_if_fail (object != NULL);
1710 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1712 sheet = PSPPIRE_SHEET (object);
1714 if (G_OBJECT_CLASS (parent_class)->finalize)
1715 (*G_OBJECT_CLASS (parent_class)->finalize) (object);
1719 psppire_sheet_dispose (GObject *object)
1721 PsppireSheet *sheet = PSPPIRE_SHEET (object);
1723 g_return_if_fail (object != NULL);
1724 g_return_if_fail (PSPPIRE_IS_SHEET (object));
1726 if ( sheet->dispose_has_run )
1729 sheet->dispose_has_run = TRUE;
1731 if ( sheet->cell_padding)
1732 g_boxed_free (GTK_TYPE_BORDER, sheet->cell_padding);
1734 if (sheet->model) g_object_unref (sheet->model);
1735 if (sheet->vaxis) g_object_unref (sheet->vaxis);
1736 if (sheet->haxis) g_object_unref (sheet->haxis);
1738 g_object_unref (sheet->button);
1739 sheet->button = NULL;
1741 /* unref adjustments */
1742 if (sheet->hadjustment)
1744 g_signal_handlers_disconnect_matched (sheet->hadjustment,
1745 G_SIGNAL_MATCH_DATA,
1749 g_object_unref (sheet->hadjustment);
1750 sheet->hadjustment = NULL;
1753 if (sheet->vadjustment)
1755 g_signal_handlers_disconnect_matched (sheet->vadjustment,
1756 G_SIGNAL_MATCH_DATA,
1760 g_object_unref (sheet->vadjustment);
1762 sheet->vadjustment = NULL;
1765 if (G_OBJECT_CLASS (parent_class)->dispose)
1766 (*G_OBJECT_CLASS (parent_class)->dispose) (object);
1770 psppire_sheet_style_set (GtkWidget *widget,
1771 GtkStyle *previous_style)
1773 PsppireSheet *sheet;
1775 g_return_if_fail (widget != NULL);
1776 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1778 if (GTK_WIDGET_CLASS (parent_class)->style_set)
1779 (*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
1781 sheet = PSPPIRE_SHEET (widget);
1783 if (GTK_WIDGET_REALIZED (widget))
1785 gtk_style_set_background (widget->style, widget->window, widget->state);
1788 set_entry_widget_font (sheet);
1793 psppire_sheet_realize (GtkWidget *widget)
1795 PsppireSheet *sheet;
1796 GdkWindowAttr attributes;
1797 const gint attributes_mask =
1798 GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP | GDK_WA_CURSOR;
1801 GdkColormap *colormap;
1802 GdkDisplay *display;
1804 g_return_if_fail (widget != NULL);
1805 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1807 sheet = PSPPIRE_SHEET (widget);
1809 colormap = gtk_widget_get_colormap (widget);
1810 display = gtk_widget_get_display (widget);
1812 attributes.window_type = GDK_WINDOW_CHILD;
1813 attributes.x = widget->allocation.x;
1814 attributes.y = widget->allocation.y;
1815 attributes.width = widget->allocation.width;
1816 attributes.height = widget->allocation.height;
1817 attributes.wclass = GDK_INPUT_OUTPUT;
1819 attributes.visual = gtk_widget_get_visual (widget);
1820 attributes.colormap = colormap;
1822 attributes.event_mask = gtk_widget_get_events (widget);
1823 attributes.event_mask |= (GDK_EXPOSURE_MASK |
1824 GDK_BUTTON_PRESS_MASK |
1825 GDK_BUTTON_RELEASE_MASK |
1826 GDK_KEY_PRESS_MASK |
1827 GDK_ENTER_NOTIFY_MASK |
1828 GDK_LEAVE_NOTIFY_MASK |
1829 GDK_POINTER_MOTION_MASK |
1830 GDK_POINTER_MOTION_HINT_MASK);
1832 attributes.cursor = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
1835 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
1837 gdk_window_set_user_data (widget->window, sheet);
1839 widget->style = gtk_style_attach (widget->style, widget->window);
1841 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1843 gdk_color_parse ("white", &sheet->color[BG_COLOR]);
1844 gdk_colormap_alloc_color (colormap, &sheet->color[BG_COLOR], FALSE,
1846 gdk_color_parse ("gray", &sheet->color[GRID_COLOR]);
1847 gdk_colormap_alloc_color (colormap, &sheet->color[GRID_COLOR], FALSE,
1852 attributes.width = sheet->column_title_area.width;
1853 attributes.height = sheet->column_title_area.height;
1856 /* column - title window */
1857 sheet->column_title_window =
1858 gdk_window_new (widget->window, &attributes, attributes_mask);
1859 gdk_window_set_user_data (sheet->column_title_window, sheet);
1860 gtk_style_set_background (widget->style, sheet->column_title_window,
1866 attributes.width = sheet->row_title_area.width;
1867 attributes.height = sheet->row_title_area.height;
1869 /* row - title window */
1870 sheet->row_title_window = gdk_window_new (widget->window,
1871 &attributes, attributes_mask);
1872 gdk_window_set_user_data (sheet->row_title_window, sheet);
1873 gtk_style_set_background (widget->style, sheet->row_title_window,
1876 /* sheet - window */
1877 attributes.cursor = gdk_cursor_new_for_display (display, GDK_PLUS);
1882 sheet->sheet_window = gdk_window_new (widget->window,
1883 &attributes, attributes_mask);
1884 gdk_window_set_user_data (sheet->sheet_window, sheet);
1886 gdk_cursor_unref (attributes.cursor);
1888 gdk_window_set_background (sheet->sheet_window, &widget->style->white);
1889 gdk_window_show (sheet->sheet_window);
1892 sheet->fg_gc = gdk_gc_new (widget->window);
1893 sheet->bg_gc = gdk_gc_new (widget->window);
1895 values.foreground = widget->style->white;
1896 values.function = GDK_INVERT;
1897 values.subwindow_mode = GDK_INCLUDE_INFERIORS;
1898 values.line_width = MAX (sheet->cell_padding->left,
1899 MAX (sheet->cell_padding->right,
1900 MAX (sheet->cell_padding->top,
1901 sheet->cell_padding->bottom)));
1903 sheet->xor_gc = gdk_gc_new_with_values (widget->window,
1911 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
1912 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
1914 gtk_widget_set_parent_window (sheet->button, sheet->sheet_window);
1915 gtk_widget_set_parent (sheet->button, GTK_WIDGET (sheet));
1917 sheet->button->style = gtk_style_attach (sheet->button->style,
1918 sheet->sheet_window);
1921 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
1923 if (sheet->column_titles_visible)
1924 gdk_window_show (sheet->column_title_window);
1925 if (sheet->row_titles_visible)
1926 gdk_window_show (sheet->row_title_window);
1928 sheet->hover_window = create_hover_window ();
1930 draw_row_title_buttons (sheet);
1931 draw_column_title_buttons (sheet);
1933 psppire_sheet_update_primary_selection (sheet);
1936 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1940 create_global_button (PsppireSheet *sheet)
1942 sheet->button = gtk_button_new_with_label (" ");
1944 GTK_WIDGET_UNSET_FLAGS(sheet->button, GTK_CAN_FOCUS);
1946 g_object_ref_sink (sheet->button);
1948 g_signal_connect (sheet->button,
1950 G_CALLBACK (global_button_clicked),
1955 size_allocate_global_button (PsppireSheet *sheet)
1957 GtkAllocation allocation;
1959 if (!sheet->column_titles_visible) return;
1960 if (!sheet->row_titles_visible) return;
1962 gtk_widget_size_request (sheet->button, NULL);
1966 allocation.width = sheet->row_title_area.width;
1967 allocation.height = sheet->column_title_area.height;
1969 gtk_widget_size_allocate (sheet->button, &allocation);
1973 global_button_clicked (GtkWidget *widget, gpointer data)
1975 psppire_sheet_click_cell (PSPPIRE_SHEET (data), -1, -1);
1980 psppire_sheet_unrealize (GtkWidget *widget)
1982 PsppireSheet *sheet;
1984 g_return_if_fail (widget != NULL);
1985 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
1987 sheet = PSPPIRE_SHEET (widget);
1989 gdk_cursor_unref (sheet->cursor_drag);
1990 sheet->cursor_drag = NULL;
1992 gdk_colormap_free_colors (gtk_widget_get_colormap (widget),
1993 sheet->color, n_COLORS);
1995 g_object_unref (sheet->xor_gc);
1996 g_object_unref (sheet->fg_gc);
1997 g_object_unref (sheet->bg_gc);
1999 destroy_hover_window (sheet->hover_window);
2001 gdk_window_destroy (sheet->sheet_window);
2002 gdk_window_destroy (sheet->column_title_window);
2003 gdk_window_destroy (sheet->row_title_window);
2005 gtk_widget_unparent (sheet->entry_widget);
2006 if (sheet->button != NULL)
2007 gtk_widget_unparent (sheet->button);
2009 if (GTK_WIDGET_CLASS (parent_class)->unrealize)
2010 (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
2014 psppire_sheet_map (GtkWidget *widget)
2016 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2018 g_return_if_fail (widget != NULL);
2019 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
2021 if (!GTK_WIDGET_MAPPED (widget))
2023 GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
2025 gdk_window_show (widget->window);
2026 gdk_window_show (sheet->sheet_window);
2028 if (sheet->column_titles_visible)
2030 draw_column_title_buttons (sheet);
2031 gdk_window_show (sheet->column_title_window);
2033 if (sheet->row_titles_visible)
2035 draw_row_title_buttons (sheet);
2036 gdk_window_show (sheet->row_title_window);
2039 if (!GTK_WIDGET_MAPPED (sheet->entry_widget)
2040 && sheet->active_cell.row >= 0
2041 && sheet->active_cell.col >= 0 )
2043 gtk_widget_show (sheet->entry_widget);
2044 gtk_widget_map (sheet->entry_widget);
2047 if (!GTK_WIDGET_MAPPED (sheet->button))
2049 gtk_widget_show (sheet->button);
2050 gtk_widget_map (sheet->button);
2053 redraw_range (sheet, NULL);
2054 change_active_cell (sheet,
2055 sheet->active_cell.row,
2056 sheet->active_cell.col);
2061 psppire_sheet_unmap (GtkWidget *widget)
2063 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2065 if (!GTK_WIDGET_MAPPED (widget))
2068 GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
2070 gdk_window_hide (sheet->sheet_window);
2071 if (sheet->column_titles_visible)
2072 gdk_window_hide (sheet->column_title_window);
2073 if (sheet->row_titles_visible)
2074 gdk_window_hide (sheet->row_title_window);
2075 gdk_window_hide (widget->window);
2077 gtk_widget_unmap (sheet->entry_widget);
2078 gtk_widget_unmap (sheet->button);
2079 gtk_widget_unmap (sheet->hover_window->window);
2082 /* get cell attributes of the given cell */
2083 /* TRUE means that the cell is currently allocated */
2084 static gboolean psppire_sheet_get_attributes (const PsppireSheet *sheet,
2086 PsppireSheetCellAttr *attributes);
2091 psppire_sheet_cell_draw (PsppireSheet *sheet, gint row, gint col)
2093 PangoLayout *layout;
2094 PangoRectangle text;
2095 PangoFontDescription *font_desc = GTK_WIDGET (sheet)->style->font_desc;
2100 PsppireSheetCellAttr attributes;
2103 g_return_if_fail (sheet != NULL);
2105 /* bail now if we aren't yet drawable */
2106 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
2109 row >= psppire_axis_unit_count (sheet->vaxis))
2113 col >= psppire_axis_unit_count (sheet->haxis))
2116 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2118 /* select GC for background rectangle */
2119 gdk_gc_set_foreground (sheet->fg_gc, &attributes.foreground);
2120 gdk_gc_set_foreground (sheet->bg_gc, &attributes.background);
2122 rectangle_from_cell (sheet, row, col, &area);
2124 gdk_gc_set_line_attributes (sheet->fg_gc, 1, 0, 0, 0);
2126 if (sheet->show_grid)
2128 gdk_gc_set_foreground (sheet->bg_gc, &sheet->color[GRID_COLOR]);
2130 gdk_draw_rectangle (sheet->sheet_window,
2134 area.width, area.height);
2138 label = psppire_sheet_cell_get_text (sheet, row, col);
2143 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), label);
2144 dispose_string (sheet, label);
2147 pango_layout_set_font_description (layout, font_desc);
2149 pango_layout_get_pixel_extents (layout, NULL, &text);
2151 gdk_gc_set_clip_rectangle (sheet->fg_gc, &area);
2153 font_height = pango_font_description_get_size (font_desc);
2154 if ( !pango_font_description_get_size_is_absolute (font_desc))
2155 font_height /= PANGO_SCALE;
2158 if ( sheet->cell_padding )
2160 area.x += sheet->cell_padding->left;
2161 area.width -= sheet->cell_padding->right
2162 + sheet->cell_padding->left;
2164 area.y += sheet->cell_padding->top;
2165 area.height -= sheet->cell_padding->bottom
2167 sheet->cell_padding->top;
2170 /* Centre the text vertically */
2171 area.y += (area.height - font_height) / 2.0;
2173 switch (attributes.justification)
2175 case GTK_JUSTIFY_RIGHT:
2176 area.x += area.width - text.width;
2178 case GTK_JUSTIFY_CENTER:
2179 area.x += (area.width - text.width) / 2.0;
2181 case GTK_JUSTIFY_LEFT:
2185 g_critical ("Unhandled justification %d in column %d\n",
2186 attributes.justification, col);
2190 gdk_draw_layout (sheet->sheet_window, sheet->fg_gc,
2195 gdk_gc_set_clip_rectangle (sheet->fg_gc, NULL);
2196 g_object_unref (layout);
2201 draw_sheet_region (PsppireSheet *sheet, GdkRegion *region)
2203 PsppireSheetRange range;
2208 PsppireSheetRange drawing_range;
2210 gdk_region_get_clipbox (region, &area);
2212 y = area.y + sheet->vadjustment->value;
2213 x = area.x + sheet->hadjustment->value;
2215 if ( sheet->column_titles_visible)
2216 y -= sheet->column_title_area.height;
2218 if ( sheet->row_titles_visible)
2219 x -= sheet->row_title_area.width;
2221 maximize_int (&x, 0);
2222 maximize_int (&y, 0);
2224 range.row0 = row_from_ypixel (sheet, y);
2225 range.rowi = row_from_ypixel (sheet, y + area.height);
2227 range.col0 = column_from_xpixel (sheet, x);
2228 range.coli = column_from_xpixel (sheet, x + area.width);
2230 g_return_if_fail (sheet != NULL);
2231 g_return_if_fail (PSPPIRE_SHEET (sheet));
2233 if (!GTK_WIDGET_DRAWABLE (GTK_WIDGET (sheet))) return;
2234 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2235 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
2238 drawing_range.row0 = MAX (range.row0, min_visible_row (sheet));
2239 drawing_range.col0 = MAX (range.col0, min_visible_column (sheet));
2240 drawing_range.rowi = MIN (range.rowi, max_visible_row (sheet));
2241 drawing_range.coli = MIN (range.coli, max_visible_column (sheet));
2243 g_return_if_fail (drawing_range.rowi >= drawing_range.row0);
2244 g_return_if_fail (drawing_range.coli >= drawing_range.col0);
2246 for (i = drawing_range.row0; i <= drawing_range.rowi; i++)
2248 for (j = drawing_range.col0; j <= drawing_range.coli; j++)
2249 psppire_sheet_cell_draw (sheet, i, j);
2252 if (sheet->select_status == PSPPIRE_SHEET_NORMAL &&
2253 sheet->active_cell.row >= drawing_range.row0 &&
2254 sheet->active_cell.row <= drawing_range.rowi &&
2255 sheet->active_cell.col >= drawing_range.col0 &&
2256 sheet->active_cell.col <= drawing_range.coli)
2257 psppire_sheet_show_entry_widget (sheet);
2263 safe_strcmp (const gchar *s1, const gchar *s2)
2265 if ( !s1 && !s2) return 0;
2266 if ( !s1) return -1;
2267 if ( !s2) return +1;
2268 return strcmp (s1, s2);
2272 psppire_sheet_set_cell (PsppireSheet *sheet, gint row, gint col,
2273 GtkJustification justification,
2276 PsppireSheetModel *model ;
2279 g_return_if_fail (sheet != NULL);
2280 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2282 if (col >= psppire_axis_unit_count (sheet->haxis)
2283 || row >= psppire_axis_unit_count (sheet->vaxis))
2286 if (col < 0 || row < 0) return;
2288 model = psppire_sheet_get_model (sheet);
2290 old_text = psppire_sheet_model_get_string (model, row, col);
2292 if (0 != safe_strcmp (old_text, text))
2294 g_signal_handler_block (sheet->model, sheet->update_handler_id);
2295 psppire_sheet_model_set_string (model, text, row, col);
2296 g_signal_handler_unblock (sheet->model, sheet->update_handler_id);
2299 if ( psppire_sheet_model_free_strings (model))
2305 psppire_sheet_cell_clear (PsppireSheet *sheet, gint row, gint column)
2307 PsppireSheetRange range;
2309 g_return_if_fail (sheet != NULL);
2310 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2311 if (column >= psppire_axis_unit_count (sheet->haxis) ||
2312 row >= psppire_axis_unit_count (sheet->vaxis)) return;
2314 if (column < 0 || row < 0) return;
2318 range.col0 = min_visible_column (sheet);
2319 range.coli = max_visible_column (sheet);
2321 psppire_sheet_real_cell_clear (sheet, row, column);
2323 redraw_range (sheet, &range);
2327 psppire_sheet_real_cell_clear (PsppireSheet *sheet, gint row, gint column)
2329 PsppireSheetModel *model = psppire_sheet_get_model (sheet);
2331 gchar *old_text = psppire_sheet_cell_get_text (sheet, row, column);
2333 if (old_text && strlen (old_text) > 0 )
2335 psppire_sheet_model_datum_clear (model, row, column);
2338 dispose_string (sheet, old_text);
2342 psppire_sheet_cell_get_text (const PsppireSheet *sheet, gint row, gint col)
2344 PsppireSheetModel *model;
2345 g_return_val_if_fail (sheet != NULL, NULL);
2346 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
2348 if (col >= psppire_axis_unit_count (sheet->haxis) || row >= psppire_axis_unit_count (sheet->vaxis))
2350 if (col < 0 || row < 0) return NULL;
2352 model = psppire_sheet_get_model (sheet);
2357 return psppire_sheet_model_get_string (model, row, col);
2361 /* Convert X, Y (in pixels) to *ROW, *COLUMN
2362 If the function returns FALSE, then the results will be unreliable.
2365 psppire_sheet_get_pixel_info (PsppireSheet *sheet,
2373 *column = -G_MAXINT;
2375 g_return_val_if_fail (sheet != NULL, 0);
2376 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2378 /* bounds checking, return false if the user clicked
2386 if ( sheet->column_titles_visible)
2387 y -= sheet->column_title_area.height;
2389 y += sheet->vadjustment->value;
2391 if ( y < 0 && sheet->column_titles_visible)
2397 trow = row_from_ypixel (sheet, y);
2398 if (trow > psppire_axis_unit_count (sheet->vaxis))
2404 if ( sheet->row_titles_visible)
2405 x -= sheet->row_title_area.width;
2407 x += sheet->hadjustment->value;
2409 if ( x < 0 && sheet->row_titles_visible)
2415 tcol = column_from_xpixel (sheet, x);
2416 if (tcol > psppire_axis_unit_count (sheet->haxis))
2426 psppire_sheet_get_cell_area (PsppireSheet *sheet,
2431 g_return_val_if_fail (sheet != NULL, 0);
2432 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), 0);
2434 if (row >= psppire_axis_unit_count (sheet->vaxis) || column >= psppire_axis_unit_count (sheet->haxis))
2437 area->x = (column == -1) ? 0 : psppire_axis_start_pixel (sheet->haxis, column);
2438 area->y = (row == -1) ? 0 : psppire_axis_start_pixel (sheet->vaxis, row);
2440 area->width= (column == -1) ? sheet->row_title_area.width
2441 : psppire_axis_unit_size (sheet->haxis, column);
2443 area->height= (row == -1) ? sheet->column_title_area.height
2444 : psppire_axis_unit_size (sheet->vaxis, row);
2450 psppire_sheet_set_active_cell (PsppireSheet *sheet, gint row, gint col)
2452 g_return_if_fail (sheet != NULL);
2453 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2455 if (row < -1 || col < -1)
2458 if (row >= psppire_axis_unit_count (sheet->vaxis)
2460 col >= psppire_axis_unit_count (sheet->haxis))
2463 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2466 if ( row == -1 || col == -1)
2468 psppire_sheet_hide_entry_widget (sheet);
2472 change_active_cell (sheet, row, col);
2476 psppire_sheet_get_active_cell (PsppireSheet *sheet, gint *row, gint *column)
2478 g_return_if_fail (sheet != NULL);
2479 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2481 if ( row ) *row = sheet->active_cell.row;
2482 if (column) *column = sheet->active_cell.col;
2486 entry_load_text (PsppireSheet *sheet)
2490 GtkJustification justification;
2491 PsppireSheetCellAttr attributes;
2493 if (!GTK_WIDGET_VISIBLE (sheet->entry_widget)) return;
2494 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2496 row = sheet->active_cell.row;
2497 col = sheet->active_cell.col;
2499 if (row < 0 || col < 0) return;
2501 text = gtk_entry_get_text (psppire_sheet_get_entry (sheet));
2503 if (text && strlen (text) > 0)
2505 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2506 justification = attributes.justification;
2507 psppire_sheet_set_cell (sheet, row, col, justification, text);
2513 psppire_sheet_hide_entry_widget (PsppireSheet *sheet)
2515 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
2518 if (sheet->active_cell.row < 0 ||
2519 sheet->active_cell.col < 0) return;
2521 gtk_widget_hide (sheet->entry_widget);
2522 gtk_widget_unmap (sheet->entry_widget);
2524 GTK_WIDGET_UNSET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2528 change_active_cell (PsppireSheet *sheet, gint row, gint col)
2530 gint old_row, old_col;
2532 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2534 if (row < 0 || col < 0)
2537 if ( row > psppire_axis_unit_count (sheet->vaxis)
2538 || col > psppire_axis_unit_count (sheet->haxis))
2541 old_row = sheet->active_cell.row;
2542 old_col = sheet->active_cell.col;
2544 entry_load_text (sheet);
2546 /* Erase the old cell border */
2547 psppire_sheet_draw_active_cell (sheet);
2549 sheet->active_cell.row = row;
2550 sheet->active_cell.col = col;
2552 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2554 GTK_WIDGET_UNSET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2556 psppire_sheet_draw_active_cell (sheet);
2557 psppire_sheet_show_entry_widget (sheet);
2559 GTK_WIDGET_SET_FLAGS (sheet->entry_widget, GTK_HAS_FOCUS);
2561 g_signal_emit (sheet, sheet_signals [ACTIVATE], 0,
2562 row, col, old_row, old_col);
2567 psppire_sheet_show_entry_widget (PsppireSheet *sheet)
2569 GtkEntry *sheet_entry;
2570 PsppireSheetCellAttr attributes;
2574 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
2576 row = sheet->active_cell.row;
2577 col = sheet->active_cell.col;
2579 /* Don't show the active cell, if there is no active cell: */
2580 if (! (row >= 0 && col >= 0)) /* e.g row or coll == -1. */
2583 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
2584 if (sheet->select_status != PSPPIRE_SHEET_NORMAL) return;
2585 if (PSPPIRE_SHEET_IN_SELECTION (sheet)) return;
2587 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (sheet->entry_widget), GTK_VISIBLE);
2589 sheet_entry = psppire_sheet_get_entry (sheet);
2591 psppire_sheet_get_attributes (sheet, row, col, &attributes);
2593 if (GTK_IS_ENTRY (sheet_entry))
2595 gchar *text = psppire_sheet_cell_get_text (sheet, row, col);
2596 const gchar *old_text = gtk_entry_get_text (GTK_ENTRY (sheet_entry));
2599 text = g_strdup ("");
2601 if (strcmp (old_text, text) != 0)
2602 gtk_entry_set_text (sheet_entry, text);
2604 dispose_string (sheet, text);
2607 switch (attributes.justification)
2609 case GTK_JUSTIFY_RIGHT:
2610 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 1.0);
2612 case GTK_JUSTIFY_CENTER:
2613 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.5);
2615 case GTK_JUSTIFY_LEFT:
2617 gtk_entry_set_alignment (GTK_ENTRY (sheet_entry), 0.0);
2623 psppire_sheet_size_allocate_entry (sheet);
2625 gtk_widget_set_sensitive (GTK_WIDGET (sheet_entry),
2626 psppire_sheet_model_is_editable (sheet->model,
2628 gtk_widget_map (sheet->entry_widget);
2632 psppire_sheet_draw_active_cell (PsppireSheet *sheet)
2635 PsppireSheetRange range;
2637 row = sheet->active_cell.row;
2638 col = sheet->active_cell.col;
2640 if (row < 0 || col < 0) return FALSE;
2642 if (!psppire_sheet_cell_isvisible (sheet, row, col))
2645 range.col0 = range.coli = col;
2646 range.row0 = range.rowi = row;
2648 psppire_sheet_draw_border (sheet, range);
2656 psppire_sheet_draw_border (PsppireSheet *sheet, PsppireSheetRange new_range)
2660 rectangle_from_range (sheet, &new_range, &area);
2665 gdk_gc_set_clip_rectangle (sheet->xor_gc, &area);
2667 area.x += sheet->cell_padding->left / 2;
2668 area.y += sheet->cell_padding->top / 2;
2669 area.width -= (sheet->cell_padding->left + sheet->cell_padding->right ) / 2;
2670 area.height -= (sheet->cell_padding->top + sheet->cell_padding->bottom ) / 2;
2672 gdk_draw_rectangle (sheet->sheet_window, sheet->xor_gc,
2679 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
2684 /* Selection related functions */
2687 psppire_sheet_select_row (PsppireSheet *sheet, gint row)
2690 sheet->select_status = PSPPIRE_SHEET_ROW_SELECTED;
2692 sheet->range.col0 = sheet->range.coli = -1;
2693 sheet->range.row0 = sheet->range.rowi = row;
2695 rectangle_from_range (sheet, &sheet->range, &area);
2699 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2701 g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, row);
2705 psppire_sheet_select_column (PsppireSheet *sheet, gint column)
2708 sheet->select_status = PSPPIRE_SHEET_COLUMN_SELECTED;
2710 sheet->range.col0 = sheet->range.coli = column;
2711 sheet->range.row0 = sheet->range.rowi = -1;
2713 rectangle_from_range (sheet, &sheet->range, &area);
2717 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2719 g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, column);
2724 psppire_sheet_select_range (PsppireSheet *sheet, const PsppireSheetRange *range)
2727 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
2729 sheet->range = *range;
2731 rectangle_from_range (sheet, range, &area);
2734 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2739 psppire_sheet_unselect_range (PsppireSheet *sheet)
2742 sheet->select_status = PSPPIRE_SHEET_NORMAL;
2744 rectangle_from_range (sheet, &sheet->range, &area);
2747 gdk_window_invalidate_rect (sheet->sheet_window, &area, FALSE);
2749 g_signal_emit (sheet, sheet_signals [SELECT_COLUMN], 0, -1);
2750 g_signal_emit (sheet, sheet_signals [SELECT_ROW], 0, -1);
2754 psppire_sheet_get_selected_range (PsppireSheet *sheet, PsppireSheetRange *range)
2756 g_return_if_fail (sheet != NULL);
2757 *range = sheet->range;
2762 psppire_sheet_expose (GtkWidget *widget, GdkEventExpose *event)
2764 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
2766 g_return_val_if_fail (event != NULL, FALSE);
2768 if (!GTK_WIDGET_DRAWABLE (widget))
2771 /* exposure events on the sheet */
2772 if (event->window == sheet->row_title_window &&
2773 sheet->row_titles_visible)
2775 draw_row_title_buttons_range (sheet,
2776 min_visible_row (sheet),
2777 max_visible_row (sheet));
2780 if (event->window == sheet->column_title_window &&
2781 sheet->column_titles_visible)
2783 draw_column_title_buttons_range (sheet,
2784 min_visible_column (sheet),
2785 max_visible_column (sheet));
2788 if (event->window == sheet->sheet_window)
2790 draw_sheet_region (sheet, event->region);
2792 if (sheet->select_status != PSPPIRE_SHEET_NORMAL)
2795 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
2796 psppire_sheet_range_draw (sheet, &sheet->range);
2798 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2799 psppire_sheet_range_draw (sheet, &sheet->drag_range);
2805 rectangle_from_range (sheet, &sheet->range, &area);
2807 gdk_draw_rectangle (sheet->sheet_window,
2810 area.x + 1, area.y + 1,
2811 area.width, area.height);
2815 if (PSPPIRE_SHEET_IN_RESIZE (sheet) || PSPPIRE_SHEET_IN_DRAG (sheet))
2816 draw_xor_rectangle (sheet, sheet->drag_range);
2821 if ((!PSPPIRE_SHEET_IN_XDRAG (sheet)) && (!PSPPIRE_SHEET_IN_YDRAG (sheet)))
2824 PsppireSheetRange range;
2825 range.row0 = range.rowi = sheet->active_cell.row;
2826 range.col0 = range.coli = sheet->active_cell.col;
2828 rectangle_from_range (sheet, &range, &rect);
2830 if (GDK_OVERLAP_RECTANGLE_OUT !=
2831 gdk_region_rect_in (event->region, &rect))
2833 psppire_sheet_draw_active_cell (sheet);
2839 (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event);
2846 psppire_sheet_button_press (GtkWidget *widget, GdkEventButton *event)
2848 PsppireSheet *sheet;
2849 GdkModifierType mods;
2854 g_return_val_if_fail (widget != NULL, FALSE);
2855 g_return_val_if_fail (PSPPIRE_IS_SHEET (widget), FALSE);
2856 g_return_val_if_fail (event != NULL, FALSE);
2858 sheet = PSPPIRE_SHEET (widget);
2860 /* Cancel any pending tooltips */
2861 if (sheet->motion_timer)
2863 g_source_remove (sheet->motion_timer);
2864 sheet->motion_timer = 0;
2867 gtk_widget_get_pointer (widget, &x, &y);
2868 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2871 if (event->window == sheet->column_title_window)
2873 sheet->x_drag = event->x;
2874 g_signal_emit (sheet,
2875 sheet_signals[BUTTON_EVENT_COLUMN], 0,
2878 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2880 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2881 g_signal_emit (sheet,
2882 sheet_signals[DOUBLE_CLICK_COLUMN], 0, column);
2886 if (event->window == sheet->row_title_window)
2888 g_signal_emit (sheet,
2889 sheet_signals[BUTTON_EVENT_ROW], 0,
2892 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2894 if ( event->type == GDK_2BUTTON_PRESS && event->button == 1)
2895 g_signal_emit (sheet,
2896 sheet_signals[DOUBLE_CLICK_ROW], 0, row);
2900 gdk_window_get_pointer (widget->window, NULL, NULL, &mods);
2902 if (! (mods & GDK_BUTTON1_MASK)) return TRUE;
2905 /* press on resize windows */
2906 if (event->window == sheet->column_title_window)
2908 sheet->x_drag = event->x;
2910 if (on_column_boundary (sheet, sheet->x_drag, &sheet->drag_cell.col))
2912 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
2913 gdk_pointer_grab (sheet->column_title_window, FALSE,
2914 GDK_POINTER_MOTION_HINT_MASK |
2915 GDK_BUTTON1_MOTION_MASK |
2916 GDK_BUTTON_RELEASE_MASK,
2917 NULL, NULL, event->time);
2919 draw_xor_vline (sheet);
2924 if (event->window == sheet->row_title_window)
2926 sheet->y_drag = event->y;
2928 if (on_row_boundary (sheet, sheet->y_drag, &sheet->drag_cell.row))
2930 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
2931 gdk_pointer_grab (sheet->row_title_window, FALSE,
2932 GDK_POINTER_MOTION_HINT_MASK |
2933 GDK_BUTTON1_MOTION_MASK |
2934 GDK_BUTTON_RELEASE_MASK,
2935 NULL, NULL, event->time);
2937 draw_xor_hline (sheet);
2942 /* the sheet itself does not handle other than single click events */
2943 if (event->type != GDK_BUTTON_PRESS) return FALSE;
2945 /* selections on the sheet */
2946 if (event->window == sheet->sheet_window)
2948 gtk_widget_get_pointer (widget, &x, &y);
2949 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
2950 gdk_pointer_grab (sheet->sheet_window, FALSE,
2951 GDK_POINTER_MOTION_HINT_MASK |
2952 GDK_BUTTON1_MOTION_MASK |
2953 GDK_BUTTON_RELEASE_MASK,
2954 NULL, NULL, event->time);
2955 gtk_grab_add (GTK_WIDGET (sheet));
2957 if ( sheet->select_status == PSPPIRE_SHEET_NORMAL)
2959 sheet->range.row0 = row;
2960 sheet->range.col0 = column;
2964 psppire_sheet_unselect_range (sheet);
2966 psppire_sheet_click_cell (sheet, row, column);
2969 if (event->window == sheet->column_title_window)
2971 gtk_widget_get_pointer (widget, &x, &y);
2972 if ( sheet->row_titles_visible)
2973 x -= sheet->row_title_area.width;
2975 x += sheet->hadjustment->value;
2977 column = column_from_xpixel (sheet, x);
2979 if (psppire_sheet_model_get_column_sensitivity (sheet->model, column))
2981 gtk_grab_add (GTK_WIDGET (sheet));
2982 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
2986 if (event->window == sheet->row_title_window)
2988 gtk_widget_get_pointer (widget, &x, &y);
2989 if ( sheet->column_titles_visible)
2990 y -= sheet->column_title_area.height;
2992 y += sheet->vadjustment->value;
2994 row = row_from_ypixel (sheet, y);
2995 if (psppire_sheet_model_get_row_sensitivity (sheet->model, row))
2997 gtk_grab_add (GTK_WIDGET (sheet));
2998 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3006 psppire_sheet_click_cell (PsppireSheet *sheet, gint row, gint column)
3008 PsppireSheetCell cell;
3009 gboolean forbid_move;
3014 if (row >= psppire_axis_unit_count (sheet->vaxis)
3015 || column >= psppire_axis_unit_count (sheet->haxis))
3020 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3021 &sheet->active_cell,
3027 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3030 row = sheet->active_cell.row;
3031 column = sheet->active_cell.col;
3033 change_active_cell (sheet, row, column);
3037 if (row == -1 && column >= 0)
3039 psppire_sheet_select_column (sheet, column);
3043 if (column == -1 && row >= 0)
3045 psppire_sheet_select_row (sheet, row);
3049 if (row == -1 && column == -1)
3051 sheet->range.row0 = 0;
3052 sheet->range.col0 = 0;
3053 sheet->range.rowi = psppire_axis_unit_count (sheet->vaxis) - 1;
3055 psppire_axis_unit_count (sheet->haxis) - 1;
3056 psppire_sheet_select_range (sheet, NULL);
3060 if (sheet->select_status == PSPPIRE_SHEET_NORMAL)
3061 change_active_cell (sheet, row, column);
3063 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3069 psppire_sheet_button_release (GtkWidget *widget,
3070 GdkEventButton *event)
3072 GdkDisplay *display = gtk_widget_get_display (widget);
3074 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3076 /* release on resize windows */
3077 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3080 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_XDRAG);
3081 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3083 gdk_display_pointer_ungrab (display, event->time);
3084 draw_xor_vline (sheet);
3087 psppire_axis_start_pixel (sheet->haxis, sheet->drag_cell.col)
3088 + sheet->hadjustment->value;
3090 set_column_width (sheet, sheet->drag_cell.col, width);
3095 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3098 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_YDRAG);
3099 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3101 gdk_display_pointer_ungrab (display, event->time);
3102 draw_xor_hline (sheet);
3105 psppire_axis_start_pixel (sheet->vaxis, sheet->drag_cell.row) +
3106 sheet->vadjustment->value;
3108 set_row_height (sheet, sheet->drag_cell.row, height);
3113 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3115 PsppireSheetRange old_range;
3116 draw_xor_rectangle (sheet, sheet->drag_range);
3117 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_DRAG);
3118 gdk_display_pointer_ungrab (display, event->time);
3120 psppire_sheet_unselect_range (sheet);
3122 old_range = sheet->range;
3123 sheet->range = sheet->drag_range;
3124 sheet->drag_range = old_range;
3125 g_signal_emit (sheet, sheet_signals[MOVE_RANGE], 0,
3126 &sheet->drag_range, &sheet->range);
3127 psppire_sheet_select_range (sheet, &sheet->range);
3130 if (PSPPIRE_SHEET_IN_SELECTION (sheet))
3132 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3133 sheet->select_status = PSPPIRE_SHEET_RANGE_SELECTED;
3135 change_active_cell (sheet, sheet->active_cell.row,
3136 sheet->active_cell.col);
3139 gdk_display_pointer_ungrab (display, event->time);
3140 gtk_grab_remove (GTK_WIDGET (sheet));
3142 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3151 /* Shamelessly lifted from gtktooltips */
3153 psppire_sheet_subtitle_paint_window (GtkWidget *tip_window)
3157 gtk_widget_size_request (tip_window, &req);
3158 gtk_paint_flat_box (tip_window->style, tip_window->window,
3159 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
3160 NULL, GTK_WIDGET(tip_window), "tooltip",
3161 0, 0, req.width, req.height);
3167 destroy_hover_window (PsppireSheetHoverTitle *h)
3169 gtk_widget_destroy (h->window);
3173 static PsppireSheetHoverTitle *
3174 create_hover_window (void)
3176 PsppireSheetHoverTitle *hw = g_malloc (sizeof (*hw));
3178 hw->window = gtk_window_new (GTK_WINDOW_POPUP);
3180 #if GTK_CHECK_VERSION (2, 9, 0)
3181 gtk_window_set_type_hint (GTK_WINDOW (hw->window),
3182 GDK_WINDOW_TYPE_HINT_TOOLTIP);
3185 gtk_widget_set_app_paintable (hw->window, TRUE);
3186 gtk_window_set_resizable (GTK_WINDOW (hw->window), FALSE);
3187 gtk_widget_set_name (hw->window, "gtk-tooltips");
3188 gtk_container_set_border_width (GTK_CONTAINER (hw->window), 4);
3190 g_signal_connect (hw->window,
3192 G_CALLBACK (psppire_sheet_subtitle_paint_window),
3195 hw->label = gtk_label_new (NULL);
3198 gtk_label_set_line_wrap (GTK_LABEL (hw->label), TRUE);
3199 gtk_misc_set_alignment (GTK_MISC (hw->label), 0.5, 0.5);
3201 gtk_container_add (GTK_CONTAINER (hw->window), hw->label);
3203 gtk_widget_show (hw->label);
3205 g_signal_connect (hw->window,
3207 G_CALLBACK (gtk_widget_destroyed),
3213 #define HOVER_WINDOW_Y_OFFSET 2
3216 show_subtitle (PsppireSheet *sheet, gint row, gint column,
3217 const gchar *subtitle)
3226 gtk_label_set_text (GTK_LABEL (sheet->hover_window->label),
3230 sheet->hover_window->row = row;
3231 sheet->hover_window->column = column;
3233 gdk_window_get_origin (GTK_WIDGET (sheet)->window, &x, &y);
3235 gtk_widget_get_pointer (GTK_WIDGET (sheet), &px, &py);
3237 gtk_widget_show (sheet->hover_window->window);
3239 width = GTK_WIDGET (sheet->hover_window->label)->allocation.width;
3245 y += sheet->column_title_area.y;
3246 y += sheet->column_title_area.height;
3247 y += HOVER_WINDOW_Y_OFFSET;
3253 x += sheet->row_title_area.x;
3254 x += sheet->row_title_area.width * 2 / 3.0;
3257 gtk_window_move (GTK_WINDOW (sheet->hover_window->window),
3262 motion_timeout_callback (gpointer data)
3264 PsppireSheet *sheet = PSPPIRE_SHEET (data);
3268 gdk_threads_enter ();
3269 gtk_widget_get_pointer (GTK_WIDGET (sheet), &x, &y);
3271 if ( psppire_sheet_get_pixel_info (sheet, x, y, &row, &column) )
3273 if (sheet->row_title_under && row >= 0)
3275 gchar *text = psppire_sheet_model_get_row_subtitle (sheet->model, row);
3277 show_subtitle (sheet, row, -1, text);
3281 if (sheet->column_title_under && column >= 0)
3283 gchar *text = psppire_sheet_model_get_column_subtitle (sheet->model,
3286 show_subtitle (sheet, -1, column, text);
3292 gdk_threads_leave ();
3297 psppire_sheet_motion (GtkWidget *widget, GdkEventMotion *event)
3299 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3300 GdkModifierType mods;
3301 GdkCursorType new_cursor;
3304 GdkDisplay *display;
3306 g_return_val_if_fail (event != NULL, FALSE);
3308 display = gtk_widget_get_display (widget);
3310 /* selections on the sheet */
3314 if (!GTK_WIDGET_VISIBLE (sheet->hover_window->window))
3316 if ( sheet->motion_timer > 0 )
3317 g_source_remove (sheet->motion_timer);
3318 sheet->motion_timer =
3319 g_timeout_add (TIMEOUT_HOVER, motion_timeout_callback, sheet);
3325 gtk_widget_get_pointer (widget, &wx, &wy);
3327 if ( psppire_sheet_get_pixel_info (sheet, wx, wy, &row, &column) )
3329 if ( row != sheet->hover_window->row ||
3330 column != sheet->hover_window->column)
3332 gtk_widget_hide (sheet->hover_window->window);
3337 if (event->window == sheet->column_title_window)
3339 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3340 on_column_boundary (sheet, x, &column))
3342 new_cursor = GDK_SB_H_DOUBLE_ARROW;
3343 if (new_cursor != sheet->cursor_drag->type)
3345 gdk_cursor_unref (sheet->cursor_drag);
3346 sheet->cursor_drag =
3347 gdk_cursor_new_for_display (display, new_cursor);
3349 gdk_window_set_cursor (sheet->column_title_window,
3350 sheet->cursor_drag);
3355 new_cursor = GDK_TOP_LEFT_ARROW;
3356 if (!PSPPIRE_SHEET_IN_XDRAG (sheet) &&
3357 new_cursor != sheet->cursor_drag->type)
3359 gdk_cursor_unref (sheet->cursor_drag);
3360 sheet->cursor_drag =
3361 gdk_cursor_new_for_display (display, new_cursor);
3362 gdk_window_set_cursor (sheet->column_title_window,
3363 sheet->cursor_drag);
3367 else if (event->window == sheet->row_title_window)
3369 if (!PSPPIRE_SHEET_IN_SELECTION (sheet) &&
3370 on_row_boundary (sheet, y, &row))
3372 new_cursor = GDK_SB_V_DOUBLE_ARROW;
3373 if (new_cursor != sheet->cursor_drag->type)
3375 gdk_cursor_unref (sheet->cursor_drag);
3376 sheet->cursor_drag =
3377 gdk_cursor_new_for_display (display, new_cursor);
3378 gdk_window_set_cursor (sheet->row_title_window,
3379 sheet->cursor_drag);
3384 new_cursor = GDK_TOP_LEFT_ARROW;
3385 if (!PSPPIRE_SHEET_IN_YDRAG (sheet) &&
3386 new_cursor != sheet->cursor_drag->type)
3388 gdk_cursor_unref (sheet->cursor_drag);
3389 sheet->cursor_drag =
3390 gdk_cursor_new_for_display (display, new_cursor);
3391 gdk_window_set_cursor (sheet->row_title_window,
3392 sheet->cursor_drag);
3397 new_cursor = GDK_PLUS;
3398 if ( event->window == sheet->sheet_window &&
3399 !POSSIBLE_DRAG (sheet, x, y, &row, &column) &&
3400 !PSPPIRE_SHEET_IN_DRAG (sheet) &&
3401 !POSSIBLE_RESIZE (sheet, x, y, &row, &column) &&
3402 new_cursor != sheet->cursor_drag->type)
3404 gdk_cursor_unref (sheet->cursor_drag);
3405 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_PLUS);
3406 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3409 new_cursor = GDK_TOP_LEFT_ARROW;
3410 if ( event->window == sheet->sheet_window &&
3411 ! (POSSIBLE_RESIZE (sheet, x, y, &row, &column) ) &&
3412 (POSSIBLE_DRAG (sheet, x, y, &row, &column) ||
3413 PSPPIRE_SHEET_IN_DRAG (sheet)) &&
3414 new_cursor != sheet->cursor_drag->type)
3416 gdk_cursor_unref (sheet->cursor_drag);
3417 sheet->cursor_drag = gdk_cursor_new_for_display (display, GDK_TOP_LEFT_ARROW);
3418 gdk_window_set_cursor (sheet->sheet_window, sheet->cursor_drag);
3421 gdk_window_get_pointer (widget->window, &x, &y, &mods);
3422 if (! (mods & GDK_BUTTON1_MASK)) return FALSE;
3424 if (PSPPIRE_SHEET_IN_XDRAG (sheet))
3426 if (event->x != sheet->x_drag)
3428 draw_xor_vline (sheet);
3429 sheet->x_drag = event->x;
3430 draw_xor_vline (sheet);
3436 if (PSPPIRE_SHEET_IN_YDRAG (sheet))
3438 if (event->y != sheet->y_drag)
3440 draw_xor_hline (sheet);
3441 sheet->y_drag = event->y;
3442 draw_xor_hline (sheet);
3448 if (PSPPIRE_SHEET_IN_DRAG (sheet))
3450 PsppireSheetRange aux;
3451 column = column_from_xpixel (sheet, x)- sheet->drag_cell.col;
3452 row = row_from_ypixel (sheet, y) - sheet->drag_cell.row;
3453 if (sheet->select_status == PSPPIRE_SHEET_COLUMN_SELECTED) row = 0;
3454 if (sheet->select_status == PSPPIRE_SHEET_ROW_SELECTED) column = 0;
3458 if (aux.row0 + row >= 0 && aux.rowi + row < psppire_axis_unit_count (sheet->vaxis) &&
3459 aux.col0 + column >= 0 && aux.coli + column < psppire_axis_unit_count (sheet->haxis))
3461 aux = sheet->drag_range;
3462 sheet->drag_range.row0 = sheet->range.row0 + row;
3463 sheet->drag_range.col0 = sheet->range.col0 + column;
3464 sheet->drag_range.rowi = sheet->range.rowi + row;
3465 sheet->drag_range.coli = sheet->range.coli + column;
3466 if (aux.row0 != sheet->drag_range.row0 ||
3467 aux.col0 != sheet->drag_range.col0)
3469 draw_xor_rectangle (sheet, aux);
3470 draw_xor_rectangle (sheet, sheet->drag_range);
3476 psppire_sheet_get_pixel_info (sheet, x, y, &row, &column);
3478 if (sheet->select_status == PSPPIRE_SHEET_NORMAL && row == sheet->active_cell.row &&
3479 column == sheet->active_cell.col) return TRUE;
3481 if ( mods & GDK_BUTTON1_MASK)
3483 if (PSPPIRE_SHEET_IN_SELECTION (sheet) )
3485 /* Redraw the old range */
3486 psppire_sheet_unselect_range (sheet);
3488 sheet->range.rowi = row;
3489 sheet->range.coli = column;
3491 /* Redraw the new range */
3492 psppire_sheet_select_range (sheet, &sheet->range);
3496 PSPPIRE_SHEET_SET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3504 psppire_sheet_crossing_notify (GtkWidget *widget,
3505 GdkEventCrossing *event)
3507 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3509 if (event->window == sheet->column_title_window)
3510 sheet->column_title_under = event->type == GDK_ENTER_NOTIFY;
3511 else if (event->window == sheet->row_title_window)
3512 sheet->row_title_under = event->type == GDK_ENTER_NOTIFY;
3514 if (event->type == GDK_LEAVE_NOTIFY)
3515 gtk_widget_hide (sheet->hover_window->window);
3522 psppire_sheet_focus_in (GtkWidget *w,
3523 GdkEventFocus *event)
3525 PsppireSheet *sheet = PSPPIRE_SHEET (w);
3527 gtk_widget_grab_focus (sheet->entry_widget);
3535 psppire_sheet_entry_key_press (GtkWidget *widget,
3539 g_signal_emit_by_name (widget, "key_press_event", key, &focus);
3544 /* Number of rows in a step-increment */
3545 #define ROWS_PER_STEP 1
3549 page_vertical (PsppireSheet *sheet, GtkScrollType dir)
3551 gint old_row = sheet->active_cell.row ;
3552 glong vpixel = psppire_axis_start_pixel (sheet->vaxis, old_row);
3556 vpixel -= psppire_axis_start_pixel (sheet->vaxis,
3557 min_visible_row (sheet));
3561 case GTK_SCROLL_PAGE_DOWN:
3562 gtk_adjustment_set_value (sheet->vadjustment,
3563 sheet->vadjustment->value +
3564 sheet->vadjustment->page_increment);
3566 case GTK_SCROLL_PAGE_UP:
3567 gtk_adjustment_set_value (sheet->vadjustment,
3568 sheet->vadjustment->value -
3569 sheet->vadjustment->page_increment);
3573 g_assert_not_reached ();
3578 vpixel += psppire_axis_start_pixel (sheet->vaxis,
3579 min_visible_row (sheet));
3581 new_row = row_from_ypixel (sheet, vpixel);
3583 change_active_cell (sheet, new_row,
3584 sheet->active_cell.col);
3589 step_sheet (PsppireSheet *sheet, GtkScrollType dir)
3591 gint current_row = sheet->active_cell.row;
3592 gint current_col = sheet->active_cell.col;
3593 PsppireSheetCell new_cell ;
3594 gboolean forbidden = FALSE;
3596 new_cell.row = current_row;
3597 new_cell.col = current_col;
3601 case GTK_SCROLL_STEP_DOWN:
3604 case GTK_SCROLL_STEP_UP:
3607 case GTK_SCROLL_STEP_RIGHT:
3610 case GTK_SCROLL_STEP_LEFT:
3613 case GTK_SCROLL_STEP_FORWARD:
3616 psppire_sheet_model_get_column_count (sheet->model))
3622 case GTK_SCROLL_STEP_BACKWARD:
3624 if (new_cell.col < 0)
3627 psppire_sheet_model_get_column_count (sheet->model) - 1;
3632 g_assert_not_reached ();
3636 g_signal_emit (sheet, sheet_signals[TRAVERSE], 0,
3637 &sheet->active_cell,
3645 maximize_int (&new_cell.row, 0);
3646 maximize_int (&new_cell.col, 0);
3648 minimize_int (&new_cell.row,
3649 psppire_axis_unit_count (sheet->vaxis) - 1);
3651 minimize_int (&new_cell.col,
3652 psppire_axis_unit_count (sheet->haxis) - 1);
3654 change_active_cell (sheet, new_cell.row, new_cell.col);
3657 if ( new_cell.col > max_fully_visible_column (sheet))
3660 psppire_axis_start_pixel (sheet->haxis,
3662 hpos -= sheet->hadjustment->page_size;
3664 gtk_adjustment_set_value (sheet->hadjustment,
3667 else if ( new_cell.col < min_fully_visible_column (sheet))
3670 psppire_axis_start_pixel (sheet->haxis,
3673 gtk_adjustment_set_value (sheet->hadjustment,
3678 if ( new_cell.row > max_fully_visible_row (sheet))
3681 psppire_axis_start_pixel (sheet->vaxis,
3683 vpos -= sheet->vadjustment->page_size;
3685 gtk_adjustment_set_value (sheet->vadjustment,
3688 else if ( new_cell.row < min_fully_visible_row (sheet))
3691 psppire_axis_start_pixel (sheet->vaxis,
3694 gtk_adjustment_set_value (sheet->vadjustment,
3698 gtk_widget_grab_focus (GTK_WIDGET (sheet->entry_widget));
3703 psppire_sheet_key_press (GtkWidget *widget,
3706 PsppireSheet *sheet = PSPPIRE_SHEET (widget);
3708 PSPPIRE_SHEET_UNSET_FLAGS (sheet, PSPPIRE_SHEET_IN_SELECTION);
3710 switch (key->keyval)
3713 step_sheet (sheet, GTK_SCROLL_STEP_FORWARD);
3716 step_sheet (sheet, GTK_SCROLL_STEP_RIGHT);
3718 case GDK_ISO_Left_Tab:
3719 step_sheet (sheet, GTK_SCROLL_STEP_BACKWARD);
3722 step_sheet (sheet, GTK_SCROLL_STEP_LEFT);
3726 step_sheet (sheet, GTK_SCROLL_STEP_DOWN);
3729 step_sheet (sheet, GTK_SCROLL_STEP_UP);
3733 page_vertical (sheet, GTK_SCROLL_PAGE_DOWN);
3736 page_vertical (sheet, GTK_SCROLL_PAGE_UP);
3740 gtk_adjustment_set_value (sheet->vadjustment,
3741 sheet->vadjustment->lower);
3743 change_active_cell (sheet, 0,
3744 sheet->active_cell.col);
3749 gtk_adjustment_set_value (sheet->vadjustment,
3750 sheet->vadjustment->upper -
3751 sheet->vadjustment->page_size -
3752 sheet->vadjustment->page_increment);
3755 change_active_cellx (sheet,
3756 psppire_axis_unit_count (sheet->vaxis) - 1,
3757 sheet->active_cell.col);
3761 psppire_sheet_real_cell_clear (sheet, sheet->active_cell.row, sheet->active_cell.col);
3772 psppire_sheet_size_request (GtkWidget *widget,
3773 GtkRequisition *requisition)
3775 PsppireSheet *sheet;
3777 g_return_if_fail (widget != NULL);
3778 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3779 g_return_if_fail (requisition != NULL);
3781 sheet = PSPPIRE_SHEET (widget);
3783 requisition->width = 3 * DEFAULT_COLUMN_WIDTH;
3784 requisition->height = 3 * DEFAULT_ROW_HEIGHT;
3786 /* compute the size of the column title area */
3787 if (sheet->column_titles_visible)
3788 requisition->height += sheet->column_title_area.height;
3790 /* compute the size of the row title area */
3791 if (sheet->row_titles_visible)
3792 requisition->width += sheet->row_title_area.width;
3797 psppire_sheet_size_allocate (GtkWidget *widget,
3798 GtkAllocation *allocation)
3800 PsppireSheet *sheet;
3801 GtkAllocation sheet_allocation;
3804 g_return_if_fail (widget != NULL);
3805 g_return_if_fail (PSPPIRE_IS_SHEET (widget));
3806 g_return_if_fail (allocation != NULL);
3808 sheet = PSPPIRE_SHEET (widget);
3809 widget->allocation = *allocation;
3810 border_width = GTK_CONTAINER (widget)->border_width;
3812 if (GTK_WIDGET_REALIZED (widget))
3813 gdk_window_move_resize (widget->window,
3814 allocation->x + border_width,
3815 allocation->y + border_width,
3816 allocation->width - 2 * border_width,
3817 allocation->height - 2 * border_width);
3819 sheet_allocation.x = 0;
3820 sheet_allocation.y = 0;
3821 sheet_allocation.width = allocation->width - 2 * border_width;
3822 sheet_allocation.height = allocation->height - 2 * border_width;
3824 if (GTK_WIDGET_REALIZED (widget))
3825 gdk_window_move_resize (sheet->sheet_window,
3828 sheet_allocation.width,
3829 sheet_allocation.height);
3831 /* position the window which holds the column title buttons */
3832 sheet->column_title_area.x = 0;
3833 sheet->column_title_area.y = 0;
3834 sheet->column_title_area.width = sheet_allocation.width ;
3837 /* position the window which holds the row title buttons */
3838 sheet->row_title_area.x = 0;
3839 sheet->row_title_area.y = 0;
3840 sheet->row_title_area.height = sheet_allocation.height;
3842 if (sheet->row_titles_visible)
3843 sheet->column_title_area.x += sheet->row_title_area.width;
3845 if (sheet->column_titles_visible)
3846 sheet->row_title_area.y += sheet->column_title_area.height;
3848 if (GTK_WIDGET_REALIZED (widget) && sheet->column_titles_visible)
3849 gdk_window_move_resize (sheet->column_title_window,
3850 sheet->column_title_area.x,
3851 sheet->column_title_area.y,
3852 sheet->column_title_area.width,
3853 sheet->column_title_area.height);
3856 if (GTK_WIDGET_REALIZED (widget) && sheet->row_titles_visible)
3857 gdk_window_move_resize (sheet->row_title_window,
3858 sheet->row_title_area.x,
3859 sheet->row_title_area.y,
3860 sheet->row_title_area.width,
3861 sheet->row_title_area.height);
3863 size_allocate_global_button (sheet);
3867 gint width = sheet->column_title_area.width;
3869 if ( sheet->row_titles_visible)
3870 width -= sheet->row_title_area.width;
3872 g_object_set (sheet->haxis,
3873 "minimum-extent", width,
3880 gint height = sheet->row_title_area.height;
3882 if ( sheet->column_titles_visible)
3883 height -= sheet->column_title_area.height;
3885 g_object_set (sheet->vaxis,
3886 "minimum-extent", height,
3891 /* set the scrollbars adjustments */
3892 adjust_scrollbars (sheet);
3896 draw_column_title_buttons (PsppireSheet *sheet)
3900 if (!sheet->column_titles_visible) return;
3901 if (!GTK_WIDGET_REALIZED (sheet))
3904 gdk_drawable_get_size (sheet->sheet_window, &width, NULL);
3907 if (sheet->row_titles_visible)
3909 x = sheet->row_title_area.width;
3912 if (sheet->column_title_area.width != width || sheet->column_title_area.x != x)
3914 sheet->column_title_area.width = width;
3915 sheet->column_title_area.x = x;
3916 gdk_window_move_resize (sheet->column_title_window,
3917 sheet->column_title_area.x,
3918 sheet->column_title_area.y,
3919 sheet->column_title_area.width,
3920 sheet->column_title_area.height);
3923 if (max_visible_column (sheet) ==
3924 psppire_axis_unit_count (sheet->haxis) - 1)
3925 gdk_window_clear_area (sheet->column_title_window,
3927 sheet->column_title_area.width,
3928 sheet->column_title_area.height);
3930 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
3932 draw_column_title_buttons_range (sheet, min_visible_column (sheet),
3933 max_visible_column (sheet));
3937 draw_row_title_buttons (PsppireSheet *sheet)
3942 if (!sheet->row_titles_visible) return;
3943 if (!GTK_WIDGET_REALIZED (sheet))
3946 gdk_drawable_get_size (sheet->sheet_window, NULL, &height);
3948 if (sheet->column_titles_visible)
3950 y = sheet->column_title_area.height;
3953 if (sheet->row_title_area.height != height || sheet->row_title_area.y != y)
3955 sheet->row_title_area.y = y;
3956 sheet->row_title_area.height = height;
3957 gdk_window_move_resize (sheet->row_title_window,
3958 sheet->row_title_area.x,
3959 sheet->row_title_area.y,
3960 sheet->row_title_area.width,
3961 sheet->row_title_area.height);
3964 if (max_visible_row (sheet) == psppire_axis_unit_count (sheet->vaxis) - 1)
3965 gdk_window_clear_area (sheet->row_title_window,
3967 sheet->row_title_area.width,
3968 sheet->row_title_area.height);
3970 if (!GTK_WIDGET_DRAWABLE (sheet)) return;
3972 draw_row_title_buttons_range (sheet, min_visible_row (sheet),
3973 max_visible_row (sheet));
3978 psppire_sheet_size_allocate_entry (PsppireSheet *sheet)
3980 GtkAllocation entry_alloc;
3981 PsppireSheetCellAttr attributes = { 0 };
3982 GtkEntry *sheet_entry;
3984 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
3985 if (!GTK_WIDGET_MAPPED (GTK_WIDGET (sheet))) return;
3987 sheet_entry = psppire_sheet_get_entry (sheet);
3989 if ( ! psppire_sheet_get_attributes (sheet, sheet->active_cell.row,
3990 sheet->active_cell.col,
3994 if ( GTK_WIDGET_REALIZED (sheet->entry_widget) )
3996 GtkStyle *style = GTK_WIDGET (sheet_entry)->style;
3998 style->bg[GTK_STATE_NORMAL] = attributes.background;
3999 style->fg[GTK_STATE_NORMAL] = attributes.foreground;
4000 style->text[GTK_STATE_NORMAL] = attributes.foreground;
4001 style->bg[GTK_STATE_ACTIVE] = attributes.background;
4002 style->fg[GTK_STATE_ACTIVE] = attributes.foreground;
4003 style->text[GTK_STATE_ACTIVE] = attributes.foreground;
4006 rectangle_from_cell (sheet, sheet->active_cell.row,
4007 sheet->active_cell.col, &entry_alloc);
4009 entry_alloc.x += sheet->cell_padding->left;
4010 entry_alloc.y += sheet->cell_padding->right;
4011 entry_alloc.width -= sheet->cell_padding->left + sheet->cell_padding->right;
4012 entry_alloc.height -= sheet->cell_padding->top + sheet->cell_padding->bottom;
4015 gtk_widget_set_size_request (sheet->entry_widget, entry_alloc.width,
4016 entry_alloc.height);
4017 gtk_widget_size_allocate (sheet->entry_widget, &entry_alloc);
4021 /* Copy the sheet's font to the entry widget */
4023 set_entry_widget_font (PsppireSheet *sheet)
4025 GtkRcStyle *style = gtk_widget_get_modifier_style (sheet->entry_widget);
4027 pango_font_description_free (style->font_desc);
4028 style->font_desc = pango_font_description_copy (GTK_WIDGET (sheet)->style->font_desc);
4030 gtk_widget_modify_style (sheet->entry_widget, style);
4034 create_sheet_entry (PsppireSheet *sheet)
4036 if (sheet->entry_widget)
4038 gtk_widget_unparent (sheet->entry_widget);
4041 sheet->entry_widget = g_object_new (sheet->entry_type, NULL);
4042 g_object_ref_sink (sheet->entry_widget);
4044 gtk_widget_size_request (sheet->entry_widget, NULL);
4046 if ( GTK_IS_ENTRY (sheet->entry_widget))
4048 g_object_set (sheet->entry_widget,
4053 if (GTK_WIDGET_REALIZED (sheet))
4055 gtk_widget_set_parent_window (sheet->entry_widget, sheet->sheet_window);
4056 gtk_widget_set_parent (sheet->entry_widget, GTK_WIDGET (sheet));
4057 gtk_widget_realize (sheet->entry_widget);
4060 g_signal_connect_swapped (sheet->entry_widget, "key_press_event",
4061 G_CALLBACK (psppire_sheet_entry_key_press),
4064 set_entry_widget_font (sheet);
4066 gtk_widget_show (sheet->entry_widget);
4070 /* Finds the last child widget that happens to be of type GtkEntry */
4072 find_entry (GtkWidget *w, gpointer user_data)
4074 GtkWidget **entry = user_data;
4075 if ( GTK_IS_ENTRY (w))
4083 psppire_sheet_get_entry (PsppireSheet *sheet)
4085 GtkWidget *w = sheet->entry_widget;
4087 g_return_val_if_fail (sheet != NULL, NULL);
4088 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4089 g_return_val_if_fail (sheet->entry_widget != NULL, NULL);
4091 while (! GTK_IS_ENTRY (w))
4093 GtkWidget *entry = NULL;
4095 if (GTK_IS_CONTAINER (w))
4097 gtk_container_forall (GTK_CONTAINER (w), find_entry, &entry);
4106 return GTK_ENTRY (w);
4111 draw_button (PsppireSheet *sheet, GdkWindow *window,
4112 PsppireSheetButton *button, gboolean is_sensitive,
4113 GdkRectangle allocation)
4115 GtkShadowType shadow_type;
4116 gint text_width = 0, text_height = 0;
4117 PangoAlignment align = PANGO_ALIGN_LEFT;
4123 g_return_if_fail (sheet != NULL);
4124 g_return_if_fail (button != NULL);
4127 rtl = gtk_widget_get_direction (GTK_WIDGET (sheet)) == GTK_TEXT_DIR_RTL;
4129 gdk_window_clear_area (window,
4130 allocation.x, allocation.y,
4131 allocation.width, allocation.height);
4133 gtk_widget_ensure_style (sheet->button);
4135 gtk_paint_box (sheet->button->style, window,
4136 GTK_STATE_NORMAL, GTK_SHADOW_OUT,
4138 GTK_WIDGET (sheet->button),
4140 allocation.x, allocation.y,
4141 allocation.width, allocation.height);
4143 state = button->state;
4144 if (!is_sensitive) state = GTK_STATE_INSENSITIVE;
4146 if (state == GTK_STATE_ACTIVE)
4147 shadow_type = GTK_SHADOW_IN;
4149 shadow_type = GTK_SHADOW_OUT;
4151 if (state != GTK_STATE_NORMAL && state != GTK_STATE_INSENSITIVE)
4152 gtk_paint_box (sheet->button->style, window,
4153 button->state, shadow_type,
4154 &allocation, GTK_WIDGET (sheet->button),
4156 allocation.x, allocation.y,
4157 allocation.width, allocation.height);
4159 if ( button->overstruck)
4161 GdkPoint points[2] = {
4162 {allocation.x, allocation.y},
4163 {allocation.x + allocation.width,
4164 allocation.y + allocation.height}
4167 gtk_paint_polygon (sheet->button->style,
4179 if (button->label_visible)
4181 text_height = DEFAULT_ROW_HEIGHT -
4182 2 * COLUMN_TITLES_HEIGHT;
4184 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4186 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc,
4189 allocation.y += 2 * sheet->button->style->ythickness;
4191 if (button->label && strlen (button->label) > 0)
4193 PangoRectangle rect;
4194 gchar *line = button->label;
4196 PangoLayout *layout = NULL;
4197 gint real_x = allocation.x;
4198 gint real_y = allocation.y;
4200 layout = gtk_widget_create_pango_layout (GTK_WIDGET (sheet), line);
4201 pango_layout_get_extents (layout, NULL, &rect);
4203 text_width = PANGO_PIXELS (rect.width);
4204 switch (button->justification)
4206 case GTK_JUSTIFY_LEFT:
4207 real_x = allocation.x + COLUMN_TITLES_HEIGHT;
4208 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4210 case GTK_JUSTIFY_RIGHT:
4211 real_x = allocation.x + allocation.width - text_width - COLUMN_TITLES_HEIGHT;
4212 align = rtl ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
4214 case GTK_JUSTIFY_CENTER:
4216 real_x = allocation.x + (allocation.width - text_width)/2;
4217 align = rtl ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
4218 pango_layout_set_justify (layout, TRUE);
4220 pango_layout_set_alignment (layout, align);
4221 gtk_paint_layout (GTK_WIDGET (sheet)->style,
4230 g_object_unref (layout);
4233 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->fg_gc[button->state],
4235 gdk_gc_set_clip_rectangle (GTK_WIDGET (sheet)->style->white_gc, NULL);
4239 psppire_sheet_button_free (button);
4243 /* Draw the column title buttons FIRST through to LAST */
4245 draw_column_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4249 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4251 if (!sheet->column_titles_visible) return;
4253 g_return_if_fail (first >= min_visible_column (sheet));
4254 g_return_if_fail (last <= max_visible_column (sheet));
4257 rect.height = sheet->column_title_area.height;
4258 rect.x = psppire_axis_start_pixel (sheet->haxis, first) + CELL_SPACING;
4259 rect.width = psppire_axis_start_pixel (sheet->haxis, last) + CELL_SPACING
4260 + psppire_axis_unit_size (sheet->haxis, last);
4262 rect.x -= sheet->hadjustment->value;
4264 minimize_int (&rect.width, sheet->column_title_area.width);
4265 maximize_int (&rect.x, 0);
4267 gdk_window_begin_paint_rect (sheet->column_title_window, &rect);
4269 for (col = first ; col <= last ; ++col)
4271 GdkRectangle allocation;
4272 gboolean is_sensitive = FALSE;
4274 PsppireSheetButton *
4275 button = psppire_sheet_model_get_column_button (sheet->model, col);
4277 allocation.x = psppire_axis_start_pixel (sheet->haxis, col)
4279 allocation.x -= sheet->hadjustment->value;
4281 allocation.height = sheet->column_title_area.height;
4282 allocation.width = psppire_axis_unit_size (sheet->haxis, col);
4283 is_sensitive = psppire_sheet_model_get_column_sensitivity (sheet->model, col);
4285 draw_button (sheet, sheet->column_title_window,
4286 button, is_sensitive, allocation);
4289 gdk_window_end_paint (sheet->column_title_window);
4294 draw_row_title_buttons_range (PsppireSheet *sheet, gint first, gint last)
4298 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet))) return;
4300 if (!sheet->row_titles_visible) return;
4302 g_return_if_fail (first >= min_visible_row (sheet));
4303 g_return_if_fail (last <= max_visible_row (sheet));
4306 rect.width = sheet->row_title_area.width;
4307 rect.y = psppire_axis_start_pixel (sheet->vaxis, first) + CELL_SPACING;
4308 rect.height = psppire_axis_start_pixel (sheet->vaxis, last) + CELL_SPACING
4309 + psppire_axis_unit_size (sheet->vaxis, last);
4311 rect.y -= sheet->vadjustment->value;
4313 minimize_int (&rect.height, sheet->row_title_area.height);
4314 maximize_int (&rect.y, 0);
4316 gdk_window_begin_paint_rect (sheet->row_title_window, &rect);
4317 for (row = first; row <= last; ++row)
4319 GdkRectangle allocation;
4321 gboolean is_sensitive = FALSE;
4323 PsppireSheetButton *button =
4324 psppire_sheet_model_get_row_button (sheet->model, row);
4326 allocation.y = psppire_axis_start_pixel (sheet->vaxis, row)
4328 allocation.y -= sheet->vadjustment->value;
4330 allocation.width = sheet->row_title_area.width;
4331 allocation.height = psppire_axis_unit_size (sheet->vaxis, row);
4332 is_sensitive = psppire_sheet_model_get_row_sensitivity (sheet->model, row);
4334 draw_button (sheet, sheet->row_title_window,
4335 button, is_sensitive, allocation);
4338 gdk_window_end_paint (sheet->row_title_window);
4345 * vadjustment_value_changed
4346 * hadjustment_value_changed */
4350 update_adjustment (GtkAdjustment *adj, PsppireAxis *axis, gint page_size)
4353 (adj->value + adj->page_size)
4355 (adj->upper - adj->lower);
4357 const glong last_item = psppire_axis_unit_count (axis) - 1;
4359 if (isnan (position) || position < 0)
4363 psppire_axis_start_pixel (axis, last_item)
4365 psppire_axis_unit_size (axis, last_item)
4369 adj->page_size = page_size;
4372 adj->value = position * (adj->upper - adj->lower) - adj->page_size;
4374 if ( adj->value < adj->lower)
4375 adj->value = adj->lower;
4378 gtk_adjustment_changed (adj);
4383 adjust_scrollbars (PsppireSheet *sheet)
4387 if (!GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4390 gdk_drawable_get_size (sheet->sheet_window, &width, &height);
4392 if ( sheet->row_titles_visible)
4393 width -= sheet->row_title_area.width;
4395 if (sheet->column_titles_visible)
4396 height -= sheet->column_title_area.height;
4398 if (sheet->vadjustment)
4400 glong last_row = psppire_axis_unit_count (sheet->vaxis) - 1;
4402 sheet->vadjustment->step_increment =
4404 psppire_axis_unit_size (sheet->vaxis, last_row);
4406 sheet->vadjustment->page_increment =
4408 sheet->column_title_area.height -
4409 psppire_axis_unit_size (sheet->vaxis, last_row);
4411 update_adjustment (sheet->vadjustment, sheet->vaxis, height);
4414 if (sheet->hadjustment)
4416 gint last_col = psppire_axis_unit_count (sheet->haxis) - 1;
4417 sheet->hadjustment->step_increment = 1;
4419 sheet->hadjustment->page_increment = width;
4421 sheet->hadjustment->upper =
4422 psppire_axis_start_pixel (sheet->haxis, last_col)
4424 psppire_axis_unit_size (sheet->haxis, last_col)
4427 update_adjustment (sheet->hadjustment, sheet->haxis, width);
4431 /* Subtracts the region of WIDGET from REGION */
4433 subtract_widget_region (GdkRegion *region, GtkWidget *widget)
4436 GdkRectangle intersect;
4439 gdk_region_get_clipbox (region, &rect);
4440 gtk_widget_intersect (widget,
4444 region2 = gdk_region_rectangle (&intersect);
4445 gdk_region_subtract (region, region2);
4446 gdk_region_destroy (region2);
4450 vadjustment_value_changed (GtkAdjustment *adjustment,
4454 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4456 g_return_if_fail (adjustment != NULL);
4458 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4460 gtk_widget_hide (sheet->entry_widget);
4463 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4465 subtract_widget_region (region, sheet->button);
4466 gdk_window_begin_paint_region (sheet->sheet_window, region);
4468 draw_sheet_region (sheet, region);
4470 draw_row_title_buttons (sheet);
4471 psppire_sheet_draw_active_cell (sheet);
4473 gdk_window_end_paint (sheet->sheet_window);
4474 gdk_region_destroy (region);
4479 hadjustment_value_changed (GtkAdjustment *adjustment,
4483 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4485 g_return_if_fail (adjustment != NULL);
4487 if ( ! GTK_WIDGET_REALIZED (sheet)) return;
4489 gtk_widget_hide (sheet->entry_widget);
4493 gdk_drawable_get_visible_region (GDK_DRAWABLE (sheet->sheet_window));
4495 subtract_widget_region (region, sheet->button);
4496 gdk_window_begin_paint_region (sheet->sheet_window, region);
4498 draw_sheet_region (sheet, region);
4500 draw_column_title_buttons (sheet);
4502 psppire_sheet_draw_active_cell (sheet);
4504 gdk_window_end_paint (sheet->sheet_window);
4506 gdk_region_destroy (region);
4510 /* COLUMN RESIZING */
4512 draw_xor_vline (PsppireSheet *sheet)
4515 gint xpos = sheet->x_drag;
4516 gdk_drawable_get_size (sheet->sheet_window,
4519 if (sheet->row_titles_visible)
4520 xpos += sheet->row_title_area.width;
4522 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4524 sheet->column_title_area.height,
4526 height + CELL_SPACING);
4531 draw_xor_hline (PsppireSheet *sheet)
4535 gint ypos = sheet->y_drag;
4537 gdk_drawable_get_size (sheet->sheet_window,
4541 if (sheet->column_titles_visible)
4542 ypos += sheet->column_title_area.height;
4544 gdk_draw_line (GTK_WIDGET (sheet)->window, sheet->xor_gc,
4545 sheet->row_title_area.width,
4547 width + CELL_SPACING,
4551 /* SELECTED RANGE */
4553 draw_xor_rectangle (PsppireSheet *sheet, PsppireSheetRange range)
4556 GdkRectangle clip_area, area;
4559 area.x = psppire_axis_start_pixel (sheet->haxis, range.col0);
4560 area.y = psppire_axis_start_pixel (sheet->vaxis, range.row0);
4561 area.width = psppire_axis_start_pixel (sheet->haxis, range.coli)- area.x+
4562 psppire_axis_unit_size (sheet->haxis, range.coli);
4563 area.height = psppire_axis_start_pixel (sheet->vaxis, range.rowi)- area.y +
4564 psppire_axis_unit_size (sheet->vaxis, range.rowi);
4566 clip_area.x = sheet->row_title_area.width;
4567 clip_area.y = sheet->column_title_area.height;
4569 gdk_drawable_get_size (sheet->sheet_window,
4570 &clip_area.width, &clip_area.height);
4572 if (!sheet->row_titles_visible) clip_area.x = 0;
4573 if (!sheet->column_titles_visible) clip_area.y = 0;
4577 area.width = area.width + area.x;
4580 if (area.width > clip_area.width) area.width = clip_area.width + 10;
4583 area.height = area.height + area.y;
4586 if (area.height > clip_area.height) area.height = clip_area.height + 10;
4590 clip_area.width += 3;
4591 clip_area.height += 3;
4593 gdk_gc_get_values (sheet->xor_gc, &values);
4595 gdk_gc_set_clip_rectangle (sheet->xor_gc, &clip_area);
4597 gdk_draw_rectangle (sheet->sheet_window,
4600 area.x + i, area.y + i,
4601 area.width - 2 * i, area.height - 2 * i);
4604 gdk_gc_set_clip_rectangle (sheet->xor_gc, NULL);
4606 gdk_gc_set_foreground (sheet->xor_gc, &values.foreground);
4611 set_column_width (PsppireSheet *sheet,
4615 g_return_if_fail (sheet != NULL);
4616 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4618 if (column < 0 || column >= psppire_axis_unit_count (sheet->haxis))
4624 psppire_axis_resize (sheet->haxis, column,
4625 width - sheet->cell_padding->left -
4626 sheet->cell_padding->right);
4628 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4630 draw_column_title_buttons (sheet);
4631 adjust_scrollbars (sheet);
4632 psppire_sheet_size_allocate_entry (sheet);
4633 redraw_range (sheet, NULL);
4638 set_row_height (PsppireSheet *sheet,
4642 g_return_if_fail (sheet != NULL);
4643 g_return_if_fail (PSPPIRE_IS_SHEET (sheet));
4645 if (row < 0 || row >= psppire_axis_unit_count (sheet->vaxis))
4651 psppire_axis_resize (sheet->vaxis, row,
4652 height - sheet->cell_padding->top -
4653 sheet->cell_padding->bottom);
4655 if (GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)) )
4657 draw_row_title_buttons (sheet);
4658 adjust_scrollbars (sheet);
4659 psppire_sheet_size_allocate_entry (sheet);
4660 redraw_range (sheet, NULL);
4665 psppire_sheet_get_attributes (const PsppireSheet *sheet, gint row, gint col,
4666 PsppireSheetCellAttr *attr)
4669 const GtkJustification *j ;
4670 GdkColormap *colormap;
4672 g_return_val_if_fail (sheet != NULL, FALSE);
4673 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), FALSE);
4675 if (row < 0 || col < 0) return FALSE;
4677 attr->foreground = GTK_WIDGET (sheet)->style->black;
4678 attr->background = sheet->color[BG_COLOR];
4680 attr->border.width = 0;
4681 attr->border.line_style = GDK_LINE_SOLID;
4682 attr->border.cap_style = GDK_CAP_NOT_LAST;
4683 attr->border.join_style = GDK_JOIN_MITER;
4684 attr->border.mask = 0;
4685 attr->border.color = GTK_WIDGET (sheet)->style->black;
4687 colormap = gtk_widget_get_colormap (GTK_WIDGET (sheet));
4688 fg = psppire_sheet_model_get_foreground (sheet->model, row, col);
4691 gdk_colormap_alloc_color (colormap, fg, TRUE, TRUE);
4692 attr->foreground = *fg;
4695 bg = psppire_sheet_model_get_background (sheet->model, row, col);
4698 gdk_colormap_alloc_color (colormap, bg, TRUE, TRUE);
4699 attr->background = *bg;
4702 attr->justification =
4703 psppire_sheet_model_get_column_justification (sheet->model, col);
4705 j = psppire_sheet_model_get_justification (sheet->model, row, col);
4707 attr->justification = *j;
4713 psppire_sheet_button_size_request (PsppireSheet *sheet,
4714 const PsppireSheetButton *button,
4715 GtkRequisition *button_requisition)
4717 GtkRequisition requisition;
4718 GtkRequisition label_requisition;
4720 label_requisition.height = DEFAULT_ROW_HEIGHT;
4721 label_requisition.width = COLUMN_MIN_WIDTH;
4723 requisition.height = DEFAULT_ROW_HEIGHT;
4724 requisition.width = COLUMN_MIN_WIDTH;
4727 *button_requisition = requisition;
4728 button_requisition->width = MAX (requisition.width, label_requisition.width);
4729 button_requisition->height = MAX (requisition.height, label_requisition.height);
4734 psppire_sheet_forall (GtkContainer *container,
4735 gboolean include_internals,
4736 GtkCallback callback,
4737 gpointer callback_data)
4739 PsppireSheet *sheet = PSPPIRE_SHEET (container);
4741 g_return_if_fail (callback != NULL);
4743 if (sheet->button && sheet->button->parent)
4744 (* callback) (sheet->button, callback_data);
4746 if (sheet->entry_widget && GTK_IS_CONTAINER (sheet->entry_widget))
4747 (* callback) (sheet->entry_widget, callback_data);
4752 psppire_sheet_get_model (const PsppireSheet *sheet)
4754 g_return_val_if_fail (PSPPIRE_IS_SHEET (sheet), NULL);
4756 return sheet->model;
4760 PsppireSheetButton *
4761 psppire_sheet_button_new (void)
4763 PsppireSheetButton *button = g_malloc (sizeof (PsppireSheetButton));
4765 button->state = GTK_STATE_NORMAL;
4766 button->label = NULL;
4767 button->label_visible = TRUE;
4768 button->justification = GTK_JUSTIFY_FILL;
4769 button->overstruck = FALSE;
4776 psppire_sheet_button_free (PsppireSheetButton *button)
4778 if (!button) return ;
4780 g_free (button->label);
4785 append_cell_text (GString *string, const PsppireSheet *sheet, gint r, gint c)
4787 gchar *celltext = psppire_sheet_cell_get_text (sheet, r, c);
4789 if ( NULL == celltext)
4792 g_string_append (string, celltext);
4798 range_to_text (const PsppireSheet *sheet)
4803 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4806 string = g_string_sized_new (80);
4808 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4810 for (c = sheet->range.col0; c < sheet->range.coli; ++c)
4812 append_cell_text (string, sheet, r, c);
4813 g_string_append (string, "\t");
4815 append_cell_text (string, sheet, r, c);
4816 if ( r < sheet->range.rowi)
4817 g_string_append (string, "\n");
4824 range_to_html (const PsppireSheet *sheet)
4829 if ( !psppire_sheet_range_isvisible (sheet, &sheet->range))
4832 string = g_string_sized_new (480);
4834 g_string_append (string, "<html>\n");
4835 g_string_append (string, "<body>\n");
4836 g_string_append (string, "<table>\n");
4837 for (r = sheet->range.row0; r <= sheet->range.rowi; ++r)
4839 g_string_append (string, "<tr>\n");
4840 for (c = sheet->range.col0; c <= sheet->range.coli; ++c)
4842 g_string_append (string, "<td>");
4843 append_cell_text (string, sheet, r, c);
4844 g_string_append (string, "</td>\n");
4846 g_string_append (string, "</tr>\n");
4848 g_string_append (string, "</table>\n");
4849 g_string_append (string, "</body>\n");
4850 g_string_append (string, "</html>\n");
4862 primary_get_cb (GtkClipboard *clipboard,
4863 GtkSelectionData *selection_data,
4867 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4868 GString *string = NULL;
4872 case SELECT_FMT_TEXT:
4873 string = range_to_text (sheet);
4875 case SELECT_FMT_HTML:
4876 string = range_to_html (sheet);
4879 g_assert_not_reached ();
4882 gtk_selection_data_set (selection_data, selection_data->target,
4884 (const guchar *) string->str, string->len);
4885 g_string_free (string, TRUE);
4889 primary_clear_cb (GtkClipboard *clipboard,
4892 PsppireSheet *sheet = PSPPIRE_SHEET (data);
4893 if ( ! GTK_WIDGET_REALIZED (GTK_WIDGET (sheet)))
4896 psppire_sheet_unselect_range (sheet);
4900 psppire_sheet_update_primary_selection (PsppireSheet *sheet)
4902 static const GtkTargetEntry targets[] = {
4903 { "UTF8_STRING", 0, SELECT_FMT_TEXT },
4904 { "STRING", 0, SELECT_FMT_TEXT },
4905 { "TEXT", 0, SELECT_FMT_TEXT },
4906 { "COMPOUND_TEXT", 0, SELECT_FMT_TEXT },
4907 { "text/plain;charset=utf-8", 0, SELECT_FMT_TEXT },
4908 { "text/plain", 0, SELECT_FMT_TEXT },
4909 { "text/html", 0, SELECT_FMT_HTML }
4912 GtkClipboard *clipboard;
4914 if (!GTK_WIDGET_REALIZED (sheet))
4917 clipboard = gtk_widget_get_clipboard (GTK_WIDGET (sheet),
4918 GDK_SELECTION_PRIMARY);
4920 if (psppire_sheet_range_isvisible (sheet, &sheet->range))
4922 if (!gtk_clipboard_set_with_owner (clipboard, targets,
4923 G_N_ELEMENTS (targets),
4924 primary_get_cb, primary_clear_cb,
4926 primary_clear_cb (clipboard, sheet);
4930 if (gtk_clipboard_get_owner (clipboard) == G_OBJECT (sheet))
4931 gtk_clipboard_clear (clipboard);